jQuery.fn.sortTable = function(params) {


  Main.log.info("        Sort departures table");

  // initialize the arrays and set a class to the last column in each row 
  var valuesToSort = [];
  var departures = [];
  $(this).css('position', 'relative');
  var doneAnimating = 0;
  var tdSelectorText = 'td'+(':nth-child('+params.onCol+')');
  $(this).find('td:nth-child('+params.onCol+')').addClass('sortOnThisCol');
  var thiss = this;


  // itterate over the table and find the values to sort on
  var counter = 0;
  $(this).find('td').each(function() {
    if (!$(this).parent().is('.proto')) {
      if ($(this).is('.sortOnThisCol')) {
        var valForSort = $(this).parent().get(0).departure.unixTime;
        var departure = $(this).parent().get(0).departure;
        valuesToSort.push(valForSort);
        departures.push(departure);
      }
    }
  });
	
  // sort the values in the array and sort the departures from the DOM by the unixTime
  valuesToSort.sort(function(a, b) { 
    return (a.replace(/[^\d\.]/g, '', a)-b.replace(/[^\d\.]/g, '', b));
  });
  departures.sort(function(a, b) {
    return a.unixTime - b.unixTime;
  });

  // now, for each row that should be moved, create div elements and move the data there
  var counter = 0;
  var rowsToSort = 0;
  $(this).find('td').each(function() {
    if (!$(this).parent().is('.proto')) {
      var departure = $(this).parent().get(0).departure;
      if (departure != departures[counter]) {
        
        var thisTDHTMLHolder = document.createElement('div');
        var childTDHTMLHolder = document.createElement('div');
        var fontSize = $(this).css("font-size").replace(/[^\d\.]/g, '');
        with($(childTDHTMLHolder)) {
          html($(this).html());
          height($(this).height() / 2);
          css({position: 'absolute', top: '50%'});
          css("margin-top", -$(this).height()/4 - fontSize/9);
          css("width", $(this).css("width"));
        }
        with($(thisTDHTMLHolder)) {
  //      html($(this).html());
          append($(childTDHTMLHolder));
          css({position: 'relative', left: 0, top: 0});
          css("height", $(this).height());
          css("color", $(this).css("color"));         
          css("background-color", $(this).css("background-color"));
          css("font-size", $(this).css("font-size"));
        }
        $(this).html('');
        $(this).css("color", "");
        $(this).css("background-color", "");
        $(this).append(thisTDHTMLHolder);
        
        if ($(this).is('.sortOnThisCol')) {
          rowsToSort++;
        }
      };
      
      if ($(this).is('.sortOnThisCol')) {
        counter++;
      }
    }
  });
  
  // animate the rows that should change place
  for(var k in valuesToSort) {
		
    // find the row from the table that should be on k-th position 
    var currTD = $($(this).find(tdSelectorText).filter(function() {
      return (($(this).get(0).parentNode.departure && departures[k] === $(this).get(0).parentNode.departure))
              && !$(this).hasClass('tableSort_TDRepopulated');
      }).get(0));
    
    Main.log.info(valuesToSort[k] + " = " + currTD);
    var targetTD = $($(this).find(tdSelectorText).get(k));

    //establish current relative positions for the current and target <td>s and use this to calculate how far each <div> needs to move
    var currPos = currTD.parent().position();
    var targetPos = targetTD.parent().position();
    var moveBy_top = targetPos.top - currPos.top;
    
    // animate only if the position should be replaced
    if (moveBy_top != 0) {
      // give current <td> a class to mark it as having been used, so we don't get confused with duplicate values
      currTD.addClass('tableSort_TDRepopulated');
      
      //establish target <td> for this value and store as a node reference on this <td>
      currTD.get(0).toTD = targetTD;
		
      // if we're sorting on a particular column and maintaining relationships, also give the other <td>s in rows a node reference
      // denoting ITS target, so they move with their lead siibling
      var counter = 0;
      $(currTD).parent().children('td').each(function() {
        $(this).get(0).toTD = $(targetTD.parent().children().get(counter));
        counter++;
      });
		
      //invert values if going backwards/upwards
      if (targetPos.top > currPos.top) moveBy_top = Math.abs(moveBy_top);
  		
      // animate
      // - work out what to animate on.
      // - animate all cols but <td>s that aren't .sortOnThisCol follow lead sibiling with .sortOnThisCol
      // - run animation. On callback, update each <td> with content of <div> that just moved into it and remove <div>s
      var animateOn = currTD.add(currTD.siblings());
      var done = 0;
    
      animateOn.children('div').animate({top: moveBy_top}, 500, null, function() {
        if ($(this).parent().is('.sortOnThisCol')) {
          done++;
          if (done == rowsToSort) {
            Main.log.info("        Restore rows, " + departures.length + " departures.");
            thiss.tableSort_cleanUp(departures);
          };
        }
      });
    }
    
  }

};


/**
 * Restore the original values in the table.
 * Move the values from the divs to td elements, remove divs and restore css.
 */
jQuery.fn.tableSort_cleanUp = function(departures) {

  // move the text from the div in a property
  $(this).find('td').each(function() {
    if($(this).get(0).toTD) {
      if ($(this).is('.time-info')) {
        $($(this).get(0).toTD).get(0).newHTML = $(this).children('div').children('div').html();
      } else {
        $($(this).get(0).toTD).get(0).newHTML = $(this).children('div').text();
      }
      $($(this).get(0).toTD).get(0).newCSSColor = $(this).children('div').css("color");
      $($(this).get(0).toTD).get(0).newCSSBGColor = $(this).children('div').css("background-color");
      $($(this).get(0).toTD).get(0).newCSSFontSize = $(this).children('div').css("font-size");
    }
  });
  
  // move the text from the property to the td element
  $(this).find('td').each(function() {
    if (($(this).get(0).newHTML != "") || ($(this).is('.disabled'))) {
      $(this).html($(this).get(0).newHTML);
    }
    $(this).css("color", $(this).get(0).newCSSColor);
    $(this).css("background-color", $(this).get(0).newCSSBGColor);
    $(this).css("font-size", $(this).get(0).newCSSFontSize);
  });
	
  // set the respective departure object
  for (var i = 0; i < $(this).children('tr').length; i++) {
    var row = $(this).children('tr').get(i);
    if (!$(row).is('proto')) {
      row.departure = departures[i];
    }
  }
	
  // clear the classes and the unneeded properties
  $('td.tableSort_TDRepopulated').removeClass('tableSort_TDRepopulated');
  $(this).find('.sortOnThisCol').removeClass('sortOnThisCol');
  $(this).find('td[newHTML]').attr('newHTML', '');
  $(this).find('td[toTD]').attr('toTD', '');
	
};
