(function( $ ){

	var tables = new Array();
	
	var msie6 = ($.browser.msie && $.browser.version < 7);
	
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// window resize event
	
	$(window).bind('resize.table', function() {
		
		recalculate();
		
	});
	
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// recalculation
	
	this.recalculate = function () {
		
		if (!tables.length) return; 
		
		for (var i in tables) {
			
			var table = tables[i];
			
			table.elements.table( table.settings, true );
		}
	}
	
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	
	$.fn.table = function (settings, recalc) {
		
		var settings = settings || { col:0, sub:'' };
		var recalc = recalc || false;
		var subcells = this.parent().attr('data-sub') || settings.sub;
		
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		// initialize base widths
		
		var parentInWidth = this.parent().width();
		var inWidth  = this.width();
		var outWidth = this.outerWidth();
		
		if (!recalc) {
		
			var elems = $([]);
			
			var col = this.parent().attr('data-col') || settings.col;
			
			var padding	 = outWidth - inWidth; 
			var border_left  = this.borderWidth('left');
			var border_right = this.borderWidth('right');
			var border = 0;
			var margin = this.margin('left') + this.margin('right');
			
			if (msie6) {
				this.css('border-left-width','0px');
				this.css('border-right-width','0px');
			}
			
			if (col.toString().match('%')) {
			
				outWidth = Math.floor((parentInWidth * parseInt(col)) / 100);
				inWidth  = outWidth - padding - border;
			
			} else if (col.toString().match('px')) {
			
				inWidth  = parseInt(col);
				outWidth = inWidth + padding + border;
				
			} else if (col != 0) {
				
				outWidth = Math.floor(parentInWidth / parseInt(col));
				inWidth  = outWidth - padding - border - margin;
			}
			
			settings.inWidth = inWidth;
			settings.outWidth = outWidth;
			
			tables.push( { 
				elements : elems.add(this),
				settings : settings
			} );
		
		} else {
			
			inWidth  = settings.inWidth;
			outWidth = settings.outWidth;
		}
		
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		// calculate number of columns
		
		var columns = Math.floor(parentInWidth / outWidth);
		
		var min_height_property = (msie6) ? 'height' : 'min-height';
		
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		
		var row   = 1;
		var col   = 0;
		var rows  = [0];
		var cells = new Array();
		
		this.each(function () {
			
			var t = $(this);
			
			var spanstart = 1;
			var colspan = 1;
				
			if (t.hasClass('colspan-2')) { colspan = 2; }
			if (t.hasClass('colspan-3')) { colspan = 2; }
			if (t.hasClass('colspan-4')) { colspan = 2; }
			if (t.hasClass('colspan-5')) { colspan = 2; }
			
			if (colspan == 5) { colspan = (columns >= 5) ? 5 : 4; }
			if (colspan == 4) { colspan = (columns >= 4) ? 4 : 3; }
			if (colspan == 3) { colspan = (columns >= 3) ? 3 : 2; }
			if (colspan == 2) { colspan = (columns >= 2) ? 2 : 1; }
			
			col += colspan;
			
			spanstart = col - colspan + 1;
			
			if (col < columns) {
				
				cells.push( { row:row, col:spanstart, colspan:colspan } );
				
				rows[row] = 0;
				
			} else if (col == columns) {
				
				var start = col - colspan + 1;
				
				cells.push( { row:row, col:spanstart, colspan:colspan } );
				
				rows[row] = 0;
				
				row++; col = 0; 
				
			} else if (col > columns) {
				
				row++; col = colspan; 
				
				cells.push( { row:row, col:1, colspan:colspan } );
				
				rows[row] = 0;
			}
		
		});	
		
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		// adjust colspans to fill up all rows except in the last row
		// mark the last column in each row
		
		var last = cells.length - 1;
		
		$.each(cells, function (i,cell) { 
			
			var next = (i < last) ? cells[i+1] : false;
			
			if (next) {
				
				cell.last_col = false;
				
				if (cell.row != next.row) {
					
					cell.colspan = cell.colspan + (columns - (cell.col + (cell.colspan - 1)));
					cell.last_col = true;
				}
			
			} else if (i == last) {
				
				cell.last_col = true;
			}
		}); 
		
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		// subcells
		
		if (subcells) {
			
			// calculate max height of each specified sub-element
			 
			for (var i = 0, subrows=[]; i < rows.length; i++) 
				subrows[i] = 0;
			
			this.each(function (index) {
				
				var cell = cells[index];
				var max	 = subrows[cell.row];
				var childOutHeight = 0;
				var childMargin = 0;
				var clear = '';
				
				$(this).children(subcells).each(function() {
					
					var t = $(this);
					
					childOutHeight = t.outerHeight(); 
					childMargin = t.margin('top') + t.margin('bottom');
					var align = t.css('float');
					
					if (align == 'none') {
						subrows[cell.row] = (childOutHeight > max) ? childOutHeight : max;
					} else {
						clear = align;
					}
				});
				
				if (clear) {
					
					var childHeight = childOutHeight + childMargin;
					
					if ($(this).height() < childHeight) {

						$(this).append('<div style="clear:'+clear+'"></div>');
					}	
				}
			});
			
			// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
			// calculate min height for each specified sub-element
			
			this.each(function (index) {
			
				var cell = cells[index];
				
				$(this).children(subcells).each(function() {
				
					t = $(this);
					
					if (subrows[cell.row]) {
					
						var minHeight = subrows[cell.row] - (t.outerHeight() - t.height());
					
						t.css(min_height_property, minHeight + 'px');
					}
					
				});
			});
			
			// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
			// vertical-alignment for each sub-elements 
			
			this.children(subcells)
				.find(subcells)
				.valign();
			
		}
		
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		// calculate the new width of each cell
		
		this.each(function (index) {
			
			var t = $(this);
			var cell = cells[index];
			
			cell.width = ( outWidth * cell.colspan ) - ( outWidth - inWidth );
			
			t.css('width',cell.width);
			t.css(min_height_property,'1px');	// reset min-height
		});
		
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		// calculate the height of each row
		
		this.each(function (index) {
		
			var t = $(this);
			var cell = cells[index];
			var max	 = rows[cell.row];
			
			cell.oHeight = t.outerHeight();
			cell.iHeight = t.height();
			
			rows[cell.row] = (cell.oHeight > max) ? cell.oHeight : max;
		});
		
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		// calculate the minimum height of each cell
		
		$.each(cells, function (i,cell) { 
		
			var rowHeight = rows[cell.row];
			
			cell.mHeight = rowHeight - (cell.oHeight - cell.iHeight);
		});
		
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		// calculate height all sub-elements that are left or right aligned
		// and have a percentage height
			
		this.each(function (index) {
		
			var cell = cells[index];
			var childOutHeight = 0;
			var childMargin = 0;
			var clear = '';
			
			$(this).children().each(function() {
			
				t = $(this);
				
				var align = t.css('float');
				
				if (align != 'none') {
					
					clear = align;
					
					childOutHeight = t.outerHeight(); 
					childMargin = t.margin('top') + t.margin('bottom');
					var height = t.css('height');
					
					if (height.match('%')) {
						
						var padding = t.outerHeight() - t.height();
						
						height = Math.floor((cell.mHeight * parseInt(height)) / 100);
						height = height - padding;
						
						t.css(min_height_property, height + 'px');
					}
				}
			});
			
			if (clear) {
					
				var childHeight = childOutHeight + childMargin;
				
				if ($(this).height() < childHeight) {
					
					$(this).append('<div style="clear:'+clear+'"></div>');
				}	
			}
		});
		
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		// add width, min-height, float & classes to each element
		
		this.each(function (index) {
			
			var t = $(this);
			var cell = cells[index];
			
			t.css('float','left');
			
			if (cell.col == 1) {
				t.css('clear','left');
				t.addClass('col-1');
			} else {
				t.css('clear', 'none');
				t.removeClass('col-1');
			}
			
			if (cell.last_col) {
				t.addClass('col-last');
			} else {
				t.removeClass('col-last');
			}
			
			if (cell.row == 1) { 
				t.addClass('row-1');
			} else {
				t.removeClass('row-1');
			}
			
			if (cell.row == rows.length-1) {
				t.addClass('row-last');
			} else {
				t.removeClass('row-last');
			}
			
			t.addClass('colspan-'+cell.colspan);
			
			t.css(min_height_property, cell.mHeight + 'px');
			
		});
		
		if (!recalc) {
			
			// a recalculation is needed to adjust the height of 
			// cells which contain floated elements that are 100% height
			
			recalculate(); 
		}
	
		return this;
	};
	
	// =========================================================================
	
	$.fn.valign = function (align) {
	
		return this.each(function () {
				
			var t = $(this);
			
			var parentHeight = t.parent().height();
			
			var inHeight  = t.height();
			var outHeight = t.outerHeight();
			var margin 	  = t.margin('top') + t.margin('bottom');
			var valign    = align || t.css('vertical-align'); 
			var top       = parentHeight - (inHeight + margin);
			
			if (top) {
				
				if (valign == 'middle') 
					top = 'top:'+Math.floor(top / 2)+'px';
				else if (valign == 'bottom') 
					top = 'top:'+top+'px';
				else
					top = 0;
				
				if (top) {
					t.attr('style',top);
					t.css('position','relative');
				}
			}
		});
	};
	
	// =========================================================================
	
	$.fn.borderWidth = function (side) {
		
		var border = this.css('border-'+side+'-width') || '';
		
		if (border.match('px')) {
			
			return parseInt(border);
		}
		
		return 0;
	}

	// =========================================================================

	$.fn.margin = function (side) {
			
		var margin = this.css('margin-'+side) || '';
		
		if (margin.match('px')) {
			
			return parseInt(margin);
		}
		
		return 0;
	}
	
})( jQuery );
