var _baseUrl = ""; // MUST BE CHANGED BASED ON ENVIRONMENT IF RUNNING WITHIN A VIRTUAL FOLDER (e.g. '/virtualfoldername')
/*
 * Functions required for the proper functioning of the SocialCrm.Framework.Web
 */

/*
* --------------------------------------------------------------------
* jQuery-Plugin - $.download - allows for simple get/post requests for files
* by Scott Jehl, scott@filamentgroup.com
* http://www.filamentgroup.com
* reference article: http://www.filamentgroup.com/lab/jquery_plugin_for_requesting_ajax_like_file_downloads/
* Copyright (c) 2008 Filament Group, Inc
* Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
* --------------------------------------------------------------------
*/

jQuery.download = function(url, data, method) {
    //url and data options required
    if (url && data) {
        //data can be string of parameters or array/object
        data = typeof data == 'string' ? data : jQuery.param(data);
        //split params into form inputs
        var inputs = '';
        jQuery.each(data.split('&'), function() {
            var pair = this.split('=');
            inputs += '<input type="hidden" name="' + pair[0] + '" value="' + decodeURIComponent(pair[1]) + '" />';
        });
        //send request
        jQuery('<form action="' + url + '" method="' + (method || 'post') + '">' + inputs + '</form>')
		.appendTo('body').submit().remove();
    };
};


/*
* Performing general initialisation required by the framework on the page.
*/
function initFramework() {
    // Changing blocking plugin defaults
    if (typeof ($.blockUI) != 'undefined') {
        $.blockUI.defaults.message = '<h3><img src="' + _baseUrl + '/Content/Images/busy-mini-bar.gif" /> Loading...</h3>';
        $.blockUI.defaults.css.border = '0px';
        $.blockUI.defaults.css.backgroundColor = 'transparent';
        $.blockUI.defaults.overlayCSS.backgroundColor = '#E7E7E7';
        $.blockUI.defaults.overlayCSS.opacity = 0.4;
    }

    inlineBlockOptions = { message: '<img src="' + _baseUrl + '/Content/Images/busy.gif" />', css: { textAlign: 'right', width: '90%', top: '0px'} };

    InitialisePanelets();
    initInPlaceEdit();
    initCompactList();
    initTogglers();
}

function intercept() {
var a = 1;
}


function initTogglers() {
    $('.toggler').live('click', function() {
        $($(this).attr('target')).toggle();
        initToggleText($(this));
    });
    $('.slideToggler').live('click', function() {
        $($(this).attr('target')).slideToggle();
        initToggleText($(this));
    });
    $('.classToggler').live('click', function() {
        var cl = $(this).attr('toggleClass');
        var target = $(this).attr('target');
        $(target).each(function() {
            if ($(this).hasClass(cl))
                $(this).removeClass(cl); else
                $(this).addClass(cl);
        });

        initToggleText($(this));
    });

    function initToggleText(toggler) {
        var toggleText = toggler.attr('toggleText');
        if (!isNullOrEmpty(toggleText)) {
            toggler.attr('toggleText', toggler.html());
            toggler.html(toggleText);
        }
    }
}

/**
* Converts a form so that the Submit is done as an Ajax operation.
* Note that the response from a the form submit adress must be in a specific Json format.
* @form: The form or its Id to ajax enable.
* @blockingElement: The element or it's Id that will be blocked during the ajax. If null, the entire screen will be blocked.
* @successCallback: callback function to be called upon a successful save operation.
*/
function CheckBrowserCompatibility() {
    var browserName = $.browser.name;
    var firefox = browserName == "firefox" ? true : false;
    var msie = browserName == "msie" ? true : false;
    var chrome = browserName == "chrome" ? true : false;
    var ver = $.browser.versionNumber;
    var validBrowser = false;

    if (chrome || (firefox && ver >= 3.5) || (msie && ver >= 8))
        validBrowser = true;

    if (!validBrowser)
        $('#divBrowser').show();
}


function InitialiseCollapsibleSideBar() {
    var theCookie = new $.cookie();
    theCookie.get();

    if (theCookie.showLeftNav == 'false') {
        $('#left').hide();
        $('#left-toggle').attr('src', _baseUrl + '/content/images/show-vertical.gif');
    }
    else {
        $('#left').show();
        $('#left-toggle').attr('src', _baseUrl + '/content/images/hide.gif');
    }

    $('#left-toggle').click(function() {
        if ($('#left:visible').length > 0) {
            $('#left').hide();
            $('#left-toggle').attr('src', _baseUrl + '/content/images/show-vertical.gif');
            theCookie.showLeftNav = 'false';
            theCookie.set({ expires: 7, path: '/' });
        }
        else {
            $('#left').show();
            $('#left-toggle').attr('src', _baseUrl + '/content/images/hide.gif');
            theCookie.showLeftNav = 'true';
            theCookie.set({ expires: 7, path: '/' });
        }
        $(window).trigger('resize');
    });
}

/*
*Initialises a form so that it can be posted by Ajax and ensure that behaviour is consistent
*throughout the app e.g. blocking behaviour, handling of validation errors
*/
function MakeAjaxForm(form, blockingElement, successCallback, hasStoredFileProperty, waitingOptions) {
    if (typeof (hasStoredFileProperty) == 'undefined' || hasStoredFileProperty == null)
        hasStoredFileProperty = false;

    var blockingOptions;
    if (waitingOptions == undefined || waitingOptions == null) {
        blockingOptions = { message: '<h3><img src="' + _baseUrl + '/Content/Images/busy-mini-bar.gif" />Saving...</h3>' };
    }
    else {
        if (typeof waitingOptions == 'string')
            blockingOptions = { message: '<h3><img src="' + _baseUrl + '/Content/Images/busy-mini-bar.gif" />' + waitingOptions + '</h3>' };
        else
            blockingOptions = waitingOptions;
    }

    if (typeof (form) == 'string')
        form = $("#" + form);
    else
        form = $(form);

    if (typeof (blockingElement) == 'string')
        blockingElement = $("#" + blockingElement);
    else
        blockingElement = $(blockingElement);

    if (form.length == 0)
        throw 'Target form not supplied or could not be identified.';

    if(hasStoredFileProperty)
        form.attr((this.encoding ? 'encoding' : 'enctype') , 'multipart/form-data');

    form.ajaxForm({
        iframe: hasStoredFileProperty,
        type: "POST",
        dataType: "json",
        beforeSubmit: function() {            
            if (blockingElement) {
                blockingElement.block(blockingOptions);
            }
            else {
                $.blockUI(blockingOptions);
            }
        },
        success: function(result) {
            form.find('.input-validation-error').removeClass('input-validation-error');

            if (result.Success) {
                form.find('div.validation-summary-errors').hide();
                if (successCallback) {
                    successCallback(result);
                }
            }
            else {
                form.find('div.validation-summary-errors ul').remove();
                form.find('div.validation-summary-errors').show();
                form.find('div.validation-summary-errors span').after('<ul></ul>');
                for (var i = 0; i < result.ErrorMessages.length; i++) {
                    var err = result.ErrorMessages[i];
                    form.find('div.validation-summary-errors ul').append('<li>' + err.ErrorMessage + '</li>');
                    form.find('#' + err.PropertyName + ', ' + '#' + err.PropertyName_USER).addClass('input-validation-error');
                }
            }
            if (blockingElement) {
                blockingElement.unblock();
            }
            else {
                $.unblockUI();
            }
        },
        error: function(xhr, textStatus, errorThrown) {
            if (blockingElement) {
                blockingElement.unblock();
            }
            else {
                $.unblockUI();
            }
            alert("Save was unsucessful as an unexpected error occured.");
        }
    });
}


/**
* Opens a dialog box with its content loaded using ajax.
* @dialogId: Id of the element of the dialog box. The element must already have been initialised as a Dialog.
* @contentUrl: The url to get the content to be displayed in the dialog box.
* @title: The title for the dialog box.
*/
function OpenAjaxDialog(dialogId, contentUrl, title) {
    $('#' + dialogId + ' div.dialogContent').load(contentUrl, function() {
        $.unblockUI();
        if (title) {
            $('#' + dialogId).dialog('option', 'title', title);
        }
        $('#' + dialogId).dialog('open');
    });
}

/**
* Makes an Ajax post with specified Url and refreshes a JQgrid table upon success if requested.
*/
function TableAjaxPost(url, refreshTableOnSuccess, tableId) {
    url = AddJsonAttribute(url);
    $.post(url,
        function(data) {
            if (refreshTableOnSuccess) {
                $('#' + tableId).trigger('reloadGrid');
            }
        },
        'json');
}

function DeleteTableItem(url, tableId) {
    if (confirm("Are you sure you wish to PERMANENTLY delete this item?")) {
        TableAjaxPost(url, true, tableId);
    }
}

function InactivateTableItem(url, tableId) {
    if (confirm("Are you sure you wish to delete this item?")) {
        TableAjaxPost(url, true, tableId);
    }
}

function DetachTableItem(url, tableId) {
    if (confirm("Are you sure you wish to remove this item. The item will not be deleted, simply no longer a child?")) {
        TableAjaxPost(url, true, tableId);
    }
}

/**
* Adds a json url attribute to a Url to request that response be returned as Json.
*/
function AddJsonAttribute(url) {
    if (url.toString().indexOf('?') > -1) {
        if (url.toString().indexOf('json=true') > -1) {
            return url; // json attribute already on url
        }
        else {
            return url + '&json=true';
        }
    } else {
        return url + '?json=true';
    }

}

/**
* Attaches a tooltip to the element specified, with tooltip displaying to the left,
* useful when the tooltipe element is close to the right edge of the page.
*/
function AttachToolTipToLeft(id, helpText) {
    var qt = $('#' + id);
    qt.qtip({
        content: helpText,
        style:
                 { width: 400,
                     padding: 3,
                     background: '#F3E7CF',
                     color: '#603A02',
                     textAlign: 'center',
                     border:
                 { width: 1,
                     radius: 5,
                     color: '#885403'
                 },
                     tip: true
                 },
        position: {
            corner: {
                target: 'bottomLeft',
                tooltip: 'topRight'
            }
        }
    })
}

/**
* Attaches a tooltip to the element specified.
*/
function AttachToolTipToRight(id, helpText) {
    var qt = $('#' + id);
    qt.qtip({
        content: helpText,
        style:
                 { width: 400,
                     padding: 3,
                     background: '#6fae6f',
                     color: '#ffffff',
                     textAlign: 'center',
                     border:
                 { width: 1,
                     radius: 5,
                     color: '#3e6e3e'
                 },
                     tip: true
                 }
    })
}

/**
* Clears all elements in the specified form
* If certain elements should not be cleared they should be 
* tagged with the 'dontClear' class.
*/
function ClearForm(form) {
    if (typeof (form) == 'string')
        form = $('#' + form);
    else
        form = $(form);

    form.find(':input').each(function() {
        if (!$(this).hasClass('dontClear')) {
            switch (this.type) {
                case 'password':
                case 'text':
                case 'hidden':
                case 'textarea':
                    $(this).val('');
                    break;
                case 'checkbox':
                case 'radio':
                    $(this).attr('checked', false);
                    //                        $(this).checked = false;
                case 'select-multiple':
                case 'select-one':
                    if ($(this).prop('selectedIndex') > 0) {
                        $(this).prop('selectedIndex', 0).change();
                    }
            }

            if (this.tagName.toLowerCase() == 'select') {
                if ($(this).prop('selectedIndex') > 0) {
                    $(this).prop('selectedIndex', 0).change();
                }
            }
        }
    });
}

/**
* Clears the value of hidden elements within the specified form
* If certain elements should not be cleared they should be 
* tagged with the 'dontClear' class.
*/
function ClearFormHiddenElements(form) {
    if (typeof (form) == 'string')
        form = $('#' + form);
    else
        form = $(form);

    form.find(':input').each(function() {
        switch (this.type) {
            case 'hidden':
                if (!$(this).hasClass('dontClear')) {
                    $(this).val('');
                }
                break;
        }
    });
}

/**
* Clears all elements in the specified form
* If certain elements should not be cleared they should be 
* tagged with the 'dontClear' class.
*/
function readOnlyForm(form) {
    if (typeof (form) == 'string')
        form = $('#' + form);
    else
        form = $(form);

    form.find(':input').each(function() {
        if (!$(this).hasClass('dontClear')) {
            switch (this.type) {
                case 'password':
                case 'text':
                case 'hidden':
                case 'textarea':
                    $(this).attr('readonly', true);
                    break;
                case 'checkbox':
                case 'radio':
                    $(this).attr('readonly', true);
                    //                        $(this).checked = false;
                case 'select-multiple':
                case 'select-one':
                    $(this).attr('readonly', true); 
            }

            if (this.tagName.toLowerCase() == 'select') {
                $(this).attr('readonly', true); 
            }
        }
    });
}

/**
* Serializes the form
*/
function SerializeForm(form) {
    if (typeof (form) == 'string')
        form = $('#' + form);
    else
        form = $(form);

    return { filter: form.serialize() };
}

$.maxZIndex = $.fn.maxZIndex = function(opt) {
    /// <summary>
    /// Returns the max zOrder in the document (no parameter)
    /// Sets max zOrder by passing a non-zero number
    /// which gets added to the highest zOrder.
    /// </summary>    
    /// <param name="opt" type="object">
    /// inc: increment value, 
    /// group: selector for zIndex elements to find max for
    /// </param>
    /// <returns type="jQuery" />
    var def = { inc: 10, group: "*" };
    $.extend(def, opt);
    var zmax = 0;
    $(def.group).each(function() {
        var cur = parseInt($(this).css('z-index'));
        zmax = cur > zmax ? cur : zmax;
    });
    if (!this.jquery)
        return zmax;

    return this.each(function() {
        zmax += def.inc;
        $(this).css("z-index", zmax);
    });
}

/**
* Initialises all elemens panelet which are collapsible.
*/
function InitialisePanelets() {
    $('.panelet.collapsible').each(function() {
        var formId = $(this).closest('form[attachedGrid]').attr('id');
        if (formId) {
            DeserializeFilterFormFromCookie(formId);
        }
    }); 
    
    TogglePaneletTitle($('.panelet.collapsible').find('.paneletTitle'));

    $('.panelet.lazyLoad').find('.paneletTitle').one('click', function() {
        var lazyDiv = $(this).parent().find('div.paneletContent');
        if (lazyDiv) {
            $(lazyDiv).show().block();

            if ($(lazyDiv).hasClass('post')) {
                // Using post to make the request and posting additional information required for the content
                postData = $(lazyDiv).find('input').val();

                $.post($(lazyDiv).attr('src'),
                { postData: postData },
                  function(data) {
                      $(lazyDiv).unblock().css('height', 'auto');
                      $(lazyDiv).empty().append(data);
                      TogglePaneletTitle($(this).parent().find('.paneletTitle'));
                  });
            }
            else {
                // Making simple request for lazy loaded content
                $(lazyDiv).load($(lazyDiv).attr('src'), function() {
                    $(lazyDiv).unblock().css('height', 'auto');
                    TogglePaneletTitle($(this).parent().find('.paneletTitle'));
                });
            }
        }
    });
    

    $('table > tbody .collapsible:not(.collapsed)').children('tr').children('td')
        .prepend('<a style="display: inline-block;" href="javascript:void(0)"><span class="ui-icon ui-icon-circle-triangle-n"></span></a>')
        .toggle(
            function() {
                $(this).parent().parent().next().hide();
                $(this).find('.ui-icon-circle-triangle-n').removeClass('ui-icon-circle-triangle-n').addClass('ui-icon-circle-triangle-s');
                OnTBodyHideShow(false, $(this).closest('tbody'));
            },
            function() {
                $(this).parent().parent().next().show();
                $(this).find('.ui-icon-circle-triangle-s').removeClass('ui-icon-circle-triangle-s').addClass('ui-icon-circle-triangle-n');
                OnTBodyHideShow(true, $(this).closest('tbody'));
            }
        );
            $('table > tbody .collapsible.collapsed').children('tr').children('td')
        .prepend('<a style="display: inline-block;" href="javascript:void(0)"><span class="ui-icon ui-icon-circle-triangle-s"></span></a>')
        .toggle(
            function() {
                $(this).parent().parent().next().show();
                $(this).find('.ui-icon-circle-triangle-s').removeClass('ui-icon-circle-triangle-s').addClass('ui-icon-circle-triangle-n');
                OnTBodyHideShow(true, $(this).closest('tbody'));
            },
            function() {
                $(this).parent().parent().next().hide();
                $(this).find('.ui-icon-circle-triangle-n').removeClass('ui-icon-circle-triangle-n').addClass('ui-icon-circle-triangle-s');
                OnTBodyHideShow(false, $(this).closest('tbody'));
            }
        ).parent().parent().next().hide();
}

function SerializeFormToCookie(formId, cookieId) {
    try {
        var formCookie = new $.cookie();
        formCookie.cookieID = (typeof(cookieId) != 'undefined' && cookieId != null) ? cookieId : formId + "-data";

        var formData = SerializeForm(formId);
        if (typeof (formData.filter) == "string") {
            var formValues = formData.filter.split("&");
            $(formValues).each(function() {
                var par_val = this.split("=");
                if (par_val.length == 2 && par_val[0] != "get" && par_val[0] != "set" && par_val[0] != "cookieID") {
                    formCookie[par_val[0]] = $('#' + par_val[0]).val();
                    if ($('#' + formId + ' #' + par_val[0]).is('select')) {
                        formCookie[par_val[0] + '_text'] = $('#' + par_val[0] + ' option:selected').text();
                    }
                }
            });
        }

        formCookie.set();
    }
    catch (e) {
    }
}

function DeserializeFormFromCookie(formId, cookieId) {
    try {
        var formCookie = new $.cookie();
        formCookie.cookieID = (typeof (cookieId) != 'undefined' && cookieId != null) ? cookieId : formId + "-data";
        formCookie.get();

        for (var prop in formCookie) {
            if (typeof (prop) == 'string' && prop != '' && prop != "get" && prop != "set" && prop != "cookieID") {
                var ctrlId = prop;
                var ctrlVal = formCookie[prop];
                var ctrl = $('#' + formId + ' #' + ctrlId);
                if (ctrl.length == 1) {
                    if ($(ctrl).is('select')) {
                        if ($('#' + ctrlId + ' option[value=' + ctrlVal + ']').length > 0)
                            $(ctrl).val(ctrlVal); else
                            $(ctrl).attr('storedval', ctrlVal);
                    } else
                        $(ctrl).val(ctrlVal);                    
                }
            }
        }
    }
    catch (e) {
    }
}

function GetPaneletNameForCookie(title) {
    var text = $(title).find('span.ui-jqgrid-title').text();
    return 'panel_' + $.trim(text);
}

function GettCollapsibleTBodyNameForCookie(tbody) {
    var text = $(tbody).find('tr:first td:first').text();
    return 'tbody_' + $.trim(text);
}

function SerializeFilterFormToCookie(formId) {
    try {
        var cookieId = GetFilterFormCookieName_Data(formId);
        if (cookieId == '')
            return; 
        SerializeFormToCookie(formId, cookieId);    
    
        var formCookie = new $.cookie();
        formCookie.cookieID = GetFilterFormCookieName_State(formId);

        $('#' + formId + ' .panelet.collapsible .paneletTitle').each(function() {
            formCookie[GetPaneletNameForCookie(this)] = $(this).parent().children('.paneletContent').is(':visible');
        });

        $('#' + formId + ' tbody.collapsible').each(function() {
            formCookie[GettCollapsibleTBodyNameForCookie(this)] = $(this).next().is(':visible'); 
        });

        formCookie.set();
    }
    catch (e) {
    }
}

function GetFilterFormCookieName_Data(formId) {
    var gridId = $('#' + formId).attr('attachedGrid');
    if (!gridId)
        return "";
    return formId + '_' + gridId + '-data';
}
function GetFilterFormCookieName_State(formId) {
    var gridId = $('#' + formId).attr('attachedGrid');
    if (!gridId)
        return "";
    return formId + '_' + gridId + '-state';
}

function DeserializeFilterFormFromCookie(formId) {
    try {
        var cookieId = GetFilterFormCookieName_Data(formId);
        if (cookieId == '')
            return;
        DeserializeFormFromCookie(formId, cookieId);    
    
        var formCookie = new $.cookie();
        formCookie.cookieID = GetFilterFormCookieName_State(formId);
        formCookie.get();

        $('#' + formId + ' .panelet.collapsible .paneletTitle').each(function() {
            var parName = GetPaneletNameForCookie(this);
            if (typeof (formCookie[parName]) != undefined) {
                if (formCookie[parName] == 'true')
                    $(this).parent().children('.paneletContent').show(); else
                    $(this).parent().children('.paneletContent').hide();
            }
        });

        $('#' + formId + ' tbody.collapsible').each(function() {
            var parName = GettCollapsibleTBodyNameForCookie(this);
        
            if (typeof (formCookie[parName]) != undefined) {
                if (formCookie[parName] == 'true')
                    $(this).removeClass('collapsed'); else
                    $(this).addClass('collapsed');
            }
        });
    }
    catch (e) {
    }
}

function GetFilterCriteriaText(formId) {
    try {
        if (typeof (cookieId) == 'undefined')
            cookieId = GetFilterFormCookieName_Data(formId);

        var formCookie = new $.cookie();
        formCookie.cookieID = (typeof (cookieId) != 'undefined' && cookieId != null) ? cookieId : formId + "-data";
        formCookie.get();
        
        var res = '';

        for (var prop in formCookie) {
            if (typeof (prop) == 'string' && prop != '' && prop != "get" && prop != "set" &&
                prop != "cookieID" && formCookie[prop] != -999 && formCookie[prop] != '') {

                if ($('#' + prop).length > 0 && !$('#' + prop).is('[type=hidden]')) {
                    var paramName = $('label[for=' + prop + ']').length > 0 ?
                        $('label[for=' + prop + ']').html() :
                        $('#' + prop).closest('td').prev().text().trim();

                    var paramValue = $('#' + prop).is('select') ?
                        formCookie[prop + '_text'] :
                        formCookie[prop];

                    res = res == '' ? '' : res + ', ';
                    res += '<b>' + paramName + '</b> \'' + paramValue + '\'';
                }
            }
        }
        return res;
    }
    catch (e) {
        return '';
    }
}

function GetFilterFromCookie(formId, cookieId) {
    try {
        if (typeof (cookieId) == 'undefined')
            cookieId = GetFilterFormCookieName_Data(formId);

        var formCookie = new $.cookie();
        formCookie.cookieID = (typeof (cookieId) != 'undefined' && cookieId != null) ? cookieId : formId + "-data";
        formCookie.get();

        var res = {filter:''};

        for (var prop in formCookie) {
            if (typeof (prop) == 'string' && prop != '' && prop != "get" && prop != "set" && prop != "cookieID") {
                if (res.filter != '')
                    res.filter += '&';
                res.filter += prop + '=' + formCookie[prop];
            }
        }
        return res;
    }
    catch (e) {
        return {filter:''};
    }
}

function GetGridStateKeyDirect(url, postData) {
    var stateKey = url;

    if (typeof (postData) != 'undefined') {
        if (typeof (postData.filter) != 'undefined') {

            var values = postData.filter.split("&");
            $(values).each(function() {
                
                var par_val = this.split("=");
                if (par_val.length == 2 && par_val[1] != -999 && par_val[1] != '') {
                    stateKey += '&' + par_val[0] + '=' + par_val[1];
                }
            });
        } 
    }
    return stateKey;
}

function GetGridStateKey(gridId) {
    return GetGridStateKeyDirect($(gridId).getGridParam('url'), $(gridId).getGridParam('postData'));
}

function SerializeGridParamsToCookie(gridId, data) {
    try {
        var cookie = new $.cookie();
        cookie.cookieID = gridId + "-state";

        cookie.stateKey = GetGridStateKey('#' + gridId);
        cookie.page = data.page;
        cookie.sortorder = $('#' + gridId).getGridParam('sortorder');
        cookie.sortname = $('#' + gridId).getGridParam('sortname');

        cookie.set();
    }
    catch (e) {
    }
}

function GetGridParamCookie(gridId, url, postData) {
    try {
        var cookie = new $.cookie();
        cookie.cookieID = gridId + "-state";
        cookie.get();

        var currentKey = GetGridStateKeyDirect(url, postData);
        // return cookie only if currentState = savedState
        if (typeof (cookie.stateKey) != 'undefined' && currentKey == cookie.stateKey)
            return cookie; else 
            return null;
    }
    catch (e) {
        return defValue;
    }
}

function GetParamOrDefault(params, paramName, def) {
    if (typeof (params) == 'undefined' || params == null)
        return def;
    if (typeof (params[paramName]) == 'undefined' || params[paramName] == null)
        return def;
    return params[paramName];
}

function GetFirstNotNull(values) {
    if (typeof (values) == 'undefined' || values == null)
        return null;
    
    $.each(values, function(idx, val) {
        if (typeof(val) != 'undefined' && val != null) {
            return val;
        }
    });
    return null;
}

function OnPaneletHideShow(visible, title) {
    if ($(title).closest('form[attachedGrid]').length == 0)
        return;

    var formId = $(title).closest('form[attachedGrid]').attr('id');
    var cookieId = GetFilterFormCookieName_State(formId);
    if (cookieId == '')
        return;

    var cookie = new $.cookie();
    cookie.cookieID = cookieId;
    cookie.get();
    cookie[GetPaneletNameForCookie(title)] = visible;
    cookie.set();
}

function OnTBodyHideShow(visible, title) {
    if ($(title).closest('form[attachedGrid]').length == 0)
        return;

    var formId = $(title).closest('form[attachedGrid]').attr('id');
    var gridId = $('#' + formId).attr('attachedGrid');
    if (!gridId)
        return;

    var id = formId + '_' + gridId

    var cookie = new $.cookie();
    cookie.cookieID = id + '-state';
    cookie.get();
    cookie[GettCollapsibleTBodyNameForCookie(title)] = visible;
    cookie.set();
}

function TogglePaneletTitle(a) {
    if ($(a).parent().children('.paneletContent').is(':visible')) {
        $(a).toggle(
        function() {
            $(this).parent().children('.paneletContent').hide();
            $(this).children('.ui-icon-circle-triangle-n').removeClass('ui-icon-circle-triangle-n').addClass('ui-icon-circle-triangle-s');
            OnPaneletHideShow(false, a);
        },
        function() {
            $(this).parent().children('.paneletContent').show();
            $(this).children('.ui-icon-circle-triangle-s').removeClass('ui-icon-circle-triangle-s').addClass('ui-icon-circle-triangle-n');
            OnPaneletHideShow(true, a);
        }
    ).find('.ui-icon-circle-triangle-s').removeClass('ui-icon-circle-triangle-s').addClass('ui-icon-circle-triangle-n');
    }
    else {
        $(a).toggle(
        function() {
            $(this).parent().children('.paneletContent').show();
            $(this).children('.ui-icon-circle-triangle-s').removeClass('ui-icon-circle-triangle-s').addClass('ui-icon-circle-triangle-n');
            OnPaneletHideShow(true, a);
        },
        function() {
            $(this).parent().children('.paneletContent').hide();
            $(this).children('.ui-icon-circle-triangle-n').removeClass('ui-icon-circle-triangle-n').addClass('ui-icon-circle-triangle-s');
            OnPaneletHideShow(false, a);
        }
    ).children('.ui-icon-circle-triangle-n').removeClass('ui-icon-circle-triangle-n').addClass('ui-icon-circle-triangle-s');
    }
}


/**
* Sets up cascading drop down lists.
*/
function CascadeDropDowns(parentDropDownId, childDropDownId, sourceUrl, noSelectionText) {
    var stored = $('#' + childDropDownId).attr('storedval');
    
    jQuery('#' + childDropDownId).cascade('#' + parentDropDownId, {
        ajax: {
            url: sourceUrl,
            data: Math.random()
        },
        template: function(item) {
            return "<option value='" + item.value + "'>" + item.text + "</option>";
        }
    }).bind('loaded.cascade', function(e, target) {
        jQuery(this).prepend("<option value='-999' selected='true'>" + noSelectionText + "</option>");
        
        if (typeof (stored) != 'undefined' && stored != -999) {
            var selItem = $('option[value=' + stored + ']', this);
            if (selItem.length == 1) {
                selItem[0].selected = true;
                $(this).attr('storedval', null);
                $(this).trigger('change');
            } else
                jQuery(this).find('option:first')[0].selected = true;
        } else
            jQuery(this).find('option:first')[0].selected = true;
    });

    if (typeof(stored) != 'undefined' &&
        typeof ($('#' + parentDropDownId).val()) != 'undefined' &&
        $('#' + parentDropDownId).val() != null &&
        $('#' + parentDropDownId).val() != -999)
        $('#' + parentDropDownId).trigger('change');
}

function ClearCascade(selector, defaultValue) {
    if (typeof (defaultValue) == 'undefined')
        defaultValue = '-999';
    $(selector).find('option[value!=' + defaultValue + ']').remove();
    $(selector).attr('disabled', 'disabled');
}

/**
* Display the specified Entity Picker Dialog box.
* @dialogId: The configuration name of the entity picker dialog box to display.
* @tableId: Id of the jqgrid table element
* @idElementId: Id of the element to populate with the Id of the selected entity once the user has made a selection.
* @displayElementId: Id of the element to populate with the displayProperty value of the selected entity once the user has made a selection.
* @displayProperty: The name of a property on the entity that will displayed to the user to indicate the selection.
*/
function ShowDialogEntityPicker(pickerDialogName, tableId, idElementId, displayElementId, displayProperty) {
    var dialogId = 'EPD-' + pickerDialogName;

    if (jQuery('#' + dialogId).length == 1) {
        ShowDialog(pickerDialogName, tableId, idElementId, displayElementId, displayProperty);
    }
    else {
        var url = _baseUrl + '/Framework/EntityPickerDialogAjax/' + pickerDialogName;

        $('<div id="' + pickerDialogName + '-Wrap" ></div>').appendTo('body');
        $.blockUI({ message: '<h4><img src="' + _baseUrl + '/Content/Images/busy-mini-bar.gif" /> Loading...</h4>', baseZ: 9999 });
        $('#' + pickerDialogName + '-Wrap').load(url, function() {
            ShowDialog(pickerDialogName, tableId, idElementId, displayElementId, displayProperty);
            PrepareFilterDefaultButtons();
            $.unblockUI();
        });
    }

    function ShowDialog(pickerDialogName, tableId, idElementId, displayElementId, displayProperty) {
        var dialogId = 'EPD-' + pickerDialogName;
        jQuery('#' + tableId).setGridParam({
            ondblClickRow: function(rowid, iRow, iCol, e) {
                if (rowid != null) {
                    var rowData = $('#' + tableId).getRowData(rowid);
                    $('#' + idElementId).val(rowData.Id);
                    $('#' + displayElementId).val(eval('rowData.' + displayProperty));
                    $('#' + dialogId).dialog('close');
                };
            }
        });
        $('#' + dialogId + '-ClearSelection').click(function() {
            $('#' + idElementId).val('');
            $('#' + displayElementId).val('');
            $('#' + dialogId).dialog('close');
        })
        
        $('#' + dialogId).dialog('open');
    }

}


/**
* Display the specified Entity Picker Dialog box.
* @context: the context from which should be able to derive which entity the history is required for
* by looking for the context attributes.
* @entityFullyQualifiedId: A fully qualified identifier for the entity for which history inforamtion is required.
*/
function showEntHistoryDialog(context, params) {
    var url = buildUrlFromContextAttributes(context, 'EntityHistoryDialogAjax', 'AuditLogEntry', params);
    showAjaxDialog(url, true)
}


/**
* Updates the contents of an element with fresh data from the server.
* @el - the element to be refreshed. 
* The information on how/what to get from the server should be specified using the various 'context' attributes 
* on the element itself or its ancestors.
*/
function refreshContent(el, block) {
    try {
        var url = buildUrlFromContextAttributes(el, 'fragment');

        if (isNullOrEmpty(block) || block) {
            $(el).block(inlineBlockOptions);
        }

        var collapsibleState = getCollapsibleState();

        $(el).load(url, function() {

            restoreCollapsibleState(collapsibleState);

            if (isNullOrEmpty(block) || block) {
                $(el).unblock();
            }
            $(el).fadeIn('slow');

        });
    }
    catch (e) { }
    
    function getCollapsibleState(container) {
        var res = $.map($('.slideToggler, .classToggler, .toggler', container), function(element) {
            var target = $(element).attr('target');
            var value = $(element).is('.classToggler') ? $(target).hasClass($(target).hasClass('toggleClass')) : $(target).is(':visible');

            return {
                state: $(element).attr('state'),
                toggleClass: $(element).attr('toggleClass'),
                target: target, 
                value: value
            };
        });
        return res;
    }
    function restoreCollapsibleState(state) {
        $.each(state, function() {
            if (!isNullOrEmpty(this.toggleClass)) {
                $(this.target).toggleClass(this.toggleClass, this.value);
            }
            else {
                $(this.target).toggle(this.value);
            }

            if (this.value) {
                var toggler = $('.slideToggler,.classToggler,.toggler[target=' + this.target + ']');

                var toggleText = $(toggler).attr('toggleText');
                $(toggler).attr('toggleText', toggler.html());
                $(toggler).html(toggleText);
            }
            
        });
    }
}

/* fix for DatePickers inside the dialog (to prevent automatically DatePicker opening) */
function DisableDatePickers(dialog) {
    $(dialog).find('.hasDatepicker').datepicker('disable').addClass('needEnable');
}
function EnableDatePickers(dialog) {
    $(dialog).find('.hasDatepicker.needEnable').datepicker('enable').removeClass('needEnable');
}

/**
* Display the content retreived from the specified Url in a dialog box.
* @dialogUrl: The url which will provide the dialog content.
* The content returned must include a div element at the root level with a 'dialog-id' attribute whose value
* is the Id of the element already initiatlised using the dialog plugin.
* This is required otherwise does not seem to be possible to find the dialog div very easily as it gets manupulated by the dialog plugin.
* This is most easily implemented by having an action which simply renders the AjaxDialog or AjaxFormDialog shared control.
* @alwaysRefresh: if true will always retreive fresh content from the server, else will simply
* open previously retreived dialog if available
* @onClose: callback to execute when the form closes.
*/
function showAjaxDialog(dialogUrl, alwaysRefresh, onClose) {
    var idKey = "showAjaxDialogId:" + dialogUrl;
    var wrapperId = $('body').data(idKey);

    if (wrapperId == null) {
        wrapperId = generateUniqueID();
        $('body').data(idKey, wrapperId);
    }

    if (jQuery('#' + wrapperId).length == 1) {
        var dialogId = $('#' + wrapperId).find('[dialog-id]').attr('dialog-id');        
        if (!alwaysRefresh) {
            DisableDatePickers('#' + dialogId);
            $('#' + dialogId).dialog('open');
            return;
        }
        else {
            $('#' + dialogId).dialog('destroy');
            $('#' + dialogId).remove();
            $('#' + wrapperId).empty();
        }
    }
    else {
        $('<div id="' + wrapperId + 'out" ><div id="' + wrapperId + '" ></div></div>').appendTo('body');
    }
    
    $.blockUI({ message: '<h4><img src="' + _baseUrl + '/Content/Images/busy-mini-bar.gif" /> Loading...</h4>', baseZ: 9999 });
    $('#' + wrapperId).load(dialogUrl, function(response, status) {
        var dialogId = $(this).find('div[dialog-id]').attr('dialog-id');
        if (dialogId == null)
            throw 'Configuration error: Name of the dialog could not be found.';

        if (onClose != null && onClose != undefined) {
            $('#' + dialogId).dialog({ close: onClose });
        }

        DisableDatePickers('#' + dialogId);
        $('#' + dialogId).dialog({ open: function(event, ui) { EnableDatePickers('#' + dialogId); } });

        $('#' + dialogId).dialog('open');
        $.unblockUI();
    });
}

/*
* Clears any standard validation messages and clears all the input and select elements from the form.
* Only input elements with dontClear class are not cleared.
*/
function clearEntityEditForm(form) {
    if (typeof(form) == 'string')
        var entityForm = $('#' + id);
    else
        var entityForm = $(form);
        
    entityForm.find('div.validation-summary-errors').hide();
    entityForm.find('.input-validation-error').removeClass('input-validation-error');
    ClearForm(entityForm);
}

function PrepareFilterDefaultButtons() {
    $('form[id$="-FilterForm"]').keypress(function(e) {
        if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) {
            button = $("input[type=button][name='Search']:first", $(this));

            if ($(button).length == 1) {
                $(button).click();
                return false;
            } else
                return true;

        } else {
            return true;
        }
    });
   
}

function GetUserSetting(settingName, successHandler) {
    $.ajax({
        url: _baseUrl + '/Framework/GetUserSetting/',
        dataType: "json",
        type: 'GET',
        data: { settingName: settingName, cachefix: Math.random() },
        success: function(json) {
            if (typeof (successHandler) != 'undefined')
                successHandler(json);
        }
    });
}

function SetUserSetting(settingName, value, successHandler) {
    $.ajax({
        url: _baseUrl + '/Framework/SetUserSetting/',
        type: 'GET',
        data: { settingName: settingName, value: value, cachefix: Math.random() },
        dataType: "json",
        success: function(json) {
            if (typeof (successHandler) != 'undefined')
                successHandler(json);
        }
    });
}

function HandleGridComplete() {
    gridCompletedCount++;
    if (gridCompletedCount == gridCount)
        RemoveAutoLayoutFixed();
}
function RemoveAutoLayoutFixed() {
    $('.autolayoutfixed').css('table-layout', '').removeClass('autolayoutfixed');
}

/*
*   Some common fixes on page load
*/
var gridCount = 0;
var gridCompletedCount = 0;
$(function() {
    PrepareFilterDefaultButtons();
    
    //workaround for fixed layout & jqGrids
    gridCount = $('.JqGridDataTableControl').length;
    if (gridCount == 0)
        RemoveAutoLayoutFixed();
});

/*
*  Generates a unique client site Id.
*/
var uniqueCounter = 0;
function generateUniqueID() {
    uniqueCounter++;
    return 'frwkclnt' + uniqueCounter.toString();
}

/*********************************  Start - In place editing functions ***********************************
/*
* Initialises the InPlace editing functionality on the page.
*/
function initInPlaceEdit() {
    //$('.inplc:not([init]) .inplcEdt').hide();

    $('.inplcVw').live('click', function() {
        beginInPlaceEdit(this);
    });

    if ($.browser.name == "firefox" || $.browser.name == "msie") {
        $('.inplcEdt input, .inplcEdt select').live('keypress', function(event) {
            if (event.keyCode == 13) {
                saveInPlaceEdit(this);
            }
            else if (event.keyCode == 27) {
                cancelInPlaceEdit(this);
            }
        });
    }
    else {
        $('.inplcEdt input, .inplcEdt select').live('keydown', event, function() {
            if (event.keyIdentifier == 'Enter') {
                saveInPlaceEdit(this);
            }
            else if (event.keyIdentifier == 'U+001B') {
                cancelInPlaceEdit(this);
            }
        });
    }

    $('.inplcCnclBtn').live('click', function() {
        cancelInPlaceEdit(this);
    });
    $('.inplcSvBtn').live('click', function() {
        saveInPlaceEdit(this);
    });
}

function beginInPlaceEdit(o) {
    var inplc = $(o).closest('.inplc')
    $(inplc).find('.inplcVw').hide();
    var inplcEdt = $(inplc).find('.inplcEdt').show();
    $(inplcEdt).find('input, select').first().focus();
    $(inplcEdt).find('.inplcSvBtn, .inplcCnclBtn').show();
}
function cancelInPlaceEdit(o) {
    var inplc = $(o).closest('.inplc');
    $(inplc).find('.inplcEdt').hide();
    $(inplc).find('.inplcVw').show();
    inplc.find('.inplcSvBtn, .inplcCnclBtn').hide();
    $(inplc).find('.inlnErrMsg').remove();
}
function saveInPlaceEdit(o) {
    var inplc = $(o).closest('.inplc');
    $(inplc).block(inlineBlockOptions);

    var postUrl = buildUrlFromContextAttributes($(inplc), 'SaveOrUpdateAjax');

    $.post(postUrl, $(inplc).find('input, select, textarea').serialize(),
        function(data) {
            if (data.Success) {
                if (data.DisplayValue == null || data.DisplayValue.length == 0) {
                    var noValText = inplc.find('[noVal]').attr('noVal');
                    if (noValText == undefined || noValText == '')
                        noValText = '-';
                    var noValHtml = '<span class="noValue">' + noValText + '</span>';
                    inplc.find('.inplcVwVal').html(noValHtml);
                }
                else {
                    inplc.find('.inplcVwVal').html(data.DisplayValue);
                }

                //Checking if need to refresh any other controls.
                var refreshSelector = inplc.attr('cascade');
                if (refreshSelector != undefined && refreshSelector.length > 0) {
                    $(refreshSelector).each(function(index, el) {
                        refreshContent(el);
                    });
                }

                cancelInPlaceEdit(o);
            }
            else {
                $(inplc).find('.inlnErrMsg').remove();
                inplc.append('<div class="inlnErrMsg" >' + data.ErrorMessages[0].ErrorMessage + '</div>');
            }
            inplc.unblock();
        });
}
/*********************************  End - In place editing functions ***********************************/

/*****************************************************************************************
                      Start - ListControl related functions                                   */
/**
* Makes an Ajax post with specified Url and refreshes a JQgrid table upon success if requested.
*/
function removeListItem(item, url) {
    var listControl = $(item).closest('.itemListControl');
    var listItem = $(item).closest('.liChild');
    url = AddJsonAttribute(url);
    $(listItem).block(inlineBlockOptions);
    $.post(url,
            function(data) {
                if (data.Success) {
                    listItem.fadeOut('slow', function() {
                        $(listItem).remove();
                        formatList(listControl);
                    });
                }
                $(listItem).unblock();
            },
            'json');
}
function deleteListItem(item, url) {
    if (confirm("Are you sure you wish to PERMANENTLY delete this item?")) {
        if (isNullOrEmpty(url))
            url = buildUrlFromContextAttributes(item, 'delete');
        removeListItem(item, url);
    }
}
function inactivateListItem(item, url) {
    if (confirm("Are you sure you wish to delete this item?")) {
        if (isNullOrEmpty(url))
            url = buildUrlFromContextAttributes(item, 'inactivate');
        removeListItem(item, url);
    }
}
function detachListItem(item, url) {
    if (confirm("Are you sure you wish to remove this item. The item will not be deleted, simply no longer a child?")) {
        if (isNullOrEmpty(url))
            url = buildUrlFromContextAttributes(item, 'detach');
        removeListItem(item, url);
    }
}
/**
* function to call during initiasation or after any changes to a list control are made (
* e.g. addition or removal of an item) to perform any 'clean-up' activities and format the control appropriately.
*/
function formatList(list) {
    var emptyDiv = $(list).children('.clEmpty');
    if (emptyDiv.length == 0)
        emptyDiv = $(list).children().children('.clEmpty');
    var listUl = $(list).find('ul.cl');
    var listDiv = $(list).find('.clItems');
    if (listDiv.length == 0)
        listDiv = listUl;

    if (listUl.children().length == 0 && emptyDiv.length == 1) {
        if (emptyDiv.is(':hidden')) {
            listDiv.fadeOut('fast', function() { emptyDiv.fadeIn('slow'); });
            listDiv.hide(); // still need this despite the faseOut()
        }
        //        else {
        //            listDiv.show();
        //        }
        //
    }
    else {
        var listDiv = $(list).find('.clItems');
        if (listDiv.is(':hidden')) {
            if (emptyDiv.length == 1) {
                emptyDiv.fadeOut('fast', function() { listDiv.fadeIn('slow') });
                emptyDiv.hide();
            }
            else {
                listDiv.show();
            }
        }
        if (emptyDiv.length == 1) {
            emptyDiv.hide();
        }
    }

    // Add/remove last Item class
}
/**
* Initialises a control used to add new items to a list.
*/
function initListItemCreateControl(control, list, onSuccess, addPosition) {
    var form = $(control).find('form');
    MakeAjaxForm(form,
    form,
    function(result) {
        clearEntityEditForm(form);    // Clear the form
        var newItem = $('<li class="liChild" style="display:none">' + result.DisplayValue + '</li>');
        var listUl = $(list).find('ul.cl').first();

        if (addPosition == 'top')
            listUl.prepend(newItem);
        else
            listUl.append(newItem);
            
        newItem.fadeIn(1000);
        formatList(list);
        $(form).find('input, select, textarea').first().focus();
        if (onSuccess)
            onSuccess(form);
    },
    false,
    inlineBlockOptions);
}

/*                      End - ListControl related functions
***************************************************************************************/


function initCompactList() {
    $('.compactListOfEntitiesControl>.addItem').live('click', function(data) {
        var listControl = $(data.target).closest('.compactListOfEntitiesControl');
        listControl.find('.addItem').hide();
        listControl.find('.clEmpty').hide();
        var addSection = listControl.find('.addListItem');
        $(addSection).find('.inlnErrMsg').remove();
        ClearForm($(addSection));
        addSection.show();
        $(addSection).find('input, select, textarea').first().focus();
    });

    $('.compactListOfEntitiesControl>.addListItem .saveAdd').live('click', function(data) {
        saveCompactListAdd(data.target);
    });

    $('.compactListOfEntitiesControl>.addListItem .cancelAdd').live('click', function(data) {
        cancelCompactListAdd(data.target);
    });

    if ($.browser.name == "firefox" || $.browser.name == "msie") {
        $('.compactListOfEntitiesControl>.addListItem input, .compactListOfEntitiesControl>.addListItem select').live('keypress', function(event) {
            if (event.keyCode == 13) {
                saveCompactListAdd(this);
            }
            else if (event.keyCode == 27) {
                cancelCompactListAdd(this);
            }
        });
    }
    else {
        $('.compactListOfEntitiesControl>.addListItem input, .compactListOfEntitiesControl>.addListItem select').live('keydown', event, function() {
            if (event.keyIdentifier == 'Enter') {
                saveCompactListAdd(this);
            }
            else if (event.keyIdentifier == 'U+001B') {
                cancelCompactListAdd(this);
            }
        });
    }

    $('.compactListOfEntitiesControl').each(function() { cancelCompactListAdd(this); });
}

function cancelCompactListAdd(context) {
    var listControl = $(context).closest('.compactListOfEntitiesControl');
    listControl.find('.addListItem').hide();
    listControl.find('.addItem').show();
    if (listControl.find('ul.cl').children().length == 0) {
        listControl.find('.clEmpty').show();
        listControl.find('.clItems').hide();
    }
    else {
        listControl.find('.clEmpty').hide();
        listControl.find('.clItems').show();

        listControl.find('.clItems ul li .first').removeClass('first');
        listControl.find('.clItems ul li .last').removeClass('last');
        listControl.find('.clItems ul li').first().addClass('first');
        listControl.find('.clItems ul li').last().addClass('last');
    }
    
    
}

function saveCompactListAdd(context) {
    var listControl = $(context).closest('.compactListOfEntitiesControl');
    var addSection = listControl.find('.addListItem');
    addSection.block(inlineBlockOptions);

    var postUrl = buildUrlFromContextAttributes(addSection, 'SaveOrUpdateAjax', null, { isnew: true });

    $.post(postUrl, $(addSection).find('input, select, textarea').serialize(),
        function(data) {
            if (data.Success) {
                var newItem = $('<li class="liChild" style="display:none">' + data.DisplayValue + '</li>');
                listControl.find('ul.cl').append(newItem);
                newItem.fadeIn(1000);

                //Checking if need to refresh any other controls.
                var refreshSelector = listControl.attr('cascade');
                if (refreshSelector != undefined && refreshSelector.length > 0) {
                    $(refreshSelector).each(function(index, el) {
                        refreshContent(el);
                    });
                }

                cancelCompactListAdd(context);
            }
            else {
                $(addSection).find('.inlnErrMsg').remove();
                addSection.prepend('<div class="inlnErrMsg" >' + data.ErrorMessages[0].ErrorMessage + '</div>');
            }
            addSection.unblock();
        });
}



/************************************************************************************
                             Start - General Entity API                             */

/**
* Deletes the entity in the specified context.
* @context: element providing sufficient context to allow the entity to be deleted to be identified.
* @refreshSelector: a valid jquery selector expression identifying all the elements to be refereshed upon successful delete.
* @onComplete: a callback to be executed after successful delete and refresh.
**/
function deleteEnt(context, refreshSelector, onComplete) {
    if (confirm("Are you sure you wish to PERMANENTLY delete this item?")) {
        var url = buildUrlFromContextAttributes(context, 'delete');
        $.post(url, function() {
            if (!isNullOrEmpty(refreshSelector)) {
                refreshContent($(refreshSelector));
            }
            if (!isNullOrEmpty(onComplete)) {
                onComplete();
            }
        });
    }
}
/**
* Inactivates the entity in the specified context.
* @context: element providing sufficient context to allow the entity to be deleted to be identified.
* @refreshSelector: a valid jquery selector expression identifying all the elements to be refereshed upon successful delete.
* @onComplete: a callback to be executed after successful delete and refresh.
**/
function inactivateEnt(context, refreshSelector, onComplete) {
    if (confirm("Are you sure you wish to delete this item?")) {
        var url = buildUrlFromContextAttributes(context, 'inactivate');
        $.post(url, function() {
            if (!isNullOrEmpty(refreshSelector)) {
                refreshContent($(refreshSelector));
            }
            if (!isNullOrEmpty(onComplete)) {
                onComplete();
            }
        });
    }
}
/*                      End - ListControl related functions
***************************************************************************************/

/**
* Contructs a url from the various special 'context' attributes which may have been applied.
* @action: the name of the action to call.
* @controller: the name of the controller to call.
* @params: an object whose properties will be added as the query string to the url.
*/
function buildUrlFromContextAttributes(el, action, controller, params) {
    var att;

    if (isNullOrEmpty(params)) {
        params = {};
    }

    if (isNullOrEmpty(params.entId))
        params.entId = $(el).attr('entId');
    if (isNullOrEmpty(params.entId))
        params.entId = $(el).closest('[entId]').attr('entId');
    if (isNullOrEmpty(params.entId))
        params.entId = $('#Id').val();
    if (isNullOrEmpty(params.entId))
        throw 'Could not identify the entity id. Ensure either the "entId" attribute is used or a Hidden input with Id';

    if (isNullOrEmpty(params.entType))
        params.entType = $(el).attr('enttype');
    if (isNullOrEmpty(params.entType))
        params.entType = $(el).closest('[enttype]').attr('enttype');

    if (isNullOrEmpty(controller))
        controller = $(el).closest('[controller]').attr('controller');

    if (isNullOrEmpty(action))
        action = $(el).closest('[action]').attr('action');
    if (isNullOrEmpty(action))
        throw 'An action needs to be specified.';

    if (isNullOrEmpty(params.idPrefix)) {
        att = $(el).closest('[idPrefix]').attr('idPrefix');
        if (!isNullOrEmpty(att))
            params.idPrefix = att;
    }

    if (isNullOrEmpty(params.json)) {
        params.json = true; // Defaulting to json request
    }    

    if (isNullOrEmpty(params.displayControl)) {
        att = $(el).attr('displayControl');     // This attribute must be on the exact element not on the ancestors to apply
        if (!isNullOrEmpty(att))
            params.displayControl = att;
    }
        
    var url;
    if (isNullOrEmpty(controller))
        url = '../' + action + '/?' + $.param(params);
    else
        url = '../../' + controller + '/' + action + '/?' + $.param(params);

    return url;
}

function isNullOrEmpty(s) {
    return (s == undefined || s === null || s.length == 0);
}

/**
* Finds an element using it's 'local id' and from an element in the same context.
* The local id is the last portion of an id without the id context/prefix.
* The id context and prefix is added on the server side to avoid id name clashes.
*/
function findByLocalId(context, localId) {
    var prefix = $(context).closest('[idprefix]').attr('idprefix');
    if (isNullOrEmpty(prefix))
        var fullId = localId;
    else
        var fullId = prefix + '-' + localId;

    return context.find('#' + fullId);
}

function exportTableToExcel(configName, params) {
    var url = _baseUrl + '/JqGridDataTableControl/Excel/' + configName
    //$.post(url, params);
    $.download(url, params, 'POST');
}

/* ListOfEntities control */

var pagerTypes = { 'Classic': 'Classic', 'ShowMore': 'ShowMore', 'ShowMoreOnScroll': 'ShowMoreOnScroll' };

function loeLoadList(listId, forceReplace) {
    var runtimeId = $('#loe_runtimeid_' + listId).val();
    var page = Number($('#loe_page_' + listId).val());
    var pageSize = Number($('#loe_pagesize_' + listId).val());
    var idPrefix = $('#loe_idprefix_' + listId).val();
    var pagerType = $('#loe_pagertype_' + listId).val();
    var sortField = $('#loe_sortfield_' + listId).val();
    var sortOrder = $('#loe_sortorder_' + listId).val();
    var doAppend = pagerType != pagerTypes.Classic && !forceReplace;
    $('#loe_loading_' + listId).show();
    var dataSourceUrl = _baseUrl + '/ListOfEntities/List/' + runtimeId;
    $.get(dataSourceUrl,
        $.extend({
            page: page,
            pageSize: pageSize,
            idPrefix: idPrefix,
            sidx: sortField,
            sord: sortOrder
        }, SerializeForm(idPrefix)),
    function(result) {
        $('#loe_loading_' + listId).hide();
        if (doAppend)
            $('#' + listId).append(result.Content);
        else {
            if (pagerType == pagerTypes.Classic && result.LastFetchSize == 0) {
                // No data. Try previous page
                if (page > 1) {
                    $('#loe_page_' + listId).val(page - 1);
                    loeLoadList(listId);
                }
            }
            $('#' + listId).html(result.Content);
        }
        $('#loe_totalpages_' + listId).val(Math.floor(result.TotalCount / pageSize) + (result.TotalCount % pageSize > 0 ? 1 : 0));
        $('#loe_totalcount_' + listId).val(result.TotalCount);
        // Update pager
        loeRefreshPager(listId, result.LastFetchSize, result.TotalCount);
        // Update "x-y of z" labels
        loeUpdateLabels(listId, result.LastFetchSize, result.TotalCount);
    });
}

function loeInitPager(listId) {
    var pagerType = $('#loe_pagertype_' + listId).val();
    switch (pagerType) {
        case pagerTypes.ShowMore:
            $('#loe_show_more_' + listId).click(function() {
                $('#loe_page_' + listId).val(Number($('#loe_page_' + listId).val()) + 1);
                loeLoadList(listId);
            });
            break;
        case pagerTypes.ShowMoreOnScroll:
            $('#loe_show_more_' + listId).click(function() {
                $('#loe_page_' + listId).val(Number($('#loe_page_' + listId).val()) + 1);
                loeLoadList(listId);
            });
            $(window).scroll(function() {
                if ($(window).scrollTop() >= $(document).height() - $(window).height() - 10) {
                    $('#loe_page_' + listId).val(Number($('#loe_page_' + listId).val()) + 1);
                    loeLoadList(listId);
                }
            });
            break;
    }
}

function loeSortList(listId, sortField, sortOrder) {
    var pagerType = $('#loe_pagertype_' + listId).val();
    $('#loe_sortfield_' + listId).val(sortField);
    $('#loe_sortorder_' + listId).val(sortOrder);
    // Move to 1st page for ShowMore lists
    if (pagerType != pagerTypes.Classic)
        $('#loe_page_' + listId).val('1');
    loeLoadList(listId, true);
}

function loeShowAll(listId) {
    var pagerType = $('#loe_pagertype_' + listId).val();
    if (pagerType == pagerTypes.Classic)
        return;
    $('#loe_page_' + listId).val(1);
    $('#loe_pagesize_' + listId).val($('#loe_totalcount_' + listId).val());
    loeLoadList(listId, true);
}

function loeNumberClick(sender, listId) {
    $('#loe_page_' + listId).val($(sender).attr('page'));
    loeLoadList(listId);
    return false;
}

function loeRefreshPager(listId, lastFetchSize, totalCount) {
    var pagerType = $('#loe_pagertype_' + listId).val();
    var pageSize = Number($('#loe_pagesize_' + listId).val());
    var page = Number($('#loe_page_' + listId).val());
    var pagerRange = Number($('#loe_pagerrange_' + listId).val());
    var container = $('#' + listId + '-Pager');
    switch (pagerType) {
        case pagerTypes.Classic:
            container.html('');
            var totalPages = Math.floor(totalCount / pageSize) + (totalCount % pageSize > 0 ? 1 : 0);
            // Prev button
            if (page > 1)
                container.append($('<a/>').attr('href', '#prev').attr('class', 'loe_prev').attr('page', page - 1).attr('onclick', 'return loeNumberClick(this, "' + listId + '")').html($('#loe_prevtext_' + listId).val()));
                container.append('&nbsp;');
            // Numeric buttons
            if (totalCount > pageSize) {
                // Render 1st page
                container.append($('<a/>').attr('href', '#' + listId + '_1').attr('class', 'loe_number').attr('page', 1).attr('onclick', 'return loeNumberClick(this, "' + listId + '")').html('1'));
                container.append('&nbsp;');
                // Render '...' if required
                if (page > pagerRange + 2) {
                    container.append('...&nbsp;');
                }
                // Render numbers
                var startPage = page - pagerRange < 3 ? 2 : (page - pagerRange);
                var endPage = page + pagerRange > (totalPages - 2) ? (totalPages - 1) : (page + pagerRange);
                for (var currentPage = startPage; currentPage <= endPage; currentPage++) {
                    container.append($('<a/>').attr('href', '#' + listId + '_' + currentPage).attr('class', 'loe_number').attr('page', currentPage).attr('onclick', 'return loeNumberClick(this, "' + listId + '")').html(currentPage));
                    container.append('&nbsp;');
                }
                if (page < totalPages - pagerRange - 1) {
                    container.append('...&nbsp;');
                }
                // Render last page
                container.append($('<a/>').attr('href', '#' + listId + '_' + totalPages).attr('class', 'loe_number').attr('page', totalPages).attr('onclick', 'return loeNumberClick(this, "' + listId + '")').html(totalPages));
                container.append('&nbsp;');
            }
            // Next button
            if (totalPages > page)
                container.append($('<a/>').attr('href', '#next').attr('class', 'loe_next').attr('page', page + 1).attr('onclick', 'return loeNumberClick(this, "' + listId + '")').html($('#loe_nexttext_' + listId).val()));
            break;
        case pagerTypes.ShowMore:
        case pagerTypes.ShowMoreOnScroll:
            // Hide the button when there are no more records to show
            if ((page - 1) * pageSize + lastFetchSize >= totalCount)
                $('#loe_show_more_' + listId).hide();
            else
                $('#loe_show_more_' + listId).show();
            break;
    }
}

function loeUpdateLabels(listId, lastFetchSize, totalCount) {
    // Update "x-y of z" labels
    var pagerType = $('#loe_pagertype_' + listId).val();
    var pageSize = Number($('#loe_pagesize_' + listId).val());
    var page = Number($('#loe_page_' + listId).val());
    $('#loe_label_from_' + listId).html(pagerType == pagerTypes.Classic ? (page - 1) * pageSize + 1 : totalCount == 0 ? 0 : 1);
    $('#loe_label_to_' + listId).html((page - 1) * pageSize + lastFetchSize);
    $('#loe_label_of_' + listId).html(totalCount);
}

/* End of ListOfEntities control */
