function townsObj(field, tPos)
  {
      this.field = field;
      this.tPos = tPos;
      this.element = document.getElementById(this.field);
      this.ajaxStream;
      this.townCount;
      this.townList = new Array();  // list of all towns received from ajax
      this.firstEight = new Array(); // the 8 towns shown on the hint list
      this.selected = -1;
      this.hints = document.getElementById("townHints");
      this.startText;

      this.keyUp = keyUp;

      this.update = update;
      this.closeHints = closeHints;

  }

  function keyUp(e)
  {
      keyCode = -1;

      if(window.event) // IE
      {
          keyCode = e.keyCode;
      }
      else if(e.which)
      {
          keyCode = e.which;
      }


      if(keyCode == 38) // up
      {
        if(this.selected > 0)
        {
          this.selected--;

          var selectedDiv = document.getElementById(this.selected);
          selectedDiv.style.background = "#55d";
          selectedDiv.style.color = "#fff";

          if(this.selected < 8 && this.selected < this.townList.length)
          {
            var lastSelectedDiv = document.getElementById(this.selected+1);
            lastSelectedDiv.style.background = "#fff";
            lastSelectedDiv.style.color = "#000";
          }
        }
      }

      else if(keyCode == 40) // down
      {
        if(this.selected < 8 && this.selected < this.townList.length)
        {
          this.selected++;

          var selectedDiv = document.getElementById(this.selected);
          selectedDiv.style.background = "#55d";
          selectedDiv.style.color = "#fff";

          if(this.selected > 0)
          {
            var lastSelectedDiv = document.getElementById(this.selected-1);
            lastSelectedDiv.style.background = "#fff";
            lastSelectedDiv.style.color = "#000";
          }
        }
      }

      else if(keyCode == 27) // escape
      {
        this.hints.style.visibility = "hidden";
      document.getElementById("hackFrame").style.visibility = "hidden";
      }

      else if(keyCode == 13) // enter/return
      {
        this.closeHints();
      }

      else
      {
          // reset the selected town value
          this.selected = -1;

          // used to reference the parent townsObj instance from the ajax object
          var thisTownsObj = this;

          // 2 letters entered, use ajax to get list of towns
          if(this.element.value.length == 3 && keyCode != 8)
          {
              thisTownsObj.selected = -1;

              //thisTownsObj.hints.innerHTML = "<div style = \"padding-left: 3px; padding-right: 3px; cursor: default;\">Searching for towns...</div>";
             // thisTownsObj.hints.style.visibility = "visible";
              thisTownsObj.hints.style.top = thisTownsObj.tPos;
              thisTownsObj.hints.style.left = "85px";

              // remove the old ajax object
              delete thisTownsObj.ajaxStream;

              thisTownsObj.ajaxStream = ajaxStart();

              thisTownsObj.ajaxStream.onreadystatechange = function()
              {
                  if(thisTownsObj.ajaxStream.readyState==4)
                  {
                      var res = thisTownsObj.ajaxStream.responseText;
                      eval(res);

                      thisTownsObj.update();
                  }
              }

              // get town hint
              thisTownsObj.ajaxStream.open("GET","getTown.php?town="+this.element.value, true);
              thisTownsObj.ajaxStream.send(null);
          }
          else if(this.element.value.length >= 3 && (thisTownsObj.ajaxStream.readyState == 0 || thisTownsObj.ajaxStream.readyState == 4))
          {
              thisTownsObj.hints.style.visibility = "visible";
              thisTownsObj.hints.style.top = thisTownsObj.tPos;
              thisTownsObj.hints.style.left = "85px";
              thisTownsObj.update();
          }
          else if(this.element.value.length == 0)
          {
            this.hints.style.visibility = "hidden";
            document.getElementById("hackFrame").style.visibility = "hidden";
          }
      }
  }

  // called when ever the list of towns is updated
  function update()
  {
      thisTownsObj = this;

      thisTownsObj.hints.innerHTML = "<div style = \"padding: 3px; padding-top: 0px; cursor: default; color: #555; font-style: italic; text-align: center;\" >suggestions:</div>";
      thisTownsObj.hints.style.visibility = "visible";

      thisTownsObj.startText = thisTownsObj.element.value;
      var townUp = thisTownsObj.startText.toUpperCase();

      var listed = 0;

      for(var i = 0; i < thisTownsObj.townCount; i++)
      {
          var listUp = thisTownsObj.townList[i].toUpperCase();

          if(listUp.indexOf(townUp) == 0)
          {
              thisTownsObj.firstEight[listed] = thisTownsObj.townList[i];
              thisTownsObj.hints.innerHTML+= "<div id = \""+listed+"\" style = \"padding-left: 3px; padding-right: 3px; cursor: default;\" onmouseover = \"this.style.background = '#55d'; this.style.color = '#fff'; thisTownsObj.selected = '"+listed+"';\" onmouseout = \"this.style.background = '#fff'; this.style.color = '#000'; thisTownsObj.selected = -1;\">"+this.townList[i]+"</div>";
              //document.getElementById("hackFrame").innerHTML+= "<div id = \""+listed+"\" style = \"padding-left: 3px; padding-right: 3px; cursor: default;\" onmouseover = \"this.style.background = '#55d'; this.style.color = '#fff'; thisTownsObj.selected = '"+listed+"';\" onmouseout = \"this.style.background = '#fff'; this.style.color = '#000'; thisTownsObj.selected = -1;\">"+this.townList[i]+"</div>";
              //
              listed++;
              if(listed == 8) i = thisTownsObj.townCount;
          }
      }

      if(listed == 0) this.closeHints(); //this.hints.innerHTML = "<div style = \"padding-left: 3px; padding-right: 3px; cursor: default;\">No matching towns found.</div>";
      else
      {
        var hintHeight = this.hints.offsetHeight; //document.defaultView.getComputedStyle(this.hints, null).getPropertyValue('height', null)
        var hintWidth = this.hints.offsetWidth; //document.defaultView.getComputedStyle(this.hints, null).getPropertyValue('width', null)
        var hintTop = this.hints.offsetTop; //document.defaultView.getComputedStyle(this.hints, null).getPropertyValue('top', null)
        var hintLeft = this.hints.offsetLeft; //document.defaultView.getComputedStyle(this.hints, null).getPropertyValue('left', null)

        document.getElementById("hackFrame").style.visibility = "visible";
        document.getElementById("hackFrame").style.height = hintHeight+"px";
        document.getElementById("hackFrame").style.width = hintWidth+"px";
        document.getElementById("hackFrame").style.top = hintTop+"px";
        document.getElementById("hackFrame").style.left = hintLeft+"px";
      }
  }

  function closeHints()
  {
      if(this.selected >= 0) this.element.value = this.firstEight[this.selected];
      if(this.hints.style.top == (this.tPos)) this.hints.style.visibility = "hidden";
      document.getElementById("hackFrame").style.visibility = "hidden";
  }

  var fromHints;

  var townCount;
  var townList;