/*

This module was originally written by: Andrew Berry
(found at http://javascript.internet.com Forms: Sort Data Table)
It was optimized and modified for browser independent use by: Eduard Gode (eduard@gode.de)

*/
function twoDig (n) { if ( n < 10 ) return '0'+n; return n; }
function setDataType(cValue) {
  // THIS FUNCTION CONVERTS DATES AND NUMBERS FOR PROPER
  // ARRAY SORTING WHEN IN THE SORT FUNCTION
  if ( cValue == null ) return '';
  if ( cValue.match( /^(\d{4})\-(\d{2})\-(\d{2})$/ ) ) cValue = RegExp.$2+'/'+RegExp.$3+'/'+RegExp.$1;
  if ( cValue.match( /^(\d{2})\.(\d{2})\.(\d{4})$/ ) ) cValue = RegExp.$2+'/'+RegExp.$1+'/'+RegExp.$3;
  if ( cValue.match( /^(\d{2})\.(\d{2})\.(\d{2})$/ ) ) cValue = RegExp.$2+'/'+RegExp.$1+'/'+RegExp.$3;
  if ( cValue.match( /^(\d+)\,(\d{3})\.(\d+)$/ )   ) cValue = RegExp.$1+RegExp.$2+'.'+RegExp.$3;
  if ( cValue.match( /^(\d+)\.(\d{3})\.(\d+)$/ )   ) cValue = RegExp.$1+RegExp.$2+'.'+RegExp.$3;

  // detect strings beginning like this as date: "Do. 16.08.05 16.00...", attention: year is always 2000+
  cValue = cValue.replace( /\240/g, ' ' ); // IE puts char 0240 for &nbsp;
  if ( cValue.match( /^([A-Z][a-z])\.(\s|\&nbsp;)+(\d{2})\.(\d{2})\.(\d{2})(\s|\&nbsp;)+(\d{2})\.(\d{2})/ ) ) {
    cValue=RegExp.$4+'/'+RegExp.$3+'/20'+RegExp.$5; //+' '+RegExp.$7+':'+RegExp.$8;
  }

  var isDate = new Date(cValue);
  if ( isDate == "NaN" || isDate == "Invalid Date" ) {
    if ( isNaN( cValue )) {
      // THE VALUE IS A STRING, MAKE ALL CHARACTERS IN
      // STRING UPPER CASE TO ASSURE PROPER A-Z SORT
      cValue = cValue.toUpperCase();
      return cValue;
    } else if ( cValue.match( /^[+-]?\d+(\.\d+)?$/ ) ) {
      return parseFloat(cValue);
    } else {
      // VALUE IS A NUMBER, TO PREVENT STRING SORTING OF A NUMBER
      // ADD AN ADDITIONAL DIGIT THAT IS THE + TO THE LENGTH OF
      // THE NUMBER WHEN IT IS A STRING
      var myNum;
      myNum = String.fromCharCode(48 + cValue.length) + cValue;
      return myNum;
    }
  } else {
    // VALUE TO SORT IS A DATE, REMOVE ALL OF THE PUNCTUATION AND
    // AND RETURN THE STRING NUMBER
    //BUG - STRING AND NOT NUMERICAL SORT .....
    // ( 1 - 10 - 11 - 2 - 3 - 4 - 41 - 5  etc.)
    var myDate = ''+ isDate.getFullYear() + ' ';
    myDate += twoDig(isDate.getMonth())   + ' ';
    myDate += twoDig(isDate.getDate())    + ' ';
    myDate += twoDig(isDate.getHours())   + ' ';
    myDate += twoDig(isDate.getMinutes()) + ' ';
    myDate += twoDig(isDate.getSeconds());
    return myDate ;
  }
}


var lastSortMap = {}; // global variable storing the last sort direction

function sortTable ( col, tableIdToSort, topRows ) {
  // determine direction
  var dir = 0; // 0 = ascending, 1 = descending
  var lastSort = lastSortMap[tableIdToSort];
  if ( lastSort && col == lastSort.col ) dir = 1 - lastSort.dir;
  lastSortMap[tableIdToSort] = { col: col, dir: dir};

  if ( null == topRows || !topRows ) topRows = 1; // number of untouched rows at the top (header)

  var tableToSort = document.getElementById(tableIdToSort);
  var totalRows   = tableToSort.rows.length;
  var srcArray    = new Array(); // array of values to sort
  var colArray    = new Array(); // array of sorted values
  var oldIndex    = new Array(); // array of source indices
  var newIndex    = new Array(); // array of reverse indices
  var i;
  var minNumber   = null; // minimal number to sort

  var xyToCellMapTable = []; // maps x,y coordinates to cells. This allows to handle rowspan and colspan
  for ( var r = topRows; r < totalRows; r++ ) {
    var y = r - topRows;
    if ( null == xyToCellMapTable[y] ) xyToCellMapTable[y] = [];
    var cells = tableToSort.rows[r].cells;
    var x = -1;
    for ( c = 0; c < cells.length; c++ ) {
      x++;
      var cell = cells[c];
      while ( null != xyToCellMapTable[y][x] ) { x++; }
      var rs = cell.rowSpan; if ( rs == null ) rs = 1;
      var cs = cell.colSpan; if ( cs == null ) cs = 1;
      var m = { cell: cell, row: r, col: c, yStart: y, xStart: x, rowspan: rs, colspan: cs };
      for ( var dy = 0; dy < rs; dy++ ) {
        if ( null == xyToCellMapTable[y+dy] ) xyToCellMapTable[y+dy] = [];
        for ( var dx = 0; dx < cs; dx++ ) {
          xyToCellMapTable[y+dy][x+dx] = m;
        }
      }
    }
  }

  // ** POPULATE THE ARRAY colArray WITH CONTENTS OF THE COLUMN SELECTED
  for ( var r = topRows; r < totalRows; r++ ) {
    var value;
    var m = xyToCellMapTable[r-topRows][col];
    if ( null != m ) {
      var valNode = m.cell.firstChild;
      while ( valNode != null && valNode.nodeType != 3 ) valNode = valNode.firstChild;
      if ( valNode != null ) value = valNode.nodeValue;
    }
    var sortVal = setDataType(value);
    if ( typeof(sortVal) == 'number' ) {
      if ( minNumber == null || sortVal < minNumber ) minNumber = sortVal;
    }
    srcArray[r-topRows] = sortVal;
    //srcArray[r-topRows] = setDataType(oldCells[col].firstChild.nodeValue);
    //srcArray[r-topRows] = setDataType(oldCells[col].innerText);
  }
  for ( i=0; i < srcArray.length; i++ ) {
    if ( typeof(srcArray[i]) == 'number' ) {
      var num = srcArray[i] - minNumber;
      // prepend number of integer digits to handle different string sizes
      // because sort works on the string representation
      var l = Math.floor(num).toString().length;
      srcArray[i] = String.fromCharCode(64 + l) + num;
    }
  }

  // ** COPY AND SORT THE VALUES
  for ( i=0; i < srcArray.length; i++ ) { colArray[i] = srcArray[i]; }
  colArray.sort();
  if ( dir ) colArray.reverse();

  // ** BUILD THE INDEX ARRAY
  for ( i=0; i < colArray.length; i++) { // LOOP THROUGH THE NEW SORTED ARRAY
    var j;
    var notIndexed = true;
    for ( j=0; notIndexed && j < srcArray.length; j++ ) { // LOOP THROUGH THE OLD ARRAY
      if ( colArray[i] == srcArray[j] && newIndex[j] == null ) {
        // WHEN THE ITEM IN THE OLD AND NEW MATCH
        // AND THE DESTINATION INDEX IS NOT STILL USED,
        // PLACE THE CURRENT ROW NUMBER IN THE PROPER POSITION
        // IN THE NEW ORDER ARRAY SO ROWS CAN BE MOVED ....
        oldIndex[i] = j;
        newIndex[j] = i;
        notIndexed = false;
      }
    }
  }

  // ** SORTING COMPLETE, ADD NEW ROWS TO BASE OF TABLE
  var zeile = 0;
  var lastRow = [];
  for ( var y = 0; y < oldIndex.length; y++ ) {
    var yo     = oldIndex[y];
    var oldRow = tableToSort.rows[yo+topRows];
    var newRow = tableToSort.insertRow(totalRows+y);
    newRow.vAlign = oldRow.vAlign;

    var xMap = xyToCellMapTable[yo];
    var c = 0;
    for ( var x = 0; x < xMap.length; x++ ) {
      var m = xMap[x];
      if ( m.xStart == x ) {
        var l = lastRow[x];
        if ( null != l && l.m == m ) {
          l.cell.rowSpan++;
          continue;
        }
        var oldCell = m.cell;
        var newCell = newRow.insertCell(c++);
        lastRow[x] = { m: m, cell: newCell };
        newCell.innerHTML = oldCell.innerHTML;
        newCell.colSpan   = oldCell.colSpan;
        newCell.rowSpan   = 1; // no rowspan in sorted table
        newCell.align     = oldCell.align;
        newCell.vAlign    = oldCell.vAlign;
        newCell.width     = oldCell.width;
        newCell.height    = oldCell.height;
        newCell.className = oldCell.className;
        newCell.id        = oldCell.id;
      }
    }
  }

  // DELETE THE OLD ROWS FROM THE TABLE
  for (i=topRows; i<totalRows; i++) {
    tableToSort.deleteRow(topRows);
  }
}

/*
<!-- example -->
<table width="75%" border="1" cellspacing="1" cellpadding="1" id="rsTable" cols="5">
  <tr>
    <th><a href="javascript:sortTable(0,'rsTable');">ID</a></th>
    <th><a href="javascript:sortTable(1,'rsTable');">NAME</a></th>
    <th><a href="javascript:sortTable(2,'rsTable');">DATE</a></th>
    <th><a href="javascript:sortTable(5,'rsTable');">PRICE</a></th>
  </tr>
  <tr>
    <td>1</td>
    <td>Simon</td>
    <td align="right">9/5/61</td>
    <td align="right">-20.00</td>
  </tr>
  <tr>
    <td>2</td>
    <td><a href="">Eduard</a></td>
    <td align="right">9/11/65</td>
    <td align="right">-10.00</td>
  </tr>
  <tr>
    <td>22</td>
    <td colspan="2"><a href="">Erhard</a></td>
    <td align="right">-5.00</td>
  </tr>
</table>
*/

