function tablesort_init() {
  tablesort_tables = document.getElementsByTagName('TABLE');
  for (var t=0; t<tablesort_tables.length; t++) {
    if (tablesort_tables[t].tHead)
      for (var y=0; y<tablesort_tables[t].tHead.rows.length; y++)
        for (var x=0; x<tablesort_tables[t].tHead.rows[y].cells.length; x++)
          tablesort_linkcell(tablesort_tables[t].tHead.rows[y].cells[x], t, x, y);
    if (tablesort_tables[t].tFoot)
      for (y=0; y<tablesort_tables[t].tFoot.rows.length; y++)
        for (x=0; x<tablesort_tables[t].tFoot.rows[y].cells.length; x++)
          tablesort_linkcell(tablesort_tables[t].tFoot.rows[y].cells[x], t, x, y);
    tablesort_lastsort[t] = 0;
  }
}

// Turn a header/footer cell into a sorting link.
function tablesort_linkcell(cell, t, x, y) {
  if (tablesort_getLabel(cell)) {
    var link = document.createElement('A');
    link.href="#Sort_"+t+"_"+x;
    link.title="Sort by this column";
    link.onclick = new Function("tablesort_click("+t+", "+x+", \""+escape(tablesort_getLabel(cell))+"\"); return false");
    cell.appendChild(link);
    for (var c=0; c<cell.childNodes.length-1; c++)
      link.appendChild(cell.childNodes[c]);
    // Add an element where the sorting arrows will go.
    var arrow = document.createElement('SPAN');
    arrow.innerHTML = tablesort_arrow_none;
    arrow.name = "tablesort_"+t+"_"+x+"_"+y;
    cell.appendChild(arrow);
  }
}

// Return the 'label' attribute for a cell.
// Opera won't let us make up novel attribute names, so we'll pick an obscure one.
function tablesort_getLabel(cell) {
  var str;
  if (window.opera) {
    // Opera 7&8 have a bug with getAttribute, so this is an ugly hack to sidestep it.
    var m = cell.outerHTML.match(/^<[^>]+LABEL=['"]*([^'" ]+)['"]*/i);
    str = m ? m[1] : "";
  } else
    str = cell.getAttribute('label');
  return str ? str.toLowerCase() : '';
}

// Sort the rows in this table by the specified column.
function tablesort_click(table, column, mode) {
  // Determine and record the direction.
  var reverse = false;
  if (Math.abs(tablesort_lastsort[table]) == column+1) {
    reverse = tablesort_lastsort[table] > 0;
    tablesort_lastsort[table] = -tablesort_lastsort[table];
  } else
    tablesort_lastsort[table] = column+1;
  // Display the correct arrows on every header/footer cell.
  var spans = document.getElementsByTagName('SPAN');
  var spanprefix1 = "tablesort_"+table+"_";
  var spanprefix2 = "tablesort_"+table+"_"+column;
  for (var s=0; s<spans.length; s++)
    if (spans[s].name && spans[s].name.substring(0, spanprefix1.length) == spanprefix1)
      if (spans[s].name.substring(0, spanprefix2.length) == spanprefix2)
        if (tablesort_lastsort[table] > 0)
          spans[s].innerHTML = tablesort_arrow_down;
        else
          spans[s].innerHTML = tablesort_arrow_up;
      else
        spans[s].innerHTML = tablesort_arrow_none;
  // Fetch the table's data and store it in a dictionary (assoc array).
  if (tablesort_tables[table].tBodies.length < 1)
    return; // No data in table.
  var tablebody = tablesort_tables[table].tBodies[0];
  var cell_dictionary = [];
  var cell;
  for (var y=0; y<tablebody.rows.length; y++) {
    if (tablebody.rows[y].cells.length > 0)
      cell = tablebody.rows[y].cells[column];
    else // Dodge Safari 1.0.3 bug
      cell = tablebody.rows[y].childNodes[column];
    cell_dictionary[y] = [tablesort_dom2txt(cell), tablebody.rows[y]];
  }
  // Convert values from strings to numbers if 'num' was the chosen type.
  if (mode == 'num')
    for (y=0; y<cell_dictionary.length; y++) {
      cell_dictionary[y][0] = parseFloat(cell_dictionary[y][0]);
      if (isNaN(cell_dictionary[y][0]))
        cell_dictionary[y][0] = -Number.MAX_VALUE;
    }
  // Convert values to all lowercase if 'nocase' was the chosen type.
  if (mode == 'nocase')
    for (y=0; y<cell_dictionary.length; y++)
      cell_dictionary[y][0] = cell_dictionary[y][0].toLowerCase();
  // Sort the dictionary.
  cell_dictionary.sort(tablesort_comparedictionary);
  // Rebuild the table with the new order.
  var i;
  for (y=0; y<cell_dictionary.length; y++) {
    i = reverse ? (cell_dictionary.length-1-y) : y;
    tablebody.appendChild(cell_dictionary[i][1]);
  }
  if (window.opera) {
    // Opera needs to rerender the last row due to a redraw bug.
    tmp_tablebody = tablebody;
    tmp_row = tablebody.removeChild(tablebody.rows[tablebody.rows.length-1]);
    setTimeout("tablesort_operafix()", 1);
  }
}
var tmp_tablebody, tmp_row;

// Restore the last row to force an Opera redraw.
function tablesort_operafix() {
  tmp_tablebody.appendChild(tmp_row);
}

// Compare two dictionary structures and indicate which is larger.
// Used by Array.sort() function
function tablesort_comparedictionary(a, b) {
  if (a[0]==b[0])
    return 0;
  return (a[0] > b[0]) ? 1: -1;
}

// Recursively build a plain-text version of a DOM structure.
// Bug: whitespace isn't always correct, but shouldn't matter for tablesort.
function tablesort_dom2txt(obj) {
  var text = "";
  if (!obj) return "";
  if (obj.nodeType==3)
    text = obj.data;
  else
    for (var x=0; x<obj.childNodes.length; x++)
      text = text + tablesort_dom2txt(obj.childNodes[x]);
  return text;
}

var tablesort_tables = [];   // List of all the tables
var tablesort_lastsort = []; // Upon which column was the table sorted last time.  -=up, +=down

var tablesort_arrow_none = " <IMG HEIGHT=10 WIDTH=10 SRC='_arrowblank.gif' ALT=''>";
var tablesort_arrow_up   = " <IMG HEIGHT=10 WIDTH=10 SRC='_arrowup.gif' ALT='&uarr;'>";
var tablesort_arrow_down = " <IMG HEIGHT=10 WIDTH=10 SRC='_arrowdown.gif' ALT='&darr;'>";

if (navigator.appName != "Microsoft Internet Explorer" || navigator.platform.indexOf("Mac") != 0)
  setTimeout("tablesort_init()", 100);