	var IDGAutoComplete 		= Class.create();
	IDGAutoComplete.prototype	= {
	
		el					: null,
		el_hidden			: null,
		el_name				: null,
		search_value		: '',
		selected_value		: '',
		selected_index		: null,
		el_list				: null,
		el_list_hilited		: null,
		list_state			: 0,
		tim_delay			: null,
		list_items			: [],
		url					: '',
		
		initialize: function( el, url, options ) {

			this.options	= {		
				value				: null,
				enableCustom		: false,
				custom_text			: 'Andet: ',
				frequency			: 200,
				minChars			: 2,
				parmSearch			: 'search',
				parmValue			: 'id',
				hidden_postfix		: '_id',
				limit				: 20,
				onSelect			: function() { },
				onLoad				: true
			}

			this.el		= $( el );

			this.options = Object.extend( this.options, options || {} );
			
			if( !this.el )
				throw( 'Element not found' );
			
			this.el_name		= this.el.getAttribute('name');
			this.url			= url;
			
			if( !this.el_name || this.el_name == '' )
				throw( 'Element has no "name" attribute' );

			// insert the hidden field before to the el
			var _el_hidden		= document.createElement('input');
			_el_hidden.type		= 'hidden';
			_el_hidden.value	= 0;
			_el_hidden.name		= this.el_name + this.options.hidden_postfix;
			_el_hidden.id		= _el_hidden.name;
			this.el_hidden		= this.el.parentNode.insertBefore( _el_hidden, this.el );
			_el_hidden			= null;
			
			if( !this.el_hidden )
				throw( 'Could not insert the hidden element' );   

			this.el.setAttribute( 'autocomplete', 'off');
			
			// create the ul container
			if( this.options.onLoad ) {
				Event.observe( window, 'load', this.loadElList.bind(this) );
			} else {
				this.loadElList();
			}
			
			Event.observe( this.el, 'blur', this.onBlur.bindAsEventListener(this) );
			Event.observe( this.el, 'click', this.onElClick.bindAsEventListener(this) );
			Event.observe( this.el, 'keydown', this.keyPress.bindAsEventListener(this) ); // handle the search requests
			
		},
		
		loadElList: function() {
			var _el_list		= document.createElement('ul');
			_el_list.className	= 'autocomplete_list';
			var _list_width		= ( Element.getStyle( _el_list, 'width' ) ) ? parseInt( Element.getStyle( _el_list, 'width' ) ) : this.el.offsetWidth;
			Element.setStyle( _el_list, { position: 'absolute', display: 'none', width: _list_width+'px' });
			this.el_list		= document.body.appendChild( _el_list );
			if( !this.el_list ){
				throw( 'Could not create the ul list' );
			}
			
			this.loadInitValue();
			
		},
		
		onElClick: function( ) {
			
			if( this.list_items.length > 0 )
				this.listShow();
		},
		
		keyPress: function( e ) {
			
			switch( e.keyCode ) {
			
				case Event.KEY_DOWN:
				case Event.KEY_UP:
				
					if( this.list_state == 2 ) {
						
						var new_item	= null;
						var new_item	= ( e.keyCode == Event.KEY_DOWN && !this.el_list_hilited ) ? this.el_list.firstChild : new_item; // down and no selected
						var new_item	= ( e.keyCode == Event.KEY_DOWN && this.el_list_hilited ) ? this.el_list_hilited.nextSibling : new_item; // down and next
						var new_item	= ( e.keyCode == Event.KEY_DOWN && !new_item ) ? this.el_list.firstChild : new_item; // down and goto first
								
						var new_item	= ( e.keyCode == Event.KEY_UP && !this.el_list_hilited ) ? this.el_list.lastChild : new_item; // up and no selected
						var new_item	= ( e.keyCode == Event.KEY_UP && this.el_list_hilited ) ? this.el_list_hilited.previousSibling : new_item; // up and next
						var new_item	= ( e.keyCode == Event.KEY_UP && !new_item ) ? this.el_list.lastChild : new_item; // up and first
						
						this.listHilite( new_item );
						this.listSelect( new_item );
					}
					
					if( this.list_state == 1 && e.keyCode == Event.KEY_DOWN ) {
						this.listShow();
					}
					
					break;

				case Event.KEY_TAB:
					this.fireOnSelect();
					this.onBlur();
					break;
					
				case Event.KEY_LEFT:
				case Event.KEY_RIGHT:
				break;
				
				case Event.KEY_RETURN: // return
					Event.stop( e );
					this.fireOnSelect();
					this.onBlur();
					this.listHide();
				break;
				
			default:
				clearTimeout( this.tim_delay );
				this.tim_delay	= setTimeout( this.searchRequest.bind(this), this.options.frequency );
				break;
			}
		},
		
		searchRequest: function() {
						
			var _el_value	= $F( this.el ).strip();
				
			if( (_el_value != '' && _el_value == this.search_value) || _el_value == this.selected_value ) { return; }

			if( (_el_value.length ) < this.options.minChars ) return;
		
			this.search_value	= $F( this.el ).strip();
			//this.selected_value	= '';
			
			new Ajax.Request( this.url, {
				method		: 'POST',
				postBody	: this.options.parmSearch + '=' + this.search_value,
				onSuccess: this.searchResponse.bind(this),
				onFailure: function() {
					throw( 'Ajax.Request error');
				}
			});
		},
		
		searchResponse: function( t, items, keep_closed ) {
		
			this.listReset();
			
			while( this.el_list.firstChild)
				this.el_list.removeChild( this.el_list.firstChild );
			
			this.list_items		= [];
			
			if( items.length == 0 && !this.options.enableCustom ) {
				this.list_state	= 0;
			} else {
			
				this.list_state	= 1;
				
				for( var i=0; i < items.length; i++ ) {
					var el_item			= document.createElement('li');
					el_item.id			= items[i].id;
					el_item.setAttribute( 'index', i );
					var regex_search	= this.search_value.replace( /[- ]/gi, '|' );
					
					el_item.innerHTML	= items[i].name.replace( new RegExp( regex_search, 'ig' ), function(match) { return '<span class="mark">' + match + '</span>' }, 'i');
					this.el_list.appendChild( el_item );
	
					Event.observe( el_item, 'mouseover', this.listHiliteMouse.bindAsEventListener(this) );
					Event.observe( el_item, 'mousedown', this.listSelectMouse.bindAsEventListener(this) );
					this.list_items[ i ]	= items[i];
				
					if( i > this.options.limit )
						break;
				}
				
				if( this.options.enableCustom ) {
					var el_item			= document.createElement('li');
					el_item.id			= -1
					el_item.innerHTML	= this.options.custom_text + this.search_value;
					Event.observe( el_item, 'mouseover', this.listHiliteMouse.bindAsEventListener(this) );
					Event.observe( el_item, 'mousedown', this.listSelectMouse.bindAsEventListener(this) );					
					this.el_list.appendChild( el_item );			
					
				}
			}
			this.listShow( keep_closed )
		},
		
		listShow: function( keep_closed ) {
			var position	= Position.cumulativeOffset( this.el );
			var x			= position[0];
			var y			= position[1] + this.el.offsetHeight;		
			var display		= this.list_state ? 'block' : 'none';
			display			= keep_closed ? 'none' : display; 
			Element.setStyle( this.el_list, { display: display, left: x+'px', top: y+'px' } );
			this.list_state	= 2;
		},
		
		listHide: function() {
			this.list_state	= 1;
			//this.listReset();
			Element.hide( this.el_list );
		},
		
		listReset: function() {
			clearTimeout( this.tim_delay );
			this.list_state			= 0;
			this.el_list_hilited	= null;		
			this.selected_index		= null;
			this.list_items			= [];
		},
		
		listSelect: function( el_li ) {
			
			if( el_li ) {

				if( el_li.nodeName == 'SPAN' )
					el_li		= el_li.parentNode;

				var item_id				= el_li.getAttribute('id');
				var item_text			= el_li.innerHTML.stripTags();

				if( item_id == -1 ) {
					item_text		= item_text.substring( this.options.custom_text.length );
				} 
				
				this.el.value			= item_text.unescapeHTML();
				this.el_hidden.value	= item_id;
				this.selected_index		= el_li.getAttribute('index');
				this.selected_value		= this.el.value;
			} else {
				this.el_hidden.value	= 0; 
				this.el.value			= this.search_value;
				this.selected_value		= '';
			}
		},
		listSelectMouse: function( e )  {
		
			var element	= Event.element(e);
			this.listSelect( element );
			this.fireOnSelect();
			this.listHide();
			this.focusNextElement();
		},
				
		listHilite: function( el_li ) {

			//if( !el_li ) return;
			
			if( el_li && el_li.nodeName == 'SPAN' )
				return;
			
			if( this.el_list_hilited )
				Element.removeClassName( this.el_list_hilited, 'selected');
			
			this.el_list_hilited	= el_li;
			
			if( this.el_list_hilited ) {
				Element.addClassName( el_li, 'selected');
			}
		},
		listHiliteMouse: function( e ) {
			this.listHilite( Event.element(e) );
		},
		
		onBlur: function() {
		
			// if no item selected and only one item on the list, and the text match the input - select it
			if( parseInt( $F( this.el_hidden ) ) <= 0 && this.el_list.childNodes && this.el_list.childNodes.length > 0 ) {
				if( ( this.options.enableCustom && this.el_list.childNodes.length == 1 ) || (this.el_list.firstChild.innerHTML.stripTags().toLowerCase() == this.search_value.toLowerCase() ) ) {
					this.listSelect( this.el_list.firstChild );
				}
			}
			this.listHide();
		},
		
		focusNextElement: function() {
		},
		
		fireOnSelect: function() {
			
			if( typeof(this.selected_index) != 'undefined' ) {
				this.options.onSelect( this.list_items[ this.selected_index ] );
			} 
			return;
		},
		
		loadInitValue: function() {
			
			if( this.options.value ) {
				
				if( ! Object.isNumber( this.options.value ) ) {
					this.el.value		= this.options.value;
					this.search_value	= this.el.value; 
					
					if( this.options.enableCustom ) {
						this.searchResponse( null, [], keep_closed=true );
						this.listSelect( this.el_list.firstChild );
						this.fireOnSelect();
						this.listHide();
					}
				} else {
					new Ajax.Request( this.url, {
						method		: 'POST',
						postBody	: this.options.parmValue + '=' + parseInt( this.options.value ),
						onSuccess: function( t, json ) {
						
							if( json ) {
								
								if( ! json.length )
									json		= [ json ];
								
								this.searchResponse( t, json, keep_closed=true );
								this.listSelect( this.el_list.firstChild );
								this.fireOnSelect();
								this.listHide();
							}
						}.bind(this),
						onFailure: function() {
							throw( 'Ajax.Request error');
						}
					});
				}
			}
		}
	}
