function LocationSelector(locationTextFieldElem, locationHiddenFieldElem, maxListSize, parentOnly) {
    // despite effort to sync, simult are occuring.  allow it for now
    window.serial = false;

    this.locationSelectActive = false;
    this.locationValid = true;
    this.locationRequestPending = false;
    this.locationMaxListSize = 10;
    this.parentOnly = parentOnly;

    if (maxListSize)
        this.locationMaxListSize = maxListSize;
    
    this.locationTextField = locationTextFieldElem;
    this.locationHiddenField = locationHiddenFieldElem;
    this.lastSelectedText = this.locationTextField.value;

    if (this.locationTextField.addEventListener) {
        this.locationTextField.addEventListener("keyup", delegateCallback(this, this.handleLocationKeyPress), false);
        this.locationTextField.addEventListener("focus", delegateCallback(this, this.handleLocationFocus), false);
    } else {
        this.locationTextField.attachEvent("onkeyup", delegateCallback(this, this.handleLocationKeyPress));
        this.locationTextField.attachEvent("onfocus", delegateCallback(this, this.handleLocationFocus));
    }

    this.locationSelectArea = document.createElement("div");
    this.locationTextField.form.appendChild(this.locationSelectArea);
    this.locationSelectArea.style.display = "none";
    this.locationSelectArea.style.position = "absolute";
    this.locationSelectArea.style.zIndex = "10000";

    // no location, but have some pre-typed text
    if (this.locationHiddenField.value == '') {
        // special case for Explorer
        var value = this.locationTextField.value;
        if (value != '' && value.indexOf("<") != 0) 
            this.checkLocation(false, null);
    }

    // can be set by caller
    this.onsubmit = null;
}

LocationSelector.prototype.handleLocationFocus = function() {
    if (this.locationTextField.value.match(/<.*?>/))
        this.locationTextField.value = '';
}

LocationSelector.prototype.handleLocationKeyPress = function(event) {
    this.checkLocation(true, event);
}

LocationSelector.prototype.checkLocation = function(keypress, evt) {
    var textKey = false;

    // if this was called as a keypress event
    if (keypress) {
        var keyCode = getKeyCode(evt);
        var key = String.fromCharCode(keyCode);
        textKey = key.match(/\w/);

        // on <enter> 63233 is safari
        if (keyCode == 13 || keyCode == 63233) {
            if (this.locationSelectActive) {
                if (this.locationSelectField.size == 3) {
                    this.locationSelectField.selectedIndex = 0;
                    this.applyLocationSelect(evt);
                }
            }

            cancelEvents(evt);
            if (this.onsubmit)
                this.onsubmit();
            return false;
        }

        // on <down arrow>
        if (keyCode == 40) {
            if (this.locationSelectActive) {
                this.locationSelectField.focus();
                cancelEvents(evt);
                return false;
            } 
        }

        if (keyCode == 8 || keyCode == 46)
            // if its a real key or backspace or delete or space, invalidate it
            if (textKey || keyCode == 8 || keyCode == 46 || keyCode == 32) {
                this.locationTextField.style.backgroundColor="#ddeeee";
                this.locationHiddenField.value = '';
                // invalidate
                this.locationValid = false;
            } else {
                // quick exit for invalid keys... they dont affect results
                return false;
            }
        }
    
        var text = this.locationTextField.value;
        // field erased, exit
        if (text.length == 0) {
            //if (text.length == 0 || (text.length == 1 && !textKey)) {
            this.locationTextField.style.backgroundColor="";
            this.locationSelectArea.style.display = "none";
            this.locationChanged('','');
            return true;
        }

        this.locationTextField.style.backgroundColor="#ddeeee";
        this.locationValid = false;
        pending++;
        setTimeout(delegateCallback(this, this.doLocationMatch), 500);
        return true;
    }

var repeatReq = false;
var pending = 0;

LocationSelector.prototype.doLocationMatch = function() {
    pending--;
    if (pending > 0)
        return;

    if (this.locationRequestPending) {
        repeatReq = true;
        return;
    }

    // make request
    this.locationRequestPending = true;
    var url = "/XmlService?event=getLocationsWithSimilarNames&name=" + encode(this.locationTextField.value);
    if (this.parentOnly)
        url += "&parentOnly=true";
    sendRequest(url, null, delegateCallback(this, this.getLocationsHandler));
}

LocationSelector.prototype.getLocationsHandler = function(result, error) {
    this.locationRequestPending = false;
    if (error) {
        valert(error);
        if (error.match(/login/)) {
            openWindow("/Users?event=displayLogin&returnToUrl=" + encode("/ui/RefreshAndClose.html"));
        }
        return;
    }

    var items = result.getElementsByTagName("name-option");

    var html = "<select name='locationSelect' id='locationSelect' size='1' style='overflow:auto'>";
    var size = items.length;
    if (this.locationMaxListSize < size)
        size = this.locationMaxListSize;
    for (var j=0; j < size; j++) {
        var item = items[j];
        html += "<option value='" + item.getAttribute("id") + "'>" + decode(item.firstChild.nodeValue) + "</option>";
    }
    if (size == this.locationMaxListSize) {
        size++;
        html += "<option value=''>...</option>";
    }

    if (!this.parentOnly) {
        if (size > 0) {
            html += "<option value=''>_______________________________</option>";
            size++;
        }
        html += "<option value='ADD'>+ Add City</option>";
        size++;
    }
    html += "</select>";
    this.locationSelectArea.innerHTML = html;
    this.locationSelectArea.firstChild.onclick = delegateCallback(this, this.applyLocationSelect);
    this.locationSelectArea.firstChild.onkeypress = delegateCallback(this, this.considerLocationSelect);

    this.locationSelectField = this.locationSelectArea.firstChild;
    if (size > 0) {
        // reposition it
        var fieldHeight = 22;
        if (this.locationTextField.clientHeight)
            fieldHeight = this.locationTextField.clientHeight + 2;
        this.locationSelectArea.style.top = (findPosY(this.locationTextField) + fieldHeight) + "px";
        this.locationSelectArea.style.left = findPosX(this.locationTextField) + "px";

        this.locationSelectArea.style.display = "";
        this.locationSelectField.size = size;
        this.locationSelectActive = true;
    } else {
        this.locationSelectActive = false
        this.locationSelectArea.style.display = "none";
    }
 
    if (repeatReq) {
        setTimeout(delegateCallback(this, this.doLocationMatch), 100);
        repeatReq = false;
    }
}

/*
LocationSelector.prototype.considerLocationEnter = function(evt) {
    if (evt) {
        var keyCode = getKeyCode(evt);
        if (keyCode == 13) {
            // get state now
            var submitEnter = locationValid;
            var value = this.locationSelectField.value;
            for (var j=0; j < this.locationSelectField.options.length; j++) {
                if (this.locationSelectField.options[j].value.indexOf(value) == 0) {
                    this.locationSelectField.selectedIndex = j;
                    this.applyLocationSelect(evt);
                    if (!submitEnter) {
                        cancelEvents(evt);
                        return false;
                    }
                    break;
                }
            }
            //cancelEvents(evt);
        }
    }
    return true;
}
*/

LocationSelector.prototype.getLocationText = function() {
    var list = this.locationSelectField;
    // in case it closed
    if (list) {
        for (var j=0; j < list.options.length; j++) 
            if (list.options[j].value == this.locationHiddenField.value)
                return list.options[j].text;
    }
    return "";
}

LocationSelector.prototype.exactMatch = function() {
    return this.lastSelectedText == this.locationTextField.value;
}

LocationSelector.prototype.considerLocationSelect = function(evt) {
        var keyCode = getKeyCode(evt);
        if (keyCode == 13) {
            this.applyLocationSelect(evt);
            cancelEvents(evt);
        } else if (keyCode == 8) {
            this.locationTextField.focus();
            this.locationSelectArea.style.display = "none";
            this.locationSelectActive = false;
            cancelEvents(evt);
        }
}

LocationSelector.prototype.applyLocationSelect = function(evt) {
    var id = this.locationSelectField.options[this.locationSelectField.selectedIndex].value;
    if (id == '')
        return;
    if (id == 'ADD') {
        this.creating = true;
        this.openLocationCreator();
        // hide select list
        this.locationSelectArea.style.display = "none";

        // return focus to location input field
        this.locationTextField.focus();

        this.locationSelectActive = false;
        return;
    }

    var value = this.locationSelectField.options[this.locationSelectField.selectedIndex].text;
    this.locationChanged(id, value);
}

LocationSelector.prototype.locationChanged = function(id,value) {
    this.lastSelectedText = value;
    this.locationHiddenField.value=id;
    this.locationTextField.value=value;
    this.locationTextField.style.backgroundColor="";

    // hide select list
    this.locationSelectArea.style.display = "none";

    // return focus to location input field
    this.locationTextField.focus();

    this.locationSelectActive = false;
    this.locationValid = true;
    // if it has value
    if (id != '') {
        // used for ContentDetails, Explorer, Portal
        if (window.locationChangeHandler)
            window.locationChangeHandler(id,value);
    }
}

LocationSelector.prototype.locationCreated = function(id, value, href) {
    this.locationChanged(id, value);
    this.creating = false;
}

LocationSelector.prototype.openLocationCreator = function(evt) {
    var name = encode(this.locationTextField.value);
    var url = "/Locations?event=addCity&popup=true&name=" + name;
    openWindow(url, 500, 400);
    //this.showLocationSelector(true);
    cancelEvents(evt);
}

/*
LocationSelector.prototype.showLocationSelector = function(createMode) {
        var url = '/Locations?event=displaySelector&popup=true';
        if (createMode) {
            var name = this.locationTextField.value;
            url += '&mode=Create&name='+name;
        } else {
            url += '&mode=Search';
        }
        // register it
        openWindow(url ,500,600);
}
*/

LocationSelector.prototype.isValid = function() {
    return (this.locationHiddenField.value);
}
