function SortableTable(table,rowOfHead)
{
	var rThead = Element.parseSelector("thead", table);
	this.list = [];

	for (var h=0; h<rThead.length; h++)
	{
		var tbody = Element.parseSelector("+tbody",rThead[h],"first");
		if (tbody) {
			this.list.push(new _SortableTable(rThead[h],tbody,rowOfHead));
		}
	}
}
SortableTable.prototype.init = function(table,rowOfHead)
{
	return new SortableTable(table, rowOfHead);
}

function _SortableTable(tHead, tBody, rowOfHead) {
	if (tHead && tBody) {
		this.eventManager = new EventManager();
		this.init(tHead, tBody, rowOfHead);
	}
};

_SortableTable.prototype.init = function(tHead, tBody, rowOfHead) {

	this.table = tHead && tHead.parentNode
		|| this.tHead && this.tHead.parentNode
		|| this.table;

	this.thead = tHead; //Element.parseSelector("thead", this.table, "first");
	this.tbody = tBody; //Element.parseSelector("tbody", this.table, "first");

	this.colHeads = Element.parseSelector("th", this.thead);
	this.sortableColHeads = Element.parseSelector("th[" + this.ATTR_DEFAULT_SORT_DIR + "]", this.thead);
	if(this.thead.WSOD_clones) {
		for(var i = 0; i < this.thead.WSOD_clones.length; i++) {
			this.sortableColHeads = this.sortableColHeads.concat(Element.parseSelector("th[" + this.ATTR_DEFAULT_SORT_DIR + "]", this.thead.WSOD_clones[i]));
		}
	}
	this.rows = Element.parseSelector("tr", this.tbody);

	this.lastSort = -1; // necessary?

	this.rowOfHead = rowOfHead || this.rowOfHead || 0;
	this.addEventHandlers();

};

_SortableTable.prototype.ATTR_DEFAULT_SORT_DIR = "sortdir";
_SortableTable.prototype.ATTR_USE_SORT_FIELD = "usesortfield";
_SortableTable.prototype.ATTR_SORT_VALUE = "sortval";
_SortableTable.prototype.CSS_SORTED = "sorted";
_SortableTable.prototype.CSS_LEFT_ALIGN = "left";
_SortableTable.prototype.CSS_SORTED_ASC = "asc";
_SortableTable.prototype.CSS_SORTED_DESC = "desc";

_SortableTable.prototype.addEventHandlers = function() {
	if (this.sortEvents) {
		this.sortEvents.removeAllElements();
	}

	this.sortEvents = this.eventManager.add(this.sortableColHeads, "click", this._sort, this);
};

_SortableTable.prototype._sort = function(e, el) {
	var addClass, replaceClass;
	
	var getCellIndex = function(el) {
		var row = el.parentNode;
		for(var i = 0; i < row.cells.length; i++) 
			if(row.cells[i] == el) return i;
		return -1;
	};
	var getOriginalCell = function(el) {
		return getRowGroup(el).WSOD_original.rows[0].cells[getCellIndex(el)];
	};
	var getRowGroup = function(el){
		return el.parentNode.parentNode;
	};
	var getCellClones = function(el) {
		var cellIndex = getCellIndex(el);
		var clones = [], row;
		for(var i = 0; i < getRowGroup(el).WSOD_clones.length; i++) {
			row = getRowGroup(el).WSOD_clones[i].rows[0];
			if (row.cells.length > cellIndex) {
				clones.push(row.cells[cellIndex]);
			}
		}
		return clones;
	};
	
	if (getRowGroup(el).WSOD_original) {
		el = getOriginalCell(el);
	}
	
	if (el.parentNode.parentNode.WSOD_original || el.parentNode.parentNode.WSOD_clones) {
		addClass = function(el, className) {
			Element.addClass(el, className);
			Element.addClass(getCellClones(el), className);
		};
		removeClass = function(el, className) {
			Element.removeClass(el, className);
			Element.removeClass(getCellClones(el), className);
		};
		replaceClass = function(el, oldClass, newClass) {
			Element.replaceClass(el, oldClass, newClass);
			Element.replaceClass(getCellClones(el), oldClass, newClass);
		};
	} else {
		addClass = function(el, className) {
			Element.addClass(el, className);
		};
		removeClass = function(el, className) {
			Element.removeClass(el, className);
		};
		replaceClass = function(el, oldClass, newClass) {
			Element.replaceClass(el, oldClass, newClass);
		};
	}

	if(el.cellIndex == this.lastSort || Element.hasClass(el, this.CSS_SORTED)) {

		if (Element.hasClass(el, this.CSS_SORTED_ASC)) {
			replaceClass(el, this.CSS_SORTED_ASC, this.CSS_SORTED_DESC);
		} else {
			replaceClass(el, this.CSS_SORTED_DESC, this.CSS_SORTED_ASC);
		}

		this.rows.reverse();

	} else {

		for (var i=0; i<this.colHeads.length; i++) {
			removeClass(this.colHeads[i], this.CSS_SORTED);
		}

		this._sortRows(el);

		addClass(el, this.CSS_SORTED);
		// not sorted, so use default
		if (this.CSS_SORTED_ASC == el.getAttribute(this.ATTR_DEFAULT_SORT_DIR)) {
			addClass(el, this.CSS_SORTED_ASC);
		}
		else {
			addClass(el, this.CSS_SORTED_DESC);
			this.rows.reverse();
		}

	}

	this.lastSort = el.cellIndex;
	this._redrawTable();
};

_SortableTable.prototype._sortRows = function(el) {
	var sortValue;
	if ("true" == el.getAttribute(this.ATTR_USE_SORT_FIELD)) {
		sortValue = this.ATTR_SORT_VALUE;
	}

	var cellIndex = el.cellIndex;
	if (el.cellIndex > 0 && this.sortableColHeads[el.cellIndex-1] && this.sortableColHeads[el.cellIndex-1].getAttribute("colSpan")) {
		 cellIndex = el.cellIndex + (this.sortableColHeads[el.cellIndex-1].getAttribute("colSpan") - 1);
	}
	

	function sorter(a,b) {
		if (sortValue) {
			var aa = String(a.cells[cellIndex].getAttribute(sortValue)).toLowerCase();
			var bb = String(b.cells[cellIndex].getAttribute(sortValue)).toLowerCase();
		}
		else {
			var aa = String(a.cells[cellIndex].innerHTML).toLowerCase();
			var bb = String(b.cells[cellIndex].innerHTML).toLowerCase();
		}

		var numAA = parseFloat(aa);
		var numBB = parseFloat(bb);

		if (!isNaN(numAA) && !isNaN(numBB)) {
			return numAA - numBB;
		}
		else if (aa>bb) {
			return 1;
		}
		else if (aa<bb) {
			return -1;
		}

		return 0;
	}

	this.rows.sort(sorter);
};

_SortableTable.prototype._redrawTable = function() {
	Element.removeChildNodes(this.tbody);
	for (var i=0; i<this.rows.length; i++) {
		Element.addChild(this.tbody, this.rows[i]);
	}
	if(this.tbody.WSOD_clone) {
		Element.removeChildNodes(this.tbody.WSOD_clone);
		for (var i=0; i<this.rows.length; i++) {
			Element.addChild(this.tbody.WSOD_clone, this.rows[i].WSOD_clone);
		}
	}
};

SortableTable.sortables = [];
SortableTable.initAllSortableTables = function(TableConstructor, tables) {
	TableConstructor = TableConstructor || SortableTable;
	tables = tables || Element.parseSelector("table.sortable");

	for (var i=0; i<tables.length; i++) {
		
		if (this.sortables[i]) {
			//already exists, reuse it
			var thead = Element.parseSelector("thead", tables[i], "first");
			var tbody = Element.parseSelector("tbody", tables[i], "first");
			this.sortables[i].init(thead, tbody);
		}
		else {
			this.sortables.push(new TableConstructor(tables[i]));
		}
	}
	return this.sortables;

};

GroupedSortableTable = function(table, rowOfHead) {
	rowOfHead = rowOfHead || 0;
	GroupedSortableTable.Super(this, null, [
		Element.parseSelector("thead", table)[rowOfHead], 
		Element.parseSelector("tbody", table, "first")
	]);
};
GroupedSortableTable.Extend(_SortableTable);

GroupedSortableTable.prototype.init = function(table, rowOfHead) {
	GroupedSortableTable.Super(this, "init", arguments);

	this.tbodys = Element.parseSelector("tbody", this.table);
};

GroupedSortableTable.prototype._sort = function(e, el) {
	var dir = -1;
	if (Element.hasClass(el, this.CSS_SORTED)) {
		if (Element.hasClass(el, this.CSS_SORTED_ASC)) {
			Element.replaceClass(el, this.CSS_SORTED_ASC, this.CSS_SORTED_DESC);
		} else {
			Element.replaceClass(el, this.CSS_SORTED_DESC, this.CSS_SORTED_ASC);
			dir = 1;
		}
	}
	else {
		if (this.colHeads[this.lastSort]) {
			Element.removeClass(this.colHeads[this.lastSort], this.CSS_SORTED);
		}

		Element.addClass(el, this.CSS_SORTED);

		if (this.CSS_SORTED_ASC == el.getAttribute(this.ATTR_DEFAULT_SORT_DIR)) {
			Element.addClass(el, this.CSS_SORTED_ASC);
			dir = 1;
		}
		else {
			Element.addClass(el, this.CSS_SORTED_DESC);
		}
	}

	this.lastSort = el.cellIndex;
	this._sortRows(el, dir);
};

GroupedSortableTable.prototype._sortRows = function(el, dir) {
	var sortValue;
	var sortFunction = this._sorter;

	if ("true" == el.getAttribute(this.ATTR_USE_SORT_FIELD)) {
		sortValue = this.ATTR_SORT_VALUE;
	}

	var groupSortRows = [];
	for (var i=0; i<this.tbodys.length; i++) {
		var rows = Element.parseSelector("tr", this.tbodys[i]);
		if (rows.length) {

			function sorter(a, b) {
				return sortFunction(rows, el, sortValue, dir, true, a, b);
			};
			rows = rows.sort(sorter);


			Element.removeChildNodes(this.tbodys[i]);
			for (var j=0; j<rows.length; j++) {
				Element.addChild(this.tbodys[i], rows[j]);
			}
			groupSortRows.push(rows[0]);
		}
	}

	function sortGroups(a, b) {
		return sortFunction(null, el, sortValue, dir, false, a, b);
	};
	groupSortRows = groupSortRows.sort(sortGroups);

	for (var i=0; i<groupSortRows.length; i++) {
		var tbody = groupSortRows[i].parentNode;
		Element.remove(tbody);
		Element.addChild(this.table, tbody);
	}

};

GroupedSortableTable.prototype._sorter = function(rows, el, sortValue, dir, preserveFirst, a, b) {
	if (preserveFirst) {
		if (rows[0] == a) {
			return -1;
		}
		else if (rows[0] == b) {
			return 1;
		}
	}

	if (sortValue) {
		var aa = String(a.cells[el.cellIndex].getAttribute(sortValue)).toLowerCase();
		var bb = String(b.cells[el.cellIndex].getAttribute(sortValue)).toLowerCase();
	}
	else {
		var aa = String(a.cells[el.cellIndex].innerHTML).toLowerCase();
		var bb = String(b.cells[el.cellIndex].innerHTML).toLowerCase();
	}

	var numAA = parseFloat(aa);
	var numBB = parseFloat(bb);

	if (!isNaN(numAA) && !isNaN(numBB)) {
		return (numAA - numBB) * dir;
	}
	else if (aa>bb) {
		return 1 * dir;
	}
	else if (aa<bb) {
		return -1 * dir;
	}

	return 0;
};

GroupedSortableTable.prototype._redrawTable = function() {
};
