/**
 * the following variables can be overwritten, if defined in the calling scope <<
 */ 

var prevLabel = '&laquo; zur&uuml;ck';

var nextLabel = 'mehr &raquo;';

var prevHint = '<<';

var nextHint = '>>';

var showTime = true;

/**
 * >>
 */

var allowSuggest = true;

var suggestions = new Hash();

var suggSplitter = /[,\./\-+&*\^%!@#$\(\)\s"']+/g;

var transport;

var Suggestion = Class.create( {
	
	source:null,
	
	sourceForm:null,
	
	container:'',

	suggestionsURL:null,

	lastSugg:'',
	
	offset:0,
	
	totalCount:0,
	
	blocks:'',

	field:'',
	
	sortByWeight:true,
	
	minLength:2,
	
	raw:false,
	
	selected:null,
	
	additionalParams:null,
	
	onSelect:function( element ){
		return this.cleanUpSuggestion( element.innerHTML );
	},
	
	handleSelect:function( e ){
		if( !this.raw ){
			e.stop();
			this.lastSugg = this.onSelect( e.findElement( 'A' ) );
			this.source.value = this.lastSugg;
		}
		this.source.focus();
		this.hideSugg();
	},	
	
	setSuggestion:function( e ){
		var elem = 'highlited' == e.element().className ? e.element().up( 0 ) : e.element();
		this.source.value = this.cleanUpSuggestion( elem.innerHTML );
	},	

	resetSuggestion:function( e ){
		this.source.value = this.lastSugg;
	},	
	
	cleanUpSuggestion:function( txt ){
		return txt.stripTags().unescapeHTML().strip();
	},

	suspendForm:function( e ){
		if( 0 < this.container.select( '.focused' ).size() ){
			e.stop();
			this.source.focus();
			if( this.raw ) document.location = this.container.down( '.focused' ).readAttribute( 'href' );
			this.hideSugg();
		}
	},	
	
	initialize:function( suggField ){
		suggField = $(suggField);
		if( 'off' == suggField.readAttribute( 'autocomplete' ) ) return; // already extended!
		this.source = suggField;
		this.sourceForm = suggField.up( 'form' );
		if( null != this.sourceForm ){
			this.sourceForm.observe( 'submit', this.suspendForm.bindAsEventListener( this ) );
		}
		this.container = 'suggestions_' + this.source.id;
		this.suggestionsURL = this.source.readAttribute( 'remoteUrl' );
		this.blocks = this.source.readAttribute( 'blocks' );
		this.raw = 'true' == this.source.readAttribute( 'raw' );
		this.additionalParams = suggField.readAttribute( 'additionalParams' );
		
		this.minLength = this.source.readAttribute( 'minLength' );
		this.field = this.source.readAttribute( 'field' );
		this.sortByWeight = 'weight' == this.source.readAttribute( 'sort' ); 
		var w = ( suggField.getWidth() - 2 ) + 'px';
		var div = new Element( 'div', { id:this.container, style:'display:none;width:' + w } ).addClassName( 'suggestions' );
		suggField.insert( { 'before':div } );
		this.container = $( this.container );
		suggField.writeAttribute( 'autocomplete', 'off' );
		this.source.observe( 'keyup', this.updateSuggestions.bindAsEventListener( this ) );
	},
	
	showFailure:function( e ){
		$( this.container ).update( '<div class="suggestion_statusline">Wird aktualisiert...</div>' );
		Effect.Appear( this.container, { duration:0.1 } );
		this.source.focus();
	},
	
	json2html:function( e ){
	  $( this.container ).update();
	  var suggCommand = eval( "("+e.responseText+")" ); 
	  if( 0 == suggCommand.totalCount ){
		  $( this.container ).hide();
		  return;
	  }
	  Effect.Appear( this.container, { duration:0.1 } );

	  this.totalCount = suggCommand.totalCount;
	  var suggBox = new Element( 'div' );
	  var thisObj = this;
	  var pattern = new RegExp( '([\\s\\-"!\\.,])(' + this.lastSugg.strip().replace( '"', '' ).toLowerCase().replace( suggSplitter, '|' ) + ')', 'gi' );
	  suggCommand.suggestions.each( function( sugg, ix ){
		var lineA = new Element( 'a', { 'href':'/' } ).addClassName( 'suggestion' ).addClassName( 0 == ix % 2 ? 'bg1' : 'bg2' );
		if( thisObj.raw ){
			if( 'string' == typeof sugg ) suggBox.insert( lineA.update( sugg ) );
			else{
				if( null != sugg.linkToHit ){ 
					lineA.writeAttribute( 'href', sugg.linkToHit );
					lineA.observe( 'click', function( e ){
						e.stop();
						document.location = e.element().hasClassName( 'suggestion' ) ? sugg.linkToHit : e.element().readAttribute( 'href' );
					} );
				}
				suggBox.insert( lineA.update( sugg.text ) );
				if( sugg.controls ) lineA.insert( { after:sugg.controls } ); 
			}
		}else{
			var txt = ( ' ' + ( 'string' == typeof( sugg ) ? sugg : sugg.text ).replace( /"/g, ' ' ) ).replace( pattern, '$1<span class="highlited">$2</span>' ).strip();
		    lineA.update( '<i>"</i>' + txt + '<i>"</i>' );
		    if( null != sugg.linkToHit ) lineA.writeAttribute( 'href', sugg.linkToHit );
		    lineA.observe( 'click', thisObj.handleSelect.bindAsEventListener( thisObj ) );
		    lineA.observe( 'focus', thisObj.onFocus.bindAsEventListener( thisObj ) );
	//	    lineA.observe( 'mouseover', thisObj.setSuggestion.bindAsEventListener( thisObj ) );
	//	    lineA.observe( 'mouseout', thisObj.resetSuggestion.bindAsEventListener( thisObj ) );
		    suggBox.insert( lineA );
		}
	  });
	  var statusLine = new Element( 'div' ).addClassName( 'suggestion_statusline' );
	  var spanInfo = new Element( 'span' ).update( 'Treffer: <b>' + this.totalCount + '</b>' ).addClassName( 'info' );
	  statusLine.insert( spanInfo );

	  var nav = new Element( 'span' ).addClassName( 'nav_buttons' );
	  var back = new Element( 'span' );
	  if( 0 < this.offset ){
	    back = new Element( 'a', { title:prevHint, href:'/' } ).addClassName( 'back_link' );
		back.observe( 'click', function(e){ 
			e.stop();
			thisObj.offset = Math.max( thisObj.offset - 10, 0 );
			thisObj.loadSuggestions();
		});
	  }
	  nav.insert( back.update( ' ' + prevLabel + ' ' ) );
		  
	  var more = new Element( 'span' );
	  if( this.totalCount > 10 + this.offset ){
	    more = new Element( 'a', { title:nextHint, href:'/' } );
	    more.observe( 'click', function( e ){
	    	e.stop();
	    	thisObj.offset += 10;
	    	thisObj.loadSuggestions();
	    } );
	  }
	  nav.insert( more.update( ' ' + nextLabel + ' ' ) );
	  statusLine.insert( nav );

	  if( showTime ){
		  statusLine.insert( 
				  new Element( 'span', { style:'position:relative;left:20px' } 
	      ).update( 't: ' + suggCommand.totalTime + ' ms' ) );
	  }
	  
	  $( this.container ).insert( suggBox );
	  $( this.container ).insert( statusLine );
	  this.source.focus();
	},
	
	hideSugg:function(){
		this.container.update();
		this.container.hide();
	},
	
	onFocus:function(){
		this.active = true;
	},
	
	hilight:function( down ){
		this.selected = this.container.down( '.focused' );
		if( null == this.selected ){
			this.selected = this.container.down( '.suggestion' );
			this.source.value = this.cleanUpSuggestion( this.selected.innerHTML );
			this.selected.addClassName( 'focused' );
			return;
		}
		this.container.select( '.suggestion' ).invoke( 'removeClassName', 'focused' );
		this.selected = down ? this.selected.next( '.suggestion' ) : this.selected.previous( '.suggestion' );
		if( null != this.selected ){ 
			this.source.value = this.cleanUpSuggestion( this.selected.innerHTML );
			this.selected.addClassName( 'focused' );
		}else if( !down ) this.source.value = this.lastSugg;
	},
	
	updateSuggestions:function( e ){
	  if( ( 37 == e.keyCode || 39 == e.keyCode ) && !e.ctrlKey || 35 == e.keyCode || 36 == e.keyCode 
			  || ( 15 < e.keyCode && 19 > e.keyCode ) ) return;
	  if( Event.KEY_ESC == e.keyCode ){
		  this.source.value = this.lastSugg;
		  this.hideSugg();
		  return;
	  }			

	  //Not caught by IE
	  if( Event.KEY_RETURN == e.keyCode ){
		  if( this.raw && null != this.selected ) document.location = this.selected.readAttribute( 'href' );
		  this.hideSugg();
		  return; 
	  }
	  if( null != this.container.down( '.suggestion' ) ){
		  switch ( e.keyCode ){
			  case Event.KEY_DOWN: this.hilight( true ); return;
			  case Event.KEY_UP: this.hilight( false ); return;
		  }
		  
		  if( e.ctrlKey ){
			  if( Event.KEY_RIGHT == e.keyCode && this.totalCount > 10 + this.offset ){
				  this.offset += 10;
				  this.loadSuggestions();
			  }else if( Event.KEY_LEFT == e.keyCode && 0 < this.offset ){
				  this.offset = Math.max( this.offset - 10, 0 );
				  this.loadSuggestions();
			  }
			  return;
		  }
	  }
	  
	  var term = this.source.value;
	  if( this.minLength > term.length || this.lastSugg == term ) return;
	  var splitted = term.split( ' ' );
	  for( var t = 0; t < splitted.length; t++ ){
	    if( 0 != splitted[ t ].length && this.minLength > splitted[ t ].length ) return;
	  }
	  this.lastSugg = term;
	  this.offset = 0;
	  setTimeout( this.loadSuggestions.bind( this ), 400 );
	},
	
	loadSuggestions:function(){
		if( !allowSuggest ) return;
		var params = $H( { 'value':this.lastSugg, 'offset':this.offset, 'blocks':this.blocks, 'field':this.field } );
		if( null != this.additionalParams ) params = params.merge( eval( '(' + this.additionalParams + ')' ) );
		
		if( this.sortByWeight ) params.set( 'sortByWeight', true );
		if( null != transport && 4 != transport.readyState ) transport.abort();
		new Ajax.Request( this.suggestionsURL, {
			onLoading:function(req){ transport = req.transport; },
			onSuccess:this.json2html.bindAsEventListener( this ),
			onFailure:this.showFailure.bindAsEventListener( this ),
			on500:this.showFailure.bindAsEventListener( this ),
			parameters:params.toQueryString() 
		});
	}
} );

function extendSuggestions(){
	if( 'undefined' != typeof suggestEnabled && !suggestEnabled ) return;
	$$( 'input[rel=suggestable]' ).each( function( suggField ){
		suggestions.set( suggField.id, new Suggestion( suggField ) ); 
	});
	document.observe( 'click', function( e ){ 
		if( !e.findElement( '.suggestion' ) ) $$( '.suggestions' ).invoke( 'hide' );
	} );
}

document.observe( 'dom:loaded', extendSuggestions );
