/*
 * SortableTable script
 * 2007 by Martin Kleinhans
 *
 * makes a table sortable by the first row with th cells
 * uses tablepagination and tableloader if available
 */
 
var SortableTable = {
	counter: 0,
	instances: [],
	
	isManaged: function(id) {
		return (SortableTable.getSortableTableById(id)?true:false);
	},
	
	getSortableTable: function(number) {
		return SortableTable.instances[number];
	},
		
	getSortableTableById: function(id) {

		var sortableTable = undefined;
		SortableTable.instances.each(function(o) {
			if(o.getId() == id) {
				sortableTable = o;
				throw $break;
			}
		});
		return sortableTable;
	},
		
	create : function(id, options){
		var table = $(id);
		if(!table || !(table.tagName == 'table' || table.tagName == 'TABLE')) {
			return;
		}
		
		if (options) {
      options = new Hash(options);
    } else {
      options =$H();
    }
    var pos = SortableTable.counter;
    var object = SortableTable.getSortableTableById(id);
    if(object) {
      pos = object.getNumber();
    } 
    
    SortableTable.instances[pos] = new SortableTableInstance(id, options);
		if(SortableTable.instances[pos].isLoadable()) {
			TableLoader.getTableLoaderById(id).onSortableCreated();
    }
    
    if(!object) {
      SortableTable.counter++;
    }
	},

	types : {
		number : function(a,b) {
			// This will grab the first thing that looks like a number from a string, so you can use it to order a column of various srings containing numbers.
			var calc = function(v) {
        if (v.match(/\.\d+,/)) {
          v = v.replace(/\./g,'');
        }
				v = parseFloat(v.replace(/^.*?([-+]?[\d]*[\.,]?[\d]+(?:[eE][-+]?[\d]+)?).*$/,"$1").replace(/,/,'.'));
				return isNaN(v) ? 0 : v;
			}      
			return SortableTable.compare(calc(a),calc(b));
		},
		
		text : function(a,b) {
			return SortableTable.compare(a ? a.toLowerCase() : '', b ? b.toLowerCase() : '');
		},
		
		casesensitivetext : function(a,b) {
			return SortableTable.compare(a,b);
		},
		
    datetime : function(a,b) {
      var calc = function(v) {
        var r = v.match(/^(\d{2}).(\d{2}).(\d{4}) (\d{2}):(\d{2})/);
        var yr_num = r[3];
        var mo_num = parseInt(r[2])-1;
        var day_num = r[1];
        var hour_num = r[4];
        var minute_num = r[5];
        return new Date(yr_num, mo_num, day_num,hour_num,minute_num).valueOf();
      }
      return SortableTable.compare(a ? calc(a) : 0, b ? calc(b) : 0);
    },
    
		date : function(a,b) {
			var calc = function(v) {
				var r = v.match(/^(\d{2}).(\d{2}).(\d{4})/);
				var yr_num = r[3];
				var mo_num = parseInt(r[2])-1;
				var day_num = r[1];
        return new Date(yr_num, mo_num, day_num).valueOf();
			}
			return SortableTable.compare(a ? calc(a) : 0, b ? calc(b) : 0);
		},
		
		time : function(a,b) {
			var d = new Date();
			var ds = d.getMonth() + "/" + d.getDate() + "/" + d.getFullYear() + " "
			return SortableTable.compare(new Date(ds + a),new Date(ds + b));
		}
	},
	
	compare : function(a,b) {
		return a < b ? -1 : a == b ? 0 : 1;
	},
	
	detectors : $A([
		{re: /^\d{2}.\d{2}.\d{4} \d{2}:\d{2}/i, type : "datetime"},
    {re: /^\d{2}.\d{2}.\d{4}/i, type : "date"},
		{re: /^\d{1,2}\:\d{2}(?:\:\d{2})?(?:\s[a|p]m)?$/i, type : "time"},
		{re: /^[$ŁĄ€¤]/, type : "currency"}, // dollar,pound,yen,euro,generic currency symbol
		{re: /^[-+]?[\d]*[\.,]?[\d]+(?:[eE][-+]?[\d]+)?/, type : "number"},
		{re: /^[A-Z]+$/, type : "casesensitivetext"},
		{re: /.*/, type : "text"}
	]),
	
	addSortType : function(name, sortfunc) {
		SortableTable.types[name] = sortfunc;
	},
	addDetector : function(rexp, name) {
		SortableTable.detectors.unshift({re:rexp,type:name});
	},
	
	getCellText : function(cell) {
		if(!cell) return "";
		return cell.textContent ? cell.textContent : cell.innerText;
	}
};

var SortableTableInstance = Class.create();

	SortableTableInstance.prototype = {
		options_default : $H({
	      id: '',
	     	order: '',
	     	column: '',
				columnClass : 'sortcol',
				descendingClass : 'sortdesc',
				ascendingClass : 'sortasc',
				nosortClass : 'nosort',
				rowEvenClass : 'roweven',
				rowOddClass : 'rowodd',
				onmouseoverColor : '',
				afterSortCallback: false
    }),
	    
		options : $H({
    }),
    
    isPaginated: function() {
			if(typeof(TablePagination) == 'object' && TablePagination.isManaged(this.getId())) {
				return true;
			}
			return false;
		},
		
		isLoadable: function() {
			if(typeof(TableLoader) == 'object' && TableLoader.isManaged(this.getId())) {
				return true;
			}
			return false;
		},
		
    getId: function() {
    	return this.options['id'];
    },
    
    getNumber: function() {
    	return this.options['number'];
    },
    
		getSortOrder: function() {
			return this.options['order'];
		},
		
		getSortColumn: function() {
			return this.options['column'];
		},
		
		setSortOrder: function(order) {
			this.sort(this.options['column'],order);
		},
		
		setSortColumn: function(column) {
			this.sort(this.options['column'],order);
		},
		
    initialize: function(id, opt) {
    	this.options = Object.clone(this.options_default);
			this.options['id'] = id;
			this.options['number'] = SortableTable.counter;
			if (opt.values().length>0) {
        this.options.merge(opt);
      }
      
      // check if a column / order is already defined
      if(this.isLoadable()) {
	    	var loader = TableLoader.getTableLoaderById(id);
	    	if(!this.getSortColumn() && loader.getSortColumn() != '') {
	    		this.options['column'] = loader.getSortColumn();
	    	}
	    	if(!this.getSortOrder() && loader.getSortOrder() != '') {
	    		this.options['order'] = loader.getSortOrder();
	    	}
      }
      var column = this.options['column'];
			var sortFirst;
			var cells = this.getHeaderCells();
			var columnClass = this.options['columnClass'];
			cells.each(function(c,i){
				c = $(c);
				if(c.id == '') {
					c.id = 'sortableTable-' + id + '-' + i;
				}
			
                Event.observe(c, 'click', function(e) {
                    if (c.hasClassName('nosort')) {
                      return true;
                    };
                    var cell = Event.element(e);
                    if(cell.tagName.toLowerCase() != "th") {
                      cell = cell.up('th');
                    }
                    var id = cell.up('table').id;
                    var sortableTable = SortableTable.getSortableTableById(id);
					sortableTable.sort(cell);
				});
				c.addClassName(columnClass);
				
				if(c.id == column) {
					sortFirst = c;
				}
                c.style.cursor = 'pointer';
                
			});
			
			if(sortFirst) {
				this.sort(column, this.options['order'],true);
			} else { // just add row stripe classes
				this.stripeRows();
			}
		},
	
		getHeaderCells : function() {
			var table = $(this.getId());
			return $A((table.tHead && table.tHead.rows.length > 0) ? table.tHead.rows[table.tHead.rows.length-1].cells : table.rows[0].cells);
		},
		
		getCellIndex : function(cell) {
			return $A(cell.parentNode.cells).indexOf(cell);
		},
		
		getDataType : function(cell,index) {
			cell = $(cell);
			var t = cell.classNames().detect(function(n){ // first look for a data type classname on the heading row cell
				return (SortableTable.types[n]) ? true : false;
			});
			if(!t) {
				var i = index ? index : this.getCellIndex(cell);
				var tbl = $(this.getId());
				var cell2;
				
				var t2 = t;
				var text = '';
				for (var j = 0; j < tbl.tBodies[0].rows.length; j++) {
          cell2 = tbl.tBodies[0].rows[j].cells[i]; // grab same index cell from second row to try and match data type
					text = SortableTable.getCellText(cell2).replace(/^\s+|\s+$/g, '').replace(/^\-/,'-0.0');
					t = SortableTable.detectors.detect(function(d){return d.re.test(text);})['type'];
					if(t2 && t != t2) {
						t = 'text';
						break;
					}
					t2 = t;
				}
				cell.addClassName(t);
			}
			return t;
		},
		
		getBodyRows : function() {
			var table = $(this.getId());
			return (table.tHead && table.tHead.rows.length > 0) ? 
					$A(table.tBodies[0].rows) : $A(table.rows).without(table.rows[0]);
		},
		
		stripeRows: function() {
		
			var evenClass = this.options['rowEvenClass'];
			var oddClass = this.options['rowOddClass'];
			var onmouseoverColor = this.options['onmouseoverColor'];
      var id = this.getId();
			var headers = this.getHeaderCells();
      
			var rows = this.getBodyRows();
			rows.each(function(r,i) {
				r = $(r);
				r.removeClassName(evenClass);
				r.removeClassName(oddClass);
				if((i+1)%2 == 0) {
					r.addClassName(oddClass);
				} else {
					r.addClassName(evenClass);
				}
				if(onmouseoverColor != '') {
					r.onmouseover = function() {this.style.backgroundColor = onmouseoverColor;}
					r.onmouseout = function() {this.style.backgroundColor = '';}
				}
        columns = r.descendants();
        if (headers instanceof Array) {
          for (var j=0; j<headers.length; j++) {
            columns[j].addClassName(id + '_' + headers[j].id);
          }
        }
        
			});
		},
		
		sort : function(index, order,oncreate, force) {

            if(this.isLoadable()) {
				if(TableLoader.getTableLoaderById(this.getId()).isLoading()) {    				
                    return;
				}
			}
			var table = $(this.getId());
			var cell;            
			if(typeof index == 'number') {
				index = Math.min(table.rows[0].cells.length, index);
				index = Math.max(1, index);
				index -= 1;
				cell = (table.tHead && table.tHead.rows.length > 0) ? $(table.tHead.rows[table.tHead.rows.length-1].cells[index]) : $(table.rows[0].cells[index]);
			} else if(typeof index == 'string') {
				cell = $(index);
				index = this.getCellIndex(cell);
			} else {
				cell = $(index);
				table = table ? $(table) : table = cell.up('table');
				index = this.getCellIndex(cell);
			}
			
				
			if(cell.hasClassName(this.options['nosortClass']) && !force) 
				return;	
			
			if(!order && this.getSortColumn() == cell.id) {
				order = (this.getSortOrder() == 'ASC'?'DESC':'ASC');
			}
			if(order != 'ASC' && order != 'DESC') {
				order = 'ASC';
			}
		
			this.options['order'] = order;
			this.options['column'] = cell.id;
			order = (order =='ASC'? 1 : -1);
		
			var descendingClass = this.options['descendingClass'];
			var ascendingClass = this.options['ascendingClass'];
			
			var hcells = this.getHeaderCells();
			$A(hcells).each(function(c,i){
				c = $(c);
				if(i == index) {
					if(order == 1) {
						c.removeClassName(descendingClass);
						c.addClassName(ascendingClass);
					} else {
						c.removeClassName(ascendingClass);
						c.addClassName(descendingClass);
					}
				} else {
					c.removeClassName(ascendingClass);
					c.removeClassName(descendingClass);
				}
			});
			
			if(this.isLoadable()) {
				if(!oncreate) {
					if(this.isPaginated()) {
						var pagination = TablePagination.getTablePaginationById(table.id);
						if(pagination.getCurrentPage() != 0) {
							pagination.setCurrentPage(0);
						} else {
							TableLoader.getTableLoaderById(table.id).load();
						}
					} else {
						TableLoader.getTableLoaderById(table.id).load();
					}
				} else {
					this.stripeRows();
				}
			} else {
				var rows = this.getBodyRows();
				var datatype = this.getDataType(cell,index);
				rows.sort(function(a,b) {
					return order * SortableTable.types[datatype](SortableTable.getCellText(a.cells[index]).replace(/^\s+|\s+$/g, ''),SortableTable.getCellText(b.cells[index]).replace(/^\s+|\s+$/g, ''));
				});
		
				rows.each(function(r,i) {
					table.tBodies[0].appendChild(r);
				});
				
				this.stripeRows();
				
				if(this.isPaginated()) {
          TablePagination.getTablePaginationById(table.id).setCurrentPage(0,false,true);
				}
			}
			if(this.options['afterSortCallback']) {
				window[this.options['afterSortCallback']]();
			}
		}
	};


