/* Kodierung: UTF-8 - Zeilenende: Windows */


function SortableTable(table)
{
  this.created=false;

  if (!table || table.tagName!='TABLE' || !table.rows || table.rows.length==0 || getClassName(table)!='table') return;

  var thisObj=this; /* in privaten Methoden anstelle von "this" verwenden, da dort kein Zugriff auf "this" möglich ist */

  this.Zeilen=table.tBodies[0].rows;

  if (table.tHead && table.tHead.rows.length>0)
  {
    this.Spalten=table.tHead.rows[0].cells;
    this.startZeile=0;
  }
  else
  {
    this.Spalten=this.Zeilen[0].cells;
    this.startZeile=1; /* wenn kein <thead> vorhanden, wird die erste Zeile immer als Tabellenkopf interpretiert und niemals mitsortiert */
  }

  this.lastSortedCol=-1; /* Index der zuletzt sortierten Salte */

/* ---------------------------------------- Start- und Endzeile berechnen ------------------------------------ */

  this.endZeile=this.Zeilen.length;

  for(var x=this.startZeile; x<this.Zeilen.length; x++)
  switch (getClassName(this.Zeilen[x]))
  {
    case 'next'   : this.startZeile=x+1; break;
    case 'before' : this.endZeile=x;     break;
  }

  if (this.endZeile-this.startZeile<2) return; /* Tabelle hat nur eine oder keine Zeile */

/* ----------------------------------------- Spaltenköpfe initialisieren ------------------------------------- */

  var MSIE  = navigator.userAgent.indexOf('MSIE')>-1;
  var MSIE6 = MSIE && parseInt(navigator.userAgent.substr(navigator.userAgent.indexOf('MSIE')+5), 10)==6;

  for (var y=0; y<this.Spalten.length; y++)
  {
    var Spalte=this.Spalten[y];

    Spalte.typ=getType(Spalte);
    if (Spalte.typ!=0)
    {
      if (this.endZeile-this.startZeile>100)
      {
        Spalte.msgwin=createSortMsg();
        Spalte.appendChild(Spalte.msgwin);
      }
      Spalte.Nr=y;
      Spalte.sortUp=false;
      Spalte.lastSortUp=true;
      addClassName(Spalte, 'sortable_column');
      Spalte.title='Sortieren';

      Spalte.onclick=function() { thisObj.sort(this); }

      if (MSIE)
      {
        Spalte.onmousedown=function() { addClassName(this, 'active'); }
        Spalte.onmouseup  =function() { delClassName(this, 'active'); }
      }
      if (MSIE6)
      {
        Spalte.onmouseover=function() { addClassName(this, 'hover'); }
        Spalte.onmouseout =function() { delClassName(this, 'hover'); }
      }
    }
  }

/* ------------------------------------------ Öffentliche Methoden ------------------------------------------- */

  this.sort=function(Spalte, sortUp)
  {
    if (Spalte.typ==0) return; /* falls sort() von externen Sortierungslinks oder zwecks Sofortsortierung direkt aufgerufen wird */
    if (sortUp!=null) Spalte.sortUp=sortUp; /* "  " */

    var spaltenNr=Spalte.Nr;

    if (this.lastSortedCol==spaltenNr)
    {
      if (sortUp==null || sortUp!=Spalte.lastSortUp)
      {
        for (var x=this.startZeile, i=this.endZeile-1; x<i; x++, i--)
        {
          var tx=this.Zeilen[x].cloneNode(true);
          var tp=this.Zeilen[i].cloneNode(true);
          table.tBodies[0].replaceChild(tp, this.Zeilen[x]);
          table.tBodies[0].replaceChild(tx, this.Zeilen[i]);
        }
        Spalte.lastSortUp=Spalte.sortUp;
        Spalte.sortUp=!Spalte.sortUp;
      }
      return;
    }

    this.lastSortedCol=spaltenNr;

    switch(Spalte.typ)
    {
      case 1  : var getCell=getStringCell;   break;
      case 2  : var getCell=getDateCell;     break;
      case 3  : var getCell=getTimeCell;     break;
      case 4  : var getCell=getDateTimeCell; break;
      case 5  : var getCell=getLinkCell;     break;
      case 6  : var getCell=getImageCell;    break;
      case 7  : var getCell=getNumberCell;   break;
      default : var getCell=getStringCell;   break;
    }
  
    for (var x=this.startZeile; x<this.endZeile-1; x++)
    {
      var t1=getCell(this.Zeilen[x].cells[spaltenNr]);
      var p=x;

      for (var i=x+1; i<this.endZeile; i++)
      {
        var t2=getCell(this.Zeilen[i].cells[spaltenNr]);
        if (Spalte.sortUp ? t1 < t2 : t1 > t2)
        {
          t1=t2;
          p=i;
        }
      }
      if (p>x)
      {
        var tx=this.Zeilen[x].cloneNode(true);
        var tp=this.Zeilen[p].cloneNode(true);
        table.tBodies[0].replaceChild(tp, this.Zeilen[x]);
        table.tBodies[0].replaceChild(tx, this.Zeilen[p]);
      }
    }

    Spalte.lastSortUp=Spalte.sortUp;
    Spalte.sortUp=!Spalte.sortUp;
  }
  /* die eigentliche Sortiermethode */


  this.createSortLink=function(linkText, spaltenNr, sortUp)
  {
    if (!this.created || this.Spalten[spaltenNr].typ==0) return null;
    var sortlink=document.createElement('a');
    sortlink.sortObj=this;
    sortlink.href='javascript:void(0)';
    sortlink.onclick=function() { this.sortObj.sort(this.sortObj.Spalten[spaltenNr], sortUp); }
    sortlink.innerHTML=linkText;
    return sortlink;
  }
  /* Zum Erzeugen von externen Sortierungslinks - Beispielcode siehe unten */

/* ------------------------------------------- Interne Methoden ---------------------------------------------- */

  function addClassName(element, className)
  {
    if (element.className=='')
    {
      element.className=className;
    }
    else
    {
      var classes=element.className.split(' ');
      classes.push(className);
      element.className=classes.join(' ');
    }
  }


  function delClassName(element, className)
  {
    if (!className || className=='') return;
  
    var classes=element.className.toLowerCase();
    className=className.toLowerCase();
    if (classes==className)
    {
      element.className='';
      return;
    }
  
    var classes=classes.split(' ');

    for (var x=classes.length-1; x>=0; x--)
    if (classes[x]==className)
    classes.splice(x, 1);

    element.className=classes.join(' ');
  }


  function getClassName(element)
  {
    var className=element.className;
    if (!className || className=='') return '';

    className=className.toLowerCase().split(' ');
    for (var x=0; x<className.length; x++)
    {
      var cn=className[x].split('_');
      if (cn[0]=='sortable' && cn.length==2) return cn[1];
    }
    return '';
  }
  /* Gibt das "Suffix" des Klassennamens von "element" zurück, wenn dieser mit "sortable_" beginnt - ansonsten 
     einen Leerstring. Das "class"-Attribut darf weitere Klassennamen enthalten */


  function getType(Spalte)
  {
    switch(getClassName(Spalte))
    {
      case 'string'   : return 1;
      case 'date'     : return 2;
      case 'time'     : return 3;
      case 'datetime' : return 4;
      case 'link'     : return 5;
      case 'image'    : return 6;
      case 'number'   : return 7;
      case 'none'     : return 0;
      default         : return 1; /* keine oder ungültige Angabe: 'string' */
    }
  }


  function parseDate(wert)
  {
    var date=wert.split('.');
    if (date.length!=3 || date[2].length==3) return wert; /* date[2].length==3, falls mal Tausenderpunkte in Zahlen erlaubt sein sollen */
    date[2]=parseInt(date[2], 10);
    if (date[2]<70) date[2]+=2000; else if (date[2]<100) date[2]+=1900;
    date=new Date(date[2], date[1]-1, date[0]);
    return String(Math.round(Date.parse(String(date))/86400000)); /* Tage seit dem 1.1.1970 */
  }
  /* identische Funktion wird auch in tablecalculator verwendet.
     Uhrzeit (5.2.2008 - 14:59) wird nicht berücksichtigt */


  function parseTime(wert)
  {
    var time=wert.split(':');
    if (time.length>3 || time.length<2) return wert;
    return time[0]*60*60+time[1]*60+(time.length==3 ? time[2] : 0); /* Sekunden seit Mitternacht */
  }
  /* Uhrzeit im Format 16:19 oder 16:19:21 */


  function parseDateTime(wert)
  {
    var datetime=wert.replace(/ /, '').split('-');
    if (datetime.length==1) return wert;
    return parseDate(datetime[0])*86400+parseTime(datetime[1]); /* Sekunden seit dem 1.1.1970 */
  }


  function parseImage(cell)
  {
    var img=cell.getElementsByTagName('img');
    if (!img || img.length==0) return '-';
    var path=img[0].src.split('/');
    return path.pop().toLowerCase();
  }
  /* gibt den Dateinamen der Grafik zurück */


  function parseNumber(wert)
  {
    var num=wert.replace(/\./g, ''); /* Tausender-Punkte entfernen */
    num=num.replace(',', '.'); /* Komma in engl. Punkt verwandeln */
    num=num.indexOf('.')>-1 ? parseFloat(num) : parseInt(num, 10);
    if (!num) num=0;
    return num;
  }


  function parseLink(cell)
  {
    var link=cell.getElementsByTagName('a');
    if (!link || link.length==0) return '-';
    return link[0].innerHTML.toLowerCase();
  }


  /* Callback-Funktionen für this.sort() */

  function getStringCell(cell)
  {
    if (!cell.sortValue) cell.sortValue=cell.innerHTML.toLowerCase();
    return cell.sortValue;
  }

  function getDateCell(cell)
  {
    if (!cell.sortValue) cell.sortValue=parseDate(cell.innerHTML);
    return cell.sortValue;
  }

  function getTimeCell(cell)
  {
    if (!cell.sortValue) cell.sortValue=parseTime(cell.innerHTML);
    return cell.sortValue;
  }

  function getDateTimeCell(cell)
  {
    if (!cell.sortValue) cell.sortValue=parseDateTime(cell.innerHTML);
    return cell.sortValue;
  }

  function getLinkCell(cell)
  {
    if (!cell.sortValue) cell.sortValue=parseLink(cell);
    return cell.sortValue;
  }

  function getImageCell(cell)
  {
    if (!cell.sortValue) cell.sortValue=parseImage(cell);
    return cell.sortValue;
  }

  function getNumberCell(cell)
  {
    if (!cell.sortValue) cell.sortValue=parseNumber(cell.innerHTML);
    return cell.sortValue;
  }


  function createSortMsg()
  {
    var msgwin=document.createElement('div');
    msgwin.className='sortable_sortmsg';
    msgwin.innerHTML='Sortiere ...';
    return msgwin;
  }
  /* wird während längerer Sortiervorgänge im Kopf der gerade sortierten Spalte angezeigt */

  this.created=true;
}


function makeTablesSortable()
{
  if (!document.getElementById || !Array.prototype.push) return; /* Netscape 4 und MSIE Mac ausschließen */

  var tables=document.getElementsByTagName('table');
  if (!tables) return;

  for (var x=0; x<tables.length; x++)
  tables[x].sortObj=new SortableTable(tables[x]);

/* // Erzeugen von externen Sortierungslinks:
  var body=document.getElementsByTagName('body')[0];
  with (tables[0])
  {
    if (!sortObj.created) return;
    for (x=0; x<sortObj.Spalten.length; x++)
    {
      var sortLink=sortObj.createSortLink('Größtes: '+sortObj.Spalten[x].innerHTML+' | ', x, true);
      if (sortLink) body.appendChild(sortLink);
      var sortLink=sortObj.createSortLink('Kleinstes: '+sortObj.Spalten[x].innerHTML+'<br>', x, false);
      if (sortLink) body.appendChild(sortLink);
    }
  }
  var spacer=document.createElement('div');
  spacer.style.height='60px';
  body.appendChild(spacer);
*/

/* // Tabelle sofort nach dem Laden sortieren:
  tables[0].sortObj.sort(tables[0].sortObj.Spalten[3], false);
*/
}

makeTablesSortable();

/*
Anwendung:
----------
Diese js-Datei muss unterhalb der letzten Tabelle oder ganz am Ende (vor dem schließenden </body>-Tag) mit

<script type="text/javascript" charset="utf-8" src="tablesorter.js"></script>

eingebunden werden. (Alternativ dazu könnte die Datei auch im <head>-Bereich eingebunden werden und 
window.onload=makeTablesSortable anstatt makeTablesSortable() aufgerufen werden.)

Bei allen Tabellen, die sortierbar gemacht werden sollen, muss wenigstens die Klasse "sortable_table" 
im <table>-Tag gesetzt sein (<table class="sortable_table">); weitere Klassen dürfen gesetzt sein.

In einer so gekennzeichneten Tabelle werden folgende Zeilen sortiert:
  * wenn die Tabelle kein <thead>-Element enthält, werden alle außer die oberste Zeile in die Sortierung mit einbezogen
  * wenn die Tabelle ein <thead>-Element enthält, werden alle außer die im <thead> enthaltenen Zeilen sortiert
  * wenn die Tabelle ein <tfoot>-Element enthält, werden die darin enthaltenen Zeilen nicht mitsortiert

Wenn die Tabelle mehr als ein <tbody>-Element enthält, werden nur die im ersten <tbody> enthaltenen Zeilen sortiert
(wenn die Tabelle kein <tbody>-Element enthält, erzeugen Browser ein "unsichtbares" tbody-Element).

Sollen außer der ersten Zeile (bzw. außer den im <thead>/<tfoot> enthaltenen Zeilen) weitere Zeilen von der Sortierung 
ausgeschlossen werden, muss die letzte noch nicht mitzusortierende Zeile im <tr>-Tag die Klasse "sortable_next" 
enthalten (<tr class="sortable_next">) und die erste nicht mehr mitzusortierende Zeile die Klasse "sortable_before" 
(<tr class="sortable_before">).

Standardmäßig wird die Sortierreihenfolge wird durch reine String-Vergleiche ermittelt. Soll eine Spalte nicht mittels 
String-Vergleich sortiert werden, muss im entsprechenden <td>- bzw. <th>-Tag der obersten Zeile eine der folgenden 
Klassen gesetzt werden:

sortable_string   : Sortierung nach String (default)
sortable_date     : Sortierung nach Datum (Datumsformat: 12.6.08, 12.06.2008 o.ä.)
sortable_time     : Sortierung nach Uhrzeit (Zeitformat: 18:31 oder 18:31:51)
sortable_datetime : Sortierung nach Datum-Uhrzeit (Format: 9.5.08 - 19:10 oder 09.05.2008-19:10:23)
sortable_link     : Sortierung nach Textinhalt von Links (<td><a ...>Textinhalt</a></td>)
sortable_image    : Sortierung nach Dateinamen der Grafik
sortable_number   : Sortierung nach Integer oder Float
sortable_none     : Spalte soll nicht sortierbar sein

Ist der Klassenname ungültig oder nicht verhanden, wird String-Sortierung angewendet.

Beim Initialisieren wird in die oberste Zelle jeder sortierbaren Spalte die Klasse "sortable_column" hinzugefügt.
In einer CSS-Datei können dann die hover-Farben usw. festgelegt werden, sowie das Verhalten des "Sortiere..."-Labels.
Beipiel: siehe admin/style.css

*/
