/* Minification failed. Returning unminified contents.
(3896,10-11): run-time error JS1014: Invalid character: `
(3896,11-12): run-time error JS1195: Expected expression: <
(3896,240-241): run-time error JS1195: Expected expression: <
(3896,257-258): run-time error JS1014: Invalid character: `
(3897,2-3): run-time error JS1002: Syntax error: }
(3898,10-11): run-time error JS1014: Invalid character: `
(3898,11-12): run-time error JS1195: Expected expression: <
(3898,108-109): run-time error JS1197: Too many errors. The file might not be a JavaScript file: $
 */
/*
  _____   _                 _       _   ____        ____            __                   _   _         
 |  ___| | |   ___  __  __ (_)     | | / ___|      |  _ \    ___   / _|   __ _   _   _  | | | |_   ___ 
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | | | |  / _ \ | |_   / _` | | | | | | | | __| / __|
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |_| | |  __/ |  _| | (_| | | |_| | | | | |_  \__ \
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |____/   \___| |_|    \__,_|  \__,_| |_|  \__| |___/
                                                                                                       
*/

FlexiJS.Defaults = {};

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/

FlexiJS.Defaults.AnimationDuration = {Fast:50, Standard: 200, Slow: 600};
FlexiJS.Defaults.DatePicker = {MinDate: new Date(1900, 0, 1), MaxDate: new Date(2099, 11, 31)};

/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/


;/*
  _____   _                 _       _   ____        ____                                                
 |  ___| | |   ___  __  __ (_)     | | / ___|      | __ )   _ __    ___   __      __  ___    ___   _ __ 
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      |  _ \  | '__|  / _ \  \ \ /\ / / / __|  / _ \ | '__|
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |_) | | |    | (_) |  \ V  V /  \__ \ |  __/ | |   
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |____/  |_|     \___/    \_/\_/   |___/  \___| |_|   
                                                                                                        
        Browser related javascript functions

        Requirements:
            None
*/

FlexiJS.Browser = new Object();
FlexiJS.Browser.GoogleAnalytics = new Object();
FlexiJS.Browser.Hotjar = new Object();
FlexiJS.Browser.UnsupportedBrowsers = new Object();

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/

FlexiJS.Browser.Browsers = {
    InternetExplorer: 'ie',
    Edge: 'eg',
    Chrome: 'crm',
    Firefox: 'ff',
    Safari: 'sfr',
    GenericWebkit: 'wbk',
    Unknown: 'unknown'
};

FlexiJS.Browser.OS = {
    Windows: 'win',
    Mac: 'mac',
    AppleDevice: 'idevice',
    Linux: 'lin'
};

/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/

FlexiJS.Browser.GetBrowserInfo = function () {
    // Description: Returns the info of the current users browser
    // Created: 13/03/2017, v3.15.1 - JD
    var returnData = { browser: '', version: '', os: '', useragent: '' + window.navigator.userAgent.toLowerCase() };
    
    if (returnData.useragent.indexOf('firefox', 0) !== -1) {
        // firefox
        var versionInfo = returnData.useragent.match(/firefox\/\d\d?\.\d/);
        if (versionInfo == null) versionInfo = '';
        returnData.version = versionInfo.toString().replace(/\./g, '').replace(/firefox\//, '');

        // checking if the version is 1\d.something
        if ((returnData.version.indexOf('1') === 0) && (returnData.version.length > 3)) { 
            returnData.version = returnData.version.substring(0, 3);
        } else {
            returnData.version = returnData.version.substring(0, 2);
        }
        
        returnData.browser = FlexiJS.Browser.Browsers.Firefox;
    } else if (returnData.useragent.indexOf('msie', 0) !== -1) {
        // Internet Explorer
        returnData.browser = FlexiJS.Browser.Browsers.InternetExplorer;
        returnData.os = FlexiJS.Browser.OS.Windows;
        var versionInfo = returnData.useragent.match(/msie[ ]\d{1,2}/);
        if (versionInfo == null) versionInfo = '';
        returnData.version = versionInfo.toString().replace(/msie[ ]/, '');

    } else if (returnData.useragent.indexOf('edge', 0) !== -1) {
        // Edge
        returnData.browser = FlexiJS.Browser.Browsers.Edge;
        returnData.os = FlexiJS.Browser.OS.Windows;
        var versionInfo = returnData.useragent.match(/edge\/\d{1,3}/);
        if (versionInfo == null) versionInfo = '';
        returnData.version = versionInfo.toString().replace(/edge\//, '');
    } else if (returnData.useragent.indexOf('trident', 0) !== -1) {
        // Internet Explorer
        returnData.browser = FlexiJS.Browser.Browsers.InternetExplorer;
        returnData.os = FlexiJS.Browser.OS.Windows;
        var versionInfo = returnData.useragent.match(/trident.*rv[ :]\d{1,3}/);
        if (versionInfo == null) versionInfo = '';
        returnData.version = versionInfo.toString().replace(/trident.*rv[ :]/, '');
    } else if (returnData.useragent.indexOf('webkit', 0) !== -1) {
        // safari and chrome & webkit 
        var vendor = ('' + window.navigator.vendor).toLowerCase(); // not a standard property
        if (vendor.search('apple') >= 0) {
            returnData.browser = FlexiJS.Browser.Browsers.Safari;
            var versionInfo = returnData.useragent.match(/version\/\d{1,3}[.]\d/);
            if (versionInfo == null) versionInfo = '';
            returnData.version = versionInfo.toString().replace(/version\//, '').replace('.', '');
        } else if (vendor.search('google') >= 0) {
            returnData.browser = FlexiJS.Browser.Browsers.Chrome;
            var versionInfo = returnData.useragent.match(/chrome\/\d\d?\.\d?/);
            if (versionInfo == null) versionInfo = '';
            returnData.version = returnData.useragent.match(/chrome\/\d{1,4}?\.\d?/).toString().replace('.', '').replace(/chrome\//, '').substring(0, 3);
        } else {
            returnData.browser = FlexiJS.Browser.Browsers.GenericWebkit;
        }
    } else {
        returnData.browser = FlexiJS.Browser.Browsers.Unknown;
        returnData.version = 'unknown';
    }

    // os
    if (returnData.os != '') {
        var platform = window.navigator.platform.toString().toLowerCase();
        if (platform.search('win') >= 0) {
            returnData.os = FlexiJS.Browser.OS.Windows;
        } else if (platform.search('mac') >= 0) {
            returnData.os = FlexiJS.Browser.OS.Mac;
        } else if (returnData.useragent.search('iphone') >= 0) {
            returnData.os = FlexiJS.Browser.OS.AppleDevice;
        } else if (platform.search('linux') >= 0) {
            returnData.os = FlexiJS.Browser.OS.Linux;
        }
    }

    return returnData;
};

FlexiJS.Browser.GetScrollbarWidth = function(){
    try{
        // See https://stackoverflow.com/questions/13382516/getting-scroll-bar-width-using-javascript
        var outer = document.createElement("div");
        outer.style.visibility = "hidden";
        outer.style.width = "100px";
        outer.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps

        document.body.appendChild(outer);

        var widthNoScroll = outer.offsetWidth;
        // force scrollbars
        outer.style.overflow = "scroll";

        // add innerdiv
        var inner = document.createElement("div");
        inner.style.width = "100%";
        outer.appendChild(inner);        

        var widthWithScroll = inner.offsetWidth;

        // remove divs
        outer.parentNode.removeChild(outer);

        return widthNoScroll - widthWithScroll;
    }
    catch(e){
        // Return a default width that covers scrollbar width if something falls over
        return 19;
    }
};

/*-- GOOGLE ANALYTICS START */
FlexiJS.Browser.GoogleAnalytics.Setup = function (googleIdentifier, disableTracking) {

    (
        function (i, s, o, g, r, a, m) {
            i['GoogleAnalyticsObject'] = r;
            i[r] = i[r] || function () { (i[r].q = i[r].q || []).push(arguments) };
            i[r].l = 1 * new Date();
            a = s.createElement(o);
            m = s.getElementsByTagName(o)[0];
            a.async = 1;
            a.src = g;
            m.parentNode.insertBefore(a, m);
        }
    )(
        window,
        document,
        'script',
        '//www.google-analytics.com/analytics.js',
        'ga'
    );

    window['ga-disable-' + googleIdentifier] = disableTracking;

    ga('create', googleIdentifier, 'auto');
    ga('send', 'pageview');
};

// Sends an event to Google Analytics
FlexiJS.Browser.GoogleAnalytics.EventTracking = function (hitType, eventCategory, eventAction, eventLabel) {
    try {
        ga('send', {
            hitType: hitType,
            eventCategory: eventCategory,
            eventAction: eventAction,
            eventLabel: eventLabel
        });
    }
    catch (ex) {
        FlexiJS.Error.LogError({ message: ex.message }, "browser.js");
    }
};

FlexiJS.Browser.GoogleAnalytics.RoleTracking = function (roleName) {
    try {
        ga('set', 'dimension1', roleName);
    }
    catch (ex) {
        FlexiJS.Error.LogError({ message: ex.message }, "browser.js");
    }
};

/*-- GOOGLE ANALYTICS END */

FlexiJS.Browser.Hotjar.Setup = function (hotjarIdentifier) {
    (
        function (h, o, t, j, a, r) {
            h.hj = h.hj || function () { (h.hj.q = h.hj.q || []).push(arguments) };
            h._hjSettings = { hjid: hotjarIdentifier, hjsv: 5 };
            a = o.getElementsByTagName('head')[0];
            r = o.createElement('script'); r.async = 1;
            r.src = t + h._hjSettings.hjid + j + h._hjSettings.hjsv;
            a.appendChild(r);
        }
    )(
        window,
        document,
        '//static.hotjar.com/c/hotjar-',
        '.js?sv='
    );
};


/*-- UNSUPPORTED BROWSERS START */

FlexiJS.Browser.UnsupportedBrowsers.ResourceSet = 'UnsupportedBrowser';

FlexiJS.Browser.UnsupportedBrowsers.GetBrowserIcon = function (browserName) {
    var icon = 'fa-exclamation-triangle';
    switch (browserName.toLowerCase()) {
        case "internet explorer":
            icon = 'fa-internet-explorer';
            break;
        case "edge legacy":
            icon = 'fa-edge-legacy';
            break;
        case "safari":
            icon = 'fa-safari';
            break;
    }
    return '<i class="fab ' + icon + '"></i>';
};


FlexiJS.Browser.UnsupportedBrowsers.DisplayBanner = function (retiredBrowserDetails) {

    var bannerMessage = FlexiJS.Resources.GetResourceText(FlexiJS.Browser.UnsupportedBrowsers.ResourceSet, retiredBrowserDetails.bannerStyle);
    bannerMessage = bannerMessage.replace('[Date]', retiredBrowserDetails.retirementMonthYear);

    var banner = "<div id='us-banner' class='unsupported-browser-warning-banner unsupported-browser-banner-" + retiredBrowserDetails.bannerStyle + "'>";
    banner += "<div class='unsupported-browser-content'>";
    banner += "<div class='unsupported-browser-icon'>" + FlexiJS.Browser.UnsupportedBrowsers.GetBrowserIcon(retiredBrowserDetails.browserName) + "</div>";
    banner += "<div class='unsupported-browser-text'><h2>" + retiredBrowserDetails.browserName + ' ' + retiredBrowserDetails.browserVersion + "</h2>";
    banner += "<p>" + bannerMessage + "</p></div>";
    banner += "</div>";
    banner += "</div>";

    $('#us-banner').remove();
    $('#container').before(banner);

};

/*-- UNSUPPORTED BROWSERS END */;/*
  ______ _           _      _  _____  _____          _           
 |  ____| |         (_)    | |/ ____|/ ____|        | |          
 | |__  | | _____  ___     | | (___ | |     ___   __| | ___  ___ 
 |  __| | |/ _ \ \/ / |_   | |\___ \| |    / _ \ / _` |/ _ \/ __|
 | |    | |  __/>  <| | |__| |____) | |___| (_) | (_| |  __/\__ \
 |_|    |_|\___/_/\_\_|\____/|_____(_)_____\___/ \__,_|\___||___/
                                                                 
        Functions To Get System codes
        
        Requirements:
            jQuery
*/

FlexiJS.Codes = new Object();

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/

FlexiJS.Codes.CodesCache = [];

/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/

FlexiJS.Codes.GetCodesByCodeType = function (codetype)
{
    // Description: Gets all codes for a given codetype
    // Created: 14/03/2017, v0.01 - DVB

    var forcereload = false;
    // -- Gets the templates file using a syncronous ajax call.
    //    File only loaded is the cached value is null or an empty string

    if (!FlexiJS.Codes.CodesCache[codetype])
    {
        //Enforces load if template not in cache or template is empty
        forcereload = true;
        //if (!FlexiJS.Codes.CodesCache[codetype]) FlexiJS.Codes.CodesCache[codetype] = [];
    }

    if (forcereload == true)
    {
        $.ajax({
            type: "POST",
            url: "/WebServices/PageService.svc/GetCodesByCodeType",
            data: JSON.stringify({ CodeType: codetype }),
            processData: false,
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            async: false,
            success: function (result)
            {
                FlexiJS.Codes.CodesCache[codetype] = $.parseJSON(result.d);
            }
        });
    }

    var returnvalue = FlexiJS.Codes.CodesCache[codetype] ? FlexiJS.Codes.CodesCache[codetype] : [];
    return returnvalue;
};

FlexiJS.Codes.GetCodesText = function (codetype, codevalue)
{
    // Description: Gets a code text (description) given the type and value
    // Created: 14/03/2017 - v.001 - DVB
    var codes = FlexiJS.Codes.GetCodesByCodeType(codetype);
    var returnvalue = '';

    $.each(codes, function (index, codeitem)
    {
        if (codeitem.value == codevalue)
        {
            returnvalue = codeitem.text;
            return true;
        }
    });

    return returnvalue;
};
;
FlexiJS.Common = {};
FlexiJS.Common.Array = {};
FlexiJS.Common.Math = {};
FlexiJS.Common.Sorting = {};
FlexiJS.Common.Services = {};
FlexiJS.Common.Checks = {};
FlexiJS.Common.Dates = {};
FlexiJS.Common.Tables = {};

FlexiJS.Common.Array.DistinctPropertyValuesFromArray = function (array, property) {
    var flags = [], output = [], l = array.length, i;
    for (i = 0; i < l; i++) {
        if (flags[array[i][property]]) continue;
        flags[array[i][property]] = true;
        output.push(array[i][property]);
    }
    return output;
};

FlexiJS.Common.Sorting.NaturalName = function (as, bs) {
    var a, b, a1, b1, i = 0, L, rx = /(\d+)|(\D+)/g, rd = /\d/;
    if (isFinite(as.Name) && isFinite(bs.Name)) return as.Name - bs.Name;
    a = String(as.Name).toLowerCase();
    b = String(bs.Name).toLowerCase();
    if (a === b) return 0;
    if (!(rd.test(a) && rd.test(b))) return a > b ? 1 : -1;
    a = a.match(rx);
    b = b.match(rx);
    L = a.length > b.length ? b.length : a.length;
    while (i < L) {
        a1 = a[i];
        b1 = b[i++];
        if (a1 !== b1) {
            if (isFinite(a1) && isFinite(b1)) {
                if (a1.charAt(0) === "0") a1 = "." + a1;
                if (b1.charAt(0) === "0") b1 = "." + b1;
                return a1 - b1;
            }
            else return a1 > b1 ? 1 : -1;
        }
    }
    return a.length - b.length;
};

FlexiJS.Common.Math.DecimalMultiplier = function (decimalplaces) {
    return Math.pow(10, decimalplaces);
};

FlexiJS.Common.Math.SplitValue = function (value, divisor, decimalplaces) {
    var decimalMultiplier = FlexiJS.Common.Math.DecimalMultiplier(decimalplaces);
    var roundedcost = mybase._makeFloatInt(value);
    var returnvalue = { splitvalue: 0, excessvalue: 0, excesscount: 0 };

    if (roundedcost > 0 || roundedcost < 0) {
        returnvalue.splitvalue = Math.floor(roundedcost / divisor);
        returnvalue.excessvalue = (returnvalue.splitvalue + 1) / decimalMultiplier;
        returnvalue.excesscount = (roundedcost - (returnvalue.splitvalue * divisor));
        returnvalue.splitvalue = returnvalue.splitvalue / decimalMultiplier;
    }

    return returnvalue;
};

FlexiJS.Common.Math.MakeIntFromFloat = function (floatVal, decimalplaces) {
    var returnVal = 0;
    var upstringcount = parseInt(decimalplaces);

    var isDecimalFormatted = ('' + floatVal).match(/^[-]?\d+(\.{1}\d*){1}$/g);
    if (isDecimalFormatted == null) {
        var isIntegerFormatted = ('' + floatVal).match(/^[-]?\d+$/g);
        if (isIntegerFormatted != null) {
            var strIntNum = '' + isIntegerFormatted[0];
            for (i = 0; i < upstringcount; i++) strIntNum = strIntNum + '0';
            returnVal = parseInt(strIntNum);
        }
    } else {
        var strNum = ('' + isDecimalFormatted[0]).split('.');
        var intHalf = strNum[0];
        var decHalf = strNum[1];
        if (decHalf.length < upstringcount) {
            for (i = decHalf.length; i < upstringcount; i++) decHalf = decHalf + '0';
        }
        for (i = 0; i < upstringcount; i++) intHalf = intHalf + decHalf[i];
        returnVal = parseInt(intHalf);
    }
    return returnVal;
};

FlexiJS.Common.FormatValue = function (value, format) {//moved
    //-- Kendo Formatting the provided value, done here to allow for custom formats
    if (format && $.isNumeric(value)) {
        var myFormat = format;
        var addPositiveSymbol = false;
        if (myFormat.charAt(0) == '+') {
            addPositiveSymbol = true;
            myFormat = myFormat.substring(1, myFormat.length);
        }
        var nvalue = Number(value);
        switch (myFormat) {
            case "%":
                return (addPositiveSymbol && nvalue > 0 ? '+' : '') + kendo.toString(nvalue, '#0.00\\\%');

            case "(c)":
                return '(' + (addPositiveSymbol && nvalue > 0 ? '+' : '') + kendo.toString(nvalue, 'c') + ')';

            default:
                return (addPositiveSymbol && nvalue > 0 ? '+' : '') + kendo.toString(nvalue, myFormat);
        }
    }
    return kendo.toString(value, format);
};

FlexiJS.Common.Services.CallGMSService = function (servicename, jsondata, sucessfunction, failfunction) {
    $.ajax({
        type: "POST",
        url: "/GMS/WebServices/GMSService.svc/" + servicename,
        data: JSON.stringify(jsondata),
        processData: false,
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        async: false,
        success: sucessfunction,
        error: failfunction
    });
};

FlexiJS.Common.Checks._browserScrollBarsDisplayable = null;
FlexiJS.Common.Checks.browserScrollBarsDisplayable = function () {
    if (FlexiJS.Common.Checks._browserScrollBarsDisplayable == null) {
        var innerElem = document.createElement('div');
        innerElem.style.width = '30px';
        innerElem.style.height = '60px';

        var scrollableElem = document.createElement('div');
        scrollableElem.style.width = '30px';
        scrollableElem.style.height = '30px';
        scrollableElem.style.overflow = 'scroll';
        scrollableElem.style.borderWidth = '0';
        scrollableElem.appendChild(innerElem);

        document.body.appendChild(scrollableElem); // Elements only have width if they're in the layout

        var diff = scrollableElem.offsetWidth - scrollableElem.clientWidth;

        document.body.removeChild(scrollableElem);
        FlexiJS.Common.Checks._browserScrollBarsDisplayable = diff > 0;
    }

    return FlexiJS.Common.Checks._browserScrollBarsDisplayable;
};

FlexiJS.Common.Dates.DotNetDateToJSDate = function (dotnetdate) {
    var re = /-?\d+/;
    var m = re.exec(dotnetdate);
    return new Date(parseInt(m[0], 10));
};

FlexiJS.Common.Dates.GetUTCJSONDate = function (dateToFormat) {
    return dateToFormat ? '\/Date(' + Date.UTC(dateToFormat.getFullYear(), dateToFormat.getMonth(), dateToFormat.getDate()) + ')\/' : null;
}

FlexiJS.Common.Dates.GetMilliSecondsFromJSONDate = function (jsondate) {
    if (jsondate) {
        var ms = parseInt(jsondate.toLowerCase().replace('\/date(', '').replace(')\/', ''));
        return isNaN(ms) ? 0 : ms;
    }
    return 0;
}

FlexiJS.Common.Tables.CreateTableCell = function (contents, rowspan, classNames, isHeader) {
    return '<t' + (isHeader ? 'h' : 'd') + (rowspan != null ? ' rowspan="' + rowspan + '"' : '') + (classNames != null ? ' class="' + classNames + '"' : '') + '>' + contents + '</t' + (isHeader ? 'h' : 'd') + '>';
};

FlexiJS.Common.SaveUserSetting = function (setting, value) {

    var userData = {
        Setting: setting,
        Value: value
    };

    $.ajax({
        url: "/WebServices/PageService.svc/SaveUserSetting",
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify(userData),
        dataType: 'json'
    }).done(function (r) {
        var result = JSON.parse(r.d);

        if (result.Result == false) {
            showNotificationMessage('error', FlexiJS.Resources.GetResourceText('Common.js', 'SaveUserSettingErrorMessage'));
        }

    }).fail(function () {
        showNotificationMessage('error', FlexiJS.Resources.GetResourceText('Common.js', 'SaveUserSettingErrorMessage'));
    });

};

FlexiJS.Common.GetUserSetting = function (setting, callBackFunction) {

    var userData = {
        Setting: setting
    };

    $.ajax({
        url: "/WebServices/PageService.svc/GetUserSetting",
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify(userData),
        dataType: 'json',
        async: false
    }).done(function (r) {
        var result = JSON.parse(r.d);

        if (result.Result) {
            if (callBackFunction && (typeof callBackFunction == 'function')) {
                callBackFunction(result.ResultDetail.length > 0 ? JSON.parse(result.ResultDetail) : result.ResultDetail);
            }
        }
        else {
            showNotificationMessage('error', FlexiJS.Resources.GetResourceText('Common.js', 'GetUserSettingErrorMessage'));
        }

    }).fail(function () {
        showNotificationMessage('error', FlexiJS.Resources.GetResourceText('Common.js', 'GetUserSettingErrorMessage'));
    });

};

FlexiJS.Common.ClassExistsInEventPath = function (event, className) {
    var ePath = event.originalEvent.path || event.originalEvent.composedPath && event.originalEvent.composedPath();
    return ePath && ePath.filter(function (pc) {
        return pc && pc.classList && pc.classList.contains(className);
    }).length > 0;
};
;/*
  _____   _                 _       _   ____         ____                   _                    _       
 |  ___| | |   ___  __  __ (_)     | | / ___|       / ___|   ___    _ __   | |_   _ __    ___   | |  ___ 
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |      / _ \  | '_ \  | __| | '__|  / _ \  | | / __|
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |___  | (_) | | | | | | |_  | |    | (_) | | | \__ \
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_)  \____|  \___/  |_| |_|  \__| |_|     \___/  |_| |___/
                                                                                                         

        Generic Controls Functions
        
        Requirements:
            --None
*/

FlexiJS.Controls = {};

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/


/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/
;
FlexiJS.Controls.AuditHistory = {};

FlexiJS.Controls.AuditHistory.GetResourceText = function (resourcekey) {
	return FlexiJS.Resources.GetResourceText('FlexiJS.Controls.AuditHistory', resourcekey);
};

FlexiJS.Controls.AuditHistory.GridType = { Application: 44644, Payments: 44664, Budgets: 44700, FundType: 44668 };



FlexiJS.Controls.AuditHistory.SetupBudgetChangesetGrid = function (applicationid) {
	linktemplate = '<a href="\\#" data-rowdata="#: JSON.stringify(data) #" onclick="FlexiJS.Controls.AuditHistory.ShowBudgetsChangesetDetails(' + applicationid + ', this); return false;">' + FlexiJS.Controls.AuditHistory.GetResourceText('budgetchangesetgrid.viewchangestext') + '</a>';
	$("#divBudgetsAuditHistory").kendoGrid({
		dataSource: {
			pageSize: 10,
			serverPaging: true,
			serverFiltering: true,
			serverSorting: true,
			transport: {
				read: { url: "/GMS/WebServices/GMSService.svc/GetBudgetTablesAuditHistoryChangesets", type: "POST", contentType: "application/json" },
				parameterMap: function (options) { return JSON.stringify({ ApplicationId: applicationid, pageIndex: options.page, pageSize: options.pageSize }); }
			},
			schema: {
				data: function (r) { return JSON.parse(r.d).data; },
				total: function (r) { return JSON.parse(r.d).totalRows; },
				model: { fields: { AuditHistoryBatch: { type: "string" }, LatestChange: { type: "date" }, ChangedByName: { type: "string" }, ChangeTypeId: { type: "integer" }, ChangeType: { type: "string" } } }
			}
		},
		filterable: false,
		sortable: false,
		pageable: true,
		columns: [
			{ field: "ChangeType", title: FlexiJS.Controls.AuditHistory.GetResourceText('budgetchangesetgrid.changetype') },
			{ field: "LatestChange", title: FlexiJS.Controls.AuditHistory.GetResourceText('budgetchangesetgrid.dateofchangetitle'), format: "{0:dd/MM/yyyy HH:mm:ss}" },
			{ field: "ChangedByName", title: FlexiJS.Controls.AuditHistory.GetResourceText('budgetchangesetgrid.changedbytitle') },
			{ template: linktemplate }
		]
	});
};

FlexiJS.Controls.AuditHistory.SetupPaymentGrid = function (applicationid) {
	$("#divPaymentAuditHistory").kendoGrid({
		dataSource: {
			pageSize: 10,
			serverPaging: true,
			serverFiltering: true,
			serverSorting: true,
			transport: {
				read: { url: "/GMS/WebServices/GMSService.svc/GetPaymentsAuditHistory", type: "POST", contentType: "application/json" },
				parameterMap: function (options) { return JSON.stringify({ ApplicationId: applicationid, pageIndex: options.page, pageSize: options.pageSize }); }
			},
			schema: {
				data: function (r) { return JSON.parse(r.d).data; },
				total: function (r) { return JSON.parse(r.d).totalRows; },
				model: {
					fields: {
						InvoiceNumber: { type: "string" },
						PaymentDescription: { type: "string" },
						AuditSubType: { type: "string" },
						NewValue: { type: "string" },
						OldValue: { type: "string" },
						Description: { type: "string" },
						DateOfChange: { type: "date" },
						ChangedByName: { type: "string" }
					}
				}
			}
		},
		filterable: false,
		sortable: false,
		pageable: true,
		columns: [
			{ title: FlexiJS.Controls.AuditHistory.GetResourceText('paymentgrid.invoicenotitle'), field: "InvoiceNumber" },
			{ title: FlexiJS.Controls.AuditHistory.GetResourceText('paymentgrid.paymentdescriptiontitle'), field: "PaymentDescription" },
			{ title: FlexiJS.Controls.AuditHistory.GetResourceText('paymentgrid.audittypetitle'), field: "AuditSubType" },
			{ title: FlexiJS.Controls.AuditHistory.GetResourceText('paymentgrid.newvaluetitle'), field: "NewValue" },
			{ title: FlexiJS.Controls.AuditHistory.GetResourceText('paymentgrid.oldvaluetitle'), field: "OldValue" },
			{ title: FlexiJS.Controls.AuditHistory.GetResourceText('paymentgrid.remarkstitle'), field: "Description" },
			{ title: FlexiJS.Controls.AuditHistory.GetResourceText('paymentgrid.dateofchangetitle'), field: "DateOfChange", format: "{0:dd/MM/yyyy HH:mm:ss}" },
			{ title: FlexiJS.Controls.AuditHistory.GetResourceText('paymentgrid.changedbytitle'), field: "ChangedByName" }
		]
	});
};

FlexiJS.Controls.AuditHistory.ShowBudgetsChangesetDetails = function (applicationid, source) {
	var linkData = $(source).data('rowdata');

	switch (linkData.ChangeTypeId) {
		case 41154:
			FlexiJS.Controls.AuditHistory.ShowBudgetConfirmationChangesetDetails(applicationid, linkData);
			break;

		case 41155:
			FlexiJS.Controls.AuditHistory.ShowBudgetApprovalChangesetDetails(applicationid, linkData);
			break;

		case 41156:
			FlexiJS.Controls.AuditHistory.ShowBudgetUnlockChangesetDetails(applicationid, linkData);
			break;

		default:
			FlexiJS.Controls.AuditHistory.ShowBudgetTableChangesChangesetDetails(applicationid, linkData);
	}
};

FlexiJS.Controls.AuditHistory.ShowBudgetTableChangesChangesetDetails = function (applicationid, linkData) {
	var dateOfChange = new Date(linkData.LatestChange);

	var changedByText = FlexiJS.Controls.AuditHistory.GetResourceText('window.changedbytext')
		.replace('[name]', linkData.ChangedByName)
		.replace('[date]', kendo.format('{0:d}', dateOfChange))
		.replace('[time]', kendo.format('{0:t}', dateOfChange))
	;
	var kw = $('<div class="audit-history-window"><h1>' + changedByText + '</h1><div id="history" class="audithistorygrid"></div><div class="buttonContainer button-save-cancel"><button id="btnCancel" class="fx-btn fg_button secondary btn-horizontal-space btn-space">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.closebutton') + '</button></div></div>')
		.kendoWindow({
			title: false,
			actions: [],
			resizable: false,
			draggable: false,
			modal: { preventScroll: true },
			scrollable: true,
			width: FlexiJS.Budgets.SignOff.PopupWidth,
			minHeight: 640,
			close: function (e) {
				//Clear Temp Data On Close
				FlexiJS.UI.ClearBodyOverflow();
			},
			open: function (e) {
				FlexiJS.UI.SetOverflowAndCenterPopup(this);
			}
		});

	var myColumns = [{ title: FlexiJS.Controls.AuditHistory.GetResourceText('budgetgrid.changetitle'), template: "# if(data.HasFriendlyDescription == true){ ##: data.FriendlyDescription ##} else {##: data.Description ## } #" }];

	kw.find('#history').kendoGrid({
		dataSource: {
			pageSize: 10,
			serverPaging: true,
			serverFiltering: true,
			serverSorting: true,
			transport: {
				read: { url: "/GMS/WebServices/GMSService.svc/GetBudgetTablesAuditHistory", type: "POST", contentType: "application/json" },
				parameterMap: function (options) { return JSON.stringify({ ApplicationId: applicationid, Changeset: linkData.AuditHistoryBatch, ChangesetDate: '/Date(' + dateOfChange.getTime() + ')/', pageIndex: options.page, pageSize: options.pageSize }); }
			},
			schema: {
				data: function (r) { return JSON.parse(r.d).data; },
				total: function (r) { return JSON.parse(r.d).totalRows; },
				model: {
					fields: { DateOfChange: { type: "date" }, Description: { type: "string" }, ChangedByName: { type: "string" }, FriendlyDescription: { type: "string" }, HasFriendlyDescription: { type: "boolean" } }
				}
			}
		},
		filterable: false,
		sortable: false,
		pageable: true,
		columns: myColumns
	});

	kw.find('#btnCancel').click(
		function () {
			kw.data("kendoWindow").close();
			if (kw.find('#btnCancel').onclick) kw.find('#btnCancel').onclick();
			kw.data("kendoWindow").destroy();
			FlexiJS.UI.ClearBodyOverflow();
		}
	).end();


	kw.data("kendoWindow").center().open();
};

FlexiJS.Controls.AuditHistory.ShowBudgetConfirmationChangesetDetails = function (applicationid, linkData) {
	$.ajax({
		url: "/GMS/WebServices/GMSService.svc/GetBudgetSignOffHistory",
		type: "POST",
		dataType: "json",
		contentType: "application/json",
		data: '{"ApplicationId": ' + applicationid + ', "Changeset":"' + linkData.AuditHistoryBatch + '", "ChangesetType":' + linkData.ChangeTypeId + '}',
		success: function (data) {
			if (data.d && data.d.trim() != '') {
				var returnedData = $.parseJSON(data.d);
				console.log(returnedData);
				var changeDate = FlexiJS.Common.Dates.DotNetDateToJSDate(returnedData.DateOfChange);
				var decisionDate = FlexiJS.Common.Dates.DotNetDateToJSDate(returnedData.SignOffs[0].DateSignedOff);


				var changedByText = FlexiJS.Controls.AuditHistory.GetResourceText('window.confirmedbytext')
					.replace('[name]', linkData.ChangedByName)
					.replace('[date]', kendo.format('{0:d}', changeDate))
					.replace('[time]', kendo.format('{0:t}', changeDate))
					;

				var windowHTML = '<div class="audit-history-window">' +
					'<h1>' + changedByText + '</h1>' +
					'<div class="bold">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.decisiondate') + '</div>' +
					'<span>' + kendo.format('{0:d}', decisionDate) + '</span>' + 
					'<div class="margin-top bold">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.periodsconfirmed') + '</div>' +
					'<div id="periods"></div>' +
					(!returnedData.SignOffs[0].Meeting || returnedData.SignOffs[0].Meeting == '' ? '' : '<div class="bold margin-top">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.meeting') + '</div><span>' + returnedData.SignOffs[0].Meeting + '</span>') +
					'<div class="margin-top bold">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.notes') +  '</div>' +
					(!returnedData.Notes || returnedData.Notes == '' ? '<span style="font-style:italic">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.nonotesentered') +  '</span>' : '<span>' + returnedData.Notes + '</span>') +
					'<div class="buttonContainer button-save-cancel"><button id="btnCancel" class="fx-btn fg_button secondary btn-horizontal-space btn-space">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.closebutton') + '</button></div></div>'
					;

				var kw = $(windowHTML)
					.kendoWindow({
						title: false,
						actions: [],
						resizable: false,
						draggable: false,
						modal: { preventScroll: true },
						scrollable: true,
						width: FlexiJS.Budgets.SignOff.PopupWidth,
						close: function (e) {
							//Clear Temp Data On Close
							FlexiJS.UI.ClearBodyOverflow();
						},
						open: function (e) {
							FlexiJS.UI.SetOverflowAndCenterPopup(this);
						}
					});

				FlexiJS.Controls.AuditHistory.BindSignoffPeriodsGrid(kw.find('#periods'), returnedData);

				kw.find('#btnCancel').click(
					function () {
						kw.data("kendoWindow").close();
						if (kw.find('#btnCancel').onclick) kw.find('#btnCancel').onclick();
						kw.data("kendoWindow").destroy();
					}
				).end();


				kw.find('.k-i-expand, .k-i-collapse').click(function () {
					//Push check until after the collapse/show takes place
					setTimeout(
						function () {
							FlexiJS.UI.SetOverflowAndCenterPopup(kw.data("kendoWindow"));
						},
						10
					);
				});


				kw.data("kendoWindow").center().open();
			} else {
				showNotificationMessage('warning', FlexiJS.Controls.AuditHistory.GetResourceText('window.loadingerror'));
			}
		},
		error: function (e) {
			showNotificationMessage('warning', FlexiJS.Controls.AuditHistory.GetResourceText('window.loadingerror'));
		}
	});
};

FlexiJS.Controls.AuditHistory.ShowBudgetApprovalChangesetDetails = function (applicationid, linkData) {
	$.ajax({
		url: "/GMS/WebServices/GMSService.svc/GetBudgetSignOffHistory",
		type: "POST",
		dataType: "json",
		contentType: "application/json",
		data: '{"ApplicationId": ' + applicationid + ', "Changeset":"' + linkData.AuditHistoryBatch + '", "ChangesetType":' + linkData.ChangeTypeId + '}',
		success: function (data) {
			if (data.d && data.d.trim() != '') {
				var returnedData = $.parseJSON(data.d);
				console.log(returnedData);
				var changeDate = FlexiJS.Common.Dates.DotNetDateToJSDate(returnedData.DateOfChange);
				var decisionDate = FlexiJS.Common.Dates.DotNetDateToJSDate(returnedData.SignOffs[0].DateSignedOff);


				var changedByText = FlexiJS.Controls.AuditHistory.GetResourceText('window.approvedbytext')
					.replace('[name]', linkData.ChangedByName)
					.replace('[date]', kendo.format('{0:d}', changeDate))
					.replace('[time]', kendo.format('{0:t}', changeDate))
					;

				var windowHTML = '<div class="audit-history-window">' +
					'<h1>' + changedByText + '</h1>' +
					'<div class="bold">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.decisiondate') +  '</div>' +
					'<span>' + kendo.format('{0:d}', decisionDate) + '</span>' +
					'<div class="margin-top bold">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.periodsapproved') + '</div>' +
					'<div id="periods"></div>' +
					'<div class="margin-top bold">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.notes') +  '</div>' +
					(!returnedData.Notes || returnedData.Notes == '' ? '<span style="font-style:italic">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.nonotesentered') +  '</span>' : '<span>' + returnedData.Notes + '</span>') +
					'<div class="buttonContainer button-save-cancel"><button id="btnCancel" class="fx-btn fg_button secondary btn-horizontal-space btn-space">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.closebutton') + '</button></div></div>'
					;

				var kw = $(windowHTML)
					.kendoWindow({
						title: false,
						actions: [],
						resizable: false,
						draggable: false,
						modal: { preventScroll: true },
						scrollable: true,
						width: FlexiJS.Budgets.SignOff.PopupWidth,
						close: function (e) {
							//Clear Temp Data On Close
							FlexiJS.UI.ClearBodyOverflow();
						},
						open: function (e) {
							FlexiJS.UI.SetOverflowAndCenterPopup(this);
						}
					});

				FlexiJS.Controls.AuditHistory.BindSignoffPeriodsGrid(kw.find('#periods'), returnedData);

				kw.find('#btnCancel').click(
					function () {
						kw.data("kendoWindow").close();
						if (kw.find('#btnCancel').onclick) kw.find('#btnCancel').onclick();
						kw.data("kendoWindow").destroy();
					}
				).end();


				kw.find('.k-i-expand, .k-i-collapse').click(function () {
					//Push check until after the collapse/show takes place
					setTimeout(
						function () {
							FlexiJS.UI.SetOverflowAndCenterPopup(kw.data("kendoWindow"));
						},
						10
					);
				});


				kw.data("kendoWindow").center().open();
			} else {
				showNotificationMessage('warning', FlexiJS.Controls.AuditHistory.GetResourceText('window.loadingerror'));
			}
		},
		error: function (e) {
			showNotificationMessage('warning', FlexiJS.Controls.AuditHistory.GetResourceText('window.loadingerror'));
		}
	});
};

FlexiJS.Controls.AuditHistory.ShowBudgetUnlockChangesetDetails = function (applicationid, linkData) {
	$.ajax({
		url: "/GMS/WebServices/GMSService.svc/GetBudgetSignOffHistory",
		type: "POST",
		dataType: "json",
		contentType: "application/json",
		data: '{"ApplicationId": ' + applicationid + ', "Changeset":"' + linkData.AuditHistoryBatch + '", "ChangesetType":' + linkData.ChangeTypeId + '}',
		success: function (data) {
			if (data.d && data.d.trim() != '') {
				var returnedData = $.parseJSON(data.d);
				console.log(returnedData);
				var changeDate = FlexiJS.Common.Dates.DotNetDateToJSDate(returnedData.DateOfChange);

				var changedByText = FlexiJS.Controls.AuditHistory.GetResourceText('window.unlockedbytext')
					.replace('[name]', linkData.ChangedByName)
					.replace('[date]', kendo.format('{0:d}', changeDate))
					.replace('[time]', kendo.format('{0:t}', changeDate))
					;
				var windowHTML = '<div class="audit-history-window">' +
					'<h1>' + changedByText + '</h1>' +
					'<div class="bold">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.periodsunlocked') + '</div>' +
					'<div id="periods"></div>' +
					'<div class="margin-top bold">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.unlockreason') +  '</div>' +
					'<span>' + returnedData.UnlockReason + '</span>' +
					'<div class="margin-top bold">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.notes') +  '</div>' +
					(!returnedData.Notes || returnedData.Notes == '' ? '<span style="font-style:italic">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.nonotesentered') +  '</span>' : '<span>' + returnedData.Notes + '</span>') +
					'<div class="buttonContainer button-save-cancel"><button id="btnCancel" class="fx-btn btn-horizontal-space btn-space">' + FlexiJS.Controls.AuditHistory.GetResourceText('window.closebutton') + '</button></div></div>'
					;

				var kw = $(windowHTML)
					.kendoWindow({
						title: false,
						actions: [],
						resizable: false,
						draggable: false,
						modal: { preventScroll: true },
						scrollable: true,
						width: FlexiJS.Budgets.SignOff.PopupWidth,
						close: function (e) {
							//Clear Temp Data On Close
							FlexiJS.UI.ClearBodyOverflow();
						},
						open: function (e) {
							FlexiJS.UI.SetOverflowAndCenterPopup(this);
						}
					});

				FlexiJS.Controls.AuditHistory.BindSignoffPeriodsGrid(kw.find('#periods'), returnedData);

				kw.find('#btnCancel').click(
					function () {
						kw.data("kendoWindow").close();
						if (kw.find('#btnCancel').onclick) kw.find('#btnCancel').onclick();
						kw.data("kendoWindow").destroy();
					}
				).end();


				kw.find('.k-i-expand, .k-i-collapse').click(function () {
					//Push check until after the collapse/show takes place
					setTimeout(
						function () {
							FlexiJS.UI.SetOverflowAndCenterPopup(kw.data("kendoWindow"));
						},
						10
					);
				});


				kw.data("kendoWindow").center().open();
			} else {
				showNotificationMessage('warning', FlexiJS.Controls.AuditHistory.GetResourceText('window.loadingerror'));
			}
		},
		error: function (e) {
			showNotificationMessage('warning', FlexiJS.Controls.AuditHistory.GetResourceText('window.loadingerror'));
		}
	});
};

FlexiJS.Controls.AuditHistory.BindSignoffPeriodsGrid = function (element, data) {
	var periodData = data.Periods.map(function (p) { return $.extend({}, p); })
		.map(
			function (p) {
				var signoff = data.SignOffs.filter(function (s) { return s.SignOffId == p.SignOffId; })[0];

				switch (signoff.TypeId) {
					case 300001:
						p.Type = FlexiJS.Controls.AuditHistory.GetResourceText('signofftype.confirmation');
						break;
					case 300002:
						p.Type = FlexiJS.Controls.AuditHistory.GetResourceText('signofftype.approval');
						break;
					default:
						p.Type = FlexiJS.Controls.AuditHistory.GetResourceText('signofftype.unknown');
				}

				p.DecisionDate = FlexiJS.Common.Dates.DotNetDateToJSDate(signoff.DateSignedOff);

				return p;
			}
		);

	var signoffids = periodData.map(function (i) { return i.SignOffId; }).filter(function (v, i, s) { return s.indexOf(v) === i; });
	var tableids = periodData.map(function (i) { return i.GMSFundTypeBudgetTableId; }).filter(function (v, i, s) { return s.indexOf(v) === i; });
	var periodgroupids = periodData.map(function (i) { return i.GMSFundTypeBudgetTablePeriodGroupId; }).filter(function (v, i, s) { return s.indexOf(v) === i; });

	var groups = [];

	if (signoffids.length > 1) groups.push({ field: "SignOffId", aggregates: [{ field: "Type", aggregate: "min" }, { field: "DecisionDate", aggregate: "min" }] });
	groups.push({ field: "GMSFundTypeBudgetTableId", aggregates: [{ field: "TableName", aggregate: "min" }] });
	if (periodgroupids.length > 0 && (periodgroupids.length > 1 || periodgroupids[0] != 0)) groups.push({ field: "GMSFundTypeBudgetTablePeriodGroupId", aggregates: [{ field: "PeriodGroupName", aggregate: "min" }] });

	element.kendoGrid(
		{
			dataSource: { data: periodData, group: groups },
			groupable: { enabled: false, showFooter: false },
			pageable: false,
			scrollable: false,
			sortable: false,
			columns: [
				{ field: "PeriodName", title: FlexiJS.Resources.GetResourceText(FlexiJS.Budgets.SignOff.ResourceSet, 'SaveConfirmation.GridPeriodTitle') },
				{ field: "SignOffId", groupHeaderTemplate: "#= aggregates.Type.min #: " + FlexiJS.Controls.AuditHistory.GetResourceText('window.decisiondate') + " #= kendo.format('{0:d}', aggregates.DecisionDate.min) #", hidden: true },
				{ field: "GMSFundTypeBudgetTableId", groupHeaderTemplate: "#= aggregates.TableName.min #", hidden: true },
				{ field: "GMSFundTypeBudgetTablePeriodGroupId", groupHeaderTemplate: "#= aggregates.PeriodGroupName.min #", hidden: true }
			]
		}
	).addClass("groupedKendoTable");
};
;

/*

  _____   _                 _       _   ____         ____                   _                    _             ____            _     _                     ____    _   _       _
 |  ___| | |   ___  __  __ (_)     | | / ___|       / ___|   ___    _ __   | |_   _ __    ___   | |  ___      | __ )   _   _  | |_  | |_    ___    _ __   |  _ \  (_) | |__   | |__     ___    _ __
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |      / _ \  | '_ \  | __| | '__|  / _ \  | | / __|     |  _ \  | | | | | __| | __|  / _ \  | '_ \  | |_) | | | | '_ \  | '_ \   / _ \  | '_ \
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |___  | (_) | | | | | | |_  | |    | (_) | | | \__ \  _  | |_) | | |_| | | |_  | |_  | (_) | | | | | |  _ <  | | | |_) | | |_) | | (_) | | | | |
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_)  \____|  \___/  |_| |_|  \__| |_|     \___/  |_| |___/ (_) |____/   \__,_|  \__|  \__|  \___/  |_| |_| |_| \_\ |_| |_.__/  |_.__/   \___/  |_| |_|


    -- ButtonRibbon --
        Additional Controls:

*/

FlexiJS.Controls.ButtonRibbon = function (src, attributes) {
    // If we pass a string, look for the element, otherwise treat the src element as a jQuery object array
    try {
        if (typeof src === "string") {
            return $(src).FlexiJS_Controls_ButtonRibbon(attributes);
        } else {
            return src.FlexiJS_Controls_ButtonRibbon(attributes);
        }
    }
    catch (ex) {
        FlexiJS.Error.LogError({ message: ex.message }, "buttonribbon.js");
        return null;
    }
};

(function ($) {
    $.fn.FlexiJS_Controls_ButtonRibbon = function (attributes) {
        //jQuery elements passed in
        var jq = this;

        //For each element passed
        $.each(jq, function (elementIndex, elementItem) {
            // DEFAULT SETTINGS
            var _baseData = {
                id: '',
                data: [],
                onChange: function (value) { },
                useValidation: false,
                isValid: false,
                renderMode: 'classic',
                showInvalidBeforeHasBeenValid: false,
                useAnimations: true,
                _enabled: true,
                _selectedValue: null,
                _hasBeenValid: false,
                _baseElement: elementItem,
                _baseElementIndex: elementIndex,
                _templateGroup: 'FlexiJS.Controls.ButtonRibbon'
            }
            // FOR ANY FUNCTION YOU SHOULD USE 'mybase' TO ACCESS THE CONTROL
            // FOR AN EVENT HANDLER THIS SHOULD BE PASSED IN AS THE EVENT DATA
            var functions = {
                _init: function () {
                    //Called on control creation
                    var mybase = this;
                    $(mybase._baseElement).empty();
                    $(mybase._renderBaseTemplate()).appendTo($(mybase._baseElement));
                    mybase._bindTooltips();
                    mybase._bindBaseEvents();
                    mybase._runDataOnContentLoadEvents(mybase.data);
                },

                
                _getTemplate: function(template) {
                    var mybase = this;
                    return mybase.renderMode + '_' + template;
                },
                _createElement: function (templateid, data) {
                    //Shortcut to template renderer, maintains lower changes required on methodology change
                    var mybase = this;
                    return FlexiJS.Kendo.Templates.RenderTemplate(mybase._templateGroup, templateid, data);
                },
                _renderBaseTemplate: function () {
                    var mybase = this;
                    return mybase._createElement(mybase._getTemplate('OptionSelector'), { mybase: mybase });
                },
                _renderSeletorTemplate: function (itemToRender, isBaseItem) {
                    var mybase = this;
                    return mybase._createElement(mybase._getTemplate('OptionSelector_ButtonGroup'), { mybase: mybase, rootItem: itemToRender, isBaseItem: isBaseItem });
                },
                _bindBaseEvents: function () {
                    var mybase = this;
                    $(mybase._baseElement).find('.js-buttonribbon-ribbonbutton').on('click', mybase, mybase._buttonActivate);
                },
                _bindTooltips: function() {
                    var mybase = this;
                    $.each(
                        $(mybase._baseElement).find('[class*="js-autotitletooltip"][title]'),
                        function(index, tooltipElement){
                            $(tooltipElement).kendoTooltip({
                                autoHide: true,
                                position: "bottom",
                                content: $(tooltipElement).prop('title')
                            });
                        }
                    );
                },
                _runDataOnContentLoadEvents: function (rootItem) {
                    var mybase = this;
                    if(rootItem != null && typeof rootItem.items !== 'undefined'){
                        var itemindex = 0;
                        for (itemindex; itemindex < rootItem.items.length; itemindex++) {
                            var item = rootItem.items[itemindex];
                            if(typeof item.onContentsLoad == 'function'){
                                item.onContentsLoad(mybase, $(mybase._baseElement).find('.buttonribbon-contents[appliesto="' + item.value + '"]'));
                            }
                            if(typeof item.subItem !== 'undefined') mybase._runDataOnContentLoadEvents(item.subItem);
                        }
                    }
                },

                _buttonActivate: function (e) {
                    var mybase = e.data;
                    if (mybase._enabled){
                        var thisButton = $(this);
                        var tindex = thisButton.attr('tindex');

                        if (!thisButton.hasClass('buttonribbon-ribbonbutton-selected')) {
                            var $mainParent = $(thisButton.parent().parent());
                            $('.js-buttonribbon-ribbonbutton', $mainParent).removeClass('buttonribbon-ribbonbutton-selected');
                            thisButton.addClass('buttonribbon-ribbonbutton-selected');

                            mybase._selectedValue = thisButton.attr('buttonvalue');
                        
                            if(mybase.useAnimations){
                                $.when($mainParent.find('.buttonribbon-contents[display!="none"]').slideUp(FlexiJS.Defaults.AnimationDuration.Standard)).done(function () {
                                    var slidedowndiv = $($mainParent.children('.buttonribbon-contents[tindex="' + tindex + '"]'));
                                    if (slidedowndiv.children().length > 0) {
                                        // If the button has a content area, slide it into view
                                        $mainParent.children('.buttonribbon-contents[tindex="' + tindex + '"]').slideDown(FlexiJS.Defaults.AnimationDuration.Standard);
                                    }

                                    mybase.Validate();
                                    mybase.onChange(mybase._selectedValue);
                                });
                            } else {
                                $mainParent.find('.buttonribbon-contents[display!="none"]').toggle(false);
                                var showdiv = $($mainParent.children('.buttonribbon-contents[tindex="' + tindex + '"]'));
                                if (showdiv.children().length > 0) {
                                    // If the button has a content area, slide it into view
                                    $mainParent.children('.buttonribbon-contents[tindex="' + tindex + '"]').toggle(true);
                                }

                                mybase.Validate();
                                mybase.onChange(mybase._selectedValue);
                            }
                        }
                    }
                    return false;
                },
                enable: function(value){
                    var mybase = this;
                    mybase._enabled = value;
                    if(!mybase._enabled){
                        $(mybase._baseElement).find('.js-buttonribbon').addClass('buttonribbon-disabled');
                    } else {
                        $(mybase._baseElement).find('.js-buttonribbon').removeClass('buttonribbon-disabled');
                    }
                    var selectedItem = mybase._getSelectedDataItem(mybase.data);
                    return (selectedItem != null && typeof selectedItem.onEnableStatusChange !== 'undefined') ?
                        selectedItem.onEnableStatusChange(mybase, $(mybase._baseElement).find('.buttonribbon-contents[appliesto="' + selectedItem.value + '"]'), mybase._enabled)
                        : {};
                },
                selectedValue: function () {
                    //Returns the selected item
                    var mybase = this;
                    return mybase._selectedValue;
                },

                selectedContentsValue: function () {
                    var mybase = this;
                    //getContentsValue
                    var selectedItem = mybase._getSelectedDataItem(mybase.data);
                    return (selectedItem != null && typeof selectedItem.getContentsValue !== 'undefined') ?
                        selectedItem.getContentsValue(mybase, $(mybase._baseElement).find('.buttonribbon-contents[appliesto="' + selectedItem.value + '"]'))
                        : {};
                },

                //TODO - Add a selected items/item tree as multiple items can be chained
                select: function (value) {
                    var mybase = this;

                    $('.js-buttonribbon-ribbonbutton', mybase._baseElement).removeClass('buttonribbon-ribbonbutton-selected');
                    
                    if(mybase.useAnimations){
                        $.when($(mybase._baseElement).find('.buttonribbon-contents[display!="none"]').slideUp(FlexiJS.Defaults.AnimationDuration.Standard)).done(function () {
                            var $button = $('.js-buttonribbon-ribbonbutton[buttonvalue="' + value + '"]', mybase._baseElement);
                            if ($button !== null && $button !== undefined) mybase._selectInDataTree($button); // Else Bad Data Source Or Value
                        });
                    } else {
                        $(mybase._baseElement).find('.buttonribbon-contents[display!="none"]').toggle(false);
                        var $button = $('.js-buttonribbon-ribbonbutton[buttonvalue="' + value + '"]', mybase._baseElement);
                        if ($button !== null && $button !== undefined) mybase._selectInDataTree($button); // Else Bad Data Source Or Value
                    }
                },
                _selectInDataTree: function ($element) {
                    var mybase = this;
                    var $parentContainer = $element.closest('.buttonribbon-contents');
                    if ($parentContainer && $parentContainer.length > 0) {
                        //Button is a sub button, we need to recurse up to find the absolute parent and start the click events there
                        var tabIndex = $parentContainer.attr("tindex");
                        if (tabIndex !== null && tabIndex !== undefined) {
                            var $parentButton = $parentContainer.prev('.buttonribbon-ribboncontainer').find('.js-buttonribbon-ribbonbutton[tindex="' + tabIndex + '"]');
                            if ($parentButton !== null && $parentButton !== undefined) {
                                mybase._selectInDataTree($parentButton);
                            } // Else Bad Data Source
                        } // Else Bad Data Source
                    }
                    $element.trigger('click');
                },
                _getSelectedDataItem: function(rootItem) {
                    var mybase = this;
                    return mybase._getDataItemByValue(rootItem, mybase._selectedValue);
                },
                _getDataItemByValue: function(rootItem, valueToFind) {
                    var mybase = this;
                    if(rootItem != null && typeof rootItem.items !== 'undefined'){
                        var itemindex = 0;
                        for (itemindex; itemindex < rootItem.items.length; itemindex++) {
                            var item = rootItem.items[itemindex];
                            if (item.value == valueToFind) {
                                return item;
                            }
                            
                            if(typeof item.subItem !== 'undefined') {
                                var founditem = mybase._getDataItemByValue(item.subItem, valueToFind);
                                if (founditem != null) return founditem;
                            }
                        }
                    }
                    return null;
                },
                _runValidation: function () {
                    var mybase = this;
                    var selectedItem = mybase._getSelectedDataItem(mybase.data);
                    if(selectedItem != null){
                        if(typeof selectedItem.isValid !== 'undefined') return selectedItem.isValid;
                        if(typeof selectedItem.validate !== 'undefined') return selectedItem.validate(mybase, $(mybase._baseElement).find('.buttonribbon-contents[appliesto="' + selectedItem.value + '"]'));
                    }
                    return false;
                },
                Validate: function() {
                    var mybase = this;
                    if(mybase.useValidation){
                        mybase.isValid = mybase._runValidation();
                        if(mybase.isValid) mybase._hasBeenValid = true;
                        $(mybase._baseElement).find('.js-buttonribbon-itemtomarkonvalidate')
                            .removeClass('buttonribbon-novalidationitemselected')
                            .removeClass('buttonribbon-isvalid')
                            .removeClass('buttonribbon-isnotvalid')
                            .addClass(mybase.isValid ? 'buttonribbon-isvalid' : (mybase._hasBeenValid || mybase.showInvalidBeforeHasBeenValid ? 'buttonribbon-isnotvalid' : ''));
                    } else {
                        mybase.isValid = true;
                    }
                }
            }

            // Add user passed attributes, overrides default
            _baseData = $.extend(true, _baseData, attributes);
            // Add functions to data element, override where user has tried to pass something
            _baseData = $.extend(_baseData, functions);
            // Set elements data object to be control data, can be accessed via $('ELEMENT').data('flexiJSControlsButtonRibbon').ACTION/VALUE
            $(elementItem).data('flexiJSControlsButtonRibbon', _baseData);
            // Initiate Control
            _baseData._init();
        })

        //Ensure we return elements to allow any further jquery methods to work
        return this;
    };
})(jQuery);;/*
  _____   _                 _       _   ____         ____                   _                    _              ____                   _                    _     _____       _   _   _
 |  ___| | |   ___  __  __ (_)     | | / ___|       / ___|   ___    _ __   | |_   _ __    ___   | |  ___       / ___|   ___    _ __   | |_    ___   _ __   | |_  | ____|   __| | (_) | |_    ___    _ __
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |      / _ \  | '_ \  | __| | '__|  / _ \  | | / __|     | |      / _ \  | '_ \  | __|  / _ \ | '_ \  | __| |  _|    / _` | | | | __|  / _ \  | '__|
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |___  | (_) | | | | | | |_  | |    | (_) | | | \__ \  _  | |___  | (_) | | | | | | |_  |  __/ | | | | | |_  | |___  | (_| | | | | |_  | (_) | | |
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_)  \____|  \___/  |_| |_|  \__| |_|     \___/  |_| |___/ (_)  \____|  \___/  |_| |_|  \__|  \___| |_| |_|  \__| |_____|  \__,_| |_|  \__|  \___/  |_|


Requirements:
    -- Any libraries that this file depends on, like jQuery or Kendo
*/

FlexiJS.Controls.ContentEditor = {};

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/

// _controlClasses. Classes for the editor
FlexiJS.Controls.ContentEditor._controlClasses = {
    contenteditorbase: 'content-editor-base'
};

// _controlIds. Control ids for the editor
FlexiJS.Controls.ContentEditor._controlIds = {
    cancelContent: 'cancelContent',
    litContent: 'litContent',
    editContent: 'editContent',
    lblEditContent: 'lblEditContent',
    lblEditTitle: 'lblEditTitle',
    litPageTitle: 'litPageTitle',
    reContentEditor: 'reContentEditor',
    saveContent: 'saveContent',
    txtPageTitle: 'txtPageTitle',
    hdnContentPageId: 'hdnContentPageId',
    hdnContentPageTenantId: 'hdnContentPageTenantId',
    hdnContentPageTypeId: 'hdnContentPageTypeId',
    hdnForcePolicyBool: 'hdnForcePolicyBool',
    hdnForcePolicyText: 'hdnForcePolicyText',
    hdnSaveCloseButtonText: 'hdnSaveCloseButtonText',
    hdnSaveContentButtonText: 'hdnSaveContentButtonText',
    hdnSystemFunctionId: 'hdnSystemFunctionId'
};

// _hiddenFieldKeys. References to the values fields within '_controlsAndValuesStructure'
//      Defined seperatley to cut down on strings
FlexiJS.Controls.ContentEditor._hiddenFieldKeys = {
    forcePolicyText: 'forcePolicyText',
    forcePolicyBool: 'forcePolicyBool',
    contentPageId: 'contentPageId',
    contentPageTenantId: 'contentPageTenantId',
    contentPageTypeId: 'contentPageTypeId',
    systemFunctionId: 'systemFunctionId',
    saveCloseButtonText: 'saveCloseButtonText',
    saveContentButtonText: 'saveContentButtonText'
};

// _controlReferences. References to the controls fields within '_controlsAndValuesStructure'
//      Defined seperatley to cut down on strings
FlexiJS.Controls.ContentEditor._controlReferences = {
    baseControl: 'baseControl',
    sourceControl: 'sourceControl',
    contentElement: 'contentElement',
    saveContentElement: 'saveContentElement',
    cancelContentElement: 'cancelContentElement',
    editContentElement: 'editContentElement',
    radEditorElement: 'radEditorElement',
    radEditor: 'radEditor',
    lblEditTitle: 'lblEditTitle',
    lblEditContent: 'lblEditContent',
    txtPageTitle: 'txtPageTitle',
    litPageTitle: 'litPageTitle'
};

// _contentPageEnums. Content page enums defined here for ease of updating
FlexiJS.Controls.ContentEditor._contentPageEnums = {
    HelpPage: 43303
};

// _controlsAndValuesStructure. Data class for '_getContentEditorControlsAndValues' function
//  Structure populated as follows
//      controls: Stores references to the controls, populated from '_controlReferences'
//      values: Stores hidden field values, populated from '_hiddenFieldKeys'
FlexiJS.Controls.ContentEditor._controlsAndValuesStructure = {
    /* Controls */
    controls: (function(){
		var returnvalue = {};
		for(key in FlexiJS.Controls.ContentEditor._controlReferences){
			returnvalue[FlexiJS.Controls.ContentEditor._controlReferences[key]] = null;
		};
		return returnvalue;
	})(),
    /* Hidden Values */
    values: (function(){
		var returnvalue = {};
		for(key in FlexiJS.Controls.ContentEditor._hiddenFieldKeys){
			returnvalue[FlexiJS.Controls.ContentEditor._hiddenFieldKeys[key]] = null;
		};
		return returnvalue;
	})()
};

// _saveEditorStripRegex. Regex to strip br tags at the end of the content editor.
//  also checks for trailing br tags within P tags (occurs when already saved with trailing br tags) & empty p tags & p tags with nbsp only
FlexiJS.Controls.ContentEditor._saveEditorStripRegex = /((\s*&nbsp;\s*|\s*<br\s*[\/]*>\s*)*|(\s*<p>(\s*&nbsp;\s*|\s*<br\s*[\/]*>\s*)*)<\/p>\s*)+$/i;

// _controlinfo. Store control data for several loops over controls.
//   Rather than have 4 seperate arrays OR updating controls directly they have been combined into this object array
//      controlid:          The ID of the control on the page
//      readcontrolto:      When populating the '_controlsAndValuesStructure', create a jquery reference
//                          for this control at the given property name. The property must exist. If null, it will be skipped
//      partofeditor:       If true, the control will be made visible when the content editor is toggled true & hidden when toggled false
//                          If false, the control will be hidden when the content editor is toggled true & shown when toggled false
//                          If null, the controls visibility will not be changed
//      contentPageTypeId:  If provided the visibility of the control will only be changed if the hidden field value for the content page type
//                          matched the value you have passed here. If null then this check is ignored.
//      readvalueto:        If the value of this field should populate an value field in the '_controlsAndValuesStructure'
FlexiJS.Controls.ContentEditor._controlinfo = [
    {
        controlid: FlexiJS.Controls.ContentEditor._controlIds.litContent,
        readcontrolto: FlexiJS.Controls.ContentEditor._controlReferences.contentElement,
        partofeditor: false,
        contentPageTypeId: null,
        readvalueto: null
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.reContentEditor,
        readcontrolto: FlexiJS.Controls.ContentEditor._controlReferences.radEditorElement,
        partofeditor: true,
        contentPageTypeId: null,
        readvalueto: null
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.saveContent,
        readcontrolto: FlexiJS.Controls.ContentEditor._controlReferences.saveContentElement,
        partofeditor: true,
        contentPageTypeId: null,
        readvalueto: null
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.cancelContent,
        readcontrolto: FlexiJS.Controls.ContentEditor._controlReferences.cancelContentElement,
        partofeditor: true,
        contentPageTypeId: null,
        readvalueto: null
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.editContent,
        readcontrolto: FlexiJS.Controls.ContentEditor._controlReferences.editContentElement,
        partofeditor: false,
        contentPageTypeId: null,
        readvalueto: null
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.lblEditContent,
        readcontrolto: FlexiJS.Controls.ContentEditor._controlReferences.lblEditContent,
        partofeditor: true,
        contentPageTypeId: FlexiJS.Controls.ContentEditor._contentPageEnums.HelpPage,
        readvalueto: null
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.lblEditTitle,
        readcontrolto: FlexiJS.Controls.ContentEditor._controlReferences.lblEditTitle,
        partofeditor: true,
        contentPageTypeId: FlexiJS.Controls.ContentEditor._contentPageEnums.HelpPage,
        readvalueto: null
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.litPageTitle,
        readcontrolto: FlexiJS.Controls.ContentEditor._controlReferences.litPageTitle,
        partofeditor: false,
        contentPageTypeId: FlexiJS.Controls.ContentEditor._contentPageEnums.HelpPage,
        readvalueto: null
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.txtPageTitle,
        readcontrolto: FlexiJS.Controls.ContentEditor._controlReferences.txtPageTitle,
        partofeditor: true,
        contentPageTypeId: FlexiJS.Controls.ContentEditor._contentPageEnums.HelpPage,
        readvalueto: null
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.hdnContentPageId,
        readcontrolto: null,
        partofeditor: null,
        contentPageTypeId: null,
        readvalueto: FlexiJS.Controls.ContentEditor._hiddenFieldKeys.contentPageId
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.hdnContentPageTenantId,
        readcontrolto: null,
        partofeditor: null,
        contentPageTypeId: null,
        readvalueto: FlexiJS.Controls.ContentEditor._hiddenFieldKeys.contentPageTenantId
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.hdnContentPageTypeId,
        readcontrolto: null,
        partofeditor: null,
        contentPageTypeId: null,
        readvalueto: FlexiJS.Controls.ContentEditor._hiddenFieldKeys.contentPageTypeId
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.hdnForcePolicyBool,
        readcontrolto: null,
        partofeditor: null,
        contentPageTypeId: null,
        readvalueto: FlexiJS.Controls.ContentEditor._hiddenFieldKeys.forcePolicyBool
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.hdnForcePolicyText,
        readcontrolto: null,
        partofeditor: null,
        contentPageTypeId: null,
        readvalueto: FlexiJS.Controls.ContentEditor._hiddenFieldKeys.forcePolicyText
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.hdnSaveCloseButtonText,
        readcontrolto: null,
        partofeditor: null,
        contentPageTypeId: null,
        readvalueto: FlexiJS.Controls.ContentEditor._hiddenFieldKeys.saveCloseButtonText
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.hdnSaveContentButtonText,
        readcontrolto: null,
        partofeditor: null,
        contentPageTypeId: null,
        readvalueto: FlexiJS.Controls.ContentEditor._hiddenFieldKeys.saveContentButtonText
    },{ 
        controlid: FlexiJS.Controls.ContentEditor._controlIds.hdnSystemFunctionId,
        readcontrolto: null,
        partofeditor: null,
        contentPageTypeId: null,
        readvalueto: FlexiJS.Controls.ContentEditor._hiddenFieldKeys.systemFunctionId
    }
];

/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        Error handling should use FlexiJS.Error to log any errors
*/

FlexiJS.Controls.ContentEditor._getContentEditorControlsAndValues = function (controlId) {
    // Returns the editor controls. Cloned from structure to remain individual
    var returnData = jQuery.extend({}, FlexiJS.Controls.ContentEditor._controlsAndValuesStructure);

    //Get controls where available
    returnData.controls.sourceControl = jQuery('#' + controlId);
    if (returnData.controls.sourceControl.length > 0) {
        returnData.controls.baseControl = returnData.controls.sourceControl.closest('.' + FlexiJS.Controls.ContentEditor._controlClasses.contenteditorbase);

        if (returnData.controls.baseControl.length > 0) {
            //Get Controls
            $.each(FlexiJS.Controls.ContentEditor._controlinfo, function (index, item) {
                var itemControl = returnData.controls.baseControl.children('[id$=' + item.controlid + ']:first');
                if (itemControl.length > 0){
                    //Get Controls / Hidden Field Data
                    if(item.readcontrolto !== null) returnData.controls[item.readcontrolto] = itemControl;
                    //Get Hidden Field Data - Sets a value in an object from a hidden field
                    if(item.readvalueto !== null) returnData.values[item.readvalueto] = itemControl.val();
                }
            });

            //Gets a reference to the rad editor
            if (returnData.controls.radEditorElement.length > 0) returnData.controls.radEditor = $find(returnData.controls.radEditorElement.attr('id'));
        }
    }

    //Null undefined controls for consistency later. Easier to check if the controls parameter is just null than null OR undefined OR empty
    for (element in returnData.controls)
        if (
            typeof returnData.controls[element] === 'undefined' ||
            (
                returnData.controls[element] != null &&
                returnData.controls[element].length < 1
            )
        ) returnData.controls[element] = null;

    return returnData;
};

FlexiJS.Controls.ContentEditor._setValueFromHiddenField = function (data, controlId, setValue) {
    //Sets a value in an object from a hidden field
    var hiddenField = data.controls[FlexiJS.Controls.ContentEditor._controlReferences.baseControl].children('[id$=' + controlId + ']:first');
    if (typeof hiddenField !== 'undefined') data.values[setValue] = hiddenField.val();
};

FlexiJS.Controls.ContentEditor._toggleContentEditor = function (editorData, showEditor, setContentsFromInputs) {
    
    //Set Visability Values For Editor & Content
    var editorDisplay = showEditor ? '' : 'none';
    var contentDisplay = showEditor ? 'none' : '';
    var controlRefs = FlexiJS.Controls.ContentEditor._controlReferences;
    var hiddenFieldKeys = FlexiJS.Controls.ContentEditor._hiddenFieldKeys;
    var helpPageEnum = FlexiJS.Controls.ContentEditor._contentPageEnums.HelpPage;

    //Get page content id, checks for help page content edit
    var thisContentPageTypeId = editorData.values[hiddenFieldKeys.contentPageTypeId];
    
    //If the editor is being shown
    if (showEditor) {
        //Set Rad Editor Contents
        if (editorData.controls[controlRefs.radEditor] !== null)
            editorData.controls[controlRefs.radEditor].set_html(
                editorData.controls[controlRefs.contentElement] !== null ? 
                    editorData.controls[controlRefs.contentElement].html() : 
                    ''
            );
    
        //Set Help Page Fields
        if (thisContentPageTypeId == helpPageEnum) {
            // Page Title Input
            if (editorData.controls[controlRefs.txtPageTitle] !== null)
                editorData.controls[controlRefs.txtPageTitle].val(
                    editorData.controls[controlRefs.litPageTitle] !== null ?
                        editorData.controls[controlRefs.litPageTitle].text() :
                        ''
                );

            // Button text changed to reflect that window closes on save if contentPageTenantId is 0
            if (editorData.controls[controlRefs.saveContentElement] !== null)
                editorData.controls[controlRefs.saveContentElement].val(
                    editorData.values[hiddenFieldKeys.contentPageTenantId] == 0 ?
                        editorData.values[hiddenFieldKeys.saveCloseButtonText] :
                        editorData.values[hiddenFieldKeys.saveContentButtonText]
                );
        }

        //Fix a rad content editor issue where no defined width leads to the input being 15px wide but defined as zero in the css
        var editor = $('#' + editorData.controls[controlRefs.radEditor].get_id());
        editor.attr('style', editor.attr('style').replace('width: 0px;', ''));
    }

    //If requested, set the page element content to the editor contents
    if (setContentsFromInputs) {
        var setContents = '';
        if (editorData.controls[controlRefs.radEditor] !== null)
            setContents = editorData.controls[controlRefs.radEditor].get_html();
        if (editorData.controls[controlRefs.contentElement] !== null)
            editorData.controls[controlRefs.contentElement].html(setContents);

        // Only for help pages
        if (editorData.values[hiddenFieldKeys.contentPageTypeId] == helpPageEnum) {
            // Set the title
            var setTitle = '';
            if (editorData.controls[controlRefs.txtPageTitle] !== null) 
                setTitle = editorData.controls[controlRefs.txtPageTitle].val();
            if (editorData.controls[controlRefs.litPageTitle] !== null)
                editorData.controls[controlRefs.litPageTitle].html('<h1>' + setTitle + '</h1>');
        }
    }

    //Toggle Editor Items Visability
    $.each(FlexiJS.Controls.ContentEditor._controlinfo, function (index, item) {
        if(item.readcontrolto !== null && item.partofeditor !== null && (item.contentPageTypeId === null || item.contentPageTypeId == thisContentPageTypeId)) {
            if (editorData.controls[item.readcontrolto] !== null)
                editorData.controls[item.readcontrolto].css('display', item.partofeditor ? editorDisplay : contentDisplay);
        }
    });

    //If toggling back from the rad editor, set the focus to the contents div, this fixes issue where the rad editor toolbar remains floating in the page
    if(!showEditor && editorData.controls[controlRefs.contentElement] !== null) editorData.controls[controlRefs.contentElement].click();
};

FlexiJS.Controls.ContentEditor.ShowEditor = function (controlId) {
    // Shows a content editor
    var editorData = FlexiJS.Controls.ContentEditor._getContentEditorControlsAndValues(controlId);
    var showEditor = true;
    var setContentsFromInputs = false;
    FlexiJS.Controls.ContentEditor._toggleContentEditor(editorData, showEditor, setContentsFromInputs);
    ScrollToControlParent(controlId);
};

FlexiJS.Controls.ContentEditor.CancelEditor = function (controlId) {
    // Hides a content editor
    var editorData = FlexiJS.Controls.ContentEditor._getContentEditorControlsAndValues(controlId);
    var showEditor = false;
    var setContentsFromInputs = false;
    FlexiJS.Controls.ContentEditor._toggleContentEditor(editorData, showEditor, setContentsFromInputs);
};

FlexiJS.Controls.ContentEditor.SaveEditor = function (controlId) {
    // Fires a save for a content editor
    var controlRefs = FlexiJS.Controls.ContentEditor._controlReferences;
    var hiddenFieldKeys = FlexiJS.Controls.ContentEditor._hiddenFieldKeys;
    var editorData = FlexiJS.Controls.ContentEditor._getContentEditorControlsAndValues(controlId);
    
    var contentHtml = '';
    var editorControl = editorData.controls[controlRefs.radEditor];
    if (editorControl !== null) {
        contentHtml = editorControl.get_html();
        if(contentHtml == null) {
            contentHtml = '';
        } else {
            contentHtml = contentHtml.replace(FlexiJS.Controls.ContentEditor._saveEditorStripRegex, '');
        }
        editorControl.set_html(contentHtml);
    }

    // Only for help pages
    var contentTitle = null;
    if (editorData.values[hiddenFieldKeys.contentPageTypeId] == FlexiJS.Controls.ContentEditor._contentPageEnums.HelpPage &&
        editorData.controls[controlRefs.txtPageTitle] !== null)
        contentTitle = editorData.controls[controlRefs.txtPageTitle].val();

    // Check for forcePolicyUpdate enabled
    var forcePolicyUpdateDate = (editorData.values[hiddenFieldKeys.forcePolicyBool] !== null &&
        editorData.values[hiddenFieldKeys.forcePolicyBool].toLowerCase() === 'true');
    if (forcePolicyUpdateDate === true) forcePolicyUpdateDate = confirm(editorData.values[hiddenFieldKeys.forcePolicyText]);
    
    www.fluenttechnology.com.ws.ContentEditingService.SaveContentPageInline(
        editorData.values[hiddenFieldKeys.contentPageId],
        contentHtml,
        contentTitle,
        editorData.values[hiddenFieldKeys.systemFunctionId],
        forcePolicyUpdateDate,
        function(){ FlexiJS.Controls.ContentEditor._onSaveEditorComplete(controlId) },
        FlexiJS.Controls.ContentEditor._onSaveEditorError
    );
};

FlexiJS.Controls.ContentEditor._onSaveEditorComplete = function (controlId) {
    // On content editor save complete
    var editorData = FlexiJS.Controls.ContentEditor._getContentEditorControlsAndValues(controlId);
    var hiddenFieldKeys = FlexiJS.Controls.ContentEditor._hiddenFieldKeys;
    var showEditor = false;
    var setContentsFromInputs = true;
    FlexiJS.Controls.ContentEditor._toggleContentEditor(editorData, showEditor, setContentsFromInputs);

    // Only for help pages, close window on save if contentPageTenantId is 0
    if (editorData.values[hiddenFieldKeys.contentPageTypeId] == FlexiJS.Controls.ContentEditor._contentPageEnums.HelpPage &&
        editorData.values[hiddenFieldKeys.contentPageTenantId] == 0)
        window.close();

    // Check for forcePolicy and refresh location to display the updated policy date
    var forcePolicyUpdateDate = (editorData.values[hiddenFieldKeys.forcePolicyBool] !== null &&
        editorData.values[hiddenFieldKeys.forcePolicyBool].toLowerCase() === 'true');
    if (forcePolicyUpdateDate === true)
        location.reload();
};

FlexiJS.Controls.ContentEditor._onSaveEditorError = function (err) {
    // On content editor save error
    FlexiJS.Error.LogError(err.get_message(), 'FlexiJS.Controls.ContentEditor._onSaveEditorError');
};
;FlexiJS.Controls.ComboButton = {};
FlexiJS.Controls.ComboButton.TooltipCache = {};
FlexiJS.Controls.ComboButton.TooltipHideDelay = 1500;
FlexiJS.Controls.ComboButton.TooltipWidth = 300;

FlexiJS.Controls.ComboButton.SetUp = function (controlId, rightAlign, isElipsisLink) {
    var button = $($('[id=' + controlId + ']')[0]);
    if (isElipsisLink) {
        button.addClass('combolinkbutton fa-ellipsis-v fas');
    } else {
        button.addClass('combobutton');
    }

    var createdtooltip = button.kendoTooltip({
        width: FlexiJS.Controls.ComboButton.TooltipWidth,
        autoHide: false,
        callout: false,
        content: function (e) {
            var target = e.target; // the element for which the tooltip is shown
            return target.data("buttonoptions"); // set the element text as content of the tooltip
        },
        show: function (e) {
            FlexiJS.Controls.ComboButton.TooltipCache[controlId] = {
                timeoutHide: null,
                onTooltip: false,
                onTarget: false,
                onCallout: false
            };

            var tooltip = this;
            var tooltipEle = tooltip.popup.element;
            tooltipEle.addClass('combobutton-content');
            tooltipEle.click(function (e) { $(this).parent().hide(); });
            tooltipEle.find('.k-tooltip-button').hide();
            tooltipEle.hover(
                function () {
                    FlexiJS.Controls.ComboButton.onHoverOn(controlId, true, false, false);
                },
                function () {
                    FlexiJS.Controls.ComboButton.onHoverOff(controlId, true, false, false);
                }
            );

            var target = tooltip.target();
            target.hover(
                function () {
                    FlexiJS.Controls.ComboButton.onHoverOn(controlId, false, true, false);
                },
                function () {
                    FlexiJS.Controls.ComboButton.onHoverOff(controlId, false, true, false);
                }
            );

            var callout = tooltip.popup.wrapper.find('.k-callout');
            // -ff class needed for Firefox tooltip styling issues
            callout.addClass('combobutton-callout' + !(window.mozInnerScreenX == null) ? '-ff' : '');
            callout.hover(
                function () {
                    FlexiJS.Controls.ComboButton.onHoverOn(controlId, false, false, true);
                },
                function () {
                    FlexiJS.Controls.ComboButton.onHoverOff(controlId, false, false, true);
                }
            );

            $.each(Object.entries(FlexiJS.Controls.ComboButton.TooltipCache), function (index, item) {
                if (item[0] != controlId) {
                    FlexiJS.Controls.ComboButton.hideTooltip(item[0]);
                }
            });
        },
        position: "bottom"
    }).data("kendoTooltip");

    //Positioning/alignment
    if (createdtooltip) {
        createdtooltip._initPopup();

        createdtooltip.popup.bind("open", function (e) {
            if (rightAlign == true) {
                e.sender.options.origin = "bottom right";
                e.sender.options.position = "top right";
            } else {
                e.sender.options.origin = "bottom left";
                e.sender.options.position = "top left";
            }
        });
    }
};

FlexiJS.Controls.ComboButton.CreateButtonOption = function (onClickFunction, text, disabledTitle, titleClass) {
    var option = '<a href=\'\\#\' onclick=&quot;' + onClickFunction + '; return false;&quot;';

    if (disabledTitle) {
        option += ' class=\'aspNetDisabled\' title=\'' + disabledTitle + '\'';
    }
    else if (titleClass) {
        option += ' class=\'' + titleClass + '\'';
    }

    option += '><div class=\'combobutton-option\'><span>' + text + '</span></div></a>';

    return option;
};

FlexiJS.Controls.ComboButton.onHoverOn = function (controlId, wasTooltip, wasTarget, wasCallout) {
    if (wasTooltip == true) FlexiJS.Controls.ComboButton.TooltipCache[controlId].onTooltip = true;
    if (wasTarget == true) FlexiJS.Controls.ComboButton.TooltipCache[controlId].onTarget = true;
    if (wasCallout == true) FlexiJS.Controls.ComboButton.TooltipCache[controlId].onCallout = true;

    FlexiJS.Controls.ComboButton.clearTimeout(controlId);
};

FlexiJS.Controls.ComboButton.onHoverOff = function (controlId, wasTooltip, wasTarget, wasCallout) {
    if (wasTooltip == true) FlexiJS.Controls.ComboButton.TooltipCache[controlId].onTooltip = false;
    if (wasTarget == true) FlexiJS.Controls.ComboButton.TooltipCache[controlId].onTarget = false;
    if (wasCallout == true) FlexiJS.Controls.ComboButton.TooltipCache[controlId].onCallout = false;

    FlexiJS.Controls.ComboButton.TooltipCache[controlId].timeoutHide = setTimeout(function () {
        var tooltipData = FlexiJS.Controls.ComboButton.TooltipCache[controlId];
        if (
            tooltipData &&
            tooltipData.onTooltip == false &&
            tooltipData.onTarget == false &&
            tooltipData.onCallout == false
        ) {
            FlexiJS.Controls.ComboButton.hideTooltip(controlId);
        }
    }, FlexiJS.Controls.ComboButton.TooltipHideDelay);
};

FlexiJS.Controls.ComboButton.hideTooltip = function (controlId) {
    var tooltip = $('[id=' + controlId + ']').data("kendoTooltip");
    if (tooltip) {
        tooltip.hide();
    } else {
        FlexiJS.Controls.ComboButton.clearUnconnectedTooltip(controlId);
    }
    FlexiJS.Controls.ComboButton.clearTimeout(controlId);
};

FlexiJS.Controls.ComboButton.clearTimeout = function (controlId) {
    if (
        controlId &&
        FlexiJS.Controls.ComboButton.TooltipCache[controlId] &&
        FlexiJS.Controls.ComboButton.TooltipCache[controlId].timeoutHide != null
    ) {
        clearTimeout(FlexiJS.Controls.ComboButton.TooltipCache[controlId].timeoutHide);
    }
};

FlexiJS.Controls.ComboButton.clearUnconnectedTooltip = function (controlId) {
    $('[id=' + controlId + '_tt_active]').closest('.k-animation-container').remove();
    delete FlexiJS.Controls.ComboButton.TooltipCache[controlId];
};;/*

  _____   _                 _       _   ____         ____                   _                    _             ____                  _       _                                  _    ____          _       _
 |  ___| | |   ___  __  __ (_)     | | / ___|       / ___|   ___    _ __   | |_   _ __    ___   | |  ___      |  _ \    __ _   ___  | |__   | |__     ___     __ _   _ __    __| |  / ___|  _ __  (_)   __| |
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |      / _ \  | '_ \  | __| | '__|  / _ \  | | / __|     | | | |  / _` | / __| | '_ \  | '_ \   / _ \   / _` | | '__|  / _` | | |  _  | '__| | |  / _` |
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |___  | (_) | | | | | | |_  | |    | (_) | | | \__ \  _  | |_| | | (_| | \__ \ | | | | | |_) | | (_) | | (_| | | |    | (_| | | |_| | | |    | | | (_| |
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_)  \____|  \___/  |_| |_|  \__| |_|     \___/  |_| |___/ (_) |____/   \__,_| |___/ |_| |_| |_.__/   \___/   \__,_| |_|     \__,_|  \____| |_|    |_|  \__,_|


Requirements:
    -- Any libraries that this file depends on, like jQuery or Kendo
*/

FlexiJS.Controls.DashboardGrid = {};

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/

FlexiJS.Controls.DashboardGrid.GridActions = null;
FlexiJS.Controls.DashboardGrid.GridProperties = null;
FlexiJS.Controls.DashboardGrid.GridWithLastAction = null;
FlexiJS.Controls.DashboardGrid.timeoutHide = null;
FlexiJS.Controls.DashboardGrid.isHoveringCell = {};
FlexiJS.Controls.DashboardGrid.PDFGenerationType = '';
FlexiJS.Controls.DashboardGrid.IsReviewer = false;
FlexiJS.Controls.DashboardGrid.AllowAbstract = false;

/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/


FlexiJS.Controls.DashboardGrid.GetFilterData = function () {
    var returnData = {
        fund: 0, fundType: 0, tags: '', area: '', fundStatus: '', application: '', applicationCategory: '',
        fundingSource: '', date: '', dateType: '', dateStart: '', dateEnd: '', contact: '',
        organisation: '', project: '', includeFailed: '', region: ''
    };

    var simpleValReads = [
        { fromId: 'hdnFilterTags', toProperty: 'tags' },
        { fromId: 'hdnFilterArea', toProperty: 'area' },
        { fromId: 'hdnFilterFundStatus', toProperty: 'fundStatus' },
        { fromId: 'hdnFilterApplication', toProperty: 'application' },
        { fromId: 'hdnFilterApplicationCategory', toProperty: 'applicationCategory' },
        { fromId: 'hdnFilterFundingSource', toProperty: 'fundingSource' },
        { fromId: 'hdnDateFilter', toProperty: 'date' },
        { fromId: 'hdnDateFilterType', toProperty: 'dateType' },
        { fromId: 'hdnDateFilterStart', toProperty: 'dateStart' },
        { fromId: 'hdnDateFilterEnd', toProperty: 'dateEnd' },
        { fromId: 'hdnContactFilter', toProperty: 'contact' },
        { fromId: 'hdnOrgFilter', toProperty: 'organisation' },
        { fromId: 'hdnProjectFilter', toProperty: 'project' },
        { fromId: 'hdnFilterIncludeFailed', toProperty: 'includeFailed' },
        { fromId: 'hdnFilterRegion', toProperty: 'region' }
    ];

    $.each(simpleValReads, function (readIndex, readItem) {
        var $actualElement = $('[id$=' + readItem.fromId + ']');
        if ($actualElement !== 'undefined' && $actualElement !== null && returnData.hasOwnProperty(readItem.toProperty)) {
            returnData[readItem.toProperty] = $actualElement.val();
        }
    });

    var $fundAndFundTypeFilterElement = $('[id$=hdnFilterFund]');
    if ($fundAndFundTypeFilterElement !== 'undefined' && $fundAndFundTypeFilterElement !== null) {
        var fundAndFundTypeFilterElementValue = $fundAndFundTypeFilterElement.val().split('-');
        if (fundAndFundTypeFilterElementValue.length > 1) {
            switch (fundAndFundTypeFilterElementValue[0]) {
                case 'fund':
                    returnData.fund = fundAndFundTypeFilterElementValue[1];
                    break;
                case 'fundtype':
                    returnData.fundType = fundAndFundTypeFilterElementValue[1];
                    break;
            }
        }
    }

    return returnData;
};

FlexiJS.Controls.DashboardGrid.GetPageOptionsData = function () {
    var returnData = {
        expandedGridsMode: '',
        isReviewerMode: '',
        gridProperties: '',
        gridActions: ''
    };

    var simpleValReads = [
        { fromId: 'hdnExpandedGridsMode', toProperty: 'expandedGridsMode' },
        { fromId: 'hdnIsReviewerMode', toProperty: 'isReviewerMode' },
        { fromId: 'hdnGridProperties', toProperty: 'gridProperties' },
        { fromId: 'hdnGridActions', toProperty: 'gridActions' },
    ];

    $.each(simpleValReads, function (readIndex, readItem) {
        var $actualElement = $('[id$=' + readItem.fromId + ']');
        if ($actualElement !== 'undefined' && $actualElement !== null && returnData.hasOwnProperty(readItem.toProperty)) {
            returnData[readItem.toProperty] = $actualElement.val();
        }
    });

    return returnData;
};

FlexiJS.Controls.DashboardGrid.AddGridToPage = function(Sender, ControlId, AppStatus, Stage, StageStatus, GridPropertyId, DashboardgroupId, AllowedActions, ScoreRoundId) {
    FlexiJS.Controls.DashboardGrid.SetGridProperties();

    var control = $("[id=" + ControlId + "]");
    var addGrid = false;

    if (DashboardgroupId == null) DashboardgroupId = 0;

    if (control.length > 0) {
        if (control.children().length > 0) {
            //Check if grid already been added
            if (control[0].AppStatus != AppStatus || control[0].Stage != Stage || control[0].StageStatus != StageStatus || control[0].DashboardgroupId != DashboardgroupId) {
                addGrid = true;
            }
            control[0].AppStatus = null;
            control[0].Stage = null;
            control[0].StageStatus = null;
            control[0].DashboardgroupId = null;

            var grid = control.find(".dashboard-grid-base").data("kendoGrid");
            grid.destroy();
            control.children().remove();
        } else {
            addGrid = true;
        }
    }

    var filterData = FlexiJS.Controls.DashboardGrid.GetFilterData();
    var pageOptions = FlexiJS.Controls.DashboardGrid.GetPageOptionsData();
    var thisGridProperties = FlexiJS.Controls.DashboardGrid.GridProperties[GridPropertyId];

    if (addGrid) {
        var defaultClass = 'AppGridContSml';
        var exportWrapperClass = 'export-tools';
        var bulkWrapperClass = 'bulkdropdown-tools';

        if (pageOptions.expandedGridsMode === 'true') defaultClass = 'AppGridContSmlExpandedMode';

        var filtersQuerystring = FlexiJS.Utils.Strings.FormatString(
            "apptags={0}&dbg={1}&as={2}&s={3}&sts={4}&sr={5}&a={6}&fs={7}&f={8}&ft={9}&af={10}&cat={11}&fundingSourcesFilter={12}&dFilter={13}&dFilterType={14}&dFilterStart={15}&dFilterEnd={16}&cFilter={17}&oFilter={18}&pFilter={19}&includeFailed={20}&regionfilter={21}",
            encodeURIComponent(filterData.tags == null ? '' : filterData.tags), DashboardgroupId, AppStatus, Stage, StageStatus, ScoreRoundId, filterData.area, filterData.fundStatus, filterData.fund, filterData.fundType,
            filterData.application, filterData.applicationCategory, filterData.fundingSource, filterData.date, filterData.dateType, filterData.dateStart, filterData.dateEnd,
            filterData.contact, filterData.organisation, filterData.project, filterData.includeFailed, filterData.region
        );

        var headerTemplate = "";

        if (pageOptions.isReviewerMode !== "true") {
            headerTemplate += FlexiJS.Utils.Strings.FormatString(
                "<a href='/DownloadFile.aspx?c=Export&gridid={0}&ct=gmsappgrid&export=excel&{1}' target='_blank' onclick=\"FlexiJS.Browser.GoogleAnalytics.EventTracking('event', 'Grid Export', 'Click', 'Export to Excel');\"  class='fx-gmsdashboardgridlink'><img src='/RadControls/FlexiGrid/Grid/ExportToExcel.gif' title='{2}' >{2}</a>",
                GridPropertyId, filtersQuerystring, thisGridProperties.ExportToExcelText
            );

            headerTemplate += FlexiJS.Utils.Strings.FormatString(
                "<a href='/DownloadFile.aspx?c=Export&gridid={0}&ct=gmsappgrid&export=word&{1}' target='_blank' onclick=\"FlexiJS.Browser.GoogleAnalytics.EventTracking('event', 'Grid Export', 'Click', 'Export to  Word');\"  class='fx-gmsdashboardgridlink'><img src='/RadControls/FlexiGrid/Grid/ExportToWord.gif' title='{2}' >{2}</a>",
                GridPropertyId, filtersQuerystring, thisGridProperties.ExportToWordText
            );

            headerTemplate += FlexiJS.Utils.Strings.FormatString(
                "<a href='/DownloadFile.aspx?c=Export&gridid=-1&ct=gmsappgrid&export=excel&{0}' target='_blank' onclick=\"FlexiJS.Browser.GoogleAnalytics.EventTracking('event', 'Grid Export', 'Click', 'Export to Excel with Additional Data');\" class='fx-gmsdashboardgridlink'><img src='/RadControls/FlexiGrid/Grid/ExportToExcel.gif' title='{1}'>{1}</a>",
                filtersQuerystring, thisGridProperties.ExportToExcelExtraText
            );
        }

        if (DashboardgroupId == 73117 || DashboardgroupId == 73113) {
            headerTemplate += FlexiJS.Utils.Strings.FormatString(
                "<a href='javascript:FlexiJS.Controls.DashboardGrid.GenerateBulkReviewPDF({0},{1},{2},{3},{4});' onclick=\"FlexiJS.Browser.GoogleAnalytics.EventTracking('event', 'Grid Export', 'Click', 'Download all as PDF');\"  class='fx-gmsdashboardgridlink'><img src='/RadControls/FlexiGrid/Grid/ExportToPDF.gif' title='{5}'>{5}</a>",
                AppStatus, Stage, StageStatus, DashboardgroupId, ScoreRoundId, thisGridProperties.ExportToPDFText
            );

            /* if export with additional data and GenerateBulkReviewPDF enabled, add wrapping class */
            if (pageOptions.isReviewerMode !== "true") {
                exportWrapperClass = 'export-tools wrapping';
                bulkWrapperClass = 'bulkdropdown-tools wrapping';
            }
        }

        headerTemplate = FlexiJS.Utils.Strings.FormatString(
            "<div class='dashboard-grid-action-base flex-aligncenter'><div class='{2}'><select class='dashboard-grid-action-select-base quick-action-menu' style='float:left;'></select><button class='js-btn-spinner has-spinner dashboard-grid-action-button-base fx-gmssummaryaddtag fg_button' onclick='FlexiJS.Controls.DashboardGrid.ProcessBulkActions(\"{3}\",\"{4}\"); return false;'>Process</button></div><div style='float:right; display:none' class='dashboard-grid-action-rollback-base half-margin-left' onclick='FlexiJS.Controls.DashboardGrid.GridPerformRollback(\"{3}\",\"{4}\"); return false;'><a href='' class='fg_button secondary with-icon undo'>Undo</a></div></div><div class='{0} float-right'>{1}</div>",
            exportWrapperClass, headerTemplate, bulkWrapperClass, ControlId, GridPropertyId
        );

        control.append(
            FlexiJS.Utils.Strings.FormatString(
                "<div class='dashboard-grid-control-base {0} FlexiKendoGrid'><div class='dashboard-grid-base'></div><div class='GridExpandLinkCont'><a href='#' class='GridExpandLinkContLink' onclick='FlexiJS.Controls.DashboardGrid.GridExpandCollapse(this,{1}); return false;'>{2}</a></div></div>",
                defaultClass, GridPropertyId, thisGridProperties.ExpandText
            )
        );
        control[0].AppStatus = AppStatus;
        control[0].Stage = Stage;
        control[0].StageStatus = StageStatus;
        control[0].DashboardgroupId = DashboardgroupId;

        var grid = control.find(".dashboard-grid-base").kendoGrid({
            toolbar: [{ name: "Export", template: headerTemplate }],
            dataSource: {
                serverFiltering: true,
                transport: {
                    read: {
                        url: "/gms/WebServices/GMSService.svc/GetDashboardApplications",
                        type: "POST",
                        contentType: "application/json",
                        data: function () {
                            var filterData = FlexiJS.Controls.DashboardGrid.GetFilterData();
                            var obj = new Object();
                            obj.areaFilterId = Number(filterData.area);
                            obj.gmsFundTypeId = filterData.fundType;
                            obj.gmsFundId = filterData.fund;
                            obj.applicationUserFilterId = Number(filterData.application);
                            obj.applicationCategoryId = Number(filterData.applicationCategory);
                            obj.applicationTags = filterData.tags;
                            obj.fundStatusId = Number(filterData.fundStatus);
                            obj.AppStatus = AppStatus;
                            obj.Stage = Stage;
                            obj.StageStatus = StageStatus;
                            obj.DashboardGroupItem = DashboardgroupId;
                            obj.DateFilter = Number(filterData.date);
                            obj.DateFilterType = Number(filterData.dateType);
                            obj.DateFilterStart = filterData.dateStart;
                            obj.DateFilterEnd = filterData.dateEnd;
                            obj.ContactFilter = filterData.contact;
                            obj.OrgFilter = filterData.organisation;
                            obj.ProjectFilter = filterData.project;
                            obj.IncludeFailed = filterData.includeFailed;
                            obj.RegionFilter = filterData.region;
                            obj.FundingSourceFilter = filterData.fundingSource;
                            obj.ScoreRoundId = ScoreRoundId
                            return obj;
                        }
                    },
                    parameterMap: function (options) {
                        return JSON.stringify(options);
                    }
                },
                schema: {
                    data: FlexiJS.Controls.DashboardGrid.GetApplicationData,
                    total: FlexiJS.Controls.DashboardGrid.GetApplicationTotal,
                    model: {
                        fields: {
                            AppRef: { type: "string" },
                            Applicant: { type: "string" },
                            LeadPerson: { type: "string" },
                            Status: { type: "string" },
                            DateSubmitted: { type: "string" },
                            AreaName: { type: "string" }
                        }
                    }
                },
                pageSize: thisGridProperties.PageSize,
                serverPaging: true,
                serverSorting: true,
                sort: {
                    field: thisGridProperties.DefaultSortColumn,
                    dir: thisGridProperties.DefaultSortDirection
                }
            },
            height: "100%",
            filterable: false,
            sortable: true,
            resizable: true,
            selectable: "multiple",
            pageable: {
                pageSize: 20,
                refresh: true,
                buttonCount: 8,
                pageSizes: [20, 50, 100, 250, 500, "all"]
            },
            change: FlexiJS.Controls.DashboardGrid.GridRowSelected,
            dataBound: FlexiJS.Controls.DashboardGrid.GridDataBound,
            columns: (function () {
                var returnColumns = [];
                var cbCol = new Object();
                cbCol.exportonly = false;
                cbCol.field = "";
                cbCol.format = null;
                cbCol.template = "<input type='checkbox' class='checkbox dashboard-grid-row-checkbox-base' id='chk_" + ControlId + "' controlid='" + ControlId + "'/>";
                cbCol.headerTemplate = "<label for='checkbox-table-head_" + ControlId + "' class='dashboard-grid-header-selected-count-base' aria-label='Rows Selected'></label><input id='checkbox-table-head_" + ControlId + "' type='checkbox' class='checkbox dashboard-grid-header-checkbox-base' controlid='" + ControlId + "' />";
                cbCol.title = "";
                cbCol.width = 48;
                cbCol.sticky = true;
                returnColumns.push(cbCol);
                $.each(thisGridProperties.Columns, function (index, value) {
                    if (!value.exportonly) { returnColumns.push(value); }
                });
                return returnColumns;
            })()
        }).data("kendoGrid");

        grid.bind("dataBound", FlexiJS.Controls.DashboardGrid.ResetGridPosition);
        grid.AllowedActions = AllowedActions;
        grid.ControlId = ControlId;
        grid.ActivatingControl = Sender;
        grid.GridPropertyId = GridPropertyId;
        FlexiJS.Controls.DashboardGrid.ShowGridActions(control, AllowedActions, thisGridProperties);
        FlexiJS.Controls.DashboardGrid.HighlightShowingGridControl(Sender);
        grid.table.on("click", ".checkbox", FlexiJS.Controls.DashboardGrid.GridCheckRow);
        grid.thead.find('#checkbox-table-head_' + ControlId).click(function (e) { FlexiJS.Controls.DashboardGrid.GridHeaderSelectChange(e); });


        var rollbackBatchId = FlexiJS.Controls.DashboardGrid.GridProperties[grid.GridPropertyId].RollbackBatchId;
        if (rollbackBatchId != typeof ('undefined') && rollbackBatchId != null) {
            FlexiJS.GMS.QuickAction.ShowHideRollbackIcon(grid, true);
        }


        // Destroy all tooltips to prevent layered tooltip issue
        control.kendoTooltip().data("kendoTooltip").destroy();
        // Width must be past to the tooltip for initial position   
        var toolTip = control.kendoTooltip({
            filter: ".applicationgridicontray",
            width: 240,
            autoHide: false,
            show: function (e) {
                FlexiJS.Controls.DashboardGrid.ClearTooltipTimeout(FlexiJS.Controls.DashboardGrid.timeoutHide);

                FlexiJS.Controls.DashboardGrid.isHoveringCell.tooltip = false;
                FlexiJS.Controls.DashboardGrid.isHoveringCell.img = true;
                FlexiJS.Controls.DashboardGrid.isHoveringCell.arrow = false;

                var tooltip = this;
                var target = tooltip.target();

                tooltip.popup.element.addClass('applicationgridquickactiontooltip-content');
                tooltip.popup.element.click(function (e) {
                    $(this).parent().hide();
                });

                var callout = tooltip.popup.wrapper.find('.k-callout');

                // Needed for Firefox tooltip styling issues
                var FF = !(window.mozInnerScreenX == null);
                if (FF) {
                    callout.addClass('applicationgridquickactiontooltip-callout-ff');
                } else {
                    callout.addClass('applicationgridquickactiontooltip-callout');
                }

                tooltip.popup.element.find('.k-tooltip-button').hide();

                tooltip.popup.element.hover(function () {
                    FlexiJS.Controls.DashboardGrid.isHoveringCell.tooltip = true;
                    FlexiJS.Controls.DashboardGrid.ClearTooltipTimeout(FlexiJS.Controls.DashboardGrid.timeoutHide);
                }, function () {
                    FlexiJS.Controls.DashboardGrid.isHoveringCell.tooltip = false;
                    FlexiJS.Controls.DashboardGrid.timeoutHide = setTimeout(function () {
                        if (FlexiJS.Controls.DashboardGrid.isHoveringCell.arrow == false && FlexiJS.Controls.DashboardGrid.isHoveringCell.img == false && FlexiJS.Controls.DashboardGrid.isHoveringCell.tooltip == false) {
                            tooltip.hide();
                        }
                    }, 1500);
                });

                target.hover(function () {
                    FlexiJS.Controls.DashboardGrid.isHoveringCell.img = true;
                    FlexiJS.Controls.DashboardGrid.ClearTooltipTimeout(FlexiJS.Controls.DashboardGrid.timeoutHide);
                }, function () {
                    FlexiJS.Controls.DashboardGrid.isHoveringCell.img = false;
                    FlexiJS.Controls.DashboardGrid.timeoutHide = setTimeout(function () {
                        if (FlexiJS.Controls.DashboardGrid.isHoveringCell.arrow == false && FlexiJS.Controls.DashboardGrid.isHoveringCell.img == false && FlexiJS.Controls.DashboardGrid.isHoveringCell.tooltip == false) {
                            tooltip.hide();
                        }
                    }, 1500);
                });

                callout.hover(function () {
                    FlexiJS.Controls.DashboardGrid.isHoveringCell.arrow = true;
                    FlexiJS.Controls.DashboardGrid.ClearTooltipTimeout(FlexiJS.Controls.DashboardGrid.timeoutHide);
                }, function () {
                    var element = $(this);
                    FlexiJS.Controls.DashboardGrid.isHoveringCell.arrow = false;
                    FlexiJS.Controls.DashboardGrid.timeoutHide = setTimeout(function () {
                        if (FlexiJS.Controls.DashboardGrid.isHoveringCell.arrow == false && FlexiJS.Controls.DashboardGrid.isHoveringCell.img == false && FlexiJS.Controls.DashboardGrid.isHoveringCell.tooltip == false) {
                            tooltip.hide();
                        }
                    }, 1500);
                });

            },
            position: "bottom"
        }).data("kendoTooltip");
    }
};

FlexiJS.Controls.DashboardGrid.ShowPanelAssignmentTool = function (AppStatus, Stage, StageStatus, DashboardgroupId, ScoreRoundId) {
    var filterData = FlexiJS.Controls.DashboardGrid.GetFilterData();
    var obj = new Object();
    obj.page = 0;
    obj.pageSize = 0;
    obj.sort = null;
    obj.areaFilterId = Number(filterData.area);
    obj.gmsFundTypeId = filterData.fundType;
    obj.gmsFundId = filterData.fund;
    obj.applicationUserFilterId = Number(filterData.application);
    obj.applicationCategoryId = Number(filterData.applicationCategory);
    obj.applicationTags = filterData.tags;
    obj.fundStatusId = Number(filterData.fundStatus);
    obj.AppStatus = AppStatus;
    obj.Stage = Stage;
    obj.StageStatus = StageStatus;
    obj.DashboardGroupItem = DashboardgroupId;
    obj.DateFilter = Number(filterData.date);
    obj.DateFilterType = Number(filterData.dateType);
    obj.DateFilterStart = filterData.dateStart;
    obj.DateFilterEnd = filterData.dateEnd;
    obj.ContactFilter = filterData.contact;
    obj.OrgFilter = filterData.organisation;
    obj.ProjectFilter = filterData.project;
    obj.IncludeFailed = filterData.includeFailed;
    obj.RegionFilter = filterData.region;
    obj.FundingSourceFilter = filterData.fundingSource;
    obj.ScoreRoundId = ScoreRoundId;
    obj.ReturnOnlyApplicationIds = true;

    $.ajax({
        type: "POST",
        url: "/gms/WebServices/GMSService.svc/GetDashboardApplications",
        data: JSON.stringify(obj),
        processData: false,
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        async: false,
        success: function (result) {
            var appList = result.d;
            if (appList && appList.hasOwnProperty('ApplicationIdList') && appList.ApplicationIdList.length > 0) {

                var applicationIdList = appList.ApplicationIdList;
                FlexiJS.Controls.DashboardGrid.StashAppIds(applicationIdList, function(err, stashData) {

                    if (err) {

                        showNotificationMessage("failure", err.Message);

                    } else {

                        openRadWindow(
                            FlexiJS.Utils.Strings.FormatString(
                                '/gms/BatchActionModal.aspx?id={0}&action=45003&exitaction=reloaddbgrid&bulkapplist={1}&rwndrnd={2}',
                                applicationIdList[0],
                                stashData.stashId,
                                Math.random()
                            ),
                            'auto',
                            995);
                    }

                    //remove spinner
                    FlexiJS.Utils.ManageButtonSpinner('hide', null);

                });

            } else {

                //remove spinner
                FlexiJS.Utils.ManageButtonSpinner('hide', null);

                showNotificationMessage('error', FlexiJS.Resources.GetResourceText('gms/applicationdashboard.aspx', 'Notification.PanelAssignmentNoApplications.text'));
            }
        }
    });
};

FlexiJS.Controls.DashboardGrid.SetGridProperties = function (force) {
    if (FlexiJS.Controls.DashboardGrid.GridProperties == null || force == true) {
        var reviver;
        var pageOptions = FlexiJS.Controls.DashboardGrid.GetPageOptionsData();
        FlexiJS.Controls.DashboardGrid.GridProperties = JSON.parse(pageOptions.gridProperties, reviver);
        if (pageOptions.gridActions) {
            FlexiJS.Controls.DashboardGrid.GridActions = JSON.parse(pageOptions.gridActions, reviver);
        }
    }
};

FlexiJS.Controls.DashboardGrid.GridExpandCollapse = function (Link, GridPropertyId) {
    var pageOptions = FlexiJS.Controls.DashboardGrid.GetPageOptionsData();
    var cont = $(Link).closest(".dashboard-grid-control-base");

    if (pageOptions.expandedGridsMode == 'true') {
        if (cont.hasClass('AppGridCont')) {
            cont.removeClass('AppGridCont');
            cont.addClass('AppGridContSmlExpandedMode');
            cont.find('.GridExpandLinkContLink').text(FlexiJS.Controls.DashboardGrid.GridProperties[GridPropertyId].ExpandText);
            var grid = cont.find(".dashboard-grid-base").data("kendoGrid");
            grid.refresh();
        } else {
            cont.addClass('AppGridCont');
            cont.removeClass('AppGridContSmlExpandedMode');
            cont.find('.GridExpandLinkContLink').text(FlexiJS.Controls.DashboardGrid.GridProperties[GridPropertyId].CollapseText);
            var grid = cont.find(".dashboard-grid-base").data("kendoGrid");
            grid.refresh();
        }
    } else {
        if (cont.hasClass('AppGridCont')) {
            cont.removeClass('AppGridCont');
            cont.addClass('AppGridContSml');
            cont.find('.GridExpandLinkContLink').text(FlexiJS.Controls.DashboardGrid.GridProperties[GridPropertyId].ExpandText);
            var grid = cont.find(".dashboard-grid-base").data("kendoGrid");
            grid.refresh();
        } else {
            cont.addClass('AppGridCont');
            cont.removeClass('AppGridContSml');
            cont.find('.GridExpandLinkContLink').text(FlexiJS.Controls.DashboardGrid.GridProperties[GridPropertyId].CollapseText);
            var grid = cont.find(".dashboard-grid-base").data("kendoGrid");
            grid.refresh();
        }
    }
};

FlexiJS.Controls.DashboardGrid.GenerateBulkReviewPDF = function (AppStatus, Stage, StageStatus, DashboardgroupId, ScoreRoundId) {
    var filterData = FlexiJS.Controls.DashboardGrid.GetFilterData();
    var appResp = new Object();
    appResp.page = 0;
    appResp.pageSize = 0;
    appResp.sort = null;
    appResp.areaFilterId = Number(filterData.area);
    appResp.gmsFundTypeId = filterData.fundType;
    appResp.gmsFundId = filterData.fund;
    appResp.applicationUserFilterId = Number(filterData.application);
    appResp.applicationCategoryId = Number(filterData.applicationCategory);
    appResp.applicationTags = filterData.tags;
    appResp.fundStatusId = Number(filterData.fundStatus);
    appResp.AppStatus = AppStatus;
    appResp.Stage = Stage;
    appResp.StageStatus = StageStatus;
    appResp.DashboardGroupItem = DashboardgroupId;
    appResp.DateFilter = Number(filterData.date);
    appResp.DateFilterType = Number(filterData.dateType);
    appResp.DateFilterStart = filterData.dateStart;
    appResp.DateFilterEnd = filterData.dateEnd;
    appResp.ContactFilter = filterData.contact;
    appResp.OrgFilter = filterData.organisation;
    appResp.ProjectFilter = filterData.project;
    appResp.IncludeFailed = filterData.includeFailed;
    appResp.RegionFilter = filterData.region;
    appResp.FundingSourceFilter = filterData.fundingSource;
    appResp.ScoreRoundId = ScoreRoundId;
    appResp.ReturnOnlyApplicationIds = false;

    $.ajax({
        type: "POST",
        url: "/gms/WebServices/GMSService.svc/GetDashboardApplications",
        data: JSON.stringify(appResp),
        processData: false,
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        async: false,
        success: function (result) {
            var appResp = result.d;
            if (appResp && appResp.hasOwnProperty('ApplicationIdList') && appResp.ApplicationIdList.length > 0 && appResp.hasOwnProperty('Applications') && appResp.Applications.length == appResp.ApplicationIdList.length) {
                FlexiJS.Controls.DashboardGrid.GenerateBulkReviewPDFByIds(FlexiJS.PDF.GetSortedApplicationIds(appResp.Applications));
            }
        },
        error: function (ex) { FlexiJS.Error.LogError(ex, 'FlexiJS.Controls.DashboardGrid.GenerateBulkReviewPDF'); }
    });
};

FlexiJS.Controls.DashboardGrid.GenerateBulkReviewPDFByIds = function (IdList) {
    var apps = IdList[0].toString();
    for (var i = 1; i < IdList.length; i++) {
        apps += ":" + IdList[i];
    }
    FlexiJS.PDF.PopupDecision(null, 44644, apps, FlexiJS.Controls.DashboardGrid.IsReviewer ? FlexiJS.Enums.System.PDFGenerationAudience.Reviewer : FlexiJS.Enums.System.PDFGenerationAudience.GMSAdmin);
};

FlexiJS.Controls.DashboardGrid.GridHeaderSelectChange = function (Event) {
    var cb = $(Event.target);
    var checked = Event.target.checked;
    var control = cb.parents().find('#' + cb.attr('controlid') + ' .dashboard-grid-base');
    var dbGrid = control.data("kendoGrid");

    var allRows = dbGrid.tbody.children('tr');
    if (dbGrid.SelectedRows == null) {
        dbGrid.SelectedRows = {};
    }

    for (var i = 0; i < allRows.length; i++) {
        var dataItem = dbGrid.dataItem(allRows[i]);

        dbGrid.SelectedRows[dataItem.ApplicationId] = new Object();
        dbGrid.SelectedRows[dataItem.ApplicationId].Selected = checked;
        dbGrid.SelectedRows[dataItem.ApplicationId].DataItem = dataItem;

        if (checked) {
            $(allRows[i]).addClass("k-state-selected");
        } else {
            $(allRows[i]).removeClass("k-state-selected");
        }

        $(allRows[i]).find('.dashboard-grid-row-checkbox-base')[0].checked = checked;

    }

    FlexiJS.Controls.DashboardGrid.SetSelectedCount(dbGrid);
};

FlexiJS.Controls.DashboardGrid.GridUnselectAll = function (Grid) {
    var allRows = Grid.tbody.children('tr');
    Grid.SelectedRows = {};

    for (var i = 0; i < allRows.length; i++) {
        $(allRows[i]).removeClass("k-state-selected");
        $(allRows[i]).find('.dashboard-grid-row-checkbox-base')[0].checked = false;
    }

    FlexiJS.Controls.DashboardGrid.SetSelectedCount(Grid);
};

FlexiJS.Controls.DashboardGrid.GridRowSelected = function (Event) {
    var allRows = Event.sender.table.find('tr');
    var controlId = Event.sender.ControlId;

    var pageActualSelected = $.map(this.select(), function (item) {
        var row = $(item);
        var control = row.parents().find('#' + controlId + ' .dashboard-grid-base')
        var dbGrid = control.data("kendoGrid");
        var dataItem = dbGrid.dataItem(row);

        if (dbGrid.SelectedRows == null) {
            dbGrid.SelectedRows = {};
        }

        return dataItem.ApplicationId;

    });

    for (var i = 0; i < allRows.length; i++) {
        var row = $(allRows[i]);
        row.find('.dashboard-grid-row-checkbox-base')[0].checked = false;
        var control = row.parents().find('#' + controlId + ' .dashboard-grid-base')
        var dbGrid = control.data("kendoGrid");

        var dataItem = dbGrid.dataItem(row);

        if (dbGrid.SelectedRows == null) {
            dbGrid.SelectedRows = {};
        }

        if (dbGrid.SelectedRows[dataItem.ApplicationId] != null) {
            if (jQuery.inArray(dataItem.ApplicationId, pageActualSelected) == -1) {
                dbGrid.SelectedRows[dataItem.ApplicationId] = new Object();
                dbGrid.SelectedRows[dataItem.ApplicationId].Selected = false;
                dbGrid.SelectedRows[dataItem.ApplicationId].DataItem = dataItem;
            }
        }
    }


    var selected = $.map(this.select(), function (item) {

        var row = $(item);
        var control = row.parents().find('#' + controlId + ' .dashboard-grid-base')
        var dbGrid = control.data("kendoGrid");

        var dataItem = dbGrid.dataItem(row);

        if (dbGrid.SelectedRows == null) {
            dbGrid.SelectedRows = {};
        }
        dbGrid.SelectedRows[dataItem.ApplicationId] = new Object();
        dbGrid.SelectedRows[dataItem.ApplicationId].Selected = true;
        dbGrid.SelectedRows[dataItem.ApplicationId].DataItem = dataItem;
        row.find('.dashboard-grid-row-checkbox-base')[0].checked = true;
    });
    FlexiJS.Controls.DashboardGrid.SetSelectedCount(this);

};

FlexiJS.Controls.DashboardGrid.GridCheckRow = function () {
    var cvControl = $(this);
    var controlId = cvControl.attr('controlid');

    var control = cvControl.parents().find('#' + controlId + ' .dashboard-grid-base')
    var dbGrid = control.data("kendoGrid");
    var checked = this.checked,
        row = cvControl.closest("tr");

    dataItem = dbGrid.dataItem(row);

    if (dbGrid.SelectedRows == null) {
        dbGrid.SelectedRows = {};
    }
    dbGrid.SelectedRows[dataItem.ApplicationId] = new Object();
    dbGrid.SelectedRows[dataItem.ApplicationId].Selected = checked;
    dbGrid.SelectedRows[dataItem.ApplicationId].DataItem = dataItem;

    if (checked) {
        row.addClass("k-state-selected");
    } else {
        row.removeClass("k-state-selected");
    }

    control.find('.dashboard-grid-header-checkbox-base')[0].checked = false;

    FlexiJS.Controls.DashboardGrid.SetSelectedCount(dbGrid);
};

FlexiJS.Controls.DashboardGrid.GetSelectedApplications = function (Grid) {
    if (Grid.SelectedRows == null) {
        Grid.SelectedRows = {};
    }

    var selectedApps = new Array();

    for (var i in Grid.SelectedRows) {
        if (!isNaN(i)) {
            if (Grid.SelectedRows[i].Selected == true) {
                selectedApps.push(Grid.SelectedRows[i]);
            }
        }
    }

    return selectedApps;
};

FlexiJS.Controls.DashboardGrid.SetSelectedCount = function (Grid) {
    if (Grid.SelectedRows == null) {
        Grid.SelectedRows = {};
    }

    var count = FlexiJS.Controls.DashboardGrid.GetSelectedApplications(Grid).length;

    if (count == 0) {
        Grid.thead.find('.dashboard-grid-header-selected-count-base').text("");
    }
    else {
        Grid.thead.find('.dashboard-grid-header-selected-count-base').text("(" + count + ")");
    }

};

FlexiJS.Controls.DashboardGrid.GridDataBound = function (Args) {
    this.thead.find('.dashboard-grid-header-checkbox-base')[0].checked = false;

    if (this.SelectedRows != null) {
        var view = this.dataSource.view();
        var rowIsNotSelected = false;

        for (var i = 0; i < view.length; i++) {
            if (this.SelectedRows[view[i].ApplicationId] && this.SelectedRows[view[i].ApplicationId].Selected) {
                this.tbody.find("tr[data-uid='" + view[i].uid + "']")
                    .addClass("k-state-selected")
                    .find(".checkbox")
                    .prop("checked", true);
            }
            else {
                rowIsNotSelected = true;
            }
        }

        if (rowIsNotSelected == false) {
            this.thead.find('.dashboard-grid-header-checkbox-base')[0].checked = true;
        }
    }

    $.each($(this.element).find('[name="ReviewRank"]'), function (index, item) {
        var controlId = $(item).attr('id');
        var scoreId = $(item).attr('fg_eid');
        $(item).kendoNumericTextBox({decimals: 0, spinners: false, format: 'n0'});
        FlexiJS.Controls.FlexiTextbox.AddAutoUpdateSaveHandler(controlId, scoreId, 44660, 204000, true, true);
    });

    var container = $(Args.sender.table);
    var acts = typeof Args.sender.AllowedActions !== 'undefined' ? Args.sender.AllowedActions.split(',') : '';
    var data = Args.sender.dataSource.data();

    for (var i = 0; i < acts.length; i++) {

        if (acts[i] == "") {
            continue;
        }

        var currentAction = FlexiJS.Controls.DashboardGrid.GridActions[acts[i]];
        var rowIndex = 0;

        if ((currentAction.ActionType == 44901 || currentAction.ActionType == 44902) && currentAction.ActionIcon != null && currentAction.ActionIcon != '' && ((currentAction.ActionPopupURL != null && currentAction.ActionPopupURL != '') || (currentAction.ActionURL != null && currentAction.ActionURL != ''))) {
            container.find("[id$=gridicontray]").each(function () {

                var hasPermission = FlexiJS.Controls.DashboardGrid.CheckAppRowForPermissions(data[rowIndex], currentAction.RequiredRolePrimary, currentAction.RequiredRoleSecondary);

                if (hasPermission == true) {
                    var url = "";
                    var popupUrl = "";
                    var googleAnalyticsScript = "";

                    if (currentAction.ActionURL != null) {
                        url = currentAction.ActionURL.replace(/\[appid\]/g, $(this).attr('appid'));
                    }

                    if (currentAction.ActionPopupURL != null) {
                        popupUrl = currentAction.ActionPopupURL.replace(/\[appid\]/g, $(this).attr('appid'));
                    }

                    // Log Google Analytics event
                    if (currentAction.ActionGoogleAnalytics != null && currentAction.ActionGoogleAnalytics != "") {
                        googleAnalyticsScript = "FlexiJS.Browser.GoogleAnalytics.EventTracking('event', 'Quick Action', 'Open Tool', '" + currentAction.ActionGoogleAnalytics + "');";
                    }

                    // Quickactions, add the appendHtml to the title attribute to build up the kendo tooltip html content
                    var appendHtml = '<a href="' + url + '" target="_blank" onclick="' + googleAnalyticsScript + ' return FlexiJS.Controls.DashboardGrid.PerformAction(this,' + "'" + popupUrl + "'" + ',' + "'" + Args.sender.ControlId + "'" + ');"><img src="' + currentAction.ActionIcon + '" title="' + currentAction.ActionDescription + '"></img><div class="applicationgridquickaction">' + currentAction.ActionDescription + '</div></a>';
                    $(this).attr("title", $(this).attr("title") + appendHtml);
                }

                rowIndex += 1;
            });
        }
        FlexiJS.Controls.DashboardGrid.SetSelectedCount(this);
    }

    var activingControl = Args.sender.ActivatingControl;
    var totalRecords = Args.sender.dataSource.total();

    if (activingControl != null) {
        if ($(activingControl).hasClass('fx-gmssummaryrowtop')) {

        }
        else if (typeof (activingControl.id) == 'undefined') {
            //Check for initial reviewer control
            $("[isdefaultforreviewer$=true]").find(".fx-gmssummaryvalue").text(totalRecords);
        }
        else if (activingControl.id == '') {
            //Check for value
            var valText = $(activingControl).find(".fx-gmssummaryvalue");
            if (valText.length == 0) {
                $(activingControl).parent().find(".fx-gmssummaryvalue").text(totalRecords);
            }
            else {
                valText.text(totalRecords);
            }
        }
    }
};

FlexiJS.Controls.DashboardGrid.CheckAppRowForPermissions = function (Row, RolePrimary, RoleSecondary) {
    var hasPermission = true;

    if (RolePrimary != 0) {
        hasPermission = FlexiJS.Controls.DashboardGrid.CheckAppRowForPermission(Row, RolePrimary);
    }

    if (RoleSecondary != 0 && hasPermission) {
        hasPermission = FlexiJS.Controls.DashboardGrid.CheckAppRowForPermission(Row, RoleSecondary);
    }

    return hasPermission;
};

FlexiJS.Controls.DashboardGrid.CheckAppRowForPermission = function (Row, Role) {
    switch (Role) {
        case 53:
            if (Row.ECFAccess == true) {
                return true;
            }
            break;
        case 57:
            if (Row.ECFPanelSetup == true) {
                return true;
            }
            break;
        case 58:
            if (Row.ECFScoring == true) {
                return true;
            }
            break;
        case 63:
            if (Row.ECFDeleting == true) {
                return true;
            }
            break;
        case 60:
            if (Row.ECFApproval == true) {
                return true;
            }
            break;
        case 65:
            if (Row.ECFSchedulePayment == true) {
                return true;
            }
            break;
        case 89:
            if (Row.ECFKeyUpdates == true) {
                return true;
            }
            break;
        case 93:
            if (Row.ECFBulkEmails == true) {
                return true;
            }
            break;
    }

    return false;
};

FlexiJS.Controls.DashboardGrid.HighlightShowingGridControl = function (Sender) {
    if (Sender != null) {
        if (typeof (Sender.id) == 'undefined') {

            //Check for initial reviewer control
            $("[isdefaultforreviewer$=true]").parents(".fx-gmssummarygroup").find(".AppGridCurrentGrid").removeClass("AppGridCurrentGrid");
            $("[isdefaultforreviewer$=true]").closest(".fx-gmssummaryvaluecell").addClass("AppGridCurrentGrid");
        }
        else if (Sender.id == '') {
            //Check for value
            $(Sender).parents(".fx-gmssummarygroup").find(".AppGridCurrentGrid").removeClass("AppGridCurrentGrid");
            $(Sender).closest(".fx-gmssummaryvaluecell").addClass("AppGridCurrentGrid");

        }
    }
};

FlexiJS.Controls.DashboardGrid.UpdateDashboardNumbers = function (Data) {
    if (typeof (Data.UpdatedStatus) != 'undefined' && Data.UpdatedStatus != null) {
        for (var i = 0; i < Data.UpdatedStatus.length; i++) {
            var enumId = Data.UpdatedStatus[i].Key;
            var updatedValue = Data.UpdatedStatus[i].Value;
            var tCtl = $("[statusenumid=" + enumId + "]");
            tCtl.text(updatedValue);
            if (updatedValue > 0) {
                tCtl.closest('.fx-gmssummaryvaluecell').show();
            }

        }
    }
};

FlexiJS.Controls.DashboardGrid.PerformAction = function (Link, ActionPopupUrl, GridContainerId) {

    //remove spinner
    FlexiJS.Utils.ManageButtonSpinner('hide', null);

    if (ActionPopupUrl == "") {
        FlexiJS.Controls.DashboardGrid.AppendTimelimit(Link);
        return true;
    }
    FlexiJS.Controls.DashboardGrid.GridWithLastAction = GridContainerId;

    var windowName;
    if (ActionPopupUrl.indexOf("actionWindowName") > -1) {
        var querystring = FlexiJS.Utils.GetURLQuerystringObject(ActionPopupUrl);
        if (querystring) {
            if (querystring['actionWindowName']) {
                windowName = querystring['actionWindowName'];
            }
        }
    }

    if (ActionPopupUrl.indexOf("Batch") > -1) {
        var width = ActionPopupUrl.indexOf("action=45002") > -1 ? 1005 : 995;
        if (windowName) {
            openRadWindowWithName(ActionPopupUrl, 500, width, null, windowName);
        } else {
            openRadWindow(ActionPopupUrl, 500, width);
        }
    } else {
        if (windowName) {
            openRadWindowWithName(ActionPopupUrl, 768, 1200, null, windowName, true);
        } else {
            openRadWindowWithName(ActionPopupUrl, 'auto', 1020, null, '', true);
        }
    }

    return false;
};

FlexiJS.Controls.DashboardGrid.AppendTimelimit = function (Link) {
    if (typeof $(Link).attr('origionalurl') == 'undefined') {
        $(Link).attr('origionalurl', $(Link).attr('href'));
    }
    $(Link).attr('href', $(Link).attr('origionalurl').replace("{timelimit}", FlexiJS.Controls.DashboardGrid.GetUtcMilliseconds().toString()));
};

FlexiJS.Controls.DashboardGrid.ShowGridActions = function (Container, AllowedActions, GridPropertyList) {
    var actionsCon = Container.find(".dashboard-grid-action-base");
    var actionsList = Container.find(".dashboard-grid-action-select-base");

    actionsList.find('option').remove();

    if (AllowedActions == '') {
        actionsCon.hide();
    }
    else {
        actionsList.append('<option value="0">' + GridPropertyList.ActionsDefaultItem + '</option>');
        actionsList.attr('aria-label', GridPropertyList.ActionsDefaultItem);
        actionsCon.find(".dashboard-grid-action-button-base").val(GridPropertyList.ActionsExecuteButtonText);

        var acts = AllowedActions.split(',');

        for (var i = 0; i < acts.length; i++) {
            var action = FlexiJS.Controls.DashboardGrid.GridActions[acts[i]];
            if (action.ActionType == 44900 || action.ActionType == 44902) {
                if (FlexiJS.GMS.QuickAction.ShouldDisplayBulkAction(action, Container[0].StageStatus)) {
                    actionsList.append('<option value="' + action.ActionId + '">' + action.ActionTitle + '</option>');
                }
            }
        }

        if (actionsList.find('option').length > 1) {
            actionsCon.show();
        }
        else {
            actionsCon.hide();
        }
    }
};

FlexiJS.Controls.DashboardGrid.ProcessBulkActions = function (GridContainerId, GridPropertyId, ActionIdOverride, AdditionalParameter, SelectedOverride) {

    var container = $("[id$=" + GridContainerId + "]");
    var grid = container.find(".dashboard-grid-base").data("kendoGrid");
    var selected = FlexiJS.Controls.DashboardGrid.GetSelectedApplications(grid);
    var button = $(container).find(".dashboard-grid-action-button-base");

    // If SelectedOverride has been set, we filter out other applications
    if (SelectedOverride) {
        selected = selected.filter(function (app) {
            var found = SelectedOverride.find(function (override) {
                return override == app.DataItem.ApplicationId;
            });

            return found != null;
        });
    }

    var selectedActionId = ActionIdOverride == null ? container.find(".dashboard-grid-action-select-base").val() : ActionIdOverride;

    if (selectedActionId == "0") {
        alert(FlexiJS.Controls.DashboardGrid.GridProperties[GridPropertyId].ActionsNoActionSelected);
        return;
    }
    else if (selected.length == 0) {
        alert(FlexiJS.Controls.DashboardGrid.GridProperties[GridPropertyId].ActionsNoAppSelected);
        return;
    }

    var selectedAction = FlexiJS.Controls.DashboardGrid.GridActions[selectedActionId];

    // Log Google Analytics event
    if (selectedAction.ActionGoogleAnalytics != null && selectedAction.ActionGoogleAnalytics != "") {
        FlexiJS.Browser.GoogleAnalytics.EventTracking('event', 'Bulk Action', 'Open Tool', selectedAction.ActionGoogleAnalytics);
    }
    
    if (selectedAction.ActionURL != null && selectedAction.ActionURL != "") {

        //Action is opening a URL
        for (var i = 0; i < selected.length; i++) {
            var dataItem = grid.dataItem(selected[i]);
            FlexiJS.Controls.DashboardGrid.ProcessBulkAction(dataItem, selectedAction);
        }
    }
    else {
        //Action is to be executed
        var appList = new Array();

        var withPermission = 0;
        var withoutPermisison = 0;

        var permissionRefs = new Array();
        var withoutpermissionRefs = new Array();

        for (var i = 0; i < selected.length; i++) {

            var dataItem = selected[i].DataItem;
            if (FlexiJS.Controls.DashboardGrid.CheckAppRowForPermissions(dataItem, selectedAction.RequiredRolePrimary, selectedAction.RequiredRoleSecondary)) {
                //Has permission
                withPermission += 1;
                appList.push(dataItem);
                permissionRefs.push(dataItem.AppRef);
            }
            else {
                //Does not have permission
                withoutPermisison += 1;
                withoutpermissionRefs.push(dataItem.AppRef);
            }
        }

        if (withPermission == 0) {
            //Does not have permission to do anything
            alert(selectedAction.NoPermission);
        }
        else if (withoutPermisison != 0) {
            //Has permission for some
            if (selectedAction.ConfirmationRequired) {
                var confMessage = selectedAction.ConfirmationPartialPermission;
                confMessage = confMessage.replace(/\[TOTAL\]/g, (withPermission + withoutPermisison).toString());
                confMessage = confMessage.replace(/\[NOPERMISSION\]/g, (withoutPermisison).toString());
                confMessage = confMessage.replace(/\[PERMISSION\]/g, (withPermission).toString());
                confMessage = confMessage.replace(/\[LINEBREAK\]/g, '\n');

                var appsWithPermission = "";
                var appsWithoutPermission = "";

                for (var i = 0; i < permissionRefs.length; i++) {
                    appsWithPermission += '\n';
                    appsWithPermission += permissionRefs[i];
                }

                for (var i = 0; i < withoutpermissionRefs.length; i++) {
                    appsWithoutPermission += '\n'
                    appsWithoutPermission += withoutpermissionRefs[i];
                }

                confMessage = confMessage.replace(/\[NOPERMISSIONREFS\]/g, appsWithoutPermission);
                confMessage = confMessage.replace(/\[PERMISSIONREFS\]/g, appsWithPermission);

                if (confirm(confMessage)) {
                    FlexiJS.Controls.DashboardGrid.ExecuteBulkAction(appList, selectedAction, grid, AdditionalParameter);
                }
            }
        }
        else {
            //Has permission for all
            if (selectedAction.ConfirmationMessage && selectedAction.ConfirmationRequired) {
                if (confirm(selectedAction.ConfirmationMessage)) {
                    FlexiJS.Controls.DashboardGrid.ExecuteBulkAction(appList, selectedAction, grid, AdditionalParameter);
                }
            }
            else {
                FlexiJS.Controls.DashboardGrid.ExecuteBulkAction(appList, selectedAction, grid, AdditionalParameter);
            }
        }
    }

    return false;
};

FlexiJS.Controls.DashboardGrid.ExecuteBulkAction = function (SelectedDataItems, Action, Grid, AdditionalParameter) {

    // Prevent button spamming
    var button = $("#" + Grid.ControlId).find(".dashboard-grid-action-button-base");
    button.attr("disabled", true);
    setTimeout(function () {
        button.removeAttr("disabled");
    }, 2500);

    var selectedAppIds = new Array();
    for (var i = 0; i < SelectedDataItems.length; i++) {
        selectedAppIds.push(SelectedDataItems[i].ApplicationId)
    }

    if (selectedAppIds.length > 0) {

        if (Action.ActionPopupURL == null || Action.ActionPopupURL == "") {

            if (Action.InternalActionId == FlexiJS.Enums.System.QuickAction.CloseApplication || Action.InternalActionId == FlexiJS.Enums.System.QuickAction.ReopenApplication || Action.InternalActionId == FlexiJS.Enums.System.QuickAction.DeleteApplication) {
                //Show Loader
                var rkName;
                switch (Action.InternalActionId) {
                    case FlexiJS.Enums.System.QuickAction.CloseApplication:
                        rkName = 'Close';
                        break;
                    case FlexiJS.Enums.System.QuickAction.ReopenApplication:
                        rkName = 'Reopen';
                        break;
                    case FlexiJS.Enums.System.QuickAction.DeleteApplication:
                        rkName = 'Delete';
                        break;
                }
                FlexiJS.Popup.ShowProgressLoader(FlexiJS.Resources.GetResourceText('QuickActions', 'QuickActions_Bulk' + rkName + 'Application_ProgressLabel'), FlexiJS.Resources.GetResourceText('QuickActions', 'QuickActions_Bulk' + rkName + 'Application_ProgressPleaseWait'), FlexiJS.Resources.GetResourceText('QuickActions', 'QuickActions_Bulk' + rkName + 'Application_ProgressMessage'));
            }

            //Action is server side
            Fluent.Website.GMS.GMSService.PerformApplicationBatchAction(
                Action.ActionId,
                selectedAppIds,
                null,
                function (Result) {
                    FlexiJS.Controls.DashboardGrid.ExecuteBulkActionCompleted(Result, Grid, Action, selectedAppIds.length);
                },
                FlexiJS.Controls.DashboardGrid.ExecuteBulkActionError,
                FlexiJS.Controls.DashboardGrid.ExecuteBulkActionError
            );
        }
        else {
            //Action is in a popup
            var popupUrl = Action.ActionPopupURL.replace(/\[appid\]/g, selectedAppIds[0].toString());
            popupUrl = popupUrl.replace(/\[actid\]/g, Action.ActionId);

            FlexiJS.Utils.ManageButtonSpinner('show', button);

            // Check for "special" actions
            switch (Action.InternalActionId) {
                case FlexiJS.Enums.System.QuickAction.BulkPDFGeneration:
                    // Here we need to check if any of the applications are post-submission
                    var postSubmission = $.grep(SelectedDataItems, function (app) { return app.StageTypeId > 70201; }).length > 0;
                    FlexiJS.PDF.PopupDecision(Grid, 44644, selectedAppIds.join(':'), FlexiJS.Controls.DashboardGrid.IsReviewer ? FlexiJS.Enums.System.PDFGenerationAudience.Reviewer : FlexiJS.Enums.System.PDFGenerationAudience.GMSAdmin, !postSubmission);
                    break;
                case FlexiJS.Enums.System.QuickAction.BulkGrantAccessToFundType:
                    FlexiJS.GMS.QuickAction.GrantAccessToFundType(Action, Grid, selectedAppIds.join(':'));
                    break;
                case FlexiJS.Enums.System.QuickAction.BulkAttribute:
                    FlexiJS.GMS.QuickAction.BulkAttribute(Action, Grid, selectedAppIds.join(':'));
                    break;
                case FlexiJS.Enums.System.QuickAction.BulkReportingForm:
                    FlexiJS.GMS.QuickAction.BulkReportingForm(Action, Grid, selectedAppIds.join(':'));
                    break;
                case FlexiJS.Enums.System.QuickAction.BulkChecking:
                    FlexiJS.GMS.QuickAction.BulkChecking(Action, Grid, selectedAppIds.join(':'));
                    break;
                default:
                    FlexiJS.Controls.DashboardGrid.StashAppIds(selectedAppIds, function(err, stashData) {

                        if (err) {
                            //remove spinner
                            FlexiJS.Utils.ManageButtonSpinner('hide', null);

                            showNotificationMessage("failure", err.message || err);

                        } else {
                            if (stashData.appId) {
                                //Redo as app id may have changed based on reference order
                                popupUrl = Action.ActionPopupURL.replace(/\[appid\]/g, stashData.appId);
                                popupUrl = popupUrl.replace(/\[actid\]/g, Action.ActionId);
                            }
                            popupUrl = popupUrl.replace(/\[applist\]/g, stashData.stashId);
                            if (AdditionalParameter != null) {
                                popupUrl += AdditionalParameter;
                            }
                            FlexiJS.Controls.DashboardGrid.PerformAction(null, popupUrl, Grid.ControlId);

                        }

                    });
                    break;
            }
        }
    }
};

FlexiJS.Controls.DashboardGrid.StashAppIds = function(appIds, callback) {

    var appIdCSV = [].concat(appIds || []).join(",");
    Fluent.Website.GMS.GMSService.GenerateApplicationIdDataStash(
        appIdCSV,
        function (data) {

            if (!(data && data.Result)) {

                var message = data.ResultMessage || FlexiJS.Resources.GetResourceText('gms/applicationdashboard.aspx', 'UnspecifiedServerError');
                callback(new Error(message));

            } else {

                callback(null, JSON.parse(data.ResultDetail));

            }

        },
        callback
    );

};

FlexiJS.Controls.DashboardGrid.ExecuteBulkActionCompleted = function (Result, Grid, Action, SelectedApplicationCount) {

    if (Result.Result) {
        var resultDetail = JSON.parse(Result.ResultDetail);
    }

    if (Result.ResultMessage != null && Result.ResultMessage != "") {

        if (Action.InternalActionId == FlexiJS.Enums.System.QuickAction.CloseApplication || Action.InternalActionId == FlexiJS.Enums.System.QuickAction.ReopenApplication) {
            setTimeout(function () {
                FlexiJS.Popup.RemoveProgressLoader();
                //For Close/Reopen Application - show kendo popup
                FlexiJS.GMS.QuickAction.BulkCloseReopenShowResultsWindow(Action, resultDetail);
            }, 4000);
           
        }
        else {

            if (Action.InternalActionId == FlexiJS.Enums.System.QuickAction.DeleteApplication) {
                setTimeout(function () {
                    //For Delete application - Hide Progress loader
                    FlexiJS.Popup.RemoveProgressLoader();
                }, 4000);
            }

            if (Result.Result) {
                showNotificationMessage('success', Result.ResultMessage);
            }
            else {
                showNotificationMessage('error', Result.ResultMessage);
            }
        }

    }

    if (Result.Result) {

        if (Action.Undoable) {
            FlexiJS.Controls.DashboardGrid.GridProperties[Grid.GridPropertyId].RollbackBatchId = resultDetail.RollbackBatchId;
            FlexiJS.GMS.QuickAction.ShowHideRollbackIcon(Grid, true);
        }
        else {
            FlexiJS.GMS.QuickAction.ShowHideRollbackIcon(Grid, false);
        }
    }

    //Run Other events for the quick action such as updated stage status and application status counts
    FlexiJS.GMS.QuickAction.ExecuteQuickActionCompletedEvents(Action, Grid, resultDetail);
};

FlexiJS.Controls.DashboardGrid.GridPerformRollback = function (GridContainerId) {
    if (!confirm("Are you sure you wish to rollback?")) return;//TODO - Resource Key

    var container = $("[id$=" + GridContainerId + "]");
    var grid = container.find(".dashboard-grid-base").data("kendoGrid");
    var rollbackBatchId = FlexiJS.Controls.DashboardGrid.GridProperties[grid.GridPropertyId].RollbackBatchId;

    if (rollbackBatchId != typeof ('undefined') && rollbackBatchId != null) {
        Fluent.Website.GMS.GMSService.PerformApplicationBatchRollback(
            rollbackBatchId,
            function (Result) {
                FlexiJS.GMS.QuickAction.ExecuteRollbackBulkActionCompleted(Result, grid);
            },
            FlexiJS.GMS.QuickAction.ExecuteRollbackBulkActionError,
            FlexiJS.GMS.QuickAction.ExecuteRollbackBulkActionError
        );
    }
    else {
        FlexiJS.GMS.QuickAction.ShowHideRollbackIcon(grid, false);
    }
};

FlexiJS.Controls.DashboardGrid.ExecuteBulkActionError = function () {
    //TODO - Log this error?
    //remove spinner - we broke something
    FlexiJS.Utils.ManageButtonSpinner('hide', null);
    FlexiJS.Popup.RemoveProgressLoader();
};


FlexiJS.Controls.DashboardGrid.ReloadDashboardGrid = function (GridContainerId) {
    GridContainerId = GridContainerId || FlexiJS.Controls.DashboardGrid.GridWithLastAction;
    if (GridContainerId) {
        var container = $("[id$=" + GridContainerId + "]");
        var grid = container.find(".dashboard-grid-base").data("kendoGrid");
        grid.dataSource.read();
        grid.refresh();
    }
};

FlexiJS.Controls.DashboardGrid.GetUtcMilliseconds = function () {
    var date = new Date();
    return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDay(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getUTCMilliseconds());
};

FlexiJS.Controls.DashboardGrid.ProcessBulkAction = function (DataItem, Action) {
    if (Action.ActionURL != null && Action.ActionURL != "") {
        var url = Action.ActionURL.replace(/\[appid\]/g, DataItem.ApplicationId);
        url = url.replace(/\[timelimit\]/g, FlexiJS.Controls.DashboardGrid.GetUtcMilliseconds().toString());
        window.open(url);
    }
};

FlexiJS.Controls.DashboardGrid.ResetGridPosition = function () {
    var grid = $(".k-grid-content");
    if (grid.length > 0) {
        grid[0].scrollTop = 0;
    }
};

FlexiJS.Controls.DashboardGrid.GetApplicationData = function (Result) {
    FlexiJS.Controls.DashboardGrid.UpdateDashboardNumbers(Result.d);
    return Result.d.Applications;
};

FlexiJS.Controls.DashboardGrid.GetApplicationTotal = function (Result) {
    return Result.d.TotalApplications;
};

FlexiJS.Controls.DashboardGrid.ClearTooltipTimeout = function (timeOutHide) {
    if (timeOutHide != null) {
        clearTimeout(timeOutHide);
    }
};
;FlexiJS.Controls.FinanceOverview = {};
FlexiJS.Controls.FinanceOverview.ResourceSet = 'financeoverview.js';
FlexiJS.Controls.FinanceOverview.OrganisationListChange = function (dropdown, orgFieldId) {
	var orgField = $('#' + orgFieldId);
	orgField.val(dropdown.options[dropdown.selectedIndex].value);
	orgField.trigger('change');
};
FlexiJS.Controls.FinanceOverview.SetupPaymentsHistoryGrid = function (gridId, fieldId, startdateinput, enddateinput, dateRefreshButton, dateValidationMessageElement, invalidDateSpan, dateStartAfterEndSpan) {

	var refreshMethod = function () { $("#" + gridId).data('kendoGrid').dataSource.read(); };
	var validateDates = function () {
		var blockUpdate = false;
		var invalidDate = false;
		var startDatePicker = $('#' + startdateinput).data("kendoDatePicker");
		var startDateValue = startDatePicker.value();
		if (startDateValue == null) {
			startDatePicker.element.addClass('kendodateselect-invalid');
			blockUpdate = true;
			invalidDate = true;
		} else {
			startDatePicker.element.removeClass('kendodateselect-invalid');
		}

		var endDatePicker = $('#' + enddateinput).data("kendoDatePicker");
		var endDateValue = endDatePicker.value();
		if (endDateValue == null) {
			endDatePicker.element.addClass('kendodateselect-invalid');
			blockUpdate = true;
			invalidDate = true;
		} else {
			endDatePicker.element.removeClass('kendodateselect-invalid');
		}

		if (!blockUpdate) {
			if (startDateValue > endDateValue) {
				startDatePicker.element.addClass('kendodateselect-invalid');
				blockUpdate = true;
            }
		}

		var messageElement = $('#' + dateValidationMessageElement);
		var invalidDateSpanElement = $('#' + invalidDateSpan);
		var dateStartAfterEndSpanElement = $('#' + dateStartAfterEndSpan);
		var updatebutton = $('#' + dateRefreshButton);
		if (blockUpdate) {
			if (invalidDate) {
				invalidDateSpanElement.css('display', '');
				dateStartAfterEndSpanElement.css('display', 'none');
			} else {
				invalidDateSpanElement.css('display', 'none');
				dateStartAfterEndSpanElement.css('display', '');
            }
			messageElement.css('display', '');
			updatebutton.addClass('aspNetDisabled');
		}
		if (!blockUpdate) {
			messageElement.css('display', 'none');
			updatebutton.removeClass('aspNetDisabled');
		}

		return !blockUpdate;
    }
	var updateDatesMethod = function () {
		if (!validateDates()) return;

		var startDatePicker = $('#' + startdateinput).data("kendoDatePicker");
		var startDateValue = startDatePicker.value();
		var endDatePicker = $('#' + enddateinput).data("kendoDatePicker");
		var endDateValue = endDatePicker.value();

		var dataElement = $('#' + fieldId);
		if (startDateValue > endDateValue) {
			//add a day to the end date so that it includes the day
			dataElement.data('startdate', endDateValue.getTime());
			dataElement.data('enddate', startDateValue.getTime());
		} else {
			//add a day to the end date so that it includes the day
			dataElement.data('startdate', startDateValue.getTime());
			dataElement.data('enddate', endDateValue.getTime());
        }
    }

	var date = new Date();
	var startDate = new Date(date.getFullYear(), date.getMonth(), 1);
	var endDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());

	var startDatePickerElement = $('#' + startdateinput);
	var endDatePickerElement = $('#' + enddateinput);

	startDatePickerElement.attr("aria-label", FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.StartDateFilter'));
	endDatePickerElement.attr("aria-label", FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.EndDateFilter'));

	startDatePickerElement.attr("placeholder", FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.StartDateFilterPlaceholder'));
	endDatePickerElement.attr("placeholder", FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.EndDateFilterPlaceholder'));

	FlexiJS.Kendo.DatePickers.SetupKendoDatePicker(startdateinput, (startDate.getTime() - startDate.getMilliseconds()) / 1000);
	FlexiJS.Kendo.DatePickers.SetupKendoDatePicker(enddateinput, (endDate.getTime() - endDate.getMilliseconds()) / 1000);
	updateDatesMethod();

	$('#' + fieldId).on('change', refreshMethod);
	$('#' + dateRefreshButton).on('click', function () { if (validateDates() == true) { updateDatesMethod(); refreshMethod(); } });

	var startDatePicker = startDatePickerElement.data("kendoDatePicker");
	var endDatePicker = endDatePickerElement.data("kendoDatePicker");

	startDatePicker.bind("change", function () { validateDates(); });
	endDatePicker.bind("change", function () { validateDates(); });;

	var valueTemplate = '<span# if(Value < 0) {# class="negative-currency"# } #># if(Value < 0) { ##= kendo.format(\'({0:c})\', Math.abs(Value)) ## } else { ##= kendo.format(\'{0:c}\', Value) ## } #</span>';
	var footerTemplate = FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.TotalFooter') + ' # var myTotal = $("\\#' + fieldId + '").data(\'totalValue\'); #<span# if(myTotal < 0) {# class="negative-currency"# } #># if(myTotal < 0) { ##= kendo.format(\'({0:c})\', Math.abs(myTotal)) ## } else { ##= kendo.format(\'{0:c}\', myTotal) ## } #</span>';

	$("#" + gridId).kendoGrid({
		toolbar: ['excel'],
		excel: {
			allPages: true
		},
		dataSource: {
			pageSize: 20,
			serverPaging: true,
			serverFiltering: true,
			serverSorting: true,
			transport: {
				read: { url: "/GMS/WebServices/GMSService.svc/GetOrganisationPaidPayments", type: "POST", contentType: "application/json" },
				parameterMap: function (options) {
					var ApplicationReferenceFilter = null;
					var InvoiceNumberFilter = null;
					var BudgetTypeFilter = null;
					var DescriptionFilter = null;
					var ValueFilter = null;
					var GLCodeFilter = null;
					var FundTypeNameFilter = null;
					var FundNameFilter = null;

					if (options.filter && options.filter.filters) {
						ApplicationReferenceFilter = FlexiJS.Kendo.Grids.GetFieldFilterValue(options, 'ApplicationReference');
						InvoiceNumberFilter = FlexiJS.Kendo.Grids.GetFieldFilterValue(options, 'InvoiceNumber');
						BudgetTypeFilter = FlexiJS.Kendo.Grids.GetFieldFilterValue(options, 'BudgetType');
						DescriptionFilter = FlexiJS.Kendo.Grids.GetFieldFilterValue(options, 'Description');
						ValueFilter = FlexiJS.Kendo.Grids.GetFieldFilterValue(options, 'Value');
						GLCodeFilter = FlexiJS.Kendo.Grids.GetFieldFilterValue(options, 'GLCode');
						FundTypeNameFilter = FlexiJS.Kendo.Grids.GetFieldFilterValue(options, 'FundTypeName');
						FundNameFilter = FlexiJS.Kendo.Grids.GetFieldFilterValue(options, 'FundName');
					}

					var orderColumn = null;
					var orderDirection = null;
					if (options.sort && options.sort[0]) {
						orderColumn = options.sort[0].field;
						orderDirection = options.sort[0].dir;
					}

					var dataField = $("#" + fieldId);

					return JSON.stringify({
						OrganisationId: dataField.val(),
						StartDate: '/Date(' + dataField.data('startdate') + ')/',
						EndDate: '/Date(' + dataField.data('enddate') + ')/',
						PageSize: options.pageSize,
						PageIndex: options.page,
						OrderColumn: orderColumn,
						OrderDirection: orderDirection,
						ApplicationReferenceFilter: ApplicationReferenceFilter,
						InvoiceNumberFilter: InvoiceNumberFilter,
						BudgetTypeFilter: BudgetTypeFilter,
						DescriptionFilter: DescriptionFilter,
						ValueFilter: ValueFilter,
						GLCodeFilter: GLCodeFilter,
						FundTypeNameFilter: FundTypeNameFilter,
						FundNameFilter: FundNameFilter
					});
				}
			},
			schema: {
				data: function (r) {
					var data = JSON.parse(r.d.ResultDetail);
					$("#" + fieldId).data('totalValue', data.TotalValue);
					return data.Payments;
				},
				total: function (r) { return JSON.parse(r.d.ResultDetail).TotalPayments; },
				model: {
					fields: {
						ApplicationReference: { type: "string" },
						InvoiceNumber: { type: "string" },
						DateInvoice: { type: "date" },
						DatePaid: { type: "date" },
						BudgetType: { type: "string" },
						Description: { type: "string" },
						Value: { type: "decimal" },
						FundTypeName: { type: "string" },
						FundName: { type: "string" },
						ProjectStartDate: { type: "date" },
						ProjectEndDate: { type: "date" },
						PrimaryApplicant: { type: "string" }
					}
				}
			}
		},
		dataBound: function (e) {
			$('[title="Clear"]').remove();
		},
		filterable: {
			mode: "row",
			extra: false,
			operators: {
				string: {
					contains: "Contains"
				}
			}
		},
		sortable: true,
		noRecords: {
			template: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.NoResults')
		},
		pageable: {
			numeric: false,
			refresh: true,
			buttonCount: 5,
			pageSizes: [20, 50, 100, 250]
		},
		excelExport: function (e) {
			var rows = e.workbook.sheets[0].rows;
			var newRows = []
			for (var ri = 0; ri < rows.length; ri++) {
				var row = rows[ri];

				if (row.type !== "group-footer" && row.type !== "footer") {
					newRows.push(row)
				}
			}
			e.workbook.sheets[0].rows = newRows

		},
		columns: [
			{
				field: "ApplicationReference",
				width: 130,
				title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.Column.ApplicationReference'),
				filterable: {
					cell: {
						enabled: true,
						delay: 1000,
						showOperators: false,
						operator: "contains",
						template: function (args) {
							$(args.element).addClass('k-textbox').attr('style', 'width:100%').attr('aria-label', FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.ColumnFilter.ApplicationReference.Label'));
						},
					}
				},
				attributes: { class: "k-gridcell" }
			},
			{
				field: "InvoiceNumber",
				width: 200,
				title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.Column.InvoiceNumber'),
				filterable: {
					cell: {
						enabled: true,
						delay: 1000,
						showOperators: false,
						operator: "contains",
						template: function (args) {
							$(args.element).addClass('k-textbox').attr('style', 'width:100%').attr('aria-label', FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.ColumnFilter.InvoiceNumber.Label'));
						},
					}
				},
				attributes: { class: "k-gridcell" }
			},
			{
				field: "PrimaryApplicant",
				width: 200,
				title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.Column.PrimaryApplicant'),
				filterable: { cell: { enabled: false } },
				attributes: { class: "k-gridcell" }
			},
			{
				field: "DateInvoice",
				width: 110,
				title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.Column.DateInvoice'),
				format: "{0:dd/MM/yyyy}",
				filterable: { cell: { enabled: false } },
				attributes: { class: "k-gridcell" }
			},
			{
				field: "DatePaid",
				width: 110,
				title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.Column.DatePaid'),
				format: "{0:dd/MM/yyyy}",
				filterable: { cell: { enabled: false } },
				attributes: { class: "k-gridcell" }
			},
			{
				field: "BudgetType",
				width: 200,
				title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.Column.BudgetType'),
				filterable: {
					cell: {
						enabled: true,
						delay: 1000,
						showOperators: false,
						operator: "contains",
						template: function (args) {
							$(args.element).addClass('k-textbox').attr('style', 'width:100%').attr('aria-label', FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.ColumnFilter.BudgetType.Label'));
						},
					}
				},
				attributes: { class: "k-gridcell" }
			},
			{
				field: "Description",
				width: 300,
				title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.Column.Description'),
				filterable: { cell: { enabled: false } },
				attributes: { class: "k-gridcell k-gridcell-nooverflow" }
			},
			{
				field: "Value",
				width: 130,
				footerTemplate: footerTemplate,
				title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.Column.Value'),
				filterable: { cell: { enabled: false } },
				template: valueTemplate,
				attributes: { class: "k-gridcell currencycell" }
			},
			{
				field: "ProjectStartDate",
				width: 110,
				title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.Column.ProjectStartDate'),
				format: "{0:dd/MM/yyyy}",
				filterable: { cell: { enabled: false } },
				attributes: { class: "k-gridcell" }
			},
			{
				field: "ProjectEndDate",
				width: 110,
				title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'PaymentHistory.Column.ProjectEndDate'),
				format: "{0:dd/MM/yyyy}",
				filterable: { cell: { enabled: false } },
				attributes: { class: "k-gridcell" }
			}
		]
	});

};
FlexiJS.Controls.FinanceOverview.SetupAwardsGrid = function (gridId, fieldId, includeCompletedCheckbox) {
	var refreshMethod = function () { $("#" + gridId).data('kendoGrid').dataSource.read(); };

	$('#' + fieldId).on('change', refreshMethod);
	$('#' + includeCompletedCheckbox).on('change', function () { refreshMethod(); });

	$("#" + gridId).kendoGrid({
		dataSource: {
			pageSize: 20,
			serverPaging: true,
			serverFiltering: false,
			serverSorting: true,
			transport: {
				read: { url: "/GMS/WebServices/GMSService.svc/GetOrganisationAwards", type: "POST", contentType: "application/json" },
				parameterMap: function (options) {
					var orderColumn = null;
					var orderDirection = null;
					if (options.sort && options.sort[0]) {
						orderColumn = options.sort[0].field;
						orderDirection = options.sort[0].dir;
					}

					var dataField = $("#" + fieldId);

					return JSON.stringify({
						OrganisationId: dataField.val(),
						IncludeCompleted: $('#' + includeCompletedCheckbox).is(":checked"),
						PageSize: options.pageSize,
						PageIndex: options.page,
						OrderColumn: orderColumn,
						OrderDirection: orderDirection
					});
				}
			},
			schema: {
				data: function (r) { return JSON.parse(r.d.ResultDetail).Awards; },
				total: function (r) { return JSON.parse(r.d.ResultDetail).TotalAwards; },
				model: {
					fields: {
						ApplicationId: { type: "integer" },
						Reference: { type: "string" },
						DateOfferIssued: { type: "date" },
						FundTypeName: { type: "string" },
						ContactFullName: { type: "string" },
						ProjectName: { type: "string" },
						ApplicationStatus: { type: "string" },
						TotalAwarded: { type: "decimal" },
						TotalPaid: { type: "decimal" },
						TotalOutstanding: { type: "decimal" },
						ProjectStartDate: { type: "date" },
						ProjectEndDate: { type: "date" },
						OfferDocumentId: { type: "integer" }
					}
				}
			}
		},
		dataBound: function (e) {
			$('[title="Clear"]').remove();
			var menuButtons = $('[id^="awardButton_"]', e.sender.wrapper);

			for (var index = 0; index < menuButtons.length; index++) {
				FlexiJS.Controls.ComboButton.SetUp(menuButtons[index].id, true, true);
			}
		},
		filterable: false,
		sortable: true,
		noRecords: {
			template: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'Awards.NoResults')
		},
		pageable: {
			numeric: false,
			refresh: true,
			buttonCount: 5,
			pageSizes: [20, 50, 100, 250]
		},
		columns: [
			{ field: "Reference", width: 130, title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'Awards.Column.Reference'), attributes: { class: "k-gridcell" } },
			{ field: "DateOfferIssued", width: 110, title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'Awards.Column.DateOfferIssued'), format: "{0:dd/MM/yyyy}", filterable: { cell: { enabled: false } }, attributes: { class: "k-gridcell" } },
			{ field: "FundTypeName", width: 300, title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'Awards.Column.FundTypeName'), attributes: { class: "k-gridcell k-gridcell-nooverflow" } },
			{ field: "ContactFullName", width: 200, title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'Awards.Column.ContactFullName'), attributes: { class: "k-gridcell" } },
			{ field: "ProjectName", width: 300, title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'Awards.Column.ProjectName'), attributes: { class: "k-gridcell k-gridcell-nooverflow" } },
			{ field: "ApplicationStatus", width: 150, title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'Awards.Column.ApplicationStatus'), filterable: { cell: { enabled: false } }, attributes: { class: "k-gridcell" } },
			{ field: "TotalAwarded", width: 130, title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'Awards.Column.TotalAwarded'), filterable: { cell: { enabled: false } }, template: '<span# if(TotalAwarded < 0) {# class="negative-currency"# } #># if(TotalAwarded < 0) { ##= kendo.format(\'({0:c})\', TotalAwarded) ## } else { ##= kendo.format(\'{0:c}\', TotalAwarded) ## } #</span>', attributes: { class: "k-gridcell currencycell" } },
			{ field: "PaymentsValue", width: 130, title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'Awards.Column.PaymentsValue'), filterable: { cell: { enabled: false } }, template: '<span# if(PaymentsValue < 0) {# class="negative-currency"# } #># if(PaymentsValue < 0) { ##= kendo.format(\'({0:c})\', PaymentsValue) ## } else { ##= kendo.format(\'{0:c}\', PaymentsValue) ## } #</span>', attributes: { class: "k-gridcell currencycell" } },
			{ field: "ProjectStartDate", width: 110, title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'Awards.Column.ProjectStartDate'), format: "{0:dd/MM/yyyy}", filterable: { cell: { enabled: false } }, attributes: { class: "k-gridcell" } },
			{ field: "ProjectEndDate", width: 110, title: FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'Awards.Column.ProjectEndDate'), format: "{0:dd/MM/yyyy}", filterable: { cell: { enabled: false } }, attributes: { class: "k-gridcell" } },
			{
				template: '<div id="awardButton_#= data.ApplicationId #" onclick="return false;" data-buttonoptions="#= FlexiJS.Controls.FinanceOverview.GridSetupOfferLetterButton(data) #"></div>',
				width: 40,
				filterable: false,
				groupable: false,
				sticky: true,
				footerTemplate: ""
			}
		]
	});
};

FlexiJS.Controls.FinanceOverview.GridSetupOfferLetterButton = function (dataItem) {
	if (dataItem.OfferDocumentId) {
		return `<a href='/DownloadFile.aspx?dsid=${dataItem.OfferDocumentId}'><div class='combobutton-option'><span>${FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'Awards.Column.Actions.DownloadLetterOfOffer')}</span></div></a>`;
	} else {
		return `<a href='#' onclick='return false;' class='aspNetDisabled'><div class='combobutton-option'><span>${FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FinanceOverview.ResourceSet, 'Awards.Column.Actions.DownloadLetterOfOfferUnavailable')}</span></div></a>`;
	}
};;/*

  _____   _                 _       _   ____         ____                   _                    _             _____   _                 _   _____                 _     _
 |  ___| | |   ___  __  __ (_)     | | / ___|       / ___|   ___    _ __   | |_   _ __    ___   | |  ___      |  ___| | |   ___  __  __ (_) |_   _|   ___  __  __ | |_  | |__     ___   __  __
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |      / _ \  | '_ \  | __| | '__|  / _ \  | | / __|     | |_    | |  / _ \ \ \/ / | |   | |    / _ \ \ \/ / | __| | '_ \   / _ \  \ \/ /
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |___  | (_) | | | | | | |_  | |    | (_) | | | \__ \  _  |  _|   | | |  __/  >  <  | |   | |   |  __/  >  <  | |_  | |_) | | (_) |  >  <
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_)  \____|  \___/  |_| |_|  \__| |_|     \___/  |_| |___/ (_) |_|     |_|  \___| /_/\_\ |_|   |_|    \___| /_/\_\  \__| |_.__/   \___/  /_/\_\


Requirements:
    -- Any libraries that this file depends on, like jQuery or Kendo
*/

FlexiJS.Controls.FlexiTextbox = {};

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/

FlexiJS.Controls.FlexiTextbox.AttributeNames = {
    entityRowId: 'fg_eid',
    entityTypeId: 'fg_etid',
    actionId: 'fg_aid',
    showLoadingIcon: 'fg_ico',
    disableWhileSaving: 'fg_dws'
};

FlexiJS.Controls.FlexiTextbox.ClassNames = {
    LoadingIconClass: 'fg_eid'
};

FlexiJS.Controls.FlexiTextbox.ControlNames = {
    LoadingIcon: 'fg_loadingicon'
};
FlexiJS.Controls.FlexiTextbox.ResourceFile = "scripts/flexijs/controlsflexitextbox.js";

/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/

FlexiJS.Controls.FlexiTextbox.AddAutoUpdateSaveHandler = function (elementId, entityRowId, entityTypeId, actionId, showLoadingIcon, disableWhileSaving) {
    // Description: Adds an on change event handler to a text box that sends the data to a web service.
    // Created: 23/05/2017, v3.17 - JD
    var $elementObject = $('#' + elementId);

    if ($elementObject !== 'undefined' && $elementObject !== null && $elementObject.length > 0 && ($elementObject.is("input[type='text']") || $elementObject.is("input[type='number']"))) {
        $elementObject
            .attr(FlexiJS.Controls.FlexiTextbox.AttributeNames.entityRowId, entityRowId)
            .attr(FlexiJS.Controls.FlexiTextbox.AttributeNames.entityTypeId, entityTypeId)
            .attr(FlexiJS.Controls.FlexiTextbox.AttributeNames.actionId, actionId)
            .attr(FlexiJS.Controls.FlexiTextbox.AttributeNames.showLoadingIcon, showLoadingIcon)
            .attr(FlexiJS.Controls.FlexiTextbox.AttributeNames.disableWhileSaving, disableWhileSaving);
        //$elementObject.css('width', '45px');
        $elementObject.unbind('change');
        $elementObject.on(
            "change",
            function (e) {
                var $ele = $(this);
                var updateValue = $ele.val();
                var updateEntityRowId = $ele.attr(FlexiJS.Controls.FlexiTextbox.AttributeNames.entityRowId);
                var updateEntityTypeId = $ele.attr(FlexiJS.Controls.FlexiTextbox.AttributeNames.entityTypeId);
                var updateActionId = $ele.attr(FlexiJS.Controls.FlexiTextbox.AttributeNames.actionId);
                var updateShowLoadingIcon = $ele.attr(FlexiJS.Controls.FlexiTextbox.AttributeNames.showLoadingIcon);
                var updateDisableWhileSaving = $ele.attr(FlexiJS.Controls.FlexiTextbox.AttributeNames.disableWhileSaving);
                if (updateShowLoadingIcon === 'true') FlexiJS.Controls.FlexiTextbox.AddLoadingIcon($ele);
                if (updateDisableWhileSaving === 'true') $ele.prop('disabled', true);
                $.ajax({
                    type: "POST",
                    url: "/WebServices/PageService.svc/SaveAutoUpdateTextbox",
                    data: JSON.stringify({ entityRowId: updateEntityRowId, entityTypeId: updateEntityTypeId, actionId: updateActionId, setValue: updateValue }),
                    processData: false,
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    async: true,
                    success: function (result) {
                        if (result.d) {
                            showNotificationMessage(result.d.Result === true ? 'success' : 'error', result.d.ResultMessage);
                        } else {
                            showNotificationMessage('error', FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FlexiTextbox.ResourceFile, 'Webservice.Error'));
                        }
                    },
                    error: function () {
                        showNotificationMessage('error', FlexiJS.Resources.GetResourceText(FlexiJS.Controls.FlexiTextbox.ResourceFile, 'Webservice.Error'));
                    },
                    complete: function () {
                        if (updateShowLoadingIcon === 'true') FlexiJS.Controls.FlexiTextbox.RemoveLoadingIcon($ele);
                        if (updateDisableWhileSaving === 'true') $ele.prop('disabled',false);
                    }
                });
            }
        );
    }
};

FlexiJS.Controls.FlexiTextbox.AddLoadingIcon = function ($element) {
    var $nextElement = $element.next();
    var hasIconAlready = false;
    if ($nextElement !== 'undefined' && $nextElement !== null && $nextElement.attr('name') === FlexiJS.Controls.FlexiTextbox.ControlNames.LoadingIcon) {
        hasIconAlready = true;
    }
    if (hasIconAlready === false) {
        $element.after('<div name="' + FlexiJS.Controls.FlexiTextbox.ControlNames.LoadingIcon + '" class="fa fa-spinner fa-pulse fa-fw" style="margin-top: 4px;"></div>');
    }
};

FlexiJS.Controls.FlexiTextbox.RemoveLoadingIcon = function ($element) {
    var $nextElement = $element.next();
    var hasIconAlready = false;
    if ($nextElement !== 'undefined' && $nextElement !== null && $nextElement.attr('name') === FlexiJS.Controls.FlexiTextbox.ControlNames.LoadingIcon) {
        $nextElement.remove();
    }
};;
FlexiJS.Controls.GenericTab = {};

FlexiJS.Controls.GenericTab.SelectTab = function (element, onShowFunction) {
    //Selects a tab in its tab control. Function is automatically
    // added as part of the vb tab control, you should not need to
    // call this method manually.
    var ele = $(element);
    var selectedTabId = ele[0].id;
    var parent = ele.closest("[role=tablist]");
    
    //Select the requested tab
    ele.addClass('selected');
    ele.attr('aria-selected', 'true');

    //Show Tab Contents
    $('[id="' + ele.attr('aria-controls') + '"]').css('display', 'block');

    //Set the selected tab index into the hidden field for postback selection retention
    $(parent.next('[id$="hdnSelectedTab"]')).val(ele.attr('tabindex'));

    //Unselect all other tabs & hide thier contents
    $.each(
        parent
            .find('[role=tab]')
            .map(
                function (i, e) {
                    //Get all non-selected tabs and the content div
                    if (selectedTabId == e.id) {
                        return null;
                    } else {
                        return {
                            tab: $(e),
                            content: $('[id="' + $(e).attr('aria-controls') + '"]')
                        };
                    }
                }
            ),
        function (index, tabElements) {
            //Unselect and tabs that aren't selected and hide the associated content area
            tabElements.tab.removeClass('selected');
            tabElements.tab.attr('aria-selected', 'false');
            tabElements.content.css('display', 'none');
        }
    );

    //If a function has been provided for on tab show then fire it now
    if (onShowFunction != null) onShowFunction();
};;

/*

  _____   _                 _       _   ____         ____                   _                    _             ____            _     _                     ____    _   _       _
 |  ___| | |   ___  __  __ (_)     | | / ___|       / ___|   ___    _ __   | |_   _ __    ___   | |  ___      | __ )   _   _  | |_  | |_    ___    _ __   |  _ \  (_) | |__   | |__     ___    _ __
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |      / _ \  | '_ \  | __| | '__|  / _ \  | | / __|     |  _ \  | | | | | __| | __|  / _ \  | '_ \  | |_) | | | | '_ \  | '_ \   / _ \  | '_ \
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |___  | (_) | | | | | | |_  | |    | (_) | | | \__ \  _  | |_) | | |_| | | |_  | |_  | (_) | | | | | |  _ <  | | | |_) | | |_) | | (_) | | | | |
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_)  \____|  \___/  |_| |_|  \__| |_|     \___/  |_| |___/ (_) |____/   \__,_|  \__|  \__|  \___/  |_| |_| |_| \_\ |_| |_.__/  |_.__/   \___/  |_| |_|


    -- ButtonRibbon --
        Additional Controls:

*/

FlexiJS.Controls.MultiSelectList = function (src, attributes) {
    // If we pass a string, look for the element, otherwise treat the src element as a jQuery object array
    try {
        if (typeof src === "string") {
            return $(src).FlexiJS_Controls_MultiSelectList(attributes);
        } else {
            return src.FlexiJS_Controls_MultiSelectList(attributes);
        }
    }
    catch (ex) {
        FlexiJS.Error.LogError({ message: ex.message }, "multiselectlist.js");
        return null;
    }
};

(function ($) {
    $.fn.FlexiJS_Controls_MultiSelectList = function (attributes) {
        //jQuery elements passed in
        var jq = this;

        //For each element passed
        $.each(jq, function (elementIndex, elementItem) {
            // DEFAULT SETTINGS
            var _baseData = {
                id: '',
                data: [],
                onChange: function (value) { },
                isValid: false,
                renderMode: 'classic',
		        dataTextField: null,
		        dataValueField: null,
		        itemTemplate: null,
                tagTemplate: null,
                headerTemplate: null,
		        placeholder: null,
                naplaceholder: null
            }
            
            // Non overridable data
            var _hardsetdata = {
                _resourcesfile: 'Scripts\\multiselectlist.js',
                _baseElement: elementItem,
                _baseElementIndex: elementIndex,
                _multiSelect: null,
                _templateGroup: 'flexijs.controls.multiselectlist',
                _baseClass: 'flexijs-controls-multiselectlist-',
                _selectedData: [],
                _nonSelectedData: [],
                _enabled: true
            };

            // FOR ANY FUNCTION YOU SHOULD USE 'mybase' TO ACCESS THE CONTROL
            // FOR AN EVENT HANDLER THIS SHOULD BE PASSED IN AS THE EVENT DATA
            var functions = {
                _init: function () {
                    //Called on control creation
                    var mybase = this;
                    mybase._bindInitialData();
                    $(mybase._baseElement).empty();
                    $(mybase._renderBaseTemplate()).appendTo($(mybase._baseElement));
                    mybase._bindBaseEvents();
                },

                _bindInitialData: function() {
                    //Sets the selected & non selected internal data variables
                    var mybase = this;
                    mybase._selectedData = [];
                    mybase._nonSelectedData = mybase.data;
                },

                _getClass: function(subclass, prefix) {
                    //Gets the class name made up as:
                    //  (prefix[If Passed])(control base class)(control render mode)(-subclass[If Passed])
                    var mybase = this;
                    return (typeof prefix != 'undefined' && prefix != null ? prefix : '') + 
                        mybase._baseClass + mybase.renderMode + (subclass != null && subclass != '' ? '-' + subclass : '');
                },
                _getTemplate: function(template) {
                    //Gets the kendo template name
                    var mybase = this;
                    return mybase.renderMode + '_' + template;
                },
                _createElement: function (templateid, data) {
                    //Shortcut to template renderer, maintains lower changes required on methodology change
                    var mybase = this;
                    return FlexiJS.Kendo.Templates.RenderTemplate(mybase._templateGroup, templateid, data);
                },

                _renderBaseTemplate: function () {
                    //Render the container template and returns the html
                    var mybase = this;
                    return mybase._createElement(mybase._getTemplate('multiselectlist_base'), { mybase: mybase });
                },
                _renderSelectedItemTemplate: function (data) {
                    //Renders a selected item using the selected item template and returns the html
                    var mybase = this;
                    var contents = kendo.Template.compile(mybase.tagTemplate)(data);
                    return mybase._createElement(mybase._getTemplate('multiselectlist_selecteditem'), { mybase: mybase, contents: contents });
                },
                _renderKendoMultiSelect: function(){
                    var mybase = this;
                    mybase._multiSelect = $(mybase._baseElement).find(mybase._getClass('ribboncontainer-button','.'))
                },

                _bindBaseEvents: function () {
                    var mybase = this;
                    //Setup the kendo multiselect list
                    mybase._multiSelect = $(mybase._baseElement)
                        .find('#' + mybase.id + '_input')
                        .kendoMultiSelect({
		                    dataSource: { data: mybase._nonSelectedData },
		                    dataTextField: mybase.dataTextField,
		                    dataValueField: mybase.dataValueField,
		                    change: function(){ mybase._multiSelectOnChange(); },
		                    itemTemplate: mybase.itemTemplate,
                            headerTemplate: mybase.headerTemplate,
		                    placeholder: mybase.placeholder,
		                    highlightFirst: false
	                    }).data('kendoMultiSelect');
                },

                _multiSelectOnChange: function() {
                    //Handle the multiselects change event, get the selected data items & pass them to the base controls onchange
                    //Multiselect selection is cleared after selecting, so we can assume every change event is an add
                    var mybase = this;
                    mybase._updateSelectedItems(mybase._multiSelect.dataItems()[0][mybase.dataValueField], true);
                },

                _updateSelectedItems: function(itemId, add){
                    //Updates the selected items. Item ID must be from the datasource in the dataValueField field
                    //If add is passed true, the item is added, otherwise it is removed
                    var mybase = this;

                    //Get the new selected items list, adding or removing the item as requested
                    mybase._selectedData = $.grep(mybase.data, function(item){
                        var myId = item[mybase.dataValueField];
                        if(add == true){
                            return myId == itemId || $.grep(mybase._selectedData, function(selItem){ return selItem[mybase.dataValueField] == myId; }).length > 0;
                        } else {
                            return myId != itemId && $.grep(mybase._selectedData, function(selItem){ return selItem[mybase.dataValueField] == myId; }).length > 0;
                        }
                    });

                    //Get the list of non selected items
                    mybase._nonSelectedData = $.grep(mybase.data, function(item){
                        var myId = item[mybase.dataValueField];
                        return $.grep(mybase._selectedData, function(selItem){ return selItem[mybase.dataValueField] == myId; }).length == 0;
                    });

                    //Update multiselect status & datasource
                    mybase._enabledStatusChanged();

                    //Clear & rebind the selected items list
                    var selectedItemsContainer = $(mybase._baseElement).find('#' + mybase.id + '_items');
                    selectedItemsContainer.empty();
                    $.each(
                        mybase._selectedData,
                        function(index, item){
                            $(mybase._renderSelectedItemTemplate(item))
                                .appendTo(selectedItemsContainer)
                                .find(
                                    mybase._getClass('selecteditem-remove','.')
                                ).on(
                                    'click',
                                    function(){
                                        if(mybase._enabled) mybase._updateSelectedItems(item[mybase.dataValueField], false);
                                    }
                                );
                        }
                    );
                    
                    //Fire the bound onchange event (if any)
                    if(typeof mybase.onChange == 'function'){
                        mybase.onChange();
                    }
                },
                
                selectedValue: function () {
                    //Returns the selected item
                    var mybase = this;
                    return mybase._selectedData;
                },
                enable: function(value){
                    var mybase = this;
                    mybase._enabled = value;
                    mybase._enabledStatusChanged();
                },
                _enabledStatusChanged: function(){
                    var mybase = this;
                    //Check if the multiselect will have data
                    var isenabled = mybase._nonSelectedData.length > 0;

                    //If we are enabling the multiselect enable it before the values are set otherwise the won't update.
                    //Only do if enabling to prevent flicker when disabled & remaining disabled
                    if(isenabled) mybase._multiSelect.enable(isenabled && mybase._enabled);
                    //Clear the selected value
                    mybase._multiSelect.value([]);
                    //Set the non selected items as the datasource
                    mybase._multiSelect.setDataSource({ data: mybase._nonSelectedData });
                    //Set the placeholder depending on the enables status
                    mybase._multiSelect.options.placeholder = isenabled ? mybase.placeholder : mybase.naplaceholder;
                    //Focus & blur to actually show the placeholder (KENDO HACK)
                    mybase._multiSelect.input.focus();
                    mybase._multiSelect.input.blur();
                    //If disabling the multiselect disable it now, after the values are set so it displays correctly
                    if(!isenabled) mybase._multiSelect.enable(isenabled);
                }
            }

            // Add user passed attributes, overrides default
            _baseData = $.extend(true, _baseData, attributes, _hardsetdata, functions);
            // Add functions to data element, override where user has tried to pass something
            //_baseData = $.extend(_baseData, functions);
            // Set elements data object to be control data, can be accessed via $('ELEMENT').data('flexiJSControlsMultiSelectList').ACTION/VALUE
            $(elementItem).data('flexiJSControlsMultiSelectList', _baseData);
            // Initiate Control
            _baseData._init();
        })

        //Ensure we return elements to allow any further jquery methods to work
        return this;
    };
})(jQuery);
;(function() {

    function switchElements() {

        var checkedSelector = $(this).data("checked-selector");
        var uncheckedSelector = $(this).data("unchecked-selector");
        if ($(this).is(":checked")) {

            $(checkedSelector).show();
            $(uncheckedSelector).hide();

        } else {

            $(checkedSelector).hide();
            $(uncheckedSelector).show();

        }

    }

    FlexiJS.Controls.elementSwitcher = function(container) {

        $(container)
            .find(".element-switcher")
            .change(switchElements) // on check/uncheck
            .each(switchElements); // initialise with the correct controls hidden/shown

    }

}());;FlexiJS.Cookies = new Object();

FlexiJS.Cookies.TemplateGroup = "cookiepolicy";
FlexiJS.Cookies.ConsentDefaultExpiry = 120;
FlexiJS.Cookies.ResourceFile = 'scripts\\cookies.js';

FlexiJS.Cookies.ShowCookieConsentPopup = function (greyWebsite) {
    //Prevent the popup from appearing inside a rad window
    if (window.radWindow) {
        var rWindow = GetRadWindow();
        if (rWindow != null) return;
    }
    var popupHTML = '';

    popupHTML = FlexiJS.Kendo.Templates.RenderTemplate(
        FlexiJS.Cookies.TemplateGroup,
        'CookiePolicy_Templates_Popup',
        { modular: greyWebsite }
    );

    var popup = $(popupHTML);

    popup.find('input[type=checkbox]').on('change', function () {
        //Change button text
        var parentPopup = $(this).closest('.cookieconsent-popup');
        var acceptButton = parentPopup.find('#btnAccept');

        acceptButton.text(acceptButton.data(parentPopup.find('input[type=checkbox]:not(:checked)').length > 0 && parentPopup.find('input[type=checkbox]:checked').length > 0 ? 'accepttext' : 'acceptalltext'));
    });

    popup.find('button').on('click', function () {
        //Accept/Decline
        var parentPopup = $(this).closest('.cookieconsent-popup');
        var acceptPerformance = 0;
        var acceptAnalytics = 0;
        if (this.id == "btnAccept") {
            if (parentPopup.find('input[type=checkbox]:checked').length != 0) {
                acceptPerformance = parentPopup.find('#chkPerformance').is(':checked') ? 1 : 0;
                acceptAnalytics = parentPopup.find('#chkAnalytics').is(':checked') ? 1 : 0;
            } else {
                acceptPerformance = 1;
                acceptAnalytics = 1;
            }
        }

        FlexiJS.Cookies.SetOptionalCookies(acceptPerformance, acceptAnalytics);

        parentPopup.remove();
        $('.cookieconsent-popup-background').remove();
    });

    popup.find('#lnkHideOrShowDetails').on('click', function () {
        //Show/Hide Details
        var parentPopup = $(this).closest('.cookieconsent-popup');
        var show = $(this).hasClass('show');

        parentPopup.find('.cookieconsent-popup-additionaldetails').toggle(show);
        $(this).removeClass(show ? 'show' : 'hide').addClass(show ? 'hide' : 'show').text(show ? $(this).data('hidetext') : $(this).data('showtext'));

        return false;
    });

    popup.appendTo('body');
};


FlexiJS.Cookies.SetCookieValue = function (property, value, days) {
    //IE doesn't support spaces in cookies, so if you're storing a string with spaces be sure to use encodeuricomponent when setting and decodeuricomponent when retrieving
    var expires = "";

    var date = new Date();
    date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
    expires = "; expires=" + date.toUTCString();

    document.cookie = property + "=" + (value || "") + expires + "; path=/;";
};

FlexiJS.Cookies.ClearCookie = function (cookieName) {
    //Some cookies are not stored at the sub domain level (notably hotjar & google analytics) so we need to iterate through the hostname to clear them correctly
    var domainParts = window.location.hostname.split('.').reverse();
    var topLevelDomain = domainParts.shift();
    var domains = [];
    for (var i = 0; i < domainParts.length; i++) {
        var domainPart = domainParts[i];
        var prevDomain = domains.slice(-1)[0] || topLevelDomain;
        domains.push(domainPart + '.' + prevDomain);
    }

    for (var i = 0; i < domains.length; i++) {
        var domain = domains[i];
        document.cookie = cookieName + '=; path=/; domain=' + domain + '; Max-Age=0;';
    }
};

FlexiJS.Cookies.GetCookieValue = function (property) {
    var i, name, value, cookies = document.cookie.split(";");

    for (i = 0; i < cookies.length; i++) {
        name = cookies[i].substr(0, cookies[i].indexOf("="));
        value = cookies[i].substr(cookies[i].indexOf("=") + 1);
        //Remove any whitespace at the start OR the end
        name = name.replace(/^\s+|\s+$/g, "");
        if (name == property) return value;
    }
};

FlexiJS.Cookies.CookieConsentPolicyRefresh = function () {
    //Change button text
    var acceptPerformance = $('#chkPerformance').is(':checked') ? 1 : 0;
    var acceptAnalytics = $('#chkAnalytics').is(':checked') ? 1 : 0;

    var setPerformance = FlexiJS.Cookies.GetCookieValue("CookieConsent.Performance");
    var setAnalytics = FlexiJS.Cookies.GetCookieValue("CookieConsent.Analytics");

    var acceptButton = $('.cookieconsent-buttons #btnAccept');
    acceptButton.prop('disabled', setAnalytics == acceptAnalytics && setPerformance == acceptPerformance && (acceptAnalytics == 1 || acceptPerformance == 1)).text(acceptPerformance != acceptAnalytics ? acceptButton.data('accepttext') : acceptButton.data('acceptalltext'));
};

FlexiJS.Cookies.CookieConsentIsPerformanceSelected = function () {
    return FlexiJS.Cookies.GetCookieValue("CookieConsent.Performance") == 1;
};

FlexiJS.Cookies.CookieConsentIsAnalyticsSelected = function () {
    return FlexiJS.Cookies.GetCookieValue("CookieConsent.Analytics") == 1;
};

FlexiJS.Cookies.IsPageExemptFromPopup = function () {
    var pagename = window.location.pathname.split("/").pop().toLowerCase();
    return pagename === "printflexiform.aspx" || pagename === "printcv.aspx" || pagename === "previousreviewhistory.aspx" || pagename === "printecfnotes.aspx";
};

FlexiJS.Cookies.UpdateLastSavedDateDisplay = function () {
    var lastUpdated = FlexiJS.Cookies.GetCookieValue("CookieConsent.LastUpdated");
    $('#spnLastSavedDate').text(FlexiJS.Resources.GetResourceText(FlexiJS.Cookies.ResourceFile, 'jsLastSaved.text') + ' ' + (lastUpdated ? decodeURIComponent(lastUpdated) : FlexiJS.Resources.GetResourceText(FlexiJS.Cookies.ResourceFile, 'jsNotSaved.text')));
};

FlexiJS.Cookies.SetOptionalCookies = function (setPerformance, setAnalytics) {
    FlexiJS.Cookies.SetCookieValue("CookieConsent.Performance", setPerformance, FlexiJS.Cookies.ConsentDefaultExpiry);
    FlexiJS.Cookies.SetCookieValue("CookieConsent.Analytics", setAnalytics, FlexiJS.Cookies.ConsentDefaultExpiry);
    FlexiJS.Cookies.SetCookieValue("CookieConsent.LastUpdated", encodeURIComponent((new Date()).toLocaleDateString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })), FlexiJS.Cookies.ConsentDefaultExpiry);

    if (setPerformance == 0) {
        //Clear associated hotjar cookies on decline
        FlexiJS.Cookies.ClearCookie("_hjIncludedInSample");
        FlexiJS.Cookies.ClearCookie("_hjAbsoluteSessionInProgress");
        FlexiJS.Cookies.ClearCookie("_hjid");
        FlexiJS.Cookies.ClearCookie("_hjTLDTest");
    }

    if (setAnalytics == 0) {
        //Clear associated google cookies on decline
        FlexiJS.Cookies.ClearCookie("_ga");
        FlexiJS.Cookies.ClearCookie("_gat");
        FlexiJS.Cookies.ClearCookie("_gid");
    }
};

FlexiJS.Cookies.CheckForCookieConsent = function () {
    //Don't do anything if the page is exempt from the cookie popup, (print view & pdf pages)
    if (FlexiJS.Cookies.IsPageExemptFromPopup()) return;

    if (window.location.pathname.split("/").pop().toLowerCase() == "sitepolicy.aspx") {
        //No need to show as options are on the page
        if (FlexiJS.Cookies.CookieConsentIsPerformanceSelected()) $('#chkPerformance').prop('checked', true);
        if (FlexiJS.Cookies.CookieConsentIsAnalyticsSelected()) $('#chkAnalytics').prop('checked', true);
        FlexiJS.Cookies.UpdateLastSavedDateDisplay();

        $('.cookieconsent-buttons button').on('click', function () {
            //Accept/Decline
            var acceptPerformance = 0;
            var acceptAnalytics = 0;
            if (this.id == "btnAccept") {
                if ($('#chkPerformance:checked, #chkAnalytics:checked').length != 0) {
                    acceptPerformance = $('#chkPerformance').is(':checked') ? 1 : 0;
                    acceptAnalytics = $('#chkAnalytics').is(':checked') ? 1 : 0;
                } else {
                    acceptPerformance = 1;
                    acceptAnalytics = 1;
                }
            }

            FlexiJS.Cookies.SetOptionalCookies(acceptPerformance, acceptAnalytics);

            $('#chkPerformance').prop('checked', FlexiJS.Cookies.CookieConsentIsPerformanceSelected());
            $('#chkAnalytics').prop('checked', FlexiJS.Cookies.CookieConsentIsAnalyticsSelected());

            FlexiJS.Cookies.CookieConsentPolicyRefresh();
            FlexiJS.Cookies.UpdateLastSavedDateDisplay();
            showNotificationMessage('success', FlexiJS.Resources.GetResourceText(FlexiJS.Cookies.ResourceFile, 'jsCookiesPreferencesUpdated.text'));
            return false;
        });

        $('#chkPerformance, #chkAnalytics').on('change', FlexiJS.Cookies.CookieConsentPolicyRefresh);

        FlexiJS.Cookies.CookieConsentPolicyRefresh();
    } else {
        if (FlexiJS.Cookies.GetCookieValue("CookieConsent.Performance") == null || FlexiJS.Cookies.GetCookieValue("CookieConsent.Analytics") == null) FlexiJS.Cookies.ShowCookieConsentPopup(true);
    }
};

$(document).ready(function () { FlexiJS.Cookies.CheckForCookieConsent(); });;
FlexiJS.CRM = {};
FlexiJS.CRM.Contacts = new Object();
FlexiJS.CRM.EmailManagement = new Object();

FlexiJS.CRM.SetupContactButtonPopup = function (popupElementId, buttonid) {
    var window = $("#" + popupElementId);

    $("#" + buttonid).bind("click", function () {
        window.css('display', 'flex');
        FlexiJS.UI.SetBodyOverflow();

        window[0].addEventListener('keydown', function (e) { FlexiJS.Popup.HandleWindowTabbing(e, "#" + popupElementId, '.close-dialog'); });

        FlexiJS.Popup.FocusToFirstElement(popupElementId);
    });


    window.find('.close-dialog').on(
        'click',
        function (e) {
            e.preventDefault();
            window.css('display', 'none');
            FlexiJS.UI.ClearBodyOverflow();
            var button = $("#" + buttonid);
            if (button) {
                button.focus();
            }
          
        }
    );
};

FlexiJS.CRM.Contacts.OfferSaveUserInfo = function (fromid, toid, changes, message, confirmtext, declinetext, savemessage, failedmessage, windowtitle) {
    var kw = $("<div class='divApplicationContactSaveChangesWindow' />").kendoWindow({
        title: windowtitle,
        resizable: false,
        modal: true,
        close: function (e) {
            if (e.userTriggered) {
                this.destroy();
            }
        }
    });
    var contents = '';

    contents += '<div class="applicationcontact-changespopup-container">';
    contents += '	<div class="applicationcontact-changespopup-row">';
    contents += '		<p>' + message + '</p>';
    contents += '		<div id="fgContainer" class="applicationcontact-changespopup-groupscontainer">';
    contents += '			<div id="fgNameContainer" class="applicationcontact-changespopup-groupcontainer"><span class="applicationcontact-changespopup-grouptitle">' + FlexiJS.Resources.GetResourceText("Controls/FlexiForm/GMS/ApplicationContact.ascx", "UserDetails.GroupTitle") + '</span><br/></div>';
    contents += '			<div id="fgAddressContainer" class="applicationcontact-changespopup-groupcontainer"><span class="applicationcontact-changespopup-grouptitle">' + FlexiJS.Resources.GetResourceText("Controls/FlexiForm/GMS/ApplicationContact.ascx", "Address.GroupTitle") + '</span><br/></div>';
    contents += '			<div id="fgEmailContainer" class="applicationcontact-changespopup-groupcontainer"><span class="applicationcontact-changespopup-grouptitle">' + FlexiJS.Resources.GetResourceText("Controls/FlexiForm/GMS/ApplicationContact.ascx", "EmailAddress.GroupTitle") + '</span><br/></div>';
    contents += '			<div id="fgPhoneContainer" class="applicationcontact-changespopup-groupcontainer"><span class="applicationcontact-changespopup-grouptitle">' + FlexiJS.Resources.GetResourceText("Controls/FlexiForm/GMS/ApplicationContact.ascx", "PhoneNumber.GroupTitle") + '</span><br/></div>';
    contents += '			<div id="fgWebsiteContainer" class="applicationcontact-changespopup-groupcontainer"><span class="applicationcontact-changespopup-grouptitle">' + FlexiJS.Resources.GetResourceText("Controls/FlexiForm/GMS/ApplicationContact.ascx", "Website.GroupTitle") + '</span><br/></div>';
    contents += '			<div id="fgOtherContainer" class="applicationcontact-changespopup-groupcontainer"><span class="applicationcontact-changespopup-grouptitle">' + FlexiJS.Resources.GetResourceText("Controls/FlexiForm/GMS/ApplicationContact.ascx", "OtherDetails.GroupTitle") + '</span><br/></div>';
    contents += '		</div>';
    contents += '	</div>';
    contents += '</div>';

    contents += '<div class="applicationcontact-changespopup-container">';
    contents += '	<div class="applicationcontact-changespopup-row">';
    contents += '		<div class="applicationcontact-changespopup-rowfiller">';
    contents += '			<a id="exc_Cancel" class="gms-button MakeBlack fg_button secondary">' + declinetext + '</a>';
    contents += '			<a id="exc_Done" class="gms-button MakeBlack fg_button delete">' + confirmtext + '</a>';
    contents += '		</div>';
    contents += '	</div>';
    contents += '</div>';

    contents = contents.replace(/(?:\r\n|\r|\n)/g, '<br />');

    kw.data("kendoWindow").content(contents).center().open();


    var changesContainerDiv = kw.find("#fgContainer");
    var changesNameDiv = kw.find("#fgNameContainer");
    var changesAddressDiv = kw.find("#fgAddressContainer");
    var changesEmailDiv = kw.find("#fgEmailContainer");
    var changesPhoneDiv = kw.find("#fgPhoneContainer");
    var changesWebsiteDiv = kw.find("#fgWebsiteContainer");
    var changesOtherDiv = kw.find("#fgOtherContainer");

    var changesNameDivAddedTo = false;
    var changesAddressDivAddedTo = false;
    var changesEmailDivAddedTo = false;
    var changesPhoneDivAddedTo = false;
    var changesWebsiteDivAddedTo = false;
    var changesOtherDivAddedTo = false;

    $.each(changes, function (index, change) {
        var divId = change.FieldGroup + '_' + change.FieldName;
        var addToDiv;
        switch (change.FieldGroup) {
            case '44643': //Name
                addToDiv = changesNameDiv;
                changesNameDivAddedTo = true;
                break;
            case '44689': //Address
                addToDiv = changesAddressDiv;
                changesAddressDivAddedTo = true;
                break;
            case '44688': //Email
                addToDiv = changesEmailDiv;
                changesEmailDivAddedTo = true;
                break;
            case '44690': //Phone
                addToDiv = changesPhoneDiv;
                changesPhoneDivAddedTo = true;
                break;
            case 'ContactWebsite': //Website
                addToDiv = changesWebsiteDiv;
                changesWebsiteDivAddedTo = true;
                break;
            default:
                addToDiv = changesOtherDiv;
                changesOtherDivAddedTo = true;
        }

        var myText = change.DisplayFieldName.replace('{0}', change.CurrentValue).replace('{1}', change.NewValue)
        addToDiv.append('<div id="fg_' + divId + '" class="applicationcontact-changespopup-groupitem"><input id="ck_' + divId + '" type="checkbox" checked/> ' + myText + '</div>');

        var addedCheckbox = $('#ck_' + divId, addToDiv);

        addedCheckbox.click(
            function () {
                if (addedCheckbox.is(":Checked")) {
                    $('#fg_' + divId, addToDiv).addClass('unselecteditem');
                }
            }
        );

        addedCheckbox.data("changeField", { FieldGroup: change.FieldGroup, FieldName: change.FieldName });
    });

    if (changesNameDivAddedTo == false) { $('#fgNameContainer', changesContainerDiv).remove(); }
    if (changesAddressDivAddedTo == false) { $('#fgAddressContainer', changesContainerDiv).remove(); }
    if (changesEmailDivAddedTo == false) { $('#fgEmailContainer', changesContainerDiv).remove(); }
    if (changesPhoneDivAddedTo == false) { $('#fgPhoneContainer', changesContainerDiv).remove(); }
    if (changesWebsiteDivAddedTo == false) { $('#fgWebsiteContainer', changesContainerDiv).remove(); }
    if (changesOtherDivAddedTo == false) { $('#fgOtherContainer', changesContainerDiv).remove(); }


    kw.find('#exc_Done').click(function () {
        var changesDiv = kw.find("#fgContainer");
        var sendFields = [];

        $.each($('[id^="ck_"]', changesDiv), function (index, checkbox) {
            if (checkbox.checked) {
                sendFields.push($(checkbox).data("changeField"));
            }
        });

        Fluent.Website.GMS.GMSService.UpdateApplicationContactToUserInfo(
            fromid,
            toid,
            JSON.stringify(sendFields),
            function (Result) {
                var resultjson = JSON.parse(Result);
                if (resultjson) {
                    if (kw && kw.data("kendoWindow")) {
                        kw.data("kendoWindow").close();
                        kw.data("kendoWindow").destroy();
                    }
                    showNotificationMessage('success', savemessage);
                } else {
                    showNotificationMessage('error', failedmessage);
                }
            },
            function () {
                //Show Unable To Add Message
                showNotificationMessage('error', failedmessage);
            }
        );
    }).end();
    kw.find('#exc_Cancel').click(function () {
        if (kw && kw.data("kendoWindow")) {
            kw.data("kendoWindow").close();
            kw.data("kendoWindow").destroy();
        }
    }).end();

};

FlexiJS.CRM.Contacts.AddDisabledInfoMessage = function (rowID, msg) {

    var divRow = $('#' + rowID.toString());
    var divInfo = `<div class="info-msg margin-top margin-bottom">${msg}</div>`;
    $(divInfo).insertAfter(divRow);

    var removeRow = $(divRow.find('.removeRow.fx-addcontactremove'));
    if (removeRow) {
        removeRow.unbind('click');
        removeRow.addClass('disabled');
    }
};

/*****************************
    RELATED ORGANISATIONS
*****************************/
FlexiJS.CRM.Contacts.PopupRelatedOrganisation = function () {
    var url = "/crm/contactModal.aspx?mt=1";
    openRadWindow(url, '640', '800');
};

FlexiJS.CRM.Contacts.RelatedOrganisationChanging = function (sender, eventArgs) {

    if ($('[id*=hdnContactCrmOrganisationId]').val() != '') {
        /*TODO: RK confirm message*/
        if (!confirm('Are you sure you want to change your associated organisation?')) {
            eventArgs.set_cancel(true);
        } else {
            eventArgs.set_cancel(false);
        }
    }
};


FlexiJS.CRM.Contacts.RelatedOrganisationChanged = function (sender, eventArgs) {
    var orgId;
    var controlId;
    var senderId;
    if (eventArgs != undefined) {
        orgId = eventArgs.get_item().get_value();
        controlId = $('#' + sender._uniqueId.replace(/\$/g, '_')).attr("departmentControlID");
        senderId = sender._uniqueId;
    }
    else {
        orgId = $('#' + sender.id).val();
        controlId = $('#' + sender.id.replace(/\$/g, '_')).attr("departmentControlID");
        senderId = sender.id;
    }

    FlexiJS.CRM.Contacts.GetOrganisationDepartments(orgId, false, controlId);
    if (senderId.endsWith('rcboSearchOrganisation')) {
        if ($('[id*=txtContactAddress1]').length > 0) {
            Fluent.Website.GMS.GMSService.GetBasicOrganisationDetails(orgId, FlexiJS.CRM.Contacts.updateContactByOrganisationDetailsSuccess, FlexiJS.CRM.Contacts.updateContactByOrganisationDetailsFail);
        }
    }
};

FlexiJS.CRM.Contacts.updateContactByOrganisationDetailsSuccess = function (result) {
    if (result.Result) {
        var orgDetails = JSON.parse(result.ResultDetail);
        var address = '';
        if (orgDetails.Address1 != null) {
            address += orgDetails.Address1;
        }
        if (orgDetails.Address2 != null) {
            address += '\n' + orgDetails.Address2;
        }
        if (orgDetails.City != null) {
            address += '\n' + orgDetails.City;
        }
        if (orgDetails.PostCode != null) {
            address += '\n' + orgDetails.PostCode;
        }
        if (address != '' && confirm('Would you like to use the contact address for ' + orgDetails.Name + '?\n\n' + address + '\n\nSelect \'OK\' for yes or select \'Cancel\' for no.')
        ) {
            $('[id*=txtContactAddress1]').val(orgDetails.Address1);
            $('[id*=txtContactAddress2]').val(orgDetails.Address2);
            $('[id*=txtContactCity]').val(orgDetails.City);
            $('[id*=txtContactCounty]').val(orgDetails.County);
            $('[id*=txtContactPostCode]').val(orgDetails.PostCode);

            var ddlContactAddressCountryId = $('[id$=ddlContactAddressCountry]').first();
            if (ddlContactAddressCountryId.length > 0) {
                var ddlContactAddressCountry = $find(ddlContactAddressCountryId[0].id);
                if (ddlContactAddressCountry != null) {
                    selectedItem = ddlContactAddressCountry.findItemByValue(orgDetails.Country);
                    if (selectedItem != null) {
                        selectedItem.select();
                    }
                }
            }

            var ddlContactAddressTypeId = $('[id$=ddlContactAddressType]').first();
            if (ddlContactAddressTypeId.length > 0) {
                var ddlContactAddressType = $find(ddlContactAddressTypeId[0].id);
                if (ddlContactAddressType != null) {
                    selectedItem = ddlContactAddressType.findItemByValue(orgDetails.AddressType);
                    if (selectedItem != null) {
                        selectedItem.select();
                    }
                }
            }
        }
    }
    else {
        showNotification({
            type: "error",
            message: result.ResultMessage,
            autoClose: true,
            duration: 7
        });
    }
};

FlexiJS.CRM.Contacts.updateContactByOrganisationDetailsFail = function (message) {
    showNotificationMessage('error', message);
};

FlexiJS.CRM.Contacts.AddRelatedOrganisation = function (orgDetails) {
    // First check that the org does not already exist
    var org = JSON.parse(orgDetails);
    var exists = jQuery.grep(FlexiJS.CRM.Contacts.RelatedOrganisations, function (ro) { return ro.Id == org.Id }).length > 0;
    var table = $('#tblRelatedOrganisations');

    if (exists) {
        showNotification({
            type: "error",
            message: table.data("add-failure-exists"),
            autoClose: true,
            duration: 7
        });
    }
    else {
        FlexiJS.CRM.Contacts.RelatedOrganisations.push(org);
        showNotification({
            type: "success",
            message: table.data("add-success"),
            autoClose: true,
            duration: 7
        });
        FlexiJS.CRM.Contacts.RenderRelatedOrganisations();
    }
};

FlexiJS.CRM.Contacts.DeletedRelatedOrganisation = function (orgId) {
    function callbackFn(arg) {
        if (arg) {
            var exists = jQuery.grep(FlexiJS.CRM.Contacts.RelatedOrganisations, function (ro) { return ro.Id == orgId }).length > 0;
            var table = $('#tblRelatedOrganisations');

            if (!exists) {
                showNotification({
                    type: "error",
                    message: table.data("delete-failure"),
                    autoClose: true,
                    duration: 7
                });
            }
            else {
                FlexiJS.CRM.Contacts.RelatedOrganisations = jQuery.grep(FlexiJS.CRM.Contacts.RelatedOrganisations, function (ro) { return ro.Id != orgId });
                showNotification({
                    type: "success",
                    message: table.data("delete-success"),
                    autoClose: true,
                    duration: 7
                });
                FlexiJS.CRM.Contacts.RenderRelatedOrganisations();
            }
        }
    }

    var table = $('#tblRelatedOrganisations');

    radconfirm(table.data("delete-confirm"), callbackFn, 380, 160);
};

FlexiJS.CRM.Contacts.SetupRelatedOrganisations = function () {
    // Related orgs are held in an array - if array does not exist, lift them from a hidden field
    if (FlexiJS.CRM.Contacts.RelatedOrganisations == null) {
        FlexiJS.CRM.Contacts.RelatedOrganisations = JSON.parse($('#hdnRelatedOrganisations').val());
    }
};

FlexiJS.CRM.Contacts.RenderRelatedOrganisations = function (d) {
    if ($('[id*=divRelatedOrganisation]').length == 0) { return; }
    FlexiJS.CRM.Contacts.SetupRelatedOrganisations();
    var table = $('#tblRelatedOrganisations');
    table.find("tr:gt(0)").remove();
    var showTable = false;
    var readonly = table.data("readonly");
    if (d == 'True') { readonly = true; }
    var alt = false;
    for (var i = 0; i < FlexiJS.CRM.Contacts.RelatedOrganisations.length; i++) {
        var org = FlexiJS.CRM.Contacts.RelatedOrganisations[i];
        if (org.Reason == null) {
            org.Reason = "";
        }
        var reason = org.Reason.length > 30 ? "<span title='" + org.Reason + "'>" + org.Reason.substring(0, 20) + "...</span>" : org.Reason;
        var button = readonly ? "&nbsp;" : "<a onclick='FlexiJS.CRM.Contacts.DeletedRelatedOrganisation(" + org.Id + "); return false;' class='color-critical'>" + table.data("delete-button") + "</a>";
        var row = "<TR" + (alt ? " class='alt'" : "") + "><TD class='name'>" + org.Name + "</TD><TD class='reason'>" + reason + "</TD><TD class='action'>" + button + "</TD></TR>";

        table.append(row);
        showTable = true;
        alt = !alt;
    }
    var sb = $('[id*=btnSaveRelatedOrgansations]')
    if (showTable) {
        table.show();
        if (!readonly) {
            sb.show();
        } else {
            sb.hide();
        }
    }
    else {
        table.hide();
        sb.hide();
    }
};

FlexiJS.CRM.Contacts.GatherRelatedOrganisations = function () {
    $('#hdnRelatedOrganisations').val(JSON.stringify(FlexiJS.CRM.Contacts.RelatedOrganisations));
};

/*********************************
    PERSON ORGANISATION ACCESS
*********************************/
FlexiJS.CRM.Contacts.PopupPersonOrganisationAccess = function () {
    var url = "/crm/contactModal.aspx?mt=2";
    openRadWindow(url, '500', '800');
};

FlexiJS.CRM.Contacts.AddPersonOrganisationAccess = function (orgDetails, acceptEnabled) {
    // First check that the org does not already exist 23
    var org = JSON.parse(orgDetails);
    var exists = jQuery.grep(FlexiJS.CRM.Contacts.PersonOrganisationAccess, function (ro) { return ro.Id == org.Id }).length > 0;
    var table = $('#tblPersonOrganisationAccess');

    if (exists) {
        showNotification({
            type: "error",
            message: table.data("add-failure-exists"),
            autoClose: true,
            duration: 7
        });
    }
    else {
        FlexiJS.CRM.Contacts.PersonOrganisationAccess.push(org);
        showNotification({
            type: "success",
            message: table.data("add-success"),
            autoClose: true,
            duration: 7
        });
        FlexiJS.CRM.Contacts.RenderPersonOrganisationAccess(acceptEnabled);
        FlexiJS.CRM.Contacts.GatherPersonOrganisationAccess();
    }
};

FlexiJS.CRM.Contacts.DeletedPersonOrganisationAccess = function (orgId, acceptEnabled) {
    function callbackFn(arg) {
        if (arg) {
            var exists = jQuery.grep(FlexiJS.CRM.Contacts.PersonOrganisationAccess, function (ro) { return ro.Id == orgId }).length > 0;
            var table = $('#tblPersonOrganisationAccess');

            if (!exists) {
                showNotification({
                    type: "error",
                    message: table.data("delete-failure"),
                    autoClose: true,
                    duration: 7
                });
            }
            else {
                FlexiJS.CRM.Contacts.PersonOrganisationAccess = jQuery.grep(FlexiJS.CRM.Contacts.PersonOrganisationAccess, function (ro) { return ro.Id != orgId });
                showNotification({
                    type: "success",
                    message: table.data("delete-success"),
                    autoClose: true,
                    duration: 7
                });
                FlexiJS.CRM.Contacts.RenderPersonOrganisationAccess(acceptEnabled);
                FlexiJS.CRM.Contacts.GatherPersonOrganisationAccess();
            }
        }
    }

    var table = $('#tblPersonOrganisationAccess');

    radconfirm(table.data("delete-confirm"), callbackFn);
};

FlexiJS.CRM.Contacts.SetupPersonOrganisationAccess = function () {
    // Related orgs are held in an array - if array does not exist, lift them from a hidden field
    if (FlexiJS.CRM.Contacts.PersonOrganisationAccess == null) {
        if ($('#hdnPersonOrganisationAccess').val() == '') {
            $('#hdnPersonOrganisationAccess').val('[]');
        }
        FlexiJS.CRM.Contacts.PersonOrganisationAccess = JSON.parse($('#hdnPersonOrganisationAccess').val());
    }
};

FlexiJS.CRM.Contacts.RenderPersonOrganisationAccess = function (acceptEnabled) {
    FlexiJS.CRM.Contacts.SetupPersonOrganisationAccess();
    var table = $('#tblPersonOrganisationAccess');
    table.find("tr:gt(0)").remove();
    var showTable = false;
    var alt = false;
    for (var i = 0; i < FlexiJS.CRM.Contacts.PersonOrganisationAccess.length; i++) {
        var org = FlexiJS.CRM.Contacts.PersonOrganisationAccess[i];

        var button = "<TD class='action'><a class='fg_button delete' onclick='FlexiJS.CRM.Contacts.DeletedPersonOrganisationAccess(" + org.Id + "," + acceptEnabled + "); return false;'>" + table.data("delete-button") + "</a></TD>";
        var name = "<TD class='name'>" + org.Name + "</TD>";
        var view = "<TD class='view'><div class='org-access-icon'><i class='fas " + (org.View ? 'fa-check-circle' : 'fa-exclamation-circle') + "'></div></TD>";
        var edit = "<TD class='edit'><div class='org-access-icon'><i class='fas " + (org.Edit ? 'fa-check-circle' : 'fa-exclamation-circle') + "'></div></TD>";
        var approve = "<TD class='approve'><div class='org-access-icon'><i class='fas " + (org.Approve ? 'fa-check-circle' : 'fa-exclamation-circle') + "'></div></TD>";
        var accept = "";
        if (acceptEnabled === true || acceptEnabled === "true") {
            var accept = "<TD class='accept'><div class='org-access-icon'><i class='fas " + (org.Accept ? 'fa-check-circle' : 'fa-exclamation-circle') + "'></div></TD>";
        }
        var row = "<TR" + (alt ? " class='alt'" : "") + ">" + name + view + edit + approve + accept + button + "</TR>";
        table.append(row);
        showTable = true;
        alt = !alt;
    }

    if (showTable) {
        table.show();
    }
    else {
        table.hide();
    }
};

FlexiJS.CRM.Contacts.GatherPersonOrganisationAccess = function () {
    $('#hdnPersonOrganisationAccess').val(JSON.stringify(FlexiJS.CRM.Contacts.PersonOrganisationAccess));
    $('#lblPersonOrganisationAccessSaveMessage').text('');
};

FlexiJS.CRM.Contacts.PersonOrganisationAccessCheckboxChange = function () {
    var chkView = $('#chkView');
    var chkEdit = $('#chkEdit');

    if (chkEdit.prop("checked")) {
        chkView.prop("checked", true);
        chkView.prop("disabled", true);
    }
    else {
        chkView.prop("disabled", false);
    }
};

/*********************************
     ORGANISATION DEPARTMENTS
*********************************/
FlexiJS.CRM.Contacts.GetOrganisationDepartments = function (organisationID, isCrmOrganisation, controlId, hdnId) {
    if (organisationID) {
        Fluent.Website.PageService.GetOrganisationDepartments(organisationID, isCrmOrganisation, function (result) { FlexiJS.CRM.Contacts.GetOrganisatonDepartmentsSuccess(result, 0, controlId, hdnId) }, FlexiJS.CRM.Contacts.GetOrganisatonDepartmentsFailed);
    }
};

FlexiJS.CRM.Contacts.GetOrganisatonDepartmentsSuccess = function (result, tryCount, controlId, hdnId) {
    var resultDetail = JSON.parse(result.ResultDetail);
    var depts = resultDetail.Departments;
    var ddl = $("#ddl" + controlId);

    // Seem to be having a race condition with setting up of controls - if the ddl
    // is null, try again in a second
    if (tryCount == null) {
        tryCount = 0;
    }
    if (ddl == null) {
        if (tryCount < 5) {
            setTimeout(function () {
                FlexiJS.CRM.Contacts.GetOrganisatonDepartmentsSuccess(result, tryCount, controlId);
            }, 1000);
        }
        return;
    }


    if (depts.length == 0) {
        $('#div' + controlId).hide();
    }
    else {
        //Clear out all previous options
        ddl.empty();
        ddl.append($('<option>', { value: '', text: 'Please select...' }));

        $.each(depts, function (i, dept) {
            ddl.append($('<option>', { value: dept.Id, text: dept.Name }));
        });



        // Now we check if a dept has been selected
        var currentDepartmentId = $('[id*=' + hdnId + ']').val();
        if (currentDepartmentId != '') {
            ddl.find('option[value="' + currentDepartmentId + '"]').prop('selected', true);
        }
        else {
            ddl.find('option:eq(0)').prop('selected', true);
        }

        $('#div' + controlId).show();
    }
};

FlexiJS.CRM.Contacts.GetOrganisatonDepartmentsFailed = function (result) {
    $('#divOrganisationDepartment').hide();
};

FlexiJS.CRM.Contacts.SetOrganisationDepartmentId = function (ddl, hdnId) {
    $('[id*=' + hdnId + ']').val($(ddl).val());
};


/*********************************
     START: PROFILE PICTURE
*********************************/

FlexiJS.CRM.InitialiseProfilePictureUpload = function (commadelimitedfileextensionsallowed, scaletoimageheight, scaletoimagewidth, maxfilesizekb, hiddenfilename, entitytype) {

    $("#files").kendoUpload({
        async: {
            saveUrl: "/GenericControls/UploadFile.ashx"
        },
        multiple: false,
        showFileList: false,
        upload: function (e) { FlexiJS.CRM.UploadProfilePic(e, "ProfilePic", "Add", commadelimitedfileextensionsallowed, scaletoimageheight, scaletoimagewidth, maxfilesizekb); },
        error: function (e) { FlexiJS.CRM.ProfilePicUploadError(e); },
        success: function (e) { if (e.operation == "upload") { FlexiJS.CRM.ShowProfilePic(e, hiddenfilename); } },
        localization: {
            dropFilesHere: "",
            select: "",
            headerStatusUploaded: "",
            headerStatusUploading: ""
        }
    });

    //if IE do not read file type (not supported) otherwise check dragging file type and warn if not an image.
    if (!$('html').hasClass('k-ie')) {
        //on drag enter detect the drag type and warn if not valid
        $(document).on('dragenter', function (ev) {
            $('body').removeClass('invalid-drag');

            if (!ev || !ev.originalEvent || !ev.originalEvent.dataTransfer) {
                $('body').addClass('invalid-drag');
                return;
            }

            var file = null;
            if (ev.originalEvent.dataTransfer.items) file = ev.originalEvent.dataTransfer.items[0];

            //if firefox get image from alternate data
            if ($('html').hasClass('k-ff') && ev.originalEvent.dataTransfer.files) {
                file = ev.originalEvent.dataTransfer.files[0];
            }

            if (!file || !file.type) {
                $('body').addClass('invalid-drag');
                return;
            }
            //get mime type
            var type = file.type.slice(0, file.type.indexOf('/'));

            //if not an image apply class to override drop area
            if (type !== 'image') {
                $('body').addClass('invalid-drag');
            }
        });
    }


    $("#divAdd").click(function () {
        $('#files').click();
    });

    $("#ProfilePic").hover(function () { $("#divAdd").addClass('show-add-overlay'); $(this).css("cursor", "pointer"); },
        function () { $("#divAdd").removeClass('show-add-overlay'); });

    $("#j-RemoveProfilePic").click(function () {
        var removeConfirmMessage = FlexiJS.Resources.GetResourceText('controls/profilepicupload.ascx', 'RemoveConfirmMessage');
        if (FlexiJS.Validation.ConfirmAction(removeConfirmMessage)) {
            FlexiJS.CRM.DeleteProfilePic(entitytype, hiddenfilename);
        }
        else {
            return false;
        }
    });

};

FlexiJS.CRM.ProfilePicUploadError = function (e) {
    showNotification({
        type: "error",
        message: "There was an error uploading your file : " + e.XMLHttpRequest.responseText,
        autoClose: false,
        duration: 7
    });
};

FlexiJS.CRM.UploadProfilePic = function (e, uploadType, commandType, fileExtensions, height, width, maxFileSizeKb) {
    e.data = { UploadType: uploadType, CommandType: commandType, FileExtensions: fileExtensions, Height: height, Width: width, MaxFileSizeKb: maxFileSizeKb };
};

FlexiJS.CRM.ShowProfilePic = function (e, hiddenfilename) {
    if (e.response.Result) {
        if (e.response.ResultDetail) {

            var results = e.response.ResultDetail.split(",");
            $('#imgProfilePic').html('<img src="' + results[0] + '"alt="Profile Picture" class="profilePic" />');
            $('#' + hiddenfilename).val(results[1]);

            showNotification({
                type: "success",
                message: FlexiJS.Resources.GetResourceText('controls/profilepicupload.ascx', 'SuccessMessage'),
                autoClose: true,
                duration: 7
            });

            $(".k-upload-status").remove();

        }
        else {
            $(".k-upload-status").remove();
            FlexiJS.UI.ShowAlert('An error occurred');
        }
    }
    else {
        $(".k-upload-status").remove();
        showNotification({
            type: "error",
            message: e.response.ResultMessage,
            autoClose: false,
            duration: 7
        });
    }
};


FlexiJS.CRM.DeleteProfilePic = function (entitytype, hiddenfilename) {

    //TODO: Update Organisation page to pass in id as lowercase in querystring
    var crmID = FlexiJS.Utils.GetURLQuerystringObjectByName("id");
    if (crmID == '') {
        crmID = FlexiJS.Utils.GetURLQuerystringObjectByName("ID");
    }

    $.ajax({
        url: "/GenericControls/UploadFile.ashx",
        type: "POST",
        contentType: "application/json; charset=utf-8",
        dataType: 'json',
        data: JSON.stringify({ UploadType: "ProfilePic", CommandType: "Remove", CRMID: crmID, EntityType: entitytype }),
        success: function (Result) {

            if (Result.Result) {
                $("#imgProfilePic").html("");
                $("#imgProfilePic").html(Result.ResultDetail);
                $("#j-RemoveProfilePic").hide();
                $('#' + hiddenfilename).val("");

                showNotification({
                    type: "success",
                    message: FlexiJS.Resources.GetResourceText('controls/profilepicupload.ascx', 'ProfilePicRemoveSuccessMessage'),
                    autoClose: true,
                    duration: 7
                });
            }

        },
        error: function (e) {
            FlexiJS.UI.ShowAlert('Failed to remove your photo, please try again.');
        }
    });
};

/*********************************
    END: PROFILE PICTURE
*********************************/



/*********************************
     EMAIL MANAGEMENT
*********************************/
FlexiJS.CRM.EmailManagement.DoAction = function (actionId, confirmMessage) {
    if (confirmMessage != null) {
        if (!confirm(confirmMessage)) {
            return;
        }
    }
    if (actionId == 110101) {
        $('[id$="divEmailManagementRequest"]').toggle();
    }
    else {
        $('#hdnEmailManagementActionId').val(actionId);
        $('#btnEmailManagementAction').click();
    }
};;/*
  _____   _                 _       _   ____        _____                                   
 |  ___| | |   ___  __  __ (_)     | | / ___|      | ____|  _ __    _   _   _ __ ___    ___ 
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      |  _|   | '_ \  | | | | | '_ ` _ \  / __|
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |___  | | | | | |_| | | | | | | | \__ \
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_____| |_| |_|  \__,_| |_| |_| |_| |___/
                                                                                            
        Functions To Get Enums Text & Groups
        
        Requirements:
            jQuery
*/

FlexiJS.Enums = new Object();

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/

FlexiJS.Enums.EnumsCache = [];
FlexiJS.Enums.GMS = {};
FlexiJS.Enums.System = {};
FlexiJS.Enums.GenericControls = {};
FlexiJS.Enums.JobItem = {};
FlexiJS.Enums.Forms = {};
FlexiJS.Enums.Forms.FormItems = {};
FlexiJS.Enums.Forms.FormItems.Table = {};
FlexiJS.Enums.Security = {};
FlexiJS.Enums.GMSPanel = {};
FlexiJS.Enums.Message = {};

/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/

FlexiJS.Enums.GetEnumsGroup = function (enumgroup, enumtype) {
    // Description: Gets an Enum group from the cache, or if not in the cache the calls a web service to add them to the cache
    // Created: ?? - JD
    // Updated: 13/03/2017 - v3.15.1 - JD - Moved from behaviours.js to new structure
    var forcereload = false;

    // -- Enum only loaded is the cached value is null or an empty string
    if (!FlexiJS.Enums.EnumsCache[enumgroup] || !FlexiJS.Enums.EnumsCache[enumgroup][enumtype]) {
        //Enforces load if enum not in cache or enum is empty
        forcereload = true;
        if (!FlexiJS.Enums.EnumsCache[enumgroup]) FlexiJS.Enums.EnumsCache[enumgroup] = [];
        FlexiJS.Enums.EnumsCache[enumgroup][enumtype] = [];
    }

    if (forcereload == true) {
        $.ajax({
            type: "POST",
            url: "/WebServices/PageService.svc/GetEnumsByGroupType",
            data: JSON.stringify({ EnumGroup: enumgroup, EnumType: enumtype }),
            processData: false,
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            async: false,
            success: function (result) {
                FlexiJS.Enums.EnumsCache[enumgroup][enumtype] = $.parseJSON(result.d);
            }
        });
    }

    var returnvalue = [];
    if (FlexiJS.Enums.EnumsCache[enumgroup][enumtype]) returnvalue = FlexiJS.Enums.EnumsCache[enumgroup][enumtype];
    return returnvalue;
};

FlexiJS.Enums.GetEnumsText = function (enumgroup, enumtype, enumvalue) {
    // Description: Gets an Enums text from the cache, or if not in the cache the calls a web service to add them to the cache
    // Created: ?? - JD
    // Updated: 13/03/2017 - v3.15.1 - JD - Moved from behaviours.js to new structure
    var enums = FlexiJS.Enums.GetEnumsGroup(enumgroup, enumtype);
    var returnvalue = '';

    $.each(enums, function (index, enumitem) {
        if (enumitem.value == enumvalue) {
            returnvalue = enumitem.text;
            return true;
        }
    })

    return returnvalue;
};

/*********************************************************************************************
    Actual enums!!
    This may be auto generated at some stage
*********************************************************************************************/

FlexiJS.Enums.GMS.BulkAttributeType = {
    CaseOfficer: 202500,
    Category: 202501,
    TagAdd: 202502,
    TagAddDiscipline: 202503,
    TagRemove: 202504,
    Panel: 202505
};

FlexiJS.Enums.GMS.BulkAttributeAction = {
    Add: 202550,
    Remove: 202551,
    Update: 202552
};

FlexiJS.Enums.GMS.BulkReportingFormDateType = {
    Fixed: 202700,
    Dynamic: 202701
};

FlexiJS.Enums.GMS.BulkReportingFormDynamicDateType = {
    FormScheduled: 202750,
    FormAccepted: 202751,
    ProjectStart: 202752,
    ProjectEnd: 202753,
    Award: 202754,
    Completion: 202755,
    LastPayment: 202756,
    NextPayment: 202757
};

FlexiJS.Enums.GMS.BulkReportingFormDynamicDateModifier = {
    WeeksAfter: 202800,
    MonthsAfter: 202801
};

FlexiJS.Enums.GMS.BulkEmailType = {
    Unspecified: 202850,
    ReportingForm: 202851
};

FlexiJS.Enums.GMS.CheckMet = {
    Undecided: 76000,
    NotMet: 76001,
    PartiallyMet: 76002,
    FullyMet: 76003
};

FlexiJS.Enums.GMS.StageType = {
    NotApplicable: 70200,
    Submission: 70201,
    Receipt: 70202,
    Checking: 70203,
    Scoring: 70204,
    Approval: 70205,
    Offer: 70206,
    Awarded: 70207,
    Withdrawal: 70208,
    Settings: 70209,
    KeyInformation: 70210
};

FlexiJS.Enums.GMS.CheckingStatus = {
    NotStarted: 70240,
    NotChecked: 70241,
    CheckPass: 70242,
    CheckFail: 70243
};

FlexiJS.Enums.GMS.SubmissionStatus = {
    NotStarted: 70220,
    Drafting: 70221,
    Submitted: 70222,
    AwaitingApproval: 70223,
    ApprovalFail: 70224,
    ApprovalCheck: 70225
};

FlexiJS.Enums.GMS.ReceiptStatus = {
    NotStarted: 70230,
    AwaitingReceipt: 70231,
    Received: 70232,
    Acknowledged: 70233,
    Failed: 70234
};

FlexiJS.Enums.GMS.ScoreStatus = {
    NotStarted: 70250,
    Undecided: 70251,
    ScorePass: 70252,
    ScoreHold: 70253,
    ScoreFail: 70254,
    Scored: 70255,
    FurtherReview: 70256,
    ReviewersAssigned: 70257,
    UnderReview: 70258
};

FlexiJS.Enums.GMS.ApprovalStatus = {
    NotStarted: 70260,
    Undecided: 70261,
    Approved: 70262,
    Rejected: 70263
};

FlexiJS.Enums.GMS.OfferStatus = {
    NotStarted: 70270,
    NoOffer: 70271,
    UnderOffer: 70272,
    OfferAccepted: 70273,
    OfferDeclined: 70274,
    OfferWithOrganisation: 70275
};

FlexiJS.Enums.GMS.DashboardGroupItem = {
    NoMonitoring: 73100,
    MonitoringDue: 73101,
    MonitoringOverdue: 73102,
    NoPaymentsScheduled: 73103,
    NewPayments: 73104,
    OutstandingBalance: 73105,
    FinalMonitoringDue: 73106,
    ClaimMonitoringDue: 73107,
    ClaimsReceived: 73108,
    ClaimsProcessed: 73109,
    PaymentsInProcess: 73110,
    PaymentsPaid: 73111,
    AwaitingApproval: 73112,
    AwaitingMyReview: 73113,
    AwaitingRemark: 73114,
    MonitoringAccepted: 73115,
    MonitoringSubmitted: 73116,
    MyReviewedApplications: 73117,
    AwardedInProcess: 73118,
    AwardedClosed: 73119,
    AwardedCompleted: 73120,
    MonitoringRejected: 73121,
    MonitoringUnderReview: 73122,
    ClaimsRejected: 73123,
    ClaimsUnderReview: 73124,
    PaymentScheduleIncomplete: 73125,
    OfferConditionIncomplete: 73126,
    OfferConditionFallingDue: 73127,
    OfferConditionOverDue: 73128,
    ProjectNotStarted: 73129,
    ProjectInProgress: 73130,
    ProjectOnHold: 73131,
    ProjectClosed: 73132,
    ProjectComplete: 73133,
    PaymentsDue: 73134,
    PaymentsOverdue: 73135,
    ReviewsWithConflicts: 73136,
    ReturnedToApplicant: 73137,
    Resubmitted: 73138,
    OfferConditionOngoing: 73139,
    AuditSelected: 73140,
    AuditComplete: 73141,
    ConflictDeclared: 73142,
    SubmitPartiallyComplete: 73143,
    PanelChairAwaitingAssignment: 73144,
    PanelChairAssigned: 73145,
    PanelChairScored: 73146,
    AwaitingAcceptance: 73148,
    NoCommitteeMeeting: 73149
};

FlexiJS.Enums.GMS.ApplicationStatus = {
    Unsubmitted: 70210,
    InProgress: 70211,
    Approved: 70212,
    Awarded: 70213,
    Closed: 70214,
    Complete: 70215,
    OnHold: 70216,
    Withdrawn: 70217
};

FlexiJS.Enums.GMS.PaymentStatus = {
    NewPayment: 71100,
    Pending: 71101,
    Paid: 71102,
    Batched: 71104,
    Processing: 71105
};

FlexiJS.Enums.GMS.PaymentMethod = {
    None: 71000,
    Simple: 71001,
    Advanced: 71002
};

FlexiJS.Enums.System.BatchActionResult = {
    None: 202400,
    Some: 202401,
    All: 202402
};

FlexiJS.Enums.System.QuickAction = {
    DeleteApplication: 45000,
    SetApplicationScoreDueDate: 45001,
    SendBulkEmail: 45002,
    AssignReviewersToPanel: 45003,
    InviteEOIsToFullApplication: 45004,
    GenerateApplicationScoreSheets: 45005,
    RemindApplicationApprovers: 45006,
    AssignApplicationCaseOfficer: 45007,
    EditApplicationTags: 45008,
    BulkPDFGeneration: 45009,
    BulkGrantAccessToFundType: 45010,
    CloseApplication: 45011,
    BulkAttribute: 45012,
    BulkReportingForm: 45013,
    ReopenApplication: 45014,
    BulkChecking: 45015
};

FlexiJS.Enums.GenericControls.TagTypes = {
    Generic: 101020,
    Discipline: 101021
};

FlexiJS.Enums.System.EntityType = {
    GMSApplication: 44644,
    Engagement: 44665,
    GMSFundTypeApprovalStage: 44670,
    GMSFundTypeCheckingStage: 44671,
    GMSFundTypeOfferStage: 44675,
    GMSFundTypeReceiptStage: 44679,
    GMSFundTypeScoreStage: 44680
};

FlexiJS.Enums.JobItem.PDFGenerationType = {
    ApplicationOnly: 202300,
    ApplicationWithRelated: 202301,
    ApplicationRelatedAndReviewHistory: 202303,
    ApplicationAbstract: 202304,
    ReportAndClaims: 202305,
    Reviewer: 202306,
    ReviewerHistory: 202307
}

FlexiJS.Enums.Forms.FieldType = {
    Paragraph: 80200,
    CheckBox: 80201,
    DateField: 80202,
    DropDown: 80203,
    Number: 80204,
    Table: 80205,
    TextBox: 80206,
    CheckBoxList: 80207,
    RadioList: 80208,
    Currency: 80209,
    MultiLineText: 80210,
    DateAndTime: 80211,
    FileUpload: 80212,
    GMSContacts: 80220,
    GMSOrganisations: 80221,
    GMSProject: 80222,
    GMSProjectActivities: 80223,
    ColumnHeader: 80225,
    RowHeader: 80226,
    AggregationColumn: 80227,
    AggregationRow: 80228,
    CalculatedColumn: 80229,
    DatasourceList: 80230,
    ActivityStatusUpdates: 80231,
    RetrieveApplicationInformation: 80232,
    PartnershipReviewer: 80233,
    BudgetTable: 80234,
    RichTextBox: 80235
};

FlexiJS.Enums.Forms.FormItems.Table.Actions = {
    Move: 206100,
    Copy: 206101,
    Delete: 206102,
    Insert: 206103,
    Clear: 206104
};

FlexiJS.Enums.Forms.FormItems.Table.Target = {
    None: 206200,
    Before: 206201,
    After: 206202,
    Overwrite: 206203
};

FlexiJS.Enums.QuickActionResultStatus = {
    Skipped: 1,
    Failed: 2
};

FlexiJS.Enums.System.PDFGenerationAudience = {
    GMSAdmin: 206400,
    Applicant: 206401,
    Reviewer: 206402,
    ReportingOnly: 206403
};

FlexiJS.Enums.System.PDFGenerationOrder = {
    ReferenceNumber: 206500,
    Surname: 206501
};

FlexiJS.Enums.Security.AccountStatus = {
    Active: 20001,
    Locked: 20002,
    Unverified: 20003,
    Unapproved: 20004,
    Deactivated: 20005
};

FlexiJS.Enums.GMS.PaymentBatchStatus = {
    Batched: 71200,
    Processing: 71201,
    Paid: 71202
};

FlexiJS.Enums.GMSPanel.PanelStatus = {
    Active: 202000,
    InActive: 202001
};

FlexiJS.Enums.Message.YesNo = {
    Yes: 14502, 
    No: 14503
};
;/*
  _____   _                 _       _   ____        _____                              
 |  ___| | |   ___  __  __ (_)     | | / ___|      | ____|  _ __   _ __    ___    _ __ 
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      |  _|   | '__| | '__|  / _ \  | '__|
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |___  | |    | |    | (_) | | |   
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_____| |_|    |_|     \___/  |_|   
                                                                                       

        Error handling functions
    
        Requirements:
            jQuery
*/

FlexiJS.Error = new Object();

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/



/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/

/*
 * Unhandled JS Exceptions and direct calls to FlexiJS.Error.LogError are logged directly into New Relic by the New Relic APM
 */
FlexiJS.Error.LogError = function (errorFired, firedFrom) {
    // Description: Attempts to log a javascript error to the exceptions log using the web service
    // Created: 13/03/2017 - v3.15.1 - JD
    if (!errorFired) return;

    var propertyNames = Object.getOwnPropertyNames(errorFired);
    postableError = 'JS Error';
    for (var property, i = 0, len = propertyNames.length; i < len; ++i) {
        postableError += '\r\n ' + propertyNames[i] + ': "' + errorFired[propertyNames[i]] + '"';
    }

    // if New Relic APM is available
    try { 
        if (typeof newrelic !== 'undefined') {
            newrelic.noticeError(postableError, { errorsource: firedFrom });
        }
    }
    catch (ex) { 
        // Error logging the error, doom to all!
    }

};

FlexiJS.Error.TrackValueChange = function (oObj, sProp) {
    var sPrivateProp = "$_" + sProp + "_$"; // to minimize the name clash risk
    oObj[sPrivateProp] = oObj[sProp];

    console.log('Watching for changes to ' + sProp);

    // overwrite with accessor
    Object.defineProperty(oObj, sProp, {
        get: function () {
            return oObj[sPrivateProp];
        },

        set: function (value) {
            console.log("setting " + sProp + " to " + value);
            debugger; // sets breakpoint
            oObj[sPrivateProp] = value;
        }
    });
};
;FlexiJS.Events = {};

/*
 * Dont use the event bus directly outside of the flexijs.events namespace, you should write wrappers for the event you want to use
 * 
 * Functions for an event should be as follows, with '_EVENTNAME_' being your events name:
 *   FlexiJS.Events.Event._EVENTNAME_ = {};
 * 
 *   FlexiJS.Events.Event._EVENTNAME_.EventName = "_EVENTNAME_";
 *
 *   FlexiJS.Events.Event._EVENTNAME_.Bind = function (callback) {
 *       FlexiJS.Events.EventBus.bind(FlexiJS.Events.Event._EVENTNAME_.EventName, callback);
 *   };
 *
 *   FlexiJS.Events.Event._EVENTNAME_.RemoveBind = function (callback) {
 *       FlexiJS.Events.EventBus.removebind(FlexiJS.Events.Event._EVENTNAME_.EventName, callback);
 *   };
 *
 *   FlexiJS.Events.Event._EVENTNAME_.Fire = function (data) {
 *       FlexiJS.Events.EventBus.fire(FlexiJS.Events.Event._EVENTNAME_.EventName, data);
 *   };
 */

FlexiJS.Events.EventBus = {
    bind: function (event, callback) {
        document.addEventListener(event, function (e) { callback(e.detail); });
    },
    fire: function (event, data) {
        document.dispatchEvent(new CustomEvent(event, { detail: data }));
    },
    removebind: function (event, callback) {
        document.removeEventListener(event, callback);
    },
};

FlexiJS.Events.Event = {};

FlexiJS.Events.Event.FinancialInformationRefresh = {};

FlexiJS.Events.Event.FinancialInformationRefresh.EventName = "FinancialInformationRefresh";

FlexiJS.Events.Event.FinancialInformationRefresh.Bind = function (callback) {
    FlexiJS.Events.EventBus.bind(FlexiJS.Events.Event.FinancialInformationRefresh.EventName, callback);
};

FlexiJS.Events.Event.FinancialInformationRefresh.RemoveBind = function (callback) {
    FlexiJS.Events.EventBus.removebind(FlexiJS.Events.Event.FinancialInformationRefresh.EventName, callback);
};

FlexiJS.Events.Event.FinancialInformationRefresh.Fire = function (data) {
    FlexiJS.Events.EventBus.fire(FlexiJS.Events.Event.FinancialInformationRefresh.EventName, data);
};

;/*
  _____   _                 _       _   ____        _____          _                                   _ 
 |  ___| | |   ___  __  __ (_)     | | / ___|      | ____| __  __ | |_    ___   _ __   _ __     __ _  | |
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      |  _|   \ \/ / | __|  / _ \ | '__| | '_ \   / _` | | |
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |___   >  <  | |_  |  __/ | |    | | | | | (_| | | |
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_____| /_/\_\  \__|  \___| |_|    |_| |_|  \__,_| |_|
                                                                                                         
        Functions For External Services
        
        Requirements:
            jQuery
*/

FlexiJS.External = new Object();

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/



/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/

;/*
  _____   _                 _       _   ____        _____          _                                   _        ___                  _       _ 
 |  ___| | |   ___  __  __ (_)     | | / ___|      | ____| __  __ | |_    ___   _ __   _ __     __ _  | |      / _ \   _ __    ___  (_)   __| |
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      |  _|   \ \/ / | __|  / _ \ | '__| | '_ \   / _` | | |     | | | | | '__|  / __| | |  / _` |
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |___   >  <  | |_  |  __/ | |    | | | | | (_| | | |  _  | |_| | | |    | (__  | | | (_| |
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_____| /_/\_\  \__|  \___| |_|    |_| |_|  \__,_| |_| (_)  \___/  |_|     \___| |_|  \__,_|
                                                                                                                                               
        Orcid functions to get Orcid ID & Data, And Handling communications to an Orcid popup
    
        Requirements:
            jQuery
*/


FlexiJS.External.Orcid = new Object();

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/

FlexiJS.External.Orcid.CallbackCommands = {
    Complete: 'Orcid_ProcessComplete',
    CheckIfComplete: 'Orcid_CheckIfProcessComplete'
};

/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/

FlexiJS.External.Orcid.ClearTextArea = function (LinkControl) {
    // Description: Clears a text area of its Orcid Data
    // Created: ?? - AN
    // Updated: 13/03/2017 - v3.15.1 - JD - Moved from behaviours.js to new structure, added checks to make sure elements exist.
    var jqlinkControl = $(LinkControl);
    if (jqlinkControl) {
        var jqparents = jqlinkControl.parents('[id*=pnlFormItem]');
        if (jqparents && jqparents.length > 0) {
            var txtArea = jqparents.first().find('[id*=fieldInputControl]');
            if (txtArea) txtArea.val('');
        }
    }
    return false;
};

FlexiJS.External.Orcid.RetrieveOrcidData = function (LinkControl, OrcidId, RequiredData, Sandbox) {
    // Description: Calls the orcid Web API
    // Created: ?? - AN
    // Updated: 13/03/2017 - v3.15.1 - JD - Moved from behaviours.js to new structure, IE8/9 code was pointing to non existant function, pointed to correct function, added error logging to ie8/9 code

    var orcidRequest = "works";
    var apiVersion = 'v3.0';

    switch (RequiredData) {
        case "1":
            //Works
            orcidRequest = "works"
            break;
        case "2":
            //Education
            orcidRequest = "educations"
            break;
        case "3":
            //Employment
            orcidRequest = "employments"
            break;
        case "4":
            //Funding
            orcidRequest = "fundings"
            break;
        case "5":
            //Peer reviews
            orcidRequest = "peer-reviews"
            apiVersion = 'v2.0';
            break;
        case "6":
            //Qualifications
            orcidRequest = "qualifications"
            break;
    }
    
    var requesturl = (Sandbox ? 'https://pub.sandbox.orcid.org/' : 'https://pub.orcid.org/') + apiVersion + '/' + OrcidId + '/' + orcidRequest;

    if (null != navigator && null != navigator.userAgent && (navigator.userAgent.indexOf("MSIE 8.0") >= 0 || navigator.userAgent.indexOf("MSIE 9.0") >= 0) && window.XDomainRequest) {
        var xdr = new XDomainRequest;
        xdr.open("GET", requesturl),
        xdr.send(""),
        xdr.onerror = function () { FlexiJS.Error.LogError({requestedurl:requesturl, orcidid: OrcidId, requireddata: RequiredData, message: 'Orcid IE8/9 XDomainRequest Error' }, 'FlexiJS.External.Orcid.RetrieveOrcidData'); },
        xdr.onload = function () { FlexiJS.External.Orcid.RetrievedOrcidData(JSON.parse(xdr.responseText), LinkControl, orcidRequest) }
    } else {
        $.ajax({
            url: requesturl,
            type: "GET",
            headers: { Accept: "application/orcid+json", "Content-Type": "application/json; charset=utf-8" },
            dataType: "json",
            data: '',
            success: function (msg) { FlexiJS.External.Orcid.RetrievedOrcidData(msg, LinkControl, orcidRequest) },
            error: function (ex, status, error) {
                var extendederror = $.extend({}, ex, {requestedurl:requesturl, orcidid: OrcidId, requireddata: RequiredData, message: error, ex_status: status});
                FlexiJS.Error.LogError(extendederror, 'FlexiJS.External.Orcid.RetrieveOrcidData');
            }
        });
    }

    return false;
};

FlexiJS.External.Orcid.RetrievedOrcidData = function (Msg, LinkControl, RequestType) {
    // Description: Clears a text area of its Orcid Data
    // Created: ?? - AN
    // Updated: 13/03/2017 - v3.15.1 - JD - Moved from behaviours.js to new structure, added checks to make sure elements exist.
    var jqlinkControl = $(LinkControl);
    if (jqlinkControl && jqlinkControl[0]) {
        var txtArea = null;
        if (jqlinkControl[0].type == "textarea") {
            txtArea = jqlinkControl;
        } else {
            var jqparents = jqlinkControl.parents('[id*=pnlFormItem]');
            if (jqparents && jqparents.length > 0) {
                txtArea = jqparents.first().find('[id*=fieldInputControl]');
            }
        }

        if (txtArea) {
            txtArea.val('');

            var txt = "";

            switch (RequestType) {
                case "works":
                    txt = FlexiJS.External.Orcid.FormatWorksRequest(Msg);
                    break;
                case "educations":
                    txt = FlexiJS.External.Orcid.FormatEducationsQualificationsRequest(Msg, true);
                    break;
                case "employments":
                    txt = FlexiJS.External.Orcid.FormatEmploymentsRequest(Msg);
                    break;
                case "fundings":
                    txt = FlexiJS.External.Orcid.FormatFundingsRequest(Msg);
                    break;
                case "peer-reviews":
                    txt = FlexiJS.External.Orcid.FormatPeerReviewsRequest(Msg);
                    break;
                case "qualifications":
                    txt = FlexiJS.External.Orcid.FormatEducationsQualificationsRequest(Msg, false);
                    break;
            }

            if (txt === null) {
                showNotification({
                    type: "warning",
                    message: FlexiJS.Resources.GetResourceText('scripts/flexijs/external/orcid.js', 'NoInfoMessage'),
                    autoClose: true,
                    duration: 7
                });
            } else {
                txtArea.val(FlexiJS.Utils.Strings.RemoveHTMLFromString(txt));
            }
        }
    }
};

FlexiJS.External.Orcid.FormatEducationsQualificationsRequest = function (Msg, isEducation) {

    var summaries = FlexiJS.External.Orcid.GetFromAffiliationGroup(Msg, isEducation ? 'education-summary' : 'qualification-summary');

    if (summaries.length == 0) {
        return null;
    }

    var txt = "";
    for (var i = 0; i < summaries.length; i++) {
        var summary = summaries[i];

        if (summary == null) {
            continue;
        }

        var orgName = FlexiJS.External.Orcid.GetNestedValue(summary, ['organization', 'name']);
        var city = FlexiJS.External.Orcid.GetNestedValue(summary, ['organization', 'address', 'city']);
        var role = FlexiJS.External.Orcid.GetNestedValue(summary, ['role-title']);
        var dept = FlexiJS.External.Orcid.GetNestedValue(summary, ['department-name']);

        if (dept != null) { dept = '(' + dept + ')' };
        var roleDept = FlexiJS.External.Orcid.JoinNonNullStrings([role, dept], ' ', false);

        var startDate = FlexiJS.External.Orcid.GetNestedValue(summary, ['start-date']);
        var endDate = FlexiJS.External.Orcid.GetNestedValue(summary, ['end-date']);
        var dateString = FlexiJS.External.Orcid.GetDateString(startDate, endDate);

        txt += FlexiJS.External.Orcid.JoinNonNullStrings([orgName, city], ': ', true);
        txt += FlexiJS.External.Orcid.JoinNonNullStrings([dateString, roleDept], ' | ', true);

        if ((summary.length - 1) != i) {
            txt += '\n';
        }
    }
    return txt;

}

FlexiJS.External.Orcid.FormatEmploymentsRequest = function (Msg) {

    var summary = FlexiJS.External.Orcid.GetFromAffiliationGroup(Msg, 'employment-summary');

    if (summary.length == 0) {
        return null;
    }

    var txt = "";
    for (var i = 0; i < summary.length; i++) {

        if (summary[i] == null) {
            continue;
        }

        var orgName = FlexiJS.External.Orcid.GetNestedValue(summary[i], ['organization', 'name']);
        var city = FlexiJS.External.Orcid.GetNestedValue(summary[i], ['organization', 'address', 'city']);
        var role = FlexiJS.External.Orcid.GetNestedValue(summary[i], ['role-title']);
        var dept = FlexiJS.External.Orcid.GetNestedValue(summary[i], ['department-name']);

        if (dept != null) { dept = '(' + dept + ')' };
        var roleDept = FlexiJS.External.Orcid.JoinNonNullStrings([role, dept], ' ', false);

        var startDate = FlexiJS.External.Orcid.GetNestedValue(summary[i], ['start-date']);
        var endDate = FlexiJS.External.Orcid.GetNestedValue(summary[i], ['end-date']);
        var dateString = FlexiJS.External.Orcid.GetDateString(startDate, endDate);

        txt += FlexiJS.External.Orcid.JoinNonNullStrings([orgName, city], ': ', true);
        txt += FlexiJS.External.Orcid.JoinNonNullStrings([dateString, roleDept], ' | ', true);

        if ((summary.length - 1) != i) {
            txt += '\n';
        }
    }
    return txt;
}

FlexiJS.External.Orcid.FormatFundingsRequest = function (Msg) {
    if (Msg['group'] == null || Msg['group'].length == 0) { return null; }

    var fundingSum = Msg['group'];

    var txt = "";
    for (var i = 0; i < fundingSum.length; i++) {

        if (fundingSum[i]['funding-summary'] == null || fundingSum[i]['funding-summary'].length == 0) {
            continue;
        }

        for (var j = 0; j < fundingSum[i]['funding-summary'].length; j++) {
            var funding = fundingSum[i]['funding-summary'][j];

            var title = FlexiJS.External.Orcid.GetNestedValue(funding, ['title', 'title', 'value']);
            var orgName = FlexiJS.External.Orcid.GetNestedValue(funding, ['organization', 'name']);
            var city = FlexiJS.External.Orcid.GetNestedValue(funding, ['organization', 'address', 'city']);
            var type = FlexiJS.External.Orcid.GetNestedValue(funding, ['type']);
            var startDate = FlexiJS.External.Orcid.GetNestedValue(funding, ['start-date']);
            var endDate = FlexiJS.External.Orcid.GetNestedValue(funding, ['end-date']);

            txt += title == null ? '' : title + '\n';
            txt += FlexiJS.External.Orcid.JoinNonNullStrings([orgName, city == null ? null : '(' + city + ')'], ' ', true);
            txt += FlexiJS.External.Orcid.JoinNonNullStrings([FlexiJS.External.Orcid.GetDateString(startDate, endDate), type], ' | ', true); 

            txt += '\n';
        }

    }
    return txt;
}

FlexiJS.External.Orcid.FormatWorksRequest = function (Msg) {
    if (Msg['group'] == null || Msg['group'].length == 0) { return null; }

    var works = Msg['group'];

    var txt = "";
    for (var i = 0; i < works.length; i++) {

        if (works[i]['work-summary'] == null || works[i]['work-summary'].length == 0) {
            continue;
        }

        var work = works[i]['work-summary'][0];

        var title = FlexiJS.External.Orcid.GetNestedValue(work, ['title', 'title', 'value']);
        var journalTitle = FlexiJS.External.Orcid.GetNestedValue(work, ['journal-title', 'value']);
        var publicationDate = FlexiJS.External.Orcid.GetNestedValue(work, ['publication-date']);
        var type = FlexiJS.External.Orcid.GetNestedValue(work, ['type']);

        txt += title == null ? '' : title + '\n';
        txt += journalTitle == null ? '' : journalTitle + '\n';
        txt += FlexiJS.External.Orcid.JoinNonNullStrings([FlexiJS.External.Orcid.GetDateString(publicationDate), type], ' | ', true);

        if ((works.length - 1) != i) {
            txt += '\n';
        }

    }
    return txt;
}

// TODO DVB - This has NOT been done for v3.0 yet as not able to get any sample data - calls for peer-review will continue to use v2.0
FlexiJS.External.Orcid.FormatPeerReviewsRequest = function (Msg) {
    if (Msg['group'] == null || Msg['group'].length == 0) { return null; }

    var peerReviewerSummary = Msg['group'];

    var txt = "";
    for (var i = 0; i < peerReviewerSummary.length; i++) {

        if (peerReviewerSummary[i]['peer-review-summary'] == null || peerReviewerSummary[i]['peer-review-summary'].length == 0) {
            continue;
        }

        for (var j = 0; j < peerReviewerSummary[i]['peer-review-summary'].length; j++) {
            var sum = peerReviewerSummary[i]['peer-review-summary'][j];

            if (sum['completion-date'] != null && sum['completion-date'].day != null) {
                txt += sum['completion-date'].day.value + '-' + sum['completion-date'].month.value + '-' + sum['completion-date'].year.value + '\n';
            }
            else if (sum['completion-date'] != null && sum['completion-date'].month != null) {
                txt += sum['completion-date'].month.value + '-' + sum['completion-date'].year.value + '\n';
            }
            else if (sum['completion-date'] != null && sum['completion-date'].year != null) {
                txt += sum['completion-date'].year.value + '\n';
            }

            if (sum['convening-organization'] != null && sum['convening-organization']['name'] != null) {
                txt += sum['convening-organization']['name'] + '\n';
            }

            txt += '\n';
        }

    }
    return txt;
}

FlexiJS.External.Orcid.PopupComplete = function (source, data) {
    // Description: Called when a popup throws back the complete message.
    // Created: 13/03/2017 - v3.15.1 - JD
    if (data && data.hasOwnProperty('RefreshContactsGrid') && data.hasOwnProperty('OrcidId') && data.hasOwnProperty('LinkURI')) {
        FlexiJS.Popup.RemoveCallbackSender(FlexiJS.External.Orcid.CallbackCommands.CheckIfComplete);

        if (data.RefreshContactsGrid == true && typeof refreshContactsGrid == 'function') {
            refreshContactsGrid();
        }
        if (data.OrcidId != null) {
            $('[id$="hdnAuthID"]').val(data.OrcidId);

            var lbtnAuthenticationProvider = $('[id$="lbtnAuthenticationProvider"]');
            lbtnAuthenticationProvider.text(FlexiJS.Resources.GetResourceText("Controls/CRM/AuthenticationProviders.ascx.vb", "lbtnAuthenticationProvider.ProfileText"));
            lbtnAuthenticationProvider.attr('title', data.LinkURI + data.OrcidId);
            lbtnAuthenticationProvider.attr('onclick', 'FlexiJS.Popup.OpenNewWindow("' + data.LinkURI + data.OrcidId + '", 725, 980, true); return false;');
        }

        source.close();
        FlexiJS.External.Orcid.HideGetIdModal();
    }
};

FlexiJS.External.Orcid.ModalManualRefresh = function () {
    // Description: 
    // Created: 13/03/2017 - v3.15.1 - JD

    $.ajax({
        url: "/gms/WebServices/GMSService.svc/External_GetUserOrcidInfo",
        type: "POST",
        dataType: "json",
        contentType: "application/json",
        data: '',
        success: function (data) {
            if (data.d.Result && data.d.Result == true) {
                var orcidData = JSON.parse(data.d.ResultDetail);

                if (typeof refreshContactsGrid == 'function') {
                    refreshContactsGrid();
                }
                if (orcidData.id != null) {
                    $('[id$="hdnAuthID"]').val(orcidData.id);

                    var lbtnAuthenticationProvider = $('[id$="lbtnAuthenticationProvider"]');
                    lbtnAuthenticationProvider.text(FlexiJS.Resources.GetResourceText("Controls/CRM/AuthenticationProviders.ascx.vb", "lbtnAuthenticationProvider.ProfileText"));
                    lbtnAuthenticationProvider.attr('title', orcidData.url);
                    lbtnAuthenticationProvider.attr('onclick', 'FlexiJS.Popup.OpenNewWindow("' + orcidData.url + '", 725, 980, true); return false;');
                }
            }
            FlexiJS.External.Orcid.HideGetIdModal();
        },
        error: function (e) {
            FlexiJS.External.Orcid.HideGetIdModal();
        }
    });
};

FlexiJS.External.Orcid.ModalIERefresh = function (eventData) {
    // Description: 
    // Created: 13/03/2017 - v3.15.1 - JD
    eventData = eventData || window.event;

    if (eventData.type in { focus: 0, focusin: 0, pageshow: 0 }) {
        $.ajax({
            url: "/gms/WebServices/GMSService.svc/External_GetUserOrcidInfo",
            type: "POST",
            dataType: "json",
            contentType: "application/json",
            data: '',
            success: function (data) {
                if (data.d.Result && data.d.Result == true) {
                    var orcidData = JSON.parse(data.d.ResultDetail);

                    if (typeof refreshContactsGrid == 'function') {
                        refreshContactsGrid();
                    }
                    if (orcidData.id != null) {
                        $('[id$="hdnAuthID"]').val(orcidData.id);

                        var lbtnAuthenticationProvider = $('[id$="lbtnAuthenticationProvider"]');
                        lbtnAuthenticationProvider.text(FlexiJS.Resources.GetResourceText("Controls/CRM/AuthenticationProviders.ascx.vb", "lbtnAuthenticationProvider.ProfileText"));
                        lbtnAuthenticationProvider.attr('title', orcidData.url);
                        lbtnAuthenticationProvider.attr('onclick', 'FlexiJS.Popup.OpenNewWindow("' + orcidData.url + '", 725, 980, true); return false;');
                    }
                    FlexiJS.External.Orcid.HideGetIdModal();
                }
            },
            error: function (e) { }
        });
    }
};

FlexiJS.External.Orcid.ListenForIsCompleteRequest = function (orcidID, orcidPublicURI, refreshContactsGrid) {
    // Description: Listens for a message sent to this window to check if the orcid process is complete
    //              Used in externalauthentication.aspx
    //              Upon recieving the message it will send back a message saying the process is complete with the orcid id & the orcid url
    //              IE8/9: Callbacks not available, just close the window
    // Created: 13/03/2017 - v3.15.1 - JD

    var useCallbackMethod = true;

    var bInfo = FlexiJS.Browser.GetBrowserInfo();
    if (bInfo.browser == FlexiJS.Browser.Browsers.InternetExplorer) {
        if (parseInt(bInfo.version) < 12) {
            //IE can't do messages between windows, so we just want the window to close
            useCallbackMethod = false;
        }
    }
    if (useCallbackMethod == false) {
        window.close();
    } else {
        FlexiJS.Popup.AddCallbackListener(
            FlexiJS.External.Orcid.CallbackCommands.CheckIfComplete,
            function (source, data) {
                FlexiJS.Popup.SendCallback(
                    source,
                    FlexiJS.Popup.CreateCallbackDataItem(
                        FlexiJS.External.Orcid.CallbackCommands.Complete,
                        {
                            OrcidId: orcidID,
                            LinkURI: orcidPublicURI,
                            RefreshContactsGrid: refreshContactsGrid
                        }
                    )
                );
            },
            true
        );
    }
};

FlexiJS.External.Orcid.OpenGetIdWindow = function (url, width, height, allowNonCompatableIEPopup) {
    // Description: Shows the Orcid Get my ID Window
    // Created: 13/03/2017 - v3.15.1 - JD
    try {
        // - IE 8 & 9 can't do callbacks across different windows (they can only handle it in iframes, which would cause orcid cdn issue)
        //   For them we need to show a refresh/I'm done button to call a ws to check
        var useCallbackMethod = true;

        //Get Browser Info
        var bInfo = FlexiJS.Browser.GetBrowserInfo();
        if (bInfo.browser == FlexiJS.Browser.Browsers.InternetExplorer) {
            // If we're in < 12
            var ieVersion = parseInt(bInfo.version);
            if (ieVersion < 12) {
                if (allowNonCompatableIEPopup == false) {
                    //End function here, used when crm person hasn't been created for IE8/9 there is no way to link back an id, show a message to tell the user to register first
                    FlexiJS.External.Orcid.ShowNoIEModal();
                    return false;
                } else {
                    //IE 8/9 can't do messages between windows, so we still want to open the window, but we don't want the callback methods setup, and we need to show an ie8/9 popup to allow manual refreshing of the page
                    useCallbackMethod = false;
                }
            }
        }

        if (useCallbackMethod == true) {
            //Show normal modal - waiting on Orcid to finish / cancel

            var windowLinkedToCallback = FlexiJS.Popup.OpenNewWindowWithCallback(
                url,
                width,
                height,
                true,
                FlexiJS.External.Orcid.PopupComplete,
                FlexiJS.External.Orcid.CallbackCommands.Complete,
                true,
                FlexiJS.External.Orcid.HideGetIdModal,
                FlexiJS.Popup.CreateCallbackDataItem(FlexiJS.External.Orcid.CallbackCommands.CheckIfComplete, {})
            );

            if (windowLinkedToCallback == true) {
                FlexiJS.External.Orcid.ShowGetIdModal(false);
            } else {
                FlexiJS.External.Orcid.ShowGetIdModal(true);
            }

        } else {
            //Show the popup without any callbacks registered
            FlexiJS.Popup.OpenNewWindow(url, width, height, true);
            //Show normal modal + ie8/9 refresh button that calls ws
            if ("onfocusin" in document) {
                FlexiJS.External.Orcid.ShowGetIdModal(false);
                document.onfocusin = FlexiJS.External.Orcid.ModalIERefresh;
            } else {
                FlexiJS.External.Orcid.ShowGetIdModal(true);
            }
        }
    }
    catch (ex) {
        FlexiJS.Error.LogError(ex, 'FlexiJS.External.Orcid.OpenGetIdWindow');

        return false;
    }
};

FlexiJS.External.Orcid.ShowGetIdModal = function (showNonCompatableMessage) {
    // Description: Shows an orcid modal div that fills the screen alerting the user the system is waiting for orcids response
    // Created: 13/03/2017 - v3.15.1 - JD
    $('body').append(FlexiJS.Kendo.Templates.RenderTemplate('orcid', 'GetOrcidIdModal', { showNonCompatableMessage: showNonCompatableMessage }))

    var modal = $('#OrcidGetIdModal');
    if (modal) {
        modal.find('[bind="manualRefresh"]').on('click', function () {
            FlexiJS.External.Orcid.ModalManualRefresh();
        });
        modal.find('[bind="cancel"]').on('click', function () {
            FlexiJS.External.Orcid.HideGetIdModal();
        });
        $('body').css('overflow', 'hidden');
    }
};

FlexiJS.External.Orcid.HideGetIdModal = function () {
    // Description: Hides/Removes the orcid modal div
    // Created: 13/03/2017 - v3.15.1 - JD
    var modal = $('#OrcidGetIdModal');
    if (modal) {
        modal.remove();
    }
    $('body').css('overflow', '').css('overflow', null);
    if ("onfocusin" in document) document.onfocusin = null;
};


FlexiJS.External.Orcid.ShowNoIEModal = function () {
    // Description: Shows an orcid modal div that fills the screen alerting the user the system that IE can't do this
    // Created: 13/03/2017 - v3.15.1 - JD
    $('body').append(FlexiJS.Kendo.Templates.RenderTemplate('orcid', 'IECannotPerformModal', {}))

    var modal = $('#OrcidIECannotPerformModalModal');
    if (modal) {
        modal.find('[bind="cancel"]').on('click', function () {
            FlexiJS.External.Orcid.HideNoIEModal();
        });
        $('body').css('overflow', 'hidden');
    }
};

FlexiJS.External.Orcid.HideNoIEModal = function () {
    // Description: Hides/Removes the orcid modal div
    // Created: 13/03/2017 - v3.15.1 - JD
    var modal = $('#OrcidIECannotPerformModalModal');
    if (modal) {
        modal.remove();
    }
    $('body').css('overflow', '').css('overflow', null);
    if ("onfocusin" in document) document.onfocusin = null;
};


FlexiJS.External.Orcid.ClosePopupIfIEIncompatable = function () {
    // Description: Closes the popup window if callbacks can't control it
    // Created: 13/03/2017 - v3.15.1 - JD

    //Get Browser Info
    var bInfo = FlexiJS.Browser.GetBrowserInfo();
    if (bInfo.browser == FlexiJS.Browser.Browsers.InternetExplorer) {
        // If we're in < 12
        var ieVersion = parseInt(bInfo.version);
        if (ieVersion < 12) {
            window.close();
        }
    }
};

FlexiJS.External.Orcid.GetNestedValue = function (Object, fields) {
    if (Object == null || fields == null || fields.length == 0) {
        return null;
    }

    $.each(fields, function (i, field) {
        Object = Object[field];
        if (Object == null) {
            return false;
        }
    });

    return Object;
}

FlexiJS.External.Orcid.FormatDateObject = function (date) {
    if (date == null) {
        return null;
    }

    // TODO DVB - Following Orcid international date format yyyy-mm-dd or parts thereof
    var day = FlexiJS.External.Orcid.GetNestedValue(date, ['day', 'value']);
    var month = FlexiJS.External.Orcid.GetNestedValue(date, ['month', 'value']);
    var year = FlexiJS.External.Orcid.GetNestedValue(date, ['year', 'value']);

    return FlexiJS.External.Orcid.JoinNonNullStrings([year, month, day], '-');
}

FlexiJS.External.Orcid.GetDateString = function (startDate, endDate) {
    return FlexiJS.External.Orcid.JoinNonNullStrings([FlexiJS.External.Orcid.FormatDateObject(startDate), FlexiJS.External.Orcid.FormatDateObject(endDate)], ' to ');
}

FlexiJS.External.Orcid.JoinNonNullStrings = function (strings, joiner, addNewline) {
    if (strings == null || strings.length == null || strings.length == 0) {
        return null;
    }

    var result = '';
    $.each(strings, function (i, string) {
        if (string != null) {
            result = result + (result == '' ? '' : joiner) + string;
        }
    });

    if (addNewline) {
        result += '\n';
    }
    return result;
}

FlexiJS.External.Orcid.GetFromAffiliationGroup = function (Data, Type) {
    var collection = [];

    if (Data['affiliation-group'] == null || Data['affiliation-group'].length == 0) { return []; }

    $.each(Data['affiliation-group'], function (i, affiliationGroup) {
        if (affiliationGroup['summaries'] != null && affiliationGroup['summaries'].length != 0) {
            $.each(affiliationGroup['summaries'], function (j, element) {
                if (element[Type] != null) {
                    collection.push(element[Type]);
                }
            });
        }
    });

    return collection;
}

;/*
 _____   _                 _       _   ____        _____                                  
|  ___| | |   ___  __  __ (_)     | | / ___|      |  ___|   ___    _ __   _ __ ___      
| |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |_     / _ \  | '__| | '_ ` _ \      
|  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  |  _|   | (_) | | |    | | | | | | 
|_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_|      \___/  |_|    |_| |_| |_| 
                                                                                                                                     
*/


FlexiJS.Form = {};;/*
 _____   _                 _       _   ____        _____                                  _   _                      _               
|  ___| | |   ___  __  __ (_)     | | / ___|      |  ___|   ___    _ __   _ __ ___       | | | |   ___    __ _    __| |   ___   _ __ 
| |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |_     / _ \  | '__| | '_ ` _ \      | |_| |  / _ \  / _` |  / _` |  / _ \ | '__|
|  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  |  _|   | (_) | | |    | | | | | |  _  |  _  | |  __/ | (_| | | (_| | |  __/ | |   
|_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_|      \___/  |_|    |_| |_| |_| (_) |_| |_|  \___|  \__,_|  \__,_|  \___| |_|   
                                                                                                                                      
*/

FlexiJS.Form.Header = {};

/*
 __     __                 _           _       _                     __    ____            _
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|


        Any namespace variables or data objects should be declared here
*/

FlexiJS.Form.Header.jsbtnSave;
FlexiJS.Form.Header.sessionTimeOut;
FlexiJS.Form.Header.parselimit;
FlexiJS.Form.Header.countdown;
FlexiJS.Form.Header.pageTitle;

FlexiJS.Form.Header.curHours;
FlexiJS.Form.Header.curMinutes;
FlexiJS.Form.Header.curSeconds;


/*
  _____                          _     _
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/

        Error handling should use FlexiJS.Error to log any errors
*/


FlexiJS.Form.Header.SetUp = function (timeOut, userLoggedIn) {
    FlexiJS.Form.Header.parselimit = timeOut * 60;
    FlexiJS.Form.Header.sessionTimeOut = FlexiJS.Form.Header.CalculateSessionTimeOut(FlexiJS.Form.Header.parselimit);
    pageTitle = document.title;

    FlexiJS.Form.Header.jsbtnSave = $('[id*=btnAutoSave]');

    if (userLoggedIn && (FlexiJS.Form.Header.jsbtnSave.length > 0)) {
        FlexiJS.Form.Header.BeginTimeOutTimer();
        $("#sessionCountdown").show();
    }
    else {
        $("#sessionCountdown").hide();
    }

    var window = $("#divViewInstructionsPopup"),
        addContact = $("#viewFormInstructions")
            .bind("click", function () {
                window.data("kendoWindow").center();
                window.data("kendoWindow").open();
            });

    if (!window.data("kendoWindow")) {
        window.kendoWindow({
            modal: true,
            preventScroll: true,
            open: function (e) {
                $("html, body").css("overflow", "hidden");
            },
            close: function (e) {
                $("html, body").css("overflow", "");
            },
            width: "710px",
            maxHeight: "500px",
            title: FlexiJS.Resources.GetResourceText('Controls/FlexiForm/GMS/summary.ascx', 'InstructionsWindow.Title'),
        });
    }
}

FlexiJS.Form.Header.CalculateSessionTimeOut = function(timeOutSeconds) {
    var seconds = timeOutSeconds;
    var hours = seconds / (60*60);
    FlexiJS.Form.Header.curHours = Math.floor(hours);
    var minutes = (hours - FlexiJS.Form.Header.curHours) * 60;
    FlexiJS.Form.Header.curMinutes = Math.floor(minutes);
    if (FlexiJS.Form.Header.curSeconds == undefined ){
        FlexiJS.Form.Header.curSeconds = 30;
        FlexiJS.Form.Header.curMinutes - 1;
    }
    else{
        FlexiJS.Form.Header.curSeconds = timeOutSeconds % 60;
    }

    var displayMins = "" + FlexiJS.Form.Header.curMinutes;
    var displaySecs = "" + FlexiJS.Form.Header.curSeconds;

    if (FlexiJS.Form.Header.curMinutes < 10) {
        displayMins = "0" + FlexiJS.Form.Header.curMinutes;
    }

    if (FlexiJS.Form.Header.curSeconds < 10) {
        displaySecs = "0" + FlexiJS.Form.Header.curSeconds;
    }

    return FlexiJS.Form.Header.curHours + ':' + displayMins + ':' + displaySecs;
}

FlexiJS.Form.Header.CloseInstructions = function () {
    $("#divViewInstructionsPopup").data("kendoWindow").close();
}

/*
    As we want to use our Kendo confirm, this approach permits us
    to call the confirm and, if positive, remove the onclick binding
    and click the button again
*/
FlexiJS.Form.Header.ConfirmOrSubmit = function (button) {
    var submitButton = $(button);
    if (submitButton.length != 0) {
        if (submitButton.data("confirm") == null || submitButton.data("confirm") == "") {
            submitButton.attr('onclick', '').unbind('click');
            submitButton.click();
        }
        else {
            FlexiJS.Kendo.Dialogs.Confirm(
                FlexiJS.Resources.GetResourceText('Controls/FlexiForm/GMS/summary.ascx', 'SubmitConfirmTitle'),
                [submitButton.data("confirm")],
                [
                    {
                        text: FlexiJS.Resources.GetResourceText('Controls/FlexiForm/GMS/summary.ascx', 'SubmitConfirmOk'),
                        actionfunction: function () {
                            submitButton.data("confirm", "");
                            submitButton.attr('onclick', '').unbind('click');
                            submitButton.click();
                        }
                    },
                    {
                        text: FlexiJS.Resources.GetResourceText('Controls/FlexiForm/GMS/summary.ascx', 'SubmitConfirmCancel'),
                    }
                ]);
        }
    }
}

FlexiJS.Form.Header.BeginTimeOutTimer = function () {

    if (FlexiJS.Form.Header.countdown == null) {
        FlexiJS.Form.Header.countdown = $('[id*=pSessionCountdown]');
    }

    if (FlexiJS.Form.Header.parselimit == 1) {
        if (FlexiJS.Form.Header.jsbtnSave.length > 0) {
            $(FlexiJS.Form.Header.jsbtnSave[0]).click();
        }
        else {
            window.location = "/logout.aspx?ReturnURL=default.aspx"
        }
    }
    else {
        FlexiJS.Form.Header.parselimit -= 1;
        curmin = Math.floor(FlexiJS.Form.Header.parselimit / 60);
        cursec = FlexiJS.Form.Header.parselimit % 60;
        if (curmin != 0) {
            curtime = FlexiJS.Resources.GetResourceText("Controls/FlexiForm/FlexiFormHeader.ascx", "InactivityCountdown.curtime");
            if ((FlexiJS.Form.Header.parselimit == 300)) {
                showNotification({
                    type: 'error',
                    message: FlexiJS.Resources.GetResourceText("Controls/FlexiForm/FlexiFormHeader.ascx", "Inactivity5MinutesTime.message"),
                    autoClose: true,
                    duration: 235
                });
            }

            if (FlexiJS.Form.Header.parselimit == 60) {
                showNotification({
                    type: 'error',
                    message: FlexiJS.Resources.GetResourceText("Controls/FlexiForm/FlexiFormHeader.ascx", "Inactivity1MinuteTime.message"),
                    autoClose: false,
                    duration: 60
                });

                $("#sessionCountdown").addClass('alarm');
                FlexiJS.Form.Header.countdown[0].setAttribute('aria-live', 'polite');
            }
        }

        if (FlexiJS.Form.Header.countdown) {
            FlexiJS.Form.Header.countdown.html(curtime + " " + FlexiJS.Form.Header.CalculateSessionTimeOut(FlexiJS.Form.Header.parselimit));
        }
        setTimeout("FlexiJS.Form.Header.BeginTimeOutTimer()", 1000);
    }
}
;/*
  _____   _                 _       _   ____        _____                              ___   _                      
 |  ___| | |   ___  __  __ (_)     | | / ___|      |  ___|   ___    _ __   _ __ ___   |_ _| | |_    ___   _ __ ___  
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |_     / _ \  | '__| | '_ ` _ \   | |  | __|  / _ \ | '_ ` _ \ 
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  |  _|   | (_) | | |    | | | | | |  | |  | |_  |  __/ | | | | | |
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_|      \___/  |_|    |_| |_| |_| |___|  \__|  \___| |_| |_| |_|
                                                                                                                    
*/

FlexiJS.FormItem = {};
;/*
  _____   _                 _       _   ____        _____                              ___   _                             ____           _                  _           _     _
 |  ___| | |   ___  __  __ (_)     | | / ___|      |  ___|   ___    _ __   _ __ ___   |_ _| | |_    ___   _ __ ___        / ___|   __ _  | |   ___   _   _  | |   __ _  | |_  (_)   ___    _ __
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |_     / _ \  | '__| | '_ ` _ \   | |  | __|  / _ \ | '_ ` _ \      | |      / _` | | |  / __| | | | | | |  / _` | | __| | |  / _ \  | '_ \
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  |  _|   | (_) | | |    | | | | | |  | |  | |_  |  __/ | | | | | |  _  | |___  | (_| | | | | (__  | |_| | | | | (_| | | |_  | | | (_) | | | | |
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_|      \___/  |_|    |_| |_| |_| |___|  \__|  \___| |_| |_| |_| (_)  \____|  \__,_| |_|  \___|  \__,_| |_|  \__,_|  \__| |_|  \___/  |_| |_|

*/

FlexiJS.FormItem.Calculation = {};

FlexiJS.FormItem.Calculation.Enums = {
    Type: {
        Sum: 80600,
        Mean: 80601,
        Min: 80602,
        Max: 80603,
        Range: 80604,
        Difference: 80605,
        DifferenceRev: 80606,
        Count: 80607
    },
    Direction: {
        Both: 80650,
        Left: 80651,
        Right: 80652,
        Up: 80653,
        Down: 80654
    },
    Restriction: {
        None: 80670,
        StopAtAgg: 80671,
        StopAtAggAndInclude: 80672
    }
};

FlexiJS.FormItem.Calculation.GlobalCachedAggControls = null;
FlexiJS.FormItem.Calculation.GlobalCachedTableControls = null;
FlexiJS.FormItem.Calculation.UpdateTableCalculationsAllFields = null;
FlexiJS.FormItem.Calculation.UpdateTableCalculationsAllCals = null;
FlexiJS.FormItem.Calculation.TemporaryFormContainer = null;

FlexiJS.FormItem.Calculation.UpdateTableAggregations = function (panel) {
    if (panel != null) {
        var aggregations = $(panel).find('[IsAgg$=true]');

        if (FlexiJS.FormItem.Calculation.GlobalCachedAggControls == null && aggregations.length != 0) {
            FlexiJS.FormItem.Calculation.GlobalCachedAggControls = new Object();
            FlexiJS.FormItem.Calculation.GlobalCachedAggControls.HiddenFields = new Object();

            var tmpCachedAggControls = $('[id*=hdnaggregation_]');
            for (var tmpCachedAggControlsIndex = 0; tmpCachedAggControlsIndex < tmpCachedAggControls.length; tmpCachedAggControlsIndex++) {
                var tmpCtl = $(tmpCachedAggControls[tmpCachedAggControlsIndex]);
                var tmpCtlParts = tmpCtl[0].id.split('_');
                FlexiJS.FormItem.Calculation.GlobalCachedAggControls.HiddenFields[tmpCtlParts[tmpCtlParts.length - 2]] = tmpCtl;
            }
        }

        for (var i = 0; i < aggregations.length; i++) {
            FlexiJS.FormItem.Calculation.UpdateTableAggregation(aggregations[i]);
        }
    }
    FlexiJS.FormItem.Calculation.UpdateTableCalculations();
};

FlexiJS.FormItem.Calculation.GetValuesForTableAggregation = function (aggregateToCell) {
    //Get the controls parent row & cell
    var cellTR = aggregateToCell.parents('tr:first');
    if (cellTR == null || cellTR.length == 0) return new Array();
    var tr = $(cellTR);

    var cellTD = aggregateToCell.parents('td:first');
    if (cellTD == null || cellTD.length == 0) return new Array();
    var td = $(cellTD);

    //Get aggregation parameters
    var position = aggregateToCell[0].getAttribute("AggPos");
    var direction = aggregateToCell[0].getAttribute("AggDirection") || FlexiJS.FormItem.Calculation.Enums.Direction.Both; //Both if null or empty
    var restriction = aggregateToCell[0].getAttribute("AggRestriction") || FlexiJS.FormItem.Calculation.Enums.Restriction.None; //None if null or empty

    switch (position.toLowerCase().trim()) {
        case "row":
            return FlexiJS.FormItem.Calculation.GetValuesForTableRowAggregation(tr, td, direction, restriction);
            
        case "col":
            return FlexiJS.FormItem.Calculation.GetValuesForTableColumnAggregation(tr, td, direction, restriction);

        default:
            return new Array();
    }
};

FlexiJS.FormItem.Calculation.GetValuesForTableRowAggregation = function (tr, td, direction, restriction) {
    var loopIndex;
    var minIndex = 0;
    var maxIndex = 0;
    var cellIndex = tr.children().index(td);
    var lookLeft = false;
    var lookRight = false;

    var tds = tr.children('td');

    if (!isNaN(direction)) direction = Number(direction);
    switch (direction) {
        case FlexiJS.FormItem.Calculation.Enums.Direction.Left:
            //All cells in row to the left
            minIndex = 0;
            maxIndex = cellIndex;
            lookLeft = true;
            break;

        case FlexiJS.FormItem.Calculation.Enums.Direction.Right:
            //All cells in row to the right
            minIndex = cellIndex;
            maxIndex = tds.length;
            lookRight = true;
            break;

        default:
            //All cells in row both directions
            minIndex = 0;
            maxIndex = tds.length;
            lookLeft = true;
            lookRight = true;
            break;
    }

    if (!isNaN(restriction)) restriction = Number(restriction);
    if (
        restriction == FlexiJS.FormItem.Calculation.Enums.Restriction.StopAtAgg
        ||
        restriction == FlexiJS.FormItem.Calculation.Enums.Restriction.StopAtAggAndInclude
    ) {
        //Check Left
        if (lookLeft && cellIndex > minIndex) {
            for (loopIndex = minIndex; loopIndex < cellIndex; loopIndex++) {
                if ($(tds[loopIndex]).find('[IsAgg$=true]').length) minIndex = loopIndex;
            }
            
            if (minIndex != 0 && restriction == FlexiJS.FormItem.Calculation.Enums.Restriction.StopAtAgg) minIndex += 1;
        }

        //Check Right
        if (lookRight && cellIndex < maxIndex) {
            for (loopIndex = maxIndex; loopIndex > cellIndex; loopIndex--) {
                if ($(tds[loopIndex]).find('[IsAgg$=true]').length) maxIndex = loopIndex;
            }

            if (maxIndex != tds.length && restriction == FlexiJS.FormItem.Calculation.Enums.Restriction.StopAtAgg) maxIndex -= 1;
        }
    }

    var values = new Array();
    for (loopIndex = minIndex; loopIndex <= maxIndex; loopIndex++) {
        if (loopIndex != cellIndex) {
            var val = FlexiJS.FormItem.Calculation.GetValueFromTableCell($(tds[loopIndex]));
            if (val != null && !isNaN(val)) values.push(Number(val));
        }
    }

    return values;
};

FlexiJS.FormItem.Calculation.GetValuesForTableColumnAggregation = function (tr, td, direction, restriction) {
    var tableTrs = td.parents('table:first').children().children('tr');
    var loopIndex;
    var minIndex = 0;
    var maxIndex = 0;
    var cellIndex = tr.children().index(td);
    var rowIndex = tableTrs.index(tr[0]);
    var lookUp = false;
    var lookDown = false;

    if (!isNaN(direction)) direction = Number(direction);
    switch (direction) {
        case FlexiJS.FormItem.Calculation.Enums.Direction.Up:
            //All cells in the column above/upward
            minIndex = 0;
            maxIndex = rowIndex;
            lookUp = true;
            break;

        case FlexiJS.FormItem.Calculation.Enums.Direction.Down:
            //All cells in the column below/downward
            minIndex = rowIndex;
            maxIndex = tableTrs.length;
            lookDown = true;
            break;

        default:
            //All cells in row both directions
            minIndex = 0;
            maxIndex = tableTrs.length;
            lookUp = true;
            lookDown = true;
            break;
    }

    if (!isNaN(restriction)) restriction = Number(restriction);
    if (
        restriction == FlexiJS.FormItem.Calculation.Enums.Restriction.StopAtAgg
        ||
        restriction == FlexiJS.FormItem.Calculation.Enums.Restriction.StopAtAggAndInclude
    ) {
        //Check above/upward
        if (lookUp && rowIndex > minIndex) {
            for (loopIndex = minIndex; loopIndex < rowIndex; loopIndex++) {
                if ($(tableTrs[loopIndex]).children('td').eq(cellIndex).find('[IsAgg$=true]').length) minIndex = loopIndex;
            }

            if (minIndex != 0 && restriction == FlexiJS.FormItem.Calculation.Enums.Restriction.StopAtAgg) minIndex += 1;
        }

        //Check below/downward
        if (lookDown && rowIndex < maxIndex) {
            for (loopIndex = maxIndex; loopIndex > rowIndex; loopIndex--) {
                if ($(tableTrs[loopIndex]).children('td').eq(cellIndex).find('[IsAgg$=true]').length) maxIndex = loopIndex;
            }
            if (maxIndex != tableTrs.length && restriction == FlexiJS.FormItem.Calculation.Enums.Restriction.StopAtAgg) maxIndex -= 1;
        }
    }

    var values = new Array();
    for (loopIndex = minIndex; loopIndex <= maxIndex; loopIndex++) {
        if (loopIndex != rowIndex) {
            var val = FlexiJS.FormItem.Calculation.GetValueFromTableCell($(tableTrs[loopIndex]).children('td').eq(cellIndex));
            if (val != null && !isNaN(val)) values.push(Number(val));
        }
    }

    return values;
};

FlexiJS.FormItem.Calculation.GetValueFromTableCell = function (Td) {
    var childInputControls = Td.find('[id*=fieldInputControl]');
    if (childInputControls.length == 0) childInputControls = Td.find('[id*=FieldInputControl]');

    //field controls
    if (childInputControls.length > 0) {
        if (FlexiJS.FormItem.Calculation.IsFieldIncludable($(childInputControls[0]))) {
            if ($(childInputControls[0]).val() != "") {
                return FlexiJS.Numbers.RemoveNonNumeric($(childInputControls[0]).val());
            } else {
                return FlexiJS.Numbers.RemoveNonNumeric($(childInputControls[0]).text());
            }
        } else {
            return null;
        }
    }

    //Calculations & Aggregations
    if (childInputControls.length == 0) {
        childInputControls = Td.find('[id*=_calculation_], [id*=_aggregation_]');
        if (childInputControls.length > 0) return $(childInputControls[0]).attr("actualvalue");
    }

    return null;
};

FlexiJS.FormItem.Calculation.IsFieldIncludable = function (field) {
    return !!field.attr("data-aggregatable");
};

FlexiJS.FormItem.Calculation.UpdateTableAggregation = function (aggregateElement) {
    var aggregateToCell = $(aggregateElement);
    if (!aggregateToCell) return null;
            
    var calculatedValue = FlexiJS.FormItem.Calculation.CalculateAggregatedValue(
        FlexiJS.FormItem.Calculation.GetValuesForTableAggregation(aggregateToCell),
        aggregateToCell[0].getAttribute("AggType")
    );

    aggregateToCell.attr("actualValue", calculatedValue);
    var fiid = aggregateToCell.attr("fiid");

    if (!FlexiJS.FormItem.Calculation.GlobalCachedAggControls.HiddenFields[fiid]) {
        //Hidden field not defined, form likely in edit mode and item is new, id so attempt to find it
        FlexiJS.FormItem.Calculation.GlobalCachedAggControls.HiddenFields[fiid] = $('[id*=hdncalculation_' + fiid + ']');
    }

    //Save in hidden field so is available serverside
    FlexiJS.FormItem.Calculation.GlobalCachedAggControls.HiddenFields[fiid].val(calculatedValue);

    if (!isNaN(calculatedValue)) {
        if (calculatedValue == null || calculatedValue.length == 0) calculatedValue = "&nbsp;";

        var df = 2;
        var displayFormat = aggregateToCell[0].getAttribute("DisplayFormat");
        if (!isNaN(displayFormat) && displayFormat != "") df = displayFormat;
        FlexiJS.FormItem.Calculation.GlobalCachedAggControls.HiddenFields[fiid].val(FlexiJS.Numbers.RoundNumberToFormat(calculatedValue, df));
        calculatedValue = FlexiJS.Numbers.FormatNumber(calculatedValue, df);
    }

    aggregateToCell.html(
        (aggregateToCell[0].getAttribute("AggPreText") || "")
        + calculatedValue
        + (aggregateToCell[0].getAttribute("AggPostText") || "")
    );

};

FlexiJS.FormItem.Calculation.CalculateAggregatedValue = function (Values, AggType) {
    if (!isNaN(AggType)) AggType = Number(AggType);
    if (!Values || Values.length < 1) return AggType == FlexiJS.FormItem.Calculation.Enums.Type.Count ? 1 : "";
    switch (AggType) {
        case FlexiJS.FormItem.Calculation.Enums.Type.Sum:
            var total = new BigNumber(0);
            Values.forEach(function (num) { total = total.plus(num); });
            return total;

        case FlexiJS.FormItem.Calculation.Enums.Type.Mean:
            var sum = new BigNumber(FlexiJS.FormItem.Calculation.CalculateAggregatedValue(Values, FlexiJS.FormItem.Calculation.Enums.Type.Sum));
            return sum.dividedBy(Values.length);

        case FlexiJS.FormItem.Calculation.Enums.Type.Min:
            var tmin = null;
            Values.forEach(function (num) { tmin = tmin === null || num < tmin ? num : tmin; });
            return tmin;

        case FlexiJS.FormItem.Calculation.Enums.Type.Max:
            var tmax = null;
            Values.forEach(function (num) { tmax = tmax == null || num > tmax ? num : tmax; });
            return tmax;

        case FlexiJS.FormItem.Calculation.Enums.Type.Range:
            if (Values.length < 2) return "";

            var max = new BigNumber(FlexiJS.FormItem.Calculation.CalculateAggregatedValue(Values, FlexiJS.FormItem.Calculation.Enums.Type.Max));
            var min = new BigNumber(FlexiJS.FormItem.Calculation.CalculateAggregatedValue(Values, FlexiJS.FormItem.Calculation.Enums.Type.Min));

            return max.minus(min).toFixed(2);

        case FlexiJS.FormItem.Calculation.Enums.Type.Difference:
            if (Values.length != 2) return "";
            return new BigNumber(Values[1]).minus(Values[0]);

        case FlexiJS.FormItem.Calculation.Enums.Type.DifferenceRev:
            if (Values.length != 2) return "";
            return new BigNumber(Values[0]).minus(Values[1]);

        case FlexiJS.FormItem.Calculation.Enums.Type.Count:
            return Values.length + 1;

        default:
            return "";
    }
};

FlexiJS.FormItem.Calculation.UpdateOnControlBlur = function (sourceControl) {
    var sourceTableContainer = $(sourceControl).parents('table').first();
    FlexiJS.FormItem.Calculation.UpdateTableCalculations();
    if (sourceTableContainer) FlexiJS.FormItem.Calculation.UpdateTableAggregations(sourceTableContainer);
    FlexiJS.FormItem.Calculation.UpdateTableCalculations();
    if (sourceTableContainer) FlexiJS.FormItem.Calculation.UpdateTableAggregations(sourceTableContainer);

    FlexiJS.FormItemFlow.RunAllFlowRules();
};

FlexiJS.FormItem.Calculation.ResetAllCalcs = function () {
    if (FlexiJS.FormItem.Calculation.UpdateTableCalculationsAllCals != 'undefined') {
        FlexiJS.FormItem.Calculation.UpdateTableCalculationsAllCals = null;
        FlexiJS.FormItem.Calculation.UpdateTableCalculationsAllFields = null;
    }

    $.each(
        $('[tableformitemid]'),
        function (index, tableElement) {
            FlexiJS.FormItem.Calculation.UpdateTableAggregations($(tableElement).closest('div'));
        }
    );
};

FlexiJS.FormItem.Calculation.UpdateTableCalculations = function () {
    var calculations;
    var cachedTableControls = FlexiJS.FormItem.Calculation.GlobalCachedTableControls;
    if (cachedTableControls == null) {
        cachedTableControls = new Object();
        cachedTableControls.AllCalculations = $('[IsCalc$=true]');
        cachedTableControls.FormItemContainer = $('[id*=pnlFormItemsContainer]');
        cachedTableControls.AllFields = cachedTableControls.FormItemContainer.find('[fiid]');
        cachedTableControls.AllFieldsInputControls = new Object();
        cachedTableControls.AllTableHiddenFields = new Object();
        cachedTableControls.AllTableInputControlDivs = new Object();

        for (var fieldIndex = 0; fieldIndex < cachedTableControls.AllFields.length; fieldIndex++) {
            var jfield = $(cachedTableControls.AllFields[fieldIndex]);
            var tmpAttr = jfield.attr("fiid");
            if (tmpAttr != null) {
                var kvpObject = new Object();
                kvpObject.key = tmpAttr;

                var innerField = jfield.find('[id*=fieldInputControl]');
                cachedTableControls.AllTableInputControlDivs[kvpObject.key] = jfield.find('[id*=divFieldInputControl]');

                if (innerField.length > 0) {
                    kvpObject.value = innerField;
                }
                else {
                    kvpObject.value = jfield;
                }

                cachedTableControls.AllFieldsInputControls[kvpObject.key] = kvpObject;
            }
        }

        var tmpCachedTableHdnFields = $('[id*=hdncalculation_]');
        for (var hdnLoopI = 0; hdnLoopI < tmpCachedTableHdnFields.length; hdnLoopI++) {
            var tmpCtl = $(tmpCachedTableHdnFields[hdnLoopI]);
            var tmpCtlParts = tmpCtl[0].id.split('_');
            cachedTableControls.AllTableHiddenFields[tmpCtlParts[tmpCtlParts.length - 2]] = tmpCtl;
        }
    }

    calculations = FlexiJS.FormItem.Calculation.UpdateTableCalculationsAllCals || cachedTableControls.AllCalculations;
    FlexiJS.FormItem.Calculation.UpdateTableCalculationsAllCals = calculations;

    for (var i = 0; i < calculations.length; i++) {
        var equation = calculations[i].getAttribute("Equation");

        var valuedEquation = "";
        var index = 0;

        while (index < equation.length) {
            if (equation.charAt(index) == "[") {

                var endIndex = index;
                while (endIndex < equation.length && equation.charAt(endIndex) != "]") {
                    endIndex += 1;
                }
                endIndex += 1;

                var valueToReplace = equation.substring(index, endIndex);
                var replaceValue = "";

                var fieldParts = valueToReplace.split('.');

                if (fieldParts.length > 0) {
                    var fieldId = valueToReplace.split('.');
                    var formItemId = fieldId[0].replace('[', '').replace(']', '');

                    if (FlexiJS.FormItem.Calculation.TemporaryFormContainer == null) {
                        FlexiJS.FormItem.Calculation.TemporaryFormContainer = cachedTableControls.FormItemContainer;
                    }
                    if (FlexiJS.FormItem.Calculation.TemporaryFormContainer != null) {
                        if (FlexiJS.FormItem.Calculation.UpdateTableCalculationsAllFields == null) {
                            FlexiJS.FormItem.Calculation.UpdateTableCalculationsAllFields = new Object();

                            for (var ficIndex in cachedTableControls.AllFieldsInputControls) {
                                if (typeof cachedTableControls.AllFieldsInputControls[ficIndex].Key == 'undefined') {
                                    var ob = cachedTableControls.AllFieldsInputControls[ficIndex];
                                    var ctlObj = new Object();
                                    ctlObj.Key = ob.key;
                                    ctlObj.value = ob.value;
                                    FlexiJS.FormItem.Calculation.UpdateTableCalculationsAllFields[ctlObj.Key] = ctlObj;
                                }
                            }
                        }

                        if (!isNaN(formItemId)) {
                            if (FlexiJS.FormItem.Calculation.UpdateTableCalculationsAllFields[formItemId] != null) {
                                var fieldObj = FlexiJS.FormItem.Calculation.UpdateTableCalculationsAllFields[formItemId];
                                if (fieldObj.value.length > 0) {
                                    if (fieldObj.value[0].id.indexOf("fieldInputControl") != -1) {
                                        replaceValue = $(fieldObj.value[0]).val();
                                    }
                                    else {
                                        if ($(fieldObj.value[0]).attr("actualValue") != null) {
                                            replaceValue = $(fieldObj.value[0]).attr("actualValue");
                                        }
                                        else {
                                            //Probably in print mode, TODO make nice (AF)
                                            var divFieldControl = fieldObj.value.find('[id*=divFieldInputControl]');
                                            if (divFieldControl.length > 0) {
                                                if (divFieldControl.children().length == 0) {
                                                    replaceValue = divFieldControl.text();
                                                }
                                                else {
                                                    replaceValue = $(fieldObj.value[0]).html();
                                                }
                                            }
                                            else {
                                                replaceValue = $(fieldObj.value[0]).html();
                                            }

                                        }
                                    }

                                }
                            }
                        }
                        else {
                            //is a field within a control (i.e. gms projects)
                            var typeAndText = formItemId.split('_');
                            if (typeAndText.length > 1) {
                                if (!isNaN(typeAndText[0])) {
                                    var control = FlexiJS.FormItem.Calculation.TemporaryFormContainer.find('[fieldtypeid=' + typeAndText[0] + ']').find('[id*=' + typeAndText[1] + ']');
                                    if (control.length > 0) {
                                        replaceValue = $(control[0]).val();
                                    }
                                }
                            }
                        }
                    }
                }

                replaceValue = FlexiJS.Numbers.RemoveNonNumeric(replaceValue);
                if (replaceValue == "") replaceValue = 0;

                valuedEquation += replaceValue;
                index = endIndex;
            }
            else {
                valuedEquation += equation.charAt(index);
                index += 1;
            }
        }

        var value = "0";

        try {
            value = Parser.evaluate(valuedEquation);
        }
        catch (e) {
            FlexiJS.Error.LogError({ equation: valuedEquation, message: e.message }, "behaviours.js");
            value = "";
        }

        var preText = calculations[i].getAttribute("CalcPreText");
        var postText = calculations[i].getAttribute("CalcPostText");
        var newHtml = preText + value + postText;

        var jCalci = $(calculations[i]);
        var tmpOldHtml = jCalci.html();

        if (newHtml.length != tmpOldHtml.length || newHtml != tmpOldHtml) {
            jCalci.attr("actualValue", value);


            var displayFormat = 2;
            if (!isNaN(calculations[i].getAttribute("DisplayFormat")) && calculations[i].getAttribute("DisplayFormat") != "") {
                displayFormat = calculations[i].getAttribute("DisplayFormat");
            }
            //Save in hidden field so is available serverside. Ensure rounded to same value as whats being displayed
            cachedTableControls.AllTableHiddenFields[calculations[i].getAttribute("fiid")].val(FlexiJS.Numbers.RoundNumberToFormat(value, displayFormat));
            value = FlexiJS.Numbers.FormatNumber(value, displayFormat);
            jCalci.html(preText + value + postText);
        }


    }

    FlexiJS.FormItem.Calculation.GlobalCachedTableControls = cachedTableControls;
    return true;
};

FlexiJS.FormItem.Calculation.ClearCachedControls = function () {
    FlexiJS.FormItem.Calculation.ClearCachedAggregateControls();
    FlexiJS.FormItem.Calculation.ClearCachedTableControls();
};

FlexiJS.FormItem.Calculation.ClearCachedTableControls = function () {
    FlexiJS.FormItem.Calculation.GlobalCachedTableControls = null;
};

FlexiJS.FormItem.Calculation.ClearCachedAggregateControls = function () {
    FlexiJS.FormItem.Calculation.GlobalCachedTableControls = null;
};

FlexiJS.FormItem.Calculation.GetCachedTableControl = function (formItemId) {
    if (FlexiJS.FormItem.Calculation.GlobalCachedTableControls != null && FlexiJS.FormItem.Calculation.GlobalCachedTableControls.AllFields != null) {
        var res = FlexiJS.FormItem.Calculation.GlobalCachedTableControls.AllFields.filter('[fiid=' + formItemId + ']');
        if (res.length > 0) return res;
    }
    return null;
};

FlexiJS.FormItem.Calculation.CleanupEquation = function (editor) {
    //var range = editor.getSelection().getRange();
    var NoIssues = true;

    var text = editor.get_text();
    var index = 0;

    while (index < text.length) {
        if (text.charAt(index) == "]") {
            var readText = text;
            text = readText.substring(0, index) + readText.substring(index + 1, readText.length);
            NoIssues = false;
        }
        else if (text.charAt(index) == "[") {
            var endIndex = index;
            var lastNameBracket = 0;

            while (endIndex < text.length && text.charAt(endIndex) != "]") {
                endIndex += 1;
                if (text.charAt(endIndex) == "}") {
                    lastNameBracket = endIndex;
                }
            }
            endIndex += 1;

            var testString = text.substring(index, endIndex);
            var validOptions = $('[id*=hdnEquationFields]').val().split("@");

            var valid = false;

            for (var i = 0; i < validOptions.length; i++) {

                if (validOptions[i].replace(/\s+/g, ' ') == testString.replace(/\s+/g, ' ')) {
                    i = validOptions.length;
                    valid = true;
                    index = endIndex;
                }
            }

            if (!valid) {
                var tmpText = text;
                if (endIndex > lastNameBracket + 1 && lastNameBracket != 0) {
                    endIndex = lastNameBracket + 1;
                }
                text = tmpText.substring(0, index) + tmpText.substring(endIndex, tmpText.length);
                NoIssues;
            }

        }
        else {
            index += 1;
        }
    }
    if (text != editor.get_text()) {
        editor.set_html(text);
    }
    ValidateEquation();
    return NoIssues;
};
;/*

  _____   _                 _       _   ____        _____                              ___   _                            _____   _   _          _   _           _                       _
 |  ___| | |   ___  __  __ (_)     | | / ___|      |  ___|   ___    _ __   _ __ ___   |_ _| | |_    ___   _ __ ___       |  ___| (_) | |   ___  | | | |  _ __   | |   ___     __ _    __| |
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |_     / _ \  | '__| | '_ ` _ \   | |  | __|  / _ \ | '_ ` _ \      | |_    | | | |  / _ \ | | | | | '_ \  | |  / _ \   / _` |  / _` |
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  |  _|   | (_) | | |    | | | | | |  | |  | |_  |  __/ | | | | | |  _  |  _|   | | | | |  __/ | |_| | | |_) | | | | (_) | | (_| | | (_| |
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_|      \___/  |_|    |_| |_| |_| |___|  \__|  \___| |_| |_| |_| (_) |_|     |_| |_|  \___|  \___/  | .__/  |_|  \___/   \__,_|  \__,_|
                                                                                                                                                        |_|

*/
FlexiJS.FormItem.FileUpload = {};

FlexiJS.FormItem.FileUpload.Setup = function (clientID, isEnabled, entityTypeID, entityRowID, documentCategoryID, includeApplicationStage, includeActionColumn, maxFileSize, overMaxSizeMessage, allowedFileTypes, invalidFileTypeMessage) {
    var uploaderClientId = "#files_" + clientID;
    var gridClientId = "#documentUploadGrid_" + clientID;
    var resourceFile = "Controls/DocumentUpload.ascx";

    //Setup the upload control
    var existingUploader = $(uploaderClientId).data('kendoUpload');
    if (!existingUploader) {
        //Add the allowed file types to the HTML5 property accept. This limits the select files dialog to the file types specified. These checks are enforced on the server, this is solely for user benifit
        if (allowedFileTypes && allowedFileTypes.length > 0) $(uploaderClientId).attr("accept", allowedFileTypes);
        $(uploaderClientId).kendoUpload({
            localization: {
                cancel: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.cancel"),
                clearSelectedFiles: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.clearSelectedFiles"),
                dropFilesHere: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.dropFilesHere"),
                headerStatusUploaded: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.headerStatusUploaded"),
                headerStatusUploading: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.headerStatusUploading"),
                invalidFileExtension: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.invalidFileExtension"),
                invalidFiles: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.invalidFiles"),
                invalidMaxFileSize: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.invalidMaxFileSize"),
                invalidMinFileSize: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.invalidMinFileSize"),
                remove: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.remove"),
                retry: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.retry"),
                select: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.select"),
                statusFailed: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.statusFailed"),
                statusUploaded: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.statusUploaded"),
                statusUploading: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.statusUploading"),
                uploadSelectedFiles: FlexiJS.Resources.GetResourceText(resourceFile, "kendoUpload.uploadSelectedFiles")
            },
            async: {
                saveUrl: "/GenericControls/UploadFile.ashx",
                autoUpload: true,
                batch: false
            },
            enabled: isEnabled,
            multiple: true,
            showFileList: false,
            select: function (e) {
                var files = e.files;

                for (var fileCntr = 0; fileCntr < files.length; fileCntr++) {
                    //Validate that file is under max file size
                    if (files[fileCntr].size > maxFileSize) {
                        showNotification({ type: "error", message: overMaxSizeMessage, autoClose: false, duration: 7 });
                        e.preventDefault();
                        return false;
                    }
                    //Validate that the file  type is in the allowed list
                    if ($.inArray(files[fileCntr].extension.toLowerCase(), allowedFileTypes) == -1) {
                        showNotification({ type: "error", message: invalidFileTypeMessage, autoClose: false, duration: 7 });
                        e.preventDefault();
                        return false;
                    }
                }
            },
            upload: function (e) {
                e.data = {
                    UploadType: "Document",
                    CommandType: "Add",
                    EntityTypeId: entityTypeID,
                    EntityRowId: entityRowID,
                    DocumentCategoryId: documentCategoryID,
                    IncludeApplicationStage: includeApplicationStage
                };
            },
            error: function (e) {
                showNotification({
                    type: "error",
                    message: FlexiJS.Resources.GetResourceText(resourceFile, "DocumentUpload.errormessage") + e.XMLHttpRequest.responseText,
                    autoClose: false,
                    duration: 7
                });
            },
            success: function (e) {
                if (e.response.Result == true) {
                    var documentName = e.response.ResultDetail.DocumentName;
                    var tempgrid = $("#documentUploadGrid_" + clientID).data("kendoGrid");
                    tempgrid.dataSource.read();
                    tempgrid.refresh();

                    showNotification({
                        type: "success",
                        message: FlexiJS.Resources.GetResourceText(resourceFile, "DocumentUpload.successmessage").replace('" + [documentName] + "', documentName),
                        autoClose: true,
                        duration: 4
                    });
                } else {
                    showNotification({
                        type: "error",
                        message: e.response.ResultMessage,
                        autoClose: true,
                        duration: 4
                    });
                }
            },
            validation: {
                maxFileSize: maxFileSize
            }
        });
    }


    var existingGrid = $(gridClientId).data('kendoGrid');
    if (!existingGrid && $(gridClientId)[0]) {
        var gridColumns = [
            {
                field: "FileName",
                title: FlexiJS.Resources.GetResourceText(resourceFile, "FileName.title"),
                sortable: true,
                template: '<a href="/downloadfile.aspx?dsid=#=DocumentID#" target="_blank">#=FileName#</a>',
                width: 250
            }, {
                field: "DateCreated",
                title: FlexiJS.Resources.GetResourceText(resourceFile, "DateCreated.title"),
                sortable: true,
                width: 200
            }
        ];

        if (includeActionColumn == true) {
            gridColumns.push(
                {
                    title: FlexiJS.Resources.GetResourceText(resourceFile, "documentUploadGrid.ActionColumnHeader"),
                    width: 80,
                    command: {
                        name: "destroy",
                        text: FlexiJS.Resources.GetResourceText(resourceFile, "documentUploadGrid.DeleteFileButton"),
                        className: "fg_button delete secondary",
                        click: function () {
                            $(gridClientId).kendoGrid.removeRow($(this));
                        }
                    }
                }
            );
        }

        $(gridClientId)[0].postData = {
            EntityTypeId: entityTypeID,
            EntityRowId: entityRowID
        };

        $(gridClientId).kendoGrid({
            dataSource: {
                transport: {
                    read: {
                        type: "POST",
                        url: "/WebServices/DocumentService.svc/GetDocuments",
                        dataType: "json",
                        contentType: "application/json",
                        data: $(gridClientId)[0].postData
                    },
                    parameterMap: function (data, operation) {
                        return JSON.stringify(data);
                    }
                },
                schema: {
                    data: function (result) {
                        var data = JSON.parse(result.d);
                        return data;
                    },
                    type: "json"
                }
            },
            columns: gridColumns,
            sortable: true,
            editable: {
                mode: "inline",
                confirmation: FlexiJS.Resources.GetResourceText(resourceFile, "documentUploadGrid.DeleteConfirmText"),
                cancelDelete: FlexiJS.Resources.GetResourceText(resourceFile, "documentUploadGrid.DeleteConfirmCancelButton"),
                confirmDelete: FlexiJS.Resources.GetResourceText(resourceFile, "documentUploadGrid.DeleteConfirmOkButton")
            },
            pageable: false,
            scrollable: false,
            autoBind: true,

            remove: function (e) {
                $.ajax({
                    type: "POST",
                    url: "/WebServices/DocumentService.svc/DeleteDocument",
                    data: '{"documentStoreId": "' + e.model.DocumentID + '"}',
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    success: function (msg) {
                        showNotification({
                            type: "success",
                            message: FlexiJS.Resources.GetResourceText(resourceFile, "DeleteDocument.successmessage"),
                            autoClose: true,
                            duration: 4
                        });
                    },
                    error: function (msg) {
                        showNotification({
                            type: "error",
                            message: FlexiJS.Resources.GetResourceText(resourceFile, "DeleteDocument.errormessage") + JSON.stringify(msg),
                            autoClose: false
                        });
                    }
                });

            }
        });
    }
}
;/*

  _____   _                 _       _   ____        _____                              ___   _                            _   _   _         _                          
 |  ___| | |   ___  __  __ (_)     | | / ___|      |  ___|   ___    _ __   _ __ ___   |_ _| | |_    ___   _ __ ___       | | | | (_)  ___  | |_    ___    _ __   _   _ 
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |_     / _ \  | '__| | '_ ` _ \   | |  | __|  / _ \ | '_ ` _ \      | |_| | | | / __| | __|  / _ \  | '__| | | | |
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  |  _|   | (_) | | |    | | | | | |  | |  | |_  |  __/ | | | | | |  _  |  _  | | | \__ \ | |_  | (_) | | |    | |_| |
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_|      \___/  |_|    |_| |_| |_| |___|  \__|  \___| |_| |_| |_| (_) |_| |_| |_| |___/  \__|  \___/  |_|     \__, |
                                                                                                                                                                 |___/ 

*/

FlexiJS.FormItem.History = {};

FlexiJS.FormItem.History.RenderPageFormItem = function (itemId) {
    var baseItem = $(itemId);
    
    if (baseItem && baseItem.attr("data-fihistory") != null) {
        var historyTooltip = "";
        //Unencode & parse history JSON
        var history = JSON.parse($("<div/>").html(baseItem.attr("data-fihistory")).text());

        historyTooltip = '<div class="form-itemhistorytooltip"><div class="form-itemhistory-section"><div class="form-itemhistory-table">';
        $.each(history.ItemHistory, function (key, value) {
            historyTooltip = historyTooltip + "<div class=\"form-itemhistory-tablecell form-itemhistory-tableheader\">" + key + "</div>";
            //Loop through each history item
            $.each(value, function (valueskey, valuesvalue) {
                historyTooltip = historyTooltip + "<div class=\"form-itemhistory-tablecell form-itemhistory-label\">" + valueskey + ":</div><div class=\"form-itemhistory-tablecell form-itemhistory-value\">" + valuesvalue + "</div>";
            });
        });
        historyTooltip = historyTooltip + '</div></div><div class="form-itemhistory-sectiondivider"></div><div class="form-itemhistory-section"><div class="form-itemhistory-table" name="divFIAuditHistoryTable"></div></div></div>';


        var historyElement = $("<div/>").html(historyTooltip);

        var divFIAuditHistoryTable = historyElement.find("[name='divFIAuditHistoryTable']");
        $.each(history.ItemAuditHistory, function (key, value) {
            divFIAuditHistoryTable.append($("<div class=\"form-itemhistory-tablecell form-itemhistory-tableheader\">" + key + "</div>"));
            var list = $("<div class=\"form-itemhistory-list\"></div>").appendTo(divFIAuditHistoryTable);

            if (!value || value.length == 0) {
                list.append(history.NoAuditHistoryMessage);
            } else {
                var itemHTML = "";
                $.each(
                    value,
                    function (index, svalue) {
                        itemHTML = itemHTML + "<div class=\"form-itemhistory-tablecell form-itemhistory-label\">" + svalue.Key + "</div><div class=\"form-itemhistory-tablecell form-itemhistory-value\">" + svalue.Value + "</div>";
                    }
                );
                list.append(itemHTML);
            }
        });

        baseItem.parent().find("[id$=spnHistory]").kendoTooltip({
            autoHide: true,
            content: historyElement.html(),
            width: 600,
            position: "top"

        });


    }
};;
FlexiJS.FormItem.Table = {};

FlexiJS.FormItem.Table.StartCutRow = function (Link) { FlexiJS.FormItem.Table.StartCutCopyRow(Link, FlexiJS.Enums.Forms.FormItems.Table.Actions.Move); };
FlexiJS.FormItem.Table.StartCopyRow = function (Link) { FlexiJS.FormItem.Table.StartCutCopyRow(Link, FlexiJS.Enums.Forms.FormItems.Table.Actions.Copy); };
FlexiJS.FormItem.Table.StartCutCopyRow = function (Link, Action) {
    var optionsPanel = Link.closest('[id$=pnlFormItem]').find('[id*=divEditRowPopout]').first();
    var rowIndex = optionsPanel.attr("editrow");

    optionsPanel.attr("cutcopyrowindex", rowIndex);
    optionsPanel.attr("cutcopyrowaction", Action);
    optionsPanel.find('.jsTablePaste').hide();

    var table = $(Link).closest('[id*=pnlFormItem]').find('table');

    table.find('tr').removeClass('form-table-row-cutmarker').removeClass('form-table-row-copymarker').removeClass('form-table-row-precutcopymarker');

    var tr = table.find('td[row="' + rowIndex + '"]:first').closest("tr");

    if (Action == FlexiJS.Enums.Forms.FormItems.Table.Actions.Move) {
        tr.addClass('form-table-row-cutmarker');
    } else {
        tr.addClass('form-table-row-copymarker');
    }
    tr.prev().addClass('form-table-row-precutcopymarker');
};
FlexiJS.FormItem.Table.CancelCutCopyRow = function (Link) {
    var optionsPanel = Link.closest('[id$=pnlFormItem]').find('[id*=divEditRowPopout]').first();
    var rowIndex = optionsPanel.attr("editrow");

    optionsPanel.attr("cutcopyrowindex", null);
    optionsPanel.attr("cutcopyrowaction", null);
    optionsPanel.find('.jsTablePaste').hide();

    var table = $(Link).closest('[id*=pnlFormItem]').find('table');

    table.find('tr').removeClass('form-table-row-cutmarker').removeClass('form-table-row-copymarker');
};

FlexiJS.FormItem.Table.SetupEditRowPanel = function (FormItem, FormPageId, FormId, formFieldId, PleaseWaitText) {
    var rowOptionsPanel = $(FormItem).find('[id*=divEditRowPopout]');
    
    //Save/Undo
    rowOptionsPanel.find('[id*=hypTableUndoChanges]').unbind('click').click(function () {
        FlexiJS.FormItems.UndoTableChanges(FormPageId, FormId, formFieldId, $(this), PleaseWaitText);
        return false;
    });
    rowOptionsPanel.find('[id*=hypTableSaveChanges]').unbind('click').click(function () {
        FlexiJS.FormItems.SaveTableChanges(FormPageId, FormId, formFieldId, $(this), PleaseWaitText);
        return false;
    });

    //Multirow
    rowOptionsPanel.find('[id*=hypTableEnableMultirow]').unbind('click').click(function () {
        FlexiJS.FormItems.CheckWantsToMarkAsTemplate(this);
        return false;
    });
    rowOptionsPanel.find('[id*=hypTableDisableMultirow]').unbind('click').click(function () {
        FlexiJS.FormItems.CheckWantsToMarkAsTemplate(this);
        return false;
    });

    //Start copy/cut
    rowOptionsPanel.find('[id*=hypTableCopyRow]').unbind('click').click(function () {
        FlexiJS.FormItem.Table.StartCopyRow($(this));
        FlexiJS.FormItem.Table.HideOptionsPanel(FormItem);
        return false;
    });
    rowOptionsPanel.find('[id*=hypTableCutRow]').unbind('click').click(function () {
        FlexiJS.FormItem.Table.StartCutRow($(this));
        FlexiJS.FormItem.Table.HideOptionsPanel(FormItem);
        return false;
    });
    rowOptionsPanel.find('[id*=hypTableCutCopyCancelRow]').unbind('click').click(function () {
        FlexiJS.FormItem.Table.CancelCutCopyRow($(this));
        FlexiJS.FormItem.Table.HideOptionsPanel(FormItem);
        return false;
    });

    //Close popup
    rowOptionsPanel.find('[id*=hypTableCancelRow]').unbind('click').click(function () {
        FlexiJS.FormItem.Table.HideOptionsPanel(FormItem);
        return false;
    });

    //WS Calls
    rowOptionsPanel.find('[id*=hypTableClearRow]').unbind('click').click(function () {
        FlexiJS.FormItem.Table.ConfirmRowAction($(this), FlexiJS.Enums.Forms.FormItems.Table.Actions.Clear, FlexiJS.Enums.Forms.FormItems.Table.Target.None, PleaseWaitText);
        return false;
    });
    rowOptionsPanel.find('[id*=hypTableDeleteRow]').unbind('click').click(function () {
        FlexiJS.FormItem.Table.ConfirmRowAction($(this), FlexiJS.Enums.Forms.FormItems.Table.Actions.Delete, FlexiJS.Enums.Forms.FormItems.Table.Target.None, PleaseWaitText);
        return false;
    });

    rowOptionsPanel.find('[id*=hypTableInsertRowBefore]').unbind('click').click(function () {
        FlexiJS.FormItem.Table.ConfirmRowAction($(this), FlexiJS.Enums.Forms.FormItems.Table.Actions.Insert, FlexiJS.Enums.Forms.FormItems.Table.Target.Before, PleaseWaitText);
        return false;
    });
    rowOptionsPanel.find('[id*=hypTableInsertRowAfter]').unbind('click').click(function () {
        FlexiJS.FormItem.Table.ConfirmRowAction($(this), FlexiJS.Enums.Forms.FormItems.Table.Actions.Insert, FlexiJS.Enums.Forms.FormItems.Table.Target.After, PleaseWaitText);
        return false;
    });
    rowOptionsPanel.find('[id*=hypTablePasteRowBefore]').unbind('click').click(function () {
        FlexiJS.FormItem.Table.ConfirmRowAction(
            $(this),
            $(this).closest('[id$=pnlFormItem]').find('[id*=divEditRowPopout]').first().attr("cutcopyrowaction"),
            FlexiJS.Enums.Forms.FormItems.Table.Target.Before,
            PleaseWaitText
        );
        return false;
    });
    rowOptionsPanel.find('[id*=hypTablePasteRowAfter]').unbind('click').click(function () {
        FlexiJS.FormItem.Table.ConfirmRowAction(
            $(this),
            $(this).closest('[id$=pnlFormItem]').find('[id*=divEditRowPopout]').first().attr("cutcopyrowaction"),
            FlexiJS.Enums.Forms.FormItems.Table.Target.After,
            PleaseWaitText
        );
        return false;
    });
    rowOptionsPanel.find('[id*=hypTablePasteRowInto]').unbind('click').click(function () {
        FlexiJS.FormItem.Table.ConfirmRowAction(
            $(this),
            $(this).closest('[id$=pnlFormItem]').find('[id*=divEditRowPopout]').first().attr("cutcopyrowaction"),
            FlexiJS.Enums.Forms.FormItems.Table.Target.Overwrite,
            PleaseWaitText
        );
        return false;
    });

    if ($(FormItem).attr("haschanged") != null && $(FormItem).attr("haschanged").toLowerCase() == "true") {
        //This is a table, show additional saving options
        FlexiJS.FormItems.ShowTableChangedOptions($(FormItem));
    }
};

FlexiJS.FormItem.Table.PerformRowAction = function (Link, Action, Target, previousIHTML, tableformitemid, currentrowindex, newrowindex) {
    try {
        Fluent.Website.GMS.GMSService.ActionTableRow(
            tableformitemid,
            Action,
            Target,
            currentrowindex,
            newrowindex,
            function (Result) {
                if (Result.Result) {
                    try {
                        UpdateFormItems();
                    }
                    catch (ex) {
                        window.location = window.location;
                    }
                }
            },
            function (Result) { Link.html(previousIHTML); },
            function (Result) { Link.html(previousIHTML); }
        );
    }
    catch (ex) {
    }

};

FlexiJS.FormItem.Table.ConfirmRowAction = function (Link, Action, Target, PleaseWaitText) {
    var previousIHTML = Link.html();
    Link.text(PleaseWaitText);

    var optionsPanel = $(Link).closest('[id$=pnlFormItem]').find('[id*=divEditRowPopout]').first();

    var actionIndex = optionsPanel.attr("editrow");
    var tableformitemid = optionsPanel.closest('[id*=pnlFormItem]').attr("fiid");
    var newrowindex = actionIndex;
    var currentrowindex = 0;
    var needsConfirmMessage = false;

    switch (parseInt(Action)) {
        case FlexiJS.Enums.Forms.FormItems.Table.Actions.Delete:
        case FlexiJS.Enums.Forms.FormItems.Table.Actions.Clear:
            needsConfirmMessage = true;
            currentrowindex = actionIndex;
            newrowindex = 0;
            break;
        case FlexiJS.Enums.Forms.FormItems.Table.Actions.Move:
        case FlexiJS.Enums.Forms.FormItems.Table.Actions.Copy:
            currentrowindex = optionsPanel.attr("cutcopyrowindex");
            needsConfirmMessage = (Target == 206203);
            break;
    }
    if (needsConfirmMessage) {
        FlexiJS.Kendo.Dialogs.Confirm(
            $(Link).attr("confirmtitle"),
            $(Link).attr("confirm"),
            [
                {
                    text: FlexiJS.Resources.GetResourceText('controls/Flexiform/FormItems/FlexiTable.ascx', 'ConfirmPopup.Yes'),
                    actionfunction: function () { FlexiJS.FormItem.Table.PerformRowAction(Link, Action, Target, previousIHTML, tableformitemid, currentrowindex, newrowindex); }
                },
                {
                    text: FlexiJS.Resources.GetResourceText('controls/Flexiform/FormItems/FlexiTable.ascx', 'ConfirmPopup.No'),
                    actionfunction: function () { Link.html(previousIHTML); }
                }
            ]
        );
    } else {
        FlexiJS.FormItem.Table.PerformRowAction(Link, Action, Target, previousIHTML, tableformitemid, currentrowindex, newrowindex);
    }
};
FlexiJS.FormItem.Table.HideOptionsPanel = function (FormItemPanel) {
    $(FormItemPanel).find('[id*=divEditRowPopout]').hide();
    $(FormItemPanel).find('.form-table-row-editrowmarker').removeClass('form-table-row-editrowmarker');
}


FlexiJS.FormItems.EnableTableDragDrop = function (PanelFieldTypeId) {
    //Enables the drag drop functionality for the table
    //Note GetDragDroppablePanels disables drag drop and only returns 
    //Panels which are appropriate (i.e. this in a table which arn't templates)
    var dragPanels = FlexiJS.FormItems.GetDragDroppablePanels(PanelFieldTypeId);

    dragPanels.mousedown(function () {
        $(this).css('cursor', 'hand');
    });
    dragPanels.mouseup(function () {
        $(this).css('cursor', 'pointer');
    });
    dragPanels.mouseover(function () {
        $(this).css('cursor', 'hand');
    });
    dragPanels.mouseout(function () {
        $(this).css('cursor', 'pointer');
    });

    dragPanels.draggable(
    {
        revert: "invalid",
        start: function (event, ui) { FlexiJS.FormItems.FormItemTableDragDropStart(event, ui, PanelFieldTypeId) },
        stop: function (event, ui) { FlexiJS.FormItems.FormItemTableDragDropStop(event, ui, PanelFieldTypeId) }
    }
    );
};

FlexiJS.FormItems.FormItemTableDragDropStop = function (event, ui, PanelFieldTypeId) {
    //DragDrop has stopped for a table, disable the droppable fields
    var dragPanels = FlexiJS.FormItems.GetDragDroppablePanels(PanelFieldTypeId);
};

FlexiJS.FormItems.FormItemTableDragDropStart = function (event, ui, PanelFieldTypeId) {
    //DragDrop has started for a table, configure the droppable fields
    var dragPanels = FlexiJS.FormItems.GetDragDroppablePanels(PanelFieldTypeId);
    dragPanels.droppable({
        activeClass: "DropHoverActive", hoverClass: "DropHoverHover", drop: function (event, ui) {
            FlexiJS.FormItems.FormItemTableDragDropDropped(event, ui, $(this));
        }
    });
};

FlexiJS.FormItems.FormItemTableDragDropDropped = function (event, ui, panel) {
    //Called when an item within the table has been dropped
    //Finds the two items, switches them and then calls the webservice to save the changes.
    var moveToTd = panel.parent('td');
    var moveFromTd = $(ui.draggable).parent('td');

    if (moveToTd.length == 0 || moveFromTd.length == 0) {
        return;
    }

    var moveToContent = moveToTd.children();
    var moveContent = moveFromTd.children();

    moveToContent.detach().css({ top: 0, left: 0 });
    moveContent.detach().css({ top: 0, left: 0 });

    moveToTd.append(moveContent);
    moveFromTd.append(moveToContent);

    var moveToCol = moveToTd.attr("col");
    var moveToRow = moveToTd.attr("row");

    var moveFromCol = moveFromTd.attr("col");
    var moveFromRow = moveFromTd.attr("row");

    moveToTd.find('div[cid]').attr('cid', moveToRow + '_' + moveToCol);
    moveToTd.find('div[trow]').attr('trow', moveToRow);
    moveToTd.find('div[tcol]').attr('tcol', moveToCol);

    moveFromTd.find('div[cid]').attr('cid', moveFromRow + '_' + moveFromCol);
    moveFromTd.find('div[trow]').attr('trow', moveFromRow);
    moveFromTd.find('div[tcol]').attr('tcol', moveFromCol);

    var tableFormItemId = panel.parents('table').first().attr("tableformitemid");
    MoveTableItems(tableFormItemId, moveFromRow, moveFromCol, moveToRow, moveToCol);
};

FlexiJS.FormItems.EnableTableResize = function (TableId, IsTableLinked) {

    //Configures table resizing and row editing options
    var table = $('[id*=' + TableId + ']');

    //Resizing
    table.find("tr").find("td:not(:last)").resizable({
        handles: "e",
        helper: "form-table-resizingaction",
        stop: function (event, ui) {
            var table = ui.element.closest("table");
            var cols = table.find("col");
            var col = cols.filter(":eq(" + ui.element.index() + ")");
            //Used for Firefox, percentage width will always come back in pixels
            var match = col.css("width").match(/px$/);

            if (typeof (match) === "undefined" || match === null) {
                var newColWidth = Math.floor(ui.size.width * 100 / table.width());
                var oriColWidth = parseInt(col.css("width"), 10);
            } else {
                var newColWidth = Math.floor((ui.size.width / table.width()) * 100);
                var oriColWidth = Math.floor((col.width() / col.parent().width()) * 100);
            }

            var diff = newColWidth - oriColWidth;

            var tableSettings = FlexiJS.FormItems.DeserializeTableSettings(col.parents('[id*=pnlFormItem]').first().find('[id*=hdnTableSettings]').text());

            if (newColWidth == oriColWidth) {
                return;
            }
            else {
                var nextCol = cols.filter(":eq(" + (ui.element.index() + 1) + ")");

                if (typeof (match) === "undefined" || match === null) {
                    var nextColWidth = parseInt(nextCol.css("width"), 10);
                } else {
                    var nextColWidth = Math.floor((nextCol.width() / nextCol.parent().width()) * 100);
                }

                var totalWidth = oriColWidth + nextColWidth;
                nextColWidth = nextColWidth - diff;

                if (nextColWidth <= 0) {
                    nextColWidth = 1;
                }
                if ((nextColWidth + newColWidth) > totalWidth) {
                    newColWidth = totalWidth - nextColWidth;
                }

                col.css("width", newColWidth + "%");
                tableSettings.ColumnSettings[ui.element.index() + 1].Width = newColWidth;
                if (tableSettings.ColumnSettings[ui.element.index() + 2]) {
                    nextCol.css("width", nextColWidth + "%");
                    tableSettings.ColumnSettings[ui.element.index() + 2].Width = nextColWidth;
                }
                var formItemPanel = col.parents('[id*=pnlFormItem]').first();
                FlexiJS.FormItems.StoreTableSettings(formItemPanel.find('[id*=hdnTableSettings]'), tableSettings);
                FlexiJS.FormItems.ShowTableChangedOptions(formItemPanel);
            }

            ui.element.removeAttr("style");
        }
    });

    //Row options
    if (IsTableLinked && IsTableLinked.toLowerCase() == "false") {
        var formItem = table.closest('[id$=pnlFormItem]');
        var pnlRowOptions = formItem.find('[id*=divEditRowPopout]').first();
        var tableSettings = FlexiJS.FormItems.DeserializeTableSettings(formItem.find('[id*=hdnTableSettings]').text());
        $.each(
            table.find('.form-table-editcell > span'),
            function(index, editSpan) {
                var lasttd = $(this).closest("td");
                var thistr = $(lasttd).closest("tr");
                var rowIndex = lasttd.attr("row");
                var thisRowsSettings = tableSettings.RowSettings[rowIndex];

                $(thistr).removeClass('form-table-row-multirowmarker');
                if (thisRowsSettings && thisRowsSettings.IsTemplate == true) $(thistr).addClass('form-table-row-multirowmarker');

                $(editSpan).click(
                    function () {
                        table.find('.form-table-row-editrowmarker').removeClass('form-table-row-editrowmarker');
                        var offset = lasttd.offset();

                        // Check that context menu has not been suppressed
                        var suppressContextMenu = $(thistr).attr("suppressContextMenu");
                        if (suppressContextMenu == null || !suppressContextMenu) {
                            $(thistr).addClass('form-table-row-editrowmarker');
                            pnlRowOptions.show();
                            pnlRowOptions.offset({ top: offset.top + (lasttd.outerHeight() / 2) - (pnlRowOptions.height() / 2), left: offset.left - pnlRowOptions.width() - 3 });
                            FlexiJS.FormItems.ConfigureRowOptions(pnlRowOptions, rowIndex, null, $(thistr).attr("suppressMultipleRows"));
                        } else {
                            $(this).hide();
                        }
                    }
                );
            }
        );
    }
}

FlexiJS.FormItems.ConfigureRowOptions = function (RowPanel, RowIndex, OptionalTableSettings, suppressMultipleRows) {
    
    RowPanel.attr("editrow", RowIndex);

    //Handle Display Of Multirow options
    suppressMultipleRows = suppressMultipleRows == null ? false : suppressMultipleRows;

    var templateContainerRow = RowPanel.find('.jsTableTemplateRow');
    var templateContainerTemplateNotSetRow = RowPanel.find('.jsTableTemplateNotSetRow');
    var templateContainerTemplateSetRow = RowPanel.find('.jsTableTemplateSetRow');
    var formItem = RowPanel.closest('[id*=pnlFormItem]').first();
    
    var tableSettings = OptionalTableSettings;
    if (!tableSettings) tableSettings = FlexiJS.FormItems.DeserializeTableSettings(formItem.find('[id*=hdnTableSettings]').text());
    
    var thisRowsSettings = tableSettings.RowSettings[RowIndex];
    var thisRow = formItem.find('td[row="' + RowIndex + '"]:first').closest("tr");

    if (thisRowsSettings) {
        //var lnkMarkAsTemplate = RowPanel.find('[id*=hypTableMarkAsTemplate]');
        //lnkMarkAsTemplate.text(thisRowsSettings.IsTemplate ? lnkMarkAsTemplate.attr("DisableMultiple") : lnkMarkAsTemplate.attr("EnableMultiple"));

        templateContainerRow.show().css('display', 'flex');
        templateContainerTemplateSetRow.hide();
        templateContainerTemplateNotSetRow.hide();
        if (thisRowsSettings.IsTemplate == true) {
            var setOptionsList = function (id, defaultOption, selectedOption, minRows, maxRows) {
                var list = RowPanel.find('[id*=' + id + ']');
                list.unbind('change');
                list.children().remove();
                list.append($("<option/>", defaultOption));
                for (var i = minRows; i < maxRows; i++) { list.append($("<option/>", {value: i, text: i})); };
                list.val(selectedOption);
                list.bind('change', function () {
                    FlexiJS.FormItems.ShowTableChangedOptionsFromChild(this);
                    FlexiJS.FormItems.UpdateRowSettings(RowPanel, RowIndex);
                });
            };

            setOptionsList('minRecords',{value: "1", text: "1 (Min)"},thisRowsSettings.MinimumTemplateRows,2,50);
            setOptionsList('maxRecords',{value: "0", text: "No Max"},thisRowsSettings.MaximumTemplateRows,2,50);

            templateContainerTemplateSetRow.show().css('display', 'flex');
        } else {
            templateContainerTemplateSetRow.hide();
            templateContainerTemplateNotSetRow.show().css('display', 'flex');
            if (suppressMultipleRows) templateContainerRow.hide();
        }
    } else {
        templateContainerRow.hide();
        templateContainerTemplateSetRow.hide();
        templateContainerTemplateNotSetRow.hide();
    }
    
    //Handle display of paste options
    var pasteOptionsContainer = RowPanel.find('.jsTablePaste');
    var cutcopyrowindex = RowPanel.attr("cutcopyrowindex");
    if (cutcopyrowindex != null && cutcopyrowindex != RowIndex) {
        pasteOptionsContainer.show().css('display', 'flex');
    } else {
        pasteOptionsContainer.hide();
    }
}

FlexiJS.FormItems.CheckWantsToMarkAsTemplate = function (Link) {
    //Confirm the user wants to mark or unmark the row as a template.

    var optionsPanel = $(Link).closest('[id$=pnlFormItem]').find('[id*=divEditRowPopout]').first();
    var settingsControl = optionsPanel.parents('[id*=pnlFormItem]').first().find('[id*=hdnTableSettings]');
    var tableSettings = FlexiJS.FormItems.DeserializeTableSettings(settingsControl.text());
    var rowIndex = optionsPanel.attr("editrow");

    FlexiJS.FormItems.MarkAsTemplate(optionsPanel, settingsControl, tableSettings, rowIndex);
    
    showNotificationMessage('success', $(Link).attr("Message"));
}

FlexiJS.FormItems.MarkAsTemplate = function (PnlRowOptions, SettingsControl, TableSettings, RowIndex) {
    //Marks or unmarks the row as a template, saves the new options and configures the table
    var pnlFormItem = SettingsControl.parents('[id*=pnlFormItem]').first();
    if (TableSettings.RowSettings[RowIndex]) {
        var setIsTemplate = !TableSettings.RowSettings[RowIndex].IsTemplate;
        TableSettings.RowSettings[RowIndex].IsTemplate = setIsTemplate;

        var thisTR = pnlFormItem.find('[class~="form-table-editcell"][row="' + RowIndex + '"]').closest('tr');
        $(thisTR).removeClass('form-table-row-multirowmarker');
        if (setIsTemplate) $(thisTR).addClass('form-table-row-multirowmarker');
    }

    FlexiJS.FormItems.ConfigureRowOptions(PnlRowOptions, RowIndex, TableSettings);
    FlexiJS.FormItems.StoreTableSettings(SettingsControl, TableSettings);
    FlexiJS.FormItems.EnableTableDragDrop(SettingsControl.parent().find('[id*=fieldInputControl]')[0].id);
    FlexiJS.FormItems.ShowTableChangedOptions(pnlFormItem);
}

FlexiJS.FormItems.ShowTableChangedOptionsFromChild = function (Child) {
    var pnl = $(Child).parents('[id*=pnlFormItem]').first();
    FlexiJS.FormItems.ShowTableChangedOptions(pnl);
}

FlexiJS.FormItems.UpdateRowSettings = function (RowPanel, RowIndex) {
    var rowTableSettings = FlexiJS.FormItems.DeserializeTableSettings(RowPanel.parents('[id*=pnlFormItem]').first().find('[id*=hdnTableSettings]').text());

    var minNumRows = RowPanel.find('[id*=minRecords]').val();
    var maxNumRows = RowPanel.find('[id*=maxRecords]').val();

    if (parseInt(maxNumRows) < parseInt(minNumRows)) {
        if (parseInt(maxNumRows) != 0) {
            maxNumRows = minNumRows;
        }
    }
    rowTableSettings.RowSettings[RowIndex].MinimumTemplateRows = minNumRows;
    rowTableSettings.RowSettings[RowIndex].MaximumTemplateRows = maxNumRows;
    FlexiJS.FormItems.StoreTableSettings(RowPanel.parents('[id*=pnlFormItem]').first().find('[id*=hdnTableSettings]'), rowTableSettings);
}

FlexiJS.FormItems.ShowTableChangedOptions = function (FormItemPanel) {
    //Show the undo/save options
    FormItemPanel.attr("haschanged", "true");
    FormItemPanel.find('.jsTableSaveRow').show().css('display', 'flex');
}

FlexiJS.FormItems.HideTableChangedOptions = function (FormItemPanel) {
    //Hide the save/undo options
    FormItemPanel.attr("haschanged", "false");
    FormItemPanel.find('.jsTableSaveRow').hide();
    FlexiJS.FormItem.Table.HideOptionsPanel(FormItemPanel);
}

FlexiJS.FormItems.UndoTableChanges = function (FormPageId, FormId, FormFieldId, Link, PleaseWaitText) {
    //Call the undo table settings function
    try {
        var previousText = Link.html();
        Link.text(PleaseWaitText);
        Fluent.Website.GMS.GMSService.GetTableSettings(
            FormPageId,
            FormId,
            FormFieldId,
            function (Result) { FlexiJS.FormItems.UndoTableChangesCompleted(FormFieldId, Result, Link, previousText); },
            function (Result) { Link.html(previousText); FlexiJS.FormItems.UndoTableChangesFormItemError(Result); },
            function (Result) { Link.html(previousText); FlexiJS.FormItems.UndoTableChangesFormItemError(Result); }
        );
    }
    catch (ex) {
    }
}

FlexiJS.FormItems.UndoTableChangesCompleted = function (FormItemId, Result, Link, PreviousText) {
    //Undoing the table settings completed, save the new changes and update table
    Link.html(PreviousText);

    var reviver;
    if (Result.Result) {
        FlexiJS.FormItems.SetTableSettings(FormItemId, Result.ResultDetail);
        FlexiJS.FormItems.HideTableChangedOptions($('[id*=pnlFormItem][fiid=' + FormItemId + ']'));
    }
    else {
        //Wasn't successful, alert the user
        showNotificationMessage('error', Result.ResultMessage);
    }
}

FlexiJS.FormItems.SetTableSettings = function (FormItemId, TableSettingsJson) {
    //Sets the table settings object, stores it and then uses the settings to configure the table
    var reviver;
    try {
        var tableSettings = JSON.parse(TableSettingsJson, reviver);
        var tableControl = $('[id*=pnlFormItem][fiid=' + FormItemId + ']');
        var table = tableControl.find('table');
        var tableSettingsField = tableControl.find('[id*=hdnTableSettings]');
        FlexiJS.FormItems.StoreTableSettings(tableSettingsField, tableSettings);
        
        //Re-add multirow markers
        tableControl.find('tr.form-table-row-multirowmarker').removeClass('form-table-row-multirowmarker');
        for (var rowsetting in tableSettings.RowSettings) {
            if (tableSettings.RowSettings[rowsetting].IsTemplate) {
                tableControl.find('td.form-table-editcell[row="' + rowsetting + '"]').closest('tr').addClass('form-table-row-multirowmarker')
            }
        }

        //Set the width of each column
        var cols = table.find('col');
        for (var i in cols) {
            var colInd = parseInt(i) + 1;
            $(cols[i]).css("width", tableSettings.ColumnSettings[colInd].Width.toString() + "%");
        }
    }
    catch (ex) {
    }
}

FlexiJS.FormItems.UndoTableChangesFormItemError = function (Result) {
    //Call to undo the table changes failed completely (internet disconnected perhaps)
}

FlexiJS.FormItems.SaveTableChanges = function (FormPageId, FormId, FormFieldId, Link, PleaseWaitText) {
    try {
        //Saves the table settings by calling the web method
        var previousText = Link.html();
        Link.text(PleaseWaitText);
        var tableSettings = $('[id*=pnlFormItem][fiid=' + FormFieldId + ']').find('[id*=hdnTableSettings]');
        Fluent.Website.GMS.GMSService.SaveTableSettings(
            FormPageId,
            FormId,
            FormFieldId,
            tableSettings.text(),
            function (Result) { FlexiJS.FormItems.SaveTableChangesFormItemComplete(FormFieldId, Result, Link, previousText); },
            function (Result) { FlexiJS.FormItems.SaveTableChangesFormItemError(Result, Link, previousText); },
            function (Result) { FlexiJS.FormItems.SaveTableChangesFormItemError(Result, Link, previousText); }
        );
    }
    catch (ex) {
    }
}

FlexiJS.FormItems.SaveTableChangesFormItemComplete = function (FormItemId, Result, Link, PreviousText) {
    //Saving the table has been completed, show the user the message and disable save/undo.
    Link.html(PreviousText);
    FlexiJS.FormItems.HideTableChangedOptions($('[id*=pnlFormItem][fiid=' + FormItemId + ']'));
    showNotificationMessage('success', Result.ResultMessage);
}

FlexiJS.FormItems.SaveTableChangesFormItemError = function (Result, Link, PreviousText) {
    Link.html(PreviousText);
    //Call to increase/decrease rows requires failed completly (internet disconnected perhaps)
}

FlexiJS.FormItems.SerializeTableSettings = function (SettingsObject) {
    //Converts the table settings object into a string
    try {
        var reviver;
        return JSON.stringify(SettingsObject, reviver);
    }
    catch (e) {
        return new String();
    }
}

FlexiJS.FormItems.DeserializeTableSettings = function (SettingsJson) {
    //Creates the table settings object by parsing the json string
    try {
        var reviver;
        return JSON.parse(SettingsJson, reviver);
    }
    catch (e) {
        return new Object();
    }
}

FlexiJS.FormItems.StoreTableSettings = function (Control, TableSettings) {
    //Stores the table settings in the supplied control
    Control.text(FlexiJS.FormItems.SerializeTableSettings(TableSettings));
}

FlexiJS.FormItems.AddTableRow = function (sender) {
    FlexiJS.FormItems.AddRemoveTableRow($(sender), true);
};

FlexiJS.FormItems.RemoveTableRow = function (sender) {
    FlexiJS.FormItems.AddRemoveTableRow($(sender), false);
};

FlexiJS.FormItems.AddRemoveTableRow = function (sender, isAddition) {
    var data = $(sender).data();
    if ($(sender).hasClass('aspNetDisabled')) return;

    $(sender).closest('td').find('a').addClass('aspNetDisabled').prop('title', data.loadingtooltip);
    FlexiJS.FormItems.HideTableChangedOptions($('[id*=pnlFormItem][fiid=' + data.formitem + ']'));

    //Call WS in a timeout to allow ui to update
    Fluent.Website.GMS.GMSService.IncreaseDecreaseRequiredRows(
        data.page,
        data.formitem,
        data.entityrow,
        data.entitytype,
        isAddition,
        data.row,
        data.childrow,
        data.minimumtemplaterows,
        function (Result) {
            //Row has been added/removed. Click the save button to update the page
            $('[id*=_btnSave]')[0].click();
        },
        function (Result) {
            //Call to increase/decrease rows requires failed completly (internet disconnected perhaps)
            $(sender).closest(td).empty();
        },
        function (Result) {
            //Call to increase/decrease rows requires failed completly (internet disconnected perhaps)
            $(sender).closest(td).empty();
        }
    );
};;FlexiJS.GMS.Application.Payments = {};

FlexiJS.GMS.Application.Payments.ResourceSet = 'FlexiJS.Payments';

FlexiJS.GMS.Application.Payments.PaymentModalURL = "/gms/GMSPaymentModal.aspx?appid={GMSApplicationID}&id={GMSApplicationPaymentID}&ct=pay";

FlexiJS.GMS.Application.Payments.PaymentControlLocation = {
    ECFFinance: 1,
    OfferStage: 2
};

FlexiJS.GMS.Application.Payments.Constants = {
    _gmsApplicationId: '',
    _gmsApplicationPaymentId: '',
    _usersHiddenColumns: '',
    _usersSortedColumn: '',
    _gridId: '',
    _totalPaymentsValue: '',
    _totalForeignCurrencyActionablePayments: '',
    _standardDisplayedColumns: ['Payee', 'InvoiceNumber', 'DateInvoice', 'Value', 'Description', 'ConditionsOutstanding', 'PaymentStatusText', 'GMSPaymentBatchID'],
    _saveGridHiddenColumnsUserSetting: 'PaymentsGridHiddenColumns',
    _saveGridSortedColumnUserSetting: 'PaymentsGridSortOrder',
    _controlLocationID: '',
    _previousGrouping: {}
}

FlexiJS.GMS.Application.Payments.GetGMSServiceMethodUrl = function (methodName) {
    return '/gms/WebServices/GMSService.svc/' + methodName;
};

FlexiJS.GMS.Application.Payments.SetUsersHiddenColumns = function (hiddenColumns) {
    FlexiJS.GMS.Application.Payments.Constants._usersHiddenColumns = hiddenColumns;
};

FlexiJS.GMS.Application.Payments.SetUsersSortedColumn = function (sortedColumn) {
    FlexiJS.GMS.Application.Payments.Constants._usersSortedColumn = sortedColumn;
};

FlexiJS.GMS.Application.Payments.HasUserHiddenColumn = function (field) {
    if (FlexiJS.GMS.Application.Payments.Constants._usersHiddenColumns.length > 0) {
        var isColumnHidden = FlexiJS.GMS.Application.Payments.Constants._usersHiddenColumns.filter(function (col) { return col == field });
        if (isColumnHidden.length == 1) {
            return true;
        }
        else {
            return false;
        }
    }
    else {
        //If it is not one of the standard columns, they should be hidden as the user has not saved their column selection yet
        var isStandardColumn = FlexiJS.GMS.Application.Payments.Constants._standardDisplayedColumns.filter(function (col) { return col == field });
        if (isStandardColumn.length == 1) {
            return false;
        }
        else {
            return true;
        }
    }
};

FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate = function (resourceKey, formattedValue) {
    if (formattedValue == undefined) {
        formattedValue = 'value';
    }
    return FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, resourceKey) + ": #: value ? " + formattedValue + ": FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.GroupingHeaderFieldNotSet') #";
};

FlexiJS.GMS.Application.Payments.SetupAndBindApplicationsPaymentGrid = function (gridId, applicationId, controlLocationId) {

    FlexiJS.GMS.Application.Payments.Constants._gridId = gridId;
    FlexiJS.GMS.Application.Payments.Constants._controlLocationId = controlLocationId;

    FlexiJS.Kendo.Grids.DestroyGrid(gridId);

    //Check to see if user has saved a different sort order than the default
    FlexiJS.Common.GetUserSetting(FlexiJS.GMS.Application.Payments.Constants._saveGridSortedColumnUserSetting, FlexiJS.GMS.Application.Payments.SetUsersSortedColumn);

    //sort
    var sort = { field: "DateInvoice", dir: "asc" };
    var usersSortedColumn = FlexiJS.GMS.Application.Payments.Constants._usersSortedColumn;
    if (JSON.stringify(usersSortedColumn) != '{}') {
        sort = { field: usersSortedColumn.field, dir: usersSortedColumn.dir };
    }

    var gridDatasource = new kendo.data.DataSource({
        serverFiltering: true,
        serverSorting: true,
        sort: sort,
        pageSize: 10,
        serverPaging: true,
        requestEnd: function (e) {
            var grid = $('#' + gridId).getKendoGrid();
            setTimeout(function () {
                kendo.ui.progress(grid.element, false);
            });
            //If sort has been reset, then default to original sort
            if (grid.dataSource._sort == undefined || grid.dataSource._sort.length == 0) {
                grid.dataSource._sort = [{ field: "DateInvoice", dir: "asc" }];
            }
        },
        transport: {
            read: {
                url: "/GMS/WebServices/GMSService.svc/SearchApplicationPayments",
                type: "POST",
                contentType: 'application/json'
            },
            parameterMap: function (options) {

                var applicationPaymentFilter = new Object();
                applicationPaymentFilter.GMSApplicationID = applicationId;
                applicationPaymentFilter.Filter = options.filter;
                applicationPaymentFilter.Sort = options.sort;
                applicationPaymentFilter.PageSize = options.pageSize;
                applicationPaymentFilter.Page = options.page;

                //Check if user has changed the default sort order, if so save it to user settings
                if ((options.sort && options.sort.length > 0) && ((options.sort[0].field.toLowerCase() == 'dateinvoice' && options.sort[0].dir.toLowerCase() != 'asc') || options.sort[0].field.toLowerCase() != 'dateinvoice')) {
                    FlexiJS.Common.SaveUserSetting(FlexiJS.GMS.Application.Payments.Constants._saveGridSortedColumnUserSetting, kendo.stringify(options.sort[0]));
                }
                else {
                    //No sort has been applied by user, clear it out, so the default sort will be applied
                    FlexiJS.Common.SaveUserSetting(FlexiJS.GMS.Application.Payments.Constants._saveGridSortedColumnUserSetting, '');
                    applicationPaymentFilter.Sort = [{ field: "DateInvoice", dir: "asc" }];
                }

                return JSON.stringify({ applicationPaymentFilter: applicationPaymentFilter });
            }
        },
        schema: {
            data: function (r) {
                FlexiJS.Dates.ParseObjectArrayDotNetDates(['DateInvoice', 'DatePaid', 'DateCreated'], r.d.Payments);
                FlexiJS.GMS.Application.Payments.Constants._totalPaymentsValue = r.d.TotalPaymentsValue;
                FlexiJS.GMS.Application.Payments.Constants._totalForeignCurrencyActionablePayments = r.d.TotalForeignCurrencyActionablePayments;
                r.d.Payments.map(function (py) {
                    var MessageEnum = py.ForeignCurrencyPayout === true ? FlexiJS.Enums.Message.YesNo.Yes : FlexiJS.Enums.Message.YesNo.No;
                    py.ForeignCurrencyPayout = FlexiJS.Enums.GetEnumsText('Message', 'YesNo', MessageEnum);
                });

                return r.d.Payments;
            },
            total: function (r) {
                return r.d.TotalPayments;
            },
            model: {
                fields: {
                    RowId: { type: "number" },
                    GMSApplicationID: { type: "number" },
                    GMSApplicationPaymentID: { type: "number" },
                    InvoiceNumber: { type: "string" },
                    DateInvoice: { type: "datetime" },
                    Description: { type: "string" },
                    Value: { type: "number" },
                    PaymentStatusID: { type: "number" },
                    GMSPaymentBatchID: { type: "number" },
                    DatePaid: { type: "datetime" },
                    DateCreated: { type: "datetime" },
                    CreatedByUser: { type: "string" },
                    BatchType: { type: "string" },
                    GLCode: { type: "string" },
                    SchemeGLCode: { type: "string" },
                    FundingSourceCode: { type: "string" },
                    BudgetTypeCode: { type: "string" },
                    BudgetType: { type: "string" },
                    Payee: { type: "string" },
                    PayeeFinanceReference: { type: "string" },
                    PayeeInferred: { type: "boolean" },
                    PaymentStatusText: { type: "string" },
                    ApplicationStatusID: { type: "number" },
                    ApplicationStatus: { type: "string" },
                    ConditionsOutstanding: { type: "string" },
                }
            }
        }
    });

    FlexiJS.Common.GetUserSetting(FlexiJS.GMS.Application.Payments.Constants._saveGridHiddenColumnsUserSetting, FlexiJS.GMS.Application.Payments.SetUsersHiddenColumns);

    var columns = [
        {
            field: "Payee",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.PayeeTitle'),
            width: 150,
            filterable: false,
            template: function (dataItem) {
                return FlexiJS.GMS.Application.Payments.SetPayeeInferredIcons('Payee', dataItem);
            },
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('Payee'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.PayeeTitle')
        },
        {
            field: "InvoiceNumber",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.InvoiceNumberTitle'),
            width: 200,
            filterable: {
                cell:
                {
                    showOperators: true,
                    operator: "contains",
                    template: function (args) {
                        FlexiJS.Kendo.Grids.CellTemplateFilterOnEnterKey(args);
                    }
                }
            },
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('InvoiceNumber'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.InvoiceNumberTitle')
        },
        {
            field: 'DateInvoice',
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.DateInvoiceTitle'),
            type: "date",
            format: "{0:d}",
            width: 110,
            filterable: false,
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('DateInvoice'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.DateInvoiceTitle', "kendo.toString(kendo.parseDate(value), 'd')")
        },
        {
            field: 'BudgetType',
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.BudgetTypeTitle'),
            width: 200,
            filterable: {
                cell: {
                    showOperators: true,
                    operator: "contains",
                    template: function (args) {
                        FlexiJS.Kendo.Grids.CellTemplateFilterOnEnterKey(args);
                    }
                }
            },
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('BudgetType'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.BudgetTypeTitle')
        },
        {
            field: 'Value',
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.ValueTitle'),
            format: "{0:c}",
            width: 200,
            filterable: {
                cell: {
                    showOperators: true,
                    operator: "eq",
                    template: function (args) {
                        FlexiJS.Kendo.Grids.CellTemplateFilterOnEnterKey(args);
                    }
                }
            },
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('Value'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.ValueTitle', "kendo.toString(value, 'c')")
        },
        {
            field: "Description",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.DescriptionTitle'),
            width: 200,
            filterable: {
                cell: {
                    showOperators: true,
                    operator: "contains",
                    template: function (args) {
                        FlexiJS.Kendo.Grids.CellTemplateFilterOnEnterKey(args);
                    }
                }
            },
            groupable: false,
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('Description'),
            template: '<p class="multiLine">#: Description ? Description : "" #</p>'
        },
        {
            field: "ConditionsOutstanding",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.ConditionsOutstandingTitle'),
            width: 100,
            filterable: false,
            sortable: false,
            headerAttributes: { "class": "k-unsortable" },
            groupable: false,
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('ConditionsOutstanding')
        },
        {
            field: "PaymentStatusText",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.PaymentStatusTitle'),
            width: 180,
            filterable: {
                cell: {
                    template: function (args) {
                        FlexiJS.GMS.Application.Payments.GridPaymentStatusFilter(args);
                    },
                    showOperators: false
                }
            },
            template: function (dataItem) {
                switch (dataItem.PaymentStatusID) {
                    case FlexiJS.Enums.GMS.PaymentStatus.NewPayment:
                        return "<span class='grid-status scheduled'>" + kendo.htmlEncode(dataItem.PaymentStatusText) + "</span>";
                        break;
                    case FlexiJS.Enums.GMS.PaymentStatus.Pending:
                        return "<span class='grid-status authorised'>" + kendo.htmlEncode(dataItem.PaymentStatusText) + "</span>";
                        break;
                    case FlexiJS.Enums.GMS.PaymentStatus.Paid:
                        return "<span class='grid-status paid'>" + kendo.htmlEncode(dataItem.PaymentStatusText) + "</span>";
                        break;
                    default:
                        return kendo.htmlEncode(dataItem.PaymentStatusText);

                }
            },
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('PaymentStatusText'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.PaymentStatusTitle')
        },
        {
            field: "GMSPaymentBatchID",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.GMSPaymentBatchID'),
            width: 150,
            filterable: {
                cell: {
                    showOperators: true,
                    operator: "eq",
                    template: function (args) {
                        FlexiJS.Kendo.Grids.CellTemplateFilterOnEnterKey(args);
                    }
                }
            },
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('GMSPaymentBatchID'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.GMSPaymentBatchID')
        },
        {
            field: "GMSApplicationClaimID",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.GMSApplicationClaimIDTitle'),
            width: 100,
            filterable: false,
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('GMSApplicationClaimID'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.GMSApplicationClaimIDTitle')
        },
        {
            field: "ClaimReference",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.ClaimReferenceTitle'),
            width: 100,
            filterable: false,
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('ClaimReference'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.ClaimReferenceTitle')
        },
        {
            field: "PayeeFinanceReference",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.PayeeFinanceReferenceTitle'),
            width: 100,
            filterable: false,
            template: function (dataItem) {
                return FlexiJS.GMS.Application.Payments.SetPayeeInferredIcons('PayeeFinanceReference', dataItem);
            },
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('PayeeFinanceReference'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.PayeeFinanceReferenceTitle')
        },
        {
            field: "ForeignCurrencyPayout",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.ForeignCurrencyPayoutTitle'),
            width: 100,
            filterable: false,
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('ForeignCurrencyPayout'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.ForeignCurrencyPayoutTitle')
        },
        {
            field: 'DatePaid',
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.DatePaidTitle'),
            type: "date",
            format: "{0:d}",
            width: 110,
            filterable: false,
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('DatePaid'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.DatePaidTitle', "kendo.toString(kendo.parseDate(value), 'd')")
        },
        {
            field: "CreatedByUser",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.CreatedByUserTitle'),
            width: 100,
            filterable: false,
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('CreatedByUser'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.CreatedByUserTitle')
        },
        {
            field: "GLCode",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.GLCodeTitle'),
            width: 100,
            filterable: false,
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('GLCode'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.GLCodeTitle')
        },
        {
            field: "SchemeGLCode",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.SchemeGLCodeTitle'),
            width: 100,
            filterable: false,
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('SchemeGLCode'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.SchemeGLCodeTitle')
        },
        {
            field: "FundingSourceCode",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.FundingSourceCodeTitle'),
            width: 100,
            filterable: false,
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('FundingSourceCode'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.FundingSourceCodeTitle')
        },
        {
            field: "FundingSourceDetail",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.FundingSourceDetailTitle'),
            width: 100,
            filterable: false,
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('FundingSourceDetail'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.FundingSourceDetailTitle')
        },
        {
            field: "BudgetTypeCode",
            title: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.BudgetTypeCodeTitle'),
            width: 100,
            filterable: false,
            hidden: FlexiJS.GMS.Application.Payments.HasUserHiddenColumn('BudgetTypeCode'),
            groupHeaderTemplate: FlexiJS.GMS.Application.Payments.SetupGridGroupHeaderTemplate('PaymentGrid.BudgetTypeCodeTitle')
        },
        {
            template: function (dataItem) {
                return '<div id="paymentButton_' + dataItem.GMSApplicationPaymentID + '" onclick="return false;" data-buttonoptions="'
                    + FlexiJS.GMS.Application.Payments.GetPaymentButtonOptions(dataItem)
                    + '"></div>';
            },
            width: 40,
            filterable: false,
            groupable: false,
            sticky: true,
            footerTemplate: "<span class='ApplicationTotalPayments'></span>"
        }
    ];

    var gridSettings = {
        GridId: gridId,
        GridDatasource: gridDatasource,
        GridColumns: columns,
        EnableSorting: true,
        EnableGrouping: true,
        EnableColumnSelector: true,
        ExcelFileName: "Payments",
        DataBoundFunction: FlexiJS.GMS.Application.Payments.GridOnDataBound,
        OnDataBindingFunction: FlexiJS.GMS.Application.Payments.GridOnDatabinding,
        SaveGridHiddenColumns: true,
        SaveGridHiddenColumnsUserSetting: FlexiJS.GMS.Application.Payments.Constants._saveGridHiddenColumnsUserSetting
    };

    FlexiJS.Kendo.Grids.SetupKendoGrid(gridSettings);

    $(window).resize(function () {
        if (FlexiJS.GMS.Application.Payments.Constants._gridId.length > 0) {
            FlexiJS.Kendo.Grids.ResizeGrid(FlexiJS.GMS.Application.Payments.Constants._gridId);
        }
    });

};
FlexiJS.GMS.Application.Payments.GridOnDatabinding = function (e) {
    var dataSource = this.dataSource;
    var groups = dataSource.group();
    var previousGrouping = FlexiJS.GMS.Application.Payments.Constants._previousGrouping;

    if (groups.length > 1) {
        e.preventDefault();
        for (var i = 0; i < groups.length; i++) {
            if (previousGrouping.length > 0 && (groups[i].field == previousGrouping[0].field)) {
                groups.splice(i, 1);
            };
        };
        dataSource.read();
    };

    FlexiJS.GMS.Application.Payments.Constants._previousGrouping = groups;
};

FlexiJS.GMS.Application.Payments.GridOnDataBound = function (e) {
    var paymentButtons = document.querySelectorAll('[id^="paymentButton_"]');

    for (var index = 0; index < paymentButtons.length; index++) {
        FlexiJS.Controls.ComboButton.SetUp(paymentButtons[index].id, true, true);
    }

    //Set Grid Footer total to the sum of all payments for the application
    var footerText = '<span class="fx-gridactivitystatusfooter"><span class=""fx-gridactivitystatusfooterlabel"">' + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.FooterText') + '</span>' +
        kendo.toString(FlexiJS.GMS.Application.Payments.Constants._totalPaymentsValue, 'c') + '</span>';
    var grid = $('#' + FlexiJS.GMS.Application.Payments.Constants._gridId).data("kendoGrid");
    grid.footer.find('.ApplicationTotalPayments').html(footerText);

    //Remove operator dropdowns
    var dropdownOperator = grid.thead.find('span.k-picker.k-dropdownlist.k-dropdown-operator');
    dropdownOperator.unbind('click');

    FlexiJS.Kendo.Grids.ResizeGrid(FlexiJS.GMS.Application.Payments.Constants._gridId);

    setTimeout(function () {
        $(window).trigger('resize');
    }, 250);

    //Set title on filter search icons
    dropdownOperator.prop('title', FlexiJS.Resources.GetResourceText('KendoGrid', 'FilterSearchTitle'));
    this.element.find("#columnChooser span.k-icon.k-i-filter").remove();
    FlexiJS.GMS.Application.Payments.SetupForeignCurrencyPayoutCheckBox();
};

FlexiJS.GMS.Application.Payments.SetPayeeInferredIcons = function (field, dataItem) {
    var fieldNameCheck = field.toLowerCase();
    if (fieldNameCheck == "payee" && dataItem.Payee != null && dataItem.Payee.toString().length > 0 || fieldNameCheck == "payeefinancereference" && dataItem.PayeeFinanceReference != null && dataItem.PayeeFinanceReference.toString().length > 0) {
        var fieldData = fieldNameCheck == 'payee' ? dataItem.Payee : dataItem.PayeeFinanceReference;
        if (dataItem.PayeeInferred) {
            var infoMessage = FlexiJS.Resources.GetResourceText('Controls/GMS/PaymentsList.ascx', field + 'Inferred.NotAuthorised');
            if (dataItem.PaymentStatusID != FlexiJS.Enums.GMS.PaymentStatus.NewPayment) {
                infoMessage = FlexiJS.Resources.GetResourceText('Controls/GMS/PaymentsList.ascx', field + 'Inferred.Default');
            }
            return fieldData + '<div class="InfoIcon infoicon-sizing" title="' + infoMessage + '"></div>';
        }
        else {
            return fieldData;
        }
    }
    else {
        return FlexiJS.Resources.GetResourceText('Controls/GMS/PaymentsList.ascx', field + 'Inferred_NotSet');
    }
};

FlexiJS.GMS.Application.Payments.GridPaymentStatusFilter = function (args) {

    var enums = FlexiJS.Enums.GMS.PaymentStatus;

    var paymentStatus = [];

    $.each(enums, function (index, enumitem) {
        paymentStatus.push({ Value: enumitem, Text: FlexiJS.Enums.GetEnumsText('GMS', 'PaymentStatus', enumitem) });
    })

    args.element.kendoDropDownList({
        valuePrimitive: true,
        dataTextField: "Text",
        dataValueField: "Value",
        dataSource: paymentStatus,
        optionLabel: "All"
    });

};

FlexiJS.GMS.Application.Payments.GetPaymentButtonOptions = function (dataItem) {

    var options = [];

    FlexiJS.GMS.Application.Payments.GridSetupEditButton(dataItem, options);
    FlexiJS.GMS.Application.Payments.GridSetupViewButton(dataItem, options);
    FlexiJS.GMS.Application.Payments.GridSetupAuthoriseButton(dataItem, options);
    FlexiJS.GMS.Application.Payments.GridSetupRollbackButton(dataItem, options);
    FlexiJS.GMS.Application.Payments.GridSetupDeleteButton(dataItem, options);

    return options.join('');
};

FlexiJS.GMS.Application.Payments.GridCreateButtonOption = function (onClickFunction, text, disabledTitle, titleClass) {
    var option = '<a href=\'\\#\' onclick=&quot;' + onClickFunction + ';return false;&quot;';

    if (disabledTitle) {
        option += ' class=\'aspNetDisabled\' title=\'' + disabledTitle + '\'';
    }
    else {
        if (titleClass) {
            option += ' class=\'' + titleClass + '\'';
        }
    }

    option += '><div class=\'combobutton-option\'><span>' + text + '</span></div></a>';

    return option;
};


FlexiJS.GMS.Application.Payments.GridSetupEditButton = function (dataItem, options) {
    if (dataItem.CanEditPayment) {
        var editOnClientClick = FlexiJS.GMS.Application.Payments.SetPaymentModalOnClientClick(dataItem, false);
        options.push(FlexiJS.GMS.Application.Payments.GridCreateButtonOption(editOnClientClick, FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.EditButtonText'), '', ''));
    }
};

FlexiJS.GMS.Application.Payments.GridSetupViewButton = function (dataItem, options) {
    if (!dataItem.CanEditPayment && dataItem.CanViewPayment) {
        var viewOnClientClick = FlexiJS.GMS.Application.Payments.SetPaymentModalOnClientClick(dataItem, true);
        options.push(FlexiJS.GMS.Application.Payments.GridCreateButtonOption(viewOnClientClick, FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.ViewButtonText'), '', ''));
    }
};

FlexiJS.GMS.Application.Payments.SetPaymentModalOnClientClick = function (dataItem, isViewMode) {
    var paymentModalURL = FlexiJS.GMS.Application.Payments.PaymentModalURL;
    paymentModalURL = paymentModalURL.replace('{GMSApplicationID}', dataItem.GMSApplicationID.toString());
    paymentModalURL = paymentModalURL.replace('{GMSApplicationPaymentID}', dataItem.GMSApplicationPaymentID.toString());
    if (isViewMode) {
        paymentModalURL = paymentModalURL + "&view=1";
    }

    var onClientClick;
    if (FlexiJS.GMS.Application.Payments.Constants._controlLocationId == FlexiJS.GMS.Application.Payments.PaymentControlLocation.OfferStage) {
        paymentModalURL = paymentModalURL + "&stage=70206";
        onClientClick = "loadRadWindowInsideWindow('" + paymentModalURL + "', 'OfferStagePayment', true)";
    }
    else {
        onClientClick = "openRadWindowWithName('" + paymentModalURL + "', 'auto', 'auto', true, '', false)"
    }

    return onClientClick;
};

FlexiJS.GMS.Application.Payments.GridSetupAuthoriseButton = function (dataItem, options) {
    var enabledAuthoriseButton = false;
    var authoriseButtonToolTip;
    var authoriseButtonOnClientClick = 'FlexiJS.GMS.Application.Payments.OnAuthorisePaymentClick(' + dataItem.GMSApplicationID + ',' + dataItem.GMSApplicationPaymentID + ')';
    var displayAuthoriseButton = false;

    var blockReasons = dataItem.AuthorisationBlockReasons.map(function (e) { return e; });
    displayAuthoriseButton = dataItem.ApplicationStatusID != FlexiJS.Enums.GMS.ApplicationStatus.Closed && dataItem.ApplicationStatusID != FlexiJS.Enums.GMS.ApplicationStatus.Complete && !blockReasons.includes(79150) && !blockReasons.includes(79151);
    enabledAuthoriseButton = blockReasons.length == 0;

    if (displayAuthoriseButton && !enabledAuthoriseButton) {

        if (blockReasons.length == 0 && !dataItem.PaymentHasLinkedClaim) {
            //If NotLinkedToClaimForm this should just warn user but allow them to proceed anyway
            authoriseButtonOnClientClick = 'FlexiJS.GMS.Application.Payments.OnPaymentNotAssociatedWithClaimClick(' + dataItem.GMSApplicationID + ',' + dataItem.GMSApplicationPaymentID + ')';
        }
        else {
            authoriseButtonOnClientClick = "return false;"
            authoriseButtonToolTip = FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.CantAuthoriseText') + '\r\n' + dataItem.AuthorisationBlockReasonsText.map(function (e) { return "• " + e; }).join('\r\n');
        }
    }

    if (displayAuthoriseButton) {
        options.push(FlexiJS.GMS.Application.Payments.GridCreateButtonOption(authoriseButtonOnClientClick, FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.AuthoriseButtonText'), enabledAuthoriseButton ? '' : authoriseButtonToolTip, ''));
    }
};


FlexiJS.GMS.Application.Payments.GridSetupRollbackButton = function (dataItem, options) {

    if (dataItem.CanRollbackPayment) {
        var rollbackButtonOnClientClick = 'FlexiJS.GMS.Application.Payments.RollbackPayment(' + dataItem.GMSApplicationID + ',' + dataItem.GMSApplicationPaymentID + ')';
        options.push(FlexiJS.GMS.Application.Payments.GridCreateButtonOption(rollbackButtonOnClientClick, FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.RollbackButtonText')));
    }

};

FlexiJS.GMS.Application.Payments.GridSetupDeleteButton = function (dataItem, options) {

    if (dataItem.CanDeletePayment) {
        var deleteButtonOnClientClick = 'FlexiJS.GMS.Application.Payments.OnDeletePaymentClick(' + dataItem.GMSApplicationID + ',' + dataItem.GMSApplicationPaymentID + ')';
        options.push(FlexiJS.GMS.Application.Payments.GridCreateButtonOption(deleteButtonOnClientClick, FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.DeleteButtonText'), '', 'combobutton-warning'));
    }

};

FlexiJS.GMS.Application.Payments.OnPaymentNotAssociatedWithClaimClick = function (gmsApplicationId, gmsApplicationPaymentId) {

    var constants = FlexiJS.GMS.Application.Payments.Constants;
    constants._gmsApplicationId = gmsApplicationId;
    constants._gmsApplicationPaymentId = gmsApplicationPaymentId;

    FlexiJS.ConfirmActions.KendoConfirmActionDialog(FlexiJS.Resources.GetResourceText('gms/ecffinance.aspx', 'process_NoClaim.Title'),
        'authorisePayment',
        FlexiJS.Resources.GetResourceText('gms/ecffinance.aspx', 'process_NoClaim.Message'),
        FlexiJS.Resources.GetResourceText('gms/ecffinance.aspx', 'process_NoClaim.Accept'),
        'btnOKAuthorisePayment',
        FlexiJS.Resources.GetResourceText('gms/ecffinance.aspx', 'process_NoClaim.Cancel'),
        'btnCancelAuthorisePayment',
        'False');

};

FlexiJS.GMS.Application.Payments.OnDeletePaymentClick = function (gmsApplicationId, gmsApplicationPaymentId) {

    var constants = FlexiJS.GMS.Application.Payments.Constants;
    constants._gmsApplicationId = gmsApplicationId;
    constants._gmsApplicationPaymentId = gmsApplicationPaymentId;

    FlexiJS.ConfirmActions.KendoConfirmActionDialog(FlexiJS.Resources.GetResourceText('gms/ecffinance.aspx', 'Delete_GMSApplicationPayment.Title'),
        'deletePayment',
        FlexiJS.Resources.GetResourceText('gms/ecffinance.aspx', 'Delete_GMSApplicationPayment.Message'),
        FlexiJS.Resources.GetResourceText('gms/ecffinance.aspx', 'Delete_GMSApplicationPayment.Accept'),
        'DeletePayment',
        FlexiJS.Resources.GetResourceText('gms/ecffinance.aspx', 'Delete_GMSApplicationPayment.Cancel'),
        'CancelDeletePayment',
        'False');

};

FlexiJS.GMS.Application.Payments.OnAuthorisePaymentClick = function (gmsApplicationId, gmsApplicationPaymentId) {

    var constants = FlexiJS.GMS.Application.Payments.Constants;
    constants._gmsApplicationId = gmsApplicationId;
    constants._gmsApplicationPaymentId = gmsApplicationPaymentId;

    var paymentData = {
        GMSApplicationID: gmsApplicationId,
        GMSApplicationPaymentID: gmsApplicationPaymentId
    };

    $.ajax({
        url: FlexiJS.GMS.Application.Payments.GetGMSServiceMethodUrl('GetPaymentAuthorisationSummary'),
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify(paymentData),
        dataType: 'json'
    }).done(function (r) {
        if (r && r.d && r.d.Result) {
            FlexiJS.GMS.Application.Payments.ShowAuthorisationWindow(gmsApplicationId, gmsApplicationPaymentId, JSON.parse(r.d.ResultDetail));
        } else {
            showNotificationMessage('error', FlexiJS.Resources.GetResourceText('Controls/GMS/PaymentsList.ascx', 'ProcessPaymentErrorMessage'));
        }
    }).fail(function () {
        showNotificationMessage('error', FlexiJS.Resources.GetResourceText('Controls/GMS/PaymentsList.ascx', 'ProcessPaymentErrorMessage'));
    });
};

FlexiJS.GMS.Application.Payments.ShowAuthorisationWindow = function (gmsApplicationId, gmsApplicationPaymentId, paymentData) {
    var claimReference = null;
    if (paymentData.Item1.GMSApplicationClaimID && paymentData.Item1.GMSApplicationClaimID > 0) {
        claimReference = '<span class="bold">' + (paymentData.Item1.InvoiceNumber || paymentData.Item1.ClaimReference) + '</span>';
    }

    var hasRelatedPayments = paymentData.Item2 && paymentData.Item2.length > 0;
    var hasBudgetItemTypes = false;
    if (paymentData.Item1.BudgetType) hasBudgetItemTypes = true;
    if (!hasBudgetItemTypes && hasRelatedPayments) {
        hasBudgetItemTypes = paymentData.Item2.filter(function (pmt) { return pmt.BudgetType != null ? true : false; }).length > 0;
    }

    var html = "<div class='process-window'>";
    html += "<div class='paymentauthorisation-popupcontainer'><div class='half-margin-bottom'><h1 class='no-margin'>" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.Title') + "</h1>";
    //To be looked at in v3.47
    //if (claimReference) {
    //html += "<span>" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.ClaimMessage').replace('[ClaimReference]', claimReference) + "</span>";
    //} else {
    html += "<span>" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.Message') + "</span>";
    //}

    html += "</div><div class='paymentauthorisation-scroller'>";

    html += "<table class=\"paymentauthorisation-table no-margin-top\">";
    html += "<tr>";
    html += "<th class=\"payee-column\">" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.PayeeColumnHeader') + "</th>";
    html += "<th class=\"scheduled-column\">" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.ScheduledDateColumnHeader') + "</th>";
    if (hasBudgetItemTypes) html += "<th class=\"budgettype-column\">" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.BudgetTypeColumnHeader') + "</th>";
    html += "<th class=\"description-column\">" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.DescriptionColumnHeader') + "</th>";
    html += "<th class=\"amount-column\">" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.AmountColumnHeader') + "</th>";
    html += "</tr>";
    html += "<tr>";
    html += "<td class=\"payee-column\">" + paymentData.Item1.Payee + "</td>";
    html += "<td class=\"scheduled-column\">" + kendo.toString(new Date(paymentData.Item1.DateInvoice), 'd') + "</td>";
    if (hasBudgetItemTypes) html += "<td class=\"budgettype-column\">" + (paymentData.Item1.BudgetType || "<span class=\"italic\">" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.NoBudgetItemText') + "</span>") + "</td>";
    html += "<td class=\"description-column\"><p class='multiLine'>" + (paymentData.Item1.Description || '') + "</p></td>";
    html += "<td class=\"amount-column\">" + kendo.toString(paymentData.Item1.Value, 'c') + "</td>";
    html += "</tr>";
    html += "</table>";

    var totalElement = "";
    if (hasRelatedPayments) {
        html += "<div class='half-margin-bottom'><h2 class='margin-top no-margin-bottom'>" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.RelatedTitle') + "</h2>";
        //To be looked at in v3.47
        //if (claimReference) {
        //html += "<span>" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.RelatedClaimMessage').replace('[ClaimReference]', claimReference) + "</span>";
        //} else {
        html += "<span>" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.RelatedMessage') + "</span></div>";
        //}

        if (paymentData.Item2.filter(function (pmt) { return !pmt.Authorisable; }).length > 0) {
            html += "<div class=\"disabledWarning\"><i class=\"fa fa-exclamation-circle\"></i>" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.UnapprovableRelatedWarning') + "</div>";
        }

        html += "<table class=\"paymentauthorisation-table\">";
        html += "<thead><tr>";
        html += "<th class=\"payee-column\">" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.PayeeColumnHeader') + "</th>";
        html += "<th class=\"scheduled-column\">" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.ScheduledDateColumnHeader') + "</th>";
        if (hasBudgetItemTypes) html += "<th class=\"budgettype-column\">" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.BudgetTypeColumnHeader') + "</th>";
        html += "<th class=\"description-column\">" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.DescriptionColumnHeader') + "</th>";
        html += "<th class=\"amount-column\">" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.AmountColumnHeader') + "</th>";
        html += "</tr></thead><tbody>";

        var rowIndex = 1;
        html += paymentData.Item2.map(function (pmt) {
            rowIndex += 1;
            var isAltRow = rowIndex % 2 == 1;
            var errorRows = '';
            if (!pmt.Authorisable) {
                errorRows = '<tr class="disabled-row disabled-row-last' + (isAltRow ? ' alt-row' : '') + '"><td colspan="' + (hasBudgetItemTypes ? 5 : 4) + '">' + pmt.AuthorisationBlockReasonsText.map(function (e) { return "<span>" + e + '</span>'; }).join('<br/>') + '</td></tr>';
            }

            var budgetTypeColumn = "";
            if (hasBudgetItemTypes) budgetTypeColumn = "<td class=\"budgettype-column\">" + (pmt.BudgetType || "<span class=\"italic\">" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.NoBudgetItemText') + "</span>") + "</td>";

            return "<tr class=\"" + (!pmt.Authorisable ? 'disabled-row disabled-row-first' : '') + (isAltRow ? ' alt-row' : '') + "\">" +
                "<td class=\"payee-column\">" + pmt.Payee + "</td>" +
                "<td class=\"scheduled-column\">" + kendo.toString(new Date(pmt.DateInvoice), 'd') + "</td>" +
                budgetTypeColumn +
                "<td class=\"description-column\"><p class='multiLine'>" + (pmt.Description || '') + "</p></td>" +
                "<td class=\"amount-column\">" + kendo.toString(pmt.Value, 'c') + "</td>" +
                "</tr>" + errorRows;
        }).join("");
        html += "</tbody></table>";

        var totalAuthorisable = paymentData.Item2.filter(function (payment) { return payment.Authorisable; }).map(function (payment) { return payment.Value; }).reduce(function (a, b) { return a + b; }, 0) + paymentData.Item1.Value;

        totalElement = "<div class='paymentauthorisation-totals margin-top'>" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.TotalToAuthorise') + ": " + kendo.toString(totalAuthorisable, 'c') + "</div>";
    }

    html += "</div><div>" + totalElement;
    html += "<div class='generic-buttongroup button-save-cancel in-modal'>";
    html += "<button id=\"btnCancel\" class=\"fg_button secondary\">" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.CancelButtonText') + "</button>";
    html += "<button id=\"btnConfirm\" class=\"fg_button primary\">" + FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentAuthorisationPopup.ApproveButtonText') + "</button>";
    html += "</div>";
    html += "</div>";
    html += "</div></div>";

    var kw = $(html)
        .kendoWindow({
            title: false,
            actions: [],
            resizable: false,
            draggable: false,
            modal: { preventScroll: true },
            scrollable: true,
            width: 980,
            close: function (e) {
                //Clear Temp Data On Close
                FlexiJS.UI.ClearBodyOverflow();
            },
            open: function (e) {
                FlexiJS.UI.SetOverflowAndCenterPopup(this);
            }
        });

    kw.find('#btnCancel').click(
        function () {
            kw.data("kendoWindow").close();
            if (kw.find('#btnCancel').onclick) kw.find('#btnCancel').onclick();
            kw.data("kendoWindow").destroy();
            FlexiJS.UI.ClearBodyOverflow();
        }
    ).end();

    kw.find('#btnConfirm').click(
        function () {
            kendo.ui.progress(kw, true);
            setTimeout(
                function () {
                    $.ajax({
                        type: "POST",
                        url: "/GMS/WebServices/GMSService.svc/AuthorisePayment",
                        data: JSON.stringify({
                            GMSApplicationID: gmsApplicationId,
                            GMSApplicationPaymentID: gmsApplicationPaymentId
                        }),
                        processData: false,
                        contentType: "application/json; charset=utf-8",
                        dataType: "json",
                        async: false,
                        success: function (result) {
                            if (!result || result == 'false' || !result.d || result.d == 'false') {
                                kendo.ui.progress(kw, false);
                                showNotificationMessage('error', FlexiJS.Resources.GetResourceText('Controls/GMS/PaymentsList.ascx', 'ProcessPaymentErrorMessage'));
                            } else {
                                kendo.ui.progress(kw, false);
                                kw.data("kendoWindow").close();
                                kw.data("kendoWindow").destroy();
                                FlexiJS.GMS.Application.Payments.ReloadPaymentsGrids();
                                showNotificationMessage('success', FlexiJS.Resources.GetResourceText('Controls/GMS/PaymentsList.ascx', 'ProcessPaymentMessage'));
                            }
                        },
                        error: function (er) {
                            kendo.ui.progress(kw, false);
                            showNotificationMessage('error', FlexiJS.Resources.GetResourceText('Controls/GMS/PaymentsList.ascx', 'ProcessPaymentErrorMessage'));
                        }
                    });
                    FlexiJS.UI.ClearBodyOverflow();
                },
                100
            );
        }
    ).end();

    kw.data("kendoWindow").center().open();
};

FlexiJS.GMS.Application.Payments.AuthorisePayment = function () {

    var constants = FlexiJS.GMS.Application.Payments.Constants;

    var paymentData = {
        GMSApplicationID: constants._gmsApplicationId,
        GMSApplicationPaymentID: constants._gmsApplicationPaymentId
    };

    $.ajax({
        url: FlexiJS.GMS.Application.Payments.GetGMSServiceMethodUrl('AuthorisePayment'),
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify(paymentData),
        dataType: 'json'
    }).done(function (r) {
        var result = r.d;
        if (result.Result) {
            showNotificationMessage('success', FlexiJS.Resources.GetResourceText('Controls/GMS/PaymentsList.ascx', 'ProcessPaymentMessage'));
            FlexiJS.GMS.Application.Payments.ReloadPaymentsGrids();
        } else {
            showNotificationMessage('error', FlexiJS.Resources.GetResourceText('Controls/GMS/PaymentsList.ascx', 'ProcessPaymentErrorMessage'));
        }
    }).fail(function () {
        showNotificationMessage('error', FlexiJS.Resources.GetResourceText('Controls/GMS/PaymentsList.ascx', 'ProcessPaymentErrorMessage'));
    });

};

FlexiJS.GMS.Application.Payments.RollbackPayment = function (gmsApplicationId, gmsApplicationPaymentId) {

    var paymentData = {
        GMSApplicationID: gmsApplicationId,
        GMSApplicationPaymentID: gmsApplicationPaymentId
    };

    $.ajax({
        url: FlexiJS.GMS.Application.Payments.GetGMSServiceMethodUrl('RollbackPayment'),
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify(paymentData),
        dataType: 'json'
    }).done(function (r) {
        var result = r.d;
        if (result.Result) {
            showNotificationMessage('success', FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.RollbackPaymentMessage'));
            FlexiJS.GMS.Application.Payments.ReloadPaymentsGrids();
        } else {
            showNotificationMessage('error', FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.RollbackPaymentErrorMessage'));
        }
    }).fail(function () {
        showNotificationMessage('error', FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.RollbackPaymentErrorMessage'));
    });

};

FlexiJS.GMS.Application.Payments.DeletePayment = function () {

    var constants = FlexiJS.GMS.Application.Payments.Constants;

    var paymentData = {
        GMSApplicationID: constants._gmsApplicationId,
        GMSApplicationPaymentID: constants._gmsApplicationPaymentId
    };

    $.ajax({
        url: FlexiJS.GMS.Application.Payments.GetGMSServiceMethodUrl('DeletePayment'),
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify(paymentData),
        dataType: 'json'
    }).done(function (r) {
        var result = r.d;
        if (result.Result) {
            showNotificationMessage('success', FlexiJS.Resources.GetResourceText('Controls/GMS/PaymentsList.ascx', 'RemovePaymentMessage'));
            FlexiJS.GMS.Application.Payments.ReloadPaymentsGrids();
        } else {
            showNotificationMessage('error', FlexiJS.Resources.GetResourceText('Controls/GMS/PaymentsList.ascx', 'RemovePaymentErrorMessage'));
        }
    }).fail(function () {
        showNotificationMessage('error', FlexiJS.Resources.GetResourceText('Controls/GMS/PaymentsList.ascx', 'RemovePaymentErrorMessage'));
    });

};

FlexiJS.GMS.Application.Payments.ReloadPaymentsGrids = function () {
    refreshPaymentGrid(null, true);
    FlexiJS.Events.Event.FinancialInformationRefresh.Fire();
};

FlexiJS.GMS.Application.Payments.ReloadFinanceRelatedControls = function () {
    var btnHiddenReloadFinance = $('[id*=btnHiddenReloadFinance]');
    if (btnHiddenReloadFinance.length > 0) {
        $(btnHiddenReloadFinance)[0].click();
    }
};

FlexiJS.GMS.Application.Payments.SetupForeignCurrencyPayoutCheckBox = function () {
    var chkLocalPayout = $('[id$=chkLocalPayout]');
    if (chkLocalPayout !== null && chkLocalPayout.length > 0) {
        var totalActionablePayments = FlexiJS.GMS.Application.Payments.Constants._totalForeignCurrencyActionablePayments;

        if (totalActionablePayments > 0) {
            chkLocalPayout.attr('onclick', 'ShowUpdateExistingPaymentsDecision(this)');
            var updateExistingPaymentsDecisionMessage = FlexiJS.Resources.GetResourceText('Controls/GMS/Payment/ApplicationPayee.ascx', 'UpdateExistingPaymentsDecisionMessage.Text');
            var lblUpdateExistingPaymentsDecisionMessage = $('[id*=lblUpdateExistingPaymentsDecisionMessage]');
            if (lblUpdateExistingPaymentsDecisionMessage && (updateExistingPaymentsDecisionMessage !==null && updateExistingPaymentsDecisionMessage.length > 0)) {
                var paymentmethodId = parseInt(lblUpdateExistingPaymentsDecisionMessage.data("paymentmethodid"));
                updateExistingPaymentsDecisionMessage = updateExistingPaymentsDecisionMessage.replace('[PaymentStatuses]', paymentmethodId == FlexiJS.Enums.GMS.PaymentMethod.Advanced ? FlexiJS.Enums.GetEnumsText('GMS', 'PaymentStatus', FlexiJS.Enums.GMS.PaymentStatus.NewPayment) : FlexiJS.Enums.GetEnumsText('GMS', 'PaymentStatus', FlexiJS.Enums.GMS.PaymentStatus.Pending));
                updateExistingPaymentsDecisionMessage = updateExistingPaymentsDecisionMessage.replace('[State]', chkLocalPayout.is(':checked') ? FlexiJS.Resources.GetResourceText('Controls/GMS/Payment/ApplicationPayee.ascx', 'UpdateExistingPaymentsDecisionMessage.OffToOn') : FlexiJS.Resources.GetResourceText('Controls/GMS/Payment/ApplicationPayee.ascx', 'UpdateExistingPaymentsDecisionMessage.OnToOff'));
                updateExistingPaymentsDecisionMessage = updateExistingPaymentsDecisionMessage.replace('[PaidOutText]', FlexiJS.Resources.GetResourceText('Controls/GMS/Payment/ApplicationPayee.ascx', 'UpdateExistingPaymentsDecisionMessage.PaidOut'));
                lblUpdateExistingPaymentsDecisionMessage.text(updateExistingPaymentsDecisionMessage);
            }
        }
        else {
            chkLocalPayout.removeAttr('onclick');
            var divUpdateExistingPaymentsDecision = $('#divUpdateExistingPaymentsDecision');
            if (divUpdateExistingPaymentsDecision) {
                divUpdateExistingPaymentsDecision.hide();
            }
        }
    }
};FlexiJS.GMS.Application.Start = {};

FlexiJS.GMS.Application.Start.SetupOnBehalf = function (params) {
    var dropdownListMaxHeight = 380;
    var containerElement = $('[id="' + params.startApplicationContainerElementId + '"]');
    var optionsChanged = function () {
        var hasValidUser = false;
        var hasValidFundType = false;

        var showNewAccountMessage = false;
        var showEmailUnavailableMessage = false;
        var showDeactvatedLoginMessage = false;

        //Check if a valid user is selected
        if (params.assignToElementId && params.assignToElementId != "") {
            var assignToComboBox = containerElement.find('[id="' + params.assignToElementId + '"]').data('kendoComboBox');

            if (assignToComboBox.selectedIndex >= 0) {

                var dataItem = assignToComboBox.dataSource.data()[assignToComboBox.selectedIndex];

                if (dataItem.PersonId < 1) {
                    //Person selected is not a valid user (placeholder text)
                    hasValidUser = false;
                } else if (dataItem.UserId != null) {
                    //User account exists, ok to use
                    hasValidUser = true;
                } else {
                    switch (params.loginMethod) {
                        case "EmailOnly":
                            hasValidUser = true;
                            showNewAccountMessage = true;
                            if (dataItem.Email == null || dataItem.EmailInUseAsLogin == 1) {
                                showEmailUnavailableMessage = true;
                                showDeactvatedLoginMessage = true;
                            }
                            break;

                        case "Any":
                            hasValidUser = true;
                            showNewAccountMessage = true;
                            if (dataItem.Email == null || dataItem.EmailInUseAsLogin == 1) {
                                showEmailUnavailableMessage = true;
                            }
                            break;

                        default:
                            hasValidUser = true;
                            showNewAccountMessage = true;
                            break;

                    }
                }
            }
        }

        //Toggle user account creation messages
        if (params.newAccountMessageElementId && params.newAccountMessageElementId != "") containerElement.find('[id="' + params.newAccountMessageElementId + '"]').toggle(showNewAccountMessage);
        if (params.emailUnavailableMessageElementId && params.emailUnavailableMessageElementId != "") containerElement.find('[id="' + params.emailUnavailableMessageElementId + '"]').toggle(showEmailUnavailableMessage);
        if (params.deactivatedLoginMessageElementId && params.deactivatedLoginMessageElementId != "") containerElement.find('[id="' + params.deactivatedLoginMessageElementId + '"]').toggle(showDeactvatedLoginMessage);

        //Check if a fund type is selected
        hasValidFundType = true;
        if (params.fundTypeElementId && params.fundTypeElementId != "") {
            var fundTypeDropdown = containerElement.find('[id="' + params.fundTypeElementId + '"]').data('kendoDropDownList');
            hasValidFundType = fundTypeDropdown.value() > 0;
        }

        if (params.startButtonId && params.startButtonId != "") {
            var startButton = containerElement.find('[id="' + params.startButtonId + '"]');
            if (hasValidUser && hasValidFundType) {
                startButton
                    .prop('disabled', false)
                    .removeClass('aspNetDisabled')
                    .prop('title','');//removeprop doesn't work for title as it shows a tooltip saying undefined
            } else {
                startButton
                    .prop('disabled', true)
                    .prop('title', params.cannotStartText)
                    .addClass('aspNetDisabled');
            }
        }
    };

    var ResizeGrouping = function (ddl) {
        // In a kendo combo box, the group label comes after the first item in the DOM, so resizing the label
        // causes the first item to look like it is in the previous group. This function loops through groups
        // that haven't had the fix class applied, moves them before the label of the first item in the DOM (the 
        // group MUST stay within the first item in the DOM to maintain combobox functionality) and resizes the group elements
        // using a cloned version of the list (required as the list won't be visible and heights will be read as 0)
        var me = ddl;
        // Gets any that haven't already been fixed
        me.list.addClass('kendocombobox-userlist');
        var groups = $('[class~="k-group"]:not([class~="kendocombobox-userlist-groupheader"])', me.list);
        var copied_elem = null;
        if (groups.length > 0) {
            copied_elem = me.list.clone().attr("id", false).css({ visibility: "hidden", display: "block", position: "absolute" });
            $("body").append(copied_elem);
        }
        $.each(groups, function (index, groupItem) {
            var thisGroup = $(groupItem);
            var parent = thisGroup.closest('li');
            var myClone = $('li[data-offset-index="' + parent.attr('data-offset-index') + '"]>[class~="k-group"]', copied_elem);
            var myHeight = myClone.outerHeight();
            parent.css('margin-top', (myHeight + 2) + 'px');
            thisGroup.addClass('kendocombobox-userlist-groupheader').css('top', '-' + (myHeight + 2) + 'px');
        });

        if (copied_elem != null) {
            copied_elem.remove();
        }
    };

    //Setup fund type dropdown list
    if (params.fundTypeElementId && params.fundTypeElementId != "") {

        containerElement.find("[id=" + params.fundTypeElementId + "]").kendoDropDownList(
            {
                template: '#: data.FundTypeName #',
                groupTemplate: '#:data#',
                fixedGroupTemplate: "#:data#",
                dataTextField: "FundTypeName",
                dataValueField: "FundTypeId",
                autoBind: true,
                height: dropdownListMaxHeight,
                dataSource: {
                    data: [{ FundTypeId: 0, FundTypeName: params.fundTypePleaseSelectText, FundName: "" }].concat(params.fundTypeList),
                    serverFiltering: false,
                    serverPaging: false,
                    group: { field: 'FundName' }
                },
                dataBound: function (e) {
                    var me = e.sender;
                    var groups = $('[class~="k-group"]', me.list);
                    $.each(groups, function (index, groupItem) {
                        var thisGroup = $(groupItem);
                        thisGroup.addClass('kendocombobox-userlist-grouprow');
                        $(thisGroup.prev('div')).before(thisGroup);
                    });
                    ResizeGrouping(me);
                    $(".k-group-header", me.list).hide();
                },
                open: function (e) {
                    ResizeGrouping(e.sender);
                },
                change: function (e) {
                    optionsChanged();
                }
            }
        );
    }

    //Setup the assign to combobox
    if (params.assignToElementId && params.assignToElementId != "") {
        var usercombobox = containerElement.find("[id=" + params.assignToElementId + "]").kendoComboBox(
            {
                template: '\
<div class="kendocombobox-row">\
<div>\
<div class="name" title="#: [data.Title, data.Firstname, data.Lastname].filter(Boolean).join(" ") #">\
#: [data.Title, data.Firstname, data.Lastname].filter(Boolean).join(" ") #\
</div>\
# if(data.Organisation != null){ #\
<div class="organisation" title="#: data.Organisation #">\
#: data.Organisation #\
</div>\
# } #\
# if(data.Email != null){ #\
<div class="email" title="#: data.Email #">\
#: data.Email #\
</div>\
# } #\
</div>\
<div>\
# if(data.City != null || data.County != null || data.Country != null){ #\
<div class="additional-info" title="#: [data.City, data.County, data.Country].filter(Boolean).join(", ") #">\
#: [data.City, data.County, data.Country].filter(Boolean).join(", ") #\
# } #\
</div>\
</div>',
                groupTemplate: '#:data#',
                fixedGroupTemplate: "#:data#",
                dataTextField: "Name",
                dataValueField: "PersonId",
                minLength: 2,
                autoBind: true,
                filter: "contains",
                height: dropdownListMaxHeight,
                dataSource: {
                    pageSize: 100,
                    transport: {
                        read: {
                            url: "/GMS/WebServices/GMSService.svc/GetNewApplicationApplicantList",
                            type: "POST",
                            contentType: 'application/json; charset=utf-8'
                        },
                        parameterMap: function (options) {
                            return JSON.stringify({
                                PageSize: options.pageSize,
                                Page: options.page,
                                SearchText: containerElement.find("[id=" + params.assignToElementId + "]").data("kendoComboBox").input.val()
                            });
                        }
                    },
                    schema: {
                        data: function (result) {
                            //Kendo (our version at least) doesn't respect the placeholder text when using remote datasources, so we have to use a dummy item for placeholder text. Add it by default at the top to the datasource
                            if (result.d.Result == true) {
                                var data = JSON.parse(result.d.ResultDetail);
                                if (data) $.each(data, function (index, value) { value.Name = [value.Title, value.Firstname, value.Lastname].filter(Boolean).join(" "); });
                                data.unshift({ PersonId: "0", Firstname: params.personPleaseSelectText, Name: params.personPleaseSelectText });
                                return data;
                            } else {
                                return [{ PersonId: "0", Firstname: params.personPleaseSelectText, Name: params.personPleaseSelectText }];
                            }
                        },
                        total: function (result) {
                            if (result.d.Result == true) {
                                var data = JSON.parse(result.d.ResultDetail);
                                return data.length;
                            } else {
                                return 0;
                            }
                        }
                    },
                    serverFiltering: true,
                    serverPaging: true
                },
                dataBound: function (e) {
                    e.sender.list.addClass('kendocombobox-userlist');
                },
                change: function (e) {
                    //Kendo (our version at least) doesn't respect the placeholder text when using remote datasources, so we have to use a dummy item for placeholder text. If no item is selected, select the placeholder item
                    if (e.sender.selectedIndex < 0) e.sender.select(0);
                    optionsChanged();
                },
                index: 0 //By default select the dummy placeholder item
            }
        ).data("kendoComboBox");

        //Kendo (our version at least) doesn't respect the placeholder text when using remote datasources, so we have to use a dummy item for placeholder text. If the placeholder item is selected, clear the text on focus
        usercombobox.input.on("focus", function () {
            if (usercombobox.selectedIndex == 0) usercombobox.text("");
        });

        if (params.initialSelectedPersonData) {
            params.initialSelectedPersonData[0].Name = [params.initialSelectedPersonData[0].Title, params.initialSelectedPersonData[0].Firstname, params.initialSelectedPersonData[0].Lastname].filter(Boolean).join(" ");
            usercombobox.setDataSource(
                new kendo.data.DataSource({
                    data: params.initialSelectedPersonData
                })
            );
            usercombobox.select(0);
            usercombobox.enable(false);
        }
    }

    optionsChanged();
};
;FlexiJS.Claims = {};

FlexiJS.Claims.GetGridButtonOptions = function (dataItem) {
    options = [];

    //View
    if (dataItem.ActionShowComplete !== true && dataItem.ActionShowCompleteAfterSubmission !== true && !dataItem.AllowEditAfterSubmission === true) {
        options.push(
            FlexiJS.Controls.ComboButton.CreateButtonOption('FlexiJS.Claims.Actions.View(' + dataItem.FormId + ', ' + dataItem.EngagementID + ', ' + dataItem.ApplicationID + ');', FlexiJS.Claims.GetResourceText('GridAction_View'), '', '')
        );
    }

    //Complete
    var gmsOnly = 'false';
    var completeResourceKey = '';
    if (dataItem.ActionShowComplete === true) {
        completeResourceKey = 'GridAction_Complete';
    } else if (dataItem.AllowEditAfterSubmission === true) {
        completeResourceKey = 'GridAction_CompleteAfterSubmission';
    } else if (dataItem.ActionShowCompleteAfterSubmission === true) {
        completeResourceKey = 'GridAction_CompleteAfterSubmission';
        gmsOnly = 'true';
    }
    if (completeResourceKey !== '') {
        options.push(
            FlexiJS.Controls.ComboButton.CreateButtonOption(
                'FlexiJS.Claims.Actions.Complete(' + dataItem.FormId + ', ' + dataItem.EngagementID + ', ' + dataItem.ApplicationID + ', ' + gmsOnly + ');',
                FlexiJS.Claims.GetResourceText(completeResourceKey),
                '',
                ''
            )
        );
    }

    //Print
    options.push(
        FlexiJS.Controls.ComboButton.CreateButtonOption('FlexiJS.Claims.Actions.Print(' + dataItem.FormId + ', ' + dataItem.EngagementID + ');', FlexiJS.Claims.GetResourceText('GridAction_Print'), '', '')
    );

    //Rollback
    if (dataItem.ActionShowRollback === true) options.push(
        FlexiJS.Controls.ComboButton.CreateButtonOption(
            dataItem.ActionDisableRollback !== true ? 'FlexiJS.Claims.Actions.Rollback(' + dataItem.EngagementID + ', ' + dataItem.ApplicationID + ');' : 'return false;',
            FlexiJS.Claims.GetResourceText('GridAction_Rollback'),
            dataItem.ActionDisableRollback === true ? FlexiJS.Claims.GetResourceText('GridAction_RollbackDisabled') : '',
            ''
        )
    );

    //Return to applicant
    if (dataItem.ActionShowReturnToAssignee === true) options.push(
        FlexiJS.Controls.ComboButton.CreateButtonOption(
            dataItem.ActionDisableRollback !== true ? 'FlexiJS.Claims.Actions.ReturnToAssignee(' + dataItem.EngagementID + ', ' + dataItem.ApplicationID + ');' : 'return false;',
            FlexiJS.Claims.GetResourceText('GridAction_RollbackReturn'),
            '',
            ''
        )
    );

    //Accept
    if (dataItem.ActionShowAccept === true) {
        var acceptFunction = 'return false;';
        var disabledText = '';
        if (dataItem.ActionDisableAccept !== true) {
            if (dataItem.IsActivityStatusApprovedClaimAmountEnabled === true && dataItem.ClaimTotal == 0) {
                acceptFunction = 'FlexiJS.Claims.Actions.AcceptBlocked(' + dataItem.EngagementID + ', ' + dataItem.ApplicationID + ');';
            } else {
                acceptFunction = 'FlexiJS.Claims.Actions.Accept(' + dataItem.EngagementID + ', ' + dataItem.ApplicationID + ', ' + dataItem.IsClaimsAgainstBudgets + ');';
            }
        } else {
            disabledText = FlexiJS.Claims.GetResourceText('GridAction_AcceptDisabled');
        }

        options.push(
            FlexiJS.Controls.ComboButton.CreateButtonOption(acceptFunction, FlexiJS.Claims.GetResourceText('GridAction_Accept'), disabledText, '')
        );
    }

    //Reject
    if (dataItem.ActionShowReject === true) options.push(
        FlexiJS.Controls.ComboButton.CreateButtonOption('FlexiJS.Claims.Actions.Reject(' + dataItem.EngagementID + ', ' + dataItem.ApplicationID + ');', FlexiJS.Claims.GetResourceText('GridAction_Reject'), '', '')
    );

    //Review
    if (dataItem.ActionShowUnderReview === true) options.push(
        FlexiJS.Controls.ComboButton.CreateButtonOption('FlexiJS.Claims.Actions.Review(' + dataItem.EngagementID + ', ' + dataItem.ApplicationID + ');', FlexiJS.Claims.GetResourceText('GridAction_Review'), '', '')
    );

    //Delete
    if (dataItem.ActionShowDelete === true) options.push(
        FlexiJS.Controls.ComboButton.CreateButtonOption('FlexiJS.Claims.Actions.Delete(' + dataItem.EngagementID + ', ' + dataItem.ApplicationID + ');', FlexiJS.Claims.GetResourceText('GridAction_Delete'), '', 'combobutton-warning')
    );

    return options.join('');
};

FlexiJS.Claims.SetupGrid = function (elementId) {

    var gridDiv = $("[id=" + elementId + "]");
    var applicationid = gridDiv.data('applicationid');
    var includeApprovedColumn = gridDiv.data('includecolumnapproved');
    var includeInvoiceNoColumn = gridDiv.data('includecolumninvoicenumber');

    var claimsColumns = [];

    claimsColumns.push({
        title: FlexiJS.Claims.GetResourceText('TableHeader.Reference'),
        field: 'Reference',
        editable: function (i) { return false; },
        attributes: { class: "k-gridcell" },
        headerAttributes: { class: "headercell" },
        template: '# if(IsActivityStatusUpdatesEnabled === true){ #<image src="/images/icons/info40.png" id="claimGridInfoIcon_#= EngagementID #" data-id="#= EngagementID #" CssClass="claim-tooltip-icon" Width="18px" Height="18px" /> # } #<div Class="tooltip-indicator" title="'
            + FlexiJS.Claims.GetResourceText("ClaimFormName_TooltipText") + '#= FormName # ('
            + FlexiJS.Enums.GetEnumsText('Forms', 'FormType', 80153)
            + ')">#= (Reference.trim() == "" ? EngagementID : Reference) #</div>',
        footerTemplate: "<div name='ClaimsGridTotalClaimed' class='fx-gridactivitystatusfooter'><span class='bold'>" + FlexiJS.Claims.GetResourceText('GridFooter_Total') + '</span>' + kendo.toString(0, 'c') + '</div>',
        width: 130,
        sortable: false
    });
    if (includeInvoiceNoColumn === true) claimsColumns.push({
        title: FlexiJS.Claims.GetResourceText('TableHeader.InvoiceNumber'),
        field: 'InvoiceNumber',
        editable: function (i) { return false; },
        attributes: { class: "k-gridcell" },
        headerAttributes: { class: "headercell" },
        width: 150,
        sortable: { allowUnsort: true }
    });
    claimsColumns.push({
        title: FlexiJS.Claims.GetResourceText('TableHeader.UserCreatedBy'),
        field: 'UserCreatedBy',
        editable: function (i) { return false; },
        attributes: { class: "k-gridcell" },
        headerAttributes: { class: "headercell" },
        width: 120,
        sortable: { allowUnsort: true }
    });
    claimsColumns.push({
        title: FlexiJS.Claims.GetResourceText('TableHeader.EngagementStatus'),
        field: 'EngagementStatus',
        editable: function (i) { return false; },
        attributes: { class: "k-gridcell" },
        headerAttributes: { class: "headercell" },
        width: 120,
        sortable: { allowUnsort: true }
    });
    claimsColumns.push({
        title: FlexiJS.Claims.GetResourceText('TableHeader.UserSubmittedBy'),
        field: 'UserSubmittedBy',
        editable: function (i) { return false; },
        attributes: { class: "k-gridcell" },
        headerAttributes: { class: "headercell" },
        width: 120,
        sortable: { allowUnsort: true }
    });
    claimsColumns.push({
        title: FlexiJS.Claims.GetResourceText('TableHeader.DateSubmitted'),
        field: 'DateSubmitted',
        format: '{0:d}',
        editable: function (i) { return false; },
        attributes: { class: "k-gridcell" },
        headerAttributes: { class: "headercell" },
        sortable: { allowUnsort: true },
        width: 110
    });
    claimsColumns.push({
        title: FlexiJS.Claims.GetResourceText(includeApprovedColumn === true ? 'TableHeader.ClaimAmount' : 'TableHeader.ClaimTotal'),
        field: 'ClaimTotal',
        format: '{0:c}',
        editable: function (i) { return false; },
        attributes: { class: "k-gridcell" },
        headerAttributes: { class: "headercell" },
        width: 145,
        sortable: { allowUnsort: true }
    });
    if (includeApprovedColumn === true) claimsColumns.push({
        title: FlexiJS.Claims.GetResourceText('TableHeader.ClaimTotal'),
        field: 'ClaimAmount',
        format: '{0:c}',
        editable: function (i) { return false; },
        attributes: { class: "k-gridcell" },
        headerAttributes: { class: "headercell" },
        width: 145,
        sortable: { allowUnsort: true }
    });
    claimsColumns.push({
        title: '',
        editable: function (i) { return false; },
        attributes: { class: "k-gridcell" },
        headerAttributes: { class: "headercell" },
        template: function (dataItem) {
            return '<div id="claimGridActionButton_' + dataItem.EngagementID + '" onclick="return false;" data-buttonoptions="'
                + FlexiJS.Claims.GetGridButtonOptions(dataItem)
                + '"></div>';
        },
        width: 40,
        sortable: false,
        sticky: true
    });


    gridDiv.kendoGrid({
        dataSource: {
            transport: {
                read: {
                    url: "/gms/WebServices/GMSService.svc/GetApplicationClaimList",
                    type: "POST",
                    contentType: "application/json",
                    data: function () {

                        var gridSearch = new Object();
                        gridSearch.ApplicationID = applicationid;
                        return gridSearch;
                    }
                },
                parameterMap: function (options) {
                    return JSON.stringify(options);
                }
            },
            schema: {
                data: function (r) {
                    var data = r.d.Claims;

                    FlexiJS.Dates.ParseObjectArrayDotNetDates(['DateSubmitted', 'DateCreated'], data);

                    return data;
                },
                total: function (r) {
                    gridDiv.data('TotalClaimed', r.d.TotalClaimed);
                    return r.d.TotalClaims;
                },
                model: {
                    fields: {
                        EngagementID: { type: "number" },
                        DateSubmitted: { type: "datetime" },
                        UserSubmittedBy: { type: "string" },
                        DateCreated: { type: "datetime" },
                        UserCreatedBy: { type: "string" },
                        Reference: { type: "string" },
                        ClaimTotal: { type: "currency" },
                        ClaimAmount: { type: "currency" },
                        IsClaimsAgainstBudgets: { type: "boolean" },
                        InvoiceNumber: { type: "string" },
                        Period: { type: "string" },
                        EngagementStatusID: { type: "number" },
                        Manage: { type: "boolean" },
                        Complete: { type: "boolean" },
                        Delete: { type: "boolean" },
                        AllowEditAfterSubmission: { type: "boolean" },
                        IsActivityStatusApprovedClaimAmountEnabled: { type: "boolean" },
                        IsActivityStatusUpdatesEnabled: { type: "boolean" },
                        FormName: { type: "string" },
                        EngagementStatus: { type: "string" }
                    }
                }
            },
            pageSize: 10,
            serverPaging: true,
            serverSorting: true
        },
        filterable: false,
        sortable: {
            mode: "single",
            allowUnsort: false
        },
        resizable: false,
        selectable: false,
        pageable: FlexiJS.Kendo.Grids.GetPagableOptions(),
        noRecords: { template: FlexiJS.Claims.GetResourceText('Table.NoData') },
        columns: claimsColumns,
        dataBound: function (e) {
            if (e.sender.items().length == 0) $(e.sender.wrapper.find('.k-grid-content')[0]).removeClass('k-grid-content');

            var actionButtons = e.sender.wrapper.find('[id^="claimGridActionButton_"]');

            for (var index = 0; index < actionButtons.length; index++) {
                FlexiJS.Controls.ComboButton.SetUp(actionButtons[index].id, true, true);
            }

            var infoIcons = e.sender.wrapper.find('[id^="claimGridInfoIcon_"]');
            if (infoIcons && infoIcons.length > 0) {
                var claimTooltipFailMessage = FlexiJS.Claims.GetResourceText('Claimtooltip_FailerMessage');
                for (var index = 0; index < infoIcons.length; index++) {
                    FlexiJS.GMS.Application.Activities.GetClaimBreakdownToolTip(infoIcons[index].id, $(infoIcons[index]).data('id'), claimTooltipFailMessage);
                }
            }

            //Set Grid Footer total to the sum of all payments for the application
            var footerText = "<span class='bold'>" + FlexiJS.Claims.GetResourceText('GridFooter_Total') + '</span>' + kendo.toString(e.sender.wrapper.data('TotalClaimed'), 'c');

            e.sender.footer.find('[name=ClaimsGridTotalClaimed]').html(footerText);
        }

    });

    FlexiJS.Events.Event.FinancialInformationRefresh.Bind(
        function (data) {
            $("[id=" + elementId + "]").data('kendoGrid').dataSource.read();
            FlexiJS.Popup.ECFFinance.ReloadFinancePage();
        }
    );
};

FlexiJS.Claims.GetResourceText = function (key) {
    return FlexiJS.Resources.GetResourceText('FlexiJS.Claims', key);
};

FlexiJS.Claims.Actions = {};

FlexiJS.Claims.GetEngagementURL = function (qs) {
    return (window.location.toString().indexOf('activities_monitoring.aspx') > 0 ? '/' : '') + 'engagement.aspx?' + qs;
};

FlexiJS.Claims.GetFormURL = function (formid, claimid, applicationid, mode) {
    return FlexiJS.Claims.GetEngagementURL('fid=' + formid + '&pid=0&etype=44665&eid=' + claimid + '&appid=' + applicationid + '&mode=' + mode);
};

FlexiJS.Claims.Actions.View = function (formid, claimid, applicationid) {
    window.open(FlexiJS.Claims.GetFormURL(formid, claimid, applicationid, 80401), '_blank');
};

FlexiJS.Claims.Actions.Print = function (formid, claimid) {
    window.open('/printflexiform.aspx?fid=' + formid + '&pid=0&etype=44665&eid=' + claimid + '&mode=80401&sdr=print', '', 'width=900,height=625,scrollbars=yes');
};

FlexiJS.Claims.Actions.Complete = function (formid, claimid, applicationid, GMSOnlyEditMode) {
    window.open(FlexiJS.Claims.GetFormURL(formid, claimid, applicationid, GMSOnlyEditMode ? 80403 : 80402), '_blank');
};

FlexiJS.Claims.Actions.Accept = function (claimid, applicationid, IsClaimsAgainstBudgets) {
    var paymentFunction = function () { };
    if (IsClaimsAgainstBudgets) {
        paymentFunction = function () { FlexiJS.Budgets.Claims.LoadAcceptWindow(applicationid, claimid); };
    } else {
        paymentFunction = function () {
            FlexiJS.Events.Event.FinancialInformationRefresh.Fire();
            openRadWindowWithName('/gms/GMSPaymentModal.aspx?appid=' + applicationid + '&ct=pay&popclm=' + claimid, null, null, null, null, 'true');
        };
    }


    FlexiJS.ConfirmActions.KendoConfirmPopup({
        title: FlexiJS.Claims.GetResourceText("ActionConfirmation_Approve_Title"),
        message: FlexiJS.Claims.GetResourceText("ActionConfirmation_Approve_Message"),
        buttons: [
            {
                onclick: FlexiJS.Claims.Actions.GetServiceActionRequester(claimid, applicationid, 'Accept'),
                uniquename: "AcceptYes_" + claimid,
                text: FlexiJS.Claims.GetResourceText("ActionConfirmation_Approve_YesText")
            },
            {
                onclick: FlexiJS.Claims.Actions.GetServiceActionRequester(claimid, applicationid, 'Accept', paymentFunction),
                uniquename: "AcceptYesWithPayment_" + claimid,
                text: FlexiJS.Claims.GetResourceText("ActionConfirmation_Approve_YesWithPaymentText")
            },
            {
                uniquename: "AcceptNo_" + claimid,
                text: FlexiJS.Claims.GetResourceText("ActionConfirmation_Approve_NoText")
            }
        ]
    });
};

FlexiJS.Claims.Actions.AcceptBlocked = function (claimid) {
    FlexiJS.ConfirmActions.KendoConfirmDialog(
        FlexiJS.Claims.GetResourceText("ActionConfirmation_ApproveBlocked_Title"),
        null,
        FlexiJS.Claims.GetResourceText("ActionConfirmation_ApproveBlocked_Message"),
        FlexiJS.Claims.GetResourceText("ActionConfirmation_ApproveBlocked_YesText"),
        "AcceptBlockedYes_" + claimid,
        '',
        '',
        true
    );
};

FlexiJS.Claims.Actions.ReturnToAssignee = function (claimid, applicationid) {
    openRadWindow('/gms/GMSPaymentModal.aspx?appid=' + applicationid + '&ct=engage&eid=' + claimid + '&a=rollback&ft=80153', '640', '800');
    return false;
};

FlexiJS.Claims.Actions.Rollback = function (claimid, applicationid) {
    FlexiJS.Claims.Actions.RequestServiceAction(claimid, applicationid, 'Rollback');
};

FlexiJS.Claims.Actions.Reject = function (claimid, applicationid) {
    FlexiJS.Claims.Actions.RequestServiceAction(claimid, applicationid, 'Reject');
};

FlexiJS.Claims.Actions.Review = function (claimid, applicationid) {
    FlexiJS.Claims.Actions.RequestServiceAction(claimid, applicationid, 'Review');
};

FlexiJS.Claims.Actions.Delete = function (claimid, applicationid) {
    FlexiJS.Claims.Actions.RequestServiceAction(claimid, applicationid, 'Delete');
};

FlexiJS.Claims.Actions.RequestServiceAction = function (claimid, applicationid, action) {
    FlexiJS.ConfirmActions.KendoConfirmDialog(
        FlexiJS.Claims.GetResourceText("ActionConfirmation_" + action + "_Title"),
        FlexiJS.Claims.Actions.GetServiceActionRequester(claimid, applicationid, action),
        FlexiJS.Claims.GetResourceText("ActionConfirmation_" + action + "_Message"),
        FlexiJS.Claims.GetResourceText("ActionConfirmation_" + action + "_YesText"),
        action + "Yes_" + claimid,
        FlexiJS.Claims.GetResourceText("ActionConfirmation_" + action + "_NoText"),
        action + "No_" + claimid,
        false
    );
};

FlexiJS.Claims.Actions.GetServiceActionRequester = function (claimid, applicationid, action, successAction) {
    return function () {
        $.ajax({
            type: "POST",
            url: "/GMS/WebServices/GMSService.svc/" + action + "Claim",
            data: JSON.stringify({ ApplicationID: applicationid, ClaimID: claimid }),
            processData: false,
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            async: true,
            success: function (result) {
                if (result && result.d) {
                    if (result.d.Result === true) {
                        if (successAction) {
                            successAction(result.d.ResultMessage);
                        } else {
                            FlexiJS.Events.Event.FinancialInformationRefresh.Fire();
                        }
                    } else {
                        showNotificationMessage('error', result.d.ResultMessage);
                    }
                } else {
                    showNotificationMessage('error', FlexiJS.Claims.GetResourceText('GridAction_UnknownError'));
                }
                return true;
            }
        });
    };
};

FlexiJS.Claims.Actions.InitiateNewClaim = function (applicationid) {
    var action = 'Initiate';
    FlexiJS.ConfirmActions.KendoConfirmDialog(
        FlexiJS.Claims.GetResourceText("ActionConfirmation_" + action + "_Title"),
        function () {
            $.ajax({
                type: "POST",
                url: "/GMS/WebServices/GMSService.svc/" + action + "Claim",
                data: JSON.stringify({ ApplicationID: applicationid }),
                processData: false,
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                async: false,
                success: function (result) {
                    if (result && result.d) {
                        if (result.d.Result === true) {
                            //Chrome activley blocks window.open in ajax calls
                            $('[id$=btnInitiate]').parent().prepend('<a id="aTempAdd" href="' + FlexiJS.Claims.GetEngagementURL("id=" + result.d.ResultMessage) + '" target="_opener">.</a>');

                            setTimeout(function () {
                                var addedLink = $('#aTempAdd');
                                addedLink[0].click();
                                addedLink.remove();
                            }, 1);

                            FlexiJS.Events.Event.FinancialInformationRefresh.Fire();
                        } else {
                            showNotificationMessage('error', result.d.ResultMessage);
                        }
                    } else {
                        showNotificationMessage('error', FlexiJS.Claims.GetResourceText('GridAction_UnknownError'));
                    }
                    return true;
                }
            });
        },
        FlexiJS.Claims.GetResourceText("ActionConfirmation_" + action + "_Message"),
        FlexiJS.Claims.GetResourceText("ActionConfirmation_" + action + "_YesText"),
        action + "Yes_" + applicationid,
        FlexiJS.Claims.GetResourceText("ActionConfirmation_" + action + "_NoText"),
        action + "No_" + applicationid,
        false
    );
};
;FlexiJS.GMS.FundType.AuthorisationLevels = {};

FlexiJS.GMS.FundType.AuthorisationLevels.SetupAuthorisationGrid = function (rootId, fundtypeid, existingUsers, newUsers, addWebServiceName, editWebServiceName, deleteWebServiceName, infoIconText) {
    var rootElement = $('#' + rootId);
    var addUserContainer = rootElement.find('[id*=_divAddUser_]');
    if (!rootElement[0]) return;

    var gridOptions = {
        columns: [
            { field: "Name", title: "Name" },
            {
                field: "Value",
                title: "Maximum Value",
                format: "{0:c}",
                editor: function (container, options) {
                    $('<input required name="' + options.field + '"/>')
                        .appendTo(container)
                        .kendoNumericTextBox({
                            format: "c",
                            decimals: 2,
                            spinners: false,
                            min: 0,
                            max: 999999999999.999
                        });
                }
            }
        ],
        dataSource: {
            data: existingUsers,
            schema: {
                model: {
                    id: "UserId",
                    fields: {
                        FundTypeId: { editable: false, type: "number" },
                        UserId: { editable: false, type: "number" },
                        Name: { editable: false, type: "string" },
                        Value: { editable: true, type: "number", validation: { min: 1, required: true } }
                    }
                }
            },
            pageSize: 10
        },
        sortable: true,
        filterable: true,
        pageable: {
            input: true,
            numeric: false
        },
        editable: false,
        noRecords: {template: "No users have been added."}
    };

    var grid = rootElement.find('[id*=_divAuthGrid_]');
    var userSelect = null;
    var maxValueInput = null;
    
    if (addUserContainer[0]) {
        userSelect = addUserContainer.find('[id*=_selAddUser_]');
        maxValueInput = addUserContainer.find('[id*=_inpMaxValue_]');
        
        gridOptions.columns.push({ command: ["edit", "destroy"], title: "&nbsp;" });
        gridOptions.editable = { mode: "inline" };
        gridOptions.save = function (e) {
            if (e.model.Value >= 1) {
                FlexiJS.Common.Services.CallGMSService(
                    editWebServiceName,
                    { FundTypeId: e.model.FundTypeId, UserId: e.model.UserId, Value: e.model.Value },
                    function (result) {
                        if (result.d != "True") {
                            e.preventDefault();
                            showNotificationMessage('error', 'Action could not be completed at this time.');
                        }
                    },
                    function () {
                        e.preventDefault();
                        showNotificationMessage('error', 'Action could not be completed at this time.');
                    }
                );
            } else {
                e.preventDefault();
            }
        };
        gridOptions.remove = function (e) {
            console.log("Removing", e.model.name);
            FlexiJS.Common.Services.CallGMSService(
                deleteWebServiceName,
                { FundTypeId: e.model.FundTypeId, UserId: e.model.UserId },
                function (result) {
                    if (result.d != "True") {
                        e.preventDefault();
                        showNotificationMessage('error', 'Action could not be completed at this time.');
                    } else {
                        userSelect.find('option[value="' + e.model.UserId + '"]').prop('disabled', false);
                    }
                },
                function () {
                    e.preventDefault();
                    showNotificationMessage('error', 'Action could not be completed at this time.');
                }
            );
        };
    }

    grid.kendoGrid(gridOptions);

    if (!addUserContainer[0]) return;

    if (newUsers && newUsers.length > 0) {
        addUserContainer.find('.InfoIcon').prop('title', infoIconText);
        userSelect.append($('<option/>', { value: 0, text: 'Select a user to add...' }));
        $(newUsers).each(function (index, user) {
            userSelect.append($('<option/>', { value: user.Id, text: user.Name }));
        });
        if (existingUsers && existingUsers.length > 0) {
            $(existingUsers).each(function (index, user) {
                userSelect.find('option[value="' + user.UserId + '"]').prop('disabled', true);
            });
        }
        maxValueInput.kendoNumericTextBox({
            format: "c",
            decimals: 2,
            spinners: false,
            min: 0,
            max: 999999999999.999
        });
        
        addUserContainer.find('[id*=_btnAddApprover_]').click(
            {
                service: addWebServiceName,
                fundtypeid: fundtypeid,
                dropdownid: userSelect.attr('id'),
                valueinputid: maxValueInput.attr('id'),
                grid: grid.attr('id')
            },
            function (event) {
                var uid = $('#' + event.data.dropdownid).val();
                var uname = $('#' + event.data.dropdownid + ' :selected').text();
                var val = $('#' + event.data.valueinputid).val();

                if (uid > 0 && val > 0) {
                    FlexiJS.Common.Services.CallGMSService(
                        event.data.service,
                        { FundTypeId: event.data.fundtypeid, UserId: uid, Value: val },
                        function (result) {
                            if (result.d != "True") {
                                showNotificationMessage('error', 'Action could not be completed at this time.');
                            } else {
                                var kgrid = $('#' + event.data.grid).data('kendoGrid');
                                kgrid.dataSource.sync();
                                kgrid.dataSource.pushCreate({ FundTypeId: event.data.fundtypeid, UserId: uid, Name: uname, Value: val });
                                kgrid.dataSource.sync();
                                userSelect.find('option[value="' + uid + '"]').prop('disabled', true);
                                userSelect[0].selectedIndex = 0;
                                maxValueInput.data('kendoNumericTextBox').value('');
                            }
                        },
                        function () {
                            showNotificationMessage('error', 'Action could not be completed at this time.');
                        }
                    );
                } else {
                    showNotificationMessage('warning', 'You must select a user and enter a valid maximum value');
                }
            }
        );
    }
};;FlexiJS.Impersonate = new Object();
FlexiJS.Impersonate.GetServiceURL = function (method) { return '/WebServices/ImpersonateService.svc/' + method; };
FlexiJS.Impersonate.SearchPageSize = 100;

FlexiJS.Impersonate.ShowWindow = function () {
    FlexiJS.Popup.AddModalBacking('impersonate-window-part');
    $('[name="impersonate-window-part"]').on('click', function () {
        return $('.impersonate-modal').focus();
    });

    $(FlexiJS.Kendo.Templates.RenderTemplate('impersonate', 'Popup', [], true)).appendTo('body');

    var impersonateModal = $('.impersonate-modal');
    impersonateModal[0].addEventListener('keydown', FlexiJS.Impersonate.HandleWindowTabbing);
    impersonateModal.find('.close-dialog').on('click', FlexiJS.Impersonate.RemoveWindow);
    impersonateModal.attr("tabindex", -1).focus();

    $('body').css('overflow', 'hidden');

    impersonateModal.find('input[id=impersonateSearchText]').on("input", function () {
        var searchInput = $(this);
        clearTimeout(searchInput.data('timeout'));

        searchInput.data('timeout', setTimeout(function () {
            var modal = $('.impersonate-modal');
            var searchInput = modal.find('input[id=impersonateSearchText]');
            var clearButton = modal.find('button.searchClearButton');
            var selectedText = searchInput.val();

            if (selectedText.trim() !== '') {
                FlexiJS.Impersonate.Search(0);
                searchInput.addClass('clearable');
                clearButton.removeClass('hidden');
                clearButton.attr('aria-hidden', null);
            } else {
                modal.find('.results').empty();
                modal.find('.search-bar [name=summary]').remove();
                searchInput.removeClass('clearable');
                clearButton.addClass('hidden');
                clearButton.attr('aria-hidden', true);
            }
        }, 250));
    });

    impersonateModal.find('button.searchClearButton').on("click", function () {
        FlexiJS.Impersonate.ClearSearch();
    });

    FlexiJS.Impersonate.RefreshRoleGroupsList();
};

FlexiJS.Impersonate.RemoveWindow = function(e) {
    if (!$(e).closest('.close-dialog').is(':disabled')) {
        $('.impersonate-modal')[0].removeEventListener('keydown', FlexiJS.Impersonate.HandleWindowTabbing);
        $('[name="impersonate-window-part"]').remove();
        $('body').css('overflow', '');
    }
};

FlexiJS.Impersonate.ClearSearch = function() {
    const modal = $('.impersonate-modal');
    const searchInput = modal.find('input[id=impersonateSearchText]');
    const clearButton = modal.find('button.searchClearButton');

    modal.find('.results').empty();
    modal.find('.search-bar [name=summary]').remove();
    searchInput.removeClass('clearable');
    searchInput.val('');
    clearButton.addClass('hidden');
    clearButton.attr('aria-hidden', true);
};

FlexiJS.Impersonate.HandleWindowTabbing = function (e) {
    FlexiJS.Popup.HandleWindowTabbing(e, '.impersonate-modal', '.close-dialog');
};

FlexiJS.Impersonate.SetupLazyLoad = function(parent) {
    const resultsList = parent.find('.search-results-list');

    resultsList.scroll(resultsList, function (e) {
        const element = $(e.target);
        const scroller = $(element.get(0));
        const scrollerHeight = scroller[0].scrollHeight - parseInt(scroller.css('padding-bottom')) - parseInt(scroller.css('padding-top'));

        if (scrollerHeight <= element.height() + element.scrollTop()) {
            FlexiJS.Impersonate.SearchLazyLoad();
        }
    });
};

FlexiJS.Impersonate.RefreshRoleGroupsList = function(retrySearch) {
    $.ajax({
        url: FlexiJS.Impersonate.GetServiceURL('GetRoles'),
        type: "POST",
        data: {},
        contentType: "application/json",
        error: function (e) { return FlexiJS.Impersonate.HandleServiceError(+(e.status || 0), null, false); },
        success: function (data, textStatus, xhr) {
            const statusCode = +(xhr.status || 0);

            if (statusCode !== 200) {
                FlexiJS.Impersonate.HandleServiceError(statusCode);
            } else {
                if (data && data.d) {
                    const parsedData = JSON.parse(data.d);

                    const modal = $('.impersonate-modal');
                    const select = modal.find('#rolegroups');

                    modal.data('check', parsedData.Check);

                    select.data('roles', parsedData.Roles);

                    if (parsedData.Total > 1) {

                        select.empty();
                        select.append($('<option>', { value: 0, text: 'All' }));

                        $.each(parsedData.Roles, function (i, item) {
                            select.append($('<option>', { value: item.Id, text: item.Name }));
                        });

                    } else if (parsedData.Total === 1) {

                        select.empty();
                        select.append($('<option>', { value: parsedData.Roles[0].Id, text: parsedData.Roles[0].Name }));

                    } else {
                        FlexiJS.Impersonate.HandleServiceError(400);
                    }

                    select.change(function change() {
                        const modal = $('.impersonate-modal');
                        const selectedText = modal.find('input[id=impersonateSearchText]').val();

                        if (selectedText.trim() !== '') FlexiJS.Impersonate.Search(0);
                    });

                    modal.find('label[for=impersonateSearchText]').each(function (index, element) {
                        $(element).text($(element).data('loadedtext'));
                    });

                    modal.find('.search-wrapper *:disabled').prop("disabled", false);
                    modal.find('.search-bar').removeClass("disabled");

                    if (retrySearch === true) FlexiJS.Impersonate.Search(0);
                }
            }
            
        }
    });
};

FlexiJS.Impersonate.Search = function(page) {
    const modal = $('.impersonate-modal');
    const select = modal.find('#rolegroups');
    const thisSearchId = +(modal.data('latest-search-id') || 0) + 1;

    modal.data('latest-search-id', thisSearchId);

    const selectedRole = select.find(':selected').val();
    const selectedText = modal.find('input[id=impersonateSearchText]').val();

    const searchResultsArea = modal.find('.results');
    page = +page || 0;

    if (page === 0) {
        searchResultsArea.empty();
        $(FlexiJS.Kendo.Templates.RenderTemplate('impersonate', 'SearchResults-Loading', {}, true)).appendTo(searchResultsArea);
    } else {
        const resultsList = searchResultsArea.find('.search-results-list');
        $(FlexiJS.Kendo.Templates.RenderTemplate('impersonate', 'SearchResults-LoadingLazy', {}, true)).appendTo(resultsList);

        resultsList.animate({
            scrollTop: resultsList.get(0).scrollHeight - resultsList.height()
        }, 25);
    }

    $.ajax({
        url: FlexiJS.Impersonate.GetServiceURL('SearchUsers'),
        type: "POST",
        data: JSON.stringify(
            {
                SearchParameters: {
                    Text: selectedText,
                    RoleGroup: +selectedRole === 0 ? null : selectedRole,
                    PageNo: (page + 1),
                    PageSize: FlexiJS.Impersonate.SearchPageSize,
                    Check: modal.data('check')
                }
            }
        ),
        contentType: "application/json",
        error: function (e) { return FlexiJS.Impersonate.HandleServiceError(+(e.status || 0), thisSearchId, page !== 0); },
        success: function (data, textStatus, xhr) {
            const statusCode = +(xhr.status || 0);
            if (statusCode !== 200) {
                FlexiJS.Impersonate.HandleServiceError(statusCode);
            } else {
                FlexiJS.Impersonate.SearchDone(data, thisSearchId, page);
            }
        }
    });

};

FlexiJS.Impersonate.HandleServiceError = function(statusCode, searchId, isLazyLoad) {
    const modal = $('.impersonate-modal');

    //Only the most recent search should affect the ui
    if (searchId && +modal.data('latest-search-id') !== searchId) return;

    if (isLazyLoad) {
        FlexiJS.Impersonate.HandleLazyLoadServiceError(statusCode);
    } else {
        FlexiJS.Impersonate.HandleFullServiceError(statusCode);
    }


};

FlexiJS.Impersonate.HandleFullServiceError = function(statusCode) {
    var template = '';
    const modal = $('.impersonate-modal');

    switch (statusCode) {
        case 205:
            template = 'SearchResults-Error-ConfigChange';
            modal.data('latest-search-id', null);
            break;

        case 401:
            template = 'SearchResults-Error-LoggedOut';
            break;

        case 403:
            template = 'SearchResults-Error-NoPermission';
            break;

        case 0:
            template = 'SearchResults-Error-NoConnection';
            break;

        default:
            template = 'SearchResults-Error-Unexpected';
            break;
    }

    const searchResultsArea = modal.find('.results');
    modal.find('.search-wrapper *').prop("disabled", true);
    modal.find('.search-bar').addClass("disabled");
    searchResultsArea.empty();
    modal.find('.search-bar [name=summary]').remove();
    $(FlexiJS.Kendo.Templates.RenderTemplate('impersonate', template, {}, true)).appendTo(searchResultsArea);
};

FlexiJS.Impersonate.HandleLazyLoadServiceError = function(statusCode) {
    var template = '';

    switch (statusCode) {
        case 205:
            FlexiJS.Impersonate.HandleFullServiceError(statusCode);
            return;
            break;

        case 401:
            FlexiJS.Impersonate.HandleFullServiceError(statusCode);
            return;
            break;

        case 403:
            FlexiJS.Impersonate.HandleFullServiceError(statusCode);
            return;
            break;
    }

    const modal = $('.impersonate-modal');
    const searchResultsArea = modal.find('.results');

    const resultsList = searchResultsArea.find('.search-results-list');

    searchResultsArea.find('.lazyload-results').remove();

    $(FlexiJS.Kendo.Templates.RenderTemplate('impersonate', 'SearchResults-LoadingLazyError', {}, true)).appendTo(resultsList);

    resultsList.animate({
        scrollTop: resultsList.get(0).scrollHeight - resultsList.height()
    }, 25);
};

FlexiJS.Impersonate.HandleErrorRetry = function() {
    const modal = $('.impersonate-modal');
    modal.find('.close-dialog').prop("disabled", false);

    if (+(modal.data('latest-search-id') || 0) === 0) {
        modal.find('.results').empty();

        FlexiJS.Impersonate.RefreshRoleGroupsList(false);
        FlexiJS.Impersonate.ClearSearch();
    } else {
        modal.find('.results').empty();

        modal.find('.search-wrapper *').prop("disabled", false);
        modal.find('.search-bar').removeClass("disabled");

        FlexiJS.Impersonate.Search(0);
    }
};

FlexiJS.Impersonate.HandleErrorLazyRetry = function() {
    $('.impersonate-modal .results .lazyload-error').remove();
    FlexiJS.Impersonate.SearchLazyLoad();
};

FlexiJS.Impersonate.HandleErrorLoggedOut = function() {
    location.href = "/login.aspx";
};

FlexiJS.Impersonate.SearchLazyLoad = function() {
    const modal = $('.impersonate-modal');
    if ((modal.data('fullyloaded') || false) === false && modal.find('.load-results').length === 0 && modal.find('.lazyload-results').length === 0 && modal.find('.lazyload-error').length === 0) FlexiJS.Impersonate.Search(+modal.data('currentpage') + 1);
};

FlexiJS.Impersonate.SearchDone = function(data, searchId, page) {
    const modal = $('.impersonate-modal');

    //Only the most recent search should affect the ui
    if (+modal.data('latest-search-id') !== searchId) return;

    if (!data || !data.d) {
        console.log('Error: error handling in later work item');
        return;
    }

    const searchResults = JSON.parse(data.d);

    if (searchResults.Total === 0) {
        FlexiJS.Impersonate.SearchResultNoneFound(modal);
    } else {
        FlexiJS.Impersonate.SearchResultAppendResults(modal, searchResults, page);
    }

    FlexiJS.Impersonate.SeachResultSetSummary(modal, searchResults, page);
};

FlexiJS.Impersonate.SearchResultNoneFound = function(modal) {
    const searchResultsArea = modal.find('.results');
    const selectedText = modal.find('input[id=impersonateSearchText]').val();

    searchResultsArea.empty();

    $(FlexiJS.Kendo.Templates.RenderTemplate('impersonate', 'SearchResults-NoResults', { SearchTerm: selectedText }, true)).appendTo(searchResultsArea);
};

FlexiJS.Impersonate.SearchResultAppendResults = function(modal, results, page) {
    modal.data('currentpage', page);
    modal.data('fullyloaded', (page + 1) * FlexiJS.Impersonate.SearchPageSize >= results.Total);


    if (page === 0) {
        const resultsArea = modal.find('.results');
        resultsArea.empty();
        $(FlexiJS.Kendo.Templates.RenderTemplate('impersonate', 'SearchResults-List', {}, true)).appendTo(resultsArea);
        FlexiJS.Impersonate.SetupLazyLoad(resultsArea);
    }

    const searchResultsArea = modal.find('.search-results-list');

    if (page > 0) {
        searchResultsArea.find('.load-results').remove();
        searchResultsArea.find('.lazyload-results').remove();
    }

    var previousRoleGroupId = 0;
    var previousRoleGroup = null;

    const selectedText = modal.find('input[id=impersonateSearchText]').val();
    const resultMarker = new RegExp(selectedText, 'gi');

    const performHighlighting = selectedText === '';
    var highlighter = function highlighter(match, offset, string) {
        return '<mark>' + match + '</mark>';
    };

    $.each(results.Users, function(i, e) {
        if (previousRoleGroupId !== e.RoleGroupId) {
            previousRoleGroup = searchResultsArea.find('[data-rolegroupid=' + e.RoleGroupId + ']');
            previousRoleGroupId = e.RoleGroupId;

            if (previousRoleGroup.length === 0) {
                $(FlexiJS.Kendo.Templates.RenderTemplate('impersonate', 'SearchResults-RoleGroupContainer', { RoleGroupId: e.RoleGroupId, RoleGroupName: e.GroupName }, true)).appendTo(searchResultsArea);
                previousRoleGroup = searchResultsArea.find('[data-rolegroupid=' + e.RoleGroupId + ']');
            }
        }


        $(FlexiJS.Kendo.Templates.RenderTemplate(
            'impersonate',
            'User',
            {
                initials: e.Initials,
                name: performHighlighting ? e.FullName : e.FullName.replace(resultMarker, highlighter),
                email: performHighlighting ? e.Email : e.Email.replace(resultMarker, highlighter),
                id: e.UserId,
                profilePicURL: e.ProfilePic
            },
            true
        )).appendTo(previousRoleGroup);
    });
};

FlexiJS.Impersonate.SeachResultSetSummary = function(modal, results, page) {
    const searchBar = modal.find('.search-bar');
    var summary = searchBar.find('[name=summary]');

    if (summary.length === 0) {
        $(FlexiJS.Kendo.Templates.RenderTemplate('impersonate', 'SearchResults-SummaryInfo', {}, true)).appendTo(searchBar);
        summary = searchBar.find('[name=summary]');
    }

    const maxPage = Math.min(FlexiJS.Impersonate.SearchPageSize * (1 + (+modal.data('currentpage')) || 0), results.Total);

    const select = modal.find('#rolegroups');
    const selectedRole = select.find(':selected').val();

    const searchText = modal.find('input[id=impersonateSearchText]').val().trim();
    var searchSummary = '';
    if (searchText === '') {
        searchSummary = select.find(':selected').text().trim();
    } else if (+selectedRole !== 0) {
        searchSummary = searchText + ' (' + select.find(':selected').text().trim() + ')';
    } else {
        searchSummary = searchText;
    }

    summary.html(summary.data('prefix') + (maxPage === 0 ? '<span class="result-number">0</span>' : ('<span class="result-number">' + maxPage + '</span>' + summary.data('seperator') + '<span class="result-number">' + results.Total + '</span>')) + summary.data('postfix') + ' ' + searchSummary);
};

FlexiJS.Impersonate.Start = function(e, id) {
    if (e.type === 'click' || e.key === 'Enter') {
        var ref = e.target != null ? e.target : e.srcElement;
        if (ref) {
            const modal = $('.impersonate-modal');
            const searchList = modal.find('.search-results-list');
            const article = $(ref).closest('article');
            const confirmText = FlexiJS.Resources.GetResourceText("Impersonate", "Start.ConfirmMessage") + article.find('[name=name]').text() + ' (' + article.find('[name=email]').text() + ')?';

            if (!article.hasClass('disabled') && !searchList.hasClass('disabled') && confirm(confirmText)) {
                modal.find('.search-wrapper *').prop("disabled", true);
                modal.find('.search-bar').addClass("disabled");
                modal.find('.close-dialog').prop("disabled", true);
                searchList.addClass("disabled");

                $.ajax({
                    url: FlexiJS.Impersonate.GetServiceURL('StartImpersonation'),
                    type: "POST",
                    data: JSON.stringify({ UserId: id }),
                    contentType: "application/json",
                    error: function(e) {
                        FlexiJS.Impersonate.HandleFullServiceError(+(e.status || 0));
                    },
                    success: function(data, textStatus, xhr) {
                        const statusCode = +(xhr.status || 0);
                        if (statusCode !== 200) {
                            FlexiJS.Impersonate.HandleFullServiceError(statusCode);
                        } else {
                            location.reload();
                        }
                    }
                });
            }
        }
    }
};

FlexiJS.Impersonate.HandleStartServiceError = function(statusCode, ref) {
    var template = '';

    switch (statusCode) {
        case 401:
            FlexiJS.Impersonate.HandleFullServiceError(statusCode);
            return;
            break;

        case 403:
            FlexiJS.Impersonate.HandleFullServiceError(statusCode);
            return;
            break;
    }

    const modal = $('.impersonate-modal');
    const resultsList = modal.find('.search-results-list');

    resultsList.removeClass("disabled");
    $(ref).closest('article').addClass('disabled');
    modal.find('.search-wrapper *:disabled').prop("disabled", false);
    modal.find('.search-bar').removeClass("disabled");
    modal.find('.close-dialog').prop("disabled", false);

    FlexiJS.Impersonate.ShowErrorToast();
};

FlexiJS.Impersonate.Finish = function() {
    $.ajax({
        url: FlexiJS.Impersonate.GetServiceURL('EndImpersonation'),
        type: "POST",
        data: null,
        contentType: "application/json",
        error: function error(e) {
            console.log('Error in select, error handling in later work item');
        },
        success: function success(data) {
            location.reload();
        }
    });
};

FlexiJS.Impersonate.ClearNotification = function(e) {
    if (e.type === 'click' || e.key === 'Enter') {
        var ref = e.target != null ? e.target : e.srcElement;
        if (ref) $(ref).closest('.warning-toast').remove();
    }
};

FlexiJS.Impersonate.RefreshWindow = function() {
    const modal = $('.impersonate-modal');
    modal.find('.results').empty();
    modal.find('.warning-toast').remove();
    modal.find('.search-wrapper *').prop("disabled", true);
    modal.find('.search-bar').addClass("disabled");

    FlexiJS.Impersonate.RefreshRoleGroupsList(true);
};

FlexiJS.Impersonate.ShowErrorToast = function() {
    const modal = $('.impersonate-modal');

    if (modal.find('.warning-toast').length === 0) $(FlexiJS.Kendo.Templates.RenderTemplate('impersonate', 'SearchResults-Error-Notification', {}, true)).appendTo(modal);
};;/*
    _    ___                                    _____          _                          _                       
   (_)  / _ \   _   _    ___   _ __   _   _    | ____| __  __ | |_    ___   _ __    ___  (_)   ___    _ __    ___ 
   | | | | | | | | | |  / _ \ | '__| | | | |   |  _|   \ \/ / | __|  / _ \ | '_ \  / __| | |  / _ \  | '_ \  / __|
   | | | |_| | | |_| | |  __/ | |    | |_| |   | |___   >  <  | |_  |  __/ | | | | \__ \ | | | (_) | | | | | \__ \
  _/ |  \__\_\  \__,_|  \___| |_|     \__, |   |_____| /_/\_\  \__|  \___| |_| |_| |___/ |_|  \___/  |_| |_| |___/
 |__/                                 |___/                                                                       

        Extensions to the jQuery object
*/

(function ($) {
    $.QueryString = (function (a) {
        /*
        From http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
        USAGE

        //Get a param
        $.QueryString.param
        //-or-
        $.QueryString["param"]
        //This outputs something like...
        //"val"

        //Get all params as object
        $.QueryString
        //This outputs something like...
        //Object { param: "val", param2: "val" }

        //Set a param (only in the $.QueryString object, doesn't affect the browser's querystring)
        $.QueryString.param = "newvalue"
        //This doesn't output anything, it just updates the $.QueryString object

        //Convert object into string suitable for url a querystring (Requires jQuery)
        $.param($.QueryString)
        //This outputs something like...
        //"param=newvalue&param2=val"

        //Update the url/querystring in the browser's location bar with the $.QueryString object
        history.replaceState({}, '', "?" + $.param($.QueryString));
        //-or-
        history.pushState({}, '', "?" + $.param($.QueryString));
        */
        if (a == "") return {};
        var b = {};
        for (var i = 0; i < a.length; ++i) {
            var p = a[i].split('=', 2);
            if (p.length != 2) continue;
            b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
        }
        return b;
    })(window.location.search.substr(1).split('&'))
})(jQuery);

//Adds a sum function to jquery
$.sum = function (arr) {
    var r = 0.0;
    $.each(arr, function (i, v) {
        if (parseFloat(v) != 0) {
            if (r != 0) {
                r += parseFloat(v);
            } else {
                r = parseFloat(v);
            }
        }
    });
    return r;
};
;/*
  _____   _                 _       _   ____        ____            __                   _   _         
 |  ___| | |   ___  __  __ (_)     | | / ___|      |  _ \    ___   / _|   __ _   _   _  | | | |_   ___ 
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | | | |  / _ \ | |_   / _` | | | | | | | | __| / __|
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |_| | |  __/ |  _| | (_| | | |_| | | | | |_  \__ \
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |____/   \___| |_|    \__,_|  \__,_| |_|  \__| |___/
                                                                                                       
*/

FlexiJS.Kendo = {};
FlexiJS.Kendo.Dialogs = {};
FlexiJS.Kendo.Editors = {};
FlexiJS.Kendo.DatePickers = {};
FlexiJS.Kendo.MultiSelects = {};
FlexiJS.Kendo.NumberInputs = {};
FlexiJS.Kendo.Grids = {};

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/


var maxDate = new Date(2399,11,31,23,59,59);
var minDate = new Date(1755,0,1,0,0,0);//Minimum SQL Datetime - See SqlDateTime.MinValue
kendo.ui.DatePicker.fn.options.max = maxDate;
kendo.ui.DatePicker.fn.options.min = minDate;
kendo.ui.DateTimePicker.fn.options.max = maxDate;
kendo.ui.DateTimePicker.fn.options.min = minDate;

/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               
    
    Move from behaviours.js 10/07/2017
*/


FlexiJS.Kendo.GetResourceText = function (key) {
    return FlexiJS.Resources.GetResourceText('Kendo', key);
};

FlexiJS.Kendo.SetCultureCurrencyFormat = function(tenantDefaultCulture, userCulture, symbol, pattern, decimalPlaces){

    if (kendo.cultures && kendo.cultures.hasOwnProperty(tenantDefaultCulture)) {
        if (symbol !== null) kendo.cultures[tenantDefaultCulture].numberFormat.currency.symbol = symbol;
        if (pattern !== null) kendo.cultures[tenantDefaultCulture].numberFormat.currency.pattern = pattern;
        if (decimalPlaces !== null) kendo.cultures[tenantDefaultCulture].numberFormat.currency.decimals = decimalPlaces;

        if (tenantDefaultCulture !== userCulture && kendo.cultures.hasOwnProperty(userCulture)) {
            kendo.cultures[userCulture].numberFormat.currency.symbol = kendo.cultures[tenantDefaultCulture].numberFormat.currency.symbol;
            kendo.cultures[userCulture].numberFormat.currency.pattern = kendo.cultures[tenantDefaultCulture].numberFormat.currency.pattern;
            kendo.cultures[userCulture].numberFormat.currency.decimals = kendo.cultures[tenantDefaultCulture].numberFormat.currency.decimals;
        }
    }
};

// Easy Dialog Controls
FlexiJS.Kendo.Dialogs.Confirm = function (titletext, paragraphs, actionButtons, fullscreen, styleVersion) {
    // titletext - The window title to show. If false is passed title and title commands will be hidden
    // paragraphs - string array containing text to display in window, each item in the array will be dispayed as a seperat paragraph with its own spacing
    // actionbuttons - object array - {text, actionfunction}    - text              - the button text to show
    //                                                          - actionfunction    - pass the function you want to call on buttons click event
    //                                                                                  e.g. {text:'Confirm', actionfunction: function(){ /*ACTIONS HERE*/ }}
    //                                                                                OPTIONAL - if no function is set nothing will be called
    //                                                                                  e.g. {text:'Cancel'}
    // window is autoclosed after buttons actionfunction is called (if passed, otherwise window will just close)
    //  an exception to this is if the actionfunction returns a false - so it can be used to do validation without closing
    // styleVersion - If not defined will default to 1 which is standard, 2 means the cancel button is not rendered and the cssClass is altered for the primary button
    var fullscreen = fullscreen == null ? false : fullscreen;
    var styleVersion = styleVersion == null ? 1 : styleVersion;
    if (fullscreen) {
        paragraphs.splice(0, 0, "<div class='gms-modal-header height-auto'><div><h1>" + titletext + "</h1></div><div><button aria-label='Close' class='btn-ico close' onclick='FlexiJS.Kendo.Dialogs.ConfirmWindowClose();'><i aria-hidden='true' class='material-icons'>&#xE5CD;</i></button></div></div>");
        titletext = false;
    }
    var kendoWindow = $("<div id='windowConfirm' />").kendoWindow({
        title: titletext, visible: false, resizable: false, modal: true, activate: function (e) {
            FlexiJS.Kendo.Dialogs.ResizeGrid(fullscreen, '#windowConfirm')
        }, resize: function (e) { FlexiJS.Kendo.Dialogs.ResizeGrid(fullscreen, '#windowConfirm') }, close: function (e) { this.destroy(); $('body').removeAttr('style'); }
    });

    kendoWindow.data("kendoWindow").content('<div class="kendoTenantFont"></div>');

    var kendoWindowContent = kendoWindow.find('.kendoTenantFont');

    if (paragraphs) {
        if (Array.isArray(paragraphs)) {
            $.each(paragraphs, function (index, paragraph) {
                $(kendoWindowContent).append('' + paragraph + '');
            });
        }
        else {
            $(kendoWindowContent).append('' + paragraphs + '');
        }
    }

    if (actionButtons) {
        $(kendoWindowContent).append('<div class="fx-flexiKendoConfirm-ButtonsContainer horizontal-buttons fx-m-t-10"></div>');

        var buttonkendoWindowContentButtonsContainer;

        if (fullscreen)
        {
            $('.bulk-tool-wrapper').append('<div class="toolbar-buttons clearfix"></div>');
            buttonkendoWindowContentButtonsContainer = $(kendoWindowContent).find('.toolbar-buttons');
        }
        else
        {
            buttonkendoWindowContentButtonsContainer = $(kendoWindowContent).find('.fx-flexiKendoConfirm-ButtonsContainer');
        }


        $.each(actionButtons, function (index, actionbutton) {

            // Don't render cancel button when in fullscreen mode
            if ((fullscreen || styleVersion == 2)  && actionbutton.cancel != null && actionbutton.cancel) {
                return;
            }

            var buttonClass = '';

            if (actionbutton.primary === true) {
                buttonClass += styleVersion == 2 ? ' modal-action' : ' primary-button';
            }

            if (actionbutton.secondary === true) {
                buttonClass += ' secondary';
            }

            if (actionbutton.bulkAction === true) {
                buttonClass += ' bulk-action-button';
            }

            // TODO Chris to sort
            if (fullscreen) {
                buttonClass += ' float-right';
            }

            if (actionbutton.class && actionbutton.class != '') {
                buttonClass += actionbutton.class;
            }

            $('<button class="fx-btn btn-horizontal-space half-margin-left' + buttonClass + '" id=>' + actionbutton.text + '</button>').appendTo(buttonkendoWindowContentButtonsContainer).click(function ()
            {
                var closeWindow = true;
                if ($.isFunction(actionbutton.actionfunction)) {
                    var result = actionbutton.actionfunction();
                    if (result != null && result == false) {
                        closeWindow = false;
                    }
                }
                if (closeWindow) {
                    FlexiJS.Kendo.Dialogs.ConfirmWindowClose();
                }
            });
        });
    }

    /*disable body scrolling*/
    $('body').css('overflow', 'hidden');

    if (fullscreen) {
        kendoWindow.data("kendoWindow").maximize().open();
    }
    else {
        kendoWindow.data("kendoWindow").center().open();
    }

    FlexiJS.Utils.ManageButtonSpinner('hide', null);
};

FlexiJS.Kendo.Dialogs.ResizeGrid = function (fullScreen, element) {
    if (fullScreen) {

        var totalHeight = 0;
        var toolbarHeight = 0;

        /*reset content height to zero to establish space. Then get window height*/
        $(element + " .k-grid-content").css('max-height', totalHeight);
        $(element + " .k-grid-content-locked").css('max-height', totalHeight);

        var windowHeight = $(element).innerHeight();

        /*get content height*/
        if ($(element + " .bulk-tool-wrapper").length > 0) {
            toolbarHeight = $(element + " .bulk-tool-wrapper").outerHeight(true);
        }

        totalHeight = $(element + ' .kendoTenantFont').outerHeight(true) + toolbarHeight;

        //calc available height
        var maxHeight = windowHeight - totalHeight;

        /*set height on content and locked cells */
        $(element + " .k-grid-content").css('max-height', maxHeight);

        var hasHorizontalScrollbar = $(element + " .k-grid-content table").width() > $(element + " .k-grid-content").width();

        /*if we have a horizontal scrollbar, we need to adjust the max-height of locked columns*/
        if (hasHorizontalScrollbar)
        {
            $(element + " .k-grid-content-locked").css('max-height', maxHeight - 20);
        }
        else
        {
            $(element + " .k-grid-content-locked").css('max-height', maxHeight);
        }
    }

};

FlexiJS.Kendo.Dialogs.ResizeFullScreenPopupContent = function (element)
{

        /*get window height*/
        var windowHeight = $(element).innerHeight();

        var contentHeight = 0;
        var totalHeight = 0;

        /*get content height*/
        contentHeight = $(element + " .gms-modal-header").outerHeight(true);

        totalHeight = windowHeight - contentHeight;

        /*reset content height to 0 

        /*set height*/
        $(element + " .modal-content").css('height', totalHeight - 40); /*margin*/

};

FlexiJS.Kendo.Dialogs.ConfirmWindowClose = function()
{
    /*enable body scrolling*/
    $(document).ready(function () {
        $('body').removeAttr('style');
    });

    // Clear the RollbackBatchId if there is a grid stored at FlexiJS.GMS.QuickAction.CurrentGrid
    // and the PopupRollback button is present
    if (FlexiJS.GMS.QuickAction.CurrentGrid && $('#btnPopupRollback').length > 0)
    {
        FlexiJS.Controls.DashboardGrid.GridProperties[FlexiJS.GMS.QuickAction.CurrentGrid.GridPropertyId].RollbackBatchId = null;
    }

    // We check it there is a close callback to take into consideration
    var settings = $('#bulkActionGrid').data("settings");
    if (settings && settings.closeCallback)
    {
        settings.closeCallback();
    }

    $('#windowConfirm').data("kendoWindow").close();
};


/* FlexiJS Kendo Editor Helper - encoding ampersands */
FlexiJS.Kendo.Editors.SetupKendoEditorMinimal = function () {
    var editorMinTools = ['formatting', 'bold', 'italic', 'underline', 'foreColor', 'justifyLeft', 'justifyCenter', 'justifyRight', 'insertOrderedList', 'indent', 'outdent', 'viewHtml', 'createLink', 'unlink']
    $('.kendo-editor-minimal').kendoEditor({
        tools: editorMinTools,
        execute: function (e) {
            if (e.name == "createlink") {
                setTimeout(function () {
                    $("#k-editor-link-url").change(function (e) {
                        // replacing ampersand sign with %26
                        e.currentTarget.value = e.currentTarget.value.replace("&", "%26");
                    });
                }, 0);
            }
        }
    });
};

FlexiJS.Kendo.Editors.SetupKendoEditorRtf = function () {
    // See http://docs.telerik.com/kendo-ui/api/javascript/ui/editor#configuration-tools for full list of tools
    var tools = ['bold', 'italic', 'underline',
        {
            name: "subscript",
            exec: function (e) {
                var editor = $(this).data("kendoEditor");
                if (editor.state("subscript") == false && editor.state("superscript") == true) {
                    editor.exec('superscript');
                }
            }
        },
        {
            name: "superscript",
            exec: function (e) {
                var editor = $(this).data("kendoEditor");
                if (editor.state("superscript") == false && editor.state("subscript") == true) {
                    editor.exec('subscript');
                }
            }
        },
        'insertUnorderedList', 'insertOrderedList', 'indent', 'outdent', 'createLink', 'unlink', 'cleanFormatting']

    $('.kendo-editor-rtf[requires-setup="true"]').kendoEditor({
        tools: tools,
        pasteCleanup: {
            msAllFormatting: true,
            msConvertLists: true,
            msTags: true
        }

    });

    $('.kendo-editor-rtf[requires-setup="true"]').removeAttr("requires-setup");
};

FlexiJS.Kendo.Editors.RefreshKendoEditor = function (clientID) {
    var editor = $('#' + clientID).data("kendoEditor");
    if (editor) editor.refresh();
};

FlexiJS.Kendo.DatePickers.SetupKendoDateTimePicker = function (clientID, epochTime, alternateDate, format) {
    var dtDate;

    if (alternateDate != null) {
        dtDate = alternateDate;
    }
    else {
        if (epochTime) {
            dtDate = FlexiJS.Dates.ConvertEpochTime(epochTime);
        }
    }

    var settings = {
        value: dtDate
    };

    if (format != null) {
        settings.format = format;
    };

    $('#' + clientID).kendoDateTimePicker(settings);
};

FlexiJS.Kendo.DatePickers.SetupKendoDatePicker = function (clientID, epochTime, alternateDate, format) {
    var dtDate;

    if (alternateDate != null) {
        dtDate = alternateDate;
    }
    else {
        if (epochTime) {
            dtDate = FlexiJS.Dates.ConvertEpochTime(epochTime);
        }
    }
    var settings = {
        value: dtDate
    };

    if (format != null) {
        settings.format = format;
    };

    $('#' + clientID).kendoDatePicker(settings);
};

FlexiJS.Kendo.MultiSelects.SetupKendoMultiSelect = function (elementid, options) {
    var datasource = {};
    var onchange = function (e) {};
    var placeholdertext = '';
    var valuefieldid = '';
    var textfieldid = '';
    var width = '100%';

    if (options) {
        datasource.sort = [];

        //Setup schema
        if (options.valuefieldid) {
            valuefieldid = options.valuefieldid;
            datasource.schema = { model: { id: options.valuefieldid } };
        } else {
            //No value field id, don't attempt to create
            return false;
        }
        if (options.textfieldid) {
            textfieldid = options.textfieldid;
        } else {
            //No text field id, use value field
            textfieldid = options.valuefieldid;
        }
        if (options.datasourcefields) {
            datasource.schema.model.fields = options.datasourcefields;
        }

        //Set the data object, either from a hidden field with a JSON string OR a directly passed variable
        if (options.datasourceelementid) {
            datasource.data = JSON.parse($('#' + options.datasourceelementid).val());
        } else if (options.data) {
            datasource.data = options.data;
        } else {
            //No data, don't attempt to create
            return false;
        }

        //Set grouping, if passed
        if (options.groupedfieldid) {
            datasource.group = { field: options.groupedfieldid };
            //Sort by the grouped field first
            datasource.sort.push({ field: options.groupedfieldid, dir: "asc" });
        }

        if (options.placeholdertext) {
            placeholdertext = options.placeholdertext;
        }
        if (options.onchange) {
            onchange = options.onchange;
        }
        if (options.width) {
            width = options.width;
        }

        //Sort by the display field
        datasource.sort.push({ field: textfieldid, dir: "asc" });
    }

    var input = $('#' + elementid);

    if (!input.hasClass('flexikendo-groupedmultiselect')) {
        input.addClass('flexikendo-groupedmultiselect');
    }
    input.css('width', width);
    input.kendoMultiSelect({
        valuePrimitive: true,
        itemTemplate: "<input id='#:data." + options.textfieldid + "#' type='checkbox'/><label class='multiselect-item' for='#:data." + options.textfieldid + "#'>#:data." + options.textfieldid + "#</label>",
        groupTemplate: "<input id='#:data#' type='checkbox'/><label for='#:data#'>#:data#</label>",
        autoClose: false,
        dataSource: datasource,
        dataBound: function (e) {

            //Add in missing group checkbox for first grant schemes as Kendo doesnt display it for the first one
            var firstLi = this.ul.find('li:first');
            firstLi.addClass("inline-block").addClass("no-padding");
            firstLi.css("width", "100%");
            var item = firstLi.find(".k-list-item-text");
            $(item).css("margin-top", "4px");
            item.addClass("inline-block");
            var fundName = this.dataSource._data[0][this.dataSource.options.group.field];
            $(firstLi).prepend('<div class="k-list-item-group-label multiselect-group-header no-margin-top"><input id="' + fundName + '" type="checkbox"/><label for="' + fundName + '">' + fundName + '</label></div>');


            var allLis = this.ul.find("li");
            allLis.each(function () {
                //$(this).addClass("no-padding");
            });

            var allItemlabels = this.ul.find("label.multiselect-item");
            allItemlabels.each(function () {
                $(this).unbind('click');
                $(this).bind('click', FlexiJS.Kendo.MultiSelects.StopLabelClick);
            });

            var firstLis = this.ul.find("li.k-first");
            firstLis.each(function () {
                var firstLi = $(this);
                var group = firstLi.find(".k-list-item-group-label");
                var item = firstLi.find(".k-list-item-text");
                if (group.length > 0) {
                    $(item).css("margin-top", "2px");
                }
                firstLi.addClass("multiselect-first").addClass("no-padding");
                firstLi.css("width", "100%");
                group.addClass("multiselect-group-header");
                group.insertBefore(item);
            });

            setTimeout(function () {
                FlexiJS.Kendo.MultiSelects.SelectChildren(allLis);
            });
        },
        dataTextField: textfieldid,
        dataValueField: valuefieldid,
        placeholder: placeholdertext,
        highlightFirst: false,
        height: 500,
        change: function (e) {
            onchange(e);
            var elements = this.ul.find("li");
            setTimeout(function () {
                FlexiJS.Kendo.MultiSelects.SelectChildren(elements);
            });
        }
    });

    var ms = $('#' + elementid).data("kendoMultiSelect");
    ms.value($('[id$=' + options.hiddenSelectedId + ']').val().split(","), true);

    $(".k-list-scroller").delegate(".multiselect-group-header", "click", { elementId: elementid, hiddenSelectedId: options.hiddenSelectedId }, FlexiJS.Kendo.MultiSelects.GroupClick);
};

FlexiJS.Kendo.MultiSelects.StopLabelClick = function (e) {
    e.stopPropagation();
};

FlexiJS.Kendo.MultiSelects.GroupClick = function (e) {
    var ms = $('#' + e.data.elementId).data("kendoMultiSelect");
    var previousSelections = ms.value();
    var isChecked = $(this).find('input')[0].checked;

    var data = ms.dataSource.data();
    for (var group of data) {
        if (group.GMSFundName === $.trim(this.textContent)) {
            if (isChecked) {
                previousSelections.push(group.GMSFundTypeID);
            }
            else {
                previousSelections = previousSelections.filter(function (item) {
                    return item !== group.GMSFundTypeID;
                });
            }
        }
    }

    ms.value(previousSelections);

    $('[id$=' + e.data.hiddenSelectedId + ']').val(ms.value());

    var items = ms.ul.find("li");
    FlexiJS.Kendo.MultiSelects.SelectChildren(items);
}

FlexiJS.Kendo.MultiSelects.SelectChildren = function (elements) {
    elements.each(function () {
        var element = $(this);
        var input = element.find("input");

        input.prop("checked", element.hasClass("k-selected"));
    });
};


FlexiJS.Kendo.NumberInputs.ChangeExistingFormat = function (existingInput, format) {
    existingInput.data("kendoNumericTextBox").setOptions({ format: format });
    //Timeout needed to refresh the input otherwise it wont change format
    setTimeout(function () {
        existingInput.data("kendoNumericTextBox").value(existingInput.data("kendoNumericTextBox").value());
        existingInput.data("kendoNumericTextBox").trigger("change");
    }, 0);
};

//Kendo Grid Helper
FlexiJS.Kendo.Grids.GetFieldFilterValue = function (options, fieldname) {
    if (!options || !options.filter || !options.filter.filters) return null;
    return options.filter.filters.filter(function (f) { return f.field == fieldname; }).map(function (m) { return m.value; })[0] || null;
};

FlexiJS.Kendo.Grids.SetupKendoGrid = function (gridSettings) {

    var toolBarItems = '<a class="k-button k-button-icontext k-grid-excel" href="##"><span class="k-icon k-i-excel"></span>Export to Excel</a>';
    if (gridSettings.EnableColumnSelector) {
        toolBarItems = toolBarItems + '<div class="margin-left-auto"><div id ="columnChooser" class="my-columns k-button"></div></div>';
    }

    var grid = $('#' + gridSettings.GridId).kendoGrid({
        toolbar: [{ template: toolBarItems }],
        excel: {
            allPages: true
        },
        excelExport: function (e) {
            e.workbook.fileName = gridSettings.ExcelFileName + '_' + kendo.toString(new Date, "dd_MM_yyyy_HH_mm") + ".xlsx";
            var sheet = e.workbook.sheets[0];
            var data = e.data;
            var gridColumns = grid.columns;
            var columns = gridColumns.map(function (col) {
                if (col.field != undefined) {
                    return {
                        value: col.title ? col.title : col.field,
                        autoWidth: true,
                        background: "#7a7a7a",
                        color: "#fff"
                    };
                }
            }).filter(Boolean);

            var rows = [{ cells: columns, type: "header" }];

            for (var i = 0; i < data.length; i++) {
                if (typeof data[i].items != 'undefined')
                {
                    for (var rowIndex = 0; rowIndex < data[i].items.length; rowIndex++) {
                        var groupItems = data[i].items[rowIndex];
                        FlexiJS.Kendo.Grids.GetExportColumnsData(gridColumns, groupItems, rows);
                    }
                }
                else
                {
                    FlexiJS.Kendo.Grids.GetExportColumnsData(gridColumns, data[i], rows);
                }
            }

            if (FlexiJS.GMS.Application.Payments.Constants._totalPaymentsValue) {
                var footerCells = [];
                footerCells.push({ value: FlexiJS.Resources.GetResourceText(FlexiJS.GMS.Application.Payments.ResourceSet, 'PaymentGrid.FooterText') });
                footerCells.push({ value: kendo.toString(FlexiJS.GMS.Application.Payments.Constants._totalPaymentsValue, 'c') });
                rows.push({ cells: footerCells, type: "footer" });
            }

            sheet.rows = rows;
        },
        dataSource: gridSettings.GridDatasource,
        dataBinding: gridSettings.OnDataBindingFunction,
        columns: gridSettings.GridColumns,
        filterable: { mode: "row" },
        sortable: gridSettings.EnableSorting,
        groupable:
        {
            enabled: gridSettings.EnableGrouping,
            messages: {
                empty: FlexiJS.Resources.GetResourceText('KendoGrid', 'GroupingMessage')
            }
        },
        pageable: FlexiJS.Kendo.Grids.GetPagableOptions(),
        dataBound: gridSettings.DataBoundFunction,
        noRecords: { template: FlexiJS.Resources.GetResourceText('KendoGrid', 'NoRecordsMessage') }
    }).data("kendoGrid");

    if (gridSettings.EnableColumnSelector) {
        FlexiJS.Kendo.Grids.SetupColumnSelector(grid, gridSettings.SaveGridHiddenColumns, gridSettings.SaveGridHiddenColumnsUserSetting);
    }

    //Adds loader to grid when exporting
    $(function () {
        $('#' + gridSettings.GridId + " .k-grid-excel").on("click", function (e) {
            kendo.ui.progress(grid.element, true);
        });
    });

};

FlexiJS.Kendo.Grids.SetupColumnSelector = function (grid, saveGridHiddenColumns, saveGridHiddenColumnsUserSetting) {

    if (grid)
    {

        var visibleColumns = grid.columns.map(function (col) {
            if (!col.hidden && col.field != undefined) {
                return { value: col.title, operator: "eq", field: "title" }
            }
        }).filter(Boolean);

        var chooserDs = new kendo.data.DataSource({
            data: grid.columns,
            filter: {
                filters: visibleColumns,
                logic: "or"
            }
        });

        $("#columnChooser").kendoFilterMultiCheck({
            field: "title",
            dataSource: chooserDs,
            search: true,
            checkAll: false,
            messages: {
                filter: FlexiJS.Resources.GetResourceText('KendoGrid', 'ColumnSelectorSaveButtonText'),
                selectedItemsFormat: "{0} columns visible"
            },
            init: function (e) {
                var checkboxes = e.sender.container.find("input[type=checkbox]");
                var applyButton = e.sender.form.find("button[type=submit]");
                checkboxes.change(function () {
                    if (checkboxes.filter(":checked").length == 0) {
                        applyButton.attr("disabled", true);
                        applyButton.addClass("aspNetDisabled");
                    }
                    else {
                        applyButton.removeAttr("disabled");
                        applyButton.removeClass("aspNetDisabled");
                    }
                });
            },
            refresh: function (e) {
                if (e.sender.dataSource.filter()) {
                    var columnsToShow = e.sender.getFilterArray();
                    var usersHiddenColumns = [];
                    $.each(grid.columns, function (i, col) {
                        if (columnsToShow.indexOf(col.title) > -1) {
                            grid.showColumn(col.field);
                        } else {
                            if (col.field != undefined) {
                                grid.hideColumn(col.field);
                                usersHiddenColumns.push(col.field);
                            }
                        }
                    });

                    if (saveGridHiddenColumns) {
                        if (usersHiddenColumns.length == 0) {
                            //Check to see if user has decided to choose all columns
                            //If so we need to save "None" in the database as the users choice
                            var gridColumnsNowShowing = grid.columns.filter(function (e) { return e.hidden == false }).length;
                            if (gridColumnsNowShowing == columnsToShow.length) {
                                usersHiddenColumns.push("None");
                            }
                        }
                        FlexiJS.Common.SaveUserSetting(saveGridHiddenColumnsUserSetting, kendo.stringify(usersHiddenColumns));
                    }

                    FlexiJS.Kendo.Grids.ResizeGrid(grid.element[0].id);
                }
            }
        });

        //Set tooltip 
        var columnbuttonText = FlexiJS.Resources.GetResourceText('KendoGrid', 'ColumnSelectorTitle');
        $("#columnChooser a.k-grid-filter").append("<i class='fal fa-cog'></i><span>" + columnbuttonText + "</span><i class='fas fa-caret-down'></i>");

    }
};

FlexiJS.Kendo.Grids.ReloadGrid = function (gridContainer) {
    var grid = $('#' + gridContainer).data('kendoGrid');
    if (grid != null) {
       grid.dataSource.read();
    }
};

FlexiJS.Kendo.Grids.DestroyGrid = function (gridContainer) {
    var grid = $('#' + gridContainer).data('kendoGrid');
    if (grid != null) {
        grid.wrapper.empty();
        grid.destroy();
    }
};

FlexiJS.Kendo.Grids.ResizeGrid = function (gridContainer) {
    setTimeout(function () {

        var grid = $('#' + gridContainer).data('kendoGrid');
        $('#' + gridContainer).find(".k-group-col,.k-group-cell").remove();
        var lockedContent = grid.wrapper.children(".k-grid-content-locked");
        var content = grid.wrapper.children(".k-grid-content");

        grid.wrapper.height("");
        lockedContent.height("");
        content.height("");
       
        //This removes the calculated width applied by Kendo when the grid
        //is resized, as less columns may now be displayed and it needs to use 100% of the container
        grid.element.find(".k-grid-header-wrap table").width("");
        grid.element.find(".k-grid-content table").width("");
       
    });
};

FlexiJS.Kendo.Grids.CellTemplateFilterOnEnterKey = function (args) {
    args.element.keydown(function (e) {
        if (e.keyCode === 13) {
            e.preventDefault();
            $(e.target).trigger("change");
        }
    });
};

FlexiJS.Kendo.Grids.GetExportColumnsData = function (gridColumns, gridData, rows) {
    var rowCells = [];
    for (var j = 0; j < gridColumns.length; j++) {
        if (typeof gridColumns[j].field != 'undefined') {
            var cellValue = gridData[gridColumns[j].field];
            var cell = { value: cellValue };
            if (("" + cellValue).indexOf('\n') >= 0) cell.wrap = true;
            rowCells.push(cell);
        }
    }
    rows.push({ cells: rowCells, type: "data" });
};

FlexiJS.Kendo.Grids.GetPagableOptions = function () {
    return {
        refresh: true,
        pageSizes: [10, 20, 50, 100, 250],
        messages: {
            allPages: FlexiJS.Kendo.GetResourceText('DataGrid.Paging.allPages'),
            display: FlexiJS.Kendo.GetResourceText('DataGrid.Paging.display'),
            empty: FlexiJS.Kendo.GetResourceText('DataGrid.Paging.empty'),
            first: FlexiJS.Kendo.GetResourceText('DataGrid.Paging.first'),
            itemsPerPage: FlexiJS.Kendo.GetResourceText('DataGrid.Paging.itemsPerPage'),
            last: FlexiJS.Kendo.GetResourceText('DataGrid.Paging.last'),
            morePages: FlexiJS.Kendo.GetResourceText('DataGrid.Paging.morePages'),
            next: FlexiJS.Kendo.GetResourceText('DataGrid.Paging.next'),
            of: FlexiJS.Kendo.GetResourceText('DataGrid.Paging.of'),
            page: FlexiJS.Kendo.GetResourceText('DataGrid.Paging.page'),
            previous: FlexiJS.Kendo.GetResourceText('DataGrid.Paging.previous'),
            refresh: FlexiJS.Kendo.GetResourceText('DataGrid.Paging.refresh')
        }
    }
};;/*

  _____   _                 _       _   ____        _  __                     _               __  __           _   _     _   ____           _                 _               ____                           ____                               _       _         _   
 |  ___| | |   ___  __  __ (_)     | | / ___|      | |/ /   ___   _ __     __| |   ___       |  \/  |  _   _  | | | |_  (_) / ___|    ___  | |   ___    ___  | |_   ___      |  _ \   _ __    ___    _ __   |  _ \    ___   __      __  _ __   | |     (_)  ___  | |_ 
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | ' /   / _ \ | '_ \   / _` |  / _ \      | |\/| | | | | | | | | __| | | \___ \   / _ \ | |  / _ \  / __| | __| / __|     | | | | | '__|  / _ \  | '_ \  | | | |  / _ \  \ \ /\ / / | '_ \  | |     | | / __| | __|
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | . \  |  __/ | | | | | (_| | | (_) |  _  | |  | | | |_| | | | | |_  | |  ___) | |  __/ | | |  __/ | (__  | |_  \__ \  _  | |_| | | |    | (_) | | |_) | | |_| | | (_) |  \ V  V /  | | | | | |___  | | \__ \ | |_ 
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_|\_\  \___| |_| |_|  \__,_|  \___/  (_) |_|  |_|  \__,_| |_|  \__| |_| |____/   \___| |_|  \___|  \___|  \__| |___/ (_) |____/  |_|     \___/  | .__/  |____/   \___/    \_/\_/   |_| |_| |_____| |_| |___/  \__|
                                                                                                                                                                                                    |_|                                                               

-- An extension of the kendo dropdown list that allows multiselection via a checkbox and selection by group
*/

FlexiJS.Kendo.MultiSelects.DropDownList = {};

//Extension to the dropdown list control
(function ($) {
    var kendo = window.kendo, ui = kendo.ui, Widget = ui.DropDownList;

    var kendoMultiSelectDropDownList = Widget.extend({
        init: function (element, options) {
            var me = this;
            
            $('#' + element.id + '-list').remove();

            if (!$(element).hasClass('flexikendo-groupedmultiselectdropdownlist')) {
                $(element).addClass('flexikendo-groupedmultiselectdropdownlist');
            }
            $(element).css('width', options.width);

            // setup template to include a checkbox
            if (options.dataSource && options.dataSource.group && options.dataSource.group.field) {
                options.template = kendo.template(
                    kendo.format('<input type="checkbox" name="{0}" value="#= {1} #" data-level="item" data-group="#= {3} #"/>&nbsp;<label for="{0}">#= {2} #</label>',
                        element.id + "_option_" + options.dataValueField,
                        options.dataValueField,
                        options.dataTextField,
                        options.dataSource.group.field
                    )
                );
            } else {
                options.template = kendo.template(
                    kendo.format('<input type="checkbox" name="{0}" value="#= {1} #" data-level="item"/>&nbsp;<label for="{0}">#= {2} #</label>',
                        element.id + "_option_" + options.dataValueField,
                        options.dataValueField,
                        options.dataTextField
                    )
                );
            }
            
            //Cache any user set databound event, we need to override it, but also run it after the override runs
            var passedDataBoundEvent = function (e) {};
            if (options.dataBound) passedDataBoundEvent = options.dataBound;
            options.dataBound = function(e) {
                var databindingList = this;

                //If the datasource is grouped, fix the group templates
                if (databindingList.dataSource.options && databindingList.dataSource.options.group && databindingList.dataSource.options.group.field) {
                    databindingList.list
                        .addClass('flexikendo-groupedmultiselectdropdownlistinnerlist')  //Add our own styling class to the dropdowns list element
                        .find('li:first')                               //Find the first list item to add a title to, kendo doesn't add a group title for the first group as it assumes the group header is always shown
                        .addClass('k-first')                            //Add the k-first class as its also missing from the first li
                        .append(                                        //Add a rendered group template to the li
                            kendo.format(
                                '<div class="k-group">{0}</div>',
                                kendo.template(databindingList.options.groupTemplate)(databindingList.dataSource._data[0][databindingList.dataSource.options.group.field])
                            )
                        );
                        
                    var groups = databindingList.list.find('[class~="k-group"]'); //Return a list of the groups
                
                    //Fix group templates to the fluent style from the kendo style.
                    if (groups.length > 0) {
                        var copiedListElement = databindingList.list.clone()
                            .attr("id", false)
                            .css(
                                { visibility: "hidden", display: "block", position: "absolute" }
                            )
                            .appendTo('body');

                        groups.each(
                            function (groupIndex, groupItem) {
                                var groupParent = $(groupItem).closest('li');
                                var groupName = groupParent.find("input[type='checkbox'][data-level='item']").attr('data-group');
                    
                                $(groupItem).prepend(
                                    kendo.format(
                                        '<input type="checkbox" name="{0}_groupedoption_{1}" data-level="group" data-group="{2}"/>&nbsp;',
                                        databindingList.list.id,
                                        databindingList.dataSource.options.dataValueField,
                                        groupName
                                    )
                                );

                                //Get the group height from the cloned element
                                var clonesHeight = copiedListElement
                                    .find(
                                        kendo.format(
                                            'li[data-offset-index="{0}"]>[class~="k-group"]',
                                            groupParent.attr('data-offset-index')
                                        )
                                    )
                                    .outerHeight();

                                //Add the top margin in the groups first item
                                groupParent.css('margin-top', (clonesHeight + 2) + 'px');

                                $(groupItem)
                                    .attr('data-group', groupName)                              //Set the group name of the item
                                    .css('top', '-' + clonesHeight + 'px')                      //Balance out the kendo styles to fit the group name
                                    .bind('click', function (event) {                           //Handle clicking the group row
                                        event.stopPropagation();                                //Stops the event bubling to the forst item in the group

                                        var groupName = $(this).attr('data-group');             //Gets the group name selected
                                        var thisCB = $(this).find('input[type=\'checkbox\'][data-level=\'group\']');   //Gets the group checkbox
                                        var setChecked = !thisCB.prop('checked');

                                        $(this).closest('ul').find("input[type='checkbox'][data-level='item'][data-group='" + groupName + "']").prop("checked", setChecked);
                                        thisCB.prop("checked", setChecked);
                                        //return false; 
                                        return true;
                                    })
                                    .find('input[type=\'checkbox\'][data-level=\'group\']')
                                    .prop(
                                        "checked", 
                                        groupParent.closest('ul').find("input[type='checkbox'][data-level='item'][data-group='" + groupName + "']:not(:checked)").length == 0
                                    )
                                ;

                            }
                        );

                        //Remove the copied list element from the page
                        copiedListElement.remove();
                    }
                }
            
                //Cancel checkbox clicks, item selection handled by row select event, which then toggles the checkbox
                databindingList.list
                    .find('li')               //Get all checkboxes in the dropdown list
                    .unbind('click')                    //Remove any set click event
                    .bind(
                        'click',                        //Bind to the click event
                        function () {
                            var thisCB = $(this).find("input[type='checkbox'][data-level='item']");
                            thisCB.prop("checked", !thisCB.prop("checked"));
                        }
                    )
                    .find('input[type=\'checkbox\']')               //Get all checkboxes in the dropdown list
                    .unbind('click')                    //Remove any set click event
                    .bind(
                        'click',                        //Bind to the click event
                        function () {
                            $(this).prop(
                                'checked',
                                !$(this).prop('checked') //Cancel the change
                            );
                        }
                    )
                ;
                passedDataBoundEvent(e);
            };
            
            var passedOpenEvent = function (e) {};
            if (options.open) passedOpenEvent = options.open;
            options.open = function (e) {
                var me = this;
                
                me.list
                    .find('input[type=\'checkbox\'][data-level=\'group\']')
                    .each(function () {
                        var groupName = $(this).attr('data-group');
                        this.checked = $(this).closest('ul').find("input[type='checkbox'][data-level='item'][data-group='" + groupName + "']:not(:checked)").length == 0;
                    })
                ;

                passedOpenEvent(e);
            };

            // create drop down UI
            Widget.fn.init.call(me, element, options);
            // setup change trigger when popup closes
            me.popup.bind('close', function () {
                var values = me.ul.find(":checked")
                    .map(function () { return this.value; }).toArray();
                // check for array inequality
                if (values < me.selectedIndexes || values > me.selectedIndexes) {
                    me._setText();
                    me._setValues();
                    me.trigger('change', {});
                }
            });
            
            me._setText();
            me._setValues();
        },
        options: {
            name: "MultiSelectDropDownList"
        },
        selectedIndexes: [],
        _accessor: function (vals, idx) { // for view model changes
            var me = this;
            if (vals === undefined) {
                return me.selectedIndexes;
            }
        },
        value: function (vals, ignoreChangeEvent) {
            var me = this;
            if (vals === undefined) { // for view model changes
                return me._accessor();
            } else { // for loading from view model
                var checkboxes = me.ul.find("input[type='checkbox'][data-level='item']");
                if (vals.length > 0) {
                    // convert to array of strings
                    var valArray = [];
                    if ($.isArray(vals)) {
                        valArray = vals;
                    } else {
                        valArray = $.toJSON(vals)
                            .map(function() { return this + ''; })
                            .toArray();
                    }
                    
                    checkboxes.each(function () {
                        this.checked = $.inArray(this.value, valArray) !== -1;
                    });

                    me._setText();
                    me._setValues();                    
                    if(!ignoreChangeEvent) me.trigger('change', {});
                }
            }
        },
        _select: function(li, e) {
            if (li) {
                var thisCB = $(li).find("input[type='checkbox'][data-level='item']");
                var lookupGroup = thisCB.attr("data-group");
                var thisGroup = thisCB.closest('ul').find("input[type='checkbox'][data-level='group'][data-group='" + lookupGroup + "']");
                var allOthersSelected = thisCB.closest('ul').find("input[type='checkbox'][data-level='item'][data-group='" + lookupGroup + "'][value!='" + thisCB.attr('value') + "']:not(:checked)").length == 0;
                if (thisCB.prop("checked") && allOthersSelected) {
                    thisGroup.prop("checked", "checked");
                } else {
                    thisGroup.prop("checked", null);
                }
            }
        }, // kills highlighting behavior
        _blur: function () { }, // kills popup-close-on-click behavior
        _setText: function () { // set text based on selections
            var me = this;
            var text = me.ul.find("input[type='checkbox'][data-level='item']:checked")
                .map(function () { return $(this).siblings("label").text(); })
                .toArray()
                .toString()
                .replace(/,/g, ', ')
                .trim();
            if(text.length < 1) text = me.options.placeholder;
            me.text(text);
        },
        _setValues: function () { // set selectedIndexes based on selection
            var me = this;
            var values = me.ul.find("input[type='checkbox'][data-level='item']:checked")
                .map(function () { return this.value; })
                .toArray();
            me.selectedIndexes = values;
        }
    });

    ui.plugin(kendoMultiSelectDropDownList);
})(jQuery);

FlexiJS.Kendo.MultiSelects.DropDownList.Setup = function (elementid, options) {
    var datasource = {};
    var onchange = function (e) {};
    var placeholdertext = '';
    var valuefieldid = '';
    var textfieldid = '';
    var width = '100%';
    var sorting = [];

    if (options) {
        //Setup schema
        if (options.valuefieldid) {
            valuefieldid = options.valuefieldid;
            datasource.schema = { model: { id: options.valuefieldid } };
        } else {
            //No value field id, don't attempt to create
            return false;
        }
        if (options.textfieldid) {
            textfieldid = options.textfieldid;
        } else {
            //No text field id, use value field
            textfieldid = options.valuefieldid;
        }
        if (options.datasourcefields) {
            datasource.schema.model.fields = options.datasourcefields;
        }

        //Set the data object, either from a hidden field with a JSON string OR a directly passed variable
        if (options.datasourceelementid) {
            datasource.data = JSON.parse($('#' + options.datasourceelementid).val());
        } else if(options.data) {
            datasource.data = options.data;
        } else {
            //No data, don't attempt to create
            return false;
        }
                    
        //Set grouping, if passed
        if (options.groupedfieldid) {
            datasource.group = { field: options.groupedfieldid };
            sorting.push({ field: options.groupedfieldid, dir: "asc" });
        }

        if (options.placeholdertext){
            placeholdertext = options.placeholdertext;
        }
        if (options.onchange){
            onchange = options.onchange;
        }
        if (options.width){
            width = options.width;
        }
        //Handle Correct Sort
        sorting.push({ field: textfieldid, dir: "asc" });
        datasource.sort = sorting;
    }
    
    $('#' + elementid).kendoMultiSelectDropDownList({
        valuePrimitive: true,
        dataSource: datasource,
        fixedGroupTemplate: '',
        dataTextField: textfieldid,
        dataValueField: valuefieldid,
        placeholder: placeholdertext,
        highlightFirst: false,
        width: width,
        change: function (e) { onchange(e); }
    });
};;/*
  _____   _                 _       _   ____        _  __                     _               _____                              _           _                
 |  ___| | |   ___  __  __ (_)     | | / ___|      | |/ /   ___   _ __     __| |   ___       |_   _|   ___   _ __ ___    _ __   | |   __ _  | |_    ___   ___ 
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | ' /   / _ \ | '_ \   / _` |  / _ \        | |    / _ \ | '_ ` _ \  | '_ \  | |  / _` | | __|  / _ \ / __|
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | . \  |  __/ | | | | | (_| | | (_) |  _    | |   |  __/ | | | | | | | |_) | | | | (_| | | |_  |  __/ \__ \
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_|\_\  \___| |_| |_|  \__,_|  \___/  (_)   |_|    \___| |_| |_| |_| | .__/  |_|  \__,_|  \__|  \___| |___/
                                                                                                                        |_|                                   
        Handles rendering & fetching of kendo templates

        Requirements:
            Kendo
*/

FlexiJS.Kendo.Templates = new Object();

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/

FlexiJS.Kendo.Templates.TemplateCache = [];

/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/

FlexiJS.Kendo.Templates.GetTemplate = function (templateGroup, templateId) {
    var forcereload = false;
    // -- Gets the templates file using a syncronous ajax call.
    //    File only loaded is the cached value is null or an empty string

    if (!FlexiJS.Kendo.Templates.TemplateCache[templateGroup] || !FlexiJS.Kendo.Templates.TemplateCache[templateGroup][templateId] || FlexiJS.Kendo.Templates.TemplateCache[templateGroup][templateId] === '') {
        //Enforces load if template not in cache or template is empty
        forcereload = true;
        if (!FlexiJS.Kendo.Templates.TemplateCache[templateGroup]) FlexiJS.Kendo.Templates.TemplateCache[templateGroup] = [];
    }

    if (forcereload === true) {
        $.ajax({
            type: "POST",
            url: "/WebServices/PageService.svc/GetKendoTemplates",
            data: JSON.stringify({ TemplateName: templateGroup }),
            processData: false,
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            async: false,
            success: function (result) {
                $.each($('script[id][type="text/x-kendo-template"]', $(result.d)), function (index, template) {
                    FlexiJS.Kendo.Templates.TemplateCache[templateGroup][$(template).attr('id')] = $(template).text();
                });
            },
        });
    }

    var returnvalue = '';
    if (FlexiJS.Kendo.Templates.TemplateCache[templateGroup][templateId]) returnvalue = FlexiJS.Kendo.Templates.TemplateCache[templateGroup][templateId];
    return returnvalue.trim();
};

FlexiJS.Kendo.Templates.RenderTemplate = function (templateGroup, templateId, data, useWithBlock) {
    if (typeof useWithBlock === 'undefined' || useWithBlock === null) useWithBlock = true;
    var template = FlexiJS.Kendo.Templates.GetTemplate(templateGroup, templateId);
    if (template !== '') {
        return kendo.Template.compile(template, { useWithBlock: useWithBlock })(data).trim();
    } else {
        return '';
    }
};
;FlexiJS.Meetings = {};
FlexiJS.Meetings.ListMeetings = {};
FlexiJS.Meetings.Meeting = {};
FlexiJS.Meetings.EcfMeetings = {};

FlexiJS.Meetings.GetGMSServiceMethodUrl = function (methodName) {
    return '/gms/WebServices/GMSService.svc/' + methodName;
};

FlexiJS.Meetings.GetResourceText = function (resourceSet, resourceKey, withHashReplace) {
    var resourceText = FlexiJS.Resources.GetResourceText(resourceSet, resourceKey);

    if (withHashReplace === true) {
        resourceText = resourceText.replace(/#/g, '\\\\#');
    }

    return resourceText;
};

FlexiJS.Meetings.ListMeetings.OnIncludePastMeetingSelectorChange = function (meetingsGridDivSelector, includePastMeetingSelector) {
    $(meetingsGridDivSelector).empty();
    FlexiJS.Meetings.ListMeetings.AddMeetingsGrid(meetingsGridDivSelector, includePastMeetingSelector);
};

FlexiJS.Meetings.ListMeetings.AddMeetingsGrid = function (meetingsGridDivSelector, includePastMeetingSelector) {
    const getMeetingsListPageResource = function (resourceKey, withHashReplace) {
        return FlexiJS.Meetings.GetResourceText('gms/listmeetings.aspx', resourceKey, withHashReplace);
    };

    const meetingsGridColumns = function () {
        const columns = [];

        columns.push({
            field: 'Name',
            title: getMeetingsListPageResource('Name')
        });

        columns.push({
            field: 'PanelName',
            title: getMeetingsListPageResource('Committee')
        });

        columns.push({
            field: 'MeetingDate',
            title: getMeetingsListPageResource('Date'),
            type: 'date',
            format: '{0:d}',
            width: 120
        });

        columns.push({
            field: 'VisibleToMembers',
            title: getMeetingsListPageResource('VisibleToMembers'),
            width: 120
        });

        columns.push({
            title: '',
            template: '<a href="/gms/meeting.aspx?id=#= MeetingID#">' + getMeetingsListPageResource('Edit', true) + '</a>',
            width: 55
        });

        return columns;
    };

    $(meetingsGridDivSelector).kendoGrid({
        dataSource: {
            type: 'json',
            transport: {
                read: {
                    url: '/gms/WebServices/GMSService.svc/GetMeetings',
                    type: 'POST',
                    contentType: 'application/json',
                    data: function () {
                        return { includePastMeetings: $(includePastMeetingSelector).is(':checked') };
                    }
                },
                parameterMap: function (options) {
                    return JSON.stringify(options);
                }
            },
            schema: {
                type: 'json',
                data: function (r) {
                    const jsonResult = JSON.parse(r.d);

                    FlexiJS.Dates.ParseObjectArrayDotNetDates(['MeetingDate'], jsonResult.Meetings);

                    return jsonResult.Meetings;
                },
                total: function (r) {
                    const jsonResult = JSON.parse(r.d);

                    return jsonResult.TotalMeetings;
                },
                model: {
                    fields: {
                        MeetingId: { type: 'number' },
                        Name: { type: 'string' },
                        MeetingDate: { type: 'datetime' },
                        Committee: { type: 'string' }
                    }
                }
            },
            pageSize: 20,
            serverPaging: true,
            serverSorting: true,
            sort: { field: 'MeetingDate', dir: 'ASC' }
        },
        sortable: true,
        pageable: {
            refresh: true,
            pageSizes: [20, 50, 100, 250, 'all']
        },
        noRecords: { template: getMeetingsListPageResource('NoRecords', true) },
        columns: meetingsGridColumns()
    });
};

FlexiJS.Meetings.Meeting.InitializeConstants = function (meetingId, saveButton, meetingNameTextBoxId, startDateDatePickerSelector, committeePanelSelectorId, startDateHiddenFieldId, panelHiddenFieldId,
    uploadGridSelector, assignedApplicationsGridSelector, meetingMemberStatusSpeaker, meetingMemberStatusAttendee, meetingMemberStatusConflicted, meetingMemberStatusNotAttending,
    meetingMemberStatusLeadSpeaker, disabledClassName) {
    FlexiJS.Meetings.Meeting.Constants = {
        _meetingId: meetingId,
        _saveButton: saveButton,
        _meetingNameTextBoxId: meetingNameTextBoxId,
        _startDateDatePickerSelector: startDateDatePickerSelector,
        _committeePanelSelectorId: committeePanelSelectorId,
        _startDateHiddenFieldId: startDateHiddenFieldId,
        _panelHiddenFieldId: panelHiddenFieldId,
        _uploadGridSelector: uploadGridSelector,
        _assignedApplicationsGridSelector: assignedApplicationsGridSelector,
        _meetingMemberStatusSpeaker: meetingMemberStatusSpeaker,
        _meetingMemberStatusAttendee: meetingMemberStatusAttendee,
        _meetingMemberStatusConflicted: meetingMemberStatusConflicted,
        _meetingMemberStatusNotAttending: meetingMemberStatusNotAttending,
        _meetingMemberStatusLeadSpeaker: meetingMemberStatusLeadSpeaker,
        _disabledClassName: disabledClassName
    };
};

FlexiJS.Meetings.Meeting.GetMeetingPageResource = function (resourceKey, withHashReplace) {
    return FlexiJS.Meetings.GetResourceText('gms/meeting.aspx', resourceKey, withHashReplace);
};

FlexiJS.Meetings.Meeting.GetSelectedPanelId = function () {
    const ddlCommitteePanel = document.getElementById(FlexiJS.Meetings.Meeting.Constants._committeePanelSelectorId);
    const selectedOption = ddlCommitteePanel.options[ddlCommitteePanel.selectedIndex];

    var selectedPanelId = null;

    if (selectedOption) {
        selectedPanelId = selectedOption.value;
    }

    return selectedPanelId;
};

FlexiJS.Meetings.Meeting.ToggleScheduleMeetingButton = function () {
    const constants = FlexiJS.Meetings.Meeting.Constants;

    const showSaveMeetingButton = function () {
        const txtMeetingName = document.getElementById(constants._meetingNameTextBoxId);
        const selectedDate = $(constants._startDateDatePickerSelector).data('kendoDatePicker').value();
        const selectedPanelId = FlexiJS.Meetings.Meeting.GetSelectedPanelId();

        const errorMessages = [];
        const result = {
            show: true
        };

        if (!txtMeetingName.value) {
            errorMessages.push(FlexiJS.Meetings.Meeting.GetMeetingPageResource('rfvMeetingName.Text'));
            result.show = false;
        }

        if (!selectedDate) {
            errorMessages.push(FlexiJS.Meetings.Meeting.GetMeetingPageResource('cvStartDate.Text'));
            result.show = false;
        }

        if (!selectedPanelId) {
            errorMessages.push(FlexiJS.Meetings.Meeting.GetMeetingPageResource('rfvCommitteePanel.Text'));
            result.show = false;
        }

        result.errorMessage = errorMessages.join(' ');

        return result;
    };

    const ssmbResult = showSaveMeetingButton();
    const sb = constants._saveButton;

    if (ssmbResult.show) {
        sb.disabled = false;
        sb.classList.remove(constants._disabledClassName);
        sb.title = '';
    } else {
        sb.title = ssmbResult.errorMessage;

        if (sb.disabled === false) {
            sb.disabled = true;
            sb.classList.add(constants._disabledClassName);
        }
    }
};

FlexiJS.Meetings.Meeting.InitializeStartDateDatePicker = function () {

    const constants = FlexiJS.Meetings.Meeting.Constants;

    const onStartDateChanged = function () {

        const dpStartDate = $(constants._startDateDatePickerSelector).data('kendoDatePicker');

        if (dpStartDate.value()) {

            const selectedDay = dpStartDate.value().getDate();
            const selectedMonth = dpStartDate.value().getMonth() + 1;
            const selectedYear = dpStartDate.value().getFullYear();                 

            document.getElementById(constants._startDateHiddenFieldId).value = selectedYear + '-' + selectedMonth + '-' + selectedDay + 'T00:00:00.000'
                            
        } else {

            document.getElementById(constants._startDateHiddenFieldId).value = '';

        }

        FlexiJS.Meetings.Meeting.ToggleScheduleMeetingButton();
    };

    $(constants._startDateDatePickerSelector).kendoDatePicker({
        change: function () { onStartDateChanged(); }
    });

    dpStartDate = $(constants._startDateDatePickerSelector).data('kendoDatePicker');

    hfStartDate = document.getElementById(constants._startDateHiddenFieldId);
        
    if (hfStartDate) {
        dpStartDate.value(hfStartDate.value);
    }
};

FlexiJS.Meetings.Meeting.RegisterControlEvents = function (panelChangedYesFunctionName, panelChangedNoFunctionName) {
    const constants = FlexiJS.Meetings.Meeting.Constants;

    document.getElementById(constants._meetingNameTextBoxId).onchange = function () {
        FlexiJS.Meetings.Meeting.ToggleScheduleMeetingButton();
    };

    document.getElementById(constants._committeePanelSelectorId).onchange = function () {
        FlexiJS.Meetings.Meeting.ToggleScheduleMeetingButton();

        const originalPanelId = document.getElementById(constants._panelHiddenFieldId).value;

        if (originalPanelId && originalPanelId !== FlexiJS.Meetings.Meeting.GetSelectedPanelId()) {
            if ($(constants._assignedApplicationsGridSelector).data("kendoGrid").dataSource.total() > 0) {
                    FlexiJS.ConfirmActions.KendoConfirmActionDialog(FlexiJS.Meetings.Meeting.GetMeetingPageResource('PanelChangedConfirmationTitle'),
                        panelChangedYesFunctionName,
                        FlexiJS.Meetings.Meeting.GetMeetingPageResource('PanelChangedConfirmationMessage'),
                        FlexiJS.Meetings.Meeting.GetMeetingPageResource('PanelChangedConfirmationYes'),
                        'btnPanelChangedConfirmationYes',
                        FlexiJS.Meetings.Meeting.GetMeetingPageResource('PanelChangedConfirmationNo'),
                        'btnPanelChangedConfirmationNo',
                        'False',
                        panelChangedNoFunctionName);
            }
        }
    };
};

FlexiJS.Meetings.Meeting.PanelChangedYes = function () {
    FlexiJS.Meetings.Meeting.Constants._saveButton.click();
};

FlexiJS.Meetings.Meeting.PanelChangedNo = function () {
    const constants = FlexiJS.Meetings.Meeting.Constants;

    const originalPanelId = document.getElementById(constants._panelHiddenFieldId).value;
    document.getElementById(constants._committeePanelSelectorId).value = originalPanelId;
};

FlexiJS.Meetings.Meeting.InitializeFileUploader = function (fileUploadSelector, allowedExtensions, maxFileSize, invalidFileTypeMessage, overMaxSizeMessage, maxAllowedFileSize, fileUploadTemplateSelector) {
    $(fileUploadSelector).kendoUpload({
        multiple: true,
        select: function (e) {
            for(var index = 0; index < e.files.length; index++) {
                if (!allowedExtensions.includes(e.files[index].extension.toLowerCase())) {
                    showNotification({ type: 'error', message: invalidFileTypeMessage, autoClose: false, duration: 7 });
                    e.preventDefault();
                    return false;
                }

                if (e.files[index].size > maxFileSize) {
                    showNotification({
                        type: 'error',
                        message: overMaxSizeMessage + ' ' + maxAllowedFileSize,
                        autoClose: false,
                        duration: 7
                    });
                    e.preventDefault();
                    return false;
                }
            }
        },
        template: kendo.template($(fileUploadTemplateSelector).html())
    });
};

FlexiJS.Meetings.Meeting.AddUploadGrid = function (removeUploadedFileFunctionName) {
    const constants = FlexiJS.Meetings.Meeting.Constants;

    function uploadsGridColumns() {
        const columns = [];

        columns.push({
            field: 'TitleOrder',
            title: FlexiJS.Meetings.Meeting.GetMeetingPageResource('MeetingUploadsGridFilename'),
            sortable: true,
            template: '<a href="#= Link#" target="_blank">#= Title#</a>',
            width: 250
        });

        columns.push({
            field: 'DateCreated',
            title: FlexiJS.Meetings.Meeting.GetMeetingPageResource('MeetingUploadsGridDateUploaded'),
            sortable: true,
            width: 180
        });

        columns.push({
            field: 'Size',
            title: FlexiJS.Meetings.Meeting.GetMeetingPageResource('MeetingUploadsGridFileSize'),
            sortable: true,
            width: 80
        });

        columns.push({
            title: '',
            template: '<a class="color-critical" onclick="FlexiJS.Meetings.Meeting.OnRemoveUploadedFileClick(#= DocumentStoreID#, \'' + removeUploadedFileFunctionName + '\'); return false;" href="javascript:return false;">'
                + FlexiJS.Meetings.Meeting.GetMeetingPageResource('Remove', true)
                + '</a>',
            width: '100px'
        });

        return columns;
    }

    if (constants._meetingId) {
        $(constants._uploadGridSelector).kendoGrid({
            dataSource: {
                type: 'json',
                transport: {
                    read: {
                        url: FlexiJS.Meetings.GetGMSServiceMethodUrl('GetMeetingUploads'),
                        type: 'POST',
                        contentType: 'application/json',
                        data: function () {
                            return { meetingId: constants._meetingId };
                        }
                    },
                    parameterMap: function (options) {
                        return JSON.stringify(options);
                    }
                },
                schema: {
                    type: 'json',
                    data: function (r) {
                        return JSON.parse(r.d).Uploads;
                    },
                    total: function (r) {
                        return JSON.parse(r.d).TotalUploads;
                    },
                    model: {
                        fields: {
                            Title: { type: 'string' },
                            Size: { type: 'string' },
                            DateCreated: { type: 'string' }
                        }
                    }
                },
                sort: ({ field: "TitleOrder", dir: "asc" })
            },
            sortable: true,
            noRecords: { template: FlexiJS.Meetings.Meeting.GetMeetingPageResource('NoDocuments', true) },
            columns: uploadsGridColumns()
        });
    }
};

FlexiJS.Meetings.Meeting.OnRemoveUploadedFileClick = function (documentStoreId, removeUploadedFileFunction) {
    FlexiJS.Meetings.Meeting.Constants._documentStoreIdToRemove = documentStoreId;

    FlexiJS.ConfirmActions.KendoConfirmActionDialog(FlexiJS.Meetings.Meeting.GetMeetingPageResource('RemoveDocumentConfirmationTitle'),
        removeUploadedFileFunction,
        FlexiJS.Meetings.Meeting.GetMeetingPageResource('RemoveDocumentConfirmationMessage'),
        FlexiJS.Meetings.Meeting.GetMeetingPageResource('RemoveDocumentConfirmationYes'),
        'btnRemoveDocumentConfirmationYes',
        FlexiJS.Meetings.Meeting.GetMeetingPageResource('RemoveDocumentConfirmationNo'),
        'btnRemoveDocumentConfirmationNo',
        'False');
};

FlexiJS.Meetings.Meeting.RemoveUploadedFile = function () {
    $.ajax({
        url: FlexiJS.Meetings.GetGMSServiceMethodUrl('RemoveMeetingDocument'),
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify({ documentStoreId: FlexiJS.Meetings.Meeting.Constants._documentStoreIdToRemove }),
        dataType: 'json'
    }).done(function (result) {
        const responseData = JSON.parse(result.d);

        if (responseData && responseData.Result === true) {
            showNotificationMessage('success', FlexiJS.Meetings.Meeting.GetMeetingPageResource('DocumentRemoveSuccess'));

            $(FlexiJS.Meetings.Meeting.Constants._uploadGridSelector).data("kendoGrid").dataSource.read();
        } else {
            FlexiJS.Meetings.Meeting.ShowRemoveUploadedFileFailedMessage();
        }
    }).fail(FlexiJS.Meetings.Meeting.ShowRemoveUploadedFileFailedMessage);
};

FlexiJS.Meetings.Meeting.ShowRemoveUploadedFileFailedMessage = function () {
    showNotificationMessage('error', FlexiJS.Meetings.Meeting.GetMeetingPageResource('DocumentRemoveFail'));
};

FlexiJS.Meetings.Meeting.OnScheduleMeetingButtonClick = function () {
    const constants = FlexiJS.Meetings.Meeting.Constants;

    constants._saveButton.disabled = true;
    constants._saveButton.classList.add(constants._disabledClassName);
};

FlexiJS.Meetings.Meeting.OnCancelMeetingClick = function (cancelMeetingFunction) {
    FlexiJS.ConfirmActions.KendoConfirmActionDialog(FlexiJS.Meetings.Meeting.GetMeetingPageResource('CancelConfirmationTitle'),
        cancelMeetingFunction,
        FlexiJS.Meetings.Meeting.GetMeetingPageResource('CancelConfirmationMessage'),
        FlexiJS.Meetings.Meeting.GetMeetingPageResource('CancelConfirmationYes'),
        'btnMeetingCancelConfirmationYes',
        FlexiJS.Meetings.Meeting.GetMeetingPageResource('CancelConfirmationNo'),
        'btnMeetingCancelConfirmationNo',
        'False');
};

FlexiJS.Meetings.Meeting.CancelMeeting = function () {
    $.ajax({
        url: FlexiJS.Meetings.GetGMSServiceMethodUrl('CancelMeeting'),
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify({ meetingId: FlexiJS.Meetings.Meeting.Constants._meetingId }),
        dataType: 'json'
    }).done(function (result) {
        const responseData = JSON.parse(result.d);

        if (responseData && responseData.Result === true) {
            window.location.replace('/gms/ListMeetings.aspx?meetingcancelled=1');
        } else {
            FlexiJS.Meetings.Meeting.ShowCancelMeetingFailedMessage();
        }
    }).fail(FlexiJS.Meetings.Meeting.ShowCancelMeetingFailedMessage);
};

FlexiJS.Meetings.Meeting.ShowCancelMeetingFailedMessage = function () {
    showNotificationMessage('error', FlexiJS.Meetings.Meeting.GetMeetingPageResource('CancellationFailedMessage'));
};

FlexiJS.Meetings.Meeting.AddAssignedApplicationsGrid = function (removeAssignedApplicationFunction) {
    const constants = FlexiJS.Meetings.Meeting.Constants;

    function assignedApplicationsGridColumns() {
        const columns = [];

        columns.push({
            field: 'Ref',
            title: FlexiJS.Meetings.Meeting.GetMeetingPageResource('ApplicationsGridRef'),
            sortable: true,
            template: '<a href="/gms/ecfsummary.aspx?id=#= ApplicationId#" target="_blank">#= Ref#</a>',
            width: 200
        });

        columns.push({
            field: 'Scheme',
            title: FlexiJS.Meetings.Meeting.GetMeetingPageResource('ApplicationsGridScheme'),
            sortable: true,
            width: 230
        });

        columns.push({
            field: 'PrimaryApplicant',
            title: FlexiJS.Meetings.Meeting.GetMeetingPageResource('ApplicationsGridPrimaryApplicant'),
            width: 200
        });

        columns.push({
            field: 'Speakers',
            title: FlexiJS.Meetings.Meeting.GetMeetingPageResource('ApplicationsGridSpeakers'),
            width: 200
        });

        columns.push({
            field: 'Conflicts',
            title: FlexiJS.Meetings.Meeting.GetMeetingPageResource('ApplicationsGridConflicts'),
            width: 200
        });

        columns.push({
            title: '',
            template: '<div class="float-right inline-block"><button class="fg_button meeting-button quarter-margin-bottom half-margin-right" onclick="FlexiJS.Meetings.Meeting.OnAssignRolesClick(#= MeetingApplicationId#, ' + "'#=RefEscaped #', " + "'#=PrimaryApplicantSafe #'" + '); return false;">'
                + FlexiJS.Meetings.Meeting.GetMeetingPageResource('ApplicationsGridAssignRoles', true) + '</button>'
                +'<button class="meeting-button fg_button delete" onclick="FlexiJS.Meetings.Meeting.OnRemoveAssignedApplicationClick(#= MeetingApplicationId#, \'' + removeAssignedApplicationFunction + '\'); return false;">'
                + FlexiJS.Meetings.Meeting.GetMeetingPageResource('ApplicationsGridRemoveApplication', true) + '</button></div>',
            width: 160,
            sticky: true
        });

        return columns;
    }

    if (constants._meetingId) {


        var headerTemplate = "<div class='export-tools float-right'>";
        headerTemplate += FlexiJS.Utils.Strings.FormatString(
            "<a href='/DownloadFile.aspx?c=export&ct=meetingapplications&export=excel&meetingId={0}' target='_blank' class='fx-gmsdashboardgridlink'><img src='/RadControls/FlexiGrid/Grid/ExportToExcel.gif' title='{1}' >{1}</a>",
            constants._meetingId, "Export assigned applications");
        headerTemplate += "</div>";

        $(constants._assignedApplicationsGridSelector).kendoGrid({
            dataSource: {
                type: 'json',
                transport: {
                    read: {
                        url: FlexiJS.Meetings.GetGMSServiceMethodUrl('GetMeetingApplications'),
                        type: 'POST',
                        contentType: 'application/json',
                        data: { meetingId: constants._meetingId }
                    },
                    parameterMap: function (options) {
                        return JSON.stringify(options);
                    }
                },
                schema: {
                    type: 'json',
                    data: function (r) {
                        var apps = JSON.parse(r.d).Applications;

                        for (var i = 0; i < apps.length; i++) {
                            apps[i].PrimaryApplicantSafe = apps[i].PrimaryApplicant.replace(/'/g, '~apos~');
                        }

                        return apps;
                    },
                    total: function (r) {
                        return JSON.parse(r.d).TotalApplications;
                    },
                    model: {
                        fields: {
                            Ref: { type: 'string' },
                            PrimaryApplicant: { type: 'string' },
                            Speakers: { type: 'string' },
                            Conflicts: { type: 'string' },
                            Scheme: { type: 'string' }
                        }
                    }
                },
                pageSize: 20
            },
            pageable: {
                pageSizes: [20, 100, 250, 'all']
            },
            sortable: true,
            toolbar: [{ name: "Export", template: headerTemplate }],
            noRecords: { template: FlexiJS.Meetings.Meeting.GetMeetingPageResource('NoApplicationsAssigned', true) },
            columns: assignedApplicationsGridColumns()
        });
    }
};

FlexiJS.Meetings.Meeting.OnRemoveAssignedApplicationClick = function (meetingApplicationId, removeAssignedApplicationFunction) {
    FlexiJS.Meetings.Meeting.Constants._meetingApplicationIdToRemove = meetingApplicationId;

    FlexiJS.ConfirmActions.KendoConfirmActionDialog(FlexiJS.Meetings.Meeting.GetMeetingPageResource('RemoveApplicationConfirmationTitle'),
        removeAssignedApplicationFunction,
        FlexiJS.Meetings.Meeting.GetMeetingPageResource('RemoveApplicationConfirmationMessage'),
        FlexiJS.Meetings.Meeting.GetMeetingPageResource('RemoveApplicationConfirmationYes'),
        'btnRemoveApplicationConfirmationYes',
        FlexiJS.Meetings.Meeting.GetMeetingPageResource('RemoveApplicationConfirmationNo'),
        'btnRemoveApplicationConfirmationNo',
        'False');
};

FlexiJS.Meetings.Meeting.RemoveAssignedApplication = function () {
    const constants = FlexiJS.Meetings.Meeting.Constants;

    $.ajax({
        url: FlexiJS.Meetings.GetGMSServiceMethodUrl('RemoveMeetingApplication'),
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify({ meetingApplicationId: constants._meetingApplicationIdToRemove }),
        dataType: 'json'
    }).done(function (result) {
        const responseData = JSON.parse(result.d);

        if (responseData && responseData.Result === true) {
            showNotificationMessage('success', FlexiJS.Meetings.Meeting.GetMeetingPageResource('ApplicationRemoveSuccess'));

            $(constants._assignedApplicationsGridSelector).data("kendoGrid").dataSource.read();
        } else {
            FlexiJS.Meetings.Meeting.ShowRemoveAssignedApplicationFailedMessage();
        }
    }).fail(FlexiJS.Meetings.Meeting.ShowRemoveAssignedApplicationFailedMessage);
};

FlexiJS.Meetings.Meeting.ShowRemoveAssignedApplicationFailedMessage = function () {
    showNotificationMessage('error', FlexiJS.Meetings.Meeting.GetMeetingPageResource('ApplicationRemoveFail'));
};

FlexiJS.Meetings.Meeting.OnBtnAssignApplicationsClick = function () {
    const constants = FlexiJS.Meetings.Meeting.Constants;
    constants._assignedApplicationIds = [];

    const schemeSelectId = 'schemeSelect';
    const stageSelectId = 'stageSelect';
    const assignableApplicationGridSelector = '#gridAssignableApplications';

    const kWindow = FlexiJS.Popup.CreateKendoWindow(
        'assignApplicationWindow',
        FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssignApplicationsWindowClose'),
        FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssignApplicationsWindowTitle'),
        FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssignApplicationsWindowDescription'),
        true
    );

    kWindow.element.find('.kendowindow-contentsarea')
        .append(FlexiJS.Kendo.Templates.RenderTemplate('assignmeetingapplications', 'AssignApplications', {}, false));

    $('#' + schemeSelectId).bind('change', function () {
        const gmsFundTypeId = $('#' + schemeSelectId).val();
        const showApplicationsButton = document.getElementById('btnShowApplications');

        if (gmsFundTypeId !== '0') {
            showApplicationsButton.disabled = false;
            showApplicationsButton.classList.remove(constants._disabledClassName);
        } else {
            showApplicationsButton.disabled = true;
            showApplicationsButton.classList.add(constants._disabledClassName);
        }

        $.ajax({
            url: FlexiJS.Meetings.GetGMSServiceMethodUrl('GetFundRounds'),
            type: "POST",
            contentType: 'application/json; charset=utf-8',
            dataType: 'json',
            data: JSON.stringify({ gmsFundTypeId: gmsFundTypeId })
        }).done(function (result) {
            const data = JSON.parse(result.d);

            if (data.Result && data.ResultDetail) {
                const select = document.getElementById('roundSelect');
                select.innerHTML = '';

                $.each(data.ResultDetail, function (index, item) {
                    const option = document.createElement('option');
                    option.value = item.GMSFundTypeScoreStageRoundId;
                    option.innerHTML = item.RoundName;

                    select.appendChild(option);
                });
            }
        });
    });

    kWindow.element.find('#btnShowApplications').bind('click', function () {
        FlexiJS.Meetings.Meeting.ToggleAddSelectedApplicationsButton();

        $(assignableApplicationGridSelector).empty();

        FlexiJS.Meetings.Meeting.AddAssignableApplicationsGrid(assignableApplicationGridSelector, '#' + schemeSelectId, '#' + stageSelectId, '#roundSelect', '#txtTag');
    });

    kWindow.element.find('#btnAddSelectedApplications').bind('click', function () {

        if ($(this).data('clicked')) {
            return false;
        } else {
            $(this).data('clicked', true);
            $.ajax({
                url: FlexiJS.Meetings.GetGMSServiceMethodUrl('AssignApplicationsToMeeting'),
                type: "POST",
                contentType: 'application/json; charset=utf-8',
                dataType: 'json',
                data: JSON.stringify({ meetingId: constants._meetingId, applicationIds: constants._assignedApplicationIds })
            }).done(function () {
                constants._assignedApplicationIds = [];
                $(constants._assignedApplicationsGridSelector).data("kendoGrid").dataSource.read();
                kWindow.close();
            }).fail(function () {
                $(this).removeData('clicked');
            });
        }
    });

    FlexiJS.Meetings.Meeting.AddAssignableApplicationsGrid(assignableApplicationGridSelector, '#' + schemeSelectId, '#' + stageSelectId, '#roundSelect', '#txtTag');

    kWindow.maximize().open();

    $.ajax({
        url: FlexiJS.Meetings.GetGMSServiceMethodUrl('GetApplicationStages'),
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        dataType: 'json'
    }).done(function (result) {
        const data = JSON.parse(result.d);

        if (data.Result && data.ResultDetail) {
            const select = document.getElementById(stageSelectId);

            $.each(data.ResultDetail, function (index, item) {
                const option = document.createElement('option');
                option.value = item.Id;
                option.innerHTML = item.Value;

                select.appendChild(option);
            });
        }
    });

    $.ajax({
        url: FlexiJS.Meetings.GetGMSServiceMethodUrl('GetApplicationSchemes'),
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        dataType: 'json'
    }).done(function (result) {
        const data = JSON.parse(result.d);

        if (data.Result && data.ResultDetail) {
            const select = document.getElementById(schemeSelectId);

            $.each(data.ResultDetail, function (index, item) {
                const option = document.createElement('option');
                option.value = item.GMSFundTypeId;
                option.innerHTML = item.FundTypeName;

                select.appendChild(option);
            });
        }
    });
};

FlexiJS.Meetings.Meeting.AddAssignableApplicationsGrid = function (assignableApplicationGridSelector, schemeSelector, stageSelector, roundSelector, tagTextSelector) {
    const constants = FlexiJS.Meetings.Meeting.Constants;
    const gmsFundTypeId = $(schemeSelector).val();
    const stageTypeId = $(stageSelector).val();
    const roundId = $(roundSelector).val();
    const tag = $(tagTextSelector).val();

    const data = {
        meetingId: constants._meetingId,
        gmsFundTypeId: gmsFundTypeId,
        stageTypeId: stageTypeId,
        roundId: roundId
    };

    if (tag) {
        data.tag = tag;
    }

    function assignableApplicationsGridColumns() {
        const columns = [];

        columns.push({
            width: 50,
            headerTemplate: '<input id="selectAllApplicationsCheckBox" type="checkbox" onchange="FlexiJS.Meetings.Meeting.OnSelectAllApplicationsCheckBoxChanged()" />',
            template: '<input id="assignApplicationCheckBox_#=ApplicationId#" type="checkbox" onchange="FlexiJS.Meetings.Meeting.OnAssignApplicationCheckBoxChanged(#=ApplicationId#)" />'
        });

        columns.push({
            field: 'Reference',
            title: FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssignableApplicationsGridReference'),
            sortable: true,
            template: '<a href="/gms/ecfsummary.aspx?id=#= ApplicationId#" target="_blank">#= Reference#</a>'
        });

        columns.push({
            field: 'PrimaryApplicant',
            title: FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssignableApplicationsGridPrimaryApplicant')
        });

        columns.push({
            field: 'Scheme',
            title: FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssignableApplicationsGridScheme')
        });

        columns.push({
            field: 'Stage',
            title: FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssignableApplicationsGridStage')
        });

        return columns;
    }

    $(assignableApplicationGridSelector).kendoGrid({
        dataSource: {
            type: 'json',
            transport: {
                read: {
                    url: FlexiJS.Meetings.GetGMSServiceMethodUrl('GetAssignableApplications'),
                    type: 'POST',
                    contentType: 'application/json',
                    data: data
                },
                parameterMap: function (options) {
                    return JSON.stringify(options);
                }
            },
            schema: {
                type: 'json',
                data: function (r) {
                    return JSON.parse(r.d).Applications;
                },
                total: function (r) {
                    return JSON.parse(r.d).TotalApplications;
                },
                model: {
                    fields: {
                        ApplicationId: { type: 'number' },
                        Ref: { type: 'string' },
                        PrimaryApplicant: { type: 'string' },
                        Scheme: { type: 'string' },
                        Stage: { type: 'string' }
                    }
                }
            },
            serverPaging: true,
            serverSorting: true,
            pageSize: 10
        },
        pageable: {
            pageSizes: [10, 50, 100, 'all']
        },
        selectable: 'row',
        change: function () {
            const selectedItem = this.dataItem(this.select());
            const checkBox = document.getElementById('assignApplicationCheckBox_' + selectedItem.ApplicationId);

            if (checkBox.checked) {
                checkBox.checked = false;
            } else {
                checkBox.checked = true;
            }

            FlexiJS.Meetings.Meeting.OnAssignApplicationCheckBoxChanged(selectedItem.ApplicationId);
        },
        dataBound: function () {
            if (constants._assignedApplicationIds.length > 0) {
                const idPrefix = 'assignApplicationCheckBox_';
                const checkBoxes = document.querySelectorAll('[id^="' + idPrefix + '"]');
                const selectAllCheckBox = document.getElementById('selectAllApplicationsCheckBox');
                var allCheckedOnPage = true;

                for (var index = 0; index < checkBoxes.length; index++) {
                    const checkBox = checkBoxes[index];
                    const appId = parseInt(checkBox.id.substring(idPrefix.length));

                    if (constants._assignedApplicationIds.includes(appId)) {
                        checkBox.checked = true;
                    } else {
                        allCheckedOnPage = false;
                    }
                }

                if (allCheckedOnPage) {
                    selectAllCheckBox.checked = true;
                } else {
                    selectAllCheckBox.checked = false;
                }
            }
        },
        sortable: true,
        noRecords: { template: FlexiJS.Meetings.Meeting.GetMeetingPageResource('noMatchingApplications', true) },
        columns: assignableApplicationsGridColumns(),
        height: 550
    });
};

FlexiJS.Meetings.Meeting.OnSelectAllApplicationsCheckBoxChanged = function () {
    const idPrefix = 'assignApplicationCheckBox_';
    const checkBoxes = document.querySelectorAll('[id^="' + idPrefix + '"]');
    const allChecked = document.getElementById('selectAllApplicationsCheckBox').checked;

    for (var index = 0; index < checkBoxes.length; index++) {
        const checkBox = checkBoxes[index];
        const appId = parseInt(checkBox.id.substring(idPrefix.length));

        if (allChecked) {
            if (!checkBox.checked) {
                checkBox.checked = true;
                FlexiJS.Meetings.Meeting.OnAssignApplicationCheckBoxChanged(appId, allChecked);
            }

        } else {
            if (checkBox.checked) {
                checkBox.checked = false;
                FlexiJS.Meetings.Meeting.OnAssignApplicationCheckBoxChanged(appId);
            }
        }
    }
};

FlexiJS.Meetings.Meeting.OnAssignApplicationCheckBoxChanged = function (applicationId, allCheckClicked) {
    const constants = FlexiJS.Meetings.Meeting.Constants;
    const idPrefix = 'assignApplicationCheckBox_';
    const checkBox = document.getElementById(idPrefix + applicationId);

    if (checkBox.checked) {
        constants._assignedApplicationIds.push(applicationId);

        if (!allCheckClicked) {
            const checkBoxes = document.querySelectorAll('[id^="' + idPrefix + '"]');
            const selectAllCheckBox = document.getElementById('selectAllApplicationsCheckBox');
            var allCheckedOnPage = true;

            for (var index = 0; index < checkBoxes.length; index++) {
                if (!checkBoxes[index].checked) {
                    allCheckedOnPage = false;
                    break;
                }
            }

            if (allCheckedOnPage) {
                selectAllCheckBox.checked = true;
            }
        }
    } else {
        constants._assignedApplicationIds = constants._assignedApplicationIds.filter(function (item) { return item !== applicationId; });
        document.getElementById('selectAllApplicationsCheckBox').checked = false;
    }

    FlexiJS.Meetings.Meeting.ToggleAddSelectedApplicationsButton();
};

FlexiJS.Meetings.Meeting.ToggleAddSelectedApplicationsButton = function () {
    const constants = FlexiJS.Meetings.Meeting.Constants;
    const btnAddSelectedApplications = document.getElementById('btnAddSelectedApplications');

    if (constants._assignedApplicationIds.length > 0) {
        btnAddSelectedApplications.disabled = false;
        btnAddSelectedApplications.classList.remove(constants._disabledClassName);
    } else {
        btnAddSelectedApplications.disabled = true;
        btnAddSelectedApplications.classList.add(constants._disabledClassName);
    }
};

FlexiJS.Meetings.Meeting.OnAssignRolesClick = function (meetingApplicationId, ref, primaryApplicant) {
    primaryApplicant = primaryApplicant.replace(/~apos~/g, "'");

    const assignRolesGridSelector = '#gridAssignRoles';
    const constants = FlexiJS.Meetings.Meeting.Constants;

    constants._meetingApplicationUserRoles = [];

    const kWindow = FlexiJS.Popup.CreateKendoWindow(
        'assignRolesWindow_' + meetingApplicationId,
        FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssignRolesWindowClose'),
        FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssignRolesWindowTitle'),
        FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssignRolesWindowDescription')
    );

    kWindow.element.find('.kendowindow-contentsarea')
        .append(FlexiJS.Kendo.Templates.RenderTemplate('assignmeetingapplications', 'AssignRoles', {}, false));

    kWindow.maximize().open();

    FlexiJS.Meetings.Meeting.AddAssignRolesGrid(assignRolesGridSelector, meetingApplicationId);

    kWindow.element.find('#headerRefAndPrimaryApplicant').text(ref + ' ' + primaryApplicant);

    kWindow.element.find('#btnSaveRoles').bind('click', function () {
        if (constants._meetingApplicationUserRoles.length > 0) {
            $.ajax({
                url: FlexiJS.Meetings.GetGMSServiceMethodUrl('SaveMeetingApplicationUserRoles'),
                type: "POST",
                contentType: 'application/json; charset=utf-8',
                data: JSON.stringify({ meetingId: constants._meetingId, meetingApplicationUserRoles: constants._meetingApplicationUserRoles }),
                dataType: 'json'
            }).done(function () {
                $('#divAssignedApplicationsGrid').data("kendoGrid").dataSource.read();
                constants._meetingApplicationUserRoles = [];
                kWindow.close();
            });
        } else {
            kWindow.close();
        }
    });

    kWindow.element.find('#btnMembersFilter').bind('click', function () {
        const clearMembersFilterButton = $('#btnClearMembersFilter');
        const memberFiltersValue = $('#txtMembersFilter').val();

        if (memberFiltersValue.length > 0) {
            clearMembersFilterButton.show();
        } else {
            clearMembersFilterButton.hide();
        }

        $(assignRolesGridSelector).empty();

        FlexiJS.Meetings.Meeting.AddAssignRolesGrid(assignRolesGridSelector, meetingApplicationId);
    });

    kWindow.element.find('#btnClearMembersFilter').bind('click', function () {
        $('#txtMembersFilter').val('');
        $('#btnClearMembersFilter').hide();
        $('#btnMembersFilter').click();
    });
};

FlexiJS.Meetings.Meeting.AddAssignRolesGrid = function (assignRolesGridSelector, meetingApplicationId) {
    const constants = FlexiJS.Meetings.Meeting.Constants;

    function assignRolesGridColumns() {
        const columns = [];

        columns.push({
            field: 'Member',
            title: FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssigRolesGridMember') + " <i class='fas fa-info-circle table-info-icon' title='" + FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssigRolesGridMember.Tooltip') + "'></i>",
            template: '<div class="clearfix relative">'
                        + '<div class="fx-contactlistingimage">#=InitialsHtmlString #</div>'
                        + '<div class= "float-left applicant-details">'
                            + '<div class= "primary-applicant-name">#=Name#</div>'
                                + '#if (Organisation != null) {# '
                                    + '<div class= "primary-organisation-name" #if (HasConflictWarning) { # class="conflict-warning" # } #>'
                                        + '#: Organisation# #if (Department != null) {# | #:Department# #}#' 
                                    + '</div>'
                                + '#}'
                                + 'if (IsReviewCompleted) {#'
                + '<div class="status-tag"><i class="fas fa-info-circle review-icon"></i><span>' + FlexiJS.Meetings.Meeting.GetMeetingPageResource('MeetingApplicationUserReviewCompleted') + '</span></div>'
                                + '#}'
                                + 'if (HasConflictWarning) {#'
                + '<div class="status-tag"><i class="fas fa-exclamation-triangle organisation-conflict-icon" ></i><span>' + FlexiJS.Meetings.Meeting.GetMeetingPageResource('MeetingApplicationUserConflictWarning') +'</span></div>'
                                + '#}#'
                            + '</div>' 
                        + '</div>'
                      + '</div>'
        });

        columns.push({
            title: FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssignRolesGridRole'),
            width: 200,
            template: '<select class="role-select" onchange="FlexiJS.Meetings.Meeting.OnSelectRoleChange(#:MeetingApplicationUserId#, this.value)">'
                + '<option #if(IsLeadSpeaker) { # selected # } # value="' + constants._meetingMemberStatusLeadSpeaker + '" >'
                    + FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssignRolesGridLeadSpeaker', true)
                + '</option>' 
                + '<option #if(IsSpeaker) { # selected # } # value="' + constants._meetingMemberStatusSpeaker + '">'
                    + FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssigRolesGridSpeaker', true)
                + '</option>' 
                + '<option #if(IsAttendee) { # selected # } # value="' + constants._meetingMemberStatusAttendee + '">'
                    + FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssigRolesGridAttendee', true)
                + '</option>'
                + '<option #if(IsConflicted) { # selected # } # value="' + constants._meetingMemberStatusConflicted + '">'
                    + FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssigRolesGridConflicted', true)
                + '</option>'
                + '<option #if(IsNotAttending) { # selected # } # value="' + constants._meetingMemberStatusNotAttending + '">'
                    + FlexiJS.Meetings.Meeting.GetMeetingPageResource('AssigRolesGridNotAttending', true)
                + '</option>'
                + '</select>'
        });

        return columns;
    }

    const data = {};
    data.meetingApplicationId = meetingApplicationId;

    const memberText = $('#txtMembersFilter').val();

    if (memberText && memberText.trim() !== '') {
        data.memberName = memberText;
    }

    $(assignRolesGridSelector).kendoGrid({
        dataSource: {
            type: 'json',
            transport: {
                read: {
                    url: FlexiJS.Meetings.GetGMSServiceMethodUrl('GetMeetingApplicationUsers'),
                    type: 'POST',
                    contentType: 'application/json',
                    data: data
                },
                parameterMap: function (options) {
                    return JSON.stringify(options);
                }
            },
            schema: {
                type: 'json',
                data: function (r) {
                    return JSON.parse(r.d).Users;
                },
                total: function (r) {
                    return JSON.parse(r.d).TotalUsers;
                },
                model: {
                    fields: {
                        MeetingApplicationUserId: { type: 'number' },
                        IsSpeaker: { type: 'boolean' },
                        IsConflicted: { type: 'boolean' },
                        IsAttendee: { type: 'boolean' },
                        IsNotAttending: { type: 'boolean' },
                        Organisation: { type: 'string' },
                        Department: { type: 'string' },
                        Name: { type: 'string' }
                    }
                }
            }
        },
        pageable: {
            pageSize: 5
        },
        dataBound: function () {
            for (var index = 0; index < constants._meetingApplicationUserRoles.length; index++) {
                const mAppUserRole = constants._meetingApplicationUserRoles[index];
                const roleCheckBox = document.querySelector('[name="rbRole_' + mAppUserRole.MeetingApplicationUserId + '"][value="' + mAppUserRole.RoleId + '"]');

                if (roleCheckBox) {
                    roleCheckBox.checked = true;
                }
            }
        },
        noRecords: { template: FlexiJS.Meetings.Meeting.GetMeetingPageResource('NoCommitteeMembers', true) },
        columns: assignRolesGridColumns()
    });
};

FlexiJS.Meetings.Meeting.OnSelectRoleChange = function (meetingApplicationUserId, roleId) {
    const constants = FlexiJS.Meetings.Meeting.Constants;

    const existingRole = constants._meetingApplicationUserRoles.find(function (role) {
        return role.MeetingApplicationUserId === meetingApplicationUserId;
    });

    if (existingRole) {
        for (var index = 0; index < constants._meetingApplicationUserRoles.length; index++) {
            const role = constants._meetingApplicationUserRoles[index];

            if (role.MeetingApplicationUserId === meetingApplicationUserId) {
                role.RoleId = roleId;
            }
        }
    } else {
        constants._meetingApplicationUserRoles.push({ MeetingApplicationUserId: meetingApplicationUserId, RoleId: roleId });
    }

    const saveRolesButton = document.getElementById('btnSaveRoles');

    if (saveRolesButton.disabled) {
        saveRolesButton.disabled = false;
        saveRolesButton.classList.remove(constants._disabledClassName);
    }
};

FlexiJS.Meetings.EcfMeetings.InitializeConstants = function (addToMeetingButtonId, disabledClassName) {
    FlexiJS.Meetings.EcfMeetings.Constants = {
        _addToMeetingButtonId: addToMeetingButtonId,
        _disabledClassName: disabledClassName
    };
};

FlexiJS.Meetings.EcfMeetings.ToggleAddToMeetingButton = function (meetingSelectorId) {
    const constants = FlexiJS.Meetings.EcfMeetings.Constants;

    const showAddToMeetingButton = function () {
        const ddlCommitteePanel = document.getElementById(meetingSelectorId);
        const selectedOption = ddlCommitteePanel.options[ddlCommitteePanel.selectedIndex];

        if (selectedOption && selectedOption.value) {
            return true;
        }

        return false;
    };

    const addToMeetingButton = document.getElementById(constants._addToMeetingButtonId);
    const show = showAddToMeetingButton();

    if (show) {
        addToMeetingButton.disabled = false;
        addToMeetingButton.classList.remove(constants._disabledClassName);
    } else if (addToMeetingButton.disabled === false) {
        addToMeetingButton.disabled = true;
        addToMeetingButton.classList.add(constants._disabledClassName);
    }
};

FlexiJS.Meetings.EcfMeetings.OnAddToMeetingButtonClick = function (addToMeetingButtonUniqueId) {
    const constants = FlexiJS.Meetings.EcfMeetings.Constants;
    const addToMeetingButton = document.getElementById(constants._addToMeetingButtonId);

    addToMeetingButton.disabled = true;
    addToMeetingButton.classList.add(constants._disabledClassName);

    __doPostBack(addToMeetingButtonUniqueId, '');
};

FlexiJS.Meetings.EcfMeetings.AddApplicationMeetingsGrid = function (applicationMeetingsGridDivSelector, applicationIdHfId, assignedMeetingsSectionSelector) {
    const getEcfMeetingsPageResource = function (resourceKey, withHashReplace) {
        return FlexiJS.Meetings.GetResourceText('gms/ecfmeetings.aspx', resourceKey, withHashReplace);
    };

    const applicationMeetingsGridColumns = function () {
        const columns = [];

        columns.push({
            field: 'Name',
            title: getEcfMeetingsPageResource('Name')
        });

        columns.push({
            field: 'PanelName',
            title: getEcfMeetingsPageResource('Committee')
        });

        columns.push({
            field: 'MeetingDate',
            title: getEcfMeetingsPageResource('Date'),
            type: 'date',
            format: '{0:d}'
        });

        columns.push({
            title: '',
            template: '<a href="/gms/meeting.aspx?id=#= MeetingId#">' + getEcfMeetingsPageResource('Edit', true) + '</a>'
        });

        return columns;
    };

    $(applicationMeetingsGridDivSelector).kendoGrid({
        dataSource: {
            type: 'json',
            transport: {
                read: {
                    url: '/gms/WebServices/GMSService.svc/GetApplicationMeetings',
                    type: 'POST',
                    contentType: 'application/json',
                    data: { applicationId: document.getElementById(applicationIdHfId).value }
                },
                parameterMap: function (options) {
                    return JSON.stringify(options);
                }
            },
            schema: {
                type: 'json',
                data: function (r) {
                    return JSON.parse(r.d).Meetings;
                },
                total: function (r) {
                    return JSON.parse(r.d).TotalMeetings;
                },
                model: {
                    fields: {
                        MeetingId: { type: 'number' },
                        Name: { type: 'string' },
                        PanelName: { type: 'string' },
                        MeetingDate: { type: 'datetime' }
                    }
                }
            },
            change: function (e) {
                if (e.items.length > 0) {
                    $(assignedMeetingsSectionSelector).show();
                }
            },
            pageSize: 5
        },
        pageable: {
            pageSizes: [5, 10, 20]
        },
        noRecords: { template: getEcfMeetingsPageResource('NoMembers', true) },
        columns: applicationMeetingsGridColumns()
    });
};;FlexiJS.Navigation = new Object();

FlexiJS.Navigation.ModuleNavigation = new Object();

FlexiJS.Navigation.ModuleNavigation.Class = 'main-navigation';

FlexiJS.Navigation.ModuleNavigation.Selector = function () { return '.' + FlexiJS.Navigation.ModuleNavigation.Class; };

FlexiJS.Navigation.ModuleNavigation.Show = function (event) {
    event.preventDefault();
    event.stopPropagation();

    FlexiJS.Navigation.ModuleNavigation.Toggle(true);
};

FlexiJS.Navigation.ModuleNavigation.Close = function (event) {
    event.preventDefault();
    event.stopPropagation();

    FlexiJS.Navigation.ModuleNavigation.Toggle(false);
};

FlexiJS.Navigation.ModuleNavigation.Toggle = function (showMenu) {
    if (showMenu === true) {
        $(FlexiJS.Navigation.ModuleNavigation.Selector()).addClass("open");
        FlexiJS.Navigation.ModuleNavigation.ShowOverlay(true);
        $('body').on('click', null, FlexiJS.Navigation.ModuleNavigation.ClickOffHandler);
        $('body').addClass('lock-scroll');
    } else {
        $(FlexiJS.Navigation.ModuleNavigation.Selector()).removeClass("open");
        FlexiJS.Navigation.ModuleNavigation.ShowOverlay(false);
        $('body').off('click', null, FlexiJS.Navigation.ModuleNavigation.ClickOffHandler);
        $('body').removeClass('lock-scroll');
    }
};

FlexiJS.Navigation.ModuleNavigation.ClickOffHandler = function (event) {
    if (!FlexiJS.Common.ClassExistsInEventPath(event, FlexiJS.Navigation.ModuleNavigation.Class)) FlexiJS.Navigation.ModuleNavigation.Toggle(false);
};

FlexiJS.Navigation.ModuleNavigation.ShowOverlay = function (showOverlay) {

    const overlayClass = 'menu-overlay';

    if (showOverlay === true) {
        $('body').append('<div class="' + overlayClass + '"></div>');
    } else {
        $('.' + overlayClass).remove();
    }
};

FlexiJS.Navigation.ModuleSidebarNavigation = new Object();

FlexiJS.Navigation.ModuleSidebarNavigation.SidebarElement = ".fx-columnleft";
FlexiJS.Navigation.ModuleSidebarNavigation.ParentElement = ".fx-columnscenter";
FlexiJS.Navigation.ModuleSidebarNavigation.ButtonElement = ".sidenav-toggle";
FlexiJS.Navigation.ModuleSidebarNavigation.HiddenNavElement = ".fx-column200left";
FlexiJS.Navigation.ModuleSidebarNavigation.AccessibleText = ".sidebar-button-text";

FlexiJS.Navigation.ModuleSidebarNavigation.Toggle = function (event) {

    event.preventDefault();
    event.stopPropagation();

    //resource keys for accessible sidenav expand button
    var collapsedTextTitle = FlexiJS.Resources.GetResourceText('Templates/MasterColumns', 'SideNavigationContainer.collapsed');;
    var expandedTextTitle = FlexiJS.Resources.GetResourceText('Templates/MasterColumns', 'SideNavigationContainer.expanded');

    if ($(FlexiJS.Navigation.ModuleSidebarNavigation.SidebarElement).hasClass("expand-menu")) {
        $(FlexiJS.Navigation.ModuleSidebarNavigation.SidebarElement).removeClass('expand-menu');
        $(FlexiJS.Navigation.ModuleSidebarNavigation.ParentElement).removeClass('adjust-menu-width');
        //accessibility options
        $(FlexiJS.Navigation.ModuleSidebarNavigation.HiddenNavElement).attr('aria-hidden',true);
        $(FlexiJS.Navigation.ModuleSidebarNavigation.ButtonElement).attr('aria-expanded', false);
        $(FlexiJS.Navigation.ModuleSidebarNavigation.ButtonElement).attr('title', collapsedTextTitle);
        $(FlexiJS.Navigation.ModuleSidebarNavigation.AccessibleText).text(collapsedTextTitle); 
    }
    else {
        $(FlexiJS.Navigation.ModuleSidebarNavigation.SidebarElement).addClass('expand-menu');
        $(FlexiJS.Navigation.ModuleSidebarNavigation.ParentElement).addClass('adjust-menu-width');
        //accessibility options
        $(FlexiJS.Navigation.ModuleSidebarNavigation.HiddenNavElement).attr('aria-hidden', false);
        $(FlexiJS.Navigation.ModuleSidebarNavigation.ButtonElement).attr('aria-expanded', true);
        $(FlexiJS.Navigation.ModuleSidebarNavigation.ButtonElement).attr('title', expandedTextTitle);
        $(FlexiJS.Navigation.ModuleSidebarNavigation.AccessibleText).text(expandedTextTitle);
    }
};

FlexiJS.Navigation.UserAvatarMenu = new Object();

FlexiJS.Navigation.UserAvatarMenu.Class = 'avatar-menu';

FlexiJS.Navigation.UserAvatarMenu.Selector = function () { return '.' + FlexiJS.Navigation.UserAvatarMenu.Class; };

FlexiJS.Navigation.UserAvatarMenu.SubMenuId = 'accountSubMenu';

FlexiJS.Navigation.UserAvatarMenu.SubMenuSelector = function () { return '#' + FlexiJS.Navigation.UserAvatarMenu.SubMenuId; };

FlexiJS.Navigation.UserAvatarMenu.AvatarClass = 'avatar-icon';

FlexiJS.Navigation.UserAvatarMenu.AvatarSelector = function () { return '.' + FlexiJS.Navigation.UserAvatarMenu.AvatarClass; };

FlexiJS.Navigation.UserAvatarMenu.AvatarEventHandler = function (event) {

    event.preventDefault();
    event.stopPropagation();

    if (event.type == "keyup") {
        if (event.keyCode == 32) {
           
            FlexiJS.Navigation.UserAvatarMenu.Toggle(!$(FlexiJS.Navigation.UserAvatarMenu.SubMenuSelector()).hasClass('open'));
        }
    } else if (event.type == "click") {
        
        FlexiJS.Navigation.UserAvatarMenu.Toggle(!$(FlexiJS.Navigation.UserAvatarMenu.SubMenuSelector()).hasClass('open'));
    }

};

FlexiJS.Navigation.UserAvatarMenu.Toggle = function (showMenu) {
    const subMenu = $(FlexiJS.Navigation.UserAvatarMenu.SubMenuSelector());
    const avatarIcon = subMenu.closest(FlexiJS.Navigation.UserAvatarMenu.Selector()).find(FlexiJS.Navigation.UserAvatarMenu.AvatarSelector());
    if (showMenu === true) {
        avatarIcon
            .attr("aria-activedescendant", "accountSubMenu")
            .attr("aria-expanded", "true");
        subMenu
            .attr("aria-hidden", "false")
            .addClass("open");
        //select first item on menu
        subMenu
            .find("a")
            .first()
            .focus();

        //bind listener for accessibility key controls
        FlexiJS.Navigation.UserAvatarMenu.BindNavigateMenu();
        $('body').on('click', null, FlexiJS.Navigation.UserAvatarMenu.ClickOffHandler);
    } else {
        avatarIcon
            .removeAttr("aria-activedescendant")
            .removeAttr("aria-expanded");
        subMenu
            .attr("aria-hidden", "true")
            .removeClass("open");

        //unbind menu listeners
        FlexiJS.Navigation.UserAvatarMenu.UnBindNavigateMenu();
        $('body').off('click', null, FlexiJS.Navigation.UserAvatarMenu.ClickOffHandler);
    }
};

FlexiJS.Navigation.UserAvatarMenu.UnBindNavigateMenu = function () {

    $('body').unbind("keydown");

}

FlexiJS.Navigation.UserAvatarMenu.NavigateItem = function (direction) {

    var accountMenu = $('#accountMenu');
    var focusedElement = $('#accountMenu li a:focus')

    //check element is focused
    if ($(focusedElement).length > 0) {

        //get index
        var elementIndex = $(focusedElement).parent("li").index();
        var elementListSize = accountMenu.children("li").length;

        if (direction == "previous") {

            //check if first element, which is number 1 as we ignore the first list item (0)
            if (elementIndex == 1) {
                $(accountMenu).children('li').eq(elementListSize-1).children('a').focus();
            }
            else {
                $(accountMenu).children('li').eq(elementIndex).prev().children('a').focus();
            }

        }
        if (direction == "next") {

            //check if last element, then jump back to position 1. Not position 0.
            if (elementIndex == elementListSize-1) {
                $(accountMenu).children('li').eq(1).children('a').focus();
            }
            else {
                $(accountMenu).children('li').eq(elementIndex).next().children('a').focus();
            }

        }

    }

}

FlexiJS.Navigation.UserAvatarMenu.BindNavigateMenu = function () {

    const avatarMenu = $("#avatarMenu");

    $('body').keydown(function (event) {
        var key = event.key,
            flag = false;

        switch (key) {
            case ' ':
            case 'ArrowDown':
            case 'Down':
                FlexiJS.Navigation.UserAvatarMenu.NavigateItem("next");
                flag = true;
                break;

            case 'Esc':
            case 'Escape':
                avatarMenu.focus();
                FlexiJS.Navigation.UserAvatarMenu.Toggle(false);
                flag = true;
                break;

            case 'Up':
            case 'ArrowUp':
                FlexiJS.Navigation.UserAvatarMenu.NavigateItem("previous");
                flag = true;
                break;

            default:
                break;
        }

        if (flag) {
            event.stopPropagation();
            event.preventDefault();
        }
    });

}

FlexiJS.Navigation.UserAvatarMenu.ClickOffHandler = function (event) {
    if (!FlexiJS.Common.ClassExistsInEventPath(event, FlexiJS.Navigation.UserAvatarMenu.Class)) FlexiJS.Navigation.UserAvatarMenu.Toggle(false);
};

FlexiJS.Navigation.Culture = new Object();

FlexiJS.Navigation.Culture.CultureChange = function(event) {
    $.ajax({
        url: "/WebServices/PageService.svc/ChangeCulture",
        type: "POST",
        contentType: "application/json",
        data: JSON.stringify({ Culture: event.target.value }),
        success: function (Result) { location.reload(); },
        error: function (Result) {
            debugger;
        }
    });

};

FlexiJS.Navigation.Area = new Object();

FlexiJS.Navigation.Area.AreaChange = function(event) {
    var area = event.target.value;
    if (area != '0') {
        if (window.location.search.length > 0) {
            var url;
            url = removeURLParam('/' + window.location.pathname.replace("/", "") + window.location.search, 'farea');
            if (url.indexOf('?') != -1) {
                window.location.href = url + '&farea=' + area;
            }
            else {
                window.location.href = url + '?farea=' + area;
            }
        }
        else {
            window.location.href = removeURLParam('/' + window.location.pathname.replace("/", ""), 'farea') + '?farea=' + area
        }
    }
    else {
        window.location.href = removeURLParam('/' + window.location.pathname.replace("/", "") + window.location.search, 'farea');
    }
};;/*


  _____   _                 _       _   ____        ____    ____    _____ 
 |  ___| | |   ___  __  __ (_)     | | / ___|      |  _ \  |  _ \  |  ___|
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |_) | | | | | | |_   
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  |  __/  | |_| | |  _|  
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_|     |____/  |_|    
                                                                          


*/

FlexiJS.PDF = new Object();

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/

FlexiJS.PDF.PollingScheduled = false;
FlexiJS.PDF.CvEnabled = false;
FlexiJS.PDF.HideCvOptions = false;


/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               
*/

FlexiJS.PDF.SetPDFSetEmailPref = function () {
    FlexiJS.Utils.Browser.AddToLocalStorage("GeneratePDF_SendEmail", $('[id*=chkSendEmailPostGeneration]').prop('checked'));
};

FlexiJS.PDF.DownloadType = function (type, value, isMultiple, selected) {
    var type = '<div>' +
        '<input type="radio" id="pdfDownloadType' + type + '" name="pdfDownloadType" value="' + value + '"' + (selected ? ' checked' : '') + ' onchange="FlexiJS.PDF.DownloadTypeChange(\'' + value + '\', ' + isMultiple +');">' +
        '<label for="pdfDownloadType' + type + '">' + FlexiJS.Resources.GetResourceText('PDFGeneration', 'DownloadType' + type) + '<small>' + FlexiJS.Resources.GetResourceText('PDFGeneration', 'DownloadType' + type + 'Description') + '</small></label>' +
        '</div>';

    return type;
}

FlexiJS.PDF.SetupAdvancedOptions = function () {
    var selectedType = $('[name=pdfDownloadType]:checked').val();

    var pdfDownloadOptionHolderGuidance = $('#pdfDownloadOptionHolderGuidance');
    var pdfDownloadOptionHolderCv = $('#pdfDownloadOptionHolderCv');
    var pdfDownloadOptionHolderCompletedReviews = $('#pdfDownloadOptionHolderCompletedReviews');

    if (selectedType == FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationRelatedAndReviewHistory) {
        pdfDownloadOptionHolderCompletedReviews.show();
    } else {
        pdfDownloadOptionHolderCompletedReviews.hide();
    }

    if (selectedType == FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationRelatedAndReviewHistory || selectedType == FlexiJS.Enums.JobItem.PDFGenerationType.ReportingFormOnly) {
        pdfDownloadOptionHolderCv.hide();
    } else {
        pdfDownloadOptionHolderCv.show();
    }

    if (selectedType == FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationAbstract) {
        pdfDownloadOptionHolderGuidance.hide();
    } else {
        pdfDownloadOptionHolderGuidance.show();
    }
}

FlexiJS.PDF.DownloadOption = function (option) {
    var option = '<li id="pdfDownloadOptionHolder' + option + '">' +
        '<input type="checkbox" id="pdfDownloadOption' + option + '">' +
        '<label for="pdfDownloadOption' + option + '">' + FlexiJS.Resources.GetResourceText('PDFGeneration', 'DownloadOption' + option) + '<small>' + FlexiJS.Resources.GetResourceText('PDFGeneration', 'DownloadOption' + option + 'Description') + '</small></label>' +
        '</li>';

    return option;
}

FlexiJS.PDF.ToggleOption = function (setupOptions) {
    if (setupOptions) {
        FlexiJS.PDF.SetupAdvancedOptions();
    }
    $('.sliding-menu').toggleClass('show-secondary');
}

FlexiJS.PDF.DownloadTypeChange = function (type, isMultiple) {
    // Determine if Advanced Options should be shown
    var cvOption = $('#pdfDownloadOptionHolderCv').length == 1 && type != FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationRelatedAndReviewHistory && type != FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationWithRelated && FlexiJS.PDF.CvEnabled && !FlexiJS.PDF.HideCvOptions;
    var completedReviews = $('#pdfDownloadOptionHolderCompletedReviews').length == 1 && type == FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationRelatedAndReviewHistory;
    var guidance = type != FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationAbstract;
    var advanced = $('.advanced-toggle');

    if (cvOption || isMultiple || guidance || completedReviews) {
        advanced.show();
    } else {
        advanced.hide();
    }
}


FlexiJS.PDF.PopupDecision = function (grid, entityType, entityIds, audienceType, hideCvOptions) {

    FlexiJS.PDF.HideCvOptions = hideCvOptions == null ? false : hideCvOptions;

    var isMultiple = entityIds.split(':').length > 1;

    var optionList = "";
    var optionType = "";

    switch (audienceType) {
        case FlexiJS.Enums.System.PDFGenerationAudience.GMSAdmin:
            optionList += FlexiJS.PDF.DownloadType('Form', FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationOnly, isMultiple, true);
            optionList += FlexiJS.PDF.DownloadType('Abstract', FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationAbstract, isMultiple);
            optionList += FlexiJS.PDF.DownloadType('Complete', FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationRelatedAndReviewHistory, isMultiple);
            optionList += FlexiJS.PDF.DownloadType('Report', FlexiJS.Enums.JobItem.PDFGenerationType.ReportAndClaims, isMultiple);
            optionList += FlexiJS.PDF.DownloadType('Reviewer', FlexiJS.Enums.JobItem.PDFGenerationType.Reviewer, isMultiple);
            optionList += FlexiJS.PDF.DownloadType('History', FlexiJS.Enums.JobItem.PDFGenerationType.ReviewerHistory, isMultiple);
            optionType = "admin-user";
            break;
        case FlexiJS.Enums.System.PDFGenerationAudience.Applicant:
            optionList += FlexiJS.PDF.DownloadType('Form', FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationOnly, isMultiple, true);
            optionList += FlexiJS.PDF.DownloadType('ApplicationWithRelated', FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationWithRelated, isMultiple);
            optionType = "applicant-user";
            break;
        case FlexiJS.Enums.System.PDFGenerationAudience.Reviewer:
            optionList += FlexiJS.PDF.DownloadType('Form', FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationOnly, isMultiple, true);
            optionList += FlexiJS.PDF.DownloadType('Abstract', FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationAbstract, isMultiple);
            optionList += FlexiJS.PDF.DownloadType('Complete', FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationRelatedAndReviewHistory, isMultiple);
            optionType = "reviewer-user";
            break;
        case FlexiJS.Enums.System.PDFGenerationAudience.ReportingOnly:
            optionList += FlexiJS.PDF.DownloadType('ReportingForm', FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationOnly, false, true);
            optionType = "report-user";
            hideCvOptions = true;
            break;
    }

    var html = '<div class="bulk-tool-wrapper margin-top clearfix">';
    html += '<div class="modal">';
    html += '   <div class="modal-header">';
    html += '       <h1 class="modal-header-title">' + FlexiJS.Resources.GetResourceText('PDFGeneration', 'DownloadTitle')  +'</h1>';
    html += '       <p>' + FlexiJS.Resources.GetResourceText('PDFGeneration', 'DownloadInstruction') + '</p>';
    html += '   </div>';
    html += '   <div class="modal-body ' + optionType + '">';
    html += '       <div class="sliding-menu">';
    html += '           <div class="main-panel">';
    html += '               <div class="button-grid">';

    html += optionList;

    html += '               </div>';
    html += ' <button class="advanced-toggle j-toggle-advanced" onclick="FlexiJS.PDF.ToggleOption(true);"><i class="fas fa-cog"></i>' + FlexiJS.Resources.GetResourceText('PDFGeneration', 'ShowAdvanced') + '</button>';

    // Only show sort order if multiple - else add a hidden field with the default order
    if (isMultiple) {
        html += ' <div class="sort-options float-right">';
        html += '   <label for="pdfGenerationOrder">' + FlexiJS.Resources.GetResourceText('PDFGeneration', 'PDFGenerationOrder') + '</label>';
        html += '   <select class="select" id="pdfGenerationOrder">';
        html += '       <option value="' + FlexiJS.Enums.System.PDFGenerationOrder.ReferenceNumber + '">' + FlexiJS.Enums.GetEnumsText('System', 'PDFGenerationOrder', FlexiJS.Enums.System.PDFGenerationOrder.ReferenceNumber) + '</option>';
        html += '       <option value="' + FlexiJS.Enums.System.PDFGenerationOrder.Surname + '">' + FlexiJS.Enums.GetEnumsText('System', 'PDFGenerationOrder', FlexiJS.Enums.System.PDFGenerationOrder.Surname) + '</option>';
        html += '   </select>';
        html += ' </div>';
    } else {
        html += '<input type="hidden" id="pdfGenerationOrder" value="' + FlexiJS.Enums.System.PDFGenerationOrder.ReferenceNumber + '" />';
    }
    html += ' </div>';

    html += '<div class="secondary-panel">';

    html += '<div class="options"><ul>';

    // Display Show Completed Reviews option
    if (audienceType == FlexiJS.Enums.System.PDFGenerationAudience.GMSAdmin || audienceType == FlexiJS.Enums.System.PDFGenerationAudience.Reviewer) {
        html += FlexiJS.PDF.DownloadOption('CompletedReviews');
    }

    // Display Include CVs option
    if (audienceType == FlexiJS.Enums.System.PDFGenerationAudience.GMSAdmin) {

        if (FlexiJS.PDF.CvEnabled && !FlexiJS.PDF.HideCvOptions) {
            html += FlexiJS.PDF.DownloadOption('Cv');
        }
        
    }

    if (isMultiple) {
        html += FlexiJS.PDF.DownloadOption('Separate');
    }
    html += FlexiJS.PDF.DownloadOption('Guidance');
    // Only commenting Email out as we 'may' want to enable it in the future
    // html += FlexiJS.PDF.DownloadOption('Email');
    html += '</ul></div>';

    html += ' <button class="close-advanced j-toggle-advanced" onclick="FlexiJS.PDF.ToggleOption();"><i class="fas fa-arrow-circle-left"></i>' + FlexiJS.Resources.GetResourceText('PDFGeneration', 'Back') + '</button>';
    html += '</div > ';

    html += '       </div>';
    html += '   </div>';
    html += '</div>';
    html += '</div>';

    // We want to sort if this is multiple entities and we've got a grid (and therefore data)
    if (grid != null) {
        // Let's transform the applications
        var apps = new Array();
        for (var key in grid.SelectedRows) {
            if (grid.SelectedRows[key].Selected) {
                apps.push(grid.SelectedRows[key].DataItem);
            }
        }
        entityIds = FlexiJS.PDF.GetSortedApplicationIds(apps).join(":");
    }

    var actions = [
        {
            text: FlexiJS.Resources.GetResourceText('PDFGeneration', 'DownloadTitle'),
            primary: true,
            actionfunction: function () {

                var selectedType = $('[name=pdfDownloadType]:checked').val();
                var email = FlexiJS.PDF.GetDownloadOptionChecked('Email');
                var cv = FlexiJS.PDF.GetDownloadOptionChecked('Cv');
                var separate = FlexiJS.PDF.GetDownloadOptionChecked('Separate');
                var guidance = FlexiJS.PDF.GetDownloadOptionChecked('Guidance');
                var order = parseInt($('#pdfGenerationOrder').val());
                var completedreviews = FlexiJS.PDF.GetDownloadOptionChecked('CompletedReviews');

                FlexiJS.PDF.RequestGeneration(
                    entityType,
                    FlexiJS.GMS.QuickAction.GetSelected().join(":"),
                    selectedType,
                    email,
                    cv,
                    separate,
                    guidance,
                    order,
                    completedreviews
                );
            }
        },
        {
            text: FlexiJS.Resources.GetResourceText('PDFGeneration', 'DecisionActionCancel'),
            cancel: true
        }];

    var settings =
    {
        title: FlexiJS.Resources.GetResourceText('PDFGeneration', 'DownloadTitle'),
        content: html,
        actions: actions,
        showSummaryGrid: false,
        filterFundType: false,
        fullScreen: false,
        styleVersion: 2
    };

    FlexiJS.GMS.QuickAction.OpenDialog(grid, entityIds, settings);
};

FlexiJS.PDF.GetDownloadOptionChecked = function (option) {
    var pdfDownloadOption = $('#pdfDownloadOption' + option);
    if (pdfDownloadOption.length == 1) {
        return pdfDownloadOption[0].checked;
    } else {
        return false;
    }
}

FlexiJS.PDF.ShowPopupDecisionFromApplicationIdList = function (IdList, AudienceType) {
    var apps = IdList[0].toString();
    for (var i = 1; i < IdList.length; i++) {
        apps += ":" + IdList[i];
    }
    FlexiJS.PDF.PopupDecision(null, 44644, apps, AudienceType, false);
};

FlexiJS.PDF.FormatOption = function (option, id, entityType, defaultOption, limitOptions) {
    var label = "";
    var tagline = "";
    var checked = false;
    switch (option) {
        case FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationOnly:
            if (entityType == FlexiJS.Enums.System.EntityType.Engagement) {
                label = FlexiJS.Resources.GetResourceText('PDFGeneration', 'DecisionOptionReportingOnly');
                tagline = FlexiJS.Resources.GetResourceText('PDFGeneration', 'DecisionOptionReportingOnlyTagline');
            }
            else {
                label = FlexiJS.Resources.GetResourceText('PDFGeneration', 'DecisionOptionAppOnly');
                tagline = FlexiJS.Resources.GetResourceText('PDFGeneration', 'DecisionOptionAppOnlyTagline');
            }
            checked = option == defaultOption || (defaultOption == FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationRelatedAndReviewHistory && limitOptions);
            break;
        case FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationWithRelated:
            label = FlexiJS.Resources.GetResourceText('PDFGeneration', 'DecisionOptionAppWithRelated');
            tagline = FlexiJS.Resources.GetResourceText('PDFGeneration', 'DecisionOptionAppWithRelatedTagline');
            checked = option == defaultOption;
            break;
        case FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationRelatedAndReviewHistory:
            label = FlexiJS.Resources.GetResourceText('PDFGeneration', 'DecisionOptionAppWithRelatedAndReviewHistory');
            tagline = FlexiJS.Resources.GetResourceText('PDFGeneration', 'DecisionOptionAppWithRelatedAndReviewHistoryTagline');
            checked = option == defaultOption;
            break;
        case FlexiJS.Enums.JobItem.PDFGenerationType.ApplicationAbstract:
            label = FlexiJS.Resources.GetResourceText('PDFGeneration', 'ApplicationAbstract');
            tagline = FlexiJS.Resources.GetResourceText('PDFGeneration', 'ApplicationAbstractTagline');
            checked = option == defaultOption;
            break;
    }

    var option = '<li><input type="radio" name="pdfType" value="' + option + '" id="' + id + '"';
    option += (checked ? 'checked="checked"' : '') + ' />';
    option += '<label for="' + id + '"" class="pdfaction"><i class="fa fa-files-o"></i><div class="radio-list-text"><div class="radio-list-title">' + label + '</div>';
    option += '<div class="radio-list-tagline">' + tagline + '</div></div></label></li>';

    return option;

};

FlexiJS.PDF.RequestGeneration = function (entityType, entityIds, type, email, cv, separate, guidance, order, completedreviews) {

    if (typeof (sendEmail) != 'undefined' && sendEmail != null && sendEmail == false) {
        sendEmail = false;
    }
    else {
        sendEmail = true;
    }

    if (typeof (forReviewers) == 'undefined' || forReviewers == null || forReviewers == false) {
        forReviewers = false;
    }

    var sendData =
    {
        EntityType: entityType,
        EntityIds: entityIds,
        Type: type,
        SendEmail: email,
        Cv: cv,
        Separate: separate,
        IncludeFormItemGuidanceNotes: guidance,
        Order: order,
        IncludeCompletedReviews: completedreviews
    };

    $.ajax({
        url: "/WebServices/PageService.svc/RequestPDFGeneration",
        type: "POST",
        contentType: "application/json",
        data: JSON.stringify(sendData),
        success: function (Result) {

            if (Result.d && Result.d.Result == true) {
                showNotification(
                    {
                        type: "success",
                        message: Result.d.ResultMessage,
                        autoClose: true,
                        duration: 7
                    }
                );
                FlexiJS.PDF.CheckPolling();
            }
            else {
                if (Result.d.ResultDetail == "UserIsAnonymous") {
                    NavigateLogin(window.location.href, Result.d.ResultDetail);
                } else {
                    showNotification(
                        {
                            type: "error",
                            message: Result.d.ResultMessage,
                            autoClose: true,
                            duration: 7
                        }
                    );
                }
            }
        },
        error: function (Result) {

            showNotification(
                {
                    type: "error",
                    message: Result.d.ResultMessage,
                    autoClose: true,
                    duration: 7
                }
            );

        }
    });
};

FlexiJS.PDF.CheckPolling = function (Force) {

    if (FlexiJS.PDF.PollingScheduled == true && (typeof (Force) == 'undefined' || Force != true)) {
        return
    }
    FlexiJS.PDF.PollingScheduled = true;
    // We just call and see if anything is going!
    $.ajax({
        url: "/WebServices/PageService.svc/CheckJobPolling",
        type: "POST",
        contentType: "application/json",
        success: function (Result) {

            if (Result.d && Result.d.Result == true) {
                var data = JSON.parse(Result.d.ResultDetail);
                if (data.Notifications.length > 0) {
                    var message = "";
                    for (var i = 0; i < data.Notifications.length; i++) {
                        FlexiJS.Notification.ShowToast(data.Notifications[i].UniqueId, data.Notifications[i].Message, data.Notifications[i].NotificationType, data.Notifications[i].ResultType, data.Notifications[i].Title, data.Notifications[i].ActionUrl, data.Notifications[i].ActionUrlText);
                    }
                }

                if (data.ContinuePolling) {
                    FlexiJS.PDF.CheckPolling.PollingScheduled = true;
                    setTimeout(function () { FlexiJS.PDF.CheckPolling(true); }, 5000);
                }
                else {
                    FlexiJS.PDF.PollingScheduled = false;
                }
            }

        }
    });
};

FlexiJS.PDF.GetSortedApplicationIds = function (apps) {
    var sorted = apps.sort(function (a, b) {
        if (a.ApplicantLastname.toLowerCase() < b.ApplicantLastname.toLowerCase()) {
            return -1;
        }
        if (a.ApplicantLastname.toLowerCase() > b.ApplicantLastname.toLowerCase()) {
            return 1;
        }
        // Lastname is the same
        if (a.ApplicantFirstname.toLowerCase() < b.ApplicantFirstname.toLowerCase()) {
            return -1;
        }
        if (a.ApplicantFirstname.toLowerCase() > b.ApplicantFirstname.toLowerCase()) {
            return 1;
        }
        return 0;
    });
    var appIds = new Array();
    for (var i = 0; i < apps.length; i++) {
        appIds.push(apps[i].ApplicationId);
    }
    return appIds;
};

FlexiJS.PDF.DeleteDownload = function (jobId) {

    FlexiJS.Kendo.Dialogs.Confirm(
        FlexiJS.Resources.GetResourceText('mydownloads.aspx', 'DeleteConfirmTitle'),
        FlexiJS.Resources.GetResourceText('mydownloads.aspx', 'DeleteConfirm'),
        [
            {
                text: FlexiJS.Resources.GetResourceText('mydownloads.aspx', 'DeleteConfirmOk'),
                actionfunction: function () {
                    // Call delete
                    Fluent.Website.PageService.DeleteDownload(jobId,
                        function (result) {
                            // On success
                            if (result.Result) {
                                // Get the item to delete and the date-div id associated with it
                                var item = $('#download-item-' + jobId);
                                var divId = item.data("date-div");
                                var parent = item.parent();
                                
                                // Remove the item
                                item.remove();

                                // Check if this was the last item for that date - if so remove the date div
                                if ($('[data-date-div=' + divId + ']').length == 0) {
                                    $('#' + divId).remove();
                                }

                                // Check if this is the last download on the page (no downloads left)
                                if (parent) {
                                    if ($(parent).find('.download-item').length == 0) {
                                        $('[id$=spNoDownloadsText]').text(FlexiJS.Resources.GetResourceText('mydownloads.aspx', 'NoDownloads'));
                                        $('[id$=divNoDownloadsContainer]').show();
                                    }
                                    else {
                                        $('[id$=divNoDownloadsContainer]').hide();
                                    }
                                }
                                else {
                                    $('[id$=divNoDownloadsContainer]').hide();
                                }
                            }
                            else {
                                showNotificationMessage('error', result.ResultMessage);
                            }
                        },
                        function (result) {
                            showNotificationMessage('error', result.ResultMessage);
                        });
                }
            },
            {
                text: FlexiJS.Resources.GetResourceText('mydownloads.aspx', 'DeleteConfirmCancel'),
            }
        ]);
}

FlexiJS.PDF.SetupDownloadTooltips = function () {
    var t=$(".download-item-more").kendoTooltip(
        {
            showOn: "click",
            width: 250,
            position: "top",
        }).data("kendoTooltip");
}

FlexiJS.PDF.ReplaceFieldSetsWithDivs = function () {
    var allFs = $("fieldset");
    for (i = 0; i < allFs.length; i++) {
        var fieldSet = $(allFs[i]);
        var fieldsetContent = fieldSet.html();
        fieldSet.replaceWith('<div>' + fieldsetContent + '</div>');
    };
}

;/*
  _____   _                 _       _   ____        ____                                  
 |  ___| | |   ___  __  __ (_)     | | / ___|      |  _ \    ___    _ __    _   _   _ __  
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |_) |  / _ \  | '_ \  | | | | | '_ \ 
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  |  __/  | (_) | | |_) | | |_| | | |_) |
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_|      \___/  | .__/   \__,_| | .__/ 
                                                                   |_|             |_|    

        Popup code
    
        Requirements:
            jQuery
            Kendo
            Telerik Radcontrols (ughhh)
*/

FlexiJS.Popup = new Object();

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/

FlexiJS.Popup.CallbackSender = {};
FlexiJS.Popup.FocusableElements = 'input, button, select, textarea, text, search, article, a';

/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/

FlexiJS.Popup.ReloadWindow = function () {
    // Description: Reloads a rad window
    // Created: ?? - ??
    // Updated: 13/03/2017 - v3.15.1 - JD - Moved from popup.js to new structure
    if (window.radWindow) {
        var financeWindow = GetRadWindow();
        if (financeWindow != null) {
            financeWindow.BrowserWindow.refresh();
        }
    }
};

FlexiJS.Popup.GetNewWindowPopupURL = function (url, refreshParent, queryStringToRemove) {
    // Description: Generates a URL from the passed parameters
    // Created: ?? - ??
    // Updated: 13/03/2017 - v3.15.1 - JD - Moved from popup.js to new structure
    return url + "&refreshParent=" + refreshParent + "&queryStringToRemove=" + queryStringToRemove;
};

FlexiJS.Popup.CreateCallbackDataItem = function (callbackName, callbackData) {
    // Description: Creates a callback dataitem, used to standardise data sent via window postmessage functions
    // Created: 13/03/2017 - v3.15.1 - JD
    return { Command: callbackName, Data: callbackData };
};

FlexiJS.Popup.SendCallback = function (target, data) {
    // Description: Sends a postmessage call to the target window with the provided data object. Data object is stringified to maintain compatability
    // Created: 13/03/2017 - v3.15.1 - JD
    target.postMessage(JSON.stringify(data), "*");
};

FlexiJS.Popup.AddCallbackListener = function (listenerName, callbackFunction, fireOnce) {
    // Description: Adds a listener function to the current window for a postmessage sent to it.
    //              Callback function needs to take 2 params, source (the source window) and data (any data passed with the event)
    // Created: 13/03/2017 - v3.15.1 - JD

    // Namespace our event listener, so it is easily removed & allows for different callbacks to happen concurrently
    var eventName = "message." + listenerName;

    // Remove an existing callback of the same name and set up the new listener
    $(window).off(eventName).on(eventName, function (e) {
        // The data from the message should have a command & data property, but will be stringified for browser compatibility
        var data = JSON.parse(e.originalEvent.data);

        if (data.hasOwnProperty('Command') && listenerName == data.Command) {
            if (data.hasOwnProperty('Data')) {
                //Call the callback with the data property
                callbackFunction(e.originalEvent.source, data.Data);
            } else {
                // There is no data property, call the callback without it
                callbackFunction(e.originalEvent.source, null);
            }

            if (fireOnce == true) $(window).off(eventName);
        }
    });
};

FlexiJS.Popup.AddCallbackSender = function (sendtowindow, failCallback, postData) {
    // Description: Sends a callback to the provided window. If the window is closed before the callback happens the failcallback function will be called
    // Created: 13/03/2017 - v3.15.1 - JD
    FlexiJS.Popup.RemoveCallbackSender(postData.Command);

    FlexiJS.Popup.CallbackSender[postData.Command] = setInterval(function () {
        try {
            //Stop x domain calls
            if (sendtowindow.document.domain === document.domain) {
                if (sendtowindow.document.readyState === "complete") {
                    // we're here when the child window returned to our domain
                    FlexiJS.Popup.SendCallback(sendtowindow, postData);
                }
            }
            if (sendtowindow.closed) { FlexiJS.Popup.RemoveCallbackSender(postData.Command); failCallback(); return; }
        }
        catch (e) {
            // we're here when the child window has been navigated away or closed
            if (sendtowindow.closed) { FlexiJS.Popup.RemoveCallbackSender(postData.Command); failCallback(); return; }
        }
    }, 500);
};

FlexiJS.Popup.RemoveCallbackSender = function (commandName) {
    // Description: Removes a callback from the callback cache
    // Created: 13/03/2017 - v3.15.1 - JD
    if (FlexiJS.Popup.CallbackSender.hasOwnProperty(commandName)) {
        clearInterval(FlexiJS.Popup.CallbackSender[commandName]);
        //Delete is used to remove the callback property so it is not just set to null
        delete FlexiJS.Popup.CallbackSender[commandName];
    }
};

FlexiJS.Popup.CreatePopupOptions = function (width, height, hidemenu, centerV, centerH, scrollbars) {
    // Description: Creates a popup windows options string
    // Created: 13/03/2017 - v3.15.1 - JD
    var windowOptions = [];
    if (hidemenu && hidemenu == true) windowOptions.push('menubar=no');
    if (width && width >= 100) {
        windowOptions.push('width=' + width);
        if (centerH == true) windowOptions.push('left=' + (((window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width) / 2) - (width / 2)) + (window.screenLeft != undefined ? window.screenLeft : screen.left));
    }
    if (height && height >= 100) {
        windowOptions.push('height=' + height);
        if(centerV == true) windowOptions.push('top=' + (((window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height) / 2) - (height / 2)) + (window.screenTop != undefined ? window.screenTop : screen.top));
    }
    if (scrollbars && scrollbars == true) windowOptions.push('scrollbars=yes')
    if (scrollbars && scrollbars == false) windowOptions.push('scrollbars=no')

    if (windowOptions.length > 0) {
        return windowOptions.join();
    } else {
        return '';
    }
};

FlexiJS.Popup.OpenNewWindowWithCallback = function (url, width, height, hidemenu, callbackFunction, callbackFunctionListenerName, callbackFireOnce, failCallback, postData) {
    // Description: Opens a popup window with a callback function, returns true if window was returned, otherwise false (IE Security issue that prevents callbacks when window is opened cross site)
    // Created: 13/03/2017 - v3.15.1 - JD
    var myWindow = window.open(url, "OrcidPopup", FlexiJS.Popup.CreatePopupOptions(width, height, hidemenu, true, true, true));

    if (myWindow != null) {
        FlexiJS.Popup.AddCallbackListener(
            callbackFunctionListenerName,
            callbackFunction,
            callbackFireOnce
        );
        FlexiJS.Popup.AddCallbackSender(myWindow, failCallback, postData);
        return true;
    } else {
        return false;
    }
};

FlexiJS.Popup.OpenNewWindow = function (url, width, height, hidemenu) {
    // Description: Opens a popup window
    // Created: 13/03/2017 - v3.15.1 - JD
    window.open(url, "_blank", FlexiJS.Popup.CreatePopupOptions(width, height, hidemenu, true, true, true));
};

FlexiJS.Popup.CreateKendoWindow = function (windowid, closelabel, title, subtitle, scrollable) {
    //Create the window & return the kendo window object    
    var isScrollable = scrollable != undefined ? scrollable : false;
    return ReturnKendoWindow(windowid, closelabel, title, subtitle, isScrollable);
}

function ReturnKendoWindow(windowid, closelabel, title, subtitle, scrollable) {
    return $(
        FlexiJS.Kendo.Templates.RenderTemplate(
            'flexikendowindow',
            'MaximisedWindow',
            {
                id: windowid,
                closelabel: closelabel,
                title: title,
                subtitle: subtitle
            },
            false
        )
    )
        .kendoWindow({
            title: false,
            visible: false,
            draggable: false,
            modal: true,
            resizable: false,
            scrollable: scrollable,
            animation: {
                open: { effects: "zoom:in", duration: 100 },
                close: { effects: "zoom:out", duration: 100 }
            },
            width: "90%",
            height: "90%",
            activate: function (e) {
                FlexiJS.Kendo.Dialogs.ResizeFullScreenPopupContent("#" + windowid);
            },
            close: function(e){
                //Clear Temp Data On Close                
                e.sender.destroy();
                $('body').removeAttr('style');
            }
        })
        .data('kendoWindow');
}

FlexiJS.Popup.GetCurrentRadWindowName = function () {
    //function isOfferStagePopup(){
    // Checks if the popup is in the offer stage
    var returnValue = "";

    try {
        var oWindow = GetRadWindow();
        if (typeof (oWindow) != 'undefined') return oWindow.get_name();
    }
    catch(ex){
        returnValue = "";
    }

    return returnValue;
};

FlexiJS.Popup.IsOfferStagePopup = function () {
    //function isOfferStagePopup(){
    // Checks if the popup is in the offer stage
    return (FlexiJS.Popup.GetCurrentRadWindowName() == 'OfferStageMonitoring');
};

FlexiJS.Popup.CloseCurrentRadWindow = function (){
    //function forceWindowClose() {
    var oWindow = GetRadWindow();
    if (typeof (oWindow) != 'undefined') oWindow.close();
};
    
FlexiJS.Popup.ResizeCurrentRadWindowForEmail = function (){
    //function forceWindowEmailResize() {
    var oWindow = GetRadWindow();
    if (typeof (oWindow) != 'undefined') {
        oWindow.setSize(1010, 760);
        oWindow.center();
    }
};

FlexiJS.Popup.ShowProgressLoader = function (ProgressLabel, ProgressPleaseWait, ProgressMessage) {
    var loader = "<div id='progress-dialog' class='loading-overlay' aria-modal='true' aria-labeledby='proccessingDialog'>";
    loader += "<div class='progress-container'>";
    loader += "<div aria-hidden='true' id='progress-circle' class='progress-circle'>";
    loader += "<span class='p-h'></span><span class='p-f'></span>";
    loader += "<span id='progress'></span>";
    loader += "<span class='progress-label'>" + ProgressLabel + "</span>";
    loader += "</div>";
    loader += "<h1 id='proccessingDialog'><span>" + ProgressPleaseWait + "</span>" + ProgressMessage + "</h1>";
    loader += "</div>";
    loader += "</div>";
    $("body").append($(loader));
};

FlexiJS.Popup.RemoveProgressLoader = function () {
    var progressLoader = $("#progress-dialog");
    if (progressLoader) {
        progressLoader.remove();
    }
};


FlexiJS.Popup.AddModalBacking = function(name) {
    $('<div name="' + name + '" class="modal-backing"></div>').appendTo('body');
};

FlexiJS.Popup.HandleWindowTabbing = function (e, popupSelector, closeSelector) {
    switch (e.keyCode) {
        case 9://Tab
            var focusable = document.querySelector(popupSelector).querySelectorAll(FlexiJS.Popup.FocusableElements);
            if (focusable.length) {
                var first = focusable[0];
                var last = focusable[focusable.length - 1];

                if (e.shiftKey && e.target === first) {
                    last.focus();
                    e.preventDefault();
                } else if (!e.shiftKey && e.target === last) {
                    first.focus();
                    e.preventDefault();
                }
            }
            break;

        case 27://Escape
            $(popupSelector).find(closeSelector).click();
            break;
    }
};

FlexiJS.Popup.FocusToFirstElement = function (popupElementId) {
    var window = $("#" + popupElementId);
    if (window) {
        if (window.length > 0) {
            var focusable = window[0].querySelectorAll('input,button,select,textarea,text,search,article');
            if (focusable) {
                if (focusable.length) focusable[0].focus();
            }
        }
    }
};

/*
  _____    ___    ____     ___          _____             ____             ____                   _                _     _____             _   _                                                                
 |_   _|  / _ \  |  _ \   / _ \   _    |_   _|   ___     | __ )    ___    |  _ \    ___    _ __  | |_    ___    __| |   |_   _|   ___     | \ | |   __ _   _ __ ___     ___   ___   _ __     __ _    ___    ___ 
   | |   | | | | | | | | | | | | (_)     | |    / _ \    |  _ \   / _ \   | |_) |  / _ \  | '__| | __|  / _ \  / _` |     | |    / _ \    |  \| |  / _` | | '_ ` _ \   / _ \ / __| | '_ \   / _` |  / __|  / _ \
   | |   | |_| | | |_| | | |_| |  _      | |   | (_) |   | |_) | |  __/   |  __/  | (_) | | |    | |_  |  __/ | (_| |     | |   | (_) |   | |\  | | (_| | | | | | | | |  __/ \__ \ | |_) | | (_| | | (__  |  __/
   |_|    \___/  |____/   \___/  (_)     |_|    \___/    |____/   \___|   |_|      \___/  |_|     \__|  \___|  \__,_|     |_|    \___/    |_| \_|  \__,_| |_| |_| |_|  \___| |___/ | .__/   \__,_|  \___|  \___|
                                                                                                                                                                                   |_|                          
*/
var bodyOverflow = "";
var htmlOverflow = "";

//Namespaces
function openRadWindowWithName(urlToWindow, height, width, modal, windowName, maximized) {
    var oWindow;

    if (window.radopen) {
        if (windowName != null) {
            oWindow = window.radopen(urlToWindow, windowName);
        }
        else {
            oWindow = window.radopen(urlToWindow);
        }
    }
    else {
        if (windowName != null) {
            oWindow = GetParentWindow().BrowserWindow.radopen(urlToWindow, windowName);
        }
        else {
            oWindow = GetParentWindow().BrowserWindow.radopen(urlToWindow);
        }
    }

    if (oWindow) {
        if (oWindow._iframe != null) {
            if (windowName != null) {
                oWindow._iframe.title = windowName;
            } else {
                oWindow._iframe.title = FlexiJS.Resources.GetResourceText('scripts/flexijs/popup/popup.js', 'GenericPopup.Title');
            }
        }
        //store the overflow   
        bodyOverflow = document.body.style.overflow;
        htmlOverflow = document.documentElement.style.overflow;
        //hide the overflow   
        document.body.style.overflow = "hidden";
        document.documentElement.style.overflow = "hidden";

        //window._resizeExtender._autoScrollEnabled on modal windows can cause scrolling bug on window close, so manually disable it
        if (modal == null || modal == true) {
            oWindow.set_modal(true);
        }

        if (maximized != null && maximized.toString().toLowerCase() == 'true') {
            //this causes scrolling issues in chrome
            oWindow.maximize();
        }
        else if (height != null && width != null) {

            if (height == 'auto') {

                var autoHeight

                if (window.innerHeight) {
                    autoHeight = window.innerHeight
                }
                else if (document.documentElement.clientHeight) {
                    autoHeight = document.documentElement.clientHeight
                }
                else {
                    autoHeight = 625
                }
                oWindow.set_height(autoHeight);
            }
            else {
                oWindow.set_height(height);
            }

            if (width == 'auto') {

                var autoWidth

                if (window.innerHeight) {
                    autoWidth = window.innerWidth
                }
                else if (document.documentElement.clientWidth) {
                    autoWidth = document.documentElement.clientWidth
                }
                else {
                    autoWidth = 625
                }

                oWindow.set_width(autoWidth);
            }
            else {
                oWindow.set_width(width);
            }

        }
        else {
            oWindow.set_autoSize(true);
        }

        oWindow.set_visibleTitlebar(true);
        oWindow.set_keepInScreenBounds(true);
        oWindow.add_close(closeHandler);
        oWindow.add_pageLoad(function (sender, args) {
            var iframe_window = sender.get_contentFrame().contentWindow;
            if (iframe_window.onPopupLoad) iframe_window.onPopupLoad();
        });
        oWindow.setActive(true);
    }
};


function openRadWindow(urlToWindow, height, width, modal) {

    openRadWindowWithName(urlToWindow, height, width, modal, null);

};

function closeRadWindow(refreshParent, queryStringToRemove, callback) {
    //restore the overflow   
    document.body.style.overflow = bodyOverflow;
    document.documentElement.style.overflow = htmlOverflow;

    var win = GetRadWindow();

    if (refreshParent != null && refreshParent == true) {
        RefreshParent(queryStringToRemove);
    }
    else {
        var radRefreshParent = FlexiJS.Utils.GetURLQuerystringObjectByName("refreshParent");
        var radQueryStringToRemove = FlexiJS.Utils.GetURLQuerystringObjectByName("queryStringToRemove");

        if (radRefreshParent != null && radRefreshParent == "true") {
            RefreshParent(radQueryStringToRemove);
        }
    }

    if (win) {
        if (callback != null) {
            callback();
        }
        win.close();
    }


    return false;
};

function RedirectParentWindow(Url) {
    GetRadWindow().BrowserWindow.location = Url;
};

function RefreshParent(queryStringToRemove) {
    GetRadWindow().BrowserWindow.updatefromWindow(queryStringToRemove);
};

function closeHandler(sender, args) {

    //restore the overflow    
    document.body.style.overflow = bodyOverflow;
    document.documentElement.style.overflow = htmlOverflow;

    sender.remove_close(closeHandler);

    FlexiJS.UI.ClearBodyOverflow();
};

function updatefromWindow(queryStringToRemove) {

    if (queryStringToRemove != undefined) {
        //Remove passed in querystring
        if (window.location.search.length > 0) {
            window.location.href = removeURLParam('/' + window.location.pathname.replace("/", "") + window.location.search, queryStringToRemove);
        }
        else {
            window.location.href = removeURLParam('/' + window.location.pathname.replace("/", ""), queryStringToRemove);
        }
    }
    else {
        window.location.href = window.location;
    }


};

function GetRadWindow() {
    var oWindow = null;
    if (window.radWindow)
        oWindow = window.radWindow; else if (window.frameElement && window.frameElement.radWindow)
            oWindow = window.frameElement.radWindow; return oWindow;
};

function loadRadWindowInsideWindow(urlToWindow, windowName, maximized) {
    var radWindow = GetRadWindow();
    var zindexBase = 0;
    var oBrowserWnd = radWindow.BrowserWindow;

    var currentWindowZ = $(radWindow.ui.container).css('z-index');
    if (!isNaN(currentWindowZ)) zindexBase = parseInt(currentWindowZ);
    if (zindexBase < 7000) zindexBase = 7000;

    setTimeout(function () {
        var oWindow = oBrowserWnd.radopen(urlToWindow, windowName);
        if (oWindow._iframe != null) {
            if (windowName != null) {
                oWindow._iframe.title = windowName;
            } else {
                oWindow._iframe.title = FlexiJS.Resources.GetResourceText('scripts/flexijs/popup/popup.js', 'GenericPopup.Title');
            }
        }
        
        if (maximized != null) {
            oWindow.maximize();
        }
        else {
            oWindow.set_minWidth(800);
            oWindow.set_minHeight(500);
            oWindow.set_autoSize(true);
            oWindow.get_popupElement().style.zIndex = zindexBase + 1000;
            oWindow.set_keepInScreenBounds(true);
            oWindow.Center();
        }
    }, 0);
};

function openVideoPopup(url, videotitle, height, width) {

    // Create video control
    $('body').append(
        FlexiJS.Utils.Strings.FormatString(
            '<div id="dvVideoPopup" style="display: none;"><iframe id="youtubeplayer" width="{0}" height="{1}" src="{2}" title="{3}" frameborder="0" allowfullscreen></iframe>',
            width,
            height,
            url,
            FlexiJS.Resources.GetResourceText('scripts/flexijs/popup/popup.js','YoutubeIFrame.Title')
        )
    );

    // Get video player
    var window = $('#dvVideoPopup');

    if (!window.data("kendoWindow")) {
        window.kendoWindow({
            title: videotitle,
            deactivate: closeVideoPopup
        });

    }

    // Show video control
    window.show();
    window.data("kendoWindow").center().open();

};

function closeVideoPopup(e) {

    $('#dvVideoPopup').remove();

    //var iframe = $('#youtubeplayer').contentWindow;

    //func = 'pauseVideo';
    //iframe.postMessage('{"event":"command","func":"' + func + '","args":""}', '*');




};

function checkHTML5Video() {
    if (!!document.createElement('video').canPlayType) {
        var vidTest = document.createElement("video");
        oggTest = vidTest.canPlayType('video/ogg; codecs="theora, vorbis"');
        if (!oggTest) {
            h264Test = vidTest.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
            if (!h264Test) {
                document.getElementById("checkVideoResult").innerHTML = "Sorry. No video support."
            }
            else {
                if (h264Test == "probably") {
                    document.getElementById("checkVideoResult").innerHTML = "Yeah! Full support!";
                }
                else {
                    document.getElementById("checkVideoResult").innerHTML = "Meh. Some support.";
                }
            }
        }
        else {
            if (oggTest == "probably") {
                document.getElementById("checkVideoResult").innerHTML = "Yeah! Full support!";
            }
            else {
                document.getElementById("checkVideoResult").innerHTML = "Meh. Some support.";
            }
        }
    }
    else {
        document.getElementById("checkVideoResult").innerHTML = "Sorry. No video support."
    }
};

function selectKendoTabStripTab(tabid) {

    $(document).ready(function () {
        var tabstrip = $('[id*=tabStrip]').data('kendoTabStrip');
        var myTab = tabstrip.tabGroup.children('li').eq(tabid);
        tabstrip.select(myTab);
    }
);

};

// Email Editor Setup and Validation //
function FixEditor() {
    var oWnd = GetRadWindow();
    var contentwindow = oWnd.get_contentFrame().contentWindow;
    var editorid = contentwindow.$telerik.$("[id$='reEmailBody']").attr("id");
    try {
        setTimeout(function () {
            $find(editorid).onParentNodeChanged();
        }, 500);
    } catch (e) { }
};

function ValidateEmailBody(source, args) {
    var emailBodyWindow;
    emailBodyWindow = GetRadWindowManager();

    if (emailBodyWindow != null) {
        emailBodyWindow = emailBodyWindow.getActiveWindow();
    }


    if (emailBodyWindow != null && emailBodyWindow._name == 'rwEmailEditor') {
        var editorid = $telerik.$("[id$='reEmailBody']").attr("id");
        var editEmailBody = $find(editorid);
        if ((editEmailBody == null || editEmailBody.get_html().toString().trim().length == 0) || editEmailBody.get_html().toString().trim() == '<p>&nbsp;</p>') {
            args.IsValid = false;
        }
        else {
            args.IsValid = true;
        }
    }
    else {
        args.IsValid = true;
    }
};

function ValidateRecipients(soure, args) {

    var recipientsWindow;
    recipientsWindow = GetRadWindowManager();

    if (recipientsWindow != null) {
        recipientsWindow = recipientsWindow.getActiveWindow();
    }

    if (recipientsWindow != null && recipientsWindow._name == 'rwEmailEditor') {

        var listboxid = $telerik.$("[id$='rlbRecipients']").attr("id");
        var listbox = $find(listboxid);
        if (listbox.get_items()._array.length > 0) {
            args.IsValid = true;
        }
        else {
            args.IsValid = false;
        }
    }
    else {
        args.IsValid = true;
    }

};

function ValidateSubject(source, args) {

    var subjectWindow;
    subjectWindow = GetRadWindowManager();

    if (subjectWindow != null) {
        subjectWindow = subjectWindow.getActiveWindow();
    }


    if (subjectWindow != null && subjectWindow._name == 'rwEmailEditor') {
        var subject = $telerik.$("[id$='txtEmailSubject']").val();
        if ((subject == null || subject.length == 0)) {
            args.IsValid = false;
        }
        else {
            args.IsValid = true;
        }
    }
    else {
        args.IsValid = true;
    }

};

function showRegisterDisclaimer(areaID, returnurl) {
    var url = '/policy.aspx';
    if (areaID != null && !isNaN(areaID) && areaID > 0) {
        url += '?farea=' + areaID.toString();

    }

    if (returnurl) {
        if (url.indexOf('?') >= 0) {
            url += '&deeplink=' + returnurl;
        } else {
            url += '?deeplink=' + returnurl;
        }
    }        
    window.location = url;
};
;/*
  _____   _                 _       _   ____        ____                                        _____    ____   _____   _____   _                                       
 |  ___| | |   ___  __  __ (_)     | | / ___|      |  _ \    ___    _ __    _   _   _ __       | ____|  / ___| |  ___| |  ___| (_)  _ __     __ _   _ __     ___    ___ 
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |_) |  / _ \  | '_ \  | | | | | '_ \      |  _|   | |     | |_    | |_    | | | '_ \   / _` | | '_ \   / __|  / _ \
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  |  __/  | (_) | | |_) | | |_| | | |_) |  _  | |___  | |___  |  _|   |  _|   | | | | | | | (_| | | | | | | (__  |  __/
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_|      \___/  | .__/   \__,_| | .__/  (_) |_____|  \____| |_|     |_|     |_| |_| |_|  \__,_| |_| |_|  \___|  \___|
                                                                   |_|             |_|                                                                                  

        Popup functions for the ecffinance page

        Requirements:
            jQuery
            Telerik Radcontrols (ughhh)
*/

FlexiJS.Popup.ECFFinance = new Object();

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/



/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/

FlexiJS.Popup.ECFFinance.ReloadFinancePage = function () {
    // Description: Reload function specific to the ecf finance page
    // Created: ?? - ??
    // Updated: 13/03/2017 - v3.15.1 - JD - Moved from popup.js to new structure
    var financeWindow
    if (window.radWindow) {
        financeWindow = GetRadWindow();
    }

    if (financeWindow != null) {
        financeWindow.BrowserWindow.refreshPaymentGrid(null, true);
    } else {
        var usedOpener = false;
        try {
            if (window.opener != null) {
                if (typeof window.opener.refreshPaymentGrid == 'function') {
                    window.opener.refreshPaymentGrid(null, true);
                    usedOpener = true;
                }
            }
        }
        finally {
            if (usedOpener === false) {
                if (typeof window.refreshPaymentGrid == 'function') {
                    window.refreshPaymentGrid(null, true);
                }
            }
        }
    }
};;/*
  _____   _                 _       _   ____        ____                                        ___           _   _     _           _            _____                                                                     _   
 |  ___| | |   ___  __  __ (_)     | | / ___|      |  _ \    ___    _ __    _   _   _ __       |_ _|  _ __   (_) | |_  (_)   __ _  | |_    ___  | ____|  _ __     __ _    __ _    __ _    ___   _ __ ___     ___   _ __   | |_ 
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |_) |  / _ \  | '_ \  | | | | | '_ \       | |  | '_ \  | | | __| | |  / _` | | __|  / _ \ |  _|   | '_ \   / _` |  / _` |  / _` |  / _ \ | '_ ` _ \   / _ \ | '_ \  | __|
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  |  __/  | (_) | | |_) | | |_| | | |_) |  _   | |  | | | | | | | |_  | | | (_| | | |_  |  __/ | |___  | | | | | (_| | | (_| | | (_| | |  __/ | | | | | | |  __/ | | | | | |_ 
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_|      \___/  | .__/   \__,_| | .__/  (_) |___| |_| |_| |_|  \__| |_|  \__,_|  \__|  \___| |_____| |_| |_|  \__, |  \__,_|  \__, |  \___| |_| |_| |_|  \___| |_| |_|  \__|
                                                                   |_|             |_|                                                                           |___/           |___/                                         

-- Handles the initiate engagement popup

Requirements:
    -- Any libraries that this file depends on, like jQuery or Kendo
*/

FlexiJS.Popup.InitiateEngagement = {};

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/


/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               
*/

//Common Data Functions
FlexiJS.Popup.InitiateEngagement.GetCompletionOption = function() {
    var checkedScheduleItems = $('input[type=radio][name=rblCompleteNow]:checked');
    var checkedValue = '';
    if (checkedScheduleItems && checkedScheduleItems.length > 0) {
        checkedValue = $(checkedScheduleItems[0]).val();
    }
    return checkedValue.toLowerCase();
};//--DONE

FlexiJS.Popup.InitiateEngagement.GetSelectedBudgetValue = function() {
    var hasBudgets = $('[id$=trBudgetPeriod]').is(':visible');
    if (hasBudgets == true) {
        return $('#ddlBudgetPeriod').data('kendoDropDownList').value();
    } else {
        return null;
    }
};//--DONE

FlexiJS.Popup.InitiateEngagement.GetGenericFormData = function(){
    var returningValue = {};
    returningValue.appId = $('[id$=hdnAppId]').val();
    returningValue.currentUserId = $('[id$=hdnCurrentUserId]').val();
    return returningValue;
};

FlexiJS.Popup.InitiateEngagement.GetRollbackFormData = function(){
    var returningValue = FlexiJS.Popup.InitiateEngagement.GetGenericFormData();
    returningValue.engagementId = $('[id$=hdnRollbackEngagementId]').val();
    returningValue.formType = $('[id$=hdnRollbackFormTypeId]').val();
    returningValue.fundTypeId = $('[id$=hdnRollbackFundTypeId]').val();

    switch(parseInt(returningValue.formType)){
        case 80153:
            returningValue.headerText = FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "litEngagementHeader_Claim_Rollback.Text");
            returningValue.confirmMessage = FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "btnRollbackClaim_Click.Confirm");
            returningValue.showDateDue = false;
            returningValue.dueDate = function(){ return null; };
            returningValue.dueDateString = function(){ return null; };
            break;

        default:
            returningValue.headerText = FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "litEngagementHeader_Rollback.Text");
            returningValue.confirmMessage = FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "btnRollBackEngagement_Click.Confirm");
            returningValue.showDateDue = true;
            returningValue.dueDate = function(){ return new Date($("#dtpRollbackDateDue").data('kendoDatePicker').value()); };
            returningValue.dueDateString = function(){
                var dueDate = new Date($("#dtpRollbackDateDue").data('kendoDatePicker').value());
                if(dueDate != null){
                    return dueDate.getFullYear()+"-"+(dueDate.getMonth()+1)+"-"+dueDate.getDate()+"  "+dueDate.getHours()+":"+dueDate.getMinutes()+":"+dueDate.getSeconds()
                } else {
                    return null;
                }
            };
            break;
    }

    returningValue.notifyApplicant = function(){ return $('#chkNotifyApplicantOfRollback').is(':checked') };
    
    return returningValue;
};

FlexiJS.Popup.InitiateEngagement.GetInitiateOrEditFormData = function(){
    var returningValue = FlexiJS.Popup.InitiateEngagement.GetGenericFormData();
        
    var datacontainer = $('[id$=hdnInitiateData]');
    if (datacontainer) {
        returningValue = $.extend( {}, returningValue, $.parseJSON(datacontainer.val()))
        returningValue.amEditingExisting = (returningValue.PassedEngagement && returningValue.PassedEngagement.Id != null && returningValue.PassedEngagement.Id != 0);
        returningValue.availableOptionsCount = (returningValue.AllowCompleteNow ? 1 : 0) + (returningValue.AllowScheduleAndEmail ? 1 : 0) + (returningValue.AllowScheduleOnly ? 1 : 0);
        returningValue.hasAvailableOptions = (returningValue.AllowCompleteNow || returningValue.AllowScheduleAndEmail || returningValue.AllowScheduleOnly);

        returningValue.scheduleValue = FlexiJS.Popup.InitiateEngagement.GetCompletionOption();
        returningValue.scheduleVisible = (returningValue.scheduleValue != 'yes' && returningValue.scheduleValue != '');

        if(returningValue.PassedEngagement.DueDate) returningValue.PassedEngagement.DueDate = kendo.parseDate(returningValue.PassedEngagement.DueDate)

        return returningValue;
    } else {
        return null;
    }


};

//Other Controls Interaction
FlexiJS.Popup.InitiateEngagement.RefreshSourceEngagementsGrid = function () {
    if (FlexiJS.Popup.IsOfferStagePopup()) {
        GetRadWindow().GetWindowManager().GetWindowByName('OfferPopup').GetContentFrame().contentWindow.FlexiJS.Events.Event.FinancialInformationRefresh.Fire();
    } else {
        GetRadWindow().BrowserWindow.FlexiJS.Events.Event.FinancialInformationRefresh.Fire();
    }
};//--DONE

//Confirm Functions
FlexiJS.Popup.InitiateEngagement.ConfirmCreateEngagement = function(message, onYes) {
    var isValid = true;
    try {
        if(FlexiJS.Popup.InitiateEngagement.GetCompletionOption() != 'yes'){
            var assignTo = $("#ddlAssignTo").data('kendoComboBox').value();
            var owner = $('#ddlUserOwner').data('kendoDropDownList').value();

            if(assignTo == "") assignTo = 0;
            if(owner == "") owner = 0;

            if ($('[id$=trAssignTo]').is(':visible') && !(assignTo > 0)) {
                isValid = false;
                $("#vldAssignTo").toggle(true);
            } else {
                $("#vldAssignTo").toggle(false);
            }

            var selecteddate = $('#dtpDateDue').data('kendoDatePicker').value();
            if ($('[id$=trDateDue]').is(':visible') && !(selecteddate != null && selecteddate != '')) {
                isValid = false;
                $("#vldDateDue").toggle(true);
            } else {
                $("#vldDateDue").toggle(false);
            }

            if (!(owner > 0)) {
                isValid = false;
                $("#vldUserOwner").toggle(true);
            } else {
                $("#vldUserOwner").toggle(false);
            }
        }
        var periodPaidShowing = $('[id$=trBudgetPeriodPaidMessage]').is(':visible');

        if (!(periodPaidShowing == false)) isValid = false;
    }
    catch(e) {
        isValid = false;
    }

    if (isValid == true) {
        FlexiJS.ConfirmActions.KendoConfirmPopup({
            title: '',
            message: message,
            buttons: [
                {
                    onclick: onYes,
                    uniquename: 'confirmengyes',
                    text: FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "ConfirmPopup.Yes")
                },{
                    uniquename: 'confirmengno',
                    text: FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "ConfirmPopup.No")
                }
            ]
        });
    }
};//--DONE

FlexiJS.Popup.InitiateEngagement.ConfirmRollBackEngagement = function(message, checkDate, onYes) {
    var isValid = true;
    try {
        if(checkDate == true){
            var selecteddate = $('#dtpRollbackDateDue').data('kendoDatePicker').value();
            if (!(selecteddate != null && selecteddate != '')) {
                isValid = false;
                $("#vldRollbackDateDue").toggle(true);
            } else {
                $("#vldRollbackDateDue").toggle(false);
            }
        }
    }
    catch (e) {
        isValid = false;
    }

    if (isValid == true) {
        FlexiJS.ConfirmActions.KendoConfirmPopup({
            title: '',
            message: message,
            buttons: [
                {
                    onclick: onYes,
                    uniquename: 'confirmrollyes',
                    text: FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "ConfirmPopup.Yes")
                },{
                    uniquename: 'confirmrollno',
                    text: FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "ConfirmPopup.No")
                }
            ]
        });
    }
};//--DONE

FlexiJS.Popup.InitiateEngagement.ConfirmNotifyOwner = function(message, onYes, onNo) {
    FlexiJS.ConfirmActions.KendoConfirmPopup({
        title: '',
        message: message,
        buttons: [
            {
                onclick: onYes,
                uniquename: 'confirmnotifycoyes',
                text: FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "ConfirmPopup.Yes")
            },{
                onclick: onNo,
                uniquename: 'confirmnotifycono',
                text: FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "ConfirmPopup.No")
            }
        ]
    });
};//--DONE

//Rollback Specific Functions
FlexiJS.Popup.InitiateEngagement.LoadRollbackFormItems = function() {
    var formData = FlexiJS.Popup.InitiateEngagement.GetRollbackFormData();
    
    $("#dtpRollbackDateDue").kendoDatePicker();
    
    $('#spnEngagementHeader').text(formData.headerText);
    $('[id$=trNewDateDue]').toggle(formData.showDateDue);
    
    var saveButton = $('[id$=btnRollBackEngagement]');
    saveButton.unbind('click');
    saveButton.on('click', function () {
        FlexiJS.Popup.InitiateEngagement.ConfirmRollBackEngagement(
            formData.confirmMessage,
            formData.showDateDue,
            function() {
                FlexiJS.Popup.InitiateEngagement.PerformRollback(formData);
            }
        );
        return false;
    });
};//--DONE//--DONE

FlexiJS.Popup.InitiateEngagement.PerformRollback = function (formData){
    Fluent.Website.GMS.GMSService.Reporting_RollbackReportingForm(
        formData.engagementId,
        formData.fundTypeId,
        formData.appId,
        formData.dueDate(),
        function (result) {
            try {
                if (result.Result == true) {
                    FlexiJS.Popup.InitiateEngagement.RollbackSaveAction(formData);
                } else {
                    showNotificationMessage('error', result.ResultMessage);
                }
            } catch (e) {
                showNotificationMessage('error', e);
            }
        },
        function (e) {
            showNotificationMessage('error', e._message);
        }
    );
};//--DONE//--DONE

FlexiJS.Popup.InitiateEngagement.RollbackSaveAction = function(formData) {
    FlexiJS.Popup.InitiateEngagement.RefreshSourceEngagementsGrid();
        
    if(formData.notifyApplicant()) {
        $('[id$=hdnDueDate]').val(formData.dueDateString());
        eval($("[id$=btnSendRollbackEmail]").attr('href'));
        FlexiJS.Popup.ResizeCurrentRadWindowForEmail();
    } else {
        closeRadWindow();
    }
    showNotificationMessage('success', FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "btnRollBackEngagement.RolledBackMessage"));
    FlexiJS.Events.Event.FinancialInformationRefresh.Fire();
};//--DONE//--DONE

//Initiate OR Edit Specific Functions
FlexiJS.Popup.InitiateEngagement.BindInitiateOrEditControls = function(data){

    
    $('input[type=radio][name=rblCompleteNow]').unbind('change');
    $('input[type=radio][name=rblCompleteNow]').change(function (e) {
        FlexiJS.Popup.InitiateEngagement.ScheduleTypeChange(data);
    });
            

    var ownerCombo = $('#ddlUserOwner').kendoDropDownList({
        dataTextField: "Name",
        dataValueField: "Id",
        dataSource: data.AssignableOfficers,
        template: '<span class="k-state-default tooltip-content-default">#: data.Name #</span>',
        index: 0,
        value: (data.amEditingExisting && data.PassedEngagement.Officer && data.PassedEngagement.Officer != null && data.PassedEngagement.Officer != 0) ? data.PassedEngagement.Officer : data.DefaultOfficerUserId
    }).data("kendoDropDownList");
    if (!ownerCombo.dataItem()) ownerCombo.value('');
        
    $('#ddlBudgetPeriod').kendoDropDownList({
        dataTextField: "Name",
        dataValueField: "Id",
        dataSource: null,
        template: '<span class="k-state-default tooltip-content-default">#: data.Name #</span>',
        index: 0,
        change: function (e) { $('[id$=trBudgetPeriodPaidMessage]').toggle(!(this.dataItem().Initiatable)); }
    }).data("kendoDropDownList");

    var dataItem = $('#ddlMonitoringForm').kendoDropDownList({
        dataTextField: "Name",
        dataValueField: "Id",
        dataSource: data.Forms,
        template: '<span class="k-state-default tooltip-content-default">#: data.Name #</span>',
        index: 0,
        value: data.amEditingExisting ? data.PassedEngagement.FormId : 0,
        enable: data.amEditingExisting ? (data.PassedEngagement.Status && data.PassedEngagement.Status == 80500) : true,
        change: function (e) {
            var dataItem = this.dataItem();
            $("#dtpDateDue").data('kendoDatePicker').value(dataItem.NextMonitoringDate);

            FlexiJS.Popup.InitiateEngagement.AssignToUserRebind(true, data.DefaultContact);

            FlexiJS.Popup.InitiateEngagement.ScheduleTypeChange(data);
        }
    }).data("kendoDropDownList").dataItem();
        
        
    $("#dtpDateDue").kendoDatePicker({
        value: data.amEditingExisting ? data.PassedEngagement.DueDate : dataItem.NextMonitoringDate,
        enable: data.amEditingExisting ? (data.PassedEngagement.Status && (data.PassedEngagement.Status == 80500 || data.PassedEngagement.Status == 80501)) : true
    });

    $("#ddlAssignTo").kendoComboBox(
        {
            template: '<div class="divAssignToItem">' + 
                '<div class="divAssignToItem_FloatContainer">' +
                '<div class="divAssignToItem_Name" title="#: data.UserName #">' +
                '<div class="divAssignToItem_PaddedText">#: data.UserName #</div>' +
                '</div>' +
                '<div class="divAssignToItem_Organisation" title="#: data.Organisation #">' +
                '<div class="divAssignToItem_PaddedLeftText">#: data.Organisation #</div>' +
                '</div>' +
                (data.AreasEnabled == true?'<div class="divAssignToItem_Area" title="#: data.AreaName #"><div class="divAssignToItem_PaddedLeftText">#: data.AreaName #</div></div>':'') + 
                '</div>' +
                '</div>',
            groupTemplate: '#:data#',
            fixedGroupTemplate: "#:data#",
            placeholder: "",
            dataTextField: "UserName",
            dataValueField: "UserID",
            minLength: 3,
            autoBind: true,
            filter: "contains",
            height: 200,
            dataSource: {
                pageSize: 100,
                transport: {
                    read: {
                        url: "/GMS/WebServices/GMSService.svc/Reporting_GetReportingFormAssignableUsers",
                        type: "POST",
                        contentType: 'application/json; charset=utf-8'
                    },
                    parameterMap: function (options) {
                        return JSON.stringify({
                            GMSFundTypeId: data.GMSFundTypeId,
                            FormId: $('#ddlMonitoringForm').data("kendoDropDownList").dataItem().Id,
                            GMSApplicationId: data.GMSApplicationId,
                            PageSize: options.pageSize,
                            Page: options.page,
                            SearchText: $("#ddlAssignTo").data("kendoComboBox").input.val()
                        });
                    }
                },
                schema: {
                    data: function (result) {
                        if (result.d.Result == true) {
                            var data = JSON.parse(result.d.ResultDetail);
                            return data.Users;
                        } else {
                            return [];
                        }
                    },
                    total: function (result) {
                        if (result.d.Result == true) {
                            var data = JSON.parse(result.d.ResultDetail);
                            return data.TotalUsers;
                        } else {
                            return 0;
                        }
                    }
                },
                serverFiltering: true,
                serverPaging: true,
                group: { field: 'RoleGroup' }
            },
            dataBound: function (e) {
                var me = e.sender;
                var groups = $('[class~="k-group"]', me.list);
                $.each(groups, function (index, groupItem) {
                    var thisGroup = $(groupItem);
                    thisGroup.addClass('kendoGroupRowFix');
                    $(thisGroup.prev('div')).before(thisGroup);
                });
                FlexiJS.Popup.InitiateEngagement.ResizeAssignToGrouping(me);
            },
            open: function (e) {
                FlexiJS.Popup.InitiateEngagement.ResizeAssignToGrouping(e.sender);
            }
        }
    );
    
    if (data.amEditingExisting && data.PassedEngagement.AssignedTo && data.PassedEngagement.AssignedTo != null && data.PassedEngagement.AssignedTo != 0) {
        FlexiJS.Popup.InitiateEngagement.AssignToUserRebind(false, {Id: data.PassedEngagement.AssignedTo});
    } else {
        FlexiJS.Popup.InitiateEngagement.AssignToUserRebind(false, data.DefaultContact);
    }
    


    
    $('#divCompleteNow_Yes').toggle(data.AllowCompleteNow);
    $('#divCompleteNow_No').toggle(data.AllowScheduleAndEmail);
    $('#divCompleteNow_NoSched').toggle(data.AllowScheduleOnly);

    if (data.hasAvailableOptions) {
        $('[id$=trCompleteNow]').toggle(data.availableOptionsCount > 1);
                    
        var selectedOption = 'NoSched';
        if (data.AllowCompleteNow && FlexiJS.Popup.IsOfferStagePopup()) {
            selectedOption = 'Yes';
        } else {
            selectedOption = 
                data.AllowScheduleOnly ? 'NoSched'
                : data.AllowCompleteNow ? 'Yes'
                : data.AllowScheduleAndEmail ? 'No'
                : 'NoSched';
        }

        $('#rblCompleteNow_' + selectedOption).prop('checked', 'checked');
    } else {
        //Server side logic should never allow code to reach here, added just as a precaution
        //Hide the radio button options
        $('[id$=trCompleteNow]').toggle(false);
        //Hide the submit button
        $('#btnSaveEngagement').toggle(false);
        
        //Use yes to set the visibility (yes has no options and hides all the options that would be visible otherwise)
        $('#rblCompleteNow_Yes').prop('checked', 'checked');
    }

    
    
    
};

FlexiJS.Popup.InitiateEngagement.AssignToUserRebind = function(keepCurrentSelection, defaultContact) {
    var assignToCombo = $("#ddlAssignTo").data('kendoComboBox');
    
    var currentValue = assignToCombo.dataItem();

    assignToCombo.value('');
    
    assignToCombo.dataSource.read().then(function () {
        FlexiJS.Popup.InitiateEngagement.ResizeAssignToGrouping(assignToCombo);

        var setUser = null;
        if(keepCurrentSelection && currentValue && currentValue.UserID){
            setUser = currentValue.UserID;
        } else if(defaultContact && defaultContact.Id) {
            setUser = defaultContact.Id;
        }

        if(setUser != null){
            //Try to select the same person the user had selected
            assignToCombo.value(setUser);

            //if user no longer in list, clear selection
            if (!assignToCombo.dataItem()) assignToCombo.value('');
        } else {
            assignToCombo.value('');
        }
    });
};

FlexiJS.Popup.InitiateEngagement.LoadInitiateFormItems = function() {
    var data = FlexiJS.Popup.InitiateEngagement.GetInitiateOrEditFormData();
    if (data != null) {
        FlexiJS.Popup.InitiateEngagement.BindInitiateOrEditControls(data);
                
        FlexiJS.Popup.InitiateEngagement.ScheduleTypeChange(data);
    }
};

FlexiJS.Popup.InitiateEngagement.PerformEditOrInitiate = function(EngagementId, GMSFundTypeId, FormId, GMSApplicationId, AssignTo, Owner, DateDue, Period) {
    var successResponse = function (result) {
        try {
            if (result.Result == true) {
                FlexiJS.Popup.InitiateEngagement.InitiateSaveAction(result.ResultDetail, AssignTo, FormId, DateDue, EngagementId != null);
            } else {
                showNotificationMessage('error', result.ResultMessage);
            }
        } catch (e) {
            showNotificationMessage('error', e);
        }
    };

    if( EngagementId == null ){
        Fluent.Website.GMS.GMSService.Reporting_InitiateReportingForm(GMSFundTypeId, FormId, GMSApplicationId, AssignTo, Owner, DateDue, Period, successResponse, function(error){ showNotificationMessage('error', error._message); });
    } else {
        Fluent.Website.GMS.GMSService.Reporting_EditReportingForm(EngagementId, GMSFundTypeId, FormId, GMSApplicationId, AssignTo, Owner, DateDue, Period, successResponse, function(error){ showNotificationMessage('error', error._message); });
    }
};//--DONE

FlexiJS.Popup.InitiateEngagement.InitiateSaveAction = function(savedEngagementId, notifyUserId, formId, dueDate, isExistingEngagement) {
    var currentUserId = $('[id$=hdnCurrentUserId]').val();
    $('[id$=hdnSendOwnerEmailAfterNotification]').val('false');
    //''Set up reload engagement grid script depending where the control was called from
    var inOfferPopup = FlexiJS.Popup.IsOfferStagePopup();
    FlexiJS.Popup.InitiateEngagement.RefreshSourceEngagementsGrid();
    
    var checkedValue = FlexiJS.Popup.InitiateEngagement.GetCompletionOption();
    var ownerCheck = FlexiJS.Popup.InitiateEngagement.HasOwnerChanged();
        
    var offerConfirmOwnerEmailPrompt = false;
    $('[id$=hdnSelectedFormId]').val(formId);

    switch (checkedValue) {
        case 'yes':
            var engagementURL = "engagement.aspx?id=" + savedEngagementId;
            if (window.location.toString().indexOf('activities_monitoring.aspx') > 0 || window.parent.window.location.toString().indexOf('activities_monitoring.aspx') > 0) engagementURL = "/" + engagementURL;
            if (window.parent) {
                window.parent.window.open(engagementURL, '_blank');
            } else {
                window.open(engagementURL, '_blank');
            }
            closeRadWindow();
            break;
        case 'nosched':
            if(dueDate != null){
                $('[id$=hdnDueDate]').val(dueDate.getFullYear()+"-"+(dueDate.getMonth()+1)+"-"+dueDate.getDate()+"  "+dueDate.getHours()+":"+dueDate.getMinutes()+":"+dueDate.getSeconds());
            } else {
                $('[id$=hdnDueDate]').val(null);
            }
            $('[id*=hdnNotifyOwnerId]').val($('#ddlUserOwner').data('kendoDropDownList').value());
            if (isExistingEngagement){
                showNotificationMessage('success', FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "btnSaveEngagement.SavedMessage"));
            } else {
                showNotificationMessage('success', FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "btnSaveEngagement.ScheduledMessage"));
            }
            
            offerConfirmOwnerEmailPrompt = true;
                        
            break;
        case 'no' : 
            if (notifyUserId && notifyUserId > 0 && notifyUserId != currentUserId) {
                if(dueDate != null){
                    $('[id$=hdnDueDate]').val(dueDate.getFullYear()+"-"+(dueDate.getMonth()+1)+"-"+dueDate.getDate()+"  "+dueDate.getHours()+":"+dueDate.getMinutes()+":"+dueDate.getSeconds());
                } else {
                    $('[id$=hdnDueDate]').val(null);
                }
                $('[id$=hdnNotifyUserId]').val(notifyUserId);
                $('[id$=hdnSendOwnerEmailAfterNotification]').val('true');
                $('[id*=hdnNotifyOwnerId]').val($('#ddlUserOwner').data('kendoDropDownList').value());

                //Clicks and causes postback
                eval($("[id$=btnSendInitiateEmail]").attr('href'));
                FlexiJS.Popup.ResizeCurrentRadWindowForEmail();
                if (isExistingEngagement){
                    showNotificationMessage('success', FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "btnSaveEngagement.SavedMessage"));
                } else {
                    showNotificationMessage('success', FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "btnSaveEngagement.ScheduledMessage"));
                }
            } else if (notifyUserId == $('[id$=hdnCurrentUserId]').val()) {
                showNotificationMessage('success', FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "btnSaveEngagement.SameRecipient"));
                offerConfirmOwnerEmailPrompt = true;
            } else {
                showNotificationMessage('error', FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "btnSaveEngagement.FailedEmail"));
                offerConfirmOwnerEmailPrompt = true;
            }
            break;
    }
    
    if(offerConfirmOwnerEmailPrompt == true){
        $('[id*=hdnNotifyOwnerId]').val($('#ddlUserOwner').data('kendoDropDownList').value());
        FlexiJS.Popup.InitiateEngagement.CallConfirmNotifyOwner(ownerCheck.changed, ownerCheck.isNew, ownerCheck.isCurrentUser, isExistingEngagement);
    }
};

FlexiJS.Popup.InitiateEngagement.CloseWindow = function(isExistingEngagement){
    if (isExistingEngagement) closeRadWindow();
};

FlexiJS.Popup.InitiateEngagement.CallConfirmNotifyOwner = function(isChanged, isNew, isCurrentUser, isExistingEngagement){
    //Wraps the case officer popup code, prevents having duplicate code on server side
    if((isChanged || isNew) && !isCurrentUser) {
        var ownerMessage = isNew
            ? FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "ConfirmNotifyOwnerNew")
            : FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "ConfirmNotifyOwnerChange");
        
        FlexiJS.Popup.InitiateEngagement.ConfirmNotifyOwner(
            ownerMessage,
            function(){ eval($("[id$=btnSendOwnerEmail]").attr('href')); FlexiJS.Popup.ResizeCurrentRadWindowForEmail(); }, 
            function(){ FlexiJS.Popup.InitiateEngagement.CloseWindow(); }
        );
    } else {
        FlexiJS.Popup.InitiateEngagement.CloseWindow(isExistingEngagement);
    }
};

//Case Officer Change
FlexiJS.Popup.InitiateEngagement.HasOwnerChanged = function(){
    var currentOwner = $('[id*=hdnCurrentOwner]').val();
    var currentUser = $('[id*=hdnCurrentUserId]').val();
    var selectedOwner = $('#ddlUserOwner').data('kendoDropDownList').value();

    return {
        changed: currentOwner != selectedOwner && currentOwner != '',
        isNew: currentOwner != selectedOwner && currentOwner == '',
        isCurrentUser: selectedOwner == currentUser
    }
};


FlexiJS.Popup.InitiateEngagement.ScheduleTypeChange = function() {
    var dataItem = $('#ddlMonitoringForm').data("kendoDropDownList").dataItem();
    var data = FlexiJS.Popup.InitiateEngagement.GetInitiateOrEditFormData();
    
    var isNotAClaimForm = (dataItem.Type != 80153);
    var showDueDate = isNotAClaimForm && data.scheduleVisible &&
        (
            !data.amEditingExisting || 
            (
                data && data.PassedEngagement && data.PassedEngagement.Status && //Make sure objects exist
                (data.PassedEngagement.Status == 80500 || data.PassedEngagement.Status == 80501) //Only show date input if the the engagement is not started OR in progress, cannot be changed afterwards
            )
        );

    // Do Display Logic Here
    $('[id$=trAssignTo]').toggle(isNotAClaimForm && data.scheduleVisible);
    $('[id$=trDateDue]').toggle(showDueDate);
    $('[id$=trOfficer]').toggle(data.scheduleVisible);

    $('[id$=trBudgetPeriod]').toggle(false);
    $('[id$=trBudgetPeriodPaidMessage]').toggle(false);
        

    //CLAIMS
    var saveButton = $('[id*=btnSaveEngagement]');
    var headerSpan = $('[id*=spnEngagementHeader]');


    if (isNotAClaimForm) {
        //Monitoring / General
        $('#ddlMonitoringForm').data("kendoDropDownList").enable(true);
        headerSpan.text(FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "litEngagementHeader.Text"));
        saveButton.text(FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "btnSaveEngagement.Text"));
        saveButton.unbind('click');
        saveButton.on('click', function () {

            FlexiJS.Popup.InitiateEngagement.ConfirmCreateEngagement(
                FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "btnSaveEngagement_Click.Confirm"),
                function() {
                    var assigningToUserHasPermissions = true;
                    var engData = FlexiJS.Popup.InitiateEngagement.GetInitiateOrEditFormData();
                    var assignTo = engData.currentUserId;
                    var owner = engData.currentUserId;
                    var setDate = !engData.amEditingExisting ? null : engData.PassedEngagement.DueDate;
                    if(engData.scheduleVisible){
                        var assignToComboItem = $("#ddlAssignTo").data('kendoComboBox').dataItem();
                        assignTo = assignToComboItem.UserID;
                        owner = $('#ddlUserOwner').data('kendoDropDownList').value();
                        setDate = showDueDate ? new Date($("#dtpDateDue").data('kendoDatePicker').value()) : setDate;
                        if (assignToComboItem.AdditionalParameters['HasPermissions'] && assignToComboItem.AdditionalParameters['HasPermissions'] == "false") assigningToUserHasPermissions = false;
                    }
                        
                    var initiateForm = function(){
                        FlexiJS.Popup.InitiateEngagement.PerformEditOrInitiate(
                            !engData.amEditingExisting ? null : engData.PassedEngagement.Id,
                            engData.GMSFundTypeId,
                            dataItem.Id,
                            engData.GMSApplicationId,
                            assignTo,
                            owner,
                            setDate,
                            FlexiJS.Popup.InitiateEngagement.GetSelectedBudgetValue()
                        );
                    }
                    //ASK ABOUT PERMISSIONS
                    if (assigningToUserHasPermissions){
                        initiateForm();
                    } else {
                        FlexiJS.ConfirmActions.KendoConfirmPopup({
                            title: '',
                            message: FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "addUserPermissionsForAssignTo.Confirm"),
                            buttons: [
                                {
                                    onclick: initiateForm,
                                    uniquename: 'confirmengyes',
                                    text: FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "ConfirmPopup.Yes")
                                },{
                                    uniquename: 'confirmengno',
                                    text: FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "ConfirmPopup.No")
                                }
                            ]
                        });
                    }
                }
            );
            return false;
        });
    } else {
        //Claims
        $('#ddlMonitoringForm').data("kendoDropDownList").enable(false);
        headerSpan.text(FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "litEngagementHeader_Claim.Text"));
        saveButton.text(FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "btnSaveEngagement_Claim.Text"));
        saveButton.unbind('click');

        saveButton.on('click', function () {
            FlexiJS.Popup.InitiateEngagement.ConfirmCreateEngagement(
                FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "btnInitiateClaim_Click.Confirm"),
                function() {
                    var engData = FlexiJS.Popup.InitiateEngagement.GetInitiateOrEditFormData();
                    var owner = engData.currentUserId;
                    if(engData.scheduleVisible) owner = $('#ddlUserOwner').data('kendoDropDownList').value();
                        
                    FlexiJS.Popup.InitiateEngagement.PerformEditOrInitiate(
                        !engData.amEditingExisting ? null : engData.PassedEngagement.Id,
                        engData.GMSFundTypeId,
                        dataItem.Id,
                        engData.GMSApplicationId,
                        null,
                        owner,
                        null,
                        FlexiJS.Popup.InitiateEngagement.GetSelectedBudgetValue()
                    );
                }
            );
            return false;
        });
    }
        
    if (dataItem.HasBudgetTables) {
        var periodsControl = $('#ddlBudgetPeriod').data('kendoDropDownList');
        periodsControl.dataSource.data(dataItem.BudgetPeriods);
        periodsControl.refresh();
        periodsControl.trigger("change");
        $('[id$=trBudgetPeriod]').toggle(true);
    } else {
        $('[id$=trBudgetPeriod]').toggle(false);
        $('[id$=trBudgetPeriodPaidMessage]').toggle(false);
    }
};//--DONE

FlexiJS.Popup.InitiateEngagement.ResizeAssignToGrouping = function(ddl) {
    // In a kendo combo box, the group label comes after the first item in the DOM, so resizing the label
    // causes the first item to look like it is in the previous group. This function loops through groups
    // that haven't had the fix class applied, moves them before the label of the first item in the DOM (the 
    // group MUST stay within the first item in the DOM to maintain combobox functionality) and resizes the group elements
    // using a cloned version of the list (required as the list won't be visible and heights will be read as 0)
    var me = ddl;
    // Gets any that haven't already been fixed
    me.list.addClass('kendoUserCombo kendocombobox-userlist');
    var groups = $('[class~="k-group"]:not([class~="kendoGroupRowAbsoluteFix"])', me.list);
    var copied_elem = null;
    if (groups.length > 0) {
        copied_elem = me.list.clone().attr("id", false).css({ visibility: "hidden", display: "block", position: "absolute" });
        $("body").append(copied_elem);
    }
    $.each(groups, function (index, groupItem) {
        var thisGroup = $(groupItem);
        var parent = thisGroup.closest('li');
        var myClone = $('li[data-offset-index="' + parent.attr('data-offset-index') + '"]>[class~="k-group"]', copied_elem);
        var myHeight = myClone.outerHeight();
        parent.css('margin-top', (myHeight + 2) + 'px');
        thisGroup.addClass('kendoGroupRowAbsoluteFix').css('top', '-' + (myHeight + 2) + 'px');
    });

    if (copied_elem != null) {
        copied_elem.remove();
    }
};

/* Error Messaging */

FlexiJS.Popup.InitiateEngagement.ShowEmailEditorError = function(isrollback) {
    if (isrollback) {
        showNotificationMessage('warning', FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "btnRollBackEngagement.FailedEmail"));
    } else {
        showNotificationMessage('warning', FlexiJS.Resources.GetResourceText("Controls/GMS/InitiateEngagement.ascx", "btnSaveEngagement.FailedEmail"));
    }
};
    ;/*
  _____   _                 _       _   ____        ____                                                          
 |  ___| | |   ___  __  __ (_)     | | / ___|      |  _ \    ___   ___    ___    _   _   _ __    ___    ___   ___ 
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | |_) |  / _ \ / __|  / _ \  | | | | | '__|  / __|  / _ \ / __|
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  |  _ <  |  __/ \__ \ | (_) | | |_| | | |    | (__  |  __/ \__ \
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_) |_| \_\  \___| |___/  \___/   \__,_| |_|     \___|  \___| |___/
                                                                                                                  


*/

FlexiJS.Resources = new Object();

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/

FlexiJS.Resources.LoadedResources = {};

/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/

FlexiJS.Resources.LoadFileResources = function (resourceset, forcereload) {
    // Description: Gets resource keys for a javascript file using a syncronous ajax call.
    //              File only loaded is the cached value is null or an empty string, or if the forcereload is passed as true
    // Created: ?? - ??
    // Updated: 13/03/2017 - v3.15.1 - JD - Moved from resources.js to new structure.
    resourceset = resourceset.toLowerCase();
    if (forcereload === true || !FlexiJS.Resources.LoadedResources.hasOwnProperty(resourceset)) {
        var inPageResources = $('input[type="hidden"][resourceset="' + resourceset + '"]');
        if ($(inPageResources).length > 0) {
            //-- Resource keys in hidden field in page, take from there
            $(FlexiJS.Resources.LoadedResources).attr(resourceset, $.parseJSON($(inPageResources).val()));
        } else {
            //-- Resource keys not in page, load from WS
            $.ajax({
                type: "POST",
                url: "/GMS/WebServices/GMSService.svc/GetJSFileResourcesTemplates",
                data: '{"resourceset":"' + resourceset.replace("\\", "\\\\") + '"}',
                processData: false,
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                async: false,
                success: function (result) {
                    $(FlexiJS.Resources.LoadedResources).attr(resourceset, $.parseJSON(result.d));
                }
            });
        }
    }
};

FlexiJS.Resources.GetResourceText = function (resourceset, resourcekey) {
    // Description: Gets resource text for the provided resource key from the cache, if it doesn't exist in the cache, it will try to refresh the resource set cache
    // Created: ?? - ??
    // Updated: 13/03/2017 - v3.15.1 - JD - Moved from resources.js to new structure. Two duplicte functions merged to one single function
    var returnVal = "";
    resourceset = resourceset.toLowerCase();
    resourcekey = resourcekey.toLowerCase();

    try {
        if (!FlexiJS.Resources.LoadedResources.hasOwnProperty(resourceset)) {
            //-- Attempt reload/load
            FlexiJS.Resources.LoadFileResources(resourceset, true);
        }
        if (FlexiJS.Resources.LoadedResources.hasOwnProperty(resourceset)) {
            //-- Resource set has been loaded
            var rs = $(FlexiJS.Resources.LoadedResources).attr(resourceset);
            if (rs) {
                for (var rk in rs) {
                    if (resourcekey === (rk + "").toLowerCase()) {
                        returnVal = $(rs).attr(rk);
                        break;
                    }
                }
            }
        }
    } catch (error) {
        //-- Log error
        FlexiJS.Error.LogError(error, 'FlexiJS.Resources.GetResourceText');
    }
    return returnVal;
};

;FlexiJS.UI = {};

FlexiJS.UI.SetBodyOverflow = function () {
    $('body').css('overflow', 'hidden');
};

FlexiJS.UI.ClearBodyOverflow = function () {
    setTimeout(function () { $('body').css('overflow', ''); }, 0);
};

FlexiJS.UI.SetOverflowAndCenterPopup = function (popup) {
    FlexiJS.UI.SetBodyOverflow();
    var testH = Math.floor($(window).height() * 0.8);
    if (testH < popup.wrapper.height()) {
        popup.setOptions({ height: testH });
    }
    popup.center();
};


//Show hide controls
FlexiJS.UI.ShowHideControl = function (ControlId, TextControlId, ShowText, HideText) {
    $('[id*=' + ControlId + ']').toggle();
    if ($('[id*=' + ControlId + ']').css('display') == 'undefined' || $('[id*=' + ControlId + ']').css('display') == 'none') {
        $('[id*=' + TextControlId + ']').text(ShowText);
    } else {
        $('[id*=' + TextControlId + ']').text(HideText);
    }
};

FlexiJS.UI.EnableDisableButton = function (Control, DisabledText) {
    //If disabled text is passed, the button will display this text when disabled, and show its pre-disabled text when re-enabled
    //If you don't pass disabled text this will not happen

    var jqControl = $(Control); //cahced as a selector may be passed
    if (jqControl.hasClass('aspNetDisabled')) {
        jqControl.removeAttr("disabled").removeClass("aspNetDisabled").css('cursor', 'pointer');
        var cachedtext = jqControl.data('cachedEnabledText');
        if (cachedtext) {
            jqControl.text(cachedtext);
            jqControl.data('cachedEnabledText', null);
        }
    } else {
        jqControl.attr("disabled", true).addClass('aspNetDisabled').css('cursor', 'not-allowed');
        if (DisabledText && DisabledText != jqControl.text()) {
            jqControl.data('cachedEnabledText', jqControl.text());
            jqControl.text(DisabledText);
        }
    }
}

FlexiJS.UI.ShowAlert = function (AlertText) {
    alert(AlertText);
};

//Form Item Table

// Used in conjunction with ControlHelper.ShowHideControl
FlexiJS.UI.RCBShowHideControl = function (Sender, EventArgs, ValueToMatch, ControlIDToShowHide, InvertShowHide) {

    // make sure the control exists
    if (document.getElementById(ControlIDToShowHide)) {

        // get the control to show/hide
        var ControlToShowHide = document.getElementById(ControlIDToShowHide);

        // get the selected item/value
        var item = EventArgs.get_item();
        var value = item.get_value();

        // check if selected value matches 'value to match'
        if (value == ValueToMatch) {
            if (InvertShowHide == "true") {
                // if a match, hide ctrl
                ControlToShowHide.style.display = "block";
            }
            else {
                // if a match, hide ctrl
                ControlToShowHide.style.display = "none";
            }
        }
        else {
            if (InvertShowHide == "true") {
                // if a match, hide ctrl
                ControlToShowHide.style.display = "none";
            }
            else {
                // if a match, hide ctrl
                ControlToShowHide.style.display = "block";
            }
        }
    }
};

// Used in conjunction with ControlHelper.ShowHideControl
FlexiJS.UI.CHKShowHideControl = function (control, ControlIDToShowHide) {

    if (control.checked) {
        $('#' + ControlIDToShowHide).show(150);
    }
    else {
        $('#' + ControlIDToShowHide).hide(150);
    }
};

// Used in conjunction with ControlHelper.ShowHideControl
FlexiJS.UI.DDLShowHideControl = function (control, ControlIDToShowHide, ValueToShow) {

    var SelectedValue = control.options[control.selectedIndex].value;

    if (SelectedValue == ValueToShow) {
        $('#' + ControlIDToShowHide).show(150);
    }
    else {
        $('#' + ControlIDToShowHide).hide(150);
    }
};

// Used in conjunction with ControlHelper.ShowHideControl
FlexiJS.UI.RBLShowHideControl = function (control, ControlIDToShowHide, ValueToShow) {

    var SelectedValue = control.value;

    if (SelectedValue == ValueToShow) {
        $('#' + ControlIDToShowHide).show(150);
    }
    else {
        $('#' + ControlIDToShowHide).hide(150);
    }
};


FlexiJS.UI.DisableKeyPress = function (ctrl, e, value) {
    //This only works for telerik control
    //ToDo: Extend to make this work for every control
    if (e.get_domEvent().rawEvent.keyCode == value) {
        e.get_domEvent().preventDefault()
        e.get_domEvent().stopPropagation()
    }
};

FlexiJS.UI.SetupFooter = function () {

    /*check footer exists*/
    if ($(".fx-containerfooter").length > 0) {

        FlexiJS.UI.PositionFooter();

        /*setup resize event*/
        $(window).on("resize", function () {

            FlexiJS.UI.PositionFooter(true);

        });

        /*setup resize event*/
        $(window).on("resize", function () {

            FlexiJS.UI.PositionFooter(true);

        });

        var resizeObserver = new ResizeObserver(function (entries) {
            return FlexiJS.UI.PositionFooter(true);
        });

        // start observing a body for size changes
        resizeObserver.observe(document.body);


    }
}

FlexiJS.UI.PositionFooter = function(resized) {

    var winHeight = window.innerHeight;
    var bodyHeight = $("body").height();
    var footerHeight = $(".fx-containerfooter").height();
    var combinedHeight = 0;

    /*if resizing and already fixed, include footerHeight in calculation*/
    if (resized == true && $(".fx-containerfooter").hasClass('fixed')) {
        combinedHeight = bodyHeight + footerHeight;
    }
    else {
        combinedHeight = bodyHeight;
    }

    if (combinedHeight <= winHeight) {
        $(".fx-containerfooter").addClass('fixed');
    }
    else {
        $(".fx-containerfooter").removeClass('fixed');
    }
}


FlexiJS.UI.ShowDefaultLoader = function (appendToControlId, message) {

    var loader = "<div class='loader-results'>";
    loader += "<div>";
    loader += "<div class='spinner' aria-hidden='true'></div>";
    loader += "<h2 role='alert'>" + message + "</h2>";
    loader += "</div>";
    loader += "</div>";

    $(loader).appendTo($("#" + appendToControlId));

}

FlexiJS.UI.RemoveDefaultLoader = function (controlId) {
    $("#" + controlId).find('.loader-results').remove();
}

;/*
  _____   _                 _       _   ____        _   _   _     _   _                _
 |  ___| | |   ___  __  __ (_)     | | / ___|      | | | | | |_  (_) | |  ___         / \     _ __   _ __    __ _   _   _   ___
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | | | | | __| | | | | / __|       / _ \   | '__| | '__|  / _` | | | | | / __|
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |_| | | |_  | | | | \__ \  _   / ___ \  | |    | |    | (_| | | |_| | \__ \
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_)  \___/   \__| |_| |_| |___/ (_) /_/   \_\ |_|    |_|     \__,_|  \__, | |___/
                                                                                                                    |___/

-- Overview of general functionality of this namespace

Requirements:
    -- Any libraries that this file depends on, like jQuery or Kendo
*/

FlexiJS.Utils.Arrays = {};

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/

/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/

FlexiJS.Utils.Arrays.ObjectPropertiesToArray = function (objectToRead) {
    // Description: Function to remove html from a string
    // Created: 09/05/2017, v3.17 - JD
    var returnArray = [];
    var propertyNames = Object.getOwnPropertyNames(objectToRead);
    if (propertyNames && propertyNames.length > 0) {
        var propertyNameLoopIndex = 0, propertyNameLoopMax = propertyNames.length;
        for (propertyNameLoopIndex = 0; propertyNameLoopIndex < propertyNameLoopMax; propertyNameLoopIndex++) {
            var propertyName = propertyNames[propertyNameLoopIndex];
            returnArray.push({ key: propertyName, value: objectToRead[propertyName] });
        }
    }
    return returnArray;
};
;/*

  _____   _                 _       _   ____        _   _   _     _   _                 _   ____     ___    _   _ 
 |  ___| | |   ___  __  __ (_)     | | / ___|      | | | | | |_  (_) | |  ___          | | / ___|   / _ \  | \ | |
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | | | | | __| | | | | / __|      _  | | \___ \  | | | | |  \| |
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |_| | | |_  | | | | \__ \  _  | |_| |  ___) | | |_| | | |\  |
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_)  \___/   \__| |_| |_| |___/ (_)  \___/  |____/   \___/  |_| \_|
                                                                                                                  

*/

FlexiJS.Utils.JSON = {};

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/


/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               
*/

FlexiJS.Utils.JSON.EnsureArrayFromExists = function () {
    //Polyfill from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Polyfill
    // Production steps of ECMA-262, Edition 6, 22.1.2.1
    if (!Array.from) {
        Array.from = (function () {
            var toStr = Object.prototype.toString;
            var isCallable = function (fn) {
                return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
            };
            var toInteger = function (value) {
                var number = Number(value);
                if (isNaN(number)) { return 0; }
                if (number === 0 || !isFinite(number)) { return number; }
                return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
            };
            var maxSafeInteger = Math.pow(2, 53) - 1;
            var toLength = function (value) {
                var len = toInteger(value);
                return Math.min(Math.max(len, 0), maxSafeInteger);
            };

            // The length property of the from method is 1.
            return function from(arrayLike/*, mapFn, thisArg */) {
                // 1. Let C be the this value.
                var C = this;

                // 2. Let items be ToObject(arrayLike).
                var items = Object(arrayLike);

                // 3. ReturnIfAbrupt(items).
                if (arrayLike == null) {
                    throw new TypeError('Array.from requires an array-like object - not null or undefined');
                }

                // 4. If mapfn is undefined, then let mapping be false.
                var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
                var T;
                if (typeof mapFn !== 'undefined') {
                    // 5. else
                    // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
                    if (!isCallable(mapFn)) {
                        throw new TypeError('Array.from: when provided, the second argument must be a function');
                    }

                    // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
                    if (arguments.length > 2) {
                        T = arguments[2];
                    }
                }

                // 10. Let lenValue be Get(items, "length").
                // 11. Let len be ToLength(lenValue).
                var len = toLength(items.length);

                // 13. If IsConstructor(C) is true, then
                // 13. a. Let A be the result of calling the [[Construct]] internal method 
                // of C with an argument list containing the single item len.
                // 14. a. Else, Let A be ArrayCreate(len).
                var A = isCallable(C) ? Object(new C(len)) : new Array(len);

                // 16. Let k be 0.
                var k = 0;
                // 17. Repeat, while k < len… (also steps a - h)
                var kValue;
                while (k < len) {
                    kValue = items[k];
                    if (mapFn) {
                        A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
                    } else {
                        A[k] = kValue;
                    }
                    k += 1;
                }
                // 18. Let putStatus be Put(A, "length", len, true).
                A.length = len;
                // 20. Return A.
                return A;
            };
        }());
    }
};

FlexiJS.Utils.JSON.CompressJSONToZipByteArray = function (JSONToCompress) {
    return FlexiJS.Utils.JSON.CompressStringToZipByteArray(JSON.stringify(JSONToCompress));
};

FlexiJS.Utils.JSON.CompressStringToZipByteArray = function (StringToCompress) {
    FlexiJS.Utils.JSON.EnsureArrayFromExists();
    var zip = new JSZip();
    zip.file(
        "data.json",
        StringToCompress
    );
    return Array.from(
        zip.generate(
            {
                type: "uint8array",
                compression: "DEFLATE",
                compressionOptions: { level: 10 }
            }
        )
    );
};;/*
  _____   _                 _       _   ____        _   _   _     _   _             ____    _            _                       
 |  ___| | |   ___  __  __ (_)     | | / ___|      | | | | | |_  (_) | |  ___      / ___|  | |_   _ __  (_)  _ __     __ _   ___ 
 | |_    | |  / _ \ \ \/ / | |  _  | | \___ \      | | | | | __| | | | | / __|     \___ \  | __| | '__| | | | '_ \   / _` | / __|
 |  _|   | | |  __/  >  <  | | | |_| |  ___) |  _  | |_| | | |_  | | | | \__ \  _   ___) | | |_  | |    | | | | | | | (_| | \__ \
 |_|     |_|  \___| /_/\_\ |_|  \___/  |____/  (_)  \___/   \__| |_| |_| |___/ (_) |____/   \__| |_|    |_| |_| |_|  \__, | |___/
                                                                                                                     |___/       

-- Overview of general functionality of this namespace

Requirements:
    -- Any libraries that this file depends on, like jQuery or Kendo
*/

FlexiJS.Utils.Strings = new Object();

/*
 __     __                 _           _       _                     __    ____            _           
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _ 
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|
                                                                                                       

        Any namespace variables or data objects should be declared here
*/

/*
  _____                          _     _                       
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___ 
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/
                                                               

        Any functions should be declared here
        Functions should ALWAYS be commented with the following format
        // Description: DESCRIPTION OF THE FUNCTIONS PURPOSE
        // Created: 00/00/0000, v0.00 - INITIALS
        // Updated: 00/00/0000, v0.00 - INITIALS - Summary Of Changes
        Error handling should use FlexiJS.Error to log any errors
*/

FlexiJS.Utils.Strings.FormatString = function () {
    // Description: Clone of .net string.format function for string replacement
    // Created: 17/03/2017, v3.15.2 - JD
    // Updated: 23/05/2017, v3.17 - JD - Changed Regex to handle multiple replaces of the same parameter
    var returnValue = '';
    if (arguments && arguments.length > 1) {
        returnValue = arguments[0];

        // Get all replacements
        var replacements = [];
        for (var i = 1; i < arguments.length; i++) {
            replacements.push({ paramIndex: i - 1, newtext: '' + arguments[i] });
        }

        // Replace Passed Params
        $.each(replacements, function (index, itemToReplace) {
            returnValue = returnValue.replace(new RegExp("[{]" + itemToReplace.paramIndex + "[}]","g"), itemToReplace.newtext);
        });

        // Remove any non populated parameter holders
        returnValue = returnValue.replace(/{[\d]*}/g, '');

    }
    return returnValue;

};


FlexiJS.Utils.Strings.RemoveHTMLFromString = function (stringToRead, encodeRemainingGtAndLt) {
    // Description: Function to remove html from a string
    // Created: 08/05/2017, v3.16.2 - JD
    var returnString = "";
    if (stringToRead === null) stringToRead = "";
    if (typeof encodeRemainingGtAndLt === 'undefined' || encodeRemainingGtAndLt === null) encodeRemainingGtAndLt = true;
    try {
        // In order for this to work we need to outer wrap the string so that nothing gets missed
        returnString = $("<div>" + stringToRead + "</div>").text();
        if (encodeRemainingGtAndLt === true) {
            returnString = returnString.replace(/</g, "&lt;").replace(/>/g, "&gt;");
        }
    }
    catch (ex) {
        returnString = "";
    }

    return returnString;
};

FlexiJS.Utils.Strings.EscapeHtmlAttribute = function (str) {
    //Produces the same result as escape, but not depreciated
    var map = {'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;'};
    return encodeURIComponent(str.replace(/[&<>"']/g, function (m) { return map[m]; }));
};

FlexiJS.Utils.Strings.DecodeHtmlAttribute = function (str) {
    //Produces the same result as unescape, but not depreciated
    var map = {'&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '"', '&#039;': "'"};
    return decodeURIComponent(str).replace(/&amp;|&lt;|&gt;|&quot;|&#039;/g, function (m) { return map[m]; });
};;/*
    Add find and findIndex prototypes to deal with IE9-IE11
*/

// https://tc39.github.io/ecma262/#sec-array.prototype.findIndex
if (!Array.prototype.findIndex)
{
    Object.defineProperty(Array.prototype, 'findIndex', {
        value: function (predicate)
        {
            // 1. Let O be ? ToObject(this value).
            if (this == null)
            {
                throw new TypeError('"this" is null or not defined');
            }

            var o = Object(this);

            // 2. Let len be ? ToLength(? Get(O, "length")).
            var len = o.length >>> 0;

            // 3. If IsCallable(predicate) is false, throw a TypeError exception.
            if (typeof predicate !== 'function')
            {
                throw new TypeError('predicate must be a function');
            }

            // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
            var thisArg = arguments[1];

            // 5. Let k be 0.
            var k = 0;

            // 6. Repeat, while k < len
            while (k < len)
            {
                // a. Let Pk be ! ToString(k).
                // b. Let kValue be ? Get(O, Pk).
                // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
                // d. If testResult is true, return k.
                var kValue = o[k];
                if (predicate.call(thisArg, kValue, k, o))
                {
                    return k;
                }
                // e. Increase k by 1.
                k++;
            }

            // 7. Return -1.
            return -1;
        }
    });
}

// https://tc39.github.io/ecma262/#sec-array.prototype.find
if (!Array.prototype.find)
{
    Object.defineProperty(Array.prototype, 'find', {
        value: function (predicate)
        {
            // 1. Let O be ? ToObject(this value).
            if (this == null)
            {
                throw new TypeError('"this" is null or not defined');
            }

            var o = Object(this);

            // 2. Let len be ? ToLength(? Get(O, "length")).
            var len = o.length >>> 0;

            // 3. If IsCallable(predicate) is false, throw a TypeError exception.
            if (typeof predicate !== 'function')
            {
                throw new TypeError('predicate must be a function');
            }

            // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
            var thisArg = arguments[1];

            // 5. Let k be 0.
            var k = 0;

            // 6. Repeat, while k < len
            while (k < len)
            {
                // a. Let Pk be ! ToString(k).
                // b. Let kValue be ? Get(O, Pk).
                // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
                // d. If testResult is true, return kValue.
                var kValue = o[k];
                if (predicate.call(thisArg, kValue, k, o))
                {
                    return kValue;
                }
                // e. Increase k by 1.
                k++;
            }

            // 7. Return undefined.
            return undefined;
        }
    });
}


if (!Array.prototype.includes)
{
    Object.defineProperty(Array.prototype, "includes", {
        enumerable: false,
        value: function (obj)
        {
            var newArr = this.filter(function (el)
            {
                return el == obj;
            });
            return newArr.length > 0;
        }
    });
}

if (!String.prototype.replacelast) {
    String.prototype.replacelast = function (what, replacement) {
        var whatRE = new RegExp(what, "gm");

        var lastStartIndex = 0;
        while (whatRE.test(this) == true) {
            lastStartIndex = whatRE.lastIndex - whatRE.source.length;
        }
        return this.replace(whatRE, function (s, i) { return i >= lastStartIndex ? replacement : s; });
    };
}

if (!String.prototype.includes) {
    String.prototype.includes = function () {
        return String.prototype.indexOf.apply(this, arguments) !== -1;
    };
}

// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
if (!Object.keys) {
    Object.keys = (function () {
        'use strict';
        var hasOwnProperty = Object.prototype.hasOwnProperty,
            hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
            dontEnums = [
                'toString',
                'toLocaleString',
                'valueOf',
                'hasOwnProperty',
                'isPrototypeOf',
                'propertyIsEnumerable',
                'constructor'
            ],
            dontEnumsLength = dontEnums.length;

        return function (obj) {
            if (typeof obj !== 'function' && (typeof obj !== 'object' || obj === null)) {
                throw new TypeError('Object.keys called on non-object');
            }

            var result = [], prop, i;

            for (prop in obj) {
                if (hasOwnProperty.call(obj, prop)) {
                    result.push(prop);
                }
            }

            if (hasDontEnumBug) {
                for (i = 0; i < dontEnumsLength; i++) {
                    if (hasOwnProperty.call(obj, dontEnums[i])) {
                        result.push(dontEnums[i]);
                    }
                }
            }
            return result;
        };
    }());
}

if (!Object.entries) {
    Object.entries = function (obj) {
        var ownProps = Object.keys(obj),
            i = ownProps.length,
            resArray = new Array(i); // preallocate the Array
        while (i--)
            resArray[i] = [ownProps[i], obj[ownProps[i]]];

        return resArray;
    };
}

if (!Object.values) {
    Object.values = function values(obj) {
        return Object.keys(obj).map(function (e) {
            return obj[e]
        });
    };
}

Number.isInteger = Number.isInteger || function (value) {
    return typeof value === 'number' &&
        isFinite(value) &&
        Math.floor(value) === value;
};

if (!Array.prototype.flat) {
    Array.prototype.flat = function (depth) {

        'use strict';

        // If no depth is specified, default to 1
        if (depth === undefined) {
            depth = 1;
        }

        // Recursively reduce sub-arrays to the specified depth
        var flatten = function (arr, depth) {

            // If depth is 0, return the array as-is
            if (depth < 1) {
                return arr.slice();
            }

            // Otherwise, concatenate into the parent array
            return arr.reduce(function (acc, val) {
                return acc.concat(Array.isArray(val) ? flatten(val, depth - 1) : val);
            }, []);

        };

        return flatten(this, depth);
    };
};

//Custom Event Polyfil For IE
if (typeof window.CustomEvent !== "function") {
    window.CustomEvent = function (event, params) {
        params = params || { bubbles: false, cancelable: false, detail: null };
        var evt = document.createEvent('CustomEvent');
        evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
        return evt;
    };
};

// Event.composedPath
if (!Event.prototype.composedPath) {
    Event.prototype.composedPath = function () {
        if (this.path) {
            return this.path;
        }
        var target = this.target;

        this.path = [];
        while (target.parentNode !== null) {
            this.path.push(target);
            target = target.parentNode;
        }
        this.path.push(document, window);
        return this.path;
    };
};;FlexiJS.Utils.Login.PasswordValidation = new Object();

/*
 __     __                 _           _       _                     __    ____            _
 \ \   / /   __ _   _ __  (_)   __ _  | |__   | |   ___   ___       / /   |  _ \    __ _  | |_    __ _
  \ \ / /   / _` | | '__| | |  / _` | | '_ \  | |  / _ \ / __|     / /    | | | |  / _` | | __|  / _` |
   \ V /   | (_| | | |    | | | (_| | | |_) | | | |  __/ \__ \    / /     | |_| | | (_| | | |_  | (_| |
    \_/     \__,_| |_|    |_|  \__,_| |_.__/  |_|  \___| |___/   /_/      |____/   \__,_|  \__|  \__,_|


        Any namespace variables or data objects should be declared here
*/
FlexiJS.Utils.Login.PasswordValidation.PasswordPolicy = {};


FlexiJS.Utils.Login.PasswordValidation.IsValid = {
    MinLengthIsValid: false,
    MaxLengthIsValid: false,
    MinNumberOfUpperCaseIsValid: false,
    MinNumberOfLowerCaseIsValid: false,
    MinNumberOfDigitsIsValid: false,
    MinNumberOfSpecialCharactersIsValid: false,
    VulnerablePasswordsIsValid: false 
};

var passwordInputSelector = '#txtPassword'

var passwordValidationSelector = '#passwordValidationSteps ';
var minLenghtSelector = '#minLength';
var maxLenghtSelector = '#maxLength';
var upperCaseSelector = '#minUpperCase';
var lowerCaseSelector = '#minLowerCase';
var minDigitsSelector = '#minDigits';
var specialCharsSelector = '#minSpecial';
var vulnerablePasswordSelector = '#vulPassword';

var passwordIsValidSelector = '#divPasswordValid';

var passwordRequirementMet = "";
var passwordRequirementNotMet = "";

/*
  _____                          _     _
 |  ___|  _   _   _ __     ___  | |_  (_)   ___    _ __    ___
 | |_    | | | | | '_ \   / __| | __| | |  / _ \  | '_ \  / __|
 |  _|   | |_| | | | | | | (__  | |_  | | | (_) | | | | | \__ \
 |_|      \__,_| |_| |_|  \___|  \__| |_|  \___/  |_| |_| |___/

        Error handling should use FlexiJS.Error to log any errors
*/

FlexiJS.Utils.Login.PasswordValidation.SetUp = function () {


    $('[id$="txtPassword"]').keyup(function () {
        ValidatorValidate($('[id$="cvPassword"]')[0]);
    });

    $('[id$="txtConfirmPassword"]').keyup(function () {
        ValidatorValidate($('[id$="cvConfirmPassword"]')[0]);
    });

    $('[id$="txtConfirmPassword"]').blur(function () {
        ValidatorValidate($('[id$="cvConfirmPassword"]')[0]);
    });

    FlexiJS.Utils.Login.PasswordValidation.GetPasswordPolicy();
    FlexiJS.Utils.Login.PasswordValidation.SetValidationMessageText();

}

FlexiJS.Utils.Login.PasswordValidation.GetPasswordPolicy = function () {
    if ($.isEmptyObject(FlexiJS.Utils.Login.PasswordValidation.PasswordPolicy) ){
        $.ajax({
            type: "POST",
            async: false,
            timeout: 500,
            contentType: "application/json",
            dataType: "json",
            url: "/validation/customvalidation.aspx/GetPasswordPolicy",
            success: function (result) {
                // TODO: handle result being null -try catch?
                FlexiJS.Utils.Login.PasswordValidation.PasswordPolicy = JSON.parse(result.d);
            },
            error: function (jqXHR, textStatus, errorThrown) {
                FlexiJS.Error.LogError(errorThrown, 'GetPasswordPolicy');
            }
        });
    };

}

FlexiJS.Utils.Login.PasswordValidation.SetValidationMessageText = function () {
    var policy = FlexiJS.Utils.Login.PasswordValidation.PasswordPolicy;
    var passwordValidation = $(passwordValidationSelector);

    passwordValidation.find("> p").text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'PasswordPolicyDescription'));

    passwordValidation.find(minLenghtSelector).find("div span").text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MinCharacterLength').replace("{0}", policy.MinLength));
    passwordValidation.find(maxLenghtSelector).find("div span").text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MaxCharacterLength').replace("{0}", policy.MaxLength));
    passwordValidation.find(upperCaseSelector).find("div span").text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MinUpperCase'));
    passwordValidation.find(lowerCaseSelector).find("div span").text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MinLowerCase'));
    passwordValidation.find(minDigitsSelector).find("div span").text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MinDigits'));
    passwordValidation.find(specialCharsSelector).find("div span").text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MinSpecialCharacters'));
    passwordValidation.find(vulnerablePasswordSelector).find("div span").text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'VulnerablePassword'));
    $(passwordIsValidSelector).find("span").text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'PasswordMatches'));

    $('[id$="cvConfirmPassword"]').text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'PasswordsDoNotMatch'));

    passwordRequirementMet = FlexiJS.Resources.GetResourceText('PasswordPolicy','PasswordRequirementMet');
    passwordRequirementNotMet = FlexiJS.Resources.GetResourceText('PasswordPolicy', 'PasswordRequirementNotMet');  
}

FlexiJS.Utils.Login.PasswordValidation.ValidatePassword = function (src, args) {

    if ($.isEmptyObject(FlexiJS.Utils.Login.PasswordValidation.PasswordPolicy)) {
        FlexiJS.Utils.Login.PasswordValidation.GetPasswordPolicy();
    }

    var policy = FlexiJS.Utils.Login.PasswordValidation.PasswordPolicy; 

    FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckMinLength(args.Value, policy.MinLength);
    FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckMaxLength(args.Value, policy.MaxLength); 
    FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckMinNumberOfUpperCase(args.Value, policy.MinNumberOfUpperCase);
    FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckMinNumberOfLowerCase(args.Value, policy.MinNumberOfLowerCase);
    FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckMinNumberOfDigits(args.Value, policy.MinNumberOfDigits);
    FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckMinNumberSpecialCharacters(args.Value, policy.MinNumberOfSpecialCharacters, policy.SpecialCharacters);
    FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckVulnerablePasswords(args.Value, policy.VulnerablePasswords);

    args.IsValid = FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.IsAllValid();

}

FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckMinLength = function (password, minLength) {
    var element = $(minLenghtSelector).find("> span");
    if (password.length < minLength) {
        element.text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MinCharacterLength').replace("{0}", minLength) + ", " + passwordRequirementNotMet);
        FlexiJS.Utils.Login.PasswordValidation.IsValid.MinLengthIsValid = false;
        FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.UncheckElement($(minLenghtSelector));
    }
    else {
        element.text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MinCharacterLength').replace("{0}", minLength) + ", " + passwordRequirementMet); 
        FlexiJS.Utils.Login.PasswordValidation.IsValid.MinLengthIsValid = true;
        FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckElement($(minLenghtSelector));
    }
}

FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckMaxLength = function (password, maxLength) {
    var element = $(maxLenghtSelector).find("> span");
    if (password.length > maxLength) {
        element.text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MaxCharacterLength').replace("{0}", maxLength) + ", " + passwordRequirementNotMet);
        FlexiJS.Utils.Login.PasswordValidation.IsValid.MaxLengthIsValid = false;
        $(maxLenghtSelector).show();
    }
    else {
        element.text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MaxCharacterLength').replace("{0}", maxLength) + ", " + passwordRequirementMet);
        FlexiJS.Utils.Login.PasswordValidation.IsValid.MaxLengthIsValid = true;
        $(maxLenghtSelector).hide();
    }
}

FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckMinNumberOfUpperCase = function (password, minUpperCase) {
    var element = $(upperCaseSelector).find("> span");
    if (password.replace(/[^A-Z]/g, "").length < minUpperCase) {
        element.text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MinUpperCase') + ", " + passwordRequirementNotMet);
        FlexiJS.Utils.Login.PasswordValidation.IsValid.MinNumberOfUpperCaseIsValid = false;
        FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.UncheckElement($(upperCaseSelector));
    }
    else {
        element.text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MinUpperCase') + ", " + passwordRequirementMet);
        FlexiJS.Utils.Login.PasswordValidation.IsValid.MinNumberOfUpperCaseIsValid = true;
        FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckElement($(upperCaseSelector));
    }
}

FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckMinNumberOfLowerCase = function (password, minLowerCase) {
    var element = $(lowerCaseSelector).find("> span");
    if (password.replace(/[^a-z]/g, "").length < minLowerCase) {
        element.text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MinLowerCase') + ", " + passwordRequirementNotMet);
        FlexiJS.Utils.Login.PasswordValidation.IsValid.MinNumberOfLowerCaseIsValid = false;
        FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.UncheckElement($(lowerCaseSelector));
    }
    else {
        element.text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MinLowerCase') + ", " + passwordRequirementMet);
        FlexiJS.Utils.Login.PasswordValidation.IsValid.MinNumberOfLowerCaseIsValid = true;
        FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckElement($(lowerCaseSelector));
    }
}

FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckMinNumberOfDigits = function (password, minDigits) {
    var element = $(minDigitsSelector).find("> span");
    if (password.replace(/[^\d]/g, "").length < minDigits) {
        element.text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MinDigits') + ", " + passwordRequirementNotMet);
        FlexiJS.Utils.Login.PasswordValidation.IsValid.MinNumberOfDigitsIsValid = false;
        FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.UncheckElement($(minDigitsSelector));
    }
    else {
        element.text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MinDigits') + ", " + passwordRequirementMet);
        FlexiJS.Utils.Login.PasswordValidation.IsValid.MinNumberOfDigitsIsValid = true;
        FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckElement($(minDigitsSelector));
    }
}

FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckMinNumberSpecialCharacters = function (password, minSpecialCharacters, specialCharacters) {
    var count = 0;

    specialCharacters.forEach(function (char) {
        var specialChar = new RegExp('[^' + char + ']', 'g')
        count += password.replace(specialChar, "").length;
    })

    var element = $(specialCharsSelector).find("> span");
    if (count < minSpecialCharacters) {
        element.text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MinSpecialCharacters') + ", " + passwordRequirementNotMet);
        FlexiJS.Utils.Login.PasswordValidation.IsValid.MinNumberOfSpecialCharactersIsValid = false;
        FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.UncheckElement($(specialCharsSelector));
    }
    else {
        element.text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'MinSpecialCharacters') + ", " + passwordRequirementMet);
        FlexiJS.Utils.Login.PasswordValidation.IsValid.MinNumberOfSpecialCharactersIsValid = true;
        FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckElement($(specialCharsSelector));
    }
}

FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckVulnerablePasswords = function (password, VulnerablePasswords) {
    var element = $(vulnerablePasswordSelector).find("> span");
    var isVulnerablePassword = false;

    VulnerablePasswords.every(function (vulPass) {
        if (password === vulPass) {
            isVulnerablePassword = true;
            return false;
        }
        return true;
    });

    if (isVulnerablePassword) {
        // show vulnerable password message 
        element.text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'VulnerablePassword'));
        FlexiJS.Utils.Login.PasswordValidation.IsValid.VulnerablePasswordsIsValid = false;
        $(vulnerablePasswordSelector).show();
    }
    else {
        // hide vulnerable password message
        element.text(FlexiJS.Resources.GetResourceText('PasswordPolicy', 'NotVulnerablePassword'));
        FlexiJS.Utils.Login.PasswordValidation.IsValid.VulnerablePasswordsIsValid = true;
        $(vulnerablePasswordSelector).hide();
    }
}

FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.IsAllValid = function () {
    var allValid = true;

    for (var property in FlexiJS.Utils.Login.PasswordValidation.IsValid) {
        if (FlexiJS.Utils.Login.PasswordValidation.IsValid[property] === false) {
            allValid = false;
            break;
        }
    }

    if (allValid === true) {
        // show password valid block and hide validation steps
        $(passwordInputSelector).prop("aria-invalid", "false");
        $(passwordIsValidSelector).show();
        $(passwordValidationSelector).hide();
        return true;
    }
    else {
        // hide password valid block and show validation steps
        $(passwordInputSelector).prop("aria-invalid", "true");
        $(passwordIsValidSelector).hide();
        $(passwordValidationSelector).show();
        return false;
    }
}


FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.CheckElement = function (element) {
    element.addClass("checked").removeClass("unchecked");
    element.find("i").addClass("fa-check-circle fas").removeClass("fa-minus-circle fal");
}

FlexiJS.Utils.Login.PasswordValidation.ValidatePassword.UncheckElement = function (element) {
    element.addClass("unchecked").removeClass("checked");
    element.find("i").addClass("fa-minus-circle fal").removeClass("fa-check-circle fas");
}

FlexiJS.Utils.Login.PasswordValidation.ValidateConfirmPassword = function (src, args) {
    var comparePassword = args.Value;
    var password = $('[id$="txtPassword"]')[0].value;
    var confirmValidationSelecter = $('[id$="cvConfirmPassword"]');

    if (comparePassword === undefined || comparePassword.length === 0) {
        args.IsValid = false;
        confirmValidationSelecter.hide();
    }
    else {

        if (comparePassword === password) {
            args.IsValid = true;
            confirmValidationSelecter.hide();
        }
        else {
            args.IsValid = false;
            confirmValidationSelecter.show();
        }
    }

}

/* Show Hide Password - start */

FlexiJS.Utils.Login.PasswordValidation.ShowHidePassword = function (src, controlName) {
    var control = $('[id$="' + controlName + '"]');
    if (control.attr('type') === 'password') {
        control[0].setAttribute("type", "text");
        // set icon
        src.find('> i').removeClass('fa-eye').addClass('fa-eye-slash');
        // change text to hide
        src.find('> span').text(FlexiJS.Resources.GetResourceText("ShowHidePassword", "Hide"));
        // accessibility 
        src.attr("aria-pressed", "true");
    }
    else {
        FlexiJS.Utils.Login.PasswordValidation.HidePasswordDefault(src, controlName);
    }
    control.focus();
};

FlexiJS.Utils.Login.PasswordValidation.HidePasswordDefault = function (src, controlName) {
    var control = $('[id$="' + controlName + '"]');
    control[0].setAttribute('type', 'password');
    // set icon
    src.find('> i').removeClass('fa-eye-slash').addClass('fa-eye');
    // change text to show
    src.find('> span').text(FlexiJS.Resources.GetResourceText("ShowHidePassword", "Show"));
    // accessibility 
    src.attr("aria-pressed", "false");
};

/* Show Hide Password - end */;//Telerik JS Fixes

$(function () {
    //Fix issue with misplaced rad control elements in chrome/ff
    //see https://www.telerik.com/forums/context-menu-placement-problem-in-chrome#lmR1aHty2UaA4vXCFF0svg
    if (typeof $telerik != 'undefined') {
        $telerik.getViewPortSize = function () {
            var width = 0;
            var height = 0;

            var canvas = document.body;

            if ((!$telerik.quirksMode && !$telerik.isSafari) ||
                (Telerik.Web.Browser.chrome && Telerik.Web.Browser.version >= 61)) {
                canvas = document.documentElement;
            }

            if (window.innerWidth) {
                // Seems there's no completely reliable way to get the viewport size in Gecko, this should be the best one
                // Check https://bugzilla.mozilla.org/show_bug.cgi?id=189112#c7
                width = Math.max(document.documentElement.clientWidth, document.body.clientWidth);
                height = Math.max(document.documentElement.clientHeight, document.body.clientHeight);

                if (width > window.innerWidth)
                    width = document.documentElement.clientWidth;
                if (height > window.innerHeight)
                    height = document.documentElement.clientHeight;
            }
            else {
                width = canvas.clientWidth;
                height = canvas.clientHeight;
            }

            width += canvas.scrollLeft;
            height += canvas.scrollTop;

            if ($telerik.isMobileSafari) {
                width += window.pageXOffset;
                height += window.pageYOffset;
            }

            return { width: width - 6, height: height - 6 };
        };
    }
});

$(function () {
    //Fix accessability issue with hidden inputs in date pickers
    if (typeof $telerik != 'undefined') {
        $telerik.updateInaccessableDateInput = function (dateSelectId) {
            var input = $("input[id='" + dateSelectId + "'");
            input.attr('aria-hidden', true);
            input.attr('aria-label', input.attr('title'));
            input.attr('title', null);
        };
    }
});

$(function () {
    //Fix issues where rad editors instanciated inside a hidden object hav an incorrect menu size
    $('.RadEditor').each(
        function (index, element) {
            if (element.control) {
                if (element.control._toolbarMode === 8) {
                    var contentArea = element.control.get_contentArea();
                    $telerik.addExternalHandler(contentArea, "click", function (e) {
                        var toolAdapter = element.control._toolAdapter._toolbarHolder;
                        if (toolAdapter) {
                            toolAdapter.Hide();
                        }
                        var realWidth = $(element).outerWidth();
                        element.control.set_toolsWidth(realWidth);
                        if (toolAdapter) {
                            toolAdapter.Show();
                        }
                    });
                }
            }
        }
    );
});

;FlexiJS.PostsAndTenure = {};
FlexiJS.PostsAndTenure.ApplicationPost = {};
FlexiJS.PostsAndTenure.EcfPostsTenure = {};

FlexiJS.PostsAndTenure.GetPageServiceMethodUrl = function (methodName) {
    return '/WebServices/PageService.svc/' + methodName;
};

FlexiJS.PostsAndTenure.GetGMSServiceMethodUrl = function (methodName) {
    return '/gms/WebServices/GMSService.svc/' + methodName;
};

FlexiJS.PostsAndTenure.GetResourceText = function (resourceSet, resourceKey, withHashReplace) {
    var resourceText = FlexiJS.Resources.GetResourceText(resourceSet, resourceKey);

    if (withHashReplace === true) {
        resourceText = resourceText.replace(/#/g, '\\\\#');
    }

    return resourceText;
};

FlexiJS.PostsAndTenure.GetPostHolderInformationTemplateResource = function (resourceKey, withHashReplace) {
    return FlexiJS.PostsAndTenure.GetResourceText('/scripts/kendotemplates/flexijs/controls/postholderinformationtemplate.html', resourceKey, withHashReplace);
};

FlexiJS.PostsAndTenure.InitializeConstants = function (appId, ddlRolesSelector, btnUpdateRoleSelector, btnCancelSelector, hfTitlesId, hfNationalitiesId, dateFormatString, dateParseFormats, dateCulture, hfOncostsData) {
    FlexiJS.PostsAndTenure.Constants = {
        _appId: appId,
        _btnAddPostSelector: '',
        _ddlRolesSelector: ddlRolesSelector,
        _btnUpdateRoleSelector: btnUpdateRoleSelector,
        _btnCancelSelector: btnCancelSelector,
        _hfTitlesId: hfTitlesId,
        _hfNationalitiesId: hfNationalitiesId,
        _dateFormatString: dateFormatString,
        _dateParseFormats: dateParseFormats,
        _dateCulture: dateCulture,
        _postGridSelector: '',
        _tsPostsSelector: '',
        _currentPostsGridSelector: '',
        _auditGridSelector: '',
        _deletePostFunctionName: 'deletePost',
        _removePersonFunctionName: 'removePerson',
        _postTitleId: 'postTitle',
        _postFormSelector: '#postForm',
        _specifyRoleDivSelector: '#divSpecifyRole',
        _incrementDateSelector: '#incrementDateField',
        _incrementDateDivSelector: '#incrementDateDiv',
        _roleFilledYesSelector: '#roleFilledYesField',
        _roleFilledNoSelector: '#roleFilledNoField',
        _hfRoleFilledId: 'hfRoleFilled',
        _sectionFilledRoleSelector: '#sectionFilledRoll',
        _monthFieldId: 'monthField',
        _lblMonthId: 'lblMonth',
        _specifyRoleFieldId: 'specifyRoleField',
        _startingGradeFieldId: 'startingGradeField',
        _spinePointFieldId: 'spinePointField',
        _fteFieldId: 'fteField',
        _lblFteId: 'lblFte',
        _startingSalaryFieldId: 'startingSalaryField',
        _lblStartingSalaryId: 'lblStartingSalary',
        _postHolderContainerSelector: '#postHolderContainer',
        _btnAddPersonId: 'btnAddPerson',
        _hfPostIdId: 'hfPostId',
        _ddlRolesValidationId: 'ddlRolesValidation',
        _specifyRoleValidationId: 'specifyRoleValidation',
        _durationValidationId: 'durationValidation',
        _fteValidationId: 'fteValidation',
        _startingSalaryValidationId: 'startingSalaryValidation',
        _incrementDateValidationId: 'incrementDateValidation',
        _incrementDateFieldKendoValidationId: 'incrementDateField_validationMessage',
        _startingGradeValidationId: 'startingGradeValidation',
        _spinePointValidationId: 'spinePointValidation',

        _openedPostId: 0,
        _postHolderCount: 0,
        _maxPostHolderCount: 50,
        _personIndexToRemove: 0,
        _initializeAttribute: 'data-initialize',
        _personIndexAttribute: 'data-person-index',
        _idPrefixAttribute: 'data-id-prefix',
        _namePrefixAttribute: 'data-name-prefix',
        _forPrefixAttribute: 'data-for-prefix',
        _toggleButtonUp: '<i class="far fa-chevron-right chevron"></i>',
        _toggleButtonDown: '<i class="far fa-chevron-down chevron"></i>',
        _collapsedAttribute: 'data-collapsed',

        _postHolderHtml: null,

        _hfOncostsData: hfOncostsData,
        _sectionOncostsSelector: '#sectionOncosts',
        _oncostGridSelector: '#onCostsField',
        _oncostsAddButtonSelector: '#btnAddPostOnCostPeriod'
    };
};

FlexiJS.PostsAndTenure.InitializeControls = function () {
    FlexiJS.PostsAndTenure.InitializeStartingSalaryField();
    FlexiJS.PostsAndTenure.InitializeFTEField();
    FlexiJS.PostsAndTenure.InitializeDurationFields();
    FlexiJS.PostsAndTenure.InitializeStartingGradeField();
    FlexiJS.PostsAndTenure.InitializeSpinePointField();
    FlexiJS.PostsAndTenure.InitializeDdlRoles();
    FlexiJS.PostsAndTenure.InitializeSpecifyRole();
    FlexiJS.PostsAndTenure.InitializeIncrementDatePicker();
    FlexiJS.PostsAndTenure.InitializeRoleFilledNoClick();
    FlexiJS.PostsAndTenure.InitializeRoleFilledYesClick();
    FlexiJS.PostsAndTenure.InitializeBtnCancelClick();
    FlexiJS.PostsAndTenure.InitializeBtnAddPersonClick();
};

FlexiJS.PostsAndTenure.InitializeStartingSalaryField = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const startingSalaryField = $('#' + constants._startingSalaryFieldId);
    const startingSalaryValidation = $('#' + constants._startingSalaryValidationId);

    startingSalaryField.kendoNumericTextBox({
        format: 'c',
        decimals: 2,
        min: 0,
        spinners: false
    });

    $('#' + constants._lblStartingSalaryId).click(function (e) {
        startingSalaryField.data('kendoNumericTextBox').focus();
    });

    startingSalaryField.blur(function () {
        if (startingSalaryField.val()) {
            startingSalaryValidation.hide();
        }
    });
};

FlexiJS.PostsAndTenure.InitializeFTEField = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const fteField = $('#' + constants._fteFieldId);
    const fteValidation = $('#' + constants._fteValidationId);

    fteField.kendoNumericTextBox({
        format: 'n',
        decimals: 2,
        min: 0,
        max: 100,
        spinners: false
    });

    $('#' + constants._lblFteId).click(function (e) {
        fteField.data('kendoNumericTextBox').focus();
    });

    fteField.blur(function () {
        if (fteField.val()) {
            fteValidation.hide();
        }
    });
};

FlexiJS.PostsAndTenure.InitializeDurationFields = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const monthField = $('#' + constants._monthFieldId);
    const durationValidation = $('#' + constants._durationValidationId);

    monthField.kendoNumericTextBox({
        format: 'n0',
        decimals: 0,
        min: 0,
        step: 1,
        spinners: false
    });

    monthField.data('kendoNumericTextBox').value(null);

    $('#' + constants._lblMonthId).click(function () {
        monthField.data('kendoNumericTextBox').focus();
    });

    monthField.blur(function () {
        if (monthField.data('kendoNumericTextBox').value()) {
            durationValidation.hide();
        }
    });
};

FlexiJS.PostsAndTenure.InitializeStartingGradeField = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const startingGradeField = $('#' + constants._startingGradeFieldId);
    const startingGradeValidation = $('#' + constants._startingGradeValidationId);

    startingGradeField.blur(function () {
        if (startingGradeField.val()) {
            startingGradeValidation.hide();
        }
    });
};

FlexiJS.PostsAndTenure.InitializeSpinePointField = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const spinePointField = $('#' + constants._spinePointFieldId);
    const spinePointValidation = $('#' + constants._spinePointValidationId);

    spinePointField.blur(function () {
        if (spinePointField.val()) {
            spinePointValidation.hide();
        }
    });
};

FlexiJS.PostsAndTenure.InitializeDdlRoles = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const ddlRoles = $(constants._ddlRolesSelector);
    const specifyRoleDiv = $(constants._specifyRoleDivSelector);
    const roleValidation = $('#' + constants._ddlRolesValidationId);

    ddlRoles.bind('change', function () {
        if (ddlRoles.val()) {
            roleValidation.hide();

            // 0 means "Other" here.
            if (ddlRoles.val() === '0') {
                specifyRoleDiv.show();
            } else {
                specifyRoleDiv.hide();
            }
        }
    });
};

FlexiJS.PostsAndTenure.InitializeSpecifyRole = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const spinePointField = $('#' + constants._specifyRoleFieldId);
    const spinePointValidation = $('#' + constants._specifyRoleValidationId);

    spinePointField.blur(function () {
        if (spinePointField.val()) {
            spinePointValidation.hide();
        }
    });
};

FlexiJS.PostsAndTenure.InitializeIncrementDatePicker = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const dpOptions = FlexiJS.PostsAndTenure.GetKendoDatePickerOptions();
    const incrementDatePicker = $(constants._incrementDateSelector);
    const incrementDateValidation = $('#' + constants._incrementDateValidationId);

    incrementDatePicker.kendoDatePicker(dpOptions);
    incrementDatePicker.attr('placeholder', dpOptions.format);

    FlexiJS.PostsAndTenure.SetupDateValidation($(constants._incrementDateDivSelector));

    incrementDatePicker.blur(function () {
        if (incrementDatePicker.data('kendoDatePicker').value()) {
            incrementDateValidation.hide();
        }
    });
};

FlexiJS.PostsAndTenure.InitializeRoleFilledYesClick = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;

    $(constants._roleFilledYesSelector).bind('click', function () {
        const hfRoleFilled = document.getElementById(constants._hfRoleFilledId);

        if (hfRoleFilled.value !== 'true') {
            hfRoleFilled.value = 'true';

            $(constants._sectionFilledRoleSelector).show();

            FlexiJS.PostsAndTenure.AddPostHolderInformationForm();
        }
    });
};

FlexiJS.PostsAndTenure.InitializeRoleFilledNoClick = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;

    $(constants._roleFilledNoSelector).bind('click', function () {
        const hfRoleFilled = document.getElementById(constants._hfRoleFilledId);

        if (hfRoleFilled.value !== 'false') {
            hfRoleFilled.value = 'false';

            $(constants._postHolderContainerSelector).empty();
            $(constants._sectionFilledRoleSelector).hide();

            constants._postHolderCount = 0;
        }
    });
};

FlexiJS.PostsAndTenure.InitializeBtnUpdateRoleClick = function (onServiceCallDoneFunction) {
    const constants = FlexiJS.PostsAndTenure.Constants;

    $(constants._btnUpdateRoleSelector).bind('click', function (e) {
        var isValid = true;

        const postData = {
            otherRole: null
        };

        postData.gmsApplicationId = constants._appId;

        // role
        const role = $(constants._ddlRolesSelector).val();
        const roleValidation = $('#' + constants._ddlRolesValidationId);

        if (role) {
            postData.role = role;
            roleValidation.hide();
        } else {

            roleValidation.html(constants._getResourceFunction('RoleValidationMessage'));
            roleValidation.show();

            isValid = false;
        }

        // specify role
        const specifyRole = document.getElementById(constants._specifyRoleFieldId);
        const specifyRoleValidation = $('#' + constants._specifyRoleValidationId);

        if (role === '0') {
            if (specifyRole.value && specifyRole.value.trim() !== '' && specifyRole.value.length <= 255) {
                postData.otherRole = specifyRole.value;
                specifyRoleValidation.hide();
            } else {
                specifyRoleValidation.html(constants._getResourceFunction('SpecifyRoleValidationMessage'));
                specifyRoleValidation.show();

                isValid = false;
            }
        } else {
            specifyRoleValidation.hide();
        }

        // duration
        const durationMonth = document.getElementById(constants._monthFieldId).value;
        const durationValidation = $('#' + constants._durationValidationId);

        if (durationMonth && parseInt(durationMonth) >= 0) {
            postData.duration = parseInt(durationMonth);
            durationValidation.hide();
        } else {
            durationValidation.html(constants._getResourceFunction('DurationValidationMessage'));
            durationValidation.show();

            isValid = false;
        }

        // starting grade
        const startingGrade = document.getElementById(constants._startingGradeFieldId).value;
        const startingGradeValidation = $('#' + constants._startingGradeValidationId);

        if (startingGrade) {
            postData.startingGrade = startingGrade;
            startingGradeValidation.hide();
        } else {
            startingGradeValidation.html(constants._getResourceFunction('StartingGradeValidationMessage'));
            startingGradeValidation.show();

            isValid = false;
        }

        // spine point
        var spinePoint = document.getElementById(constants._spinePointFieldId).value;
        const spinePointValidation = $('#' + constants._spinePointValidationId);

        if (spinePoint) {
            postData.spinePoint = spinePoint;
            spinePointValidation.hide();
        } else {
            spinePointValidation.html(constants._getResourceFunction('SpinePointValidationMessage'));
            spinePointValidation.show();

            isValid = false;
        }

        // FTE
        const fte = document.getElementById(constants._fteFieldId).value;
        const fteValidation = $('#' + constants._fteValidationId);

        if (fte && parseInt(fte) > 0) {
            postData.fte = fte;
            fteValidation.hide();
        } else {
            fteValidation.html(constants._getResourceFunction('FTEValidationMessage'));
            fteValidation.show();

            isValid = false;
        }

        // starting salary
        const startingSalary = document.getElementById(constants._startingSalaryFieldId).value;
        const startingSalaryValidation = $('#' + constants._startingSalaryValidationId);

        if (startingSalary && parseFloat(startingSalary) > 0) {
            postData.basicStartingSalary = startingSalary;
            startingSalaryValidation.hide();
        } else {
            startingSalaryValidation.html(constants._getResourceFunction('StartingSalaryValidationMessage'));
            startingSalaryValidation.show();

            isValid = false;
        }

        // increment date
        const incrementDate = $(constants._incrementDateSelector).data('kendoDatePicker').value();
        const incrementDateValidation = $('#' + constants._incrementDateValidationId);

        if (incrementDate) {
            postData.incrementDate = FlexiJS.Common.Dates.GetUTCJSONDate(incrementDate);
            incrementDateValidation.hide();
        } else {
            incrementDateValidation.html(constants._getResourceFunction('IncrementDateValidationMessage'));
            incrementDateValidation.show();

            isValid = false;
        }

        // is filled
        postData.isFilled = Boolean.parse(document.getElementById(constants._hfRoleFilledId).value);

        // postholders
        const postHolderInformation = FlexiJS.PostsAndTenure.CollectPostHolderInformation();

        if (postHolderInformation.isValid) {
            postData.postHolders = postHolderInformation.postHolders;
        } else {
            isValid = false;
        }

        // on costs
        const onCostInformation = FlexiJS.PostsAndTenure.CollectOnCostsInformation();
        if (onCostInformation.isValid) {
            postData.postOnCosts = onCostInformation.postOnCosts;
        } else {
            isValid = false;
        }

        if (isValid) {
            const postId = document.getElementById(constants._hfPostIdId).value;

            if (postId) {
                postData.gmsPostId = postId;

                if (postData.postOnCosts && postData.postOnCosts.length > 0) {
                    $.each(postData.postOnCosts, function (index, oncost) { oncost.GMSApplicationPostId = postId; });
                }
            }

            if (constants._formPageId) {
                postData.formPageId = constants._formPageId;
            }


            $(constants._postFormSelector).hide();
            $(constants._btnAddPostSelector).show();
            $(constants._postGridSelector).show();
            $(constants._tsPostsSelector).show();

            $.ajax({
                url: constants._getServiceMethodUrlFunction('SavePost'),
                type: "POST",
                contentType: 'application/json; charset=utf-8',
                data: JSON.stringify(postData),
                dataType: 'json'
            }).done(function () {
                onServiceCallDoneFunction();

                // clear fields
                FlexiJS.PostsAndTenure.ClearPostForm();

                showNotificationMessage('success', constants._getResourceFunction('PostSaveSuccess'));
            }).fail(function () {
                showNotificationMessage('error', constants._getResourceFunction('PostSaveErrorMessage'));
            });
        } else {
            showNotificationMessage('error', constants._getResourceFunction('ValidationErrorMessage'));
        }

        e.preventDefault();
    });
};

FlexiJS.PostsAndTenure.InitializeBtnCancelClick = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;

    $(constants._btnCancelSelector).bind('click', function (e) {
        FlexiJS.PostsAndTenure.ClearPostForm();
        $(constants._postFormSelector).hide();
        $(constants._btnAddPostSelector).show();
        $(constants._postGridSelector).show();
        $(constants._tsPostsSelector).show();

        e.preventDefault();
    });
};

FlexiJS.PostsAndTenure.InitializeBtnAddPersonClick = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;

    $('#' + constants._btnAddPersonId).bind('click', function (e) {
        FlexiJS.PostsAndTenure.AddPostHolderInformationForm();

        e.preventDefault();
    });
};

FlexiJS.PostsAndTenure.ClearPostForm = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;

    constants._openedPostId = 0;
    $(constants._ddlRolesSelector).val('');
    $(constants._specifyRoleDivSelector).hide();
    document.getElementById(constants._specifyRoleFieldId).value = '';
    $('#' + constants._monthFieldId).data('kendoNumericTextBox').value(null);
    document.getElementById(constants._startingGradeFieldId).value = '';
    document.getElementById(constants._spinePointFieldId).value = '';
    $('#' + constants._fteFieldId).data('kendoNumericTextBox').value(null);
    $('#' + constants._startingSalaryFieldId).data('kendoNumericTextBox').value(null);
    $(constants._incrementDateSelector).data('kendoDatePicker').value('');
    document.getElementById(constants._hfPostIdId).value = '';
    $(constants._roleFilledNoSelector).click();

    // hide validation messages
    $('#' + constants._ddlRolesValidationId).hide();
    $('#' + constants._specifyRoleValidationId).hide();
    $('#' + constants._durationValidationId).hide();
    $('#' + constants._startingGradeValidationId).hide();
    $('#' + constants._spinePointValidationId).hide();
    $('#' + constants._fteValidationId).hide();
    $('#' + constants._startingSalaryValidationId).hide();
    $('#' + constants._incrementDateValidationId).hide();
    $('#' + constants._incrementDateFieldKendoValidationId).hide()
};

FlexiJS.PostsAndTenure.GetKendoDatePickerOptions = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;

    const options = {
        format: constants._dateFormatString,
        parseFormats: constants._dateParseFormats,
        open: function () {
            var calendar = this.dateView.calendar;

            if (!this.element.val()) {
                calendar.navigate(new Date());
            }
        }
    };

    if (constants._dateCulture !== null) {
        options.culture = constants._dateCulture;
    }

    return options;
};

FlexiJS.PostsAndTenure.SetupDateValidation = function (datePickerContainer) {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const dpOptions = FlexiJS.PostsAndTenure.GetKendoDatePickerOptions();
    const errorTemplate = '<div class="clearfix"><span class="field-validation-error">#=message#</span></div>';

    datePickerContainer.kendoValidator({
        errorTemplate: errorTemplate,
        rules: {
            datepicker: function (input) {
                var datePickerValidationResult = true;

                if (input.is('[data-role=datepicker]')) {
                    if (input.val().length > 0) {
                        datePickerValidationResult = input.data('kendoDatePicker').value();
                    }
                }

                return datePickerValidationResult;
            }
        },
        messages: {
            datepicker: constants._getResourceFunction('DateInvalidFormatMessage') + ' ' + dpOptions.format
        }
    }).data('kendoValidator');
};

FlexiJS.PostsAndTenure.ParseTextValuePairs = function (hiddenField) {
    const result = [];
    const textValuePairs = hiddenField.value.split(';');

    for (var i = 0; i < textValuePairs.length; i++) {
        const textValuePair = textValuePairs[i].split(':');

        result.push({
            text: textValuePair[0],
            value: textValuePair[1]
        });
    }

    return result;
};

FlexiJS.PostsAndTenure.SetPostHolderInformationFormAttributesByDOMElement = function (domElement) {
    const constants = FlexiJS.PostsAndTenure.Constants;

    domElement.setAttribute(constants._personIndexAttribute, constants._postHolderCount);
    domElement.id = domElement.getAttribute(constants._idPrefixAttribute) + constants._postHolderCount;
    domElement.removeAttribute(constants._idPrefixAttribute);
    domElement.removeAttribute(constants._initializeAttribute);
};

FlexiJS.PostsAndTenure.OnRemovePersonClick = function (personIndex) {
    const constants = FlexiJS.PostsAndTenure.Constants;
    constants._personIndexToRemove = personIndex;

    const getResource = constants._getResourceFunction;

    FlexiJS.ConfirmActions.KendoConfirmActionDialog(getResource('RemovePersonConfirmationTitle'),
        constants._removePersonFunctionName,
        getResource('RemovePersonConfirmationMessage'),
        getResource('RemovePersonConfirmationYes'),
        'btnRemovePersonConfirmationYes',
        getResource('RemovePersonConfirmationNo'),
        'btnRemovePersonConfirmationNo',
        'False');
};

FlexiJS.PostsAndTenure.PopulateTitleSelector = function (titleSelector) {
    const hfTitles = document.getElementById(FlexiJS.PostsAndTenure.Constants._hfTitlesId);
    const titles = FlexiJS.PostsAndTenure.ParseTextValuePairs(hfTitles);

    for (var i = 0; i < titles.length; i++) {
        const option = document.createElement('option');
        option.value = titles[i].value;
        option.innerHTML = titles[i].text;

        titleSelector.appendChild(option);
    }
};

FlexiJS.PostsAndTenure.PopulateContactSelector = function (contactSelector) {
    const contacts = FlexiJS.PostsAndTenure.Constants._applicationContacts;

    for (var i = 0; i < contacts.length; i++) {
        const option = document.createElement('option');
        option.value = contacts[i].id;
        option.innerHTML = contacts[i].fullname;

        contactSelector.appendChild(option);
    }
};

FlexiJS.PostsAndTenure.PopulateNationalitySelector = function (nationalitySelector) {
    const hfNationalities = document.getElementById(FlexiJS.PostsAndTenure.Constants._hfNationalitiesId);
    const nationalities = FlexiJS.PostsAndTenure.ParseTextValuePairs(hfNationalities);

    for (var i = 0; i < nationalities.length; i++) {
        const option = document.createElement('option');
        option.value = nationalities[i].value;
        option.innerHTML = nationalities[i].text;

        nationalitySelector.appendChild(option);
    }
};

FlexiJS.PostsAndTenure.SetPostHolderInformationFormLabels = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const labels = document.querySelectorAll('label[' + constants._initializeAttribute + ']');

    for (var index = 0; index < labels.length; index++) {
        const label = labels[index];

        label.htmlFor = label.getAttribute(constants._forPrefixAttribute) + constants._postHolderCount;
        label.setAttribute(constants._personIndexAttribute, constants._postHolderCount);
        label.removeAttribute(constants._initializeAttribute);
        label.removeAttribute(constants._forPrefixAttribute);
    }
};

FlexiJS.PostsAndTenure.SetPostHolderInformationFormValidationMessages = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const validationMessageSpans = document.querySelectorAll('span.field-validation-error[' + constants._initializeAttribute + ']');

    for (var index = 0; index < validationMessageSpans.length; index++) {
        const validationMessageSpan = validationMessageSpans[index];

        validationMessageSpan.setAttribute(constants._personIndexAttribute, constants._postHolderCount);
        validationMessageSpan.removeAttribute(constants._initializeAttribute);
    }
};

FlexiJS.PostsAndTenure.InitializePostHolderInformationForm = function (postHolderHtml, personData) {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const bodyTagStart = '<body>';
    const bodyTagEnd = '</body>';

    // strip html tags
    const contentIndexAfterBodyTag = postHolderHtml.indexOf(bodyTagStart) + bodyTagStart.length;

    postHolderHtml = postHolderHtml.substring(contentIndexAfterBodyTag, postHolderHtml.indexOf(bodyTagEnd)).trim();

    $(constants._postHolderContainerSelector).append(postHolderHtml);
    constants._postHolderCount += 1;

    FlexiJS.PostsAndTenure.SetPostHolderInformationFormValidationMessages();

    const setPostHolderInformationByDOMElement = FlexiJS.PostsAndTenure.SetPostHolderInformationFormAttributesByDOMElement;

    const postHolderInformation = document.querySelector('.post-holder-information[' + constants._initializeAttribute + ']'); // post holder information section
    setPostHolderInformationByDOMElement(postHolderInformation);

    const collapsable = document.querySelector('.collapsable[' + constants._initializeAttribute + ']'); // collapsable div
    setPostHolderInformationByDOMElement(collapsable);
    collapsable.setAttribute('aria-expanded', true);

    const toggleButton = $('.toggle-button[' + constants._initializeAttribute + ']'); // accordion button span
    toggleButton[0].innerHTML = constants._toggleButtonDown;
    setPostHolderInformationByDOMElement(toggleButton[0]);

    toggleButton.bind('click', function (e) {
        const collapsedAttribute = $(collapsable).attr(constants._collapsedAttribute);

        if (collapsedAttribute && collapsedAttribute === 'true') {
            $(this).html(constants._toggleButtonDown);
            $(collapsable).show();

            $(collapsable).attr(constants._collapsedAttribute, false);
            $(collapsable).attr('aria-expanded', true);
        } else {
            $(this).html(constants._toggleButtonUp);
            $(collapsable).hide();

            $(collapsable).attr(constants._collapsedAttribute, true);
            $(collapsable).attr('aria-expanded', false);
        }
    });

    const personIndex = document.querySelector('.person-index[' + constants._initializeAttribute + ']'); // person index span
    personIndex.innerHTML = constants._postHolderCount;
    setPostHolderInformationByDOMElement(personIndex);

    const btnRemovePerson = $('.btn-remove-person[' + constants._initializeAttribute + ']'); // remove person button
    setPostHolderInformationByDOMElement(btnRemovePerson[0]);

    btnRemovePerson.bind('click', function (e) {
        FlexiJS.PostsAndTenure.OnRemovePersonClick($(this).attr(constants._personIndexAttribute));

        e.preventDefault();
    });

    const postHolderNameFields = document.querySelector('.post-holder-name-fields[' + constants._initializeAttribute + ']'); // postholder name fields div
    setPostHolderInformationByDOMElement(postHolderNameFields);

    const titleSelector = document.querySelector('.title-selector[' + constants._initializeAttribute + ']'); // title selector
    setPostHolderInformationByDOMElement(titleSelector);
    FlexiJS.PostsAndTenure.PopulateTitleSelector(titleSelector);

    const firstname = document.querySelector('.firstname-field[' + constants._initializeAttribute + ']'); // firstname textbox
    setPostHolderInformationByDOMElement(firstname);

    const surname = document.querySelector('.surname-field[' + constants._initializeAttribute + ']'); // surname textbox
    setPostHolderInformationByDOMElement(surname);

    const isInContactsHidden = document.querySelector('.is-in-contacts-hidden[' + constants._initializeAttribute + ']'); // is in contacts hiddenfield
    const isInContactsYes = document.querySelector('.is-in-contacts-yes[' + constants._initializeAttribute + ']'); // is in contacts yes radiobutton
    const applicationContactSelectorDiv = document.querySelector('.application-contact-selector-div[' + constants._initializeAttribute + ']'); // application contact selector div
    const contactSelector = document.querySelector('.contact-selector[' + constants._initializeAttribute + ']'); // contact selector
    const fte = $('.fte-field[' + constants._initializeAttribute + ']'); // fte text

    if (constants._isEcf) {
        setPostHolderInformationByDOMElement(applicationContactSelectorDiv);

        const postHolderContactQuestionDiv = document.querySelector('.post-holder-contact-question[' + constants._initializeAttribute + ']'); // postholder contact question div
        setPostHolderInformationByDOMElement(postHolderContactQuestionDiv);
        $(postHolderContactQuestionDiv).show();

        setPostHolderInformationByDOMElement(isInContactsHidden);

        const isInContactsNo = document.querySelector('.is-in-contacts-no[' + constants._initializeAttribute + ']'); // is in contacts no radiobutton
        setPostHolderInformationByDOMElement(isInContactsNo);
        isInContactsNo.name = isInContactsNo.getAttribute(constants._namePrefixAttribute) + constants._postHolderCount;
        isInContactsNo.removeAttribute(constants._namePrefixAttribute);

        $(isInContactsNo).click(function () {
            $(postHolderNameFields).show();
            $(applicationContactSelectorDiv).hide();
            isInContactsHidden.value = 'false';
        });

        setPostHolderInformationByDOMElement(isInContactsYes);
        isInContactsYes.name = isInContactsYes.getAttribute(constants._namePrefixAttribute) + constants._postHolderCount;
        isInContactsYes.removeAttribute(constants._namePrefixAttribute);

        $(isInContactsYes).click(function () {
            $(postHolderNameFields).hide();
            $(applicationContactSelectorDiv).show();
            isInContactsHidden.value = 'true';
        });

        setPostHolderInformationByDOMElement(contactSelector);
        FlexiJS.PostsAndTenure.PopulateContactSelector(contactSelector);

        const contactValidation = $('.contact-validation[' + constants._personIndexAttribute + '="' + constants._postHolderCount + '"]');

        $(contactSelector).change(function () {
            const contactId = $(this).val();

            if (contactId) {
                contactValidation.hide();

                const selectedContact = constants._applicationContacts.filter(function (contact) {
                    return contact.id === contactId;
                });

                $('#' + titleSelector.id + ' option').each(function () {
                    if ($(this).text() === selectedContact[0].title) {
                        $(this).attr('selected', 'selected');
                    }
                });

                $(firstname).val(selectedContact[0].firstname);
                $(surname).val(selectedContact[0].lastname);
            }
        });

        $('.fte-row').show();

        fte.kendoNumericTextBox({
            format: 'n',
            decimals: 2,
            min: 0,
            max: 100,
            spinners: false
        });

        setPostHolderInformationByDOMElement(fte[0]);

        $('.fte-label[' + constants._initializeAttribute + ']').click(function (e) {
            fte.data('kendoNumericTextBox').focus();
        });
    }

    const nationalitySelector = document.querySelector('.nationality-selector[' + constants._initializeAttribute + ']'); // nationality selector
    setPostHolderInformationByDOMElement(nationalitySelector);
    FlexiJS.PostsAndTenure.PopulateNationalitySelector(nationalitySelector);

    const dpOptions = FlexiJS.PostsAndTenure.GetKendoDatePickerOptions();

    const dateOfBirth = $('.date-of-birth-field[' + constants._initializeAttribute + ']'); // date of birth datepicker
    dateOfBirth.kendoDatePicker(dpOptions);
    dateOfBirth.attr('placeholder', dpOptions.format);
    setPostHolderInformationByDOMElement(dateOfBirth[0]);

    const dateOfBirthDiv = $('.date-of-birth-div[' + constants._initializeAttribute + ']'); // date of birth datepicker container
    setPostHolderInformationByDOMElement(dateOfBirthDiv[0]);

    FlexiJS.PostsAndTenure.SetupDateValidation(dateOfBirthDiv);

    const qualifications = document.querySelector('.qualifications-and-achievement-date-field[' + constants._initializeAttribute + ']'); // qualifications and achievement date textarea
    setPostHolderInformationByDOMElement(qualifications);

    const dateOfPHD = $('.date-of-phd-field[' + constants._initializeAttribute + ']'); // date of phd
    dateOfPHD.kendoDatePicker(dpOptions);
    dateOfPHD.attr('placeholder', dpOptions.format);
    setPostHolderInformationByDOMElement(dateOfPHD[0]);

    const dateOfPHDDiv = $('.date-of-phd-div[' + constants._initializeAttribute + ']'); // date of phd datepicker container
    setPostHolderInformationByDOMElement(dateOfPHDDiv[0]);

    FlexiJS.PostsAndTenure.SetupDateValidation(dateOfPHDDiv);

    const currentBasicSalary = $('.current-basic-salary-field[' + constants._initializeAttribute + ']'); // current basic salary text

    currentBasicSalary.kendoNumericTextBox({
        format: 'c',
        decimals: 2,
        min: 0,
        spinners: false
    });

    setPostHolderInformationByDOMElement(currentBasicSalary[0]);

    $('.current-basic-salary-label[' + constants._initializeAttribute + ']').click(function (e) {
        currentBasicSalary.data('kendoNumericTextBox').focus();
    });

    //const sourceOfFunding = document.querySelector('.source-of-funding-field[' + constants._initializeAttribute + ']'); // source of funding
    //setPostHolderInformationByDOMElement(sourceOfFunding);

    const endDate = $('.end-date-field[' + constants._initializeAttribute + ']'); // end date datepicker
    endDate.kendoDatePicker(dpOptions);
    endDate.attr('placeholder', dpOptions.format);
    setPostHolderInformationByDOMElement(endDate[0]);

    const endDateDiv = $('.end-date-div[' + constants._initializeAttribute + ']'); // end date datepicker container
    setPostHolderInformationByDOMElement(endDateDiv[0]);

    FlexiJS.PostsAndTenure.SetupDateValidation(endDateDiv);

    const hfPostTenureId = document.querySelector('.post-tenure-id-hf[' + constants._initializeAttribute + ']'); // post tenure id hidden field
    setPostHolderInformationByDOMElement(hfPostTenureId);

    const spinePoint = document.querySelector('.spinepoint-field[' + constants._initializeAttribute + ']'); // Spine Point
    setPostHolderInformationByDOMElement(spinePoint);

    const startingGrade = document.querySelector('.starting-grade-field[' + constants._initializeAttribute + ']'); // Starting Grade
    setPostHolderInformationByDOMElement(startingGrade);

    const startOfTenure = $('.start-of-tenure-field[' + constants._initializeAttribute + ']'); // Start Of Tenure datepicker
    startOfTenure.kendoDatePicker(dpOptions);
    startOfTenure.attr('placeholder', dpOptions.format);
    setPostHolderInformationByDOMElement(startOfTenure[0]);

    const startOfTenureDiv = $('.start-of-tenure-div[' + constants._initializeAttribute + ']'); // Start Of Tenure datepicker container
    setPostHolderInformationByDOMElement(startOfTenureDiv[0]);

    FlexiJS.PostsAndTenure.SetupDateValidation(startOfTenureDiv);


    if (personData) {
        if (personData.Title) {
            titleSelector.value = personData.Title;
        } else {
            titleSelector.value = '';
        }

        firstname.value = personData.Firstname;
        surname.value = personData.Surname;

        if (personData.Nationality) {
            nationalitySelector.value = personData.Nationality;
        } else {
            nationalitySelector.value = '';
        }

        dateOfBirth.data('kendoDatePicker').value(personData.DateOfBirth);
        qualifications.value = personData.Qualifications;
        dateOfPHD.data('kendoDatePicker').value(personData.DateOfPHD);
        currentBasicSalary.data('kendoNumericTextBox').value(personData.BasicSalary);
        // sourceOfFunding.value = personData.SourceOfFunding;
        endDate.data('kendoDatePicker').value(personData.EndDate);
        hfPostTenureId.value = personData.PostTenureId;

        spinePoint.value = personData.SpinePoint;
        startingGrade.value = personData.StartingGrade;
        startOfTenure.data('kendoDatePicker').value(personData.StartOfTenure);

        if (constants._isEcf) {
            fte.data('kendoNumericTextBox').value(personData.FTE);

            if (personData.GMSApplicationContactId) {
                isInContactsYes.checked = true;
                isInContactsHidden.value = 'true';
                $(postHolderNameFields).hide();
                $(applicationContactSelectorDiv).show();
                contactSelector.value = personData.GMSApplicationContactId;
            }
        }
    }

    FlexiJS.PostsAndTenure.SetPostHolderInformationFormLabels(); // set labels for attribute

    // hide validation messages when correct input provided
    const firstnameValidation = $('.firstname-validation[' + constants._personIndexAttribute + '="' + constants._postHolderCount + '"]');

    $(firstname).blur(function () {
        if (firstname.value) {
            firstnameValidation.hide();
        }
    });

    const surnameValidation = $('.surname-validation[' + constants._personIndexAttribute + '="' + constants._postHolderCount + '"]');

    $(surname).blur(function () {
        if (surname.value) {
            surnameValidation.hide();
        }
    });

    const nationalityValidation = $('.nationality-validation[' + constants._personIndexAttribute + '="' + constants._postHolderCount + '"]');

    $(nationalitySelector).change(function () {
        if (nationalitySelector.value) {
            nationalityValidation.hide();
        }
    });

    const dateOfBirthValidation = $('.date-of-birth-validation[' + constants._personIndexAttribute + '="' + constants._postHolderCount + '"]');

    dateOfBirth.blur(function () {
        if (dateOfBirth.data('kendoDatePicker').value()) {
            dateOfBirthValidation.hide();
        }
    });

    const currentBasicSalaryValidation = $('.current-basic-salary-validation[' + constants._personIndexAttribute + '="' + constants._postHolderCount + '"]');

    currentBasicSalary.blur(function () {
        if (currentBasicSalary.val()) {
            currentBasicSalaryValidation.hide();
        }
    });

    const startingGradeValidation = $('.starting-grade-validation[' + constants._personIndexAttribute + '="' + constants._postHolderCount + '"]');

    $(startingGrade).blur(function () {
        if (startingGrade.value) {
            startingGradeValidation.hide();
        }
    });

    const spinePointValidation = $('.spinepoint-validation[' + constants._personIndexAttribute + '="' + constants._postHolderCount + '"]');

    $(spinePoint).blur(function () {
        if (spinePoint.value) {
            spinePointValidation.hide();
        }
    });

    const startOfTenureValidation = $('.startOfTenure-validation[' + constants._personIndexAttribute + '="' + constants._postHolderCount + '"]');

    startOfTenure.blur(function () {
        if (startOfTenure.data('kendoDatePicker').value()) {
            startOfTenureValidation.hide();
        }
    });
};

FlexiJS.PostsAndTenure.SetAddPersonButtonVisibility = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const addPersonButton = $('#' + constants._btnAddPersonId);

    if (constants._postHolderCount >= constants._maxPostHolderCount) {
        addPersonButton.hide();
    } else {
        addPersonButton.show();
    }
};

FlexiJS.PostsAndTenure.AddPostHolderInformationForm = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;

    if (constants._postHolderCount < constants._maxPostHolderCount) {
        const postData = {
            gmsApplicationId: constants._appId
        };

        if (constants._formPageId) {
            postData.formPageId = constants._formPageId;
        }

        $.ajax({
            url: constants._getServiceMethodUrlFunction('GetPostHolderInformationHTML'),
            type: "POST",
            contentType: 'application/json',
            data: JSON.stringify(postData),
            dataType: 'html'
        }).done(function (r) {
            const result = JSON.parse(r);

            FlexiJS.PostsAndTenure.InitializePostHolderInformationForm(result.d);
            FlexiJS.PostsAndTenure.SetAddPersonButtonVisibility();
        });
    }
};

FlexiJS.PostsAndTenure.CollectPostHolderInformation = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const result = {
        isValid: true,
        postHolders: []
    };

    for (var index = 1; index <= constants._postHolderCount; index++) {
        const contactValidation = $('.contact-validation[' + constants._personIndexAttribute + '="' + index + '"]');
        const firstnameValidation = $('.firstname-validation[' + constants._personIndexAttribute + '="' + index + '"]');
        const surnameValidation = $('.surname-validation[' + constants._personIndexAttribute + '="' + index + '"]');
        const nationalityValidation = $('.nationality-validation[' + constants._personIndexAttribute + '="' + index + '"]');
        const dateOfBirthValidation = $('.date-of-birth-validation[' + constants._personIndexAttribute + '="' + index + '"]');

        const currentBasicStartingSalaryValidation = $('.current-basic-salary-validation[' + constants._personIndexAttribute + '="' + index + '"]');
        const spinePointValidation = $('.spinepoint-validation[' + constants._personIndexAttribute + '="' + index + '"]');
        const startingGradeValidation = $('.starting-grade-validation[' + constants._personIndexAttribute + '="' + index + '"]');
        const startOfTenureValidation = $('.startOfTenure-validation[' + constants._personIndexAttribute + '="' + index + '"]');
        const endDateValidation = $('.end-date-validation[' + constants._personIndexAttribute + '="' + index + '"]');

        const postHolder = {};
        const indexedElements = document.querySelectorAll('[' + constants._personIndexAttribute + '="' + index + '"]');

        for (var elementIndex = 0; elementIndex < indexedElements.length; elementIndex++) {
            const element = indexedElements[elementIndex];

            if (constants._isEcf && element.classList.contains('contact-selector')) {
                const isInApplicationContacts = $('.is-in-contacts-hidden[' + constants._personIndexAttribute + '="' + index + '"]').val();

                if (isInApplicationContacts === 'true') {
                    if (element.value) {
                        postHolder.GMSApplicationContactId = element.value;
                    } else {
                        contactValidation.html(FlexiJS.PostsAndTenure.GetPostHolderInformationTemplateResource('ContactValidationMessage'));
                        contactValidation.show();

                        result.isValid = false;
                    }
                }
            } else if (element.classList.contains('title-selector')) {
                if (element.value) {
                    postHolder.Title = element.value;
                }
            } else if (element.classList.contains('firstname-field')) {
                if (element.value && element.value.trim() !== '') {
                    postHolder.Firstname = element.value;

                    firstnameValidation.hide();
                } else {
                    firstnameValidation.html(FlexiJS.PostsAndTenure.GetPostHolderInformationTemplateResource('FirstnameValidationMessage'));
                    firstnameValidation.show();

                    result.isValid = false;
                }
            } else if (element.classList.contains('surname-field')) {
                if (element.value && element.value.trim() !== '') {
                    postHolder.Surname = element.value;

                    surnameValidation.hide();
                } else {
                    surnameValidation.html(FlexiJS.PostsAndTenure.GetPostHolderInformationTemplateResource('SurnameValidationMessage'));
                    surnameValidation.show();

                    result.isValid = false;
                }
            } else if (element.classList.contains('nationality-selector')) {
                if (element.value) {
                    postHolder.Nationality = element.value;

                    nationalityValidation.hide();
                } else {
                    nationalityValidation.html(FlexiJS.PostsAndTenure.GetPostHolderInformationTemplateResource('NationalityValidationMessage'));
                    nationalityValidation.show();

                    result.isValid = false;
                }
            } else if (element.classList.contains('date-of-birth-field')) {
                const dateOfBirth = $(element).data('kendoDatePicker').value();

                if (dateOfBirth) {
                    postHolder.DateOfBirth = FlexiJS.Common.Dates.GetUTCJSONDate(dateOfBirth);
                    dateOfBirthValidation.hide();
                } else {
                    dateOfBirthValidation.html(FlexiJS.PostsAndTenure.GetPostHolderInformationTemplateResource('DateOfBirthValidationMessage'));
                    dateOfBirthValidation.show();

                    result.isValid = false;
                }
            } else if (element.classList.contains('qualifications-and-achievement-date-field')) {
                if (element.value) {
                    postHolder.Qualifications = element.value;
                }
            } else if (element.classList.contains('date-of-phd-field')) {
                const dateOfPHD = $(element).data('kendoDatePicker').value();

                if (dateOfPHD) {
                    postHolder.DateOfPHD = FlexiJS.Common.Dates.GetUTCJSONDate(dateOfPHD);
                }
            } else if (element.classList.contains('current-basic-salary-field')) {
                if (element.value && element.value.trim() !== '') {
                    postHolder.BasicSalary = element.value;
                    currentBasicStartingSalaryValidation.hide();
                }
                else {
                    currentBasicStartingSalaryValidation.html(FlexiJS.PostsAndTenure.GetPostHolderInformationTemplateResource('CurrentBasicStartingSalaryValidationMessage'));
                    currentBasicStartingSalaryValidation.show();
                    result.isValid = false;
                }
                //} else if (element.classList.contains('source-of-funding-field')) {
                //    if (element.value) {
                //        postHolder.SourceOfFunding = element.value;
                //    }
            } else if (element.classList.contains('end-date-field')) {
                const endDate = $(element).data('kendoDatePicker').value();
                const formattedEndDate = FlexiJS.Common.Dates.GetUTCJSONDate(endDate);
                const msStartDate = FlexiJS.Common.Dates.GetMilliSecondsFromJSONDate(postHolder.StartOfTenure);
                const msEndDate = FlexiJS.Common.Dates.GetMilliSecondsFromJSONDate(formattedEndDate);

                if (msStartDate > 0 && msEndDate > 0 && (msStartDate > msEndDate)) {
                    endDateValidation.html(FlexiJS.PostsAndTenure.GetPostHolderInformationTemplateResource('EndDateOfTenureNotInRangeValidationMessage'));
                    endDateValidation.show();
                    startOfTenureValidation.html(FlexiJS.PostsAndTenure.GetPostHolderInformationTemplateResource('StartOfTenureNotInRangeValidationMessage'));
                    startOfTenureValidation.show();
                    result.isValid = false;
                }
                else {
                    postHolder.EndDate = formattedEndDate;
                    endDateValidation.hide();
                }
            } else if (element.classList.contains('fte-field')) {
                if (element.value) {
                    postHolder.FTE = element.value;
                }
            } else if (element.classList.contains('spinepoint-field')) {

                if (element.value && element.value.trim() !== '') {
                    postHolder.SpinePoint = element.value;
                    spinePointValidation.hide();
                }
                else {
                    spinePointValidation.html(FlexiJS.PostsAndTenure.GetPostHolderInformationTemplateResource('SpinePointValidationMessage'));
                    spinePointValidation.show();
                    result.isValid = false;
                }
            } else if (element.classList.contains('starting-grade-field')) {

                if (element.value && element.value.trim() !== '') {
                    postHolder.StartingGrade = element.value;
                    startingGradeValidation.hide();
                }
                else {
                    startingGradeValidation.html(FlexiJS.PostsAndTenure.GetPostHolderInformationTemplateResource('StartingGradeValidationMessage'));
                    startingGradeValidation.show();
                    result.isValid = false;
                }

            } else if (element.classList.contains('start-of-tenure-field')) {
                const startOfTenure = $(element).data('kendoDatePicker').value();

                if (startOfTenure) {
                    const formattedStartOfTenure = FlexiJS.Common.Dates.GetUTCJSONDate(startOfTenure);
                    const msStartDate = FlexiJS.Common.Dates.GetMilliSecondsFromJSONDate(formattedStartOfTenure);
                    const msEndDate = FlexiJS.Common.Dates.GetMilliSecondsFromJSONDate(postHolder.EndDate);

                    if (msEndDate > 0 && msStartDate > 0 && (msEndDate < msStartDate)) {
                        startOfTenureValidation.html(FlexiJS.PostsAndTenure.GetPostHolderInformationTemplateResource('StartOfTenureNotInRangeValidationMessage'));
                        startOfTenureValidation.show();
                        endDateValidation.html(FlexiJS.PostsAndTenure.GetPostHolderInformationTemplateResource('EndDateOfTenureNotInRangeValidationMessage'));
                        endDateValidation.show();
                        result.isValid = false;
                    }
                    else {
                        postHolder.StartOfTenure = formattedStartOfTenure;
                        startOfTenureValidation.hide();
                    }
                } else {
                    startOfTenureValidation.html(FlexiJS.PostsAndTenure.GetPostHolderInformationTemplateResource('StartOfTenureValidationMessage'));
                    startOfTenureValidation.show();
                    result.isValid = false;
                }
            }
            else if (element.classList.contains('post-tenure-id-hf')) {
                const postHolderId = element.value;

                if (!isNaN(postHolderId)) {
                    postHolder.PostTenureId = parseInt(postHolderId);
                } else {
                    postHolder.PostTenureId = 0;
                }
            }
        }

        postHolder.PersonIndex = index;

        result.postHolders.push(postHolder);

    }

    return result;
};

FlexiJS.PostsAndTenure.CollectOnCostsInformation = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const result = {
        isValid: true,
        postOnCosts: []
    };

    if (constants._hfOncostsData) {
        result.isValid = FlexiJS.PostsAndTenure.ValidateOnCostsInformation(true);

        var staticOnCostsData = JSON.parse($('#' + constants._hfOncostsData).val());

        $.each(
            $(constants._oncostGridSelector).find('table').find('tr:visible[data-periodid][data-periodid!="total"]'),
            function (index, oncostrow) {
                var values = FlexiJS.PostsAndTenure.PostOncostGridGetRowValues(oncostrow);
                var periodId = $(oncostrow).data('periodid');
                var onCostId = $(oncostrow).data('oncostid');

                if (!onCostId) onCostId = 0;

                result.postOnCosts.push({
                    GMSApplicationOnCostId: onCostId,
                    GMSApplicationBudgetTableId: staticOnCostsData.GMSApplicationBudgetTableId,
                    GMSApplicationPostId: 0,
                    GMSFundTypeBudgetTablePeriodGroupId: staticOnCostsData.IsGrouped == true ? periodId : null,
                    GMSFundTypeBudgetTablePeriodId: staticOnCostsData.IsGrouped != true ? periodId : null,
                    Value1: values.value1,
                    Value1Percentage: values.value1percentage,
                    Value2: values.value2 || 0,
                    Value3: values.value3 || 0,
                    Value4: values.value4 || 0
                });

            }
        );
    }

    return result;
};

FlexiJS.PostsAndTenure.ValidateOnCostsInformation = function (scrollToValidationMessages) {
    const constants = FlexiJS.PostsAndTenure.Constants;

    var isValid = true;

    var requiredMessage = constants._getResourceFunction('OnCostValidation.Required');
    var value1FieldName = constants._getResourceFunction('OnCostColumn.Value1');
    var value1PercentageFieldName = constants._getResourceFunction('OnCostColumn.Value1Percentage');

    if (constants._hfOncostsData) {
        var validationMessages = [];

        var staticOnCostsData = JSON.parse($('#' + constants._hfOncostsData).val());

        $.each(
            $(constants._oncostGridSelector).find('table').find('tr:visible[data-periodid][data-periodid!="total"]'),
            function (index, oncostrow) {
                $(oncostrow).find('td').removeClass('invalid');

                var values = FlexiJS.PostsAndTenure.PostOncostGridGetRowValues(oncostrow);
                var periodId = $(oncostrow).data('periodid');
                var periodName = staticOnCostsData.Periods.filter(function (p) { return p.Id == periodId; })[0].Name;


                //Validation Message
                var periodRequiredMessage = requiredMessage.replace('[period]', periodName);

                //Required Values
                var value1Input = $(oncostrow).find('input[data-field="Value1"]');
                var value1PercentageInput = $(oncostrow).find('input[data-field="Value1Percentage"]');
                var value1 = value1Input != null ? value1Input.val().trim() : '';
                var value1Percentage = value1PercentageInput != null ? value1PercentageInput.val().trim() : ''; 

                if (!values.value1 && value1 === '') {
                    isValid = false;
                    $(oncostrow).find('input[data-field="Value1"]').closest('td').addClass('invalid');
                    validationMessages.push(periodRequiredMessage.replace('[field]', value1FieldName));
                }
                if (!values.value1percentage && value1Percentage === '') {
                    isValid = false;
                    $(oncostrow).find('input[data-field="Value1Percentage"]').closest('td').addClass('invalid');
                    validationMessages.push(periodRequiredMessage.replace('[field]', value1PercentageFieldName));
                }
            }
        );

        var validationMessageElement = $('#onCostsValidation');

        if (!isValid) {
            var messagesUL = $(validationMessageElement.find('ul'));
            messagesUL.empty();
            messagesUL.append(validationMessages.map(function (m) { return "<li>" + m + "</li>"; }).join(''));
            validationMessageElement.toggle(true);
            if (scrollToValidationMessages == true) {
                $([document.documentElement, document.body]).animate({
                    scrollTop: validationMessageElement.offset().top
                }, 500);
            }
        } else {

            validationMessageElement.toggle(false);
        }
    }

    return isValid;
};

FlexiJS.PostsAndTenure.ReindexPostHolderInformationForms = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    var needsReindex = false;
    var newIndex = 0;

    for (var index = 1; index <= constants._postHolderCount; index++) {
        const indexedElements = document.querySelectorAll('[' + constants._personIndexAttribute + '="' + index + '"]');

        if (indexedElements.length === 0) {
            needsReindex = true;
            newIndex = index;
        } else if (needsReindex) {
            for (var elementIndex = 0; elementIndex < indexedElements.length; elementIndex++) {
                const element = indexedElements[elementIndex];

                if (element.classList.contains('person-index')) {
                    element.innerHTML = newIndex;
                }

                element.setAttribute(constants._personIndexAttribute, newIndex);
            }

            newIndex++;
        }
    }
};

FlexiJS.PostsAndTenure.RemovePerson = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const postHolderInformation = document.querySelector('.post-holder-information[' + constants._personIndexAttribute + '="' + constants._personIndexToRemove + '"]');
    const lastIndexDeleted = postHolderInformation.getAttribute(constants._personIndexAttribute) === constants._postHolderCount.toString();
    const newPostHolderCount = constants._postHolderCount - 1;

    postHolderInformation.parentNode.removeChild(postHolderInformation);

    if (newPostHolderCount <= 0) {
        $(constants._roleFilledNoSelector).click();
    } else if (!lastIndexDeleted) {
        FlexiJS.PostsAndTenure.ReindexPostHolderInformationForms();
    }

    constants._postHolderCount = newPostHolderCount;

    FlexiJS.PostsAndTenure.SetAddPersonButtonVisibility();
};

FlexiJS.PostsAndTenure.OnApplicationPostEditClick = function (postId, postNumber) {
    const constants = FlexiJS.PostsAndTenure.Constants;

    const postData = {
        gmsApplicationId: constants._appId,
        postId: postId
    };

    if (constants._formPageId) {
        postData.formPageId = constants._formPageId;
    }

    $.ajax({
        url: constants._getServiceMethodUrlFunction('GetPost'),
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify(postData),
        dataType: 'json'
    }).done(function (r) {
        $(constants._btnAddPostSelector).hide();
        $(constants._postGridSelector).hide();
        $(constants._tsPostsSelector).hide();
        const response = JSON.parse(r.d);

        FlexiJS.PostsAndTenure.ClearPostForm();
        constants._openedPostId = postId;

        $(constants._ddlRolesSelector).val(response.Role);
        if (response.OtherRole) {
            $(constants._specifyRoleDivSelector).show();
            document.getElementById(constants._specifyRoleFieldId).value = response.OtherRole;
        }

        const months = response.Duration;

        $('#' + constants._monthFieldId).data('kendoNumericTextBox').value(months);
        document.getElementById(constants._startingGradeFieldId).value = response.StartingGrade;
        document.getElementById(constants._spinePointFieldId).value = response.SpinePoint;
        $('#' + constants._fteFieldId).data('kendoNumericTextBox').value(response.FTE);
        document.getElementById(constants._fteFieldId).value = response.FTE;
        $('#' + constants._startingSalaryFieldId).data('kendoNumericTextBox').value(response.BasicStartingSalary);
        document.getElementById(constants._hfPostIdId).value = response.GMSApplicationPostId;

        const hfRoleFilled = document.getElementById(constants._hfRoleFilledId);

        if (response.IsFilled) {
            $(constants._roleFilledYesSelector).prop('checked', true);
            hfRoleFilled.value = 'true';

            if (response.PostTenures) {
                const postData = { gmsApplicationId: constants._appId };

                if (constants._formPageId) {
                    postData.formPageId = constants._formPageId;
                }

                $.ajax({
                    url: constants._getServiceMethodUrlFunction('GetPostHolderInformationHTML'),
                    type: "POST",
                    contentType: 'application/json',
                    data: JSON.stringify(postData),
                    dataType: 'html'
                }).done(function (r) {
                    const result = JSON.parse(r);
                    const html = result.d;

                    for (var index = 0; index < response.PostTenures.length; index++) {
                        FlexiJS.PostsAndTenure.InitializePostHolderInformationForm(html, response.PostTenures[index]);
                    }

                    $(constants._sectionFilledRoleSelector).show();
                });
            }

        } else {
            hfRoleFilled.value = 'false';
        }

        FlexiJS.Dates.ParseObjectDotNetDates(['IncrementDate'], response);

        $(constants._incrementDateSelector).data('kendoDatePicker').value(response.IncrementDate);

        document.getElementById(constants._postTitleId).innerHTML = constants._toggleButtonDown + ' ' + constants._getResourceFunction('EditingPost') + ' ' + postNumber;

        if ($('#' + constants._hfOncostsData).length > 0) FlexiJS.PostsAndTenure.InitializePostOncostGrid(constants._oncostGridSelector, constants._hfOncostsData, response.PostOnCosts);

        $(constants._postFormSelector).show();

        if ($('#' + constants._hfOncostsData).length > 0) FlexiJS.PostsAndTenure.PostOncostShowHideAddButton();
    });
};

FlexiJS.PostsAndTenure.OnApplicationPostDeleteClick = function (applicationPostId, removeApplicationPostFunction) {
    const constants = FlexiJS.PostsAndTenure.Constants;
    constants._applicationPostIdToRemove = applicationPostId;

    FlexiJS.ConfirmActions.KendoConfirmActionDialog(constants._getResourceFunction('RemoveApplicationPostConfirmationTitle'),
        removeApplicationPostFunction,
        constants._getResourceFunction('RemoveApplicationPostConfirmationMessage'),
        constants._getResourceFunction('RemoveApplicationPostConfirmationYes'),
        'btnRemoveApplicationPostConfirmationYes',
        constants._getResourceFunction('RemoveApplicationPostConfirmationNo'),
        'btnRemoveApplicationPostConfirmationNo',
        'False');
};

FlexiJS.PostsAndTenure.DeletePost = function (onPostDeleteSuccessFunction) {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const postData = {
        gmsApplicationId: constants._appId,
        gmsApplicationPostId: constants._applicationPostIdToRemove
    };

    if (constants._formPageId) {
        postData.formPageId = constants._formPageId;
    }

    $.ajax({
        url: constants._getServiceMethodUrlFunction('DeletePost'),
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify(postData),
        dataType: 'json'
    }).done(function (r) {
        const response = JSON.parse(r.d);

        if (response.Result) {
            showNotificationMessage('success', constants._getResourceFunction('PostDeleteSuccess'));
            onPostDeleteSuccessFunction();
        }
        else {
            showNotificationMessage('error', constants._getResourceFunction('PostDeleteErrorMessage'));
        }
    }).fail(function () {
        showNotificationMessage('error', constants._getResourceFunction('PostDeleteErrorMessage'));
    });
};

FlexiJS.PostsAndTenure.ApplicationPost.InitializeConstants = function (appId, formPageId, btnAddPostSelector, ddlRolesSelector, btnUpdateRoleSelector, btnCancelSelector, hfTitlesId,
    hfNationalitiesId, hfIsInReadOnlyModeId, dateFormatString, dateParseFormats, dateCulture, hfOncostsData) {

    FlexiJS.PostsAndTenure.InitializeConstants(appId, ddlRolesSelector, btnUpdateRoleSelector, btnCancelSelector, hfTitlesId, hfNationalitiesId, dateFormatString, dateParseFormats, dateCulture, hfOncostsData);

    // Application post specific constants
    const constants = FlexiJS.PostsAndTenure.Constants;
    constants._getServiceMethodUrlFunction = FlexiJS.PostsAndTenure.GetPageServiceMethodUrl;
    constants._getResourceFunction = FlexiJS.PostsAndTenure.ApplicationPost.GetApplicationPostResource;
    constants._btnAddPostSelector = btnAddPostSelector;
    constants._formPageId = formPageId;
    constants._hfIsInReadOnlyModeId = hfIsInReadOnlyModeId;
    constants._postGridSelector = '#gridPosts';
};

FlexiJS.PostsAndTenure.ApplicationPost.GetApplicationPostResource = function (resourceKey, withHashReplace) {
    return FlexiJS.PostsAndTenure.GetResourceText('controls/flexiform/gms/applicationpost.ascx', resourceKey, withHashReplace);
};

FlexiJS.PostsAndTenure.ApplicationPost.InitializeControls = function () {
    FlexiJS.PostsAndTenure.InitializeControls();

    // Application form specific control initialization
    FlexiJS.PostsAndTenure.ApplicationPost.InitializeAddPostClick();
    FlexiJS.PostsAndTenure.InitializeBtnUpdateRoleClick(FlexiJS.PostsAndTenure.ApplicationPost.AddPostGrid);
};

FlexiJS.PostsAndTenure.ApplicationPost.InitializeAddPostClick = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;

    $(constants._btnAddPostSelector).bind('click', function (e) {
        document.getElementById(constants._postTitleId).innerHTML = constants._getResourceFunction('AddPost');
        if ($('#' + constants._hfOncostsData).length > 0) FlexiJS.PostsAndTenure.InitializePostOncostGrid(constants._oncostGridSelector, constants._hfOncostsData, null);

        FlexiJS.PostsAndTenure.ClearPostForm();
        $(constants._postFormSelector).show();
        $(this).hide();
        $(constants._postGridSelector).hide();

        e.preventDefault();
    });
};

FlexiJS.PostsAndTenure.ApplicationPost.AddPostGrid = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const hfIsInReadOnlyMode = document.getElementById(constants._hfIsInReadOnlyModeId);

    const postsGridColumns = function () {
        const columns = [];

        columns.push({
            field: 'RowId',
            title: constants._getResourceFunction('PostGridPost'),
            width: 80
        });

        columns.push({
            field: 'Role',
            title: constants._getResourceFunction('PostGridRole'),
        });

        columns.push({
            field: 'Salary',
            title: constants._getResourceFunction('PostGridSalary'),
            template: '<span style="text-align: right; display: inline-block; width: 100%;">#:Salary#</span>',
            width: 120
        });

        columns.push({
            field: 'Duration',
            title: constants._getResourceFunction('PostGridDuration'),
            width: 140
        });

        columns.push({
            field: 'PositionFilled',
            title: constants._getResourceFunction('PostGridFilled'),
            width: 100
        });

        if (hfIsInReadOnlyMode.value !== 'True') {
            columns.push({
                title: '',
                template: '<button class="application-post-button fg_button" onclick="FlexiJS.PostsAndTenure.OnApplicationPostEditClick(#= PostId#, #= RowId#); return false;">'
                    + constants._getResourceFunction('PostGridEdit')
                    + '</button> '
                    + '<button class="application-post-button cancel-button fg_button secondary delete" onclick="FlexiJS.PostsAndTenure.OnApplicationPostDeleteClick(#= PostId#, \''
                    + constants._deletePostFunctionName + '\'); return false;">'
                    + constants._getResourceFunction('PostGridDelete')
                    + '</button> ',
                width: 240
            });
        }

        return columns;
    };

    const postGridPostData = {
        gmsApplicationId: constants._appId,
        formPageId: constants._formPageId,
        readOnlyMode: hfIsInReadOnlyMode.value === 'True'
    };

    $(constants._postGridSelector).kendoGrid({
        dataSource: {
            type: 'json',
            transport: {
                read: {
                    url: constants._getServiceMethodUrlFunction('GetPosts'),
                    type: 'POST',
                    contentType: 'application/json',
                    data: postGridPostData
                },
                parameterMap: function (options) {
                    return JSON.stringify(options);
                }
            },
            schema: {
                type: 'json',
                data: function (r) {
                    return JSON.parse(r.d);
                },
                model: {
                    fields: {
                        PostId: { type: 'number' },
                        RowId: { type: 'number' },
                        Role: { type: 'string' },
                        Salary: { type: 'string' },
                        Duration: { type: 'string' },
                        PositionFilled: { type: 'string' }
                    }
                }
            }
        },
        noRecords: { template: constants._getResourceFunction('PostGridNoRecords', true) },
        columns: postsGridColumns()
    });
};

FlexiJS.PostsAndTenure.EcfPostsTenure.InitializeConstants = function (appId, btnDeletePostSelector, ddlRolesSelector, btnUpdateRoleSelector, btnCancelSelector, hfTitlesId, hfNationalitiesId,
    dateFormatString, dateParseFormats, dateCulture, hfApplicationContactsId, btnAddPostSelector, postStatusAwardedId, postStatusNoLongerNeededId, tenureStatusConfirmedId,
    tenureStatusRetiredId, tenureStatusAbeyanceId, tenureStatusNotAcceptingPostId, hfOncostsData) {
    FlexiJS.PostsAndTenure.InitializeConstants(appId, ddlRolesSelector, btnUpdateRoleSelector, btnCancelSelector, hfTitlesId, hfNationalitiesId, dateFormatString, dateParseFormats, dateCulture, hfOncostsData);

    // ecf posts specific constants
    const constants = FlexiJS.PostsAndTenure.Constants;

    constants._btnDeletePostSelector = btnDeletePostSelector;
    constants._hfApplicationContactsId = hfApplicationContactsId;
    constants._getServiceMethodUrlFunction = FlexiJS.PostsAndTenure.GetGMSServiceMethodUrl;
    constants._getResourceFunction = FlexiJS.PostsAndTenure.EcfPostsTenure.GetEcfPostsTenureResource;
    constants._tsPostsSelector = '#tsPosts';
    constants._currentPostsGridSelector = '#gridCurrentPosts';
    constants._auditGridSelector = '#gridAudit';
    constants._isEcf = true;
    constants._ecfGridEditable = false;
    constants._btnAddPostSelector = btnAddPostSelector;
    constants._postStatusAwardedId = postStatusAwardedId;
    constants._postStatusNoLongerNeededId = postStatusNoLongerNeededId;
    constants._tenureStatusConfirmedId = tenureStatusConfirmedId;
    constants._tenureStatusRetiredId = tenureStatusRetiredId;
    constants._tenureStatusAbeyanceId = tenureStatusAbeyanceId;
    constants._tenureStatusNotAcceptingPostId = tenureStatusNotAcceptingPostId;
    constants._tenureRetireFunctionName = 'tenureRetire';
    constants._tenureDeleteFunctionName = 'tenureDelete';

    FlexiJS.PostsAndTenure.EcfPostsTenure.SetApplicationContactConstant();
};

FlexiJS.PostsAndTenure.EcfPostsTenure.SetApplicationContactConstant = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const appContactString = document.getElementById(constants._hfApplicationContactsId).value;
    const appContacts = appContactString.split(';');

    constants._applicationContacts = [];

    for (var i = 0; i < appContacts.length; i++) {
        const appContactData = appContacts[i].split(',');
        const appContactTitle = appContactData[1];
        const appContactFirstName = appContactData[2];
        const appContactLastName = appContactData[3];

        var appContactFullName = '';

        if (appContactTitle) {
            appContactFullName += appContactTitle + ' ';
        }

        appContactFullName += appContactFirstName + ' ' + appContactLastName;

        constants._applicationContacts.push({
            id: appContactData[0],
            title: appContactTitle,
            firstname: appContactFirstName,
            lastname: appContactLastName,
            fullname: appContactFullName
        });
    }
};

FlexiJS.PostsAndTenure.EcfPostsTenure.GetEcfPostsTenureResource = function (resourceKey, withHashReplace) {
    return FlexiJS.PostsAndTenure.GetResourceText('gms/ecfpoststenure.aspx', resourceKey, withHashReplace);
};

FlexiJS.PostsAndTenure.EcfPostsTenure.InitializeControls = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;

    FlexiJS.PostsAndTenure.InitializeControls();

    $(constants._tsPostsSelector).kendoTabStrip({
        navigatable: false,
        animation: {
            open: {
                effects: 'fadeIn'
            }
        }
    });

    $(constants._btnDeletePostSelector).click(function (e) {
        e.preventDefault();

        FlexiJS.PostsAndTenure.OnApplicationPostDeleteClick(constants._openedPostId, constants._deletePostFunctionName);
    });

    $.ajax({
        url: constants._getServiceMethodUrlFunction('GetEcfGridEditable'),
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify({ gmsApplicationId: constants._appId }),
        dataType: 'json'
    }).done(function (r) {
        constants._ecfGridEditable = JSON.parse(r.d);

        if (constants._ecfGridEditable !== true) {
            $(constants._currentPostsGridSelector).parent().removeClass();
            $(constants._currentPostsGridSelector).parent().addClass('grid-hover-disabled');
        }

        FlexiJS.PostsAndTenure.EcfPostsTenure.AddGrids();
    });

    FlexiJS.PostsAndTenure.ApplicationPost.InitializeAddPostClick();

    FlexiJS.PostsAndTenure.InitializeBtnUpdateRoleClick(FlexiJS.PostsAndTenure.EcfPostsTenure.AddGrids);
};

FlexiJS.PostsAndTenure.EcfPostsTenure.OnPostDeleted = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;

    FlexiJS.PostsAndTenure.ClearPostForm();
    $(constants._postFormSelector).hide();
    $(constants._tsPostsSelector).show();
    $(constants._btnAddPostSelector).show();

    FlexiJS.PostsAndTenure.EcfPostsTenure.AddGrids();
};

FlexiJS.PostsAndTenure.EcfPostsTenure.AddGrids = function () {
    FlexiJS.PostsAndTenure.EcfPostsTenure.AddCurrentPostsGrid();
    FlexiJS.PostsAndTenure.EcfPostsTenure.AddAuditGrid();
};

FlexiJS.PostsAndTenure.EcfPostsTenure.EditPost = function (postId, postNumber) {
    FlexiJS.PostsAndTenure.OnApplicationPostEditClick(postId, postNumber);
};

FlexiJS.PostsAndTenure.EcfPostsTenure.AwardPost = function (postId) {
    FlexiJS.PostsAndTenure.EcfPostsTenure.SetPostStatus(postId, "AwardPost");
};

FlexiJS.PostsAndTenure.EcfPostsTenure.UnawardPost = function (postId) {
    FlexiJS.PostsAndTenure.EcfPostsTenure.SetPostStatus(postId, "UnawardPost");
};

FlexiJS.PostsAndTenure.EcfPostsTenure.PostNoLongerNeeded = function (postId) {
    FlexiJS.PostsAndTenure.EcfPostsTenure.SetPostStatus(postId, "PostNoLongerNeeded");
};

FlexiJS.PostsAndTenure.EcfPostsTenure.SetPostStatus = function (postId, serviceMethodName) {
    const constants = FlexiJS.PostsAndTenure.Constants;

    const postData = {
        gmsApplicationId: constants._appId,
        gmsApplicationPostId: postId
    }

    $.ajax({
        url: constants._getServiceMethodUrlFunction(serviceMethodName),
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify(postData),
        dataType: 'json'
    }).done(function (r) {
        const result = JSON.parse(r.d);

        if (result.Result) {
            showNotificationMessage('success', constants._getResourceFunction('PostSaveSuccess'));

            FlexiJS.PostsAndTenure.EcfPostsTenure.AddGrids();
        } else {
            showNotificationMessage('error', constants._getResourceFunction('PostSaveErrorMessage'));
        }
    }).fail(function () {
        showNotificationMessage('error', constants._getResourceFunction('PostSaveErrorMessage'));
    });
};

FlexiJS.PostsAndTenure.EcfPostsTenure.DeletePost = function (postId) {
    FlexiJS.PostsAndTenure.OnApplicationPostDeleteClick(postId, FlexiJS.PostsAndTenure.Constants._deletePostFunctionName);
};

FlexiJS.PostsAndTenure.EcfPostsTenure.GetPostButtonOptions = function (postId, postNumber, postStatusId) {
    const constants = FlexiJS.PostsAndTenure.Constants;

    const createOption = function (onClickFunction, title, titleClass) {

        var option = '<a href=\'\\#\' onclick=\'' + onClickFunction + ';return false;\'><div class=\'combobutton-option\'><span';

        if (titleClass) {
            option += ' class=\'' + titleClass + '\'';
        }

        option += '>' + title + '</span></div></a> ';

        return option;
    };

    const options = [];

    options.push(createOption('FlexiJS.PostsAndTenure.EcfPostsTenure.EditPost(' + postId + ', ' + postNumber + ')', constants._getResourceFunction('PostGridEdit')));

    if (postStatusId !== constants._postStatusAwardedId) {
        options.push(createOption('FlexiJS.PostsAndTenure.EcfPostsTenure.AwardPost(' + postId + ')', constants._getResourceFunction('AwardPost')));
    } else {
        options.push(createOption('FlexiJS.PostsAndTenure.EcfPostsTenure.UnawardPost(' + postId + ')', constants._getResourceFunction('UnawardPost')));
    }

    if (postStatusId !== constants._postStatusNoLongerNeededId) {
        options.push(createOption('FlexiJS.PostsAndTenure.EcfPostsTenure.PostNoLongerNeeded(' + postId + ')', constants._getResourceFunction('PostNoLongerNeeded')));
    }

    options.push(createOption('FlexiJS.PostsAndTenure.EcfPostsTenure.DeletePost(' + postId + ')', constants._getResourceFunction('DeletePost'), 'combobutton-warning'));

    return options.join('');
};

FlexiJS.PostsAndTenure.EcfPostsTenure.TenureConfirm = function (tenureId, postId) {
    FlexiJS.PostsAndTenure.EcfPostsTenure.SetTenureStatus(tenureId, "TenureConfirm", postId);
};

FlexiJS.PostsAndTenure.EcfPostsTenure.TenureUnconfirm = function (tenureId, postId) {
    FlexiJS.PostsAndTenure.EcfPostsTenure.SetTenureStatus(tenureId, "TenureUnconfirm", postId);
};

FlexiJS.PostsAndTenure.EcfPostsTenure.TenureAbeyance = function (tenureId, postId) {
    FlexiJS.PostsAndTenure.EcfPostsTenure.SetTenureStatus(tenureId, "TenureAbeyance", postId);
};

FlexiJS.PostsAndTenure.EcfPostsTenure.TenureRetireConfirmation = function (tenureId, postId, staffName, postName) {
    const constants = FlexiJS.PostsAndTenure.Constants;
    constants._tenureIdToRetire = tenureId;
    constants._postIdToRefresh = postId;

    const confirmationMessage = constants._getResourceFunction('TenureRetireConfirmationMessage').replace('{0}', FlexiJS.Utils.Strings.DecodeHtmlAttribute(staffName)).replace('{1}', postName);

    FlexiJS.ConfirmActions.KendoConfirmActionDialog(constants._getResourceFunction('TenureRetireConfirmationTitle'),
        constants._tenureRetireFunctionName,
        confirmationMessage,
        constants._getResourceFunction('TenureRetireConfirmationYes'),
        'btnTenureRetireConfirmationYes',
        constants._getResourceFunction('TenureRetireConfirmationNo'),
        'btnTenureRetireConfirmationNo',
        'False');
};

FlexiJS.PostsAndTenure.EcfPostsTenure.TenureRetire = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;

    FlexiJS.PostsAndTenure.EcfPostsTenure.SetTenureStatus(constants._tenureIdToRetire, "TenureRetire", constants._postIdToRefresh);
};

FlexiJS.PostsAndTenure.EcfPostsTenure.TenureNotAcceptingPost = function (tenureId, postId) {
    FlexiJS.PostsAndTenure.EcfPostsTenure.SetTenureStatus(tenureId, "TenureNotAcceptingPost", postId);
};

FlexiJS.PostsAndTenure.EcfPostsTenure.SetTenureStatus = function (tenureId, serviceMethodName, postId) {
    const constants = FlexiJS.PostsAndTenure.Constants;

    const postData = {
        gmsApplicationId: constants._appId,
        gmsApplicationPostTenureId: tenureId
    }

    $.ajax({
        url: constants._getServiceMethodUrlFunction(serviceMethodName),
        type: "POST",
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify(postData),
        dataType: 'json'
    }).done(function (r) {
        const result = JSON.parse(r.d);

        if (result.Result) {
            showNotificationMessage('success', constants._getResourceFunction('PostSaveSuccess'));

            $('#tenureGrid_' + postId).data('kendoGrid').dataSource.read();
            $(constants._auditGridSelector).data('kendoGrid').dataSource.read();
        } else {
            showNotificationMessage('error', constants._getResourceFunction('PostSaveErrorMessage'));
        }
    }).fail(function () {
        showNotificationMessage('error', constants._getResourceFunction('PostSaveErrorMessage'));
    });
};

FlexiJS.PostsAndTenure.EcfPostsTenure.TenureDeleteConfirmation = function (tenureId, postId, staffName) {
    const constants = FlexiJS.PostsAndTenure.Constants;
    constants._tenureIdToDelete = tenureId;
    constants._postIdToRefresh = postId;

    const confirmationMessage = constants._getResourceFunction('TenureDeleteConfirmationMessage').replace('{0}', FlexiJS.Utils.Strings.DecodeHtmlAttribute(staffName));

    FlexiJS.ConfirmActions.KendoConfirmActionDialog(constants._getResourceFunction('TenureDeleteConfirmationTitle'),
        constants._tenureDeleteFunctionName,
        confirmationMessage,
        constants._getResourceFunction('TenureDeleteConfirmationYes'),
        'btnTenureDeleteConfirmationYes',
        constants._getResourceFunction('TenureDeleteConfirmationNo'),
        'btnTenureDeleteConfirmationNo',
        'False');
};

FlexiJS.PostsAndTenure.EcfPostsTenure.TenureDelete = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;

    FlexiJS.PostsAndTenure.EcfPostsTenure.SetTenureStatus(constants._tenureIdToDelete, "DeleteTenure", constants._postIdToRefresh);
};

FlexiJS.PostsAndTenure.EcfPostsTenure.GetTenureButtonOptions = function (tenureId, tenureStatusId, postId, staffName, postName) {
    const constants = FlexiJS.PostsAndTenure.Constants;

    const createOption = function (onClickFunction, title, titleClass) {
        var option = '<a href=\'\\#\' onclick=&quot;' + onClickFunction + ';return false;&quot;><div class=\'combobutton-option\'><span';

        if (titleClass) {
            option += ' class=\'' + titleClass + '\'';
        }

        option += '>' + title + '</span ></div ></a > ';

        return option;
    };

    const options = [];

    if (tenureStatusId !== constants._tenureStatusRetiredId) {
        const isAlreadyConfirmed = tenureStatusId === constants._tenureStatusConfirmedId
            || tenureStatusId === constants._tenureStatusAbeyanceId;

        if (isAlreadyConfirmed) {
            options.push(createOption('FlexiJS.PostsAndTenure.EcfPostsTenure.TenureUnconfirm(' + tenureId + ', ' + postId + ')', constants._getResourceFunction('UnconfirmPost')));

            if (tenureStatusId !== constants._tenureStatusAbeyanceId) {
                options.push(createOption('FlexiJS.PostsAndTenure.EcfPostsTenure.TenureAbeyance(' + tenureId + ', ' + postId + ')', constants._getResourceFunction('Abeyance')));
            } else {
                options.push(createOption('FlexiJS.PostsAndTenure.EcfPostsTenure.TenureConfirm(' + tenureId + ', ' + postId + ')', constants._getResourceFunction('ReturnToWork')));
            }

            options.push(createOption('FlexiJS.PostsAndTenure.EcfPostsTenure.TenureRetireConfirmation(' + tenureId + ', ' + postId + ', \'' + FlexiJS.Utils.Strings.EscapeHtmlAttribute(staffName) + '\', \'' + postName + '\')',
                constants._getResourceFunction('Retire')));
        } else {
            options.push(createOption('FlexiJS.PostsAndTenure.EcfPostsTenure.TenureConfirm(' + tenureId + ', ' + postId + ')', constants._getResourceFunction('ConfirmPost')));

            if (tenureStatusId !== constants._tenureStatusNotAcceptingPostId) {
                options.push(createOption('FlexiJS.PostsAndTenure.EcfPostsTenure.TenureNotAcceptingPost(' + tenureId + ', ' + postId + ')', constants._getResourceFunction('NotAcceptingPost')));
            }
        }
    } else {
        options.push(createOption('FlexiJS.PostsAndTenure.EcfPostsTenure.TenureConfirm(' + tenureId + ', ' + postId + ')', constants._getResourceFunction('Unretire')));
    }

    options.push(createOption('FlexiJS.PostsAndTenure.EcfPostsTenure.TenureDeleteConfirmation(' + tenureId + ', ' + postId + ', \'' + FlexiJS.Utils.Strings.EscapeHtmlAttribute(staffName) + '\')',
        constants._getResourceFunction('DeleteTenure'), 'combobutton-warning'));

    return options.join('');
};

FlexiJS.PostsAndTenure.EcfPostsTenure.AddCurrentPostsGrid = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;

    const postsGridColumns = function () {
        const columns = [];

        columns.push({
            template: '<div><span class="post-grid-header">' + constants._getResourceFunction('Post') + ' #:PostNumber#</span></div>'
                + '<div><span>#:PostName#</span></div>',
            headerAttributes: {
                style: "display: none"
            }
        });

        columns.push({
            template: '<div><span class="post-grid-header">' + constants._getResourceFunction('PostGridSpinePointStartingGrade') + '</span></div>'
                + '<div><span>#:SpinePoint#/#:StartingGrade#</span></div>',
            headerAttributes: {
                style: "display: none"
            }
        });

        columns.push({
            template: '<div><span class="post-grid-header">' + constants._getResourceFunction('PostGridStartingSalary') + '</span></div>'
                + '<div><span>#:StartingSalary#</span></div>',
            headerAttributes: {
                style: "display: none"
            }
        });

        columns.push({
            template: '<div><span class="post-grid-header">' + constants._getResourceFunction('PostGridPostDuration') + '</span></div>'
                + '<div><span>#:PostDuration#</span></div>',
            headerAttributes: {
                style: "display: none"
            }
        });

        columns.push({
            template: '<div><span class="post-grid-header">' + constants._getResourceFunction('PostGridPostFTE') + '</span></div>'
                + '<div><span>#:FTE#</span></div>',
            headerAttributes: {
                style: "display: none"
            }
        });

        columns.push({
            template: '<div><span class="post-grid-header">' + constants._getResourceFunction('PostGridIncrementDate') + '</span></div>'
                + '<div><span>#:kendo.toString(kendo.parseDate(IncrementDate), "' + constants._dateFormatString + '")#</span></div>',
            headerAttributes: {
                style: "display: none"
            }
        });

        columns.push({
            template: '<div><span class="post-grid-header">' + constants._getResourceFunction('PostGridStatus') + '</span></div>'
                + '<div><span>#:Status#</span></div>',
            headerAttributes: {
                style: "display: none"
            }
        });

        if (constants._ecfGridEditable) {
            columns.push({
                template: function (dataItem) {
                    return '<button id="postButton_' + dataItem.PostId + '" onclick="return false;" data-buttonoptions="'
                        + FlexiJS.PostsAndTenure.EcfPostsTenure.GetPostButtonOptions(dataItem.PostId, dataItem.PostNumber, dataItem.StatusId, dataItem.StaffName)
                        + '"></button>';
                },
                headerAttributes: {
                    style: "display: none"
                },
                width: 40
            });
        }

        return columns;
    };

    const tenureGridColumns = function (postName) {
        const constants = FlexiJS.PostsAndTenure.Constants;
        const columns = [];

        columns.push({
            field: 'StaffName',
            width: 300,
            title: constants._getResourceFunction('PostGridStaffName')
        });

        columns.push({
            field: 'StartingBasicSalary',
            title: constants._getResourceFunction('PostGridStartingBasicSalary')
        });

        columns.push({
            field: 'FTE',
            title: constants._getResourceFunction('PostGridFTE')
        });

        columns.push({
            field: 'StartDate',
            title: constants._getResourceFunction('PostGridStartDate'),
            type: 'date',
            format: '{0:d}',
            filterable: false,
            template: '#if (StartDate) { # <span>#:kendo.toString(kendo.parseDate(StartDate), "' + constants._dateFormatString + '")#</span> # }'
                + 'else { #<span class= "no-response"> ' + constants._getResourceFunction('NoResponse', true) + '</span># }#'
        });

        columns.push({
            field: 'EndDate',
            title: constants._getResourceFunction('PostGridEndDate'),
            type: 'date',
            format: '{0:d}',
            filterable: false,
            template: '#if (EndDate) { # <span>#:kendo.toString(kendo.parseDate(EndDate), "' + constants._dateFormatString + '")#</span> # }'
                + 'else { #<span class= "no-response"> ' + constants._getResourceFunction('NoResponse', true) + '</span># }#'
        });

        columns.push({
            field: 'Status',
            title: constants._getResourceFunction('PostGridStatus')
        });

        if (constants._ecfGridEditable) {
            columns.push({
                template: function (dataItem) {
                    return '<button id="tenureButton_' + dataItem.TenureId + '" onclick="return false;" data-buttonoptions="'
                        + FlexiJS.PostsAndTenure.EcfPostsTenure.GetTenureButtonOptions(dataItem.TenureId, dataItem.StatusId, dataItem.PostId, dataItem.StaffName, postName)
                        + '"></button>';
                },
                width: 40
            });
        }

        return columns;
    };

    const tenureInit = function (e) {
        $('<div class="tenure-grid" id="tenureGrid_' + e.data.PostId + '"/>').appendTo(e.detailCell).kendoGrid({
            dataSource: {
                type: 'json',
                transport: {
                    read: {
                        url: constants._getServiceMethodUrlFunction('GetEcfPostTenures'),
                        type: 'POST',
                        contentType: 'application/json',
                        data: { gmsApplicationId: constants._appId, postId: e.data.PostId }
                    },
                    parameterMap: function (options) {
                        return JSON.stringify(options);
                    }
                },
                schema: {
                    type: 'json',
                    data: function (r) {
                        const result = JSON.parse(r.d);

                        return result.Tenures;
                    },
                    total: function (r) {
                        const jsonResult = JSON.parse(r.d);

                        return jsonResult.TotalTenures;
                    },
                    model: {
                        fields: {
                            PostId: { type: 'number' },
                            TenureId: { type: 'number' },
                            StaffName: { type: 'string' },
                            StartingSalary: { type: 'string' },
                            FTE: { type: 'string' },
                            StartDate: { type: 'date' },
                            EndDate: { type: 'date' },
                            StatusId: { type: 'number' },
                            Status: { type: 'string' }
                        }
                    }
                }
            },
            columns: tenureGridColumns(e.data.PostName),
            scrollable: false,
            dataBound: function () {
                const tenureButtons = document.querySelectorAll('[id^="tenureButton_"]');

                for (var index = 0; index < tenureButtons.length; index++) {
                    FlexiJS.Controls.ComboButton.SetUp(tenureButtons[index].id, true, true);
                }
            },
            noRecords: { template: constants._getResourceFunction('TenureGridNoRecords', true) }
        });
    };

    $(constants._currentPostsGridSelector).empty();
    $(constants._currentPostsGridSelector).kendoGrid({
        dataSource: {
            type: 'json',
            transport: {
                read: {
                    url: constants._getServiceMethodUrlFunction('GetEcfPosts'),
                    type: 'POST',
                    contentType: 'application/json',
                    data: { gmsApplicationId: constants._appId }
                },
                parameterMap: function (options) {
                    return JSON.stringify(options);
                }
            },
            schema: {
                type: 'json',
                data: function (r) {
                    const result = JSON.parse(r.d);

                    return result.Posts;
                },
                total: function (r) {
                    const jsonResult = JSON.parse(r.d);

                    return jsonResult.TotalPosts;
                },
                model: {
                    fields: {
                        PostId: { type: 'number' },
                        PostNumber: { type: 'number' },
                        PostName: { type: 'string' },
                        SpinePoint: { type: 'string' },
                        StartingGrade: { type: 'string' },
                        StartingSalary: { type: 'string' },
                        PostDuration: { type: 'string' },
                        FTE: { type: 'string' },
                        IncrementDate: { type: 'date' },
                        StatusId: { type: 'number' },
                        Status: { type: 'string' }
                    }
                }
            }
        },
        detailInit: tenureInit,
        scrollable: true,
        noRecords: { template: constants._getResourceFunction('PostGridNoRecords', true) },
        columns: postsGridColumns(),
        dataBound: function () {
            const postButtons = document.querySelectorAll('[id^="postButton_"]');

            for (var index = 0; index < postButtons.length; index++) {
                FlexiJS.Controls.ComboButton.SetUp(postButtons[index].id, true, true);
            }
        }
    });
    $(constants._currentPostsGridSelector + ' .k-grid-header').css('display', 'none');
};

FlexiJS.PostsAndTenure.EcfPostsTenure.AddAuditGrid = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    const getEcfPostsTenureResource = constants._getResourceFunction;

    const auditGridColumns = function () {
        const columns = [];

        columns.push({
            field: 'Type',
            title: getEcfPostsTenureResource('AuditGridType')
        });

        columns.push({
            field: 'Post',
            title: getEcfPostsTenureResource('AuditGridPost')
        });

        columns.push({
            field: 'Description',
            title: getEcfPostsTenureResource('AuditGridDescription')
        });

        columns.push({
            field: 'AuditDate',
            title: getEcfPostsTenureResource('AuditGridDate')
        });

        columns.push({
            field: 'User',
            title: getEcfPostsTenureResource('AuditGridUser')
        });

        return columns;
    };

    $(constants._auditGridSelector).empty();
    $(constants._auditGridSelector).kendoGrid({
        dataSource: {
            type: 'json',
            transport: {
                read: {
                    url: constants._getServiceMethodUrlFunction('GetEcfPostAuditHistories'),
                    type: 'POST',
                    contentType: 'application/json',
                    data: { gmsApplicationId: constants._appId, history: true }
                },
                parameterMap: function (options) {
                    return JSON.stringify(options);
                }
            },
            schema: {
                type: 'json',
                data: function (r) {
                    const result = JSON.parse(r.d);

                    return result.PostAuditHistories;
                },
                total: function (r) {
                    const jsonResult = JSON.parse(r.d);

                    return jsonResult.TotalPostAuditHistories;
                },
                model: {
                    fields: {
                        RowId: { type: 'number' },
                        Type: { type: 'string' },
                        Post: { type: 'number' },
                        Description: { type: 'string' },
                        AuditDate: { type: 'string' },
                        User: { type: 'string' }
                    }
                }
            }
        },
        sortable: true,
        scrollable: true,
        noRecords: { template: constants._getResourceFunction('AuditGridNoRecords', true) },
        columns: auditGridColumns()
    });
};

FlexiJS.PostsAndTenure.InitializePostOncostGrid = function (elementId, hiddenFieldId, postsData) {
    $('#onCostsValidation').toggle(false);

    if (hiddenFieldId) {
        const constants = FlexiJS.PostsAndTenure.Constants;
        var myData = JSON.parse($('#' + hiddenFieldId).val());

        if (!myData || !myData.Periods || myData.Periods.length < 1) return;

        var tableHTML = '<table cellspacing="0" cellpadding="0" border="1" class="on-cost-table border-box scrollable">' +
            '<thead>' +
            '<tr>' +
            '<th class="on-cost-period">' + constants._getResourceFunction('OnCostColumn.Period') + '</th>' +
            '<th class="on-cost-salary"><span class="required">*</span> ' + constants._getResourceFunction('OnCostColumn.Value1') + '</th>' +
            '<th class="on-cost-fte"> <span class="required">*</span> ' + constants._getResourceFunction('OnCostColumn.Value1Percentage') + '</th>' +
            '<th class="on-cost-allowance" colspan="2">' + constants._getResourceFunction('OnCostColumn.Value2') + '</th>' +
            '<th class="on-cost-insurance" colspan="2">' + constants._getResourceFunction('OnCostColumn.Value3') + '</th>' +
            '<th class="on-cost-superannuation" colspan="2">' + constants._getResourceFunction('OnCostColumn.Value4') + '</th>' +
            '<th class="on-cost-total">' + constants._getResourceFunction('OnCostColumn.Total') + '</th>' +
            '</tr>' +
            '</thead><tbody>';

        $.each(myData.Periods, function (index, period) {
            var safeDQName = period.Name.replace(/"/g, '\\"');
            tableHTML += '<tr data-oncostid="0" data-periodid="' + period.Id + '"' + (period.Optional == true ? ' style="display:none"' : '') + '>' +
                '<td class="noninput-paddingmatch"><div class="label-cell">' + period.Name + (period.Optional == true ? ' <i class="removeitem fas fa-minus-circle" onclick="FlexiJS.PostsAndTenure.PostOncostGridRemovePeriod(this); return false;" title="' + constants._getResourceFunction('OnCostRow.Remove') + '"></i>' : '') + '</div></td>' +
                '<td class="numericfield"><input aria-label="' + safeDQName + ' ' + constants._getResourceFunction('OnCostColumn.Value1') + '" data-field="Value1" data-inputtype="currency" /></td>' +
                '<td class="numericfield"><input aria-label="' + safeDQName + ' ' + constants._getResourceFunction('OnCostColumn.Value1Percentage') + '" data-field="Value1Percentage" data-inputtype="percentage" /></td>' +
                '<td class="border-noright numericfield"><input aria-label="' + safeDQName + ' ' + constants._getResourceFunction('OnCostColumn.Value2') + '" data-field="Value2" data-inputtype="currency" /></td><td class="border-noleft numericfield noninput-paddingmatch" data-field="Value2PercentOfValue1"></td>' +
                '<td class="border-noright numericfield"><input aria-label="' + safeDQName + ' ' + constants._getResourceFunction('OnCostColumn.Value3') + '" data-field="Value3" data-inputtype="currency" /></td><td class="border-noleft numericfield noninput-paddingmatch" data-field="Value3PercentOfValue1"></td>' +
                '<td class="border-noright numericfield"><input aria-label="' + safeDQName + ' ' + constants._getResourceFunction('OnCostColumn.Value4') + '" data-field="Value4" data-inputtype="currency" /></td><td class="border-noleft numericfield noninput-paddingmatch" data-field="Value4PercentOfValue1"></td>' +
                '<td class="total-cell numericfield noninput-paddingmatch" data-field="Total">£0.00</td>' +
                '</tr>';
        });

        tableHTML += '</tbody><tfoot>' +
            '<tr data-periodid="total">' +
            '<td class="noninput-paddingmatch">' + constants._getResourceFunction('OnCostRow.Total') + '</td>' +
            '<td data-field="Value1" class="numericfield noninput-paddingmatch">£0.00</td>' +
            '<td data-field="Value1Percentage" class="numericfield noninput-paddingmatch">-</td>' +
            '<td data-field="Value2" class="border-noright numericfield noninput-paddingmatch" colspan="2">£0.00</td>' +
            '<td data-field="Value3" class="border-noright numericfield noninput-paddingmatch" colspan="2">£0.00</td>' +
            '<td data-field="Value4" class="border-noright numericfield noninput-paddingmatch" colspan="2">£0.00</td>' +
            '<td data-field="Total" class="numericfield noninput-paddingmatch">£0.00</td>' +
            '</tr>' +
            '</tfoot>' +
            '</table>'
            ;

        var tableContainer = $(elementId);
        tableContainer.empty().append($(tableHTML));

        if (postsData) {
            var lastPeriodWithData = Math.max.apply(0, postsData.map(function (p) { return myData.IsGrouped == true ? p.GMSFundTypeBudgetTablePeriodGroupId : p.GMSFundTypeBudgetTablePeriodId; }));

            $.each(
                tableContainer.find('tr[data-periodid]'),
                function (index, row) {
                    if ($(row).data('periodid') <= lastPeriodWithData) {
                        $(row).toggle(true);
                    }
                }
            );

            $.each(
                postsData,
                function (index, postData) {
                    var row = tableContainer.find('tr[data-periodid="' + (myData.IsGrouped == true ? postData.GMSFundTypeBudgetTablePeriodGroupId : postData.GMSFundTypeBudgetTablePeriodId) + '"]');
                    if (row) {
                        $(row).data('oncostid', postData.GMSApplicationOnCostId);
                        $(row).find('input[data-field="Value1"]').val(postData.Value1);
                        $(row).find('input[data-field="Value1Percentage"]').val(postData.Value1Percentage);
                        $(row).find('input[data-field="Value2"]').val(postData.Value2);
                        $(row).find('input[data-field="Value3"]').val(postData.Value3);
                        $(row).find('input[data-field="Value4"]').val(postData.Value4);
                    }
                }
            );
        }

        $(tableContainer).find('input[data-inputtype="currency"]').kendoNumericTextBox({
            decimals: kendo.culture().numberFormat.currency.decimals,
            spinners: false,
            format: "c",
            width: 150,
            min: 0,
            max: 999999999999999,
            change: function () {
                FlexiJS.PostsAndTenure.PostOncostGridCalculateTotals(null, this.element);
                if ($(this.element).closest('td').hasClass('invalid')) {
                    FlexiJS.PostsAndTenure.ValidateOnCostsInformation(false);
                }
            }
        });
        $(tableContainer).find('input[data-inputtype="currency"]').keypress(function (e) {
            if (e.keyCode === 13) e.preventDefault();
        });

        $(tableContainer).find('input[data-inputtype="percentage"]').kendoNumericTextBox({
            format: "0.00\\\%",
            width: 150,
            min: 0,
            max: 100,
            spinners: false,
            change: function () {
                if ($(this.element).closest('td').hasClass('invalid')) {
                    FlexiJS.PostsAndTenure.ValidateOnCostsInformation(false);
                }
            }
        });
        $(tableContainer).find('input[data-inputtype="percentage"]').keypress(function (e) {
            if (e.keyCode === 13) e.preventDefault();
        });

        FlexiJS.PostsAndTenure.PostOncostGridCalculateTotals(tableContainer, null);

        FlexiJS.PostsAndTenure.PostOncostShowHideAddButton();
    }
};

FlexiJS.PostsAndTenure.PostOncostGridCalculateTotals = function (tableContainer, firingElement) {
    if (tableContainer == null) {
        if (firingElement == null) return;
        tableContainer = $(firingElement).closest('table').parent();
    }

    var currencyplaces = kendo.culture().numberFormat.currency.decimals;
    var overalTotal = {
        value1: 0,
        value2: 0,
        value3: 0,
        value4: 0,
        total: 0,
        value2percentofvalue1: 0,
        value3percentofvalue1: 0,
        value4percentofvalue1: 0
    };

    $.each(
        $(tableContainer).find('tr[data-periodid][data-periodid!="total"]'),
        function (index, row) {
            var values = FlexiJS.PostsAndTenure.PostOncostGridGetRowValues(row, firingElement == null);
            $(row).find('td[data-field="Total"]').empty().append(kendo.format('{0:c}', values.total));
            $(row).find('td[data-field="Value2PercentOfValue1"]').empty().append(kendo.format('{0:p2}', values.value2percentofvalue1));
            $(row).find('td[data-field="Value3PercentOfValue1"]').empty().append(kendo.format('{0:p2}', values.value3percentofvalue1));
            $(row).find('td[data-field="Value4PercentOfValue1"]').empty().append(kendo.format('{0:p2}', values.value4percentofvalue1));

            overalTotal.value1 = parseFloat((new BigNumber(overalTotal.value1)).plus(values.value1).toFixed(currencyplaces));
            overalTotal.value2 = parseFloat((new BigNumber(overalTotal.value2)).plus(values.value2).toFixed(currencyplaces));
            overalTotal.value3 = parseFloat((new BigNumber(overalTotal.value3)).plus(values.value3).toFixed(currencyplaces));
            overalTotal.value4 = parseFloat((new BigNumber(overalTotal.value4)).plus(values.value4).toFixed(currencyplaces));
            overalTotal.total = parseFloat((new BigNumber(overalTotal.total)).plus(values.total).toFixed(currencyplaces));
        }
    );

    if (overalTotal.value1 > 0) {
        overalTotal.value2percentofvalue1 = parseFloat((new BigNumber(overalTotal.value2)).dividedBy(overalTotal.value1).toFixed(4));
        overalTotal.value3percentofvalue1 = parseFloat((new BigNumber(overalTotal.value3)).dividedBy(overalTotal.value1).toFixed(4));
        overalTotal.value4percentofvalue1 = parseFloat((new BigNumber(overalTotal.value4)).dividedBy(overalTotal.value1).toFixed(4));
    }

    var footerRow = $(tableContainer).find('tr[data-periodid][data-periodid="total"]');

    $(footerRow).find('td[data-field="Value1"]').empty().append(kendo.format('{0:c}', overalTotal.value1));
    $(footerRow).find('td[data-field="Value2"]').empty().append(kendo.format('{0:c}', overalTotal.value2));
    $(footerRow).find('td[data-field="Value3"]').empty().append(kendo.format('{0:c}', overalTotal.value3));
    $(footerRow).find('td[data-field="Value4"]').empty().append(kendo.format('{0:c}', overalTotal.value4));
    $(footerRow).find('td[data-field="Total"]').empty().append(kendo.format('{0:c}', overalTotal.total));
};

FlexiJS.PostsAndTenure.PostOncostGridGetRowValues = function (row, ignoreVisibleCheck) {
    var currencyplaces = kendo.culture().numberFormat.currency.decimals;

    if ($(row).is(":visible") == true || ignoreVisibleCheck == true) {
        var values = {
            value1: parseFloat($(row).find('input[data-field="Value1"]').val() || 0),
            value1percentage: parseFloat($(row).find('input[data-field="Value1Percentage"]').val() || 0),
            value2: parseFloat($(row).find('input[data-field="Value2"]').val() || 0),
            value3: parseFloat($(row).find('input[data-field="Value3"]').val() || 0),
            value4: parseFloat($(row).find('input[data-field="Value4"]').val() || 0),
            total: 0,
            value2percentofvalue1: 0,
            value3percentofvalue1: 0,
            value4percentofvalue1: 0
        };

        values.total = parseFloat((new BigNumber(values.value1)).plus(values.value2).plus(values.value3).plus(values.value4).toFixed(currencyplaces));

        if (values.value1 > 0) {
            values.value2percentofvalue1 = parseFloat((new BigNumber(values.value2)).dividedBy(values.value1).toFixed(4));
            values.value3percentofvalue1 = parseFloat((new BigNumber(values.value3)).dividedBy(values.value1).toFixed(4));
            values.value4percentofvalue1 = parseFloat((new BigNumber(values.value4)).dividedBy(values.value1).toFixed(4));
        }

        return values;
    } else {
        return {
            value1: 0,
            value1percentage: 0,
            value2: 0,
            value3: 0,
            value4: 0,
            total: 0,
            value2percentofvalue1: 0,
            value3percentofvalue1: 0,
            value4percentofvalue1: 0
        };
    }
};

FlexiJS.PostsAndTenure.PostOncostGridAddPeriod = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    var tableContainer = $(constants._oncostGridSelector).find('table').parent();
    var nextPeriod = $(tableContainer).find('tr:hidden');

    if (nextPeriod.length > 0) {
        $(nextPeriod[0]).toggle(true);

        FlexiJS.PostsAndTenure.PostOncostGridCalculateTotals(tableContainer, {});
    }
    FlexiJS.PostsAndTenure.PostOncostShowHideAddButton();
    //FlexiJS.PostsAndTenure.ValidateOnCostsInformation(false);
};

FlexiJS.PostsAndTenure.PostOncostGridRemovePeriod = function (firingElement) {
    const constants = FlexiJS.PostsAndTenure.Constants;

    if (confirm(constants._getResourceFunction('RemoveRow.Line1') + '\r\n' + constants._getResourceFunction('RemoveRow.Line2'))) {
        var row = $(firingElement).closest('tr');
        var periodId = $(row).data('periodid');
        var table = $(row).closest('table').parent();

        var rows = $(table).find('tr[data-periodid][data-periodid!="total"]').filter(function () {
            return $(this).data("periodid") >= periodId;
        });

        rows.toggle(false);

        $.each(rows.find('input[data-inputtype="percentage"], input[data-inputtype="currency"]'), function (index, element) { $(element).data('kendoNumericTextBox').value(null); });

        FlexiJS.PostsAndTenure.PostOncostGridCalculateTotals(table, row);

        FlexiJS.PostsAndTenure.PostOncostShowHideAddButton();
    }
};

FlexiJS.PostsAndTenure.PostOncostShowHideAddButton = function () {
    const constants = FlexiJS.PostsAndTenure.Constants;
    var tableContainer = $(constants._oncostGridSelector).find('table').parent();
    if ($(tableContainer).find('tr:hidden').length > 0 && $(tableContainer).find('tr').length != 3) {
        $(constants._oncostsAddButtonSelector).removeClass('disabled').removeAttr("disabled");
    } else {
        $(constants._oncostsAddButtonSelector).addClass('disabled').attr('disabled', 'disabled');
    }
    $(tableContainer).find('.removeitem').toggle(false);
    $(tableContainer).find('tr:visible .removeitem').last().toggle(true);
};
;FlexiJS.GMS.Application.Settings = {};
FlexiJS.GMS.Application.Settings.JointFunding = {};

FlexiJS.GMS.Application.Settings.JointFunding.ConfirmJointFunding = function (callbackFunctionName) {

    AddJointFunderCallback = FlexiJS.GMS.Application.Settings.JointFunding.AddJointFunder;

    FlexiJS.ConfirmActions.KendoConfirmActionDialog(
        '',                                                                                                                 // title
        callbackFunctionName,                                                                                           // JqueryCommandName
        FlexiJS.Resources.GetResourceText('Controls/GMS/Process/SettingsDetails.ascx', 'AddJointFunder.Message'),           // messagehdnApplicationID
        FlexiJS.Resources.GetResourceText('Controls/GMS/Process/SettingsDetails.ascx', 'AddJointFunder.Accept'),            // btnyestext
        'btnJointFundingConfirmationYes',                                                                                   // btnyesuniquename
        FlexiJS.Resources.GetResourceText('Controls/GMS/Process/SettingsDetails.ascx', 'AddJointFunder.Cancel'),            // btnnotext
        'btnJointFundingConfirmationNo',                                                                                    // btnnouniquename
        'False',                                                                                                            // singleButtonConfirm
        '');                                                                                                                 // jQueryCommandNameNo
};
;FlexiJS.GMS.Application.Project = {};


FlexiJS.GMS.Application.Project.SetupTotalCostPercentageCalculation = function () {

    if ($("[id$='hdnCalculatePercentage']").val() == 'true') {
        $('.percentage').blur(function () {
            FlexiJS.GMS.Application.Project.CalculateTotalCostPercentage();
        });

        //Run on page load
        FlexiJS.GMS.Application.Project.CalculateTotalCostPercentage();
    }

};


FlexiJS.GMS.Application.Project.CalculateTotalCostPercentage = function () {
    var readOnly = $("#divPercentageDifference").data("readonly");
    var isReadOnly = readOnly !== undefined ? String(readOnly).toLowerCase() === "true" : false;
    var projectCosts = isReadOnly ? parseFloat(FlexiJS.Numbers.RemoveNonNumeric($("#lblReadOnlyCost").text())) : parseFloat(FlexiJS.Numbers.RemoveNonNumeric($('[id$="txtCost"]').val()));
    var valueSought = isReadOnly ? parseFloat(FlexiJS.Numbers.RemoveNonNumeric($("#lblReadOnlyValueSought").text())) : parseFloat(FlexiJS.Numbers.RemoveNonNumeric($('[id$="txtValueSought"]').val()));

    if (isNaN(projectCosts) === false && projectCosts > 0 && isNaN(valueSought) === false && valueSought > 0) {
        var percentage = 0.0;
        var percentageAllowed = 0.0;
        percentage = ((100 / projectCosts) * valueSought);
        percentageAllowed = parseFloat($("[id$='hdnFundTypeMaxPercentage']").val());
        if (isNaN(percentage) == false && percentage > 0) {
            percentage = FlexiJS.Numbers.FormatXDecimals(percentage, 2);
            $('#lblPercentageDifferenceValue').text(percentage);
            //Set print value as well
            $('#printPercentageDifferenceResponse').text(percentage);
            if (percentage > percentageAllowed) {
                $('[id$="lblPercentageError"]').text(FlexiJS.Resources.GetResourceText('Controls/FlexiForm/GMS/ApplicationProject.ascx', 'lblMaxPercentageAllowed.Message').replace('[percentage]', percentageAllowed));
            }
            else {
                $('[id$="lblPercentageError"]').text('');
            }
        }
    }

};;;(function (globalObject) {
  'use strict';

/*
 *      bignumber.js v9.0.0
 *      A JavaScript library for arbitrary-precision arithmetic.
 *      https://github.com/MikeMcl/bignumber.js
 *      Copyright (c) 2019 Michael Mclaughlin <M8ch88l@gmail.com>
 *      MIT Licensed.
 *
 *      BigNumber.prototype methods     |  BigNumber methods
 *                                      |
 *      absoluteValue            abs    |  clone
 *      comparedTo                      |  config               set
 *      decimalPlaces            dp     |      DECIMAL_PLACES
 *      dividedBy                div    |      ROUNDING_MODE
 *      dividedToIntegerBy       idiv   |      EXPONENTIAL_AT
 *      exponentiatedBy          pow    |      RANGE
 *      integerValue                    |      CRYPTO
 *      isEqualTo                eq     |      MODULO_MODE
 *      isFinite                        |      POW_PRECISION
 *      isGreaterThan            gt     |      FORMAT
 *      isGreaterThanOrEqualTo   gte    |      ALPHABET
 *      isInteger                       |  isBigNumber
 *      isLessThan               lt     |  maximum              max
 *      isLessThanOrEqualTo      lte    |  minimum              min
 *      isNaN                           |  random
 *      isNegative                      |  sum
 *      isPositive                      |
 *      isZero                          |
 *      minus                           |
 *      modulo                   mod    |
 *      multipliedBy             times  |
 *      negated                         |
 *      plus                            |
 *      precision                sd     |
 *      shiftedBy                       |
 *      squareRoot               sqrt   |
 *      toExponential                   |
 *      toFixed                         |
 *      toFormat                        |
 *      toFraction                      |
 *      toJSON                          |
 *      toNumber                        |
 *      toPrecision                     |
 *      toString                        |
 *      valueOf                         |
 *
 */


  var BigNumber,
    isNumeric = /^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,
    mathceil = Math.ceil,
    mathfloor = Math.floor,

    bignumberError = '[BigNumber Error] ',
    tooManyDigits = bignumberError + 'Number primitive has more than 15 significant digits: ',

    BASE = 1e14,
    LOG_BASE = 14,
    MAX_SAFE_INTEGER = 0x1fffffffffffff,         // 2^53 - 1
    // MAX_INT32 = 0x7fffffff,                   // 2^31 - 1
    POWS_TEN = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13],
    SQRT_BASE = 1e7,

    // EDITABLE
    // The limit on the value of DECIMAL_PLACES, TO_EXP_NEG, TO_EXP_POS, MIN_EXP, MAX_EXP, and
    // the arguments to toExponential, toFixed, toFormat, and toPrecision.
    MAX = 1E9;                                   // 0 to MAX_INT32


  /*
   * Create and return a BigNumber constructor.
   */
  function clone(configObject) {
    var div, convertBase, parseNumeric,
      P = BigNumber.prototype = { constructor: BigNumber, toString: null, valueOf: null },
      ONE = new BigNumber(1),


      //----------------------------- EDITABLE CONFIG DEFAULTS -------------------------------


      // The default values below must be integers within the inclusive ranges stated.
      // The values can also be changed at run-time using BigNumber.set.

      // The maximum number of decimal places for operations involving division.
      DECIMAL_PLACES = 20,                     // 0 to MAX

      // The rounding mode used when rounding to the above decimal places, and when using
      // toExponential, toFixed, toFormat and toPrecision, and round (default value).
      // UP         0 Away from zero.
      // DOWN       1 Towards zero.
      // CEIL       2 Towards +Infinity.
      // FLOOR      3 Towards -Infinity.
      // HALF_UP    4 Towards nearest neighbour. If equidistant, up.
      // HALF_DOWN  5 Towards nearest neighbour. If equidistant, down.
      // HALF_EVEN  6 Towards nearest neighbour. If equidistant, towards even neighbour.
      // HALF_CEIL  7 Towards nearest neighbour. If equidistant, towards +Infinity.
      // HALF_FLOOR 8 Towards nearest neighbour. If equidistant, towards -Infinity.
      ROUNDING_MODE = 4,                       // 0 to 8

      // EXPONENTIAL_AT : [TO_EXP_NEG , TO_EXP_POS]

      // The exponent value at and beneath which toString returns exponential notation.
      // Number type: -7
      TO_EXP_NEG = -7,                         // 0 to -MAX

      // The exponent value at and above which toString returns exponential notation.
      // Number type: 21
      TO_EXP_POS = 21,                         // 0 to MAX

      // RANGE : [MIN_EXP, MAX_EXP]

      // The minimum exponent value, beneath which underflow to zero occurs.
      // Number type: -324  (5e-324)
      MIN_EXP = -1e7,                          // -1 to -MAX

      // The maximum exponent value, above which overflow to Infinity occurs.
      // Number type:  308  (1.7976931348623157e+308)
      // For MAX_EXP > 1e7, e.g. new BigNumber('1e100000000').plus(1) may be slow.
      MAX_EXP = 1e7,                           // 1 to MAX

      // Whether to use cryptographically-secure random number generation, if available.
      CRYPTO = false,                          // true or false

      // The modulo mode used when calculating the modulus: a mod n.
      // The quotient (q = a / n) is calculated according to the corresponding rounding mode.
      // The remainder (r) is calculated as: r = a - n * q.
      //
      // UP        0 The remainder is positive if the dividend is negative, else is negative.
      // DOWN      1 The remainder has the same sign as the dividend.
      //             This modulo mode is commonly known as 'truncated division' and is
      //             equivalent to (a % n) in JavaScript.
      // FLOOR     3 The remainder has the same sign as the divisor (Python %).
      // HALF_EVEN 6 This modulo mode implements the IEEE 754 remainder function.
      // EUCLID    9 Euclidian division. q = sign(n) * floor(a / abs(n)).
      //             The remainder is always positive.
      //
      // The truncated division, floored division, Euclidian division and IEEE 754 remainder
      // modes are commonly used for the modulus operation.
      // Although the other rounding modes can also be used, they may not give useful results.
      MODULO_MODE = 1,                         // 0 to 9

      // The maximum number of significant digits of the result of the exponentiatedBy operation.
      // If POW_PRECISION is 0, there will be unlimited significant digits.
      POW_PRECISION = 0,                    // 0 to MAX

      // The format specification used by the BigNumber.prototype.toFormat method.
      FORMAT = {
        prefix: '',
        groupSize: 3,
        secondaryGroupSize: 0,
        groupSeparator: ',',
        decimalSeparator: '.',
        fractionGroupSize: 0,
        fractionGroupSeparator: '\xA0',      // non-breaking space
        suffix: ''
      },

      // The alphabet used for base conversion. It must be at least 2 characters long, with no '+',
      // '-', '.', whitespace, or repeated character.
      // '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_'
      ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';


    //------------------------------------------------------------------------------------------


    // CONSTRUCTOR


    /*
     * The BigNumber constructor and exported function.
     * Create and return a new instance of a BigNumber object.
     *
     * v {number|string|BigNumber} A numeric value.
     * [b] {number} The base of v. Integer, 2 to ALPHABET.length inclusive.
     */
    function BigNumber(v, b) {
      var alphabet, c, caseChanged, e, i, isNum, len, str,
        x = this;

      // Enable constructor call without `new`.
      if (!(x instanceof BigNumber)) return new BigNumber(v, b);

      if (b == null) {

        if (v && v._isBigNumber === true) {
          x.s = v.s;

          if (!v.c || v.e > MAX_EXP) {
            x.c = x.e = null;
          } else if (v.e < MIN_EXP) {
            x.c = [x.e = 0];
          } else {
            x.e = v.e;
            x.c = v.c.slice();
          }

          return;
        }

        if ((isNum = typeof v == 'number') && v * 0 == 0) {

          // Use `1 / n` to handle minus zero also.
          x.s = 1 / v < 0 ? (v = -v, -1) : 1;

          // Fast path for integers, where n < 2147483648 (2**31).
          if (v === ~~v) {
            for (e = 0, i = v; i >= 10; i /= 10, e++);

            if (e > MAX_EXP) {
              x.c = x.e = null;
            } else {
              x.e = e;
              x.c = [v];
            }

            return;
          }

          str = String(v);
        } else {

          if (!isNumeric.test(str = String(v))) return parseNumeric(x, str, isNum);

          x.s = str.charCodeAt(0) == 45 ? (str = str.slice(1), -1) : 1;
        }

        // Decimal point?
        if ((e = str.indexOf('.')) > -1) str = str.replace('.', '');

        // Exponential form?
        if ((i = str.search(/e/i)) > 0) {

          // Determine exponent.
          if (e < 0) e = i;
          e += +str.slice(i + 1);
          str = str.substring(0, i);
        } else if (e < 0) {

          // Integer.
          e = str.length;
        }

      } else {

        // '[BigNumber Error] Base {not a primitive number|not an integer|out of range}: {b}'
        intCheck(b, 2, ALPHABET.length, 'Base');

        // Allow exponential notation to be used with base 10 argument, while
        // also rounding to DECIMAL_PLACES as with other bases.
        if (b == 10) {
          x = new BigNumber(v);
          return round(x, DECIMAL_PLACES + x.e + 1, ROUNDING_MODE);
        }

        str = String(v);

        if (isNum = typeof v == 'number') {

          // Avoid potential interpretation of Infinity and NaN as base 44+ values.
          if (v * 0 != 0) return parseNumeric(x, str, isNum, b);

          x.s = 1 / v < 0 ? (str = str.slice(1), -1) : 1;

          // '[BigNumber Error] Number primitive has more than 15 significant digits: {n}'
          if (BigNumber.DEBUG && str.replace(/^0\.0*|\./, '').length > 15) {
            throw Error
             (tooManyDigits + v);
          }
        } else {
          x.s = str.charCodeAt(0) === 45 ? (str = str.slice(1), -1) : 1;
        }

        alphabet = ALPHABET.slice(0, b);
        e = i = 0;

        // Check that str is a valid base b number.
        // Don't use RegExp, so alphabet can contain special characters.
        for (len = str.length; i < len; i++) {
          if (alphabet.indexOf(c = str.charAt(i)) < 0) {
            if (c == '.') {

              // If '.' is not the first character and it has not be found before.
              if (i > e) {
                e = len;
                continue;
              }
            } else if (!caseChanged) {

              // Allow e.g. hexadecimal 'FF' as well as 'ff'.
              if (str == str.toUpperCase() && (str = str.toLowerCase()) ||
                  str == str.toLowerCase() && (str = str.toUpperCase())) {
                caseChanged = true;
                i = -1;
                e = 0;
                continue;
              }
            }

            return parseNumeric(x, String(v), isNum, b);
          }
        }

        // Prevent later check for length on converted number.
        isNum = false;
        str = convertBase(str, b, 10, x.s);

        // Decimal point?
        if ((e = str.indexOf('.')) > -1) str = str.replace('.', '');
        else e = str.length;
      }

      // Determine leading zeros.
      for (i = 0; str.charCodeAt(i) === 48; i++);

      // Determine trailing zeros.
      for (len = str.length; str.charCodeAt(--len) === 48;);

      if (str = str.slice(i, ++len)) {
        len -= i;

        // '[BigNumber Error] Number primitive has more than 15 significant digits: {n}'
        if (isNum && BigNumber.DEBUG &&
          len > 15 && (v > MAX_SAFE_INTEGER || v !== mathfloor(v))) {
            throw Error
             (tooManyDigits + (x.s * v));
        }

         // Overflow?
        if ((e = e - i - 1) > MAX_EXP) {

          // Infinity.
          x.c = x.e = null;

        // Underflow?
        } else if (e < MIN_EXP) {

          // Zero.
          x.c = [x.e = 0];
        } else {
          x.e = e;
          x.c = [];

          // Transform base

          // e is the base 10 exponent.
          // i is where to slice str to get the first element of the coefficient array.
          i = (e + 1) % LOG_BASE;
          if (e < 0) i += LOG_BASE;  // i < 1

          if (i < len) {
            if (i) x.c.push(+str.slice(0, i));

            for (len -= LOG_BASE; i < len;) {
              x.c.push(+str.slice(i, i += LOG_BASE));
            }

            i = LOG_BASE - (str = str.slice(i)).length;
          } else {
            i -= len;
          }

          for (; i--; str += '0');
          x.c.push(+str);
        }
      } else {

        // Zero.
        x.c = [x.e = 0];
      }
    }


    // CONSTRUCTOR PROPERTIES


    BigNumber.clone = clone;

    BigNumber.ROUND_UP = 0;
    BigNumber.ROUND_DOWN = 1;
    BigNumber.ROUND_CEIL = 2;
    BigNumber.ROUND_FLOOR = 3;
    BigNumber.ROUND_HALF_UP = 4;
    BigNumber.ROUND_HALF_DOWN = 5;
    BigNumber.ROUND_HALF_EVEN = 6;
    BigNumber.ROUND_HALF_CEIL = 7;
    BigNumber.ROUND_HALF_FLOOR = 8;
    BigNumber.EUCLID = 9;


    /*
     * Configure infrequently-changing library-wide settings.
     *
     * Accept an object with the following optional properties (if the value of a property is
     * a number, it must be an integer within the inclusive range stated):
     *
     *   DECIMAL_PLACES   {number}           0 to MAX
     *   ROUNDING_MODE    {number}           0 to 8
     *   EXPONENTIAL_AT   {number|number[]}  -MAX to MAX  or  [-MAX to 0, 0 to MAX]
     *   RANGE            {number|number[]}  -MAX to MAX (not zero)  or  [-MAX to -1, 1 to MAX]
     *   CRYPTO           {boolean}          true or false
     *   MODULO_MODE      {number}           0 to 9
     *   POW_PRECISION       {number}           0 to MAX
     *   ALPHABET         {string}           A string of two or more unique characters which does
     *                                       not contain '.'.
     *   FORMAT           {object}           An object with some of the following properties:
     *     prefix                 {string}
     *     groupSize              {number}
     *     secondaryGroupSize     {number}
     *     groupSeparator         {string}
     *     decimalSeparator       {string}
     *     fractionGroupSize      {number}
     *     fractionGroupSeparator {string}
     *     suffix                 {string}
     *
     * (The values assigned to the above FORMAT object properties are not checked for validity.)
     *
     * E.g.
     * BigNumber.config({ DECIMAL_PLACES : 20, ROUNDING_MODE : 4 })
     *
     * Ignore properties/parameters set to null or undefined, except for ALPHABET.
     *
     * Return an object with the properties current values.
     */
    BigNumber.config = BigNumber.set = function (obj) {
      var p, v;

      if (obj != null) {

        if (typeof obj == 'object') {

          // DECIMAL_PLACES {number} Integer, 0 to MAX inclusive.
          // '[BigNumber Error] DECIMAL_PLACES {not a primitive number|not an integer|out of range}: {v}'
          if (obj.hasOwnProperty(p = 'DECIMAL_PLACES')) {
            v = obj[p];
            intCheck(v, 0, MAX, p);
            DECIMAL_PLACES = v;
          }

          // ROUNDING_MODE {number} Integer, 0 to 8 inclusive.
          // '[BigNumber Error] ROUNDING_MODE {not a primitive number|not an integer|out of range}: {v}'
          if (obj.hasOwnProperty(p = 'ROUNDING_MODE')) {
            v = obj[p];
            intCheck(v, 0, 8, p);
            ROUNDING_MODE = v;
          }

          // EXPONENTIAL_AT {number|number[]}
          // Integer, -MAX to MAX inclusive or
          // [integer -MAX to 0 inclusive, 0 to MAX inclusive].
          // '[BigNumber Error] EXPONENTIAL_AT {not a primitive number|not an integer|out of range}: {v}'
          if (obj.hasOwnProperty(p = 'EXPONENTIAL_AT')) {
            v = obj[p];
            if (v && v.pop) {
              intCheck(v[0], -MAX, 0, p);
              intCheck(v[1], 0, MAX, p);
              TO_EXP_NEG = v[0];
              TO_EXP_POS = v[1];
            } else {
              intCheck(v, -MAX, MAX, p);
              TO_EXP_NEG = -(TO_EXP_POS = v < 0 ? -v : v);
            }
          }

          // RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or
          // [integer -MAX to -1 inclusive, integer 1 to MAX inclusive].
          // '[BigNumber Error] RANGE {not a primitive number|not an integer|out of range|cannot be zero}: {v}'
          if (obj.hasOwnProperty(p = 'RANGE')) {
            v = obj[p];
            if (v && v.pop) {
              intCheck(v[0], -MAX, -1, p);
              intCheck(v[1], 1, MAX, p);
              MIN_EXP = v[0];
              MAX_EXP = v[1];
            } else {
              intCheck(v, -MAX, MAX, p);
              if (v) {
                MIN_EXP = -(MAX_EXP = v < 0 ? -v : v);
              } else {
                throw Error
                 (bignumberError + p + ' cannot be zero: ' + v);
              }
            }
          }

          // CRYPTO {boolean} true or false.
          // '[BigNumber Error] CRYPTO not true or false: {v}'
          // '[BigNumber Error] crypto unavailable'
          if (obj.hasOwnProperty(p = 'CRYPTO')) {
            v = obj[p];
            if (v === !!v) {
              if (v) {
                if (typeof crypto != 'undefined' && crypto &&
                 (crypto.getRandomValues || crypto.randomBytes)) {
                  CRYPTO = v;
                } else {
                  CRYPTO = !v;
                  throw Error
                   (bignumberError + 'crypto unavailable');
                }
              } else {
                CRYPTO = v;
              }
            } else {
              throw Error
               (bignumberError + p + ' not true or false: ' + v);
            }
          }

          // MODULO_MODE {number} Integer, 0 to 9 inclusive.
          // '[BigNumber Error] MODULO_MODE {not a primitive number|not an integer|out of range}: {v}'
          if (obj.hasOwnProperty(p = 'MODULO_MODE')) {
            v = obj[p];
            intCheck(v, 0, 9, p);
            MODULO_MODE = v;
          }

          // POW_PRECISION {number} Integer, 0 to MAX inclusive.
          // '[BigNumber Error] POW_PRECISION {not a primitive number|not an integer|out of range}: {v}'
          if (obj.hasOwnProperty(p = 'POW_PRECISION')) {
            v = obj[p];
            intCheck(v, 0, MAX, p);
            POW_PRECISION = v;
          }

          // FORMAT {object}
          // '[BigNumber Error] FORMAT not an object: {v}'
          if (obj.hasOwnProperty(p = 'FORMAT')) {
            v = obj[p];
            if (typeof v == 'object') FORMAT = v;
            else throw Error
             (bignumberError + p + ' not an object: ' + v);
          }

          // ALPHABET {string}
          // '[BigNumber Error] ALPHABET invalid: {v}'
          if (obj.hasOwnProperty(p = 'ALPHABET')) {
            v = obj[p];

            // Disallow if only one character,
            // or if it contains '+', '-', '.', whitespace, or a repeated character.
            if (typeof v == 'string' && !/^.$|[+-.\s]|(.).*\1/.test(v)) {
              ALPHABET = v;
            } else {
              throw Error
               (bignumberError + p + ' invalid: ' + v);
            }
          }

        } else {

          // '[BigNumber Error] Object expected: {v}'
          throw Error
           (bignumberError + 'Object expected: ' + obj);
        }
      }

      return {
        DECIMAL_PLACES: DECIMAL_PLACES,
        ROUNDING_MODE: ROUNDING_MODE,
        EXPONENTIAL_AT: [TO_EXP_NEG, TO_EXP_POS],
        RANGE: [MIN_EXP, MAX_EXP],
        CRYPTO: CRYPTO,
        MODULO_MODE: MODULO_MODE,
        POW_PRECISION: POW_PRECISION,
        FORMAT: FORMAT,
        ALPHABET: ALPHABET
      };
    };


    /*
     * Return true if v is a BigNumber instance, otherwise return false.
     *
     * If BigNumber.DEBUG is true, throw if a BigNumber instance is not well-formed.
     *
     * v {any}
     *
     * '[BigNumber Error] Invalid BigNumber: {v}'
     */
    BigNumber.isBigNumber = function (v) {
      if (!v || v._isBigNumber !== true) return false;
      if (!BigNumber.DEBUG) return true;

      var i, n,
        c = v.c,
        e = v.e,
        s = v.s;

      out: if ({}.toString.call(c) == '[object Array]') {

        if ((s === 1 || s === -1) && e >= -MAX && e <= MAX && e === mathfloor(e)) {

          // If the first element is zero, the BigNumber value must be zero.
          if (c[0] === 0) {
            if (e === 0 && c.length === 1) return true;
            break out;
          }

          // Calculate number of digits that c[0] should have, based on the exponent.
          i = (e + 1) % LOG_BASE;
          if (i < 1) i += LOG_BASE;

          // Calculate number of digits of c[0].
          //if (Math.ceil(Math.log(c[0] + 1) / Math.LN10) == i) {
          if (String(c[0]).length == i) {

            for (i = 0; i < c.length; i++) {
              n = c[i];
              if (n < 0 || n >= BASE || n !== mathfloor(n)) break out;
            }

            // Last element cannot be zero, unless it is the only element.
            if (n !== 0) return true;
          }
        }

      // Infinity/NaN
      } else if (c === null && e === null && (s === null || s === 1 || s === -1)) {
        return true;
      }

      throw Error
        (bignumberError + 'Invalid BigNumber: ' + v);
    };


    /*
     * Return a new BigNumber whose value is the maximum of the arguments.
     *
     * arguments {number|string|BigNumber}
     */
    BigNumber.maximum = BigNumber.max = function () {
      return maxOrMin(arguments, P.lt);
    };


    /*
     * Return a new BigNumber whose value is the minimum of the arguments.
     *
     * arguments {number|string|BigNumber}
     */
    BigNumber.minimum = BigNumber.min = function () {
      return maxOrMin(arguments, P.gt);
    };


    /*
     * Return a new BigNumber with a random value equal to or greater than 0 and less than 1,
     * and with dp, or DECIMAL_PLACES if dp is omitted, decimal places (or less if trailing
     * zeros are produced).
     *
     * [dp] {number} Decimal places. Integer, 0 to MAX inclusive.
     *
     * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {dp}'
     * '[BigNumber Error] crypto unavailable'
     */
    BigNumber.random = (function () {
      var pow2_53 = 0x20000000000000;

      // Return a 53 bit integer n, where 0 <= n < 9007199254740992.
      // Check if Math.random() produces more than 32 bits of randomness.
      // If it does, assume at least 53 bits are produced, otherwise assume at least 30 bits.
      // 0x40000000 is 2^30, 0x800000 is 2^23, 0x1fffff is 2^21 - 1.
      var random53bitInt = (Math.random() * pow2_53) & 0x1fffff
       ? function () { return mathfloor(Math.random() * pow2_53); }
       : function () { return ((Math.random() * 0x40000000 | 0) * 0x800000) +
         (Math.random() * 0x800000 | 0); };

      return function (dp) {
        var a, b, e, k, v,
          i = 0,
          c = [],
          rand = new BigNumber(ONE);

        if (dp == null) dp = DECIMAL_PLACES;
        else intCheck(dp, 0, MAX);

        k = mathceil(dp / LOG_BASE);

        if (CRYPTO) {

          // Browsers supporting crypto.getRandomValues.
          if (crypto.getRandomValues) {

            a = crypto.getRandomValues(new Uint32Array(k *= 2));

            for (; i < k;) {

              // 53 bits:
              // ((Math.pow(2, 32) - 1) * Math.pow(2, 21)).toString(2)
              // 11111 11111111 11111111 11111111 11100000 00000000 00000000
              // ((Math.pow(2, 32) - 1) >>> 11).toString(2)
              //                                     11111 11111111 11111111
              // 0x20000 is 2^21.
              v = a[i] * 0x20000 + (a[i + 1] >>> 11);

              // Rejection sampling:
              // 0 <= v < 9007199254740992
              // Probability that v >= 9e15, is
              // 7199254740992 / 9007199254740992 ~= 0.0008, i.e. 1 in 1251
              if (v >= 9e15) {
                b = crypto.getRandomValues(new Uint32Array(2));
                a[i] = b[0];
                a[i + 1] = b[1];
              } else {

                // 0 <= v <= 8999999999999999
                // 0 <= (v % 1e14) <= 99999999999999
                c.push(v % 1e14);
                i += 2;
              }
            }
            i = k / 2;

          // Node.js supporting crypto.randomBytes.
          } else if (crypto.randomBytes) {

            // buffer
            a = crypto.randomBytes(k *= 7);

            for (; i < k;) {

              // 0x1000000000000 is 2^48, 0x10000000000 is 2^40
              // 0x100000000 is 2^32, 0x1000000 is 2^24
              // 11111 11111111 11111111 11111111 11111111 11111111 11111111
              // 0 <= v < 9007199254740992
              v = ((a[i] & 31) * 0x1000000000000) + (a[i + 1] * 0x10000000000) +
                 (a[i + 2] * 0x100000000) + (a[i + 3] * 0x1000000) +
                 (a[i + 4] << 16) + (a[i + 5] << 8) + a[i + 6];

              if (v >= 9e15) {
                crypto.randomBytes(7).copy(a, i);
              } else {

                // 0 <= (v % 1e14) <= 99999999999999
                c.push(v % 1e14);
                i += 7;
              }
            }
            i = k / 7;
          } else {
            CRYPTO = false;
            throw Error
             (bignumberError + 'crypto unavailable');
          }
        }

        // Use Math.random.
        if (!CRYPTO) {

          for (; i < k;) {
            v = random53bitInt();
            if (v < 9e15) c[i++] = v % 1e14;
          }
        }

        k = c[--i];
        dp %= LOG_BASE;

        // Convert trailing digits to zeros according to dp.
        if (k && dp) {
          v = POWS_TEN[LOG_BASE - dp];
          c[i] = mathfloor(k / v) * v;
        }

        // Remove trailing elements which are zero.
        for (; c[i] === 0; c.pop(), i--);

        // Zero?
        if (i < 0) {
          c = [e = 0];
        } else {

          // Remove leading elements which are zero and adjust exponent accordingly.
          for (e = -1 ; c[0] === 0; c.splice(0, 1), e -= LOG_BASE);

          // Count the digits of the first element of c to determine leading zeros, and...
          for (i = 1, v = c[0]; v >= 10; v /= 10, i++);

          // adjust the exponent accordingly.
          if (i < LOG_BASE) e -= LOG_BASE - i;
        }

        rand.e = e;
        rand.c = c;
        return rand;
      };
    })();


    /*
     * Return a BigNumber whose value is the sum of the arguments.
     *
     * arguments {number|string|BigNumber}
     */
    BigNumber.sum = function () {
      var i = 1,
        args = arguments,
        sum = new BigNumber(args[0]);
      for (; i < args.length;) sum = sum.plus(args[i++]);
      return sum;
    };


    // PRIVATE FUNCTIONS


    // Called by BigNumber and BigNumber.prototype.toString.
    convertBase = (function () {
      var decimal = '0123456789';

      /*
       * Convert string of baseIn to an array of numbers of baseOut.
       * Eg. toBaseOut('255', 10, 16) returns [15, 15].
       * Eg. toBaseOut('ff', 16, 10) returns [2, 5, 5].
       */
      function toBaseOut(str, baseIn, baseOut, alphabet) {
        var j,
          arr = [0],
          arrL,
          i = 0,
          len = str.length;

        for (; i < len;) {
          for (arrL = arr.length; arrL--; arr[arrL] *= baseIn);

          arr[0] += alphabet.indexOf(str.charAt(i++));

          for (j = 0; j < arr.length; j++) {

            if (arr[j] > baseOut - 1) {
              if (arr[j + 1] == null) arr[j + 1] = 0;
              arr[j + 1] += arr[j] / baseOut | 0;
              arr[j] %= baseOut;
            }
          }
        }

        return arr.reverse();
      }

      // Convert a numeric string of baseIn to a numeric string of baseOut.
      // If the caller is toString, we are converting from base 10 to baseOut.
      // If the caller is BigNumber, we are converting from baseIn to base 10.
      return function (str, baseIn, baseOut, sign, callerIsToString) {
        var alphabet, d, e, k, r, x, xc, y,
          i = str.indexOf('.'),
          dp = DECIMAL_PLACES,
          rm = ROUNDING_MODE;

        // Non-integer.
        if (i >= 0) {
          k = POW_PRECISION;

          // Unlimited precision.
          POW_PRECISION = 0;
          str = str.replace('.', '');
          y = new BigNumber(baseIn);
          x = y.pow(str.length - i);
          POW_PRECISION = k;

          // Convert str as if an integer, then restore the fraction part by dividing the
          // result by its base raised to a power.

          y.c = toBaseOut(toFixedPoint(coeffToString(x.c), x.e, '0'),
           10, baseOut, decimal);
          y.e = y.c.length;
        }

        // Convert the number as integer.

        xc = toBaseOut(str, baseIn, baseOut, callerIsToString
         ? (alphabet = ALPHABET, decimal)
         : (alphabet = decimal, ALPHABET));

        // xc now represents str as an integer and converted to baseOut. e is the exponent.
        e = k = xc.length;

        // Remove trailing zeros.
        for (; xc[--k] == 0; xc.pop());

        // Zero?
        if (!xc[0]) return alphabet.charAt(0);

        // Does str represent an integer? If so, no need for the division.
        if (i < 0) {
          --e;
        } else {
          x.c = xc;
          x.e = e;

          // The sign is needed for correct rounding.
          x.s = sign;
          x = div(x, y, dp, rm, baseOut);
          xc = x.c;
          r = x.r;
          e = x.e;
        }

        // xc now represents str converted to baseOut.

        // THe index of the rounding digit.
        d = e + dp + 1;

        // The rounding digit: the digit to the right of the digit that may be rounded up.
        i = xc[d];

        // Look at the rounding digits and mode to determine whether to round up.

        k = baseOut / 2;
        r = r || d < 0 || xc[d + 1] != null;

        r = rm < 4 ? (i != null || r) && (rm == 0 || rm == (x.s < 0 ? 3 : 2))
              : i > k || i == k &&(rm == 4 || r || rm == 6 && xc[d - 1] & 1 ||
               rm == (x.s < 0 ? 8 : 7));

        // If the index of the rounding digit is not greater than zero, or xc represents
        // zero, then the result of the base conversion is zero or, if rounding up, a value
        // such as 0.00001.
        if (d < 1 || !xc[0]) {

          // 1^-dp or 0
          str = r ? toFixedPoint(alphabet.charAt(1), -dp, alphabet.charAt(0)) : alphabet.charAt(0);
        } else {

          // Truncate xc to the required number of decimal places.
          xc.length = d;

          // Round up?
          if (r) {

            // Rounding up may mean the previous digit has to be rounded up and so on.
            for (--baseOut; ++xc[--d] > baseOut;) {
              xc[d] = 0;

              if (!d) {
                ++e;
                xc = [1].concat(xc);
              }
            }
          }

          // Determine trailing zeros.
          for (k = xc.length; !xc[--k];);

          // E.g. [4, 11, 15] becomes 4bf.
          for (i = 0, str = ''; i <= k; str += alphabet.charAt(xc[i++]));

          // Add leading zeros, decimal point and trailing zeros as required.
          str = toFixedPoint(str, e, alphabet.charAt(0));
        }

        // The caller will add the sign.
        return str;
      };
    })();


    // Perform division in the specified base. Called by div and convertBase.
    div = (function () {

      // Assume non-zero x and k.
      function multiply(x, k, base) {
        var m, temp, xlo, xhi,
          carry = 0,
          i = x.length,
          klo = k % SQRT_BASE,
          khi = k / SQRT_BASE | 0;

        for (x = x.slice(); i--;) {
          xlo = x[i] % SQRT_BASE;
          xhi = x[i] / SQRT_BASE | 0;
          m = khi * xlo + xhi * klo;
          temp = klo * xlo + ((m % SQRT_BASE) * SQRT_BASE) + carry;
          carry = (temp / base | 0) + (m / SQRT_BASE | 0) + khi * xhi;
          x[i] = temp % base;
        }

        if (carry) x = [carry].concat(x);

        return x;
      }

      function compare(a, b, aL, bL) {
        var i, cmp;

        if (aL != bL) {
          cmp = aL > bL ? 1 : -1;
        } else {

          for (i = cmp = 0; i < aL; i++) {

            if (a[i] != b[i]) {
              cmp = a[i] > b[i] ? 1 : -1;
              break;
            }
          }
        }

        return cmp;
      }

      function subtract(a, b, aL, base) {
        var i = 0;

        // Subtract b from a.
        for (; aL--;) {
          a[aL] -= i;
          i = a[aL] < b[aL] ? 1 : 0;
          a[aL] = i * base + a[aL] - b[aL];
        }

        // Remove leading zeros.
        for (; !a[0] && a.length > 1; a.splice(0, 1));
      }

      // x: dividend, y: divisor.
      return function (x, y, dp, rm, base) {
        var cmp, e, i, more, n, prod, prodL, q, qc, rem, remL, rem0, xi, xL, yc0,
          yL, yz,
          s = x.s == y.s ? 1 : -1,
          xc = x.c,
          yc = y.c;

        // Either NaN, Infinity or 0?
        if (!xc || !xc[0] || !yc || !yc[0]) {

          return new BigNumber(

           // Return NaN if either NaN, or both Infinity or 0.
           !x.s || !y.s || (xc ? yc && xc[0] == yc[0] : !yc) ? NaN :

            // Return ±0 if x is ±0 or y is ±Infinity, or return ±Infinity as y is ±0.
            xc && xc[0] == 0 || !yc ? s * 0 : s / 0
         );
        }

        q = new BigNumber(s);
        qc = q.c = [];
        e = x.e - y.e;
        s = dp + e + 1;

        if (!base) {
          base = BASE;
          e = bitFloor(x.e / LOG_BASE) - bitFloor(y.e / LOG_BASE);
          s = s / LOG_BASE | 0;
        }

        // Result exponent may be one less then the current value of e.
        // The coefficients of the BigNumbers from convertBase may have trailing zeros.
        for (i = 0; yc[i] == (xc[i] || 0); i++);

        if (yc[i] > (xc[i] || 0)) e--;

        if (s < 0) {
          qc.push(1);
          more = true;
        } else {
          xL = xc.length;
          yL = yc.length;
          i = 0;
          s += 2;

          // Normalise xc and yc so highest order digit of yc is >= base / 2.

          n = mathfloor(base / (yc[0] + 1));

          // Not necessary, but to handle odd bases where yc[0] == (base / 2) - 1.
          // if (n > 1 || n++ == 1 && yc[0] < base / 2) {
          if (n > 1) {
            yc = multiply(yc, n, base);
            xc = multiply(xc, n, base);
            yL = yc.length;
            xL = xc.length;
          }

          xi = yL;
          rem = xc.slice(0, yL);
          remL = rem.length;

          // Add zeros to make remainder as long as divisor.
          for (; remL < yL; rem[remL++] = 0);
          yz = yc.slice();
          yz = [0].concat(yz);
          yc0 = yc[0];
          if (yc[1] >= base / 2) yc0++;
          // Not necessary, but to prevent trial digit n > base, when using base 3.
          // else if (base == 3 && yc0 == 1) yc0 = 1 + 1e-15;

          do {
            n = 0;

            // Compare divisor and remainder.
            cmp = compare(yc, rem, yL, remL);

            // If divisor < remainder.
            if (cmp < 0) {

              // Calculate trial digit, n.

              rem0 = rem[0];
              if (yL != remL) rem0 = rem0 * base + (rem[1] || 0);

              // n is how many times the divisor goes into the current remainder.
              n = mathfloor(rem0 / yc0);

              //  Algorithm:
              //  product = divisor multiplied by trial digit (n).
              //  Compare product and remainder.
              //  If product is greater than remainder:
              //    Subtract divisor from product, decrement trial digit.
              //  Subtract product from remainder.
              //  If product was less than remainder at the last compare:
              //    Compare new remainder and divisor.
              //    If remainder is greater than divisor:
              //      Subtract divisor from remainder, increment trial digit.

              if (n > 1) {

                // n may be > base only when base is 3.
                if (n >= base) n = base - 1;

                // product = divisor * trial digit.
                prod = multiply(yc, n, base);
                prodL = prod.length;
                remL = rem.length;

                // Compare product and remainder.
                // If product > remainder then trial digit n too high.
                // n is 1 too high about 5% of the time, and is not known to have
                // ever been more than 1 too high.
                while (compare(prod, rem, prodL, remL) == 1) {
                  n--;

                  // Subtract divisor from product.
                  subtract(prod, yL < prodL ? yz : yc, prodL, base);
                  prodL = prod.length;
                  cmp = 1;
                }
              } else {

                // n is 0 or 1, cmp is -1.
                // If n is 0, there is no need to compare yc and rem again below,
                // so change cmp to 1 to avoid it.
                // If n is 1, leave cmp as -1, so yc and rem are compared again.
                if (n == 0) {

                  // divisor < remainder, so n must be at least 1.
                  cmp = n = 1;
                }

                // product = divisor
                prod = yc.slice();
                prodL = prod.length;
              }

              if (prodL < remL) prod = [0].concat(prod);

              // Subtract product from remainder.
              subtract(rem, prod, remL, base);
              remL = rem.length;

               // If product was < remainder.
              if (cmp == -1) {

                // Compare divisor and new remainder.
                // If divisor < new remainder, subtract divisor from remainder.
                // Trial digit n too low.
                // n is 1 too low about 5% of the time, and very rarely 2 too low.
                while (compare(yc, rem, yL, remL) < 1) {
                  n++;

                  // Subtract divisor from remainder.
                  subtract(rem, yL < remL ? yz : yc, remL, base);
                  remL = rem.length;
                }
              }
            } else if (cmp === 0) {
              n++;
              rem = [0];
            } // else cmp === 1 and n will be 0

            // Add the next digit, n, to the result array.
            qc[i++] = n;

            // Update the remainder.
            if (rem[0]) {
              rem[remL++] = xc[xi] || 0;
            } else {
              rem = [xc[xi]];
              remL = 1;
            }
          } while ((xi++ < xL || rem[0] != null) && s--);

          more = rem[0] != null;

          // Leading zero?
          if (!qc[0]) qc.splice(0, 1);
        }

        if (base == BASE) {

          // To calculate q.e, first get the number of digits of qc[0].
          for (i = 1, s = qc[0]; s >= 10; s /= 10, i++);

          round(q, dp + (q.e = i + e * LOG_BASE - 1) + 1, rm, more);

        // Caller is convertBase.
        } else {
          q.e = e;
          q.r = +more;
        }

        return q;
      };
    })();


    /*
     * Return a string representing the value of BigNumber n in fixed-point or exponential
     * notation rounded to the specified decimal places or significant digits.
     *
     * n: a BigNumber.
     * i: the index of the last digit required (i.e. the digit that may be rounded up).
     * rm: the rounding mode.
     * id: 1 (toExponential) or 2 (toPrecision).
     */
    function format(n, i, rm, id) {
      var c0, e, ne, len, str;

      if (rm == null) rm = ROUNDING_MODE;
      else intCheck(rm, 0, 8);

      if (!n.c) return n.toString();

      c0 = n.c[0];
      ne = n.e;

      if (i == null) {
        str = coeffToString(n.c);
        str = id == 1 || id == 2 && (ne <= TO_EXP_NEG || ne >= TO_EXP_POS)
         ? toExponential(str, ne)
         : toFixedPoint(str, ne, '0');
      } else {
        n = round(new BigNumber(n), i, rm);

        // n.e may have changed if the value was rounded up.
        e = n.e;

        str = coeffToString(n.c);
        len = str.length;

        // toPrecision returns exponential notation if the number of significant digits
        // specified is less than the number of digits necessary to represent the integer
        // part of the value in fixed-point notation.

        // Exponential notation.
        if (id == 1 || id == 2 && (i <= e || e <= TO_EXP_NEG)) {

          // Append zeros?
          for (; len < i; str += '0', len++);
          str = toExponential(str, e);

        // Fixed-point notation.
        } else {
          i -= ne;
          str = toFixedPoint(str, e, '0');

          // Append zeros?
          if (e + 1 > len) {
            if (--i > 0) for (str += '.'; i--; str += '0');
          } else {
            i += e - len;
            if (i > 0) {
              if (e + 1 == len) str += '.';
              for (; i--; str += '0');
            }
          }
        }
      }

      return n.s < 0 && c0 ? '-' + str : str;
    }


    // Handle BigNumber.max and BigNumber.min.
    function maxOrMin(args, method) {
      var n,
        i = 1,
        m = new BigNumber(args[0]);

      for (; i < args.length; i++) {
        n = new BigNumber(args[i]);

        // If any number is NaN, return NaN.
        if (!n.s) {
          m = n;
          break;
        } else if (method.call(m, n)) {
          m = n;
        }
      }

      return m;
    }


    /*
     * Strip trailing zeros, calculate base 10 exponent and check against MIN_EXP and MAX_EXP.
     * Called by minus, plus and times.
     */
    function normalise(n, c, e) {
      var i = 1,
        j = c.length;

       // Remove trailing zeros.
      for (; !c[--j]; c.pop());

      // Calculate the base 10 exponent. First get the number of digits of c[0].
      for (j = c[0]; j >= 10; j /= 10, i++);

      // Overflow?
      if ((e = i + e * LOG_BASE - 1) > MAX_EXP) {

        // Infinity.
        n.c = n.e = null;

      // Underflow?
      } else if (e < MIN_EXP) {

        // Zero.
        n.c = [n.e = 0];
      } else {
        n.e = e;
        n.c = c;
      }

      return n;
    }


    // Handle values that fail the validity test in BigNumber.
    parseNumeric = (function () {
      var basePrefix = /^(-?)0([xbo])(?=\w[\w.]*$)/i,
        dotAfter = /^([^.]+)\.$/,
        dotBefore = /^\.([^.]+)$/,
        isInfinityOrNaN = /^-?(Infinity|NaN)$/,
        whitespaceOrPlus = /^\s*\+(?=[\w.])|^\s+|\s+$/g;

      return function (x, str, isNum, b) {
        var base,
          s = isNum ? str : str.replace(whitespaceOrPlus, '');

        // No exception on ±Infinity or NaN.
        if (isInfinityOrNaN.test(s)) {
          x.s = isNaN(s) ? null : s < 0 ? -1 : 1;
        } else {
          if (!isNum) {

            // basePrefix = /^(-?)0([xbo])(?=\w[\w.]*$)/i
            s = s.replace(basePrefix, function (m, p1, p2) {
              base = (p2 = p2.toLowerCase()) == 'x' ? 16 : p2 == 'b' ? 2 : 8;
              return !b || b == base ? p1 : m;
            });

            if (b) {
              base = b;

              // E.g. '1.' to '1', '.1' to '0.1'
              s = s.replace(dotAfter, '$1').replace(dotBefore, '0.$1');
            }

            if (str != s) return new BigNumber(s, base);
          }

          // '[BigNumber Error] Not a number: {n}'
          // '[BigNumber Error] Not a base {b} number: {n}'
          if (BigNumber.DEBUG) {
            throw Error
              (bignumberError + 'Not a' + (b ? ' base ' + b : '') + ' number: ' + str);
          }

          // NaN
          x.s = null;
        }

        x.c = x.e = null;
      }
    })();


    /*
     * Round x to sd significant digits using rounding mode rm. Check for over/under-flow.
     * If r is truthy, it is known that there are more digits after the rounding digit.
     */
    function round(x, sd, rm, r) {
      var d, i, j, k, n, ni, rd,
        xc = x.c,
        pows10 = POWS_TEN;

      // if x is not Infinity or NaN...
      if (xc) {

        // rd is the rounding digit, i.e. the digit after the digit that may be rounded up.
        // n is a base 1e14 number, the value of the element of array x.c containing rd.
        // ni is the index of n within x.c.
        // d is the number of digits of n.
        // i is the index of rd within n including leading zeros.
        // j is the actual index of rd within n (if < 0, rd is a leading zero).
        out: {

          // Get the number of digits of the first element of xc.
          for (d = 1, k = xc[0]; k >= 10; k /= 10, d++);
          i = sd - d;

          // If the rounding digit is in the first element of xc...
          if (i < 0) {
            i += LOG_BASE;
            j = sd;
            n = xc[ni = 0];

            // Get the rounding digit at index j of n.
            rd = n / pows10[d - j - 1] % 10 | 0;
          } else {
            ni = mathceil((i + 1) / LOG_BASE);

            if (ni >= xc.length) {

              if (r) {

                // Needed by sqrt.
                for (; xc.length <= ni; xc.push(0));
                n = rd = 0;
                d = 1;
                i %= LOG_BASE;
                j = i - LOG_BASE + 1;
              } else {
                break out;
              }
            } else {
              n = k = xc[ni];

              // Get the number of digits of n.
              for (d = 1; k >= 10; k /= 10, d++);

              // Get the index of rd within n.
              i %= LOG_BASE;

              // Get the index of rd within n, adjusted for leading zeros.
              // The number of leading zeros of n is given by LOG_BASE - d.
              j = i - LOG_BASE + d;

              // Get the rounding digit at index j of n.
              rd = j < 0 ? 0 : n / pows10[d - j - 1] % 10 | 0;
            }
          }

          r = r || sd < 0 ||

          // Are there any non-zero digits after the rounding digit?
          // The expression  n % pows10[d - j - 1]  returns all digits of n to the right
          // of the digit at j, e.g. if n is 908714 and j is 2, the expression gives 714.
           xc[ni + 1] != null || (j < 0 ? n : n % pows10[d - j - 1]);

          r = rm < 4
           ? (rd || r) && (rm == 0 || rm == (x.s < 0 ? 3 : 2))
           : rd > 5 || rd == 5 && (rm == 4 || r || rm == 6 &&

            // Check whether the digit to the left of the rounding digit is odd.
            ((i > 0 ? j > 0 ? n / pows10[d - j] : 0 : xc[ni - 1]) % 10) & 1 ||
             rm == (x.s < 0 ? 8 : 7));

          if (sd < 1 || !xc[0]) {
            xc.length = 0;

            if (r) {

              // Convert sd to decimal places.
              sd -= x.e + 1;

              // 1, 0.1, 0.01, 0.001, 0.0001 etc.
              xc[0] = pows10[(LOG_BASE - sd % LOG_BASE) % LOG_BASE];
              x.e = -sd || 0;
            } else {

              // Zero.
              xc[0] = x.e = 0;
            }

            return x;
          }

          // Remove excess digits.
          if (i == 0) {
            xc.length = ni;
            k = 1;
            ni--;
          } else {
            xc.length = ni + 1;
            k = pows10[LOG_BASE - i];

            // E.g. 56700 becomes 56000 if 7 is the rounding digit.
            // j > 0 means i > number of leading zeros of n.
            xc[ni] = j > 0 ? mathfloor(n / pows10[d - j] % pows10[j]) * k : 0;
          }

          // Round up?
          if (r) {

            for (; ;) {

              // If the digit to be rounded up is in the first element of xc...
              if (ni == 0) {

                // i will be the length of xc[0] before k is added.
                for (i = 1, j = xc[0]; j >= 10; j /= 10, i++);
                j = xc[0] += k;
                for (k = 1; j >= 10; j /= 10, k++);

                // if i != k the length has increased.
                if (i != k) {
                  x.e++;
                  if (xc[0] == BASE) xc[0] = 1;
                }

                break;
              } else {
                xc[ni] += k;
                if (xc[ni] != BASE) break;
                xc[ni--] = 0;
                k = 1;
              }
            }
          }

          // Remove trailing zeros.
          for (i = xc.length; xc[--i] === 0; xc.pop());
        }

        // Overflow? Infinity.
        if (x.e > MAX_EXP) {
          x.c = x.e = null;

        // Underflow? Zero.
        } else if (x.e < MIN_EXP) {
          x.c = [x.e = 0];
        }
      }

      return x;
    }


    function valueOf(n) {
      var str,
        e = n.e;

      if (e === null) return n.toString();

      str = coeffToString(n.c);

      str = e <= TO_EXP_NEG || e >= TO_EXP_POS
        ? toExponential(str, e)
        : toFixedPoint(str, e, '0');

      return n.s < 0 ? '-' + str : str;
    }


    // PROTOTYPE/INSTANCE METHODS


    /*
     * Return a new BigNumber whose value is the absolute value of this BigNumber.
     */
    P.absoluteValue = P.abs = function () {
      var x = new BigNumber(this);
      if (x.s < 0) x.s = 1;
      return x;
    };


    /*
     * Return
     *   1 if the value of this BigNumber is greater than the value of BigNumber(y, b),
     *   -1 if the value of this BigNumber is less than the value of BigNumber(y, b),
     *   0 if they have the same value,
     *   or null if the value of either is NaN.
     */
    P.comparedTo = function (y, b) {
      return compare(this, new BigNumber(y, b));
    };


    /*
     * If dp is undefined or null or true or false, return the number of decimal places of the
     * value of this BigNumber, or null if the value of this BigNumber is ±Infinity or NaN.
     *
     * Otherwise, if dp is a number, return a new BigNumber whose value is the value of this
     * BigNumber rounded to a maximum of dp decimal places using rounding mode rm, or
     * ROUNDING_MODE if rm is omitted.
     *
     * [dp] {number} Decimal places: integer, 0 to MAX inclusive.
     * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
     *
     * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {dp|rm}'
     */
    P.decimalPlaces = P.dp = function (dp, rm) {
      var c, n, v,
        x = this;

      if (dp != null) {
        intCheck(dp, 0, MAX);
        if (rm == null) rm = ROUNDING_MODE;
        else intCheck(rm, 0, 8);

        return round(new BigNumber(x), dp + x.e + 1, rm);
      }

      if (!(c = x.c)) return null;
      n = ((v = c.length - 1) - bitFloor(this.e / LOG_BASE)) * LOG_BASE;

      // Subtract the number of trailing zeros of the last number.
      if (v = c[v]) for (; v % 10 == 0; v /= 10, n--);
      if (n < 0) n = 0;

      return n;
    };


    /*
     *  n / 0 = I
     *  n / N = N
     *  n / I = 0
     *  0 / n = 0
     *  0 / 0 = N
     *  0 / N = N
     *  0 / I = 0
     *  N / n = N
     *  N / 0 = N
     *  N / N = N
     *  N / I = N
     *  I / n = I
     *  I / 0 = I
     *  I / N = N
     *  I / I = N
     *
     * Return a new BigNumber whose value is the value of this BigNumber divided by the value of
     * BigNumber(y, b), rounded according to DECIMAL_PLACES and ROUNDING_MODE.
     */
    P.dividedBy = P.div = function (y, b) {
      return div(this, new BigNumber(y, b), DECIMAL_PLACES, ROUNDING_MODE);
    };


    /*
     * Return a new BigNumber whose value is the integer part of dividing the value of this
     * BigNumber by the value of BigNumber(y, b).
     */
    P.dividedToIntegerBy = P.idiv = function (y, b) {
      return div(this, new BigNumber(y, b), 0, 1);
    };


    /*
     * Return a BigNumber whose value is the value of this BigNumber exponentiated by n.
     *
     * If m is present, return the result modulo m.
     * If n is negative round according to DECIMAL_PLACES and ROUNDING_MODE.
     * If POW_PRECISION is non-zero and m is not present, round to POW_PRECISION using ROUNDING_MODE.
     *
     * The modular power operation works efficiently when x, n, and m are integers, otherwise it
     * is equivalent to calculating x.exponentiatedBy(n).modulo(m) with a POW_PRECISION of 0.
     *
     * n {number|string|BigNumber} The exponent. An integer.
     * [m] {number|string|BigNumber} The modulus.
     *
     * '[BigNumber Error] Exponent not an integer: {n}'
     */
    P.exponentiatedBy = P.pow = function (n, m) {
      var half, isModExp, i, k, more, nIsBig, nIsNeg, nIsOdd, y,
        x = this;

      n = new BigNumber(n);

      // Allow NaN and ±Infinity, but not other non-integers.
      if (n.c && !n.isInteger()) {
        throw Error
          (bignumberError + 'Exponent not an integer: ' + valueOf(n));
      }

      if (m != null) m = new BigNumber(m);

      // Exponent of MAX_SAFE_INTEGER is 15.
      nIsBig = n.e > 14;

      // If x is NaN, ±Infinity, ±0 or ±1, or n is ±Infinity, NaN or ±0.
      if (!x.c || !x.c[0] || x.c[0] == 1 && !x.e && x.c.length == 1 || !n.c || !n.c[0]) {

        // The sign of the result of pow when x is negative depends on the evenness of n.
        // If +n overflows to ±Infinity, the evenness of n would be not be known.
        y = new BigNumber(Math.pow(+valueOf(x), nIsBig ? 2 - isOdd(n) : +valueOf(n)));
        return m ? y.mod(m) : y;
      }

      nIsNeg = n.s < 0;

      if (m) {

        // x % m returns NaN if abs(m) is zero, or m is NaN.
        if (m.c ? !m.c[0] : !m.s) return new BigNumber(NaN);

        isModExp = !nIsNeg && x.isInteger() && m.isInteger();

        if (isModExp) x = x.mod(m);

      // Overflow to ±Infinity: >=2**1e10 or >=1.0000024**1e15.
      // Underflow to ±0: <=0.79**1e10 or <=0.9999975**1e15.
      } else if (n.e > 9 && (x.e > 0 || x.e < -1 || (x.e == 0
        // [1, 240000000]
        ? x.c[0] > 1 || nIsBig && x.c[1] >= 24e7
        // [80000000000000]  [99999750000000]
        : x.c[0] < 8e13 || nIsBig && x.c[0] <= 9999975e7))) {

        // If x is negative and n is odd, k = -0, else k = 0.
        k = x.s < 0 && isOdd(n) ? -0 : 0;

        // If x >= 1, k = ±Infinity.
        if (x.e > -1) k = 1 / k;

        // If n is negative return ±0, else return ±Infinity.
        return new BigNumber(nIsNeg ? 1 / k : k);

      } else if (POW_PRECISION) {

        // Truncating each coefficient array to a length of k after each multiplication
        // equates to truncating significant digits to POW_PRECISION + [28, 41],
        // i.e. there will be a minimum of 28 guard digits retained.
        k = mathceil(POW_PRECISION / LOG_BASE + 2);
      }

      if (nIsBig) {
        half = new BigNumber(0.5);
        if (nIsNeg) n.s = 1;
        nIsOdd = isOdd(n);
      } else {
        i = Math.abs(+valueOf(n));
        nIsOdd = i % 2;
      }

      y = new BigNumber(ONE);

      // Performs 54 loop iterations for n of 9007199254740991.
      for (; ;) {

        if (nIsOdd) {
          y = y.times(x);
          if (!y.c) break;

          if (k) {
            if (y.c.length > k) y.c.length = k;
          } else if (isModExp) {
            y = y.mod(m);    //y = y.minus(div(y, m, 0, MODULO_MODE).times(m));
          }
        }

        if (i) {
          i = mathfloor(i / 2);
          if (i === 0) break;
          nIsOdd = i % 2;
        } else {
          n = n.times(half);
          round(n, n.e + 1, 1);

          if (n.e > 14) {
            nIsOdd = isOdd(n);
          } else {
            i = +valueOf(n);
            if (i === 0) break;
            nIsOdd = i % 2;
          }
        }

        x = x.times(x);

        if (k) {
          if (x.c && x.c.length > k) x.c.length = k;
        } else if (isModExp) {
          x = x.mod(m);    //x = x.minus(div(x, m, 0, MODULO_MODE).times(m));
        }
      }

      if (isModExp) return y;
      if (nIsNeg) y = ONE.div(y);

      return m ? y.mod(m) : k ? round(y, POW_PRECISION, ROUNDING_MODE, more) : y;
    };


    /*
     * Return a new BigNumber whose value is the value of this BigNumber rounded to an integer
     * using rounding mode rm, or ROUNDING_MODE if rm is omitted.
     *
     * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
     *
     * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {rm}'
     */
    P.integerValue = function (rm) {
      var n = new BigNumber(this);
      if (rm == null) rm = ROUNDING_MODE;
      else intCheck(rm, 0, 8);
      return round(n, n.e + 1, rm);
    };


    /*
     * Return true if the value of this BigNumber is equal to the value of BigNumber(y, b),
     * otherwise return false.
     */
    P.isEqualTo = P.eq = function (y, b) {
      return compare(this, new BigNumber(y, b)) === 0;
    };


    /*
     * Return true if the value of this BigNumber is a finite number, otherwise return false.
     */
    P.isFinite = function () {
      return !!this.c;
    };


    /*
     * Return true if the value of this BigNumber is greater than the value of BigNumber(y, b),
     * otherwise return false.
     */
    P.isGreaterThan = P.gt = function (y, b) {
      return compare(this, new BigNumber(y, b)) > 0;
    };


    /*
     * Return true if the value of this BigNumber is greater than or equal to the value of
     * BigNumber(y, b), otherwise return false.
     */
    P.isGreaterThanOrEqualTo = P.gte = function (y, b) {
      return (b = compare(this, new BigNumber(y, b))) === 1 || b === 0;

    };


    /*
     * Return true if the value of this BigNumber is an integer, otherwise return false.
     */
    P.isInteger = function () {
      return !!this.c && bitFloor(this.e / LOG_BASE) > this.c.length - 2;
    };


    /*
     * Return true if the value of this BigNumber is less than the value of BigNumber(y, b),
     * otherwise return false.
     */
    P.isLessThan = P.lt = function (y, b) {
      return compare(this, new BigNumber(y, b)) < 0;
    };


    /*
     * Return true if the value of this BigNumber is less than or equal to the value of
     * BigNumber(y, b), otherwise return false.
     */
    P.isLessThanOrEqualTo = P.lte = function (y, b) {
      return (b = compare(this, new BigNumber(y, b))) === -1 || b === 0;
    };


    /*
     * Return true if the value of this BigNumber is NaN, otherwise return false.
     */
    P.isNaN = function () {
      return !this.s;
    };


    /*
     * Return true if the value of this BigNumber is negative, otherwise return false.
     */
    P.isNegative = function () {
      return this.s < 0;
    };


    /*
     * Return true if the value of this BigNumber is positive, otherwise return false.
     */
    P.isPositive = function () {
      return this.s > 0;
    };


    /*
     * Return true if the value of this BigNumber is 0 or -0, otherwise return false.
     */
    P.isZero = function () {
      return !!this.c && this.c[0] == 0;
    };


    /*
     *  n - 0 = n
     *  n - N = N
     *  n - I = -I
     *  0 - n = -n
     *  0 - 0 = 0
     *  0 - N = N
     *  0 - I = -I
     *  N - n = N
     *  N - 0 = N
     *  N - N = N
     *  N - I = N
     *  I - n = I
     *  I - 0 = I
     *  I - N = N
     *  I - I = N
     *
     * Return a new BigNumber whose value is the value of this BigNumber minus the value of
     * BigNumber(y, b).
     */
    P.minus = function (y, b) {
      var i, j, t, xLTy,
        x = this,
        a = x.s;

      y = new BigNumber(y, b);
      b = y.s;

      // Either NaN?
      if (!a || !b) return new BigNumber(NaN);

      // Signs differ?
      if (a != b) {
        y.s = -b;
        return x.plus(y);
      }

      var xe = x.e / LOG_BASE,
        ye = y.e / LOG_BASE,
        xc = x.c,
        yc = y.c;

      if (!xe || !ye) {

        // Either Infinity?
        if (!xc || !yc) return xc ? (y.s = -b, y) : new BigNumber(yc ? x : NaN);

        // Either zero?
        if (!xc[0] || !yc[0]) {

          // Return y if y is non-zero, x if x is non-zero, or zero if both are zero.
          return yc[0] ? (y.s = -b, y) : new BigNumber(xc[0] ? x :

           // IEEE 754 (2008) 6.3: n - n = -0 when rounding to -Infinity
           ROUNDING_MODE == 3 ? -0 : 0);
        }
      }

      xe = bitFloor(xe);
      ye = bitFloor(ye);
      xc = xc.slice();

      // Determine which is the bigger number.
      if (a = xe - ye) {

        if (xLTy = a < 0) {
          a = -a;
          t = xc;
        } else {
          ye = xe;
          t = yc;
        }

        t.reverse();

        // Prepend zeros to equalise exponents.
        for (b = a; b--; t.push(0));
        t.reverse();
      } else {

        // Exponents equal. Check digit by digit.
        j = (xLTy = (a = xc.length) < (b = yc.length)) ? a : b;

        for (a = b = 0; b < j; b++) {

          if (xc[b] != yc[b]) {
            xLTy = xc[b] < yc[b];
            break;
          }
        }
      }

      // x < y? Point xc to the array of the bigger number.
      if (xLTy) t = xc, xc = yc, yc = t, y.s = -y.s;

      b = (j = yc.length) - (i = xc.length);

      // Append zeros to xc if shorter.
      // No need to add zeros to yc if shorter as subtract only needs to start at yc.length.
      if (b > 0) for (; b--; xc[i++] = 0);
      b = BASE - 1;

      // Subtract yc from xc.
      for (; j > a;) {

        if (xc[--j] < yc[j]) {
          for (i = j; i && !xc[--i]; xc[i] = b);
          --xc[i];
          xc[j] += BASE;
        }

        xc[j] -= yc[j];
      }

      // Remove leading zeros and adjust exponent accordingly.
      for (; xc[0] == 0; xc.splice(0, 1), --ye);

      // Zero?
      if (!xc[0]) {

        // Following IEEE 754 (2008) 6.3,
        // n - n = +0  but  n - n = -0  when rounding towards -Infinity.
        y.s = ROUNDING_MODE == 3 ? -1 : 1;
        y.c = [y.e = 0];
        return y;
      }

      // No need to check for Infinity as +x - +y != Infinity && -x - -y != Infinity
      // for finite x and y.
      return normalise(y, xc, ye);
    };


    /*
     *   n % 0 =  N
     *   n % N =  N
     *   n % I =  n
     *   0 % n =  0
     *  -0 % n = -0
     *   0 % 0 =  N
     *   0 % N =  N
     *   0 % I =  0
     *   N % n =  N
     *   N % 0 =  N
     *   N % N =  N
     *   N % I =  N
     *   I % n =  N
     *   I % 0 =  N
     *   I % N =  N
     *   I % I =  N
     *
     * Return a new BigNumber whose value is the value of this BigNumber modulo the value of
     * BigNumber(y, b). The result depends on the value of MODULO_MODE.
     */
    P.modulo = P.mod = function (y, b) {
      var q, s,
        x = this;

      y = new BigNumber(y, b);

      // Return NaN if x is Infinity or NaN, or y is NaN or zero.
      if (!x.c || !y.s || y.c && !y.c[0]) {
        return new BigNumber(NaN);

      // Return x if y is Infinity or x is zero.
      } else if (!y.c || x.c && !x.c[0]) {
        return new BigNumber(x);
      }

      if (MODULO_MODE == 9) {

        // Euclidian division: q = sign(y) * floor(x / abs(y))
        // r = x - qy    where  0 <= r < abs(y)
        s = y.s;
        y.s = 1;
        q = div(x, y, 0, 3);
        y.s = s;
        q.s *= s;
      } else {
        q = div(x, y, 0, MODULO_MODE);
      }

      y = x.minus(q.times(y));

      // To match JavaScript %, ensure sign of zero is sign of dividend.
      if (!y.c[0] && MODULO_MODE == 1) y.s = x.s;

      return y;
    };


    /*
     *  n * 0 = 0
     *  n * N = N
     *  n * I = I
     *  0 * n = 0
     *  0 * 0 = 0
     *  0 * N = N
     *  0 * I = N
     *  N * n = N
     *  N * 0 = N
     *  N * N = N
     *  N * I = N
     *  I * n = I
     *  I * 0 = N
     *  I * N = N
     *  I * I = I
     *
     * Return a new BigNumber whose value is the value of this BigNumber multiplied by the value
     * of BigNumber(y, b).
     */
    P.multipliedBy = P.times = function (y, b) {
      var c, e, i, j, k, m, xcL, xlo, xhi, ycL, ylo, yhi, zc,
        base, sqrtBase,
        x = this,
        xc = x.c,
        yc = (y = new BigNumber(y, b)).c;

      // Either NaN, ±Infinity or ±0?
      if (!xc || !yc || !xc[0] || !yc[0]) {

        // Return NaN if either is NaN, or one is 0 and the other is Infinity.
        if (!x.s || !y.s || xc && !xc[0] && !yc || yc && !yc[0] && !xc) {
          y.c = y.e = y.s = null;
        } else {
          y.s *= x.s;

          // Return ±Infinity if either is ±Infinity.
          if (!xc || !yc) {
            y.c = y.e = null;

          // Return ±0 if either is ±0.
          } else {
            y.c = [0];
            y.e = 0;
          }
        }

        return y;
      }

      e = bitFloor(x.e / LOG_BASE) + bitFloor(y.e / LOG_BASE);
      y.s *= x.s;
      xcL = xc.length;
      ycL = yc.length;

      // Ensure xc points to longer array and xcL to its length.
      if (xcL < ycL) zc = xc, xc = yc, yc = zc, i = xcL, xcL = ycL, ycL = i;

      // Initialise the result array with zeros.
      for (i = xcL + ycL, zc = []; i--; zc.push(0));

      base = BASE;
      sqrtBase = SQRT_BASE;

      for (i = ycL; --i >= 0;) {
        c = 0;
        ylo = yc[i] % sqrtBase;
        yhi = yc[i] / sqrtBase | 0;

        for (k = xcL, j = i + k; j > i;) {
          xlo = xc[--k] % sqrtBase;
          xhi = xc[k] / sqrtBase | 0;
          m = yhi * xlo + xhi * ylo;
          xlo = ylo * xlo + ((m % sqrtBase) * sqrtBase) + zc[j] + c;
          c = (xlo / base | 0) + (m / sqrtBase | 0) + yhi * xhi;
          zc[j--] = xlo % base;
        }

        zc[j] = c;
      }

      if (c) {
        ++e;
      } else {
        zc.splice(0, 1);
      }

      return normalise(y, zc, e);
    };


    /*
     * Return a new BigNumber whose value is the value of this BigNumber negated,
     * i.e. multiplied by -1.
     */
    P.negated = function () {
      var x = new BigNumber(this);
      x.s = -x.s || null;
      return x;
    };


    /*
     *  n + 0 = n
     *  n + N = N
     *  n + I = I
     *  0 + n = n
     *  0 + 0 = 0
     *  0 + N = N
     *  0 + I = I
     *  N + n = N
     *  N + 0 = N
     *  N + N = N
     *  N + I = N
     *  I + n = I
     *  I + 0 = I
     *  I + N = N
     *  I + I = I
     *
     * Return a new BigNumber whose value is the value of this BigNumber plus the value of
     * BigNumber(y, b).
     */
    P.plus = function (y, b) {
      var t,
        x = this,
        a = x.s;

      y = new BigNumber(y, b);
      b = y.s;

      // Either NaN?
      if (!a || !b) return new BigNumber(NaN);

      // Signs differ?
       if (a != b) {
        y.s = -b;
        return x.minus(y);
      }

      var xe = x.e / LOG_BASE,
        ye = y.e / LOG_BASE,
        xc = x.c,
        yc = y.c;

      if (!xe || !ye) {

        // Return ±Infinity if either ±Infinity.
        if (!xc || !yc) return new BigNumber(a / 0);

        // Either zero?
        // Return y if y is non-zero, x if x is non-zero, or zero if both are zero.
        if (!xc[0] || !yc[0]) return yc[0] ? y : new BigNumber(xc[0] ? x : a * 0);
      }

      xe = bitFloor(xe);
      ye = bitFloor(ye);
      xc = xc.slice();

      // Prepend zeros to equalise exponents. Faster to use reverse then do unshifts.
      if (a = xe - ye) {
        if (a > 0) {
          ye = xe;
          t = yc;
        } else {
          a = -a;
          t = xc;
        }

        t.reverse();
        for (; a--; t.push(0));
        t.reverse();
      }

      a = xc.length;
      b = yc.length;

      // Point xc to the longer array, and b to the shorter length.
      if (a - b < 0) t = yc, yc = xc, xc = t, b = a;

      // Only start adding at yc.length - 1 as the further digits of xc can be ignored.
      for (a = 0; b;) {
        a = (xc[--b] = xc[b] + yc[b] + a) / BASE | 0;
        xc[b] = BASE === xc[b] ? 0 : xc[b] % BASE;
      }

      if (a) {
        xc = [a].concat(xc);
        ++ye;
      }

      // No need to check for zero, as +x + +y != 0 && -x + -y != 0
      // ye = MAX_EXP + 1 possible
      return normalise(y, xc, ye);
    };


    /*
     * If sd is undefined or null or true or false, return the number of significant digits of
     * the value of this BigNumber, or null if the value of this BigNumber is ±Infinity or NaN.
     * If sd is true include integer-part trailing zeros in the count.
     *
     * Otherwise, if sd is a number, return a new BigNumber whose value is the value of this
     * BigNumber rounded to a maximum of sd significant digits using rounding mode rm, or
     * ROUNDING_MODE if rm is omitted.
     *
     * sd {number|boolean} number: significant digits: integer, 1 to MAX inclusive.
     *                     boolean: whether to count integer-part trailing zeros: true or false.
     * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
     *
     * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {sd|rm}'
     */
    P.precision = P.sd = function (sd, rm) {
      var c, n, v,
        x = this;

      if (sd != null && sd !== !!sd) {
        intCheck(sd, 1, MAX);
        if (rm == null) rm = ROUNDING_MODE;
        else intCheck(rm, 0, 8);

        return round(new BigNumber(x), sd, rm);
      }

      if (!(c = x.c)) return null;
      v = c.length - 1;
      n = v * LOG_BASE + 1;

      if (v = c[v]) {

        // Subtract the number of trailing zeros of the last element.
        for (; v % 10 == 0; v /= 10, n--);

        // Add the number of digits of the first element.
        for (v = c[0]; v >= 10; v /= 10, n++);
      }

      if (sd && x.e + 1 > n) n = x.e + 1;

      return n;
    };


    /*
     * Return a new BigNumber whose value is the value of this BigNumber shifted by k places
     * (powers of 10). Shift to the right if n > 0, and to the left if n < 0.
     *
     * k {number} Integer, -MAX_SAFE_INTEGER to MAX_SAFE_INTEGER inclusive.
     *
     * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {k}'
     */
    P.shiftedBy = function (k) {
      intCheck(k, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER);
      return this.times('1e' + k);
    };


    /*
     *  sqrt(-n) =  N
     *  sqrt(N) =  N
     *  sqrt(-I) =  N
     *  sqrt(I) =  I
     *  sqrt(0) =  0
     *  sqrt(-0) = -0
     *
     * Return a new BigNumber whose value is the square root of the value of this BigNumber,
     * rounded according to DECIMAL_PLACES and ROUNDING_MODE.
     */
    P.squareRoot = P.sqrt = function () {
      var m, n, r, rep, t,
        x = this,
        c = x.c,
        s = x.s,
        e = x.e,
        dp = DECIMAL_PLACES + 4,
        half = new BigNumber('0.5');

      // Negative/NaN/Infinity/zero?
      if (s !== 1 || !c || !c[0]) {
        return new BigNumber(!s || s < 0 && (!c || c[0]) ? NaN : c ? x : 1 / 0);
      }

      // Initial estimate.
      s = Math.sqrt(+valueOf(x));

      // Math.sqrt underflow/overflow?
      // Pass x to Math.sqrt as integer, then adjust the exponent of the result.
      if (s == 0 || s == 1 / 0) {
        n = coeffToString(c);
        if ((n.length + e) % 2 == 0) n += '0';
        s = Math.sqrt(+n);
        e = bitFloor((e + 1) / 2) - (e < 0 || e % 2);

        if (s == 1 / 0) {
          n = '1e' + e;
        } else {
          n = s.toExponential();
          n = n.slice(0, n.indexOf('e') + 1) + e;
        }

        r = new BigNumber(n);
      } else {
        r = new BigNumber(s + '');
      }

      // Check for zero.
      // r could be zero if MIN_EXP is changed after the this value was created.
      // This would cause a division by zero (x/t) and hence Infinity below, which would cause
      // coeffToString to throw.
      if (r.c[0]) {
        e = r.e;
        s = e + dp;
        if (s < 3) s = 0;

        // Newton-Raphson iteration.
        for (; ;) {
          t = r;
          r = half.times(t.plus(div(x, t, dp, 1)));

          if (coeffToString(t.c).slice(0, s) === (n = coeffToString(r.c)).slice(0, s)) {

            // The exponent of r may here be one less than the final result exponent,
            // e.g 0.0009999 (e-4) --> 0.001 (e-3), so adjust s so the rounding digits
            // are indexed correctly.
            if (r.e < e) --s;
            n = n.slice(s - 3, s + 1);

            // The 4th rounding digit may be in error by -1 so if the 4 rounding digits
            // are 9999 or 4999 (i.e. approaching a rounding boundary) continue the
            // iteration.
            if (n == '9999' || !rep && n == '4999') {

              // On the first iteration only, check to see if rounding up gives the
              // exact result as the nines may infinitely repeat.
              if (!rep) {
                round(t, t.e + DECIMAL_PLACES + 2, 0);

                if (t.times(t).eq(x)) {
                  r = t;
                  break;
                }
              }

              dp += 4;
              s += 4;
              rep = 1;
            } else {

              // If rounding digits are null, 0{0,4} or 50{0,3}, check for exact
              // result. If not, then there are further digits and m will be truthy.
              if (!+n || !+n.slice(1) && n.charAt(0) == '5') {

                // Truncate to the first rounding digit.
                round(r, r.e + DECIMAL_PLACES + 2, 1);
                m = !r.times(r).eq(x);
              }

              break;
            }
          }
        }
      }

      return round(r, r.e + DECIMAL_PLACES + 1, ROUNDING_MODE, m);
    };


    /*
     * Return a string representing the value of this BigNumber in exponential notation and
     * rounded using ROUNDING_MODE to dp fixed decimal places.
     *
     * [dp] {number} Decimal places. Integer, 0 to MAX inclusive.
     * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
     *
     * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {dp|rm}'
     */
    P.toExponential = function (dp, rm) {
      if (dp != null) {
        intCheck(dp, 0, MAX);
        dp++;
      }
      return format(this, dp, rm, 1);
    };


    /*
     * Return a string representing the value of this BigNumber in fixed-point notation rounding
     * to dp fixed decimal places using rounding mode rm, or ROUNDING_MODE if rm is omitted.
     *
     * Note: as with JavaScript's number type, (-0).toFixed(0) is '0',
     * but e.g. (-0.00001).toFixed(0) is '-0'.
     *
     * [dp] {number} Decimal places. Integer, 0 to MAX inclusive.
     * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
     *
     * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {dp|rm}'
     */
    P.toFixed = function (dp, rm) {
      if (dp != null) {
        intCheck(dp, 0, MAX);
        dp = dp + this.e + 1;
      }
      return format(this, dp, rm);
    };


    /*
     * Return a string representing the value of this BigNumber in fixed-point notation rounded
     * using rm or ROUNDING_MODE to dp decimal places, and formatted according to the properties
     * of the format or FORMAT object (see BigNumber.set).
     *
     * The formatting object may contain some or all of the properties shown below.
     *
     * FORMAT = {
     *   prefix: '',
     *   groupSize: 3,
     *   secondaryGroupSize: 0,
     *   groupSeparator: ',',
     *   decimalSeparator: '.',
     *   fractionGroupSize: 0,
     *   fractionGroupSeparator: '\xA0',      // non-breaking space
     *   suffix: ''
     * };
     *
     * [dp] {number} Decimal places. Integer, 0 to MAX inclusive.
     * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
     * [format] {object} Formatting options. See FORMAT pbject above.
     *
     * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {dp|rm}'
     * '[BigNumber Error] Argument not an object: {format}'
     */
    P.toFormat = function (dp, rm, format) {
      var str,
        x = this;

      if (format == null) {
        if (dp != null && rm && typeof rm == 'object') {
          format = rm;
          rm = null;
        } else if (dp && typeof dp == 'object') {
          format = dp;
          dp = rm = null;
        } else {
          format = FORMAT;
        }
      } else if (typeof format != 'object') {
        throw Error
          (bignumberError + 'Argument not an object: ' + format);
      }

      str = x.toFixed(dp, rm);

      if (x.c) {
        var i,
          arr = str.split('.'),
          g1 = +format.groupSize,
          g2 = +format.secondaryGroupSize,
          groupSeparator = format.groupSeparator || '',
          intPart = arr[0],
          fractionPart = arr[1],
          isNeg = x.s < 0,
          intDigits = isNeg ? intPart.slice(1) : intPart,
          len = intDigits.length;

        if (g2) i = g1, g1 = g2, g2 = i, len -= i;

        if (g1 > 0 && len > 0) {
          i = len % g1 || g1;
          intPart = intDigits.substr(0, i);
          for (; i < len; i += g1) intPart += groupSeparator + intDigits.substr(i, g1);
          if (g2 > 0) intPart += groupSeparator + intDigits.slice(i);
          if (isNeg) intPart = '-' + intPart;
        }

        str = fractionPart
         ? intPart + (format.decimalSeparator || '') + ((g2 = +format.fractionGroupSize)
          ? fractionPart.replace(new RegExp('\\d{' + g2 + '}\\B', 'g'),
           '$&' + (format.fractionGroupSeparator || ''))
          : fractionPart)
         : intPart;
      }

      return (format.prefix || '') + str + (format.suffix || '');
    };


    /*
     * Return an array of two BigNumbers representing the value of this BigNumber as a simple
     * fraction with an integer numerator and an integer denominator.
     * The denominator will be a positive non-zero value less than or equal to the specified
     * maximum denominator. If a maximum denominator is not specified, the denominator will be
     * the lowest value necessary to represent the number exactly.
     *
     * [md] {number|string|BigNumber} Integer >= 1, or Infinity. The maximum denominator.
     *
     * '[BigNumber Error] Argument {not an integer|out of range} : {md}'
     */
    P.toFraction = function (md) {
      var d, d0, d1, d2, e, exp, n, n0, n1, q, r, s,
        x = this,
        xc = x.c;

      if (md != null) {
        n = new BigNumber(md);

        // Throw if md is less than one or is not an integer, unless it is Infinity.
        if (!n.isInteger() && (n.c || n.s !== 1) || n.lt(ONE)) {
          throw Error
            (bignumberError + 'Argument ' +
              (n.isInteger() ? 'out of range: ' : 'not an integer: ') + valueOf(n));
        }
      }

      if (!xc) return new BigNumber(x);

      d = new BigNumber(ONE);
      n1 = d0 = new BigNumber(ONE);
      d1 = n0 = new BigNumber(ONE);
      s = coeffToString(xc);

      // Determine initial denominator.
      // d is a power of 10 and the minimum max denominator that specifies the value exactly.
      e = d.e = s.length - x.e - 1;
      d.c[0] = POWS_TEN[(exp = e % LOG_BASE) < 0 ? LOG_BASE + exp : exp];
      md = !md || n.comparedTo(d) > 0 ? (e > 0 ? d : n1) : n;

      exp = MAX_EXP;
      MAX_EXP = 1 / 0;
      n = new BigNumber(s);

      // n0 = d1 = 0
      n0.c[0] = 0;

      for (; ;)  {
        q = div(n, d, 0, 1);
        d2 = d0.plus(q.times(d1));
        if (d2.comparedTo(md) == 1) break;
        d0 = d1;
        d1 = d2;
        n1 = n0.plus(q.times(d2 = n1));
        n0 = d2;
        d = n.minus(q.times(d2 = d));
        n = d2;
      }

      d2 = div(md.minus(d0), d1, 0, 1);
      n0 = n0.plus(d2.times(n1));
      d0 = d0.plus(d2.times(d1));
      n0.s = n1.s = x.s;
      e = e * 2;

      // Determine which fraction is closer to x, n0/d0 or n1/d1
      r = div(n1, d1, e, ROUNDING_MODE).minus(x).abs().comparedTo(
          div(n0, d0, e, ROUNDING_MODE).minus(x).abs()) < 1 ? [n1, d1] : [n0, d0];

      MAX_EXP = exp;

      return r;
    };


    /*
     * Return the value of this BigNumber converted to a number primitive.
     */
    P.toNumber = function () {
      return +valueOf(this);
    };


    /*
     * Return a string representing the value of this BigNumber rounded to sd significant digits
     * using rounding mode rm or ROUNDING_MODE. If sd is less than the number of digits
     * necessary to represent the integer part of the value in fixed-point notation, then use
     * exponential notation.
     *
     * [sd] {number} Significant digits. Integer, 1 to MAX inclusive.
     * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
     *
     * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {sd|rm}'
     */
    P.toPrecision = function (sd, rm) {
      if (sd != null) intCheck(sd, 1, MAX);
      return format(this, sd, rm, 2);
    };


    /*
     * Return a string representing the value of this BigNumber in base b, or base 10 if b is
     * omitted. If a base is specified, including base 10, round according to DECIMAL_PLACES and
     * ROUNDING_MODE. If a base is not specified, and this BigNumber has a positive exponent
     * that is equal to or greater than TO_EXP_POS, or a negative exponent equal to or less than
     * TO_EXP_NEG, return exponential notation.
     *
     * [b] {number} Integer, 2 to ALPHABET.length inclusive.
     *
     * '[BigNumber Error] Base {not a primitive number|not an integer|out of range}: {b}'
     */
    P.toString = function (b) {
      var str,
        n = this,
        s = n.s,
        e = n.e;

      // Infinity or NaN?
      if (e === null) {
        if (s) {
          str = 'Infinity';
          if (s < 0) str = '-' + str;
        } else {
          str = 'NaN';
        }
      } else {
        if (b == null) {
          str = e <= TO_EXP_NEG || e >= TO_EXP_POS
           ? toExponential(coeffToString(n.c), e)
           : toFixedPoint(coeffToString(n.c), e, '0');
        } else if (b === 10) {
          n = round(new BigNumber(n), DECIMAL_PLACES + e + 1, ROUNDING_MODE);
          str = toFixedPoint(coeffToString(n.c), n.e, '0');
        } else {
          intCheck(b, 2, ALPHABET.length, 'Base');
          str = convertBase(toFixedPoint(coeffToString(n.c), e, '0'), 10, b, s, true);
        }

        if (s < 0 && n.c[0]) str = '-' + str;
      }

      return str;
    };


    /*
     * Return as toString, but do not accept a base argument, and include the minus sign for
     * negative zero.
     */
    P.valueOf = P.toJSON = function () {
      return valueOf(this);
    };


    P._isBigNumber = true;

    if (configObject != null) BigNumber.set(configObject);

    return BigNumber;
  }


  // PRIVATE HELPER FUNCTIONS

  // These functions don't need access to variables,
  // e.g. DECIMAL_PLACES, in the scope of the `clone` function above.


  function bitFloor(n) {
    var i = n | 0;
    return n > 0 || n === i ? i : i - 1;
  }


  // Return a coefficient array as a string of base 10 digits.
  function coeffToString(a) {
    var s, z,
      i = 1,
      j = a.length,
      r = a[0] + '';

    for (; i < j;) {
      s = a[i++] + '';
      z = LOG_BASE - s.length;
      for (; z--; s = '0' + s);
      r += s;
    }

    // Determine trailing zeros.
    for (j = r.length; r.charCodeAt(--j) === 48;);

    return r.slice(0, j + 1 || 1);
  }


  // Compare the value of BigNumbers x and y.
  function compare(x, y) {
    var a, b,
      xc = x.c,
      yc = y.c,
      i = x.s,
      j = y.s,
      k = x.e,
      l = y.e;

    // Either NaN?
    if (!i || !j) return null;

    a = xc && !xc[0];
    b = yc && !yc[0];

    // Either zero?
    if (a || b) return a ? b ? 0 : -j : i;

    // Signs differ?
    if (i != j) return i;

    a = i < 0;
    b = k == l;

    // Either Infinity?
    if (!xc || !yc) return b ? 0 : !xc ^ a ? 1 : -1;

    // Compare exponents.
    if (!b) return k > l ^ a ? 1 : -1;

    j = (k = xc.length) < (l = yc.length) ? k : l;

    // Compare digit by digit.
    for (i = 0; i < j; i++) if (xc[i] != yc[i]) return xc[i] > yc[i] ^ a ? 1 : -1;

    // Compare lengths.
    return k == l ? 0 : k > l ^ a ? 1 : -1;
  }


  /*
   * Check that n is a primitive number, an integer, and in range, otherwise throw.
   */
  function intCheck(n, min, max, name) {
    if (n < min || n > max || n !== mathfloor(n)) {
      throw Error
       (bignumberError + (name || 'Argument') + (typeof n == 'number'
         ? n < min || n > max ? ' out of range: ' : ' not an integer: '
         : ' not a primitive number: ') + String(n));
    }
  }


  // Assumes finite n.
  function isOdd(n) {
    var k = n.c.length - 1;
    return bitFloor(n.e / LOG_BASE) == k && n.c[k] % 2 != 0;
  }


  function toExponential(str, e) {
    return (str.length > 1 ? str.charAt(0) + '.' + str.slice(1) : str) +
     (e < 0 ? 'e' : 'e+') + e;
  }


  function toFixedPoint(str, e, z) {
    var len, zs;

    // Negative exponent?
    if (e < 0) {

      // Prepend zeros.
      for (zs = z + '.'; ++e; zs += z);
      str = zs + str;

    // Positive exponent
    } else {
      len = str.length;

      // Append zeros.
      if (++e > len) {
        for (zs = z, e -= len; --e; zs += z);
        str += zs;
      } else if (e < len) {
        str = str.slice(0, e) + '.' + str.slice(e);
      }
    }

    return str;
  }


  // EXPORT


  BigNumber = clone();
  BigNumber['default'] = BigNumber.BigNumber = BigNumber;

  // AMD.
  if (typeof define == 'function' && define.amd) {
    define(function () { return BigNumber; });

  // Node.js and other environments that support module.exports.
  } else if (typeof module != 'undefined' && module.exports) {
    module.exports = BigNumber;

  // Browser.
  } else {
    if (!globalObject) {
      globalObject = typeof self != 'undefined' && self ? self : window;
    }

    globalObject.BigNumber = BigNumber;
  }
})(this);
;