
//Table

var Table = new Class({

	Implements: [Options, Events],

	options: {
		classZebra: 'odd',
		grouped: 1
	},

	initialize: function(element, options){
		this.setOptions(this.options, options);

		this.element = $(element);
		this.body = $(this.element.tBodies[0]);
		this.head = $(this.element.tHead.rows[0]);

		this.update();
	},

	update: function(){
		Array.each(this.body.rows, this.zebra, this);
	},

	zebra: function(row, i){
		if (i % 2) row.removeClass(this.options.classZebra);
		else row.addClass(this.options.classZebra);
		return row;
	}

});

//Table.Sort

Table.Sort = new Class({

	Extends: Table,

	options: {
		sort: null,
		onSort: $empty,
		parsers: [],
		defaultParser: 'string',
		classSort: 'selected',
		classSortRev: 'selected',
		classNoSort: 'nosort'
	},

	initialize: function(element, options){
		this.parent(element, options);

		this.sorted = {col: null, dir: 1};
		this.heads = $$(this.head.cells);

		var parsers = this.options.parsers;
		var rows = this.body.rows;

		this.parsers = this.heads.map(function(cell, index){
			if (cell.hasClass(this.options.classNoSort)) return null;
			cell.addEvent('click', this.headClick.bind(this, [cell]));

			var parser = parsers[index];
			switch ($type(parser)){
				case 'function': return {convert: parser};
				case 'string': return parser;
			}
			Table.Parsers.some(function(current){
				var match = current.match;
				if (!match) return false;
				for (var i = 0, j = rows.length; i < j; i++){
					var text = rows[i].cells[index].innerHTML.trim();
					if (text && match.test(text)){
						parser = current;
						return true;
					}
				}
				return false;
			});
			return parser || this.options.defaultParser;
		}, this);

		if (this.options.sort) this.sort(this.options.sort);
	},

	headClick: function(el){
		this.sort(Array.indexOf(this.head.cells, el), el);
		this.heads.removeClass(this.options.classSort);
		el.addClass(this.options.classSort);
		return false;
	},

	sort: function(index, head){
		var parser = this.parsers[index];
		if ($type(parser) == 'string') parser = Table.Parsers.get(parser);

		if (!parser) return;

		if (this.sorted.index == index){
			this.sorted.dir *= -1;
		} else if (this.sorted.index != null){
			this.sorted.dir = 1;
			this.head.cells[this.sorted.index].removeClass(this.options.classSort);
		}
		head.addClass(this.options.classHeadSort);
		this.sorted.index = index;

		var rel = this.body.getParent();
		this.body.dispose();

		var data = Array.map(this.body.rows, function(row, i){
			var value = parser.convert.call(row.cells[index], i);
			if (parser.number){
				value = String(value).replace(/[^\d]/, '');
				value = '00000000000000000000000000000000'.substr(0, 32 - value.length).concat(value);
			}
			row.cells[index].title = value;
			return {
				position: i,
				toString:  function(){
					return value;
				}
			};

		}, this);
		data.sort();
		if (this.sorted.dir == 1) data.reverse(true);

		var l = data.length, body = this.body;
		var i, position, entry;
		while (l){
			position = data[--l].position;
			body.appendChild(this.zebra(body.rows[position], l));
			for (i = 0; i < l; i++){
				entry = data[i];
				if (entry.position > position) entry.position--;
			}
		};
		data = null;
		rel.grab(body);

		this.fireEvent('onSort', [body, index]);
	}

});

//Table.Select

Table.Select = new Class({

	Extends: Table.Sort,

	options: {
		classRowSelected: 'selected',
		classRowHovered: 'hover',
		onRowSelect: $empty,
		onRowUnselect: $empty
	},

	initialize: function(element, options){
		this.parent(element, options);
		this.selectedRows = [];
	},

	update: function(){
		Array.each(this.body.rows, function(el, i){
			if (!el.isExtenedRow){
				this.zebra(el, i).addEvents({
					'mouseover': this.rowOver.bind(this, [el]),
					'mouseout': this.rowOut.bind(this, [el]),
					'click': this.rowSelect.bind(this, [el])
				});
				el.isExtenedRow = true;
			}
		}, this);

	},

	rowOver: function(el){
		el.addClass(this.options.classRowHovered);
	},

	rowOut: function(el){
		el.removeClass(this.options.classRowHovered);
	},

	rowSelect: function(el){
		var i = this.selectedRows.indexOf(el);
		var check = el.getElement('input[type=checkbox]');
		if (i == -1){
			this.selectedRows.push(el);
			if (check) check.checked = true;
			el.addClass(this.options.classRowSelected);
			this.fireEvent('onRowSelect', [el, this.selectedRows]);
		} else {
			this.selectedRows.splice(i, 1);
			if (check) check.checked = false;
			el.removeClass(this.options.classRowSelected);
			this.fireEvent('onRowUnselect', [el, this.selectedRows]);
		}
	}

});

//Table.Parsers

Table.Parsers = new Hash({

	'date': {
		type: 'date',
		match: /^\d{4}[^\d]|[^\d]\d{4}$/,
		convert: function(){
			return Date.parse(this.innerHTML);
		}
	},

	'input-checked': {
		match: / type="(radio|checkbox)" /,
		convert: function(){
			return this.getElement('input').checked;
		}
	},

	'input-text': {
		match: /<input/,
		convert: function(){
			return this.getElement('input').value;
		}
	},

	'number': {
		number: true,
		match: /^\d+[^\d.,]*$/,
		convert: function(){
			return parseInt(this.innerHTML);
		}
	},

	'numberLax': {
		number: true,
		match: /^[^\d]+\d+$/,
		convert: function(){
			return parseInt(this.innerHTML.replace(/[^0-9]/, ''));
		}
	},

	'float': {
		number: true,
		match: /^[\d]+\.[\d]+/,
		convert: function(){
			return parseFloat(this.innerHTML.replace(/[^\d.]/, ''));
		}
	},

	'floatLax': {
		number: true,
		match: /^[^\d]+[\d]+\.[\d]+$/,
		convert: function(){
			return this.innerHTML.replace(/[^\d.]/, '');
		}
	},

	'string': {
		match: null,
		convert: function(){
			return this.innerHTML;
		}
	}

});

