// SortedTable created and licensed by fry @ friedcellcollective
// http://friedcellcollective.net/js/
// This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License (http://creativecommons.org/licenses/by-sa/2.5/)
// Version: 0.8f

//  -- YOU CAN DELETE THIS PART ON PRODUCTION VERSIONS -- 
// This script depends on Event.js created by John Resig (http://ejohn.org/projects/flexible-javascript-events/) and altered by Quirksmode (http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html)
// Packaged to Event.js by fry @ friedcellcollective. Any other package with the same functions should also work.

// To create a new sorted table create a new SortedTable object with
//   new SortedTable(id)
// If no id is passed it will look for tables with class="sorted"
//   new SortedTable()
// Make sure you're doing this after the DOM has been parsed

// Tested in MSIE 6 @ WinXP and Mozilla 1.73 (Gecko/20040910) @ Windows XP

/*
History:
0.8    - first version that actually works on windows and has most of the needed features
0.8a   - changed the behaviour of sorting:
         - axis="string" now defaults to case insensitive smartcompare that puts 10 after 9, not after 1
         - no axis does normal compare (broken in 0.8)
         - axis="sstring" added - does smartcompare case sensitively
0.8b   - corrected a weird error that broke some sorts in ie6
       - added the possibility for a nonsortable column via class="nosort" on the th cell
       - added the possibility to 'regroup' tbody elements if there's more than one (for whatever reason)
       - added additional hook to put your custom javascript on (onsort)
0.8c   - added additional hook to put your custom javascript on (onmove)
0.8d   - added the option to allow only single line selects
       - corrected compare so that only strings enter compareSmart (error when trying to do split on a DOMElement)
       - changed parseInt to parseFloat for axis=number when sorting
       - changed compare - title now always overrides the value
       - added onselect that happens after onbodyclick
0.8e   - corrected a bunch of small issues noticed while writing the documentation
         - added return true to selectRange (when it's actually a range)
         - corrected prepHeader (callbacks on hover were broken)
         - removed redundant s_table variable from prepBody
         - moved onselect to the select method
         - added ondeselect
         - changed parameters to onsort and onmove
       - corrected a few sorting bugs
         - on axis=number NaN is now changed to Infinity to place nonnumeric values at the end
         - in smartcompare numbers go before text
       - corrected an error in the removeBeforeSort code (no nextSibling broke it)
       - added checking in prep so it cannot be done twice
       - added the option to disallow deselect
0.8f   - a quick closure optimization of sort to shave a lot of overhead out of the compare function
*/

//  -- YOU CAN DELETE THIS PART ON PRODUCTION VERSIONS -- 

SortedTable = function(id) {
	this.table = null;
	if (!document.getElementById || !document.getElementsByTagName) return false;
	this.init(document.getElementById(id));
	this.prep();
	if (!id) new SortedTable();
}

// static
SortedTable.tables = new Array();

SortedTable.findParent = function(elm,tag) {
	while (elm && elm.tagName && elm.tagName.toLowerCase()!=tag) elm = elm.parentNode;
	return elm;
}
SortedTable.getEventElement = function(e) {
	if (!e) e = window.event;
	return (e.target)? e.target : e.srcElement;
}
SortedTable.getSortedTable = function(elm) {
	elm = SortedTable.findParent(elm,'table');
	for (var i=0;i<SortedTable.tables.length;i++) {
		var t = SortedTable.tables[i].table;
		if (t==elm) return SortedTable.tables[i];
	}
	return null;
}
SortedTable.gecko = (navigator.product=="Gecko");
SortedTable.removeBeforeSort = SortedTable.gecko;

// dynamic
SortedTable.prototype = {
// before init finished
	init:function(elm) {
		if (!elm) return false;
		// main DOM properties
		this.table = elm;
		this.body = elm.getElementsByTagName('tbody')[0];
		this.elements = this.body.getElementsByTagName('tr');
	},
	prep:function() {
		if (!this.table || SortedTable.getSortedTable(this.table)) return;
		this.register();
		this.prepBody();
	},
	register:function() {
		SortedTable.tables.push(this);
	},
// helpers
	trim:function(str) {
		while (str.substr(0,1)==' ') str = str.substr(1);
		while (str.substr(str.length-1,1)==' ') str = str.substr(0,str.length-1);
		return str;
	},
	hasClass:function(elm,findclass) {
		if (!elm) return null;
		return (' '+elm.className+' ').indexOf(' '+findclass+' ')+1;
	},
	changeClass:function(elm,oldclass,newclass) {
		if (!elm) return null;
		var c = elm.className.split(' ');
		for (var i=0;i<c.length;i++) {
			c[i] = this.trim(c[i]);
			if (c[i]==oldclass || c[i]==newclass || c[i]=='') c.splice(i,1);
		}
		c.push(newclass);
		elm.className = this.trim(c.join(' '));
	},
	elementIndex:function(elm) {
		for (var i=0;i<this.elements.length;i++) {
			if (this.elements[i]==elm) return i;
		}
		return -1;
	},
	findParent:SortedTable.findParent,
// events
	callBodyClick:function(e) {
		var elm = SortedTable.getEventElement(e);
		var st = SortedTable.getSortedTable(elm);
		if (!st) return false;
		if (typeof(st.onbodyclick)=='function') st.onbodyclick(elm,e);
		var elm = st.findParent(elm,'tr');
			if (!st.selected(elm)) 
			{
				st.cleanselect();
				st.select(elm);
			}
		return false;
	},
	prepBody:function() {
		var elm = this.body.lastChild;
		var pelm;
		while (elm) {
			pelm = elm.previousSibling;
			if (elm.nodeType!=1) this.body.removeChild(elm);
			elm = pelm;
		}
		var trs = this.elements;
		for (var i=0;i<trs.length;i++) {
			trs[i].style.cursor = 'pointer';
			addEvent(trs[i],'click',this.callBodyClick);
		}
	},
// selecting
	selected:function(elm) {
		return this.hasClass(elm,'selrow');
	},
	select:function(elm) {
		this.changeClass(elm,'','selrow');
		//this.selectedElements.push(elm);
		if (typeof(this.onselect)=='function') this.onselect(elm);
	},
	deselect:function(elm) {
		this.changeClass(elm,'selrow','');
		if (typeof(this.ondeselect)=='function') this.ondeselect(elm);
	},
	cleanselect:function() {
		for (var i=0;i<this.elements.length;i++) {
			if (this.selected(this.elements[i])) this.deselect(this.elements[i]);
		}
	}
}