function Suggester()
{
	this.Initialize = function(Options)
	{
		this.Options =
		{
			TargetElement: '',
			DataSource: '',
			LettersToSuggestion: 3,
			Suggestions: 10,
			DivClass: '',
			ListItemClass: '',
			ListItemHover: '',
			Timeout: 200
		}

		Object.extend(this.Options, Options || {});

		if (!$(this.Options.TargetElement))
		{
			return;
		}

		this.ObserverId = Event.observe($(this.Options.TargetElement), 'keyup', this.CheckStatus.bindAsEventListener(this), false);

		this.DivId = 'suggesterdiv';

		this.SuggestTimeout = 0;

		if (!$(this.DivId))
		{
			var Div = document.createElement('div');

			Div.style.position = 'absolute';
			Div.id = this.DivId;
			Div.style.display = 'none';

			Div.className = this.Options.DivClass;

			document.body.appendChild(Div);
		}
	}

	this.CheckStatus = function(evt)
	{
		//console.log($(this.DivId).getElementsByTagName('li').length + " " + evt.keyCode);
		switch (evt.keyCode)
		{
			case 40:
			//key down
				this.SelectNextElement();
				return;
			break;

			case 38:
			//key up
				this.SelectPreviousElement();
				return;
			break;

			case 13:
			//case 12: //-test CENTRE key (num-5)
				this.EnterHandler();
				return;
			break;

			case 27:
			//escape key
				this.HidePane();
				return;
			break;
		}

		if ($(this.Options.TargetElement).value.length >= this.Options.LettersToSuggestion)
		{
			if (!this.SuggestTimeout)
			{
				this.SuggestTimeout = setTimeout(this.RequestData.bindAsEventListener(this), this.Options.Timeout);
			}
		}
		else
		{
			this.HidePane();
		}
	}

	this.ShowPane = function()
	{
		var Coordinates = Position.cumulativeOffset($(this.Options.TargetElement));

		var TargetSize = Element.getDimensions($(this.Options.TargetElement))

		$(this.DivId).style.left = Coordinates[0] + 'px';
		$(this.DivId).style.top = Coordinates[1] + TargetSize.height + 'px';

		$(this.DivId).style.width = TargetSize.width + 'px';

		if ($(this.DivId))
		{
			$(this.DivId).style.display = 'block';
		}

		this.ClickObserver = Event.observe(document, 'click', this.ClickHandler.bindAsEventListener(this), false);
	}

	this.HidePane = function()
	{
		Event.stopObserving(document, 'click', this.ClickObserver);

		if ($(this.DivId))
		{
			$(this.DivId).style.display = 'none';
		}

		clearTimeout(this.SuggestTimeout);

		this.ClearSuggestions();
	}

	this.RequestData = function()
	{
		this.SuggestTimeout = 0;

		var Keywords = $(this.Options.TargetElement).value;

		var MyAjax = new Ajax.Request
		(
			this.Options.DataSource,
			{
				method: 'post',
				parameters: 'keyword=' + Keywords +
							'&limit=' + this.Options.Suggestions,
				onSuccess: this.Populate.bindAsEventListener('', this),
				onFailure: this.HandleError.bindAsEventListener()
			}
		);
	}

	this.Populate = function(Response, Object)
	{
		var Result = Response.responseText.evalJSON();
		var SuggestionsLength = Result.Suggestions.length;

		Object.ClearSuggestions();

		if (SuggestionsLength > 0)
		{
			var ListContainer = document.createElement('ul');

			for (i = 0;i < SuggestionsLength;i++)
			{
				var Item = document.createElement('li');

				Item.className = Object.Options.ListItemClass;

				Item.onmouseover = Object.MouseOverItem.bindAsEventListener('', Object);

				Item.onclick = Object.ItemSelected.bindAsEventListener('', Object);

				var Text = document.createTextNode(Result.Suggestions[i]);

				Item.appendChild(Text);

				$(ListContainer).appendChild(Item);
			}

			$(Object.DivId).appendChild(ListContainer);

			Object.ShowPane();
		}
		else
		{
			this.HidePane();
		}
	}

	this.ClickHandler = function()
	{
		this.HidePane();
	}

	this.EnterHandler = function()
	{
		var ListItems = $(this.DivId).getElementsByTagName('li');
		var ListItemsLength = ListItems.length;

		if (ListItemsLength > 0)
		{
			for (i = 0; i < ListItemsLength; i++)
			{
				if (ListItems[i].className == this.Options.ListItemHover)
				{
					$(this.Options.TargetElement).value = ListItems[i].innerHTML;
				}
			}
		}

		this.HidePane();
	}

	this.ItemSelected = function(evt, Object)
	{
		$(Object.Options.TargetElement).value = Event.element(evt).innerHTML;
		$(Object.Options.TargetElement).focus();

		Object.HidePane();
	}

	this.SelectNextElement = function()
	{
		var ListItems = $(this.DivId).getElementsByTagName('li');
		var ListItemsLength = ListItems.length;

		if (ListItemsLength > 0)
		{
			for (i = 0; i < ListItemsLength; i++)
			{
				if (ListItems[i].className == this.Options.ListItemHover)
				{
					var Target = ListItems[i].nextSibling;
				}

				ListItems[i].className = this.Options.ListItemClass;
			}

			if (Target)
			{
				Target.className = this.Options.ListItemHover;
			}
			else
			{
				ListItems[0].className = this.Options.ListItemHover;
			}
		}
	}

	this.SelectPreviousElement = function()
	{
		var ListItems = $(this.DivId).getElementsByTagName('li');
		var ListItemsLength = ListItems.length;

		if (ListItemsLength > 0)
		{
			for (i = 0; i < ListItemsLength; i++)
			{
				if (ListItems[i].className == this.Options.ListItemHover)
				{
					var Target = ListItems[i].previousSibling;
				}

				ListItems[i].className = this.Options.ListItemClass;
			}

			if (Target)
			{
				Target.className = this.Options.ListItemHover;
			}
			else
			{
				ListItems[ListItemsLength - 1].className = this.Options.ListItemHover;
			}
		}
	}

	this.MouseOverItem = function(evt, Object)
	{
		var ListItems = $(Object.DivId).getElementsByTagName('li');
		var ListItemsLength = ListItems.length;

		if (ListItemsLength > 0)
		{
			for (i = 0; i < ListItemsLength; i++)
			{
				ListItems[i].className = Object.Options.ListItemClass;
			}
		}

		Event.element(evt).className = Object.Options.ListItemHover;
	}

	this.ClearSuggestions = function()
	{
		if ($(this.DivId).firstChild)
		{
			$(this.DivId).removeChild($(this.DivId).firstChild);
		}
	}

	this.HandleError = function()
	{
		this.HidePane();
	}
}