﻿/*******************************************************************************
 Functions for RSWeb.NET
 
 Must include OSI_Init.js before this file.
*******************************************************************************/
OSI.addScriptLink("scripts/RSCommon.js");

// Create RSWebNET namespace
OSI.RSWebNET = { }
// alias
$RSWeb = OSI.RSWebNET;

/*******************************************************************************
Reset timeout to original value. This verifies that rsfooter_ResetTimeout()
exists in case the footer is not being displayed.

Pass true for the "forceReset" parameter if you have just successfully returned
from an AJAX call and know that the timeout should be reset. This is in case
the timeout expired message occurred just as you were trying to reset it.
*******************************************************************************/
OSI.RSWebNET.resetTimeout = function(forceReset) {
    if (typeof(rsfooter_ResetTimeout) == 'function')
        rsfooter_ResetTimeout(forceReset);
}

/*******************************************************************************
When calling alert(), the timer does not fire and does not count down. So, if
the timer is set to 15 seconds, the timer still would not fire for 15 seconds
AFTER the user has pressed OK on the Alert message. This could be confusing if
the message is left on the screen for over 15 seconds.

Call this to update the footer to reflect the actual time remaining timeout to
original value. This verifies that rsfooter_Timer() exists in case the footer is
not being displayed.
*******************************************************************************/
OSI.RSWebNET.updateTimeout = function() {
    if (typeof(rsfooter_Timer) == 'function')
        rsfooter_Timer();
}


/*******************************************************************************
Add strings needed for page. Strings will be loaded via $(document).ready()

You can either:
1) Add single string to list of strings to load
                OR
2) id is an object with members in the following format:
    name    string      a number or the WebNetStrings enum value
    value   string      default string in case string can't be loaded

    Example:
        var imageStrings = {
            "ErrNoMarkedRows" : "No rows have been selected.",
            "MnuViewImages": "View Images"
        };
*******************************************************************************/
OSI.RSWebNET.useString = function(id, defaultValue) {
    if (isNull(OSI.RSWebNET.strings))
        OSI.RSWebNET.strings = new Object();

    if (typeof(id) == "string")
        OSI.RSWebNET.strings[id] = defaultValue;
    else {
        $.each(id, function(index, value) {
            OSI.RSWebNET.strings[index] = value;
        });
    }
}

/*******************************************************************************
Query server to return specified array of strings

strArray is an object with members in the following format:
    name    string      a number or the WebNetStrings enum value
    value   string      default string in case string can't be loaded

Example:
    var imageStrings = {
        "ErrNoMarkedRows" : "No rows have been selected.",
        "MnuViewImages": "View Images"
    };
*******************************************************************************/
OSI.RSWebNET.loadStrings = function(strArray, context, callback) {
    if (isNotNull(strArray))
        // Save to global string list
        OSI.RSWebNET.useString(strArray);
    else
        // Load strings get by calling useString()
        strArray = OSI.RSWebNET.strings;

    // Create a string of ids only separated by commas
    var data = {
        Type: $RSWeb.eRequestType.Strings, // Strings request
        Strings: String.empty
    };
    // Create comma delimited string list
    var first = true;
    $.each(strArray, function(index, value) {
        if (first)
            first = false;
        else
            data.Strings += ",";
        data.Strings += index;
    });

    OSI.RSWebNET.ajaxRequest(data, {
        context: context,
        callback: callback },
        function(data, textStatus, XMLHttpRequest, errorThrown) {
            if (isNotNull(data)) {
                $.each(data, function(index, value) {
                    $ODL.log(value.Id + ": " + value.Value, null, "String");
                    // Set string cache
                    if (value.LoadSuccess)
                        $RSWeb.strings[value.Id] = value.Value;
                });
            }

            if (this.callback) {
                this.callback.call(this.context);
            }
        });
}

/*******************************************************************************
Query server to return translated strings based on what strings are defined in
the "html" parameter.

The format to translate strings in the html is the following:
    !~~~@<string name>:<default text>@~~~!

Example:
    !~~~@ErrNoMarkedRows:No rows have been selected.@~~~!
*******************************************************************************/
OSI.RSWebNET.translate = function(html, context, callback) {
    var fmt = "!~~~@{0}:(.+?)@~~~!";
    var re = new RegExp(String.format(fmt, "(.+?)"), "gm");
    var loadList = {};
    var strList = {};
    var m;

    // Gather list of strings to load
    while (m = re.exec(html)) {
        if (!$RSWeb.getString(m[1]))
            // Add to list if not already cached
            loadList[m[1]] = m[2];
        else
            // List of strings to replace
            strList[m[1]] = m[2];
    }

    $RSWeb.loadStrings(loadList, this, function() {
        $.extend(strList, loadList);

        // Replace html with translated strings
        $.each(strList, function(index, value) {
            // Replace:
            //      !~~~@ErrNoMarkedRows:No rows have been selected.@~~~!
            // with
            //      No rows have been selected.
            // (or actual translated string)
            var re = new RegExp(String.format(fmt, index), "gm");
            html = html.replace(re, $RSWeb.getString(index));
        });

        callback.call(context, html);
    });
}

/*******************************************************************************
Get specified string from array of strings that have been loaded from the
server. Call OSI.RSWebNET.useString() to defer load 
*******************************************************************************/
OSI.RSWebNET.getString = function(id) {
    return OSI.RSWebNET.strings[id];
}

OSI.RSWebNET.ajaxURL = "ajax.aspx";

/*******************************************************************************
 This must match the RequestType enum in ajax.aspx.cs
*******************************************************************************/
OSI.RSWebNET.eRequestType = {
    /***************************************************************************
    Request string translations

    Input:
        Type: 0
        Strings: comma delimited list of WebClient.WebNetStrings (RSSQL.NET.h)
                 enums
    Output:
        Array of JSON.String (see JSON.cs) objects
    **************************************************************************/
    Strings: 0,
    /***************************************************************************
    Request images contained in specified document

    Input:
        Type: 1
        id: Document identity value

    Output:
        Array of image ids
    ***************************************************************************/
    DocumentImages: 1,
    /***************************************************************************
    Request image detail info

    Input:
        Type: 2
        id: Image identity value

    Output:
        JSON.ImageRec
    ***************************************************************************/
    ImageDetail: 2,
    /***************************************************************************
    Request document detail info

    Input:
        Type: 3
        id: document identity value

    Output:
        JSON.DocumentRec
    ***************************************************************************/
    DocumentDetail: 3,
    /***************************************************************************
    Request container detail info

    Input:
        Type: 4
        id: container identity value

    Output:
        JSON.ContainerRec
    ***************************************************************************/
    ContainerDetail: 4,
    /***************************************************************************
    Request filefolder detail info

    Input:
        Type: 5
        id: filefolder identity value

    Output:
        JSON.FilefolderRec
    ***************************************************************************/
    FilefolderDetail: 5,
    /***************************************************************************
    Request Yes/No strings

    Input:
        Type: 6

    Output:
        JSON.YesNoRec
    ***************************************************************************/
    YesNoDetail: 6,
    /***************************************************************************
    Request selected rows for specified grid

    Input:
        Type: 7

    Output:
        Array of identity values
    ***************************************************************************/
    GetSelectedRows: 7,
    /***************************************************************************
    Request images contained in specified container

    Input:
        Type: 8
        id: Container identity value

    Output:
        Array of image ids
    ***************************************************************************/
    ContainerImages: 8,
    /***************************************************************************
    Request images contained in specified filefolder

    Input:
        Type: 9
        id: Filefolder identity value

    Output:
        Array of image ids
    ***************************************************************************/
    FilefolderImages: 9,
    /***************************************************************************
    Get status of last request that was run in a thread on the server

    Input:
        Type: 10

    Output:
        ThreadStatus object
    ***************************************************************************/
    ThreadStatus: 10,
    /***************************************************************************
    Image express grid requests

    Input:
        Type: 11
        subType:
            0: Grid defaults (classes, images, text strings, Full View headings)
            1: Full View Row Detail
               Additional parameters:
                    offset: Row to start
                    numRows: Maximum number of rows to return
            2: Filefolder grid settings
               Additional parameters:
                    identity: container to load filefolders for
            3: Filefolder row detail
               Additional parameters:
                    identity: container to load filefolders for
                    offset: Row to start
                    numRows: Maximum number of rows to return

    Output:
        Depends on subType
    ***************************************************************************/
    ImageExpressResults: 11,
    /***************************************************************************
    Request item detail info

    Input:
        Type: 12
        id: identity value

    Output:
        JSON.ItemRec
    ***************************************************************************/
    ItemDetail: 12,
    /***************************************************************************
    Request Account detail info

    Input:
        Type: 13
        id: identity value

    Output:
        JSON.AccountRec
    ***************************************************************************/
    AccountDetail: 13,
    /***************************************************************************
    Request Object detail info

    Input:
        Type: 14
        id: identity value

    Output:
        JSON.ObjectRec
    ***************************************************************************/
    ObjectDetail: 14,
    /***************************************************************************
    Request Item Status detail info

    Input:
        Type: 15
        id: identity value

    Output:
        JSON.ItemStatusRec
    ***************************************************************************/
    ItemStatusDetail: 15,
    /***************************************************************************
    Request Category detail info

    Input:
        Type: 16
        id: identity value

    Output:
        JSON.CategoryRec
    ***************************************************************************/
    CategoryDetail: 16,
    /***************************************************************************
    Request Security detail info

    Input:
        Type: 17
        id: identity value

    Output:
        JSON.SecurityRec
    ***************************************************************************/
    SecurityDetail: 17,
    /***************************************************************************
    Request RecordSeries detail info

    Input:
        Type: 18
        id: identity value

    Output:
        JSON.RecordSeriesRec
    ***************************************************************************/
    RecordSeriesDetail: 18,
    /***************************************************************************
    Request Item Description text

    Input:
        Type: 19
        id: identity value

    Output:
        JSON.TextValue
    ***************************************************************************/
    ItemDescriptionText: 19,
    /***************************************************************************
    Request Item Contents text

    Input:
        Type: 20
        id: identity value

    Output:
        JSON.TextValue
    ***************************************************************************/
    ItemContentsText: 20,
    /***************************************************************************
    Request Document keyword text

    Input:
        Type: 21
        id: identity value

    Output:
        JSON.TextValue
    ***************************************************************************/
    DocumentKeywordText: 21,
    /***************************************************************************
    Request Document Type detail

    Input:
        Type: 22
        id: identity value

    Output:
        JSON.DocTypeRec
    ***************************************************************************/
    DocumentTypeDetail: 22,
    /***************************************************************************
    Request Image sequence exists
    
    Input:
        Type: 23
        documentID: identity value
        itemID: identity value
        sequence: sequence number

    Output:
        JSON.Bool
    ***************************************************************************/
    ImageSequenceExists: 23
}

// Create JSON namespace
OSI.RSWebNET.JSON = {};

$JSON = OSI.RSWebNET.JSON;

// Helper to call base constructor
var $base = function(cls, context) {
    cls.superclass.constructor.apply(context,
        Array.prototype.slice.call(arguments, 2));
}

// Keep array of classes based on Type
$JSON.Classes = {
    classes: {},
    add: function(cls, base) {
        $Y.extend(cls, base);
        var c = new cls();
        this.classes[c.getType()] = c;
    },
    addRec: function(cls) {
        this.add(cls, $JSON.Rec);
    },
    addID: function(cls) {
        this.add(cls, $JSON.Identity);
    },
    // Apply one of the classes defined below (e.g. $JSON.DocumentID) to
    // the specified object that was sent from the server.
    extend: function(obj) {
        if (isNotNull(obj) && isNotNull(obj.Type) &&
                    isNotNull(this.classes[obj.Type])) {
            $Y.mix(obj, this.classes[obj.Type]);

            // Also apply to all members
            for (m in obj)
                this.extend(obj[m]);
        }
    },
    exists: function(obj) {
        return isNotNull(obj) && isNotNull(obj.Type) &&
            isNotNull(this.classes[obj.Type]);
    },
    isRSWebNETTimeoutError: function(obj) {
        return isNotNull(obj) && isNotNull(obj.isTimeoutError) &&
            obj.isTimeoutError();
    },
    isRSWebNETError: function(obj) {
        return isNotNull(obj) && isNotNull(obj.isError) && obj.isError();
    },
    isTypeOf: function(obj, type) {
        return isNotNull(obj) && isNotNull(obj.getType) &&
               obj.getType() == type;
    }
}

$Y.use("oop", function(Y) {
    /***************************************************************************
                                BASE

    Base class for all JSON objects received via AJAX call.
    Type member must match Type property (i.e. class names) in App_Code/JSON.cs
    ***************************************************************************/
    $JSON.Base = function(type) {
        this.Type = type;

        this.getType = function() { return this.Type; }
        this.getString = function(context, callback) {
            callback.call(context, this.Type);
        }
        this.isError = function() { return false; }
        this.isTimeoutError = function() { return false; }
    }

    /***************************************************************************
                                ERROR

    Error has occurred.
    ***************************************************************************/
    $JSON.Error = function() {
        $base($JSON.Error, this, "Error");

        this.getString = function(context, callback) {
            callback.call(context, this.ErrorCode + ": " + this.Message);
        }

        // This must match the Error.ErrorType enum in App_Code/JSON.cs
        this.eErrorType = {
            exception:      0,
            timeout:        1,
            invalidRequest: 2
        }

        this.isError = function() { return true; }
        this.isTimeoutError = function() {
            return this.ErrorCode == this.eErrorType.timeout;
        }
    }
    $JSON.Classes.add($JSON.Error, $JSON.Base);

    /***************************************************************************
                                DATE

    Date only text value
    ***************************************************************************/
    $JSON.Date = function() {
        $base($JSON.Date, this, "Date");

        this.getString = function(context, callback) {
            callback.call(context, this.Value);
        }
    }
    $JSON.Classes.add($JSON.Date, $JSON.Base);

    /***************************************************************************
                             DATETIME

    Date and time text value
    ***************************************************************************/
    $JSON.DateTime = function() {
        $base($JSON.DateTime, this, "DateTime");

        this.getString = function(context, callback) {
            callback.call(context, this.Value);
        }
    }
    $JSON.Classes.add($JSON.DateTime, $JSON.Base);

    /***************************************************************************
                             BOOL

    Bool value
    ***************************************************************************/
    $JSON.Bool = function() {
        $base($JSON.Bool, this, "Bool");

        this.getString = function(context, callback) {
            callback.call(context, this.Value);
        }
    }
    $JSON.Classes.add($JSON.Bool, $JSON.Base);
    
    /***************************************************************************
                                Thread Status

    Status of thread executing on server side
    ***************************************************************************/
    $JSON.ThreadStatus = function() {
        $base($JSON.ThreadStatus, this, "ThreadStatus");

        this.getString = function(context, callback) {
            callback.call(context, this.Description);
        }

        this.isRunning = function() {
            return this.Status == this.eThreadStatus.StartInProgress ||
                   this.Status == this.eThreadStatus.Started ||
                   this.Status == this.eThreadStatus.CancelInProgress;
        }
        this.isCompleted = function() {
            return this.Status == this.eThreadStatus.Completed;
        }

        // This must match the ThreadEx.ThreadStatus enum in
        // rswebnetlib\thread.cs
        this.eThreadStatus = {
            NotStarted:         0,
            Error:              1,
            StartInProgress:    2,
            Started:            3,
            CancelInProgress:   4,
            Canceled:           5,
            Completed:          6,
            Failed:             7
        }
    }
    $JSON.Classes.add($JSON.ThreadStatus, $JSON.Base);

    /***************************************************************************
                                Redirect

    Automatically redirect to specified page
    ***************************************************************************/
    $JSON.Redirect = function() {
        $base($JSON.Redirect, this, "Redirect");

        this.getString = function(context, callback) {
            callback.call(context, this.Message);
        }

        this.redirect = function() {
            if (this.Message)
                $Modal.PleaseWait.setHtml(this.Message);

            window.location.href = this.Page;
        }
    }
    $JSON.Classes.add($JSON.Redirect, $JSON.Base);

    $JSON.ImageExpressResultsGridDefaults = function() {
        $base($JSON.ImageExpressResultsGridDefaults, this,
              "ImageExpressResultsGridDefaults");
    }
    $JSON.Classes.add($JSON.ImageExpressResultsGridDefaults, $JSON.Base);

    $JSON.ImageExpressResultsRowDetail = function() {
        $base($JSON.ImageExpressResultsRowDetail, this,
              "ImageExpressResultsRowDetail");
    }
    $JSON.Classes.add($JSON.ImageExpressResultsRowDetail, $JSON.Base);

    $JSON.ImageExpressResultsFilefolderContaineesGrid = function() {
        $base($JSON.ImageExpressResultsFilefolderContaineesGrid, this,
              "ImageExpressResultsFilefolderContaineesGrid");
    }
    $JSON.Classes.add($JSON.ImageExpressResultsFilefolderContaineesGrid,
        $JSON.Base);

    $JSON.ImageExpressResultsFilefolderContaineesRowDetail = function() {
        $base($JSON.ImageExpressResultsFilefolderContaineesRowDetail, this,
              "ImageExpressResultsFilefolderContaineesRowDetail");
    }
    $JSON.Classes.add($JSON.ImageExpressResultsFilefolderContaineesRowDetail,
        $JSON.Base);

    $JSON.ImageExpressResultsFilefolderMatchesGrid = function() {
        $base($JSON.ImageExpressResultsFilefolderMatchesGrid, this,
              "ImageExpressResultsFilefolderMatchesGrid");
    }
    $JSON.Classes.add($JSON.ImageExpressResultsFilefolderMatchesGrid,
        $JSON.Base);

    $JSON.ImageExpressResultsFilefolderMatchesRowDetail = function() {
        $base($JSON.ImageExpressResultsFilefolderMatchesRowDetail, this,
              "ImageExpressResultsFilefolderMatchesRowDetail");
    }
    $JSON.Classes.add($JSON.ImageExpressResultsFilefolderMatchesRowDetail,
        $JSON.Base);
});

/*******************************************************************************
Make AJAX request to ajax.aspx

Parameters:
    params      object containing the Type (see eRequestType above) and any
                extra parameters needed for request
    context     Context to use for the callback
    callback    Function to call when ajax call has completed or failed:
                    function(data, textStatus, XMLHttpRequest, errorThrown)
                data: object returned from ajax call. If ajax call failed, this
                      is null
                textStatus: a string describing the status or type of error
                            that occurred
                XMLHttpRequest: XMLHttpRequest object
                errorThrown: optional exception object, if one occurred
                See http://api.jquery.com/jQuery.ajax/ for more detail
    addlSettings optional. Any additional settings or override defaults.
*******************************************************************************/
OSI.RSWebNET.ajaxRequest = function(params, context, callback, addlSettings) {
    var settings = {
        data: params,
        context: this,
        success: function(data, textStatus, XMLHttpRequest) {
            $ODL.log("ajax request successful", null, "ajax");
            if (isNotNull(data)) {
                // Add additional properties/members
                $JSON.Classes.extend(data);
            }

            if ($JSON.Classes.isRSWebNETTimeoutError(data)) {
                // timeout has occurred. Redirect to session timeout page.
                window.location = $RSWeb.sessionTimeoutPage;
                return;
            }
            else if ($JSON.Classes.isTypeOf(data, "Redirect")) {
                data.redirect();
                // Redirect doesn't redirect right away.
                return;
            }
            else
            // Reset footer timer
                $RSWeb.resetTimeout(true);

            if ($JSON.Classes.isRSWebNETError(data)) {
                $ODL.log("RSWeb.NET error: " + data, null, "ajax");

                if (addlSettings && addlSettings.autoDisplayError)
                    $RSWeb.MessageBox({ bodyText: data.Message });
            }

            // Report back success
            if (callback)
                callback.apply(context, arguments);
        },
        error: function(XMLHttpRequest, textStatus, errorThrown) {
            var error = String.format("ajax request failed (status {0})",
                XMLHttpRequest.status);

            if (addlSettings && addlSettings.autoDisplayError)
                $RSWeb.MessageBox({ bodyText: error });

            $ODL.logError(error, "ajax");

            // An error occurred
            if (callback)
                callback.call(context, null, textStatus,
                    XMLHttpRequest, errorThrown);
        },
        complete: function() {
            $Modal.PleaseWait.hide();
        },
        // IE always caches, so force to reload from server
        cache: !$.browser.isIE(),
        dataType: "json",
        url: this.ajaxURL
    }

    // Additional settings or override default settings
    if (addlSettings)
        $.extend(settings, addlSettings);

    // Add query string to indicate AJAX request
    settings.url += "?AjaxRequest=1";
    
    $Modal.PleaseWait.show();
    // Make sure JSON classes are set up before attempting to make AJAX
    // request.
    $Y.use("oop", function(Y) {
        $ODL.log("ajax request begin", null, "ajax");
        $.ajax(settings);
    });
}

/*******************************************************************************
Make AJAX request to ajax.aspx or specified url and wait for completion of
request by making AJAX requests until it is indicated that the request has
completed.

Parameters:
    params      object containing the Type (see eRequestType above) and any
                extra parameters needed for request
    context     Context to use for the callback
    callback    Function to call when ajax call has completed or failed:
                    function(data, textStatus, XMLHttpRequest, errorThrown)
                data: object returned from ajax call. If ajax call failed, this
                      is null
                textStatus: a string describing the status or type of error
                            that occurred
                XMLHttpRequest: XMLHttpRequest object
                errorThrown: optional exception object, if one occurred
                See http://api.jquery.com/jQuery.ajax/ for more detail
    addlSettings optional. Any additional settings or override defaults.
*******************************************************************************/
OSI.RSWebNET.ajaxWait = function(params, context, callback, addlSettings) {
    // Make sure this isn't cached
    if (addlSettings)
        addlSettings.cache = false;
    else
        addlSettings = { cache: false }
        
    $RSWeb.ajaxRequest(params, context,
        function(data, textStatus, XMLHttpRequest, errorThrown) {
            if (data && !data.isError()) {
                if (data.Detail)
                    // Update with status
                    $Modal.PleaseWait.setHtml(data.Detail);

                // Now make ajax request until completed
                $RSWeb.ajaxThreadStatus(context, callback,
                    addlSettings.autoDisplayError);
            }
            else if (callback)
                callback.apply(context, arguments);
        }, addlSettings);
}

OSI.RSWebNET.ajaxThreadStatus = function(context, callback, autoDisplayError) {
    function checkThreadStatus() {
        // Check status again in a second
        setTimeout(function() {
            $RSWeb.ajaxThreadStatus(context, callback, autoDisplayError);
        }, 1000);
    }

    $RSWeb.ajaxRequest({Type: $RSWeb.eRequestType.ThreadStatus}, this,
        function(data, textStatus, XMLHttpRequest, errorThrown) {
            if (data && !data.isError() && data.isRunning()) {
                if (data.Detail)
                    // Update with status
                    $Modal.PleaseWait.setHtml(data.Detail);

                checkThreadStatus();
            }
            else if (callback && callback.apply(context, arguments)) {
                checkThreadStatus();
            }
            // Make sure cache does not occur
        }, { cache: false,
             autoDisplayError: autoDisplayError });
}

/*******************************************************************************
Load specified HTML to specified element

Parameters:
    url         url of HTML to load
    appendTo    any valid object that can be passed to jQuery
    context     Context to use for the callback
    callback    Function to call when ajax call has completed or failed:
                    function(data, textStatus, XMLHttpRequest, errorThrown)
                data: object returned from ajax call. If ajax call failed, this
                      is null
                textStatus: a string describing the status or type of error
                            that occurred
                XMLHttpRequest: XMLHttpRequest object
                errorThrown: optional exception object, if one occurred
                See http://api.jquery.com/jQuery.ajax/ for more detail
*******************************************************************************/
OSI.RSWebNET.loadHTML = function(url, appendTo, context, callback) {
    var settings = {
        context: this,
        success: function(data, textStatus, XMLHttpRequest) {
            $ODL.log("ajax request successful", null, "loadHTML");

            $RSWeb.translate(data, this, function(html) {
                $(appendTo).append(html);
                // Report back success
                if (callback)
                    callback.call(context, html);
            });
        },
        error: function(XMLHttpRequest, textStatus, errorThrown) {
            $ODL.logError("ajax request failed: " + errorThrown, "loadHTML");

            // An error occurred
            if (callback)
                callback.call(context, null);
        },
        dataType: "html",
        url: url,
        // IE always caches, so force load from server
        cache: !$.browser.isIE()
    }

    $ODL.log("ajax request begin", null, "ajax");
    $.ajax(settings);
}

OSI.RSWebNET.ToDatePickerFormat = function(fmt) {
    return fmt.toLowerCase().replace("yyyy", "yy");
}

OSI.RSWebNET.datePickerID = 0;
OSI.RSWebNET.AttachDatePicker = function(date) {
    if (date.length == 0)
        return;

    // Need to create unique ID so that the correct input control gets
    // updated with the date.
    date.each(function(index, element) {
        if (isNull(element.id) || element.id == String.empty)
            element.id = "DatePicker" + OSI.RSWebNET.datePickerID++;
    });

    // Remove hasDatepicker class so that datepicker knows to initialize
    // correctly. See:
    // http://old.nabble.com/Using-datepicker-in-dynamic-DOM-td21324444s27240.html
    date.removeClass('hasDatepicker').datepicker({
        autoSize: true,
        changeMonth: true,
        changeYear: true,
        dateFormat: OSI.RSWebNET.ToDatePickerFormat(
            OSI.RSWebNET.ShortDatePattern),
        gotoCurrent: true,
        showOn: 'focus',
        yearRange: '-20:+20'
    });

    var lastGoodDateKey = "LastGoodDate";
    var badDateClass = "DatePicker-InvalidDate";

    // Check after each character is pressed
    date.keyup(function(event) {
        var dp = $(this);

        try {
            var val = dp.val();

            if (val.length > 0) {
                $.datepicker.parseDate(dp.datepicker("option", "dateFormat"),
                    val);
            }
            dp.data(lastGoodDateKey, val);
            dp.removeClass(badDateClass);
        }
        catch (ex) {
            if (!dp.hasClass(badDateClass))
                dp.addClass(badDateClass);
        }
    });
    // Check when input changes and loses focus
    date.change(function(event) {
        var dp = $(this);

        try {
            var val = dp.val();

            if (val.length > 0) {
                $.datepicker.parseDate(dp.datepicker("option", "dateFormat"),
                    val);
            }

            dp.data(lastGoodDateKey, val);
            dp.removeClass(badDateClass);
        }
        catch (ex) {
            // Restore last good date if exists
            dp.val(dp.data(lastGoodDateKey));
        }
    });
}

var messageBoxStrings = {
    "BtnClose" : "Close"
};
// Load strings on $(document).ready()
$RSWeb.useString(messageBoxStrings);

OSI.RSWebNET.MessageBox = function(options) {
    if (isNull(options.buttons))
        options.buttons = [
            { text : $RSWeb.getString("BtnClose") }
        ];

    $Modal.MessageBox.show(options);
}

OSI.RSWebNET.MessageAdd = function(msgID, msg, cssClass) {
    var $msg = $("#" + msgID);

    $msg.show();

    if (isNotNull(cssClass))
        cssClass = String.format(" class='{0}'", cssClass);
    else
        cssClass = String.empty;

    $msg.append(String.format("<div{0}>{1}</div>", cssClass, msg));
};

OSI.RSWebNET.MessageSet = function(msgID, msg, cssClass) {
    $RSWeb.MessageClear(msgID);

    $RSWeb.MessageAdd(msgID, msg, cssClass);
};

OSI.RSWebNET.MessageClear = function(msgID, setToNBSPIfNotEmpty) {
    var $msg = $("#" + msgID);
    var html = String.empty;

    if (setToNBSPIfNotEmpty && $msg.html() != String.empty)
        html = "&nbsp;";
    else
        $msg.hide();

    $msg.html(html);
};


$(document).ready(function() {
    $RSWeb.MessageLine = new function() {
        var $msg = $("#lblMessage");

        this.setID = function(msgID) { $msg = $("#" + msgID); };

        this.text = function(msg) {
            if (isNull(msg) || msg == String.empty)
                this.html();
            else
                this.html(escape(msg));
        };
        this.html = function(msg) {
            if ($msg.length == 0)
                return ;

            if (isNull(msg) || msg == String.empty) {
                $msg.html(String.empty);
                $msg.hide();
            }
            else {
                $msg.html(msg);
                $msg.show();
            }
        };
    };

    if ($RSWeb.isLoggedIn && isNotNull(OSI.RSWebNET.strings))
        OSI.RSWebNET.loadStrings();

    // Initialize datepicker defaults
    var dateFields = $(".DatePicker");

    // Initialize all input fields marked with DatePicker class
    OSI.RSWebNET.AttachDatePicker(dateFields);

    if (dateFields.length > 0) {
        // One or more date fields exist on page, so initialize text
        var monthIds = {
            "TxtJanuary": "January",
            "TxtFebruary": "February",
            "TxtMarch": "March",
            "TxtApril": "April",
            "TxtMay": "May",
            "TxtJune": "June",
            "TxtJuly": "July",
            "TxtAugust": "August",
            "TxtSeptember": "September",
            "TxtOctober": "October",
            "TxtNovember": "November",
            "TxtDecember": "December"
        };
        var monthShortIds = {
            "TxtJanuaryShort": "Jan",
            "TxtFebruaryShort": "Feb",
            "TxtMarchShort": "Mar",
            "TxtAprilShort": "Apr",
            "TxtMayShort": "May",
            "TxtJuneShort": "Jun",
            "TxtJulyShort": "Jul",
            "TxtAugustShort": "Aug",
            "TxtSeptemberShort": "Sep",
            "TxtOctoberShort": "Oct",
            "TxtNovemberShort": "Nov",
            "TxtDecemberShort": "Dec"
        };
        var dayIds = {
            "TxtSunday": "Sunday",
            "TxtMonday": "Monday",
            "TxtTuesday": "Tuesday",
            "TxtWednesday": "Wednesday",
            "TxtThursday": "Thursday",
            "TxtFriday": "Friday",
            "TxtSaturday": "Saturday"
        };
        var dayShortIds = {
            "TxtSundayShort": "Sun",
            "TxtMondayShort": "Mon",
            "TxtTuesdayShort": "Tue",
            "TxtWednesdayShort": "Wed",
            "TxtThursdayShort": "Thu",
            "TxtFridayShort": "Fri",
            "TxtSaturdayShort": "Sat"
        };
        var dayMinIds = {
            "TxtSundayMin": "Su",
            "TxtMondayMin": "Mo",
            "TxtTuesdayMin": "Tu",
            "TxtWednesdayMin": "We",
            "TxtThursdayMin": "Th",
            "TxtFridayMin": "Fr",
            "TxtSaturdayMin": "Sa"
        };
        var miscIds = {
            "TxtCalendarNext": "Next",
            "TxtCalendarPrevious": "Previous"
        };
        var allStrings = {};
        $Y.mix(allStrings, monthIds);
        $Y.mix(allStrings, monthShortIds);
        $Y.mix(allStrings, dayIds);
        $Y.mix(allStrings, dayShortIds);
        $Y.mix(allStrings, dayMinIds);
        $Y.mix(allStrings, miscIds);

        OSI.RSWebNET.loadStrings(allStrings, this, function() {
            var monthNames = [];
            $.each(monthIds, function(index, value) {
                monthNames.push($RSWeb.getString(index));
            });
            var monthShortNames = [];
            $.each(monthShortIds, function(index, value) {
                monthShortNames.push($RSWeb.getString(index));
            });
            var dayNames = [];
            $.each(dayIds, function(index, value) {
                dayNames.push($RSWeb.getString(index));
            });
            var dayShortNames = [];
            $.each(dayShortIds, function(index, value) {
                dayShortNames.push($RSWeb.getString(index));
            });
            var dayMinNames = [];
            $.each(dayMinIds, function(index, value) {
                dayMinNames.push($RSWeb.getString(index));
            });

            // Set defaults for all date pickers.
            $.datepicker.setDefaults({
                closeText: $RSWeb.getString("TxtCalendarClose"),
                currentText: $RSWeb.getString("TxtCalendarToday"),
                dayNames: dayNames,
                dayNamesMin: dayMinNames,
                dayNamesShort: dayShortNames,
                monthNames: monthNames,
                monthNamesShort: monthShortNames,
                nextText: $RSWeb.getString("TxtCalendarNext"),
                prevText: $RSWeb.getString("TxtCalendarPrevious")
            });
        });
    }
});

