(function($) {
	$.fn.Suggest = function(settings) {
		var config = {};
		settings = $.extend(true,config,settings);
		
		var plugin = new Suggest(this,settings);
		plugin.init();
		return plugin;
	};
	
	$.Suggest = function(elem,settings) {
		return $(elem).Suggest(settings);
	};
	
	Suggest = function(e,settings)
	{
		settings.onselect 			= settings.onselect || {};
		settings.onchoose 			= settings.onchoose || {};
		settings.onselectTextBox	= settings.onselectTextBox || this.defaultSelectTextBox;
		
		settings.hideonchoose 		= settings.hideonchoose == undefined ? true : settings.hideonchoose;
		settings.toggleElement 		= settings.toggleElement == undefined ? true : settings.toggleElement;
		settings.enableKeyboard		= settings.enableKeyboard == undefined ? true : settings.enableKeyboard;settings.enableKeyboard	= settings.enableKeyboard == undefined ? true : settings.enableKeyboard;
		
		this.settings = settings;
		
		this.e 				= e;
		this.list 			= settings.list;
		this.items			= this.getListItems();
		this.closed 		= true;
		this.currentItem 	= 1;
		this.text 			= "";
	};
	
	Suggest.prototype.init = function()
	{
		this.close();
		this.bindEvents();
		this.text = this.getText();
	};
	
	Suggest.prototype.bindMouseEvents = function()
	{
		var that = this;
		$(this.items).each( function() {
			if( !that.isTextBox(this) ) {
				$(this).mouseover( function(e) {
					that.selectItem(this,e);
				} ).click( function(e) {
					that.chooseItem(this,e);
				} );
			}
		} );
		
		this.e.unbind('mouseover');
		this.e.bind('mouseover', function(e) {
			that.selectItem(this,e);
		} );
		
		$(document).click( function(e) {
			if( $(e.target).attr('id') != (that.e.attr('id')) ) {
				that.close();
			}
		} );
	};
	
	Suggest.prototype.bindEvents = function()
	{
		if( this.settings.enableKeyboard == true ) {
			this.bindKeyboardEvents();
		}
	};

	Suggest.prototype.bindKeyboardEvents = function()
	{
		var that = this;
		$(document).keydown( function(e) {
			if( !that.isClosed() ) {
				switch( e.which ) {
				case 40:
					e.preventDefault();
					var item = that.getNextItem();
					that.selectItem(item,e);
					break;
				case 38:
					e.preventDefault();
					var item = that.getPrevItem();
					that.selectItem(item,e);
					break;
				case 39:
				case 13:
					if( that.currentItem != 0 ) {
						var item = that.getCurrentItem();
						that.chooseItem(item,e);
					}
					break;
				}
			}
		} );
	};
	
	Suggest.prototype.openList = function()
	{
		if( this.getNbItems() > 0 ) {
			this.setListPositon();
			this.list.show();
			this.closed = false;
		}
	};
	
	Suggest.prototype.setListPositon = function()
	{
		var position = this.e.offset();
		this.list.css( {
			position: 'absolute',
			top: position.top + this.e.outerHeight(),
			left: position.left,
			zIndex: 1000
		} );
	};
	
	Suggest.prototype.getNbItems = function()
	{
		return this.items.length;
	};
	
	Suggest.prototype.isClosed = function()
	{
		return this.closed;
	};
	
	Suggest.prototype.getText = function()
	{
		return this.e.val();
	};
	
	Suggest.prototype.getListItems = function()
	{
		var list = new Array();
		list.push(this.e);
		this.text = this.e.val();
		var children = this.list.children("li");
		children.each( function() {
			list.push($(this));
		} );
		
		return (list.length == 0 ? new Array() : list);
	};
	
	Suggest.prototype.getPrevItem = function()
	{
		return (this.currentItem-1 <= 0 ? this.items[this.getNbItems()-1] : this.items[this.currentItem-2]);
	};
	
	Suggest.prototype.getCurrentItem = function()
	{
		return this.items[this.currentItem-1];
	};
	
	Suggest.prototype.getNextItem = function()
	{
		return this.items[this.currentItem % (this.getNbItems())];
	};
	
	Suggest.prototype.selectItem = function(item,event)
	{
		var istextbox = this.isTextBox(item);
		
		this.currentItem = istextbox ? 1 : $(item).prevAll().length + 2;
		this.list.children("li").removeClass('selected');
		$(item).addClass('selected');
		this.scroll(item);
		
		var f1 = this.settings.onselectTextBox;
		var f2 = this.settings.onselect;
		
		if( istextbox && $.isFunction(f1) ) {
			f1.apply(item,[{currentItem:this.currentItem, event:event, text:this.text}]);
		} else if( $.isFunction(f2) ) {
			f2.apply(item,[{curentItem:this.currentItem, event:event}]);
		}
		
	};
	
	Suggest.prototype.chooseItem = function(item,event)
	{
		if( !this.isTextBox(item) ) {
			var f = this.settings.onchoose;
			if( $.isFunction(f) ) {
				f.apply(item,[{curentItem: this.currentItem,event:event}]);
			}
			
			if( this.settings.hideonchoose == false ) {
				event.stopPropagation();
			} else {
				this.close();
			}
		}
	};
	
	Suggest.prototype.scroll = function(item)
	{
		if( this.isTextBox(item) ) {
			this.list.scrollTop(0);
		}
		var itemheight = $(item).outerHeight();
		var prevheight = itemheight;
		$(item).prevAll().each( function() {
			prevheight += $(this).outerHeight();
		} );
		var listheight = this.list.height();
		var scrollTop = this.list.scrollTop();
		
		if( prevheight > (listheight + scrollTop) ) {
			this.list.scrollTop(prevheight-listheight);
		} else if( prevheight-itemheight < scrollTop ) {
			this.list.scrollTop(prevheight-itemheight);
		}
	};
	
	Suggest.prototype.isTextBox = function(elem)
	{
		return ($(elem).is("input[type='text']"));
	};
	
	Suggest.prototype.hideList = function()
	{
		this.list.hide();
		this.list.children("li").removeClass('selected');
	};
	
	Suggest.prototype.close = function()
	{
		this.hideList();
		this.currentItem = 1;
		this.closed = true;
		this.e.unbind('mouseover');
	};
	
	Suggest.prototype.update = function()
	{
		this.items = this.getListItems();
		this.currentItem = 1;
		this.bindMouseEvents();
		this.openList();
	};

	Suggest.prototype.defaultSelectTextBox = function(o)
	{
		$(this).val(o.text);
	};
	
} )(jQuery);
















