
/// <reference path="CalendarBase.js" />
/// <reference path="DateScripts.js" />
/// <reference path="DialogOptions.js" />
/// <reference path="Security.js" />


var NullGuid = "{00000000-0000-0000-0000-000000000000}";

/* MsgBox Return Values */ 
var vbOK		=	1;
var vbCancel	=	2;
var vbAbort		=	3;
var vbRetry		=	4;
var vbIgnore	=	5;
var vbYes		=	6;
var vbNo		=	7;

/* Msgbox Options */
var vbOKOnly		=	0;
var vbOKCancel		=	1;
var vbAbortRetryIgnore	=	2;
var vbYesNoCancel	=	3;
var vbYesNo			=	4;
var vbRetryCancel	=	5;
var vbCritical		=	16;
var vbQuestion		=	32;
var vbExclamation	=	48;
var vbInformation	=	64;
var vbDefaultButton1	=   0;
var vbDefaultButton2	=   256;
var vbDefaultButton3	=   512;
var vbDefaultButton4	=   768;

Object.prototype.Inherits       = inherits;
Function.prototype.Implements   = _implements;
Function.Inherits               = inheritsPrototype;
Object.prototype.Constructor    = Object_Constructor;         // WARNING!!!!!!!!!!! REMOVING THIS WILL BREAK FUSION CHARTS!


var DebugMode = false;

var __SuppressErrors = false;

function _implements(pClass)
{
    for (var n in pClass)
    {
        if (typeof(pClass[n] != 'function')) continue;
        
        if (this[n] == undefined)
        {
            var e = new Error()
            e.message = this.ConstructorName() + " does not implement " + pClass.FunctionName() + "." + n;
            e.description = e.message;
            throw e;   
        }
    }
}


function inherits(oClass, ConstructorArgs) 
{
	var newClass = new oClass(ConstructorArgs);
	for (var n in newClass) 
	{
	    // do not copy over prototyped functions
	    if (this.constructor.prototype[n] != undefined) continue;
	    
	    if (typeof(newClass[n]) != 'function') 
	    {
	        // value/reference type
	        this[n.toString()] = newClass[n]
	        continue;
	    } 
	    else 
	    {	        	        
	        // function
		    this[n.toString()] = function() {}		
		    this[n.toString()] = newClass[n.toString()].valueOf();
		}
	}
}

function inheritsPrototype(oClass) 
{
	for (var n in oClass.prototype) 
	{
		this.prototype[n] = oClass.prototype[n];
	}
}

var ApplicationEvents       = null;
var ApplicationFunctions    = null;
var ApplicationObjects      = null;

try {
    if (window.dialogArguments != undefined) 
    {    
        // Dialog
        ApplicationEvents       = window.dialogArguments.Opener.ApplicationEvents;
        ApplicationFunctions    = window.dialogArguments.Opener.ApplicationFunctions;
        ApplicationObjects      = window.dialogArguments.Opener.ApplicationObjects;
    } else if (window.top != window) {
        // Non top frame
        ApplicationEvents = window.top.ApplicationEvents;    
        ApplicationFunctions = window.top.ApplicationFunctions;    
        ApplicationObjects = window.top.ApplicationObjects;
        
    } else if (window.opener) {
        // Opened window
        ApplicationEvents       = window.opener.ApplicationEvents;
        ApplicationFunctions    = window.opener.ApplicationFunctions;
        ApplicationObjects      = window.opener.ApplicationObjects;
       
    }
} catch (err)
{
}


function Object_Constructor(ConstructorArguments)
{
    if (ConstructorArguments == undefined || ConstructorArguments == null || typeof(ConstructorArguments) != "object") return false;
    
    for (var n in ConstructorArguments)
    {
        if (ConstructorArguments.hasOwnProperty(n))
            this[n] = ConstructorArguments[n];
    }

}


var Cast = { As: {} };


Cast.As.DataList = function(val) {
    /// <summary>Returns a casted DataList</summary>
    /// <param name="val" type="DataList">Value to cast</param>
    /// <returns type="DataList" />
    return val;
}

Cast.As.Array = function(val) {
    /// <summary>Returns a casted array</summary>
    ///	<param name="val"	type="Array">Value to cast</param>
    /// <returns type="Array" />	
    return val;
}

Cast.As.Boolean = function(val) {
    /// <summary>Returns a casted Boolean</summary>
    /// <param name="val" type="Boolean" />
    /// <returns type="Boolean" />
    return val;
}
Cast.As.Number = function(val) {
    /// <summary>Returns a casted number</summary>
    /// <param name="val" type="Number" />
    /// <returns type="Number" />
    return val;
}



Array.prototype.Contains = __Array_FindByProperty;
Array.prototype.FindByProperty = __Array_FindByProperty;

function __Array_FindByProperty(PropertyName, PropertyValueOrFunctionToEvaluate, CaseInsensitive) {
    ///<summary>Find a property by name</summary>
    ///	<param name="PropertyName"	type="String">Property to test</param>
    ///	<param name="PropertyValueOrFunctionToEvaluate"	type="Object" >Value to test against OR a function that evaluates the property value and returns a boolean</param>
    ///
    /// <returns type="Array" elementType="Object" default="Array">Array of matching objects</returns>
   

    var bUseFunction  = typeof(PropertyValueOrFunctionToEvaluate) == 'function';
    var functionName = PropertyValueOrFunctionToEvaluate;
    var propertyValue = PropertyValueOrFunctionToEvaluate;
    if (CaseInsensitive && typeof(propertyValue) == 'string') propertyValue = propertyValue.toUpperCase();
    if (CaseInsensitive == undefined || CaseInsensitive == null) CaseInsensitive = true;
    
    var aReturn = new Array(0);
            
    for (var n in this)
    {
        if (!this.hasOwnProperty(n))        continue;
        if (typeof(this[n]) != 'object')    continue;
        
        if (bUseFunction)
        {
            if (functionName(this[n][PropertyName]))
            {
                aReturn[aReturn.length] = this[n];
            }
        }
        else
        {
            var PropVal = this[n][PropertyName];
            
            if (typeof(PropVal) == 'string' && CaseInsensitive && PropVal.toUpperCase() == propertyValue)
            {
                aReturn[aReturn.length] = this[n];
            }
            else if (this[n][PropertyName] == propertyValue)
            {
                aReturn[aReturn.length] = this[n];
            }
        }
    }
    
    return aReturn;
}


Array.prototype.Contains = __Array_Contains;
 
function __Array_Contains(value) {

    for (var i = 0; i < this.length; i++) if (this[i] == value) return true;
    
    return false;
}


Array.prototype.SameValuesAs = __Array_SameValuesAs;
 
function __Array_SameValuesAs(aCompareTo)
{
    if (aCompareTo.length != this.length) return false;
    
    for (var i = 0; i < this.length; i++)
    {
        if (this[i] == aCompareTo[i]) continue;
        if (typeof(this[i]) == 'date' && typeof(aCompareTo) == 'date' && this[i].valueOf() == aCompareTo[i].valueOf()) continue;
        return false;
    }
    
    return true;
}


Array.prototype.Clone = __Array_Clone;
function __Array_Clone()
{
    var a = new Array(this.length);
    for (var i = 0; i < a.length; i++)
        a[i] = this[i];
    
    return a;
}


function __QueryStringObject() {
	this.Inherits(Collection);
	this.Keys = new Object();
	
	var sQueryString = window.location.href;
	
	var iIndex = sQueryString.indexOf("?");
	
	if (iIndex == -1) {
		return;			// No querystring found.
	}
	
	var sQueryString = sQueryString.substr(iIndex+1);
	
	if (sQueryString.indexOf("&") == -1)
	{
		// Only one item.
		var o = new Object();
		o.Key = sQueryString.substr(0, sQueryString.indexOf("="));
		o.Value = sQueryString.substr(sQueryString.indexOf("=")+1);
		
		this.Items.splice(0, 0, o);
	} else {
		// Multiple Keys
		Keys = sQueryString.split("&");
		
		for (var k = 0; k < Keys.length; k++)
		{
			var o = new Object();
			o.Key = unescape(Keys[k].substr(0, Keys[k].indexOf("=")));
			o.Value = unescape(Keys[k].substr(Keys[k].indexOf("=")+1));
			
			this.Items.splice(this.Items.length, 0, o);					// Splice new keys into the Collection.
		}
	}
	
	for (var i = 0; i < this.Items.length; i++) this.Keys[this.Items[i].Key] = this.Items[i].Value;	
	
}
var QueryString = new __QueryStringObject();




function CodeLibrariesClass() {        
}
CodeLibrariesClass.prototype.Register   = __CodeLibrariesClass_Register;
CodeLibrariesClass.prototype.Unregister = __CodeLibrariesClass_Unregister;
CodeLibrariesClass.prototype.Exists     = __CodeLibrariesClass_Exists;
CodeLibrariesClass.prototype.toString   = __CodeLibrariesClass_toString;

function __CodeLibrariesClass_toString() {
    return '[CodeLibraries]';
}

function __CodeLibrariesClass_Register(sLibraryName, libLibrary) {
    if (this.Exists(sLibraryName)) {
        var e = new Error();
        e.message = 'Unable to register library [' + sLibraryName + '], it has already been registered.';
        e.description = e.message;
        throw e;
    }
    
    this[sLibraryName] = libLibrary;
}

function __CodeLibrariesClass_Unregister(sLibraryName) {
    if (!this.Exists(sLibraryName)) {
        var e = new Error();
        e.message = 'Unable to unregister library [' + sLibraryName + '], it is not registered.';
        e.description = e.message;
        throw e;
    }
    
    delete this[sLibraryName];
}
function __CodeLibrariesClass_Exists(sLibraryName) {
    return this[sLibraryName] != undefined;
}

function CodeLibraryClass() {
}
CodeLibraryClass.prototype.toString = __CodeLibraryClass_toString;

function __CodeLibraryClass_toString() {
    return '[CodeLibrary]';
}



Collection.prototype.toString = function() { return "[Collection]"; }
function Collection() {
	this.DefaultCollectionObject = Object;
				
	this.Items = new Array();
		
	var pfDispose = function() {	
		__Disposer(this);
	}
	this.Dispose = pfDispose;
}
Collection.prototype.Add    = __Collection_Add;
Collection.prototype.Insert = __Collection_Insert;
Collection.prototype.Remove = __Collection_Remove;
Collection.prototype.Clear  = __Collection_Clear; 
Collection.prototype.Find   =__Collection_Find;
Collection.prototype.Count  =__Collection_Count;
Collection.prototype.Contains =__Collection_Contains;

	
function __Collection_Add() {
	var tmpIdx = this.Items.length;
	if (this.Add.arguments.length > 1 && this.Add.arguments[0].toString() == "[object Object]" && !isNaN(this.Add.arguments[1])) tmpIdx = this.Add.arguments[1];

	switch (this.Add.arguments.length) 
	{
		case 0:
			var newObj = new this.DefaultCollectionObject();
			this.Items.splice(tmpIdx, 0, newObj);
			return newObj;
			break;
	
		default:	
		
			if (typeof(this.Add.arguments[0]) == "object") {
				this.Items.splice(tmpIdx, 0, this.Add.arguments[0]);
				return this.Add.arguments[0];
				break;
				
			} else {
				var newObj = new this.DefaultCollectionObject(this.Add.arguments);
				this.Items.splice(tmpIdx, 0, newObj);
				return newObj;
				break;
			}		
	}
}

function  __Collection_Insert() {
    var tmpIdx = this.Items.length;
				
	if (this.Insert.arguments.length != 0) {
	
		if (this.Insert.arguments.length == 1 && !isNaN(this.Insert.arguments[0])) {
			tmpIdx = this.Insert.arguments[0];
		}
		
		if (this.Insert.arguments.length == 2 && !isNaN(this.Insert.arguments[1])) {
			tmpIdx = this.Insert.arguments[1];
		}
	}

	switch (this.Insert.arguments.length) 
	{
		case 0:
			var newObj = new this.DefaultCollectionObject();
			this.Items.splice(tmpIdx, 0, newObj);
			
			return newObj;
			break;
			
		case 2:
			if (typeof(this.Add.arguments[1]) == "object") {
				this.Items.splice(tmpIdx, 0, this.Insert.arguments[0]);
				
				return this.Insert.arguments[0];
				break;
				
			} else {
				var args = new Array(this.Insert.arguments.length - 1);
				for (var i = 0; i < args.length; i++) args[i] = this.Insert.arguments[i+1];
				
				var newObj = new this.DefaultCollectionObject(args);
				this.Items.splice(tmpIdx, 0, newObj);

				return newObj;
				break;
			}	
		
		default:	
				var args = new Array(this.Insert.arguments.length - 1);
				for (var i = 0; i < args.length; i++) args[i] = this.Insert.arguments[i+1];
				
				var newObj = new this.DefaultCollectionObject(args);
				this.Items.splice(tmpIdx, 0, newObj);
				
				return newObj;
				break;
					
	}
}

function __Collection_Remove(uIndex) {  
    if (typeof(uIndex == 'object')) {
        var iIndex = null;
	        for (var i = 0; i < this.Items.length; i++) if (this.Items[i] == uIndex) { iIndex = i; break; }
	        if (iIndex == null) return false;	        
	        Index = iIndex;
    } else {
	    if (isNaN(uIndex) || parseInt(uIndex, 10) < 0 || parseInt(uIndex, 10) >= this.Items.length) {
	        return false;
	    }
    }
    
	this.Items.splice(parseInt(Index, 10), 1);
	return true;	
}

function __Collection_Clear() {
	this.Items.splice(0, this.Items.length);
	return true;
}


function __Collection_Find(Attribute, Value, SingleOnly) {
	if (!SingleOnly) 
		var output = new Array();
	
	for (var i = 0; i < this.Items.length; i++) {
		if (this.Items[i][Attribute] != undefined && this.Items[i][Attribute] == Value) {
			if (SingleOnly) {
				return this.Items[i];
			}
			
			output.splice(output.length, 0, this.Items[i]);
		}
	}
		
		
	return output;
}


function __Collection_Count() {
    return this.Items.length; 
}

		
function __Collection_Contains(Attribute, Value) {
	return (this.Find(Attribute, Value).length != 0);
}



function DelayedExecutorClass() {
	this.CallBackHandle = null;				// Timer call back handle
	
	this.DelayInterval	= 10000;			// Amount of time to delay
	
	this.ExecuteFunction= null;				// Function pointer to writer function
	this.DelayFunction	= null;
}
DelayedExecutorClass.prototype.toString     = __DEXEC_toString;
DelayedExecutorClass.prototype.DelayExecute	= __DEXEC_DelayExecute;
DelayedExecutorClass.prototype.ForceExecute	= __DEXEC_ForceExecute;
DelayedExecutorClass.prototype.Cancel		= __DEXEC_Cancel;
DelayedExecutorClass.prototype.Clear		= __DEXEC_Clear;

function __DEXEC_toString() {
    return '[DelayedExecutorClass]';
}

function __DEXEC_Clear() {
	if (this.CallBackHandle != null) {
		clearTimeout(this.CallBackHandle);		
		this.CallBackHandle = null;
	}
}

function __DEXEC_Cancel() {
	this.Clear();
}

function __DEXEC_ForceExecute() {
	this.Clear();
	
	this.ExecuteFunction();
}

function __DEXEC_DelayExecute() {
	if (typeof(this.ExecuteFunction) != 'function') {
		var e = new Error();
		e.message = 'No execute function delegate specified for [DelayedExecutor.ExecuteFunction]';
		e.description = e.message;
		throw e;
	}

	this.Clear();
	
	if (this.DelayFunction != null) this.DelayFunction();
	
	var o = this;
	var f = function() { o.ForceExecute(); }		
	
	this.CallBackHandle = setTimeout(f, o.DelayInterval);
}



function __DLPO_PageQueueItem() {
    this.Page = -1;
    this.Status = "WAITING";
    this.Transaction = null;
}

function DataListPagingObject(pParentDataList) {
    this.DataList = pParentDataList;
    this.DataList.Cursor.Events.Add('MOVE', __DLPO_CursorMoveEvent, this);

    this.__Active           = false;

    this.QueueSystem        = null;

    this.RequestBuilder     = null;
    this.RequestHandler     = null;
    
    this.DataSetPath        = "//DataTransfer/Response/DataSet";
    this.TotalCountPath     = "//DataTransfer/Response/TotalCount";
    this.XMLLoadArguments   = new XMLLoadArguments();        
        this.XMLLoadArguments.XML                = null;
        this.XMLLoadArguments.InsertAt           = null;
        this.XMLLoadArguments.Replace            = true;
        this.XMLLoadArguments.AllowAutoExpand    = false;
        this.XMLLoadArguments.AutoCreateColumns  = false;
        this.XMLLoadArguments.UseMappings        = false;
    
    this.PageSize           = 100;
    
    this.Queue = new Array();
    this.Executing = false;
}
DataListPagingObject.prototype.LoadNextPage     = __DLPO_LoadNextPage;
DataListPagingObject.prototype.QueuePage        = __DLPO_QueuePage;
DataListPagingObject.prototype.Refresh          = __DLPO_RefreshData;
DataListPagingObject.prototype.ResetRows        = __DLPO_ResetDataRows;
DataListPagingObject.prototype.Clear            = __DLPO_ResetDataRows;
DataListPagingObject.prototype.Enable           = __DLPO_Enable;
DataListPagingObject.prototype.Disable          = __DLPO_Disable;


function __DLPO_Enable() {
    if (this.RequestBuilder     == null) {
        var e = new Error()
        e.description = 'No RequestBuilder function specified for DataListPagingObject';
        e.message = e.description;
        throw e;   
    }
    
    if (this.RequestHandler     == null) {
        var e = new Error()
        e.description = 'No RequestHandler function specified for DataListPagingObject';
        e.message = e.description;
        throw e;   
    }
    
    if (this.QueueSystem        == null) {
        var e = new Error()
        e.description = 'An AJAXQueueSystem object needs to be specified for the DataListPagingObject';
        e.message = e.description;
        throw e;   
    }

    this.__Active = true;
    
    this.DataList.Events.RaiseEvent("PAGING_ENABLED", null);
}

function __DLPO_Disable() {
    this.DataList.Events.RaiseEvent("PAGING_DISABLED", null);
    this.__Active = false;
}

function __DLPO_RefreshData() {

    // Cancel currently running queued pages.
    for (var i = 0; i < this.Queue.length; i++) {
        var PageItem  = this.Queue[i]
        
        if (PageItem.Status == 'EXECUTING') PageItem.Transaction.Cancel();
        
        PageItem.Status = 'CANCELLED'                
    }
    this.Queue.splice(0, this.Queue.length);
    
    this.ResetRows();    
    
    var iPage =  (this.DataList.Cursor.Index() != -1) ? parseInt((this.DataList.Cursor.Index()+1) / this.PageSize, 10) : 0;
    this.QueuePage(iPage);
}

function __DLPO_ResetDataRows() {
    this.Pages = null;
    
    this.DataList.Rows.Items.splice(0, this.DataList.Rows.Items.length);
    this.DataList.Cursor.Index(-1);
    this.DataList.Events.RaiseEvent('DATALIST_CHANGE', null);
}

function __DLPO_CursorMoveEvent(oSrc, aArgs1, aArgs2) {
    var PagingObject = aArgs1[0];
    if (PagingObject.__Active == false) return;
    if (PagingObject.DataList.Cursor.Index() == -1) return;    
}

function __DLPO_PageRequest_PreBuilder(oJSCtl, oTransaction, oAJAXRequest) {
    var oPagingObject = oTransaction.PagingObject;
   
 
    try 
    {
        if (this.PagingObject.RequestBuilder(oJSCtl, oTransaction, oAJAXRequest) == false) return false;
        
        // Check for 'Modifiers' Param
        var rParams = oAJAXRequest.RequestParameters;
        var nodModifiers = null;
        if (rParams.Contains("Name", "Modifiers")) {
            nodModifiers = rParams.Find("Name", "Modifiers", true);
        } else {
            nodModifiers = rParams.Add("Modifiers");
        }
        
        nodModifiers.Nodes.Add("PageSize", oPagingObject.PageSize);
        nodModifiers.Nodes.Add("PageNumber", oTransaction.PageItem.PageNumber + 1);        
        
        oPagingObject.Executing = true;
        oTransaction.PageItem.Status = 'EXECUTING'; 
    
    } catch (err) {
        Msgbox("An error has occurred while trying to process a DataListPageQueue Page request build.\n\n" + err.message, vbCritical + vbOKOnly);
        return false; 
    }    
}
function __DLPO_PageRequest_PostHandler(oTransaction, oAJAXRequest) {

    var oPagingObject = oTransaction.PagingObject;
    
    try 
    {
        if (oTransaction.PageItem.Status == 'CANCELLED') return;

        if (oPagingObject.RequestHandler(oTransaction,oAJAXRequest) == false) return;

        var oDataSet = oAJAXRequest.XMLResponse.selectSingleNode(oPagingObject.DataSetPath);
        var oTotalCount = oAJAXRequest.XMLResponse.selectSingleNode(oPagingObject.TotalCountPath);     
   
        if (oAJAXRequest.Response.ErrorNumber != 0) {
            new AJAXError(oAJAXRequest, 'Error occurred while paging data').Show();
            return;
        }
        
        if (oDataSet == null) {
            Msgbox("Unable to retrieve the dataset node from the queued data page", vbCritical + vbOKOnly);
            return;
        }
        
        if (oTotalCount == null) {
            Msgbox("Unable to retrieve the total count node from the queued data page", vbCritical + vbOKOnly);
            return;
        }
       
        var iTotalCount = oTotalCount.text.toString().ToInt();
        var iPageNumber = oTransaction.PageItem.PageNumber;
        var iPageSize   = oPagingObject.PageSize;
        var iTotalPages = ((iTotalCount / iPageSize) +  (iTotalCount % iPageSize > 0 ? 1 : 0)).toString().ToInt();  

        if (oPagingObject.Pages == null) {    
            oPagingObject.DataList.Rows.Items.splice(0, oPagingObject.DataList.Rows.Items.length);
            oPagingObject.DataList.Rows.Items[iTotalCount-1] = undefined;
            oPagingObject.Pages = new Array(iTotalPages);
        }
            
        oPagingObject.XMLLoadArguments.InsertAt      = iPageNumber * iPageSize;
        oPagingObject.XMLLoadArguments.XML           = oDataSet;        
        oPagingObject.DataList.Rows.LoadXML(oPagingObject.XMLLoadArguments);

        oPagingObject.Pages[iPageNumber] = true;        
      
        var EventArgs = new EventArguments();
        EventArgs.PageNumber = iPageNumber;
        EventArgs.PageSize   = iPageSize;
        EventArgs.TotalPages = iTotalPages;
        EventArgs.TotalCount = iTotalCount;
                        
        oPagingObject.DataList.Events.RaiseEvent("DATAPAGELOADED", null, EventArgs);
        
    } catch (err)
    {
        Msgbox("An error has occurred while trying to process a DataListPageQueue Page request handler: " + err.message, vbCritical + vbOKOnly);
        return;
    }
    finally {
        for (var i = 0; i < oPagingObject.Queue.length; i++) if (oPagingObject.Queue[i] == oTransaction.PageItem) { oPagingObject.Queue.splice(i, 1); break; }         
        oPagingObject.LoadNextPage();
    }
   
}


function __DLPO_QueuePage(PageNumber) {
    for (var i = 0; i < this.Queue.length; i++) if (this.Queue[i].PageNumber == PageNumber) return;
    
    var PageItem = new __DLPO_PageQueueItem();
    PageItem.PageNumber = PageNumber;
    PageItem.PagingObject = this;

    this.Queue.splice(0, 0, PageItem);

    if (this.Executing == false) this.LoadNextPage();
}

function __DLPO_LoadNextPage() {
    var PageItem = null;     

    for (var i = 0; i < this.Queue.length; i++)
    {
        if (this.Queue[i].Status == 'WAITING') {
            PageItem = this.Queue[i];   
            break;
        }
    }
    
    if (PageItem == null) {
        this.Executing = false;
        return;
    } 

    var t = this.QueueSystem.Transactions.Add();
    t.RequestBuilder = __DLPO_PageRequest_PreBuilder;
    t.ResponseHandler = __DLPO_PageRequest_PostHandler;
    t.PageItem = PageItem;   
    PageItem.Transaction = t;
    t.PagingObject = this;
    t.Ready();
}




function DataList() 
{
    /// <summary>Used for handling tabular data</summary>
    /// <field name="Events" type="Events">Events for the datalist</field>
    /// <field name="Rows" type="DataRows">Rows Collection</field>
    /// <field name="Columns" type="DataColumns">Columns collection</field>
    /// <field name="Cursor" type="__DATALIST_Cursor">Cursor for the DataList</field>
    /// <field name="Paging" type="DataListPagingObject">Bound Paging object for the DataList</field>
    /// <field name="AutoValidate" type="Boolean">Whether to auto validate rows against the schema</field>
    /// <field name="RaiseEvents" type="Boolean">Whether to raise events when rows are inserted, cell values changed ect</field>

    this.AutoValidate = false;
    this.RaiseEvents = false;
    
    this.Events = new Events();
    this.Rows = new DataRows(this);
        this.Rows.Events = this.Events;

    this.Columns = new DataColumns(this);
        this.Columns.Events = this.Events;
		
	this.Cursor = new __DATALIST_Cursor(this.Rows.Items);
	
	this.Paging = new DataListPagingObject(this);
}
DataList.prototype.toString = __DataList_toString;
DataList.prototype.CopyFrom = __DataList_CopyFrom;
DataList.prototype.GetCell = __DataList_GetCell;
DataList.prototype.SetCell = __DataList_SetCell;
DataList.prototype.Clear = __DataList_Clear;
DataList.prototype.WriteTable = __DataList_WriteTable;


function __DataList_toString() {
    return '[DataList]';
}

function __DataList_CopyFrom(dlDataList) {
    this.Columns.CopyFrom(dlDataList);    
    this.Rows.CopyFrom(dlDataList);
}


function __DataList_GetCell(iRow, iCol) {
    ///<summary>
    ///	<param name="iRow"	type="Object"			description="Row Index OR Row Object to get the cell value from">
    ///	<param name="iCol"	type="numeric"			description="Col Index or Column Name to get the cell value from">
    ///
    /// <returns type="variant/null" default="null" description="returns the value of the cell, returns null if failed">
    ///
    ///	<description>
    ///	</description>
    ///</summary>

    var aRow = null;

    // Check Row argument (physical row or Index)
    if (typeof (iRow) == 'object' && iRow.constructor == Array) {
        aRow = iRow;
    } else if (!isNaN(iRow)) {
        aRow = this.Rows.Items[iRow];
    } else {
        // nothing
    }

    var iFoundCol = null;

    // Check column argument
    if (typeof (iCol) == 'string') {
        iFoundCol = this.Columns.GetColumnIndexByName(iCol);
    } else if (typeof (iCol) == 'number') {
        iFoundCol = iCol;
    } else if (typeof (iCol) == 'object') {
        iFoundCol = this.Columns.GetIndex(iCol);
    } else {
        // nothing
    }

    if (aRow == null) {
        var e = new Error();
        e.message = 'Unable to attain specified row for GetCell()';
        e.description = e.message;
        throw e;
    }

    if (iFoundCol == null) {
        var e = new Error();
        e.message = 'Unable to attain specified column for GetCell()';
        e.description = e.message;
        throw e;
    }

    return aRow[iFoundCol];
}


function __DataList_SetCell(iRow, iCol, uValue) {
    ///<summary>Sets the value of a specified cell.</summary>
    ///	<param name="iRow"		type="Object">Row or Row Index to set the cell on</param>
    ///	<param name="iCol"		type="Object">Column Name, Column or Colum Index to set the cell on</param>
    ///	<param name="uValue"	type="Object">Value the cell is to be set to</param>
    /// <returns type="Boolean" >returns false if failed, returns true if the cell was set">

    var dl = null;

    switch (this.constructor) {
        case DataList:
            dl = this;
            break;

        case DataRows:
        case DataColumns:
            dl = this._pDataList;
            break;

        default:
            // Unsupported.
            break;
    }

    if (iRow == undefined || iCol == undefined) {
        return false;
    }

    switch (typeof (iCol)) {
        case "string":
            iCol = this.Columns.GetColumnIndexByName(iCol);
            break;


        case "number":
            // nothing
            break;

        case "object":
            if (iCol.constructor == DataColumn) {
                iCol = this.Columns.Index(iCol);
            } else {
                iCol = -1;
            }
            break;
    }

    if (iCol < 0 || iCol >= this.Columns.Items.length) {
        var e = new Error();
        e.message = 'Invalid column specified for SetCell';
        e.description = e.message;
        throw e;
    }

    var aRow = null;

    if (typeof (iRow) == 'object' && iRow.constructor == Array) {
        aRow = iRow;
    } else {
        if (isNaN(iRow) || iRow > dl.Rows.Items.length - 1) return false;

        aRow = dl.Rows.Items[iRow];
    }

    aRow[iCol] = uValue;

    if (dl.AutoValidate == true) {
        var aDataRows = new Array(1);
        aDataRows[0] = aRow;
        dl.Rows.Validate(aDataRows);
    }

    if (dl.Rows.TrackModified) aRow.Modified = true;
    if (dl.RaiseEvents) dl.Events.RaiseEvent("DATALIST_CHANGE", this);
    if (dl.RaiseEvents) dl.Events.RaiseEvent("DATALIST_DATAROW_CELLVALUECHANGE", this, iRow, iCol, uValue);

    return true;
}

function __DataList_Clear() {
    ///<summary>Clears ALL columns and rows from the data list.</summary>
	///<returns type="Boolean">returns true if successful</returns>
	
	this.Columns.Clear();
	this.Rows.Clear();
	return true;
}


function __DataList_WriteTable(oTarget, iRowCount) {
    /// <summary></summary>
    /// <param name="oTarget" type="HTMLElement">HTML Element to write the output to</param>
    /// <param name="iRowCount" type="Number" optional="true">How many rows to write out
    var aOut = new Array(0);
    var idx = 0;
    if (iRowCount == null || iRowCount == undefined) iRowCount = this.Rows.Items.length;


    aOut[idx++] = "<table border=1 bgcolor=white><tr>";

    for (var i = 0; i < this.Columns.Count(); i++) {
        aOut[idx++] = "<td style='white-space: nowrap;'><b>[" + i + "] " + this.Columns.Items[i].Name + "</b></td>";
    }

    sOut += "</tr>";

    for (var i = 0; i < iRowCount; i++) {
        aOut[idx++] = "<tr>";

        for (var n = 0; n < this.Rows.Items[i].length; n++) {
            aOut[idx++] = "<td style='white-space: nowrap;'>" + ((this.Rows.Items[i][n] != undefined) ? this.Rows.Items[i][n] : "<i>&lt;NULL&gt;</i>") + "</td>";
        }

        aOut[idx++] = "</tr>";
    }

    aOut[idx++] = "</table>";

    var sOut = aOut.join("");
    oTarget.innerHTML = sOut;
}


function __DATALIST_Cursor(pDataRows) {
    this.Events = new Events();
    this.__pDataRows = pDataRows;
    this.__Index = -1;        
}
__DATALIST_Cursor.prototype.MoveNext     = __DATALIST_Cursor_Next;
__DATALIST_Cursor.prototype.MovePrevious = __DATALIST_Cursor_Previous;
__DATALIST_Cursor.prototype.MoveFirst    = __DATALIST_Cursor_First;
__DATALIST_Cursor.prototype.MoveLast     = __DATALIST_Cursor_Last;
__DATALIST_Cursor.prototype.Index    = __DATALIST_Cursor_Index;
__DATALIST_Cursor.prototype.Row      = __DATALIST_Cursor_Row;
__DATALIST_Cursor.prototype.EOF      = __DATALIST_Cursor_EOF;
__DATALIST_Cursor.prototype.BOF      = __DATALIST_Cursor_BOF;

function __DATALIST_Cursor_Next() {
    if (this.__Index+1 > this.__pDataRows.length-1) return false;
    this.Index(this.__Index+1);
    this.Events.RaiseEvent('MOVE_NEXT');
    return true;
}
function __DATALIST_Cursor_Previous() {
    if (this.__Index-1 < 0) return false;
    this.Index(this.__Index-1);
    this.Events.RaiseEvent('MOVE_PREVIOUS');
    return true;
}
function __DATALIST_Cursor_First() {
    this.Index((this.__pDataRows.length > 0) ? 0 : -1);
    this.Events.RaiseEvent('MOVE_FIRST');
    return true;
}
function __DATALIST_Cursor_Last() {
    this.Index(this.__pDataRows.length-1);
    this.Events.RaiseEvent('MOVE_LAST');
    return true;
}
function __DATALIST_Cursor_Index(iIndexOrArrayRow) {
    if (iIndexOrArrayRow == undefined) {
        // return index
        return this.__Index;
    } else {
        // set index
        var rows = this.__pDataRows;
        
        if (isNaN(iIndexOrArrayRow)) {            
            var aRow = iIndexOrArrayRow;            
            var iIndexOrArrayRow = -1;

		    for (var i = 0; i < rows.length; i++) if (rows[i] == aRow) { iIndexOrArrayRow = i; break; }
        }
        
        if (iIndexOrArrayRow < -1 || iIndexOrArrayRow > rows.length -1) return;
        
        var iPreviousIndex = this.__Index;
        this.__Index = iIndexOrArrayRow;        
        this.Events.RaiseEvent('MOVE', this, iIndexOrArrayRow, iPreviousIndex);
    }   
}
function __DATALIST_Cursor_Row() {
    return this.__pDataRows[this.Index()];           
}
function __DATALIST_Cursor_BOF() {
    return (this.Index() == 0);
}
function __DATALIST_Cursor_EOF() {
    return (this.Index() == this.__pDataRows.length-1);
}



DataColumn.prototype.Sort = __DATACOLUMN_Sort;
DataColumn.prototype.NextIncrement = __DataColumn_NextIncrement;

function DataColumn(Arguments) {
    /// <summary>A DataColumn used by the DataList, holds information relating to a columns schema</summary>
    /// <param name="Arguments" type="Object">Arguments object to initilize values</param>
    /// <field name="Parent" type="DataColumns">Bound DataColumns Collection</field>
    /// <field name="Name" type="String">Name of the column</field>
    /// <field name="Alias" type="String">Alias of the column</field>
    /// <field name="DataType" type="String">DataType (eg, string, date, numeric, array, object)</field>
    /// <field name="Sorted" type="Boolean">Used to determine if the current datalist is sorted by this column</field>
    /// <field name="SortAscending" type="Boolean">Used to determine which direction the column is sorted by</field>
    /// <field name="Caption" type="String">Caption displayed on a DataGrid, if not specified the Alias, and then Name is used</field>
    /// <field name="Width" type="String">Either set to a number or '*', or a percetenage '10%'</field>
    /// <field name="Visible" type="Boolean">Whether the column will appear on objects with a UI</field>
    /// <field name="DefaultValue" type="Object">Default value of a column, you can also specify a function to generate one on the fly</field>
    /// <field name="Validator" type="Function">Function that will validate a value in this column</field>
    /// <field name="Style" type="">CSS Format that is applied to this column</field>
    /// <field name="Format" type="">DataType format to be applied to this column (eg, 'dd/mm/yyyy' for dates)</field>
    /// <field name="CaptionTextAlignment" type="String">Caption alignment</field>
    /// <field name="ListValues" type="Collection">List of possible list values for a column</field>

    // Set Default    
    this.Parent = null;

	this.Name = "";                     // Name of the field the data is being sourced from
	this.Alias = null;                  // Used when compiling a Field List
	this.DataType = "string";			// string, numeric, date, boolean, object, array, other... 
	
	this.Sorted = false;
	this.AllowSort = false;
	this.SortAscending = true;
	
	this.Caption = "";	
	this.Width = "*";	
	this.Visible = true;		
	this.DefaultValue = undefined;
	this.Validator = null;
	this.Style = '';
	this.Format = "";
	this.CaptionTextAlignment = null;
	
	this.ListValues = new Collection();
	    this.ListValues.DefaultIndex = null;
	    this.ListValues.DefaultValue = null;
		this.ListValues.Add = function(Text, Value) {
			var o = new Object();
				o.Text	= (Text == undefined) ? "" : Text;
				o.Value	= (Value == undefined) ? "" : Value;
			
			this.Items.splice(this.Items.length, 0, o);
			
			return o;
		}
	
	if (Arguments != undefined)
    {
        for (var n in Arguments) 
        {
            if (Arguments.hasOwnProperty(n)) 
            {
                this[n] = Arguments[n];
            }
        }
    }
    
}


function __DATACOLUMN_Sort(bAscending) {
    /// <summary>Sorts the column asc or desc</summary>
    /// <param name="bAscending" type="Boolean">Sorts ascending or descending depending on input</param>
    
    var iColIndex = -1;
    var oCols = this.Parent.Items;
    for (var c = 0; c < oCols.length; c++)
        if (oCols[c] == this) { iColIndex = c; break; }
        
    if (iColIndex == -1) {
        var e = new Error();
        e.description = "An error occurred trying to perform the sort.  The column object has been disconnected from its parent";
        throw e;
    }     

    if (bAscending == undefined) bAscending = this.SortAscending;

    this.Parent._pDataList.Rows.Sort(iColIndex, bAscending);
    this.SortAscending = bAscending
}



function __DataColumn_NextIncrement()
{
    ///<summary>Returns next numeric increment for a column</summary>
	/// <returns type="Number">returns next numeric increment for a column</returns>
	if (this.Parent == null)
	    throw new Error('Column must first be attached to a DataList');
	
	var keepsearching = true;
	var colIndex = this.Parent.GetIndex(this);    
    var allRows = this.Parent._pDataList.Rows.Items;
    var curIndex = allRows.length;
    var nextIncrement = 0;

    while (keepsearching)
    {
        curIndex--;
  
        if (curIndex < 0) 
        {
            keepsearching = false;
            nextIncrement = 0;
            break;
        }

        if (typeof(allRows[curIndex][colIndex]) == 'number')
        {
            keepsearching = false;
            nextIncrement = allRows[curIndex][colIndex] + 1;
            break;
        }
    }
    
    return nextIncrement;	
}


function DataColumns(pDataList) {
    /// <summary>Data columns collection used in the DataList</summary>
    /// <field name="Rows" type="">Rows collection of the bound DataList</field>
    /// <field name="_pDataList" type="">Bound DataList</field>
    /// <field name="AllowSort" type="Boolean">Is this column allowed to be sorted</field>

	this.Inherits(Collection);

	this.AllowSort = false;                 // Default Value for whether a column allows sorting (from DataGrid)
	
	this._pDataList = pDataList;	
	this.Rows = pDataList.Rows;
	
	// Routed methods
	this.GetCell = pDataList.GetCell;
	this.SetCell = pDataList.SetCell;	
}
DataColumns.prototype.GetIndex          = __DATACOLUMNS_GetIndex;
DataColumns.prototype.Get               = __DATACOLUMNS_GetColumn;
DataColumns.prototype.FirstVisible      = __DATACOLUMNS_GetFirstVisibleColumn;
DataColumns.prototype.FirstVisibleIndex = __DATACOLUMNS_GetFirstVisibleColumnIndex;
DataColumns.prototype.CopyFrom          = __DataColumns_CopyFrom;
DataColumns.prototype.DistinctValues    = __DATACOLUMNS_DistinctValues;
DataColumns.prototype.GetFieldList = __DATACOLUMNS_GetFieldList;
DataColumns.prototype.Validate = __DATACOLUMNS_Validate;
DataColumns.prototype.Sort = __DATACOLUMNS_Sort;
DataColumns.prototype.MultiSort = __DATACOLUMNS_MultiColumnSort;
DataColumns.prototype.GetData = __DATACOLUMNS_GetData;
DataColumns.prototype.GetColumnIndexByName = __DATACOLUMNS_GetColumnIndexByName;
DataColumns.prototype.GetColumnByName = __DATACOLUMNS_GetColumnByName;
DataColumns.prototype.Add = __DATACOLUMNS_Add;
DataColumns.prototype.SortedColumn = __DATACOLUMNS_GetSortedColumn;

function __DATACOLUMNS_Add(NameOrArguments, Type, Index) {
    ///	<summary>
    ///		Adds a new column to the columns collection
    ///	</summary>
    ///	<param name="NameOrArguments"	type="Object" optional="true">Arguments object or Name of the column you want returned</param>
    ///	<param name="Type"	type="String" optional="true">DataType of the column you want returned</param>
    ///	<param name="Index"	type="Number" optional="true">Index of where the column should be placed</param>
    /// <returns type="DataColumn">Column</returns>
    
    var o = null;

    if (NameOrArguments != undefined && typeof (NameOrArguments) == 'object') {
        var Arguments = NameOrArguments;
        o = new DataColumn(Arguments);
        o.Parent = this;
    }
    else {
        var Name = NameOrArguments;
        if (Name == undefined) Name = "";
        if (Type == undefined) Type = "string";


        o = new DataColumn();
        o.Name = Name;
        o.Type = Type;
        o.AllowSort = this.AllowSort;
        o.Parent = this;
    }

    if (Index == undefined) Index = this.Items.length;

    this.Items.splice(Index, 0, o);

    return o;
}


function __DATACOLUMNS_GetColumnByName(sName) {
    ///	<summary>
    ///		Function searches the columns collection for a column with the given name, null is returned if a matching column
    ///		is not found.
    ///	</summary>
    ///	<param name="sName"	type="String">Name of the column you want returned</param>
    /// <returns type="DataColumn">Column (if found), otherwise null</returns>

    var i = this.GetColumnIndexByName(sName);
    if (i == -1) {
        return null;
    } else {
        return this.Items[i];
    }
}

function __DATACOLUMNS_GetColumnIndexByName(sName) {
    ///	<summary>
    ///		Function searches the columns collection for a column with the given name, -1 is returned if a matching column
    ///		is not found.
    ///	</summary>
    ///	<param name="sName"	type="String">Name of the column whos index you want returned</param>
    /// <returns type="Number">Index of the column (if found).  -1 will be returned if a column with the specified name is not found</returns>

    sName = sName.toUpperCase();

    for (var i = 0; i < this.Items.length; i++) {
        if (sName == this.Items[i].Name.toUpperCase()) return i;
    }

    return -1;
}


function __DATACOLUMNS_GetData(iColIndex, iStart, iCount) {
    ///<summary>Function returns an array slice from one column (specified) in the function call.</summary>
    ///	<param name="iColIndex"	type="Number">Column index to return</param>
    ///	<param name="iStart"	type="Number"	optional="true">Row Index to start at</param>
    ///	<param name="iCount"	type="Number"	optional="true">Amount of rows to return, default is all</param>
    /// <returns type="Array">An array containing a column slice of the DataList</returns>

    var aReturn = new Array(0);
    var iColCount = this.DataList.Columns.Items.length;
    var paData = this.Rows;
    var iMaxRowCount = paData.length;

    iStart = (iStart == undefined || isNaN(iStart)) ? 0 : parseInt(iStart, 10); 				// default returns all cells
    iCount = (iCount == undefined || isNaN(iCount)) ? iMaxRowCount : parseInt(iCount, 10); 	// default returns all cells start point

    var iGetCount = 0;

    if ((iStart + iCount) - 1 < iMaxRowCount) {
        iGetCount = iCount;
    } else {
        iGetCount = iCount - ((iStart + iCount) - iMaxRowCount);
    }

    if (iStart > iMaxRowCount) return aReturn; 	// nothing will be grabbed, out of bounds

    aReturn = new Array(iGetCount);

    for (var i = 0; i < iGetCount; i++) {
        aReturn[i] = paData[((iStart + i) * iColCount)][iColIndex];
    }

    return aReturn;
}


function __DATACOLUMNS_Validate(ColIndex, paRows, iStart, iCount) {
    ///<summary>Function validates the data a single given columns datatype.</summary>
    ///	<param name="ColIndex"	type="Number">Column to validate</param>
    ///	<param name="paRows"	type="Array">Rows to valide</param>
    ///	<param name="iStart"	type="Number" optional="true">Starting row to validate from</param>
    ///	<param name="iCount"	type="Number" optional="true">Amount of rows to validate</param>
    /// <returns type="Boolean">Returns true if validation was completed successfully</returns>
    return this.Rows.Validate(paRows, iStart, iCount, ColIndex);
}



function __DATACOLUMNS_Sort(iColIndex, sAscDesc) {
    ///<summary>Sorts the datalist by a given column, sorts by the columns datatype</summary>
    ///	<param name="iColIndex"	type="Number">Index of the column to sort on</param>
    ///	<param name="sAscDesc"	type="Boolean"	optional="true">Way in which to sort the list, defaults to ascending</param>
    /// <returns type="Boolean">Returns true if the sort was successful, otherwise returns false.</returns>

    var iSortCol = (iColIndex == undefined || isNaN(iColIndex)) ? 0 : parseInt(iColIndex, 10);
    var bASC = (sAscDesc == undefined || sAscDesc.toUpperCase() == "ASC");

    if (iSortCol >= this.Items.length || iSortCol < 0) return false; 			// trying to sort on a column that doesnt exist, too big or too small

    this.Sorter = function(a, b) {
        var sComparison = "numeric";
        var iRetVal = 0;

        switch (sComparison) {
            case "numeric":
                var x = parseInt(a[0], 10); 				// 1 = Column Number
                var y = parseInt(b[0], 10);

                iRetVal = (bASC) ? x - y : y - x;
                break;

            case "string":
                var x = "";
                var y = "";
                break;

            case "date":
                var x = "";
                var y = "";
                break;
        }

        return (isNaN(iRetVal)) ? 0 : iRetVal;
    }

    var a = this.Get(iColIndex);

    var b = new Array(a.length);
    for (var i = 0; i < a.length; i++) {
        b[i] = new Array();
        b[i][0] = a[i];
        b[i][1] = i;
    }

    b.sort(this.Sorter);

    // Temporarily store our data...
    for (var i = 0; i < b.length; i++)
        b[i][0] = this.DataList.Rows.Get(b[i][1], 1);


    var paData = this.DataList.Data;
    var iColCount = this.Items.length;

    // Re-Construct our newly sorted array
    for (var i = 0; i < b.length; i++) {
        for (var n = 0; n < b[i][0][0].length; n++) {
            paData[(i * iColCount) + n] = b[i][0][0][n];
        }
    }

    return true;
}

function SortCmd(ColumnOrObjectParams, Direction, DataType)
{
    var Column;
    // Using an Object Param as the intializer
    if (typeof (ColumnOrObjectParams) == "object")
    {
        Column = ColumnOrObjectParams.Column;
        Direction = ColumnOrObjectParams.Direction;
        DataType = ColumnOrObjectParams.DataType;
    }
    else
    {
        Column = ColumnOrObjectParams;
    }

    this.Column = Column;
    this.Direction = Direction;
    this.DataType = DataType;
}

function __DATACOLUMNS_MultiColumnSort(uSortParams)
{
    if (__DATACOLUMNS_MultiColumnSort.arguments.length == 0)
        throw new Error("No SortCmds have been specified");

    var sorters = new Array();
        
    for (var i = 0; i < __DATACOLUMNS_MultiColumnSort.arguments.length; i++)
    {
        var curArg =  __DATACOLUMNS_MultiColumnSort.arguments[i];

        if (typeof (curArg) != "object" || curArg.constructor != SortCmd)
            throw new Error("Argument " + i + " is not a valid SortCmd object");

        if (typeof (curArg.Column) != "string" && typeof (curArg.Column) != "number")
            throw new Error("Argument " + i + " does not have a valid Column specified, value specified: " + curArg.Column);

        var targetColumnIndex = null;
        if (typeof (curArg.Column) == "string")
        {
            targetColumnIndex = this.GetColumnIndexByName(curArg.Column);
            if (targetColumnIndex == -1)
                throw new Error("Argument " + i + "'s specified column '" + curArg.Column + "' does not exist");
        }
        else
        {
            if ((curArg.Column < 0) || (curArg.Column >= this.Items.length))
                throw new Error("Argument " + i + "'s specified column " + curArg.Column + " is out of bounds");

            targetColumnIndex = curArg.Column;
        }

        
        var targetColumnDataType = null;
        
        if (curArg.DataType == null || curArg.DataType == undefined)
        {
            // Try determine the datatype from the column header
            var columnDataType = this.Items[targetColumnIndex].DataType;

            if (typeof(columnDataType) == "string")
            {
                // Use the specified data type
                targetColumnDataType = columnDataType;
            }
            else
            {
                // Attempt to use the first value available to determine the type
                if (this.DataList.Rows.Items.length > 0)
                {
                    switch (typeof (this._pDataList.Rows.Items[0][targetColumnIndex]))
                    {
                        case "number":
                            targetColumnDataType = "numeric";
                            break;

                        case "object":
                            if (this._pDataList.Rows.Items[0][targetColumnIndex].constructor == Date)
                            {
                                // Check if the object is a date
                                targetColumnDataType = "date";
                            }
                            else
                            {
                                // Default back to String
                                targetColumnDataType = "string";
                            }
                            break;
                            
                        case "string":
                        default:
                            targetColumnDataType = "string";
                            break;
                    }
                }
                else
                {
                    // Default to string sorting
                    targetColumnDataType = "string";
                } 
            }                        
        }
        else if (typeof(curArg.DataType) != "string")
        {
            throw new Error("Argument " + i + "'s DataType parameter is not a string as expected");
        }
        

        var targetColumnDataTypeSorter = null;

        switch (targetColumnDataType.toUpperCase())
        {
            case "NUMBER":
            case "NUMERIC":
            case "INTEGER":
            case "FLOAT":
            case "DOUBLE":
                targetColumnDataTypeSorter = Sorter__NumericSort;
                break;

            case "STRING":
                targetColumnDataTypeSorter = Sorter__StringSort;
                break;

            case "DATE":
            case "DATETIME":
                targetColumnDataTypeSorter = Sorter__DateSort;
                break;

            default:
                throw new Error("Unknown data type to sort as for argument " + i + ", DataType: " + curArg.DataType + " - try string, numeric or date");                                            
        }

        var targetColumnDirection = curArg.Direction;
        if (targetColumnDirection == null || targetColumnDirection == undefined) targetColumnDirection = "Asc";
        if (typeof (targetColumnDirection) != "string")
            throw new Error("Sort direction for argument " + i + " is not a valid string - try Asc, Desc, Ascending, Descending");
             
        switch (targetColumnDirection.toUpperCase())
        {
            case "ASC":
            case "ASCENDING":
                targetColumnDirection = "Asc";
                break;

            case "DESC":
            case "DESCENDING":
                targetColumnDirection = "Desc";
                break;

            default:
                throw new Error("Sort direction for argument " + i + " is not a valid option - try Asc, Desc, Ascending, Descending");
                break;
        }

        sorters[i] = { ColumnIndex: targetColumnIndex, Direction: targetColumnDirection, Sorter: targetColumnDataTypeSorter };
    }

    var mainSorter = function(rowA, rowB)
    {
        var sortVal = null;
        for (var i = 0; i < sorters.length; i++)
        {
            sortVal = sorters[i].Sorter(rowA[sorters[i].ColumnIndex], rowB[sorters[i].ColumnIndex]);
            if (sortVal != 0)
            {
                if (sorters[i].Direction == "Desc")
                {
                    sortVal = sortVal * -1;
                }

                return sortVal;
            }
        }

        return 0;
    }

    this._pDataList.Rows.Items.sort(mainSorter);
}


function Sorter__StringSort(val1, val2)
{
    // Check to make sure they're both strings
    if (typeof (val1) != "string") return -1;
    if (typeof (val2) != "string") return 1;
    
    // check for equality
    if (val1 == val2) return 0;
    if (val1.toUpperCase() == val2.toUpperCase()) return 0;

    // check for greater than
    if (val1.toUpperCase() > val2.toUpperCase()) return 1;

    if (val1.toUpperCase() < val2.toUpperCase()) return -1;
}
function Sorter__DateSort(val1, val2)
{
    // Check to make sure they're both dates
    if (val1.constructor != Date) return -1;
    if (val2.constructor != Date) return 1;
    
    var val1Value = val1.valueOf();
    var val2Value = val2.valueOf();

    if (val1Value > val2Value) return 1;
    if (val1Value == val2Value) return 0;
    if (val1Value < val2Value) return -1;
}
function Sorter__NumericSort(val1, val2)
{
    // Check to make sure they're both numerics
    if (typeof (val1) != "number") return -1;
    if (typeof (val2) != "number") return 1;
        
    if (val1 > val2) return 1;
    if (val1 == val2) return 0;
    if (val1 < val2) return -1;
}

function __DATACOLUMNS_GetFieldList()
{
    var oCols = this.Items;
    var fldlist = '';
    for (var i = 0; i < oCols.length; i++)
    {
        if (i > 0) fldlist += ",";
                
        fldlist += (oCols[i].Alias == null || typeof(oCols[i].Alias) != 'string' || oCols[i].Alias.length == 0) ? oCols[i].Name : oCols[i].Alias;
    }
        
    return fldlist;
}

function __DATACOLUMNS_GetFirstVisibleColumn() {
    var oCols = this.Items;
    var i = this.FirstVisibleIndex();
    return (i == -1) ? null : oCols[i];    
}

function __DATACOLUMNS_GetFirstVisibleColumnIndex() {
    var oCols = this.Items;
    for (var i = 0; i < oCols.length; i++) {
        if (oCols[i].Visible == true) return i;
    }
    
    return -1;
}

///<summary>
///
/// <returns type="object/DataColumn" default="null" description="returns the datacolumn that is currently sorted, may return null">
///
///	<description>
///		Returns the column that is currently sorted
///	</description>
///</summary>
function __DATACOLUMNS_GetSortedColumn() {
    var oCols = this.Items;
    for (var c = 0; c < oCols.length; c++)
    {
        if (oCols[c].Sorted) return oCols[c];   // sorted column found.
    }
    
    return null;    // no column found
}

function __DATACOLUMNS_GetColumn(uVal) {
    var iColumnIndex = null;
    
    switch (typeof(uVal)) 
    {
        case "number":
            iColumnIndex = uVal;
            break;
            
        case "string":
            iColumnIndex  = this.GetColumnIndexByName(uVal);
            break;
        
        default:
            var e  = new Error();
            e.message = 'Invalid argument type passed to GetColumn.  String / Numeric only';
            e.description = e.message;
            throw e;
    }  
    
    if (iColumnIndex < 0 || iColumnIndex >= this.Items.length) {
        return null;
    } else {
        return this.Items[iColumnIndex];
    }
}

function __DATACOLUMNS_GetIndex(uVal) {
    var iColumnIndex = null;
    
    switch (typeof(uVal)) 
    {
        case "object":
            if (uVal.constructor != DataColumn) {
                var e  = new Error();
                e.message = 'Invalid argument type passed to GetIndex.  String / Column Object only';
                e.description = e.message;
                throw e;
            }
            
            for (var i = 0; i < this.Items.length; i++) {
                if (this.Items[i] == uVal) { iColumnIndex = i; break; }                                 
            }
            break;
            
        case "string":
            iColumnIndex  = this.GetColumnIndexByName(uVal);
            break;
            
        default:
            var e  = new Error();
            e.message = 'Invalid argument type passed to GetIndex.  String / Column Object only';
            e.description = e.message;
            throw e;
    }  
    
    if (iColumnIndex == null || iColumnIndex < 0 || iColumnIndex >= this.Items.length) {
        return null;
    } else {
        return iColumnIndex;
    }
}


function __DataColumns_CopyFrom(pUnknown) {
    switch (typeof(pUnknown))
    {
        case "object":
            switch (pUnknown.ConstructorName())
            {
                case "DataColumns":
                    pUnknown = pUnknown._pDataList;
                case "DataList":
                    var sourceColumns = pUnknown.Columns.Items;
                    var destColumns = this.Items;
  
                    for (var r = 0; r < sourceColumns.length; r++) 
                    {
                        this.Add();
                        var srcCol = sourceColumns[r];
                        var newCol = destColumns[destColumns.length-1];
                        for (var n in srcCol) 
                        {
                            var sType = typeof(srcCol[n]);
                            switch (sType)
                            {
                                case "function":
                                case "object":
                                    continue;
                            }
                            
                            newCol[n] = srcCol[n];
                        }                        
                    }
                    break;
                    
                default:
                    var e = new Error();
                    e.message = 'Unknown datasource type for DataColumns.CopyFrom()';
                    e.description = e.message;
                    throw e;
            }
            break;
            
        default:
            var e = new Error();
            e.message = 'Unknown datasource type for DataColumns.CopyFrom()';
            e.description = e.message;
            throw e;

    }
}

///<summary>
/// <param type="Array[object|numeric|string]" description = "Either the column, column index, or column names to distinct on">
///
/// <returns type="object/Array" default="Array(0)" description="returns a distinct list of values on the column(s) specified">
///
///	<description>
///		returns a distinct list of values on the column(s) specified
///	</description>
///</summary> 
function __DATACOLUMNS_DistinctValues(uColumnOrNamesOrIndexes)
{
    if (__DATACOLUMNS_DistinctValues.arguments.length == 0) throw new Error('Atleast 1 column index or name must be specified');
    
    var ColIndexes = new Array(__DATACOLUMNS_DistinctValues.arguments.length);
    
    for (var i = 0; i < ColIndexes.length; i++)
    {
        var uColumnOrNameOrIndex = __DATACOLUMNS_DistinctValues.arguments[i];
        switch (typeof(uColumnOrNameOrIndex))
        {
            case "number":
                if (uColumnOrNameOrIndex < 0 || uColumnOrNameOrIndex > this.Items.length-1)
                    throw new Error('Column index out of bounds');
                
                ColIndexes[i] = uColumnOrNameOrIndex;
                break;
                
            case "string":
                var idx = this.GetColumnIndexByName(uColumnOrNameOrIndex);
                if (idx == -1)
                    throw new Error('No Column exists with the specified name');
                  
                ColIndexes[i] = idx;  
                break;
            
            case "object":
                var idx = -1;
                for (var iColumn = 0; iColumn < this.Items.length; iColumn++)
                {
                    if (this.Items[iColumn] == uColumnOrNameOrIndex)
                    {
                        idx = iColumn;
                        break;
                    }
                }
                
                if (idx == -1)
                    throw new Error('Object specified is not a column object of this DataList');
                    
                ColIndexes[i] = idx;  
                break;
                
            default:
                throw new Error('Invalid value specified for column ' + i + '.  Column, Name or Index only');
        }        
    }
    
    var DValues = new Array(0);
    var Rows = this.Rows.Items;
      
    for (var r = 0; r < Rows.length; r++)
    {
        var RowValues = new Array(ColIndexes.length);
        for (var z = 0; z < ColIndexes.length; z++)
        {
            RowValues[z] = Rows[r][ColIndexes[z]];
        }
        
        var found = false;
        for (var n = 0; n < DValues.length; n++)
        {
            if (DValues[n].SameValuesAs(RowValues)) 
            {
                found = true;
                break;   
            }            
        }
        
        if (found) continue;
        
        DValues[DValues.length] = RowValues;
    }
    
    return DValues;
}

DataRows.prototype.LoadXML = __DataRows_LoadXML;
DataRows.prototype.Sort = __DataRows_Sort;
DataRows.prototype.Validate = __DataRows_Validate;
DataRows.prototype.Find = __DataRows_Find;
DataRows.prototype.AdvFind = __DataRows_AdvancedFind;
DataRows.prototype.Remove = __DataRows_Remove;
DataRows.prototype.Delete = __DataRows_Remove;
DataRows.prototype.Insert = __DataRows_Insert;
DataRows.prototype.Create = __DataRows_Create;
DataRows.prototype.Add = __DataRows_Add;
DataRows.prototype.Get = __DataRows_Get;
DataRows.prototype.Clear = __DataRows_Clear;
DataRows.prototype.Count = __DataRows_Count;
DataRows.prototype.Bind = __DataRows_Bind;
DataRows.prototype.CopyFrom = __DataRows_CopyFrom;
DataRows.prototype.Modified = __DataRows_GetModified;
DataRows.prototype.ConvertToObject = __DataRows_ConvertToObject;

function DataRows(pDataList) {
    /// <summary>DataRows collection, used for managing rows in a DataList</summary>
    /// <field name="DataList" type="DataList">Bound DataList</field>
    /// <field name="Items" type="Array" elementType="Array">Row Item collection</field>
    /// <field name="TrackModified" type="Boolean">Whether to track modified rows</field>

    
	this.DataList = pDataList;
	this.Items = new Array(0);
	    this.Items._pDataRows = this;
	    
	this.TrackModified = false;
	
	this._pDataList = pDataList;
	
	this.GetCell = pDataList.GetCell;
	this.SetCell = pDataList.SetCell;
}


function __DataRows_Bind(pArray) {
    this.DataList.Columns.Rows = pArray;
    this.DataList.Cursor.__pDataRows = pArray;
    this.Items = pArray;
    
    if (this.DataList.RaiseEvents) {        
        this.DataList.Events.RaiseEvent("DATALIST_BINDROWS", this, pArray);
        this.DataList.Events.RaiseEvent("DATALIST_CHANGE", this);
    }
}

function __DataRows_Create() {
    ///<summary>Returns a new row (array) suitable for insertion into the DataList</summary>
	/// <returns type="Array">returns a new row suitable for this DataList with defaulted values</returns>
	var Cols = this.DataList.Columns.Items;
    var arrNewRow = new Array(Cols.length);
    for (var c = 0; c < Cols.length; c++) {
        var val = undefined;
        
        if (Cols[c].DefaultValue === undefined) {											
		    switch (Cols[c].DataType.toUpperCase())
		    {
			    case "STRING":
				    val = '';
				    break;
    				
			    case "NUMERIC":
				    val = 0;
				    break;
    				
			    case "DATE":
				    val = new Date();
				    break;
    			
    			case "AUTOINCREMENT":
    			    val = Cols[c].NextIncrement();    			    
    			    break;
    			
			    default:
				    // nothing
		    }
	    } else {
		    if (typeof(Cols[c].DefaultValue) == 'function') {
			    val = Cols[c].DefaultValue();
		    } else {
			    val = Cols[c].DefaultValue;
		    }		
	    }
	
        arrNewRow[c] = val;
    }
    return arrNewRow;
}


function __DataRows_Count() {
	///<summary>Returns the row count of the DataList</summary>
	/// <returns type="Number">returns row count of the DataList</returns>
	return this.Items.length;
}

function __DataRows_Clear() {
    /// <summary>Clears the data list.</summary>
	/// <returns type="Boolean">returns true if successful</returns>
	
	this.Items.splice(0, this.Items.length);
	
	if (this.DataList.RaiseEvents) {
		this.DataList.Events.RaiseEvent("DATALIST_CLEAR", this);
		this.DataList.Events.RaiseEvent("DATALIST_CHANGE", this);
	}
	
	return true;
}

function __DataRows_Get(iStart, iCount) {
    ///<summary>Function returns a set of rows as defined by the input parameters.</summary>
	///	<param name="iStart"	type="Number">Row index to start getting rows from, if not passed, row extraction starts from row 0</param>
	///	<param name="iCount"	type="Number">Amount of rows to return from the starting point.  If not passed, all rows from the starting point are returned</param>
    /// <returns type="Array" elementType="Array">Returns an array that holds all the captured rows</returns>

	
	var aReturn = new Array(0);				
	var iColCount = this.DataList.Columns.Items.length;
	var paData = this.Items;
	var iMaxRowCount = paData.length;
	
	if (iStart == undefined || isNaN(iStart)) iStart = 0;					// default returns all rows
	if (iCount == undefined || isNaN(iCount)) iCount = iMaxRowCount;		// default returns all rows from start point

	var iGetCount = 0;

	if((iStart + iCount) - 1 < iMaxRowCount) {
		iGetCount = iCount;
	} else {			
		iGetCount = iCount - ((iStart + iCount) - iMaxRowCount);
	}
	
	if (iStart > iMaxRowCount) return aReturn;		// nothing will be grabbed, out of bounds
	aReturn = new Array(iGetCount);
	
	for (var i = 0; i < iGetCount; i++) {			
		aReturn[i] = paData[((i + iStart) * iColCount)]								
	} 
	
	return aReturn;
}

function __DataRows_Add(aList) {
    ///<summary>Function inserts a new row at the bottom of the datalist.</summary>
    ///	<param name="aList" parameterArray="true" >Pass in an array of values that the row will contain, alternatively, pass multiple arguments into the function to have those added</param>
	/// <returns type="Boolean">returns true if no error occurred</returns>
   
	var aNewRow = null;

	if (aList != undefined && aList.constructor == Array)
	{
		aNewRow = aList;				
	} else {
		aNewRow = new Array(this.Add.arguments.length);
		
		for (var i = 0; i < this.Add.arguments.length; i++) {
			aNewRow[i] = this.Add.arguments[i];
		}
	}

	return this.Insert(-1, aNewRow);
}

function __DataRows_Insert(iStart, aList) {
    ///<summary>Function inserts a new row into the DataList, using values from either aList[] or a list of args passed in
    ///		after iStart.
    ///</summary>
	///	<param name="iStart"	type="Number"	optional="true">Point of insertion for the new row, pass null to add it to the end of the array, 0 to add it to the start</param>
    ///	<param name="aList"		type="Array"	parameterArray="true">Pass in an array of values that the row will contain, alternatively, pass multiple following arguments into the function to have those added</param>
	/// <returns type="Boolean">returns true if no error occurred</returns>

	
	var aColumns = this.DataList.Columns.Items;
	var iColCount = aColumns.length;
	var iMaxRowCount = this.Items.length;
	var aNewRow = null;
	
	if (iStart == undefined || iStart == null || (!isNaN(iStart) && (iStart > iMaxRowCount || iStart < 0))) iStart = iMaxRowCount;		// Default appends to the end of our data

	if (aList.constructor == Array)
	{
	    // Create an array that satifies the minimum column count of the setup columns.
	    var ArraySize = iColCount > aList.length ? iColCount : aList.length;
	    
		aNewRow = new Array(ArraySize);			// Create a new array of correct size
	
		for (var i = 0; i < ArraySize; i++) 
		{	// Copy Data			
			var val = (i < aList.length) ? aList[i] : undefined;
			
			if (val == undefined) 
			{		
				if (aColumns[i].DefaultValue === undefined) 
				{		
				
					switch (aColumns[i].DataType.toUpperCase())
					{
						case "STRING":
							val = '';
							break;
							
						case "FLOAT":
						case "NUMBER":
						case "DECIMAL":
						case "INTEGER":
						case "NUMERIC":
							val = 0;
							break;
							
						case "DATE":
							val = new Date();
							break;
						
						case "AUTOINCREMENT":
						    val = aColumns[i].NextIncrement();
						    break;
						
						default:
							// nothing
							break;
					}
				} else {

					if (typeof(aColumns[i].DefaultValue) == 'function') 
					{
						val = aColumns[i].DefaultValue();
					} else {
						val = aColumns[i].DefaultValue;
					}		
				}
			} 				

			aNewRow[i] = val;
		}
		
				
	} 
	else 
	{
		aNewRow = new Array(iColCount);			// Create a new array of correct size
			
		for (var i = 0; i < this.Insert.arguments.length; i++)  
		{
			if (this.Insert.arguments.length - 1 > i) 
			{
				aNewRow[i] = this.Insert.arguments[i+1];			// Copy passed argument
			} else {
				aNewRow[i] = "";								// Fill blank if argument doesnt exist.
			}
		}
	}
	
	
	// Switch is done to optimise the splice method.
	this.Items.splice(iStart, 0, aNewRow);
	
	if (this.DataList.AutoValidate == true) {
	    var aRowsArray = new Array(1);
	    aRowsArray[0] = aNewRow;
	    
	    this.Validate(aRowsArray);
	}
		
	
	if (this.DataList.RaiseEvents) {
		this.DataList.Events.RaiseEvent("DATALIST_INSERT", this);
		this.DataList.Events.RaiseEvent("DATALIST_CHANGE", this);
	}
	
	return true;
}

function __DataRows_Remove(uStartRow, iCount) {
    /// <summary>
    ///		Function removes a specified amount of rows from a given starting point.  Not specifying the count
    ///		will mean only the row index specified will be removed.
    /// </summary>
	///	<param name="uStartRow"	type="Number">Row index to start removing from</param>
    ///	<param name="iCount"	type="Number" optional="true">Amount of rows to remove</param>
	///
	/// <returns type="Number">Amount of rows removed</returns>

	
	
	var iDelCount = 0;
	var iColCount = this.DataList.Columns.Items.length;
	var iMaxRowCount = this.Items.length;
	
	
	if (typeof(uStartRow) == "object") {
	    // a physical row has been passed in to be removed.
	    var rows = this.Items;
	    var aRow = uStartRow;                   // array object
	    if (iCount == undefined) iCount = 1;    // By default only try to remove the first instance of it
	    
	    for (var r = rows.length-1; r >= 0; r--)
	    {
	        if (rows[r] == aRow) { 
	            iDelCount++; rows.splice(r, 1); 
	            if (iDelCount == iCount) {
	            
	                if ((iCount > 0) && this.DataList.RaiseEvents) {
		                this.DataList.Events.RaiseEvent("DATALIST_REMOVE", null, 0, iCount);
		                this.DataList.Events.RaiseEvent("DATALIST_CHANGE");
	                }
	                return iDelCount;
                }		                
	        }
	    }
	    
	    if ((iCount > 0) && this.DataList.RaiseEvents) {
            this.DataList.Events.RaiseEvent("DATALIST_REMOVE", null, 0, iCount);
            this.DataList.Events.RaiseEvent("DATALIST_CHANGE");
        }
	} else {
	
	    var iStart = uStartRow;
		
	    if (iStart == undefined) return iDelCount;
		
	    if (iCount == undefined) iCount = 1;
		
	    if (isNaN(iCount))
	    {
		    var e = new Error();
		    e.description = "DataList.Remove(" + iStart + "," + iCount + ") - Invalid iCount";			
		    throw e;
	    }
		
	    if (isNaN(iStart))
	    {
		    var e = new Error();
		    e.description = "DataList.Remove(" + iStart + "," + iCount + ") - Invalid iStart";
		    throw e;
	    }
		
		
	    if (iStart > iMaxRowCount) return iDelCount;		// nothing will be deleted, out of bounds
	
	    if((iStart + iCount) - 1 < iMaxRowCount) {
		    iDelCount = iCount;
	    } else {			
		    iDelCount = iCount - ((iStart + iCount) - iMaxRowCount);
	    }
							
	    this.Items.splice(iStart, iCount);
		
	    if ((iCount > 0) && this.DataList.RaiseEvents) {
		    this.DataList.Events.RaiseEvent("DATALIST_REMOVE", null, iStart, iCount);
		    this.DataList.Events.RaiseEvent("DATALIST_CHANGE");
	    }
	}
	
	return iDelCount;
}

function __DataRows_Find(uSearchFor, iColIndex, iStart, iCount, iMaxReturn, bReturnIndexsOnly) {
    ///<summary>
    ///		Function searches the DataList for the value specified and returns the rows that had cell values that matched
    ///		in an array.  A zero length array will be returned if an error occurred or no matches were found.	
    ///</summary>
    ///	<param name="uSearchFor"	type="Object">Value to search for</param>
    ///	<param name="iColIndex"		type="Object"	optional="true">Column to search, either by numeric index, column name or * for all fields</param>
    ///	<param name="iStart"		type="Number"					optional="true">Starting row index to search from - default is row 0</param>
    ///	<param name="iCount"		type="Number"					optional="true">Amount of rows to search from the starting point - default is all</param>
    ///	<param name="iMaxReturn"	type="Number"					optional="true">Maximum amount of rows to return on a search</param>
    ///	<param name="bReturnIndexsOnly"	type="Boolean"				optional="true">Set to true so that only the Indexes are returned</param>   
    /// <returns type="Array">Returns an array of rows that were matched</returns>
	

	var iColCount = this.DataList.Columns.Items.length;
	var iMaxRowCount = this.Items.length;
	var paData = this.Items;
	var aReturn = new Array(0);
	var bCaseSensitive = true;
	var xSearchFor = '';
	
	if (uSearchFor != null && uSearchFor != undefined && typeof(uSearchFor) == "object" && uSearchFor.constructor == ROWFindArguments)
	{
	    // Arguments object used
	    iMaxReturn =  uSearchFor.MaxReturn
	    xSearchFor = uSearchFor.SearchFor;
	    iColIndex = uSearchFor.ColumnIndex;
	    iMaxRowCount = (uSearchFor.MaxReturn == 0) ? this.Items.length : uSearchFor.MaxReturn;
	    bReturnIndexsOnly = uSearchFor.ReturnIndexsOnly;
	    bCaseSensitive = uSearchFor.CaseSensitive;
	    iCount = (uSearchFor.Count == 0) ? iMaxRowCount : uSearchFor.Count;
	    iStart = uSearchFor.StartIndex;		    
	} else {		
	    // Arguments passed directly in.
	    xSearchFor = uSearchFor;
	}
	
	if (iMaxReturn == undefined || iMaxReturn == null || isNaN(iMaxReturn)) iMaxReturn = 0;
	
    if (iStart == undefined || iStart == null || !isNaN(iStart)) iStart = 0;
    iCount = (iCount == undefined || isNaN(iCount)) ? iMaxRowCount : parseInt(iCount, 10);
		
    if (iStart + iCount > iMaxRowCount) iCount = iMaxRowCount - iStart;
	
    if (iColIndex == undefined || iColIndex == null || iColIndex == "*") {
	    iColIndex = "*"
		
    } else if (!isNaN(iColIndex)) {
	    iColIndex = parseInt(iColIndex, 10);
	
    } else if (isNaN(iColIndex)) {
	    iColIndex = this.DataList.Columns.GetColumnIndexByName(iColIndex);
	    if (iColIndex == -1) return aReturn;
		
    } else {
	    iColIndex = "*"
    }
		
    if (bReturnIndexsOnly == undefined || bReturnIndexsOnly == null) bReturnIndexsOnly = false;

	

	if (!bCaseSensitive && typeof(xSearchFor) == "string") xSearchFor = xSearchFor.toUpperCase();

	
	// Optimised for single column searches...
	if (iColIndex != "*") {
		for (var i = 0; i < iCount; i++)
		{
			if (paData[i + iStart] == undefined) continue;
			var bMatch = false;

			if (!bCaseSensitive && typeof(paData[i + iStart][iColIndex]) == "string") {
			    // non case sensitive search
		        bMatch = (paData[i + iStart][iColIndex].toUpperCase() == xSearchFor)
			} else {
			    // Standard case sensitive search				
			    bMatch = (paData[i + iStart][iColIndex] == xSearchFor);
			}
			
			if (bMatch) aReturn.splice(aReturn.length, 0, ((bReturnIndexsOnly) ? i + iStart: paData[i + iStart])  );
		}
	} else {

	//Full column search "*"
		for (var i = 0; i < iCount; i++)
		{
			for (var nCol = 0; nCol < iColCount; nCol++) 
			{
				if (paData[i] == undefined) continue;
				var bMatch = false;
				if (!bCaseSensitive && typeof(paData[i + iStart][nCol]) == "string") {
				    // non case sensitive search
				    bMatch = (paData[i + iStart][nCol].toUpperCase() == xSearchFor);
				} else {				
				    // case sensitive search	    					
				    bMatch = (paData[i + iStart][nCol] == xSearchFor);
				}
				
				if (bMatch) {
				    aReturn.splice(aReturn.length, 0, ((bReturnIndexsOnly) ? i+iStart : paData[i + iStart]));
					if (iMaxReturn !=0 && aReturn.length == iMaxReturn) return aReturn;
					break;
				}
			}
		}		
	}		
	return aReturn;
}    



function __DataRows_AdvancedFind(findArguments)
{
    var aReturn = new Array(0);
    var paColumns =  this.DataList.Columns.Items;
    var paData = this.Items;
    var aRestrictions = findArguments.Restrictions;
    
    // Compile Rules
    for (var r = 0; r < aRestrictions.length; r++)
    {
        var res = aRestrictions[r];
        var Columns = Array(0);
        // Columns
        if (res.TargetColumns == "*")
        {
            for (var c = 0; c < paColumns.length; c++)
            {
                Columns.splice(Columns.length, c, 0);
            }    
            
        } else if (typeof(res.TargetColumns) == "number")
        {
            Columns.splice(0, res.TargetColumns, 0);
        } else
        {
            var aCols = res.TargetColumns.split(",");
            
            for (var c = 0; c < aCols.length; c++)
            {            
                var iColIndex = aCols[c].IsNumeric() ? aCols[c].ToInt() : this.DataList.Columns.GetColumnIndexByName(aCols[c]);
                if (iColIndex == -1) continue;
                Columns.splice(Columns.length, iColIndex, 0);
            }
        }                    
    }
}




function __DataRows_GetModified(bIndexesOnly)
{
    ///	<summary>
    ///     This function returns the modified rows from the datalist.  The 'TrackModified' property must be
    ///     set to True, or Rows need to be manually marked as Modified = true for this to return anything.
    ///	</summary>
    ///	<param name="bIndexesOnly"		type="Boolean"	optional="true">Whether to return the indexes or the actual row reference</param>
    /// <returns type="Array">Returns either an array of Rows, or Row Indexes</returns>

	
	bIndexesOnly = bIndexesOnly == true;
	
	var Rows = this.Items;
	var Output = new Array();
	
	// Passes rows by the assigned attribute name rather than row length to deal correctly with paged records.
	// The length of the array can be [n] figure, but the actual count of assigned rows on a paged dataset is usually much lower
	for (var r in Rows)
	{
		if (isNaN(r)) continue;	    
        if (Rows[r] == undefined) continue;
        
	    if (Rows[r].Modified == true) Output.splice(Output.length, 0, bIndexesOnly ? r : Rows[r]);
	}    
	return Output;
}

function __DataRows_LoadXML(uXML, iMax, iInsertAt, bReplace, aMapping) {
    ///	<summary>
    ///		This function is used to Load XML data from a standard format from an XML document.  Either an XML fragment (text) OR an Xml node (preferred) needs to be passed
    ///		in.  You have the ability to specifiy a set of mappings, or by default, the entire dataset will attempt to be loaded in.  If using mappings, you are also able
    ///		to specify custom Transform functions for each column.  By default the data is just loaded in as text, however if specifying a transform you are able to pass
    ///		each node individually so you can pass back objects, dates, numerics ect.  It should be noted that by default this function appends data, it does not replace
    //		it unless otherwise specified.
    ///	</summary>
	///	<param name="uXML"		type="Object">Either a text XML fragment  (including root node) or an XML root node.  Both need to support the Row/Col setup</param>
	///	<param name="iMax"		type="Number"	optional="true">Maximum amount of records to extract from the XML fragment</param>
    ///	<param name="iInsertAt"	type="Number"	optional="true">The row at which to start inserting new records at</param>
    ///	<param name="bReplace"	type="Boolean"	optional="true">Whether to replace ro overwrite the rows at a given point</param>
    ///	<param name="aMapping"	type="Array" 	elementType="XMLColumnMapping" optional="true">An array of XMLColumnMapping objects specifying column mappings</param>
    /// <returns type="Boolean">Returns true if everything was parsed correctly</returns>

	var bAutoExpand = true;
	var bAutoCreateColumns = false;
	var bIgnoreMissingColumns = false;
    var bValidate = this.DataList.AutoValidate;

	if (typeof(uXML) == 'object' && (uXML.FunctionArguments == true || (uXML.XML != undefined && typeof(uXML.XML) == 'object' && uXML.XML.constructor == undefined))) {
    // If the CONSTRUCTOR property is undefined, then the object will be an activeX (XML NODE) object
		var oSettings = uXML;
		uXML		= oSettings.XML == undefined ?          null : oSettings.XML ;
		iMax		= oSettings.MaxRecords == undefined ?   null : oSettings.MaxRecords;
		iInsertAt	= oSettings.InsertAt == undefined ?     null : oSettings.InsertAt;
		bReplace	= oSettings.Replace == undefined ?      false: oSettings.Replace;
		aMapping	= oSettings.Mappings;
		bAutoExpand = oSettings.AllowAutoExpand == undefined ? true: oSettings.AllowAutoExpand;
		bAutoCreateColumns = oSettings.AutoCreateColumns == undefined ? false : oSettings.AutoCreateColumns;
		bIgnoreMissingColumns = oSettings.IgnoreMissingColumns == undefined ? false : oSettings.IgnoreMissingColumns;
		
		if (uXML == null || uXML == undefined)
		    throw new Error('XML Source not specified');
		
		if (oSettings.Validate == true || oSettings.Validate == false) bValidate = oSettings.Validate
	}


	var oRoot = null;
	var bUseMapping = (aMapping != undefined && typeof(aMapping) == "object" && aMapping.constructor == Array  && aMapping.length != 0);

	if (uXML != null && typeof(uXML) == "object" && uXML.FunctionArguments == true && uXML.UseMappings != false) bUseMapping = false;
	
	if (typeof(uXML) == "string") {											// Use xml fragment
		var oDoc = new ActiveXObject("MSXML2.DOMDocument.4.0");
		oDoc.loadXML(uXML);														// Attempt to parse
	
		if (oDoc.parseError != 0) return false;									
	
		oRoot = null;
		    throw new Error("not supported...or invalid xml fragment passed.");
		return false;
	} else {

		oRoot = uXML;															// use XML Node passed in
	}
	
	if (iMax == undefined || iMax == null) iMax = oRoot.childNodes.length;
	if (iInsertAt == undefined || iInsertAt == null) { iInsertAt = this.DataList.Rows.Count() }
	if (bReplace == undefined || bReplace == null) { bReplace = false } else { bReplace = true } 
	
	var oldState = this.DataList.RaiseEvents;
	this.DataList.RaiseEvents = false;
	var Items = this.Items;
	var oRows = oRoot.childNodes;
	var Columns = this.DataList.Columns.Items;
	
	var InsertedRows = new Array(0);
	
	// Pre-Compute Destination Columns
	if (bUseMapping) {
		for (var n = 0; n < aMapping.length; n++) {
			var DestColumnIndex = -1;
						
			if (isNaN(aMapping[n].Destination)) {
			    DestColumnIndex = this.DataList.Columns.GetColumnIndexByName(aMapping[n].Destination);
			} else {
			    DestColumnIndex = aMapping[n].Destination;
			}
			
			if (DestColumnIndex < 0 || DestColumnIndex > Columns.length-1) 
			{
			    var e = new Error();
			    e.description = "Transformation destination index is out of range or does not exist (" + aMapping[n].Destination + ")";
			    throw e;
			}
			
			aMapping[n].Destination = DestColumnIndex;
		}
	}
	
		
	for (var i = 0; i < iMax; i++)
	{		
		var oCells  = oRows[i].childNodes;
		
		var o = new Array( ((bUseMapping) ? this.DataList.Columns.Items.length : oCells.length) );			

		if (bUseMapping)                // Use custom mappings?
		{									
	
			for (var n = 0; n < aMapping.length; n++)			// Go through mappings
			{
				if (aMapping[n] == undefined || typeof(aMapping[n]) != 'object' || aMapping[n].constructor != XMLColumnMapping) 
				{
					// incorrect mapping object.
					throw new Error("DataRows:: LoadXML(uXML, iMax, iInsertAt, bReplace, aMapping)\nInvalid or no mapping object specified for LoadXML() @ array position " + n);
				}
				
				
			    var SourceNode = null;
			    var DestColumnIndex = -1;
			    
			    if (isNaN(aMapping[n].Source)) 
			    {
			        SourceNode = oRows[i].selectSingleNode(aMapping[n].Source.toString());
			    } else {
			        SourceNode = oCells[aMapping[n].Source];
			    }
			    
			    					    
			    if (SourceNode == null) 
			    {

			        if (!bIgnoreMissingColumns) 
			        {
			          var e = new Error();
			            e.description = "source data node not found, argument (NodeName/ColumnIndex) specified: " + aMapping[n].Source + '\nYou have specified a column/childnode integer that is out of range for the current row or have specified a node name that could not be found in the child nodes collection.';
			            throw e;
			        } else {
			            continue;
			        }   
			    }
			    					    
			    if (isNaN(aMapping[n].Destination)) 
			    {
			        DestColumnIndex = this.DataList.Columns.GetColumnIndexByName();
			    } else {
			        DestColumnIndex = aMapping[n].Destination;
			    }
			    
			    if (DestColumnIndex < 0 || DestColumnIndex > Columns.length-1)
			     {
			        var e = new Error();
			        e.description = "Transformation destination index is out of range (" + DestColumnIndex + ")";
			        throw e;
			    }
			    
			    if (aMapping[n].Transformer != null) 
			    {			// Use custom transform ?
			        o[DestColumnIndex] = aMapping[n].Transformer(SourceNode, oDoc);		// Node is passed in, xmlDoc is also passed in    						
			    } else {
				    o[DestColumnIndex] = SourceNode.text;	// No custom transform, copy standard text
			    }

			}
			
		} 
		else 
		{

			var iMaxLength = ((bAutoExpand) ? oCells.length : ((oCells.length > Columns.length) ? Columns.length : oCells.length));

			if (bAutoExpand && bAutoCreateColumns && (oCells.length > Columns.length)) 
			{					
				for (var j = Columns.length; j < oCells.length; j++)
				{									
					var oCol = this.DataList.Columns.Add();												
					oCol.Name = oCells[j].nodeName;
					oCol.Caption = oCol.Name;
				}
			}
		
			for (var n = 0; (n < oCells.length && n < iMaxLength); n++)				// No mapping used, copy all data
			{
				o[n] = oCells[n].text;
			}
		}

		if (iInsertAt < Items.length ) 
		{			
			if (bReplace) 
			{
				Items[iInsertAt] = o;				// Insert/replace in stack
			} 
			else 
			{			
				Items.splice(iInsertAt, 0, o);		// Insert into stack	
			}
			
		} 
		else
		{			
			this.Add(o); 							// Add to bottom of stack
		}
		
		if (bValidate == true) InsertedRows.splice(InsertedRows.length, 0, o);
		
		iInsertAt++;	
	}


	if (bValidate == true) this.DataList.Rows.Validate(InsertedRows);				// Validate (if required)
	
	this.DataList.RaiseEvents = oldState;	
	this.Events.RaiseEvent("DATALIST_CHANGE");
}

function __DataRows_CopyFrom(pUnknown) {
    switch (typeof(pUnknown))
    {
        case "object":
            switch (pUnknown.ConstructorName())
            {
                case "Array":
                    if (pUnknown._pDataRows == undefined)
                    {
                        var e = new Error();
                        e.message = 'Unknown datasource type for DataRows.CopyFrom()';
                        e.description = e.message;
                        throw e;
                    }                    
                    pUnknown = pUnknown._pDataRows;
                    
                case "DataRows":
                    pUnknown = pUnknown._pDataList;
                case "DataList":
                    var sourceRows = pUnknown.Rows.Items;
                    var destRows = this.Items;
                    for (var r = 0; r < sourceRows.length; r++) 
                    {
                        var srcRow = sourceRows[r];
                        var newRow = new Array(srcRow.length);
                        for (var n in srcRow) 
                        {
                            newRow[n] = typeof(srcRow[n]) != 'object' ? srcRow[n] : null;
                        }
                  
                        destRows.splice(destRows.length, 0, newRow);
                    }
                    break;
                    
                default:
                    var e = new Error();
                    e.message = 'Unknown datasource type for DataRows.CopyFrom()';
                    e.description = e.message;
                    throw e;
            }
            break;
            
        default:
            var e = new Error();
            e.message = 'Unknown datasource type for DataRows.CopyFrom()';
            e.description = e.message;
            throw e;

    }
}


function __DataRows_Validate(paRows, iStart, iCount, sCols) {
    ///<summary>Validates an array of rows</summary>
    ///	<param name="paRows"	type="Array"	optional="true">A subset of rows from the dataset to validate; if null is passed then the validation is performed on the entire dataset</param>
    ///	<param name="iStart"	type="Number"	optional="true">Row index in the array to start validat</param>
    ///	<param name="iCount"	type="Number"	optional="true">Amount of rows to validate after the starting point</param>
    ///	<param name="sCols"		type="String"	optional="true">Columns to validate, [null], '*' and [undefined] will mean every column is validated</param>
	/// <returns type="Boolean">Returns true if validated successfully, false if otherwise</returns>
		
	var aRows = (paRows == undefined || paRows == null || typeof(paRows) != 'object' || paRows.contructor != "Array") ? this.Items : paRows;
	var Columns = this.DataList.Columns.Items;
	var ColCount = Columns.length;
	var ColTypes = new Array(ColCount);
	var ColDefaults = new Array(ColCount);
	var ColSkip = new Array(ColCount);
	
	if (iStart == undefined || iStart == null) iStart = 0;
	if (iCount == undefined || iCount == null) iCount = this.Items.length;	
	
	if(isNaN(iStart)) return false;
	if(isNaN(iCount)) return false;
	
	if (iStart + iCount > aRows.length) iCount = aRows.length - iStart;

	
	if (sCols != undefined && sCols != null && typeof(sCols) == 'string') {
		if (sCols.indexOf(",")) {
			sCols = sCols.split(",");
		} else {
			sCols = new Array("" + sCols);
		}
		
		for (var i = 0; i < sCols.length; i++) {
			if (isNaN(sCols[i])) {
				var colIndex = Columns.GetColumnIndexByName(sCols[i]);
				if (colIndex != -1 && colIndex < ColCount) ColSkip[colIndex] = true;
			
			} else {
				// column index
				if (sCols[i] < ColCount) ColSkip[parseInt(sCols[i], 10)] = true;
			}
		}
	}
		
		
		
		
	for (var n = 0; n < ColCount; n++)
	{
		ColTypes[n] = Columns[n].DataType;
		ColDefaults[n] = Columns[n].DefaultValue;
		
		if (Columns[n].Validator != null) continue;		// Custom validator included, do not skip this column.
		
		var strDataType = Columns[n].DataType;
		if (strDataType == undefined || strDataType == null) strDataType = "";
		
		switch (strDataType.toLowerCase())
		{
			case "numeric":
			case "number":			
			case "integer":
			
			case "double":
			case "float":
			
			case "date":
			case "datetime":
			
			case "string":
			case "boolean":
				break;
				
			case undefined:
			case null:
			case "object":
			case "array":
			default:
				ColSkip[n] = true;						// This column will skip validation regardless of what the person chooses (unhandled data type validation);
				break;
		}		
	}
		
	for (var i = 0; i < iCount; i++)
	{
		var oRow = aRows[i + iStart];
		
		if (oRow == undefined) continue;			// Allow for blank / uninitialized rows.
		
		for (var n = 0; n < ColCount; n++)			// Validate each cell
		{
			if (ColSkip[n]) continue;					// Skip pre-determined columns
			
			if (Columns[n].Validator != null) {			// Run custom validator, pass in the cell to validate, the column index, and the row.
				oRow[n] = Columns[n].Validator(oRow[n], n, oRow);	
				continue;									// Ignore primary data type validators if a custom validator is included.
			}
			
			var CurrentValue = oRow[n];
			var strDataType = ColTypes[n];
			
			if (oRow[n] == null) continue;
			
			// No custom validator passed, use one of the primary data type validators.
			switch (strDataType.toLowerCase())
			{
			    case "autoincrement":
			        alert("!");
			        if (CurrentValue == undefined || CurrentValue == null)
			        {
			            CurrentValue = Columns[n].NextIncrement();
			            
			        }
			        break;
			        
			    case "number":			    
			    case "integer":
				case "numeric":
				case "float":
				case "double":
				    if (typeof(CurrentValue) == 'numeric') continue;
				    
					if (CurrentValue == undefined || isNaN(CurrentValue) || CurrentValue == "") {
						oRow[n] = (ColDefaults[n] != undefined && !isNaN(ColDefaults[n]) && !isNaN(ColDefaults[n])) ? parseInt(ColDefaults[n], 10) : 0;
						continue;
					}
					
					switch (strDataType.toLowerCase()) 
					{
					    case "number":
					    case "integer":
					    case "numeric":			
					        oRow[n] = parseInt(CurrentValue, 10);
					        break;
					        
					    case "float":
					    case "double":
					        oRow[n] = parseFloat(CurrentValue);
					        break;
					}
					continue;
					break;
					
				case "date":
				case "datetime":			
				    if (typeof(CurrentValue) == 'object' && CurrentValue.constructor == Date) continue;
				
					if (CurrentValue == undefined || CurrentValue == null) {
						CurrentValue = (ColDefaults[n] != undefined && ColDefaults[n] != undefined) ? ColDefaults[n] : "" ;
					}
					
					if (typeof(CurrentValue) != 'string') oRow[n] = CurrentValue.toString(); 
				        				    
					var dtTest = new Date();
					if (dtTest.ParseDate(CurrentValue) == false) continue;				
									    
					oRow[n] = new Date();
					oRow[n].CopyFrom(dtTest);
					continue;
					break;
					
				case "string":
				    if (typeof(CurrentValue) == 'string') continue;
				    
					if (CurrentValue == undefined) {					
						oRow[n] = (ColDefaults[n] != undefined && ColDefaults[n] != undefined) ? ((typeof(ColDefaults[n]) == 'function') ? ColDefaults[n]() : ColDefaults[n]) : "" ;	
					} else {
					    oRow[n] = CurrentValue.toString();
					}
					
					continue;						
					break;
					
				case "boolean":
				    if (typeof(CurrentValue) == 'boolean') continue;
				
					if (CurrentValue == undefined || CurrentValue == null) {
						oRow[n] = (ColDefaults[n] != undefined && ColDefaults[n] != undefined) ? ColDefaults[n] : false;
					} else {
						if (CurrentValue.ToBool) {
							oRow[n] = CurrentValue.ToBool();
						} else {
							oRow[n] = (ColDefaults[n] != undefined && ColDefaults[n] != undefined) ? ColDefaults[n] : false;
						}
					}
					
					continue;
					break;
			}
		}
	}
	
	return true;
}




function __DataRows_Sort(uColumnIndex, bAscending) {
    ///<summary>Sorts a datalist by a given column</summary>
    ///	<param name="iColumnIndex"	type="Object"	optional="true">Column to sort by (can specify by name)</param>
    ///	<param name="bAscending"	type="Boolean"	optional="true">Whether to sort the list Asc or Desc</param>

    var iColumnIndex = (uColumnIndex == undefined) ? 0 : uColumnIndex;
    bAscending = (bAscending == true) ? 'true' : 'false';
   
    // try match a name to the column
    if (typeof(uColumnIndex) == "string") {
        iColumnIndex = this.DataList.Columns.GetColumnIndexByName(uColumnIndex);
        if (iColumnIndex == 0) {
            var e = new Error();
            e.description = "Column '" + uColumnIndex + "' not found";
            throw e;
        } 
    }

    // create a new function identifier
    var fIdent = 'sorter' + parseInt(Math.random() * 10000000, 10);

    // create the function			    	            		    			    			    
    var f = new Function('a', 'b', "var sorter = window." + fIdent + "; var iSortColumn = " + iColumnIndex + "; var bAsc = " + bAscending + "; var v1 = (typeof(a[iSortColumn]) == 'string') ? a[iSortColumn].toUpperCase() : a[iSortColumn]; var v2 = (typeof(b[iSortColumn]) == 'string') ? b[iSortColumn].toUpperCase() : b[iSortColumn]; if (!bAsc) { return v1 > v2 ? -1 : 1; } else { return v1 > v2 ? 1 : -1; }");		    	    
  	    
    // atttach it to the window
    window[fIdent] = f;
 
    // perform the sort
    this.Items.sort(f);
			    
    // de-reference the function 			    			    
    window[fIdent] = null;	
    f = null;
    
    // Reset any columns previously set as sorted
    var oCols = this.DataList.Columns.Items;    
    for (var c = 0; c < oCols.length; c++) oCols[c].Sorted = false;
    
    // set the sorted column as sorted
    oCols[iColumnIndex].Sorted = true;
    
    // refresh the datagrid
    this.DataList.Events.RaiseEvent("DATALIST_SORT", iColumnIndex);     // notify the sort is complete
    this.DataList.Events.RaiseEvent("DATALIST_CHANGE");                 // notify data has changed
}


function __DataRows_ConvertToObject(indexOrArrayRow)
{
    /// <summary>Converts a row from a datalist into an object</summary>
    ///	<param name="indexOrArrayRow"	type="Object">Index or Row to convert">
    /// <returns type="Object">Converted object</returns>
    
    var Row     = null;
    var Columns = this._pDataList.Columns.Items;
    
    if (typeof(indexOrArrayRow) == 'numeric')
    {
        // Index check
        if (this.Items.length <= indexOrArrayRow || indexOrArrayRow < 0)
        {
            throw new Error('index out of bounds');
        }
        
        Row = this.Items[indexOrArrayRow];
    }
    else if (typeof(indexOrArrayRow) == 'object' && indexOrArrayRow.constructor == Array)
    {
        Row = indexOrArrayRow;
        
        if (Row.length != Columns.length)
            throw new Error('Array row length does not match Column count for this DataList, the row likely does not belong to this DataList');
    }
    else
    {
        throw new Error('Invalid argument passed for indexOrArrayRow, argument type is not valid, expected integer or array');
    }
    
    var o = {};
    
    for (var c = 0; c < Columns.length; c++)
    {
        var col = Columns[c];
        var propName = '';
        
        if (col.Alias == undefined || typeof(col.Alias) != 'string' || col.Alias.length == 0)
        {
            if (col.Name == undefined || typeof(col.Name) != 'string' || col.Name.length == 0)
            {
                if (col.Display == undefined || typeof(col.Display) != 'string' || col.Display.length == 0)
                {
                    throw new Error('Column ' + c + ' has no Alias, Name or Display to set as the property');
                }
                
                propName = col.Display;   
            }
            else
            {
                propName = col.Name;
            }
        }
        else
        {
            propName = col.Alias;
        }
        
        // Replace non Alhapnumeric chars with underscores
        var validChars = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM';
        var validPropName = '';
        for (var i = 0; i < propName.length; i++)
        {
            validPropName += validChars.indexOf(propName.substr(i, 1)) == -1 ? '_' : propName.substr(i, 1);
        }
        
        o[validPropName] = Row[c];
    }
    
    return o;
}


function ROWFindArguments() 
{
    this.SearchFor = '';
    this.ColumnIndex = '*';     // Specify 1 or more columns
    this.StartIndex = 0;
    this.Count = 0;
    this.MaxReturn = 0;
    this.ReturnIndexsOnly = false;
    this.CaseSensitive = true;               
}

function ROWAdvFindArguments()
{
    this.StartIndex = 0;
    this.Count = 0;
    this.MaxReturn = 0;
    this.ReturnIndexsOnly = false;
    
    this.Restrictions = new Array(0);
        this.Restrictions.Add = ROWAdvFindRestriction_AddRestriction;
}

function ROWAdvFindRestriction_AddRestriction(TargetColumns, Operation, uValue1, uValue2)
{
    var r = null;
    
    switch (Operation.toUpperCase())
    {
        case "==":
        case "=":
        case "EQUALS": 
            r = new ROWAdvFindRestriction_Equals();
            r.Value = Value1;
            break;        
            
        case "!=":
        case "<>":
        case "NOTEQUALS":
            r = new ROWAdvFindRestriction_NotEquals();
            r.Value = Value1;
            break;   
    
        default:
            var e = new Error();
            e.message = "Unknown operator for advanced restrictions";
            e.description = e.message;
            throw e;
    }
    
    r.TargetColumns = TargetColumns;
    
    this.splice(this.length, 0, r);
    
    return r;
}

function ROWAdvFindRestriction()
{
    this.Operation      = null;
    this.TargetColumns  = null;
    this.Operation      = null;
    this.CaseSensitive  = true;    
}
function ROWAdvFindRestriction_Equals()
{
    this.Inherits(ROWAdvFindRestriction);
    
    this.Operation  = "=";    
    this.Value      = null;  
}
ROWAdvFindRestriction_Equals.prototype.Comparitor = ROWAdvFindRestriction_Equals_Comparitor;

function ROWAdvFindRestriction_Equals_Comparitor(aDataRow)
{
    var bString = typeof(this.Value) == 'string';
    var val = bString && this.CaseSensitive ? this.Value : this.Value.toUpperCase();
    
    for (var c = 0; c < this.__Columns.length; c++)
    {
        if (bString && !this.CaseSensitive)
        {
            if (aDataRow[this.__Columns[c]].toUpperCase() != val) return false;
        } else 
        {
            if (aDataRow[this.__Columns[c]] != val) return false;
        }
    }
    
    return true;
}

function ROWAdvFindRestriction_NotEquals()
{
    this.Inherits(ROWAdvFindRestriction);
    
    this.Operation  = "!=";    
    this.Value      = null;  
}
ROWAdvFindRestriction_NotEquals.prototype.Comparitor = ROWAdvFindRestriction_NotEquals_Comparitor;

function ROWAdvFindRestriction_NotEquals_Comparitor(aDataRow)
{
    var bString = typeof(this.Value) == 'string';
    var val = bString && this.CaseSensitive ? this.Value : this.Value.toUpperCase();
    
    for (var c = 0; c < this.__Columns.length; c++)
    {
        if (bString && !this.CaseSensitive)
        {
            if (aDataRow[this.__Columns[c]].toUpperCase() == val) return false;
        } else 
        {
            if (aDataRow[this.__Columns[c]] == val) return false;
        }
    }
    
    return true;
}


XMLLoadArguments.prototype.toString = function() { return 'XMLLoadArguments'; }
function XMLLoadArguments(uXML, iMax, iInsertAt, bReplace, aMapping) {
    /// <summary>Arguments object used for customising more complex transform behavior for the LoadXML</summary>
    /// <param name="uXML" type="Object">XML String, or DataSet Node</param>
    /// <param name="iMax" type="Number">Maximum number to parse, (default is all)</param>
    /// <param name="iInsertAt" type="Number">Index at which to begin inserting rows at</param>
    /// <param name="bReplace" type="Boolean" >Whether to Append or Replace from insertion point</param>
    /// <param name="aMapping" type="Array" elementType="XMLColumnMapping">Array of column mappings</param>
    /// <field name="FunctionArguments" type="Boolean">Leave as true, used as an identifer for the LoadXML function</field>
    /// <field name="XML" type="Object">XML String or DataSet XML node</field>
    /// <field name="MaxRecords" type="Number" ></field>
    /// <field name="InsertAt" type="Number" ></field>
    /// <field name="Replace" type="Boolean" >Whether to replace or append data from the insertion point</field>
    /// <field name="AllowAutoExpand" type="" ></field>
    /// <field name="AutoCreateColumns" type="" ></field>
    /// <field name="IgnoreMissingColumns" type="Boolean" >If set to true, errors will not be thrown when trying to load an incosistent dataset (ie; certain expected data source nodes are missing from some records)</field>
    /// <field name="Validate" type="Boolean" >Whether to validate data</field>
    /// <field name="UseMappings" type="Boolean" ></field>
    /// <field name="Mappings" type="Array" elementType="XMLColumnMapping">Array of column mappings</field>
 
    this.FunctionArguments = true;
	this.XML			= (uXML != undefined) ? uXML : null;							// XML Text or XML Root Node
	this.MaxRecords		= (iMax != undefined) ? imax : null;							// Maximum records to convert
	this.InsertAt		= (iInsertAt != undefined) ? iInsertAt : null;					// Insertion point into the datalist
	this.Replace		= (bReplace != undefined) ? bReplace : false;					// Whether to Append or Replace from insertion point
	this.AllowAutoExpand        = true;														// Allows dynamic expansion of column data on individual rows
	this.AutoCreateColumns	    = false;    
	this.IgnoreMissingColumns   = false;                                                // If set to true, errors will not be thrown when trying to load an incosistent dataset (ie; certain expected data source nodes are missing from some records)
	this.Validate       = null;                                                         // Whether to validate or not.  Null = Use the datalists current settings
	this.UseMappings	= true;													// If this option AND dynamic expansion is enabled, then any expanded columns will also have matching columns created
	this.Mappings		= (aMapping != undefined) ? aMapping : new Array(0);			// Column Mappings
		this.Mappings.Add = __XMLLoadArguments_Mapping_Add;
}
function __XMLLoadArguments_Mapping_Add(Source, Destination, Transformer) {    
	this.splice(this.length, 0, new XMLColumnMapping(Source, Destination, Transformer));
}

function XMLColumnMapping(Source, Destination, Transformer) {
    /// <summary>Column mapping object</summary>
    /// <param name="Source" type="String">Source node name or index</param>
    /// <param name="Source" type="String">Destination node name or index</param>
    /// <param name="Transformer" type="Function">If custom parsing is to be done, specify the function to perform the transform</param>
    
    
	this.Source = Source;												// Source column (xml childnode) index 
	this.Destination = Destination;										// destination column index	
	this.Transformer = null;
	if (Transformer != undefined) {
		if (typeof(Transformer) != "function") {
			var e = new Error();
			e.description = "Constructor Error::[XMLColumnMapping](Source, Destination, Transformer)\nInvalid function pointer passed as [Transformer] argument.";
			throw e;
		}
		
		this.Transformer = Transformer;	// custom parser (function pointer) to a function that will process the node.
	}
}

Function.prototype.GetFunctionName = __Function_GetFunctionName;
function __Function_GetFunctionName()
{
	var sFunctionCode = this.toString();
	sFunctionCode = sFunctionCode.substr(sFunctionCode.indexOf(" "), sFunctionCode.indexOf("(") - sFunctionCode.indexOf(" "));
    return sFunctionCode;
}


function IsRGB(uRGB) {
    ///<summary>Validates whether a string is a proper formed RGB or not.  This function is prototyped to the string object
    ///		as String.IsRGB()
    ///</summary>
    ///	<param name="uRGB"	type="String">Value to test</param>
	/// <returns type="Boolean" />

	if (uRGB == undefined) uRGB = this.toString();
	
	if (typeof(uRGB) != 'string') return false;
	
	if (uRGB.match('#[\\dABCDEFabcdef]{6}') == null) {
		return false;
	} else {
		return true;
	}
}
String.prototype.IsRGB = IsRGB;
String.prototype.IsRgb = IsRGB;


String.prototype.ReplaceAnyChar = function(replaceableChars, replaceValue, replaceCharsNotInList) {
    var tmp = new Array(this.length);
    replaceCharsNotInList = replaceCharsNotInList === true;

    for (var i = 0; i < this.length; i++) {
        var curChar = this.charAt(i);
        var charInList = replaceableChars.indexOf(curChar) != -1;

        if (charInList) {
            if (replaceCharsNotInList)
                tmp[i] = curChar;
            else
                tmp[i] = replaceValue;
        }
        else {
            if (replaceCharsNotInList)
                tmp[i] = replaceValue;
            else
                tmp[i] = curChar;
        }
    }

    return (tmp.length == 0 ? "" : tmp.join(""));
}

function IsBool(value) {
    /// <returns type='Boolean'>Returns true if the value can be equated to a boolean (true/false)</returns>
    
    var val = value.toString();
    return val.IsBool();
}

String.prototype.IsBool = __String_IsBool;

function __String_IsBool() {
    /// <returns type='Boolean'>Returns true if the value can be equated to a boolean (true/false)</returns>
    var val = this.toString();
    return (val.toUpperCase() == 'TRUE' || val.toUpperCase() == 'FALSE');
}

String.prototype.PadLeft = __String_PadLeft;
function __String_PadLeft(iMinimumStringLength)
{
    var iCurLength = this.length;
    if (iCurLength >= iMinimumStringLength) return this;
    
    var iRequiredPadding = iMinimumStringLength - iCurLength;
    var sOrigString = this.toString();
    
    for (var i = 0; i < iRequiredPadding; i++)
        sOrigString = " " + sOrigString;
        
    return sOrigString;
}

String.prototype.PadRight = __String_PadRight;
function __String_PadRight(iMinimumStringLength)
{
    var iCurLength = this.length;
    if (iCurLength >= iMinimumStringLength) return this;
    
    var iRequiredPadding = iMinimumStringLength - iCurLength;
    var sOrigString = this.toString();
    
    for (var i = 0; i < iRequiredPadding; i++)
        sOrigString += " ";
        
    return sOrigString;
}

String.prototype.ToDate = __String_ToDate;
function __String_ToDate() {
    var s = this.toString();
    var dt = new Date();
  
    if (dt.ParseDate(s) == false) {
        var e = new Error();
        e.message = 'String "' + s + '" is not a valid parseable date';
        e.description = e.message;
        throw e;
    }
    
    return dt;
}


String.prototype.IsEmailAddress = __String_IsEmailAddress;
function __String_IsEmailAddress() {
    var sValidationRegEx = "\\w+([-+.']\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*";
    var objRegEx = new RegExp(sValidationRegEx); 
    
    return this.match(objRegEx) != null;
}

String.prototype.toNormalCase = __String_ToNormalCase;
String.prototype.ToNormalCase = __String_ToNormalCase;
function __String_ToNormalCase() {
    var s = this;
    if (s.length == 0) return '';
    
    var a = s.split(" ");
    
    for (var i = 0; i < a.length; i++) {
        a[i] = a[i].substr(0,1).toUpperCase() + a[i].substr(1, a[i].length-1).toLowerCase();
    }
    
    return a.join(" ");
}

function __Object_IsNumeric() {
    var s = this.toString();
	return !(s == '' || s == undefined || s == null || isNaN(s));
}
Object.prototype.IsNumeric = __Object_IsNumeric;


function IsValidGUID(uGuid) {
	///<summary>
	///	<param name=""	type=""	required=""		description="">
	///
	/// 	<returns type=""  description="">
	///
	///	<description>
	///		Validates whether a string is a proper formed GUID or not.  This function is prototyped to the string object
	///		as String.IsGUID()
	///	</description>
	///</summary>

	if (uGuid == undefined) uGuid = this.toString();

	if (typeof(uGuid) != 'string') return false;
	
	if (uGuid.match('{[\\dABCDEFabcdef]{8}-[\\dABCDEFabcdef]{4}-[\\dABCDEFabcdef]{4}-[\\dABCDEFabcdef]{4}-[\\dABCDEFabcdef]{12}}') == null) {
		return false;
	} else {
		return true;
	}
}
String.prototype.IsGUID = IsValidGUID;
String.prototype.IsGuid = IsValidGUID;


function SelectNewID() { 
	return RandomGUID(); 
}

function ISNAN(Value) {
	return (Value == null || Value == undefined || Trim(Value) == "" || isNaN(Value));
}


function RandomGUID() {
	var sOutID = "";

	for (var i = 0; i < 32; i++) {
		switch (i) {
			case 8:
			case 12:
			case 16:
			case 20:
				sOutID += "-"
				break;
		}


		var rNum = parseInt(Math.random() * 15, 10);
		var newrNum = 0;

		switch (rNum) {
			case 10:
				newrNum = "A";
				break;
			case 11:
				newrNum = "B";
				break;
			case 12:
				newrNum = "C";
				break;
			case 13:
				newrNum = "D";
				break;
			case 14:
				newrNum = "E";
				break;
			case 15:
				newrNum = "F";
				break;

			default:
				newrNum = rNum.toString();
				break
		}

		sOutID += newrNum;
	}

	return "{" + sOutID + "}";
}			

// This one sux, dont use.
String.prototype.toBool = toBool;
function toBool(val) {
	switch (typeof(val))
	{
		case "string":
			if (val.IsNumeric()) { return (val.ToInt() != 0) }
			if (val.toUpperCase() == "TRUE") { return true; } else { return false }			
			
		case "number":
			if (val != 0) { return true } else { return false }
		default:
			//alert("Unsupport variable type relayed to toBool([" + typeof(val) + "])");
			return false;
	}
}


String.prototype.ToBool = __String_ToBool;
function __String_ToBool() {
	var val = this.toString();
	
	if (!ISNAN(val)) {
	    val = parseInt(val, 10) != 0 ? "TRUE": "FALSE";	    
	}
	
	switch (val.toUpperCase())
	{
		case "FALSE":
		case "0":
		    return false;
		
		case "TRUE":		
			return true; 
			   	
		default:
			return false;
	}
}	


Object.prototype.ToCurrency = __Object_ToCurrency;
function __Object_ToCurrency(NumDigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegatives, GroupDigits) {
	var val = this.toString();
	
	if (NumDigitsAfterDecimal == null || NumDigitsAfterDecimal == undefined) NumDigitsAfterDecimal = 2;
	if (IncludeLeadingDigit == null || IncludeLeadingDigit == undefined) IncludeLeadingDigit = true;
	if (UseParensForNegatives == null || UseParensForNegatives == undefined) UseParensForNegatives = false;
	if (GroupDigits == null || GroupDigits == undefined) GroupDigits = true;
	
	var Currency = "$";
	if (ApplicationObjects != undefined && ApplicationObjects != null)
	{
	    if (ApplicationObjects.User.Currency != undefined)
	    {
	        Currency = ApplicationObjects.User.Currency;
	    }
	}
	
	return vbToCurrencyWrapper(val, NumDigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegatives, GroupDigits);
    
}



String.prototype.Reverse = Reverse;
function Reverse(s) {
	if (s == undefined) s = this;
	if (s == undefined) return "";
	
	if (!s.length) return "";
	var o = "";
	
	for (var i = s.length - 1; i > -1; i--)
	{
		o = o + s.substr(i,1); 
	}

	return o;
}


String.prototype.GetFileExtension = GetFileExtension;
function GetFileExtension(f) {
	if (f.indexOf(".") == -1) return "";
	
	var fr = Reverse(f);
	var i = fr.indexOf(".");
	
	return f.substr((f.length) - i);
}


function FormatCurrency(sNumber, DigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegativeNumbers, GroupDigits) {
	if (DigitsAfterDecimal == undefined)			DigitsAfterDecimal = 2;
	if (IncludeLeadingDigit == undefined)			IncludeLeadingDigit = false;
	if (UseParensForNegativeNumbers == undefined)	UseParensForNegativeNumbers = false;
	if (GroupDigits == undefined)					GroupDigits = false;
	
	if (ISNAN(sNumber)) return "NaN";
	
	return vbFormatCurrencyWrapper(sNumber, DigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegativeNumbers, GroupDigits);
}
String.prototype.FormatCurrency = __String_FormatCurrency;
function __String_FormatCurrency (DigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegativeNumbers, GroupDigits) 
{ 
    var i = this.toString();
    return FormatCurrency(i,  DigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegativeNumbers, GroupDigits); 
} 

Number.prototype.FormatCurrency = __Number_FormatCurrency;
function __Number_FormatCurrency(DigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegativeNumbers, GroupDigits) 
{ 
    var i = parseFloat(this);
    return FormatCurrency(i,  DigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegativeNumbers, GroupDigits); 
}

function FormatNumber(sNumber, DigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegativeNumbers, GroupDigits) {
    if (DigitsAfterDecimal == undefined)			DigitsAfterDecimal = 2;
    if (IncludeLeadingDigit == undefined)			IncludeLeadingDigit = false;
    if (UseParensForNegativeNumbers == undefined)	UseParensForNegativeNumbers = false;
    if (GroupDigits == undefined)					GroupDigits = false;

    if (!sNumber.IsNumeric()) return "NaN";

    return vbFormatNumberWrapper(sNumber, DigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegativeNumbers, GroupDigits);
}

String.prototype.FormatNumber = __String_FormatNumber;
function __String_FormatNumber (DigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegativeNumbers, GroupDigits) 
{ 
    var i = this.toString();
    return FormatNumber(i,  DigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegativeNumbers, GroupDigits); 
} 

Number.prototype.FormatNumber = __Number_FormatNumber;
function __Number_FormatNumber(DigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegativeNumbers, GroupDigits) 
{ 
    var i = parseFloat(this);
    return FormatNumber(i,  DigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegativeNumbers, GroupDigits); 
}


Date.prototype.ToUniversalDate = ToUniversalDate;
function ToUniversalDate(sDate, sFormat, MonthOffSet) {
	if (sDate == undefined) sDate = this;
	if (sFormat == undefined) sFormat = "dd/mm/yyyy";
	if (MonthOffSet == undefined) MonthOffSet = 0;

	try {
		// try treat as date object
		var sOut = "" + sDate.getFullYear() + "-" + sDate.getMonth() + "-" + sDate.getDate();
		return sOut;
		
	} catch (err) {
		
		try {
			var arrDate = sDate.split("/");
		
			if (sFormat == "dd/mm/yyyy") {
				return arrDate[2] + "-" + (parseInt(arrDate[1], 10) + MonthOffSet) + "-" + arrDate[0];
			} else {
				//american format coming in
				return arrDate[2] + "-" + (parseInt(arrDate[0], 10) + MonthOffSet) + "-" + arrDate[1];
			}
		} catch (err) {
			return sDate;
		}
	}
	
}


Date.prototype.FromUniversalDate = FromUniversalDate;
function FromUniversalDate(dtDate) {
	if (dtDate == undefined) dtDate = this;
	
	try
	{
		var arrDate = dtDate.split("-");
		if (arrDate.length == 3) return arrDate[2] + "/" + arrDate[1] + "/" + arrDate[0];
		else return dtDate
	}
	catch (err)
	{
		return dtDate;
	}
}


function ParseUniversalDate(sDate) {
	var retDate = new Date();
	retDate.setFullYear(sDate.substr(0, 4), parseInt(sDate.substr(5,2), 10)-1, sDate.substr(8,2) );
	retDate.setHours( (parseInt(sDate.substr(11,2), 10) + ((sDate.substr(20, 2) == "AM") ? 0 : 12))  , sDate.substr(14, 2), sDate.substr(17, 2) );
	
	return retDate;
}



Date.prototype.ToShortDate = ToShortDate;
function ToShortDate(dtDate) {
	if (dtDate == undefined) dtDate = this;

	return this.ShortDate();
}


Date.prototype.ToMonthString = ToMonthString;
function ToMonthString(iMonth) {
	if (iMonth == undefined) iMonth = this;
	if (isNaN(iMonth)) return null;

	switch(iMonth)
	{
		case 0:
			return "January";
		case 1:
			return "February";
		case 2:
			return "March";				
		case 3:
			return "April";				
		case 4:
			return "May";	
		case 5:
			return "June";	
		case 6:
			return "July";	
		case 7:
			return "August";	
		case 8:
			return "September";	
		case 9:
			return "October";	
		case 10:
			return "November";	
		case 11:
			return "December";
	
		default:
			return null;
	}
}


Date.prototype.ToDayString = ToDayString;
function ToDayString(iDay) {
	if (iDay == undefined) iDay = this;
	if (isNaN(iDay)) return null; 
	
	switch(iDay)
	{
		case 0:
			return "Sunday";
		case 1:
			return "Monday";
		case 2:
			return "Tuesday";				
		case 3:
			return "Wednesday";				
		case 4:
			return "Thursday";	
		case 5:
			return "Friday";	
		case 6:
			return "Saturday";
			
		default:
			return null;
	}
}


String.prototype.ToFloat = __String_ToFloat;
function __String_ToFloat() {
	if (this == "") return 0;
	
	if (isNaN(this)) {
		var s = this.toString().toUpperCase();
		if (s == "TRUE") {
			return 1;
		} else {
			return 0;
		}		
	} else {
		return parseFloat(this.replace(",", ""));
	}
}

Object.prototype.ToInt = __Object_ToInt;
function __Object_ToInt() {
    var s = this.toString();
	if (s == "") return 0;
	
	if (isNaN(s)) {
		var s = s.toUpperCase();
		if (s == "TRUE") {
			return 1;
		} else {
			return 0;
		}		
	} else {
		return parseInt(s.replace(",", ""), 10);
	}
}



function IsDate(sDate) {
	return vbIsDateWrapper(sDate);
}

function CDate(sDate) {
	return vbCDateWrapper(sDate);
}




function GetDatePortion(sDate, iPortion, sDelimiter) {
	var a = sDate.split(sDelimiter);
	
	if (iPortion <= a.length) return a[iPortion];
}


function FormatDate(dtDate, sFormat, MonthModifier) {
	if (MonthModifier == undefined) MonthModifier = 1;
	
	//replace month
	var sOut = sFormat.replace("mmmm", ToMonthString(dtDate.getMonth()));
		sOut = sOut.replace("mm", dtDate.getMonth()+MonthModifier);
		
	//replace year
		sOut = sOut.replace("yyyy", dtDate.getFullYear());
		sOut = sOut.replace("yy", dtDate.getFullYear());
		
	//replace day
		sOut = sOut.replace("dddd", ToDayString(dtDate.getDay()));
		sOut = sOut.replace("dd", dtDate.getDate());
		
		return sOut;
}




	function EncodeHTML(s) {
		if (s == undefined || typeof(s) != 'string') {
			return "";
		}
		
		var sOut="";
		var aOut = new Array();

		try
		{
			
			if (typeof(s) != "string") return "";
					
			for (var i=0; i < s.toString().length; i++)
			{
			    aOut[i] = IntToHex(s.charCodeAt(i));
				//sOut = sOut + IntToHex(s.charCodeAt(i));
			}
			sOut = aOut.join("");
			return sOut;
		}
		catch(e)
		{
			return "";
		}
	}
	
	
	
	var hD="0123456789ABCDEF";
	
	function IntToHex(d) {
		var h = hD.substr(d&15,1);
		
		while(d>15) 
		{
			d >>= 4;
			h=hD.substr(d&15,1)+h 
		}
	
		//make sure it returns a double digit hex number
		if (h.toString().length == 1) h = "0" + h;
		
		return h;
	}
	function HexToInt(h) { return parseInt(h,16) } 
	

	function UnencodeHTML(s) {
		var sOut="";
		var aOut = new Array(0);
		try
		{
			if (typeof(s) != "string") return "";
			
			for (var i=0; i < s.toString().length; i+=2)
			{
			    aOut[i] = String.fromCharCode(HexToInt(s.substr(i,2)));
				//sOut = sOut + String.fromCharCode(HexToInt(s.substr(i,2)));
			}
			
			sOut = aOut.join("");
			return sOut;
		}
		catch(e)
		{
			return "";
		}
	}
	
	String.prototype.ToSafeString = function() { return SafeDisplay(this) }
	function SafeDisplay(s) {
		s.replace("<", "&lt;");
		s.replace(">", "&gt;");
	}
	
	String.prototype.JSSafeQuotes = __String_JSSafeQuotes;
	function __String_JSSafeQuotes(sQuote) {
		// No specific quote Specified, try work out which one to use...
		
		var sOut = "gdgfd";//this; // this.replace("\\", "\\\\");
		//sOut =  sOut.replace(sQuote, "\\" + sQuote);
		
		alert(sOut);
		
		return sOut;
	}
	
	
	
	function IsAlphaChar(c)					// checks to see if a char is alpha numeric 
	{
		var val = parseInt(c,10);
		if ((val >= 64) && (val <= 90) ||
			(val >= 97) && (val <= 122))
			return true;
		else
			return false;
	}
	
	function IsDisplay(str)					// Test for displayable char 
	{
		var i;
		for (i = 0; i < str.length; i++)
		{
			if (!IsDisplayChar(str[i]))
			{
				return false;
			}
		}
		return true;
	}
	
	function ToDisplay(str)					// Removes non-displayable chars 
	{
		var newstr,i;
		newstr = "";
		for (i = 0; i < str.length; i++)
		{
			if (IsDisplayChar(str.substr(i,1))) newstr += str.substr(i,1);
		}
		return newstr;
	}
	
	function IsDisplayChar(c)				// Tests for a non-displayable char 
	{
		var val = ("" + c).charCodeAt(0);
		if ((val >= 32) && (val <= 126) ||
			(val == 9) || (val == 13))
			return true;
		else
			return false;
	}

var Authent_Key				= null;
var Authent_UserID			= null;
var Authent_OrganisationID	= null;
var Authent_LoggedIn		= false;
var Authent_Username		= null;
var Authent_DatabaseID = null;
var Authent_CurrentPage = null;
var Authent_CorrelationId = null;
function DecodeAuthenticationKey()
{
    Authent_CorrelationId = RandomGUID();
    Authent_CurrentPage = window.location.href;
     
	Authent_Key = getCookie("AuthenticationKey");
	Authent_Username = getCookie("CurrentUsername");
	Authent_DatabaseID = getCookie("CurrentDatabaseID");
	
	if (Authent_Key == null)
	{
		Authent_LoggedIn = false;
		return;
	}
	Authent_LoggedIn = true;
	
	var aParts = Authent_Key.split("#");

	Authent_UserID = UnencodeHTML(aParts[0]);
	Authent_OrganisationID = UnencodeHTML(aParts[1]);
}	

DecodeAuthenticationKey();	
	






function newDialog(path, pWidth, pHeight) {

	if ((viewCookie("CurrentUserID") == '' || viewCookie("CurrentUserID") == null || viewCookie("AuthenticationKey") == null) && path.indexOf("login.asp") == -1)
	{
		alert("Your session has expired. Please login again to continue.");
		  
		newDialog('/login.asp?forwardPage=' + path + '&forwardW=' + pWidth + '&forwardH=' + pHeight, '500', '300');
		return; 
	}
  
	// Get screen width / height
	scrW = screen.availwidth; 
	scrH = screen.availheight;

	// Find the difference between the screen width/height and the
	// page that we're opening
	wDif = scrW - pWidth;
	hDif = scrH - pHeight;

	//  Determine the center position for the window
	xPos = wDif / 2;
	yPos = hDif / 2;

	// Opens a new window in the center of the screen with all various tool bars hidden.
	attribs = 'width=' + pWidth + ', height=' + pHeight + ', top=' + yPos + ', screenY=' + yPos + ', left=' + xPos + ', screenX=' + xPos + ', resizable=yes, scrollbars=yes, directories=no, status=yes, menubar=no, copyhistory=no'

	var retVal;

	if ((newDialog.arguments.length > 3) && (newDialog.arguments[2] == "LoginPage")) {
		// If logging into the web interface
	}
  
	retVal = window.open(path,'',attribs);

	if (retVal == null) {
		return false;
	} else {
		return true;
	}
}



function IsString(u) {
	return (typeof(u) == 'object' && u.constructor == String); 
}



// Gets a cookie value when passed its [name]
// Used specifically by newDialog() function
function viewCookie(name) { 
	var re = new RegExp(name + "=([^;]+)");
	var value = re.exec(document.cookie);
	return (value != null) ? unescape(value[1]) : null;
}


// Gets a cookie value when passed its [name]
function getCookie(name) { 
	var re = new RegExp(name + "=([^;]+)");
	var value = re.exec(document.cookie);
	return (value != null) ? unescape(value[1]) : null;
}


// Sets or Creates a cookie value when passed a [name] AND [value]
function setCookie(name, value) { // use: setCookie("name", value);
	var today = new Date();
	var sCookie;
	
	sCookie = name + "=" + escape(value);
	
	if (setCookie.arguments.length == 3 && setCookie.arguments[2] == true) {
		var expiry = new Date(today.getTime() + 28 * 24 * 60 * 60 * 1000);	// plus 28 days (bigger expiry)
	} else {
		//var expiry = new Date(today.getTime() + 2 * 60 * 60 * 1000);		// plus 2 hours
		var expiry = today;
	}
		
	if (expiry != today) sCookie = sCookie + "; expires=" + expiry.toGMTString();	
	
	document.cookie = sCookie;
}


// Resets all the selection cookies to empty
function resetCookies() {
	setCookie("selectedCompany","");
	setCookie("selectedContact","");
}


function ButtonPressed(i) {
	if (i == undefined && window.event) {
	    i = window.event.button;		// If no value is passed, and an event triggered this code, use the event default.
    }
    
	switch (i)
	{
		case 0:		return "NONE";
		case 1:		return "LEFT";
		case 2:		return "RIGHT";
		case 3:		return "LEFTRIGHT";
		case 4:		return "MIDDLE";
		case 5:		return "LEFTMIDDLE";
		case 6:		return "RIGHTMIDDLE";
		case 7:		return "LEFTRIGHTMIDDLE";
		default:
			return "UNKNOWN";
	}
}


function Msgbox(sPrompt, iButtons, sTitle) {
    /// <returns type="Number">Message box result</returns>
	if (sPrompt == undefined) sPrompt = "";
	if (iButtons == undefined) iButtons = 0;
	if (sTitle == undefined) sTitle = "";

	var returnVal = null;
	
	eval('returnVal = vbMsgboxWrapper(sPrompt, iButtons, sTitle);');
	return returnVal;
}


function CreateXMLNode(sName, sValue, xmlDoc, uParent) {
	if (xmlDoc == null || xmlDoc == undefined) {
		xmlDoc = new ActiveXObject("MSXML2.DOMDocument.4.0");
		document.write("crashed");
	}
	
	var oXMLNode = xmlDoc.createElement(sName);
	if (sValue != null) oXMLNode.nodeTypedValue = sValue;
	
	if (uParent != null && uParent != undefined) {
		try {
			uParent.appendChild(oXMLNode);
		} catch(err)
		{
			// Failed to attach.
			throw new Error("CreateXMLNode() Failed to attach to [uParent].\n\nError:" + err.description);
		}
	}
	
	xmlDoc = null;
	return oXMLNode;
}


function __Object_CopyFrom(oTarget, MaxRecursion, DoNotCopyReferences)
{
    if (typeof(MaxRecursion) != 'numeric') MaxRecursion = 3;
    
    for (var n in oTarget)
    {         
        if (this.constructor.prototype[n] != undefined) continue;  // Do not copy over prototyped functions
        
        if (DoNotCopyReferences)
        {
            if (typeof(oTarget[n]) == 'function') continue;
        }
        
    
        if (MaxRecursion > 0 && typeof(oTarget[n]) == 'object')
        {
            if (typeof(oTarget[n].CopyFrom) == 'function')
            {
                // Deep Clone if possible
                this[n] = {};
                this[n].CopyFrom(oTarget[n], MaxRecursion--, DoNotCopyReferences);
            }
            else
            {
                this[n] = oTarget[n];
            }
        }
        else
        {
            this[n] = oTarget[n];
        }
        
    }
}
Object.prototype.CopyFrom = __Object_CopyFrom;

function __Object_ConstructorName() {
    var iStart = null;
    var iEnd   = null;
    var finished = false;
           
    var constValue = this.constructor.valueOf().toString();
    iStart = constValue.indexOf("function") + 8;

    while (!finished) {
        if (constValue.charAt(iStart) == ' ') {
            iStart++;
        } else {
            finished = true;
        }
    }

    finished = false;
    iEnd = iStart+1;
    
    while (!finished)
    {
        if (constValue.charAt(iEnd) != ' ' && constValue.charAt(iEnd) != '(') {
            iEnd++;
        } else {
            finished = true;
        }
    }
    
    var strConstructorName = constValue.substr(iStart, iEnd - iStart);

    return strConstructorName;

}
Object.prototype.ConstructorName = __Object_ConstructorName;


function __Object_EnumerateProperties(uTarget) {
	if (uTarget == undefined) uTarget = this;
	
	var s = '';

	for (var n in uTarget)
	{
		switch (typeof(uTarget[n]))
		{
			case 'string':
			case 'number':
			case 'boolean':
				s += n + ' :' + uTarget[n];
				break;
				
			case 'object':
				s += n + ' [object]';
				break;
				
			case 'function':
				s += n + ' (function)';
				break;
				
			default:
				s += n;
		}
		
		s+= '\n';
	}
	
	return s;
}
Object.prototype.EnumerateProperties = __Object_EnumerateProperties;
Object.prototype.Props      = __Object_EnumerateProperties;
Object.prototype.ShowProps  = __Object_EnumerateProperties;
Object.EnumerateProperties  = __Object_EnumerateProperties;
Object.Props                = __Object_EnumerateProperties;

FunctionArguments.prototype.Props = __FunctionArguments_EnumerateProperties;
FunctionArguments.prototype.EnumerateProperties = __FunctionArguments_EnumerateProperties;
function FunctionArguments(oArguments)
{
    for (var n in oArguments)
    {
        if (oArguments.hasOwnProperty(n))
            this[n] = oArguments[n];
    }
}

function __FunctionArguments_EnumerateProperties()
{
    var Props = new Array(0);
    for (var n in this)
    {
        if (this.hasOwnProperty(n))
        {
            Props[Props.length] = n + (this[n] != null ? ' : ' + this[n]: '');
        }
    }
    
    var s = Props.join("\n");
    return s;
}   


function __Array_PushUp(iRowIndex) {
	// pushes a row up
	if (isNaN(iRowIndex) || iRowIndex == 0) return false;
	
	this.Switch(iRowIndex, iRowIndex-1);
	
	return true;
}
Array.prototype.PushUp = __Array_PushUp;


function __Array_PushDown(iRowIndex) {
	// Pushes a row down
	if (isNaN(iRowIndex) || iRowIndex == (this.length-1)) return false;

	this.Switch(iRowIndex, iRowIndex+1);
	
	return true;
}
Array.prototype.PushDown = __Array_PushDown;



function __Array_Switch(iFirstRowIndex, iSecondRowIndex) {
	// Switches 2 row position values around
	var tmp = this[iFirstRowIndex];
	this[iFirstRowIndex] = this[iSecondRowIndex];
	this[iSecondRowIndex] = tmp;
	
	return true;
}
Array.prototype.Switch = __Array_Switch;

function __Array_FillOptionList(oOptionList, clearExisting) {
    /// <summary>Fills a HTML select option list with the values in the array.  You can use either an array of strings, or an array of objects with a 'Display' and 'Value' attribute</summary>
    /// <param name="oOptionList" type="Array">Either an array of strings, or objects with 'Display' and 'Value' attributes'</param>
    /// <param name="clearExisting" type="Boolean">Whether to clear existing items in the option list, default is true</param>

    clearExisting = !(clearExisting === false);

    if (clearExisting) {
        while (oOptionList.options.length != 0) {
            oOptionList.options[0] = null;
        }
    }

	for (var i = 0; i < this.length; i++)
	{
		if (typeof(this[i]) == "object") {
			oOptionList.options[oOptionList.options.length] = new Option(this[i].Display, this[i].Value);
		} else {
			oOptionList.options[oOptionList.options.length] = new Option(this[i], this[i]);
		}
	}	
}		
Array.prototype.FillOptionList = __Array_FillOptionList;

function __String_Replace(Find, ReplaceWith) {
    var s = this;
    
    while (s.indexOf(Find) != -1) s = s.replace(Find, ReplaceWith);
    
    return s;
}
String.prototype.Replace = __String_Replace;

function __Array_ForEach(EvalCode, iStartIndex, iFinishIndex, iStep) {
    if (EvalCode == null || EvalCode == undefined) {
        var e = new Error();
        e.description = 'No code to run!'
        e.message = e.description;
        throw e;
    }
    
    if (iStartIndex == null || iStartIndex == undefined || isNaN(iStartIndex)) iStartIndex = 0;
    if (iFinishIndex == null || iFinishIndex == undefined || isNaN(iFinishIndex)) iFinishIndex = this.length;
    if (iStep == null || iStep == undefined || isNaN(iStep)) iStep  = 1;
    
    for (var i = iStartIndex; i < iFinishIndex; i+= iStep) eval(EvalCode.Replace("$$Index", i));
}
Array.prototype.ForEach  = __Array_ForEach;

function $iswitch(value) {
	var ArgLength = $iswitch.arguments.length;
    var dftValue = null;
    
	if (ArgLength % 2 != 0) {
	    
		//var err = new Error();
		//err.description	= "Uneven argument length";
		//err.name	= "Uneven argument length";
		//throw err;
		
		dftValue = $iswitch.arguments[ArgLength-1];
		ArgLength-=1;
	}		
	
	var aEval = new Array(ArgLength/2);
	var aVal  = new Array(ArgLength/2);				
	var uTest = $iswitch.arguments[0];
	
	for (var i = 0; i < $iswitch.arguments.length / 2; i++)
	{
		aEval[i] = $iswitch.arguments[i*2+1];
		aVal[i] = $iswitch.arguments[i*2 + 2];
	}
	
	for (var i = 0; i < aEval.length; i++)
		if (uTest == aEval[i]) return aVal[i];
	
	return dftValue;
}

function a$(message) {
	if (message == undefined) message ='';
	alert(message);
}


function f$(sItemName) {
	try {
		return document.all[sItemName];
	} catch (err) {
		return null;
	}
}


String.prototype.GetProperty = __String_GetProperty;
function __String_GetProperty(strName) {
    var strProps = this;
    strName = strName.toUpperCase();
    
    var aProps = strProps.split(";");
    
    for (var i = 0; i < aProps.length; i++)
    {
        var strCur = aProps[i];
        var iColonPos = strCur.indexOf(":");
        if (iColonPos == -1) continue;
     
        var curPropName = strCur.substr(0, iColonPos).Trim();
        if (curPropName.toUpperCase() != strName) continue;
     
        return strCur.substr(iColonPos+1).Trim();
    }
    
    return null;
}

String.prototype.Trim = __String_Trim;
function __String_Trim() {
    var iLeft = 0;
    var iRight = 0;
    
    // Get Left clipping
    for (var i = 0; i < this.length && this.charAt(i) == " "; i++) iLeft++;
    for (var i = this.length -1; i >= 0 && this.charAt(i) == " "; i--) iRight++;
    
    if (this.length == iLeft) return "";
    var s = this.substr(iLeft);
    
    s = s.substr(0, s.length - iRight);
    return s;
}


// ** Builds an AJAX restriction node based on the supplied parameters
function CreateAJAXRequestRestrictionNode(paramRestrictions, strObject, strField, strOperator, strValue1, strValue2) {

    try
    {
        if (paramRestrictions == null || paramRestrictions == undefined)
            return;

        if (strObject == "" || strField == "" || strOperator == "")
        {
            alert("Supplied parameters are invalid.");
            return;
        }
        
        var paramRestriction = paramRestrictions.ExtendedParameters.Add("Restriction");
        
        var paramObject      = paramRestriction.ExtendedParameters.Add("Object",    strObject);
        var paramField       = paramRestriction.ExtendedParameters.Add("Field",     strField);
        var paramOperator    = paramRestriction.ExtendedParameters.Add("Operator",  strOperator);
        var paramValue1      = paramRestriction.ExtendedParameters.Add("Value1",    strValue1);
        var paramValue2      = paramRestriction.ExtendedParameters.Add("Value2",    strValue2);
        
        return true;
    }
    catch (err)
    { 
        alert(err.message);
    }
    
    return false;    
}

function BlankCoalesce()
{
    for (var i = 0; i < BlankCoalesce.arguments.length; i++) 
        if (BlankCoalesce.arguments[i] != null && BlankCoalesce.arguments[i].toString().length != 0) return BlankCoalesce.arguments[i].toString();

    return "";
}
function Coalesce() 
{
    for (var i = 0; i < Coalesce.arguments.length; i++) 
        if (Coalesce.arguments[i] != null) return Coalesce.arguments[i];
        
    return null;
}


function IXMLDOMDocument() {
    /// <field name="xml" type="String">return the XML source for the node and each of its descendants</field>   
}
IXMLDOMDocument.prototype =
{
    abort: function() { },
    appendChild: function(newChild) {
    /// <param name="newChild" type"IXMLDOMNode" />
     /// <returns type="IXMLDOMNode" />
     },
     cloneNode: function(deep) {
     /// <param name="deep" type"Boolean" />
     /// <returns type="IXMLDOMNode" /> 
     },
    createAttribute: function(name) { },
    createCDATASection: function(data) { },
    createComment: function(data) { },
    createDocumentFragment: function() { },
    createElement: function(tagName) {
    /// <param name="tagName" type="String" />
    /// <returns type="IXMLDOMNode" />
      },
    createEntityReference: function(name) { },
    createNode: function(type, name, namespaceURI) { },
    createProcessingInstruction: function(target, data) { },
    createTextNode: function(data) { },
    getElementsByTagName: function(tagName) { },
    getProperty: function(name) { },
    hasChildNodes: function() { },
    insertBefore: function(newChild, refChild) { },
    load: function(xmlSource) { },
    loadXML: function(bstrXML) { },
    nodeFromID: function(idString) { },
    removeChild: function(childNode) { },
    replaceChild: function(newChild, oldChild) { },
    save: function(destination) { },
    selectNodes: function(queryString) 
    {
        /// <param name="queryString" type="String" />
        /// <returns type="Array" elementType="IXMLDOMNode" />
    },
    selectSingleNode: function(queryString) 
    { 
    /// <param name="queryString" type="String" />
        /// <returns type="IXMLDOMNode" />
    }
    ,
    setProperty: function(name, value) { },
    transformNode: function(stylesheet) { },
    transformNodeToObject: function(stylesheet, outputObject) { },
    validate: function() { }    
    
}
Cast.As.IXMLDOMDocument = function(val) {
    /// <summary>Returns a casted IXMLDOMDocument</summary>
    /// <param name="val" type="IXMLDOMDocument">Value to cast</param>
    /// <returns type="IXMLDOMDocument" />
    return val;
}

function IXMLHTTP() 
{
    /// <field name="onreadystatechange" type="Function" />
    /// <field name="readyState" type="Number" />
    /// <field name="responseBody" type="String" />
    /// <field name="responseStream" type="String" />
    /// <field name="responseText" type="String" />
    /// <field name="responseXML" type="IXMLDOMDocument" />
    /// <field name="status" type="Number" />
    /// <field name="statusText" type="String" />    
}
IXMLHTTP.prototype =
{
    abort: function() { }
    , getAllResponseHeaders: function() {
        /// <returns type="String" />
    }
    , getResponseHeader: function(bstrHeader) {
        /// <param name="bstrHeader" type="String" />
        /// <returns type="String" />
    }
    , open: function(bstrMethod, bstrUrl, async, user, password) {
        /// <param name="bstrMethod" type="String" />
        /// <param name="bstrUrl" type="String" />
        /// <param name="async" type="Boolean">Optional</param>
        /// <param name="user" type="String">Optional</param>
        /// <param name="password" type="String">Optional</param>
    }
    , send: function(body) {
        /// <param name="body" type="String" />
    }
    , setRequestHeader : function(bstrHeader, bstrValue)
    {
        /// <param name="bstrHeader" type="String" />
        /// <param name="bstrValue" type="String" />
        /// <returns type="String" />
    }
}
Cast.As.IXMLHTTP = function(val) {
    /// <summary>Returns a casted IXMLHTTP</summary>
    /// <param name="val" type="IXMLHTTP">Value to cast</param>
    /// <returns type="IXMLHTTP" />
    return val;
}

function IXMLDOMNode() { 
    /// <field name="baseName" type="String" />
    /// <field name="childNodes" type="Array" elementType="IXMLDOMNode" />
    /// <field name="definition" type="IXMLDOMNode" />
    /// <field name="firstChild" type="IXMLDOMNode" />
    /// <field name="lastChild" type="IXMLDOMNode" />
    /// <field name="namespaceURI" type="String" />
    /// <field name="nextSibling" type="IXMLDOMNode" />
    /// <field name="nodeName" type="String" />
    /// <field name="nodeType" type="DOMNodeType" />
    /// <field name="nodeTypedValue" type="Variant" />
    /// <field name="nodeTypeString" type="String" />
    /// <field name="nodeValue" type="Variant" />
    /// <field name="ownerDocument" type="DOMDocument" />
    /// <field name="parentNode" type="IXMLDOMNode" />
    /// <field name="parsed" type="Boolean" />
    /// <field name="prefix" type="String" />
    /// <field name="previousSibling" type="IXMLDOMNode" />
    /// <field name="text" type="String">text content of the node and subtree</field>
    /// <field name="specified" type="Boolean" />
    /// <field name="xml" type="String">return the XML source for the node and each of its descendants</field>        
}
IXMLDOMNode.prototype =
{
    appendChild: function(newChild) {
        /// <param name="newChild" type="IXMLDOMNode" />
        /// <returns type="IXMLDOMNode" />
    }

    , cloneNode: function(deep) {
        /// <param name="deep" type="Boolean" />
        /// <returns type="IXMLDOMNode" />
    }

    , hasChildNodes: function() {
        /// <returns type="Boolean" />
    }

    , insertBefore: function(newChild, refChild) {
    /// <param name="newChild" type="IXMLDOMNode" />
    /// <param name="refChild" type="IXMLDOMNode" />
    /// <returns type="IXMLDOMNode" />
    }
    
    , removeChild: function(childNode) {
        /// <param name="childNode" type="IXMLDOMNode" />
        /// <returns type="IXMLDOMNode" />
    }
    , replaceChild: function(newChild, oldChild) {
    /// <param name="newChild" type="IXMLDOMNode" />
    /// <param name="oldChild" type="IXMLDOMNode" />
    /// <returns type="IXMLDOMNode" />
    }

    , selectNodes: function(queryString) {
    /// <param name="queryString" type="String" />
    /// <returns type="Array" elementType="IXMLDOMNode" />
    }

    , selectSingleNode: function(queryString) {
    /// <param name="queryString" type="String" />
    /// <returns type="IXMLDOMNode" />
    }

    , transformNode: function(stylesheet) {
        /// <param name="stylesheet" type="IXMLDOMNode" />
        /// <returns type="String" />
    }

    , transformNodeToObject: function(stylesheet, outputObject) {
    /// <param name="stylesheet" type="IXMLDOMNode" />
    /// <param name="outputObject" type="Object" />
    }
}
Cast.As.IXMLDOMNode = function(val) {
/// <summary>Returns a casted IXMLDOMNode</summary>
/// <param name="val" type="IXMLDOMNode">Value to cast</param>
/// <returns type="IXMLDOMNode" />
    return val;
}


var XMLUtils = {}
XMLUtils.ToObject  =  __XMLUtils_ToObject;

XMLUtils.Types = {}
XMLUtils.Types.XMLElement = {};
XMLUtils.Types.XMLDocument = {};

XMLUtils.CreateXMLDocument = function() {
    /// <returns type="IXMLDOMDocument" />
    var xmlDoc = new ActiveXObject("MSXML2.DOMDocument.4.0");
    return xmlDoc;
}

function __XMLUtils_ToObject(nodRoot, typeInference) {
    /// <summary>Coverts an xml hierarchy, optionally you can also chose to have the function attempt to infer the datatypes of the data.</summary>
    /// <param name="nodRoot" type="Object">XML Node to covert</param>
    /// <param name="typeInference" type="Boolean">Attempt to infer and cast the data types?</param>
    /// <returns type="Object">Converted object</returns>

    typeInference = typeInference == true;
    
    var o = {};
    
    for (var i = 0; i < nodRoot.childNodes.length; i++)
    {    
        var nod = nodRoot.childNodes[i];
        if (nod.childNodes.length == 0)
        {
            o[nod.tagName] = '';
        }
        else if (nod.childNodes.length == 1) {
            var val = nod.text;
            
            if (typeInference) {
                if (IsDate(val)) {
                // Test for date
                    val = new Date();
                    val.ParseDate(val);
                }
                else if (IsNumeric(val)) {
                // Test for numeric
                    val = parseFloat(val);
                }
                else if (IsBool(val)) {
                // Test for bool
                    val = val.ToBool();
                }               
            }

            o[nod.tagName] = val;
        }
        else
        {
            o[nod.tagName] =  __XMLUtils_ToObject(nod); 
        } 
    }
    
    return o;
}

var HTMLUtils = {
                  IsChildElementOf : __HTMLUtils_IsChildElementOf,
                  GetExactLocation: __HTMLUtils_GetExactLocation
                }
    

function __HTMLUtils_IsChildElementOf(parenthtmlElement, potentialChildHtmlElement)
{
    if (typeof(parenthtmlElement) != 'object')
        throw new Error('Invalid htmlElement passed for parent');
        
    if (typeof(potentialChildHtmlElement) != 'object')
        throw new Error('Invalid htmlElement passed for child');
    
    var currentParentHtmlElement = potentialChildHtmlElement.parentElement;
    var bKeepSearching  = true;

    while (bKeepSearching)
    {
        if (currentParentHtmlElement != parenthtmlElement)
        {
            // Hit the document root
            if (window.document.body == currentParentHtmlElement.parentElement) return false;
            
            // Move to the next parent
            currentParentHtmlElement = currentParentHtmlElement.parentElement;
        }
        else
        {
            // Matching parent!
            return true;
        }
    }

    return false;
}

function __HTMLUtils_GetExactLocation(targetHtmlElement) {
    var position = { Left: 0, Top: 0 };

    do {
        position.Left += targetHtmlElement.offsetLeft;
        position.Top += targetHtmlElement.offsetTop;

    } while (targetHtmlElement = targetHtmlElement.offsetParent);

    return position;
}


function CopyToClipboad(value)
{
    if (window.clipboardData && clipboardData.setData)
    {
        clipboardData.setData("Text", value);
    }
    else
    {
        // You have to sign the code to enable this or allow the action in about:config by changing
        user_pref("signed.applets.codebase_principal_support", true);
        netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');

        var clip = Components.classes['@mozilla.org/widget/clipboard;[[[[1]]]]'].createInstance(Components.interfaces.nsIClipboard);
        if (!clip) return;

        // create a transferable
        var trans = Components.classes['@mozilla.org/widget/transferable;[[[[1]]]]'].createInstance(Components.interfaces.nsITransferable);
        if (!trans) return;

        // specify the data we wish to handle. Plaintext in this case.
        trans.addDataFlavor('text/unicode');

        // To get the data from the transferable we need two new objects
        var str = new Object();
        var len = new Object();

        var str = Components.classes["@mozilla.org/supports-string;[[[[1]]]]"].createInstance(Components.interfaces.nsISupportsString);

        var copytext = value;

        str.data = copytext;

        trans.setTransferData("text/unicode", str, copytext.length * [[[[2]]]]);

        var clipid = Components.interfaces.nsIClipboard;

        if (!clip) return false;

        clip.setData(trans, null, clipid.kGlobalClipboard);
    }
}

