/**
* @copyright ScanFrame 2005-2010, All Rights Reserved.
* @author avo <dvlp@scanframe.nl>
* @package ScanFrame
* @category Page Classes
* @version $Revision: 1.13 $
* @cvs_source $Source: /export/cvs/primo/com/jscripts/listtable.js,v $
* @cvs_author $Author: arjan $
* @purpose Class for handling listables.
*/
//---------------------------------------------------------------------------
/**
* Table list constructor.
* @constructor
*/
function TableListObject(id, has_keycol, pagehref, action, multiselect)
{
	/**
	* Assign the name of the table and the associated script object.
	* @type string
	*/
	this.Id = id;
	/**
	* Same as 'Id' for backwards compatibility.
	* @depricated
	* @type string
	*/
	this.id = id;
	/**
	* @description Reference to this objects url for retrieviong a partial HTML.
	* @private
	* @type string
	*/
	this.PageHref = pagehref;
	/**
	* Holds the action string.
	* @type string
	*/
	this.Action = action;
	/**
	* Flag not set when action events are not allowed.
	* @type boolean
	*/
	this.HasKeyCol = has_keycol;
	/**
	* Flag making the list have multi selectable rows.
	* @type boolean
	*/
	this.MultiSelect =(typeof multiselect == 'undefined') ? true : multiselect;
	/**
	* Flag for closing the dialog with ok event on dblclick.
	* @type boolean
	*/
	this.DblClickOk = true;
	/**
	* When this flag is true the a row click is skipped.
	* @type boolean
	*/
	this.SkipCheckedChange = false;
	/**
	* Flag which prevents the Initialize function to be called more then once.
	* @type boolean
	*/
	this.Initialized = false;
	/**
	* Called from an event.Initialize
	* @returns void
	*/
	this.Initialize = function()
	{
		if (this.Initialized == false)
		{
			this.Initialized = true;
			this.ActionHandler(self, 'init');
		}
		this.ResizeInputs();
	}
	/**
	* Called from a selection checkbox.
	* @returns void
	*/
	this.CheckBoxClick = function(chkbox)
	{
		// Skip call in other handler.
		this.SkipCheckedChange = true;
		this.SetClassName(chkbox.parentNode.parentNode, chkbox.checked);
		// When single select another checked row should be unselected
		if (!this.MultiSelect)
		{
			var checkboxes = document.getElementById(this.Id + '_body').getElementsByTagName('input');
			for (var i = 0; i < checkboxes.length; i++ )
				if (checkboxes[i].type == 'checkbox' && checkboxes[i].checked && chkbox != checkboxes[i])
					{
						checkboxes[i].checked = false;
						this.SetClassName(checkboxes[i].parentNode.parentNode, false);
					}
		}
		// Call a handler if one exists.
		this.HandleAction(this, 'selchange');
	}
	/**
	* Called when a row is clicked or double clicked.
	* @returns void
	*/
	this.RowClick = function(row, double)
	{
		// Was this a double click event.
		if (double)
		{
			// Check if double clicked was done in a modal dialog.
			if (this.DblClickOk && Dialog.IsModal && !this.MultiSelect)
			{
				this.SetSelected(row.getAttribute('rowid'));
				Dialog.EvButtonClick('ok');
			}
			// Call a handler if one exists.
			this.HandleAction(this, 'dblclick', row.getAttribute('rowid'));
		}
		else
		{
			// Skip the change of the checked flag and continue.
			if (this.SkipCheckedChange)
				this.SkipCheckedChange = false;
			else
				// Toggle the selection.
				this.RowSelect(row, 0, true);
			// Call a handler if one exists.
			this.HandleAction(this, 'click', row.getAttribute('rowid'));
		}
	}
	/**
	* Set/Unset/Toggle the selection of a row.
	* @param HTMLElement
	* @param integer
	* @param boolean
	* @returns void
	*/
	this.RowSelect = function(row, how, over)
	{
		var changed = false;
		var elem = document.getElementById(this.Id + '_row_id_' + row.getAttribute('rowid'));
		if (elem)
		switch (how)
		{
			// Toggle the row.
			case 0:
				if (!this.MultiSelect && !elem.checked)
					this.SetSelection(false, true)
				changed = true;
				elem.checked = !elem.checked;
				break;

			// Select the row.
			case 1:
				if (!this.MultiSelect)
					this.SetSelection(false, true)
				changed = elem.checked != true;
				elem.checked = true;
				break;

			// Unselect the row.
			case -1:
				changed = elem.checked != false;
				elem.checked = false;
				break;
		}
		// Set the class name of the row depending on the selection status.
		this.SetClassName(row, over);
		// Call a selection change handler if one exists.
		if (changed)
			this.HandleAction(this, 'selchange');
	}
	/**
	* Returns true when the passed row is selected.
	* @param object
	*/
	this.IsSelected = function(row)
	{
		var elem = document.getElementById(this.Id + '_row_id_' + row.getAttribute('rowid'));
		if (elem)
			return elem.checked;
		return false;
	}
	/**
	* Returns an array of selected row ID's.
	* @param boolean single
	* @return Array/string
	*/
	this.GetSelected = function(single)
	{
		var retval = new Array();
		var checkboxes = document.getElementById(this.Id + '_body').getElementsByTagName('input');
		for (var i = 0; i < checkboxes.length; i++ )
			if (checkboxes[i].type == 'checkbox')
				if (checkboxes[i].checked)
					retval.push(checkboxes[i].value);
	 	// When single value is required. Return the first of the array.
		if (single)
			return retval.length ? retval[0] : '';
		// Normal operation return all of them
		return retval;
	}
	/**
	* Selects rows identified by the given row id's in an array or a scalar value.
	* Returns true when there was a change.
	* @param Array/scalar row_ids
	* @returns boolean
	*/
	this.SetSelected = function(row_ids)
	{
		var change = false;
		// Create an array when none array value is psssed.
		if (typeof row_ids != 'Array')
			row_ids = new Array(row_ids);
		// Only allow selection of a single row.
		if (!this.MultiSelect && row_ids.length > 1 )
			row_ids = new Array(row_ids[1]);
		//
		var checkboxes = document.getElementById(this.Id + '_body').getElementsByTagName('input');
		for (var i = 0; i < checkboxes.length; i++ )
			if (checkboxes[i].type == 'checkbox')
			{
				var checked = in_array(checkboxes[i].value, row_ids);
				// Check for a change.
				if (checkboxes[i].checked != checked)
				{
					// Assign the correct value.
					checkboxes[i].checked = checked;
					// Set the right color selection.
					this.SetClassName(checkboxes[i].parentNode.parentNode, false);
					// Set the flag to call the handler.
					change = true;
				}
			}
		// When an change in selection accurred call the handler if one exists.
		if (change)
			this.HandleAction(this, 'selchange');
		return change;
	}
	/**
	* Selects or deselects all rows.
	*/
	this.SetSelection = function(checked, skipevent)
	{
		var changed = false;
		var checkboxes = document.getElementById(this.Id + '_body').getElementsByTagName('input');
		for (var i = 0; i < checkboxes.length; i++ )
			if (checkboxes[i].type == 'checkbox')
				// Only update the ones that need to be changed.
				if ((checkboxes[i].checked > 0) != checked)
				{
					checkboxes[i].checked = checked;
					this.SetClassName(checkboxes[i].parentNode.parentNode, false);
					changed = true;
				}
		// Call a selection change handler if one exists.
		if (changed && !skipevent)
			this.HandleAction(this, 'selchange');
		// Signal ifd something has changed.
		return changed;
	}
	/**
	* Handles mouse movements over each row.
	* @param HTMLElement
	* @param string
	* @param boolean
	*/
	this.MouseOverOut = function(row, over)
	{
		this.SetClassName(row, over);
		this.HandleAction(this, 'overout', row.getAttribute('rowid'), over);
	}
	/**
	* Sets the right class attribute for a row.
	* @param HTMLElement
	* @param string
	* @param boolean
	*/
	this.SetClassName = function(row, over)
	{
		if (this.IsSelected(row))
			row.className = row.getAttribute(over ? 'classselover' : 'classsel');
		else
			row.className = row.getAttribute(over ? 'classover' : 'classnorm');
	}
	/**
	* Generates the url encoded string for the selected ID's in comma separated form.
	* @public
	* @returns string
	*/
	this.GetUrlFromIds = function(ids)
	{
		if (typeof ids == 'undefined')
			ids = this.GetSelected();
		var retval = "";
		for (i in ids)
		{
			// Add separator when needed.
			if (retval.length)
				retval += ';';
			retval += ids[i];
		}
		return escape(retval);
	}
	/**
	* Called from the button in the table list.
	* @private
	* @returns void
	*/
	this.HandleAction = function(self, action, row_id, param1, param2, param3)
	{
		// Check the flag be fore executing.
		if (this.HasKeyCol)
			return this.ActionHandler(self, action, row_id, param1, param2, param3);
		return false;
	}
	/**
	* Calls the assigned action handler when availeble/implemented.
	* @private
	* @returns void
	*/
	this.ActionHandler = function(self, action, row_id, param1, param2, param3)
	{
		var func = 'this.ActionHandler_' + action;
		if (eval('typeof(' + func + ')') == 'function')
			return eval(func + '(self, row_id, param1, param2, param3)')
		//
		DebugOut("No handler available for action '" + action + "' with row ID '" +
			row_id + "'on TableList object '" + this.Id + "'.");
		//
		return false;
	}
	/**
	* Action handler which can be overloaded.
	* @returns void
	*/
	this.ActionHandler_overout = function(self, row_id) {}
	/**
	* Action handler which can be overloaded.
	* @returns void
	*/
	this.ActionHandler_click = function(self, row_id) {}
	/**
	* Action handler which can be overloaded.
	* @returns void
	*/
	this.ActionHandler_dblclick = function(self, row_id) {}
	/**
	* Action handler which can be overloaded.
	* @returns void
	*/
	this.ActionHandler_selected = function(self, row_id) {}
	/**
	* Action handler which can be overloaded.
	* @returns void
	*/
	this.ActionHandler_selchange = function(self, row_id) {}
	/**
	* Action handler which can be overloaded.
	* @returns void
	*/
	this.ActionHandler_init = function(self, row_id) {}
	/**
	* Show the previous page.
	* @param object Object responsible for calling the function.
	* @returns void
	*/
	this.PagePrev = function(self)
	{
		var elem = document.getElementById(this.Id + '_pageprev');
		elem.click();
	}
	/**
	* Show the next page.
	* @param object Object responsible for calling the function.
	* @returns void
	*/
	this.PageNext = function(self)
	{
		var elem = document.getElementById(this.Id + '_pagenext');
		elem.click();
	}
	/**
	* Updates the table html using a post query.
	* @param string
	* @returns void
	*/
	this.UpdateByQuery = function(query)
	{
		// Check if there are rows selected and set the flag too trigger a selection change.
		var selected = this.GetSelected().length > 0;
		// Relay the query.
		SetInnerHtmlByHttpPost('dynamic_' + this.Id, query);
		// Signal a selection change.
		if (selected)
			this.HandleAction(this, 'selchange');
	}
	/**
	* Updates the list keeping or not keeping the current selection.
	* @param boolean
	* @returns void
	*/
	this.Submit = function(keepselection)
	{
		var selected = null;
		if (keepselection)
			selected = this.GetSelected();
		var url = '?' + GetFormPostQuery('form_' + this.Id) + '&pagesection=' + this.Id;
		this.UpdateByQuery(url);
		this.ResizeInputs();
		if (keepselection)
			this.SetSelected(selected);
	}
	/**
	* Clear all filters and ordering.
	* @returns void
	*/
	this.Clear = function(self)
	{
		var indexmax = document.forms.namedItem('form_' + this.Id).indexmax.value;
		this.UpdateByQuery(this.PageHref + '&action=' + this.Action + '&indexmax=' +
			indexmax + '&pagesection=' + this.Id);
		this.ResizeInputs();
	}
	/**
	* Clears the table order.
	* @returns void
	*/
	this.ResetOrder = function(self)
	{
		var theform = document.forms.namedItem('form_' + this.Id);
		// Clear the order inputs.
		theform['orderby'].value = '';
		theform['order'].value = '';
		// Submit the inputs to refresh the list.
		this.Submit();
	}
	/**
	* Clears the column filters.
	* @returns void
	*/
	this.ResetColumnFilters = function(self)
	{
		var theform = document.forms.namedItem('form_' + this.Id);
		// Cannot be replace using for ... in statement!!!!
		for (e = 0; e < theform.elements.length; e++)
		{
			// Column filters have a prefix 's_'.
			if (theform.elements[e].name.match(/^s_.+/))
			{
				if (theform.elements[e].type == 'text')
					theform.elements[e].value = '';
				if (theform.elements[e].type == 'select-one')
					theform.elements[e].selectedIndex = 0;
			}
		}
		this.Submit();
	}
	/**
	* Could be used to handle keys to control the list.
	* @param string NAme of the key.
	* @returns void
	*/
	this.DoKey = function(keyname)
	{
		switch (keyname)
		{
			case 'S+PgUp': this.PagePrev(null);	return true;
			case 'S+PgDown': this.PageNext(null);	return true;
		}
		return false;
	}
	/**
	* Resize the inputs in the header part.
	* @returns void
	*/
	this.ResizeInputs = function()
	{
		try
		{
			// Seems to be resolved by setting width per input to 100% in the
			// style attribute.
			/*
			this.DoResizeInputs();
			*/
		}
		catch (e)
		{
			DebugOut("An exception occurred in the script. Error name: " +
				e.name	+ ". Error message: " + e.message, true);
		}
	}
	/**
	* Optional but not used.
	* @returns void
	*/
	this.DoResizeInputs = function()
	{
		var table_head = document.getElementById(this.Id + '_head');
		// Array to hold the elements.
		var elems = new Array();
		// Get all elements of type 'input' starting with the name prefix 's_'.
		var tmp = table_head.getElementsByTagName('input');
		for (var i = 0; i < tmp.length; i++)
		if (tmp[i].type == 'text' && tmp[i].name.substr(0, 2) == 's_')
			elems.push(tmp[i]);
		// Get all elements of type 'select' starting with the name prefix 's_'.
		tmp = table_head.getElementsByTagName('select');
		for (var i = 0; i < tmp.length; i++)
		if (tmp[i].name.substr(0, 2) == 's_')
			elems.push(tmp[i]);
		for (var j = 0; j < 2; j++)
		{
			// Save the current width.
			var widths = new Array();
			for (var i in elems)
				widths[i] = elems[i].parentNode.offsetWidth;
			// Set the width according the saved ones.
			for (var i = 0; i < elems.length; i++)
				elems[i].style.width = widths[i];
		}
	}
	/**
	* Sets the filter name and submits the form.
	* Optional but not used.
	* @param string Filter name
	* @returns void
	*/
	this.SetFilter = function(name)
	{
		var form = document.forms.namedItem('form_' + this.Id);
		if (form)
		{
			// To perfom backwards compatibility for this function.
			if (name.indexOf(':') > 0)
				return this.SetFilterKey(name);
			form.filter.value = name;
			this.Submit();
		}
	}
	/**
	* Sets the key filter value and submits the form.
	* Optional but not used.
	* @param string Filter key and value combination.
	* @returns void
	*/
	this.SetFilterKey = function(key_value)
	{
		var form = document.forms.namedItem('form_' + this.Id);
		if (form)
		{
			form.filterkey.value = key_value;
			this.Submit();
		}
	}
	/**
	* Selects a row using the position in the current list.
	* @param integer
	* @param string
	* @returns void
	*/
	this.SelectByIndex = function(index, value)
	{
		var checkboxes = document.getElementById(this.Id + '_body').getElementsByTagName('input');
		// Check if the inmdex is in range.
		if (checkboxes.length && index >= 0 && index < checkboxes.length)
			if (checkboxes[index].checked != value)
				return this.RowSelect(checkboxes[index].parentNode.parentNode, 1, false);
		return false;
	}
	// Attach some window events.
	AddEvent(window, 'load', new Closure(this, 'Initialize').Call);
	AddEvent(window, 'resize', new Closure(this, 'ResizeInputs').Call);
}
//---------------------------------------------------------------------------
