String.prototype.trim = function() { return this.replace(/^\\s*|\\s*$/g, ""); }
String.prototype.normalize = function() { return this.replace(/\\s\\s+/g, " "); }

var ctl = { "off" : "../g/but/off.png", "asc" : "../g/but/asc.png", "desc": "../g/but/desc.png" };

function TableSorter(table) {
  var self = this;
  var tbody = table.get('tbody')(0);

  this.rows  = tbody.get('tr')();
  this.sortedOn = undefined;

  var __constructor = function() {


    (function(elem, i) {
      var btn, listener;
      btn = create('button');
      btn.className = "ctl";
      elem.appendChild(btn);
      listener = new ButtonListener(btn, i++, self);
    }).Iterate(table.tHead.get('th')());

  }

  // this sort method is way to slow for large
  // numbers of items...

  this.sort = function(column, order) {
    var oldStyle = tbody.style.display;
    tbody.style.display = "none";
    var min, max, a, b, temp;

    for (var i = 0, elem; elem = this.rows.item(i); i++ ) {
      min = i;
      a = this.strip_tags(elem.cells[column]);
      for (j = i + 1; j < this.rows.length; j++) {
        b = this.strip_tags(this.rows.item(j).cells[column]);
        if (this.compare(a, b, order) > 0) {
          min = j;
          a = b;
        }
      }
      if (min > i) {
        temp = tbody.removeChild(tbody.rows[min]);
        tbody.insertBefore(temp, tbody.rows[i]);
      }
    }

    tbody.style.display = oldStyle;

  }

  this.strip_tags = function(obj) {
    var i, s = "", elem;
    if (obj.childNodes && obj.childNodes.length > 0) {
      elem = obj.firstChild;
      do {
        if (elem.nodeType == Node.ELEMENT_NODE) {
          if (elem.nodeName == "IMG") {
            s = (elem.src.indexOf('yes') != -1) ? 1 : -1;
          }
          else {
            s += (elem.nodeName == "BR") ? " " : this.strip_tags(elem);
          }
        }
        else if (elem.nodeType == Node.TEXT_NODE) {
          s += elem.nodeValue;
        }
      } while ( (elem = elem.nextSibling) );
    }
    s = s.toString();
    s = s.normalize();
    s = s.trim();
    return s;
  }

  this.compare = function(a1, b1, order) {
    var a2, b2;

    a1 = a1.toLowerCase();
    b1 = b1.toLowerCase();

    a2 = a1.match(/^([0-9]{2})\.([0-9]{2})\.([0-9]{2})$/);
    b2 = b1.match(/^([0-9]{2})\.([0-9]{2})\.([0-9]{2})$/);
    if ( a2 && b2 ) {
      a1 = Date.UTC(a2[3], a2[2] - 1, a2[1]);
      b1 = Date.UTC(b2[3], b2[2] - 1, b2[1]);
    }
    else {
      // compare for numbers
      a2 = parseFloat(a1);
      b2 = parseFloat(b1);
      if (!isNaN(a2) && !isNaN(b2)) {
        a1 = a2; b1 = b2;
      }
    }

    if (a1 == b1) { return 0; }
    else if (a1 > b1) { return (order == 1) ? 1 : -1; }
    else { return (order == 1) ? -1 : 1; }
  }

  __constructor();

}

function ButtonListener(elem, column, sorter) {
  EventListener.call(this, elem);
  this.column = column;
  this.sorter = sorter;
  var self = this;
  var ctls = {};
  var order;

  var __constructor = function() {
    for (img in ctl) {
      ctls[img] = create('img');
      ctls[img].src = ctl[img];
    }
    elem.appendChild(ctls.off);
    self.register('click', self.run);
  }

  this.run = function(evt) {
    self.invoke(evt);
    self.cancelDefault();
    var coords = getPosEvtObj(self.evt, self.elem),
        size = getObjSize(self.elem);
    self.order = (coords.y < (size.h / 2)) ? 1 : -1;
    if (sorter.sortedOn instanceof ButtonListener && sorter.sortedOn != self) {
      sorter.sortedOn.order = 0;
      sorter.sortedOn.repaint();
    }
    sorter.sortedOn = self;
    sorter.sort(self.column, self.order);
    self.repaint();
  }

  this.repaint = function() {
    if (self.order == 1) {
      elem.replaceChild(ctls.asc, elem.firstChild);
    }
    else if (self.order == -1) {
      elem.replaceChild(ctls.desc, elem.firstChild);
    }
    else {
      elem.replaceChild(ctls.off, elem.firstChild);
    }
  }

  __constructor();
}
ButtonListener.prototype = new EventListener;
ButtonListener.prototype.constructor = ButtonListener;

function synchronizeColumnStyles(table) {
// This utility function will set CSS rules for Mozilla
// to synchronize with inline rules set for <col>
// i.e. Moz ignores align attributes on col elements

  var nodes = document.createTreeWalker(table,
                                      NodeFilter.SHOW_ELEMENT,
                                      {
                                        acceptNode : function(n) {
                                          return (n.tagName == "COL" ||
                                                  (n.tagName == "COLGROUP" && n.childNodes.length == 0)) ?
                                            NodeFilter.FILTER_ACCEPT :
                                            NodeFilter.FILTER_SKIP;
                                          }
                                        },
                                      false);

   var inline = create('style');
   inline.setAttribute('type', 'text/css');
   var rule = "tbody th", rules = "", span, halign, valign, i, temp;
   (function(elem){
     temp = "";
     span = elem.getAttribute('span');
     halign = elem.getAttribute('align');
     valign = elem.getAttribute('valign');
     if (halign != null) {
       temp += "text-align:" + halign + ";";
     }
     if (valign != null) {
       temp += "vertical-align:" + valign + ";";
     }
     i = (span != null) ? span : 1;
     do {
       if (temp.length > 0) {
         rules += rule + "{" + temp + "}\n";
       }
       rule += "+td";
     } while (--i > 0);
   }).Iterate(table.get('col')());
   inline.appendChild(create(rules,Node.TEXT_NODE));
   get('head')(0).appendChild(inline);

}

function confirmDelete(evt) {

  var elem = getTarget(evt);
  var anchor = elem.parentNode;
  var str = (anchor.title && anchor.title.length > 0) ? anchor.title.replace(/Delete\s/, '') : "this item";

  var ask = window.confirm("Are you sure you wish to delete " + str + "? This action cannot be undone.\nAll references to this item will be removed from the server and database.");
  if (ask == true) {
    return true;
  }
  else {
    cancelDefault(evt);
    return false;
  }
}

// place an event handler on all anchors that contain delete in the href
// which will display the confirmation pop-up for the deletion.
function confirmationOnDelete(table) {
  (function(elem){
    if (/delete/.test(elem.href)) {
      elem.onclick=confirmDelete;
    }
  }).Iterate(table.get('a')());
}

function __init() {
   var table = get('table')(0);
   if (table != null && table.className == 'crosstab') {
    return 0;  
   }
   if (table != null && table.tHead && table.rows.length > 1)  { var sorter = new TableSorter(table); confirmationOnDelete(table); }
   if (table != null && table.tHead && document.createTreeWalker) {synchronizeColumnStyles(table)};
}

addLoadEvent(__init);
