function fcSuggest() {
    var pRequest;
    var pLayer;
    var pDebug = false;
    var pSearchURL = "";
    var pQueryParamName = "";
    var pForm = null;
    var pLayerName = "";
    var pAnchor;
    var pTemplate;
    var pQueryInput;
    var pSuggest = new Array();
    var pLastQuery;
    var pCurrentSelection = 0;
    var submitted = false;
    var aParameterKeys = new Array();
    var aParameterValues = new Array();

    this.init = function(searchURL, formid, queryParamName, queryFieldId, divLayername, debugMode) {
        pSearchURL = searchURL;
        pForm = document.getElementById(formid);
        pQueryParamName = queryParamName;
        pLayerName = divLayername;
        pDebug = debugMode;

        if (pSearchURL == "") {
            if (pDebug) alert("no searchurl defined");
            return null;
        } else if (pForm == null) {
            if (pDebug) alert("no form found");
            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.getElementById(queryFieldId);
        pQueryInput.onkeyup = handleKeyPress;
        pQueryInput.onblur = hideLayer;

        pTemplate = document.getElementById("suggestRow");
        pAnchor = document.getElementById("suggestAnchor");
        pAnchor.removeChild(pTemplate);

        pForm.onsubmit = handleSubmit;
    }
    
    this.addParameter = function(sKey, sValue) {
        aParameterKeys.push(sKey);
        aParameterValues.push(sValue);
    }

    function handleSubmit() {
        submitted = true;
        if (pSuggest[pCurrentSelection] != undefined) {
            //if (pSuggest[pCurrentSelection].type != "Kategorie") {
                pQueryInput.value = pSuggest[pCurrentSelection].title.replace(/<b>/g, '').replace(/<\/b>/g, '');
            //}
        }
    }

    this.handleClick = function() {
        if (pSuggest[pCurrentSelection] != undefined) {
            if (pSuggest[pCurrentSelection].type == "Kategorie" && pForm.elements["filterkategorie"]) {
                pForm.elements["filterkategorie"].value = "__" + pSuggest[pCurrentSelection].type + "__";
                pQueryInput.value = "";
                pForm.submit();
            } else {
                pQueryInput.value = pSuggest[pCurrentSelection].title.replace(/<b>/g, '').replace(/<\/b>/g, '');
                pForm.submit();
            }
        }
    }

    this.handleMouseOver = function(elem) {
        highlightSuggest(elem);
        pCurrentSelection = elem.getAttribute("id").split('_')[1];
    }

    this.handleMouseOut = function(elem) {
        unmarkSuggest(elem);
        pCurrentSelection = -1;
    }

    function handleKeyPress(evt) {
        evt = (evt) ? evt : ((event) ? event : null);
        var keyCode = evt.keyCode;
        if(keyCode == 13) {
        // ignore enter
        } else if (keyCode == 38) {
            moveSelection("up")
        } else if (keyCode == 40) {
            moveSelection("down");
        } else {
            if (pQueryInput.value == "") {
                hideLayer();
                return;
            }
            if (pLastQuery != pQueryInput.value)
                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 trim(str) {
        return str.replace(/^\s*/, "").replace(/\s*$/, "");
    }

    function startAjax() {
        // without the next code-line the suggestion layer will not be shown, if we selected
        // an already searched query and change our query after this.
        // e.g. search for hello, then type hell and select hello from the suggestions
        // now removing the 'o' will not show the layer
        submitted = false;
        
        var queryString = pQueryParamName + "=" + trim(pQueryInput.value);
        
        for(var i = 0; i < aParameterValues.length; i++) {
            queryString += "&" + aParameterKeys[i] +"=" + encodeURIComponent(trim(aParameterValues[i]));
        }
        
        // use utf8 aware encodeURIComponent instead escape
        var requestURL = pSearchURL + "?" + queryString;

        //		// k.f.: Suchraum Einschränkung, z.B. Musik => finde keine Book suggestions
        //		filter=getParam("fq");
        //		type=getCategories();
        //		if(type!="all")filter="type_s:"+type;
        //		if(filter!=null && filter!="")
        //        	requestURL = requestURL + "&filter=" + encodeURIComponent(filter);

        try {
            if (window.XMLHttpRequest) {
                pRequest = new XMLHttpRequest();
            } else if (window.ActiveXObject) {
                pRequest = new ActiveXObject("Microsoft.XMLHTTP");
            } else {
                if (pDebug) alert("");
            }

            pLayer = document.getElementById(pLayerName);
            if (pLayer != null) {
                if (queryString != "") {
                    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 );
                console.log("Error: " + ex.getmessage)
            } else {
                console.log("Error: " + ex.getmessage)
            //                if (pDebug) alert( "Error: " + ex );
            }
        }
    }

    function hideLayer() {
        if (pLayer != null) {
            pLayer.style.display = "none";
        }
    }

    function showLayer() {
        if (pLayer != null && pSuggest != null && pSuggest.length >= 1) {
            pLayer.style.display = "block";
        }
    }

    // Make public
    this.hideLayer = hideLayer;
    this.showLayer = showLayer;

    function callbackAjax() {
        if (submitted == false) {
            if (pRequest.readyState == 4) {
                if (pRequest.status == 0) {
                    if (pDebug)
                        console.log("status 0: cant ajax to different server!")
                } else if (pRequest.status == 200) {
                    handleResponse(pRequest.responseText);
                } else {
                    if (pDebug)
                        console.log("Error (" + pRequest.status + "): " + pRequest.statusText);
                }
            }
        } else {
            if (pDebug)
                console.log("already submitted!");
        }
    }

    function handleResponse(text) {
        pCurrentSelection = -1;
        if(text != '') {
            if(typeof JSON != 'undefined') {
                pSuggest = JSON.parse(text);
            } else {
                pSuggest = eval(text);
            }
        }

        var newSuggest = new Array();
        var noDuplicates = new Object();

        for (var i = 0; i < pSuggest.length; i++) {
            if (i >= 15) {
                break;
            }
            var firstChar = pSuggest[i].title.charCodeAt(0);
            if (firstChar != 13 && firstChar != 10 && pSuggest[i].title.length > 0) {
                if (pSuggest[i].title == null)
                    continue;

                // do not add duplicate item
                if (noDuplicates[pSuggest[i].title] == null) {
                    noDuplicates[pSuggest[i].title] = pSuggest[i];
                    newSuggest.push(pSuggest[i]);
                }
            }
        }
        pSuggest = newSuggest;

        while (node = pAnchor.childNodes[0]) {
            node = pAnchor.removeChild(node);
            delete node;
        }

        var tmpCounter = 0;
        var blOtherHitsAdded = false;
        //for (var j = 0; i < pSuggest.length; j++) {
        for (var j in pSuggest) {
            var newNode = pTemplate.cloneNode(true);
            newNode.setAttribute("id", "suggestRow_" + tmpCounter);
            tmpCounter++;
            newNode.onmouseover = function() {
                // use global variable !!
                oSuggest.handleMouseOver(this);
            }
            newNode.onmouseout = function() {
                // use global variable !!
                oSuggest.handleMouseOut(this);
            }

            for (var i = 0; i < newNode.childNodes.length; i++) {
                var node = newNode.childNodes[i];
                if (!node.firstChild)
                    continue;

                if (node.firstChild.data == "Text")
                    node.firstChild.data = pSuggest[j].title;
                else if (node.firstChild.data == "Anzahl")
                    node.firstChild.data = pSuggest[j].count;
                else if (node.firstChild.data == "Typ")
                    node.firstChild.data = pSuggest[j].type;
            }

            if(pSuggest[j].type == 'typo3' && blOtherHitsAdded == false) {                
                var oNewHeaderNode = document.createElement('h3');
                oNewHeaderNode.className = 'small meta';
                var oNewHeaderTextNode = document.createTextNode('Andere Treffer');
                oNewHeaderNode.appendChild(oNewHeaderTextNode);
                newNode.children[0].className = 'otherHits';
                newNode.children[0].insertBefore(oNewHeaderNode, newNode.children[0].firstChild);
                blOtherHitsAdded = true;
            }
            pAnchor.appendChild(newNode);
            pAnchor.children[pAnchor.children.length-1].children[0].innerHTML = pAnchor.children[pAnchor.children.length-1].children[0].innerHTML.replace(/&lt;/g, '<').replace(/&gt;/g, '>');
        }

        //TODO if enter => hide
        if (pSuggest.length >= 1)
            showLayer();
        else
            hideLayer();
    }

    this.handleMouseOver = function(elem) {
        highlightSuggest(elem);
        pCurrentSelection = elem.getAttribute("id").split('_')[1];
    }

    this.handleMouseOut = function(elem) {
        unmarkSuggest(elem);
        pCurrentSelection = -1;
    }

    function highlightSuggest(tblCell) {
        tblCell.className = "highlight";
    }

    function unmarkSuggest(tblCell) {
        tblCell.className = "";
    }

    function unmarkAll() {
        for (var i = 0; i < pAnchor.childNodes.length; i++)
            unmarkSuggest(pAnchor.childNodes[i]);
    }

    function getTableCell(pos) {
        return document.getElementById("suggestRow_" + pos);
    }
}
