function Suggest() {
    var KEY_DOWN = 40;
    var KEY_ENTER = 13;
    var KEY_ESC = 27;
    var KEY_UP = 38;

    var suggestField = null;
    var suggestFile = null;
    var suggestOpacity = 1.0;
    var suggestSkipLabel = false;
    var suggestSubmitFkt = null;
    var suggestSubmiOnes = false;

    var suggestTable = null;
    var suggestFieldText = "";
    var suggestLineText = "";
    var suggestId = "";
    var suggestLine = -1;
    var suggestParam = null;
    var suggestMore = null;
    var moreLine = -1;

    var keyPE = null;
    var queryPE = null;
    var executeClickSuggest = true;
    var executeSubmit = true;
    var isMoreN = false;
    var isMoreR = false;
    var isMoreRequest = false;
    var counter = 0;

    var self = this;

    //===========================================================================

    if (arguments.length >= 2 && arguments.length <= 6) {
        suggestField = arguments[0];
        suggestFile = arguments[1];
        if (arguments.length > 2) {
            suggestOpacity = arguments[2]
        }
        if (arguments.length > 3) {
            suggestSkipLabel = arguments[3];
        }
        if (arguments.length > 4) {
            suggestSubmitFkt = arguments[4];
        }
        if (arguments.length > 5) {
            suggestSubmiOnes = arguments[5];
        }
    }

    //===========================================================================

    this.querySuggestRequestStop = function() {
        if (queryPE instanceof PeriodicalExecuter) {
            queryPE.stop();
        }
        queryPE = null;
    }

    this.querySuggestRequest = function() {
        self.querySuggestRequestStop();

        queryPE = new PeriodicalExecuter(function() {
            self.ajaxSuggestRequest();
        }, 0.001);
    }

    this.ajaxSuggestRequest = function() {
        if (!isMoreRequest) {
            isMoreRequest = isMoreN || isMoreR;

            //self.clearSuggest();

            self.querySuggestRequestStop();

            counter++;

            params = "&counter=" + counter + "&suggest=" + suggestFieldText;
            if (suggestParam != null && suggestParam != "") {
                params += "&suggestParam=" + suggestParam;
            }
            if (suggestMore != null && suggestMore != "") {
                params += "&suggestMore=" + suggestMore;
            }

            new Ajax.Request(suggestFile, {
                method: "get",
                asynchronous: true,
                parameters: params,

                onFailure: function(response) {
                    self.stopSuggest();
                    isMoreN = false;
                    isMoreR = false;
                    isMoreRequest = false;
                },

                onSuccess: function(response) {
                    self.ajaxSuggestResponse(response.responseText);
                    if (isMoreR) {
                        suggestLine = self.getSuggestRowCnt() - moreLine;
                        self.selectSuggest();
                    }
                    isMoreN = false;
                    isMoreR = false;
                    isMoreRequest = false;
                }
            });
        }
    }

    //-------------------------------------------------------------------------

    this.ajaxSuggestResponse = function(responseText) {
        suggestArray = responseText.split("|");

        if (suggestArray[suggestArray.length - 1] == counter) {
            self.clearSuggest();

            for (var i = 0; i < suggestArray.length - 4; i += 4) {
                if (suggestArray[i].length > 0) {
                    self.appendSuggest(suggestArray[i], suggestArray[i + 1], suggestArray[i + 2], suggestArray[i + 3], i / 4);
                }
            }

            if (self.getSuggestRowCnt() > 0) {
                self.selectSuggest();
                self.showSuggest();
            } else {
                self.stopSuggest();
            }
        }
    };

    //===========================================================================

    this.getElements = function() {
        return $(suggestTable).getElementsByTagName("td");
    };

    //-------------------------------------------------------------------------

    this.focusSuggest = function() {
        $(suggestField).focus();
        // Bringt den Cursor im IE6 an das Zeielenende
        //suggestField.value = suggestField.value;
    };

    //-------------------------------------------------------------------------

    this.clickSuggest = function(event) {
        if (executeClickSuggest) {
            self.hideSuggest();
            suggestLine = -1;
            //self.focusSuggest();
        }
        executeClickSuggest = true;
    };

    //-------------------------------------------------------------------------

    this.placeAndResizeSuggest = function() {
        $(suggestTable).setStyle({ "border": "1px solid black", "left": $(suggestField).cumulativeOffset().left + "px", "top": ($(suggestField).cumulativeOffset().top + $(suggestField).offsetHeight) + "px", "width": $(suggestField).offsetWidth + "px" });
    };

    //-------------------------------------------------------------------------

    this.getSuggestRowCnt = function() {
        return $(suggestTable).rows ? $(suggestTable).rows.length : -1;
    };

    //-------------------------------------------------------------------------

    this.isShownSuggest = function() {
        return $(suggestTable).getStyle("visibility") == "visible";
    };

    //-------------------------------------------------------------------------

    this.showSuggest = function() {
        self.placeAndResizeSuggest();
        $(suggestTable).setStyle({ "visibility": "visible" });
    };

    //-------------------------------------------------------------------------

    this.hideSuggest = function() {
        $(suggestTable).setStyle({ "visibility": "hidden" });
    };

    //-------------------------------------------------------------------------

    this.stopSuggest = function() {
        self.hideSuggest();
        self.focusSuggest();
        suggestLine = -1;
        // IE
        var value = $(suggestField).value;
        $(suggestField).value = value + " ";
        $(suggestField).value = value;
    };

    //-------------------------------------------------------------------------

    this.clearSuggest = function() {
        while (self.getSuggestRowCnt() > 0) {
            $(suggestTable).deleteRow(0);
        }
    };

    //-------------------------------------------------------------------------

    this.stripHTML = function(s) {
        return s.replace(/<[^>]*>/g, "");
    };

    //-------------------------------------------------------------------------

    this.isLabel = function(theClass) {
        return theClass.toUpperCase() == theClass;
    };

    //-------------------------------------------------------------------------

    this.appendSuggest = function(lineText, fieldText, id, theClass, line) {
        tdElement = new Element("td", { }).update(lineText);
        $(tdElement).theFieldText = self.stripHTML(fieldText);
        $(tdElement).theLineText = self.stripHTML(lineText);
        $(tdElement).theId = id;
        //console.info(theClass);
        $(tdElement).theClass = theClass;
        $(tdElement).line = line;
        $(tdElement).name = $(suggestField).name;

        $(tdElement).observe('mousemove', function() {
            suggestLine = this.line;
            self.selectSuggest();
            return false;
        });

        $(tdElement).observe("mouseout", function() {
            suggestLine = -1;
            self.selectSuggest();
            return false;
        });

        if (!self.isLabel(theClass)) {
            $(tdElement).observe("click", function() {
                self.handleSelection(this.theFieldText, this.theId);
                return true;
            });
        } else {//if user clicks label "Siedlungen" etc.
            $(tdElement).observe("click", function() {
                executeClickSuggest = false;
                return true;
            });
        }

        $(suggestTable).insertRow(-1).appendChild($(tdElement));
    };

    //-------------------------------------------------------------------------

    this.selectSuggest = function() {
        var elements = self.getElements();
        for (var i = 0; i < $(elements).length; i++) {
            if ($(elements[i]).line == suggestLine) {
                if (suggestSkipLabel && self.isLabel(self.getElements()[suggestLine].theClass)) {
                    $(elements[i]).className = $(elements[i]).theClass + " " + $(elements[i]).theClass + "passive";
                } else {
                    $(elements[i]).className = $(elements[i]).theClass + " " + $(elements[i]).theClass + "aktive";
                }
            } else {
                $(elements[i]).className = $(elements[i]).theClass + " " + $(elements[i]).theClass + "passive";
            }
        }
        self.focusSuggest();
    };

    //-------------------------------------------------------------------------

    this.handleKey = function(key) {
        var suggesValue = $(suggestField).value;

        if (key == KEY_ESC) {
            //$(suggestField).value = suggestFieldText = "";
            self.stopSuggest();
            return false;
        }

        if (suggestFieldText != suggesValue) {
            suggestFieldText = suggesValue;

            suggestLine = -1;
            suggestId = "";
            self.selectSuggest();

            if (suggestFieldText.length > 0) {
                executeSubmit = true;
                self.querySuggestRequest();
            } else {
                self.stopSuggest();
            }
        }

        if (self.isShownSuggest()) {
            if (key == KEY_UP || key == KEY_DOWN) {
                if (key == KEY_UP) {
                    if (suggestLine >= 0) {
                        suggestLine--;
                        if (suggestLine >= 0) {
                            if (suggestSkipLabel && self.isLabel(self.getElements()[suggestLine].theClass)) {
                                self.handleKey(KEY_UP);
                            }
                        } else {
                            suggestId = "";
                        }
                    } else {
                        suggestLine = self.getSuggestRowCnt() - 1;
                    }
                }
                if (key == KEY_DOWN) {
                    suggestLine++;
                    if (suggestLine < self.getSuggestRowCnt()) {
                        if (suggestSkipLabel && self.isLabel(self.getElements()[suggestLine].theClass)) {
                            self.handleKey(KEY_DOWN);
                        }
                    } else {
                        suggestLine = -1;
                        suggestId = "";
                    }
                }
                self.selectSuggest();
                return false;
            } else {
                if (key == KEY_ENTER) {
                    var executeEnter = true;
                    if (suggestLine >= 0) {
                        var elements = self.getElements();
                        for (var i = 0; executeEnter && i < $(elements).length; i++) {
                            if ($(elements[i]).line == suggestLine) {
                                executeEnter = !self.isLabel($(elements[i]).theClass);
                                if (executeEnter) {
                                    self.handleSelection($(elements[i]).theFieldText, $(elements[i]).theId);
                                }
                            }
                        }
                    } else { // Cursor im Textfeld
                        self.handleSelection(suggestFieldText, "");
                    }
                    return false;
                } else {
                    //suggestLine = -1;
                    //suggestId = "";
                    //self.selectSuggest();
                }
            }

        } else {
            if (key == KEY_ENTER) {
                if (executeSubmit || !suggestSubmiOnes) {
                    executeSubmit = false;
                    if (suggestSubmitFkt != null) {
                        suggestSubmitFkt();
                    }
                }
            } else {
                //if (key == KEY_UP || key == KEY_DOWN) {
                //  executeSubmit = true;
                //  self.showSuggest();
                //}
            }
        }

        return false;
    };

    //-------------------------------------------------------------------------

    this.handleSelection = function(fieldText, id) {
        isMoreN = id.length > 0 && id.lastIndexOf('\n') + 1 == id.length;
        isMoreR = id.length > 0 && id.lastIndexOf('\r') + 1 == id.length;
        if (isMoreN || isMoreR) {
            executeClickSuggest = false;
            suggestMore = id;
            //alert(id);
            if (isMoreR) {
                moreLine = self.getSuggestRowCnt() - suggestLine;
            }
            //            console.info(self.getSuggestRowCnt() + " - " + suggestLine + " - " + moreLine);
            self.ajaxSuggestRequest();
            suggestMore = null;
        } else {
            $(suggestField).value = suggestFieldText = fieldText;
            suggestId = id;
            self.stopSuggest();
            //submit on FIRST enter press or mouse click
            executeSubmit = false;//
            if (suggestSubmitFkt != null) {
                suggestSubmitFkt();
            }
        }
        return false;
    }

    //-------------------------------------------------------------------------

    this.keySuggestDown = function(event) {
        var key = event.keyCode;

        if (key == KEY_UP || key == KEY_DOWN) {
            if (keyPE == null) {
                self.handleKey(key);

                keyPE = new PeriodicalExecuter(function() {
                    self.handleKey(key);
                }, 0.2);
            }
        }
    };

    //-------------------------------------------------------------------------

    this.keySuggestUp = function(event) {
        var key = event.keyCode;

        if (keyPE instanceof PeriodicalExecuter) {
            keyPE.stop();
        } else {
            self.handleKey(key);
        }
        keyPE = null;
    };

    //-------------------------------------------------------------------------

    this.focusLost = function(event) {
        self.stopSuggest();
    };

    //---------------------------------------------------------------------------

    this.getSuggestParam = function() {
        return suggestParam;
    };

    //---------------------------------------------------------------------------

    this.setSuggestParam = function(pSuggestParam) {
        suggestParam = pSuggestParam;
    };

    //---------------------------------------------------------------------------

    this.getSelectedText = function() {
        return suggestFieldText;
    };

    //---------------------------------------------------------------------------

    this.setSelectedText = function(psuggestFieldText) {
        suggestFieldText = psuggestFieldText;
        $(suggestField).value = psuggestFieldText;
    };

    //---------------------------------------------------------------------------

    this.getSelectedId = function() {
        return suggestId;
    };

    //---------------------------------------------------------------------------

    this.setSelectedId = function(psuggestId) {
        suggestId = psuggestId;
    };

    //---------------------------------------------------------------------------

    this.clear = function() {
        suggestParam = null;
        suggestMore = null;
        suggestFieldText = "";
        suggestId = "";
        $(suggestField).value = "";
    }

    //---------------------------------------------------------------------------

    this.createSuggest = function() {
        suggestTable = new Element("table", { "border": "0", "cellSpacing": "0", "cellPadding": "0" });
        $(suggestTable).setStyle({ "position": "absolute" });
        $(suggestTable).setStyle({ "zIndex": "100" });
        $(suggestTable).setOpacity(suggestOpacity);
        $(document).observe("click", this.clickSuggest);
        $(suggestField).observe("keydown", this.keySuggestDown);
        $(suggestField).observe("keyup", this.keySuggestUp);
        //$(suggestField).observe("blur", this.focusLost); // IE ???
        $(document.body).appendChild($(suggestTable));
    }

    //---------------------------------------------------------------------------

    self.createSuggest();
}

