  /* the keyword for which an HTTP request has been initiated */
  var httpRequestKeyword = "";
  /* initial user keyword */
  var userKeyword = "";
  /* the last suggestion that has been used for autocompleting the keyword */
  var autocompletedKeyword = "";
  /* flag that indicates if there are results for the current requested keyword*/
  var hasResults = false;
  /* flag that indicates if the up or down arrow keys were pressed
     the last time a keyup event occurred  */
  var isKeyUpDownPressed = false;
  /* the identifier used to cancel the evaluation with the clearTimeout method. */
  var timeoutId = -1;
  /* the currently selected suggestion (by arrow keys or mouse)*/
  var position = -1;
  /* cache object containing the retrieved suggestions for different keywords */
  var oCache = new Object();
  /* the XMLHttp object for communicating with the server */
  var xmlHttpGetSuggestions = createXmlHttpRequestObject();
  /* set debugging */
  var debugMode = false;
  var suggestions = 0;
  

/**
	* createXmlHttpRequestObject()
	* @purpose: creates an XMLHttpRequest instance
	* @return NULL
	*/	
  function createXmlHttpRequestObject() {
    var xmlHttp;
    try {
      xmlHttp = new XMLHttpRequest();
    } catch(e) {
      var XmlHttpVersions = new Array("MSXML2.XMLHTTP.6.0",
                                      "MSXML2.XMLHTTP.5.0",
                                      "MSXML2.XMLHTTP.4.0",
                                      "MSXML2.XMLHTTP.3.0",
                                      "MSXML2.XMLHTTP",
                                      "Microsoft.XMLHTTP");
      for (var i=0; i<XmlHttpVersions.length && !xmlHttp; i++) {
        try { 
          xmlHttp = new ActiveXObject(XmlHttpVersions[i]);
        } catch (e) {
        } // try-catch
      } // for
    } // try-catch
    if (!xmlHttp) {
      alert("Error creating the XMLHttpRequest object.");
      return;
    } else {
      return xmlHttp;
    } // if-else
  } // function
  
  

/**
	* init_livesearch()
	* @purpose:  function that initializes the page
	* @return NULL
	*/	
  function init_livesearch() {
    var userKeyword = "";
    if (document.getElementById("tagname")) {
      // retrieve the input control for the keyword
      var oKeyword = document.getElementById("tagname");    
      // prevent browser from starting the autofill function
      oKeyword.setAttribute("autocomplete", "off");  
      // reset the content of the keyword and set the focus on it
      oKeyword.value = "";
      oKeyword.focus();
    } // if
    return;
  } // function
  
  
/**
	* getSuggestions()
	* @purpose:  initiate HTTP request to retrieve suggestions for the current keyword
	* @param   (string)  keyword
	* @return NULL
	*/	
  function getSuggestions(keyword) {  
    if(keyword != "" && !isKeyUpDownPressed) {
      // check to see if the keyword is in the cache
      isInCache = checkCache(keyword);
      // if keyword is in cache...
      if(isInCache == true) {   
        // retrieve the results from the cache          
        httpRequestKeyword=keyword;
        userKeyword=keyword;
             
        // display the results in the cache
        displayResults(keyword, oCache[keyword]);                          
      } else { // if the keyword isn't in cache, make an HTTP request
        if(xmlHttpGetSuggestions) { 
          try {
            /* if the XMLHttpRequest object isn't busy with a previous
               request... */
            if (xmlHttpGetSuggestions.readyState == 4 || 
                xmlHttpGetSuggestions.readyState == 0) {    
              
              httpRequestKeyword = keyword;
              userKeyword = keyword;
              var action = document.getElementById("action").value;
              var oref = document.getElementById("objectref").value;
              if (oref != undefined) var action_2 = oref+"/";
              else var action_2 = "";
              
              if(keyword == "EMPTY" || keyword == "") {
                document.getElementById("tagresults").style.display = "none";
              } else if (keyword.length<3) {
                document.getElementById("tagresults").style.display = "block";
                var oSuggest = document.getElementById("tagresults");  
                oSuggest.innerHTML = "Vorschl&auml;ge gibt's erst ab 3 Buchstaben";
                
              } else {
                document.getElementById("tagresults").style.display = "none";          
                document.getElementById("status_circle").style.visibility='visible';
                xmlHttpGetSuggestions.open("GET", 
                                  "/"+action+"/tagsearch/"+action_2+"?query=" + encode(keyword), true);
                xmlHttpGetSuggestions.onreadystatechange = 
                                                  handleGettingSuggestions; 
                xmlHttpGetSuggestions.send(null);
              } // if-elseif-else
            } else { // if the XMLHttpRequest object is busy...
              // retain the keyword the user wanted             
              userKeyword = keyword;
              // clear any previous timeouts already set
              if(timeoutId != -1) {
                clearTimeout(timeoutId);
              } // if  
              // try again in 0.1 seconds     
              timeoutId = setTimeout("getSuggestions(userKeyword);", 100);
            } //if-else
          } catch(e) {
            displayError("Can't connect to server:\n" + e.toString());
          } // try-catch
        } // if
      } //if-else
    } // if
    return;
  } // function
  

/**
	* addToCache()
	* @purpose:  unction that adds to a keyword an array of values
	* @param   (string)  keyword
	* @param   (string)  string	
	* @return NULL
	*/	
  function addToCache(keyword, string)
  {
    oCache[keyword] = new Array();
    oCache[keyword] = string;
    return;
  }
  

/**
	* checkCache()
	* @purpose:  function that checks to see if a keyword is in the cache
	* @param   (string)  v
	* @return NULL
	*/	
  function checkCache(keyword)
  {
    if(oCache[keyword]) {
      return true;
    } else {
      return false;
    } // if-else
  } // function
  
  
/**
	* handleGettingSuggestions()
	* @purpose:  handles the server's response
	* @return NULL
	*/	
  function handleGettingSuggestions() 
  {
    //if the process is completed, decide what to do with the returned data
    if (xmlHttpGetSuggestions.readyState == 4) {
      // only if HTTP status is "OK"
      if (xmlHttpGetSuggestions.status == 200) { 
        try {
          // process the server's response
          updateSuggestions();
        } catch(e) {
          // display the error message
          displayError(e.toString()); 
        } // try-catch  
      } else {
        displayError("There was a problem retrieving the data:\n" + 
                     xmlHttpGetSuggestions.statusText);
      } // if-else     
    } // if
    return;
  } // function
  

/**
	* updateSuggestions()
	* @purpose:  function that updates the suggestions
	* @return NULL
	*/	
  function updateSuggestions() {
    var response = xmlHttpGetSuggestions.responseText;
  
    // check to see if other keywords are already being searched for
    if(httpRequestKeyword == userKeyword) {
      displayResults(httpRequestKeyword, response);
    } else {
      // we don't need to display the results since they are no longer useful
      addToCache(httpRequestKeyword, response);              
    } // if-else
    return;
  } // function
  

/**
	* displayResults()
	* @purpose:  populates the list with the current suggestions
	* @param   (string)  keyword
	* @param   (string)  resultString
	* @return NULL
	*/	
  function displayResults(keyword, resultString) {  
    // if the searched for keyword is not in the cache then add it to the cache
    if(!oCache[keyword] && keyword) {
      addToCache(keyword, resultString);
    } // if
    hasResults = true;
    position = -1;
    isKeyUpDownPressed = false;
    document.getElementById("tagresults").style.display = "block";
    document.getElementById("status_circle").style.visibility='hidden';
    var oSuggest = document.getElementById("tagresults");  
    oSuggest.innerHTML = resultString;
    return;
  } // function
  
  
/**
	* checkForChanges()
	* @purpose:  function that periodically checks to see if the typed keyword has changed
	* @return NULL
	*/	
  function checkForChanges() {
    // retrieve the keyword object
   
    var keyword = document.getElementById("tagname").value;
    // check to see if the keyword is empty
    
    if(keyword == "" && userKeyword != "") {
      // hide the suggestions
      getSuggestions("EMPTY");
      // reset the keywords 
      userKeyword="EMPTY";
      httpRequestKeyword="EMPTY";
    } // if
    // check to see if there are any changes
    if((userKeyword != keyword) && 
       (autocompletedKeyword != keyword)) {
      // update the suggestions
      userKeyword = keyword;
      getSuggestions(keyword);
    } // if
    return;
  } // function
  
  
/**
	* handleKeyUp()
	* @purpose:  function that handles the keys that are pressed
	* @param   (eventhandler)  e
	* @return NULL
	*/	
  function handleKeyUp(e) {
    // get the event
    e = (!e) ? window.event : e;
    // get the event's target
    target = (!e.target) ? e.srcElement : e.target;
    if (target.nodeType == 3) {
      target = target.parentNode;
    } // if
      // get the character code of the pressed button
      code = 
        (e.charCode) ? e.charCode :
        ((e.keyCode) ? e.keyCode :
        ((e.which) ? e.which : 0));
    // check to see if the event was keyup
    if (e.type == "keyup") {    
      checkForChanges();
      isKeyUpDownPressed =false; 
      // check to see we if are interested in the current character
      if ((code < 13 && code != 8) || 
          (code >=14 && code < 32) || 
          (code >= 33 && code <= 46 && code != 38 && code != 40) || 
          (code >= 112 && code <= 123)) 
      {
        // do nothing
      }
      else if(code == 13) {
        // enter key pressed
        if(position>=0) {
          document.getElementById("a" + position).onclick();
        } // if     
      } else if(code == 40){
        // if the down arrow is pressed we go to the next suggestion
        newTR=document.getElementById("tr"+(++position));
        oldTR=document.getElementById("tr"+(--position));
        
        // deselect the old selected suggestion   
        if(position>=0 && document.getElementById("tr"+(position+1)))
          oldTR.className = "";
 
        // select the new suggestion and update the keyword
        if(document.getElementById("tr"+(position+1)))
        {
          newTR.className = "highlightrow";
          position++;         
        }     
        e.cancelBubble = true;
        e.returnValue = false;
        isKeyUpDownPressed = true;        
      } else if(code == 38) {
        // if the up arrow is pressed we go to the previous suggestion
        newTR=document.getElementById("tr"+(--position));
        oldTR=document.getElementById("tr"+(++position));
        // deselect the old selected position
        if(position>=0 && document.getElementById("tr"+(position-1))) {       
          oldTR.className = "";
        } // if
        // select the new suggestion and update the keyword
        if(position > 0) {
          newTR.className = "highlightrow";
          position--;
          // scroll up if the current window is no longer valid
        } else {
          if(position == 0) {
            position--;
          } // if
          e.cancelBubble = true;
          e.returnValue = false;
          isKeyUpDownPressed = true;  
        } // if-else
      } // if-elseif-elseif-elseif
    } // if
    return;
  } // function
  

/**
	* handleOnMouseOver()
	* @purpose:  function that handles the mouse entering over a suggestion's area event
	* @param   (object)  oTr
	* @return NULL
	*/	
  function handleOnMouseOver(oTr) {
    deselectAll();  
    oTr.className = "highlightrow";  
    position = oTr.id.substring(2, oTr.id.length);
    return;
  } // function
  

/**
	* handleOnMouseOut()
	* @purpose:  function that handles the mouse exiting a suggestion's area event
	* @param   (object)  oTr
	* @return NULL
	*/	
  function handleOnMouseOut(oTr) {
    oTr.className = "";   
    position = -1;
    return;
  } // function
  

/**
	* deselectAll()
	* @purpose: function that removes the style from all suggestions
	* @return NULL
	*/	
  function deselectAll() { 
    for(i=0; i<suggestions; i++) {
      var oCrtTr = document.getElementById("tr" + i);
      oCrtTr.className = "";    
    } // for
    return;
  } // function
  
  
/**
	* encode()
	* @purpose:  function that escapes a string
	* @param   (string)  uri
	* @return NULL
	*/	
  function encode(uri) {
    if (encodeURIComponent) {
      return encodeURIComponent(uri);
    } // if
    if (escape) {
      return escape(uri);
    } // if
  } // function
  
  
/**
	* displayError()
	* @purpose:  function that displays an error message
	* @param   (string)  message
	* @return NULL
	*/	
  function displayError(message)
  {
    // display error message, with more technical details if debugMode is true
    /*alert("Error accessing the server! "+
          (debugMode ? "\n" + message : ""));*/
    alert("Error accessing the server! "+"\n" + message );
    return;
  } // function
  
  
/**
	* xmlToArray()
	* @purpose:  transforms all the children of an xml node into an array
	* @param   (object)  resultsXml
	* @return NULL
	*/	
  function xmlToArray(resultsXml) {
    // initiate the resultsArray
    var resultArray = new Array();  
    // loop through all the xml nodes retrieving the content  
    for(i=0;i<resultsXml.length;i++) {
      resultArray[i] = new Array();
      for(j=0;j<resultsXml.item(i).childNodes.length;j++) {
        node_name = resultsXml.item(i).childNodes[j].nodeName;
        node_value = resultsXml.item(i).childNodes[j].getAttribute('value');
        resultArray[i][node_name] = node_value;
      } // for
    } // for
    
    // return the node's content as an array
    return resultArray;
  } // function
  
