﻿/// <reference name="MicrosoftAjax.js"/>
Type.registerNamespace("AsiaAustraliaTech");

/// <summary>
/// This component is responsible for table body scrolling
///</summary>
AsiaAustraliaTech.FrozenHeaderBehavior = function (element) {

	AsiaAustraliaTech.FrozenHeaderBehavior.initializeBase(this, [element]);

	//fields
	this._containerElementID = null;
	this._containerClassName = null;
	this._headerTableClassName = null;
	this._headerTableBorder = null;
	this._headerTableCellPadding = null;
	this._headerTableCellSpacing = null;
	this._footerTableClassName = null;
	this._footerTableBorder = null;
	this._footerTableCellPadding = null;
	this._footerTableCellSpacing = null;
	this._scrollBarWidth = 16;
	this._fixHeaderHeight = true;
	this._fixFooterHeight = true;
	this._isSectionedGrid = null;
	this._freezeFooter = false;
}

AsiaAustraliaTech.FrozenHeaderBehavior.prototype = {
	initialize: function () {
		AsiaAustraliaTech.FrozenHeaderBehavior.callBaseMethod(this, 'initialize');
		this._initScrollBarWidth();
		this._initTable();
	},

	dispose: function () {
		AsiaAustraliaTech.FrozenHeaderBehavior.callBaseMethod(this, 'dispose');
	},

	//properties

	///The wrapping div
	get_ContainerElementID: function () {
		return this._containerElementID;
	},
	set_ContainerElementID: function (value) {
		this._containerElementID = value;
	},

	///The css class for the table which will contain header row
	get_HeaderTableClassName: function () {
		return this._headerTableClassName;
	},
	set_HeaderTableClassName: function (value) {
		this._headerTableClassName = value;
	},

	///The border size for the header table
	get_HeaderTableBorder: function () {
		return this._headerTableBorder;
	},
	set_HeaderTableBorder: function (value) {
		this._headerTableBorder = value;
	},

	///The cell padding value for the header table
	get_HeaderTableCellPadding: function () {
		return this._headerTableCellPadding;
	},
	set_HeaderTableCellPadding: function (value) {
		this._headerTableCellPadding = value;
	},

	///The cell spacing value for the header table
	get_HeaderTableCellSpacing: function () {
		return this._headerTableCellSpacing;
	},

	set_HeaderTableCellSpacing: function (value) {
		this._headerTableCellSpacing = value;
	},

	///If true, the header table will have explicit height 
	get_FixHeaderHeight: function () {
		return this._fixHeaderHeight;
	},

	set_FixHeaderHeight: function (value) {
		this._fixHeaderHeight = value;
	},

	///If true, the footer will be frozen
	get_FreezeFooter: function () {
		return this._freezeFooter;
	},

	set_FreezeFooter: function (value) {
		this._freezeFooter = value;
	},

	///The css class for the table which will contain header row
	get_FooterTableClassName: function () {
		return this._footerTableClassName;
	},
	set_FooterTableClassName: function (value) {
		this._footerTableClassName = value;
	},

	///The border size for the header table
	get_FooterTableBorder: function () {
		return this._footerTableBorder;
	},
	set_FooterTableBorder: function (value) {
		this._footerTableBorder = value;
	},

	///The cell padding value for the header table
	get_FooterTableCellPadding: function () {
		return this._footerTableCellPadding;
	},
	set_FooterTableCellPadding: function (value) {
		this._footerTableCellPadding = value;
	},

	///The cell spacing value for the header table
	get_FooterTableCellSpacing: function () {
		return this._footerTableCellSpacing;
	},

	set_FooterTableCellSpacing: function (value) {
		this._footerTableCellSpacing = value;
	},

	///If true, the header table will have explicit height 
	get_FixHeaderHeight: function () {
		return this._fixHeaderHeight;
	},

	set_FixHeaderHeight: function (value) {
		this._fixHeaderHeight = value;
	},

	///If true, the footer table will have explicit height 
	get_FixFooterHeight: function () {
		return this._fixFooterHeight;
	},

	set_FixFooterHeight: function (value) {
		this._fixFooterHeight = value;
	},

	///Scrollbar width (16px by default)
	get_scrollBarWidth: function () {
		return this._scrollBarWidth;
	},

	//private methods

	_getIsSectionedGrid: function () {
		if (this._isSectionedGrid == null) {
			this._isSectionedGrid = this._getTable().find('td:fist').hasClass('Section');
		}
		return this._isSectionedGrid;
	},

	//returns the table
	_getTable: function () {
		return $(this.get_element());
	},

	//returns the unique id for the header div
	_getHeaderID: function () {
		return this.get_id() + '_header';
	},

	//returns the unique id for the footer div
	_getFooterID: function () {
		return this.get_id() + '_footer';
	},

	//returns the container div
	_getContainer: function () {
		return $('#' + this.get_ContainerElementID());
	},

	//returns tbody
	_getTBody: function () {
		return this._getTable().find('tbody')[0];
	},

	//returns thead
	_getTHead: function () {
		var thead = this._getTable().find('thead');
		if (thead.size() > 0) {
			return thead[0];
		}
		return $('#' + this._getHeaderID()).find('thead')[0];
	},

	//returns last table row
	_getLastRow: function () {
		return this._getTable().find('tr:last')[0];
	},

	_initScrollBarWidth: function () {
		var inner = $('<p style="width:100%;height:200px;"></p>');
		var outer = $('<div style="position:absolute;top:0px;left:0px;visibility:hidden;width:200px;height:150px;overflow:hidden;"></div>');
		outer.append(inner);
		$(document.body).append(outer);
		var w1 = inner[0].offsetWidth;
		outer[0].style.overflow = 'scroll';
		var w2 = inner[0].offsetWidth;
		if (w1 == w2) {
			w2 = outer[0].clientWidth;
		}
		document.body.removeChild(outer[0]);
		this._scrollBarWidth = (w1 - w2);
	},

	//adds scrollbar width value to the last column's width
	_increaseLastColWidth: function (col) {
		$(col).attr('width', String.format('{0}px', parseInt($(col).attr('width').replace('px', '')) + this.get_scrollBarWidth()));
	},

	//computes the real padding and returns this value + scrollbar width and
	_computeLastCellPadding: function (cell) {
		var realPadding = 0;
		if (cell.currentStyle) {
			realPadding = cell.currentStyle.paddingRight;
		}
		else if (window.getComputedStyle) {
			realPadding = document.defaultView.getComputedStyle(cell, null).getPropertyValue('padding-right');
		}

		if (realPadding == null) {
			realPadding = 0;
		} else {
			realPadding = realPadding.replace('px', '');
		}

		return (this.get_scrollBarWidth() - parseInt(realPadding)) + 'px';
	},

	//For IE < 8 increases the width of the last column and wraps values into the span with padding to make it visible
	_handleOldIE: function () {
		if (Sys.Browser.agent == Sys.Browser.InternetExplorer &&
			Sys.Browser.version < 8) {//if old IE
			//get table
			table = this._getTable()[0];
			//get table's columns count
			var columnCount = table.rows[0].cells.length;
			//for all rows
			for (var i = 0; i < table.rows.length; ++i) {
				if (table.rows[i].cells.length == columnCount) {//if this is the last cell (there could be rowspan)
					//get table cell
					var td = table.rows[i].cells[columnCount - 1];
					//wrap the cell's value into the span with the padding-right equal to scrollbar width
					td.innerHTML = String.format('<span style="padding-right: {0}px;">{1}</span>', this.get_scrollBarWidth(), td.innerText);
				}
			}
			//add scrollbar width to last col to adjust the header
			var col = $(table).find('col:last')[0];
			this._increaseLastColWidth(col);
		}
	},

	//If the table has no THEAD - create it and put the first row inside THEAD
	_createTHead: function () {
		var table = this._getTable();
		if (table.find('thead').size() == 0) {//if there is no thead tag
			var tbody = this._getTBody(table);
			//insert empty thead
			$(tbody).before('<thead></thead>');
			//move table's first row to thead
			var thead = this._getTHead(table);
			var firstRow = table.find('tr:first')[0];
			if (this._getIsSectionedGrid()) {
				var headerRow = $('<tr></tr>');
				$(thead).append(headerRow);
				var headerCells = [];
				for (var i = 0; i < firstRow.cells.length; ++i) {
					var td = $('<th></th>');
					headerRow.append(td);
					td.css({ width: $(firstRow.cells[i]).css('width') });
					headerCells.push(td);
				}
				table.find('td.Section').find('table').each(function (index, childTable) {
					var newTable = $(childTable).clone();
					newTable.find('tr:gt(0)').remove();
					headerCells[index].append(newTable);
				});
			} else {
				$(firstRow).remove();
				$(thead).append(firstRow);
			}
		}
	},

	//Create colgroup inside the table to make the column widths for the main table and header table equal.
	//This colgroup will be copied to the header table
	_createColgroup: function () {
		var isSectionedGrid = this._getIsSectionedGrid();
		var table = this._getTable();
		//add colgroup with the explicit column widths to the table        
		var headerRows = isSectionedGrid
			? table.find('td.Section').find('tr:eq(0)')
			: table.find('tr:first');
		var thead = this._getTHead();
		headerRows.each(function (index, row) {
			var headerCells = $(row).find('th');
			var colgroup = $('<colgroup></colgroup>');
			headerCells.each(function () {
				colgroup.append($(String.format('<col width="{0}" />', $(this).outerWidth())));
			});
			if (isSectionedGrid) {
				$(thead).find(String.format('table:eq({0})', index)).find('tbody').before(colgroup);
				$(row).parent().before(colgroup.clone());
				$(row).hide();
			} else {
				$(thead).before(colgroup);
			}
		});
	},

	//Creates the div above the table, adds the separate 'header' table to this div with the same column widths,
	//moves the THEAD to the header table - this makes the THEAD row 'frozen' while the main table is scrollable
	_createHeaderDiv: function () {
		//create header div
		var headerDiv = $(String.format('<div id="{0}"></div>', this._getHeaderID()));
		//put it before the scrollable div which contains the main table
		headerDiv.insertBefore(this._getContainer());
		//get thead
		var thead = $(this._getTHead());
		//resize the header div by thead bounds
		headerDiv.css(String.format('width: {0}px; height: {0}px', thead.width(), thead.height()));
		//create the header table using the extender's parameters
		var table = headerDiv.append(String.format('<table border="{1}" cellpadding="{2}" cellspacing="{3}" class="{0}"></table>',
			this.get_HeaderTableClassName(), this.get_HeaderTableBorder(), this.get_HeaderTableCellPadding(), this.get_HeaderTableCellSpacing()));
		//fix table's height (somewhere we have big min-height)
		if (this.get_FixHeaderHeight()) {
			table.height(thead.height());
		}
		if (!this._getIsSectionedGrid()) {
			//copy main table's colgroup to the header table to make the column widths equal
			headerDiv.find('table').append(this._getTable().find('colgroup').clone());
			var col = (this._getIsSectionedGrid() ? thead : headerDiv).find('col:last')[0];
			//add scrollbar width to the last col to adjust headers
			this._increaseLastColWidth(col);
		} else {
			var col = $('<th></th>');
			thead.find('tr:first').append(col);
		}
		//move thead to the header table
		headerDiv.find('table').append(thead);
		//shift last header to make it above the column but not above the scrollbar
		var lastCell = (this._getIsSectionedGrid() ? thead : headerDiv).find('th:last');
		lastCell.css({ 'padding-right': this._computeLastCellPadding(lastCell[0]) });
	},

	//Creates the div below the table, adds the separate 'footer' table to this div with the same column widths,
	//moves the last row to the footer table - this makes the footer row 'frozen' while the main table is scrollable
	_createFooterDiv: function () {
		//create footer div
		var footerDiv = $(String.format('<div id="{0}"></div>', this._getFooterID()));
		//put it after the scrollable div which contains the main table
		footerDiv.insertAfter(this._getContainer());
		//get last row
		var lastRow = $(this._getLastRow());
		//resize the footer div by last row bounds
		footerDiv.css(String.format('width: {0}px; height: {0}px', lastRow.width(), lastRow.height()));
		//create the footer table using the extender's parameters
		footerDiv.append(String.format('<table border="{1}" cellpadding="{2}" cellspacing="{3}" class="{0}"></table>',
			this.get_FooterTableClassName(), this.get_FooterTableBorder(), this.get_FooterTableCellPadding(), this.get_FooterTableCellSpacing()));
		//fix table's height (somewhere we have big min-height)
		if (this.get_FixFooterHeight()) {			
			footerDiv.find('table').height(lastRow.height());
		}
		//copy main table's colgroup to the footer table to make the column widths equal
		footerDiv.find('table').append(this._getTable().find('colgroup').clone());
		var col = footerDiv.find('col:last')[0];
		//add scrollbar width to the last col to adjust headers
		this._increaseLastColWidth(col);
		//move last row to the footer table
		footerDiv.find('table').append(lastRow);
		//shift last cell to make it below the column but not below the scrollbar
		var lastCell = footerDiv.find('td:last');
		lastCell.css({ 'padding-right': this._computeLastCellPadding(lastCell[0]) });
	},

	_initTable: function () {
		//get table
		var table = this._getTable();
		if (table.attr('scrollable') != true.toString()) { //if not already processed
			//remove previously created header div
			$('#' + this._getHeaderID()).remove();
			//remove previously created footer div
			if (this.get_FreezeFooter()) {
				$('#' + this._getFooterID()).remove();
			}
			//create thead tag if we don't have it (first row will be moved to thead)
			this._createTHead();
			//create colgroup to fix column widths
			this._createColgroup();
			//create header div with the header table
			this._createHeaderDiv();
			//create footer div for the last row
			if (this.get_FreezeFooter()) {
				this._createFooterDiv();
			}
			//for IE < 8 make table's content hidden by scrollbar visible
			this._handleOldIE();
		}
		table.attr('scrollable', true); //mark table as processed
	}
}

AsiaAustraliaTech.FrozenHeaderBehavior.registerClass('AsiaAustraliaTech.FrozenHeaderBehavior', Sys.UI.Behavior);

if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();

