function SideEffectList(settings) {
    // check settings
    if (!settings) {
        alert("Please set settings to SideEffectList!");
        return;
    }
    if (!settings.namePrefix) {
        alert("Please set namePrefix to settings for SideEffectList!");
        settings.namePrefix = "";
        return;
    }

    // find list
    this.list = $("#" + settings.namePrefix + "List").get(0);
    if (!this.list) {
        alert("Can't find " + settings.namePrefix + "List");
        return;
    }

    // find popup
    this.popup = $("#" + settings.namePrefix + "Popup").get(0);
    if (!this.popup) {
        alert("Can't find for SideEffectList popup!");
        return;
    }

    // find popup items
    this.popupItems = $("#" + settings.namePrefix + "PopupItems").get(0);
    if (!this.popupItems) {
        alert("Can't find for SideEffectList popup items!");
        return;
    }

    // find search input
    this.search = $("#" + settings.namePrefix + "Search").get(0);
    if (!this.search) {
        alert("Can't find SideEffectList search input!");
        return;
    }

    var self = this;
    _addToListOfPopups(this);
    this.settings = settings;
    this.selectedIndex = -1;
    this.requestCount = 0;
    this.lazyPopupShow = null;
    this.kbTimeout = 200;
    this.lastCaller = null;
    this.processEnter = false;

    // configure search input
    $(this.search).bind("keyup click", function(event) {
        var keyCode = getKeyCode(event);
        if (keyCode == 38 || keyCode == 40) return true;
        if (keyCode == 13) {
            if (self.lastCaller || self.lazyPopupShow != null) this.processEnter = true;
            self.hidePopup();
            self.search.value = "";
            self.addItemByIndex(self.selectedIndex);
            return false;
        } else if (keyCode == 27 || (keyCode == 9) || (keyCode == 9 && event.shiftKey)) {
            //            $(this.popup).css("display", "none");
            return false;
        } else {
            //            $(this.popup).css("display", "none");
            this.selectedIndex = -1;
            var searchValue = this.value;
            if (searchValue.length >= 3) {
                // cancel timer if there is one, then short timeout before getList
                if (this.lazyPopupShow != null) {
                    clearTimeout(this.lazyPopupShow);
                }
                this.lazyPopupShow = setTimeout(function() {
                    // Start request on server. 
                    if (this.lastCaller) this.lastCaller.cancelRequest();
                    this.lastCaller = new AsyncAjax();
                    this.lastCaller.init(
                            "/pages/sideEffectPopup.jsp?namePrefix=" + settings.namePrefix
                                    + "&searchText=" + searchValue);
                    try {
                        this.lastCaller.setRequestId(++self.requestCount);
                        this.lastCaller.asyncGET({
                            onLoad: function (client) {
                                // if this request not last
                                if (client.getRequestId() != self.requestCount) return;
                                // invoke if server response ok
                                var text = trim(client.xmlhttp.responseText);
                                if (text.length > 0) self.showPopup(text);
                                self.selectedIndex = 0;
                                self.lastCaller = null;
                                if (self.processEnter) {
                                    self.selectCurrentRow(0);
                                    self.processEnter = false;
                                }
                            },
                            onError: function(status, statusText) {
                                // invoke in other cases
                                self.lastCaller = null;
                                self.processEnter = false;
                                alert("Error: " + status + ": " + statusText + "<br>");
                            }});
                    } catch (exception) {
                        alert(exception);
                    }

                    self.lazyPopupShow = null;
                }, this.kbTimeout);
            }
        }
        return true;
    });

    $(this.search).bind("keypress", function(event) {
        var keyCode = getKeyCode(event);
        if (keyCode == 38) {
            self.scrollVisibleList("up");
        } else if (keyCode == 40) {
            self.scrollVisibleList("down");
        } else if (keyCode == 9) {
            this.hidePopup();
        }
    });
}

SideEffectList.prototype.showPopup = function(responseText) {
    this.popupItems.innerHTML = responseText;
    // show popup in need place
    var sxListWidth = 430;

    this.popup.style.width = sxListWidth + "px";
    //    var obj1TopX = _getLeft(this.search);
    //    var obj1TopY = _getTop(this.search);
    var obj1BottomX = _getLeft(this.search);
    obj1BottomX = calculateWindowX(obj1BottomX, sxListWidth);
    var obj1BottomY = _getTop(this.search) + this.search.offsetHeight + 6;
    this.popup.style.left = obj1BottomX + "px";
    this.popup.style.top = obj1BottomY + "px";

    // add index to every items for use it in events
    var items = $("." + this.settings.namePrefix + "sideEffectItems").get();
    for (var i = 0; i < items.length; i++) {
        items[i].index = i;
        items[i].self = this;
    }

    // bind events for item
    var self = this;
    $("." + this.settings.namePrefix + "sideEffectItems").bind("mouseover", function () {
        this.self.highliterow(this.index);
    });

    $("." + this.settings.namePrefix + "sideEffectItems").bind("click", function () {
        self.hidePopup();
        self.search.value = "";
        self.addItemByIndex(self.selectedIndex);
    });

    $(this.popup).css("display", "block");
    $(this.popup).css("visibility", "visible");
    this.highliterow(0);
}

SideEffectList.prototype.addItemByIndex = function (index) {
    var itemId = document.getElementById(this.settings.namePrefix + "ItemId" + index).value;
    var itemName = document.getElementById(this.settings.namePrefix + "ItemName" + index).value;
    this.addItem(itemId, itemName);
}

SideEffectList.prototype.scrollVisibleList = function(direction) {
    if (direction == "down") {
        this.highliterow(this.selectedIndex + 1);
    } else if (direction == "up" && this.selectedIndex > 0) {
        this.highliterow(this.selectedIndex - 1);
    }
    if (document.getElementById(this.settings.namePrefix + "Pickrow" + this.selectedIndex)) {
        document.getElementById(this.settings.namePrefix + "Scroller").scrollTop =
        document.getElementById(this.settings.namePrefix + "Pickrow" + this.selectedIndex).offsetTop - 20;
    }
}

SideEffectList.prototype.getItemIdAndNames = function() {
    var itemIdAndNames = new Array();
    var i = 0;
    for (; ; i++) {
        var item = document.getElementById(this.settings.namePrefix + "Item" + i);
        if (!item) break;
        itemIdAndNames.push({itemId: item.itemId, itemName: item.itemName});
    }
    return itemIdAndNames;
};

SideEffectList.prototype.addItem = function(itemId, itemName) {
    var i = 0;
    for (; ; i++) {
        var item = document.getElementById(this.settings.namePrefix + "Item" + i);
        if (!item) break;
        if (item.itemId == itemId) return;
    }
    var newItem = document.createElement("div");
    newItem.id = this.settings.namePrefix + "Item" + i;
    newItem.itemId = itemId;
    newItem.itemName = itemName;
    newItem.innerHTML =
    "<span onmouseover=\"Tip('Remove " + itemName + "', BALLOON, true)\" onmouseout=\"UnTip()\">" +
    "<img class='cursorhand'  src='./images/glythe/trash.gif' id='" + this.settings.namePrefix + "ItemDelete" + i + "'/></span> &nbsp;" + itemName;
    this.list.appendChild(newItem);
    var self = this;
    document.getElementById(this.settings.namePrefix + "ItemDelete" + i).onclick = function () {
        UnTip();
        newItem.parentNode.removeChild(newItem);
        if (self.onRemove) self.onRemove(newItem.itemId);
    };

    if (this.onAdd) this.onAdd(itemId, itemName);
};

SideEffectList.prototype.removeItem = function(itemId) {
    var i = 0;
    for (; ; i++) {
        var item = document.getElementById(this.settings.namePrefix + "Item" + i);
        if (!item) break;
        if (item.itemId == itemId) {
            item.parentNode.removeChild(item);
            if (this.onRemove) this.onRemove(itemId);
            return;
        }
    }
}

SideEffectList.prototype.removeAll = function() {
    var i = 0;
    for (; ; i++) {
        var item = document.getElementById(this.settings.namePrefix + "Item" + i);
        if (!item) break;
        item.parentNode.removeChild(item);
        if (this.onRemove) this.onRemove(itemId);
        return;
    }
}

SideEffectList.prototype.hidePopup = function() {
    $(this.popup).css("display", "none");
}

SideEffectList.prototype.highliterow = function(index) {
    // off all
    if (document.getElementById(this.settings.namePrefix + "_row_middle" + this.selectedIndex)) {
        document.getElementById(this.settings.namePrefix + "_row_left" + this.selectedIndex).className = "inputLeftRound";
        document.getElementById(this.settings.namePrefix + "_row_right" + this.selectedIndex).className = "inputRightRound";
        document.getElementById(this.settings.namePrefix + "_row_middle" + this.selectedIndex).className = "unhighlighted";
    }

    // up one
    $("#" + this.settings.namePrefix + "_row_left" + index).addClass("inputLeftRound_h");
    $("#" + this.settings.namePrefix + "_row_right" + index).addClass("inputRightRound_h");
    if (document.getElementById(this.settings.namePrefix + "_row_middle" + index)) {
        document.getElementById(this.settings.namePrefix + "_row_middle" + index).className = "highlighted";
    }
    this.selectedIndex = index;
}