function FFSuggest() {

	var pDebug					= false;
	var pInstanceName			= '';
	var pSearchURL				= '';
	var pQueryParamName			= '';
	var pFormname 				= '';
	var pLayerName				= '';
	var pQueryInput;
	var pMaxQueryCount			= 6;
	
	var pSuggestIds				= new Array();
	var pCurrentPos = 0;
	
	var pLastQuery;
	var pSearchField;
	var pShowImages				= false;

	var pSuggestImageClass 		= 'suggestImage';
	var pSuggestQueryClass 		= 'suggestTextQuery';
	var pSuggestTypeClass 		= 'suggestTextType';
	var pSuggestAmountClass     = 'suggestTextAmount';
	var pSuggestQueryTypedClass = 'suggestTextQueryTyped';
	var pSuggestFooterClass     = 'suggestFooter';
	var pSuggestHeaderClass     = 'suggestHeader';
	var pSuggestRowClass	    = 'suggestRow';
	var pSuggestHighlightClass  = 'suggestHighlight';
	
	var pTranslation;
	
	this.init = function(searchURL, formname, queryParamName, divLayername, instanceName, debugMode, channelParamName, channel, showImages) {
		pSearchURL			= searchURL;
		pFormname			= formname;
		pQueryParamName		= queryParamName;
		pChannelParamName	= channelParamName;
		pChannel			= channel;
		pLayerName			= divLayername;
		pInstanceName		= instanceName;
 		pDebug				= debugMode;
 		pShowImages			= showImages;
		if (pSearchURL == '') {		
			if (pDebug) alert('no searchurl defined');
			return null;
		} else if (pInstanceName == '') {
			if (pDebug) alert('no instancename defined');
			return null;
		} else if (pFormname == '') {
			if (pDebug) alert('no formname defined');
			return null;
		} else if (pQueryParamName == '') {
			if (pDebug) alert('no queryparamname defined');
			return null;
		} else if (pLayerName == '') {
			if (pDebug) alert('need a layer for output');
		}
		pQueryInput = document[pFormname][pQueryParamName];
		$(pQueryInput)	.keyup(function(ev){handleKeyPress(ev);})
						.keydown(function(){removeDefaultText();})
						.click(function(){removeDefaultText();})
						.blur(function(){setDefaultTextIfEmpty(); eval(pInstanceName+".hideLayerOutsideCall();");})
						.focus(function(){showLayer();});
		$(document[pFormname]).submit(function(){removeDefaultText(); return handleSubmit();});
		
		pTranslation = new Array();
		pTranslation['Suchbegriffe']	= 'Angebot/Produkte';
		pTranslation['Hersteller']		= 'Hersteller';
		pTranslation['FirmaOrt']		= 'Firmenname des Lieferanten';
		pTranslation['Lieferanten']		= 'Lieferanten';
		pTranslation['Lieferant']		= 'Lieferant &nbsp;&nbsp;&nbsp;';
		
		setDefaultTextIfEmpty();
	}
	
	function removeDefaultText() {
		stopTicker();
		var text = $('#search').val();
		if (text.indexOf(pCurrentTickerText) > -1) {
			$('#search').val(text.replace(pCurrentTickerText,''));
		}
	}
	
	function setDefaultTextIfEmpty() {
		if ($('#search').val() == '') {
			startTicker();
		}
	}
	
	/***** ticker methods *****/
	var pTickerTexts = ["Produkt    ", "Marke    ", "Hersteller    ", "Dienstleistung    "];
	var pTickerPosition = 0;
	var pTickerTextPosition = 0;
	var pTickerTimeout = 150;
	var pTickerStopped = true;
	var pCurrentTickerText = "";
	
	function startTicker() {
		pTickerPosition = 0;
		pTickerTextPosition = 0;
		pTickerStopped = false;
		eval(pInstanceName+".tick()");
	}
	
	this.tick = function (){
		if (pTickerStopped == true) return;
		
		//go to next character
		pTickerTextPosition++;
		
		// jump to the beginning of the next word
		if (pTickerTextPosition > pTickerTexts[pTickerPosition].length) {
			pTickerPosition++;
			if (pTickerPosition >= pTickerTexts.length) pTickerPosition = 0;
			pTickerTextPosition = 0;
		}
	
		pCurrentTickerText = pTickerTexts[pTickerPosition].substring(0, pTickerTextPosition);
		$('#search').val(pCurrentTickerText);
		setTimeout(pInstanceName+".tick()", pTickerTimeout);
	}
	
	function stopTicker() {
		pTickerStopped = true;
	}
	/***** end of ticker methods *****/
	
	function isQueryOK() {
		if($('#search').val().length < 2) {
			$('#search').addClass('searchError');
			return false;
		} else {
			return true;
		}
	}
	
	function handleSubmit() {
		addInputToForm('userInput', pLastQuery);
		addInputToForm('queryFromSuggest', 'true');
		
		var action = document[pFormname].action;
		var query = document[pFormname][pQueryParamName].value;
		
		//replace special characters
		query = query.replace("&", "-und-");
		
		query = encodeURI(query);
		
		if (pSearchField == 'Hersteller') {
			action = 'hersteller-'+query+'.html';
		} else if (pSearchField == 'FirmaOrt') {
			var id = $('tr.'+pSuggestHighlightClass).attr("lieferantenId")
			action = 'lieferant-'+id+'-'+query.replace(/%20-%20[^-]+$/g, "")+'.html';
		} else { //if (pSearchField == 'Suchbegriffe' || pSearchField == undefined) {
			action = 'produkt-'+query+'.html';
		} 
		
		if ( action != document[pFormname].action ) {
			action = action.replace(/(\/|%20|-)+/g, '-') + "?log=true";
			document[pFormname].action = action;
		}
		if (isQueryOK()) {
			window.location.href = action;
		}
		return false;
	}
	
	this.handleClick = function(id) {
		takeQuery(id);
		$(document[pFormname]).submit();
	}

	this.handleMouseOver = function(id) {
		for(var i = 0; i < pSuggestIds.length; i++) {
			if (pSuggestIds[i] == id) {
				pCurrentPos = i;
				break;
			}
		}
		selectRow(id);
	}
	
	function selectRow(id) {
		unmarkAll();
		var row = $('#'+pLayerName+' tr[id="'+id+'"]');
		row.removeClass(pSuggestRowClass).addClass(pSuggestHighlightClass);
	}
	
	function takeQuery(id) {
		var row = $('#'+pLayerName+' tr[id="'+id+'"]');
		document[pFormname][pQueryParamName].value = row.attr('query');
		pSearchField = row.attr("searchField");
	}
	
	this.handleMouseOut = function(id) {
		$('#'+pLayerName+' tr[id="'+id+'"]').removeClass(pSuggestHighlightClass).addClass(pSuggestRowClass);
	}
	
	function handleKeyPress(evt) {
		evt = (evt) ? evt : ((event) ? event : null);
		var keyCode = evt.keyCode;
		if (keyCode == 38) {
			moveUp();
		} else if (keyCode == 27) {
			hideLayer();
		} else if (keyCode == 40) {
			moveDown();
		} else {
			if (pQueryInput.value == '') {
				hideLayer();
				pLastQuery = '';
				return null;
			}
			if (pLastQuery != pQueryInput.value){ getSuggestions(); }
			pLastQuery = pQueryInput.value;
			pSearchField = "";
		}
	}
	
	
	function moveUp() {
		if (pSuggestIds.length == 0) return;
		
		if($('tr.'+pSuggestHighlightClass).length == 0) {
			pCurrentPos	= pSuggestIds.length-1;
		} else {
			pCurrentPos--;
			if (pCurrentPos < 0) {
				// at the top go to end of list
				pCurrentPos	= pSuggestIds.length-1;
			}
		}
		takeQuery(pSuggestIds[pCurrentPos]);
		selectRow(pSuggestIds[pCurrentPos]);
	}

	function moveDown() {
		if (pSuggestIds.length == 0) return;
		
		if($('tr.'+pSuggestHighlightClass).length == 0) {
			pCurrentPos = 0;
		} else {
			pCurrentPos++;
			if (pSuggestIds[pCurrentPos] == undefined) {
				// at the end go to start of list
				pCurrentPos	= 0;
			}
		}
		takeQuery(pSuggestIds[pCurrentPos]);
		selectRow(pSuggestIds[pCurrentPos]);
	}
	
	function getSuggestions() {
		var query = $('input[name='+pQueryParamName+']').attr('value');
		//check if the same query was asked before
		if(pLastQuery == query){
			return;
		}
		else {
			pLastQuery = query;
		}
			var requestURL = pSearchURL +'?'+ pQueryParamName +'='+ encodeURIComponent(query) +'&'+ pChannelParamName +'='+ pChannel+'&format=json';
		$.ajax({
			type: "GET",
			url: requestURL,
			contentType: "application/x-www-form-urlencoded; charset=UTF-8",
			success:
				function (json, textStatus) {
					var jsonObj = eval(json);
					if (json != "" && jsonObj.length > 0) {
						// group suggestions into their type/target-field-name
						var groupedSuggestions = new Array();
						for (var i=0; i<jsonObj.length; i++) {
							if (groupedSuggestions[jsonObj[i].searchField] == undefined) {
								groupedSuggestions[jsonObj[i].searchField] = new Array();
							}
							var groupLength = groupedSuggestions[jsonObj[i].searchField].length;
							if (groupLength < pMaxQueryCount) {
								groupedSuggestions[jsonObj[i].searchField][groupLength] = jsonObj[i];
							}
						}
						
						// build html
						var addRowSpace = false;
						var searchFields = ['Suchbegriffe', 'Hersteller', 'FirmaOrt'];
						var output = '<table class="suggest" cellspacing=0 cellpadding=0><tbody>';
						
						pSuggestIds = new Array();
						
						for (var i=0; i < searchFields.length; i++) {
							if (groupedSuggestions[searchFields[i]] != undefined) {
								if (addRowSpace == true) {
									output += '<tr height=5><td colspan=2>&nbsp;</td></tr>';
								}
								
								
								var searchField = searchFields[i];
								var queries = groupedSuggestions[searchField];
								
								output += '<tr><td colspan=2 class=titel><b>' + pTranslation[searchField] + '</b></td></tr>';
								
								// queries itself
								for(var k=0; k < queries.length; k++) {
									var id = (i+1)+""+k;
									output += '<tr id="'+id+'" class="'+pSuggestRowClass+' suggest_zeile" '
											+ 'onMouseDown="' + pInstanceName + '.handleClick(' + id + ');"'
											+ 'onMouseMove="' + pInstanceName + '.handleMouseOver(' + id + ');" '
											+ 'onMouseOut="' + pInstanceName + '.handleMouseOut(' + id + ');" '
											+ 'query="' + queries[k].query + '" '
											+ 'searchField="' + searchField + '" '
											+ 'lieferantenId="' + queries[k].id + '">';
									if (queries[k].hitCount != 0 && searchField != 'FirmaOrt') {
										output += '<td>'
												+ queries[k].query
												+ '</td>'
												+ '<td align="right" class="suggest_zelle1">'
												+ queries[k].hitCount + ' ';
										if (queries[k].hitCount > 1) {
											output += pTranslation['Lieferanten'];
										} else {
											output += pTranslation['Lieferant'];
										}
										output += '</td>';
									} else {
										output += '<td colspan="2" class="suggest_zelle1">'
												+ queries[k].query
												+ '</td>';
									}
									output += '</tr>';
									
									pSuggestIds[pSuggestIds.length] = id;
								}
								
								addRowSpace = true;
							}
						}
						
						output += '</tbody></table>';
						
						//show layer
						$('div#'+pLayerName).html(output).show();
					}
					else {
						//hide layer
						$('div#'+pLayerName).hide();
					}				
				},
			error:
				function (e, xhr, settings, exception) {
					if (pDebug) {
						alert('Error:\nHTTP result code: ' + e.status+'\nrequested URL: '+requestURL);
					}
				}
		});
	}
	
	function hideLayer() {
		unmarkAll();
		$('div#'+pLayerName).hide();
		fireSuggestLayerHidden();
	}
	
	this.hideLayerOutsideCall = function() {
		hideLayer();
	}

	function showLayer() {
		$('div#'+pLayerName).show();
	}

	// calls the callback for "outside" listeners if the callback is implemented
	function fireSuggestCompleted(suggestLayerIsVisible) {
		if (typeof(onSuggestCompleted) == 'function') {
			onSuggestCompleted(suggestLayerIsVisible);
		}
	}

	// calls the callback for "outside" listeners if the callback is implemented
	function fireSuggestLayerHidden() {
		if (typeof(onSuggestLayerHidden) == 'function') {
			onSuggestLayerHidden();
		}
	}

	function unmarkAll() {
		$('tr.'+pSuggestHighlightClass).each(function(i) {
			$(this).removeClass(pSuggestHighlightClass).addClass(pSuggestRowClass);			
		});
	}
	
	function addInputToForm(name, value) {
		if (document[pFormname][name] == undefined) {
			var element = document.createElement('input');
			element.name = name;
			element.type = 'hidden';
			element.value = value;
			document[pFormname].appendChild(element);
		} else {
			document[pFormname][name].value = value;
		}
	}
}
