/*--------------------------------------------------------------------------
 File      : grid.js
 Summary   : AJAX grid implementation
 Programmer: Sorin Vinatoru

 Revisions :	yyyy-mm-dd	sign		comment
				2006-04-01	sorin		created
				2006-05-01	gabriel		IE compatibility
				2006-06-27	sorin		added options for footer display (has prev, next, last, pageno)
				2006-07-25	gabriel		removed footer in case of: "Page 1 of 1" with option 'FooterWhenNeeded' set to 'true'
				2006-11-15	sorin		converted from XML to JSON
				2006-11-15	sorin		Enhanced with several functionalities: resize, change order position, add/del columns
				2007-04-02	Emilian		added option: callback function for showing and hiding a customised 'loading' message
				2007-07-26	gabriel		switch to prototype 1.5.1.1, removed ParentObj kinda way, fixes for IE compatibility
				2007-08-06	gabriel		added a switch to differentiate between data types
				2007-09-04	Emilian		Simplified sort parameter, added "LegacySortMode" option, for backwards compatibility
				2007-09-06	raluca + gabriel		added the caption with the column image and the rows calculation
				2007-09-07	raluca					added a global save button that will save all the grid changes
				2007-09-10	raluca					added a select used to select rows per pages
				2007-09-18	raluca					added the possibility to have static classes for each column and header column
				2008-04-15  mihaela		added grid alignament possibility and defaul definition for columns

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 to do list
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 This file is part of Content Management System (CMS) Project.

 Copyright (C) Abroad Software SRL
 Developed by Abroad Software
 --------------------------------------------------------------------------*/
if( AbScript == null)
{
	var AbScript =
	{
	  Version: '0.0.1',
	  prototypeVersion: parseFloat(Prototype.Version.split(".")[0] + "." + Prototype.Version.split(".")[1])
	}
}

function IsFunction(a) { return typeof a == 'function'; }
function IsArray(a)    { return IsObject(a) && a.constructor == Array; }
function IsObject(a)   { return (typeof a == 'object' && !!a) || IsFunction(a); }

AbScript.AbGridJson = Class.create();

AbScript.AbGridJson.prototype =
{
	initialize: function (TableId, Options)
	{

		this.Table = $(TableId);
		this.Rows = new Array();


		this.Options = {
			PageScript: '',
			RowsPerPage: 20,
			MaxRowsPerPage: 100,
			RowsPerPageText: 'Rows per page ',
			HasCaption: false,
			CaptionElements: [false, false, false],
			CaptionClass: 'BrandNewCaptionClass',
			CaptionText: 'Showing records #1 from #2', // #1 and #2 are not negociable, they must be present
			CaptionPosition: 0,
			HasHeader: true,
			HasFooter: true,
			FooterWhenNeeded: false,		// If there is only one page "Page 1 of 1 " the footer is not displayed when set to true
			SortAscendImg: '../img/sort_asc.gif',
			SortDescendImg: '../img/sort_desc.gif',
			SortImageWidth: 9,
			SortImageHeight: 5,
			SortURLParms: [],
			TableClass: $(TableId).className,
			OddClass: 'OddCell',
			EvenClass: 'EvenCell',
			FooterClass: 'FooterClass',
			FooterAClass: 'FooterAClass',
			ColumnPrefixClass: 'Col',
			CallBack: null,
			HeaderPrefixClass: 'Header',
			DragOverClass: 'DragOver',
			ColumnDragClass: 'ColumnDragClass',
			GroupClass: 'GroupClass',
			GlobalCallBack: null,
			SaveCallback: null,
			HasPrev: true,
			HasNext: true,
			HasLast: true,
			HasFirst: true,
			PrevText: 'Prev',
			NextText: 'Next',
			LastText: 'Last',
			FirstText: 'First',
			HasPageNo: true,
			DisableFirstUse: false,
			LoadingMessageFunction: null,	// true=display / false=hide
			LegacySortMode: true,			// For backwards compatibility, set to false for +/- field sort style
			ColumnMoveImage: '',
			InitialSort: '',
			LeftRightPadding: 10,
			ResizeInfo: {Opacity: 50, Color: '#666666', MinWidth: '10'},
			HandlerInfo:
				{
				Enabled: false,
				Img: '',
				Opacity: '50',
				Config:
					{
						Class: '',
						ColumnsHeaderText: 'Select Columns',
						ColumnsHeaderClass: '',
						SaveText: 'Save',
						GlobalSaveText: 'Save All Grid Changes',
						GlobalSaveClass: '',
						CancelText: 'Cancel',
						SaveClass: ''
					}
				},
			Columns: null
			};

		Object.extend(this.Options, Options || {});

		this.CurrentPage = 0;

		//compute and save rows collection with start and end row
		this.TableRows = this.Table.rows;

		// Column elements
		this.DefaultColumnDef =
			{
			Title: 'Title',
			Alias: 'Alias',
			Sortable: false,
			Width: 100,
			Resizable: false,
			Position: 0,
			Visible: 1,
			Grouping: false,
			Halign: 'left'
			}

		OptionsColumns = new Array();

		// extend column definitions with default values DefaultColumnDef
		for (i = 0; i < this.Options.Columns.length; i++)
		{
			var DefaultColumnDefCopy = Object.clone(this.DefaultColumnDef);

			Object.extend(DefaultColumnDefCopy, this.Options.Columns[i] || {});

			if(DefaultColumnDefCopy.Alias == 'Alias')
			{
				alert('Alias not defined for column ' + DefaultColumnDefCopy.Title + ' !');
				break;
			}

			if(DefaultColumnDefCopy.Position == 0)
				DefaultColumnDefCopy.Position = i+1;

			OptionsColumns[i] = DefaultColumnDefCopy;
		}

		this.Options.Columns = OptionsColumns;


		//populate sorting options
		if (this.Options.InitialSort > '')
		{
			var ColumnIndex = 0;

			for (i = 0; i < this.Options.Columns.length; i++)
			{
				if (this.Options.Columns[i].Alias == this.Options.InitialSort)
				{
					ColumnIndex = i;
					break;
				}
			}

			this.SortColumn = this.Options.Columns[ColumnIndex].Alias;
			this.SortDir = 'ASC';
			this.Grouping = this.Options.Columns[ColumnIndex].Grouping;
		}
		else
		{
			this.SortColumn = '';
			this.SortDir = '';
			this.Grouping = false;
		}

		this.RemapColumns();

		this.ResetTable();

		if (this.Options.DisableFirstUse)
			this.Options.DisableFirstUse = false;
		else
			this.PopulateTable(this.CurrentPage);
	},

	RemapColumns: function()
	{
		//first, sort columns by position
		this.Options.Columns.sort(this.ColumnSortCompare);

		//this computes the first position available after the visible columns
		var NotVisiblePosition = 0

		for (i = 0; i < this.Options.Columns.length; i++)
			if (this.Options.Columns[i].Visible)
				NotVisiblePosition++;
		//NotVisiblePosition++;

		this.MappedColumns = new Array();

		var NewColumns = new Array();

		for (i = 0, j = 0; i < this.Options.Columns.length; i++, j++)
			if (this.Options.Columns[i].Visible)
			{
				this.Options.Columns[i].Position = j;
				NewColumns[j] = this.Options.Columns[i];
				this.MappedColumns[j] = {Index: j, Alias: this.Options.Columns[i].Alias, Position: j};
			}
			else
			{
				this.Options.Columns[i].Position = NotVisiblePosition;
				NewColumns[NotVisiblePosition] = this.Options.Columns[i];
				NotVisiblePosition++;
				j--;
			}

		this.Options.Columns = NewColumns;
	},

	ColumnSortCompare: function(A, B)
	{
		if ( A.Position < B.Position ) return -1;
		if ( A.Position > B.Position ) return 1;
		return 0;
	},

	ComputeRowStart: function()
	{
		Start = 0;

		if(this.Options.HasHeader)
			Start++

		return Start;
	},

	ComputeRowEnd: function()
	{
		if (this.Options.HasFooter)
		{
			if(this.Options.HasHeader)
				return 2;
			else
				return 1;
		}
		else
		{
			if(this.Options.HasHeader)
				return 1;
			else
				return 0;
		}
	},

	ComputeTableLength: function()
	{
		return this.TableRows.length;
	},

	ResetTable: function ()
	{
		if(this.Table.rows == undefined) return;

		while (this.Table.rows.length > 0) this.Table.deleteRow(0);

		this.RePopulateCaption();

		this.FooterRow = false;

		//hook sort columns
		if (this.Options.HasHeader)
		{
			EmptyHeader = (this.TableRows.length == 0);

			if (this.TableRows.length == 0) //no row
				HeaderRow = this.Table.insertRow(0);
			else
				HeaderRow = this.TableRows[0];

			for (var i = 0, j = 0; i < this.Options.Columns.length; i++, j++)
			{
				if (!this.Options.Columns[i].Visible)
				{
					j--;
					continue;
				}

				if (EmptyHeader)
					HeaderRow.insertCell(-1);
				else
				{
					this.TableRows[0].cells[j].innerHTML = '';
				}

				if(this.Options.Columns[i].HeaderClass)
					this.TableRows[0].cells[j].className = this.Options.Columns[i].HeaderClass;
				else
					this.TableRows[0].cells[j].className = this.Options.HeaderPrefixClass + this.MappedColumns[j].Index.toString();

				this.TableRows[0].cells[j].style.width = (this.Options.Columns[this.MappedColumns[j].Index].Width != 0) ? (this.Options.Columns[this.MappedColumns[j].Index].Width + 'px') : 'auto';

				if (this.Options.Columns[this.MappedColumns[j].Index].Resizable == true)
				{
					ResizeMainDiv = document.createElement("div");
					ResizeMainDiv.style.verticalAlign = 'middle';
					ResizeMainDiv.style.position = 'relative';

					ResizeMainDiv.style.width = this.TableRows[0].cells[j].style.width + 'px';
					ResizeMainDiv.style.height = '100%';
					ResizeMainDiv.style.cursor = 'move';
					ResizeMainDiv.style.padding = '2px 0px 2px 0px';

					ResizeMainDiv.setAttribute('ColumnIndex', j);
					ResizeMainDiv.setAttribute('TableId', this.Table.id);
					Event.observe(ResizeMainDiv, 'mousedown', this.StartColumnMoveWait.bindAsEventListener(this));

					ResizeDiv = document.createElement("div");
					ResizeDiv.style.zIndex = 0;
					ResizeDiv.style.opacity = .3;
					ResizeDiv.style.cursor = 'e-resize';
					ResizeDiv.style.position = 'absolute';
					ResizeDiv.style.top = '0px';
					ResizeDiv.style.right = '-6px';
					ResizeDiv.style.height = '100%';
					ResizeDiv.style.width = '10px';
					Event.observe(ResizeDiv, 'mousedown', this.StartColumnSizeWait.bindAsEventListener(this));
					ResizeMainDiv.insertBefore(ResizeDiv, null);

					this.TableRows[0].cells[j].innerHTML = '';
					this.TableRows[0].cells[j].insertBefore(ResizeMainDiv, null);
				}

				if (this.Options.Columns[this.MappedColumns[j].Index].Sortable == true)
				{
					HeaderAnchor = document.createElement("a");
					HeaderAnchor.style.color = this.TableRows[0].cells[j].style.color;
					HeaderAnchor.innerHTML = this.Options.Columns[this.MappedColumns[j].Index].Title;// + ' ' + this.MappedColumns[j].Index;
					HeaderAnchor.href = 'javascript:void(0)';

					Event.observe(HeaderAnchor, 'click', this.HandleClick.bindAsEventListener(this));
					Event.observe(HeaderAnchor, 'mousedown', this.CancelMouseDown.bindAsEventListener(this));

					HeaderAnchor.setAttribute('ColumnIndex', this.MappedColumns[j].Index);
					HeaderAnchor.setAttribute('TableId', this.Table.id);

					if (this.Options.Columns[this.MappedColumns[j].Index].Resizable == true)
					{
						ResizeMainDiv.insertBefore(HeaderAnchor, ResizeDiv);
					}
					else
					{
						this.TableRows[0].cells[j].innerHTML = '';
						this.TableRows[0].cells[j].insertBefore(HeaderAnchor, null);
					}
				}
				else
				{
					HeaderDiv = document.createElement("div");
					HeaderDiv.style.color = this.TableRows[0].cells[j].style.color;
					HeaderDiv.innerHTML = this.Options.Columns[this.MappedColumns[j].Index].Title;// + ' ' + this.MappedColumns[j].Index;
					HeaderDiv.setAttribute('ColumnIndex', i);
					HeaderDiv.setAttribute('TableId', this.Table.id);
					if (this.Options.Columns[this.MappedColumns[j].Index].Resizable == true)
					{
						HeaderDiv.style.zIndex = 3;
						ResizeMainDiv.insertBefore(HeaderDiv, ResizeDiv);
					}
					else
					{
						HeaderDiv.style.padding = '2px 0px 2px 0px';
						this.TableRows[0].cells[j].insertBefore(HeaderDiv, null);
					}
				}

			}

			this.CreateHandler();
		}

	},

	CreateHandler: function()
	{
		this.Clicked = 1;
		//hook table config handler if provided
		if ((this.Options.HandlerInfo != false) && this.Options.HandlerInfo.Enabled)
		{
			if (!IsObject($(this.Table.id + 'HandlerDiv')))
			{
				HandlerDiv = document.createElement('SPAN');
				HandlerDiv.id = this.Table.id + 'HandlerDiv';

				HandlerImg = document.createElement('img');
				HandlerImg.src = this.Options.HandlerInfo.Img;
				HandlerImg.style.cursor = 'pointer';
				Event.observe(HandlerImg, 'click', this.HandleConfigClick.bindAsEventListener(this));
				HandlerImg.setAttribute('TableId', this.Table.id);

				document.body.insertBefore(HandlerDiv, null);
				HandlerDiv.insertBefore(HandlerImg, null);

				Coords = this.FindPos(this.Table);
//				HandlerDiv.style.position = 'absolute';
//				HandlerDiv.style.top = Coords[1] + 'px';
//				HandlerDiv.style.left = (Coords[0] + this.Table.offsetWidth - HandlerImg.offsetWidth) + 'px';
				HandlerDiv.style.opacity = this.Options.HandlerInfo.Opacity / 100;

				//create the config div
				ConfigDiv = document.createElement('div');
				document.body.insertBefore(ConfigDiv, null);
				this.ConfigDiv = ConfigDiv;
				ConfigDiv.className = this.Options.HandlerInfo.Config.Class;
				ConfigDiv.style.display = 'none';
				ConfigDiv.style.position = 'absolute';

				ColumnsDiv = document.createElement('div');
				ConfigDiv.insertBefore(ColumnsDiv, null);

				ColumnsHeader = document.createElement('div');
				ColumnsHeader.innerHTML = this.Options.HandlerInfo.Config.ColumnsHeaderText;
				ColumnsHeader.className = this.Options.HandlerInfo.Config.ColumnsHeaderClass;
				ColumnsDiv.insertBefore(ColumnsHeader, null);

				ColumnsUl = document.createElement('ul');
				ColumnsUl.id = 'HandlerDivSelectionList';
				ColumnsDiv.insertBefore(ColumnsUl, null);

				SortingDiv = document.createElement('div');
				ConfigDiv.insertBefore(SortingDiv, null);

				SaveDiv = document.createElement('div');
				SaveDiv.className = this.Options.HandlerInfo.Config.SaveClass;
				ConfigDiv.insertBefore(SaveDiv, null);

				SaveButton = document.createElement('input');
				SaveButton.type = 'button';
				SaveButton.value = this.Options.HandlerInfo.Config.SaveText;
				Event.observe(SaveButton, 'click', this.SaveConfig.bindAsEventListener(this));
				SaveButton.setAttribute('TableId', this.Table.id);
				SaveDiv.insertBefore(SaveButton, null);

				CancelButton = document.createElement('input');
				CancelButton.type = 'button';
				CancelButton.value = this.Options.HandlerInfo.Config.CancelText;
				Event.observe(CancelButton, 'click', this.CloseConfig.bindAsEventListener(this));
				CancelButton.setAttribute('TableId', this.Table.id);
				SaveDiv.insertBefore(CancelButton, null);
			}
			else
			{
				HandlerDiv = $(this.Table.id + 'HandlerDiv');
				Coords = this.FindPos(this.Table);
				HandlerDiv.style.top = Coords[1] + 'px';
				HandlerDiv.style.left = (Coords[0] + this.Table.offsetWidth - HandlerImg.offsetWidth) + 'px';
			}

			//here we create the div with the columns to select
			this.CreateConfigDiv();
		}
	},

	CreateConfigDiv: function()
	{
		$('HandlerDivSelectionList').innerHTML = '';
		for (i = 0; i < this.Options.Columns.length; i++)
			{
				TempLi = document.createElement('li');
				ColumnsUl.insertBefore(TempLi, null);
				TempChk = document.createElement('input');
				TempChk.type = 'checkbox';
				TempChk.id = this.Table.id + 'ConfigChk' + i;
				TempLi.insertBefore(TempChk, null);
				TempLabel = document.createElement('label');
				TempLabel.innerHTML = this.Options.Columns[i].Title;
				TempLabel.htmlFor = TempChk.id;
				TempLabel.style.cursor = 'pointer';
				TempLi.insertBefore(TempLabel, null);
				TempLi.style.listStyle = 'none';
			}
	},

	CloseConfig: function(event)
	{
	    var Target = Event.element(event);

		if (Target.attributes['TableId'] == null) return;

		this.ConfigDiv.style.display = 'none';
	},

	SaveConfig: function (event)
	{
	    var Target = Event.element(event);

		if (Target.attributes['TableId'] == null) return;

		for (i = 0; i < this.Options.Columns.length; i++)
		{
			TempChk = $(this.Table.id + 'ConfigChk' + i);
			this.Options.Columns[i].Visible = TempChk.checked;
		}

		//(this.Options.SaveCallback)(this.Options.Columns);

		this.ConfigDiv.style.display = 'none';
		this.RemapColumns();

		(this.Options.SaveCallback)(this.Options.Columns);

		this.ResetTable();
		this.PopulateTable(this.CurrentPage);
	},

	GlobalSaveConfig: function (event)
	{
	    var Target = Event.element(event);

		if (Target.attributes['TableId'] == null) return;

		for (i = 0; i < this.Options.Columns.length; i++)
		{
			this.Options.Columns[i].Visible = this.Options.Columns[i].Visible;
		}

		//(this.Options.SaveCallback)(this.Options.Columns);

		this.ConfigDiv.style.display = 'none';
		this.RemapColumns();

		(this.Options.SaveCallback)(this.Options.Columns);

		this.Reload();
//		this.ResetTable();
//		this.PopulateTable(this.CurrentPage);
	},

	HandleConfigClick: function (event)
	{
	    var Target = Event.element(event);

		if (Target.attributes['TableId'] == null) return;

		for (i = 0; i < this.Options.Columns.length; i++)
		{
			TempChk = $(this.Table.id + 'ConfigChk' + i);
			if (this.Options.Columns[i].Visible)
				TempChk.checked = true;
			else
				TempChk.checked = false;
		}

		Coords = this.FindPos(Target);

		this.Clicked++;

		this.ConfigDiv.style.top = this.Options.CaptionPosition + 'px';

		if(this.Clicked % 2 != 0)
			this.ConfigDiv.style.left = (Coords[0] + Target.offsetWidth - this.ConfigDiv.clientWidth) + 'px';
		else
			this.ConfigDiv.style.left = (Coords[0] + Target.offsetWidth) + 'px';

		this.ConfigDiv.style.display = 'block';
	},

	CancelMouseDown: function (event)
	{
	    Event.stop(event);
	},

	FindPos: function (Object)
	{
		var CurLeft = 0;
		var CurTop = 0;

		if (Object.offsetParent) {
			CurLeft = Object.offsetLeft
			CurTop = Object.offsetTop
			while (Object = Object.offsetParent) {
				CurLeft += Object.offsetLeft
				CurTop += Object.offsetTop
			}
		}
		return [CurLeft, CurTop];
	},

	StartColumnMoveWait: function(event)
	{
	    var Target = Event.element(event);

		if (Target.attributes['TableId'] == null) return;

		var ColumnIndex = Target.attributes['ColumnIndex'].value;

		this.MovingColumn = parseInt(ColumnIndex);
		this.IsColumnMoving = false;

		this.SaveMouseMove = this.GetHandlerIfExists('onmousemove');
		this.SaveMouseUp = this.GetHandlerIfExists('onmouseup');
		this.SaveMozSelect = this.GetHandlerIfExists('onselectstart');

		this.SaveSelectStart = document.body.style.MozUserSelect;

		this.DoColumnMoveCache = this.DoColumnMove.bindAsEventListener(this);
		Event.observe(document.body, 'mousemove', this.DoColumnMoveCache);

		this.EndColumnMoveCache = this.EndColumnMove.bindAsEventListener(this);
		Event.observe(document.body, 'mouseup', this.EndColumnMoveCache);

		document.body.setAttribute('ColumnIndex', ColumnIndex);
		document.body.setAttribute('TableId', this.Table.id);

		Event.observe(document.body, 'selectstart', this.DoNothing.bindAsEventListener(this));

		document.body.style.MozUserSelect = 'none';

		//create ghost div for moving
		GhostMoveDiv$= document.createElement("div");
		GhostMoveDiv.innerHTML = Target.innerHTML;
		GhostMoveDiv.className = this.Options.ColumnDragClass;
		GhostMoveDiv.style.position = 'absolute';

		var Coords = this.FindPos(Target);

		GhostMoveDiv.style.left = Coords[0] + 'px';
		GhostMoveDiv.style.top = Coords[1] + 'px';
		GhostMoveDiv.style.width = this.Table.rows[0].cells[ColumnIndex].clientWidth + 'px';
		GhostMoveDiv.style.height = this.Table.rows[0].cells[ColumnIndex].clientHeight + 'px';

		document.body.insertBefore(GhostMoveDiv, null);

		x = Event.pointerX(event);
	    y = Event.pointerY(event);

		GhostMoveDiv.CursorStartX = x;
		GhostMoveDiv.CursorStartY = y;
		GhostMoveDiv.StartLeft  = Coords[0];
		GhostMoveDiv.StartTop   = Coords[1];
		if (isNaN(GhostMoveDiv.StartLeft)) GhostMoveDiv.StartLeft = 0;
		if (isNaN(GhostMoveDiv.StartTop)) GhostMoveDiv.StartTop  = 0;

		this.GhostMoveDiv = GhostMoveDiv;

		GhostMoveArrow = document.createElement("img");
		GhostMoveArrow.src = this.Options.ColumnMoveImage;
		GhostMoveArrow.style.display = 'none';
		GhostMoveArrow.style.position = 'absolute';

		document.body.insertBefore(GhostMoveArrow, null);

		this.GhostMoveArrow = GhostMoveArrow;
	},

	EndColumnMove: function(event)
	{
	    var Target = Event.element(event);

		var ColumnIndex = document.body.attributes['ColumnIndex'].value;

		Event.stopObserving(document.body, 'mousemove', this.DoColumnMoveCache);
		Event.stopObserving(document.body, 'mouseup', this.EndColumnMoveCache);

		//restore grid state from column move
		if((typeof this.GhostMoveArrow == 'object') && this.IsColumnMoving) document.body.removeChild(this.GhostMoveArrow);
		if((typeof this.GhostMoveDiv == 'object') && this.IsColumnMoving) document.body.removeChild(this.GhostMoveDiv);

		if (this.SaveMouseMove != false)
			Event.observe(document.body, 'mousemove', this.SaveMouseMove.bindAsEventListener(this));

		if (this.SaveMouseUp != false)
			Event.observe(document.body, 'mouseup', this.SaveMouseUp.bindAsEventListener(this));

		if (this.SaveMozSelect != false)
			Event.observe(document.body, 'selectstart', this.SaveMozSelect.bindAsEventListener(this));


		document.body.style.MozUserSelect = this.SaveSelectStart;

		if (!this.IsColumnMoving)
		{
			this.MovingColumn = -1;
			if(typeof this.GhostMoveDiv == 'object') document.body.removeChild(this.GhostMoveDiv);
			if(typeof this.GhostMoveArrow == 'object') document.body.removeChild(this.GhostMoveArrow);

			return;
		}
		else
		{
			x = Event.pointerX(event);
			y = Event.pointerY(event);

			ColumnMovedAfter = -1;
			for (i = 0; i < this.Table.rows[0].cells.length; i++)
			{
				var Coords = this.FindPos(this.Table.rows[0].cells[i]);
				var RowWidth = this.Table.rows[0].cells[i].clientWidth;
				if ((Coords[0] < x) && (Coords[0] + RowWidth > x))
					ColumnMovedAfter = i;

				this.Table.rows[0].cells[i].className = this.Options.HeaderPrefixClass + this.MappedColumns[i].Index.toString();
			}

			//handle actual column move
			if (ColumnMovedAfter >= 0) //dropped inside the table, so we can move the column
			{
				if (this.MovingColumn < ColumnMovedAfter)
				{
					Temp = this.MappedColumns[this.MovingColumn];
					for (i = this.MovingColumn; i < ColumnMovedAfter; i++)
						this.MappedColumns[i] = this.MappedColumns[i + 1];
					this.MappedColumns[ColumnMovedAfter] = Temp;
				}
				else if (this.MovingColumn > ColumnMovedAfter)
				{
					Temp = this.MappedColumns[this.MovingColumn];
					for (i = this.MovingColumn; i >= ColumnMovedAfter + 2; i--)
						this.MappedColumns[i] = this.MappedColumns[i - 1];
					this.MappedColumns[ColumnMovedAfter + 1] = Temp;
				}

				//this computes the first position available after the visible columns
				var NotVisiblePosition = 0
				for (i = 0; i < this.Options.Columns.length; i++)
					if (this.Options.Columns[i].Visible)
						NotVisiblePosition++;
				NotVisiblePosition++;

				//recompute column positions
				for (i = 0; i < this.Options.Columns.length; i++)
					if (this.Options.Columns[i].Visible)
					{
						for (j = 0; j < this.MappedColumns.length; j++)
							if (this.MappedColumns[j].Alias == this.Options.Columns[i].Alias)
								this.Options.Columns[i].Position = j;
					}
					else
					{
						this.Options.Columns[i].Position = NotVisiblePosition;
						NotVisiblePosition++;
						j--;
					}

			}

			this.ResetTable();
			this.RepaintGrid();
		}
	},

	Debug: function(DebugString, DebugObject)
	{
		DebugObject.innerHTML += DebugString.toJSONString() + "\n";
	},

	DoColumnMove: function(event)
	{
	    var Target = Event.element(event);

		var ColumnIndex = document.body.attributes['ColumnIndex'].value;

		this.IsColumnMoving = true;

		x = Event.pointerX(event);
	    y = Event.pointerY(event);

		this.GhostMoveDiv.style.left = (this.GhostMoveDiv.StartLeft + x - this.GhostMoveDiv.CursorStartX) + 'px';

		for (i = 0; i < this.Table.rows[0].cells.length; i++)
		{
			var Coords = this.FindPos(this.Table.rows[0].cells[i]);
			var RowWidth = this.Table.rows[0].cells[i].clientWidth;
			if ((Coords[0] < x) && (Coords[0] + RowWidth > x))
			{
				this.Table.rows[0].cells[i].className += ' ' + this.Options.DragOverClass;

				this.GhostMoveArrow.style.left = (Coords[0] + RowWidth - (this.GhostMoveArrow.clientWidth / 2)) + 'px';
				this.GhostMoveArrow.style.top = (Coords[1] - this.GhostMoveArrow.clientHeight) + 'px';
				this.GhostMoveArrow.style.display = 'block';
			}
			else
				this.Table.rows[0].cells[i].className = this.Options.HeaderPrefixClass + this.MappedColumns[i].Index.toString();
		}
	},

	StartColumnSizeWait: function(event)
	{
	    var Target = Event.element(event);

		if (Target.parentNode.attributes['TableId'] == null) return;

		var ColumnIndex = Target.parentNode.attributes['ColumnIndex'].value;

		this.SizingColumn = parseInt(ColumnIndex);
		this.IsColumnSizing = false;

		this.SaveMouseMove = this.GetHandlerIfExists('onmousemove');
		this.SaveMouseUp = this.GetHandlerIfExists('onmouseup');

		this.SaveBodyCursor = document.body.style.cursor;
		this.SaveMozSelect = this.GetHandlerIfExists('onselectstart');
		this.SaveSelectStart = document.body.style.MozUserSelect;

		this.DoColumnSizeCache = this.DoColumnSize.bindAsEventListener(this);
		Event.observe(document.body, 'mousemove', this.DoColumnSizeCache);

		this.EndColumnSizeCache = this.EndColumnSize.bindAsEventListener(this);
		Event.observe(document.body, 'mouseup', this.EndColumnSizeCache);

		document.body.style.cursor = 'e-resize';

		document.body.setAttribute('ColumnIndex', ColumnIndex);
		document.body.setAttribute('TableId', this.Table.id);
		Event.observe(document.body, 'selectstart', this.DoNothing.bindAsEventListener(this));
		document.body.style.MozUserSelect = 'none';

		//create ghost div for moving
		GhostSizeDiv = document.createElement("div");

		GhostSizeDiv.style.position = 'absolute';
		var Coords = this.FindPos(this.Table.rows[0].cells[ColumnIndex]);
		GhostSizeDiv.style.top = Coords[1] + 'px';
		GhostSizeDiv.style.left = Coords[0] + 'px';
		GhostSizeDiv.style.width = this.Table.rows[0].cells[ColumnIndex].clientWidth + 'px';
		GhostSizeDiv.style.height = (this.Table.clientHeight) + 'px';
		GhostSizeDiv.style.opacity = this.Options.ResizeInfo.Opacity / 100;
		GhostSizeDiv.style.filter = 'alpha(opacity=' + (this.Options.ResizeInfo.Opacity / 100) + ')';
		GhostSizeDiv.style.backgroundColor = this.Options.ResizeInfo.Color;

		document.body.insertBefore(GhostSizeDiv, null);

		x = Event.pointerX(event);
		GhostSizeDiv.CursorStartX = x;
		GhostSizeDiv.StartWidth  = this.Table.rows[0].cells[ColumnIndex].clientWidth;

		this.GhostSizeDiv = GhostSizeDiv;
	},

	EndColumnSize: function(event)
	{
	    var Target = Event.element(event);

		var ColumnIndex = document.body.attributes['ColumnIndex'].value;

		Event.stopObserving(document.body, 'mousemove', this.DoColumnSizeCache);
		Event.stopObserving(document.body, 'mouseup', this.EndColumnSizeCache);

		if(typeof this.GhostSizeDiv == 'object') document.body.removeChild(this.GhostSizeDiv);
					document.body.style.cursor = this.SaveBodyCursor;

		if (!this.IsColumnSizing)
		{
			this.SizingColumn = -1;
			return;
		}
		else
		{
			//restore grid state from column size

			if(this.SaveMouseMove != false)
			{
				Event.observe(document.body, 'mousemove', this.SaveMouseMove.bindAsEventListener(this));
			}
			if(this.SaveMouseUp != false)
			{
				Event.observe(document.body, 'mouseup', this.SaveMouseUp.bindAsEventListener(this));
			}

			if(this.SaveMozSelect != false)
			{
				Event.observe(document.body, 'selectstart', this.SaveMozSelect.bindAsEventListener(this));
			}

			document.body.style.MozUserSelect = this.SaveSelectStart;

			x = Event.pointerX(event);
			y = Event.pointerY(event);

			var ComputedWidth = parseInt(this.GhostSizeDiv.style.width);
			ComputedWidth = isNaN(ComputedWidth) ? 0 : ComputedWidth;

			this.Options.Columns[this.MappedColumns[ColumnIndex].Index].Width = ComputedWidth;

			this.ResetTable();
			this.RepaintGrid();
		}
	},

	DoColumnSize: function(event)
	{
	    var Target = Event.element(event);

		var ColumnIndex = document.body.attributes['ColumnIndex'].value;

		this.IsColumnSizing = true;

		x = Event.pointerX(event);
	    y = Event.pointerY(event);

		if ((this.GhostSizeDiv.StartWidth + x - this.GhostSizeDiv.CursorStartX) < this.Options.ResizeInfo.MinWidth)
			this.GhostSizeDiv.style.width = this.Options.ResizeInfo.MinWidth + 'px';
		else
			this.GhostSizeDiv.style.width = (this.GhostSizeDiv.StartWidth + x - this.GhostSizeDiv.CursorStartX) + 'px';

	},

	HandleClick: function(event)
	{
		var Target = Event.element(event);

		var ColumnIndex = Target.attributes['ColumnIndex'].value;
		this.SortColumn = this.Options.Columns[ColumnIndex].Alias;
		this.SortDir = this.Options.Columns[ColumnIndex].SortDir;
		this.Grouping = this.Options.Columns[ColumnIndex].Grouping;
		this.CurrentPage = 0; //always start from first page when sorting
		this.PopulateTable(this.CurrentPage);

		if (this.Options.Columns[ColumnIndex].SortDir == 'ASC')
			this.Options.Columns[ColumnIndex].SortDir = 'DESC';
		else
			this.Options.Columns[ColumnIndex].SortDir = 'ASC';

		Event.stop(event);
	},

	PopulateTable: function(PageNo)
	{
		var PageParams = 'Start=' + (PageNo * this.Options.RowsPerPage ) + '&Rows=' + this.Options.RowsPerPage;
		var SortSign = ((this.SortDir == 'ASC') ? '+' : '-');

		if (this.SortColumn != '')
		{
			if (!this.Options.LegacySortMode)
			{
				PageParams = PageParams + '&Sort=' + SortSign + this.SortColumn;
			}
			else
			{
				PageParams += '&Sort=' + this.SortColumn;
				if (this.SortDir == 'ASC')
					PageParams += '&Dir=ASC';
				else
					PageParams += '&Dir=DESC';
			}
		}

		var Cols = '';
		for (i = 0; i < this.Options.Columns.length; i++)
			if (this.Options.Columns[i].Visible)
				Cols += this.Options.Columns[i].Alias + ';';

		PageParams += '&Cols=' + Cols;

		// Display loading message
		if (IsFunction(this.Options.LoadingMessageFunction))
			(this.Options.LoadingMessageFunction)(true);

		var AjaxRequest = new Ajax.Request(
								this.Options.PageScript,
								{
									method: 'get',
									parameters: PageParams,
									onSuccess: this.HandlePopulateTable.bindAsEventListener(this)
								}
							);
	},

	HandlePopulateTable: function(response)
	{
		if (!response) return;

		// User can set an onFailure option - which will be called by prototype
		if (response.status != 200) return;

		this.AjaxResponse = response.responseText.strip().evalJSON();

		if (this.AjaxResponse == null) return;
		this.TotalRows = this.AjaxResponse.TotalRows;
		this.RepaintGrid();
	},

	RepaintGrid: function()
	{
		var DelRow = this.ComputeRowStart();

		this.RePopulateCaption();

		//always start with empty table if grouping
		if (this.Grouping)
		{
			var PageLength = 0;

			if (this.Options.HasFooter)
				PageLength++;
			if (this.Options.HasHeader)
				PageLength++;

			while (parseInt(PageLength) < this.ComputeTableLength())
				this.Table.deleteRow(DelRow);

		}
		else
		{
			var PageLength = this.AjaxResponse.Rows.length;

			if (this.Options.HasFooter)
				PageLength++;
			if (this.Options.HasHeader)
				PageLength++;

			while (parseInt(PageLength) < this.ComputeTableLength())
				this.Table.deleteRow(DelRow);
		}

		//iterate rows
		var RowIndex = this.ComputeRowStart();
		var Rows = this.AjaxResponse.Rows;

		var OldGroup = '';

		for ( var i = 0 ; i < Rows.length ; i++ )
		{
			var RowElement = Rows[i];

			var AbsoluteRowIndex = RowIndex;
			if (this.Options.HasHeader)
				AbsoluteRowIndex++;
			if (this.Options.HasFooter)
				AbsoluteRowIndex++;

			if (AbsoluteRowIndex > this.ComputeTableLength())
				NewRow = this.Table.insertRow(RowIndex);
			else
				NewRow = this.TableRows[RowIndex];

			//if grouping and found a new group
			if (this.Grouping && (OldGroup != Rows[i][this.SortColumn]))
			{
				if (NewRow.cells.length > 0)
				{
					while (NewRow.cells.length > 1)
						NewRow.cells.deleteCell();
				}
				else
					NewRow.insertCell(-1);

				//grab sort column name
				var SortColumnName = '';
				for (k = 0; k < this.Options.Columns.length; k++)
				{
					if (this.Options.Columns[k].Alias == this.SortColumn)
						SortColumnName = this.Options.Columns[k].Title + ': ';
				}

				var ColSpan = 0;
				for (elem in Rows[0])
					ColSpan++;

				NewRow.cells[0].colSpan = ColSpan;
				NewRow.cells[0].innerHTML = SortColumnName + Rows[i][this.SortColumn];
				NewRow.cells[0].className = this.Options.GroupClass;

				OldGroup = Rows[i][this.SortColumn];
				RowIndex++;
				i--;
				continue;
			}

			var ColIndex = 0;

			for ( var j = 0, k = 0; k < this.Options.Columns.length; j++, k++ )
			{
				if (!this.Options.Columns[k].Visible)
				{
					j--;
					continue;
				}

				var ColElement = RowElement[this.MappedColumns[j].Alias];

				if (NewRow.cells.length <= ColIndex)
					NewRow.insertCell(-1);
				else
					if (NewRow.cells[ColIndex].colSpan > 1)
						NewRow.cells[ColIndex].colSpan = 1;

				var ColumnClass = '';

				if(this.Options.Columns[k].Class)
					ColumnClass = this.Options.Columns[k].Class;
				else if (this.Options.ColumnPrefixClass != '')
					ColumnClass = this.Options.ColumnPrefixClass + this.MappedColumns[ColIndex].Index;

				if ((RowIndex % 2) == 0)
				{
					if (this.Options.EvenClass != '')
					{
						if (ColumnClass != '')
							NewRow.cells[ColIndex].className = this.Options.EvenClass + ' ' + ColumnClass;
						else
							NewRow.cells[ColIndex].className = this.Options.EvenClass;
					}
					else
					{
						if (ColumnClass != '')
							NewRow.cells[ColIndex].className = ColumnClass;
					}

				}
				else
				{
					if (this.Options.OddClass != '')
					{
						if (ColumnClass != '')
							NewRow.cells[ColIndex].className = this.Options.OddClass + ' ' + ColumnClass;
						else
							NewRow.cells[ColIndex].className = this.Options.OddClass;
					}
					else
					{
						if (ColumnClass != '')
							NewRow.cells[ColIndex].className = ColumnClass;
					}
				}

				//apply halign for columns

				if(this.Options.Columns[ColIndex].Halign == 'left')
				{
					NewRow.cells[ColIndex].setAttribute('style', "text-align: left; padding-left:" + this.Options.LeftRightPadding + "px");
				}
				else
				{
					if(this.Options.Columns[ColIndex].Halign == 'right')
					{
						NewRow.cells[ColIndex].setAttribute('style', "text-align: right; padding-right: " + this.Options.LeftRightPadding + "px");
					}
					else
					{
						NewRow.cells[ColIndex].setAttribute('style', "text-align: center;");
					}
				}


				switch(typeof ColElement)
				{
					case 'string':
						ColElement = ColElement.strip();
						break;
					case 'number':
						ColElement = ColElement.toPaddedString(1);
						break;
					case 'object':
					case 'boolean':
					default:
						ColElement = '&nbsp;**';
						break;
				}

				NewRow.cells[ColIndex].innerHTML = ColElement;

				ColIndex++;
			}

			if (IsFunction(this.Options.CallBack))
			{
				(this.Options.CallBack)(NewRow, this.AjaxResponse, i);
			}

			RowIndex++;
		}

		this.HandlePopulateFooter(this.AjaxResponse.TotalRows);

		// Hides loading message
		if (IsFunction(this.Options.LoadingMessageFunction))
		{
			(this.Options.LoadingMessageFunction)(false);
		}

		if (IsFunction(this.Options.GlobalCallBack))
		{
			(this.Options.GlobalCallBack)(this, this.AjaxResponse);
		}
	},


	AppendAnchor: function(Parent, Href, InnerHtml, Page, OnClick)
	{
		var Anch = document.createElement('a');
		Anch.href = Href;
		Anch.innerHTML = InnerHtml;
		Anch.setAttribute('Page', Page);
		Anch.setAttribute('TableId', this.Table.id);
		Parent.appendChild(Anch);

		Event.observe(Anch, 'click', this.HandlePage.bindAsEventListener(this));
		if (this.Options.FooterAClass != '') Anch.className = this.Options.FooterAClass;

		this.AppendLabel(Parent, '&nbsp;');
	},

	AppendLabel: function(Parent, InnerHtml)
	{
		var Label = document.createElement('label');
		Label.innerHTML = InnerHtml;
		Parent.appendChild(Label);
	},

	AppendSelect: function(Parent, InnerHtml)
	{
		var Select = document.createElement('select');
		Select.innerHTML = InnerHtml;
		Parent.appendChild(Select);
		Select.setAttribute('TableId', this.Table.id);

		return Select;
	},

	AppendImage: function(Parent, ImageSource)
	{
		var Img = document.createElement('img');
		Img.src = ImageSource;
		Parent.appendChild(Img);
	},

	HandlePopulateFooter: function(TotalRows)
	{
		if (!this.Options.HasFooter)
			return;

		var ColSpan = 0;
		for (i = 0; i < this.Options.Columns.length; i++)
		{
			if (this.Options.Columns[i].Visible)
			{
				ColSpan++;
			}
		}

		if (!this.FooterRow)
		{
			this.FooterRow = this.Table.insertRow(-1);
			this.FooterRow.insertCell(-1);
			this.FooterRow.cells[0].colSpan = ColSpan;
		}


		var MaxPages = Math.ceil(TotalRows / this.Options.RowsPerPage) - 1;

		if(isNaN(MaxPages))
		{
			MaxPages = 0;
		}

		// If this is page 1 of 1 don't display the footer at all
		if( (MaxPages <= 0) && this.Options.FooterWhenNeeded)
			return;

		if (this.Options.FooterClass != '')
			this.FooterRow.cells[0].className = this.Options.FooterClass;

		this.FooterRow.cells[0].innerHTML = '';
		var LeftFooter = document.createElement('SPAN');
		if(! Prototype.Browser.IE)
		{
			LeftFooter.setStyle({
				'cssFloat': 'left'
				});
		}

		LeftFooter.innerHTML = this.Options.RowsPerPageText;

		this.FooterRow.cells[0].appendChild(LeftFooter);

		SelectRowsPerPage = '';
		if (this.Options.HasPageNo)
		{
			for ( i = 10; i <= this.Options.MaxRowsPerPage; i = i + 10)
			{
				Selected = (i == this.Options.RowsPerPage) ? ' selected="selected"' : '';
				SelectRowsPerPage += ' <option value="' + i + '" ' + Selected + '>' + i + '</option>';
			}

			Select = this.AppendSelect(LeftFooter, SelectRowsPerPage);
			Event.observe(Select, 'change', this.ViewRowsPerPage.bindAsEventListener(this));
		}

		// Hides loading message
		if (IsFunction(this.Options.LoadingMessageFunction))
		{
			(this.Options.LoadingMessageFunction)(false);
		}

		var RightFooter = document.createElement('SPAN');

		this.FooterRow.cells[0].appendChild(RightFooter);

		if(! Prototype.Browser.IE)
		{
			RightFooter.setStyle({
				'cssFloat': 'right'
				});
		}

		if (this.CurrentPage > 0)
		{
			if (this.Options.HasFirst)
				this.AppendAnchor(RightFooter, '#', '[<< ' + this.Options.FirstText + ']', '0', this.HandlePage);
			if (this.Options.HasPrev)
				this.AppendAnchor(RightFooter, '#', '[< ' + this.Options.PrevText + ']', parseInt(this.CurrentPage) - 1, this.HandlePage);
		}
		else
		{
			if (this.Options.HasFirst)
				this.AppendLabel(RightFooter, '[<< ' + this.Options.FirstText + ']&nbsp;');
			if (this.Options.HasPrev)
				this.AppendLabel(RightFooter, '[< ' + this.Options.PrevText + ']&nbsp;');
		}

		SelectPage = '';
		if (this.Options.HasPageNo)
		{

			for ( i = 1; i <= parseInt(MaxPages) + 1; i++)
			{
				SelectPage += '<option value = "' + i + '">' + i + '</option>';
			}

			// Select = this.AppendSelect(RightFooter, SelectPage);
			//Event.observe(Select, 'change', this.GoToSelectedPage.bindAsEventListener(this));
			//Select.value = parseInt(this.CurrentPage) + 1;

			this.AppendLabel(RightFooter, (parseInt(this.CurrentPage) + 1) + ' / ' + (parseInt(MaxPages) + 1) + '&nbsp;');
		}

		if (this.CurrentPage < MaxPages)
		{
			if (this.Options.HasNext)
				this.AppendAnchor(RightFooter, '#', '[' + this.Options.NextText + '>]', parseInt(this.CurrentPage) + 1, this.HandlePage);
			if (this.Options.HasLast)
				this.AppendAnchor(RightFooter, '#', '[' + this.Options.LastText + '>>]', MaxPages, this.HandlePage);
		}
		else
		{
			if (this.Options.HasNext)
				this.AppendLabel(RightFooter, '[' + this.Options.NextText + ' >]&nbsp;');
			if (this.Options.HasLast)
				this.AppendLabel(RightFooter, '[' + this.Options.LastText + ' >>]&nbsp;');
		}

		this.PerPageNumbering(this.AjaxResponse.TotalRows, this.CurrentPage, this.Options.RowsPerPage);
	},

	PerPageNumbering: function(TotalNr, CurrentPage, RowsPerPage)
	{
		var MyElements = Array();

		CurrentPage = parseInt(CurrentPage) + 1;

		AccrRest = TotalNr % RowsPerPage;
		Pages = (TotalNr - AccrRest) / RowsPerPage;

		if(Pages == 0)
		{
			if(TotalNr != 0)
			{
				FirstEl = parseInt(parseInt(parseInt(CurrentPage) - 1) * RowsPerPage) + 1;
				LastEl = parseInt(FirstEl) + parseInt(parseInt(AccrRest) - 1);
			}
			else
			{
				FirstEl = 0;
				LastEl = 0;
			}
		}
		else if(CurrentPage == 1)
		{
			FirstEl = CurrentPage;

			if(TotalNr != RowsPerPage)
				LastEl = parseInt(parseInt(FirstEl) + RowsPerPage) - 1;
			else
				LastEl = TotalNr;
		}
		else if(CurrentPage == (Pages + 1))
		{
			FirstEl = parseInt(parseInt(parseInt(CurrentPage) - 1) * RowsPerPage) + 1;
			LastEl = parseInt(FirstEl) + parseInt(parseInt(AccrRest) - 1);
		}
		else
		{
			FirstEl = parseInt(parseInt(parseInt(CurrentPage) - 1) * RowsPerPage) + 1;
			LastEl = parseInt(FirstEl) + (RowsPerPage - 1);
		}

		MyElements[0] = parseInt(FirstEl);
		MyElements[1] = parseInt(LastEl);

		if(this.Options.CaptionElements[0])
		{
			this.CaptionLeftDiv.appendChild(HandlerDiv);
		}

		if(this.Options.CaptionElements[2])
		{
			var ct = this.Options.CaptionText.replace('#1',  (MyElements[0] + ' - ' + MyElements[1]).bold() );
			ct = ct.replace('#2', TotalNr.bold());

			if(this.CaptionRightDiv != undefined)
			{
				this.CaptionRightDiv.innerHTML = ct;
			}
		}
	},


	ViewRowsPerPage: function(event)
	{
		 var Target = Event.element(event);

		 if(parseInt(Target.value) != -1)
		 	this.Options.RowsPerPage = parseInt(Target.value);
		 else
		 	this.Options.RowsPerPage = this.AjaxResponse.TotalRows;

		 this.Reload();
	},


	HandlePage: function(event)
	{
	    var Target = Event.element(event);

		this.CurrentPage = Target.attributes['Page'].value;
		this.PopulateTable(this.CurrentPage);
	},

	Reload: function()
	{
		this.PopulateTable(this.CurrentPage);
	},

	GoToSelectedPage : function(event)
	{
	    var Target = Event.element(event);

		if (Target.attributes['TableId'] == null) return;

		this.CurrentPage = parseInt(Target.value) - 1;
		this.Reload();
	},

	DoNothing: function()	// this function intentionally does nothing
	{
		return false;
	},

	GetHandlerIfExists: function(HandlerName)
	{
		var TheHandler = document.body.HandlerName;
		if(typeof TheHandler != 'undefined' && TheHandler != null)
		{
			return TheHandler;
		}
		return false;
	},

	RePopulateCaption: function()
	{
		if( ! this.Options.HasCaption)
		{
			return;
		}

		var AllCaptions = this.Table.getElementsByTagName('CAPTION');
		if(AllCaptions.length >= 1)
		{
			for(var j = 0; j <= AllCaptions.length; j++)
			{
				this.Table.removeChild(this.Table.getElementsByTagName('CAPTION')[0]);
			}
		}

		var TableCaption = document.createElement('CAPTION');
		if(this.Options.CaptionClass != '')
			TableCaption.className = this.Options.CaptionClass;

		if(this.Options.CaptionElements[0])
		{
			var LeftDiv = document.createElement('SPAN');
			if(! Prototype.Browser.IE)
			{
				LeftDiv.setStyle({
					'cssFloat': 'left'
					});
			}
			this.CaptionLeftDiv = LeftDiv;
			TableCaption.appendChild(LeftDiv);
		}

		if(this.Options.CaptionElements[1])
		{
			SaveGlobalButton = document.createElement('input');
			SaveGlobalButton.type = 'button';
			SaveGlobalButton.value = this.Options.HandlerInfo.Config.GlobalSaveText;
			SaveGlobalButton.className = this.Options.HandlerInfo.Config.GlobalSaveClass;
			Event.observe(SaveGlobalButton, 'click', this.GlobalSaveConfig.bindAsEventListener(this));
			SaveGlobalButton.setAttribute('TableId', this.Table.id);
			TableCaption.appendChild(SaveGlobalButton);
		}

		if(this.Options.CaptionElements[2])
		{
			var RightDiv = document.createElement('SPAN');
			if(! Prototype.Browser.IE)
			{
				RightDiv.setStyle({
					'cssFloat': 'right'
					});
			}
			this.CaptionRightDiv = RightDiv;
			TableCaption.appendChild(RightDiv);
		}

		if(this.Table.hasChildNodes())
		{
			this.Table.insertBefore(TableCaption, this.Table.firstChild);
		}
		else
		{
			this.Table.appendChild(TableCaption);
		}
	}
}
