// function getParent(el, pTagName) {
// 	if (el == null) return null;
// 	else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase())	// Gecko bug, supposed to be uppercase
// 		return el;
// 	else
// 		return getParent(el.parentNode, pTagName);
// }

/*-------------------------------------------
	SortableTable Class
-------------------------------------------*/
var SortableTable = {};
SortableTable = Class.create({
	
	initializing: true,
	
	initialize: function(tableElemOrId, options) {
		this.tableElem = $(tableElemOrId);
		this.columns = new Array();
		// Set default options
		this.options = {
			initialSort: true,
			initialSortColumn: null,
			initialSortDirection: 'asc',
			compares: []
		};
		Object.extend(this.options, options || { });
		// Don't sort if there is only one row
		if (this.tableElem.rows.length <= 1) return;
		// Configure table
		this.currentDirection = this.options.initialSortDirection;
		this.tableElem.select('thead th').each(function(thElem,index) {
			var triggerElem = thElem.down('a');
			triggerElem.insert({before:'<span class="arrow" style="display:none"></span>'});
			Event.observe(triggerElem, 'click', this.trigger.bindAsEventListener(this));
			Event.observe(thElem, 'mouseover', function() {thElem.addClassName('over')}.bind(this));
			Event.observe(thElem, 'mouseout', function() {thElem.removeClassName('over')}.bind(this));
			if(index == 0) {
				if(this.options.initialSortColumn) {
					this.makeCurrent(this.options.initialSortColumn);
				} else {
					this.makeCurrent(thElem);
				}
			}
			if(thElem != this.currentColumn) {
				thElem.addClassName('asc');
			}
		}.bind(this));
		this.addRowClasses();
		if(this.options.initialSort) setTimeout(function() {this.sort()}.bind(this), 10);
		this.initializing = false;
	},
	
	addRowClasses: function() {
		this.tableElem.select('tr').each(function(trElem,index) {
			trElem.removeClassName('even');
			if(index % 2 == 0) trElem.addClassName('even');
		});
	},
	
	trigger: function(e) {
		this.makeCurrent(e.target.up())
		this.sort();
	},
	
	makeCurrent: function(columnElem) {
		if(this.currentColumn) {
			this.currentColumn.removeClassName('current');
			this.currentColumn.select('span.arrow').first().hide();
			if(this.currentColumn == columnElem) {
				this.currentDirection = columnElem.hasClassName('asc') ? 'desc' : 'asc';
			} else {
				this.currentDirection = columnElem.hasClassName('asc') ? 'asc' : 'desc';
			}
			columnElem.removeClassName('asc');
			columnElem.removeClassName('desc');
		}
		this.currentColumn = columnElem;
		this.currentColumn.addClassName('current');
		this.currentColumn.addClassName(this.currentDirection);
		if(this.initializing) {
			setTimeout(function() {this.currentColumn.select('span.arrow').first().show()}.bind(this), 15);
		} else {
			this.currentColumn.select('span.arrow').first().show();
		}
	},
	
	sort: function() {
    // Work out a type for the column
    var column = this.currentColumn.cellIndex;
    var itm = this.getInnerText(this.tableElem.rows[1].cells[column]);
    var sortfn = this.compareCaseInsensitive;
    if(itm.match(/^\d\d[\/-]\d\d[\/-]\d\d\d\d/)) sortfn = this.compareDate;
    if(itm.match(/^\d\d[\/-]\d\d[\/-]\d\d/)) sortfn = this.compareDate;
    if(itm.match(/^[£$]/)) sortfn = this.compareCurrency;
    if(itm.match(/^[\d\.]+$/)) sortfn = this.compareNumeric;
		// Look for custom patterns defined by constructor
		if(this.options.compares.length > 0) {
			this.options.compares.each(function(compare) {
				var re = new RegExp(compare[0])
				if(itm.match(re)) sortfn = compare[1];
			}.bind(this));
		}
		// Do the sort
    var firstRow = new Array();
    var newRows = new Array();
    for (i=0;i<this.tableElem.rows[0].length;i++) { firstRow[i] = this.tableElem.rows[0][i]; }
    for (j=1;j<this.tableElem.rows.length;j++) { newRows[j-1] = this.tableElem.rows[j]; }
    newRows.sort(sortfn.bind(this));
		if(this.currentDirection == 'desc') newRows.reverse();
    // Don't do sortbottom rows
		newRows.each(function(row) {
			if(!row.hasClassName('sortbottom')) {
	    	this.tableElem.tBodies[0].appendChild(row);
	    }
		}.bind(this));
    // for (i=0;i<newRows.length;i++) {
    // 	if (!newRows[i].className || (newRows[i].className && (newRows[i].className.indexOf('sortbottom') == -1))){
    // 	    	this.tableElem.tBodies[0].appendChild(newRows[i]);
    // 	    }
    // }
    // Do sortbottom rows only
		newRows.each(function(row) {
    	if (row.hasClassName('sortbottom')){
    		this.tableElem.tBodies[0].appendChild(row);
    	}
    }.bind(this));
    // for (i=0;i<newRows.length;i++) {
    // 	if (newRows[i].className && (newRows[i].className.indexOf('sortbottom') != -1)){
    // 		this.tableElem.tBodies[0].appendChild(newRows[i]);
    // 	}
    // }
		this.addRowClasses();
	},
	
	compareDate: function(a,b) {
	    // y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
	    aa = this.getInnerText(a.cells[this.currentColumn.cellIndex]);
	    bb = this.getInnerText(b.cells[this.currentColumn.cellIndex]);

	        dt1 = aa.substr(6,4)+aa.substr(0,2)+aa.substr(3,2);
	        dt2 = bb.substr(6,4)+bb.substr(0,2)+bb.substr(3,2);

	    if (dt1==dt2) return 0;
	    if (dt1<dt2) return -1;
	    return 1;
	},

	compareCurrency: function(a,b) { 
	    aa = this.getInnerText(a.cells[this.currentColumn.cellIndex]).replace(/[^0-9.]/g,'');
	    bb = this.getInnerText(b.cells[this.currentColumn.cellIndex]).replace(/[^0-9.]/g,'');
	    return parseFloat(aa) - parseFloat(bb);
	},

	compareNumeric: function(a,b) { 
	    aa = parseFloat(this.getInnerText(a.cells[this.currentColumn.cellIndex]));
	    if (isNaN(aa)) aa = 0;
	    bb = parseFloat(this.getInnerText(b.cells[this.currentColumn.cellIndex])); 
	    if (isNaN(bb)) bb = 0;
	    return aa-bb;
	},

	compareCaseInsensitive: function(a,b) {
	    aa = this.getInnerText(a.cells[this.currentColumn.cellIndex]).toLowerCase();
	    bb = this.getInnerText(b.cells[this.currentColumn.cellIndex]).toLowerCase();
	    if (aa==bb) return 0;
	    if (aa<bb) return -1;
	    return 1;
	},

	compareDefault: function(a,b) {
	    aa = this.getInnerText(a.cells[this.currentColumn.cellIndex]);
	    bb = this.getInnerText(b.cells[this.currentColumn.cellIndex]);
	    if (aa==bb) return 0;
	    if (aa<bb) return -1;
	    return 1;
	},
	
	getInnerText: function(el) {
		if (typeof el == "string") return el;
		if (typeof el == "undefined") { return el };
		if (el.innerText) return el.innerText;	//Not needed but it is faster
		var str = "";
		var cs = el.childNodes;
		var l = cs.length;
		for (var i = 0; i < l; i++) {
			switch (cs[i].nodeType) {
				case 1: //ELEMENT_NODE
					str += this.getInnerText(cs[i]);
					break;
				case 3:	//TEXT_NODE
					str += cs[i].nodeValue;
					break;
			}
		}
		return str;
	}
	
});