
/*
 * sortiereTabelleAPI.js
 * @date 07.10.2005
 * @author Jeremias R&ouml;&szlig;ler, roesslerj@web.de
 * @version 1.2
 */
 
/**
 * Diese API macht HTML-Tabellen sortierbar.<br />
 *
 * Die Tabelle wird einfach in ihrer normalen Gestalt im HTML aufgebaut (mit THEAD und TBODY).
 * Vorteile:
 * <ul>
 * <li>Tabellen sind mit HTML-Verwaltungssoftware erstell- und wartbar</li>
 * <li>ohne JavaScipt lesbar</li>
 * <li>dadurch barrierefrei</li>
 * <li>Daten sind einfach zu handhaben</li>
 * <li>nutzbar auch ohne JavaScript-Kenntnisse</li>
 * <li>auch generierte (Bsp.: JSP, PHP) Tabellen problemlos sortierbar</li>
 * </ul>
 *
 * Ein Beispiel einer Nutzung:
 * <table id="tabelle">
 *   <thead>
 *     <tr><th>Geburtsdatum</th><th>Name</th><th>Alter</th></tr>
 *     <tr>
 *       <td>
 *         <a href="javascript:sortiereTabelleNach('tabelle', 0, 'nachDatum', 'aufsteigend')">Aufsteigend</a>
 *         <a href="javascript:sortiereTabelleNach('tabelle', 0, 'nachDatum', 'absteigend')">Absteigend</a>
 *       </td>
 *       <td>
 *         <a href="javascript:sortiereTabelleNach('tabelle', 1, 'alphabetisch', 'aufsteigend')">Aufsteigend</a>
 *         <a href="javascript:sortiereTabelleNach('tabelle', 1, 'alphabetisch', 'absteigend')">Absteigend</a>
 *       </td>
 *       <td>
 *         <a href="javascript:sortiereTabelleNach('tabelle', 2, 'alphabetisch', 'aufsteigend')">Aufsteigend</a>
 *         <a href="javascript:sortiereTabelleNach('tabelle', 2, 'alphabetisch', 'absteigend')">Absteigend</a>
 *       </td>
 *     </tr>
 *   </thead>
 *   <tbody>
 *     <tr><td>20.01.1935</td><td>Mueller</td><td>70 Jahre</td></tr>
 *     <tr><td>21.02.1925</td><td>Meier</td><td>80 Jahre</td></tr>
 *     <tr><td>20.01.</td><td>Schulz</td><td>68 Jahre</td></tr>
 *   </tbody>
 * </table>
 */
 
/**
 * Sortiert die Tabelle tabellenId der Spalte spaltenNr nach.
 * Die Tabelle muss mit <thead> und <tbody> (optional <tfooter>) aufgebaut sein.
 * Dabei muss angegeben werden, ob es sich um eine Text, Datums oder Zahlenspalte handelt:
 * "alphabetisch", "nachDatum", "numerisch".
 * Außerdem muss die Richtung angegeben werden: "aufsteigend", "absteigend".
 * @param tabellenId ID einer Tabelle auf der Seite.
 * @param spaltenNr Nr der Spalte, nach welcher sortiert werden soll. Beginnend bei links, zählend von 0.
 * @param spaltenArt Art des Inhaltes der Spalte. Unterstützte Werte: "alphabetisch", "nachDatum", "numerisch".
 * @param richtung Richtung in die sortiert werden soll. Unterstützte Werte: "aufsteigend", "absteigend".
 */
function sortiereTabelleNach(tabellenId, spalteNr, spaltenArt, richtung){
    var tBody = getTBody(document.getElementById(tabellenId));
    var tData = zerlegeTabelle(tBody);
    tData = sortiereNachSpalte(tData, spalteNr, spaltenArt, richtung);
    baueTabelle(tBody, tData);
}

/**
 * Liefert das Kindelement TBODY der übergebenen Tabelle zurück.
 * @param tabelle Ein HTML-Tabellenelement.
 * @return Der TBODY der Tabelle.
 */
function getTBody(tabelle){
	var tBody = null;
  for(var kdx = 0; kdx < tabelle.childNodes.length; kdx++){
    if(tabelle.childNodes[kdx].nodeName == "tbody" || tabelle.childNodes[kdx].nodeName == "TBODY"){
      tBody = tabelle.childNodes[kdx];
    }
  }
  return tBody;
}

/**
 * Erwartet das TBODY Kindelement einer Tabelle.
 * Dieser wird zerlegt und in eine Matrix, also einen verschachtelten Array gespeichert nach dem Muster:
 * Element[Spalte][Zeile].
 * @param Das TBODY Kindelement einer Tabelle. Dieser wird dabei geleert!
 * @return Eine verschatelter Array der enthaltenen Daten in Ihren TD-Knoten.
 */
function zerlegeTabelle(tBody){
  var tData = new Array();
  var node = null;
  var zeilenCounter = 0;
  var spaltenCounter = 0;
  for(var idx = 0; idx < tBody.childNodes.length; idx++){
    if(tBody.childNodes[idx].nodeName == "tr" || tBody.childNodes[idx].nodeName == "TR"){
      spaltenCounter = 0;
      for(var jdx = 0; jdx < tBody.childNodes[idx].childNodes.length; jdx++){
        node = tBody.childNodes[idx].childNodes[jdx];
        if(node.nodeName == "td" || node.nodeName == "TD"){
	        if(tData[spaltenCounter] == null){
				    tData[spaltenCounter] = new Array();
		      }
          tData[spaltenCounter][zeilenCounter] = tBody.childNodes[idx].removeChild(node);
          spaltenCounter++;
          jdx--;
        }
      }
    zeilenCounter++;
    }
  }
  return tData;
}

/**
 * Erwartet ein bestehendes TBODY-Gerüst aus TR-Nodes passender Länge!
 * Baut dann die Daten aus tData in dieses Gerüst ein.
 * @param tBody Ein bestehendes Node-Gerüst passender Länge und Breite,
 *        in welches die Daten eingearbeitet werden.
 * @param tData Ist ein verschachtelter Array der Art Element[Spalte][Zeile] mit TD-Knoten.
 */
function baueTabelle(tBody, tData){
  var node;
  var zeilenCounter = 0;
  for(var idx = 0; idx < tBody.childNodes.length; idx++){
    if(tBody.childNodes[idx].nodeName == "tr" || tBody.childNodes[idx].nodeName == "TR"){
      for(var jdx = 0; jdx < tData.length; jdx++){
        tBody.childNodes[idx].appendChild(tData[jdx][zeilenCounter]);
      }
    zeilenCounter++;
    }
  }
}

/**
 * Zieht alle Daten (Text) aus einem Knoten, und hängt sie zusammen.
 * @param node Beliebiger HTML-Element-Knoten, wie z.B. childNodes einer ist.
 * @return String der alle enthaltenen Daten aneinander hängt.
 */
function getNodeData(node){
  if(node.hasChildNodes){
		var result = null;
		var data;
		for(var idx = 0; idx < node.childNodes.length; idx++){
			if(node.childNodes[idx].data != null){
				data = node.childNodes[idx].data;
			}
			else{
				if(node.childNodes[idx].hasChildNodes){
					data = getNodeData(node.childNodes[idx]);
				}
	    }
	  	if(result == null || result == ""){
			  result = data;
	    }
	    else{
				result = result.concat(data);
			}
		}
	}
	return result;
}

/**
 * Sortiert die übergebene Matrix der Art Element[Spalte][Zeile]
 * nach der übergebenen Spalte entsprechend der Art und Richtung.
 * @param mData Die zu sortierenden Daten.
 * @param spaltenNr Die Nr der zu sortierenden Spalte in der mData-Matrix. Bei 0 beginnend.
 * @param art Die Art der Spalte, ausschlaggebend für die korrekte Sortierung.
 *        Möglich sind: "alphabetisch", "numerisch", "nachDatum".
 * @param richtung Die Richtung der Sortierung.
 *        Köglich sind: "aufsteigend", "absteigend".
 * @return Die übergebenen Daten in entsprechend sortierter Reihenfolge.
 */
function sortiereNachSpalte(mData, spaltenNr, art, richtung) {
  // sortiere relevante Spalte
  var sortierteSpalte = new Array();
  var sortierteDaten = new Array();
  for(var ldx = 0; ldx < mData[spaltenNr].length; ldx++){
    sortierteSpalte[ldx] = getNodeData(mData[spaltenNr][ldx]);
  }
  for(var mdx = 0; mdx < mData.length; mdx++){
      sortierteDaten[mdx] = new Array();
  }
  if(art == "alphabetisch"){
    sortierteSpalte.sort();
  }
  if(art == "numerisch"){
    sortierteSpalte.sort(numSort);
  }
  if(art == "nachDatum"){
    sortierteSpalte.sort(dateSort);
  }

  if(richtung == "absteigend"){
    sortierteSpalte.reverse();
  }

  // Sortiere restliche Matrix entsprechend Spalte
  var found;
  for(var kdx = 0; kdx < sortierteSpalte.length; kdx++){
    found = false;
    var idx = 0;
    while(idx < mData[spaltenNr].length && !found){
      if(getNodeData(mData[spaltenNr][idx]) == sortierteSpalte[kdx]){
        for(var jdx = 0; jdx < mData.length; jdx++){
          sortierteDaten[jdx][kdx] = mData[jdx][idx];
        }
        mData[spaltenNr][idx] = "found:" + idx;
        found = true;
      }
      idx++;
    }
  }
  return sortierteDaten;
}

/**
 * Sortiert zwei übergebene Zahlen.
 * Diese Funktion wird dem Sortier-Algorithmus übergeben.
 * @param a Der erste Vergleichsparameter.
 * @param b Der zweite Vergleichsparameter.
 * @return Ist der Rückgabewert größer als 0, so bedeutet das, der Parameter a hat einen höheren Index als der Parameter b.
 *   In der Sortierreihenfolge kommt damit b vor a.<br />
 *   Ist der Rückgabewert kleiner als 0, so bedeutet das, der Parameter a hat einen niedrigeren Index als der Parameter b.
 *   In der Sortierreihenfolge kommt damit a vor b. <br />
 *   Tritt der Rückgabewert 0 ein, so sind beide Elemente gleich und werden in ihrer Reihenfolge nicht verändert.
 */
function numSort(a, b){
  return a - b;
}

/**
 * Sortiert zwei übergebene Datumsangaben der Art TT.MM.[[JJ]JJ].
 * Diese Funktion wird dem Sortier-Algorithmus übergeben.
 * @param a Der erste Vergleichsparameter.
 * @param b Der zweite Vergleichsparameter.
 * @return Ist der Rückgabewert größer als 0, so bedeutet das, der Parameter a hat einen höheren Index als der Parameter b.
 *   In der Sortierreihenfolge kommt damit b vor a.<br />
 *   Ist der Rückgabewert kleiner als 0, so bedeutet das, der Parameter a hat einen niedrigeren Index als der Parameter b.
 *   In der Sortierreihenfolge kommt damit a vor b. <br />
 *   Tritt der Rückgabewert 0 ein, so sind beide Elemente gleich und werden in ihrer Reihenfolge nicht verändert.
 */
function dateSort(a, b){
  var result = 0;
  if(a == null || b == null){
    result = 0;
    if(a == null){
      result = +1;
    }
    if(b == null){
      result = -1;
    }
    if(a == null && b == null){
      result = 0;
    }
  }
  else{
    var dateA = a.split("+");
    var dateB = b.split("+");
    if(dateA.length > 0){
	  dateA = dateA[0];
    }
    if(dateB.length > 0){
      dateB = dateB[0];
    }
    dateA = dateA.split("-");
    dateB = dateB.split("-");
    if(dateA.length > 0){
	  dateA = dateA[0];
    }
    if(dateB.length > 0){
      dateB = dateB[0];
    }
    dateA = dateA.split(".");
    dateB = dateB.split(".");
    var daysA = parseInt(eval(dateA[0]));
    var daysB = parseInt(eval(dateB[0]));
    var monthsA = parseInt(eval(dateA[1]));
    var monthsB = parseInt(eval(dateB[1]));
    var yearsA = parseInt(eval(dateA[2]));
    var yearsB = parseInt(eval(dateB[2]));
    if(yearsA != null && !isNaN(yearsA) &&
		   yearsB != null && !isNaN(yearsB)){
      result = yearsA - yearsB;
    }
    if(result == 0){
      result = monthsA - monthsB;
    }
    if(result == 0){
      result = daysA - daysB;
    }
  }
  return result;
}


