// ******************************************
// * FormHandler class
// ******************************************

var FormHandler = function (sId, sParent, sMethod)
{
	if (sId == undefined || sId == null) 
	{
		alert("You must provide id of the EXISTING form");
		return false;
	}
	this.useRTF = false;
	this.WithTable = true; // Determines wheter form elements are arranged with table
	this.aryValidateFields = new Array();
	this.ndForm = GetObj(sId);
	if (!this.ndForm) 
	{
		alert("The form with provided id has not been found!"); 
		return false;
	}
	else if ( this.ndForm.tagName != "FORM" )
	{
		alert("The id you provided doesn't refer to <form> element!");
		return false;
	}
}

FormHandler.prototype.OnSubmit = function ( sFunctionName, aryArguments )
{
	var me = this;
	this.ndForm.onsubmit = function ()
	{
		if ( !me.PerformValidation() )
			return false;
		if ( aryArguments == null )
			sFunctionName.apply( me );
		else
			sFunctionName.apply( me, aryArguments );
		return false;
	}
}

FormHandler.prototype.OnCancel = function ( sFunctionName, aryArguments )
{
	var me = this;
	if( this.ndForm.cancel )
	{
		this.ndForm.cancel.onclick = function () {
		if ( aryArguments == null )
			sFunctionName.apply( me );
		else
			sFunctionName.apply( me, aryArguments );
	}
	return;
	}
}

FormHandler.prototype.VisibilitySwitch = function ( sChkbox, sField )
{
	var me = this;
	var ndChkBox = this.ndForm[sChkbox];
	if ( !ndChkBox ) return false; // Field not present, do not warn because it might be the case :-)
	if( ndChkBox.type != 'checkbox' ) 
	{
		alert('Object passed to VisibilitySwitch method is not a checkbox!');
		return false;
	}
	if ( GetObj(sField).value.length > 0 ) ndChkBox.checked = true;	
	if ( ndChkBox.checked == true )
		this.ShowField( sField );
	else
		this.HideField( sField );
	
	ndChkBox.onclick = function () { me.VisibilitySwitch( sChkbox, sField ); }
}

FormHandler.prototype.FCKEditor = function ( sTextArea )
{
	var oFCKEditor = new FCKeditor( sTextArea );
	oFCKEditor.BasePath = "/includes/editor/";
	oFCKEditor.ReplaceTextarea();
	return oFCKEditor;
}

FormHandler.prototype.HideField = function ( sFieldName )
{
	if ( this.WithTable == true )
	{
		var ndTr = this.ndForm[sFieldName].parentNode.parentNode;
		this.ndForm[sFieldName].value = "";
		ndTr.style.display = "none";
	}
	else
	{
		var ndField = this.ndForm[sFieldName];
		if ( ndField.type != 'checkbox') // Checkboxes have labels on the right side
			var ndLabel = ndField.previousSibling;
		else
			var ndLabel = ndField.nextSibling;
		
		ndField.value = "";
		ndField.style.display = "none";
		ndLabel.style.display = "none";
	}
}

FormHandler.prototype.ShowField = function ( sFieldName )
{
	if ( this.WithTable == true )
	{
		var	ndTr = this.ndForm[sFieldName].parentNode.parentNode;
		ndTr.style.display = "";
	}
	else
	{
		var ndField = this.ndForm[sFieldName];
		if ( ndField.type != 'checkbox') // Checkboxes have labels on the right side
			var ndLabel = ndField.previousSibling;
		else
			var ndLabel = ndField.nextSibling;
		
		ndField.style.display = "none";
		ndLabel.style.display = "none";
	}
}

FormHandler.prototype.AddValidationRule = function ( ndField, sValueMin, sValueMax )
{
	if ( sValueMin === undefined &&  sValueMax === undefined )
		this.aryValidateFields[ ndField.id ] = "required";
	else
		this.aryValidateFields[ ndField.id ] = sValueMin + " | " + sValueMax;
}

FormHandler.prototype.RemoveValidationRule = function ( ndField )
{
	delete this.aryValidateFields[ ndField.id ];
}

FormHandler.prototype.PerformValidation = function ()
{
	var sMessage = "";
	for ( var sField in this.aryValidateFields )
	{
		var ndField = GetObj(sField);
		var sValue = this.aryValidateFields[sField];
		if ( sValue == "required" && ndField.value.length == 0 ) {
			ndField.className += " error";
			sMessage += "The field " + ndField.previousSibling.innerHTML + " is required";
		}
		else {
			var aryValues = sValue.split(" | ");
			// TODO: Implement checking for ranges with respect to data type e.g. dates, numeric values & whatever is needed
		}
	}
	if ( sMessage != "" ) {
		alert( sMessage );
		return false;
	}
	else return true;
}
// ************************************
// * End of the Form class definition *
// ************************************

function StopEventPropagation(e)
{
	if(!e) var e = window.event;
	if(document.all) { var e = window.event; e.cancelBubble = true; }
	else e.stopPropagation();
	
	return e;
}

function getQueryStringVars()
{
   var queryString = location.search;
   var data = queryString.slice(1,queryString.length);
   if ( data != "" ) 
   {
		var dataArray = data.split("&");
		for (j=0;j<dataArray.length;j++)
		{
			var qsVal = dataArray[j].split("=");
			eval(qsVal[0] + "=\"" + qsVal[1]+"\"");
		}
	}
}

function isDate()
{
	if ( typeof arguments[0] == 'object' ) {
		var test = arguments[0].constructor.toString().match(/date/i);
		return ( test != null );
	}
	return false;
}

function StripHTML(sString)
{
	var regexp = /<\S[^>]*>/g
	return sString.replace(regexp,"");
}
	
function HTMLDecode(string)
{
	var tmp = string;
	tmp = tmp.replace(/&quot;/g,'"');
	tmp = tmp.replace(/&amp;/g,"&");
	tmp = tmp.replace(/&#039;/g,"'");
	tmp = tmp.replace(/&lt;/g,"<");
	tmp = tmp.replace(/&gt;/g,">");
	
	return tmp;
}

function GetE(e)
{
    if (typeof e == 'undefined') e = window.event; 
    if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
    if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
    return e;
}

function FindPosX(obj)
{
	var curleft = 0;
	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			curleft += obj.offsetLeft
			obj = obj.offsetParent;
		}
	}
	else if (obj.x)
		curleft += obj.x;
	return curleft;
}

function FindPosY(obj)
{
	var curtop = 0;
	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			curtop += obj.offsetTop
			obj = obj.offsetParent;
		}
	}
	else if (obj.y)
		curtop += obj.y;
	return curtop;
}

// Extended verion of setTimeout that enables to pass arguments to the called function
function SetTimeoutExt (functionName, functionArgs, intTime, me) 
{
	var functionRef = CreateFuncRef (functionName, functionArgs, me);
	var timeoutID = setTimeout(functionRef, intTime);
	return timeoutID;
}
// JS closure to create referecnce to function called with arguments
function CreateFuncRef (functionName, functionArgs, me)
{
	return (function(e) {
		//alert(functionName.constructor);
		if ( functionArgs == null )
			functionName.apply(me);
		else
			functionName.apply(me, functionArgs)
	});
}
// retirves specific node using different DOMs to ensure browser comatibility
function GetObj(name)
{
	if (document.getElementById)
		return document.getElementById(name);
	else if (document.all)
		return document.all[name];
	else if (document.layers)
		return document.layers[name];
}

// **************************************************************************************
// * Allows to call server-side scripts and pass returned result (XMLDOM or plain text)
// * to a callback function. Usage:
// *
// * var myObject = new ScriptExec('script_name.asp','data','method');
// * myObject.Execute(SomeFunction, ArgumentsArray);
// *
// * Where:
// * var myObject ... = new instance of ScriptExec object
// * script_name.asp = name o the script to be called
// * data = data to be passed to the server-side script
// * method = either GET or POST
// * 
// * myObject.Execute(...) = executes server-side script
// * SomeFunction = function that will be used to handle data returned by the script
// * ArgumentsArray = array that holds optional arguments to be passed to SomeFunction
// **************************************************************************************

var ScriptExec = function (sUrl, varList, sMethod)
{
	this.sUrl = sUrl;
	this.sMethod = sMethod || "GET";
	this.varList = varList;
	this.ndWait;
	this.oResponse; // XMLHttprequest object used to post and receive response data
	this.callback = null;
	this.aryArgs = null;
}

ScriptExec.prototype.Execute = function (callbackFunction, callbackArgs)
{
	var me = this;
	SetTimeoutExt(me.Send,Array(callbackFunction, callbackArgs),200,me);
}

ScriptExec.prototype.Send = function (callbackFunction, callbackArgs)
{
	var me = this; // preserves reference to the constructor 
	this.callback = callbackFunction;
	this.aryArgs = callbackArgs;
	// For standards compliant browsers
	if ( window.XMLHttpRequest ) {
		this.oResponse = new XMLHttpRequest();
	}
	// For browsers with serious security vulnerabilites like Internet Exploder :-P
	else if ( window.ActiveXObject ) 
		this.oResponse = new ActiveXObject("Microsoft.XMLHTTP");
	else return false;

	if( this.ndWait ) 
	{ 
		this.ndWait.style.display = 'block';
		this.ndWait.onmouseover = function (e) { StopEventPropagation(e) }
	}

	this.oResponse.onreadystatechange = function() { me.CheckStatus(); } // restores proper constructor that was destroyed by event handler function call
	if ( this.sMethod != "GET" )
	{
		this.oResponse.open(this.sMethod, this.sUrl, true);
		this.oResponse.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;');
		this.oResponse.send(this.BuildURL());
	}
	else 
	{
		this.oResponse.open(this.sMethod, this.sUrl + "?" + this.BuildURL(), true);
		this.oResponse.send(null);
	}
}

ScriptExec.prototype.CheckStatus = function ()
{
	
	if ( this.oResponse.readyState == 4 )
	{
		//alert(me.constructor);
		if ( this.ndWait ) this.ndWait.style.display = 'none';
		//alert(this.oResponse.status);
		if ( this.oResponse.status == 200 )
		{
			if (this.oResponse.responseXML && this.oResponse.responseXML.documentElement) {
				var ndRoot = this.oResponse.responseXML.documentElement
				if (ndRoot.firstChild && ndRoot.firstChild.nodeName == "error")
				{
					Debug( ndRoot.firstChild.firstChild.nodeValue, ndRoot.firstChild.getAttribute("type") );
					return false;
				}
			}
			if ( this.callback != null )
				if ( this.aryArgs == null )
					this.callback.apply(this.oResponse);
				else
					this.callback.apply(this.oResponse, this.aryArgs);
		}
		else
		{
			Debug ( this.oResponse.responseText, "script" );
			return false;
		}
	}
}
ScriptExec.prototype.BuildURL = function()
{
	var separator = '';
	var url = '';
	for ( name in this.varList ) 
	{
		var sIndex = name;
		name = name.replace(/_c\d+$/gi, "");
		url += separator + encodeURIComponent(name)
			+ '=' + encodeURIComponent(this.varList[sIndex]);
		separator = '&';
	}
	return url;
}

function Debug( sData, sType )
{
	if ( sData == undefined ) sData = this.responseText;
	if ( sType == undefined ) sType = "debug";
	sData = sData.replace(/\<\!\[CDATA\[/gi, "");
	sData = sData.replace(/\]\]\>/gi,"");
	var ndError = document.createElement("div");
	ndError.style.position = "absolute";
	ndError.style.top = "30%";
	ndError.style.width = "70%";
	ndError.style.left = "10%";
	
	ndError.style.color = "#FFFFFF";
	ndError.style.border = "solid 3px #000000";
	ndError.style.padding = "1em";
	switch ( sType )
	{
		case "debug":
			ndError.style.backgroundColor = "#55AA11";
			ndError.innerHTML = "Script Response Output:<p>" + sData + "</p><p><button id='btnClose'>Close</button></p>";
			break;
		case "script":
			ndError.style.backgroundColor = "#FF0000";
			ndError.innerHTML = "Application Error!<p>" + sData + "</p><p><button id='btnClose'>Close</button></p>";
			break;
		case "user":
			ndError.style.backgroundColor = "#EFCC2C";
			ndError.style.color = "black";
			ndError.innerHTML = "User Error<p>" + sData + "</p><p><button id='btnClose'>Close</button></p>";
			break;
	}
		
	
	ndBody = document.body;
	ndBody.appendChild(ndError);
	var ndButton = GetObj("btnClose");
	ndButton.onclick = function () { ndBody.removeChild(ndError);  }
	var ndDND = new DragAndDrop( ndError, false );
	ndDND.Initialize()
}
/*
****************************************
* End of ScriptExec object definition  *
****************************************
*/


/* 
*************************
* Drag & Drop Functions
*************************
*/

var DragAndDrop = function (ndObj, clone) 
{
	this.makeClone = clone || false;
	this.ndObj = ndObj || null;
	this.intDelay = 1000; // time in ms before the acutal dragging is enabled for the object
	this.onDragStart = new Function();
	this.onDrag = new Function();
	this.onDragEnd = new Function();
}
	
DragAndDrop.prototype.Initialize = function (e)
{
	//this.DisableTextSelect(); // Firefox workaround for event propagation on absolute postioned elements 
	var me = this; // preserve reference to the constructor;
	if ( this.ndObj.onmousedown == null || this.ndObj.onmousedown == undefined ) 
		// if mousedown wasn't defined before, create new event handler 
		this.ndObj.onmousedown = function (e) { 
			var e = GetE(e);
			me.x = e.clientX + 2;
			me.y = e.clientY + 2;
			me.timeoutID = SetTimeoutExt(me.StartDrag, null, me.intDelay, me);
		}
	else 
	{ 
		// just enable dragging without destroying orginal event handler
		var e = GetE(e);
		this.x = e.clientX + 2;
		this.y = e.clientY + 2;
		this.timeoutID = SetTimeoutExt(this.StartDrag, null, this.intDelay, this);
	}	
	document.onmouseup = function() { clearTimeout(me.timeoutID); me.EnableTextSelect(); }
	document.onmousemove = function () { clearTimeout(me.timeoutID); }
}

DragAndDrop.prototype.StartDrag = function ()
{
	this.DisableTextSelect();
	this.onDragStart();
	var me = this;
	if ( this.makeClone == true )
	{
		if ( this.ndObj.tagName == "TR" && document.all )
		{
			var ndTempTable = document.createElement("table");
			var ndTBody = document.createElement("tbody");
			ndTempTable.appendChild(ndTBody);
			ndTempTable.tBodies[0].appendChild(this.ndObj.cloneNode(true));
			this.ndObj = ndTempTable;
		}
		else this.ndObj = this.ndObj.cloneNode(true);
		if (document.all) this.ndObj.style.filter = "alpha(opacity=50)";
		else this.ndObj.style.MozOpacity = 0.5;
		document.body.appendChild(this.ndObj);
	}
	//document.body.style.cursor = "move";
	this.ndObj.style.position = "absolute";
	this.ndObj.style.zIndex = "255";
	this.ndObj.style.marginTop = "0px";
	this.ndObj.style.marginLeft = "0px";
	this.ndObj.style.top = this.y + "px";
	this.ndObj.style.left = this.x + "px";
	
	document.onmousemove = function (e) { me.Drag(e); }
	document.onmouseup = function (e) { me.StopDrag(e); }
}

DragAndDrop.prototype.DisableTextSelect = function ()
{
	document.body.onselectstart = function () { return false; }
	if ( !document.all )
	{
		var ndNodes = document.childNodes;
		for (var i=0; i < ndNodes.length; i++)
			if (ndNodes[i].style)
				ndNodes[i].style.MozUserSelect = "none";
	}
}

DragAndDrop.prototype.EnableTextSelect = function ()
{
	document.body.onselectstart = function () { return true; }
	if ( !document.all )
	{
		var ndNodes = document.childNodes;
		for (var i=0; i < ndNodes.length; i++)
			if (ndNodes[i].style)
				ndNodes[i].style.MozUserSelect = "";
	}
}

DragAndDrop.prototype.Drag = function (e)
{
	this.onDrag();
	var me = this;
	var e = GetE(e);
	this.ndObj.style.top = e.clientY  + 5 + "px";
	this.ndObj.style.left = e.clientX + 5 + "px";
	document.onmouseup = function (e) { me.StopDrag(e); }
}

DragAndDrop.prototype.StopDrag = function (e)
{
	var me = this;
	var e = GetE(e);
	if ( this.makeClone == true ) document.body.removeChild(this.ndObj);
	//else this.ndObj.style.position = "";
	this.EnableTextSelect();
	this.onDragEnd();
	document.onmousemove = null;
	document.onmouseup = null;
}

/* =======================
   = DOM event functions =
   ======================= */
function addEvent( ndObj, sType, refFunction )
{
	if (ndObj.addEventListener)
	{
		ndObj.addEventListener( sType, refFunction, false );
	}
	else if (ndObj.attachEvent)
	{
		ndObj["e"+sType+refFunction] = refFunction;
		ndObj[sType+refFunction] = function() { ndObj["e"+sType+refFunction]( window.event ); }
		ndObj.attachEvent( "on"+sType, ndObj[sType+refFunction] );
	}
}

function removeEvent( obj, type, fn )
{
	if (obj.removeEventListener)
		obj.removeEventListener( type, fn, false );
	else if (obj.detachEvent)
	{
		obj.detachEvent( "on"+type, obj[type+fn] );
		obj[type+fn] = null;
		obj["e"+type+fn] = null;
	}
}

/* ===========================
   = Table sorting functions =
   =========================== */
var SORT_COLUMN_INDEX;

function sortables_init(intStartCol)
{
    // Find all tables with class sortable and make them sortable
    if (intStartCol == undefined) intStartCol = 0
    if (!document.getElementsByTagName) return;
    tbls = document.getElementsByTagName("table");
    for (ti=0;ti<tbls.length;ti++)
    {
        thisTbl = tbls[ti];
        if (((' '+thisTbl.className+' ').indexOf("sortable") != -1))
        {
            //initTable(thisTbl.id);
            ts_makeSortable(thisTbl, intStartCol);
        }
    }
}

function ts_makeSortable(table, intStartCol)
{
    if (table.rows && table.rows.length > 0)
    {
        var firstRow = table.rows[0];
    }
    if (!firstRow) return;
    
    // We have a first row: assume it's the header, and make its contents clickable links
    for (var i=intStartCol;i<firstRow.cells.length;i++)
    {
        var cell = firstRow.cells[i];
        var txt = ts_getInnerText(cell);
        cell.innerHTML = '<a href="#" class="sortheader" onclick="ts_resortTable(this);return false;">'+txt+'<span class="sortarrow">&nbsp;&nbsp;&nbsp;</span></a>';
    }
}

function ts_getInnerText(el)
{
	if (typeof el == "string") return el;
	if (typeof el == "undefined") { return el };
	if (el.innerText) return el.innerText;	//Not needed but it is faster
	var str = "";
	
	var cs = el.childNodes;
	var l = cs.length;
	for (var i = 0; i < l; i++) 
	{
		switch (cs[i].nodeType)
		{
			case 1: //ELEMENT_NODE
				str += ts_getInnerText(cs[i]);
				break;
			case 3:	//TEXT_NODE
				str += cs[i].nodeValue;
				break;
		}
	}
	return str;
}

function ts_resortTable(lnk)
{
    // get the span
    var span;
    for (var ci=0;ci<lnk.childNodes.length;ci++) {
        if (lnk.childNodes[ci].tagName && lnk.childNodes[ci].tagName.toLowerCase() == 'span') span = lnk.childNodes[ci];
    }
    var spantext = ts_getInnerText(span);
    var td = lnk.parentNode;
    var column = td.cellIndex;
    var table = getParent(td,'TABLE');
    
    // Work out a type for the column
    if (table.rows.length <= 1) return;
    var itm = ts_getInnerText(table.rows[1].cells[column]);
    sortfn = ts_sort_caseinsensitive;
    if (itm.match(/^\d\d[\/-]\d\d[\/-]\d\d\d\d$/)) sortfn = ts_sort_date;
    if (itm.match(/^\d\d[\/-]\d\d[\/-]\d\d$/)) sortfn = ts_sort_date;
    if (itm.match(/^[£$]/)) sortfn = ts_sort_currency;
    if (itm.match(/^[\d\.]+$/)) sortfn = ts_sort_numeric;
    SORT_COLUMN_INDEX = column;
    var firstRow = new Array();
    var newRows = new Array();
    for (i=0;i<table.rows[0].length;i++) { firstRow[i] = table.rows[0][i]; }
    for (j=1;j<table.rows.length;j++) 
    { 
    	alert(table.rows[j].cells[0].colspan);
    	newRows[j-1] = table.rows[j];
    }

    newRows.sort(sortfn);

    if (span.getAttribute("sortdir") == 'down') {
        ARROW = '&nbsp;&nbsp;&uarr;';
        newRows.reverse();
        span.setAttribute('sortdir','up');
    } else {
        ARROW = '&nbsp;&nbsp;&darr;';
        span.setAttribute('sortdir','down');
    }
    
    // We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
    // don't do sortbottom rows
    for (i=0;i<newRows.length;i++) { if (!newRows[i].className || (newRows[i].className && (newRows[i].className.indexOf('sortbottom') == -1))) table.tBodies[0].appendChild(newRows[i]);}
    // do sortbottom rows only
    for (i=0;i<newRows.length;i++) { if (newRows[i].className && (newRows[i].className.indexOf('sortbottom') != -1)) table.tBodies[0].appendChild(newRows[i]);}
    
    // Delete any other arrows there may be showing
    var allspans = document.getElementsByTagName("span");
    for (var ci=0;ci<allspans.length;ci++) {
        if (allspans[ci].className == 'sortarrow') {
            if (getParent(allspans[ci],"table") == getParent(lnk,"table")) { // in the same table as us?
                allspans[ci].innerHTML = '&nbsp;&nbsp;&nbsp;';
            }
        }
    }
        
    span.innerHTML = ARROW;
}

function getParent(el, pTagName) 
{
	if (el == null) return null;
	else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase())	// Gecko bug, supposed to be uppercase
		return el;
	else
		return getParent(el.parentNode, pTagName);
}

function ts_sort_date(a,b)
{
    // y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
    if (aa.length == 10)
    {
        dt1 = aa.substr(6,4)+aa.substr(3,2)+aa.substr(0,2);
    } else 
    {
        yr = aa.substr(6,2);
        if (parseInt(yr) < 50) { yr = '20'+yr; } else { yr = '19'+yr; }
        dt1 = yr+aa.substr(3,2)+aa.substr(0,2);
    }
    if (bb.length == 10) 
    {
        dt2 = bb.substr(6,4)+bb.substr(3,2)+bb.substr(0,2);
    } else 
    {
        yr = bb.substr(6,2);
        if (parseInt(yr) < 50) { yr = '20'+yr; } else { yr = '19'+yr; }
        dt2 = yr+bb.substr(3,2)+bb.substr(0,2);
    }
    if (dt1==dt2) return 0;
    if (dt1<dt2) return -1;
    return 1;
}

function ts_sort_currency(a,b) 
{ 
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g,'');
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g,'');
    return parseFloat(aa) - parseFloat(bb);
}

function ts_sort_numeric(a,b) 
{ 
    aa = parseFloat(ts_getInnerText(a.cells[SORT_COLUMN_INDEX]));
    if (isNaN(aa)) aa = 0;
    bb = parseFloat(ts_getInnerText(b.cells[SORT_COLUMN_INDEX])); 
    if (isNaN(bb)) bb = 0;
    return aa-bb;
}

function ts_sort_caseinsensitive(a,b) 
{
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).toLowerCase();
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).toLowerCase();
    if (aa==bb) return 0;
    if (aa<bb) return -1;
    return 1;
}

function ts_sort_default(a,b) 
{
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
    if (aa==bb) return 0;
    if (aa<bb) return -1;
    return 1;
}
/* ==================================
   = End of table sorting functions =
   ================================== */
function setClipboard(text){
	var url = [
		'data:text/html;charset=utf-8;base64,PGJvZHk+PC9ib2',
		'R5PjxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij4KKGZ1',
		'bmN0aW9uKGVuY29kZWQpe3ZhciBzd2ZfZGF0YSA9IFsKICdkYX',
		'RhOmFwcGxpY2F0aW9uL3gtc2hvY2t3YXZlLWZsYXNoO2Jhc2U2',
		'NCxRMWRUQjJ3JywKICdBQUFCNG5EUGdZbGpBd01qSTRNejAlMk',
		'YlMkY5JTJGZTJaZkJnYUdhV3dNRE1uNUthJywKICdrTU10TjRH',
		'ZGdaZ1NJTXdaWEZKYW01UUFFJTJCQm9iaTFCTG5uTXlDcFB6RW',
		'9oU0dJJywKICdQRnAlMkZBeHNEREJRa3BGWkRGUUZGQ2d1eVM4',
		'QXlqSTRBRVVCaXkwVndBJTNEJTNEJwpdLmpvaW4oIiIpOwpkb2',
		'N1bWVudC5ib2R5LmlubmVySFRNTCA9IFsKICc8ZW1iZWQgc3Jj',
		'PSInLHN3Zl9kYXRhLCciICcsCiAnRmxhc2hWYXJzPSJjb2RlPS',
		'csZW5jb2RlZCwnIj4nLAogJzwvZW1iZWQ+JwpdLmpvaW4oIiIp',
		'Owp9KSgi',
		base64encode( encodeURIComponent(text) + '")</'+'script>')
	].join("");
	var tmp = document.createElement("div");
	tmp.innerHTML = [
		 '<iframe src="',url,'"'
		,' width="0" height="0">'
		,'</iframe>'
	].join("");
	with(tmp.style){
		position ="absolute";
		left = "-10px";
		top  = "-10px";
		visibility = "hidden";
	};
	document.body.appendChild(tmp);
	setTimeout(function(){document.body.removeChild(tmp)},1000);
	function base64encode(str){
		var Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");
		var c1, c2, c3;
		var buf = [];
		var len = str.length;
		var i = 0;
		while(i < len){
			c1 = str.charCodeAt(i) & 0xff;
			c2 = str.charCodeAt(i+1);
			c3 = str.charCodeAt(i+2);
			buf.push(Chars[(c1 >> 2)]);
			if(i+1 == len){
				buf.push(Chars[(c1 & 0x3) << 4],"==");
				break;
			}
			buf.push(Chars[((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)]);
			if(i+2 == len){
				buf.push(Chars[(c2 & 0xF) << 2],"=");
				break;
			}
			buf.push(
				Chars[((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)],
				Chars[(c3 & 0x3F)]
			);
			i+=3;
		}
		return buf.join("")
	}
}

function ClipboardCopy(text) {
    if (window.clipboardData) {
        // Internet Explorer
        window.clipboardData.setData("Text", text);
        return true;
    } else if (window.netscape) {
        // Mozilla
        setClipboard(text);
	/*var ndError = document.createElement("div");
		ndError.style.position = "absolute";
		ndError.style.top = "30%";
		ndError.style.width = "70%";
		ndError.style.left = "10%";
	
		ndError.style.color = "#FFFFFF";
		ndError.style.border = "solid 3px #000000";
		ndError.style.padding = "1em";
		ndError.style.backgroundColor = "#55AA11";
        
        ndError.innerHTML = "Your browser doesn't permit to automatically copy a text to clipborad. Please select the text below and copy it as you do with any other application.<br /><p style='background-color: white; color: black; padding: 0.5em; border: solid 1px black;'>" + text + "</p><p><button id='btnClose'>Close</button></p>";
        ndBody = document.body;
		ndBody.appendChild(ndError);
		var ndButton = GetObj("btnClose");
		ndButton.onclick = function () { ndBody.removeChild(ndError);  }
        return true;*/
   }
   
   return false;
}