/*
 Suggest-Suche
 (c) Felix Wirtz, bierschinken.net
 Holt Array (JSON-Format) von Webserver (Format "key":"value","key2":"value2" ) und stellt ihn in Liste unter Input-Feld dar
 Navigation in der Liste moeglich
 Initialisierung mit "elementname = new autocomplete(input,div,file,line,lineprefix)"
 Parameter: input      - ID des Input-Feldes
			div        - ID des Objektes, in das die Liste geschrieben wird
			file       - Datei, aus der die Liste geladen wird
					     Variable %searchvalue% fuer Inhalt des Input-Feldes
			line  	   - Ausgabezeile eines Array-Elementes
						 Variable %index% fuer Index im Array
						 Variable %key%   fuer den Schluessel
						 Variable %value% fuer den Wert
						 Fuer die Navigation sollte die Zeile in einem HTML-Objekt mit id="elementname.lineprefix+%index%", title="$value%" und onmouseover="dd_select(os,%index%) stehen
			lineprefix - Name der Listenelemente
 
 Beispiel fuer Input/Div-Feld in HTML:

Initialisieren mit JavaScript:
>>    var elementname = new autocomplete("suche","suchenliste","index.php?ajax=%searchvalue%","<div id=\"e_%index%\" title=\"%value%\" onmouseover=\"dd_select(os,%index%)\">%value%</div>","e_");
HTML-Aufbau:
>>    <input type="input" name="suche" style="width:200px" value="" id="id_suche" onkeydown="return keydown(elementname,event)" onkeyup="return keyup(elementname,event);" autocomplete="off">
>>    <br><span id="suchenliste" style="position:absolute;width:200px;border: thin dotted Black;visibility: hidden;max-height: 150px;overflow : auto;"></span>
 
 Optional kann elementname.jumpelement gesetzt werden, dann wird nach Druecken der Enter-Taste zum angegebenen Element gesprungen, statt das Formular auszuloesen
 Optional kann elementname.listmax gesetzt werden fuer die maximale Anzahl von Elementen in der Liste - von der Serverdatei muss aber auch diese Anzahl zurueckgegeben werden. Standard 30
 Optional kann elementname.classname_normal gesetzt werden fuer die CSS-Klasse von Elementen in der Liste. Standard dd_normal
 Optional kann elementname.classname_selected gesetzt werden fuer die CSS-Klasse von ausgewaehlten Elementen in der Liste. Standard dd_selected
 Optional kann elementname.directlink gesetzt werden, dann wird nach Druecken der Enter-Taste und bei aus der Liste gewaehltem Element der angegebene Link aufgerufen. Variable %searchvalue% fuer Inhalt des Input-Feldes
 
 Um die Liste zu schliessen, falls neben die Liste geklickt wird:
 
>>    function hide(ev) {
>>     document.getElementById(elementname.div).style.visibility = 'hidden'; 
>>    }
>>    document.onmouseup = hide;

*/

 function livesearch(ac) { //Wird nach Buchstabeneingabe in Input-Feld ausgeloest, prueft ob die Datei vom Webserver neu geladen werden muss
  var res = '';
  ac.searchvalue = document.getElementById(ac.input).value.toLowerCase();
  if (ac.searchvalue.length > 0)
   if((ac.searchvalue.indexOf(ac.svalue)!=-1) && (ac.svalue.length > 0) && (count(ac.dataarray) < ac.listmax))
    setoldvalue(ac); //Ergebnisse sind in altem Array enthalten, aktualisiere diesen
   else {
    ac.svalue = ac.searchvalue;
    loadgetdata(ac,ac.file.replace(/%searchvalue%/g,ac.searchvalue)); //Daten neu vom Server laden
   }
  else setorhide(ac,res); //Liste verstecken, falls sie leer ist
 }

 function setvalue(ac,data) { //Daten vom Server in Array umwandeln und in Liste einfuegen
  var res = '';
  ac.lineindex = 0;
  if (data.length > 4) {
   ac.dataarray = eval('(' + data + ')'); //Daten aus JSON umwandeln
   for (var entry in ac.dataarray)
    if (entry != 0) res = res + setline(ac,entry); //in Liste einfuegen
  }
  else res = '';
  setorhide(ac,res); //Liste verstecken, falls sie leer ist
 }
 
 function setoldvalue(ac) { //Alte Daten vom Server aktualisieren
  var res = '';
  ac.lineindex = 0;
  for (var entry in ac.dataarray) {
    if ((ac.dataarray[entry].toLowerCase().indexOf(ac.searchvalue)!=-1)) //Nur die Daten in die Liste schreiben, die auf neue Suchanfrage zutreffen
      res = res + setline(ac,entry);
  }
  setorhide(ac,res); //Liste verstecken, falls sie leer ist
 } 
 
 function setline(ac,key) { //Zeile in Liste mit den o.a. Parametern formatieren
  ac.lineindex = ac.lineindex+1;
  var b = ac.line.replace(/%key%/g,key);
  b = b.replace(/%index%/g,ac.lineindex);
  return b.replace(/%value%/g,ac.dataarray[key]);
 }
 

 
 function setorhide(ac,res) { //Liste verstecken, falls sie leer ist
  document.getElementById(ac.div).innerHTML = res;
  if (res.length > 2) document.getElementById(ac.div).style.visibility = 'visible';
  else document.getElementById(ac.div).style.visibility = 'hidden'; 
 }

 function loadgetdata(ac,file) { //Daten vom Server laden
  if (ac.xmlHttp) {
   if (ac.loading) {
    ac.xmlHttp.onreadystatechange = function () {}
    ac.xmlHttp.abort();
   }
   ac.xmlHttp.open('GET', file, true);
   ac.loading = true;
   ac.xmlHttp.onreadystatechange = function () {
         if (ac.xmlHttp.readyState == 4) {
		   setvalue(ac,ac.xmlHttp.responseText);
		   ac.loading = false;
		  }
		 else setorhide(ac,'');
   };
   ac.xmlHttp.send(null);
  }
 }
 
function keydown(ac,e) { //Navigation in Liste mit Taste-Runter/Taste-Rauf und Enter
 if(window.event) // IE
  keynum = e.keyCode;
 else if(e.which) // Netscape/Firefox/Opera
  keynum = e.which;

 if (keynum == 38) { //Taste runter, ein Element weiter oben auswaehlen
  dd_select(ac,ac.selected-1);
  return false;
 }
 else if (keynum == 40) { //Taste rauf, ein Element weiter oben auswaehlen
  dd_select(ac,ac.selected+1);
  return false;
 }
 else if ((keynum == 13) && (document.getElementById(ac.div).style.visibility == 'visible')) { //Enter: Falls Liste offen, diese verstecken
  document.getElementById(ac.div).style.visibility == 'hidden';
  return false;
 }
 else if ((keynum == 13) && (ac.jumpelement)) { //Enter: Falls jumpelement angegeben, zu diesem springen
  document.getElementById(ac.jumpelement).focus();
  return false;
 }
 else if (keynum == 13) {
  if ((!ac.setmanual)&&(ac.directlink != false)) {
   location.href = ac.directlink.replace(/%searchvalue%/g,document.getElementById(ac.input).value);
   return false;
  }
 }
 else if ((keynum == 9) && (document.getElementById(ac.div).style.visibility == 'visible')) { //Enter: Falls Liste offen, diese verstecken
  //document.getElementById(ac.div).style.visibility == 'hidden';
  setorhide(ac,'');
  if (ac.jumpelement) {
   document.getElementById(ac.jumpelement).focus();
   return false;
  }
 }
 else ac.setmanual = true;
}

function keyup(ac,e) { //Suche ausloesen
 if(window.event) // IE
  keynum = e.keyCode;
 else if(e.which) // Netscape/Firefox/Opera
  keynum = e.which;

 if ((keynum == 38)||(keynum == 40)) //Taste-Runter/Taste-Rauf unterdruecken
  return false;
 else if (keynum == 13) {//Enter: Liste verstecken
  document.getElementById(ac.div).style.visibility = 'hidden'; 
  ac.onenter(ac,document.getElementById(ac.lineprefix+ac.selected));
  return false; 
 }
 else {
  ac.setmanual = true;
  livesearch(ac); //Suche ausloesen
  ac.selected = 0;
 }
}


function dd_select(ac,index) { //Element in Liste selektieren
if ((index != ac.selected)&&(elementExists(ac.lineprefix+index))) { //Nur wenn Element existiert
  e_suchen = document.getElementById(ac.div); //Liste
  e_a = document.getElementById(ac.lineprefix+index); //Element
  e_a.className = ac.classname_selected; //Element selektieren
  document.getElementById(ac.input).value = e_a.title; //Text in Input-Feld setzen
  if (e_a.offsetTop > e_suchen.scrollTop+(e_suchen.offsetHeight-e_a.offsetHeight))//Pruefen, ob in Liste gescrollt werden muss, um das ausgewaehlte Element anzuzeigen
   e_suchen.scrollTop = e_suchen.scrollTop+e_a.offsetHeight;
  else if (e_a.offsetTop < e_suchen.scrollTop)
   e_suchen.scrollTop = e_a.offsetTop;
  if (elementExists(ac.lineprefix+ac.selected)) //Vormals selektiertes Element deselektieren
   document.getElementById(ac.lineprefix+ac.selected).className = ac.classname_normal;
  ac.selected = index;
  ac.setmanual = false;
  ac.onselect(ac,document.getElementById(ac.lineprefix+ac.selected));
 }
}

function autocomplete(input,div,file,line,lineprefix) { //Konstruktor
 this.input = input; //ID des Input-Feldes
 this.div = div; //ID des Feldes fuer die Liste
 this.file = file; //Server-Dateiname, Variable %searchvalue%
 this.line = line; //Formatierung der Listenelemente, Variablen %index%, %value%, %key%
 this.lineprefix = lineprefix; //Prefix fuer ID der Listenelemente
 this.searchvalue = ''; //aktueller Suchtext
 this.svalue = ''; //Suchtext, fuer die Array geladen ist
 this.dataarray = new Array(); //geladener Array vom Server
 this.lineindex = 0; //aktueller Index von Zeilen, die in Liste eingefuegt werden
 this.selected = 0; //Index des selektierten Listenelements
 this.jumpelement = false; //Optional: Auf ID des Elementes setzen, zu dem bei Enter gesprungen werden soll
 this.listmax = 30; //Maximale Anzahl von Zeilen, die die Server-Datei zurueckgibt
 this.classname_selected = 'dd_selected'; //CSS-Klasse von selektiertem Listenelement
 this.classname_normal = 'dd_normal'; //CSS-Klasse von nicht selektirtem Listenelement
 this.setmanual = true; //Gibt an, ob aktueller Eintrag in Input-Formular manuell ueber Tastatur eingegeben wurde (true) oder aus Liste ausgewaehlt (false)
 this.directlink = false; //Optional: Wurde der Eintrag im Input-Formular ueber die Liste ausgewaehlt, wird nach Druck der Enter-Taste zu diesem Link gesprungen (Variable %searchvalue%)
 this.loading = false; //Gibt an, ob gerade Daten geladen werden;
 this.onselect = function (ac,selected) {} //Optional: Funktion, die aufgerufen wird, wenn Eintrag ausgewaehlt wird
 this.onenter = function(ac,selected) {}
 // xmlhttp-Klasse fuer IE erstellen
 try { this.xmlHttp  = new ActiveXObject("Msxml2.XMLHTTP"); }
 catch(e) {
  try { this.xmlHttp  = new ActiveXObject("Microsoft.XMLHTTP"); }
  catch(e) { this.xmlHttp  = false; }
 }
 // xmlhttp-Klasse fuer sonstige Browser erstellen
 if (!this.xmlHttp  && typeof XMLHttpRequest != 'undefined')
 this.xmlHttp = new XMLHttpRequest();
}

function count(array) { //Elemente in Array zaehlen, in Gegensatz zu length auch fuer Hash-Arrays
 var res = 0;
 for (var entry in array)
  res = res + 1;
 return res;
}

function elementExists(ElementName) { //Prueft ob Element existiert
 return isNaN(document.getElementById(ElementName));
} 

/*
url - URL, von der die Infos geholt werden
*/
var loading = false;
function fetchinfo(file,todo) {
// xmlhttp-Klasse fuer IE erstellen
 var xmlHttp;
 try { xmlHttp  = new ActiveXObject("Msxml2.XMLHTTP"); }
 catch(e) {
  try { xmlHttp  = new ActiveXObject("Microsoft.XMLHTTP"); }
  catch(e) { xmlHttp  = false; }
 }
 // xmlhttp-Klasse fuer sonstige Browser erstellen
 if (!xmlHttp  && typeof XMLHttpRequest != 'undefined')
 xmlHttp = new XMLHttpRequest();
 
 if (xmlHttp) {
   if (loading) {
    xmlHttp.onreadystatechange = function () {}
    xmlHttp.abort();
   }
   xmlHttp.open('GET', file, true);
   loading = true;
   xmlHttp.onreadystatechange = function () {
         if (xmlHttp.readyState == 4) {
		   todo(xmlHttp.responseText);
		   loading = false;
		  }
   };
   xmlHttp.send(null);
  }
  
}

var thediv="";
function setdiv(file,div) {
 document.getElementById(div).innerHTML = "<img src=\"/buttons/loader.gif\" border=0 alt=\"lade...\">";
 thediv = div;
 fetchinfo(file,setdiv2);
}

function setdiv2(text) {
 document.getElementById(thediv).innerHTML = text;
}

var ljsf;
var schonEingebunden = ""; // Speichert die bereits eingebundenen Scripts
function loadJSFile(filename,einmaligEinbinden) {
         if (einmaligEinbinden) {
            // schon einbebunden?
            if (schonEingebunden.indexOf("|"+filename+"|")!=-1) return true;
            // Nein, in die Liste damit
            schonEingebunden = schonEingebunden + "|" + filename + "|";
         }

        // Instanz einmalig erstellen
        if (ljsf == null) ljsf = (navigator.userAgent.indexOf("MSIE")+1)?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest;
        
        ljsf.open('get',filename,true);
        ljsf.setRequestHeader("Connection","close");
        ljsf.onreadystatechange = function() {
                if(ljsf.readyState == 4){
                        eval(ljsf.responseText);        
                }
        }
        ljsf.send(null);
}
