
/// <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.Inherits = inherits;
Function.prototype.Implements = _implements;
Function.prototype.Inherits = inheritsPrototype;

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 = Object.ConstructorName(this) + " does not implement " + pClass.FunctionName() + "." + n;
            e.description = e.message;
            throw e;
        }
    }
}


function inherits(oTarget, oClass, ConstructorArgs) {
    var newClass = new oClass(ConstructorArgs);
    for (var n in newClass) {
        // do not copy over prototyped functions
        if (oTarget.constructor.prototype[n] != undefined) continue;

        if (typeof (newClass[n]) != 'function') {
            // value/reference type
            oTarget[n.toString()] = newClass[n]
            continue;
        }
        else {
            // function
            oTarget[n.toString()] = function () { }
            oTarget[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() {
    Object.Inherits(this, Collection);
    this.Keys = new Object();

    var sQueryString = window.location.href;

    var iIndex = sQueryString.indexOf("?");

    if (iIndex == -1) {
        return; 		// No querystring found.
    }

    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(); this.__Popup
        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];

    var newObj;
    switch (this.Add.arguments.length) {
        case 0:
            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 {
                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];
        }
    }

    var newObj;
    switch (this.Insert.arguments.length) {
        case 0:
            newObj = new this.DefaultCollectionObject();
            this.Items.splice(tmpIdx, 0, newObj);

            return newObj;

        case 2:

            if (typeof (this.Add.arguments[1]) == "object") {

                this.Items.splice(tmpIdx, 0, this.Insert.arguments[0]);


                newObj = this.Insert.arguments[0];

                return newObj;
            } 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];


                newObj = new this.DefaultCollectionObject(args);

                this.Items.splice(tmpIdx, 0, newObj);

                return newObj;
            }

        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];

            newObj = new this.DefaultCollectionObject(args);
            this.Items.splice(tmpIdx, 0, newObj);

            return newObj;
    }
}

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>

    Object.Inherits(this, 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 (Object.ConstructorName(pUnknown)) {
                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()\nType Specified:' + Object.ConstructorName(pUnknown);
                    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].toString().IsNumeric() ? aCols[c].toString().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 (Object.ConstructorName(pUnknown)) {
                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":
                    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 (typeof (CurrentValue) == 'object' && Object.ConstructorName(CurrentValue) == 'Date')
                        CurrentValue = CurrentValue.Format("yyyy-mm-dd hh:nn:ss");

                    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() {
    Object.Inherits(this, 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() {
    Object.Inherits(this, 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 __String_IsNumeric() {
    var s = this.toString();
    return !(s == '' || s == undefined || s == null || isNaN(s));
}
String.prototype.IsNumeric = __String_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('[{]{0,1}[\\dABCDEFabcdef]{8}-[\\dABCDEFabcdef]{4}-[\\dABCDEFabcdef]{4}-[\\dABCDEFabcdef]{4}-[\\dABCDEFabcdef]{12}[}]{0,1}') == 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.toString().IsNumeric()) { return (val.toString().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;
    }
}


String.prototype.ToCurrency = __String_ToCurrency;
function __String_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.toString().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() + 1) + "-" + 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(",", ""));
    }
}

String.prototype.ToInt = __String_ToInt;
function __String_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);

    Msgbox(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) {
        Msgbox("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);

    var expiry;
    if (setCookie.arguments.length == 3 && setCookie.arguments[2] == true) {
        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
        expiry = today;
    }

    if (expiry != today) sCookie = sCookie + "; expires=" + expiry.toGMTString();
    sCookie += "; path=/";

    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.CopyFrom = __Object_CopyFrom;

function __Object_ConstructorName(target) {
    var iStart = null;
    var iEnd = null;
    var finished = false;

    var constValue = target.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.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.Props = __Object_EnumerateProperties;
Object.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;
    Find = Find.replace(/\\/gi, "\\\\");
    var execString = "s.replace(/" + Find + "/gi, ReplaceWith); ";

    var newValue = eval(execString);

    return newValue;
}
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.EscapeHTML = function () {
    var escaped = this.toString();
    var findReplace = [[/&/g, "&amp;"], [/</g, "&lt;"], [/>/g, "&gt;"], [/"/g, "&quot;"]]
    for (var item in findReplace)
        escaped = escaped.replace(item[0], item[1]);

    return escaped;
}

String.prototype.EscapeJS = function () {
    var escaped = this.toString();
    escaped = escaped.replace(/'/g, "\\\'");
    escaped = escaped.replace(/"/g, '\\\"');
    return escaped;
}

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 == "") {
            Msgbox("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) {
        Msgbox(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 = 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);
    }
}

var Guid = {};
Guid.IsEmpty = function (guidString) {
    var isEmpty = guidString.indexOf("00000000-0000-0000-0000-000000000000") != -1;
    return isEmpty;
}

Guid.IsBlankOrEmpty = function (guidString) {
    if (guidString.length == 0)
        return true;

    return Guid.IsEmpty(guidString);
}

Guid.Equals = function (guid1, guid2) {
    var match1 = guid1.match('[\\dABCDEFabcdef]{8}-[\\dABCDEFabcdef]{4}-[\\dABCDEFabcdef]{4}-[\\dABCDEFabcdef]{4}-[\\dABCDEFabcdef]{12}');
    var match2 = guid2.match('[\\dABCDEFabcdef]{8}-[\\dABCDEFabcdef]{4}-[\\dABCDEFabcdef]{4}-[\\dABCDEFabcdef]{4}-[\\dABCDEFabcdef]{12}');

    if (match1 == null && match2 == null)
        return true;
    if (match1 == null || match2 == null)
        return false;

    return match1.toString() == match2.toString();
}

// Abstracts the 'popup' usage from IE's underlying class.
var Popup = function (bAsChromelessWindow) {
    this.IsChromelessWindow = bAsChromelessWindow == true;

    if (bAsChromelessWindow) {
        // IE Only chromeless window
        this._popup = window.createPopup();
        this.document = this._popup.document;

        this.document.__RootPopupXOffset = 0;
        this.document.__RootPopupYOffset = 0;

        this.document.__RefXOffset = 0;         // Carries through the XOffsets caused by the refrenced object to anchor a popup against
        this.document.__RefYOffset = 0;         // Carries through the XOffsets caused by the refrenced object to anchor a popup against
    }
    else {

        // Default behaviour
        // Do nothing, iframe is created on first call to show()

        if (document.body != undefined) {
            var iframe = document.createElement("IFRAME");
            iframe.style.visibility = "hidden";
            iframe.style.position = "absolute";
            iframe.id = "popup_" + RandomGUID;
            iframe.name = "myid";
            this._popup = iframe;
            document.body.appendChild(this._popup);
            this.document = this._popup.contentWindow.document;

            var t = this;
            // Attach click event to document to allow closing of the popup
            document.body.attachEvent("onmousedown", function () { t.parentDocumentClickEvent() });
        }
    }


};

Popup.prototype =
{
    IsChromelessWindow: null,

    _popup: null,

    document: null,

    parentDocumentClickEvent: function () {
        if (this.get_isOpen()) {
            this.hide();
        }
    },

    show: function (xOffset, yOffset, width, height, referenceObject) {
        var f = null;
        var t = this;
        if (this.IsChromelessWindow) {
            f = function () { t.showChromeless(xOffset, yOffset, width, height, referenceObject); };
        }
        else {
            f = function () { t.showNonChromeless(xOffset, yOffset, width, height, referenceObject) };
        }

        setTimeout(f, 0);
    },

    showChromeless: function (xOffset, yOffset, width, height, referenceObject) {
        if (window.document.__popupDepth) {
            this.document.__popupDepth = window.document.__popupDepth + 1;
        }
        else {
            this.document.__popupDepth = 1;
            this.document.__RefXOffset = 0;
            this.document.__RefYOffset = 0;
        }

        if (this.document.__popupDepth == 1) {
            this.document.__RootPopupXOffset = 0;
            this.document.__RootPopupYOffset = 0;
        }
        else {
            this.document.__RootPopupXOffset = window.document.__RootPopupXOffset;
            this.document.__RootPopupYOffset = window.document.__RootPopupYOffset;

            this.document.__RefXOffset = window.document.__RefXOffset;
            this.document.__RefYOffset = window.document.__RefYOffset;
        }


        var refTop = 0;
        var refLeft = 0;

        if (this.__RootPopupXOffset != 0) {
            //alert("this.__RootPopupXOffset: " + this.document.__RootPopupXOffset + "\nthis.__RootPopupYOffset: " + this.document.__RootPopupXOffset);
        }

        xOffset += this.document.__RootPopupXOffset;
        yOffset += this.document.__RootPopupYOffset;

        if (referenceObject != null) {
            var loc = HTMLUtils.GetExactLocation(referenceObject);
            refTop = loc.Top + this.document.__RefYOffset;
            refLeft = loc.Left + this.document.__RefXOffset;
        }

        var wnd = window.top != window ? window.top : window;
        var bod = wnd.document.body;

        var windowHeight = bod.offsetHeight;
        var windowWidth = bod.offsetWidth;
        var scrollOffsetY = bod.scrollTop;

        var xPosition = refLeft + xOffset + width;
        var yPosition = refTop + yOffset + height;

        var finalYOffset = 0;
        var finalXOffset = 0;

        if (xPosition > windowWidth) {
            finalXOffset = xOffset + (windowWidth - xPosition);
        }
        else {
            finalXOffset = xOffset;
        }

        if ((yPosition - scrollOffsetY) > windowHeight) {
            // Adjusts the position of the Y if the window is going to go off the screen (bottom)
            finalYOffset = (windowHeight - (yPosition - scrollOffsetY));
        }
        else {
            finalYOffset = yOffset;
        }

        var s = "finalYOffset: " + finalYOffset + "\nfinalXOffset: " + finalXOffset + "\nwidth: " + width + "\nheight: " + height;
        var thisObject = this;

        this.document.__RootPopupXOffset += finalXOffset;
        this.document.__RotPopupYOffset += finalYOffset;

        this.document.__RefXOffset += refLeft;
        this.document.__RefYOffset += refTop;

        // Do not remove this code, it stops a ridiculous error from occurring.
        // Likely caused by the popup's document not being loaded in time
        var f = function () {
            if (!(thisObject) || !(thisObject) || (thisObject.document.readyState != "complete" && thisObject.document.readyState != "interactive")) {
                setTimeout(f, 50); return;
            }

            thisObject._popup.show(finalXOffset, finalYOffset, width, height, referenceObject);
        }

        setTimeout(f, 50);
        thisObject._popup.show(finalXOffset, finalYOffset, width, height, referenceObject);
    },

    showNonChromeless: function (xOffset, yOffset, width, height, referenceObject) {
        if (this._popup == null) {
            var iframe = document.createElement("IFRAME");
            iframe.style.visibility = "hidden";
            iframe.style.position = "absolute";
            iframe.id = "popup_" + RandomGUID;
            iframe.name = "myid";
            this._popup = iframe;
            document.body.appendChild(this._popup);
            this.document = this._popup.contentWindow.document;

            var t = this;
            // Attach click event to document to allow closing of the popup
            document.body.attachEvent("onmousedown", function () { t.parentDocumentClickEvent() });
        }



        this._popup.style.height = height;
        this._popup.style.width = width;

        if (xOffset == undefined)
            xOffset = 0
        else
            xOffset = parseInt(xOffset, 10);

        if (yOffset == undefined)
            yOffset = 0
        else
            yOffset = parseInt(yOffset, 10);



        if (referenceObject != undefined) {
            var loc = HTMLUtils.GetExactLocation(referenceObject);
            xOffset += loc.Left;
            yOffset += loc.Top;
        }

        var docWidth = document.body.clientWidth;
        if (xOffset + width > docWidth)
            xOffset = xOffset + (docWidth - (xOffset + width));

        this._popup.style.left = xOffset + "px";
        this._popup.style.top = yOffset + "px";
        this._popup.style.zIndex = 10000;

        this._popup.style.visibility = "visible";
        this._popup.style.display = "block";

    },

    hide: function () {
        if (this.IsChromelessWindow) {
            this._popup.hide();
        }
        else {
            // hide the iFrame
            this._popup.style.visibility = "hidden";
            this._popup.style.display = "none";
        }
    },

    get_isOpen: function () {
        if (this.IsChromelessWindow) {
            return this._popup.isOpen;
        }
        else {
            // hide the iFrame
            return !(this._popup.style.visibility == "hidden");
        }
    }
}

Popup.createPopup = function (bAsChromelessWindow) {
    return new Popup(bAsChromelessWindow);
}
