;(function($) {

	$.autocomplete = {
		input: $("input.autocomplete"),
		suggest: $("<select id='autocomplete' size='7'></select>"),
		getData: function() {
			var inputData = $.data(this.input.get(0), this.input.attr("id"));
			var params = {};
			params[inputData.config.queryMarker] = this.input.val();
			params["max"] = inputData.config.maxRetrieve;
			$.getJSON(inputData.config.url, params, function(resultData) {
				if (resultData[inputData.config.payloadId].length > 0) {
					inputData.items = resultData[inputData.config.payloadId];
					inputData.hasItems = true;
					$.autocomplete.populateSuggest($.autocomplete.input.val());
					$.autocomplete.showSuggest();
				}
			});
		},

		populateSuggest: function(value) {
			var inputData = $.data(this.input.get(0), this.input.attr("id"));
			var suggest = this.suggest;
			suggest.find("option").remove();
			$.each(inputData.items, function(i, v) {
				if (i < inputData.config.maxToShow) {
					if (v[inputData.config.dataLabel].toLowerCase().indexOf(value.toLowerCase()) > -1) {
						suggest.append("<option value='" + v[inputData.config.dataValue] + "'>" + v[inputData.config.dataLabel] + "</option>");
					}
				}
			});
		},
		showSuggest: function() {
			var suggest = this.suggest;
			if (suggest.find("option").length != 0) {
				var inputData = $.data(this.input.get(0), this.input.attr("id"));
				var size = (suggest.get(0).length < inputData.config.maxItems) ? ((suggest.get(0).length == 1) ? 2 : suggest.get(0).length) : inputData.config.maxItems;
				suggest.attr("size", size);
				$("body").append(suggest);
				suggest.css({
					left: this.input.offset().left,
					top: this.input.offset().top,
					zIndex: 99
				}).fadeIn("slow");
			} else {
				suggest.hide();
				suggest.remove();
			}
		},
		useSuggestion: function() {
			var inputData = $.data(this.input.get(0), this.input.attr("id"));
			inputData.value = this.suggest.find("option:selected").text();
			this.suggest.fadeOut("slow");
			this.input.val(this.suggest.find("option:selected").text());
			this.input.focus();

			/* callback */
			switch(typeof inputData.config.callback) {
				case "string":
					eval(inputData.config.callback).apply(this.input);
					break;
				case "function":
					inputData.config.callback.apply(this.input);
					break;
				default:
					return;
			}
		}
	};

	$.fn.autocomplete = function(config) {

		var config = $.extend({
			dataValue: "id",
			dataLabel: "name",
			maxItems: 7,
			maxRetrieve: 100,
			maxToShow: 100,
			minChars: 2,
			payloadId: "d",
			queryMarker: "q",
			url: "autocomplete.aspx"
		}, config);

		/* handle enter, space keys */
		$.autocomplete.suggest.keyup(function(e) {
			(e.keyCode == 13 || e.keyCode == 32) ? $.autocomplete.useSuggestion() : void 0;
		});

		/* handle tab key */
		$.autocomplete.suggest.keydown(function(e) {
			(e.keyCode == 9) ? $.autocomplete.useSuggestion(): void 0;
			(e.keyCode == 27) ? $.autocomplete.suggest.fadeOut("slow"): void 0;
		});

		/* handle mouse click */
		$.autocomplete.suggest.click(function(e) {
			$.autocomplete.useSuggestion();
		});

		/* add suggest listbox to page */
		//$("body").append($.autocomplete.suggest);

		/* handle multiple autocomplete fields */
		return this.each(function(i, input) {

			var input   = $(input);
			var suggest = $.autocomplete.suggest;
			var keys    = [27,37,38,39,40]; // escape, arrow keys
			var inputData;

			/* store individual autocomplete data */
			inputData = $.data(input.get(0), input.attr("id"), {
				config: config,
				hasItems: false,
				items: null,
				value: ""
			});

			/* switch off browser autocomplete */
			input.attr("autocomplete","off");

			/* handle enter, escape, arrow down key*/
			input.keypress(function(e) {
				switch(e.keyCode) {
					/*case 13:
						if (suggest.filter(":visible").length > 0) {
							suggest.focus().find("option:first").attr("selected","selected");
							$.autocomplete.useSuggestion();
							e.preventDefault();
						} else {
							return;
						}
						break;*/
					case 27:
						suggest.fadeOut("slow");
						break;
					case 40:
						if (input.val() != "") {
							suggest.focus().find("option:first").attr("selected","selected");
							e.preventDefault();
						}
						break;
					default:
						return;
				}
			});

			/* handle tab, arrow down (ie) key */
			input.keydown(function(e) {
				if (e.keyCode == 40 && ie) {
					if (input.val() != "") {
						suggest.focus().find("option:first").attr("selected","selected");
						e.preventDefault();
					}
				}
				if (e.keyCode == 9 && suggest.find("option").length != 0) {
					if (input.val() != suggest.find("option:selected").text() && input.val() != "") {
						suggest.focus().find("option:first").attr("selected","selected");
						e.preventDefault();
					} else {
						return;
					}
				}
			});

			/* handle input key events */
			input.keyup(function(e) {

				/* do not autocomplete numbers */
				if (!isNaN(input.val().substring(0,1))) {
					return;
				}

				/* set reference to current autocomplete field */
				$.autocomplete.input = $(e.target);

				/* handle [minChars] amount of characters: show suggest listbox */
				if (input.val().length == (inputData.config.minChars)) {

					/* ignore esc and arrow keys */
					if ($.inArray(e.keyCode, keys) == -1) {

						/* only retrieve data when none has been stored or input is different from previously stored value */
						var newInput = input.val().toLowerCase() != inputData.value.substring(0,input.val().length).toLowerCase();
						if (!inputData.hasItems || newInput) {
							$.autocomplete.getData();
						} else {
							$.autocomplete.populateSuggest(input.val());
							if (input.val() != suggest.find("option:selected").text()) {
								$.autocomplete.showSuggest();
							}
						}
					}
				}

				/* handle input after [minChars] amount of characters: filter suggest listbox items */
				if (input.val().length > (inputData.config.minChars) && (input.val() != suggest.find("option:selected").text())) {
					$.autocomplete.populateSuggest(input.val());
					$.autocomplete.showSuggest();
				}
			});

		});
	}

})(jQuery);

$(function() {
	$("input.autocomplete").each(function() {
		var config = $.classToObject($(this).attr("class"));
		$(this).autocomplete(config);
	});
});
