function FFSuggest() {

	var pRequest;
	var pLayer;
	var pDebug					= false;
	var pInstanceName			= '';
	var pSearchURL				= '';
	var pQueryParamName			= '';
	var pFormname 				= '';
	var pLayerName				= '';
	var pQueryInput;
	var pSuggest				= new Array();
	var pLastQuery;
	var pCurrentSelection		= 0;
	var submitted				= false;
    var elementId				= '';

	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 testzentrale 			= '99999999'; //realy high number fo testzentrale cell for custom handling

	this.init = function(searchURL, formname, queryParamName, divLayername, instanceName, debugMode, channelParamName, channel) {
		pSearchURL			= searchURL;
		pFormname			= formname;
		pQueryParamName		= queryParamName;
		pChannelParamName	= channelParamName;
		pChannel			= channel;
		pLayerName			= divLayername;
		pInstanceName		= instanceName;
 		pDebug				= debugMode;

		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.onkeyup	= handleKeyPress;
		pQueryInput.onfocus	= showLayer;
		pQueryInput.onblur	= hideLayer;
		document[pFormname].onsubmit = handleSubmit;
	}

	function handleSubmit() {
		submitted = true;
		if (pSuggest[pCurrentSelection] != undefined) {
			document[pFormname][pQueryParamName].value = pSuggest[pCurrentSelection].split('###')[0];
			if(document[pFormname]['queryFromSuggest'] != null){
				document[pFormname]['queryFromSuggest'].value = true;
			}
		}
	}

	this.handleClick = function() {
	    
		 if(elementId == '99999999'){
				window.open('http://www.testzentrale.de/','_blank');
		 }else if (pSuggest[pCurrentSelection] != undefined) {
			document[pFormname][pQueryParamName].value = pSuggest[pCurrentSelection].split('###')[0];
			if(document[pFormname]['queryFromSuggest'] != null){
				document[pFormname]['queryFromSuggest'].value = true;
			}
			//check the channel and the element id, change the form action only for testzentrale elements
			if(pChannel == 'de'){
				pSuggestDeClean = cleanArray(pSuggestDe);
				alert('länge: '+pSuggestDe);
				alert('ElementID: '+elementId);
				//if(elementId >= pSuggestDeClean.length || pSuggestDe.without('').length == 0){
				if(elementId > pSuggestDeClean.length-1){
					//change get action for the testzentrale suggest links and pop up a msg
					document[pFormname].action  = 'http://www.testzentrale.de/programm/factfinder/';
					document.getElementById('search_msg').style.display='block';
					document.getElementById('overlay').style.display='block';
				}else{
				    alert(1);
					document[pFormname].action  = 'http://www.hogrefe.de/programm/factfinder/';
					
				}
			}
		}
	}

	this.handleMouseOver = function(pos) {
		//set elementId with the pos value
		elementId = pos;
		if(typeof(pos)=='number'){
			var tblCell = getTableCell(pos);
			unmarkAll();
			if (tblCell != null) {
				highlightSuggest(tblCell);
				pCurrentSelection = pos;
			}
		}
	}

	this.handleMouseOut = function(pos) {
		if(typeof(pos)=='number'){
			var tblCell = getTableCell(pos);
			if (tblCell != null) {
				unmarkSuggest(tblCell);
				pCurrentSelection = -1
			}
		}
	}

	function handleKeyPress(evt) {
		evt = (evt) ? evt : ((event) ? event : null);
		var keyCode = evt.keyCode;
		if (keyCode == 38) {
			moveSelection('up')
		} else if (keyCode == 27) {
			hideLayer();
		} else if (keyCode == 40) {
			moveSelection('down');
		} else {
			if (pQueryInput.value == '') {
				hideLayer();
				if (pLayer != null){ pLayer.innerHTML = ''; }
				return null;
			}
			//only for (de) channel call the custom ajax request
			if (pLastQuery != pQueryInput.value){
				if(pChannel == 'de'){
					startCustomAjax();
				}else{
					startAjax();
				}
			}
			pLastQuery = pQueryInput.value;
		}
	}

	function moveSelection(direction) {
		var pos = pCurrentSelection;
		if (direction == 'up'){	pos--; }
		else{ 					pos += 1; }

		if (pos < 0) {
			unmarkAll();
			pQueryInput.focus();
			pCurrentSelection	= -1;
		} else {
			var tblCell = getTableCell(pos);
			if (tblCell != null) {
				unmarkAll();
				highlightSuggest(tblCell);
				pCurrentSelection = pos;
			}
		}

		var query = pQueryInput.value;
		pQueryInput.value = '';
		pQueryInput.focus();
		pQueryInput.value = query;
	}

	function startAjax() {
		var query = pQueryInput.value;
		var requestURL = pSearchURL +'?'+ pQueryParamName +'='+ escape(query) +'&'+ pChannelParamName +'='+ pChannel;


		try {
			if( window.XMLHttpRequest ) {
				pRequest = new XMLHttpRequest();
			} else if( window.ActiveXObject ) {
				pRequest = new ActiveXObject( "Microsoft.XMLHTTP" );
			} else {
				if (pDebug) alert( 'no ajax connection' );
			}
			
			pLayer = document.getElementById(pLayerName);
			if (pLayer != null) {
				if (query != '') {

					pRequest.open( "GET", requestURL, true );
					pRequest.onreadystatechange = callbackAjax;
					pRequest.send( null );
				} else {
					hideLayer();
				}
			} else {
				if (pDebug) alert( 'no layer for output found' );
			}
		} catch( ex ) {
			hideLayer();
			if (ex == undefined) {
				if (pDebug) alert( 'Error: ' + ex.getmessage );
			} else {
				if (pDebug) alert( 'Error: ' + ex );
			}
		}
	}

	function hideLayer() {
		if (pLayer != null) {
			pLayer.style.display = 'none';
			fireSuggestLayerHidden();
		}
	}

	this.hideLayerOutsideCall = function() {
		if (pLayer != null) {
			pLayer.style.display = 'none';
			fireSuggestLayerHidden();
		}
	}

	function showLayer() {
		if (pLayer != null && pSuggest != null && pSuggest.length >= 1) {
			pLayer.style.display = 'block';
		}
	}

	function callbackAjax() {
		if (submitted == false) {
			if (pRequest.readyState == 4) {
				if (pRequest.status != 200) {
					//hideLayer();

					alert(pRequest.responseText);
					if (pDebug) alert( 'Error (' + pRequest.status + '): ' + pRequest.statusText );
				} else {
					if(pChannel == 'de'){
						handleResponseTestzentraleDe(pRequest.responseText);
					}else{
						handleResponse(pRequest.responseText);
					}
				}
			}
		}
  }

	// 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 handleResponse(text) {
		pCurrentSelection = -1;
		pSuggest = new Array();
		pSuggest = text.split('\n');
		var outputText = '<table cellpadding="0" cellspacing="0" class="' + pLayerName + '" width="100%" border="0" onMouseDown="' + pInstanceName + '.handleClick();">';
		outputText += '<tr class="'+pSuggestHeaderClass+'" ><td nowrap="nowrap" colspan="3"><ff:jsescape><bean:message bundle="Suggest" key="header" /></ff:jsescape></td></tr>';

		var pNewSuggest = new Array();
		//for (var i in pSuggest) {
		for (var i = 0; i < pSuggest.length; i++) {
			var firstChar = pSuggest[i].charCodeAt(0);

			if (firstChar != 13 && firstChar != 10 && pSuggest[i].length >= 1) {
				pNewSuggest.push(pSuggest[i]);
			}
		}
		pSuggest = pNewSuggest;
		var query = pQueryInput.value;

		for (var i = 0; i < pSuggest.length; i++) {
			pSuggestParts = new Array();
			pSuggestParts = pSuggest[i].split("###");

			outputText += '<tr id="' + pLayerName + '_' + i + '" class="'+pSuggestRowClass+'" onMouseOver="' + pInstanceName + '.handleMouseOver(' + i + ');" onMouseOut="' + pInstanceName + '.handleMouseOut(' + i + ');">'
								+'<td nowrap="nowrap" class="'+ pSuggestQueryClass +'">' + pSuggestParts[0].replace(new RegExp("("+query+")","ig"),'<span class="'+pSuggestQueryTypedClass+'">$1</span>') + '</td>'
								+'<td nowrap="nowrap" class="'+ pSuggestTypeClass +'">' + pSuggestParts[2] + '</td>'
								+'<td nowrap="nowrap" class="'+ pSuggestAmountClass +'">' + pSuggestParts[1] + '</td>'
						+'</tr>';
		}
		outputText += '<tr><td class="'+pSuggestFooterClass+'" colspan="3">&nbsp;</td></tr></table>';
		if (pSuggest.length >= 1) {
			showLayer();
			pLayer.innerHTML = outputText;

			// calback for "outside" listeners
			fireSuggestCompleted(true);
		} else {
			hideLayer();
			pLayer.innerHTML = '';

			// calback for "outside" listeners
			fireSuggestCompleted(false);
		}

	}

	function highlightSuggest(tblCell) {
		tblCell.className = pSuggestHighlightClass;
	}

	function unmarkSuggest(tblCell) {
		tblCell.className = pSuggestRowClass;
	}

	function unmarkAll() {
		var tblCell;

		//for (var i in pSuggest) {
		for (var i = 0; i < pSuggest.length; i++) {
			tblCell = getTableCell(i);
			if (tblCell != null) {
				unmarkSuggest(tblCell);
			}
		}
	}

	function getTableCell(pos) {
		var tblCell;
		tblCell = document.getElementById(pLayerName + '_' + pos);
		return tblCell;
	}

	//function used only in de for diplsaying also testzentrale suggest results
	//ajax request for testzentrale suggest data
	function startCustomAjax(){

		var query = pQueryInput.value;
		var ajaxRequest;
		var requestURL = pSearchURL +'?'+ pQueryParamName +'='+ escape(query) +'&'+ pChannelParamName +'='+ 'testzentrale';

		try {
			if( window.XMLHttpRequest ) {
				ajaxRequest = new XMLHttpRequest();
			} else if( window.ActiveXObject ) {
				ajaxRequest = new ActiveXObject( "Microsoft.XMLHTTP" );
			} else {
				if (pDebug) alert( 'no ajax connection' );
			}
				pLayer = document.getElementById(pLayerName);
				
				if (pLayer != null) {
				    
					if (query != '') {

						ajaxRequest.onreadystatechange = function(){
						if(ajaxRequest.readyState == 4){
								testezentraleText = ajaxRequest.responseText;
								//after the testzentrale.de request call a request for hogrefe.de
								startAjax();
							}
						}
					ajaxRequest.open("GET", requestURL, true);
					ajaxRequest.send(null);
					} else {
						hideLayer();
					}
				} else {
					if (pDebug) alert( 'no layer for output found' );
				}
		} catch( ex ) {
			hideLayer();
			if (ex == undefined) {
				if (pDebug) alert( 'Error: ' + ex.getmessage );
			} else {
				if (pDebug) alert( 'Error: ' + ex );
			}
		}
	}

	function handleResponseTestzentraleDe(text) {

		pSuggestTestezentrale = new Array();
		pSuggestTestezentrale = testezentraleText.split('\n');

		pCurrentSelection = -1;
		pSuggestDe = new Array();
		pSuggestDe = text.split('\n');

		pSuggest = new Array();
		pSuggest = pSuggestDe.concat(pSuggestTestezentrale);


		var outputText = '<table cellpadding="0" cellspacing="0" class="' + pLayerName + '" width="100%" border="0" onMouseDown="' + pInstanceName + '.handleClick();">';
		outputText += '<tr class="'+pSuggestHeaderClass+'" ><td nowrap="nowrap" colspan="3"><ff:jsescape><bean:message bundle="Suggest" key="header" /></ff:jsescape></td></tr>';
		outputText += '<tr class="suggestHeaderCustom"><td class="suggestHeaderCustomCell" colspan=3><span>Bücher</span><td></tr>';

		var pNewSuggest = new Array();
		//for (var i in pSuggest) {
		for (var i = 0; i < pSuggest.length; i++) {
			var firstChar = pSuggest[i].charCodeAt(0);

			if (firstChar != 13 && firstChar != 10 && pSuggest[i].length >= 1) {
				pNewSuggest.push(pSuggest[i]);
			}
		}
		pSuggest = pNewSuggest;
		var query = pQueryInput.value;
		pSuggestTestezentraleClean = cleanArray(pSuggestTestezentrale);

		for (var i = 0; i < pSuggest.length; i++) {
			pSuggestParts = new Array();
			pSuggestParts = pSuggest[i].split("###");
			if (i == pSuggestTestezentraleClean.length){
				outputText += '<tr><td class="'+pSuggestFooterClass+'" colspan="3">&nbsp;</td></tr><tr id="'+ testzentrale +'" class="suggestHeaderCustom" onMouseOver="' + pInstanceName + '.handleMouseOver('+ testzentrale +');" onMouseOut="' + pInstanceName + '.handleMouseOut('+ testzentrale +');"><td class="suggestHeaderCustomCell" colspan=3><span>Tests zu Ihrem Suchbegriff in der Testzentrale</span><span style="color:#00539F">(www.testzentrale.de)</span> </td></tr>';
			}

			outputText += '<tr id="' + pLayerName + '_' + i + '" class="'+pSuggestRowClass+'" onMouseOver="' + pInstanceName + '.handleMouseOver(' + i + ');" onMouseOut="' + pInstanceName + '.handleMouseOut(' + i + ');">'
								+'<td class="'+ pSuggestQueryClass +'">' + pSuggestParts[0].replace(new RegExp("("+query+")","ig"),'<span class="'+pSuggestQueryTypedClass+'">$1</span>') + '</td>'
								+'<td nowrap="nowrap" class="'+ pSuggestTypeClass +'">' + pSuggestParts[2] + '</td>'
								+'<td nowrap="nowrap" class="'+ pSuggestAmountClass +'">' + pSuggestParts[1] + '</td>'
						+'</tr>';
		}

		outputText += '<tr><td class="'+pSuggestFooterClass+'" colspan="3">&nbsp;</td></tr></table>';
		if (pSuggest.length >= 1) {
			showLayer();
			pLayer.innerHTML = outputText;

			// calback for "outside" listeners
			fireSuggestCompleted(true);
		} else {
			hideLayer();
			pLayer.innerHTML = '';

			// calback for "outside" listeners
			fireSuggestCompleted(false);
		}
	}
	//remove emtpy data
	function cleanArray(actual){
		  var newArray = new Array();
		  for(var i = 0; i<actual.length; i++){
		      if (actual[i]){
		        newArray.push(actual[i]);
		    }
		  }
		  return newArray;
		}

}
