/* Minification failed. Returning unminified contents.
(11739,19-20): run-time error JS1010: Expected identifier: .
(11739,19-20): run-time error JS1195: Expected expression: .
(11744,19-20): run-time error JS1010: Expected identifier: .
(11744,19-20): run-time error JS1195: Expected expression: .
(11749,19-20): run-time error JS1010: Expected identifier: .
(11749,19-20): run-time error JS1195: Expected expression: .
(11754,19-20): run-time error JS1010: Expected identifier: .
(11754,19-20): run-time error JS1195: Expected expression: .
 */
window.UNGM = {
    autocompletes: [],
    initialised: false,
    isAuthenticated: false,
    isVendor: false,
    ungmNumber: null,
    isRtl: false,
    mobileTabWidth: 768,
    siteRoot: null, // this should be set by a razor view via config
    clipBoardToolBarItems: ['Cut', 'Copy', 'Paste', '-', 'Undo', 'Redo', 'RemoveFormat', '-', 'HorizontalRule', '-', 'Scayt'],
    styleToolBarItems: ['Format', 'Bold', 'Italic', '-', 'NumberedList', 'BulletedList', '-', 'Outdent', 'Indent'],
    sourceToolBarItems: ['-', 'Source'],
    linkToolBarItems: ['-', 'Link', 'Unlink'],
    tableToolBarItems: ['-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'Table'],
    standardToolbar: null,
    standardToolbarWithSource: null,
    standardToolbarWithLinks: null,
    standardToolbarWithSourceLink: null,
    standardToolbarWithSourceLinkTable: null,
    init: function () {
        $.ajaxSetup({ cache: false }); // Disable caching of AJAX responses
        $("input").attr('autocomplete', 'off');
        $(document).ajaxError(UNGM.ajaxErrorHandler);
        $(document).ajaxSend(UNGM.ajaxSendHandler);
        
        UNGM.highlightMenuItem();
        UNGM.highlightTreeMenuItem();

        UNGM.initDatePickers();
        UNGM.initTabs();
        UNGM.initFilters();
        UNGM.initTopMenu();
        UNGM.initCollapsables();
        UNGM.initTextEditorToolBarItems();

        // Update the email count after a while to avoid the query to block other queries more important
        setTimeout(UNGM.updateInboxCount, 100);
        setTimeout(UNGM.updateUngmNumber, 100);

        UNGM.getUserLanguage();
        $(".liLanguage:not('.liLanguageSelected')").bind("click", UNGM.languageSelectClicked);
        $("#nav-icon-top-bar").bind("click", UNGM.openOrCloseNavigation);
        $("#btnMyNote").bind("click", UNGM.UserNoteClick);
        $("#cmdEndImpersonation, #cmdEndReadOnlyImpersonation").bind("click", UNGM.EndImpersonation);
        $(".track-document-download").off("click").on("click", function (e) {
            UNGM.gaEvent("DocumentDownload", "download", $(e.currentTarget).data("document-name"));
        });

        $(".logoHolder").bind("click", function () {
            window.location.href = UNGM.siteRoot;
        });

        $(".numericOnly").on("keydown", UNGM.ensureNumeric); //move any filter advanced buttons down to after the basic div

        $(".filterExpandHolder").each(function (index, selector) {
            $(selector).insertAfter($(selector).siblings(".filterBasic"));
        });


        // bind to the dialog open and close event to prevent bg scrolling
        $(document).bind("dialogopen open", ".ui-dialog", function (event, ui) {
            $('body').css('overflow', 'hidden');

            try {
                // Include validation on hidden fields (for invalid data on non-active tab)
                $.validator.setDefaults({ ignore: '' });
                //Parse the ajax-loaded content for stuff to validate
                window.UNGM.Validation.init();
                $.validator.unobtrusive.parse($(".ui-dialog"));
            }
            catch (e) { }
        });
        $(document).on("dialogclose close", ".ui-dialog", function (event, ui) {
            // Verify that there are no dialogs opened
            if ($(".ui-dialog:visible").length == 0) {
                $('body').css('overflow', 'scroll');
            }
        });

        UNGM.initialised = true;
        // This should mark all required fields in forms with a star *
        UNGM.markRequiredFields();
    },
    initTextEditorToolBarItems: function()
    {
        UNGM.standardToolbar = [UNGM.clipBoardToolBarItems, UNGM.styleToolBarItems];
        UNGM.standardToolbarWithSource = [UNGM.clipBoardToolBarItems.concat(UNGM.sourceToolBarItems), UNGM.styleToolBarItems];
        UNGM.standardToolbarWithLinks = [UNGM.clipBoardToolBarItems.concat(UNGM.linkToolBarItems), UNGM.styleToolBarItems];
        UNGM.standardToolbarWithSourceLink = [UNGM.clipBoardToolBarItems.concat(UNGM.sourceToolBarItems).concat(UNGM.linkToolBarItems), UNGM.styleToolBarItems];
        UNGM.standardToolbarWithSourceLinkTable = [UNGM.clipBoardToolBarItems.concat(UNGM.sourceToolBarItems).concat(UNGM.linkToolBarItems), UNGM.styleToolBarItems.concat(UNGM.tableToolBarItems)];
    },
    getDatepickerVal: function (selector) {
        var date = $(selector).datepicker("getDate");
        return $.datepicker.formatDate('yy-mm-dd', date);
    },
    parseLocalDateToDate: function (localDate) {
        var format = UNGM.currentuserCookieLanguage === 'zh' ? 'yy-M-dd' : UNGM.currentuserCookieLanguage === 'ru' ? 'dd.mm.yy' : 'dd-M-yy';
        return $.datepicker.parseDate(format, localDate);
    },
    formatLocalDate: function (date) {
        var format = UNGM.currentuserCookieLanguage === 'zh' ? 'yy-M-dd' : UNGM.currentuserCookieLanguage === 'ru' ? 'dd.mm.yy' : 'dd-M-yy';
        return $.datepicker.formatDate(format, date);
    },
    ensureNumeric: function (evt) {

        var character = String.fromCharCode(evt.which || evt.keyCode);
        var matches = character.match(/^[A-Z]+$/);

        if (matches && matches.length > 0 && !evt.shiftKey && !evt.ctrlKey && !evt.altKey) {
            evt.preventDefault();
            evt.stopPropagation();
            evt.cancel = true;
            return false;
        }
    },
    highlightMenuItem: function () {
        $("#left li a:not(.nav-content)").each(function () {
            var linkRef = $(this).attr("href");
            linkRef = linkRef.replace("/", "");
            if (UNGM.siteRoot + linkRef == window.location.href) {
                if ($(this).is("#my-tenders-link")) {
                    UNGM.loadLinksToEprocurementAppsForMyTenders();
                }
                $(this).addClass("current");
                if (UNGM.browsingWithInternetExplorer7()) {
                    // do the css stuff manually for the benefit of IE7
                    if ($(this).parent("li").children("ul.navLevel3").length > 0) {
                        // the selected item has a navlevel3 subdir
                        $(this).parent("li").children("ul.navLevel3").show();
                    } else if ($(this).parents("ul.navLevel3").length > 0) {
                        // the selected item is in a navlevel4 subdir
                        $(this).parents("ul.navLevel3").show();
                        $(this).parents("ul.navLevel4").show();
                        $(this).parent("li").children("ul.navLevel4").show();
                    }
                }
                // end of IE7 polyfill
                var l3parent = $(this).parents("ul.navLevel3:first");
                if (l3parent.length > 0) {
                    l3parent.prev().addClass("current");
                }

                var l4parent = $(this).parents("ul.navLevel4:first");
                if (l4parent.length > 0) {
                    l4parent.prev().addClass("current");
                }
            }
        });
    },
    highlightTreeMenuItem: function () {
        $("#left ul li.nav > a.nav-content").each(function () {
            var element = $(this);
            var link = UNGM.siteRoot + element.attr("href").replace("/", "");
            if (link == window.location.href)
            {
                element.parents(".nav").addClass("current");
            }
        });
    },
    loadLinksToEprocurementAppsForMyTenders: function () {
        UNGM.Throbber.Push();
        $.get("/Vendor/Menu/LinksToEprocurementAppsForMyTenders")
            .done(
                function (data) {
                    $("#my-tenders-link").next("ul").html(data);
                    UNGM.Throbber.Pop();
                });
    },
    EndImpersonation: function () {
        UNGM.throbOver($('body'));
        $.ajax({
            url: UNGM.siteRoot + 'Admin/Users/EndImpersonation',
            type: 'GET',
            success: UNGM.onImpersonationEnded
        });
    },
    onImpersonationEnded: function () {
        window.location.href = UNGM.siteRoot;
    },
    UserNoteClick: function () {
        UNGM.throbOver($("#wholePage"));
        $.ajax({
            url: UNGM.siteRoot + 'UNUser/MyNote',
            type: 'GET',
            async: false,
            success: UNGM.onNoteDialogLoaded
        });
    },
    onNoteDialogLoaded: function (data) {
        $("#myNoteContent").html(data);
        $("#myNoteContent").dialog({
            open: function() {
                $(this).css("maxHeight", 600);                
            },
            modal: true,
            width: '50%', 
            hide: { effect: 'fade', duration: 100 },
            show: { effect: 'fade', duration: 100 },
            close: function () {
                UNGM.UserNotes.ckEditorLoaded = false;
                // destroy the edit dialog. Not sure why I have to do this, but if I do not, it's duplicated if I reopen the notes functionality
                if ($("#EditNote").hasClass('ui-dialog-content')) {
                    $("#EditNote").dialog("destroy").remove();
                }
            }
        });
        UNGM.hideThrobber();
    },
    ajaxErrorHandler: function (event, xhr, options, exc) {
        UNGM.hideThrobber();
        // xhr status is reset to zero on page unload, so this should stop the error message if the user has just navigated away from the page during an ajax request.
        // unfortunately, Chrome also resets the xhr status for a timeout, so also have to check the statusText.
        if (xhr.status != 0 || xhr.statusText == "timeout") {

            var error = {
                ThrownError: exc,
                Status: xhr.status,
                StatusText: xhr.statusText,
                RequestedPage: options.url
            };

            $.confirm(
                UNGM.ErrorDialogTitle,
                UNGM.ErrorDialogMessage,
                UNGM.ErrorDialogBtnSendFeedback,
                UNGM.ErrorDialogBtnCancel,
                function () {
                }, //"yes" callback
                400 // the minimum width of the model dialog
            );
        }
    },
    ajaxSendHandler: function (event, xhr, settings) {
        try {
            ga.push(['_trackPageview', settings.url]);
        }
        catch (ex) { }
    },
    throbOver: function (elem) {

        // Per element... which means you have to get the right element 100 times
        /*var height = $(elem).outerHeight();
        var width = $(elem).outerWidth();
        var throbDiv = $("#mainThrobber");
        throbDiv.css("height", height + "px").css("width", width + "px");
        var position = $(elem).offset();
        throbDiv.css(position);
        throbDiv.show();*/

        // Or brute force... Whole page to make sure (note: CSS changes also required)
        $("#mainThrobber").fadeIn('fast');
    },
    hideThrobber: function () {
        $("#mainThrobber").fadeOut('fast');
    },
    initDatePickers: function () {
        if ($.datepicker) {
            var dateFormat = UNGM.currentuserCookieLanguage === 'zh' ? 'yy-M-dd' : UNGM.currentuserCookieLanguage === 'ru' ? 'dd.mm.yy' : 'dd-M-yy';
            $.datepicker.setDefaults($.datepicker.regional[$('html').attr('lang')]);
            $.datepicker.setDefaults({ dateFormat: dateFormat });
            $(".dateField").datepicker({
                onSelect: function () {
                    // Ensure the change event is fired on the underlying textbox
                    $(this).change();
                },
                changeYear: true,
                changeMonth: true
            });
        }
    },
    initDatePickersIn: function (container) {
        container.find(".dateField").datepicker({
            onSelect: function () {
                // Ensure the change event is fired on the underlying textbox
                $(this).change();
            },
            changeYear: true,
            changeMonth: true
        });
    },
    initTabs: function () {
        // if we're on a mobile device, change the ordering, so it goes tab - content - tab - content- etc, etc
        if ($(window).width() < UNGM.mobileTabWidth) {
            var numTabs = $(".tabHeader").length;
            for (var i = numTabs; i > 0; i--) {
                $(".tabBodies .tab:nth-child(" + i + ")").insertAfter(".tabHeaders .tabHeader:nth-child(" + i + ")");
            }
        }

        $(".tabHeader:not(.tabDisabled)").unbind("click").bind("click", UNGM.tabHeaderClicked);

        // Identify the tab to select by default
        var parameter = "#tab=";
        if (window.location.href.indexOf(parameter) !== -1) {
            // Index of parameter tab
            var index1 = window.location.href.indexOf(parameter) + parameter.length;

            // Index of next parameter, if any
            var index2 = window.location.href.indexOf("&", index1);
            if (index2 === -1) {
                // There are no more parameters, get the end of the URL
                index2 = window.location.href.length;
            }
            var tabId = window.location.href.substring(index1, index2);

            if (tabId.length) {
                var tabHeader;
                if (isNaN(tabId)) {
                    // The tab to select is an ID selector
                    tabHeader = $(".tabs .tabHeader#" + tabId);
                } else {
                    // The tab to select is the index of tabs
                    tabId = parseInt(tabId);
                    tabHeader = $(".tabs .tabHeader:nth-child(" + tabId + ")");
                }

                if (tabHeader.length) {
                    tabHeader.click();
                    return true;
                }
            }
        }

        $(".tabs").find(".tabHeader:first").click();
    },
    initTabsIn: function (container) {
        // if we're on a mobile device, change the ordering, so it goes tab - content - tab - content- etc, etc
        if ($(window).width() < UNGM.mobileTabWidth) {
            var numTabs = container.find(".tabHeader").length;
            for (var i = numTabs; i > 0; i--) {
                container.find(".tabBodies .tab:nth-child(" + i + ")").insertAfter(container.find(".tabHeaders .tabHeader:nth-child(" + i + ")"));
            }
        }
        container.off("click", ".tabHeader:not(.tabDisabled)", UNGM.tabHeaderClicked);
        container.on("click", ".tabHeader:not(.tabDisabled)", UNGM.tabHeaderClicked);
        container.find(".tabHeader:first").click();
    },
    tabHeaderClicked: function (evt) {
        var sender = $(evt.currentTarget);
        var parent = sender.closest(".tabs");
        var tabIndex = parent.find(".tabHeader").index(sender);
        
        var isMobileDevice = $(window).width() < UNGM.mobileTabWidth;
        //if we're on a mobile device, behave like an accordion
        if (isMobileDevice) {
            if (sender.hasClass("activeTab")) {
                sender.removeClass("activeTab");
                parent.find(".tab").eq(tabIndex).slideUp(400);
            }
            else {
                sender.addClass("activeTab");
                parent.find(".tab").eq(tabIndex).slideDown(400);
            }
        }
        else {
            var sender = $(evt.currentTarget);
            var parent = sender.closest(".tabs");
            parent.children(".tabHeaders").children(".activeTab").removeClass("activeTab");
            sender.addClass("activeTab");
            var tabIndex = parent.find(".tabHeader").index(sender);
            parent.children(".tabBodies").children(".tab").hide().eq(tabIndex).fadeIn('fast');
        }
        var jsName = parent.find(".tab").eq(tabIndex).find("#jsName").val();
        if (typeof jsName !== "undefined") {
            window["UNGM"][jsName]["init"]();
        }

        // Add GA on tab clicked
        var eventLabel = sender.data("galabel");
        var eventCategory = sender.parents(".tabs").data("gacategory");
        UNGM.gaEvent(eventCategory, "Tab click", eventLabel);

        // Second part: load content
        var tabElement;
        if (isMobileDevice) {
            tabElement = parent.find(".tab").eq(tabIndex);
        } else {
            tabElement = parent.children(".tabBodies").children(".tab").eq(tabIndex);
        }

        var url = tabElement.data("url");
        if (url === null || url === undefined || !url.length) {
            return true;
        }

        var reloadContent = tabElement.data("reload") === undefined || tabElement.data("reload") === true;
        if (!reloadContent) {
            return true;
        }
        tabElement.data("reload", false);
        UNGM.reloadTab(tabElement);
    },
    reloadTab: function(selector) {
        var tab = $(selector);
        UNGM.Throbber.Push();
        $.ajax({
            url: UNGM.siteRoot + tab.data('url'),
            type: 'GET',
            success: function (data) {
                tab.html(data);
                if (tab.find(".tabs").length) {
                    UNGM.initTabsIn(tab);
                }
                if (tab.find("form").length) {
                    UNGM.Validation.initForElement(tab.find("form"));
                    UNGM.markRequiredFields();
                }
                if (UNGM.tabContentReceivedCallback) {
                    UNGM.tabContentReceivedCallback(tab);
                }
            },
            complete: UNGM.Throbber.Pop
        });
    },
    gaEvent: function (eventCategory, eventAction, eventLabel) {
        try {
            if (eventLabel !== undefined && eventLabel !== null && eventCategory !== undefined && eventCategory !== null) {
                ga("send", "event", eventCategory, eventAction, eventLabel);
            }
        }
        catch (ex) { }
    },
    getCookie: function (name) {
        var i, x, y, ARRcookies = document.cookie.split(";");
        for (i = 0; i < ARRcookies.length; i++) {
            x = ARRcookies[i].substr(0, ARRcookies[i].indexOf("="));
            y = ARRcookies[i].substr(ARRcookies[i].indexOf("=") + 1);
            x = x.replace(/^\s+|\s+$/g, "");
            if (x == name) {
                return unescape(y);
                }
            }
    },
    cookiesEnabled: function () {
        document.cookie = "cookietest=1";
        var enabled = document.cookie.indexOf("cookietest=") != -1;
        document.cookie = "cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT";

        return enabled;
    },
    setUserLanguage: function (preferredLanguage) {
        if (preferredLanguage !== UNGM.currentuserCookieLanguage) {
            var postObject = {
                PreferredLanguage: preferredLanguage
            };
            
            $.ajax({
                url: UNGM.siteRoot + "Account/UserSettings/PreferredLanguage",
                type: 'POST',
                contentType: 'application/json',
                data: JSON.stringify(postObject),
                complete: function () {
                    window.location.reload();
                }
            });
        }
    },
    getUserLanguage: function () {
        // Read cookie and set language
        if (UNGM.currentuserCookieLanguage != '' && UNGM.currentuserCookieLanguage != UNGM.defaultLanguage) {
            UNGM.setUserLanguage(UNGM.currentuserCookieLanguage);
        } else if (UNGM.currentuserCookieLanguage === '') {
            // If there isn't any cookie, set language according to the accept-language of the browser
            // but only if the browser accepts cookies
            if (!UNGM.cookiesEnabled()) {
                alert('You must activate cookies to use www.ungm.org properly.');
                return;
            }

            $.ajax({
                url: UNGM.siteRoot + 'Account/UserSettings/LanguageSuggestion',
                type: 'GET',
                success: UNGM.onGotLanguageSuggestion
            });
        }
    },
    onGotLanguageSuggestion: function (response) {
        if (response === "" || response === undefined) {
            return false;
        }

        $("<div>").attr("id", "languageSuggestionModal").html(response).dialog({
            modal: true,
            title: 'Language preferences',
            width: '66%',
            height: 200,
            hide: { effect: 'fade', duration: 100 },
            show: { effect: 'fade', duration: 100 },
            open: function () {
                $(".btnAcceptLanguageSuggestion").bind("click", UNGM.acceptLanguageSuggestionClicked);
                $(".btnCancelLanguageSuggestion").bind("click", UNGM.cancelLanguageSuggestionClicked);
                $("#englishMessage").bind("click", UNGM.englishMessageClicked);
            }
        });
    },
    acceptLanguageSuggestionClicked: function (evt) {
        UNGM.throbOver($('body'));
        var sender = $(evt.currentTarget);
        var preferredLanguage = sender.attr("userLanguage");
        UNGM.setUserLanguage(preferredLanguage);
        $("#languageSuggestionModal").dialog("close");
    },
    cancelLanguageSuggestionClicked: function () {
        $("#languageSuggestionModal").dialog("close");
    },
    englishMessageClicked: function () {
        $(".suggestionMsg, .enMsg").toggle();
        $(".englishMessage").hide();
    },
    languageSelectClicked: function (evt) {
        UNGM.throbOver($('body'));
        var sender = $(evt.currentTarget);
        var preferredLanguage = sender.attr("id");

        // Change dropdown list of user settings if it's visible
        if ($("#PreferredLanguage").length) {
            $("#PreferredLanguage").val(preferredLanguage);
        }

        UNGM.setUserLanguage(preferredLanguage);
    },
    initFilters: function () {
        $(".expandFilter").off("click").on("click", UNGM.toggleAdvancedFilter);
        $(".expandAllFilter").off("click").on("click", UNGM.toggleAllFilter);
    },
    toggleAdvancedFilter: function (evt) {
        var sender = $(evt.currentTarget);
        var filter = sender.parents(".filter:first").find(".filterAdvanced");
        if (filter.is(":visible")) {
            filter.hide('fast');
            sender.attr("value", sender.data("invisibletext"));
        }
        else {
            filter.show('fast');
            sender.attr("value", sender.data("visibletext"));
        }
    },
    toggleAllFilter: function (evt) {
        var sender = $(evt.currentTarget);
        var filter = sender.parents(".filter:first").find(".filterAll");
        if (filter.is(":visible")) {
            filter.hide('fast');
            sender.attr("value", sender.data("invisibletext"));
        }
        else {
            filter.show('fast');
            sender.attr("value", sender.data("visibletext"));
        }
    },
    openMenu: function () {
        if (UNGM.isRtl) {
            $("#top").animate({
                right: "200px"
            }, { duration: 300, queue: false });
            $("#main").animate({
                right: "200px"
            }, { duration: 300, queue: false });
        } else {
            $("#top").animate({
                left: "200px"
            }, { duration: 300, queue: false });
            $("#main").animate({
                left: "200px"
            }, { duration: 300, queue: false });
        }
    },
    closeMenu: function () {
        if (UNGM.isRtl) {
            $("#top").animate({
                right: "0px"
            }, { duration: 180, queue: false });
            $("#main").animate({
                right: "0px"
            }, { duration: 180, queue: false });
        } else {
            $("#top").animate({
                left: "0px"
            }, { duration: 180, queue: false });
            $("#main").animate({
                left: "0px"
            }, { duration: 180, queue: false });
        }
    },
    openOrCloseNavigation: function (e) {
        e.preventDefault();
        var leftval = $("#main").css('left');

        if (leftval == "0px") {
            UNGM.openMenu();
        }
        else {
            UNGM.closeMenu();
        }
    },
    updateInboxCount: function () {
        if (UNGM.isAuthenticated === false) {
            return;
        }

        $.ajax({
            url: "/Account/Inbox/Count",
            type: 'GET',
            contentType: 'application/json',
            success: function (count) {
                if (count && count > 0) {
                    $("#inbox-count").css('visibility', 'visible').html(count);
                }
                else {
                    $("#inbox-count").css('visibility', 'hidden');
                }
            }
        });
    },
    updateUngmNumber: function () {
        if (UNGM.isVendor === false) {
            return;
        }

        if (UNGM.ungmNumber === null) {
            $.get("/Account/Account/MyUngmNumber", function (ungmNumber) {
                UNGM.ungmNumber = ungmNumber;
                $("#ungm-number-top-bar").html(UNGM.ungmNumber);
            });
        }
    },
    getQueryStringParam: function (name) {
        var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
        return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
    },
    markRequiredFields: function () {
        $("form input[type='datetime'], input[type='text'], input[type='password'], select, textarea").each(function () {
            if ($(this).data("val-required")) {
                try {
                    var label = $(this).siblings("label");
                    if (label.html().indexOf("*") < 0) {
                        label.html(label.html() + "&nbsp*");
                    }
                }
                catch (ex) { }
            }
            else if ($(this).data("val-requiredif")) {
                try {
                    var dependentPropertyName = $(this).data("val-requiredif-propertyname");
                    var desiredValue = $(this).data("val-requiredif-desiredvalue");
                    var dependentProperty = $("[name=" + dependentPropertyName + "]");

                    var label = $(this).siblings("label");
                    if (dependentProperty.val() == desiredValue) {
                        if (label.html().indexOf("*") < 0) {
                            label.html(label.html() + "&nbsp*");
                        }
                    }                    
                }
                catch (ex) { }
            }
        });

        $("form input.DocumentId").each(function () {
            if ($(this).data("val-required")) {
                var name = $(this).attr("name");
                var label = $("label[for=" + name + "]");
                if (label.html().indexOf("*") < 0) {
                    label.html(label.html() + "&nbsp*");
                }
            }
        });

        $('.radio-button-list .radio-button-list-item input[type="radio"][data-val-required]').each(function () {
            var input = $(this);
            var name = input.attr('name');
            var label = $('label[for="' + name + '"]');
            if (label.html() && (label.html().indexOf('*') === -1)) { 
                label.html(label.html() + '&nbsp*');
            }
        });
    },
    markAsRequiredFields: function (elements)
    {
        $(elements).each(function () {
            try {
                var label = $(this).siblings("label");
                if (label.html().indexOf("*") < 0) {
                    label.html(label.html() + "&nbsp*");
                }
            }
            catch (ex) { }
        });
    },
    serializeIncludingDisabled: function (element)
    {
        // Find disabled inputs, and remove the "disabled" attribute
        var disabled = element.find(':input:disabled').removeAttr('disabled');

        // serialize the form
        var serialized = element.serialize();

        // re-disabled the set of inputs that you previously enabled
        disabled.attr('disabled', 'disabled');

        return serialized;
    },
    initTopMenu: function () {
        $(".top-bar__dropdown").off("click").on("click", UNGM.topMenuDDClicked);
        $("body").off("click").on("click", function () {
            $(".top-bar__dropdown-options:visible").hide();
        });
    },
    topMenuDDClicked: function (evt) {
        var sender = $(evt.currentTarget);
        var options = sender.find(".top-bar__dropdown-options");
        if (options.is(":visible")) {
            options.slideUp('fast');
        }
        else {
            $(".top-bar__dropdown-options:visible").hide();
            options.slideDown('fast');
        }
        evt.stopPropagation();
    },
    ieHackTableHeader: function () {
        // IE7 hack to convert display:table to a table
        if (UNGM.browsingWithInternetExplorer7()) {
            $(".tableCell").wrap("<td style='padding:0px;' />");
            $(".tableRow").wrap("<tr />");
            $(".tableHead").wrap("<thead />");
            $(".tableBody").wrap("<tbody />");
            $(".table").wrapInner("<table style='width:100%' />");
            $(".tableCell.header").css("width", "100%");
            $(".tableCell.header").css("padding-left", "0px");
            $(".tableCell.header").css("padding-right", "0px");
        }
    },
    ieHackTableCells: function () {
        // IE7 hack to convert display:table to a table
        if (UNGM.browsingWithInternetExplorer7()) {
            $(".tableCell:not(.header):not(.done)").wrap("<td style='padding:0px; margin:0px;' />").addClass("done");
            $(".tableRow:not(.done)").wrap("<tr style='padding:0px; margin:0px; border:0px' />").addClass("done");
            $(".tableCell:not(.header)").css("border", "0px");
        }
    },
    initCollapsables: function () {
        $("fieldset legend.expandable").off("click").on("click", function (evt) {
            var sender = $(evt.currentTarget);
            var parent = sender.parent();
            if (parent.hasClass('collapsed')) {
                parent.removeClass('collapsed');
                sender.addClass('expanded');
            }
            else {
                parent.addClass('collapsed');
                sender.removeClass('expanded');
            }
        });
    },
    onRemoveSuggestAssistedRegistrationClicked: function (evt) {
        var sender = $(evt.currentTarget);
        var parent = sender.closest("#lnkAssistedRegistration");
        parent.remove();
    },
    initJQueryUIDialogWithCKEDITOR: function () {
        //
        // There is an issue with jquery ui working with ckeditor.
        // This part of code is needed as a workaround.
        //
        // For more information: see http://bugs.jqueryui.com/ticket/9087#comment:30
        //
        $.widget("ui.dialog", $.ui.dialog, {
            /*! jQuery UI - v1.10.2 - 2013-12-12
             *  http://bugs.jqueryui.com/ticket/9087#comment:27 - bugfix
             *  http://bugs.jqueryui.com/ticket/4727#comment:23 - bugfix
             *  allowInteraction fix to accommodate windowed editors
             */
            _allowInteraction: function (event) {
                if (this._super(event)) {
                    return true;
                }
                // address interaction issues with general iframes with the dialog
                if (event.target.ownerDocument != this.document[0]) {
                    return true;
                }
                // address interaction issues with dialog window
                if ($(event.target).closest(".cke_dialog").length) {
                    return true;
                }
                // address interaction issues with iframe based drop downs in IE
                if ($(event.target).closest(".cke").length) {
                    return true;
                }
            },
            /*! jQuery UI - v1.10.2 - 2013-10-28
             *  http://dev.ckeditor.com/ticket/10269 - bugfix
             *  moveToTop fix to accommodate windowed editors
             */
            _moveToTop: function (event, silent) {
                if (!event || !this.options.modal) {
                    this._super(event, silent);
                }
            }
        });
    },
    browsingWithInternetExplorer7: function () {
        var BrowserDetect = {
            init: function () {
                this.browser = this.searchString(this.dataBrowser) || "Other";
                this.version = this.searchVersion(navigator.userAgent) || this.searchVersion(navigator.appVersion) || "Unknown";
            },
            searchString: function (data) {
                for (var i = 0; i < data.length; i++) {
                    var dataString = data[i].string;
                    this.versionSearchString = data[i].subString;

                    if (dataString.indexOf(data[i].subString) !== -1) {
                        return data[i].identity;
                    }
                }
            },
            searchVersion: function (dataString) {
                var index = dataString.indexOf(this.versionSearchString);
                if (index === -1) {
                    return;
                }

                var rv = dataString.indexOf("rv:");
                if (this.versionSearchString === "Trident" && rv !== -1) {
                    return parseFloat(dataString.substring(rv + 3));
                } else {
                    return parseFloat(dataString.substring(index + this.versionSearchString.length + 1));
                }
            },
            dataBrowser: [
                { string: navigator.userAgent, subString: "Edge", identity: "MS Edge" },
                { string: navigator.userAgent, subString: "MSIE", identity: "Explorer" },
                { string: navigator.userAgent, subString: "Trident", identity: "Explorer" },
                { string: navigator.userAgent, subString: "Firefox", identity: "Firefox" },
                { string: navigator.userAgent, subString: "Opera", identity: "Opera" },
                { string: navigator.userAgent, subString: "OPR", identity: "Opera" },
                { string: navigator.userAgent, subString: "Chrome", identity: "Chrome" },
                { string: navigator.userAgent, subString: "Safari", identity: "Safari" }
            ]
        };
        BrowserDetect.init();
        return BrowserDetect.browser == "Explorer" && BrowserDetect.version == 7;
    },
    markTabComplete: function (elementSelector, complete) {
        var element = $(elementSelector);
        if (element.length === 0) {
            return false;
        }
        element.removeClass("tabComplete tabIncomplete");
        complete ? element.addClass("tabComplete") : element.addClass("tabIncomplete");
        return true;
    },
    appendErrorTo: function (element, response) {
        if (element.length) {
            element.append("<div class='error'>" + response.Message + "</div>");
        }
    },
    removeError: function () {
        $("div.error").remove();
    }
};

$.extend({
    confirm: function (title, message, yesText, noText, yesCallback, width) {
        var btns = new Array();

        if (yesText.length != 0) {
            btns.push({
                text: yesText,
                id: "btnDialogOK",
                click: function () {
                    yesCallback();
                    $(this).dialog('close');
                }
            });
        }
        if (noText.length != 0) {
            btns.push({
                text: noText,
                id: "btnDialogNo",
                click: function () {
                    $(this).dialog('close');
                }
            });
        }
        if (width == 0 || width == undefined) {
            width = 500;
        }
        $("<div id='errorPopup'></div>").dialog({
            buttons: btns,
            minWidth: width,
            minHeight: 160,
            close: function (event, ui) { $(this).remove(); },
            resizable: false,
            title: title,
            modal: true
        }).html(message).parent().addClass("alert");
    }
});

$.extend({
    confirmWithNoCallback: function (title, message, yesText, noText, yesCallback, noCallback, width) {
        var btns = new Array();

        if (yesText.length != 0) {
            btns.push({
                text: yesText,
                id: "btnDialogOK",
                click: function () {
                    yesCallback();
                    $(this).dialog('close');
                }
            });
        }
        if (noText.length != 0) {
            btns.push({
                text: noText,
                id: "btnDialogNo",
                click: function () {
                    noCallback();
                    $(this).dialog('close');
                }
            });
        }
        if (width == 0 || width == undefined) {
            width = 500;
        }
        $("<div id='errorPopup'></div>").dialog({
            buttons: btns,
            minWidth: width,
            minHeight: 160,
            close: function (event, ui) { $(this).remove(); },
            resizable: false,
            title: title,
            modal: true
        }).html(message).parent().addClass("alert");
    }
});

$(document).ready(function () {
    if (!UNGM.initialised) {
        UNGM.init();
    }
    if (jQuery && jQuery.validator) {
        jQuery.validator.setDefaults({
            ignore: ""
        }); // In the library the default for this is :hidden. We still want to validate hidden inputs for, e.g., the country selector so don't ignore anything.
    }
});


;
window.UNGM.Validation = {
    init: function () {
        $(".formRow input").each(function () {
            $(this).bind("focusin", UNGM.Validation.elementGotFocus);
            $(this).bind("focusout", UNGM.Validation.elementLostFocus);
        });
        $(".formRow select").each(function () {
            $(this).bind("focusin", UNGM.Validation.elementGotFocus);
            $(this).bind("focusout", UNGM.Validation.elementLostFocus);
        });
        $(".formRow textarea").each(function () {
            $(this).bind("focusin", UNGM.Validation.elementGotFocus);
            $(this).bind("focusout", UNGM.Validation.elementLostFocus);
        });

        $(".emailValidated").bind("focusout", UNGM.Validation.validateEmail);

        if ($.validator) {
            $.validator.unobtrusive.adapters.addBool("PasswordStrengthValidation");
            $.validator.addMethod("PasswordStrengthValidation", function (value, element, param) {
                var strength = UNGM.ManageAccount.getPasswordStrength(value);
                if (strength > 1) {
                    return true;
                }
                else {
                    return false;
                }
            });

            $.validator.unobtrusive.adapters.addBool("bodyText");
            $.validator.addMethod("bodyText", function (value, element) {
                // We need 2 forms of validation. First time around we need to check if the CKEditors are active
                if (CKEDITOR.instances.txtEnglishBody) {
                    for (var instanceName in CKEDITOR.instances) {
                        var theText = CKEDITOR.instances[instanceName].getData();
                        if (theText.length > 0) {
                            // If the body has text, the corresponding subject must also have text
                            var subject = $(CKEDITOR.instances[instanceName].element.$).parents(".tab:first").find("input.subjectText");
                            if (subject.val().length > 0) {
                                return true;
                            }
                        }
                    }
                }
                else {
                    // If the CKEditors aren't there then that means we have destroyed them (to fill the underlying textarea for POST) but the
                    // EditableList plugin will validate the form again so we need to return the right value here
                    var isValid = false;
                    $("textarea.bodyText").each(function () {
                        if ($(this).val().length > 0) {
                            // If the body has text, the corresponding subject must also have text
                            var subject = $(this).parents(".tab:first").find("input.subjectText");
                            if (subject.val().length > 0) {
                                // Just returning true directly will only exist the .each loop and not the validator function
                                isValid = true;
                            }
                        }
                    });

                    return isValid;
                }

                return false;

            }, "");

            $.validator.unobtrusive.adapters.addBool("DateFormatValidation");
            $.validator.addMethod("DateFormatValidation", function (value, element, param) {
                var expression = UNGM.Validation.dateFormatValidationRegEx(false);

                // Hack for Spanish March which has MS bug UNGMVRR-4717
                var culture = $('html').attr('lang');
                if (culture == "es" && value.toLowerCase().indexOf("-mar-") >= 0) {
                    value = value.replace("-mar-", "-marzo-");
                }

                if (value.trim().length == 0 || value.match(expression)) {
                    return true;
                }
                else {
                    return false;
                }
            });

            $.validator.unobtrusive.adapters.addBool("DateTimeFormatValidation");
            $.validator.addMethod("DateTimeFormatValidation", function (value, element, param) {
                var expression = UNGM.Validation.dateFormatValidationRegEx(true);

                // Hack for Spanish March which has MS bug UNGMVRR-4717
                var culture = $('html').attr('lang');
                if (culture == "es" && value.toLowerCase().indexOf("-mar-") >= 0) {
                    value = value.replace("-mar-", "-marzo-");
                }

                if (value.length == 0 || value.match(expression)) {
                    return true;
                }
                else {
                    return false;
                }
            });
            // START OF MY NEW DATE STUFF
            $.validator.addMethod('dateafter', function (value, element, param) {
                var dateVal = $("#" + element.id).datepicker("getDate");
                var minDate;
                var pastDateAllow = false;

                if (value.length < 1 && param.nullable)
                    return true;

                if (param.minSelector.length > 0) {
                    var minDateElement = $("#" + param.minSelector);
                    minDate = minDateElement.datepicker("getDate");
                }
                else {
                    var now = new Date();
                    minDate = new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), 0, 0, 0);
                }

                if (param.afterDateAllow.length > 0 && param.afterDateAllow.toLowerCase() == "true") {
                    var statusId = parseInt($("#Status").val());
                    if (statusId == param.afterDateAllowStatus) { // allow edit statusId pass from attribute
                        // get original date
                        var savedDate = new Date($(element).siblings("#SavedDate").val());
                        // if original date is not change, allow to edit.
                        if (Date.parse(dateVal) == Date.parse(savedDate)) {
                            pastDateAllow = true;
                        }
                    }
                }

                return minDate <= dateVal || minDate == null || pastDateAllow;
            });

            $.validator.addMethod('datebefore', function (value, element, param) {
                var dateVal = $("#" + element.id).datepicker("getDate");
                var maxDate;
                if (value.length < 1 && param.nullable)
                    return true;

                if (param.maxSelector.length > 0) {
                    var maxDateElement = $("#" + param.maxSelector);
                    maxDate = maxDateElement.datepicker("getDate");
                }
                else {
                    maxDate = new Date();
                    maxDate.setHours(23, 59, 59, 999);
                }

                return maxDate >= dateVal || maxDate == null;
            });

            // The adapter to support ASP.NET MVC unobtrusive validation
            $.validator.unobtrusive.adapters.add('dateafter', ['afterdatefield', 'nullable', 'allowdateafter', 'allowdateafterstatus'],
                function (options) {
                    var params = {
                        minSelector: options.params.afterdatefield,
                        nullable: options.params.nullable == "true",
                        afterDateAllow: options.params.allowdateafter,
                        afterDateAllowStatus: options.params.allowdateafterstatus
                    };

                    options.rules['dateafter'] = params;
                    if (options.message) {
                        options.messages['dateafter'] = options.message;
                    }
                });
            $.validator.unobtrusive.adapters.add('datebefore', ['beforedatefield', 'nullable'],
                function (options) {
                    var params = {
                        maxSelector: options.params.beforedatefield,
                        nullable: options.params.nullable == "true"
                    };

                    options.rules['datebefore'] = params;
                    if (options.message) {
                        options.messages['datebefore'] = options.message;
                    }
                });

            $.validator.unobtrusive.adapters.addBool("requiredif");
            $.validator.addMethod("requiredif", function (value, element, param) {
                if ($("#" + element.id).is(':radio')) {
                    value = $("[name='" + element.name + "']:checked").val();
                }

                var dependentEl = $("#" + element.id).data("val-requiredif-propertyname");
                var desiredValue = $("#" + element.id).data("val-requiredif-desiredvalue");
                // If desiredValue is a collection, it is sent as a string containing all the values separated by a comma
                var desiredValues = desiredValue != undefined ? desiredValue.split(",") : desiredValue;
                if (desiredValues != undefined && desiredValues.length) {
                    for (var i = 0; i < desiredValues.length; i++) {
                        desiredValues[i] = desiredValues[i].toLowerCase().trim();
                    }
                }

                var dependentElValue;
                if ($("[name='" + dependentEl + "']").is(':radio')) {
                    dependentElValue = $("[name='" + dependentEl + "']:checked").val();
                } else {
                    dependentElValue = $("[name='" + dependentEl + "']").val();
                }

                if (dependentElValue != undefined) {
                    dependentElValue = dependentElValue.toLowerCase();
                }
                
                if ($.inArray(dependentElValue, desiredValues) > -1 && (value === undefined || value == "")) {
                    return false;
                }
                else {
                    return true;
                }
            });
        }
        // END OF MY NEW DATE STUFF
    },
    initForElement: function (element) {
        element.find(".formRow input, .formRow select, .formRow textarea").each(function () {
            $(this).bind("focusin", UNGM.Validation.elementGotFocus);
            $(this).bind("focusout", UNGM.Validation.elementLostFocus);
        });
        if ($.validator) { $.validator.unobtrusive.parse(element); }
    },
    elementGotFocus: function (evt) {
        var sender = $(evt.currentTarget);
        sender.parents(".formRow").addClass('focus');
        sender.siblings(".field-validation-error").hide();
        sender.siblings(".validationIcon").hide();
        sender.siblings(".validationHint").css("visibility", "visible").css("display", "block");
    },
    elementLostFocus: function (evt) {
        var sender = $(evt.currentTarget);
        sender.parents(".formRow").removeClass('focus');
        sender.siblings(".validationHint").css("visibility", "hidden");
        UNGM.Validation.validateElement(sender);

        // Add validation to country autocompletes
        if (sender.hasClass("ui-autocomplete-input")) {
            var countryList = sender.siblings(".countryPicker,.countryTelephonePicker");
            if (countryList.length) {
                UNGM.Validation.validateElement(countryList);
            }
        }
        
        // Add country and fiscal code unique validation on account/vendor regisration
        if (sender.siblings(".duplicateOfCountryAndFiscalCode").length > 0) {
            if (sender.siblings(".duplicateOfCountryAndFiscalCode").is(":visible")) {
                UNGM.Validation.markElementInvalid(countryList);
            }
            else {
                UNGM.Validation.validateElement(countryList);
            }
        }
    },
    validateEmail: function (evt) {
        var sender = $(evt.currentTarget);
    },
    validateElement: function (element) {
        try {
            if (element.valid()) {
                UNGM.Validation.markElementValid(element);
            }
            else {
                UNGM.Validation.markElementInvalid(element);
            }
        }
        catch (ex) { }
    },
    markElementValid: function (element) {
        var exists = element.siblings(".validationIcon.valid");
        if (exists.length > 0) {
            exists.show();
        }
        else {
            var elem = $("<span class='validationIcon valid' />");
            element.after(elem);
        }
        element.siblings(".validationIcon.invalid").hide();
        element.siblings(".field-validation-error").hide();
        //element.siblings(".validationHint").css("visibility", "visible");
    },
    markElementInvalid: function (element) {
        var exists = element.siblings(".validationIcon.invalid");
        if (exists.length > 0) {
            exists.show();
        }
        else {
            var elem = $("<span class='validationIcon invalid' />");
            element.after(elem);
        }
        element.siblings(".validationIcon.valid").hide();
        element.siblings(".field-validation-error").css('visibility', 'visible').css("display", "block");
        element.siblings(".validationHint").css("display", "none");
    },
    dateFormatValidationRegEx: function (isDateTime) {
        var culture = $('html').attr('lang');
        var languageCode = UNGM.Validation.getLanguageCode(culture);

        var months = $.datepicker.regional[languageCode].monthNamesShort.join("|");
        var time = (isDateTime ? "\\s([01]?[0-9]|2[0-3]):[0-5][0-9]" : "");

        var str = culture === "zh"
            ? "^\\d{4}\-(" + months + ")\-(([0-2][0-9])|([3][0-1]))" + time + "$"
            : culture === "ru"
            ? "^(([0-2][0-9])|([3][0-1]))\.(0?[1-9]|1[012])\.\\d{4}" + time + "$"
            : culture === "ar"
            ? "^(([0-2][0-9])|([3][0-1]))\-([1-9]|1[012])\-\\d{4}" + time + "$"
            : "^(([0-2][0-9])|([3][0-1]))\-(" + months + ")\-\\d{4}" + time + "$";
        return new RegExp(str, "i");
    },
    getLanguageCode: function (culture) {
        var defaultLanguageCode = "en";
        var languageCodes = {
            "en": "en",
            "fr": "fr",
            "es": "es",
            "pt": "pt",
            "zh": "zh-CN",
            "ru": "ru",
            "ar": "ar"
        };
        return languageCodes[culture] || defaultLanguageCode;
    }
}

$(document).ready(function () {
    UNGM.Validation.init();
});


$(".dateField").datepicker();
UNGM.ManageAccount = {
    init: function () {
        $("#cmdChangeEmail").off("click").on("click", UNGM.ManageAccount.onChangeEmailClicked);
        $("#NewEmail").off("focus").on("focus", UNGM.ManageAccount.onEmailGotFocus);
        $("#NewPassword").off("keyup").on("keyup", UNGM.ManageAccount.onPasswordKeyUp);
    },
    onEmailGotFocus:function(){
        $('#UNGMEmailValidation').html('');
    },
    onChangeEmailClicked: function (evt) {
        //get the form
        var form = $(evt.currentTarget).parents('form');
        if (form.valid()) {
            UNGM.throbOver($("#changeEmailPartial"));
            var formData = { newEmail: unescape($("#NewEmail").val()) };
            $.ajax({
                url: UNGM.siteRoot + 'Account/Account/ChangeEmail',
                type: 'POST',
                contentType: 'application/json',
                data: JSON.stringify(formData),
                success: UNGM.ManageAccount.onEmailChangeRequestSubmitted,
                traditional: true
            });
        }
    },
    onEmailChangeRequestSubmitted: function (data) {
        if(data == 'success'){
            // Success, show success message
            $('#UNGMEmailValidation').html('');
            $.confirm(
                UNGM.ManageAccount.ChangeEmailConfirmedTitle, // Title
                UNGM.ManageAccount.ChangeEmailConfirmedMessage, // Message
                UNGM.ManageAccount.ChangeEmailConfirmedOK, //yes button text (blank hides button)
                "", //no button text (blank hides button)
                function () { } //"yes" callback
            );
            // clear the input field
            $("#NewEmail").val("");
        }
        else {
            // failure, show the validation error
            $('#UNGMEmailValidation').html("<span class='validationIcon invalid' />" + data);
        }
        UNGM.hideThrobber();
    },
    getPasswordStrength: function (password) {
        var score = 0;
        var lowerCase = password.match(/[a-z]/g);
        var upperCase = password.match(/[A-Z]/g);
        var digits = password.match(/\d/g);
        var nonAlpha = password.match(/[^\w\s]/g);
        var whitespace = password.match(/\s/);

        if (upperCase && lowerCase) {
            score += 1;
        }

        if (digits) {
            score += 1;
        }

        if (nonAlpha) {
            score += 1;
        }

        if (whitespace) {
            score += 1;
        }

        var length = password.length;
        if (length >= 8) {
            score += 10;
        }

        var strength = 0;
        if (score >= 12) {
            strength = 4;
        } else if (score >= 11) {
            strength = 3;
        } else if (score >= 10) {
            strength = 2;
        }

        return strength;
    },
    onPasswordKeyUp: function (evt) {
        classes= ['pw-very-weak', 'pw-weak', 'pw-mediocre', 'pw-strong', 'pw-very-strong'];
        texts = [UNGM.PasswordVeryWeak, UNGM.PasswordWeak, UNGM.PasswordMediocre, UNGM.PasswordStrong, UNGM.PasswordVeryStrong];
        var sender = $(evt.currentTarget);

        var strength = UNGM.ManageAccount.getPasswordStrength(sender.val());

        var strengthClass = classes[strength];
        var indicator = $('#' + sender.data('indicator'));
        indicator.removeClass(indicator.data('pwclass'));
        indicator.data('pwclass', strengthClass);
        indicator.addClass(strengthClass);
        indicator.find('.label').html(texts[strength]);
    }
};;
window.UNGM.ContractAwardSearch = {
    pageIndex: 0,
    prevPageIndex: 0,
    paging: false,
    timeoutFunc: null,
    selectedCountries: [],
    selectedAgencies: [],
    selectedAgenciesReadonly: false,
    selectedUNSPSCs: [],
    selectedUNSPSCsReadonly: false,
    sortField: "AwardDate",
    sortAscending: false,
    inSearch: false,
    pendingSearchRequest: null,
    init: function () {
        $("#txtContractAwardFilterTitle").bind("keyup", UNGM.ContractAwardSearch.filterKeyUp);
        $("#txtContractAwardFilterDesc").bind("keyup", UNGM.ContractAwardSearch.filterKeyUp);
        $("#txtContractAwardFilterRef").bind("keyup", UNGM.ContractAwardSearch.filterKeyUp);
        $("#txtContractAwardFilterSupplier").bind("keyup", UNGM.ContractAwardSearch.filterKeyUp);
        $("#txtContractAwardFrom").bind("change", UNGM.ContractAwardSearch.filterDateChange);
        $("#txtContractAwardTo").bind("change", UNGM.ContractAwardSearch.filterDateChange);

        // Sorting except FirstCountry
        $("#tblContractAwards .tableHead .tableRow .tableCell").not(".notsortable").on("click", UNGM.ContractAwardSearch.sort);

        // Search Selectors
        $("#selContractAwardCountry").bind("change", UNGM.ContractAwardSearch.onContractAwardCountrySelect);
        $("#selContractAwardAgency").bind("change", UNGM.ContractAwardSearch.onContractAwardAgencySelect);
        $("#lnkShowUNSPSC").on("click", UNGM.ContractAwardSearch.showUNSPSC);
        $("#lnkExpandUNSPSC").on("click", UNGM.ContractAwardSearch.expandUNSPSC);
        $("#lnkCollapseUNSPSC").on("click", UNGM.ContractAwardSearch.collapseUNSPSC);
        $("#contractAwardFilter #lnkShowUNSPSC").on("click", function () { UNGM.ContractAwardSearch.showUNSPSC(); });
        $("#contractAwardFilter #lnkExpandUNSPSC").on("click", UNGM.ContractAwardSearch.expandUNSPSC);
        $("#contractAwardFilter #lnkCollapseUNSPSC").on("click", UNGM.ContractAwardSearch.collapseUNSPSC);

        $("#isCountrySelected").on("change", UNGM.ContractAwardSearch.clearCountry);
        $("#isAgencySelected").on("change", UNGM.ContractAwardSearch.clearAgency);

        // Clear All
        $("#contractAwardFilter #lnkClearAll").on("click", UNGM.ContractAwardSearch.clearAll);

        // Set height to show loading for first time
        var contractDiv = $("#contractAwards");
        contractDiv.css("height", 150 + "px");
        // Run search
        UNGM.ContractAwardSearch.search();
        // Set height back to auto
        contractDiv.css("height", "auto");

        UNGM.ieHackTableHeader();
    },

    //#region Search Functions
    filterKeyUp: function () {
        if (UNGM.ContractAwardSearch.timeoutFunc) {
            clearTimeout(UNGM.ContractAwardSearch.timeoutFunc);
        }
        UNGM.ContractAwardSearch.pageIndex = 0;
        UNGM.ContractAwardSearch.paging = false;
        $.waypoints('destroy');
        UNGM.ContractAwardSearch.timeoutFunc = setTimeout("UNGM.ContractAwardSearch.search();", 300);
    },
    buildOptions: function () {
        var opts =
        {
            PageIndex: UNGM.ContractAwardSearch.pageIndex,
            PageSize: 15,
            Title: $("#txtContractAwardFilterTitle").val(),
            Description: $("#txtContractAwardFilterDesc").val(),
            Reference: $('#txtContractAwardFilterRef').val(),
            Supplier: $('#txtContractAwardFilterSupplier').val(),
            AwardFrom: $("#txtContractAwardFrom").val(),
            AwardTo: $("#txtContractAwardTo").val(),
            Countries: UNGM.ContractAwardSearch.selectedCountries,
            Agencies: UNGM.ContractAwardSearch.selectedAgencies,
            UNSPSCs: UNGM.ContractAwardSearch.selectedUNSPSCs,
            SortField: UNGM.ContractAwardSearch.sortField,
            SortAscending: UNGM.ContractAwardSearch.sortAscending
        };
        return opts;
    },
    sort: function (evt) {
        var sender = $(evt.currentTarget);
        var newSortField = sender.attr('id');
        if (UNGM.ContractAwardSearch.sortField == newSortField) {
            // 2 clicks on the same column means swap order
            UNGM.ContractAwardSearch.sortAscending = !UNGM.ContractAwardSearch.sortAscending;
        }
        else {
            // First time column is clicked sorts ASC by default
            UNGM.ContractAwardSearch.sortField = newSortField;
            UNGM.ContractAwardSearch.sortAscending = true;
        }
        $("#tblContractAwards .sortedAsc").removeClass("sortedAsc");
        $("#tblContractAwards .sortedDesc").removeClass("sortedDesc");

        var newClass = UNGM.ContractAwardSearch.sortAscending ? "sortedAsc" : "sortedDesc";
        sender.addClass(newClass);

        UNGM.ContractAwardSearch.PageAndSearch();
    },
    search: function () {
        if (UNGM.ContractAwardSearch.pendingSearchRequest) {
            UNGM.ContractAwardSearch.pendingSearchRequest.abort();
        }
        var opts = UNGM.ContractAwardSearch.buildOptions();
        var url = "";
        if (document.getElementById("txtContractAwardFilterSupplier") === null) {
            url = 'Public/ContractAward/SearchByVendor';
        } else {
            url = 'Public/ContractAward/PublicSearch';
        }
         
        UNGM.throbOver("#contractAwards");
        UNGM.ContractAwardSearch.pendingSearchRequest = $.ajax({
            url: UNGM.siteRoot + url,
            type: 'POST',
            contentType: 'application/json',
            data: JSON.stringify(opts),
            success: UNGM.ContractAwardSearch.onGotData
        });
    },
    PageAndSearch: function () {
        UNGM.ContractAwardSearch.pageIndex = 0;
        UNGM.ContractAwardSearch.paging = false;
        UNGM.ContractAwardSearch.search();
    },
    onGotData: function (data) {
        UNGM.ContractAwardSearch.prevPageIndex = UNGM.ContractAwardSearch.pageIndex;
        data = $.trim(data);
        var dataArray = $(data).toArray();
        var returnRows = $(dataArray).filter(".dataRow").length;
        if (returnRows < 1) {
            if (UNGM.ContractAwardSearch.paging) {
                UNGM.ContractAwardSearch.paging = false;
            }
            else {
                $("#contractAwardsEmpty").show();
                $("#tblContractAwards").hide();
                $("#contractAwardsTotal").hide();
            }
        }
        else {
            $("#tblContractAwards .tableBody").children("script").remove();
            if (UNGM.ContractAwardSearch.paging) {
                $("#tblContractAwards .tableBody").append(data);
                UNGM.ContractAwardSearch.paging = false;
            }
            else {
                $("#tblContractAwards .tableBody").html(data);
                $("#tblContractAwards").show();
                $("#contractAwardsTotal").show();
                $("#contractAwardsEmpty").hide();
            }
            var rows = $("#tblContractAwards").find(".tableRow").length - 1;
            $("#searchRows").html(rows);
            $("#contractAwardsTotal").show();
            $("#tblContractAwards").find(".tableBody .tableRow").off("click").on("click", UNGM.ContractAwardSearch.awardClicked);

            var contractAwardContext = null;
            var contractAwardOffset = null;

            var contextHeight = $('#contractAwards').height();
            var tableHeight = $('#tblContractAwards').height();
            if (contextHeight < tableHeight) {
                contractAwardOffset = -(tableHeight - contextHeight);
                contractAwardContext = $('#contractAwards');
            } else {
                contractAwardOffset = 'bottom-in-view';
            }

            $('#tblContractAwards').waypoint(function (direction) {
                if (direction === 'down' && !UNGM.ContractAwardSearch.inSearch) {
                    $.waypoints('destroy');
                    UNGM.ContractAwardSearch.pageIndex = UNGM.ContractAwardSearch.prevPageIndex + 1;
                    UNGM.ContractAwardSearch.paging = true;
                    UNGM.ContractAwardSearch.inSearch = true;
                    UNGM.ContractAwardSearch.search();
                }
            }, {
                context: contractAwardContext,
                offset: contractAwardOffset
            });
            UNGM.ContractAwardSearch.inSearch = false;
        }
        // Trigger country hidden to clear value
        $("#isCountrySelected").trigger("change");
        // Trigger agency hidden to clear value
        $("#isAgencySelected").trigger("change");
        UNGM.ieHackTableCells();
        UNGM.hideThrobber();
    },
    awardClicked: function (evt) {
        if (!$(evt.target).is("a")) {
            UNGM.throbOver("#notices");
            var id = $(evt.currentTarget).data("contractawardid");
            var isPicker = ($("#IsPicker").val() == 'True' ? "?isPicker=true" : "");
            $.ajax({
                url: UNGM.siteRoot + 'Public/ContractAward/Popup/' + id + isPicker,
                type: 'GET',
                success: UNGM.ContractAwardSearch.onGotAwardDetail
            });
        }
    },
    onGotAwardDetail: function (detail) {
        UNGM.hideThrobber();
        var winHeight = $(window).height();
        var detailElem = $(detail);
        var title = detailElem.find("#awardTitle").html();
        $("#contractAwardDetail").dialog({
            modal: true,
            open: function () {
                $("#contractAwardDetail").empty().append(detailElem);
                $("#contractAwardDetail .pickAward").bind("click", UNGM.ContractAwardSearch.pickAwardClicked);
            },
            title: title,
            width: '66%',
            height: winHeight - (winHeight * 0.2),
            hide: { effect: 'fade', duration: 100 },
            show: { effect: 'fade', duration: 100 }
        });

    },
    clearAll: function () {
        $("#txtContractAwardFilterTitle").val('');
        $("#txtContractAwardFilterDesc").val('');
        $("#txtContractAwardFilterRef").val('');
        $("#txtContractAwardFilterSupplier").val('');
        $("#txtContractAwardFrom").val('');
        $("#txtContractAwardTo").val('');
        UNGM.ContractAwardSearch.selectedCountries = [];
        // Clear selected countries div
        $("#selContractAwardCountry option").eq(0).attr('selected', 'selected');
        $("#selContractAwardCountry").parent(".filterRow").find('.ui-autocomplete-input').val("");
        $("#contractAwardSearchCountry").children("div").remove();
        $("#contractAwardSearchCountry").hide();

        if (!UNGM.ContractAwardSearch.selectedAgenciesReadonly) {
            // Clear selected agencies
            UNGM.ContractAwardSearch.selectedAgencies = [];
        $("#selContractAwardAgency option").eq(0).attr('selected', 'selected');
        $("#selContractAwardAgency").parent(".filterRow").find('.ui-autocomplete-input').val("");
        $("#contractAwardSearchAgency").children("div").remove();
        $("#contractAwardSearchAgency").hide();
        }

        if (!UNGM.ContractAwardSearch.selectedUNSPSCsReadonly) {
        // Clear selected UNSPSCs
            UNGM.ContractAwardSearch.selectedUNSPSCs = [];
        $("#contractAwardSearchUNSPSC").children("div").remove();
        $("#contractAwardSearchUNSPSC").hide();
        }
        // Clear contractAward type checkbox list
        $(".contractAwardTypeCheckboxList").removeAttr('checked');
        // Set pageing default
        UNGM.ContractAwardSearch.pageIndex = 0;
        UNGM.ContractAwardSearch.paging = false;
        UNGM.ContractAwardSearch.search();
    },
    //#endregion

    filterDateChange: function () {
        var dateFrom = $("#txtContractAwardFrom");
        var dateTo = $("#txtContractAwardTo");
        dateFrom.datepicker("option", "maxDate", dateTo.datepicker("getDate"));
        dateTo.datepicker("option", "minDate", dateFrom.datepicker("getDate"));
        UNGM.ContractAwardSearch.filterKeyUp();
    },

    //#region UNSPSC Functions
    showUNSPSC: function () {
        UNGM.UNSPSC.saveCallback = UNGM.ContractAwardSearch.unspscSaveCallback;
        //refresh the picker selection
        var picker = $("#contractAwardUNSPSCSelector .unspsc");
        UNGM.UNSPSC.bindSelectedCodes(picker);
        var winHeight = $(window).height();
        $("#contractAwardUNSPSCSelector").dialog({
            modal: true,
            open: function () {
            },
            title: UNGM.ContractAwardSearch.UNSPSCTitle,
            width: '66%',
            height: winHeight - (winHeight * 0.2),
            hide: { effect: 'fade', duration: 100 },
            show: { effect: 'fade', duration: 100 }
        });
    },
    expandUNSPSC: function () {
        $(".contractAwardSelectedUNSPSC").show();
        $("#lnkExpandUNSPSC").hide();
        $("#lnkCollapseUNSPSC").show();
    },
    collapseUNSPSC: function () {
        $(".contractAwardSelectedUNSPSC").hide();
        $("#lnkExpandUNSPSC").show();
        $("#lnkCollapseUNSPSC").hide();
    },
    unspscSaveCallback: function () {
        //Add selected UNSPSC to place holder
        var holder = $("#contractAwardSearchUNSPSC");
        holder.find("div").remove();
        var unspscId = null;
        var Code = null;
        var UNSPSCName = null;
        UNGM.ContractAwardSearch.selectedUNSPSCs = [];
        for (var i = 0; i < UNGM.UNSPSC.selectedCodes.length; i++) {
            unspscId = parseInt(UNGM.UNSPSC.selectedCodes[i].Id, 10);
            Code = UNGM.UNSPSC.selectedCodes[i].Code;
            UNSPSCName = UNGM.UNSPSC.selectedCodes[i].Name;
            UNGM.ContractAwardSearch.selectedUNSPSCs.push(unspscId);
            // Add to place holder
            UNGM.ContractAwardSearch.addItemToPlaceHolder(unspscId, UNSPSCName, Code, "#contractAwardSearchUNSPSC", 'contractAwardSelectedUNSPSC', 'contractAwardSelectedUNSPSC', UNGM.ContractAwardSearch.removeUNSPSCClicked);
        }

        if (UNGM.ContractAwardSearch.selectedUNSPSCs.length > 0) {
            holder.show();
        }
        else {
            holfer.hide();
        }

        $("#contractAwardUNSPSCSelector").dialog('close');
        UNGM.ContractAwardSearch.pageIndex = 0;
        UNGM.ContractAwardSearch.paging = false;
        UNGM.ContractAwardSearch.search();
    },
    removeUNSPSCClicked: function (evt) {
        var removedId = $(evt.currentTarget).data("Id")
        UNGM.UNSPSC.removeCode(removedId);
        UNGM.ContractAwardSearch.removeItemClicked(UNGM.ContractAwardSearch.selectedUNSPSCs, removedId, ".contractAwardSelectedUNSPSC", "#contractAwardSearchUNSPSC")
    },
    //#endregion

    //#region Agency Functions
    onContractAwardAgencySelect: function (evt) {
        var sender = $(evt.currentTarget);
        var agencyId = parseInt(sender.val());
        if (sender.val() == '') {
            return;
        }
        var agencyName = sender.children(':selected').text();

        UNGM.ContractAwardSearch.onItemSelect(agencyId, agencyName, UNGM.ContractAwardSearch.selectedAgencies, "#contractAwardSearchAgency", 'contractAwardSelectedAgency', 'contractAwardSelectedAgencyName', UNGM.ContractAwardSearch.removeAgencyClicked);
        $("#isAgencySelected").val(1);
    },
    removeAgencyClicked: function (evt) {
        UNGM.ContractAwardSearch.removeItemClicked(UNGM.ContractAwardSearch.selectedAgencies, $(evt.currentTarget).data("Id"), ".contractAwardSelectedAgency", "#contractAwardSearchAgency")
    },
    clearAgency: function (evt) {
        UNGM.ContractAwardSearch.clearSelection(evt, "#selContractAwardAgency");
    },
    setSelectedAgenciesReadonly: function (agencies) {
        agencies = (agencies instanceof Array) ? agencies : [agencies];
        UNGM.ContractAwardSearch.selectedAgencies = agencies;
        UNGM.ContractAwardSearch.selectedAgenciesReadonly = true;
    },

    //#endregion

    //#region Country Functions
    onContractAwardCountrySelect: function (evt) {
        var sender = $(evt.currentTarget);
        var countryId = sender.val();
        if (countryId == '') {
            return;
        }
        var countryName = sender.children(':selected').text();
        var selectedCountry = {
            Id: countryId,
            Name: countryName
        };
        UNGM.ContractAwardSearch.onItemSelect(countryId, countryName, UNGM.ContractAwardSearch.selectedCountries, "#contractAwardSearchCountry", 'contractAwardSelectedCountry', 'contractAwardSelectedCountryName', UNGM.ContractAwardSearch.removeCountryClicked);
        $("#isCountrySelected").val(1);
    },
    removeCountryClicked: function (evt) {
        UNGM.ContractAwardSearch.removeItemClicked(UNGM.ContractAwardSearch.selectedCountries, $(evt.currentTarget).data("Id"), ".contractAwardSelectedCountry", "#contractAwardSearchCountry")
    },
    clearCountry: function (evt) {
        UNGM.ContractAwardSearch.clearSelection(evt, "#selContractAwardCountry");
    },
    setSelectedUNSPSCsReadonly: function (unspscs) {
        unspscs = (unspscs instanceof Array) ? unspscs : [unspscs];
        unspscs.forEach(function (unspsc) {
            UNGM.ContractAwardSearch.selectedUNSPSCs.push(unspsc.Id);
            console.log(unspsc);
            UNGM.ContractAwardSearch.addItemToPlaceHolder(unspsc.Id, unspsc.Name, unspsc.Code, "#contractAwardSearchUNSPSC", "contractAwardSelectedUNSPSC", "contractAwardSelectedUNSPSC");
        });
        UNGM.ContractAwardSearch.selectedUNSPSCsReadonly = true;
        $(".contractAwardSelectedUNSPSC").hide();
    },
    //#endregion

    //#region Helper Functions
    onItemSelect: function (itemId, itemName, storedItemArray, holderClassName, selectedIdDiv, selectedNameSpan, removeCallback ) {
        // Do nothing if agency already in list
        if ($.grep(storedItemArray, function (value) { return value == itemId; }).length > 0) { return; };
        // Add selected agency to the array
        storedItemArray.push(itemId);
        //Add selected agency to place holder
        UNGM.ContractAwardSearch.addItemToPlaceHolder(itemId, itemName, null, holderClassName, selectedIdDiv, selectedNameSpan, removeCallback);

        if (storedItemArray.length > 0) {
            $(holderClassName).show();
        }
        else {
            $(holderClassName).hide();
        }
        // Set pageing default
        UNGM.ContractAwardSearch.PageAndSearch();
    },
    addItemToPlaceHolder: function (itemId, itemName, itemCode, holderClassName, selectedIdDiv, selectedNameSpan, removeCallback) {
        var holder = $(holderClassName);
        var elem = $("<div class='" + selectedIdDiv + "'></div>");
        elem.data("ID", itemId);
        var span = $("<span class='" + selectedNameSpan + "'></span>");
        span.html((itemCode !== null ? itemCode + " - " : "") + itemName);
        elem.append(span);
        if (removeCallback !== undefined) {
        var removeButton = $("#removeButtonTemplate").clone();
        removeButton.removeAttr("id").show();
        removeButton.data("Id", itemId);
        removeButton.bind("click", removeCallback);
            elem.append(removeButton);
        }
        holder.append(elem);
    },
    removeItemClicked: function (removedArray, removedId, selectedItemDivClass, SelectedItemsDivId) {
        removedArray.splice($.inArray(removedId, removedArray), 1);
        // Remove selected agency div from place holder
        $(selectedItemDivClass).filter(function () { return $(this).data("ID") == removedId }).remove();
        //hide selected UNSPSC div when all are removed.
        if (removedArray.length == 0) {
            $(SelectedItemsDivId).hide();
        }
        // Set pageing default
        UNGM.ContractAwardSearch.PageAndSearch()
    },
    clearSelection: function (evt, divToClear) {
        var change = $(evt.currentTarget).val();
        if (change == 1) {
            $(divToClear).parent(".filterRow").find('.ui-autocomplete-input').val("");
            $(this).val("0");
        }
    },
    setResultTableMaxHeight: function (height) {
        var contractAwards = $("#contractAwards");
        contractAwards.css("max-height", height);
        contractAwards.css("overflow", "auto");
    }
    //#endregion
}
;
(function ($) {
    var methods = {
        init: function (options) {

            if (!options.listUrl || !options.getEditUrl || !options.getInsertUrl || !options.postEditUrl || !options.postInsertUrl || !options.deleteUrl) {
                throw ("Invalid options for editableList plugin. See UNGM.EditableList.js for the minimum required set of options that must be supplied.");
            }

            settings = options;

            elements.container = this.eq(0);
            // save setting in container data
            elements.container.data("settings", settings);
            var addButton;

            if (elements.container.find("#addanother").length > 0) {
                addButton = elements.container.find("#addanother");
            }
            else {
                addButton = $("<input type='button' value ='"+ UNGM.editableListAddText +" "+ settings.entityName + "' id='addanother' class='addanother' />");
            }
            addButton.unbind("click.editableList").bind("click.editableList", methods.addAnother);
            elements.container.append(addButton);

            if (!settings.isReadonly
                && (settings.maxItems == null || (settings.maxItems != null && settings.maxItems > elements.container.find(".editableListItem").length))) {
                elements.container.find("#addanother").show();
                elements.container.find("#addanother").unbind("click.editableList").bind("click.editableList", methods.addAnother);
            }
            else {
                elements.container.find("#addanother").hide();
                elements.container.find("#addanother").unbind("click.editableList", methods.addAnother);
            }
            elements.listContainer = elements.container.find(".listContainer");
            methods.bindOptions(elements.listContainer);

            if (settings.itemClickedCallback) {
                elements.listContainer.find(".editableListItem").addClass("clickableListItem").unbind("click", methods.itemClicked).bind("click", methods.itemClicked);
            }
        },
        addAnother: function (evt) {
            var maincontainer = $(evt.currentTarget).parent("div");
            if (maincontainer.length > 0) {
                settings = maincontainer.data("settings");
            }
            UNGM.throbOver(elements.container);
            $.ajax({
                type: 'GET',
                url: settings.getInsertUrl,
                success: function (data) {
                    var newForm = $(data);
                    elements.contextForm = newForm;
                    elements.contextForm.find("form").attr("action", settings.postInsertUrl);
                    elements.contextForm.find("input[type='submit']").bind("click", methods.submitForm);
                    elements.contextForm.find("input[type='datetime']").datepicker({
                        onSelect: function (dateText, inst) {
                            UNGM.Validation.validateElement($(this));
                        },
                        changeYear: true,
                        changeMonth: true
                    });
                    UNGM.Validation.initForElement(elements.contextForm);
                    var modal = $("<div>").dialog({
                        modal: true,
                        title: UNGM.editableListAddDialogTitle,
                        width: '80%',
                        height: settings.dialogHeight,
                        hide: { effect: 'fade', duration: 100 },
                        show: { effect: 'fade', duration: 100 },
                        close: function () { elements.contextForm.parent().remove(); }


                    }).append(elements.contextForm);

                    if (!settings.dialogHeight) {
                        modal.dialog('option', 'height', elements.contextForm.outerHeight());
                    }

                    UNGM.hideThrobber();
                    UNGM.initTabsIn(elements.contextForm);
                    elements.contextForm.find(".validationIcon").hide();
                    elements.contextForm.find(".field-validation-error").hide();
                }
            });
        },
        submitForm: function (evt) {
            var form = $(evt.currentTarget).parents('form');
            var actionUrl = form.find("#Id").length ? settings.postEditUrl : settings.postInsertUrl;
            
            if (form.valid()) {
                UNGM.throbOver(elements.contextForm);
                $.ajax({
                    type: form.attr('method'),
                    url: actionUrl,
                    data: form.serialize(),
                    success: function (data) {
                        if ( data && data.error) {
                            // an error message was returned, so don't continue, show the error message instead
                            $.confirm(
                                "", // title
                                data.message, // message
                                "OK", // yes button
                                "", // no button
                                function () { }, //"yes" callback
                                300 // the minimum width of the model dialog
                            );
                            UNGM.hideThrobber();
                        }
                        else {
                            if (settings.formSubmittedCallback) {
                                settings.formSubmittedCallback(form);
                            }

                            elements.contextForm.parent().dialog('close');

                            var container = $(evt.currentTarget).data("container");
                            if (container != null) {
                                elements.container = $("#" + container);
                                elements.listContainer = elements.container.find(".listContainer");
                            }

                            methods.list();
                        }
                    }
                });
            }
            return false;
        },
        bindOptions: function (element) {
            element.find(".btnEdit").unbind("click").bind("click", methods.showEdit);
            element.find(".btnDelete").unbind("click").bind("click", methods.deleteItem);
        },
        list: function () {
            var url = settings.listUrl;
            UNGM.throbOver(elements.container);
            $.ajax({
                type: 'GET',
                url: url,
                success: function (data) {

                    elements.listContainer.html(data);

                    if (settings.itemClickedCallback) {
                        elements.listContainer.find(".editableListItem").addClass("clickableListItem").unbind("click").bind("click", methods.itemClicked);
                    }

                    methods.bindOptions(elements.listContainer);
                    // Look for a callback method here
                    if (UNGM.EditableListCallbacks && UNGM.EditableListCallbacks.listChangedCallback) {
                        UNGM.EditableListCallbacks.listChangedCallback();
                    }

                    // Hide 'add another button' if no more items are allowed
                    if (settings.maxItems == null || (settings.maxItems != null && settings.maxItems > elements.container.find(".editableListItem").length)) {
                        elements.container.find("#addanother").show();
                        elements.container.find("#addanother").unbind("click.editableList").bind("click.editableList", methods.addAnother);
                    }
                    else {
                        elements.container.find("#addanother").hide();
                        elements.container.find("#addanother").unbind("click.editableList", methods.addAnother);
                    }

                    UNGM.hideThrobber();
                }
            });
        },
        get: function (itemId) {
            var url;
            if (settings.getEditUrl.indexOf("{0}") >= 0) {
                url = settings.getEditUrl.replace("{0}", itemId);
            } else {
                url = settings.getEditUrl + itemId;
            }

            UNGM.throbOver(elements.container);
            $.ajax({
                type: 'GET',
                url: url,
                success: function (data) {
                    var newForm = $(data);
                    elements.contextForm = newForm;
                    elements.contextForm.find("input[type='submit']").bind("click", methods.submitForm);
                    elements.contextForm.find("input[type='datetime']").datepicker({
                        onSelect: function (dateText, inst) {
                            UNGM.Validation.validateElement($(this));
                        },
                        changeYear: true,
                        changeMonth: true
                    });
                    UNGM.Validation.initForElement(elements.contextForm);

                    var modal = $("<div>").dialog({
                        modal: true,
                        title: UNGM.editableListEditDialogTitle,
                        width: '66%',
                        height: settings.dialogHeight,
                        hide: { effect: 'fade', duration: 100 },
                        show: { effect: 'fade', duration: 100 },
                        close: function () { elements.contextForm.parent().remove(); }
                    }).append(elements.contextForm);

                    if (!settings.dialogHeight) {
                        modal.dialog('option', 'height', elements.contextForm.outerHeight());
                    }

                    UNGM.hideThrobber();
                    UNGM.initTabsIn(elements.contextForm);
                }
            });
        },
        showEdit: function (evt) {
            var item = $(evt.currentTarget).parents(".editableListItem:first");
            var maincontainer = item.parents("div.listMainContainer");
            if (maincontainer.length > 0) {
                settings = maincontainer.data("settings");
            }
            var itemId = item.data("itemid");
            methods.get(itemId);
            evt.stopPropagation();
        },
        deleteItem: function (evt) {

            $.confirm(
                UNGM.editableListConfirmDialogTitle,
                UNGM.editableListConfirmDialogText,
                UNGM.editableListConfirmDialogYes,
                UNGM.editableListConfirmDialogNo,
                function () {
                    var item = $(evt.currentTarget).parents(".editableListItem:first");
                    var maincontainer = item.parents("div.listMainContainer");
                    if (maincontainer.length > 0) {
                        settings = maincontainer.data("settings");
                    }
                    var itemId = item.data("itemid");
                    var obj = { itemId: itemId };
                    var url = settings.deleteUrl;

                    // If there's a file involved in the item, make it available in the document picker
                    if (item.data("documentid") !== undefined && UNGM.DocumentPicker) {
                        var removedDocId = item.data("documentid");

                        for (var i = 0; i < UNGM.DocumentPicker.filesUsed.length; i++) {
                            if (UNGM.DocumentPicker.filesUsed[i] == removedDocId) {
                                UNGM.DocumentPicker.filesUsed.splice(i, 1);
                                $("#documentSearch .isused[data-documentid='" + removedDocId + "']").val(0);
                                UNGM.DocumentPicker.disableUnavailableDocs();
                            }
                        }
                    }
                    elements.listContainer = $(evt.currentTarget).parents(".listContainer");
                   
                    UNGM.throbOver(elements.container);
                    $.ajax({
                        type: 'POST',
                        url: url,
                        data: obj,
                        success: function (data) {
                            methods.list();
                            UNGM.hideThrobber();
                        }
                    });
                }
            );


            evt.stopPropagation();
        },
        itemClicked: function (evt) {
            var maincontainer = $(evt.currentTarget).parent("div").parent("div");
            if (maincontainer !== undefined) {
                settings = maincontainer.data("settings");
            }
            if (settings.itemClickedCallback) {
                var sender = $(evt.currentTarget);
                settings.itemClickedCallback(sender);
            }
        },
        option: function (name, value) {
            if (value === undefined) {
                return settings[name];
            }
            settings[name] = value;
        }
    };

    elements = {
        contextForm: null
    };
    settings = {
        listUrl: null,
        getInsertUrl: null,
        postInsertUrl: null,
        getEditUrl: null,
        postEditUrl: null,
        deleteUrl: null,
        itemClickedCallback: null,
        formSubmittedCallback: null,
        entityName: null,
        isReadonly: false,
        maxItems: null,
        dialogHeight: null
    };

    $.fn.editableList = function (method) {

        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.editableList');
        }

    };

})(jQuery);;
UNGM.ExpandableContacts = {
    btnShowDetailsText: "Show details",     // this value will be replaced with localised UI text
    btnHideDetailsText: "Hide details",     // this value will be replaced with localised UI text

    init: function () {
        $(".editableListItem > span").css("cursor", "pointer").off("click").on("click", UNGM.ExpandableContacts.onContactClicked);
        $(".btnExpandAll").off("click").on("click", UNGM.ExpandableContacts.onExpandAllContactsClicked);
        $(".btnCollapseAll").off("click").on("click", UNGM.ExpandableContacts.onCollapseAllContactsClicked);
    },
    onContactClicked: function (e) {
        $details = $(e.target).parents(".editableListItem").children(".contactDetails");
        $details.slideToggle(function () {
            $(this).siblings(".options").find(":button").prop("value", $(this).is(":visible") ? UNGM.ExpandableContacts.btnHideDetailsText : UNGM.ExpandableContacts.btnShowDetailsText);
        });
    },
    onExpandAllContactsClicked: function () {
        $(".contactDetails").slideDown(function () {            
            $(".btnExpandAll").parent().find(".options :button").prop("value", UNGM.ExpandableContacts.btnHideDetailsText);
        });
    },
    onCollapseAllContactsClicked: function () {
        $(".contactDetails").slideUp(function () {            
            $(".btnCollapseAll").parent().find(".options :button").prop("value", UNGM.ExpandableContacts.btnShowDetailsText);
        });
    }
};
'use strict';

(function (namespace, undefined) {
    namespace.ExpandableItems = Factory;

    function Factory(selector) {
        var root = $(selector);
        root.on("click", ".btnDetails", toggleDetail);

        function toggleDetail(e) {
            var detail = $(e.target).parents(".expandableListItem").children(".body");
                detail.slideToggle(function () {
                    $(this).siblings(".options").find(".btnDetails").prop("value", $(this).is(":visible") ? "Hide details" : "Show details");
                    $(this).siblings(".expandable, .options").toggleClass("expanded");
                });
        }
    }
})(window.UNGM = window.UNGM || {});;
window.UNGM.Inbox = {
    subject: null,
    currentConversationId: null,
    replyToEmailId: null,
    resendConfirmationMessageTitle: "",
    resendConfirmationMessageMessage: "",
    resendConfirmationMessageYes: "",
    resendConfirmationMessageNo: "",
    searchFunction: null,
    pendingSearchRequest: null,
    general: {
        pageIndex: 0,
        sortField: "Id",
        sortAscending: true,
        paging: false,
        inSearch: false,
        folderId: "receivedItems"   // The default view shows the 'receivedItems'
    },/*
    conversationsFolder: {
        pageIndex: 0,
        sortField: "Id",
        sortAscending: true,
        paging: false,
        folderId: "Conversations",
        isContentLoaded: false
    },*/
    receivedItemsFolder: {
        pageIndex: 0,
        sortField: "Date",
        sortAscending: false,
        paging: false,
        folderId: "ReceivedItems",
        isContentLoaded: false
    },
    sentItemsFolder: {
        pageIndex: 0,
        sortField: "Date",
        sortAscending: false,
        paging: false,
        folderId: "SentItems",
        isContentLoaded: false
    },
    init: function () {
        $(".tableBody").on("click", ".tableRow", UNGM.Inbox.onConversationClicked);
        $("#conversationContainer").on("click", ".editableListItem .emailSummary > div:not(.btnContainer,.contactDetails)", UNGM.Inbox.onEmailClicked);
        $("#btnBack").bind("click", UNGM.Inbox.onBackClicked);
        $(".btnRefresh").bind("click", UNGM.Inbox.onRereshClicked);
        $("#conversationContainer").on("click", ".btnReply", UNGM.Inbox.onReplyClicked);
        $("#conversationContainer").on("click", ".btnReplyWithTemplate", UNGM.Inbox.onReplyWithTemplateClicked);
        $("#conversationContainer").on("click", ".btnSend", UNGM.Inbox.onSendClicked);
        $("#conversationContainer").on("click", ".btnResend", UNGM.Inbox.onResendClicked);
        $("#conversationContainer").on("click", ".btnCancel", UNGM.Inbox.onCancelClicked);
        $("#inboxContainer .tabHeader").bind("click", UNGM.Inbox.onFolderChanged);
        $(".txtDateSentFrom, .txtDateSentTo").bind("change", UNGM.Inbox.filterKeyUp);
        $(".txtEmail, .txtSubject, .txtBody").bind("keyup", UNGM.Inbox.filterKeyUp);
        $(".clearFilter").bind("click", UNGM.Inbox.clearAll);
        // Conversations tab is not sortable
        // $("#ConversationsFolder .tableHead .header").addClass("notsortable");
        $(".folderHolder .tableHead .header").not(".notsortable").on("click", UNGM.Inbox.sort);

        // Load message on load if the url contains the id
        UNGM.Inbox.onPageLoaded();

        UNGM.ieHackTableHeader();
    },
    onFolderChanged: function (e) {
        var currentFolderId = $(e.currentTarget).attr("id");

        $.waypoints('destroy');

        switch (currentFolderId) {
            /*case "Conversations":
                UNGM.Inbox.general = UNGM.Inbox.conversationsFolder;
                UNGM.Inbox.searchFunction = UNGM.Inbox.list;

                if (!UNGM.Inbox.conversationsFolder.isContentLoaded) {
                    UNGM.Inbox.list();
                    UNGM.Inbox.conversationsFolder.isContentLoaded = true;
                }
                else {
                    UNGM.Inbox.setWaypoints();
                }
                break;*/
            case "ReceivedItems":
                UNGM.Inbox.general = UNGM.Inbox.receivedItemsFolder;
                UNGM.Inbox.searchFunction = UNGM.Inbox.search;

                if (!UNGM.Inbox.receivedItemsFolder.isContentLoaded) {
                    UNGM.Inbox.general.pageIndex = 0;
                    UNGM.Inbox.general.paging = false;
                    UNGM.Inbox.search();
                    UNGM.Inbox.receivedItemsFolder.isContentLoaded = true;
                }
                else {
                    UNGM.Inbox.setWaypoints();
                }
                break;
            case "SentItems":
                UNGM.Inbox.general = UNGM.Inbox.sentItemsFolder;
                UNGM.Inbox.searchFunction = UNGM.Inbox.search;

                if (!UNGM.Inbox.sentItemsFolder.isContentLoaded) {
                    UNGM.Inbox.general.pageIndex = 0;
                    UNGM.Inbox.general.paging = false;
                    UNGM.Inbox.search();
                    UNGM.Inbox.sentItemsFolder.isContentLoaded = true;
                }
                else {
                    UNGM.Inbox.setWaypoints();
                }
                break;
        }
    },
    setWaypoints: function () {
        // Only set the waypoints if there are more items to load
        var $folder = $("#" + UNGM.Inbox.general.folderId + "Folder");

        if ($folder.find(".showing").html() == $folder.find(".searchTotal").html()) {
            return;
        }
        
        $folder.find(".folderHolder").waypoint(function (direction) {
            if (direction === 'down' && !UNGM.Inbox.general.inSearch) {
                $.waypoints('destroy');
                UNGM.Inbox.general.inSearch = false;
                UNGM.Inbox.general.pageIndex += 1;
                UNGM.Inbox.general.paging = true;
                UNGM.Inbox.searchFunction();
            }
        }, { offset: 'bottom-in-view' });
        UNGM.Inbox.general.inSearch = false;
    },
    clearAll: function (e) {
        $target = $(e.currentTarget);
        $filter = $target.parents(".folderFilter");
        $filter.find("input[type=text]:visible").val('');

        UNGM.Inbox.general.pageIndex = 0;
        UNGM.Inbox.general.paging = false;
        UNGM.Inbox.search();
    },
    filterKeyUp: function () {
        if (UNGM.Inbox.timeoutFunc) {
            clearTimeout(UNGM.Inbox.timeoutFunc);
        }

        UNGM.Inbox.timeoutFunc = setTimeout(function () {
            UNGM.Inbox.general.pageIndex = 0;
            UNGM.Inbox.general.paging = false;
            UNGM.Inbox.search();
        }, 700);
    },
    sort: function (evt) {
        var sender = $(evt.currentTarget);
        var newSortField = sender.attr('id');
        if (UNGM.Inbox.general.sortField == newSortField) {
            UNGM.Inbox.general.sortAscending = !UNGM.Inbox.general.sortAscending;
        } else {
            // First time column is clicked sorts ASC by default
            UNGM.Inbox.general.sortField = newSortField;
            UNGM.Inbox.general.sortAscending = true;
        }

        $(".folderHolder:visible .header.sortedAsc").removeClass("sortedAsc");
        $(".folderHolder:visible .header.sortedDesc").removeClass("sortedDesc");

        var newClass = UNGM.Inbox.general.sortAscending ? "sortedAsc" : "sortedDesc";
        sender.addClass(newClass);

        UNGM.Inbox.general.pageIndex = 0;
        UNGM.Inbox.general.paging = false;
        UNGM.Inbox.search();
    },
    list: function () {
        UNGM.throbOver();

        var opts = UNGM.Inbox.buildOptions();

        $.ajax({
            url: 'Inbox/ListConversations',
            type: 'POST',
            contentType: 'application/json',
            data: JSON.stringify(opts),
            success: UNGM.Inbox.onGetEmailsSuccess
        });
    },
    buildOptions: function () {
        var $filter = $(".folderFilter:visible");

        var opts = {
            PageIndex: UNGM.Inbox.general.pageIndex,
            SortField: UNGM.Inbox.general.sortField,
            SortAscending: UNGM.Inbox.general.sortAscending,
            FolderId: UNGM.Inbox.general.folderId,
            DateFrom: $filter.find(".txtDateSentFrom").val(),
            DateTo: $filter.find(".txtDateSentTo").val(),
            Email: $filter.find(".txtEmail").val(),
            Subject: $filter.find(".txtSubject").val(),
            Body: $filter.find(".txtBody").val()
        };

        return opts;
    },
    search: function () {
        // do not search if we're viewing an email
        if ($("#btnBack").is(':visible')) {
            return;
        }
        UNGM.throbOver();

        var opts = UNGM.Inbox.buildOptions();

        if (UNGM.Inbox.pendingSearchRequest) {
            UNGM.Inbox.pendingSearchRequest.abort();
        }
        UNGM.Inbox.pendingSearchRequest = $.ajax({
            url: 'Inbox/Search',
            type: 'POST',
            contentType: 'application/json',
            data: JSON.stringify(opts),
            success: UNGM.Inbox.onGetEmailsSuccess
        });
    },
    onGetEmailsSuccess: function (response) {
        if (response.length) {
            var $folderContainer = $("#" + UNGM.Inbox.general.folderId + "Folder")
            var $tableBody = $folderContainer.find(".tableBody");

            if (UNGM.Inbox.general.paging) {
                $tableBody.append(response);
                UNGM.Inbox.general.paging = false;
            }
            else {
                $tableBody.html(response);
            }

            UNGM.ieHackTableCells();
        }

        var showing = $tableBody.find(".tableRow").length;
        $folderContainer.find(".showing").html(showing);

        if (showing > 0) {
            $folderContainer.find(".noEmailsInfo").hide();
            $folderContainer.find(".resultsContainer").show();
        }
        else {
            $folderContainer.find(".noEmailsInfo").show();
            $folderContainer.find(".resultsContainer").hide();
        }

        UNGM.Inbox.setWaypoints();
        UNGM.hideThrobber();
    },
    onPageLoaded: function () {
        UNGM.Inbox.general = UNGM.Inbox.receivedItemsFolder;
        UNGM.Inbox.searchFunction = UNGM.Inbox.search;

        if (window.location.href.indexOf('#') != -1) {
            UNGM.Inbox.currentConversationId = window.location.href.substring(window.location.href.indexOf('#') + 1, window.location.href.length);
            UNGM.Inbox.getConversation();
        }        
        else {
            UNGM.Inbox.search();
            //UNGM.Inbox.conversationsFolder.isContentLoaded = true;
        }
        
    },
    onConversationClicked: function (e) {
        var $target = $(e.currentTarget);
        UNGM.Inbox.currentConversationId = $target.data("conversationid");

        // Add id of conversation to url
        if (typeof window.history.replaceState == 'function') {
            window.history.pushState("Open conversation", "Open conversation", "#" + UNGM.Inbox.currentConversationId);
        }

        // Set value to the button to go back
        switch (UNGM.Inbox.general.folderId) {
            //case "Conversations":
            //    $("#btnBack").val(UNGM.Inbox.btnBackToConversationsValue);
            //    break;
            case "ReceivedItems":
                $("#btnBack").val(UNGM.Inbox.btnBackToReceivedItemsValue);
                break;
            case "SentItems":
                $("#btnBack").val(UNGM.Inbox.btnBackToSentItemsValue);
                break;
        }
        
        UNGM.Inbox.getConversation();
    },
    getConversation: function () {
        UNGM.throbOver($("#inbox"));
        $.ajax({
            url: UNGM.siteRoot + "Account/Inbox/GetConversation?conversationid=" + UNGM.Inbox.currentConversationId,
            type: 'GET',
            success: UNGM.Inbox.onGotConversation,
            error: function (event, jqXHR, ajaxSettings, thrownError) {
                if (jqXHR.status == "403") {
                    // The user does not have access to the email
                    return false;
                }
            }
        });
    },
    onGotConversation: function (data) {
        $("#inboxContainer").hide();
        $("#conversationEmails").html(data);
        $("#conversationSubject").html(UNGM.Inbox.subject);
        $("#conversationContainer").fadeIn("fast");
        $("#conversationContainer .editableListItem:last").find(".body").show();
        $("#btnBack").scrollintoview();
        UNGM.hideThrobber();
    },
    onEmailClicked: function (e) {
        var $email = $(e.currentTarget).parents(".email");
        $email.find(".body").slideToggle("fast");
    },
    onBackClicked: function () {
        if (typeof window.history.replaceState == 'function') {
            window.history.pushState("Inbox", "Inbox", "#");
            history.replaceState({}, '', window.location.href.slice(0, window.location.href.indexOf('#') - window.location.href.length));
        }
        UNGM.Inbox.currentConversationId = null;
        $("#conversationContainer").hide();
        $("#inboxContainer").fadeIn("fast");
        // make sure the waypoints are switched back on for the search tab we're returning to
        var activeTab = $(".activeTab")
        var newObj = { currentTarget: activeTab };
        UNGM.Inbox.onFolderChanged(newObj);
        //if (!UNGM.Inbox.conversationsFolder.isContentLoaded) {
        //    UNGM.Inbox.list();
        //    UNGM.Inbox.conversationsFolder.isContentLoaded = true;
        //}
    },
    onRereshClicked: function () {
        $.waypoints('destroy');
        UNGM.Inbox.general.pageIndex = 0;
        UNGM.Inbox.general.paging = false;
        UNGM.Inbox.searchFunction();
    },
    onReplyWithTemplateClicked: function(e) {
        var $button = $(e.currentTarget);
        var $email = $button.parents(".editableListItem");
        UNGM.Inbox.currentConversationId = $email.data("conversationid");
        UNGM.Inbox.replyToEmailId = $email.data("id");
        UNGM.FeedbackEmail.ccEmails = [];
        UNGM.FeedbackEmail.bccEmails = [];
        // open the email dialog
        UNGM.throbOver("#conversationContainer");
        $.ajax({
            url: UNGM.siteRoot + 'UNUser/EmailTemplates/EmailFeedback',
            type: 'POST',
            contentType: 'application/json',
            success: UNGM.Inbox.onFeedbackEmail
        });
    },
    onFeedbackEmail: function (data) {
        $("#emailDialog").html(data);
        $("#radShowAll").click();
        $("#divAllOrMatching").hide();
        UNGM.HelpdeskTemplates.isInboxEmail = true;
        // open the dialog
        $("#emailDialog").dialog({
            modal: true,
            title: "Reply to feedback",
            width: 900,
            height: 640,
            hide: { effect: 'fade', duration: 100 },
            show: { effect: 'fade', duration: 100 }
        });
        UNGM.hideThrobber();
    },
    sendTemplateEmail: function () {
        // If the user is not active the document picker doesn't exist.
        try {
            var attachments = $.map(UNGM.DocumentPicker.filesUsed, function (val) { return val.Id; });
        }
        catch (e) { }

        var newEmail = {
            Id: UNGM.Inbox.replyToEmailId,
            Body: UNGM.HelpdeskTemplates.getCurrentBodyText(),
            Attachments: attachments,
            cc: UNGM.FeedbackEmail.ccEmails,
            bcc: UNGM.FeedbackEmail.bccEmails
        };
        // form has already been validated  in the UNGM.HelpdeskTemplates.js script
        UNGM.throbOver($("#conversationContainer"));
        $.ajax({
            url: UNGM.siteRoot + "Account/Inbox/ReplyEmail",
            type: 'POST',
            contentType: 'application/json',
            data: JSON.stringify(newEmail),
            success: function () {
                // If the user is not active the document picker doesn't exist.
                try {
                    UNGM.DocumentPicker.filesUsed = [];
                }
                catch (e) { }
                $("#emailDialog").dialog("close");
                // Show a message telling the user that the emails have been sent
                $.confirm(
                    UNGM.HelpdeskTemplates.EmailSentTitle, // Title
                    UNGM.HelpdeskTemplates.EmailSentMessage, // Message
                    "",
                    UNGM.HelpdeskTemplates.EmailSentOK,
                    ""  //"yes" callback
               );

                UNGM.Inbox.getConversation();
            },
            complete: function () {
                UNGM.hideThrobber();
            }
        });
    },
    onReplyClicked: function (e) {
        var $button = $(e.currentTarget);
        var $email = $button.parents(".editableListItem");

        if (!$email.find(".replyForm").length) {
            $form = $("#replyForm").clone().removeAttr("id").addClass("replyForm");
            $email.find(".body").append($form);
        }
        else {
            $form = $email.find(".replyForm");
        }
        $textarea = $form.find("textarea");
        $textarea.attr("cols", $email.width() / 8.3);
        setTimeout(function () { $textarea.focus(); }, 0);
        $form.fadeIn("fast");
        $email.find(".body").slideDown("fast");
        UNGM.DocumentPicker.init();
        $textarea[0].setSelectionRange(0, 0);
    },
    onCancelClicked: function (e) {
        var $button = $(e.currentTarget);
        $button.parents(".replyForm").fadeOut("fast", function () {
            $(this).remove();
        });
    },
    onSendClicked: function (e) {
        var $button = $(e.currentTarget);
        var $email = $button.parents(".email");
        UNGM.Inbox.currentConversationId = $email.data("conversationid");

        // If the user is not active the document picker doesn't exist.
        try {
            var attachments = $.map(UNGM.DocumentPicker.filesUsed, function (val) { return val.Id; });
        }
        catch (e) { }

        var newEmail = {
            Id: $email.data("id"),
            Body: $email.find("textarea").val().replace(/\n/g, '<br />'),
            Attachments: attachments
        };

        // Validate the form manually because there's no model in the view
        if (!$.trim(newEmail.Body).length) {
            $email.find(".errorMessageEmpty").show();
            $email.find("textarea").bind("focus", function () {
                $email.find(".errorMessageEmpty").hide();
            });
            return false;
        }

        UNGM.throbOver($("#conversationContainer"));
        $.ajax({
            url: UNGM.siteRoot + "Account/Inbox/ReplyEmail",
            type: 'POST',
            contentType: 'application/json',
            data: JSON.stringify(newEmail),
            success: function () {
                // If the user is not active the document picker doesn't exist.
                try {
                    UNGM.DocumentPicker.filesUsed = [];
                }
                catch (e) { }

                $(".docslist").html("");
                $(".noDocsMessage").show();
                $button.parents(".replyForm").fadeOut("fast", function () {
                    $(this).remove();
                });
                UNGM.Inbox.getConversation();
                $('html, body').animate({
                }, 500);
            },
            complete: function () {
                UNGM.hideThrobber();
            }
        });
    },
    onResendClicked: function (e) {

        var resendEmailConfirmed = function () {
            var $button = $(e.currentTarget);
            var $email = $button.parents(".email");
            var id = $email.data("id");
            UNGM.Inbox.currentConversationId = $email.data("conversationid");

            UNGM.throbOver($("#conversationContainer"));
            $.ajax({
                url: UNGM.siteRoot + "Account/Inbox/ResendEmail/",
                type: 'POST',
                data: "id=" + id,
                success: function () {
                    UNGM.Inbox.getConversation();
                },
                complete: function () {
                    UNGM.hideThrobber();
                }
            });
        };

        $.confirm(
            UNGM.Inbox.resendConfirmationMessageTitle, // Title
            UNGM.Inbox.resendConfirmationMessageMessage, // Message
            UNGM.Inbox.resendConfirmationMessageYes,
            UNGM.Inbox.resendConfirmationMessageNo,
            resendEmailConfirmed  //"yes" callback
        );
    }
};
window.UNGM.ManageUserSettings = {
    RevokeClientAccessConfirmationTitle: '',
    RevokeClientAccessConfirmationMessage: '',
    RevokeClientAccessConfirmationBtnYes: '',
    RevokeClientAccessConfirmationBtnNo: '',
    init: function () {
        $('#PreferredLanguage').val(UNGM.currentuserCookieLanguage);      // needed for safari
        $('#PreferredLanguage').bind('change', UNGM.ManageUserSettings.preferredLanguageChanged);
        $('#ReceiveOptionalEmails').bind('change', UNGM.ManageUserSettings.receiveOptionalEmailsChanged);
        $('.btnRevokeClientAccess').bind('click', UNGM.ManageUserSettings.onRevokeClientAccessClicked);
        $('#ReceivesNotificationsOfCompanyNameChange').on('change', UNGM.ManageUserSettings.onReceivesNotificationsOfCompanyNameChangeChanged);
    },
    preferredLanguageChanged: function () {
        UNGM.Throbber.Push();
        var preferredLanguage = $('#PreferredLanguage').val();
        UNGM.setUserLanguage(preferredLanguage);
    },
    receiveOptionalEmailsChanged: function (event) {
        UNGM.Throbber.Push();
        var url = UNGM.siteRoot + 'Account/UserSettings/ReceiveOptionalEmails';
        var data = { ReceiveOptionalEmails: $(event.currentTarget).prop('checked') };
        $.post(url, data).always(UNGM.Throbber.Pop);
    },
    onRevokeClientAccessClicked: function (e) {
        var apiClientId = $(e.currentTarget).data('api-client-id');

        var revokeAccessOk = function () {
            UNGM.throbOver();

            $.ajax({
                url: UNGM.siteRoot + 'Account/Account/RevokeClientAccess',
                type: 'POST',
                data: { 'apiClientId': apiClientId },
                success: function (response) {
                    $('#clientGrantHolder').html(response);
                    // Bind button events
                    $('.btnRevokeClientAccess').bind('click', UNGM.ManageUserSettings.onRevokeClientAccessClicked);
                },
                complete: UNGM.hideThrobber
            });
        }

        $.confirm(
            UNGM.ManageUserSettings.RevokeClientAccessConfirmationTitle,
            UNGM.ManageUserSettings.RevokeClientAccessConfirmationMessage,
            UNGM.ManageUserSettings.RevokeClientAccessConfirmationBtnYes,
            UNGM.ManageUserSettings.RevokeClientAccessConfirmationBtnNo,
            revokeAccessOk
        );
    },
    onReceivesNotificationsOfCompanyNameChangeChanged: function (event) {
        UNGM.Throbber.Push();
        var receivesNotificationsOfCompanyNameChange = $(event.currentTarget).prop('checked');
        var url = receivesNotificationsOfCompanyNameChange
            ? UNGM.siteRoot + 'Account/UserSettings/OptInForNotificationsOfCompanyNameChange'
            : UNGM.siteRoot + 'Account/UserSettings/OptOutOfNotificationsOfCompanyNameChange';
        $.post(url).always(UNGM.Throbber.Pop);
    }
}
;
(function (namespace, undefined) {

    //
    // NoticeSearch
    //

    namespace.NoticeSearch = function (parameter) {
        this.divNoticeMain = $(parameter.divNoticeMainSelector);

        this.selNoticeAgency = $(parameter.selNoticeAgencySelector);
        this.hidIsAgencySelected = $(parameter.hidIsAgencySelectedSelector);
        this.divNoticeSearchAgency = $(parameter.divNoticeSearchAgencySelector);
        this.hidIsCountrySelected = $(parameter.hidIsCountrySelectedSelector);
        this.selNoticeCountry = $(parameter.selNoticeCountrySelector);
        this.divNoticeSearchCountry = $(parameter.divNoticeSearchCountrySelector);
        this.btnEOI = $(parameter.btnEOISelector);
        this.lblNoticeSearchTotal = $(parameter.lblNoticeSearchTotalSelector);
        this.txtNoticePublishedFrom = $(parameter.txtNoticePublishedFromSelector);
        this.txtNoticePublishedTo = $(parameter.txtNoticePublishedToSelector);
        this.txtNoticeDeadlineFrom = $(parameter.txtNoticeDeadlineFromSelector);
        this.txtNoticeDeadlineTo = $(parameter.txtNoticeDeadlineToSelector);
        this.parameter = parameter;

        this.chbSustainableNoticeClass = this.divNoticeMain.find("#chkSustainable");
        this.chbNoticeTypeClass = this.divNoticeMain.find(".noticeTypeCheckboxList");
        this.chbTasStatusClass = this.divNoticeMain.find(".tasStatusCheckboxList");
        this.chbTypeOfCompetitions = this.divNoticeMain.find(".typeOfCompetitionCheckboxList");
        this.pickNotice = this.divNoticeMain.find(".pickNotice");
        this.divNoticeData = this.divNoticeMain.find('#notices');
        this.divNoticeTotal = this.divNoticeMain.find('#noticesTotal');
        this.txtNoticeFilterTitle = this.divNoticeMain.find("#txtNoticeFilterTitle");
        this.txtNoticeFilterDesc = this.divNoticeMain.find("#txtNoticeFilterDesc");
        this.txtNoticeFilterRef = this.divNoticeMain.find("#txtNoticeFilterRef");
        this.divNoticeSearchUNSPSC = this.divNoticeMain.find("#noticeSearchUNSPSC");
        this.divNoticeUNSPSCSelector = this.divNoticeMain.find("#noticeUNSPSCSelector");
        this.btnClearAll = this.divNoticeMain.find("#lnkClearAll");
        this.lnkShowUNSPSC = this.divNoticeMain.find("#lnkShowUNSPSC");
        this.divEditNotice = this.divNoticeMain.find("#editNotice");
        this.tblNotices = this.divNoticeMain.find("#tblNotices");
        this.hidIsEOISearch = this.divNoticeMain.find("#isEOISearch");
        this.hidIsPicker = this.divNoticeMain.find("#IsPicker");
        this.hidWidgetUNSPSCs = this.divNoticeMain.find("#WidgetUNSPSCs");
        this.hidWidgetAgency = this.divNoticeMain.find("#WidgetAgency");
        this.hidNoticeDisplayType = this.divNoticeMain.find("#NoticeDisplayType");

        this.unspscTitleText = parameter.UNSPSCTitleText;
        this.EOIFailureTitle = parameter.EOIFailureTitle;
        this.EOIFailureMessage = parameter.EOIFailureMessage;
        this.EOIFailureOK = parameter.EOIFailureOK;

        this.CanNotExpressInterestTitle = parameter.CanNotExpressInterestTitle;
        this.CanNotExpressInterestMessage = parameter.CanNotExpressInterestMessage;
        this.CanNotExpressInterestConfirmButton = parameter.CanNotExpressInterestConfirmButton;

        this.ShowSubscriptionMessage = parameter.ShowSubscriptionMessage;
        this.SubscribeTitle = parameter.SubscribeTitle;
        this.SubscribeMessage = parameter.SubscribeMessage;
        this.SubscribeConfirmButtonText = parameter.SubscribeConfirmButtonText;
        this.SubscribeCancelButtonText = parameter.SubscribeCancelButtonText;

        this.selectedCountries = [];
        this.selectedAgencies = [];
        this.selectedUNSPSCs = [];
        this.selectedTasStatus = [];
        this.selectedTypeOfCompetitions = [];
        this.sustainableNotice = false;
        this.selectedNoticeTypes = [];
        this.selectedNoticeDisplayType = null;
        this.prevPageIndex = 0;
        this.pageIndex = 0;
        this.paging = false;
        this.inSearch = false;
        this.timeoutFunc = null;
        this.sortField = "DatePublished"; // default sort by
        this.sortAscending = false;
        this.pendingSearchRequest = null;

        this.txtNoticeFilterTitle.off("keyup").on("keyup", this.FilterKeyUp(this));
        this.txtNoticeFilterDesc.off("keyup").on("keyup", this.FilterKeyUp(this));
        this.txtNoticeFilterRef.off("keyup").on("keyup", this.FilterKeyUp(this));

        this.txtNoticePublishedFrom.off("change").on("change", this.FilterKeyUp(this));
        this.txtNoticePublishedTo.off("change").on("change", this.FilterKeyUp(this));
        this.txtNoticeDeadlineFrom.off("change").on("change", this.FilterKeyUp(this));
        this.txtNoticeDeadlineTo.off("change").on("change", this.FilterKeyUp(this));

        this.selNoticeAgency.bind("change", this.onNoticeAgencySelect(this));
        this.prePopulateFilters(this);
        this.selNoticeCountry.bind("change", this.onNoticeCountrySelect(this));
        this.lnkShowUNSPSC.on("click", this.showUNSPSC(this));

        this.chbNoticeTypeClass.off("click").on("click", this.selectNoticeType(this));
        this.chbTasStatusClass.off("click").on("click", this.selectTasStatus(this));
        this.chbTypeOfCompetitions.off("click").on("click", this.selectTypeOfCompetition(this));

        this.chbSustainableNoticeClass.off("click").on("click", this.selectSustainableNotice(this));

        this.btnClearAll.on("click", this.clearAll(this));

        this.tblNotices.find('.tableHead .tableRow .tableCell').not(".notsortable").on("click", this.Sort(this));

        var todayDate = new Date();

        this.txtNoticeDeadlineFrom.datepicker("setDate", todayDate);
        this.txtNoticePublishedTo.datepicker("option", "maxDate", todayDate);
        this.txtNoticePublishedTo.datepicker("setDate", todayDate);

        this.hidIsAgencySelected.on("change", this.clearAgency(this));
        this.hidIsCountrySelected.on("change", this.clearCountry(this));

        // Load UNSPSCs,Agency,NoticeDisplayType from Widget setting
        if (this.hidWidgetUNSPSCs.val()) {
            this.renderReadOnlyUNSPSCs(this);
        }
        if (this.hidWidgetAgency.val()) {
            this.renderReadOnlyAgency(this);
        }
        if (this.hidNoticeDisplayType.val()) {
            this.renderReadOnlyDisplayType(this);
        }

        //only run the search if we're not in picker mode (search will not be needed if the picker isn't used) 
        if (this.hidIsPicker !== undefined && this.hidIsPicker.length !== 0) {
            var ip = this.hidIsPicker.val().toLowerCase();
            if (!(ip == 'true')) {
                // Set height to show loading for first time
                this.divNoticeData.css("height", 150 + "px");
                this.Search(this).call();
                // Set height back to auto
                this.divNoticeData.css("height", "auto");
            }
        }
        //Check if called from widget's page
        if (this.divNoticeMain.attr("id") != "noticeMain") {
            this.divNoticeData.css("max-height", 400 + "px");
            this.divNoticeData.css("overflow", "auto");
        }
        // Datepickers select change
        this.txtNoticePublishedFrom.datepicker({
            onSelect: function (dateText, inst) {
            }
        });
        UNGM.ieHackTableHeader();
    }

    namespace.NoticeSearch.prototype.ReAssignEOIAndIntendButtons = function (parameter) {
        this.btnEOI = $(parameter.btnEOISelector);
        this.btnEOI.off("click").on("click", this.expressInterestClick(this));
    }

    namespace.NoticeSearch.prototype.ReAssignSearchTotal = function (parameter, searchTotal) {
        this.lblNoticeSearchTotal = $(parameter.lblNoticeSearchTotalSelector);
        this.lblNoticeSearchTotal.html("");
        this.lblNoticeSearchTotal.html(searchTotal);
    }

    namespace.NoticeSearch.prototype.Search = function ($this) {
        return function () {
            if ($this.pendingSearchRequest) {
                $this.pendingSearchRequest.abort();
            }
            var opts = $this.BuildOptions($this);
            UNGM.throbOver($this.divNoticeData);
            var url = "";
            if ($this.hidIsEOISearch.val() === "true") {
                url = UNGM.siteRoot + 'Public/Notice/SearchEOI';
            }
            else {
                url = UNGM.siteRoot + 'Public/Notice/Search';
            }
            $this.pendingSearchRequest =
                $.ajax({
                    url: url,
                    type: 'POST',
                    contentType: 'application/json',
                    data: JSON.stringify(opts),
                    success: function (data) { $this.onGotData(data, $this); }
                });
        }
    }

    namespace.NoticeSearch.prototype.FilterKeyUp = function ($this) {
        return function () {
            if ($this.timeoutFunc) {
                clearTimeout($this.timeoutFunc);
            }
            $this.pageIndex = 0;
            $this.paging = false;
            $.waypoints('destroy');

            $this.timeoutFunc = setTimeout(function () { $this.Search($this).call(); }, 1500);
        }
    }

    namespace.NoticeSearch.prototype.BuildOptions = function ($this) {

        var opts =
        {
            PageIndex: $this.pageIndex,
            PageSize: 15,
            Title: $this.txtNoticeFilterTitle.val(),
            Description: $this.txtNoticeFilterDesc.val(),
            Reference: $this.txtNoticeFilterRef.val(),
            PublishedFrom: $this.txtNoticePublishedFrom.val(),
            PublishedTo: $this.txtNoticePublishedTo.val(),
            DeadlineFrom: $this.txtNoticeDeadlineFrom.val(),
            DeadlineTo: $this.txtNoticeDeadlineTo.val(),
            Countries: $this.selectedCountries,
            Agencies: $this.selectedAgencies,
            UNSPSCs: $this.selectedUNSPSCs,
            NoticeTypes: $this.selectedNoticeTypes,
            SortField: $this.sortField,
            SortAscending: $this.sortAscending,
            isPicker: $this.hidIsPicker.val().toLowerCase() == 'true',
            NoticeTASStatus: $this.selectedTasStatus,
            IsSustainable: $this.sustainableNotice,
            NoticeDisplayType: $this.selectedNoticeDisplayType,
            NoticeSearchTotalLabelId: $this.lblNoticeSearchTotal.attr('id'),
            TypeOfCompetitions: $this.selectedTypeOfCompetitions
           };
        return opts;
    }

    namespace.NoticeSearch.prototype.Sort = function ($this) {
        return function (evt) {
            var sender = $(evt.currentTarget);
            var newSortField = sender.attr('id');
            switch (newSortField) {
                case "id_Deadline":
                    newSortField = "Deadline";
                    break;
                case "id_DatePublished":
                    newSortField = "DatePublished";
                    break;
            }
            if (newSortField == undefined || newSortField == "") {
                newSortField = "DatePublished";
            }

            if ($this.sortField == newSortField) {
                // 2 clicks on the same column means swap order
                $this.sortAscending = !$this.sortAscending;
            }
            else {
                // First time column is clicked sorts ASC by default
                $this.sortField = newSortField;
                $this.sortAscending = true;
            }

            $this.divNoticeMain.find("#tblNotices .sortedAsc").removeClass("sortedAsc");
            $this.divNoticeMain.find("#tblNotices .sortedDesc").removeClass("sortedDesc");

            var newClass = $this.sortAscending ? "sortedAsc" : "sortedDesc";
            sender.addClass(newClass);

            $this.pageIndex = 0;
            $this.paging = false;
            $this.Search($this).call();
        }
    }

    namespace.NoticeSearch.prototype.onGotData = function (data, $this) {
        $this.prevPageIndex = $this.pageIndex;
        data = $.trim(data);
        var dataArray = $(data).toArray();
        var returnRows = $(dataArray).filter(".dataRow").length;
        if (returnRows < 1) {
            if ($this.paging) {
                $this.paging = false;
            }
            else {
                $this.divNoticeData.find("#noticesEmpty").show();
                $this.divNoticeData.find("#tblNotices").hide();
                $this.divNoticeTotal.hide();
                $this.divNoticeData.find("#intendEOI").hide();
            }
        }
        else {
            $this.divNoticeData.children("script").remove();
            if ($this.paging) {
                $this.divNoticeData.find(".tableBody").append(data);
                $this.paging = false;
            }
            else {
                $this.divNoticeData.find(".tableBody").html(data);
                $this.divNoticeData.find("#tblNotices").show();
                $this.divNoticeTotal.show();
                $this.divNoticeData.find("#noticesEmpty").hide();
            }

            if ($("#IsPicker").val().toLowerCase() == 'true') {
                $this.pickNotice.bind("click", $this.pickNoticeClicked);
            }

            $this.showingCount = ($this.tblNotices.find(".tableRow").length - 1);
            $this.totalCount = parseInt($this.lblNoticeSearchTotal.html(), 10);


            $this.ReAssignEOIAndIntendButtons($this.parameter);
            $this.divNoticeData.find(".btnEdit").off("click").on("click", $this.editNotice($this));
            $this.divNoticeMain.find(".tableBody .tableRow").off("click").on("click", $this.noticeClicked($this));
            $this.divNoticeMain.find('.tableHead .tableRow .tableCell').not(".notsortable").off("click").on("click", this.Sort(this));
            $this.divNoticeMain.find("#SearchCountOf").show();
            $this.divNoticeMain.find("#searchCount").text($this.divNoticeMain.find("#searchOneTo").val() + " " + $this.showingCount);
            // showhide EOI
            $this.ShowHideEOI($this);

            // Calculate context and offset depending on the display page
            var noticeContext = null;
            var noticeOffset = null;

            var contextHeight = $this.divNoticeData.height();
            var tableHeight = $this.divNoticeData.find("#tblNotices").height();
            if (contextHeight < tableHeight) {
                noticeOffset = -(tableHeight - contextHeight);
                noticeContext = $this.divNoticeData;
            } else {
                // if picker, context is dialogbox id
                if ($("#IsPicker").val().toLowerCase() == 'true') {
                    noticeContext = $("#dlgNoticeSearch");
                    noticeOffset = -(tableHeight - $("#dlgNoticeSearch").height());
                } else {
                    noticeOffset = 'bottom-in-view';
                }
            }

            $this.divNoticeData.find("#tblNotices").waypoint(function (direction) {
                if (direction === 'down' && !$this.inSearch && $this.showingCount < $this.totalCount) {
                    $this.divNoticeData.find("#tblNotices").waypoint('destroy');
                    $this.pageIndex = $this.prevPageIndex + 1;
                    $this.inSearch = true;
                    $this.paging = true;
                    $this.Search($this).call();
                }
            }, {
                context: noticeContext,
                offset: noticeOffset
            });
            $this.inSearch = false;

        }
        // Trigger agency hidden to clear value
        $this.hidIsAgencySelected.trigger("change");
        // Trigger country hidden to clear value
        $this.hidIsCountrySelected.trigger("change");
        UNGM.ieHackTableCells();
        UNGM.hideThrobber();
    }

    namespace.NoticeSearch.prototype.ShowHideEOI = function ($this) {
        if ($this.divNoticeData.find("#tblNotices").find('.buttonIntend').length > 0) {
            $this.divNoticeData.find('#intendEOI').show();
        }
        else {
            $this.divNoticeData.find('#intendEOI').hide();
        }
    }

    namespace.NoticeSearch.prototype.onNoticeAgencySelect = function ($this) {
        return function (evt) {

            var sender = $(evt.currentTarget);
            var agencyId = parseInt(sender.val());
            if (sender.val() == '') {
                return;
            }
            var agencyAbbreviation = sender.children(':selected').text();

            $this.addAgencyFilter($this, agencyId, agencyAbbreviation);

            $this.pageIndex = 0;
            $this.paging = false;
            $this.Search($this).call();
            // Set hidden value to 1 to trigger change.
            $this.hidIsAgencySelected.val(1);
        }
    }

    namespace.NoticeSearch.prototype.addAgencyFilter = function ($this, agencyId, agencyAbbreviation) {
        for (var i = 0; i < $this.selectedAgencies.length; i++) {
            if ($this.selectedAgencies[i] == agencyId) {
                return; // agency is already in the list, so do nothing
            }
        }
        // Add selected agency to the array
        $this.selectedAgencies.push(agencyId);

        //Add selected agency to place holder
        var holder = $this.divNoticeSearchAgency;
        var elem = $("<div class='noticeSelectedAgency'></div>");
        elem.data("ID", agencyId);

        var span = $("<span class='noticeSelectedAgencyName'></span>");
        span.html(agencyAbbreviation);

        var removeButton = $("#removeButtonTemplate").clone();
        removeButton.removeAttr("id").show();
        removeButton.data("Id", agencyId);
        removeButton.bind("click", $this.removeAgencyClicked($this));

        elem.append(span).append(removeButton);
        holder.append(elem);

        if ($this.selectedAgencies.length > 0) {
            $this.divNoticeSearchAgency.show();
        }
        else {
            $this.divNoticeSearchAgency.hide();
        }
    }

    namespace.NoticeSearch.prototype.prePopulateFilters = function ($this) {
        if ($this.parameter.PrePopulateFilters && $this.parameter.PrePopulateFilters.AgencyId) {
            var agencyId = $this.parameter.PrePopulateFilters.AgencyId;
            var agencyAbbreviation = $this.parameter.PrePopulateFilters.AgencyAbbreviation;

            $this.addAgencyFilter($this, agencyId, agencyAbbreviation);
        }
    }

    namespace.NoticeSearch.prototype.onNoticeCountrySelect = function ($this) {
        return function (evt) {
            var sender = $(evt.currentTarget);
            var countryId = sender.val();
            if (countryId == '') {
                return;
            }
            var countryName = sender.children(':selected').text();
            var selectedCountry = {
                Id: countryId,
                Name: countryName
            };

            // Search 
            for (var i = 0; i < $this.selectedCountries.length; i++) {
                if ($this.selectedCountries[i] == countryId) {
                    return; // country is already in the list, so do nothing
                }
            }
            // Add selected country to the array
            $this.selectedCountries.push(countryId);

            //Add selected country to place holder
            var holder = $this.divNoticeSearchCountry;
            var elem = $("<div class='noticeSelectedCountry'></div>");
            elem.data("ID", countryId);

            var span = $("<span class='noticeSelectedCountryName'></span>");
            span.html(countryName);

            var removeButton = $("#removeButtonTemplate").clone();
            removeButton.removeAttr("id").show();
            removeButton.data("Id", countryId);
            removeButton.bind("click", $this.removeCountryClicked($this));

            elem.append(span).append(removeButton);
            holder.append(elem);

            if ($this.selectedCountries.length > 0) {
                $this.divNoticeSearchCountry.show();
            }
            else {
                $this.divNoticeSearchCountry.hide();
            }
            $this.pageIndex = 0;
            $this.paging = false;
            $this.Search($this).call();

            // Set hidden valaue to 1 to trigger change.
            $this.hidIsCountrySelected.val(1);
        }
    }

    namespace.NoticeSearch.prototype.removeAgencyClicked = function ($this) {
        return function (evt) {
            var sender = $(evt.currentTarget);
            var removedArray = "";
            removedArray = $this.selectedAgencies;
            var removedId = sender.data("Id");

            for (var i = 0; i < removedArray.length; i++) {
                if (removedArray[i] == removedId) {
                    removedArray.splice(i, 1);
                }
            }
            // Remove selected agency div from place holder
            var parentMain = sender.parents($this.divNoticeSearchAgency);
            parentMain.find(".noticeSelectedAgency").each(function () {
                if ($(this).data("ID") == removedId) {
                    $(this).closest("div").remove();
                }
            });
            //hide selected agency div when all are removed.
            if (removedArray.length == 0) {
                $this.divNoticeSearchAgency.hide();
            }
            // Set pageing default
            $this.pageIndex = 0;
            $this.paging = false;
            $this.Search($this).call();
        }
    }

    namespace.NoticeSearch.prototype.removeCountryClicked = function ($this) {
        return function (evt) {
            var sender = $(evt.currentTarget);
            var removedArray = $this.selectedCountries;

            var removedId = sender.data("Id");

            for (var i = 0; i < removedArray.length; i++) {
                if (removedArray[i] == removedId) {
                    removedArray.splice(i, 1);
                }
            }
            // Remove selected country div from place holder
            var parentMain = sender.parents($this.divNoticeSearchCountry);
            parentMain.find(".noticeSelectedCountry").each(function () {
                if ($(this).data("ID") == removedId) {
                    $(this).closest("div").remove();
                }
            });
            //hide selected country div when all are removed.
            if (removedArray.length == 0) {
                $this.divNoticeSearchCountry.hide();
            }
            // Set pageing default
            $this.pageIndex = 0;
            $this.paging = false;
            $this.Search($this).call();
        }
    }

    namespace.NoticeSearch.prototype.removeUNSPSCClicked = function ($this) {
        return function (evt) {
            var sender = $(evt.currentTarget);
            var removedArray = $this.selectedUNSPSCs;

            var removedId = sender.data("Id");

            for (var i = 0; i < removedArray.length; i++) {
                if (removedArray[i] == removedId) {
                    removedArray.splice(i, 1);
                }
            }
            UNGM.UNSPSC.removeCode(removedId);

            // Remove selected UNSPSC div from place holder
            var parentMain = sender.parents($this.divNoticeSearchUNSPSC);
            parentMain.find(".noticeSelectedUNSPSC").each(function () {
                if ($(this).data("ID") == removedId) {
                    $(this).closest("div").remove();
                }
            });
            //hide selected UNSPSC div when all are removed.
            if (removedArray.length == 0) {
                $this.divNoticeSearchUNSPSC.hide();
            }
            // Set pageing default
            $this.pageIndex = 0;
            $this.paging = false;
            $this.Search($this).call();
        }
    }


    namespace.NoticeSearch.prototype.clearCountry = function ($this) {
        return function () {
            var change = $(this).val();
            if (change == 1) {
                $this.selNoticeCountry.parent(".filterRow").find('.ui-autocomplete-input').val("");
                $(this).val("0");
            }
        }
    }

    namespace.NoticeSearch.prototype.clearAgency = function ($this) {
        return function () {
            var change = $(this).val();
            if (change == 1) {
                $this.selNoticeAgency.parent(".filterRow").find('.ui-autocomplete-input').val("");
                $(this).val("0");
            }
        }
    }

    namespace.NoticeSearch.prototype.showUNSPSC = function ($this) {
        return function () {
            UNGM.UNSPSC.saveCallback = $this.unspscSaveCallback($this);

            //refresh the picker selection
            var picker = $this.divNoticeUNSPSCSelector.find(".unspsc");
            UNGM.UNSPSC.bindSelectedCodes(picker);

            var winHeight = $(window).height();
            $this.divNoticeUNSPSCSelector.dialog({
                modal: true,
                title: $this.unspscTitleText,
                width: $(window).width() < 480 ? '100%' : '66%',
                height: $(window).width() < 480 ? $(window).height() : winHeight - (winHeight * 0.2),
                hide: { effect: 'fade', duration: 100 },
                show: { effect: 'fade', duration: 100 }
            });
        }
    }

    namespace.NoticeSearch.prototype.unspscSaveCallback = function ($this) {
        return function () {
            //Add selected UNSPSC to place holder
            var holder = $this.divNoticeSearchUNSPSC;
            holder.find("div").remove();
            var unspscId = null;
            var Code = null;
            var UNSPSCName = null;
            $this.selectedUNSPSCs = [];
            for (var i = 0; i < UNGM.UNSPSC.selectedCodes.length; i++) {
                unspscId = parseInt(UNGM.UNSPSC.selectedCodes[i].Id, 10);
                Code = UNGM.UNSPSC.selectedCodes[i].Code;
                UNSPSCName = UNGM.UNSPSC.selectedCodes[i].Name;
                $this.selectedUNSPSCs.push(unspscId);

                // Add to place holder
                var elem = $("<div class='noticeSelectedUNSPSC'></div>");
                elem.data("ID", unspscId);

                var span = $("<span class='noticeSelectedUNSPSC'></span>");
                span.html(Code + " - " + UNSPSCName);

                var removeButton = $("#removeButtonTemplate").clone();
                removeButton.removeAttr("id").show();
                removeButton.data("Id", unspscId);
                removeButton.bind("click", $this.removeUNSPSCClicked($this));

                elem.append(span).append(removeButton);
                holder.append(elem);
            }

            if ($this.selectedUNSPSCs.length > 0) {
                holder.show();
            }
            else {
                holder.hide();
            }

            var selector = $this.divNoticeUNSPSCSelector;
            if (selector.is(':data(dialog)') || selector.hasClass('ui-dialog-content')) {
                selector.dialog("close");
            }
            $this.pageIndex = 0;
            $this.paging = false;
            $this.Search($this).call();
        }
    }


    namespace.NoticeSearch.prototype.selectNoticeType = function ($this) {
        return function (evt) {
            var sender = $(evt.currentTarget);
            var checked = sender.prop("checked");
            var typeId = sender.attr("id");

            if (checked) {
                $this.selectedNoticeTypes.push(typeId);
            }
            else {
                for (var i = 0; i < $this.selectedNoticeTypes.length; i++) {
                    if ($this.selectedNoticeTypes[i] == typeId) {
                        $this.selectedNoticeTypes.splice(i, 1);
                    }
                }
            }
            // Set pageing default
            $this.pageIndex = 0;
            $this.paging = false;
            $this.Search($this).call();
        }
    }

    namespace.NoticeSearch.prototype.selectSustainableNotice = function ($this) {
        return function (evt) {
            var sender = $(evt.currentTarget);
            var checked = sender.prop("checked");

            $this.sustainableNotice = checked;

            // Set pageing default
            $this.pageIndex = 0;
            $this.paging = false;
            $this.Search($this).call();
        }
    }

    namespace.NoticeSearch.prototype.selectTasStatus = function ($this) {
        return function (evt) {
            var sender = $(evt.currentTarget);
            var checked = sender.prop("checked");
            var typeId = sender.attr("id");

            if (checked) {
                $this.selectedTasStatus.push(typeId);
            }
            else {
                if ($this.selectedTasStatus.length == 1) {
                    $this.selectedTasStatus = [];
                }
                else {
                    for (var i = 0; i < $this.selectedTasStatus.length; i++) {
                        if ($this.selectedTasStatus[i] == sender) {
                            $this.selectedTasStatus.splice(i, 1);
                        }
                    }
                }
            }

            // Set pageing default
            $this.pageIndex = 0;
            $this.paging = false;
            $this.Search($this).call();
        }
    }

    namespace.NoticeSearch.prototype.selectTypeOfCompetition = function ($this) {
        return function (evt) {
            var sender = $(evt.currentTarget);
            var checked = sender.prop("checked");

            if (checked) {
                $this.selectedTypeOfCompetitions.push(sender.val());
            }
            else {
                if ($this.selectedTypeOfCompetitions.length == 1) {
                    $this.selectedTypeOfCompetitions = [];
                }
                else {
                    for (var i = 0; i < $this.selectedTypeOfCompetitions.length; i++) {
                        if ($this.selectedTypeOfCompetitions[i] == sender.val()) {
                            $this.selectedTypeOfCompetitions.splice(i, 1);
                        }
                    }
                }
            }

            // Set pageing default
            $this.pageIndex = 0;
            $this.paging = false;
            $this.Search($this).call();
        }
    }

    namespace.NoticeSearch.prototype.clearAll = function ($this) {
        return function () {

            $this.txtNoticeFilterTitle.val('');
            $this.txtNoticeFilterDesc.val('');
            $this.txtNoticeFilterRef.val('');

            $this.selectedCountries = [];
            $this.selectedNoticeTypes = [];
            $this.selectedTasStatus = [];
            $this.selectedTypeOfCompetitions = [];
            $this.sustainableNotice = false;

            $this.selNoticeCountry.find('option').eq(0).attr('selected', 'selected');
            $this.selNoticeCountry.parent(".filterRow").find('.ui-autocomplete-input').val("");
            $this.divNoticeSearchCountry.children("div").remove();
            $this.divNoticeSearchCountry.hide();

            $this.chbNoticeTypeClass.removeAttr('checked');
            $this.chbTasStatusClass.removeAttr('checked');
            $this.chbSustainableNoticeClass.removeAttr('checked');
            $this.chbTypeOfCompetitions.removeAttr('checked');

            if (!$this.hidWidgetAgency.val()) {
                $this.selectedAgencies = [];
                $this.selNoticeAgency.find('option').eq(0).attr('selected', 'selected');
                $this.selNoticeAgency.parent(".filterRow").find('.ui-autocomplete-input').val("");
                $this.divNoticeSearchAgency.children("div").remove();
                $this.divNoticeSearchAgency.hide();
            }

            if (!$this.hidWidgetUNSPSCs.val()) {
                $this.selectedUNSPSCs = [];
                $this.divNoticeSearchUNSPSC.children("div").remove();
                $this.divNoticeSearchUNSPSC.hide();
            }

            if (!$this.hidNoticeDisplayType.val() || $this.hidNoticeDisplayType.val().indexOf('All') > 0) {
                $this.txtNoticePublishedFrom.val('');
                $this.txtNoticePublishedTo.val('');
                $this.txtNoticeDeadlineFrom.val('');
                $this.txtNoticeDeadlineTo.val('');
            }

            $this.pageIndex = 0;
            $this.paging = false;
            $this.Search($this).call();
        }
    }

    namespace.NoticeSearch.prototype.editNotice = function ($this) {
        return function (evt) {
            evt.stopPropagation();
            var id = $(evt.currentTarget).data("noticeid");
            UNGM.throbOver($this.divNoticeData);
            var url = UNGM.siteRoot + 'UNUser/Notice/PopupEdit?id=' + id;
            $.ajax({
                url: url,
                type: 'POST',
                contentType: 'application/json',
                success: function (data) { $this.onNoticeEdit(data, $this); }
            });
        }
    },
    namespace.NoticeSearch.prototype.onNoticeEdit = function (data, $this) {

        var winHeight = $(window).height();
        $this.divEditNotice.hide().html(data).dialog({
            modal: true,
            width: '90%',
            height: winHeight - (winHeight * 0.1),
            hide: { effect: 'fade', duration: 100 },
            show: { effect: 'fade', duration: 100 },
            open: function () {
                UNGM.initTabsIn($this.divEditNotice);
                UNGM.Validation.init();
                var mainDialog = $this.divEditNotice;
                mainDialog.find("form").each(function () {
                    $(this).find(".validationIcon,.field-validation-error").hide();
                    $(this).removeData("validator");
                    $(this).removeData("unobtrusiveValidation");
                    $.validator.unobtrusive.parse(this);
                });
            },
            close: function () {
                // destroy the CKEDitor otherwise we'll get errors
                for (var instanceName in CKEDITOR.instances) {
                    CKEDITOR.instances[instanceName].updateElement();
                    CKEDITOR.instances[instanceName].destroy(); // The instances must be destroyed or the next time we open a modal we get errors
                }
            }
        });

        UNGM.hideThrobber();
    }

    namespace.NoticeSearch.prototype.noticeClicked = function ($this) {
        return function (evt) {
            if (!$(evt.target).is("a *")&& !$($(evt).attr('target')).hasClass('buttonActionLinkViewDocuments')) {
                UNGM.gaEvent('Access to notice detail', 'Click', 'Whole table row -> Dialog');
                UNGM.throbOver($this.divNoticeData);
                var id = $(evt.currentTarget).data("noticeid");
                var isPicker = ($this.hidIsPicker.val().toLowerCase() == 'true' ? "?isPicker=true" : "");
                $.ajax({
                    url: UNGM.siteRoot + 'Public/Notice/Popup/' + id + isPicker,
                    type: 'GET',
                    success: function(data) { $this.onGotNoticeDetail(data, $this); },
                    cache: true
                })
            } else {
                UNGM.gaEvent('Access to notice detail', 'Click', 'Button with icon -> New tab');
            }
        }

    }

    namespace.NoticeSearch.prototype.onGotNoticeDetail = function (detail, $this) {
        UNGM.hideThrobber();
        var winHeight = $(window).height();
        var detailElem = $(detail);
        var title = detailElem.find("#noticeTitle").html();

        $("<div>").attr("id", "noticeDetail")
            .html(detail)
            .dialog({
                modal: true,
                width: $(window).width() < 480 ? '100%' : '66%',
                title: title,
                height: $(window).width() < 480 ? $(window).height() : winHeight - (winHeight * 0.2),
                hide: { effect: 'fade', duration: 100 },
                show: { effect: 'fade', duration: 100 },
                open: function () {
                    UNGM.ExpandableContacts.init();
                    UNGM.NoticeView.init();
                    UNGM.UNSPSC.init();
                    UNGM.initTabs();
                    UNGM.Notice.ViewDocument.initFor("#notice-detail-view-document-container");
                    $("#noticeDetail .pickNotice").bind("click", $this.pickNoticeClicked($this));
                    $this.ReAssignEOIAndIntendButtons($this.parameter);
                    $('#wholePage').css('overflow', 'hidden');
                },
                close: function () { $("#noticeDetail").dialog("destroy"); }
            });
    }

    namespace.NoticeSearch.prototype.pickNoticeClicked = function ($this) {
        return function (evt) {
            // Call this back at the root
            UNGM.NoticePickerCallbacks.pickNoticeClicked(evt);
        }
    }

    namespace.NoticeSearch.prototype.expressInterestClick = function ($this) {
        return function (evt) {
            evt.stopPropagation();

            UNGM.throbOver(this.divNoticeData);

            $.ajax({
                url: UNGM.siteRoot + 'Public/Notice/IsUserAuthenticated',
                type: 'GET',
                contentType: 'application/json',
                success: function (isAuthenticated) {
                    if (isAuthenticated === "False") {
                        window.location = UNGM.siteRoot + 'Account/Account/Login?returnUrl=' + window.location.pathname;
                    } else {
                        var $target = $(evt.currentTarget);

                        // if notice is published through UNGM and does not require EOI then show a detail message and return
                        if ($target.data('noticecanbeexpressedinterest') === "False") {
                            $.confirm(
                                $this.CanNotExpressInterestTitle,
                                $this.CanNotExpressInterestMessage,
                                $this.CanNotExpressInterestConfirmButton,
                                "",
                                function () { }
                            );

                            UNGM.hideThrobber();
                            return;
                        }

                        var id = $target.data("noticeid");
                        var agencyid = $target.data("agencyid");
                        var data = {
                            noticeId: id,
                            agencyId: agencyid
                        };

                        var url = UNGM.siteRoot + 'Public/Notice/ExpressInterest';
                        $.ajax({
                            url: url,
                            data: JSON.stringify(data),
                            type: 'POST',
                            contentType: 'application/json',
                            success: function (response) {
                                if (response.success) {
                                    $target.hide();
                                    $target.parents(".table-cell-eoi").next().show();
                                    $(".view-document-container").show();
                                    if (response.isVendorSubsribedToTenderAlertService === false && $this.ShowSubscriptionMessage !== "False") {
                                        $this.ShowSubscriptionMessage = "False";

                                        $.confirmWithNoCallback(
                                            $this.SubscribeTitle,
                                            $this.SubscribeMessage,
                                            $this.SubscribeConfirmButtonText,
                                            $this.SubscribeCancelButtonText,
                                            function () {
                                                window.location.href = UNGM.siteRoot + '/Vendor/Tas';
                                            },
                                            function() {
                                                $.ajax({
                                                    url: UNGM.siteRoot + 'Public/Notice/MarkVendorAsNotInterestedInSubscribingToTenderAlertService',
                                                    type: 'POST'
                                                });
                                            }
                                        );
                                    }
                                }
                                else {
                                    $.confirm(
                                        $this.EOIFailureTitle, // Title
                                        $this.EOIFailureMessage, // Message
                                        $this.EOIFailureOK, //yes button text (blank hides button)
                                        "", //no button text (blank hides button)
                                        function ($this) {
                                        } //"yes" callback
                                    );
                                }
                                UNGM.hideThrobber();
                            }
                        });
                    }
                }
            });


        }
    }

    namespace.NoticeSearch.prototype.expandUNSPSCSelectedClick = function ($this) {
        return function (evt) {
            var lnkExpandUNSPSC = $(evt.currentTarget);

            if (lnkExpandUNSPSC.text().match("Expand")) {
                $this.divNoticeSearchUNSPSC.find('.noticeSelectedUNSPSC').show();
                lnkExpandUNSPSC.text("Collapse all");
            }
            else {
                $this.divNoticeSearchUNSPSC.find('.noticeSelectedUNSPSC').hide();
                lnkExpandUNSPSC.text("Expand all");
            }
        }
    }

    namespace.NoticeSearch.prototype.renderReadOnlyUNSPSCs = function ($this) {
        //Add selected UNSPSC to place holder
        var holder = $this.divNoticeSearchUNSPSC;
        holder.find("div").remove();
        var unspscId = null;
        var Code = null;
        var UNSPSCName = null;
        $this.selectedUNSPSCs = [];
        UNGM.UNSPSC.selectedCodes = JSON.parse($this.hidWidgetUNSPSCs.val());

        // Add to place holder
        var elemExpand = $("<a href='javascript:;'>Expand all</a>");
        elemExpand.on("click", $this.expandUNSPSCSelectedClick($this));
        holder.append(elemExpand);

        for (var i = 0; i < UNGM.UNSPSC.selectedCodes.length; i++) {
            unspscId = parseInt(UNGM.UNSPSC.selectedCodes[i].Id, 10);
            Code = UNGM.UNSPSC.selectedCodes[i].Code;
            UNSPSCName = UNGM.UNSPSC.selectedCodes[i].Name;
            $this.selectedUNSPSCs.push(unspscId);

            // Add to place holder
            var elem = $("<div class='noticeSelectedUNSPSC' style='display: none;'></div>");
            elem.data("ID", unspscId);

            var span = $("<span class='noticeSelectedUNSPSC'></span>");
            span.html(Code + " - " + UNSPSCName);

            elem.append(span);
            holder.append(elem);
        }

        if ($this.selectedUNSPSCs.length > 0) {
            holder.show();
        }
        else {
            holder.hide();
        }

        var selector = $this.divNoticeUNSPSCSelector;
        if (selector.is(':data(dialog)') || selector.hasClass('ui-dialog-content')) {
            selector.dialog("close");
        }

        $this.lnkShowUNSPSC.hide();

        $this.pageIndex = 0;
        $this.paging = false;
        if (!$this.hidWidgetUNSPSCs.val()) {
            $this.Search($this).call();
        }
    }

    namespace.NoticeSearch.prototype.renderReadOnlyAgency = function ($this) {
        var widgetAgency = JSON.parse($this.hidWidgetAgency.val());
        // Add selected agency to the array
        $this.selectedAgencies.push(widgetAgency.Id);

        //Add selected agency to place holder
        var holder = $this.divNoticeSearchAgency;
        var elem = $("<div class='NoticeSelectedAgency'></div>");
        elem.data("ID", widgetAgency.Id);

        var span = $("<span class='NoticeSelectedAgencyName'></span>");
        span.html(widgetAgency.Name);

        elem.append(span);
        holder.append(elem);

        $this.divNoticeSearchAgency.show();
    }

    namespace.NoticeSearch.prototype.renderReadOnlyDisplayType = function ($this) {
        var noticeDisplayType = JSON.parse($this.hidNoticeDisplayType.val());
        var date = new Date();
        var yDate = new Date(date.setDate(date.getDate() - 1))
        var today = $.datepicker.formatDate('dd-M-yy', new Date());
        var yesterday = $.datepicker.formatDate('dd-M-yy', yDate);

        switch (noticeDisplayType.Id) {
            case 1: // All                
                $this.txtNoticePublishedFrom.val('');
                $this.txtNoticePublishedTo.val('');
                $this.txtNoticeDeadlineFrom.val('');
                $this.txtNoticeDeadlineTo.val('');
                break;
            case 2: // Current active
                $this.txtNoticePublishedFrom.val('');
                $this.txtNoticePublishedTo.val(today);
                $this.txtNoticeDeadlineFrom.val(today);
                $this.txtNoticeDeadlineTo.val('');
                $this.divNoticeMain.find(".filterDatePairLabel").hide();
                $this.divNoticeMain.find(".filterDatePair").hide();
                break;
            case 3: // Previous
                $this.txtNoticePublishedFrom.val('');
                $this.txtNoticePublishedTo.val(yesterday);
                $this.txtNoticeDeadlineFrom.val('');
                $this.txtNoticeDeadlineTo.val(yesterday);
                $this.divNoticeMain.find(".filterDatePairLabel").hide();
                $this.divNoticeMain.find(".filterDatePair").hide();
                break;
        }
    }

}(window.UNGM.Notice = window.UNGM.Notice || {}));
;
window.UNGM.Notice.ViewDocument = {
    initFor: function (containerSelector) {
        $(containerSelector).get(0).addEventListener("click", UNGM.Notice.ViewDocument.onViewDocumentClicked, true);
    },
    onViewDocumentClicked: function (event) {
        var target = $(event.target);
        if (target.is(".buttonActionLinkViewDocuments")) {
            event.stopPropagation();
            $.get(`/Public/Notice/ViewDocument/${target.data("notice-id")}`)
                .done(
                    function (data) {
                        if (data.Url) {
                            window.open(data.Url);
                        } else if (data.AlternativeText) {
                            $.confirm(data.AlternativeText.HeaderText, data.AlternativeText.MainText, data.AlternativeText.ButtonText, "", function () { });
                        }
                    });
        }
    }
};
UNGM.NoticeView = {
    init: function () {
        $(".btnDetailsRevision, #revisionsHolder .editableListItem > span:not(.options)").css("cursor", "pointer").unbind("click").bind("click", UNGM.NoticeView.onRevisionDetailsClicked);
    },
    onRevisionDetailsClicked: function (evt) {
        evt.stopPropagation();
        $details = $(evt.currentTarget).parents(".editableListItem").find(".revisionDetails");

        // Set the textarea heights
        $(".displayTextarea").each(function (i, el) {
            $(el).height(el.scrollHeight);
        });

        var button = $details.prev(".options").find(":button.btnDetailsRevision")
        // Get the corresponding detail div
        $details.slideToggle(function () {
            button.prop("value", $details.is(":visible") ? button.data("hide") : button.data("show"));
        });

        // Set the textarea heights. Not sure why this has to be called twice, but it dowsn't work reliably with only one call. Maybe setting a timeout?
        $(".displayTextarea").each(function (i, el) {
            $(el).height(el.scrollHeight);
        });
    }
};
window.UNGM.Registration = {
    userExists: false,
    init: function () {
        $("#companyBox").bind("focusout", UNGM.Registration.companyLostFocus);
        $("#companyBox").bind("focusin", UNGM.Registration.companyGotFocus);
        $("#btnContinueInactivated").bind("click", UNGM.Registration.activationPendingClicked);
        $("#autoPopulateUserDetails").bind("change", UNGM.Registration.autoPopulateUserDetailsChanged);
        $("#frmVendorRegister").bind("submit", UNGM.Registration.onFormSubmit);

        if ($("#btnContinueInactivated").attr("url") == undefined) {
            $("#btnContinueInactivated").hide();
        }
    },  
    activationPendingClicked: function (evt) {
        window.location.href = $(evt.currentTarget).attr("url");
    },
    companyLostFocus: function (evt) {
        var sender = $(evt.currentTarget);
        var company = sender.val();
        var vendorId = $('#Id').val();
        var obj = { companyName: (unescape(company)).replace(/<\/?[^>]+(>|$)/g, ""), vendorID: vendorId };
        $.ajax({
            url: UNGM.siteRoot + 'Vendor/Registration/IsCompanyAvailable',
            type: 'POST',
            data: JSON.stringify(obj),
            contentType: 'application/json',
            success: UNGM.Registration.onCheckedCompany,
            error: function (xhr, status, error) {
                alert("Error!" + xhr.status);
            }
        });
    },
    onCheckedCompany: function (isAvailable) {
        var box = $("#companyBox");
        if (isAvailable) {
            UNGM.Validation.markElementValid(box);
            $("#companyExists").hide();
            UNGM.Validation.validateElement(box);
        } else {
            UNGM.Validation.markElementInvalid(box);
            $("#companyExists").show();
        }
    },
    companyGotFocus: function (evt) {
        $("#CompanyNameExists").hide();
    },
    autoPopulateUserDetailsChanged: function (e) {
        $checkbox = $(e.currentTarget);

        if ($checkbox.is(":checked")) {
            $("#frmVendorRegister #FirstName").val($("#frmVendorRegister #CompanyDirectorFirstName").val());
            $("#frmVendorRegister #LastName").val($("#frmVendorRegister #CompanyDirectorLastName").val());
        }
        else {
            $("#frmVendorRegister #FirstName,#LastName").val("");
        }
    },
    onFormSubmit: function (e) {
        $("#ConfirmPassword").blur();

        var $form = $(e.currentTarget);
        
        // $form.valid() doesn't work when the error is 'a username exists'
        if (!$form.valid() || !UNGM.EmailValidation.verified) {
            e.preventDefault();
            return false;
        }

        // If it gets here means that we can submit
        UNGM.throbOver($form);
    }
};
;
window.UNGM.UNSPSC = {
    timeoutFunc: null,
    select: "SELECT",
    remove: "REMOVE",
    showSelectRemoveAll: false,
    isReadOnly: false,
    showSelectAsParent: false,
    selectedCodes: [],
    selectParentCode: null,
    saveCallback: null,
    selectCallBack: null,
    alreadySavedCodes: [],
    selectCodesAfterSearch: null,
    asrYear: null,
    pendingSearchRequest: null,
    init: function () {
        $(".nodeName.expandable, .nodeName.expanded").off("click").on("click", UNGM.UNSPSC.nodeClicked);
        $(".txtSearchUnspsc").off("keyup").on("keyup", UNGM.UNSPSC.filterKeyUp);
        $(".chkUnspsc").off("change").on("change", UNGM.UNSPSC.checkboxChanged);
        $(".btnSaveUnspsc").off("click").on("click", UNGM.UNSPSC.saveClicked);
        $(".lnkRemoveUnspsc").off("click").on("click", UNGM.UNSPSC.removeButtonClicked);
        $(".lnkUspscRemoveAll").off("click").on("click", UNGM.UNSPSC.removeAllClicked);
        $(".unspscTree").off("click").on("click", ".lnkUspscRemoveChildren", { select: UNGM.UNSPSC.remove }, UNGM.UNSPSC.selectOrRemoveChildrenClicked);
        $(".unspscTree").off("click").on("click", ".lnkUspscSelectChildren", { select: UNGM.UNSPSC.select }, UNGM.UNSPSC.selectOrRemoveChildrenClicked);
        $(".lnkUspscSelectParent").off("click").on("click", UNGM.UNSPSC.selectCodeAsParent);
        $(".lnkExpandAll").off("click").on("click", UNGM.UNSPSC.expandAllClicked);
        $(".lnkCollapseAll").off("click").on("click", UNGM.UNSPSC.collapseAllClicked);
        UNGM.UNSPSC.updateUnspscSelectedCodeHeight();
    },
    getSelectedCodes: function (picker) {
        UNGM.UNSPSC.selectedCodes = picker.find(".unspscSelectedCodes").data("codes");
    },
    setSelectedCodes: function (picker) {
        picker.find(".unspscSelectedCodes").data("codes", UNGM.UNSPSC.selectedCodes);
    },
    removeCode: function (removedId) {
        // remove it from the picker array UNGM.UNSPSC.selectedCodes
        for (var i = 0; i < UNGM.UNSPSC.selectedCodes.length; i++) {
            if (UNGM.UNSPSC.selectedCodes[i].Id == removedId) {
                UNGM.UNSPSC.selectedCodes.splice(i, 1);
            }
        }
    },
    nodeClicked: function (evt) {
        var sender = $(evt.currentTarget);
        var appendTo;
        if (sender.siblings(".selectRemoveAll").length > 0) {
            appendTo = sender.siblings(".selectRemoveAll");
        }
        else if (sender.siblings(".selectAsParent").length > 0) {
            appendTo = sender.siblings(".selectAsParent");
        }
        else {
            appendTo = sender;
        }
        var existingChildren = sender.siblings(".unspscChildren");
        if (existingChildren.length > 0) {
            if (existingChildren.is(":visible")) {
                existingChildren.hide();
                sender.removeClass("expanded").addClass("expandable");
            }
            else {
                existingChildren.show();
                sender.removeClass("expandable").addClass("expanded");
            }
            UNGM.UNSPSC.updateUnspscSelectedCodeHeight();
        }
        else {
            var nodeId = sender.data("nodeid");
            var picker = sender.parents(".unspsc");
            UNGM.throbOver(picker);
            $.ajax({
                url: UNGM.siteRoot + 'UNSPSC/Children/' + nodeId
                    + "?showSelectRemoveAll=" + UNGM.UNSPSC.showSelectRemoveAll
                    + "&isReadOnly=" + UNGM.UNSPSC.isReadOnly
                    + "&showSelectAsParent=" + UNGM.UNSPSC.showSelectAsParent
                    + "&isASRUNSPSC=" + (UNGM.UNSPSC.asrYear != null && UNGM.UNSPSC.asrYear != 0),
                type: 'GET',
                success: function (data) {
                    var elem = $("<div class='unspscChildren'></div");
                    elem.append(data);
                    appendTo.after(elem);
                    elem.find(".chkUnspsc").bind("change", UNGM.UNSPSC.checkboxChanged);
                    sender.removeClass("expandable").addClass("expanded");
                    $(".nodeName.expandable, .nodeName.expanded").off("click").on("click", UNGM.UNSPSC.nodeClicked);
                    $(".lnkUspscSelectParent").off("click").on("click", UNGM.UNSPSC.selectCodeAsParent);
                    // Check the codes in the tree
                    UNGM.UNSPSC.checkUncheckCodesInTree(picker);
                    UNGM.UNSPSC.updateUnspscSelectedCodeHeight();
                    UNGM.hideThrobber();
                }
            });
        }
    },
    filterKeyUp: function (evt) {
        if (UNGM.UNSPSC.timeoutFunc) {
            clearTimeout(UNGM.UNSPSC.timeoutFunc);
        }
        UNGM.UNSPSC.timeoutFunc = setTimeout(function () {
            UNGM.UNSPSC.search(evt);
        }, 600);
    },
    search: function (evt) {
        if (UNGM.UNSPSC.pendingSearchRequest) {
            UNGM.UNSPSC.pendingSearchRequest.abort();
        }

        var sender = $(evt.currentTarget);
        var appendTo;
        if (sender.parents(".unspsc").find(".selectAsParent").length > 0) {
            appendTo = sender.parents(".unspsc").find(".selectAsParent")[0];
        }
        var picker = sender.parents(".unspsc");
        var searchText = sender.val();

        // /i means: canse insensitive
        if (searchText.match(/^unselect:/i)) {
            UNGM.UNSPSC.selectCodesAfterSearch = false;
            searchText = searchText.replace(/unselect:/i, '');
        }
        else if (searchText.match(/^select:/i)) {
            UNGM.UNSPSC.selectCodesAfterSearch = true;
            searchText = searchText.replace(/select:/i, '');
        }

        if (searchText == null || searchText == '' || (UNGM.UNSPSC.asrYear != null && UNGM.UNSPSC.asrYear != 0 && searchText.length < 4)) {
            picker.find(".unspscTree").show();
            picker.find(".unspscSearchResults").hide();
            picker.find(".unspscNoResults").hide();
        }
        else {
            picker.find(".unspscTree").hide();
            UNGM.throbOver(picker);
            UNGM.UNSPSC.pendingSearchRequest = $.ajax({
                url: UNGM.siteRoot + 'UNSPSC/Search',
                type: 'POST',
                data: {
                    filter: searchText,
                    isReadOnly: UNGM.UNSPSC.isReadOnly,
                    showSelectAsParent: UNGM.UNSPSC.showSelectAsParent,
                    alreadySavedCodes: UNGM.UNSPSC.alreadySavedCodes,
                    asrYear: UNGM.UNSPSC.asrYear
                },
                success: function (data) {
                    UNGM.UNSPSC.onGotSearch(data, picker, searchText);
                }
            });
        }
        // append selectAsParent to every node except level rank 5
        var tree = picker.find(".unspscTree");
        $(tree).find('*[data-isparent="True"]').each(function () {
            var cur = $(this);
            if ($(this).siblings(".selectAsParent").length < 1) {
                appendTo.after($(this));
            }
        });
    },
    onGotSearch: function (data, picker, searchText) {
        if (data && data.trim().length > 0) {
            picker.find(".unspscSearchResults").html(data).show().find(".chkUnspsc").bind("change", UNGM.UNSPSC.checkboxChanged);
            picker.find(".unspscSearchResults").find(".nodeName.expanded").bind("click", UNGM.UNSPSC.nodeClicked);
            picker.find(".unspscSearchResults").find(".lnkUspscSelectParent").off("click").on("click", UNGM.UNSPSC.selectCodeAsParent);
            picker.find(".unspscNoResults").hide();

            // Check the codes of the result
            UNGM.UNSPSC.checkUncheckCodesInTree(picker);
        }
        else {
            picker.find(".unspscNoResults").show();
            picker.find(".unspscSearchResults").hide();
        }

        // Functionality to bulk select/unselect codes
        if (UNGM.UNSPSC.selectCodesAfterSearch !== null) {
            var allCodesInFilter = $.map(searchText.split(','), $.trim);
            UNGM.UNSPSC.getSelectedCodes(picker);

            for (var index = 0; index < allCodesInFilter.length; index++) {
                var $codeCbx = $("input[data-unspscode='" + allCodesInFilter[index] + "']");

                if ($codeCbx.length > 0) {
                    var codeInFilter = {
                        Id: $codeCbx.data("unspscid"),
                        Code: $codeCbx.data("unspscode"),
                        Name: $codeCbx.parent().siblings(".nodeName").html().replace($codeCbx.data("unspscode") + ' - ', '')
                    };

                    if (UNGM.UNSPSC.selectCodesAfterSearch == true) {
                        if ($codeCbx.is(":checked") == false) {
                            $codeCbx.prop("checked", true);
                            UNGM.UNSPSC.selectedCodes.push(codeInFilter);
                        }
                    }
                    else {
                        if ($codeCbx.is(":checked") == true) {
                            $codeCbx.prop("checked", false);
                            for (var i = 0; i < UNGM.UNSPSC.selectedCodes.length; i++) {
                                if (UNGM.UNSPSC.selectedCodes[i].Id == codeInFilter.Id) {
                                    UNGM.UNSPSC.selectedCodes.splice(i, 1);
                                }
                            }
                        }
                    }
                }
            }

            UNGM.UNSPSC.bindSelectedCodes(picker);
            UNGM.UNSPSC.selectCodesAfterSearch = null;
        }
        UNGM.hideThrobber();
    },
    checkboxChanged: function (evt) {
        var sender = $(evt.currentTarget);
        var picker = sender.parents(".unspsc");
        var unspscid = sender.data("unspscid");
        var unspscode = sender.data("unspscode");
        var name = sender.parent().siblings(".nodeName").find("span:last").html();
        name = name.replace(unspscode + ' - ', '');
        var selectedCode = {
            Id: unspscid,
            Code: unspscode,
            Name: name
        };
        UNGM.UNSPSC.getSelectedCodes(picker);

        if (sender.is(":checked")) {
            for (var i = 0; i < UNGM.UNSPSC.selectedCodes.length; i++) {
                if (UNGM.UNSPSC.selectedCodes[i].Id == unspscid) {
                    return; // code is already there so do nothing
                }
            }
            UNGM.UNSPSC.selectedCodes.push(selectedCode);
        }
        else {
            for (var i = 0; i < UNGM.UNSPSC.selectedCodes.length; i++) {
                if (UNGM.UNSPSC.selectedCodes[i].Id == unspscid) {
                    UNGM.UNSPSC.selectedCodes.splice(i, 1);
                }
            }
        }
        UNGM.UNSPSC.bindSelectedCodes(picker);
    },
    bindSelectedCodes: function (picker) {
        var holder = picker.find(".unspscSelectedCodeTree");
        holder.empty();

        UNGM.UNSPSC.getSelectedCodes(picker);
        if (UNGM.UNSPSC.selectedCodes) {
            UNGM.UNSPSC.selectedCodes = UNGM.UNSPSC.selectedCodes.sort(UNGM.UNSPSC.sortComparer);
        }

        if (UNGM.UNSPSC.selectedCodes) {
            for (var i = 0; i < UNGM.UNSPSC.selectedCodes.length; i++) {
                var elem = $("<div class='unspscSelectedCode'></div>");
                var span = $("<span class='unspscSelectedCodeName'></span>");
                var removeButton = $("#removeButtonTemplate").clone();
                var deleted = "";
                removeButton.removeAttr("id").show();
                removeButton.data("unspscid", UNGM.UNSPSC.selectedCodes[i].Id);
                span.html(UNGM.UNSPSC.selectedCodes[i].Code + " - " + UNGM.UNSPSC.selectedCodes[i].Name);
                if (UNGM.UNSPSC.selectedCodes[i].IsDeleted) {
                    span.addClass("unspscDeleted");
                    deleted = $("<span class='unspscDeletedInfo'></span>");
                }
                removeButton.bind("click", UNGM.UNSPSC.removeButtonClicked);
                elem.append(span).append(deleted).append(removeButton);
                holder.append(elem);
            }
        }

        if (UNGM.UNSPSC.selectedCodes && UNGM.UNSPSC.selectedCodes.length > 0) {
            picker.find(".unspscNoCodes").hide();
        }
        else {
            picker.find(".unspscNoCodes").show();
        }
        $("#codeCount").html(UNGM.UNSPSC.selectedCodes ? UNGM.UNSPSC.selectedCodes.length : "");
        UNGM.UNSPSC.checkUncheckCodesInTree(picker);
        UNGM.UNSPSC.setSelectedCodes(picker);
    },
    removeButtonClicked: function (evt) {
        var sender = $(evt.currentTarget);
        var picker = sender.parents(".unspsc");
        var unspscid = sender.data("unspscid");
        UNGM.UNSPSC.getSelectedCodes(picker);
        if (UNGM.UNSPSC.selectedCodes) {
            for (var i = 0; i < UNGM.UNSPSC.selectedCodes.length; i++) {
                if (UNGM.UNSPSC.selectedCodes[i].Id == unspscid) {
                    UNGM.UNSPSC.selectedCodes.splice(i, 1);
                }
            }
        }
        UNGM.UNSPSC.setSelectedCodes(picker);
        UNGM.UNSPSC.bindSelectedCodes(picker);
    },
    saveClicked: function (evt) {
        var picker = $(evt.currentTarget).parents(".unspsc");
        if (!UNGM.UNSPSC.saveCallback) {
            throw ("the UNGM.UNSPSC.saveCallback property must be set in order to use the 'save selected codes' button in this control");
        }
        UNGM.UNSPSC.getSelectedCodes(picker);
        UNGM.UNSPSC.saveCallback(evt);
    },
    removeAllClicked: function (evt) {
        var picker = $(evt.currentTarget).parents(".unspsc");
        UNGM.UNSPSC.selectedCodes = [];
        UNGM.UNSPSC.setSelectedCodes(picker);
        UNGM.UNSPSC.bindSelectedCodes(picker);
    },
    selectOrRemoveChildrenClicked: function (evt) {
        UNGM.throbOver($(".unspsc"));
        var picker = $(evt.currentTarget).parents(".unspsc");
        // get the children from the server
        var codeId = $(evt.currentTarget).data("unspscid")
        $.ajax({
            url: UNGM.siteRoot + 'UNSPSC/GetChildren?id=' + codeId,
            type: 'GET',
            contentType: 'application/json',
            success: function (data) {
                UNGM.UNSPSC.onGotChildrenForSelectOrRemove(data, picker, evt.data.select);
            }
        });
    },
    onGotChildrenForSelectOrRemove: function (children, picker, select) {
        // get the current ids
        UNGM.UNSPSC.getSelectedCodes(picker);
        var currentIds = [];
        // for some reason $.map isn't working here. So do it the old fashioned way.
        $.each(UNGM.UNSPSC.selectedCodes, function (index, value) {
            currentIds.push(value.Id);
        });
        for (var i = 0; i < children.length; i++) {
            if (select == UNGM.UNSPSC.select) {
                // push
                if ($.inArray(children[i].Id, currentIds) == -1) {
                    UNGM.UNSPSC.selectedCodes.push(children[i]);
                };
            } else {
                // pop
                UNGM.UNSPSC.selectedCodes = $.map(UNGM.UNSPSC.selectedCodes, function (u) { if (u.Id != children[i].Id) { return u; } });
            }
        }
        // set the codes for the picker
        UNGM.UNSPSC.setSelectedCodes(picker);
        //bind the new code set to the UI
        UNGM.UNSPSC.bindSelectedCodes(picker);
        UNGM.hideThrobber();
    },
    sortComparer: function (a, b) {
        if (a.Code < b.Code) return -1;
        if (a.Code > b.Code) return 1;
        return 0;
    },
    expandAllClicked: function (evt) {
        var picker = $(evt.currentTarget).parents(".unspscSelector");
        picker.find(".expandable").click();
    },
    collapseAllClicked: function (evt) {
        var picker = $(evt.currentTarget).parents(".unspscSelector");
        picker.find(".expanded").each(function () {
            var existingChildren = $(this).siblings(".unspscChildren");
            if (existingChildren.length > 0) {
                existingChildren.hide();
                $(this).removeClass("expanded").addClass("expandable");
                $(this).focus();
            }
        });
    },
    checkUncheckCodesInTree: function (picker) {
        UNGM.UNSPSC.getSelectedCodes(picker);
        picker.find(".chkUnspsc").prop("checked", false);
        if (UNGM.UNSPSC.selectedCodes) {
            for (var i = 0; i < UNGM.UNSPSC.selectedCodes.length; i++) {
                picker.find(".chkUnspsc[data-unspscid=" + UNGM.UNSPSC.selectedCodes[i].Id + "]").prop("checked", true);
            }
        }
    },
    selectCodeAsParent: function (evt) {
        var sender = $(evt.currentTarget);
        if (!UNGM.UNSPSC.selectCallBack) {
            throw ("the UNGM.UNSPSC.selectCallBack property must be set in order to use the 'select as parent' function");
        }
        var unspscid = sender.data("unspscid");
        var unspscode = sender.data("unspscode");
        var rank = sender.data("rank");
        var name = sender.parent().siblings(".nodeName").html();
        name = name.replace(unspscode + ' - ', '');
        var selectedCode = {
            Id: unspscid,
            Code: unspscode,
            Rank: rank,
            Name: name
        };
        UNGM.UNSPSC.selectParentCode = selectedCode;
        var test = "";
        UNGM.UNSPSC.selectCallBack(evt);
    },
    updateUnspscSelectedCodeHeight: function () {
        $(".unspscSelectedCodeTree").height($(".unspscSearchBar").height() + $(".unspscTree").height());
    }
}
;
window.UNGM.OrganizationUserAccountRegistration = {
    emailInvalid: true,
    init: function() {
        $("#Password").bind("keyup", UNGM.ManageAccount.onPasswordKeyUp);
        $("form").bind("focusin", UNGM.removeError);
        $("form").bind("submit", UNGM.OrganizationUserAccountRegistration.validateForm);
        $("#btnAcceptStandardsOfConduct").bind("click", UNGM.OrganizationUserAccountRegistration.acceptStandardsOfConduct);
    },
    validateForm: function (e) {
        e.preventDefault();
        var form = $("#frmOrganizationUser");

        if (form.valid() && UNGM.EmailValidation.verified) {
            grecaptcha.execute();
            console.log("validating");
            return true;
        }

        return false;
    },
    submitForm: function () {
        var form = $("#frmOrganizationUser");
        
        UNGM.throbOver();
        $.ajax({
            url: form.attr("action"),
            type: 'POST',
            data: form.serialize(),
            global: false, // Disable global events, as UNGM.ajaxErrorHandler 
            success: function () {
                window.location = UNGM.siteRoot + "Account/Registration/ActivatePending";
            },
            error: function (response) {
                UNGM.appendErrorTo(form, response.responseJSON);
                UNGM.hideThrobber();
            }
        });
    },
    acceptStandardsOfConduct: function() {
        $(".organization-registration-step1").hide();
        $(".organization-registration-step2").show();
    }
};
window.UNGM.VendorRegistration = {
    currentVendorId: 0,
    selectedExportCountries: [],
    hasGeneralCountryChanged: false,
    hasIndividualConsultantTypeChanged: false,
    isAssistedRegistration: false,
    companyNameIsAvailable: true,
    completionPercent: 1,
    isUnspscTabInitialized: false,
    submittingAgencyIds: null,
    tenderAlertServiceTabCallback: null,
    init: function (completionModel) {
        $(".tabHeader").bind("click", UNGM.VendorRegistration.onTabClicked);
        $("#Unspsc").on("click", UNGM.VendorRegistration.initUnspscTab);

        $(".tabs").on("submit", "form", UNGM.VendorRegistration.onFormSubmitted);
        $(".tabs").on("focusin", "form :input", UNGM.removeError);
        $(".tabs").on("click", "[name='DeclarationOfEligibility']", UNGM.VendorRegistration.declarationOfEligibilityClicked);
        $(".tabs").on("click", "[name='RegistrationType']", UNGM.VendorRegistration.registrationTypeClicked);
        $(".tabs").on("change", "#DummyCountryId", UNGM.VendorRegistration.onExportCountrySelected);
        $(".tabs").on("click", "#selectAllCountries", UNGM.VendorRegistration.onSelectAllCountriesClicked);
        $(".tabs").on("click", "#removeAllCountries", UNGM.VendorRegistration.onRemoveAllCountriesClicked);

        if (UNGM.VendorRegistration.isAssistedRegistration) {
            UNGM.tabContentReceivedCallback = UNGM.AssistedRegistration.onTabContentReceived;
        } else {
            UNGM.tabContentReceivedCallback = UNGM.VendorRegistration.tabContentReceivedCallback;
        }

        $(".back-to-previous-step").off("click").on("click", UNGM.VendorRegistration.goToPreviousTab);

        $("#vendor-registration-steps .progress-steps__step-actions").off("click").on("click", UNGM.VendorRegistration.goToCorrespondingTab);

        UNGM.VendorRegistration.setBirthdateDatepicker();

        // Change company name & contacts tab funcionalitiy
        $(".tabs").on("click", "#btnInviteUser", UNGM.VendorRegistration.inviteUser);
        $(".tabs").on("click", ".btnCancelInvitation", UNGM.VendorRegistration.cancelInvite);
        $(".tabs").on("click", ".btnResendInvitation", UNGM.VendorRegistration.resendInvitation);
        $(".tabs").on("click", "#tblContacts tbody tr", UNGM.VendorRegistration.showDetails);
        $(".tabs").on("click", ".btnEditContact", UNGM.VendorRegistration.editContact);
        $(".tabs").on("click", ".btnSetAsPrimaryContact", UNGM.VendorRegistration.setAsPrimaryContact);
        $(".tabs").on("click", ".btnDeleteContact", UNGM.VendorRegistration.deleteContact);
        $(".btnChangeName").off("click").on("click", UNGM.VendorRegistration.changeName);
        $("#CompanyName").on("focusout", UNGM.VendorRegistration.companyNameFocusOut);
        $("#ChangeCompanyBox").on("focusout", UNGM.VendorRegistration.changeCompanyNameLostFocus);
        $("#ChangeCompanyBox").on("focusin", UNGM.VendorRegistration.companyGotFocus);
        $("#Comments").on("focusout", UNGM.VendorRegistration.commentsLostFocus);
        $("#btnSaveChangeName").off("click").on("click", UNGM.VendorRegistration.saveChangeName);
        $("#Reason").on("change", UNGM.VendorRegistration.reasonChange);
        $("#btnAcceptRegistration").off("click").on("click", UNGM.VendorRegistration.acceptRegistrationInvitation);
        $("#GeneralCountryId").on("change", UNGM.VendorRegistration.checkCountryAndFiscalCodeUnique);
        $("#FiscalCode").on("focusout", UNGM.VendorAccountRegistration.checkCountryAndFiscalCodeUnique);
        // Submission        
        $("#btnResubmitRegistration").off("click").on("click", UNGM.VendorRegistration.resubmitRegistration);
        $('#parentCompany').autocompleteNoConflict({ serviceUrl: UNGM.siteRoot + 'Vendor/Registration/GetParentSuggestions' });
        $("input[name=IndividualConsultantTypeId]:radio").on("click", UNGM.VendorRegistration.onIndividualConsultantTypeClicked);

        $('[name=OwnershipType]').change(UNGM.VendorRegistration.onCompanyOwnershipTypeChanged);
        $('[name=WomenOwnership]').change(UNGM.VendorRegistration.onCompanyWomenOwnershipChanged);
        UNGM.VendorRegistration.onCompanyOwnershipTypeChanged();

        $('#TenderAlertServiceTab').click(UNGM.VendorRegistration.onTenderAlertServiceTabClicked);
        

        UNGM.VendorRegistration.updateCompletion(completionModel);
    },
    initUnspscTab: function () {
        if (!UNGM.VendorRegistration.isUnspscTabInitialized) {
            UNGM.VendorRegistration.isUnspscTabInitialized = true;
            uvm.bootstrap('#UnspscTab', ['UNGM.Vendor.Registration']);
        }
    },
    setBirthdateDatepicker: function () {
        $("[name='Birthdate']").datepicker({
            maxDate: 0,
            changeMonth: true,
            changeYear: true,
            yearRange: "-100:+0",
            onSelect: function () {
                if ($("[name='Birthdate']").val().length > 0) {
                    UNGM.Validation.validateElement($("[name='Birthdate']"));
                }
                UNGM.Validation.validateElement($(this));
            }
        });
    },
    tabContentReceivedCallback: function (tabElement) {
        if (tabElement.find("#VendorRegistrationTypeInfo").length) {
            UNGM.VendorRegistration.bindExportCountries();
        }

        UNGM.Validation.initForElement(tabElement.find("form"));
        UNGM.markRequiredFields();
        $(".back-to-previous-step").off("click").on("click", UNGM.VendorRegistration.goToPreviousTab);
    },
    registrationTypeClicked: function (e) {
        if ($(this).val() === "National") {
            $("#exportToCountrySelector").hide(300);
            $("[name='CountryIds']").remove();
            UNGM.VendorRegistration.selectedExportCountries = [];
        } else {
            $("#exportToCountrySelector").show(300);
        }
        // rebind the countries
        UNGM.VendorRegistration.bindExportCountries();
    },
    onExportCountrySelected: function (e) {
        var sender = $(e.currentTarget);
        var countryId = sender.val();
        if (countryId == '') {
            return;
        }
        var countryName = sender.children(':selected').text();

        var country = {
            Id: countryId,
            Name: countryName
        };
        // Check if the country is already in the list
        if ($.grep(UNGM.VendorRegistration.selectedExportCountries, function (e) { return e.Id == country.Id; }).length == 0 && country.Id.length) {
            UNGM.VendorRegistration.selectedExportCountries.push(country);
            UNGM.VendorRegistration.bindExportCountries();
        }
        // HACK: doing this immediately doesn't work for some reason (due to plugin) so wait a little before clearing text
        setTimeout(' $("#DummyCountryId").val(""); $("#exportToCountryPickerHolder input").val("").focus();', 200);
    },
    bindExportCountries: function () {
        // Render the countries on the page
        var holder = $("#exportToCountryHolder");
        holder.empty();
        $("[name='CountryIds']").remove();

        if (UNGM.VendorRegistration.selectedExportCountries.length === 0) {
            $("#exportToCountryNoCountries").show();
            $("#exportToCountryHolder").css('height', '0'); // Hack for IE
        } else {
            $("#exportToCountryNoCountries").hide();
            for (var i = 0; i < UNGM.VendorRegistration.selectedExportCountries.length; i++) {
                holder.append(UNGM.VendorRegistration.renderExportCountry(UNGM.VendorRegistration.selectedExportCountries[i]));
            }
            $("#exportToCountryHolder").css('height', ''); // Hack for IE
        }
    },
    renderExportCountry: function (country) {
        var elem = $("<div class='exportToCountry editableListItem'></div>");
        var name = $("<span class='exportToCountryName'></span>");
        var options = $("<span class='options'></span>");
        if (!UNGM.VendorRegistration.countriesReadonly) {
            var remove = $("<a href='javascript:;' class='btn btn-small'>" + UNGM.VendorRegistration.removeCountryButtonText + "</a>");
            remove.data("countryId", country.Id);
            remove.bind("click", UNGM.VendorRegistration.removeExportCountry);
            options.append(remove);
        }

        name.html(country.Name);
        var newCountryHidden = $("<input type='hidden' value='" + country.Id + "' name='CountryIds'/>");
        var clearElement = $("<div class='clear'></div>");
        elem.append(name).append(options).append(newCountryHidden).append(clearElement);
        return elem;
    },
    removeExportCountry: function (evt) {
        var sender = $(evt.currentTarget);
        var countryId = sender.data("countryId");

        for (var i = 0; i < UNGM.VendorRegistration.selectedExportCountries.length; i++) {
            if (UNGM.VendorRegistration.selectedExportCountries[i].Id == countryId) {
                UNGM.VendorRegistration.selectedExportCountries.splice(i, 1);
            }
        }
        UNGM.VendorRegistration.bindExportCountries();
    },
    onRemoveAllCountriesClicked: function () {
        UNGM.VendorRegistration.selectedExportCountries = [];
        UNGM.VendorRegistration.bindExportCountries();
    },
    onSelectAllCountriesClicked: function () {
        UNGM.VendorRegistration.selectedExportCountries = [];
        $("#DummyCountryId option:not(:first)").each(function (i, o) {
            var country = {
                Id: $(o).val(),
                Name: $(o).text()
            };
            UNGM.VendorRegistration.selectedExportCountries.push(country);
        });
        UNGM.VendorRegistration.bindExportCountries();
    },
    declarationOfEligibilityClicked: function () {
        if ($(this).val() === "Yes") {
            $("#eligibilityCommentsDiv").hide();
        } else {
            $("#eligibilityCommentsDiv").show();
        }
    },
    onFormSubmitted: function (e) {
        e.preventDefault();
        var form = $(e.currentTarget);

        if (!form.valid()) {
            return false;
        }
        if (form.attr('id') === "frmGeneral") {
            if (UNGM.VendorAccountRegistration.countryAndFiscalCodeInvalid
                || !UNGM.VendorRegistration.companyNameIsAvailable) {
                return false;
            }
            if ((UNGM.VendorRegistration.hasGeneralCountryChanged === true
                || UNGM.VendorRegistration.hasIndividualConsultantTypeChanged === true)
                && UNGM.VendorRegistration.AtLeastOneActiveSubmission) {
                UNGM.VendorRegistration.saveAndConfirmCountryOrIndividualConsultantTypeChange();
                return;
            }
        }

        UNGM.VendorRegistration.submitForm(form);
    },
    submitForm: function (form) {
        UNGM.Throbber.Push();
        $.ajax({
            url: form.attr("action"),
            type: "POST",
            data: form.serialize(),
            global: false,
            success: function (response) {
                if (form.find("#Surname").length) {
                    // Reload declaration of eligibility
                    $("#DeclarationOfEligibilityContent").data("reload", true);
                }

                var hasJustBeenCreated = response.id !== null && response.id > 0;
                if (UNGM.VendorRegistration.isAssistedRegistration && hasJustBeenCreated) {
                    UNGM.AssistedRegistration.reloadPage(response.id);
                    return;
                }

                UNGM.VendorRegistration.updateCompletion(response);
            },
            error: function (response) {
                UNGM.appendErrorTo(form, response.responseJSON);
            },
            complete: UNGM.Throbber.Pop
        });
    },
    goToTab: function (tab) {
        var currentActive = $(".tab.step-tab-active");
        currentActive.removeClass("step-tab-active");
        tab.addClass("step-tab-active");
        UNGM.VendorRegistration.setCurrentStep(tab);
        window.scrollTo(0, 0);

        if (tab.data("tab") === "general-tab") {
            return;
        }

        if (tab.data("tab") === "unspsc-tab") {
            UNGM.VendorRegistration.initUnspscTab();
            return;
        }

        if (tab.data("tab") === "what-next-tab") {
            UNGM.VendorRegistration.tenderAlertServiceTabCallback();
            return;
        }

        UNGM.reloadTab(tab);
    },
    goToCorrespondingTab: function(e) {
        var sender = $(e.currentTarget);
        var parent = sender.closest("#vendor-registration-steps .progress-steps__step");

        UNGM.VendorRegistration.goToTab($(".tab[data-tab='" + parent.data("tab") + "']"));
    },
    goToPreviousTab: function (e) {
        var currentActive = $(".tab.step-tab-active");
        var previousTab = currentActive.prev();
        UNGM.VendorRegistration.goToTab(previousTab);
    },
    updateCompletion: function (response) {
        if (response.completion === 100) {
            var isSubmissionBlockForDuplication = UNGM.VendorRegistration.isAssistedRegistration ? false : response.isSubmissionBlockForDuplication;
            if (isSubmissionBlockForDuplication) {
                $("#duplicateWarning").show();
            }
            $("#btnResubmitRegistration").removeProp('disabled');
            $(".incomplete-registration").removeClass('incomplete-registration').addClass('complete-registration');
        } else {
            $("#duplicateWarning").hide();
            $("#btnResubmitRegistration").prop('disabled', true);
            $(".complete-registration").removeClass('complete-registration').addClass('incomplete-registration');
        }

        var lastActiveStep = UNGM.VendorRegistration.findLastActiveStep(response);
        UNGM.VendorRegistration.goToTab($(".tab[data-tab='" + lastActiveStep.data("tab") + "']"));
    },
    findLastActiveStep: function (response) {
        var lastActiveStep = $(".general-info-step");
        if (response.generalTabComplete) {
            lastActiveStep = $(".address-step");
            lastActiveStep.addClass("progress-steps__step--active");

        } else {
            return lastActiveStep;
        }

        if (response.addressTabComplete) {
            lastActiveStep = $(".registration-type-step");
            lastActiveStep.addClass("progress-steps__step--active");
        } else {
            return lastActiveStep;
        }

        if (response.nationalTabComplete) {
            lastActiveStep = $(".contact-step");
            lastActiveStep.addClass("progress-steps__step--active");
        } else {
            return lastActiveStep;
        }

        if (response.contactTabComplete) {
            lastActiveStep = $(".unspsc-step");
            lastActiveStep.addClass("progress-steps__step--active");
        } else {
            return lastActiveStep;
        }

        if (response.codingTabComplete) {
            if (UNGM.VendorRegistration.isAssistedRegistration) {
                lastActiveStep = $(".documents-step");
                lastActiveStep.addClass("progress-steps__step--active");
                return lastActiveStep;
            } else {
                lastActiveStep = $(".declaration-step");
                lastActiveStep.addClass("progress-steps__step--active");
            }
        } else {
            return lastActiveStep;
        }

        if (response.declarationTabComplete) {
            lastActiveStep = $(".whatnext-step");
            lastActiveStep.addClass("progress-steps__step--active");
        } else {
            return lastActiveStep;
        }

        return lastActiveStep;
    },
    setCurrentStep: function (currentTab) {
        var currentStep = $("#vendor-registration-steps .progress-steps__step[data-tab='" + currentTab.data("tab") + "']");
        $("#vendor-registration-steps .progress-steps__step").removeClass("progress-steps__step--current-step");
        currentStep.addClass("progress-steps__step--current-step");
    },
    setCountryPicker: function (picker, optionValue) {
        picker.val(optionValue); //change the select box
        picker.parent('.formRow').find('.ui-autocomplete-input').val(picker.find("option[value='" + optionValue + "']").text());
    },
    saveAndConfirmCountryOrIndividualConsultantTypeChange: function (e) {
        UNGM.Throbber.Push();
        var postObj = {
            generalCountryId: $("#GeneralCountryId").val(),
            individualConsultantTypeId: $("input[name=IndividualConsultantTypeId]:checked").val() !== undefined ? $("input[name=IndividualConsultantTypeId]:checked").val() : ""
        };

        $.ajax({
            url: UNGM.siteRoot + 'Vendor/Registration/UpdateSensitiveData',
            type: 'POST',
            data: postObj,
            success: function (data) {
                UNGM.VendorRegistration.reloadAgencySubmissionTab();
                UNGM.VendorRegistration.submitForm($("#frmGeneral"));
                UNGM.Throbber.Pop();
            },
            traditional: true
        });
    },
    postForm: function (evt, form) {
        var sender = null;

        if (form === undefined) {
            form = $(evt.currentTarget).parents('form');
            sender = $(evt.currentTarget);
        }
        else {
            sender = form.find("input[type=submit]");
        }

        var nexttab = sender.data("nexttab");
        if (form.valid() && !form.find("#companyExists:visible").length) {      // valid() returns true when a company exists
            UNGM.throbOver(form);

            if (form.attr('action').indexOf("/General") > 0
                && (UNGM.VendorRegistration.hasGeneralCountryChanged === true || UNGM.VendorRegistration.hasIndividualConsultantTypeChanged === true)
                && UNGM.VendorRegistration.AtLeastOneActiveSubmission) {
                UNGM.VendorRegistration.saveAndConfirmCountryOrIndividualConsultantTypeChange();
                return;
            }

            $.ajax({
                type: "POST",
                url: form.attr('action'),
                data: form.serialize(),
                success: function (response) {
                    // If the country has changed or individual consultant change, the Vendor-Agency match may have changed too
                    if (form.attr('action').indexOf("/General") > 0
                        && UNGM.VendorRegistration.hasGeneralCountryChanged == true || UNGM.VendorRegistration.hasIndividualConsultantTypeChanged == true) {
                        UNGM.VendorRegistration.reloadAgencySubmissionTab();
                    }
                    // In assisted registrations the action returns the new id of the vendor
                    if (UNGM.VendorRegistration.currentVendorId == 0 && UNGM.VendorRegistration.currentVendorId) {
                        $(".zeroPercent").removeClass("zeroPercent");
                        UNGM.VendorRegistration.currentVendorId = response.Id;
                        $(".Id").val(UNGM.VendorRegistration.currentVendorId);
                        // Allow the UN user to continue the registration
                        $("#msgGeneralInfoMandatory").fadeOut("fast");
                        $(".tabDisabled").removeClass("tabDisabled");
                        UNGM.initTabs();
                        // Add id to url
                        if (window.location.href.indexOf("/" + UNGM.VendorRegistration.currentVendorId) < 0) {
                            if (typeof window.history.replaceState == 'function') {
                                window.history.pushState("Assisted registration", "Assisted registration", window.location + "/" + UNGM.VendorRegistration.currentVendorId);
                            }
                            else {
                                window.location.href = UNGM.siteRoot + "UNUser/AssistedRegistration/" + UNGM.VendorRegistration.currentVendorId;
                            }
                        }
                    }
                    UNGM.VendorRegistration.updateCompletion();
                    UNGM.hideThrobber();
                    if (nexttab) {
                        $(".tabHeader").eq(nexttab).click();
                        $(".tabHeader").scrollintoview();
                    }
                }
            });
        }
        else {
            UNGM.hideThrobber();
        }
    },

    handleNotSubmittedAgencyWarning: function () {
        if ($(".chkNotSubmittedAgency:checked").length > 0) {
            $("#divAgencyNotSelected").hide();
        }
        else if (UNGM.VendorRegistration.completionPercent == 100) {
            $("#divAgencyNotSelected").show();
        }
    },

    // Company Available 
    resendInvitation: function (evt) {
        // Get the Id of the cancelled invite
        var button = $(evt.currentTarget);
        var Id = button.data('invitee-id');

        UNGM.throbOver($('#vendorRegistration'));
        $.ajax({
            type: "POST",
            url: UNGM.siteRoot + 'Vendor/Registration/ResendUserInvitation?inviteId=' + Id,
            success: UNGM.VendorRegistration.displayPendingInvites
        });
    },
    inviteUser: function (evt) {
        var winHeight = $(window).height();
        $('#emailBox').val('');
        $('#Language').val(UNGM.currentuserCookieLanguage);
        $('#InviteText').val('');
        $("#userInviteForm").dialog({
            modal: true,
            title: $("#inviteUserDialogTitle").val(),
            width: 700,
            hide: { effect: 'fade', duration: 100 },
            show: { effect: 'fade', duration: 100 },
            open: function () {
                $("#btnSendInvite").off("click").on("click", UNGM.VendorRegistration.sendInviteValidation);
            }
        });
    },
    cancelInvite: function (evt) {
        // Get the Id of the cancelled invite
        var button = $(evt.currentTarget);
        var Id = button.data('invitee-id');

        var deleteOk = function () {
            UNGM.throbOver($('#vendorRegistration'));
            $.ajax({
                type: "POST",
                url: UNGM.siteRoot + 'Vendor/Registration/CancelUserInvitation?inviteId=' + Id,
                success: UNGM.VendorRegistration.displayPendingInvites
            });
        };

        // confirmation
        $.confirm(
            UNGM.VendorRegistration.DeleteInviteConfirmationTitle,
            UNGM.VendorRegistration.DeleteInviteConfirmationMessage,
            UNGM.VendorRegistration.DeleteInviteConfirmationYesButton,
            UNGM.VendorRegistration.DeleteInviteConfirmationNoButton,
            deleteOk
        );
    },
    displayPendingInvites: function (data) {
        try {
            $('#pendingInvites').html(data);
            //now rebind the click handlers
            $(".tabs").off("click", ".btnCancelInvitation");
            $(".btnCancelInvitation").off("click").on("click", UNGM.VendorRegistration.cancelInvite);
            $(".btnResendInvitation").off("click").on("click", UNGM.VendorRegistration.resendInvitation);
        }
        finally {
            UNGM.hideThrobber();
        }
    },
    sendInviteValidation: function (evt) {
        evt.preventDefault();
        var form = $(evt.currentTarget).parents('form');

        if (!form.valid() || !UNGM.EmailValidation.verified) {
            return false;
        }

        grecaptcha.execute();
        UNGM.GoogleRecaptcha.successCallback = UNGM.VendorRegistration.sendInvite;
        
        return true;
    },
    sendInvite: function () {
        UNGM.throbOver(form);
        var form = $("#inviteContactForm");
        $.ajax({
            type: "POST",
            url: form.attr('action'),
            data: form.serialize(),
            success: UNGM.VendorRegistration.inviteSent
        });
    },
    inviteSent: function (data) {
        try {
            // Close the dialog
            $('#userInviteForm').dialog('close');
            UNGM.VendorRegistration.displayPendingInvites(data);
        }
        finally {
            UNGM.hideThrobber();
        }
    },
    showDetails: function (evt) {
        // Get the agency Object
        var sender = $(evt.currentTarget);
        var parentRow = sender;//.parents("tr:first");
        var contactObject = sender.data("object");
        //open the dialog box, showing all the Details
        var winHeight = $(window).height();
        $("#contactDetails").dialog({
            modal: true,
            open: function () {
                $("#viewUserTitle").html(contactObject.UserTitleLocalName);
                $("#viewFirstName").html(contactObject.FirstName);
                $("#viewMiddleName").html(contactObject.MiddleName);
                $("#viewSurname").html(contactObject.Surname);
                $("#viewCompanyPosition").html(contactObject.CompanyPosition);
                $("#viewCountryName").html(contactObject.CountryName);
                $("#viewEmail").html(contactObject.Email);
                $("#viewTelephoneCountryName").html(contactObject.ContactTelephoneCountryName);
                $("#viewTelephoneNumber").html(contactObject.TelephoneNumber);
                $("#viewTelephoneExtension").html(contactObject.TelephoneExtension);
                $("#viewMobileCountryName").html(contactObject.MobileCountryName);
                $("#viewMobileNumber").html(contactObject.MobileNumber);
            },
            title: contactObject.UserTitleName + " " + contactObject.FirstName + " " + contactObject.Surname,
            width: '550px',
            height: winHeight - (winHeight * 0.45),
            hide: { effect: 'fade', duration: 100 },
            show: { effect: 'fade', duration: 100 }
        });
    },
    deleteContact: function (evt) {
        // Stop the even propagation
        evt.stopPropagation();
        var deleteOk = function () {
            // Carry out the delete
            // Get the Id of the cancelled invite
            var button = $(evt.currentTarget);
            var id = button.data('contact-id');

            var postObject = {};
            postObject.Id = id;
            UNGM.throbOver($('#vendorRegistration'));
            $.ajax({
                type: "POST",
                url: UNGM.siteRoot + 'Vendor/Registration/DeactivateUser',
                contentType: 'application/json',
                data: JSON.stringify(postObject),
                success: UNGM.VendorRegistration.onDeleteContact
            });

        };
        //are you sure?
        $.confirm(
            $("#delConfirmationTitle").val(), // Title
            $("#delConfirmationMessage").val(), // Message
            $("#delConfirmationDeleteText").val(), //yes button text (blank hides button)
            $("#delConfirmationCancelText").val(), //no button text (blank hides button)
            deleteOk //"yes" callback
        );
    },
    onDeleteContact: function (data) {
        // If user has deleted themsevles, go to the main page
        if (data.length === undefined) {
            // User has deleted themsevles, go to the main page
            location.href = $("#RedirectToHome").val();
        }
        else {
            // User deleted a different contact, refresh the contacts data
            try {
                $('#allContacts').html(data);
            }
            finally {
                UNGM.hideThrobber();
            }
        }
    },
    editContact: function (evt) {
        // Get the agency Object
        var sender = $(evt.currentTarget);
        var parentRow = sender.parents("tr:first");
        var contactObject = parentRow.data("object");
        //open the dialog box, showing all the Details
        var winHeight = $(window).height();
        //Have to remove all script elements, as the autocompletes have already been run
        //jQuery re-calls document.Ready once it's created the dialog, so autocompletes were being set twice.
        $("#contactEditor").find("script").remove();
        $("#contactEditor").dialog({
            modal: true,
            open: function () {
                $("#btnCancelEdit").on("click", UNGM.VendorRegistration.cancelEdit);
                $("#btnSaveContact").on("click", UNGM.VendorRegistration.saveContact);
                $("#contactEditForm #Id").val(contactObject.Id);
                $("#contactEditForm #UserTitle").val(contactObject.UserTitleName);
                $("#contactEditForm #FirstName").val(contactObject.FirstName);
                $("#contactEditForm #MiddleName").val(contactObject.MiddleName);
                $("#contactEditForm #Surname").val(contactObject.Surname);
                $("#contactEditForm #CompanyPosition").val(contactObject.CompanyPosition);
                UNGM.VendorRegistration.setCountryPicker($("#contactEditForm #CountryId"), contactObject.CountryId);
                //$("#contactEditForm #CountryId").val(contactObject.CountryId);
                $("#contactEditForm #Email").val(contactObject.Email);
                UNGM.VendorRegistration.setCountryPicker($("#contactEditForm #ContactTelephoneCountryId"), contactObject.ContactTelephoneCountryId);
                //$("#contactEditForm #ContactTelephoneCountryId").val(contactObject.ContactTelephoneCountryId);
                $("#contactEditForm #TelephoneNumber").val(contactObject.TelephoneNumber);
                $("#contactEditForm #TelephoneExtension").val(contactObject.TelephoneExtension);
                UNGM.VendorRegistration.setCountryPicker($("#contactEditForm #MobileCountryId"), contactObject.MobileCountryId);
                //$("#contactEditForbtnSaveContactm #MobileCountryId").val(contactObject.MobileCountryId);
                $("#contactEditForm #MobileNumber").val(contactObject.MobileNumber);
                $("#contactEditForm .validationIcon, #contactEditForm .field-validation-error").hide();
            },
            title: UNGM.VendorRegistration.editContactDialogTitle + " - " + contactObject.UserTitleName + " " + contactObject.FirstName + " " + contactObject.Surname,
            width: '750px',
            height: winHeight - (winHeight * 0.15),
            hide: { effect: 'fade', duration: 100 },
            show: { effect: 'fade', duration: 100 }
        });
        evt.stopPropagation();
    },
    setAsPrimaryContact: function (evt) {
        evt.stopPropagation();

        var button = $(evt.currentTarget);
        var contactId = button.data('contact-id');

        UNGM.throbOver();
        $.ajax({
            type: "POST",
            url: "Registration/SelectCompanyPrimaryContact?contactUserId=" + contactId,
            contentType: 'application/json',
            complete: function () {
                UNGM.hideThrobber();
                UNGM.VendorRegistration.reloadContacts();
            }
        });
    },
    cancelEdit: function (evt) {
        //just close the dialog.
        $("#contactEditor").dialog('close');
    },
    saveContact: function (evt) {
        evt.preventDefault();

        var form = $(evt.currentTarget).parents('form');

        if (!form.valid()) {
            return false;
        }

        UNGM.throbOver(form);
        $.ajax({
            url: form.attr('action'),
            type: 'POST',
            data: form.serialize(),
            success: UNGM.VendorRegistration.onSavedContact
        });
    },
    onSavedContact: function (response) {
        $("#contactEditor").dialog('close');
        UNGM.VendorRegistration.updateCompletion(response);
        UNGM.VendorRegistration.reloadContacts();
    },
    reloadContacts: function () {
        $.ajax({
            url: UNGM.siteRoot + "Vendor/Registration/ListContacts",
            type: 'GET',
            contentType: 'application/json',
            success: function (data) {
                $('#allContacts').html(data);
                $(".btnSaveContact").off("click").on("click", UNGM.VendorRegistration.saveContact);
                $(".btnSetAsPrimaryContact").off("click").on("click", UNGM.VendorRegistration.setAsPrimaryContact);
                UNGM.hideThrobber();
            }
        });
    },
    // This function is called from the callback: UNGM.UNSPSC.saveCallback in file: UNGM.Web/Areas/Vendor/Views/UNSPSC/Index.cshtml
    // Also when the the country in the General tab changes
    reloadAgencySubmissionTab: function () {
        if (!UNGM.VendorRegistration.isAssistedRegistration) {
            UNGM.Throbber.Push();
            $.ajax({
                type: "GET",
                url: UNGM.siteRoot + "Vendor/VendorAgency/Index",
                success: function (response) {
                    $("#divAgencySelection").html(response);
                    UNGM.VendorRegistration.handleNotSubmittedAgencyWarning();
                },
                complete: function () {
                    UNGM.VendorRegistration.hasGeneralCountryChanged = false;
                    UNGM.VendorRegistration.hasIndividualConsultantTypeChanged = false;
                    UNGM.Throbber.Pop();
                }
            });
        }
    },
    submitRegistration: function () {
        var submitOk = function () {
            UNGM.throbOver($('#centre'));
            $.ajax({
                type: "POST",
                url: UNGM.siteRoot + 'Vendor/Registration/SubmitRegistration?vendorId=' + UNGM.VendorRegistration.currentVendorId + '&isassistedregistration=' + UNGM.VendorRegistration.isAssistedRegistration,
                success: function () {
                    if (UNGM.VendorRegistration.isAssistedRegistration) {
                        window.location.href = UNGM.siteRoot + "UNUser/AssistedRegistration/" + UNGM.VendorRegistration.currentVendorId;
                    }
                    else {
                        window.location.href = UNGM.siteRoot + "Vendor/Registration/Registration";
                    }
                }
            });
        };

        //are you sure?
        $.confirm(
            UNGM.VendorRegistration.submitConfirmationTitle, // Title
            UNGM.VendorRegistration.submitConfirmationMessage, // Message
            UNGM.VendorRegistration.submitConfirmationYesText, //yes button text (blank hides button)
            UNGM.VendorRegistration.submitConfirmationCancelText, //no button text (blank hides button)
            submitOk //"yes" callback
        );
    },
    resubmitRegistration: function () {
        var clickedOnOk = function () {
            UNGM.throbOver($('#vendorRegistration').prev());
            $.ajax({
                type: "POST",
                url: UNGM.siteRoot + 'Vendor/Registration/ResubmitRegistration',
                success: function () {
                    window.location.href = UNGM.siteRoot + "Vendor/Registration/Registration";
                }
            });
        };

        //are you sure?
        $.confirm(
            UNGM.VendorRegistration.resubmitConfirmationTitle, // Title
            UNGM.VendorRegistration.resubmitConfirmationMessage, // Message
            UNGM.VendorRegistration.resubmitConfirmationYesText, //yes button text (blank hides button)
            UNGM.VendorRegistration.resubmitConfirmationCancelText, //no button text (blank hides button)
            clickedOnOk //"yes" callback
        );
    },
    companyNameFocusOut: function (event) {
        var companyName = $(event.currentTarget);
        var checkingAvailability = companyName.siblings(".checkingAvailability");
        var companyExists = companyName.siblings("#companyExists");
        checkingAvailability.show();
        UNGM.VendorRegistration.checkCompanyNameAvailable(companyName.val()).then(function (isAvailable) {
            companyExists.toggle(!isAvailable);
            UNGM.VendorRegistration.companyNameIsAvailable = isAvailable;
            checkingAvailability.hide();
        });
    },
    checkCompanyNameAvailable: function (companyName) {
        var deferred = $.Deferred();
        $.ajax({
            url: UNGM.siteRoot + 'Account/Registration/IsCompanyNameAvailable?companyName=' + encodeURIComponent(companyName) + "&vendorId=" + UNGM.VendorRegistration.currentVendorId,
            type: 'GET',
            contentType: 'application/json',
            success: function (response) { deferred.resolve(response.isAvailable); }
        });
        return deferred.promise();
    },
    changeCompanyNameLostFocus: function (e) {
        var sender = $(e.currentTarget);
        var newCompanyName = UNGM.VendorRegistration.normalizeCompanyName(sender.val());
        sender.val(newCompanyName);

        if (!sender.valid() || !newCompanyName.length) {
            return false;
        }
        if (UNGM.VendorRegistration.isNewCompanyNameEqualToOldCompanyName()) {
            sender.siblings(".noChangeInCompanyName").show();
            return false;
        }
        sender.siblings(".noChangeInCompanyName").hide();

        sender.siblings(".checkingAvailability").show();
        $.ajax({
            url: UNGM.siteRoot + 'Account/Registration/IsCompanyNameAvailable?companyName=' + encodeURIComponent(newCompanyName) + "&vendorId=" + UNGM.VendorRegistration.currentVendorId,
            type: 'GET',
            contentType: 'application/json',
            success: UNGM.VendorRegistration.onCheckedCompany
        });
    },
    onCheckedCompany: function (response) {
        var box = $("#CompanyName");
        $(".checkingAvailability").hide();

        if (response.isAvailable) {
            UNGM.Validation.markElementValid(box);
            $("#frmCompanyNameChange .companyExists").hide();
            UNGM.VendorRegistration.companyNameIsAvailable = true;
            UNGM.Validation.validateElement(box);
        } else {
            UNGM.Validation.markElementInvalid(box);
            UNGM.VendorRegistration.companyNameIsAvailable = false;
            $("#frmCompanyNameChange .companyExists").show();
        }
    },
    changeName: function () {

        UNGM.throbOver($("#frmGeneral"));
        var postObj = { 'currentCompanyName': $("#CompanyName").val() };
        $.ajax({
            type: 'POST',
            url: UNGM.siteRoot + 'Vendor/Registration/CompanyNameChange',
            data: postObj,
            success: function (data) {
                var newForm = $(data);
                elements.contextForm = newForm;
                UNGM.hideThrobber();
                UNGM.Validation.initForElement(elements.contextForm);
                $("#divNameChange").html(elements.contextForm);
                $("#divNameChange").find("#btnSaveChangeName").off("click").on("click", UNGM.VendorRegistration.saveChangeName);
                $("#divNameChange").find("#ChangeCompanyBox").off("focusout").on("focusout", UNGM.VendorRegistration.changeCompanyNameLostFocus);
                $("#divNameChange").find("#ChangeCompanyBox").off("focusin").on("focusin", UNGM.VendorRegistration.companyGotFocus);
                $("#divNameChange").dialog({
                    modal: true,
                    width: '80%',
                    hide: { effect: 'fade', duration: 100 },
                    show: { effect: 'fade', duration: 100 }
                });
                $("#divNameChange").css('maxHeight', $(window).height() * 0.8);
            }
        });
    },
    commentsLostFocus: function (evt) {
        var sender = $("#Comments");
        if ($("#Reason option:selected").val() != "Other") {
            UNGM.Validation.markElementValid(sender);
        }
    },
    reasonChange: function (evt) {
        var sender = $("#Comments");
        UNGM.Validation.markElementValid(sender);
    },
    saveChangeName: function (evt) {
        var docElement = $("#CertificateOfIncorporationDocumentId");
        if (docElement.length > 0 && docElement.val() === "") {
            $(".requiredDocument").show();
            err = true;
            return;
        }
        else {
            $(".requiredDocument").hide();
        }
        if (!UNGM.VendorRegistration.companyNameIsAvailable) {
            err = true;
            return;
        }

        if ($("#frmCompanyNameChange").valid() && !UNGM.VendorRegistration.isNewCompanyNameEqualToOldCompanyName()) {
            var obj = {};
            obj.CompanyName = $("#OldCompanyName").val();
            obj.Reason = $("#Reason option:selected").val();
            obj.Comments = $("#Comments").val();
            obj.CertificateOfIncorporationDocumentId = $("#CertificateOfIncorporationDocumentId").val();
            obj.VendorId = UNGM.VendorRegistration.currentVendorId;
            obj.NewCompanyName = $("#ChangeCompanyBox").val(); // Save current company name as change company name

            UNGM.throbOver($("#divNameChange"));

            $.ajax({
                url: UNGM.siteRoot + 'Vendor/Registration/SaveCompanyNameChange',
                type: 'POST',
                contentType: 'application/json',
                data: JSON.stringify(obj),
                success: function () {
                    window.location.href = window.location.href;
                }
            });
        }
    },
    normalizeCompanyName: function (companyName) {
        return $.trim(companyName.replace(/\s+/g, ' '));
    },
    isNewCompanyNameEqualToOldCompanyName: function () {
        var oldCompanyName = $("#OldCompanyName").val();
        var newCompanyName = $("#ChangeCompanyBox").val();
        return oldCompanyName === newCompanyName.trim();
    },
    acceptRegistrationInvitation: function (evt) {
        var form = $(evt.currentTarget).parents('form');
        if ($("#CodeOfConduct:checked").length == 0) {
            evt.preventDefault();
            $("#divCocRejected").show();
            $("#divCoc").hide();
            return;
        }
        if (form.valid()) {
            var postObject = {};
            postObject.UserName = form.find("#UserName").val();
            postObject.CompanyName = form.find("#CompanyName").val();
            postObject.FirstName = form.find("#FirstName").val();
            postObject.LastName = form.find("#LastName").val();
            postObject.CountryId = form.find("#CountryId").val();
            postObject.Password = form.find("#Password").val();
            postObject.CompanyDirectorFirstName = form.find("#CompanyDirectorFirstName").val();
            postObject.CompanyDirectorLastName = form.find("#CompanyDirectorLastName").val();
            postObject.CodeOfConduct = true;
            UNGM.throbOver(form);
            $.ajax({
                type: "POST",
                url: form.attr('action'),
                contentType: 'application/json',
                data: JSON.stringify(postObject),
                success: UNGM.VendorRegistration.acceptRegistrationComplete,
                complete: function () { UNGM.hideThrobber(); }
            });
        }
    },
    acceptRegistrationComplete: function (data) {
        UNGM.hideThrobber();
        if (data.success) {
            // Show welcome popup
            var OkHandler = function () {
                // Redirect to Home
                location.href = $("#RedirectToHome").val();
            };
            $.confirm(data.title, data.message, data.okButton, "", OkHandler);
        }
        else {
            $.confirm(data.title, data.message, data.okButton, "", function () { });
        }
    },
    checkCountryAndFiscalCodeUnique: function (e) {
        UNGM.VendorRegistration.hasGeneralCountryChanged = true;
        UNGM.VendorRegistration.isGeneralCountryChangedBackToInitialValue(e);
        UNGM.VendorAccountRegistration.checkCountryAndFiscalCodeUnique(e);
    },
    onIndividualConsultantTypeClicked: function (e) {
        UNGM.VendorRegistration.hasIndividualConsultantTypeChanged = true;
        if ($('#individualConsultantTypeDescription').is(":visible") == false) {
            $('#individualConsultantTypeDescription').show();
        }
        $('#individualConsultantTypeDescription').text($(this).attr('data-description'));
        UNGM.VendorRegistration.isIndividualConsultantTypeChangedBackToInitialValue(e);
    },
    isGeneralCountryChangedBackToInitialValue: function (e) {
        var sender = $(e.currentTarget);
        if (sender.val() == $('#OldGeneralCountryId').val()) {
            UNGM.VendorRegistration.hasGeneralCountryChanged = false;
        }
    },
    isIndividualConsultantTypeChangedBackToInitialValue: function (e) {
        var sender = $(e.currentTarget);
        if (sender.val() == $('#OldIndividualConsultantTypeId').val()) {
            UNGM.VendorRegistration.hasIndividualConsultantTypeChanged = false;
        }
    },
    onCompanyOwnershipTypeChanged: function () {
        var elements = $('[data-enable-if-ownership-type]');
        elements.each(function (_index, item) {
            var element = $(item);
            var enable = UNGM.VendorRegistration.isOwnershipTypeEnabled(element);
            if (enable) {
                element.slideDown();
            } else {
                element.slideUp();
            }
        });
    },
    onCompanyWomenOwnershipChanged: function () {
        var elements = $('[data-enable-if-women-ownership]');
        elements.each(function (_index, item) {
            var element = $(item);
            var enable = UNGM.VendorRegistration.isWomenOwnershipEnabled(element);
            if (enable) {
                element.slideDown();
            } else {
                element.slideUp();
            }
        });
    },
    isOwnershipTypeEnabled: function (element) {
        var ownerships = JSON.parse(element.attr('data-enable-if-ownership-type'));
        var ownershipType = $('[name=OwnershipType]').filter(function (_index, item) { return $(item).prop('checked'); }).val();
        return $.inArray(ownershipType, ownerships) !== -1;
    },
    isWomenOwnershipEnabled: function (element) {
        var womenOwnerships = JSON.parse(element.attr('data-enable-if-women-ownership'));
        var womenOwnership = $('[name=WomenOwnership]').filter(function (_index, item) { return $(item).prop('checked'); }).val();
        return $('[name=WomenOwnership]').is(':visible') && $.inArray(womenOwnership, womenOwnerships) !== -1;
    },
    onTenderAlertServiceTabClicked: function () {
        UNGM.VendorRegistration.tenderAlertServiceTabCallback();
    },
    setTenderAlertServiceTabCallback: function (callback) {
        UNGM.VendorRegistration.tenderAlertServiceTabCallback = callback;
    }
};;
window.UNGM.VendorAccountRegistration = {
    companyInvalid: true,
    countryAndFiscalCodeInvalid: false,
    init: function () {
        $("#CompanyName").bind("focusin", UNGM.VendorAccountRegistration.companyGotFocus);
        $("#CompanyName").bind("focusout", UNGM.VendorAccountRegistration.companyLostFocus);
        $("#autoPopulateUserDetails").bind("change", UNGM.VendorAccountRegistration.autoPopulateUserDetailsChanged);
        $("#Password").bind("keyup", UNGM.ManageAccount.onPasswordKeyUp);
        $("form").bind("submit", UNGM.VendorAccountRegistration.validateForm);
        $("#FiscalCode").on("focusout", UNGM.VendorAccountRegistration.checkCountryAndFiscalCodeUnique);
        $("#GeneralCountryId").on("change", UNGM.VendorAccountRegistration.checkCountryAndFiscalCodeUnique);        
        $("input[name=IndividualConsultantTypeId]:radio").on("click", UNGM.VendorAccountRegistration.onIndividualConsultantTypeClicked);        
    },
    validateForm: function(e) {
        e.preventDefault();
        var form = $(e.currentTarget);
        UNGM.removeError();
        if (($("#CompanyName").length && UNGM.VendorAccountRegistration.companyInvalid) ||
            !UNGM.EmailValidation.verified ||
            UNGM.VendorAccountRegistration.countryAndFiscalCodeInvalid ||
            !form.valid() ||
            !$("#CodeOfConduct").is(":checked")) {
            return false;
        }

        grecaptcha.execute();

        return true;
    },
    submitForm: function () {
        UNGM.throbOver();
        var form = $("#frmVendorAccount");

        $.ajax({
            url: form.attr("action"),
            type: 'POST',
            data: form.serialize(),
            global: false,  // Disable global events, as UNGM.ajaxErrorHandler 
            success: function () {
                window.location = UNGM.siteRoot + "Account/Registration/ActivatePending";
            },
            error: function (response) {
                UNGM.appendErrorTo(form, response.responseJSON);
                UNGM.hideThrobber();
            }
        });
    },
    companyGotFocus: function () {
        $(".checkingAvailability, #companyExists").hide();
    },
    companyLostFocus: function (e) {
        var sender = $(e.currentTarget);
        var company = sender.val();

        if (!sender.valid() || !company.length) {
            return false;
        }

        sender.siblings(".checkingAvailability").show();
        $.ajax({
            url: UNGM.siteRoot + 'Account/Registration/IsCompanyNameAvailable?companyName=' + encodeURIComponent(company) + "&vendorId=" + UNGM.VendorRegistration.currentVendorId,
            type: 'GET',
            contentType: 'application/json',
            success: UNGM.VendorAccountRegistration.onCheckedCompany
        });
    },
    onCheckedCompany: function (response) {
        var box = $("#CompanyName");
        $(".checkingAvailability").hide();

        if (response.isAvailable) {
            UNGM.Validation.markElementValid(box);
            $("#companyExists").hide();
            UNGM.VendorAccountRegistration.companyInvalid = false;
            UNGM.Validation.validateElement(box);
        } else {
            UNGM.Validation.markElementInvalid(box);
            UNGM.VendorAccountRegistration.companyInvalid = true;
            $("#companyExists").show();
        }
    },
    autoPopulateUserDetailsChanged: function (e) {
        var checkbox = $(e.currentTarget);

        if (checkbox.is(":checked")) {
            $("#FirstName").val($("#CompanyDirectorFirstName").val());
            $("#LastName").val($("#CompanyDirectorLastName").val());
        }
        else {
            $("#FirstName, #LastName").val("");
        }
    },
    checkCountryAndFiscalCodeUnique: function (e) {
        var sender = $(e.currentTarget);        
        if (!sender.valid() || !sender.val()) {
            $(".duplicateOfCountryAndFiscalCode").hide();
            return false;
        }
        sender.siblings(".checkingForUnique").show();
        var generalCountryId = $("#GeneralCountryId").val();
        var fiscalCode = $("#FiscalCode").val();
        if (generalCountryId.length > 0 && fiscalCode.length > 0) {
            $.ajax({
                url: UNGM.siteRoot + 'Account/Registration/IsCombinationOfCountryAndFiscalCodeUnique',
                data: {
                    generalCountryId: generalCountryId,
                    fiscalCode: fiscalCode,
                    vendorId: UNGM.VendorRegistration.currentVendorId
                },
                type: 'GET',
                contentType: 'application/json',                
                success: function (response) {
                    sender.siblings(".checkingForUnique").hide()
                    if (response == false) {                        
                        UNGM.Validation.markElementInvalid($("#GeneralCountryId"));
                        UNGM.Validation.markElementInvalid($("#FiscalCode"));
                        UNGM.VendorAccountRegistration.countryAndFiscalCodeInvalid = true;
                        $(".duplicateOfCountryAndFiscalCode").show();
                    } else {                                                
                        UNGM.Validation.markElementValid(sender);
                        UNGM.VendorAccountRegistration.countryAndFiscalCodeInvalid = false;
                        $(".duplicateOfCountryAndFiscalCode").hide();                        
                    }
                }
            });
        }
        else {
            sender.siblings(".checkingForUnique").hide();            
        }        
    },
    onIndividualConsultantTypeClicked: function () {
        $('#individualConsultantTypeDescription').show();
        $('#individualConsultantTypeDescription').text($(this).attr('data-description'));        
    }
}
;
window.UNGM.AcceptInvitation = {
    invalidInvitationTitle: '',
    invalidInvitationButton: '',
    unknownErrorMessage: '',
    invalidInvitationText: '',
    activeEmailExistsText: '',
    inviteSuccesfullyAcceptedTitle: '',
    inviteSuccesfullyAcceptedButton: '',
    inviteSuccesfullyAcceptedText: '',
    init: function () {
        $("#btnSave").on("click", UNGM.AcceptInvitation.saveClicked);
        $("#Password").off("keyup").on("keyup", UNGM.ManageAccount.onPasswordKeyUp);
    },
    saveClicked: function (evt) {
        var form = $(evt.currentTarget).parents('form');
        if (form.valid()) {

            var Info = {
                Id: form.find("#Id").val(),
                UserTitle: form.find("#Info_UserTitle").val(),
                FirstName: form.find("#Info_FirstName").val(),
                MiddleName: form.find("#Info_MiddleName").val(),
                Surname: form.find("#Info_Surname").val(),
                CompanyPosition: form.find("#Info_CompanyPosition").val(),
                CountryId: form.find("#Info_CountryId").val(),
                Email: form.find("#Info_Email").val(),
                ContactTelephoneCountryId: form.find("#Info_ContactTelephoneCountryId").val(),
                TelephoneNumber: form.find("#Info_TelephoneNumber").val(),
                TelephoneExtension: form.find("#Info_TelephoneExtension").val(),
                MobileCountryId: form.find("#Info_MobileCountryId").val(),
                MobileNumber: form.find("#Info_MobileNumber").val()
            };
            var postObject = {
                Info: Info,
                Password: form.find("#Password").val(),
                ConfirmPassword: form.find("#ConfirmPassword").val(),
                InviteToken: $('#InviteToken').val()
            };
            

            UNGM.throbOver(form);
            $.ajax({
                type: "POST",
                url: form.attr('action'),
                contentType: 'application/json',
                data: JSON.stringify(postObject),
                success: UNGM.AcceptInvitation.saveComplete,
                global: false,  // Disable global events, as UNGM.ajaxErrorHandler 
                error: function (response) {
                    UNGM.appendErrorTo(form, response.responseJSON);
                    UNGM.hideThrobber();
                },
                complete: function () { UNGM.hideThrobber(); }
            });
        }
    },
    saveComplete: function (data) {
        UNGM.hideThrobber();
        if (data.success) {
            // Show welcome popup
            var OkHandler = function () {
                // Redirect to Home
                location.href = UNGM.siteRoot + 'Account/Account/CheckUser?' + $("#RedirectToHome").val();
            };
            $.confirm(UNGM.AcceptInvitation.inviteSuccesfullyAcceptedTitle,
                UNGM.AcceptInvitation.inviteSuccesfullyAcceptedText,
                UNGM.AcceptInvitation.inviteSuccesfullyAcceptedButton,
                "",
                OkHandler);
        }
        else {
            var msg = '';
            switch(data.reason)
            {
                case "ActiveEmailExists":
                    msg = UNGM.AcceptInvitation.activeEmailExistsText;
                    break;
                case "Unknown":
                    msg = UNGM.AcceptInvitation.unknownErrorMessage;
                    break;
                case "InvalidInvitation":
                    msg = UNGM.AcceptInvitation.invalidInvitationText;
                    break;
            }

            $.confirm(UNGM.AcceptInvitation.invalidInvitationTitle,
                    msg,
                    UNGM.AcceptInvitation.invalidInvitationButton,
                    "",
                    function () { });
        }
    }
}
;
UNGM.TASDashboard = {
    generalFolder: {
        pageIndex: 0,
        prevPageIndex: 0,
        paging: false,
        inSearch: false,
        folderId: ""
    },
    tasVoucherFolder: {
        pageIndex: 0,
        prevPageIndex: 0,
        paging: false,
        inSearch: false,
        folderId: "tasVoucherList",
        isContentLoaded: false
    },
    tabToggledCallback: {},
    updatePlansCallback: null,
    init: function () {
        $(".lnkShowUsers").bind("click", UNGM.TASDashboard.onLnkShowUsersClicked);
        $("#txtDateFrom, #txtDateTo").bind("change", UNGM.TASDashboard.refreshReport);

        // Set default dates
        var day1OfThisMonth = new Date(new Date().setDate(1));
        $("#txtDateFrom").datepicker('setDate', day1OfThisMonth);
        $("#txtDateTo").datepicker('setDate', new Date());

        $(".tabHeader").bind("click", UNGM.TASDashboard.toggleTab);
    },
    setTabToggledCallback: function (tab, callback) {
        UNGM.TASDashboard.tabToggledCallback[tab] = callback;
    },
    setUpdatePlansCallback: function (callback) {
        UNGM.TASDashboard.updatePlansCallback = callback;
    },
    triggerUpdatePlans: function (plans) {
        if (UNGM.TASDashboard.updatePlansCallback) {
            UNGM.TASDashboard.updatePlansCallback(plans);
        }
    },
    toggleTab: function (e) {
        var toggledTab = $(e.currentTarget).data("tabid");
        $.each(UNGM.TASDashboard.tabToggledCallback, function (tab) {
            var callback = UNGM.TASDashboard.tabToggledCallback[tab];
            callback(tab === toggledTab);
        });
    },
    OnGotData: function (response) {
        UNGM.TASDashboard.generalFolder.prevPageIndex = UNGM.TASDashboard.generalFolder.pageIndex;
        if (response.length) {
            var $folderContainer = $("#" + UNGM.TASDashboard.generalFolder.folderId + "Folder")
            var $tableBody = $folderContainer.find(".tableBody");
            $tableBody.children("script").remove();

            if (UNGM.TASDashboard.generalFolder.paging) {
                $tableBody.append(response);
                UNGM.TASDashboard.generalFolder.paging = false;
            }
            else {
                $tableBody.html(response);
            }

            UNGM.ieHackTableCells();
        }

        var showing = $tableBody.find(".tableRow").length;
        $folderContainer.find(".showing").html(showing);

        if (showing > 0) {
            $folderContainer.find("#" + UNGM.TASDashboard.generalFolder.folderId + "Empty").hide();
            $folderContainer.find(".resultsContainer").show();
        }
        else {
            $folderContainer.find("#" + UNGM.TASDashboard.generalFolder.folderId + "Empty").show();
            $folderContainer.find(".resultsContainer").hide();
        }

        UNGM.TASDashboard.setWaypoints();
        UNGM.hideThrobber();
    },
    setWaypoints: function () {
        // Only set the waypoints if there are more items to load
        var $folder = $("#" + UNGM.TASDashboard.generalFolder.folderId + "Folder");

        if ($folder.find(".showing").html() == $folder.find(".searchTotal").html()) {
            return;
        }

        $folder.find(".folderHolder").waypoint(function (direction) {
            if (direction === 'down') {
                if (!UNGM.TASDashboard.generalFolder.inSearch) {
                    $.waypoints('destroy');
                    UNGM.TASDashboard.generalFolder.inSearch = true;
                    UNGM.TASDashboard.generalFolder.pageIndex = UNGM.TASDashboard.generalFolder.prevPageIndex + 1;
                    UNGM.TASDashboard.generalFolder.paging = true;
                    UNGM.TASDashboard.searchFunction();
                }
            }
        }, { offset: 'bottom-in-view' });
        UNGM.TASDashboard.generalFolder.inSearch = false;
    },
    buildOptions: function () {
        var opts = {
            dateFrom: UNGM.getDatepickerVal("#txtDateFrom"),
            dateTo: UNGM.getDatepickerVal("#txtDateTo"),
        };

        return opts;
    },
    refreshReport: function () {
        var opts = UNGM.TASDashboard.buildOptions();

        UNGM.throbOver(".tasReports");
        $.ajax({
            url: UNGM.siteRoot + 'Admin/TASDashboard/TenderAlertServiceReport',
            type: 'POST',
            contentType: 'application/json',
            data: JSON.stringify(opts),
            success: UNGM.TASDashboard.onGotReportData
        });
    },
    onGotReportData: function (data) {
        $("#tenderAlertServiceReports").html(data);
        $(".lnkShowUsers").off("click").on("click", UNGM.TASDashboard.onLnkShowUsersClicked);

        UNGM.hideThrobber();
    },
    onLnkShowUsersClicked: function (e) {
        UNGM.throbOver(".tasReports");

        var $link = $(e.currentTarget);
        var title = $link.parents(".reportRow").find("label").text();
        var reportName = $link.data("reportname");

        var opts = UNGM.TASDashboard.buildOptions();

        $.ajax({
            url: UNGM.siteRoot + 'Admin/TASDashboard/' + reportName + '?dateFrom=' + opts.dateFrom + '&dateTo=' + opts.dateTo,
            type: 'GET',
            contentType: 'application/json',
            success: function (data) {
                UNGM.TASDashboard.onGotUsersData(data, title, reportName);
            }
        });
    },
    onGotUsersData: function (data, title, reportName) {
        $("<div>").html(data).dialog({
            title: title,
            modal: true,
            width: '80%',
            height: "600",
            hide: { effect: 'fade', duration: 100 },
            show: { effect: 'fade', duration: 100 },
            open: function () {
                $("#btnExportToExcel").data("reportName", reportName).bind("click", UNGM.TASDashboard.onBtnExportToExcelClicked);
                UNGM.ieHackTableHeader();
                UNGM.hideThrobber();
            },
            close: function () {
                $(this).dialog('destroy').remove();
            }
        });
    },
    onBtnExportToExcelClicked: function (e) {
        // Have to use a GET as we can't get a binary response via jQuery AJAX and we can't send a POST using window.location.href
        var opts = UNGM.TASDashboard.buildOptions();
        var reportName = $(e.currentTarget).data("reportName");
        window.location.href = UNGM.siteRoot + 'Admin/TASDashboard/' + reportName + 'ToExcel?dateFrom=' + opts.dateFrom + '&dateTo=' + opts.dateTo;
    }
};
window.UNGM.AcceptRegistration = {
    init: function () {
        $("#btnCancel").bind("click", UNGM.AcceptRegistration.cancelClicked);
        $("#btnSave").bind("click", UNGM.AcceptRegistration.saveClicked);
        $("#Password").off("keyup").on("keyup", UNGM.ManageAccount.onPasswordKeyUp);
    },
    saveClicked: function (evt) {
        var form = $(evt.currentTarget).parents('form');
        if (form.valid()) {

            var Info = {
                Id: form.find("#Id").val(),
                UserTitle: form.find("#Info_UserTitle").val(),
                FirstName: form.find("#Info_FirstName").val(),
                MiddleName: form.find("#Info_MiddleName").val(),
                Surname: form.find("#Info_Surname").val(),
                CompanyPosition: form.find("#Info_CompanyPosition").val(),
                CountryId: form.find("#Info_CountryId").val(),
                Email: form.find("#Info_Email").val(),
                ContactTelephoneCountryId: form.find("#Info_ContactTelephoneCountryId").val(),
                TelephoneNumber: form.find("#Info_TelephoneNumber").val(),
                TelephoneExtension: form.find("#Info_TelephoneExtension").val(),
                MobileCountryId: form.find("#Info_MobileCountryId").val(),
                MobileNumber: form.find("#Info_MobileNumber").val()
            };
            var postObject = {
                Info: Info,
                Password: form.find("#Password").val(),
                ConfirmPassword: form.find("#ConfirmPassword").val(),
                ActivationCode: $('#ActivationCode').val()
            };

            UNGM.throbOver(form);
            $.ajax({
                type: "POST",
                url: form.attr('action'),
                contentType: 'application/json',
                data: JSON.stringify(postObject),
                success: UNGM.AcceptRegistration.saveComplete,
                complete: function () { UNGM.hideThrobber(); }
            });
        }
    },
    saveComplete: function (data) {
        UNGM.hideThrobber();
        if (data.success) {
            // Show welcome popup
            var OkHandler = function () {
                // Redirect to Home
                location.href = UNGM.siteRoot + 'Account/Account/CheckUser?';
            };
            $.confirm(data.title, data.message, data.okButton, "", OkHandler);
        }
        else {
            $.confirm(data.title, data.message, data.okButton, "", function () { });
        }
    }
};
(function (namespace, undefined) {

    namespace.Push = function () {
        namespace.Counter().Increase();
        namespace.Render();
    }

    namespace.Pop = function () {
        namespace.Counter().Decrease();
        namespace.DelayRender();
    }

    namespace.Reset = function () {
        // this function shouldn't be called if the throbber is maintained correctly
        namespace.Counter().Reset();
        UNGM.hideThrobber();
    }

    namespace.Render = function () {
        if (namespace.Counter().GetCount() > 0) {
            UNGM.throbOver();
        } else {
            UNGM.hideThrobber();
        }
    }

    namespace.DelayRender = function () {
        namespace.renderTimeoutId = namespace.renderTimeoutId || null;
        clearTimeout(namespace.renderTimeoutId);
        namespace.renderTimeoutId = setTimeout(namespace.Render, 300);
    }

    namespace.Counter = function () {
        return namespace.counter = namespace.counter || new Counter();
    }

    ////////////////////////////////////////////////////////////////////////////////
    //
    // Counter
    //

    function Counter() {
        this.counter = 0;
    }

    Counter.prototype.Increase = function () {
        ++this.counter;
    }
    
    Counter.prototype.Decrease = function () {
        this.counter = Math.max(this.counter - 1, 0);
    }
 
    Counter.prototype.GetCount = function () {
        return this.counter;
    }

    Counter.prototype.Reset = function () {
        // this function shouldn't be called if the throbber is maintained correctly
        this.counter = 0;
    }

})(window.UNGM.Throbber = window.UNGM.Throbber || {});
;
(function (namespace, undefined) {

    ////////////////////////////////////////////////////////////////////////////////
    //
    // Detail
    //

    namespace.Detail = function (element) {
        $this = this;
        this.element = element;
        this.children = this.element.find(".children-area");
        this.childrenRefreshUrl = this.children.data("children-refresh-url");
        this.childrenEmptyMessage = this.children.find(".children-empty-message");
        this.childrenListArea = this.children.find(".children-list-area");
        this.childrenList = this.children.find(".children-list");
        this.childTemplate = new ChildTemplate(this.children.find(".child.template"));
        this.btnReset = this.element.find("#btnReset");
        this.btnSearch = this.element.find("#btnSearch");
        this.txtKeywords = this.element.find("#txtKeywords");
        this.btnReset.off("click").on("click", function () { location.reload() } );
        this.btnSearch.off("click").on("click", this.doSearch(element));
        this.txtKeywords.off("keypress").on("keypress", function (e) {
            if (e.keyCode == 13) {
                $this.doSearch(element).call();
    }
        });
    }

    namespace.Detail.prototype.SetCallbackOnChildRender = function (callbackOnChildRender) {
        this.callbackOnChildRender = callbackOnChildRender;
    }

    namespace.Detail.prototype.RefreshChildren = function () {
        var deferred = $.Deferred();
        var $this = this;
        UNGM.Throbber.Push();
        $.post(this.childrenRefreshUrl, function (children) {
            if (typeof (children) == "string") { children = JSON.parse(children); }
            $this.RenderChildren(children);
            deferred.resolve(children);
            UNGM.Throbber.Pop();
        });
        return deferred.promise();
    }

    namespace.Detail.prototype.RenderChildren = function (children) {
        if (typeof (children) == "string") { children = JSON.parse(children); }
        var $this = this;
        this.childrenList.empty();
        if (children.length === 0) {
            this.childrenEmptyMessage.show();
            this.childrenListArea.hide();
        } else {
            $.each(children, function (index, child) {
                var row = $this.childTemplate.Render(child);
                if ($this.callbackOnChildRender !== undefined) { $this.callbackOnChildRender(row); }
                $this.childrenList.append(row);
            });
            this.childrenEmptyMessage.hide();
            this.childrenListArea.show();
        }
    }

    namespace.Detail.prototype.BindAction = function (element, callbackOnDialogLoaded, callbackOnActionTaken) {
        var $this = this;
        element.click(function (event) {
            var button = $(this);
            var url = button.data("action-url");
            if (button.hasClass("display-in-dialog")) {
                var title = button.data("dialog-title");
                $this.OpenDialog(title, url, callbackOnDialogLoaded);
            } else if (button.hasClass("action-no-dialog")) {
                var data = {};
                var key = button.data("action-data-key");
                if (key !== undefined) { data[key] = button.data("action-data-value"); }
                $this.TakeAction(url, data, button, callbackOnActionTaken);
            } else {
                location.href = url;
            }
        });
    }

    namespace.Detail.prototype.OpenDialog = function (title, url, callbackOnDialogLoaded) {
        var $this = this;
        UNGM.Throbber.Push();
        UNGM.initJQueryUIDialogWithCKEDITOR();
        $.get(url, function (result) {
            var childFormDialog = namespace.Detail.GetChildDialogHolder();
            childFormDialog.html(result).dialog({
                modal: true,
                title: title,
                width: "50%",
                height: "auto",
                hide: { effect: "fade", duration: 100 },
                show: { effect: "fade", duration: 100 }
            });
            if (callbackOnDialogLoaded !== undefined) { callbackOnDialogLoaded(childFormDialog); }
            UNGM.Throbber.Pop();
        });
    }

    namespace.Detail.prototype.TakeAction = function (url, data, button, callbackOnActionTaken) {
        var $this = this;
        UNGM.Throbber.Push();
        $.post(url, data, function (result) {
            if (callbackOnActionTaken !== undefined) { callbackOnActionTaken(button); };
            UNGM.Throbber.Pop();
        });
    }

    namespace.Detail.GetChildDialogHolder = function () {
        //
        // static function to reuse dialog holder
        //
        // NOTE: This is required because CKEDITOR is in used for the child form.
        // If there are multiple dialog holders, the code would fail the second time
        // the form is loaded because CKEDITOR has already been initialised for the
        // textarea with the same id.
        //
        return this.childDialogHolder = this.childDialogHolder || $("<div></div>");
    }

    ////////////////////////////////////////////////////////////////////////////////
    //
    // ChildTemplate
    //

    function ChildTemplate(element) {
        this.template = element.removeClass("template")[0];
    }

    ChildTemplate.prototype.Render = function (child) {
        var regex = new RegExp("{{\\s*(\\w+)\\s*([|]\\s*(\\w+)\\s*)?}}", "g"); // matching {{ key | filter }}
        var html = this.template.outerHTML.replace(regex, function (match, key, _, filter, _, _) {
            var value = child[key];
            if (value === undefined) { return match; }
            if (filter !== undefined) {
                switch (filter.toLowerCase()) {
                    case "replace_line_break_with_br": return value.replace(/\n/g, "<br />");
                    case "strip_tags_and_trim": return UNGM.Forum.Formatter.StripTagAndTrim(value, 120);
                    case "datetime": return UNGM.Forum.Formatter.FormatDateTime(value);
                    case "date": return UNGM.Forum.Formatter.FormatDate(value);
                    case "time": return UNGM.Forum.Formatter.FormatTime(value);
                    case "bool": return value == true ? "Yes" : "No";
                }
            }
            return value;
        });
        var renderredElement = $(html);
        renderredElement.find("[data-render-if]").each(function (_index, item) {
            var renderIf = eval($(item).data("render-if"));
            var render = renderIf === true || renderIf === "true";
            if (!render) { $(item).remove(); }
            $(item).removeAttr("data-render-if");
        });
        return renderredElement;
    }

    namespace.Detail.prototype.doSearch = function (element) {
        return function () {            
            var keywords = element.find("#txtKeywords").val().trim();
            var cleanText = keywords.replace(/<\/?[^>]+(>|$)/g, "");
            if (cleanText.trim().length > 0) {
                UNGM.Throbber.Push();
                var url = UNGM.siteRoot + "Shared/Forum/Search";
                    $.get(url, { id: element.attr("data-forum-id"), keywords: cleanText })
                .success(function (response) {
                        element.find(".search-area").html(response);
                    element.find(".children-area").hide();
                })
                .always(function () { 
                    UNGM.Throbber.Pop(); 
                });
            }
        }            
    }   

})(window.UNGM.Forum = window.UNGM.Forum || {});
;
(function (namespace, undefined) {

    ////////////////////////////////////////////////////////////////////////////////
    //
    // Form
    //

    namespace.Form = function (element) {
        this.form = element;
        this.submit = this.form.find("input[type='submit']");

        var $this = this;
        this.submit.click(function (event) { $this.Submit(event); });

        this.ckeditor = new CkeditorAdapter(this.form.find(".ckeditor"), UNGM.standardToolbarWithSource, this.form);
    }

    namespace.Form.prototype.SetCallbackOnSuccess = function (callbackOnSuccess) {
        this.callbackOnSuccess = callbackOnSuccess;
    }

    namespace.Form.prototype.Submit = function (event) {
        event.preventDefault();
        this.ckeditor.UpdateElement();
        if (this.form.valid()) {
            UNGM.Throbber.Push();
            var data = {};
            var inputs = this.form.find("input[type='hidden'], input[type='text'], textarea");
            inputs.each(function (index, input) {
                var key = $(input).attr('name');
                var value = $(input).val();
                data[key] = value;
            });
            var $this = this;
            $.post(this.form.attr("action"), data, function (result) {
                if ($this.callbackOnSuccess !== undefined) { $this.callbackOnSuccess(result); }
                UNGM.Throbber.Pop();
            });
        }
    }

    ////////////////////////////////////////////////////////////////////////////////
    //
    // CkeditorAdapter
    //

    function CkeditorAdapter(element, toolbar, form) {
        this.element = element;
        this.enabled = this.element.length !== 0;
        if (this.enabled) {
            this.ckeditor = CKEDITOR.replace(this.element.get(0), { toolbar: toolbar });
            this.validator = form.validate();
            this.validator.settings.ignore = "";

            var $this = this;
            this.ckeditor.on("blur", function () { $this.UpdateElement(); });
        }
    }

    CkeditorAdapter.prototype.UpdateElement = function () {
        if (this.enabled) {
            this.ckeditor.updateElement();
            this.validator.element(this.element);
        }
    }

})(window.UNGM.Forum = window.UNGM.Forum || {});
;
(function (namespace, undefined) {

    ////////////////////////////////////////////////////////////////////////////////
    //
    // Formatter
    //

    namespace.Formatter = function () {}

    namespace.Formatter.StripTagAndTrim = function (value, maxLength) {
        var text = $(value).text();
        return text.length < maxLength ? text : text.substr(0, maxLength) + "...";
    }

    namespace.Formatter.FormatDateTime = function (value) {
        return namespace.Formatter.FormatDate(value) + " " + namespace.Formatter.FormatTime(value);
    }

    namespace.Formatter.FormatDate = function (value) {
        var datetime = new Date(value);
        return $.datepicker.formatDate("dd-M-yy", datetime);
    }

    namespace.Formatter.FormatTime = function (value) {
        var datetime = new Date(value);
        return datetime.toLocaleTimeString(namespace.Formatter.GetLocale(), { hour12: false });
    }

    namespace.Formatter.GetLocale = function () {
        return namespace.Formatter.locale = namespace.Formatter.locale || UNGM.currentuserCookieLanguage;
    }

})(window.UNGM.Forum = window.UNGM.Forum || {});
;
(function (namespace, undefined) {

    namespace.Search = function (element) {
        this.searchResults = element.find(".search-results");
        this.keywords = element.find("#txtKeywords");        
	    
        if (this.searchResults.length > 0 && this.keywords.val().trim().length > 0) {
            Highlight(this.searchResults, this.keywords);
	    }
	    
        function Highlight(element, keywords) {
	        var htmlElement = element.html();
                var term = keywords.val().trim().split(' ');
	        $.each(term, function (index, value) {
	            term = value.replace(/(\s+)/, "(<[^>]+>)*$1(<[^>]+>)*");        
	            var pattern = new RegExp("(" + term + ")(?!([^<]+)?>)", "gi");	        

	            htmlElement = htmlElement.replace(pattern, "<mark>$1</mark>");
	            htmlElement = htmlElement.replace(/(<mark>[^<>]*)((<[^>]+>)+)([^<>]*<\/mark>)/, "$1</mark>$2<mark>$4");
	        });	        
	        element.html(htmlElement);
	        element.find(".result-label").each(function (index) {	            
	            $(this).text($(this).text());// unhighlight	result labels           
	        });
	    }   
    }
})(window.UNGM.Forum = window.UNGM.Forum || {});;
(function (namespace, undefined) {

    namespace.Map = function (parameter) {
        var $this = this;

        this.SearchFilter = new SearchFilter(parameter.SearchFilter);
        this.SearchFilter.OnChange(function () {
            $this.Search();
            $this.UpdateExecutiveSummaryContainer();
        });
        this.MapContainerId = parameter.MapContainerId;
        this.Gmap = null;
        this.Markers = [];
        this.Coordinates = [];
        this.MapOptionsZoom = parameter.MapOptionsZoom || 2;
        // Center the map in Algeria by default (it shows Denmark center-on top)
        this.MapOptionsLat = parameter.MapOptionsLat || 28.033;
        this.MapOptionsLon = parameter.MapOptionsLon || 1.659;
        this.MapDialogContent = parameter.MapDialogContent;
        this.MapLegend = parameter.MapLegend;
        this.MapLegendTextAmountInMillions = parameter.MapLegendTextAmountInMillions;
        this.MapLegendTextAmount = parameter.MapLegendTextAmount;
        // executive summary  
        this.DdlReportsForDownloadSelector = $(parameter.DdlReportsForDownloadSelector);
        this.DdlReportsForDownloadSelector.change(function (event) { $this.DownloadReport(event); });
        this.PdfContainerSelector = parameter.PdfContainerSelector;
        this.ExecutiveSummary2017Title = parameter.ExecutiveSummary2017Title;
        this.ExecutiveSummary2016Title = parameter.ExecutiveSummary2016Title;
        this.ExecutiveSummary2015Title = parameter.ExecutiveSummary2015Title;
        this.ExecutiveSummary2014Title = parameter.ExecutiveSummary2014Title;
        this.ExecutiveSummary2013Title = parameter.ExecutiveSummary2013Title;
        this.ExecutiveSummary2017Link = parameter.ExecutiveSummary2017Link;
        this.ExecutiveSummary2016Link = parameter.ExecutiveSummary2016Link;
        this.ExecutiveSummary2015Link = parameter.ExecutiveSummary2015Link;
        this.ExecutiveSummary2014Link = parameter.ExecutiveSummary2014Link;
        this.ExecutiveSummary2013Link = parameter.ExecutiveSummary2013Link;
        this.ExecutiveSummaryNotAvailableInfo = parameter.ExecutiveSummaryNotAvailableInfo;
        this.ExecutiveSummaryNotAvailableHolderSelector = parameter.ExecutiveSummaryNotAvailableHolderSelector;
        $this.UpdateExecutiveSummaryContainer();
    }

    namespace.Map.prototype.InitMap = function () {
        var $this = this;

        var mapOptions = {
            center: { lat: $this.MapOptionsLat, lng: $this.MapOptionsLon },
            zoom: $this.MapOptionsZoom,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };

        $this.Gmap = new google.maps.Map(document.getElementById($this.MapContainerId), mapOptions);
    }

    namespace.Map.prototype.Search = function () {
        var $this = this;
        UNGM.Throbber.Push();

        $this.ResetMarkers();

        // Get markers
        var data = $this.SearchFilter.GetData();
        $.ajax({
            url: UNGM.siteRoot + "Public/ASRDataArchive/GetVolumeOfAreaByYear",
            type: "POST",
            data: JSON.stringify({ year: data.Year, area: data.Area }),
            contentType: "application/json",
            success: function (response) {
                $this.LoadCoordinates(response);
                $this.RenderMarkers();
                UNGM.Throbber.Pop();
            }
        });
    }

    namespace.Map.prototype.Init = function () {
        var $this = this;

        UNGM.Throbber.Push();
        $this.InitMap();
        $this.Search();
        UNGM.Throbber.Pop();
    }

    namespace.Map.prototype.LoadCoordinates = function (response) {
        var $this = this;

        if (typeof response != 'object') { response = JSON.parse(response); }

        $this.Coordinates = response;
    }

    namespace.Map.prototype.RenderMarkers = function () {
        var $this = this;
        var createInfoWindows = $this.Coordinates.length < 40;

        $(this.MapLegend + " div").empty();
        $(this.MapLegend).fadeOut();

        for (var i = 0; i < $this.Coordinates.length; i++) {
            if ($this.Coordinates[i].Lat === null) {
                var amountInM = $this.Coordinates[i].Amount / 1000000;
                var text = "";
                if (amountInM > 0.1) {
                    text = this.MapLegendTextAmountInMillions.replace("[AREANAME]", $this.Coordinates[i].Name).replace("[AMOUNTINMILLIONS]", amountInM.toFixed(1));
                } else {
                    text = this.MapLegendTextAmount.replace("[AREANAME]", $this.Coordinates[i].Name).replace("[AMOUNT]", $this.Coordinates[i].Amount.toFixed(1));
                }
                $(this.MapLegend + " div").append(text + " <br/>");
                $(this.MapLegend).fadeIn();
                continue;
            }

            var marker = new google.maps.Marker({
                position: new google.maps.LatLng($this.Coordinates[i].Lat, $this.Coordinates[i].Lng),
                map: $this.Gmap,
                name: $this.Coordinates[i].Name,
                amount: $this.Coordinates[i].Amount,
                icon: $this.GetCircle($this.Coordinates[i].Amount)
            });

            if (createInfoWindows) {
                marker['infowindow'] = new google.maps.InfoWindow({
                    content: $this.Coordinates[i].Name
                });

                google.maps.event.addListener(marker, 'mouseover', function () {
                    this['infowindow'].open($this.Gmap, this);
                });

                google.maps.event.addListener(marker, 'mouseout', function () {
                    this['infowindow'].close();
                });
            }

            $this.Markers.push(marker);

            marker.addListener('click', function () {
                $this.OnMarkerClicked(this.name, this.amount);
            });
        }
    }

    namespace.Map.prototype.GetCircle = function (amount) {
        var scale = amount / 30000000;

        var circle = {
            path: google.maps.SymbolPath.CIRCLE,
            scale: scale > 10 ? scale : 10,
            fillOpacity: .5,
            fillColor: "blue",
            strokeColor: "white",
            strokeWeight: .8
        };

        return circle;
    }

    namespace.Map.prototype.OnMarkerClicked = function (name, amount) {
        var $this = this;

        $this.ShowMarkerDetail($this.SearchFilter.Year.GetData(), name, amount);
    }

    namespace.Map.prototype.ShowMarkerDetail = function (year, name, amount) {
        UNGM.Throbber.Push();
        var $this = this;

        var pdfUrl = UNGM.siteRoot + "Areas/Public/Downloads/ASR/" + year + "/Country/" + encodeURIComponent(name.replace(/ /g, '')).replace(/'/g, escape) + year + ".pdf#view=FitH";
        var dialogContent = "";
        var dialogDiv = $("<div>");

        $.ajax({
            url: pdfUrl,
            type: 'HEAD',   // Gets only size of the header, not the actual file
            global: false,  // Disable global events, as UNGM.ajaxErrorHandler 
            error: function () {
                var amountInM = amount / 1000000;
                dialogContent = "<div class='info'>" + $this.MapDialogContent.replace("{0}", name).replace("{1}", amountInM.toFixed(1)) + "</div>";
            },
            success: function () {
                var pdfViewer = UNGM.siteRoot + "Scripts/PDF/web/viewer.html?file=";
                dialogContent = "<iframe src='" + pdfViewer + pdfUrl + "' style='width: " + 0.6 * $(window).width() + "px; height: " + 0.7 * $(window).height() + "px;'></iframe>";
            },
            complete: function () {
                dialogDiv.html(dialogContent);
                dialogDiv.dialog({
                    modal: true,
                    title: name,
                    width: "auto",
                    height: "auto",
                    hide: { effect: 'fade', duration: 100 },
                    show: { effect: 'fade', duration: 100 }
                });
                UNGM.Throbber.Pop();
            }
        });
    }

    namespace.Map.prototype.ResetMarkers = function () {
        var $this = this;

        // Remove markers from map
        for (var i = 0; i < $this.Markers.length; i++) {
            $this.Markers[i].setMap(null);
        }

        $this.Markers = [];
    }

    namespace.Map.prototype.UpdateExecutiveSummaryContainer = function () {
        var $this = this;
        var selectedYear = $this.SearchFilter.Year.GetData();

        var executiveSummaryTitle = "";
        var executiveSummaryLink = "";

        // Unfortunately every year this needs to be updated
        if (selectedYear === "2017") {
            executiveSummaryTitle = $this.ExecutiveSummary2017Title;
            executiveSummaryLink = UNGM.siteRoot + $this.ExecutiveSummary2017Link;
        }
        if (selectedYear === "2016") {
            executiveSummaryTitle = $this.ExecutiveSummary2016Title;
            executiveSummaryLink = UNGM.siteRoot + $this.ExecutiveSummary2016Link;
        }
        if (selectedYear === "2015") {
            executiveSummaryTitle = $this.ExecutiveSummary2015Title;
            executiveSummaryLink = UNGM.siteRoot + $this.ExecutiveSummary2015Link;
        }
        if (selectedYear === "2014") {
            executiveSummaryTitle = $this.ExecutiveSummary2014Title;
            executiveSummaryLink = UNGM.siteRoot + $this.ExecutiveSummary2014Link;
        } else if (selectedYear === "2013") {
            executiveSummaryTitle = $this.ExecutiveSummary2013Title;
            executiveSummaryLink = UNGM.siteRoot + $this.ExecutiveSummary2013Link;
        }

        if (executiveSummaryTitle !== "" && executiveSummaryLink !== "") {
            $($this.ExecutiveSummaryNotAvailableHolderSelector).hide();
            $(".executiveSummaryHolder .executiveSummaryTitle").show().text(executiveSummaryTitle);
            $($this.PdfContainerSelector).show().prop("src", UNGM.siteRoot + "Scripts/PDF/web/viewer.html?file=" + executiveSummaryLink);
        } else {
            $(".executiveSummaryHolder .executiveSummaryTitle").hide();
            $($this.PdfContainerSelector).hide();
            $($this.ExecutiveSummaryNotAvailableHolderSelector).show().text($this.ExecutiveSummaryNotAvailableInfo.replace("[year]", selectedYear));
        }
    }

    namespace.Map.prototype.DownloadReport = function () {
        var $this = this;
        var selectedReportOption = $this.DdlReportsForDownloadSelector.find(":selected");

        if (selectedReportOption.val() === "") {
            // The first empty option has been selected
            return;
        }

        // Log google analytics
        var gaInfo = selectedReportOption.data("gainfo");
        try {
            // ga is not defined in dev environment
            ga('send', 'event', 'StatReports', 'Download', gaInfo);
        }
        catch (ex) {
            console.log("Error logging the Google analytics download for: " + gaInfo);
        }

        // Download the file
        var pdfLink = UNGM.siteRoot + selectedReportOption.val();
        window.open(pdfLink);
    }

    //
    // SearchFilter
    //

    function SearchFilter(params) {
        this.Year = new Year(params.Year);
        this.Area = new Area(params.Area);
    }

    SearchFilter.prototype.GetData = function () {
        var data = {
            Year: this.Year.GetData(),
            Area: this.Area.GetData()
        };

        return data;
    }

    SearchFilter.prototype.OnChange = function (callback) {
        this.Year.OnChange(callback);
        this.Area.OnChange(callback);
    }

    //
    // Year
    //

    function Year(params) {
        this.Element = $(params.Selector);
        this.Default = this.Element.val();
    }

    Year.prototype.GetData = function () {
        return this.Element.val();
    }

    Year.prototype.OnChange = function (callback) {
        this.Element.change(callback);
    }

    //
    // Area
    //

    function Area(params) {
        this.Element = $(params.Selector);
    }

    Area.prototype.GetData = function () {
        return this.Element.filter(":checked").val();
    }

    Area.prototype.OnChange = function (callback) {
        this.Element.click(callback);
    }

}(window.UNGM.OnlineASR = window.UNGM.OnlineASR || {}));
;
window.UNGM.OnlineASR = window.UNGM.OnlineASR || {};

(function (namespace, undefined) {

    ////////////////////////////////////////////////////////////////////////////////
    //
    // YearSelector
    //

    namespace.YearSelector = function () {
        this.element = null;
        this.callbacks = [];
        this.url = "";
    }

    namespace.YearSelector.GetInstance = function () {
        return this.instance = this.instance || new namespace.YearSelector();
    }

    namespace.YearSelector.prototype.SetElement = function (element) {
        var $this = this;
        if (this.element) { this.element.unbind("change"); }
        this.element = element;
        this.element.change(function () { $this.Change(); });
    }

    namespace.YearSelector.prototype.GetYear = function () {
        return this.element.val();
    }

    namespace.YearSelector.prototype.AttachCallbackOnChange = function (callback) {
        this.callbacks.push(callback);
    }

    namespace.YearSelector.prototype.Change = function () {
        var $this = this;
        var year = this.element.val();
        $.each(this.callbacks, function (index, callback) { callback(year); });
    }

    ////////////////////////////////////////////////////////////////////////////////
    //
    // TabHeader
    //

    namespace.TabHeader = function (element) {
        namespace.TabHeader.instances = namespace.TabHeader.instances || [];
        var instances = $.grep(namespace.TabHeader.instances, function (instance) { return instance.element[0] === element[0] });
        if (instances.length !== 0) {
            return instances[0];
        } else {
            var instance = new TabHeader(element);
            namespace.TabHeader.instances.push(instance);
            return instance;
        }
    }

    function TabHeader(element) {
        this.element = element;
        this.callbacksOnActive = [];
        this.callbacksOnInactive = [];
        this.element.click(function (event) {
            $.each(namespace.TabHeader.instances, function (index, instance) {
                var isActive = instance.element[0] === event.currentTarget;
                if (isActive) {
                    instance.NotifyCallbackOnActive();
                } else {
                    instance.NotifyCallbackOnInactive();
                }
            });
        });
    }

    TabHeader.prototype.IsActive = function () {
        return this.element.hasClass("activeTab");
    }

    TabHeader.prototype.AttachCallbackOnActive = function (callback) {
        this.callbacksOnActive.push(callback);
        if (this.IsActive()) { callback(); }
    }

    TabHeader.prototype.AttachCallbackOnInactive = function (callback) {
        this.callbacksOnInactive.push(callback);
        if (!this.IsActive()) { callback(); }
    }

    TabHeader.prototype.NotifyCallbackOnActive = function () {
        $.each(this.callbacksOnActive, function (index, callback) { callback(); });
    }

    TabHeader.prototype.NotifyCallbackOnInactive = function () {
        $.each(this.callbacksOnInactive, function (index, callback) { callback(); });
    }

    ////////////////////////////////////////////////////////////////////////////////
    //
    // Search
    //

    namespace.Search = function (element) {
        this.element = element;
        this.legend = element.find(".legend");
        this.content = element.children().not(".legend");
        this.filters = new SearchFilterWrapper(element.find(".search-filters"));
        this.sorters = new SearchSorterWrapper(element.find(".search-sorters"));
        this.results = new SearchResultWrapper(element.find(".search-results"));
        this.paginationEnabled = false;
        this.pageCurrentIndex = -1;
        this.lastPageReached = false;
        this.isSearching = false;
        this.isSearchingTotal = false;
        this.isSearchingTotalUnspecified = false;
        this.requireSearchTotal = false;
        this.expanded = true;
        this.url = null;
        this.totalUrl = null;
        this.totalUnspecifiedUrl = null;

        var $this = this;
        this.delayedSearch = UNGM.ViewModel.DelayedFunction(function () { $this.SearchImpl() });
        this.filters.AttachCallbackOnChange(function () { $this.Refresh(); });
        this.sorters.AttachCallbackOnChange(function () { $this.Refresh(); });
    }

    namespace.Search.prototype.SetResourceUrl = function (url) {
        this.url = url;
    }

    namespace.Search.prototype.SetTotalResourceUrl = function (url) {
        this.totalUrl = url;
    }

    namespace.Search.prototype.SetTotalUnspecifiedResourceUrl = function (url) {
        this.totalUnspecifiedUrl = url;
    }

    namespace.Search.prototype.SetResource = function (resource) {
        var data = resource.hasOwnProperty("Data") ? resource.Data : resource;
        var total = resource.hasOwnProperty("Total") ? resource.Total : null;
        if (this.pageCurrentIndex === -1) {
            this.results.SetData(data);
        } else {
            this.results.AddData(data);
        }
        this.results.SetTotal(total);
        this.results.Render();
    }

    namespace.Search.prototype.SetTotalResource = function (resource) {
        this.results.SetTotalData(resource);
        this.results.Render();
    }

    namespace.Search.prototype.SetTotalUnspecifiedResource = function (resource) {
        this.results.SetTotalUnspecifiedData(resource);
        this.results.Render();
    }

    namespace.Search.prototype.SetDefaultSort = function (field, descending) {
        this.sorters.SetDefaultSort(field, descending);
    }

    namespace.Search.prototype.CreateHiddenSearchFilter = function (selector) {
        return this.filters.CreateHiddenSearchFilter(selector);
    }

    namespace.Search.prototype.CreateMultipleResourceSearchFilter = function (selector, params) {
        return this.filters.CreateMultipleResourceSearchFilter(selector, params);
    }

    namespace.Search.prototype.CreateUNSPSCSearchFilter = function (selector, params) {
        return this.filters.CreateUNSPSCSearchFilter(selector, params);
    }

    namespace.Search.prototype.ClearAllFilters = function (selector, params) {
        this.filters.ClearAllFilters(selector, params);
    }

    namespace.Search.prototype.EnablePagination = function () {
        this.paginationEnabled = true;
        this.CreateSearchWaypoint();
    }

    namespace.Search.prototype.DisablePagination = function () {
        this.paginationEnabled = false;
        this.DestroySearchWaypoint();
    }

    namespace.Search.prototype.EnableToggling = function () {
        var $this = this;
        this.legend.click(function () { $this.Toggle(); });
    }

    namespace.Search.prototype.Toggle = function () {
        var $this = this;
        this.expanded = !this.expanded;
        if ($this.expanded) {
            $this.legend.addClass("expanded");
        } else {
            $this.legend.removeClass("expanded");
        }
        this.content.slideToggle({
            complete: function () {
                if ($this.paginationEnabled) {
                    if ($this.expanded) {
                        $this.CreateSearchWaypoint();
                    } else {
                        $this.DestroySearchWaypoint();
                    }
                }
            }
        });
    }

    namespace.Search.prototype.Refresh = function () {
        this.pageCurrentIndex = -1;
        this.lastPageReached = false;
        this.requireSearchTotal = true;
        this.Search();
    }

    namespace.Search.prototype.Search = function () {
        this.delayedSearch.invoke();
    }

    namespace.Search.prototype.SearchImpl = function () {
        this.CallServerSearch();
        this.CallServerTotal();
        this.CallServerTotalUnspecified();
    }

    namespace.Search.prototype.CallServerSearch = function () {
        if (!this.isSearching) {
            this.isSearching = true;
            UNGM.Throbber.Push();
            var $this = this;
            $.ajax({
                url: this.url,
                type: "POST",
                data: JSON.stringify(this.GetSearchOptions()),
                contentType: "application/json"
            }).success(function (response) {
                if (typeof (response) !== "object") { response = JSON.parse(response); }
                $this.SetResource(response);
                $this.SetPdfUrl();
                $this.lastPageReached = $this.results.GetCount() === $this.results.GetTotal();
                if (!$this.lastPageReached) {
                    $this.pageCurrentIndex += 1;
                    $this.CreateSearchWaypoint();
                }
            }).always(function () {
                UNGM.Throbber.Pop();
                $this.isSearching = false;
            });
        }
    }

    namespace.Search.prototype.CallServerTotal = function () {
        if (!this.isSearchingTotal && this.requireSearchTotal && this.totalUrl !== null) {
            this.isSearchingTotal = true;
            UNGM.Throbber.Push();
            var $this = this;
            $.ajax({
                url: this.totalUrl,
                type: "POST",
                data: JSON.stringify(this.GetSearchOptions()),
                contentType: "application/json"
            }).success(function (response) {
                if (typeof (response) !== "object") { response = JSON.parse(response); }
                $this.SetTotalResource(response);
            }).always(function () {
                UNGM.Throbber.Pop();
                $this.isSearchingTotal = false;
                $this.requireSearchTotal = false;
            });
        }
    }

    namespace.Search.prototype.CallServerTotalUnspecified = function () {
        if (!this.isSearchingTotalUnspecified && this.totalUnspecifiedUrl !== null) {
            this.isSearchingTotalUnspecified = true;
            UNGM.Throbber.Push();
            var $this = this;
            $.ajax({
                url: this.totalUnspecifiedUrl,
                type: "POST",
                data: JSON.stringify(this.GetSearchOptions()),
                contentType: "application/json"
            }).success(function (response) {
                if (typeof (response) !== "object") { response = JSON.parse(response); }
                $this.SetTotalUnspecifiedResource(response);
            }).always(function () {
                UNGM.Throbber.Pop();
                $this.isSearchingTotalUnspecified = false;
            });
        }
    }

    namespace.Search.prototype.GetSearchOptions = function () {
        var options = {};
        $.extend(options, this.filters.GetFilterOptions());
        $.extend(options, this.sorters.GetSortOptions());
        $.extend(options, { PageIndex: this.pageCurrentIndex + 1 });
        return options;
    }

    namespace.Search.prototype.CreateSearchWaypoint = function () {
        if (this.paginationEnabled && this.expanded && !this.lastPageReached) {
            var $this = this;
            this.DestroySearchWaypoint();
            this.results.element.waypoint(function (direction) {
                if (direction === "down") {
                    $this.DestroySearchWaypoint();
                    $this.Search();
                }
            }, { offset: 'bottom-in-view' });
        }
    }

    namespace.Search.prototype.DestroySearchWaypoint = function () {
        this.results.element.waypoint("destroy");
    }

    namespace.Search.prototype.SetPdfUrl = function () {        
        this.element.find(".search-result-template").on("click", function () {
            if ($(this).attr("data-agency-name") !== undefined) {
                UNGM.OnlineASR.ProcurementReports.OpenAgencyPdf($(this).attr("data-agency-name"));
            }
            else if ($(this).attr("data-country-name") !== undefined) {
                UNGM.OnlineASR.ProcurementReports.OpenCountryPdf($(this).attr("data-country-name"));
            }
        });
    }

    ////////////////////////////////////////////////////////////////////////////////
    //
    // SearchSorterWrapper
    //

    function SearchSorterWrapper(element) {
        this.element = element;
        this.sorters = element.find(".sortable");
        this.sortField = "";
        this.sortDescending = null;
        this.callbacks = [];

        var $this = this;
        this.sorters.click(function (event) {
            var sorter = $(event.currentTarget);
            var sortField = sorter.data("sort-field");
            $this.UpdateSortField(sortField);
        });
    }

    SearchSorterWrapper.prototype.SetDefaultSort = function (field, descending) {
        this.sortField = field;
        this.sortDescending = descending;
        this.Render();
    }

    SearchSorterWrapper.prototype.UpdateSortField = function (sortField) {
        if (this.sortField !== sortField) {
            this.sortField = sortField;
            this.sortDescending = false;
        } else {
            this.sortDescending = !this.sortDescending;
        }
        this.Render();
        this.NotifyCallbackOnChange();
    }

    SearchSorterWrapper.prototype.Render = function () {
        this.sorters
            .removeClass("sortedAsc sortedDesc")
            .filter("[data-sort-field='" + this.sortField + "']")
            .addClass(this.sortDescending ? "sortedDesc" : "sortedAsc");
    }

    SearchSorterWrapper.prototype.GetSortOptions = function () {
        return { SortField: this.sortField, SortDescending: this.sortDescending };
    }

    SearchSorterWrapper.prototype.AttachCallbackOnChange = function (callback) {
        this.callbacks.push(callback);
    }

    SearchSorterWrapper.prototype.NotifyCallbackOnChange = function () {
        $.each(this.callbacks, function (index, callback) { callback(); });
    }

    ////////////////////////////////////////////////////////////////////////////////
    //
    // SearchFilterWrapper
    //

    function SearchFilterWrapper(element) {
        this.element = element;
        this.callbacks = [];

        var $this = this;
        this.element.find(".search-filter").change(function () { $this.NotifyCallbackOnChange(); });
    }

    SearchFilterWrapper.prototype.CreateHiddenSearchFilter = function (selector) {
        var $this = this;
        var hiddenSearchFilter = new HiddenSearchFilter(this.element.find(selector));
        hiddenSearchFilter.AttachCallbackOnChange(function () { $this.NotifyCallbackOnChange(); });
        return hiddenSearchFilter;
    }

    SearchFilterWrapper.prototype.CreateMultipleResourceSearchFilter = function (selector, params) {
        var $this = this;
        var multipleResourcePicker = new MultipleResourceSearchFilter(this.element.find(selector));
        multipleResourcePicker.SetResourceParams(params);
        multipleResourcePicker.AttachCallbackOnChange(function () { $this.NotifyCallbackOnChange(); });
        return multipleResourcePicker;
    }

    SearchFilterWrapper.prototype.CreateUNSPSCSearchFilter = function (selector, params) {
        var $this = this;
        var unspscSearchFilter = new UNSPSCSearchFilter(this.element.find(selector), params);
        unspscSearchFilter.AttachCallbackOnChange(function () { $this.NotifyCallbackOnChange(); });
        return unspscSearchFilter;
    }

    SearchFilterWrapper.prototype.GetFilterOptions = function () {
        var $this = this;
        var options = {};
        $.each(this.element.find(".search-filter"), function (index, filter) {
            var element = $(filter);
            if (element.attr("type") === "checkbox" && !element.prop("checked")) { return; }
            var key = element.attr("name");
            var value = element.val();
            if (element.hasClass("multiple")) {
                options[key] = options[key] || [];
                options[key].push(value);
            } else {
                options[key] = value;
            }
        });
        return options;
    }

    SearchFilterWrapper.prototype.ClearAllFilters = function (selector, params) {
        var $this = this;
        this.element.find(selector).click(function () {
            params.ASRCountriesSearchFilter.ClearAllFilters();
            params.ASRAgenciesSearchFilter.ClearAllFilters();
            params.ASRUNSPSCSearchFilter.ClearAllFilters();
            $this.NotifyCallbackOnChange();
        });
    }

    SearchFilterWrapper.prototype.AttachCallbackOnChange = function (callback) {
        this.callbacks.push(callback);
    }

    SearchFilterWrapper.prototype.NotifyCallbackOnChange = function () {
        $.each(this.callbacks, function (index, callback) { callback(); });
    }

    ////////////////////////////////////////////////////////////////////////////////
    //
    // SearchResultWrapper
    //

    function SearchResultWrapper(element) {
        this.element = element;
        this.searchResultsEmpty = element.find(".search-results-empty");
        this.searchResultsFound = element.find(".search-results-found");
        this.searchResultsCount = element.find(".search-results-count");
        this.searchResultsCountTemplate = this.searchResultsCount.html();
        var totalElement = element.find(".search-result-total");
        if (totalElement.length !== 0) {
            this.bondTotal = UNGM.ViewModel.Binder()
                .filter("in_USD", function (value) {
                    var number = parseFloat(value);
                    if (isNaN(number)) { return ""; }
                    if (number < 0) { return "-$" + Math.abs(number).toLocaleString("en", { minimumFractionDigits: 2, maximumFractionDigits: 2, useGrouping: true }); }
                    return "$" + number.toLocaleString("en", { minimumFractionDigits: 2, maximumFractionDigits: 2, useGrouping: true });
                })
                .filter("in_percentage", function (value) {
                    var number = parseFloat(value);
                    if (isNaN(number)) { return ""; }
                    var string = number.toLocaleString("en", { minimumFractionDigits: 2, maximumFractionDigits: 2, useGrouping: true }) + "%";
                    return (string == "0.00%") ? "< 0.01%" : string;
                }).bind(totalElement);
        }
        var unspecifiedTotalElement = element.find(".search-result-total-unspecified");
        if (unspecifiedTotalElement.length !== 0) {
            this.bondUnspecifiedTotal = UNGM.ViewModel.Binder()
                .filter("in_USD", function (value) {
                    var number = parseFloat(value);
                    if (isNaN(number)) { return ""; }
                    if (number < 0) { return "-$" + Math.abs(number).toLocaleString("en", { minimumFractionDigits: 2, maximumFractionDigits: 2, useGrouping: true }); }
                    return "$" + number.toLocaleString("en", { minimumFractionDigits: 2, maximumFractionDigits: 2, useGrouping: true });
                })
                .filter("in_percentage", function (value) {
                    var number = parseFloat(value);
                    if (isNaN(number)) { return ""; }
                    var string = number.toLocaleString("en", { minimumFractionDigits: 2, maximumFractionDigits: 2, useGrouping: true }) + "%";
                    return string;
                }).bind(unspecifiedTotalElement);
        }
        UNGM.ViewModel.Filter("in_USD", function (value) {
            var number = parseFloat(value);
            if (isNaN(number)) { return ""; }
            if (number < 0) { return "-$" + Math.abs(number).toLocaleString("en", { minimumFractionDigits: 2, maximumFractionDigits: 2, useGrouping: true }); }
            return "$" + number.toLocaleString("en", { minimumFractionDigits: 2, maximumFractionDigits: 2, useGrouping: true });
        });
        UNGM.ViewModel.Filter("in_percentage", function (value) {
            var number = parseFloat(value);
            if (isNaN(number)) { return ""; }
            var string = number.toLocaleString("en", { minimumFractionDigits: 2, maximumFractionDigits: 2, useGrouping: true }) + "%";
            return (string == "0.00%") ? "< 0.01%" : string;
        });
        this.repeat = UNGM.ViewModel.Repeat(element.find(".search-result-template"));
        this.repeat.setIndex("Index", 1);
        this.total = 0;
    }

    SearchResultWrapper.prototype.SetData = function (data) {
        this.repeat.data(data);
    }

    SearchResultWrapper.prototype.AddData = function (data) {
        this.repeat.data(this.repeat.data().concat(data))
    }

    SearchResultWrapper.prototype.SetTotal = function (total) {
        this.total = total;
    }

    SearchResultWrapper.prototype.SetTotalData = function (total) {
        if (this.bondTotal) {
            this.bondTotal.data(total);
        }
    }

    SearchResultWrapper.prototype.SetTotalUnspecifiedData = function (totalUnspecified) {
        if (this.bondUnspecifiedTotal) {
            this.bondUnspecifiedTotal.data(totalUnspecified);
        }
    }

    SearchResultWrapper.prototype.GetTotal = function (total) {
        return this.total;
    }

    SearchResultWrapper.prototype.Render = function (result) {
        var count = this.GetCount();
        var total = this.GetTotal();
        if (count !== 0) {
            if (this.searchResultsCountTemplate) {
                var message = this.searchResultsCountTemplate
                    .replace(/{{\s*count\s*}}/gi, count)
                    .replace(/{{\s*total\s*}}/gi, this.total);
                this.searchResultsCount.html(message);
                this.searchResultsCount.show();
            }
            this.searchResultsEmpty.hide();
            this.searchResultsFound.show();
        } else {
            this.searchResultsEmpty.show();
            this.searchResultsCount.hide();
            this.searchResultsFound.hide();
        }
    }

    SearchResultWrapper.prototype.GetCount = function () {
        return this.repeat.data().length;
    }

    ////////////////////////////////////////////////////////////////////////////////
    //
    // HiddenSearchFilter
    //

    function HiddenSearchFilter(element) {
        var $this = this;
        this.element = element;
        this.callbacks = [];
    }

    HiddenSearchFilter.prototype.Change = function (value) {
        this.element.val(value);
        this.NotifyCallbackOnChange();
    }

    HiddenSearchFilter.prototype.AttachCallbackOnChange = function (callback) {
        this.callbacks.push(callback);
    }

    HiddenSearchFilter.prototype.NotifyCallbackOnChange = function () {
        $.each(this.callbacks, function (index, callback) { callback(); });
    }

    ////////////////////////////////////////////////////////////////////////////////
    //
    // MultipleResourceSearchFilter
    //

    function MultipleResourceSearchFilter(element) {
        var $this = this;
        this.element = element;
        this.element.removeClass("search-filter");
        this.usingIE = (/msie/.test(navigator.userAgent.toLowerCase()) && parseInt(navigator.appVersion, 10) <= 6);
        if (this.usingIE) {
            this.element.on("change", function (event) {
                $this.Select($this.element.find("option:selected").text());
                $this.element.val("");
            })
        } else {
            this.autocompleteElement = this.element.siblings(".ui-autocomplete-input");
            if (this.autocompleteElement.length === 0) {
                this.element.selectToAutocomplete();
                this.autocompleteElement = this.element.siblings(".ui-autocomplete-input");
            }
            this.autocompleteElement.on("autocompleteselect", function (event, ui) { $this.Select(ui.item.label); });
            this.autocompleteElement.on("blur", function () { $this.autocompleteElement.val(""); });
        }
        this.selectedLabels = [];
        this.callbacks = [];
        this.url = null;
        this.valueField = null;
        this.textField = null;
    }

    MultipleResourceSearchFilter.prototype.SetResourceParams = function (params) {
        if (params) {
            this.url = params.Url;
            this.valueField = params.ValueField;
            this.textField = params.TextField;
        }
    }

    MultipleResourceSearchFilter.prototype.DisplayAllOptionsOnFocus = function () {
        var $this = this;
        if (!this.usingIE) {
            this.autocompleteElement.on("focus", function () {
                $this.autocompleteElement.autocomplete("search", " ");
            })
        }
    }

    MultipleResourceSearchFilter.prototype.Refresh = function (params) {
        if (this.url) {
            UNGM.Throbber.Push();
            var $this = this;
            this.ClearAllFilters();
            $.ajax({
                url: this.url,
                type: "POST",
                data: JSON.stringify(params),
                contentType: "application/json"
            }).success(function (response) {
                if (typeof (response) !== "object") { response = JSON.parse(response); }
                $this.UpdateOptions(response);
                $this.UpdateAutocompleteElementSource(response);
            }).always(function () { UNGM.Throbber.Pop(); });
        }
    }

    MultipleResourceSearchFilter.prototype.ClearAllFilters = function () {
        this.selectedLabels = [];
        this.RedrawSelectedLabels();
    }

    MultipleResourceSearchFilter.prototype.UpdateOptions = function (response) {
        var $this = this;
        this.element.empty();
        this.element.append($("<option></option>"));
        $.each(response, function (index, item) {
            var option = $("<option value='" + item[$this.valueField] + "'>" + item[$this.textField] + "</option>");
            $this.element.append(option);
        });
    }

    MultipleResourceSearchFilter.prototype.GetValue = function (label) {
        var option = this.element.find("option").filter(function () { return $(this).html() == label; });
        return option.val();
    }

    MultipleResourceSearchFilter.prototype.UpdateAutocompleteElementSource = function (response) {
        if (!this.usingIE) {
            var $this = this;
            var source = $.map(response, function (item) { return item[$this.textField]; });
            this.autocompleteElement.autocomplete("option", "source", source);
        }
    }

    MultipleResourceSearchFilter.prototype.Select = function (label) {
        if ($.trim(label).length !== 0 && $.inArray(label, this.selectedLabels) === -1) {
            this.selectedLabels.push(label);
            this.selectedLabels.sort();
            this.selectedLabels.reverse();
            this.RedrawSelectedLabels();
            this.NotifyCallbackOnChange();
        }
    }

    MultipleResourceSearchFilter.prototype.Remove = function (label) {
        var index = $.inArray(label, this.selectedLabels);
        this.selectedLabels.splice(index, 1);
        this.RedrawSelectedLabels();
        this.NotifyCallbackOnChange();
    }

    MultipleResourceSearchFilter.prototype.RedrawSelectedLabels = function () {
        var $this = this;
        var name = this.element.attr("name");
        this.element.siblings('.filterDiv').remove();
        var textbox = this.element.siblings('input:text');
        $.each(this.selectedLabels, function (index, label) {
            var value = $this.GetValue(label);
            var element = $("<div class='filterDiv'></div>");
            var valueElement = $("<input type='hidden' class='search-filter multiple' name='" + name + "' value='" + value + "' />");
            var labelElement = $("<span>" + label + "</span>");
            var removeElement = $("<a href='javascript:void(0)' class='lnkRemoveUnspscFilter'>(remove)</a>");
            element.append(valueElement);
            element.append(labelElement);
            element.append(removeElement);
            removeElement.click(function () { $this.Remove(label); });
            if ($this.usingIE) {
                element.insertAfter($this.element);
            } else {
                element.insertAfter(textbox);
            }
        });
    }

    MultipleResourceSearchFilter.prototype.AttachCallbackOnChange = function (callback) {
        this.callbacks.push(callback);
    }

    MultipleResourceSearchFilter.prototype.NotifyCallbackOnChange = function () {
        $.each(this.callbacks, function (index, callback) { callback(); });
    }

    ////////////////////////////////////////////////////////////////////////////////
    //
    // UNSPSCSearchFilter
    //

    function UNSPSCSearchFilter(element, params) {
        var $this = this;
        this.callbacks = [];
        this.SelectedUNSPSCs = [];
        this.LinkSelector = element;
        this.UNSPSCSelector = null;
        this.UNSPSCHolder = params.UNSPSCSelectorHolder;
        this.Url = params.Url;
        this.LinkSelector.click(function () { $this.ShowUNSPSC() });
    }

    UNSPSCSearchFilter.prototype.ShowUNSPSC = function () {
        var $this = this;
        UNGM.UNSPSC.saveCallback = function () { $this.UNSPSCSaveCallback(); };

        //refresh the picker selection
        var picker = this.UNSPSCSelector.find(".unspsc");
        UNGM.UNSPSC.bindSelectedCodes(picker);

        var winHeight = $(window).height();

        this.UNSPSCSelector.dialog({
            modal: true,
            title: 'UNSPSC',
            width: $(window).width() < 480 ? '100%' : '66%',
            height: $(window).width() < 480 ? $(window).height() : winHeight - (winHeight * 0.2),
            hide: { effect: 'fade', duration: 100 },
            show: { effect: 'fade', duration: 100 }
        });
    }

    UNSPSCSearchFilter.prototype.UNSPSCSaveCallback = function () {
        //Add selected UNSPSC to place holder
        var $this = this;
        this.LinkSelector.siblings('.filterDiv').remove();
        var unspscId = null;
        var Code = null;
        var UNSPSCName = null;
        this.SelectedUNSPSCs = [];

        for (var i = 0; i < UNGM.UNSPSC.selectedCodes.length; i++) {
            unspscId = parseInt(UNGM.UNSPSC.selectedCodes[i].Id, 10);
            Code = UNGM.UNSPSC.selectedCodes[i].Code;
            UNSPSCName = UNGM.UNSPSC.selectedCodes[i].Name;
            this.SelectedUNSPSCs.push(unspscId);

            // redraw elements
            var element = $("<div class='filterDiv'></div>");
            var valueElement = $("<input type='hidden' class='search-filter multiple' name='ASRUNSPSCs' value='" + unspscId + "' />");
            var labelElement = $("<span>" + Code + " - " + UNSPSCName + "</span>");
            var removeElement = $("<a href='javascript:void(0)' class='lnkRemoveUnspscFilter'>(remove)</a>");
            element.append(valueElement);
            element.append(labelElement);
            element.append(removeElement);
            removeElement.click(function (event) { $this.RemoveUNSPSC(event) });
            element.insertAfter(this.LinkSelector);
        }

        if (this.UNSPSCSelector.is(':data(dialog)') || this.UNSPSCSelector.hasClass('ui-dialog-content')) {
            this.UNSPSCSelector.dialog("close");
        }
        this.NotifyCallbackOnChange();
    }

    UNSPSCSearchFilter.prototype.RemoveUNSPSC = function (event) {
        var removeElement = $(event.currentTarget);
        var removedArray = this.SelectedUNSPSCs;

        var removedId = removeElement.siblings("input").val();
        this.SelectedUNSPSCs = $.grep(removedArray, function (value) { return value != removedId; });

        UNGM.UNSPSC.removeCode(removedId);

        // Remove selected UNSPSCs from place holder
        removeElement.siblings().remove();
        removeElement.remove();
        this.NotifyCallbackOnChange();
    }

    UNSPSCSearchFilter.prototype.RefreshUNSPSCSearchFilter = function (params) {
        UNGM.Throbber.Push();
        var $this = this;

        this.ClearAllFilters();

        $.ajax({
            url: $this.Url,
            type: "POST",
            data: JSON.stringify(params),
            contentType: "application/json"
        }).success(function (response) {
            $this.UNSPSCHolder.html(response);
            $this.UNSPSCSelector = $this.UNSPSCHolder.children("div");
        }).always(function () { UNGM.Throbber.Pop(); });
    }

    UNSPSCSearchFilter.prototype.ClearAllFilters = function () {
        this.LinkSelector.siblings('.filterDiv').remove();
        $.each(this.SelectedUNSPSCs, function (index, value) {
            UNGM.UNSPSC.removeCode(value);
        });
        this.SelectedUNSPSCs = [];
    }

    UNSPSCSearchFilter.prototype.AttachCallbackOnChange = function (callback) {
        this.callbacks.push(callback);
    }

    UNSPSCSearchFilter.prototype.NotifyCallbackOnChange = function () {
        $.each(this.callbacks, function (index, callback) { callback(); });
    }

    ////////////////////////////////////////////////////////////////////////////////
    //
    // OpenCountryPdf, OpenAgencyPdf 
    //

    namespace.OpenCountryPdf = function (country) {
        var year = UNGM.OnlineASR.ProcurementReports.YearSelector.GetInstance().GetYear();
        this.OpenCountryPdfByYear(country, year);
    }

    namespace.OpenCountryPdfByYear = function (country, year) {
        var pdfUrl = this.GetCountryPdfUrl(country, year);
        PdfDialog.GetInstance().Open(country, pdfUrl);
    }

    namespace.GetCountryPdfUrl = function (country, year) {
        return this.GetCountryPdfPath(year) + encodeURIComponent(this.GetCountryPdfName(country, year)).replace(/'/g, escape);
    }

    namespace.GetCountryPdfPath = function (year) {
        return UNGM.siteRoot + "Areas/Public/Downloads/ASR/" + year + "/Country/";
    }

    namespace.GetCountryPdfName = function (country, year) {
        return (country + year + ".pdf").replace(/ /g, "");
    }

    namespace.OpenAgencyPdf = function (agency) {
        var year = UNGM.OnlineASR.ProcurementReports.YearSelector.GetInstance().GetYear();        
        this.OpenAgencyPdfByYear(agency, year);
    }

    namespace.OpenAgencyPdfByYear = function (agency, year) {
        var pdfUrl = this.GetAgencyPdfUrl(agency, year);
        PdfDialog.GetInstance().Open(agency, pdfUrl);
    }

    namespace.GetAgencyPdfUrl = function (agency, year) {
        return this.GetAgencyPdfPath(year) + encodeURIComponent(this.GetAgencyPdfName(agency, year)).replace(/'/g, escape);
    }

    namespace.GetAgencyPdfPath = function (year) {
        return UNGM.siteRoot + "Areas/Public/Downloads/ASR/" + year + "/Agency/";
    }

    namespace.GetAgencyPdfName = function (agency, year) {
        return (agency + "_" + year + ".pdf");
    }

    namespace.OpenAgencyPdf = function (agency) {
        var year = UNGM.OnlineASR.ProcurementReports.YearSelector.GetInstance().GetYear();        
        var filePath = UNGM.siteRoot + "Areas/Public/Downloads/ASR/" + year + "/Agency/";
        var fileName = (agency + "_" + year + ".pdf").replace(/ /g, "");
        PdfDialog.GetInstance().Open(agency, filePath + encodeURIComponent(fileName).replace(/'/g, escape));
    }

    ////////////////////////////////////////////////////////////////////////////////
    //
    // PdfDialog
    //

    function PdfDialog() {
        this.element = $("<div></div>");
    }

    PdfDialog.GetInstance = function () {
        return this.instance = this.instance || new PdfDialog();
    }

    PdfDialog.prototype.Open = function (title, pdfUrl) {
        UNGM.Throbber.Push();
        this.element.empty();
        var $this = this;
        $.ajax({
            url: pdfUrl,
            type: 'HEAD',   // Gets only size of the header, not the actual file
            global: false,  // Disable global events, as UNGM.ajaxErrorHandler 
            error: function () {
                dialogContent = "<div class='info'>The document is not yet avaiable.</div>";
            },
            success: function () {
                var pdfViewer = UNGM.siteRoot + "Scripts/PDF/web/viewer.html?file=";
                dialogContent = "<iframe src='" + pdfViewer + pdfUrl + "' style='width: " + 0.6 * $(window).width() + "px; height: " + 0.7 * $(window).height() + "px;'></iframe>";
            },
            complete: function () {
                $this.element.html(dialogContent);
                $this.element.dialog({
                    modal: true,
                    title: title,
                    width: "auto",
                    height: "auto",
                    hide: { effect: 'fade', duration: 100 },
                    show: { effect: 'fade', duration: 100 }
                });
                UNGM.Throbber.Pop();
            }
        });
    }

})(window.UNGM.OnlineASR.ProcurementReports = window.UNGM.OnlineASR.ProcurementReports || {});
/*! gridster.js - v0.5.6 - 2014-09-25
* http://gridster.net/
* Copyright (c) 2014 ducksboard; Licensed MIT */

;(function(root, factory) {

    if (typeof define === 'function' && define.amd) {
        define('gridster-coords', ['jquery'], factory);
    } else {
       root.GridsterCoords = factory(root.$ || root.jQuery);
    }

}(this, function($) {
    /**
    * Creates objects with coordinates (x1, y1, x2, y2, cx, cy, width, height)
    * to simulate DOM elements on the screen.
    * Coords is used by Gridster to create a faux grid with any DOM element can
    * collide.
    *
    * @class Coords
    * @param {HTMLElement|Object} obj The jQuery HTMLElement or a object with: left,
    * top, width and height properties.
    * @return {Object} Coords instance.
    * @constructor
    */
    function Coords(obj) {
        if (obj[0] && $.isPlainObject(obj[0])) {
            this.data = obj[0];
        }else {
            this.el = obj;
        }

        this.isCoords = true;
        this.coords = {};
        this.init();
        return this;
    }


    var fn = Coords.prototype;


    fn.init = function(){
        this.set();
        this.original_coords = this.get();
    };


    fn.set = function(update, not_update_offsets) {
        var el = this.el;

        if (el && !update) {
            this.data = el.offset();
            this.data.width = el.width();
            this.data.height = el.height();
        }

        if (el && update && !not_update_offsets) {
            var offset = el.offset();
            this.data.top = offset.top;
            this.data.left = offset.left;
        }

        var d = this.data;

        typeof d.left === 'undefined' && (d.left = d.x1);
        typeof d.top === 'undefined' && (d.top = d.y1);

        this.coords.x1 = d.left;
        this.coords.y1 = d.top;
        this.coords.x2 = d.left + d.width;
        this.coords.y2 = d.top + d.height;
        this.coords.cx = d.left + (d.width / 2);
        this.coords.cy = d.top + (d.height / 2);
        this.coords.width  = d.width;
        this.coords.height = d.height;
        this.coords.el  = el || false ;

        return this;
    };


    fn.update = function(data){
        if (!data && !this.el) {
            return this;
        }

        if (data) {
            var new_data = $.extend({}, this.data, data);
            this.data = new_data;
            return this.set(true, true);
        }

        this.set(true);
        return this;
    };


    fn.get = function(){
        return this.coords;
    };

    fn.destroy = function() {
        this.el.removeData('coords');
        delete this.el;
    };

    //jQuery adapter
    $.fn.coords = function() {
        if (this.data('coords') ) {
            return this.data('coords');
        }

        var ins = new Coords(this, arguments[0]);
        this.data('coords', ins);
        return ins;
    };

    return Coords;

}));

;(function(root, factory) {

    if (typeof define === 'function' && define.amd) {
        define('gridster-collision', ['jquery', 'gridster-coords'], factory);
    } else {
        root.GridsterCollision = factory(root.$ || root.jQuery,
            root.GridsterCoords);
    }

}(this, function($, Coords) {

    var defaults = {
        colliders_context: document.body,
        overlapping_region: 'C'
        // ,on_overlap: function(collider_data){},
        // on_overlap_start : function(collider_data){},
        // on_overlap_stop : function(collider_data){}
    };


    /**
    * Detects collisions between a DOM element against other DOM elements or
    * Coords objects.
    *
    * @class Collision
    * @uses Coords
    * @param {HTMLElement} el The jQuery wrapped HTMLElement.
    * @param {HTMLElement|Array} colliders Can be a jQuery collection
    *  of HTMLElements or an Array of Coords instances.
    * @param {Object} [options] An Object with all options you want to
    *        overwrite:
    *   @param {String} [options.overlapping_region] Determines when collision
    *    is valid, depending on the overlapped area. Values can be: 'N', 'S',
    *    'W', 'E', 'C' or 'all'. Default is 'C'.
    *   @param {Function} [options.on_overlap_start] Executes a function the first
    *    time each `collider ` is overlapped.
    *   @param {Function} [options.on_overlap_stop] Executes a function when a
    *    `collider` is no longer collided.
    *   @param {Function} [options.on_overlap] Executes a function when the
    * mouse is moved during the collision.
    * @return {Object} Collision instance.
    * @constructor
    */
    function Collision(el, colliders, options) {
        this.options = $.extend(defaults, options);
        this.$element = el;
        this.last_colliders = [];
        this.last_colliders_coords = [];
        this.set_colliders(colliders);

        this.init();
    }

    Collision.defaults = defaults;

    var fn = Collision.prototype;


    fn.init = function() {
        this.find_collisions();
    };


    fn.overlaps = function(a, b) {
        var x = false;
        var y = false;

        if ((b.x1 >= a.x1 && b.x1 <= a.x2) ||
            (b.x2 >= a.x1 && b.x2 <= a.x2) ||
            (a.x1 >= b.x1 && a.x2 <= b.x2)
        ) { x = true; }

        if ((b.y1 >= a.y1 && b.y1 <= a.y2) ||
            (b.y2 >= a.y1 && b.y2 <= a.y2) ||
            (a.y1 >= b.y1 && a.y2 <= b.y2)
        ) { y = true; }

        return (x && y);
    };


    fn.detect_overlapping_region = function(a, b){
        var regionX = '';
        var regionY = '';

        if (a.y1 > b.cy && a.y1 < b.y2) { regionX = 'N'; }
        if (a.y2 > b.y1 && a.y2 < b.cy) { regionX = 'S'; }
        if (a.x1 > b.cx && a.x1 < b.x2) { regionY = 'W'; }
        if (a.x2 > b.x1 && a.x2 < b.cx) { regionY = 'E'; }

        return (regionX + regionY) || 'C';
    };


    fn.calculate_overlapped_area_coords = function(a, b){
        var x1 = Math.max(a.x1, b.x1);
        var y1 = Math.max(a.y1, b.y1);
        var x2 = Math.min(a.x2, b.x2);
        var y2 = Math.min(a.y2, b.y2);

        return $({
            left: x1,
            top: y1,
             width : (x2 - x1),
            height: (y2 - y1)
          }).coords().get();
    };


    fn.calculate_overlapped_area = function(coords){
        return (coords.width * coords.height);
    };


    fn.manage_colliders_start_stop = function(new_colliders_coords, start_callback, stop_callback){
        var last = this.last_colliders_coords;

        for (var i = 0, il = last.length; i < il; i++) {
            if ($.inArray(last[i], new_colliders_coords) === -1) {
                start_callback.call(this, last[i]);
            }
        }

        for (var j = 0, jl = new_colliders_coords.length; j < jl; j++) {
            if ($.inArray(new_colliders_coords[j], last) === -1) {
                stop_callback.call(this, new_colliders_coords[j]);
            }

        }
    };


    fn.find_collisions = function(player_data_coords){
        var self = this;
        var overlapping_region = this.options.overlapping_region;
        var colliders_coords = [];
        var colliders_data = [];
        var $colliders = (this.colliders || this.$colliders);
        var count = $colliders.length;
        var player_coords = self.$element.coords()
                             .update(player_data_coords || false).get();

        while(count--){
          var $collider = self.$colliders ?
                           $($colliders[count]) : $colliders[count];
          var $collider_coords_ins = ($collider.isCoords) ?
                  $collider : $collider.coords();
          var collider_coords = $collider_coords_ins.get();
          var overlaps = self.overlaps(player_coords, collider_coords);

          if (!overlaps) {
            continue;
          }

          var region = self.detect_overlapping_region(
              player_coords, collider_coords);

            //todo: make this an option
            if (region === overlapping_region || overlapping_region === 'all') {

                var area_coords = self.calculate_overlapped_area_coords(
                    player_coords, collider_coords);
                var area = self.calculate_overlapped_area(area_coords);
                var collider_data = {
                    area: area,
                    area_coords : area_coords,
                    region: region,
                    coords: collider_coords,
                    player_coords: player_coords,
                    el: $collider
                };

                if (self.options.on_overlap) {
                    self.options.on_overlap.call(this, collider_data);
                }
                colliders_coords.push($collider_coords_ins);
                colliders_data.push(collider_data);
            }
        }

        if (self.options.on_overlap_stop || self.options.on_overlap_start) {
            this.manage_colliders_start_stop(colliders_coords,
                self.options.on_overlap_start, self.options.on_overlap_stop);
        }

        this.last_colliders_coords = colliders_coords;

        return colliders_data;
    };


    fn.get_closest_colliders = function(player_data_coords){
        var colliders = this.find_collisions(player_data_coords);

        colliders.sort(function(a, b) {
            /* if colliders are being overlapped by the "C" (center) region,
             * we have to set a lower index in the array to which they are placed
             * above in the grid. */
            if (a.region === 'C' && b.region === 'C') {
                if (a.coords.y1 < b.coords.y1 || a.coords.x1 < b.coords.x1) {
                    return - 1;
                }else{
                    return 1;
                }
            }

            if (a.area < b.area) {
                return 1;
            }

            return 1;
        });
        return colliders;
    };


    fn.set_colliders = function(colliders) {
        if (typeof colliders === 'string' || colliders instanceof $) {
            this.$colliders = $(colliders,
                 this.options.colliders_context).not(this.$element);
        }else{
            this.colliders = $(colliders);
        }
    };


    //jQuery adapter
    $.fn.collision = function(collider, options) {
          return new Collision( this, collider, options );
    };

    return Collision;

}));

;(function(window, undefined) {

    /* Delay, debounce and throttle functions taken from underscore.js
     *
     * Copyright (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and
     * Investigative Reporters & Editors
     *
     * Permission is hereby granted, free of charge, to any person
     * obtaining a copy of this software and associated documentation
     * files (the "Software"), to deal in the Software without
     * restriction, including without limitation the rights to use,
     * copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the
     * Software is furnished to do so, subject to the following
     * conditions:
     *
     * The above copyright notice and this permission notice shall be
     * included in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
     * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
     * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     * OTHER DEALINGS IN THE SOFTWARE.
     */

    window.delay = function(func, wait) {
        var args = Array.prototype.slice.call(arguments, 2);
        return setTimeout(function(){ return func.apply(null, args); }, wait);
    };

    window.debounce = function(func, wait, immediate) {
        var timeout;
        return function() {
          var context = this, args = arguments;
          var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
          };
          if (immediate && !timeout) func.apply(context, args);
          clearTimeout(timeout);
          timeout = setTimeout(later, wait);
        };
    };

    window.throttle = function(func, wait) {
        var context, args, timeout, throttling, more, result;
        var whenDone = debounce(
            function(){ more = throttling = false; }, wait);
        return function() {
          context = this; args = arguments;
          var later = function() {
            timeout = null;
            if (more) func.apply(context, args);
            whenDone();
          };
          if (!timeout) timeout = setTimeout(later, wait);
          if (throttling) {
            more = true;
          } else {
            result = func.apply(context, args);
          }
          whenDone();
          throttling = true;
          return result;
        };
    };

})(window);

;(function(root, factory) {

    if (typeof define === 'function' && define.amd) {
        define('gridster-draggable', ['jquery'], factory);
    } else {
        root.GridsterDraggable = factory(root.$ || root.jQuery);
    }

}(this, function($) {

    var defaults = {
        items: 'li',
        distance: 1,
        limit: true,
        offset_left: 0,
        autoscroll: true,
        ignore_dragging: ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'], // or function
        handle: null,
        container_width: 0,  // 0 == auto
        move_element: true,
        helper: false,  // or 'clone'
        remove_helper: true
        // drag: function(e) {},
        // start : function(e, ui) {},
        // stop : function(e) {}
    };

    var $window = $(window);
    var dir_map = { x : 'left', y : 'top' };
    var isTouch = !!('ontouchstart' in window);

    var capitalize = function(str) {
        return str.charAt(0).toUpperCase() + str.slice(1);
    };

    var idCounter = 0;
    var uniqId = function() {
        return ++idCounter + '';
    }

    /**
    * Basic drag implementation for DOM elements inside a container.
    * Provide start/stop/drag callbacks.
    *
    * @class Draggable
    * @param {HTMLElement} el The HTMLelement that contains all the widgets
    *  to be dragged.
    * @param {Object} [options] An Object with all options you want to
    *        overwrite:
    *    @param {HTMLElement|String} [options.items] Define who will
    *     be the draggable items. Can be a CSS Selector String or a
    *     collection of HTMLElements.
    *    @param {Number} [options.distance] Distance in pixels after mousedown
    *     the mouse must move before dragging should start.
    *    @param {Boolean} [options.limit] Constrains dragging to the width of
    *     the container
    *    @param {Object|Function} [options.ignore_dragging] Array of node names
    *      that sould not trigger dragging, by default is `['INPUT', 'TEXTAREA',
    *      'SELECT', 'BUTTON']`. If a function is used return true to ignore dragging.
    *    @param {offset_left} [options.offset_left] Offset added to the item
    *     that is being dragged.
    *    @param {Number} [options.drag] Executes a callback when the mouse is
    *     moved during the dragging.
    *    @param {Number} [options.start] Executes a callback when the drag
    *     starts.
    *    @param {Number} [options.stop] Executes a callback when the drag stops.
    * @return {Object} Returns `el`.
    * @constructor
    */
    function Draggable(el, options) {
      this.options = $.extend({}, defaults, options);
      this.$document = $(document);
      this.$container = $(el);
      this.$dragitems = $(this.options.items, this.$container);
      this.is_dragging = false;
      this.player_min_left = 0 + this.options.offset_left;
      this.id = uniqId();
      this.ns = '.gridster-draggable-' + this.id;
      this.init();
    }

    Draggable.defaults = defaults;

    var fn = Draggable.prototype;

    fn.init = function() {
        var pos = this.$container.css('position');
        this.calculate_dimensions();
        this.$container.css('position', pos === 'static' ? 'relative' : pos);
        this.disabled = false;
        this.events();

        $(window).bind(this.nsEvent('resize'),
            throttle($.proxy(this.calculate_dimensions, this), 200));
    };

    fn.nsEvent = function(ev) {
        return (ev || '') + this.ns;
    };

    fn.events = function() {
        this.pointer_events = {
            start: this.nsEvent('touchstart') + ' ' + this.nsEvent('mousedown'),
            move: this.nsEvent('touchmove') + ' ' + this.nsEvent('mousemove'),
            end: this.nsEvent('touchend') + ' ' + this.nsEvent('mouseup'),
        };

        this.$container.on(this.nsEvent('selectstart'),
            $.proxy(this.on_select_start, this));

        this.$container.on(this.pointer_events.start, this.options.items,
            $.proxy(this.drag_handler, this));

        this.$document.on(this.pointer_events.end, $.proxy(function(e) {
            this.is_dragging = false;
            if (this.disabled) { return; }
            this.$document.off(this.pointer_events.move);
            if (this.drag_start) {
                this.on_dragstop(e);
            }
        }, this));
    };

    fn.get_actual_pos = function($el) {
        var pos = $el.position();
        return pos;
    };


    fn.get_mouse_pos = function(e) {
        if (e.originalEvent && e.originalEvent.touches) {
            var oe = e.originalEvent;
            e = oe.touches.length ? oe.touches[0] : oe.changedTouches[0];
        }

        return {
            left: e.clientX,
            top: e.clientY
        };
    };


    fn.get_offset = function(e) {
        e.preventDefault();
        var mouse_actual_pos = this.get_mouse_pos(e);
        var diff_x = Math.round(
            mouse_actual_pos.left - this.mouse_init_pos.left);
        var diff_y = Math.round(mouse_actual_pos.top - this.mouse_init_pos.top);

        var left = Math.round(this.el_init_offset.left +
            diff_x - this.baseX + $(window).scrollLeft() - this.win_offset_x);
        var top = Math.round(this.el_init_offset.top +
            diff_y - this.baseY + $(window).scrollTop() - this.win_offset_y);

        if (this.options.limit) {
            if (left > this.player_max_left) {
                left = this.player_max_left;
            } else if(left < this.player_min_left) {
                left = this.player_min_left;
            }
        }

        return {
            position: {
                left: left,
                top: top
            },
            pointer: {
                left: mouse_actual_pos.left,
                top: mouse_actual_pos.top,
                diff_left: diff_x + ($(window).scrollLeft() - this.win_offset_x),
                diff_top: diff_y + ($(window).scrollTop() - this.win_offset_y)
            }
        };
    };


    fn.get_drag_data = function(e) {
        var offset = this.get_offset(e);
        offset.$player = this.$player;
        offset.$helper = this.helper ? this.$helper : this.$player;

        return offset;
    };


    fn.set_limits = function(container_width) {
        container_width || (container_width = this.$container.width());
        this.player_max_left = (container_width - this.player_width +
            - this.options.offset_left);

        this.options.container_width = container_width;

        return this;
    };


    fn.scroll_in = function(axis, data) {
        var dir_prop = dir_map[axis];

        var area_size = 50;
        var scroll_inc = 30;

        var is_x = axis === 'x';
        var window_size = is_x ? this.window_width : this.window_height;
        var doc_size = is_x ? $(document).width() : $(document).height();
        var player_size = is_x ? this.$player.width() : this.$player.height();

        var next_scroll;
        var scroll_offset = $window['scroll' + capitalize(dir_prop)]();
        var min_window_pos = scroll_offset;
        var max_window_pos = min_window_pos + window_size;

        var mouse_next_zone = max_window_pos - area_size;  // down/right
        var mouse_prev_zone = min_window_pos + area_size;  // up/left

        var abs_mouse_pos = min_window_pos + data.pointer[dir_prop];

        var max_player_pos = (doc_size - window_size + player_size);

        if (abs_mouse_pos >= mouse_next_zone) {
            next_scroll = scroll_offset + scroll_inc;
            if (next_scroll < max_player_pos) {
                $window['scroll' + capitalize(dir_prop)](next_scroll);
                this['scroll_offset_' + axis] += scroll_inc;
            }
        }

        if (abs_mouse_pos <= mouse_prev_zone) {
            next_scroll = scroll_offset - scroll_inc;
            if (next_scroll > 0) {
                $window['scroll' + capitalize(dir_prop)](next_scroll);
                this['scroll_offset_' + axis] -= scroll_inc;
            }
        }

        return this;
    };


    fn.manage_scroll = function(data) {
        this.scroll_in('x', data);
        this.scroll_in('y', data);
    };


    fn.calculate_dimensions = function(e) {
        this.window_height = $window.height();
        this.window_width = $window.width();
    };


    fn.drag_handler = function(e) {
        var node = e.target.nodeName;
        // skip if drag is disabled, or click was not done with the mouse primary button
        if (this.disabled || e.which !== 1 && !isTouch) {
            return;
        }

        if (this.ignore_drag(e)) {
            return;
        }

        var self = this;
        var first = true;
        this.$player = $(e.currentTarget);

        this.el_init_pos = this.get_actual_pos(this.$player);
        this.mouse_init_pos = this.get_mouse_pos(e);
        this.offsetY = this.mouse_init_pos.top - this.el_init_pos.top;

        this.$document.on(this.pointer_events.move, function(mme) {
            var mouse_actual_pos = self.get_mouse_pos(mme);
            var diff_x = Math.abs(
                mouse_actual_pos.left - self.mouse_init_pos.left);
            var diff_y = Math.abs(
                mouse_actual_pos.top - self.mouse_init_pos.top);
            if (!(diff_x > self.options.distance ||
                diff_y > self.options.distance)
                ) {
                return false;
            }

            if (first) {
                first = false;
                self.on_dragstart.call(self, mme);
                return false;
            }

            if (self.is_dragging === true) {
                self.on_dragmove.call(self, mme);
            }

            return false;
        });

        if (!isTouch) { return false; }
    };


    fn.on_dragstart = function(e) {
        e.preventDefault();

        if (this.is_dragging) { return this; }

        this.drag_start = this.is_dragging = true;
        var offset = this.$container.offset();
        this.baseX = Math.round(offset.left);
        this.baseY = Math.round(offset.top);
        this.initial_container_width = this.options.container_width || this.$container.width();

        if (this.options.helper === 'clone') {
            this.$helper = this.$player.clone()
                .appendTo(this.$container).addClass('helper');
            this.helper = true;
        } else {
            this.helper = false;
        }

        this.win_offset_y = $(window).scrollTop();
        this.win_offset_x = $(window).scrollLeft();
        this.scroll_offset_y = 0;
        this.scroll_offset_x = 0;
        this.el_init_offset = this.$player.offset();
        this.player_width = this.$player.width();
        this.player_height = this.$player.height();

        this.set_limits(this.options.container_width);

        if (this.options.start) {
            this.options.start.call(this.$player, e, this.get_drag_data(e));
        }
        return false;
    };


    fn.on_dragmove = function(e) {
        var data = this.get_drag_data(e);

        this.options.autoscroll && this.manage_scroll(data);

        if (this.options.move_element) {
            (this.helper ? this.$helper : this.$player).css({
                'position': 'absolute',
                'left' : data.position.left,
                'top' : data.position.top
            });
        }

        var last_position = this.last_position || data.position;
        data.prev_position = last_position;

        if (this.options.drag) {
            this.options.drag.call(this.$player, e, data);
        }

        this.last_position = data.position;
        return false;
    };


    fn.on_dragstop = function(e) {
        var data = this.get_drag_data(e);
        this.drag_start = false;

        if (this.options.stop) {
            this.options.stop.call(this.$player, e, data);
        }

        if (this.helper && this.options.remove_helper) {
            this.$helper.remove();
        }

        return false;
    };

    fn.on_select_start = function(e) {
        if (this.disabled) { return; }

        if (this.ignore_drag(e)) {
            return;
        }

        return false;
    };

    fn.enable = function() {
        this.disabled = false;
    };

    fn.disable = function() {
        this.disabled = true;
    };

    fn.destroy = function() {
        this.disable();

        this.$container.off(this.ns);
        this.$document.off(this.ns);
        $(window).off(this.ns);

        $.removeData(this.$container, 'drag');
    };

    fn.ignore_drag = function(event) {
        if (this.options.handle) {
            return !$(event.target).is(this.options.handle);
        }

        if ($.isFunction(this.options.ignore_dragging)) {
            return this.options.ignore_dragging(event);
        }

        return $(event.target).is(this.options.ignore_dragging.join(', '));
    };

    //jQuery adapter
    $.fn.drag = function ( options ) {
        return new Draggable(this, options);
    };

    return Draggable;

}));

;(function(root, factory) {

    if (typeof define === 'function' && define.amd) {
        define(['jquery', 'gridster-draggable', 'gridster-collision'], factory);
    } else {
        root.Gridster = factory(root.$ || root.jQuery, root.GridsterDraggable,
            root.GridsterCollision);
    }

 }(this, function($, Draggable, Collision) {

    var defaults = {
        namespace: '',
        widget_selector: 'li',
        widget_margins: [10, 10],
        widget_base_dimensions: [400, 225],
        extra_rows: 0,
        extra_cols: 0,
        min_cols: 1,
        max_cols: Infinity,
        min_rows: 15,
        max_size_x: false,
        autogrow_cols: false,
        autogenerate_stylesheet: true,
        avoid_overlapped_widgets: true,
        auto_init: true,
        serialize_params: function($w, wgd) {
            return {
                col: wgd.col,
                row: wgd.row,
                size_x: wgd.size_x,
                size_y: wgd.size_y
            };
        },
        collision: {},
        draggable: {
            items: '.gs-w',
            distance: 4,
            ignore_dragging: Draggable.defaults.ignore_dragging.slice(0)
        },
        resize: {
            enabled: false,
            axes: ['both'],
            handle_append_to: '',
            handle_class: 'gs-resize-handle',
            max_size: [Infinity, Infinity],
            min_size: [1, 1]
        }
    };

    /**
    * @class Gridster
    * @uses Draggable
    * @uses Collision
    * @param {HTMLElement} el The HTMLelement that contains all the widgets.
    * @param {Object} [options] An Object with all options you want to
    *        overwrite:
    *    @param {HTMLElement|String} [options.widget_selector] Define who will
    *     be the draggable widgets. Can be a CSS Selector String or a
    *     collection of HTMLElements
    *    @param {Array} [options.widget_margins] Margin between widgets.
    *     The first index for the horizontal margin (left, right) and
    *     the second for the vertical margin (top, bottom).
    *    @param {Array} [options.widget_base_dimensions] Base widget dimensions
    *     in pixels. The first index for the width and the second for the
    *     height.
    *    @param {Number} [options.extra_cols] Add more columns in addition to
    *     those that have been calculated.
    *    @param {Number} [options.extra_rows] Add more rows in addition to
    *     those that have been calculated.
    *    @param {Number} [options.min_cols] The minimum required columns.
    *    @param {Number} [options.max_cols] The maximum columns possible (set to null
    *     for no maximum).
    *    @param {Number} [options.min_rows] The minimum required rows.
    *    @param {Number} [options.max_size_x] The maximum number of columns
    *     that a widget can span.
    *    @param {Boolean} [options.autogenerate_stylesheet] If true, all the
    *     CSS required to position all widgets in their respective columns
    *     and rows will be generated automatically and injected to the
    *     `<head>` of the document. You can set this to false, and write
    *     your own CSS targeting rows and cols via data-attributes like so:
    *     `[data-col="1"] { left: 10px; }`
    *    @param {Boolean} [options.avoid_overlapped_widgets] Avoid that widgets loaded
    *     from the DOM can be overlapped. It is helpful if the positions were
    *     bad stored in the database or if there was any conflict.
    *    @param {Boolean} [options.auto_init] Automatically call gridster init
    *     method or not when the plugin is instantiated.
    *    @param {Function} [options.serialize_params] Return the data you want
    *     for each widget in the serialization. Two arguments are passed:
    *     `$w`: the jQuery wrapped HTMLElement, and `wgd`: the grid
    *     coords object (`col`, `row`, `size_x`, `size_y`).
    *    @param {Object} [options.collision] An Object with all options for
    *     Collision class you want to overwrite. See Collision docs for
    *     more info.
    *    @param {Object} [options.draggable] An Object with all options for
    *     Draggable class you want to overwrite. See Draggable docs for more
    *     info.
    *       @param {Object|Function} [options.draggable.ignore_dragging] Note that
    *        if you use a Function, and resize is enabled, you should ignore the
    *        resize handlers manually (options.resize.handle_class).
    *    @param {Object} [options.resize] An Object with resize config options.
    *       @param {Boolean} [options.resize.enabled] Set to true to enable
    *        resizing.
    *       @param {Array} [options.resize.axes] Axes in which widgets can be
    *        resized. Possible values: ['x', 'y', 'both'].
    *       @param {String} [options.resize.handle_append_to] Set a valid CSS
    *        selector to append resize handles to.
    *       @param {String} [options.resize.handle_class] CSS class name used
    *        by resize handles.
    *       @param {Array} [options.resize.max_size] Limit widget dimensions
    *        when resizing. Array values should be integers:
    *        `[max_cols_occupied, max_rows_occupied]`
    *       @param {Array} [options.resize.min_size] Limit widget dimensions
    *        when resizing. Array values should be integers:
    *        `[min_cols_occupied, min_rows_occupied]`
    *       @param {Function} [options.resize.start] Function executed
    *        when resizing starts.
    *       @param {Function} [otions.resize.resize] Function executed
    *        during the resizing.
    *       @param {Function} [options.resize.stop] Function executed
    *        when resizing stops.
    *
    * @constructor
    */
    function Gridster(el, options) {
        this.options = $.extend(true, {}, defaults, options);
        this.$el = $(el);
        this.$wrapper = this.$el.parent();
        this.$widgets = this.$el.children(
            this.options.widget_selector).addClass('gs-w');
        this.widgets = [];
        this.$changed = $([]);
        this.wrapper_width = this.$wrapper.width();
        this.min_widget_width = (this.options.widget_margins[0] * 2) +
          this.options.widget_base_dimensions[0];
        this.min_widget_height = (this.options.widget_margins[1] * 2) +
          this.options.widget_base_dimensions[1];

        this.generated_stylesheets = [];
        this.$style_tags = $([]);

        this.options.auto_init && this.init();
    }

    Gridster.defaults = defaults;
    Gridster.generated_stylesheets = [];


    /**
    * Sorts an Array of grid coords objects (representing the grid coords of
    * each widget) in ascending way.
    *
    * @method sort_by_row_asc
    * @param {Array} widgets Array of grid coords objects
    * @return {Array} Returns the array sorted.
    */
    Gridster.sort_by_row_asc = function(widgets) {
        widgets = widgets.sort(function(a, b) {
            if (!a.row) {
                a = $(a).coords().grid;
                b = $(b).coords().grid;
            }

           if (a.row > b.row) {
               return 1;
           }
           return -1;
        });

        return widgets;
    };


    /**
    * Sorts an Array of grid coords objects (representing the grid coords of
    * each widget) placing first the empty cells upper left.
    *
    * @method sort_by_row_and_col_asc
    * @param {Array} widgets Array of grid coords objects
    * @return {Array} Returns the array sorted.
    */
    Gridster.sort_by_row_and_col_asc = function(widgets) {
        widgets = widgets.sort(function(a, b) {
           if (a.row > b.row || a.row === b.row && a.col > b.col) {
               return 1;
           }
           return -1;
        });

        return widgets;
    };


    /**
    * Sorts an Array of grid coords objects by column (representing the grid
    * coords of each widget) in ascending way.
    *
    * @method sort_by_col_asc
    * @param {Array} widgets Array of grid coords objects
    * @return {Array} Returns the array sorted.
    */
    Gridster.sort_by_col_asc = function(widgets) {
        widgets = widgets.sort(function(a, b) {
           if (a.col > b.col) {
               return 1;
           }
           return -1;
        });

        return widgets;
    };


    /**
    * Sorts an Array of grid coords objects (representing the grid coords of
    * each widget) in descending way.
    *
    * @method sort_by_row_desc
    * @param {Array} widgets Array of grid coords objects
    * @return {Array} Returns the array sorted.
    */
    Gridster.sort_by_row_desc = function(widgets) {
        widgets = widgets.sort(function(a, b) {
            if (a.row + a.size_y < b.row + b.size_y) {
                return 1;
            }
           return -1;
        });
        return widgets;
    };



    /** Instance Methods **/

    var fn = Gridster.prototype;

    fn.init = function() {
        this.options.resize.enabled && this.setup_resize();
        this.generate_grid_and_stylesheet();
        this.get_widgets_from_DOM();
        this.set_dom_grid_height();
        this.set_dom_grid_width();
        this.$wrapper.addClass('ready');
        this.draggable();
        this.options.resize.enabled && this.resizable();

        $(window).bind('resize.gridster', throttle(
            $.proxy(this.recalculate_faux_grid, this), 200));
    };


    /**
    * Disables dragging.
    *
    * @method disable
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.disable = function() {
        this.$wrapper.find('.player-revert').removeClass('player-revert');
        this.drag_api.disable();
        return this;
    };


    /**
    * Enables dragging.
    *
    * @method enable
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.enable = function() {
        this.drag_api.enable();
        return this;
    };



    /**
    * Disables drag-and-drop widget resizing.
    *
    * @method disable
    * @return {Class} Returns instance of gridster Class.
    */
    fn.disable_resize = function() {
        this.$el.addClass('gs-resize-disabled');
        this.resize_api.disable();
        return this;
    };


    /**
    * Enables drag-and-drop widget resizing.
    *
    * @method enable
    * @return {Class} Returns instance of gridster Class.
    */
    fn.enable_resize = function() {
        this.$el.removeClass('gs-resize-disabled');
        this.resize_api.enable();
        return this;
    };


    /**
    * Add a new widget to the grid.
    *
    * @method add_widget
    * @param {String|HTMLElement} html The string representing the HTML of the widget
    *  or the HTMLElement.
    * @param {Number} [size_x] The nº of rows the widget occupies horizontally.
    * @param {Number} [size_y] The nº of columns the widget occupies vertically.
    * @param {Number} [col] The column the widget should start in.
    * @param {Number} [row] The row the widget should start in.
    * @param {Array} [max_size] max_size Maximun size (in units) for width and height.
    * @param {Array} [min_size] min_size Minimum size (in units) for width and height.
    * @return {HTMLElement} Returns the jQuery wrapped HTMLElement representing.
    *  the widget that was just created.
    */
    fn.add_widget = function(html, size_x, size_y, col, row, max_size, min_size) {
        var pos;
        size_x || (size_x = 1);
        size_y || (size_y = 1);

        if (!col & !row) {
            pos = this.next_position(size_x, size_y);
        } else {
            pos = {
                col: col,
                row: row,
                size_x: size_x,
                size_y: size_y
            };

            this.empty_cells(col, row, size_x, size_y);
        }

        var $w = $(html).attr({
                'data-col': pos.col,
                'data-row': pos.row,
                'data-sizex' : size_x,
                'data-sizey' : size_y
            }).addClass('gs-w').appendTo(this.$el).hide();

        this.$widgets = this.$widgets.add($w);

        this.register_widget($w);

        this.add_faux_rows(pos.size_y);
        //this.add_faux_cols(pos.size_x);

        if (max_size) {
            this.set_widget_max_size($w, max_size);
        }

        if (min_size) {
            this.set_widget_min_size($w, min_size);
        }

        this.set_dom_grid_width();
        this.set_dom_grid_height();

        this.drag_api.set_limits(this.cols * this.min_widget_width);

        return $w.fadeIn();
    };


    /**
    * Change widget size limits.
    *
    * @method set_widget_min_size
    * @param {HTMLElement|Number} $widget The jQuery wrapped HTMLElement
    *  representing the widget or an index representing the desired widget.
    * @param {Array} min_size Minimum size (in units) for width and height.
    * @return {HTMLElement} Returns instance of gridster Class.
    */
    fn.set_widget_min_size = function($widget, min_size) {
        $widget = typeof $widget === 'number' ?
            this.$widgets.eq($widget) : $widget;

        if (!$widget.length) { return this; }

        var wgd = $widget.data('coords').grid;
        wgd.min_size_x = min_size[0];
        wgd.min_size_y = min_size[1];

        return this;
    };


    /**
    * Change widget size limits.
    *
    * @method set_widget_max_size
    * @param {HTMLElement|Number} $widget The jQuery wrapped HTMLElement
    *  representing the widget or an index representing the desired widget.
    * @param {Array} max_size Maximun size (in units) for width and height.
    * @return {HTMLElement} Returns instance of gridster Class.
    */
    fn.set_widget_max_size = function($widget, max_size) {
        $widget = typeof $widget === 'number' ?
            this.$widgets.eq($widget) : $widget;

        if (!$widget.length) { return this; }

        var wgd = $widget.data('coords').grid;
        wgd.max_size_x = max_size[0];
        wgd.max_size_y = max_size[1];

        return this;
    };


    /**
    * Append the resize handle into a widget.
    *
    * @method add_resize_handle
    * @param {HTMLElement} $widget The jQuery wrapped HTMLElement
    *  representing the widget.
    * @return {HTMLElement} Returns instance of gridster Class.
    */
    fn.add_resize_handle = function($w) {
        var append_to = this.options.resize.handle_append_to;
        $(this.resize_handle_tpl).appendTo( append_to ? $(append_to, $w) : $w);

        return this;
    };


    /**
    * Change the size of a widget. Width is limited to the current grid width.
    *
    * @method resize_widget
    * @param {HTMLElement} $widget The jQuery wrapped HTMLElement
    *  representing the widget.
    * @param {Number} size_x The number of columns that will occupy the widget.
    *  By default <code>size_x</code> is limited to the space available from
    *  the column where the widget begins, until the last column to the right.
    * @param {Number} size_y The number of rows that will occupy the widget.
    * @param {Function} [callback] Function executed when the widget is removed.
    * @return {HTMLElement} Returns $widget.
    */
    fn.resize_widget = function($widget, size_x, size_y, callback) {
        var wgd = $widget.coords().grid;
        var col = wgd.col;
        var max_cols = this.options.max_cols;
        var old_size_y = wgd.size_y;
        var old_col = wgd.col;
        var new_col = old_col;

        size_x || (size_x = wgd.size_x);
        size_y || (size_y = wgd.size_y);

        if (max_cols !== Infinity) {
            size_x = Math.min(size_x, max_cols - col + 1);
        }

        if (size_y > old_size_y) {
            this.add_faux_rows(Math.max(size_y - old_size_y, 0));
        }

        var player_rcol = (col + size_x - 1);
        if (player_rcol > this.cols) {
            this.add_faux_cols(player_rcol - this.cols);
        }

        var new_grid_data = {
            col: new_col,
            row: wgd.row,
            size_x: size_x,
            size_y: size_y
        };

        this.mutate_widget_in_gridmap($widget, wgd, new_grid_data);

        this.set_dom_grid_height();
        this.set_dom_grid_width();

        if (callback) {
            callback.call(this, new_grid_data.size_x, new_grid_data.size_y);
        }

        return $widget;
    };


    /**
    * Mutate widget dimensions and position in the grid map.
    *
    * @method mutate_widget_in_gridmap
    * @param {HTMLElement} $widget The jQuery wrapped HTMLElement
    *  representing the widget to mutate.
    * @param {Object} wgd Current widget grid data (col, row, size_x, size_y).
    * @param {Object} new_wgd New widget grid data.
    * @return {HTMLElement} Returns instance of gridster Class.
    */
    fn.mutate_widget_in_gridmap = function($widget, wgd, new_wgd) {
        var old_size_x = wgd.size_x;
        var old_size_y = wgd.size_y;

        var old_cells_occupied = this.get_cells_occupied(wgd);
        var new_cells_occupied = this.get_cells_occupied(new_wgd);

        var empty_cols = [];
        $.each(old_cells_occupied.cols, function(i, col) {
            if ($.inArray(col, new_cells_occupied.cols) === -1) {
                empty_cols.push(col);
            }
        });

        var occupied_cols = [];
        $.each(new_cells_occupied.cols, function(i, col) {
            if ($.inArray(col, old_cells_occupied.cols) === -1) {
                occupied_cols.push(col);
            }
        });

        var empty_rows = [];
        $.each(old_cells_occupied.rows, function(i, row) {
            if ($.inArray(row, new_cells_occupied.rows) === -1) {
                empty_rows.push(row);
            }
        });

        var occupied_rows = [];
        $.each(new_cells_occupied.rows, function(i, row) {
            if ($.inArray(row, old_cells_occupied.rows) === -1) {
                occupied_rows.push(row);
            }
        });

        this.remove_from_gridmap(wgd);

        if (occupied_cols.length) {
            var cols_to_empty = [
                new_wgd.col, new_wgd.row, new_wgd.size_x, Math.min(old_size_y, new_wgd.size_y), $widget
            ];
            this.empty_cells.apply(this, cols_to_empty);
        }

        if (occupied_rows.length) {
            var rows_to_empty = [new_wgd.col, new_wgd.row, new_wgd.size_x, new_wgd.size_y, $widget];
            this.empty_cells.apply(this, rows_to_empty);
        }

        // not the same that wgd = new_wgd;
        wgd.col = new_wgd.col;
        wgd.row = new_wgd.row;
        wgd.size_x = new_wgd.size_x;
        wgd.size_y = new_wgd.size_y;

        this.add_to_gridmap(new_wgd, $widget);

        $widget.removeClass('player-revert');

        //update coords instance attributes
        $widget.data('coords').update({
            width: (new_wgd.size_x * this.options.widget_base_dimensions[0] +
                ((new_wgd.size_x - 1) * this.options.widget_margins[0]) * 2),
            height: (new_wgd.size_y * this.options.widget_base_dimensions[1] +
                ((new_wgd.size_y - 1) * this.options.widget_margins[1]) * 2)
        });

        $widget.attr({
            'data-col': new_wgd.col,
            'data-row': new_wgd.row,
            'data-sizex': new_wgd.size_x,
            'data-sizey': new_wgd.size_y
        });

        if (empty_cols.length) {
            var cols_to_remove_holes = [
                empty_cols[0], new_wgd.row,
                empty_cols.length,
                Math.min(old_size_y, new_wgd.size_y),
                $widget
            ];

            this.remove_empty_cells.apply(this, cols_to_remove_holes);
        }

        if (empty_rows.length) {
            var rows_to_remove_holes = [
                new_wgd.col, new_wgd.row, new_wgd.size_x, new_wgd.size_y, $widget
            ];
            this.remove_empty_cells.apply(this, rows_to_remove_holes);
        }

        this.move_widget_up($widget);

        return this;
    };


    /**
    * Move down widgets in cells represented by the arguments col, row, size_x,
    * size_y
    *
    * @method empty_cells
    * @param {Number} col The column where the group of cells begin.
    * @param {Number} row The row where the group of cells begin.
    * @param {Number} size_x The number of columns that the group of cells
    * occupy.
    * @param {Number} size_y The number of rows that the group of cells
    * occupy.
    * @param {HTMLElement} $exclude Exclude widgets from being moved.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.empty_cells = function(col, row, size_x, size_y, $exclude) {
        var $nexts = this.widgets_below({
                col: col,
                row: row - size_y,
                size_x: size_x,
                size_y: size_y
            });

        $nexts.not($exclude).each($.proxy(function(i, w) {
            var wgd = $(w).coords().grid;
            if ( !(wgd.row <= (row + size_y - 1))) { return; }
            var diff =  (row + size_y) - wgd.row;
            this.move_widget_down($(w), diff);
        }, this));

        this.set_dom_grid_height();

        return this;
    };


    /**
    * Move up widgets below cells represented by the arguments col, row, size_x,
    * size_y.
    *
    * @method remove_empty_cells
    * @param {Number} col The column where the group of cells begin.
    * @param {Number} row The row where the group of cells begin.
    * @param {Number} size_x The number of columns that the group of cells
    * occupy.
    * @param {Number} size_y The number of rows that the group of cells
    * occupy.
    * @param {HTMLElement} exclude Exclude widgets from being moved.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.remove_empty_cells = function(col, row, size_x, size_y, exclude) {
        var $nexts = this.widgets_below({
            col: col,
            row: row,
            size_x: size_x,
            size_y: size_y
        });

        $nexts.not(exclude).each($.proxy(function(i, widget) {
            this.move_widget_up( $(widget), size_y );
        }, this));

        this.set_dom_grid_height();

        return this;
    };


    /**
    * Get the most left column below to add a new widget.
    *
    * @method next_position
    * @param {Number} size_x The nº of rows the widget occupies horizontally.
    * @param {Number} size_y The nº of columns the widget occupies vertically.
    * @return {Object} Returns a grid coords object representing the future
    *  widget coords.
    */
    fn.next_position = function(size_x, size_y) {
        size_x || (size_x = 1);
        size_y || (size_y = 1);
        var ga = this.gridmap;
        var cols_l = ga.length;
        var valid_pos = [];
        var rows_l;

        for (var c = 1; c < cols_l; c++) {
            rows_l = ga[c].length;
            for (var r = 1; r <= rows_l; r++) {
                var can_move_to = this.can_move_to({
                    size_x: size_x,
                    size_y: size_y
                }, c, r);

                if (can_move_to) {
                    valid_pos.push({
                        col: c,
                        row: r,
                        size_y: size_y,
                        size_x: size_x
                    });
                }
            }
        }

        if (valid_pos.length) {
            return Gridster.sort_by_row_and_col_asc(valid_pos)[0];
        }
        return false;
    };


    /**
    * Remove a widget from the grid.
    *
    * @method remove_widget
    * @param {HTMLElement} el The jQuery wrapped HTMLElement you want to remove.
    * @param {Boolean|Function} silent If true, widgets below the removed one
    * will not move up. If a Function is passed it will be used as callback.
    * @param {Function} callback Function executed when the widget is removed.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.remove_widget = function(el, silent, callback) {
        var $el = el instanceof $ ? el : $(el);
        var wgd = $el.coords().grid;

        // if silent is a function assume it's a callback
        if ($.isFunction(silent)) {
            callback = silent;
            silent = false;
        }

        this.cells_occupied_by_placeholder = {};
        this.$widgets = this.$widgets.not($el);

        var $nexts = this.widgets_below($el);

        this.remove_from_gridmap(wgd);

        $el.fadeOut($.proxy(function() {
            $el.remove();

            if (!silent) {
                $nexts.each($.proxy(function(i, widget) {
                    this.move_widget_up( $(widget), wgd.size_y );
                }, this));
            }

            this.set_dom_grid_height();

            if (callback) {
                callback.call(this, el);
            }
        }, this));

        return this;
    };


    /**
    * Remove all widgets from the grid.
    *
    * @method remove_all_widgets
    * @param {Function} callback Function executed for each widget removed.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.remove_all_widgets = function(callback) {
        this.$widgets.each($.proxy(function(i, el){
              this.remove_widget(el, true, callback);
        }, this));

        return this;
    };


    /**
    * Returns a serialized array of the widgets in the grid.
    *
    * @method serialize
    * @param {HTMLElement} [$widgets] The collection of jQuery wrapped
    *  HTMLElements you want to serialize. If no argument is passed all widgets
    *  will be serialized.
    * @return {Array} Returns an Array of Objects with the data specified in
    *  the serialize_params option.
    */
    fn.serialize = function($widgets) {
        $widgets || ($widgets = this.$widgets);

        return $widgets.map($.proxy(function(i, widget) {
            var $w = $(widget);
            return this.options.serialize_params($w, $w.coords().grid);
        }, this)).get();
    };


    /**
    * Returns a serialized array of the widgets that have changed their
    *  position.
    *
    * @method serialize_changed
    * @return {Array} Returns an Array of Objects with the data specified in
    *  the serialize_params option.
    */
    fn.serialize_changed = function() {
        return this.serialize(this.$changed);
    };


    /**
    * Convert widgets from DOM elements to "widget grid data" Objects.
    *
    * @method dom_to_coords
    * @param {HTMLElement} $widget The widget to be converted.
    */
    fn.dom_to_coords = function($widget) {
        return {
            'col': parseInt($widget.attr('data-col'), 10),
            'row': parseInt($widget.attr('data-row'), 10),
            'size_x': parseInt($widget.attr('data-sizex'), 10) || 1,
            'size_y': parseInt($widget.attr('data-sizey'), 10) || 1,
            'max_size_x': parseInt($widget.attr('data-max-sizex'), 10) || false,
            'max_size_y': parseInt($widget.attr('data-max-sizey'), 10) || false,
            'min_size_x': parseInt($widget.attr('data-min-sizex'), 10) || false,
            'min_size_y': parseInt($widget.attr('data-min-sizey'), 10) || false,
            'el': $widget
        };
    };


    /**
    * Creates the grid coords object representing the widget an add it to the
    * mapped array of positions.
    *
    * @method register_widget
    * @param {HTMLElement|Object} $el jQuery wrapped HTMLElement representing
    *  the widget, or an "widget grid data" Object with (col, row, el ...).
    * @return {Boolean} Returns true if the widget final position is different
    *  than the original.
    */
    fn.register_widget = function($el) {
        var isDOM = $el instanceof jQuery;
        var wgd = isDOM ? this.dom_to_coords($el) : $el;
        var posChanged = false;
        isDOM || ($el = wgd.el);

        var empty_upper_row = this.can_go_widget_up(wgd);
        if (empty_upper_row) {
            wgd.row = empty_upper_row;
            $el.attr('data-row', empty_upper_row);
            this.$el.trigger('gridster:positionchanged', [wgd]);
            posChanged = true;
        }

        if (this.options.avoid_overlapped_widgets &&
            !this.can_move_to(
             {size_x: wgd.size_x, size_y: wgd.size_y}, wgd.col, wgd.row)
        ) {
            $.extend(wgd, this.next_position(wgd.size_x, wgd.size_y));
            $el.attr({
                'data-col': wgd.col,
                'data-row': wgd.row,
                'data-sizex': wgd.size_x,
                'data-sizey': wgd.size_y
            });
            posChanged = true;
        }

        // attach Coord object to player data-coord attribute
        $el.data('coords', $el.coords());
        // Extend Coord object with grid position info
        $el.data('coords').grid = wgd;

        this.add_to_gridmap(wgd, $el);

        this.options.resize.enabled && this.add_resize_handle($el);

        return posChanged;
    };


    /**
    * Update in the mapped array of positions the value of cells represented by
    * the grid coords object passed in the `grid_data` param.
    *
    * @param {Object} grid_data The grid coords object representing the cells
    *  to update in the mapped array.
    * @param {HTMLElement|Boolean} value Pass `false` or the jQuery wrapped
    *  HTMLElement, depends if you want to delete an existing position or add
    *  a new one.
    * @method update_widget_position
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.update_widget_position = function(grid_data, value) {
        this.for_each_cell_occupied(grid_data, function(col, row) {
            if (!this.gridmap[col]) { return this; }
            this.gridmap[col][row] = value;
        });
        return this;
    };


    /**
    * Remove a widget from the mapped array of positions.
    *
    * @method remove_from_gridmap
    * @param {Object} grid_data The grid coords object representing the cells
    *  to update in the mapped array.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.remove_from_gridmap = function(grid_data) {
        return this.update_widget_position(grid_data, false);
    };


    /**
    * Add a widget to the mapped array of positions.
    *
    * @method add_to_gridmap
    * @param {Object} grid_data The grid coords object representing the cells
    *  to update in the mapped array.
    * @param {HTMLElement|Boolean} value The value to set in the specified
    *  position .
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.add_to_gridmap = function(grid_data, value) {
        this.update_widget_position(grid_data, value || grid_data.el);

        if (grid_data.el) {
            var $widgets = this.widgets_below(grid_data.el);
            $widgets.each($.proxy(function(i, widget) {
                this.move_widget_up( $(widget));
            }, this));
        }
    };


    /**
    * Make widgets draggable.
    *
    * @uses Draggable
    * @method draggable
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.draggable = function() {
        var self = this;
        var draggable_options = $.extend(true, {}, this.options.draggable, {
            offset_left: this.options.widget_margins[0],
            offset_top: this.options.widget_margins[1],
            container_width: this.cols * this.min_widget_width,
            limit: true,
            start: function(event, ui) {
                self.$widgets.filter('.player-revert')
                    .removeClass('player-revert');

                self.$player = $(this);
                self.$helper = $(ui.$helper);

                self.helper = !self.$helper.is(self.$player);

                self.on_start_drag.call(self, event, ui);
                self.$el.trigger('gridster:dragstart');
            },
            stop: function(event, ui) {
                self.on_stop_drag.call(self, event, ui);
                self.$el.trigger('gridster:dragstop');
            },
            drag: throttle(function(event, ui) {
                self.on_drag.call(self, event, ui);
                self.$el.trigger('gridster:drag');
            }, 60)
          });

        this.drag_api = this.$el.drag(draggable_options);
        return this;
    };


    /**
    * Bind resize events to get resize working.
    *
    * @method resizable
    * @return {Class} Returns instance of gridster Class.
    */
    fn.resizable = function() {
        this.resize_api = this.$el.drag({
            items: '.' + this.options.resize.handle_class,
            offset_left: this.options.widget_margins[0],
            container_width: this.container_width,
            move_element: false,
            resize: true,
            limit: this.options.autogrow_cols ? false : true,
            start: $.proxy(this.on_start_resize, this),
            stop: $.proxy(function(event, ui) {
                delay($.proxy(function() {
                    this.on_stop_resize(event, ui);
                }, this), 120);
            }, this),
            drag: throttle($.proxy(this.on_resize, this), 60)
        });

        return this;
    };


    /**
    * Setup things required for resizing. Like build templates for drag handles.
    *
    * @method setup_resize
    * @return {Class} Returns instance of gridster Class.
    */
    fn.setup_resize = function() {
        this.resize_handle_class = this.options.resize.handle_class;
        var axes = this.options.resize.axes;
        var handle_tpl = '<span class="' + this.resize_handle_class + ' ' +
            this.resize_handle_class + '-{type}" />';

        this.resize_handle_tpl = $.map(axes, function(type) {
            return handle_tpl.replace('{type}', type);
        }).join('');

        if ($.isArray(this.options.draggable.ignore_dragging)) {
            this.options.draggable.ignore_dragging.push(
                '.' + this.resize_handle_class);
        }

        return this;
    };


    /**
    * This function is executed when the player begins to be dragged.
    *
    * @method on_start_drag
    * @param {Event} event The original browser event
    * @param {Object} ui A prepared ui object with useful drag-related data
    */
    fn.on_start_drag = function(event, ui) {
        this.$helper.add(this.$player).add(this.$wrapper).addClass('dragging');

        this.highest_col = this.get_highest_occupied_cell().col;

        this.$player.addClass('player');
        this.player_grid_data = this.$player.coords().grid;
        this.placeholder_grid_data = $.extend({}, this.player_grid_data);

        this.set_dom_grid_height(this.$el.height() +
            (this.player_grid_data.size_y * this.min_widget_height));

        this.set_dom_grid_width(this.cols);

        var pgd_sizex = this.player_grid_data.size_x;
        var cols_diff = this.cols - this.highest_col;

        if (this.options.autogrow_cols && cols_diff <= pgd_sizex) {
            this.add_faux_cols(Math.min(pgd_sizex - cols_diff, 1));
        }

        var colliders = this.faux_grid;
        var coords = this.$player.data('coords').coords;

        this.cells_occupied_by_player = this.get_cells_occupied(
            this.player_grid_data);
        this.cells_occupied_by_placeholder = this.get_cells_occupied(
            this.placeholder_grid_data);

        this.last_cols = [];
        this.last_rows = [];

        // see jquery.collision.js
        this.collision_api = this.$helper.collision(
            colliders, this.options.collision);

        this.$preview_holder = $('<' + this.$player.get(0).tagName + ' />', {
              'class': 'preview-holder',
              'data-row': this.$player.attr('data-row'),
              'data-col': this.$player.attr('data-col'),
              css: {
                  width: coords.width,
                  height: coords.height
              }
        }).appendTo(this.$el);

        if (this.options.draggable.start) {
          this.options.draggable.start.call(this, event, ui);
        }
    };


    /**
    * This function is executed when the player is being dragged.
    *
    * @method on_drag
    * @param {Event} event The original browser event
    * @param {Object} ui A prepared ui object with useful drag-related data
    */
    fn.on_drag = function(event, ui) {
        //break if dragstop has been fired
        if (this.$player === null) {
            return false;
        }

        var abs_offset = {
            left: ui.position.left + this.baseX,
            top: ui.position.top + this.baseY
        };

        // auto grow cols
        if (this.options.autogrow_cols) {
            var prcol = this.placeholder_grid_data.col +
                this.placeholder_grid_data.size_x - 1;

            // "- 1" due to adding at least 1 column in on_start_drag
            if (prcol >= this.cols - 1 && this.options.max_cols >= this.cols + 1) {
                this.add_faux_cols(1);
                this.set_dom_grid_width(this.cols + 1);
                this.drag_api.set_limits(this.container_width);
            }

            this.collision_api.set_colliders(this.faux_grid);
        }

        this.colliders_data = this.collision_api.get_closest_colliders(
            abs_offset);

        this.on_overlapped_column_change(
            this.on_start_overlapping_column, this.on_stop_overlapping_column);

        this.on_overlapped_row_change(
            this.on_start_overlapping_row, this.on_stop_overlapping_row);


        if (this.helper && this.$player) {
            this.$player.css({
                'left': ui.position.left,
                'top': ui.position.top
            });
        }

        if (this.options.draggable.drag) {
            this.options.draggable.drag.call(this, event, ui);
        }
    };


    /**
    * This function is executed when the player stops being dragged.
    *
    * @method on_stop_drag
    * @param {Event} event The original browser event
    * @param {Object} ui A prepared ui object with useful drag-related data
    */
    fn.on_stop_drag = function(event, ui) {
        this.$helper.add(this.$player).add(this.$wrapper)
            .removeClass('dragging');

        ui.position.left = ui.position.left + this.baseX;
        ui.position.top = ui.position.top + this.baseY;
        this.colliders_data = this.collision_api.get_closest_colliders(
            ui.position);

        this.on_overlapped_column_change(
            this.on_start_overlapping_column,
            this.on_stop_overlapping_column
        );

        this.on_overlapped_row_change(
            this.on_start_overlapping_row,
            this.on_stop_overlapping_row
        );

        this.$player.addClass('player-revert').removeClass('player')
            .attr({
                'data-col': this.placeholder_grid_data.col,
                'data-row': this.placeholder_grid_data.row
            }).css({
                'left': '',
                'top': ''
            });

        this.$changed = this.$changed.add(this.$player);

        this.cells_occupied_by_player = this.get_cells_occupied(
            this.placeholder_grid_data);
        this.set_cells_player_occupies(
            this.placeholder_grid_data.col, this.placeholder_grid_data.row);

        this.$player.coords().grid.row = this.placeholder_grid_data.row;
        this.$player.coords().grid.col = this.placeholder_grid_data.col;

        if (this.options.draggable.stop) {
          this.options.draggable.stop.call(this, event, ui);
        }

        this.$preview_holder.remove();

        this.$player = null;
        this.$helper = null;
        this.placeholder_grid_data = {};
        this.player_grid_data = {};
        this.cells_occupied_by_placeholder = {};
        this.cells_occupied_by_player = {};

        this.set_dom_grid_height();
        this.set_dom_grid_width();

        if (this.options.autogrow_cols) {
            this.drag_api.set_limits(this.cols * this.min_widget_width);
        }
    };


    /**
    * This function is executed every time a widget starts to be resized.
    *
    * @method on_start_resize
    * @param {Event} event The original browser event
    * @param {Object} ui A prepared ui object with useful drag-related data
    */
    fn.on_start_resize = function(event, ui) {
        this.$resized_widget = ui.$player.closest('.gs-w');
        this.resize_coords = this.$resized_widget.coords();
        this.resize_wgd = this.resize_coords.grid;
        this.resize_initial_width = this.resize_coords.coords.width;
        this.resize_initial_height = this.resize_coords.coords.height;
        this.resize_initial_sizex = this.resize_coords.grid.size_x;
        this.resize_initial_sizey = this.resize_coords.grid.size_y;
        this.resize_initial_col = this.resize_coords.grid.col;
        this.resize_last_sizex = this.resize_initial_sizex;
        this.resize_last_sizey = this.resize_initial_sizey;

        this.resize_max_size_x = Math.min(this.resize_wgd.max_size_x ||
            this.options.resize.max_size[0],
            this.options.max_cols - this.resize_initial_col + 1);
        this.resize_max_size_y = this.resize_wgd.max_size_y ||
            this.options.resize.max_size[1];

        this.resize_min_size_x = (this.resize_wgd.min_size_x ||
            this.options.resize.min_size[0] || 1);
        this.resize_min_size_y = (this.resize_wgd.min_size_y ||
            this.options.resize.min_size[1] || 1);

        this.resize_initial_last_col = this.get_highest_occupied_cell().col;

        this.set_dom_grid_width(this.cols);

        this.resize_dir = {
            right: ui.$player.is('.' + this.resize_handle_class + '-x'),
            bottom: ui.$player.is('.' + this.resize_handle_class + '-y')
        };

        this.$resized_widget.css({
            'min-width': this.options.widget_base_dimensions[0],
            'min-height': this.options.widget_base_dimensions[1]
        });

        var nodeName = this.$resized_widget.get(0).tagName;
        this.$resize_preview_holder = $('<' + nodeName + ' />', {
              'class': 'preview-holder resize-preview-holder',
              'data-row': this.$resized_widget.attr('data-row'),
              'data-col': this.$resized_widget.attr('data-col'),
              'css': {
                  'width': this.resize_initial_width,
                  'height': this.resize_initial_height
              }
        }).appendTo(this.$el);

        this.$resized_widget.addClass('resizing');

		if (this.options.resize.start) {
            this.options.resize.start.call(this, event, ui, this.$resized_widget);
        }

        this.$el.trigger('gridster:resizestart');
    };


    /**
    * This function is executed every time a widget stops being resized.
    *
    * @method on_stop_resize
    * @param {Event} event The original browser event
    * @param {Object} ui A prepared ui object with useful drag-related data
    */
    fn.on_stop_resize = function(event, ui) {
        this.$resized_widget
            .removeClass('resizing')
            .css({
                'width': '',
                'height': ''
            });

        delay($.proxy(function() {
            this.$resize_preview_holder
                .remove()
                .css({
                    'min-width': '',
                    'min-height': ''
                });

            if (this.options.resize.stop) {
                this.options.resize.stop.call(this, event, ui, this.$resized_widget);
            }

            this.$el.trigger('gridster:resizestop');
        }, this), 300);

        this.set_dom_grid_width();

        if (this.options.autogrow_cols) {
            this.drag_api.set_limits(this.cols * this.min_widget_width);
        }
    };


    /**
    * This function is executed when a widget is being resized.
    *
    * @method on_resize
    * @param {Event} event The original browser event
    * @param {Object} ui A prepared ui object with useful drag-related data
    */
    fn.on_resize = function(event, ui) {
        var rel_x = (ui.pointer.diff_left);
        var rel_y = (ui.pointer.diff_top);
        var wbd_x = this.options.widget_base_dimensions[0];
        var wbd_y = this.options.widget_base_dimensions[1];
        var margin_x = this.options.widget_margins[0];
        var margin_y = this.options.widget_margins[1];
        var max_size_x = this.resize_max_size_x;
        var min_size_x = this.resize_min_size_x;
        var max_size_y = this.resize_max_size_y;
        var min_size_y = this.resize_min_size_y;
        var autogrow = this.options.autogrow_cols;
        var width;
        var max_width = Infinity;
        var max_height = Infinity;

        var inc_units_x = Math.ceil((rel_x / (wbd_x + margin_x * 2)) - 0.2);
        var inc_units_y = Math.ceil((rel_y / (wbd_y + margin_y * 2)) - 0.2);

        var size_x = Math.max(1, this.resize_initial_sizex + inc_units_x);
        var size_y = Math.max(1, this.resize_initial_sizey + inc_units_y);

        var max_cols = (this.container_width / this.min_widget_width) -
            this.resize_initial_col + 1;
        var limit_width = ((max_cols * this.min_widget_width) - margin_x * 2);

        size_x = Math.max(Math.min(size_x, max_size_x), min_size_x);
        size_x = Math.min(max_cols, size_x);
        width = (max_size_x * wbd_x) + ((size_x - 1) * margin_x * 2);
        max_width = Math.min(width, limit_width);
        min_width = (min_size_x * wbd_x) + ((size_x - 1) * margin_x * 2);

        size_y = Math.max(Math.min(size_y, max_size_y), min_size_y);
        max_height = (max_size_y * wbd_y) + ((size_y - 1) * margin_y * 2);
        min_height = (min_size_y * wbd_y) + ((size_y - 1) * margin_y * 2);

        if (this.resize_dir.right) {
            size_y = this.resize_initial_sizey;
        } else if (this.resize_dir.bottom) {
            size_x = this.resize_initial_sizex;
        }

        if (autogrow) {
            var last_widget_col = this.resize_initial_col + size_x - 1;
            if (autogrow && this.resize_initial_last_col <= last_widget_col) {
                this.set_dom_grid_width(Math.max(last_widget_col + 1, this.cols));

                if (this.cols < last_widget_col) {
                    this.add_faux_cols(last_widget_col - this.cols);
                }
            }
        }

        var css_props = {};
        !this.resize_dir.bottom && (css_props.width = Math.max(Math.min(
            this.resize_initial_width + rel_x, max_width), min_width));
        !this.resize_dir.right && (css_props.height = Math.max(Math.min(
            this.resize_initial_height + rel_y, max_height), min_height));

        this.$resized_widget.css(css_props);

        if (size_x !== this.resize_last_sizex ||
            size_y !== this.resize_last_sizey) {

            this.resize_widget(this.$resized_widget, size_x, size_y);
            this.set_dom_grid_width(this.cols);

            this.$resize_preview_holder.css({
                'width': '',
                'height': ''
            }).attr({
                'data-row': this.$resized_widget.attr('data-row'),
                'data-sizex': size_x,
                'data-sizey': size_y
            });
        }

        if (this.options.resize.resize) {
            this.options.resize.resize.call(this, event, ui, this.$resized_widget);
        }

        this.$el.trigger('gridster:resize');

        this.resize_last_sizex = size_x;
        this.resize_last_sizey = size_y;
    };


    /**
    * Executes the callbacks passed as arguments when a column begins to be
    * overlapped or stops being overlapped.
    *
    * @param {Function} start_callback Function executed when a new column
    *  begins to be overlapped. The column is passed as first argument.
    * @param {Function} stop_callback Function executed when a column stops
    *  being overlapped. The column is passed as first argument.
    * @method on_overlapped_column_change
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.on_overlapped_column_change = function(start_callback, stop_callback) {
        if (!this.colliders_data.length) {
            return this;
        }
        var cols = this.get_targeted_columns(
            this.colliders_data[0].el.data.col);

        var last_n_cols = this.last_cols.length;
        var n_cols = cols.length;
        var i;

        for (i = 0; i < n_cols; i++) {
            if ($.inArray(cols[i], this.last_cols) === -1) {
                (start_callback || $.noop).call(this, cols[i]);
            }
        }

        for (i = 0; i< last_n_cols; i++) {
            if ($.inArray(this.last_cols[i], cols) === -1) {
                (stop_callback || $.noop).call(this, this.last_cols[i]);
            }
        }

        this.last_cols = cols;

        return this;
    };


    /**
    * Executes the callbacks passed as arguments when a row starts to be
    * overlapped or stops being overlapped.
    *
    * @param {Function} start_callback Function executed when a new row begins
    *  to be overlapped. The row is passed as first argument.
    * @param {Function} end_callback Function executed when a row stops being
    *  overlapped. The row is passed as first argument.
    * @method on_overlapped_row_change
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.on_overlapped_row_change = function(start_callback, end_callback) {
        if (!this.colliders_data.length) {
            return this;
        }
        var rows = this.get_targeted_rows(this.colliders_data[0].el.data.row);
        var last_n_rows = this.last_rows.length;
        var n_rows = rows.length;
        var i;

        for (i = 0; i < n_rows; i++) {
            if ($.inArray(rows[i], this.last_rows) === -1) {
                (start_callback || $.noop).call(this, rows[i]);
            }
        }

        for (i = 0; i < last_n_rows; i++) {
            if ($.inArray(this.last_rows[i], rows) === -1) {
                (end_callback || $.noop).call(this, this.last_rows[i]);
            }
        }

        this.last_rows = rows;
    };


    /**
    * Sets the current position of the player
    *
    * @param {Number} col
    * @param {Number} row
    * @param {Boolean} no_player
    * @method set_player
    * @return {object}
    */
    fn.set_player = function(col, row, no_player) {
        var self = this;
        if (!no_player) {
            this.empty_cells_player_occupies();
        }
        var cell = !no_player ? self.colliders_data[0].el.data : {col: col};
        var to_col = cell.col;
        var to_row = row || cell.row;

        this.player_grid_data = {
            col: to_col,
            row: to_row,
            size_y : this.player_grid_data.size_y,
            size_x : this.player_grid_data.size_x
        };

        this.cells_occupied_by_player = this.get_cells_occupied(
            this.player_grid_data);

        var $overlapped_widgets = this.get_widgets_overlapped(
            this.player_grid_data);

        var constraints = this.widgets_constraints($overlapped_widgets);

        this.manage_movements(constraints.can_go_up, to_col, to_row);
        this.manage_movements(constraints.can_not_go_up, to_col, to_row);

        /* if there is not widgets overlapping in the new player position,
         * update the new placeholder position. */
        if (!$overlapped_widgets.length) {
            var pp = this.can_go_player_up(this.player_grid_data);
            if (pp !== false) {
                to_row = pp;
            }
            this.set_placeholder(to_col, to_row);
        }

        return {
            col: to_col,
            row: to_row
        };
    };


    /**
    * See which of the widgets in the $widgets param collection can go to
    * a upper row and which not.
    *
    * @method widgets_contraints
    * @param {jQuery} $widgets A jQuery wrapped collection of
    * HTMLElements.
    * @return {object} Returns a literal Object with two keys: `can_go_up` &
    * `can_not_go_up`. Each contains a set of HTMLElements.
    */
    fn.widgets_constraints = function($widgets) {
        var $widgets_can_go_up = $([]);
        var $widgets_can_not_go_up;
        var wgd_can_go_up = [];
        var wgd_can_not_go_up = [];

        $widgets.each($.proxy(function(i, w) {
            var $w = $(w);
            var wgd = $w.coords().grid;
            if (this.can_go_widget_up(wgd)) {
                $widgets_can_go_up = $widgets_can_go_up.add($w);
                wgd_can_go_up.push(wgd);
            } else {
                wgd_can_not_go_up.push(wgd);
            }
        }, this));

        $widgets_can_not_go_up = $widgets.not($widgets_can_go_up);

        return {
            can_go_up: Gridster.sort_by_row_asc(wgd_can_go_up),
            can_not_go_up: Gridster.sort_by_row_desc(wgd_can_not_go_up)
        };
    };


    /**
    * Sorts an Array of grid coords objects (representing the grid coords of
    * each widget) in descending way.
    *
    * @method manage_movements
    * @param {jQuery} $widgets A jQuery collection of HTMLElements
    *  representing the widgets you want to move.
    * @param {Number} to_col The column to which we want to move the widgets.
    * @param {Number} to_row The row to which we want to move the widgets.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.manage_movements = function($widgets, to_col, to_row) {
        $.each($widgets, $.proxy(function(i, w) {
            var wgd = w;
            var $w = wgd.el;

            var can_go_widget_up = this.can_go_widget_up(wgd);

            if (can_go_widget_up) {
                //target CAN go up
                //so move widget up
                this.move_widget_to($w, can_go_widget_up);
                this.set_placeholder(to_col, can_go_widget_up + wgd.size_y);

            } else {
                //target can't go up
                var can_go_player_up = this.can_go_player_up(
                    this.player_grid_data);

                if (!can_go_player_up) {
                    // target can't go up
                    // player cant't go up
                    // so we need to move widget down to a position that dont
                    // overlaps player
                    var y = (to_row + this.player_grid_data.size_y) - wgd.row;

                    this.move_widget_down($w, y);
                    this.set_placeholder(to_col, to_row);
                }
            }
        }, this));

        return this;
    };

    /**
    * Determines if there is a widget in the row and col given. Or if the
    * HTMLElement passed as first argument is the player.
    *
    * @method is_player
    * @param {Number|HTMLElement} col_or_el A jQuery wrapped collection of
    * HTMLElements.
    * @param {Number} [row] The column to which we want to move the widgets.
    * @return {Boolean} Returns true or false.
    */
    fn.is_player = function(col_or_el, row) {
        if (row && !this.gridmap[col_or_el]) { return false; }
        var $w = row ? this.gridmap[col_or_el][row] : col_or_el;
        return $w && ($w.is(this.$player) || $w.is(this.$helper));
    };


    /**
    * Determines if the widget that is being dragged is currently over the row
    * and col given.
    *
    * @method is_player_in
    * @param {Number} col The column to check.
    * @param {Number} row The row to check.
    * @return {Boolean} Returns true or false.
    */
    fn.is_player_in = function(col, row) {
        var c = this.cells_occupied_by_player || {};
        return $.inArray(col, c.cols) >= 0 && $.inArray(row, c.rows) >= 0;
    };


    /**
    * Determines if the placeholder is currently over the row and col given.
    *
    * @method is_placeholder_in
    * @param {Number} col The column to check.
    * @param {Number} row The row to check.
    * @return {Boolean} Returns true or false.
    */
    fn.is_placeholder_in = function(col, row) {
        var c = this.cells_occupied_by_placeholder || {};
        return this.is_placeholder_in_col(col) && $.inArray(row, c.rows) >= 0;
    };


    /**
    * Determines if the placeholder is currently over the column given.
    *
    * @method is_placeholder_in_col
    * @param {Number} col The column to check.
    * @return {Boolean} Returns true or false.
    */
    fn.is_placeholder_in_col = function(col) {
        var c = this.cells_occupied_by_placeholder || [];
        return $.inArray(col, c.cols) >= 0;
    };


    /**
    * Determines if the cell represented by col and row params is empty.
    *
    * @method is_empty
    * @param {Number} col The column to check.
    * @param {Number} row The row to check.
    * @return {Boolean} Returns true or false.
    */
    fn.is_empty = function(col, row) {
        if (typeof this.gridmap[col] !== 'undefined') {
			if(typeof this.gridmap[col][row] !== 'undefined' &&
				 this.gridmap[col][row] === false
			) {
				return true;
			}
			return false;
		}
		return true;
    };


    /**
    * Determines if the cell represented by col and row params is occupied.
    *
    * @method is_occupied
    * @param {Number} col The column to check.
    * @param {Number} row The row to check.
    * @return {Boolean} Returns true or false.
    */
    fn.is_occupied = function(col, row) {
        if (!this.gridmap[col]) {
            return false;
        }

        if (this.gridmap[col][row]) {
            return true;
        }
        return false;
    };


    /**
    * Determines if there is a widget in the cell represented by col/row params.
    *
    * @method is_widget
    * @param {Number} col The column to check.
    * @param {Number} row The row to check.
    * @return {Boolean|HTMLElement} Returns false if there is no widget,
    * else returns the jQuery HTMLElement
    */
    fn.is_widget = function(col, row) {
        var cell = this.gridmap[col];
        if (!cell) {
            return false;
        }

        cell = cell[row];

        if (cell) {
            return cell;
        }

        return false;
    };


    /**
    * Determines if there is a widget in the cell represented by col/row
    * params and if this is under the widget that is being dragged.
    *
    * @method is_widget_under_player
    * @param {Number} col The column to check.
    * @param {Number} row The row to check.
    * @return {Boolean} Returns true or false.
    */
    fn.is_widget_under_player = function(col, row) {
        if (this.is_widget(col, row)) {
            return this.is_player_in(col, row);
        }
        return false;
    };


    /**
    * Get widgets overlapping with the player or with the object passed
    * representing the grid cells.
    *
    * @method get_widgets_under_player
    * @return {HTMLElement} Returns a jQuery collection of HTMLElements
    */
    fn.get_widgets_under_player = function(cells) {
        cells || (cells = this.cells_occupied_by_player || {cols: [], rows: []});
        var $widgets = $([]);

        $.each(cells.cols, $.proxy(function(i, col) {
            $.each(cells.rows, $.proxy(function(i, row) {
                if(this.is_widget(col, row)) {
                    $widgets = $widgets.add(this.gridmap[col][row]);
                }
            }, this));
        }, this));

        return $widgets;
    };


    /**
    * Put placeholder at the row and column specified.
    *
    * @method set_placeholder
    * @param {Number} col The column to which we want to move the
    *  placeholder.
    * @param {Number} row The row to which we want to move the
    *  placeholder.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.set_placeholder = function(col, row) {
        var phgd = $.extend({}, this.placeholder_grid_data);
        var $nexts = this.widgets_below({
                col: phgd.col,
                row: phgd.row,
                size_y: phgd.size_y,
                size_x: phgd.size_x
            });

        // Prevents widgets go out of the grid
        var right_col = (col + phgd.size_x - 1);
        if (right_col > this.cols) {
            col = col - (right_col - col);
        }

        var moved_down = this.placeholder_grid_data.row < row;
        var changed_column = this.placeholder_grid_data.col !== col;

        this.placeholder_grid_data.col = col;
        this.placeholder_grid_data.row = row;

        this.cells_occupied_by_placeholder = this.get_cells_occupied(
            this.placeholder_grid_data);

        this.$preview_holder.attr({
            'data-row' : row,
            'data-col' : col
        });

        if (moved_down || changed_column) {
            $nexts.each($.proxy(function(i, widget) {
                this.move_widget_up(
                 $(widget), this.placeholder_grid_data.col - col + phgd.size_y);
            }, this));
        }

        var $widgets_under_ph = this.get_widgets_under_player(
            this.cells_occupied_by_placeholder);

        if ($widgets_under_ph.length) {
            $widgets_under_ph.each($.proxy(function(i, widget) {
                var $w = $(widget);
                this.move_widget_down(
                 $w, row + phgd.size_y - $w.data('coords').grid.row);
            }, this));
        }

    };


    /**
    * Determines whether the player can move to a position above.
    *
    * @method can_go_player_up
    * @param {Object} widget_grid_data The actual grid coords object of the
    *  player.
    * @return {Number|Boolean} If the player can be moved to an upper row
    *  returns the row number, else returns false.
    */
    fn.can_go_player_up = function(widget_grid_data) {
        var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1;
        var result = true;
        var upper_rows = [];
        var min_row = 10000;
        var $widgets_under_player = this.get_widgets_under_player();

        /* generate an array with columns as index and array with upper rows
         * empty as value */
        this.for_each_column_occupied(widget_grid_data, function(tcol) {
            var grid_col = this.gridmap[tcol];
            var r = p_bottom_row + 1;
            upper_rows[tcol] = [];

            while (--r > 0) {
                if (this.is_empty(tcol, r) || this.is_player(tcol, r) ||
                    this.is_widget(tcol, r) &&
                    grid_col[r].is($widgets_under_player)
                ) {
                    upper_rows[tcol].push(r);
                    min_row = r < min_row ? r : min_row;
                } else {
                    break;
                }
            }

            if (upper_rows[tcol].length === 0) {
                result = false;
                return true; //break
            }

            upper_rows[tcol].sort(function(a, b) {
                return a - b;
            });
        });

        if (!result) { return false; }

        return this.get_valid_rows(widget_grid_data, upper_rows, min_row);
    };


    /**
    * Determines whether a widget can move to a position above.
    *
    * @method can_go_widget_up
    * @param {Object} widget_grid_data The actual grid coords object of the
    *  widget we want to check.
    * @return {Number|Boolean} If the widget can be moved to an upper row
    *  returns the row number, else returns false.
    */
    fn.can_go_widget_up = function(widget_grid_data) {
        var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1;
        var result = true;
        var upper_rows = [];
        var min_row = 10000;

        /* generate an array with columns as index and array with topmost rows
         * empty as value */
        this.for_each_column_occupied(widget_grid_data, function(tcol) {
            var grid_col = this.gridmap[tcol];
            upper_rows[tcol] = [];

            var r = p_bottom_row + 1;
            // iterate over each row
            while (--r > 0) {
                if (this.is_widget(tcol, r) && !this.is_player_in(tcol, r)) {
                    if (!grid_col[r].is(widget_grid_data.el)) {
                        break;
                    }
                }

                if (!this.is_player(tcol, r) &&
                    !this.is_placeholder_in(tcol, r) &&
                    !this.is_player_in(tcol, r)) {
                    upper_rows[tcol].push(r);
                }

                if (r < min_row) {
                    min_row = r;
                }
            }

            if (upper_rows[tcol].length === 0) {
                result = false;
                return true; //break
            }

            upper_rows[tcol].sort(function(a, b) {
                return a - b;
            });
        });

        if (!result) { return false; }

        return this.get_valid_rows(widget_grid_data, upper_rows, min_row);
    };


    /**
    * Search a valid row for the widget represented by `widget_grid_data' in
    * the `upper_rows` array. Iteration starts from row specified in `min_row`.
    *
    * @method get_valid_rows
    * @param {Object} widget_grid_data The actual grid coords object of the
    *  player.
    * @param {Array} upper_rows An array with columns as index and arrays
    *  of valid rows as values.
    * @param {Number} min_row The upper row from which the iteration will start.
    * @return {Number|Boolean} Returns the upper row valid from the `upper_rows`
    *  for the widget in question.
    */
    fn.get_valid_rows = function(widget_grid_data, upper_rows, min_row) {
        var p_top_row = widget_grid_data.row;
        var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1;
        var size_y = widget_grid_data.size_y;
        var r = min_row - 1;
        var valid_rows = [];

        while (++r <= p_bottom_row ) {
            var common = true;
            $.each(upper_rows, function(col, rows) {
                if ($.isArray(rows) && $.inArray(r, rows) === -1) {
                    common = false;
                }
            });

            if (common === true) {
                valid_rows.push(r);
                if (valid_rows.length === size_y) {
                    break;
                }
            }
        }

        var new_row = false;
        if (size_y === 1) {
            if (valid_rows[0] !== p_top_row) {
                new_row = valid_rows[0] || false;
            }
        } else {
            if (valid_rows[0] !== p_top_row) {
                new_row = this.get_consecutive_numbers_index(
                    valid_rows, size_y);
            }
        }

        return new_row;
    };


    fn.get_consecutive_numbers_index = function(arr, size_y) {
        var max = arr.length;
        var result = [];
        var first = true;
        var prev = -1; // or null?

        for (var i=0; i < max; i++) {
            if (first || arr[i] === prev + 1) {
                result.push(i);
                if (result.length === size_y) {
                    break;
                }
                first = false;
            } else {
                result = [];
                first = true;
            }

            prev = arr[i];
        }

        return result.length >= size_y ? arr[result[0]] : false;
    };


    /**
    * Get widgets overlapping with the player.
    *
    * @method get_widgets_overlapped
    * @return {jQuery} Returns a jQuery collection of HTMLElements.
    */
    fn.get_widgets_overlapped = function() {
        var $w;
        var $widgets = $([]);
        var used = [];
        var rows_from_bottom = this.cells_occupied_by_player.rows.slice(0);
        rows_from_bottom.reverse();

        $.each(this.cells_occupied_by_player.cols, $.proxy(function(i, col) {
            $.each(rows_from_bottom, $.proxy(function(i, row) {
                // if there is a widget in the player position
                if (!this.gridmap[col]) { return true; } //next iteration
                var $w = this.gridmap[col][row];
                if (this.is_occupied(col, row) && !this.is_player($w) &&
                    $.inArray($w, used) === -1
                ) {
                    $widgets = $widgets.add($w);
                    used.push($w);
                }

            }, this));
        }, this));

        return $widgets;
    };


    /**
    * This callback is executed when the player begins to collide with a column.
    *
    * @method on_start_overlapping_column
    * @param {Number} col The collided column.
    * @return {jQuery} Returns a jQuery collection of HTMLElements.
    */
    fn.on_start_overlapping_column = function(col) {
        this.set_player(col, false);
    };


    /**
    * A callback executed when the player begins to collide with a row.
    *
    * @method on_start_overlapping_row
    * @param {Number} row The collided row.
    * @return {jQuery} Returns a jQuery collection of HTMLElements.
    */
    fn.on_start_overlapping_row = function(row) {
        this.set_player(false, row);
    };


    /**
    * A callback executed when the the player ends to collide with a column.
    *
    * @method on_stop_overlapping_column
    * @param {Number} col The collided row.
    * @return {jQuery} Returns a jQuery collection of HTMLElements.
    */
    fn.on_stop_overlapping_column = function(col) {
        this.set_player(col, false);

        var self = this;
        this.for_each_widget_below(col, this.cells_occupied_by_player.rows[0],
            function(tcol, trow) {
                self.move_widget_up(this, self.player_grid_data.size_y);
        });
    };


    /**
    * This callback is executed when the player ends to collide with a row.
    *
    * @method on_stop_overlapping_row
    * @param {Number} row The collided row.
    * @return {jQuery} Returns a jQuery collection of HTMLElements.
    */
    fn.on_stop_overlapping_row = function(row) {
        this.set_player(false, row);

        var self = this;
        var cols = this.cells_occupied_by_player.cols;
        for (var c = 0, cl = cols.length; c < cl; c++) {
            this.for_each_widget_below(cols[c], row, function(tcol, trow) {
                self.move_widget_up(this, self.player_grid_data.size_y);
            });
        }
    };


    /**
    * Move a widget to a specific row. The cell or cells must be empty.
    * If the widget has widgets below, all of these widgets will be moved also
    * if they can.
    *
    * @method move_widget_to
    * @param {HTMLElement} $widget The jQuery wrapped HTMLElement of the
    * widget is going to be moved.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.move_widget_to = function($widget, row) {
        var self = this;
        var widget_grid_data = $widget.coords().grid;
        var diff = row - widget_grid_data.row;
        var $next_widgets = this.widgets_below($widget);

        var can_move_to_new_cell = this.can_move_to(
            widget_grid_data, widget_grid_data.col, row, $widget);

        if (can_move_to_new_cell === false) {
            return false;
        }

        this.remove_from_gridmap(widget_grid_data);
        widget_grid_data.row = row;
        this.add_to_gridmap(widget_grid_data);
        $widget.attr('data-row', row);
        this.$changed = this.$changed.add($widget);


        $next_widgets.each(function(i, widget) {
            var $w = $(widget);
            var wgd = $w.coords().grid;
            var can_go_up = self.can_go_widget_up(wgd);
            if (can_go_up && can_go_up !== wgd.row) {
                self.move_widget_to($w, can_go_up);
            }
        });

        return this;
    };


    /**
    * Move up the specified widget and all below it.
    *
    * @method move_widget_up
    * @param {HTMLElement} $widget The widget you want to move.
    * @param {Number} [y_units] The number of cells that the widget has to move.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.move_widget_up = function($widget, y_units) {
        var el_grid_data = $widget.coords().grid;
        var actual_row = el_grid_data.row;
        var moved = [];
        var can_go_up = true;
        y_units || (y_units = 1);

        if (!this.can_go_up($widget)) { return false; } //break;

        this.for_each_column_occupied(el_grid_data, function(col) {
            // can_go_up
            if ($.inArray($widget, moved) === -1) {
                var widget_grid_data = $widget.coords().grid;
                var next_row = actual_row - y_units;
                next_row = this.can_go_up_to_row(
                    widget_grid_data, col, next_row);

                if (!next_row) {
                    return true;
                }

                var $next_widgets = this.widgets_below($widget);

                this.remove_from_gridmap(widget_grid_data);
                widget_grid_data.row = next_row;
                this.add_to_gridmap(widget_grid_data);
                $widget.attr('data-row', widget_grid_data.row);
                this.$changed = this.$changed.add($widget);

                moved.push($widget);

                $next_widgets.each($.proxy(function(i, widget) {
                    this.move_widget_up($(widget), y_units);
                }, this));
            }
        });

    };


    /**
    * Move down the specified widget and all below it.
    *
    * @method move_widget_down
    * @param {jQuery} $widget The jQuery object representing the widget
    *  you want to move.
    * @param {Number} y_units The number of cells that the widget has to move.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.move_widget_down = function($widget, y_units) {
        var el_grid_data, actual_row, moved, y_diff;

        if (y_units <= 0) { return false; }

        el_grid_data = $widget.coords().grid;
        actual_row = el_grid_data.row;
        moved = [];
        y_diff = y_units;

        if (!$widget) { return false; }

        if ($.inArray($widget, moved) === -1) {

            var widget_grid_data = $widget.coords().grid;
            var next_row = actual_row + y_units;
            var $next_widgets = this.widgets_below($widget);

            this.remove_from_gridmap(widget_grid_data);

            $next_widgets.each($.proxy(function(i, widget) {
                var $w = $(widget);
                var wd = $w.coords().grid;
                var tmp_y = this.displacement_diff(
                             wd, widget_grid_data, y_diff);

                if (tmp_y > 0) {
                    this.move_widget_down($w, tmp_y);
                }
            }, this));

            widget_grid_data.row = next_row;
            this.update_widget_position(widget_grid_data, $widget);
            $widget.attr('data-row', widget_grid_data.row);
            this.$changed = this.$changed.add($widget);

            moved.push($widget);
        }
    };


    /**
    * Check if the widget can move to the specified row, else returns the
    * upper row possible.
    *
    * @method can_go_up_to_row
    * @param {Number} widget_grid_data The current grid coords object of the
    *  widget.
    * @param {Number} col The target column.
    * @param {Number} row The target row.
    * @return {Boolean|Number} Returns the row number if the widget can move
    *  to the target position, else returns false.
    */
    fn.can_go_up_to_row = function(widget_grid_data, col, row) {
        var ga = this.gridmap;
        var result = true;
        var urc = []; // upper_rows_in_columns
        var actual_row = widget_grid_data.row;
        var r;

        /* generate an array with columns as index and array with
         * upper rows empty in the column */
        this.for_each_column_occupied(widget_grid_data, function(tcol) {
            var grid_col = ga[tcol];
            urc[tcol] = [];

            r = actual_row;
            while (r--) {
                if (this.is_empty(tcol, r) &&
                    !this.is_placeholder_in(tcol, r)
                ) {
                    urc[tcol].push(r);
                } else {
                    break;
                }
            }

            if (!urc[tcol].length) {
                result = false;
                return true;
            }

        });

        if (!result) { return false; }

        /* get common rows starting from upper position in all the columns
         * that widget occupies */
        r = row;
        for (r = 1; r < actual_row; r++) {
            var common = true;

            for (var uc = 0, ucl = urc.length; uc < ucl; uc++) {
                if (urc[uc] && $.inArray(r, urc[uc]) === -1) {
                    common = false;
                }
            }

            if (common === true) {
                result = r;
                break;
            }
        }

        return result;
    };


    fn.displacement_diff = function(widget_grid_data, parent_bgd, y_units) {
        var actual_row = widget_grid_data.row;
        var diffs = [];
        var parent_max_y = parent_bgd.row + parent_bgd.size_y;

        this.for_each_column_occupied(widget_grid_data, function(col) {
            var temp_y_units = 0;

            for (var r = parent_max_y; r < actual_row; r++) {
                if (this.is_empty(col, r)) {
                    temp_y_units = temp_y_units + 1;
                }
            }

            diffs.push(temp_y_units);
        });

        var max_diff = Math.max.apply(Math, diffs);
        y_units = (y_units - max_diff);

        return y_units > 0 ? y_units : 0;
    };


    /**
    * Get widgets below a widget.
    *
    * @method widgets_below
    * @param {HTMLElement} $el The jQuery wrapped HTMLElement.
    * @return {jQuery} A jQuery collection of HTMLElements.
    */
    fn.widgets_below = function($el) {
        var el_grid_data = $.isPlainObject($el) ? $el : $el.coords().grid;
        var self = this;
        var ga = this.gridmap;
        var next_row = el_grid_data.row + el_grid_data.size_y - 1;
        var $nexts = $([]);

        this.for_each_column_occupied(el_grid_data, function(col) {
            self.for_each_widget_below(col, next_row, function(tcol, trow) {
                if (!self.is_player(this) && $.inArray(this, $nexts) === -1) {
                    $nexts = $nexts.add(this);
                    return true; // break
                }
            });
        });

        return Gridster.sort_by_row_asc($nexts);
    };


    /**
    * Update the array of mapped positions with the new player position.
    *
    * @method set_cells_player_occupies
    * @param {Number} col The new player col.
    * @param {Number} col The new player row.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.set_cells_player_occupies = function(col, row) {
        this.remove_from_gridmap(this.placeholder_grid_data);
        this.placeholder_grid_data.col = col;
        this.placeholder_grid_data.row = row;
        this.add_to_gridmap(this.placeholder_grid_data, this.$player);
        return this;
    };


    /**
    * Remove from the array of mapped positions the reference to the player.
    *
    * @method empty_cells_player_occupies
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.empty_cells_player_occupies = function() {
        this.remove_from_gridmap(this.placeholder_grid_data);
        return this;
    };


    fn.can_go_up = function($el) {
        var el_grid_data = $el.coords().grid;
        var initial_row = el_grid_data.row;
        var prev_row = initial_row - 1;
        var ga = this.gridmap;
        var upper_rows_by_column = [];

        var result = true;
        if (initial_row === 1) { return false; }

        this.for_each_column_occupied(el_grid_data, function(col) {
            var $w = this.is_widget(col, prev_row);

            if (this.is_occupied(col, prev_row) ||
                this.is_player(col, prev_row) ||
                this.is_placeholder_in(col, prev_row) ||
                this.is_player_in(col, prev_row)
            ) {
                result = false;
                return true; //break
            }
        });

        return result;
    };


    /**
    * Check if it's possible to move a widget to a specific col/row. It takes
    * into account the dimensions (`size_y` and `size_x` attrs. of the grid
    *  coords object) the widget occupies.
    *
    * @method can_move_to
    * @param {Object} widget_grid_data The grid coords object that represents
    *  the widget.
    * @param {Object} col The col to check.
    * @param {Object} row The row to check.
    * @param {Number} [max_row] The max row allowed.
    * @return {Boolean} Returns true if all cells are empty, else return false.
    */
    fn.can_move_to = function(widget_grid_data, col, row, max_row) {
        var ga = this.gridmap;
        var $w = widget_grid_data.el;
        var future_wd = {
            size_y: widget_grid_data.size_y,
            size_x: widget_grid_data.size_x,
            col: col,
            row: row
        };
        var result = true;

        //Prevents widgets go out of the grid
        var right_col = col + widget_grid_data.size_x - 1;
        if (right_col > this.cols) {
            return false;
        }

        if (max_row && max_row < row + widget_grid_data.size_y - 1) {
            return false;
        }

        this.for_each_cell_occupied(future_wd, function(tcol, trow) {
            var $tw = this.is_widget(tcol, trow);
            if ($tw && (!widget_grid_data.el || $tw.is($w))) {
                result = false;
            }
        });

        return result;
    };


    /**
    * Given the leftmost column returns all columns that are overlapping
    *  with the player.
    *
    * @method get_targeted_columns
    * @param {Number} [from_col] The leftmost column.
    * @return {Array} Returns an array with column numbers.
    */
    fn.get_targeted_columns = function(from_col) {
        var max = (from_col || this.player_grid_data.col) +
            (this.player_grid_data.size_x - 1);
        var cols = [];
        for (var col = from_col; col <= max; col++) {
            cols.push(col);
        }
        return cols;
    };


    /**
    * Given the upper row returns all rows that are overlapping with the player.
    *
    * @method get_targeted_rows
    * @param {Number} [from_row] The upper row.
    * @return {Array} Returns an array with row numbers.
    */
    fn.get_targeted_rows = function(from_row) {
        var max = (from_row || this.player_grid_data.row) +
            (this.player_grid_data.size_y - 1);
        var rows = [];
        for (var row = from_row; row <= max; row++) {
            rows.push(row);
        }
        return rows;
    };

    /**
    * Get all columns and rows that a widget occupies.
    *
    * @method get_cells_occupied
    * @param {Object} el_grid_data The grid coords object of the widget.
    * @return {Object} Returns an object like `{ cols: [], rows: []}`.
    */
    fn.get_cells_occupied = function(el_grid_data) {
        var cells = { cols: [], rows: []};
        var i;
        if (arguments[1] instanceof $) {
            el_grid_data = arguments[1].coords().grid;
        }

        for (i = 0; i < el_grid_data.size_x; i++) {
            var col = el_grid_data.col + i;
            cells.cols.push(col);
        }

        for (i = 0; i < el_grid_data.size_y; i++) {
            var row = el_grid_data.row + i;
            cells.rows.push(row);
        }

        return cells;
    };


    /**
    * Iterate over the cells occupied by a widget executing a function for
    * each one.
    *
    * @method for_each_cell_occupied
    * @param {Object} el_grid_data The grid coords object that represents the
    *  widget.
    * @param {Function} callback The function to execute on each column
    *  iteration. Column and row are passed as arguments.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.for_each_cell_occupied = function(grid_data, callback) {
        this.for_each_column_occupied(grid_data, function(col) {
            this.for_each_row_occupied(grid_data, function(row) {
                callback.call(this, col, row);
            });
        });
        return this;
    };


    /**
    * Iterate over the columns occupied by a widget executing a function for
    * each one.
    *
    * @method for_each_column_occupied
    * @param {Object} el_grid_data The grid coords object that represents
    *  the widget.
    * @param {Function} callback The function to execute on each column
    *  iteration. The column number is passed as first argument.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.for_each_column_occupied = function(el_grid_data, callback) {
        for (var i = 0; i < el_grid_data.size_x; i++) {
            var col = el_grid_data.col + i;
            callback.call(this, col, el_grid_data);
        }
    };


    /**
    * Iterate over the rows occupied by a widget executing a function for
    * each one.
    *
    * @method for_each_row_occupied
    * @param {Object} el_grid_data The grid coords object that represents
    *  the widget.
    * @param {Function} callback The function to execute on each column
    *  iteration. The row number is passed as first argument.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.for_each_row_occupied = function(el_grid_data, callback) {
        for (var i = 0; i < el_grid_data.size_y; i++) {
            var row = el_grid_data.row + i;
            callback.call(this, row, el_grid_data);
        }
    };



    fn._traversing_widgets = function(type, direction, col, row, callback) {
        var ga = this.gridmap;
        if (!ga[col]) { return; }

        var cr, max;
        var action = type + '/' + direction;
        if (arguments[2] instanceof $) {
            var el_grid_data = arguments[2].coords().grid;
            col = el_grid_data.col;
            row = el_grid_data.row;
            callback = arguments[3];
        }
        var matched = [];
        var trow = row;


        var methods = {
            'for_each/above': function() {
                while (trow--) {
                    if (trow > 0 && this.is_widget(col, trow) &&
                        $.inArray(ga[col][trow], matched) === -1
                    ) {
                        cr = callback.call(ga[col][trow], col, trow);
                        matched.push(ga[col][trow]);
                        if (cr) { break; }
                    }
                }
            },
            'for_each/below': function() {
                for (trow = row + 1, max = ga[col].length; trow < max; trow++) {
                    if (this.is_widget(col, trow) &&
                        $.inArray(ga[col][trow], matched) === -1
                    ) {
                        cr = callback.call(ga[col][trow], col, trow);
                        matched.push(ga[col][trow]);
                        if (cr) { break; }
                    }
                }
            }
        };

        if (methods[action]) {
            methods[action].call(this);
        }
    };


    /**
    * Iterate over each widget above the column and row specified.
    *
    * @method for_each_widget_above
    * @param {Number} col The column to start iterating.
    * @param {Number} row The row to start iterating.
    * @param {Function} callback The function to execute on each widget
    *  iteration. The value of `this` inside the function is the jQuery
    *  wrapped HTMLElement.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.for_each_widget_above = function(col, row, callback) {
        this._traversing_widgets('for_each', 'above', col, row, callback);
        return this;
    };


    /**
    * Iterate over each widget below the column and row specified.
    *
    * @method for_each_widget_below
    * @param {Number} col The column to start iterating.
    * @param {Number} row The row to start iterating.
    * @param {Function} callback The function to execute on each widget
    *  iteration. The value of `this` inside the function is the jQuery wrapped
    *  HTMLElement.
    * @return {Class} Returns the instance of the Gridster Class.
    */
    fn.for_each_widget_below = function(col, row, callback) {
        this._traversing_widgets('for_each', 'below', col, row, callback);
        return this;
    };


    /**
    * Returns the highest occupied cell in the grid.
    *
    * @method get_highest_occupied_cell
    * @return {Object} Returns an object with `col` and `row` numbers.
    */
    fn.get_highest_occupied_cell = function() {
        var r;
        var gm = this.gridmap;
        var rl = gm[1].length;
        var rows = [], cols = [];
        var row_in_col = [];
        for (var c = gm.length - 1; c >= 1; c--) {
            for (r = rl - 1; r >= 1; r--) {
                if (this.is_widget(c, r)) {
                    rows.push(r);
                    cols.push(c);
                    break;
                }
            }
        }

        return {
            col: Math.max.apply(Math, cols),
            row: Math.max.apply(Math, rows)
        };
    };


    fn.get_widgets_from = function(col, row) {
        var ga = this.gridmap;
        var $widgets = $();

        if (col) {
            $widgets = $widgets.add(
                this.$widgets.filter(function() {
                    var tcol = $(this).attr('data-col');
                    return (tcol === col || tcol > col);
                })
            );
        }

        if (row) {
            $widgets = $widgets.add(
                this.$widgets.filter(function() {
                    var trow = $(this).attr('data-row');
                    return (trow === row || trow > row);
                })
            );
        }

        return $widgets;
    };


    /**
    * Set the current height of the parent grid.
    *
    * @method set_dom_grid_height
    * @return {Object} Returns the instance of the Gridster class.
    */
    fn.set_dom_grid_height = function(height) {
        if (typeof height === 'undefined') {
            var r = this.get_highest_occupied_cell().row;
            height = r * this.min_widget_height;
        }

        this.container_height = height;
        this.$el.css('height', this.container_height);
        return this;
    };

    /**
    * Set the current width of the parent grid.
    *
    * @method set_dom_grid_width
    * @return {Object} Returns the instance of the Gridster class.
    */
    fn.set_dom_grid_width = function(cols) {
        if (typeof cols === 'undefined') {
            cols = this.get_highest_occupied_cell().col;
        }

        var max_cols = (this.options.autogrow_cols ? this.options.max_cols :
            this.cols);

        cols = Math.min(max_cols, Math.max(cols, this.options.min_cols));
        this.container_width = cols * this.min_widget_width;
        this.$el.css('width', this.container_width);
        return this;
    };


    /**
    * It generates the neccessary styles to position the widgets.
    *
    * @method generate_stylesheet
    * @param {Number} rows Number of columns.
    * @param {Number} cols Number of rows.
    * @return {Object} Returns the instance of the Gridster class.
    */
    fn.generate_stylesheet = function(opts) {
        var styles = '';
        var max_size_x = this.options.max_size_x || this.cols;
        var max_rows = 0;
        var max_cols = 0;
        var i;
        var rules;

        opts || (opts = {});
        opts.cols || (opts.cols = this.cols);
        opts.rows || (opts.rows = this.rows);
        opts.namespace || (opts.namespace = this.options.namespace);
        opts.widget_base_dimensions ||
            (opts.widget_base_dimensions = this.options.widget_base_dimensions);
        opts.widget_margins ||
            (opts.widget_margins = this.options.widget_margins);
        opts.min_widget_width = (opts.widget_margins[0] * 2) +
            opts.widget_base_dimensions[0];
        opts.min_widget_height = (opts.widget_margins[1] * 2) +
            opts.widget_base_dimensions[1];

        // don't duplicate stylesheets for the same configuration
        var serialized_opts = $.param(opts);
        if ($.inArray(serialized_opts, Gridster.generated_stylesheets) >= 0) {
            return false;
        }

        this.generated_stylesheets.push(serialized_opts);
        Gridster.generated_stylesheets.push(serialized_opts);

        /* generate CSS styles for cols */
        for (i = opts.cols; i >= 0; i--) {
            styles += (opts.namespace + ' [data-col="'+ (i + 1) + '"] { left:' +
                ((i * opts.widget_base_dimensions[0]) +
                (i * opts.widget_margins[0]) +
                ((i + 1) * opts.widget_margins[0])) + 'px; }\n');
        }

        /* generate CSS styles for rows */
        for (i = opts.rows; i >= 0; i--) {
            styles += (opts.namespace + ' [data-row="' + (i + 1) + '"] { top:' +
                ((i * opts.widget_base_dimensions[1]) +
                (i * opts.widget_margins[1]) +
                ((i + 1) * opts.widget_margins[1]) ) + 'px; }\n');
        }

        for (var y = 1; y <= opts.rows; y++) {
            styles += (opts.namespace + ' [data-sizey="' + y + '"] { height:' +
                (y * opts.widget_base_dimensions[1] +
                (y - 1) * (opts.widget_margins[1] * 2)) + 'px; }\n');
        }

        for (var x = 1; x <= max_size_x; x++) {
            styles += (opts.namespace + ' [data-sizex="' + x + '"] { width:' +
                (x * opts.widget_base_dimensions[0] +
                (x - 1) * (opts.widget_margins[0] * 2)) + 'px; }\n');
        }

        this.remove_style_tags();

        return this.add_style_tag(styles);
    };


    /**
    * Injects the given CSS as string to the head of the document.
    *
    * @method add_style_tag
    * @param {String} css The styles to apply.
    * @return {Object} Returns the instance of the Gridster class.
    */
    fn.add_style_tag = function(css) {
        var d = document;
        var tag = d.createElement('style');

        d.getElementsByTagName('head')[0].appendChild(tag);
        tag.setAttribute('type', 'text/css');

        if (tag.styleSheet) {
            tag.styleSheet.cssText = css;
        } else {
            tag.appendChild(document.createTextNode(css));
        }

        this.$style_tags = this.$style_tags.add(tag);

        return this;
    };


    /**
    * Remove the style tag with the associated id from the head of the document
    *
    * @method  remove_style_tag
    * @return {Object} Returns the instance of the Gridster class.
    */
    fn.remove_style_tags = function() {
        var all_styles = Gridster.generated_stylesheets;
        var ins_styles = this.generated_stylesheets;

        this.$style_tags.remove();

        Gridster.generated_stylesheets = $.map(all_styles, function(s) {
            if ($.inArray(s, ins_styles) === -1) { return s; }
        });
    };


    /**
    * Generates a faux grid to collide with it when a widget is dragged and
    * detect row or column that we want to go.
    *
    * @method generate_faux_grid
    * @param {Number} rows Number of columns.
    * @param {Number} cols Number of rows.
    * @return {Object} Returns the instance of the Gridster class.
    */
    fn.generate_faux_grid = function(rows, cols) {
        this.faux_grid = [];
        this.gridmap = [];
        var col;
        var row;
        for (col = cols; col > 0; col--) {
            this.gridmap[col] = [];
            for (row = rows; row > 0; row--) {
                this.add_faux_cell(row, col);
            }
        }
        return this;
    };


    /**
    * Add cell to the faux grid.
    *
    * @method add_faux_cell
    * @param {Number} row The row for the new faux cell.
    * @param {Number} col The col for the new faux cell.
    * @return {Object} Returns the instance of the Gridster class.
    */
    fn.add_faux_cell = function(row, col) {
        var coords = $({
                        left: this.baseX + ((col - 1) * this.min_widget_width),
                        top: this.baseY + (row -1) * this.min_widget_height,
                        width: this.min_widget_width,
                        height: this.min_widget_height,
                        col: col,
                        row: row,
                        original_col: col,
                        original_row: row
                    }).coords();

        if (!$.isArray(this.gridmap[col])) {
            this.gridmap[col] = [];
        }

        this.gridmap[col][row] = false;
        this.faux_grid.push(coords);

        return this;
    };


    /**
    * Add rows to the faux grid.
    *
    * @method add_faux_rows
    * @param {Number} rows The number of rows you want to add to the faux grid.
    * @return {Object} Returns the instance of the Gridster class.
    */
    fn.add_faux_rows = function(rows) {
        var actual_rows = this.rows;
        var max_rows = actual_rows + (rows || 1);

        for (var r = max_rows; r > actual_rows; r--) {
            for (var c = this.cols; c >= 1; c--) {
                this.add_faux_cell(r, c);
            }
        }

        this.rows = max_rows;

        if (this.options.autogenerate_stylesheet) {
            this.generate_stylesheet();
        }

        return this;
    };

     /**
    * Add cols to the faux grid.
    *
    * @method add_faux_cols
    * @param {Number} cols The number of cols you want to add to the faux grid.
    * @return {Object} Returns the instance of the Gridster class.
    */
    fn.add_faux_cols = function(cols) {
        var actual_cols = this.cols;
        var max_cols = actual_cols + (cols || 1);
        max_cols = Math.min(max_cols, this.options.max_cols);

        for (var c = actual_cols + 1; c <= max_cols; c++) {
            for (var r = this.rows; r >= 1; r--) {
                this.add_faux_cell(r, c);
            }
        }

        this.cols = max_cols;

        if (this.options.autogenerate_stylesheet) {
            this.generate_stylesheet();
        }

        return this;
    };


    /**
    * Recalculates the offsets for the faux grid. You need to use it when
    * the browser is resized.
    *
    * @method recalculate_faux_grid
    * @return {Object} Returns the instance of the Gridster class.
    */
    fn.recalculate_faux_grid = function() {
        var aw = this.$wrapper.width();
        this.baseX = ($(window).width() - aw) / 2;
        this.baseY = this.$wrapper.offset().top;

        $.each(this.faux_grid, $.proxy(function(i, coords) {
            this.faux_grid[i] = coords.update({
                left: this.baseX + (coords.data.col -1) * this.min_widget_width,
                top: this.baseY + (coords.data.row -1) * this.min_widget_height
            });
        }, this));

        return this;
    };


    /**
    * Get all widgets in the DOM and register them.
    *
    * @method get_widgets_from_DOM
    * @return {Object} Returns the instance of the Gridster class.
    */
    fn.get_widgets_from_DOM = function() {
        var widgets_coords = this.$widgets.map($.proxy(function(i, widget) {
            var $w = $(widget);
            return this.dom_to_coords($w);
        }, this));

        widgets_coords = Gridster.sort_by_row_and_col_asc(widgets_coords);

        var changes = $(widgets_coords).map($.proxy(function(i, wgd) {
            return this.register_widget(wgd) || null;
        }, this));

        if (changes.length) {
            this.$el.trigger('gridster:positionschanged');
        }

        return this;
    };


    /**
    * Calculate columns and rows to be set based on the configuration
    *  parameters, grid dimensions, etc ...
    *
    * @method generate_grid_and_stylesheet
    * @return {Object} Returns the instance of the Gridster class.
    */
    fn.generate_grid_and_stylesheet = function() {
        var aw = this.$wrapper.width();
        var max_cols = this.options.max_cols;

        var cols = Math.floor(aw / this.min_widget_width) +
                   this.options.extra_cols;

        var actual_cols = this.$widgets.map(function() {
            return $(this).attr('data-col');
        }).get();

        //needed to pass tests with phantomjs
        actual_cols.length || (actual_cols = [0]);

        var min_cols = Math.max.apply(Math, actual_cols);

        this.cols = Math.max(min_cols, cols, this.options.min_cols);

        if (max_cols !== Infinity && max_cols >= min_cols && max_cols < this.cols) {
            this.cols = max_cols;
        }

        // get all rows that could be occupied by the current widgets
        var max_rows = this.options.extra_rows;
        this.$widgets.each(function(i, w) {
            max_rows += (+$(w).attr('data-sizey'));
        });

        this.rows = Math.max(max_rows, this.options.min_rows);

        this.baseX = ($(window).width() - aw) / 2;
        this.baseY = this.$wrapper.offset().top;

        if (this.options.autogenerate_stylesheet) {
            this.generate_stylesheet();
        }

        return this.generate_faux_grid(this.rows, this.cols);
    };

    /**
     * Destroy this gridster by removing any sign of its presence, making it easy to avoid memory leaks
     *
     * @method destroy
     * @param {Boolean} remove If true, remove gridster from DOM.
     * @return {Object} Returns the instance of the Gridster class.
     */
    fn.destroy = function(remove) {
        this.$el.removeData('gridster');

        // remove bound callback on window resize
        $(window).unbind('.gridster');

        if (this.drag_api) {
            this.drag_api.destroy();
        }

        this.remove_style_tags();

        remove && this.$el.remove();

        return this;
    };


    //jQuery adapter
    $.fn.gridster = function(options) {
        return this.each(function() {
            if (! $(this).data('gridster')) {
                $(this).data('gridster', new Gridster( this, options ));
            }
        });
    };

    return Gridster;

}));
;
window.UNGM = window.UNGM || {};
window.UNGM.ManageGridster = {
    margin: 5,
    columnCount: null,
    cellWidth: null,
    cellHeight: null,
    isGridsterUsed: null,
    isPrintPage: null,
    gridster: null,
    searchResultsTableMaxHeight: 400,
    setGridParameters: function (columnCount, defaultGridWidth) {
        UNGM.ManageGridster.isGridsterUsed = $(".gridster").length;
        UNGM.ManageGridster.isPrintPage = $(".kc-print-page").length;
        if (UNGM.ManageGridster.isGridsterUsed) {
            UNGM.ManageGridster.columnCount = columnCount;
            var gridWidth = $(".gridster").width();
            if (defaultGridWidth != undefined) {
                gridWidth = defaultGridWidth;
            }

            UNGM.ManageGridster.cellWidth = (gridWidth - 2 * UNGM.ManageGridster.margin * columnCount) / columnCount;
            UNGM.ManageGridster.cellHeight = UNGM.ManageGridster.cellWidth / 2;
        }
    },
    initGridsterForNonKCAdmin: function () {
        UNGM.ManageGridster.gridster = $(".gridster > ul").gridster({
            widget_margins: [UNGM.ManageGridster.margin, UNGM.ManageGridster.margin],
            widget_base_dimensions: [UNGM.ManageGridster.cellWidth, UNGM.ManageGridster.cellHeight],
            resize: {
                enabled: false
            },
            avoid_overlapped_widgets: true,
            min_cols: UNGM.ManageGridster.columnCount,
            min_rows: 12
        }).data("gridster");

        UNGM.ManageGridster.autoResizeWidgets();
        UNGM.ManageGridster.gridster.disable();
    },
    autoResizeWidgets: function () {
        var widgetContents = $(".content-fit-widget");
        for (var i = 0; i < widgetContents.length ; i++) {
            // added +2 because if the widgetHeight - contentHeight < 2, the scrollbar appears, even with border-box
            var contentHeight = $(widgetContents[i]).outerHeight(true) + 2;
            if ($(widgetContents[i]).is(".search-widget") && !UNGM.ManageGridster.isPrintPage) {
                contentHeight += UNGM.ManageGridster.searchResultsTableMaxHeight;
            }

            var widget = $(widgetContents[i]).closest("li.widget");
            var adjustedHeight = Math.ceil((contentHeight + 2 * UNGM.ManageGridster.margin) / (UNGM.ManageGridster.cellHeight + 2 * UNGM.ManageGridster.margin));
            UNGM.ManageGridster.gridster.resize_widget($(widget), $(widget).data("sizex"), adjustedHeight);
        }
    },
    getHighestUnoccupiedRow: function() {
        var highestOccupiedCell = UNGM.ManageGridster.gridster.get_highest_occupied_cell();
        return (highestOccupiedCell.row === -Infinity) ? 1 : highestOccupiedCell.row + 1;
    }
};
window.UNGM.ManagePagePrint = {
    init: function (elementIdToPrint, elementIdsToExcludeFromPrint, gridsterColumnNumber) {
        $("#btnPrintPage").off("click").on("click", function () { UNGM.ManagePagePrint.renderPrintPage(elementIdToPrint, elementIdsToExcludeFromPrint, gridsterColumnNumber) });
    },
    renderPrintPage: function (elementIdToPrint, elementIdsToExcludeFromPrint, gridsterColumnCount) {
        var elementsToPrint = $(elementIdToPrint).clone();
        $.each(elementIdsToExcludeFromPrint, function (index, item) {
            $(elementsToPrint).find(item).remove();
        });
        elementsToPrint = elementsToPrint.html();
        var pageWidth = $(".gridster").width();
        var printPageWidth = 986;
        var popupWin = window.open('', '_blank', 'scrollbars=1, width=' + pageWidth + ', height=' + $(window).height() + ', location=no');
        popupWin.document.open();
        var googleAPIFontStyleSheet = '<link href="https://Fonts.googleapis.com/css?family=Open+Sans:300,400" rel="stylesheet" type="text/css">';
        var jqueryScript = '<script src="/Scripts/jquery-1.12.0.js"></script>';
        var gridSterScript = '<script src="/Scripts/Gridster/jquery.gridster.js"></script>';
        var manageGridSterScript = '<script src="/Scripts/UNGM.ManageGridster.js"></script>';
        var gridSterStyleSheet = '<link rel="stylesheet" type="text/css" href="/Scripts/Gridster/jquery.gridster.min.css">';
        var ungmStyleSheet = '<link href="/Styles/ungm.ltr.css" rel="stylesheet" type="text/css">';
        var pageTransformStyle = '<style type="text/css"> body { -webkit-transform: scale(' + printPageWidth / pageWidth + ', ' + printPageWidth / pageWidth + ') translateX(-' + (pageWidth - printPageWidth) / 2 + 'px) translateY(-' + (pageWidth - printPageWidth) / 2 + 'px); } </style>';
        var manageGridSterInitScript = '<script>$(window).load(function () { UNGM.ManageGridster.setGridParameters(' + gridsterColumnCount + ', ' + pageWidth + '); if (UNGM.ManageGridster.isGridsterUsed) { UNGM.ManageGridster.initGridsterForNonKCAdmin(); } window.print();});</script>';
        var htmlHead = '<head>' + googleAPIFontStyleSheet + jqueryScript + gridSterScript + manageGridSterScript + manageGridSterInitScript + gridSterStyleSheet + ungmStyleSheet + pageTransformStyle + '</head>';
        var htmlBody = '<body class="kc-print-page">' + elementsToPrint + '</body>';
        popupWin.document.write('<html>' + htmlHead + htmlBody + '</html>');
        popupWin.document.close();
    }
};
(function (namespace, undefined) {

    namespace.ElementHash = ElementHashFactory;

    function ElementHashFactory() {
        return new ElementHash();
    }

    function ElementHash() {
        this._items = [];
    }

    ElementHash.prototype.value = value;
    ElementHash.prototype.item = item;

    function value(element, value) {
        var item = this.item(element);
        return item.value = value || item.value;
    }

    function item(element) {
        var items = $.grep(this._items, function (item) { return $(item.element)[0] === $(element)[0]; });
        if (items.length !== 0) {
            return item = items[0];
        } else {
            var item = { element: $(element)[0], value: undefined };
            this._items.push(item);
            return item;
        }
    }

})(window.UNGM.ViewModel = window.UNGM.ViewModel || {});;
'use strict';

(function (namespace, undefined) {

    namespace.Binder = BinderFactory;

    var binderCount = 0;

    function BinderFactory() {
        var binder = {
            provider: provider,
            factory: factory,
            directive: directive,
            filter: filter,
            bind: bind
        };

        var name = 'binder-' + (++binderCount);
        var module = uvm.module(name, ['uvm']);

        function provider(name, provider) {
            module.provider(name, provider);
            return binder;
        }

        function factory(name, factory) {
            module.factory(name, factory);
            return binder;
        }

        function directive(name, directiveFactory) {
            module.directive(name, directiveFactory);
            return binder;
        }

        function filter(name, filterFunction) {
            module.filter(name, function () { return filterFunction; });
            return binder;
        }

        function bind(selector, options) {
            var $injector = uvm.bootstrap(selector, [name]);
            var $rootScope = $injector.get('$rootScope');

            var bond = {
                data: data,
                watch: watch
            };

            bond.data((options || {}).data || {});

            function data(data) {
                if (data !== undefined) {
                    uvm.Util.extend($rootScope, data);
                    $rootScope.$apply();
                }
                return $rootScope;
            }

            function watch(expression, callback) {
                $rootScope.$watch(expression, callback);
            }

            return bond;
        }

        return binder;
    }

})(window.UNGM.ViewModel = window.UNGM.ViewModel || {});
(function (namespace, undefined) {

    namespace.Repeat = RepeatFactory;

    var hash = namespace.ElementHash();

    function RepeatFactory(element, data) {
        var repeat = hash.value(element) || hash.value(element, new Repeat(element));
        if (data) { repeat.data(data); }
        return repeat;
    }

    function Repeat(element) {
        this._template = $(element)[0].outerHTML;
        this._nodes = [$(element)[0]];
        this._indexKey = null;
        this._data = [];
        this.render();
    }

    Repeat.prototype.setIndex = setIndex;
    Repeat.prototype.data = data;
    Repeat.prototype.render = render;

    function setIndex(key, offset) {
        this._indexKey = key;
        this._indexOffset = offset || 0;
    }

    function data(data) {
        if (data instanceof Array) {
            this._data = data;
            this.render();
            return this._data;
        } else if (data === undefined) {
            return this._data;
        } else {
            throw "Repeat.prototype.data() only accepts array; instead got: " + data;
        }
    }

    function render() {
        switch (this._data.length) {
            case 0:
                var nodes = createCommentNodes.apply(this, [this._template]);
                replaceNodes(this._nodes, nodes);
                this._nodes = nodes;
                break;
            default:
                var nodes = createElementNodes.apply(this, [this._template, this._data]);
                replaceNodes(this._nodes, nodes);
                this._nodes = nodes;
                break;
        }
    }

    function createCommentNodes(template) {
        var nodes = [document.createComment(template)];
        return nodes;
    }

    function createElementNodes(template, data) {
        var $this = this;
        var interpolate = namespace.Interpolate(template);
        var nodes = $.map(data, function (datum, index) {
            if ($this._indexKey !== null) {
                datum = $.extend({}, datum);
                datum[$this._indexKey] = index + $this._indexOffset;
            }
            return $(interpolate(datum))[0];
        });
        return nodes;
    }

    function replaceNodes(oldNodes, newNodes) {
        var parentNode = oldNodes[0].parentNode;
        parentNode.replaceChild(newNodes[0], oldNodes[0]);
        for (var i = 1; i < oldNodes.length; ++i) {
            parentNode.removeChild(oldNodes[i]);
        }
        for (var i = 1; i < newNodes.length; ++i) {
            parentNode.insertBefore(newNodes[i], newNodes[i - 1].nextSibling);
        }
    }

})(window.UNGM.ViewModel = window.UNGM.ViewModel || {});;
(function (namespace, undefined) {

    namespace.Filter = Filter;

    var filters = {};

    function Filter(filter, filterFunction) {
        return filters[filter] = filterFunction || filters[filter];
    }

})(window.UNGM.ViewModel = window.UNGM.ViewModel || {});;
(function (namespace, undefined) {

    namespace.DelayedFunction = DelayedFunctionFactory;

    function DelayedFunctionFactory(func, timeout) {
        var _timeout = timeout || 300;
        var _timeoutId = null;

        var instance = {
            invoke: invoke,
            cancel: cancel
        }

        function invoke(thisArg, argsArray) {
            cancel();
            _timeoutId = setTimeout(func, _timeout);
        }

        function cancel() {
            clearTimeout(_timeoutId);
        }

        return instance;
    }

})(window.UNGM.ViewModel = window.UNGM.ViewModel || {});
;
"use strict";

(function (namespace, undefined) {

    namespace.MultipleCountryPicker = MultipleCountryPickerFactory;

    var instances = {};

    function MultipleCountryPickerFactory(params) {
        
        if (instances[params.Id] !== undefined) { return instances[params.Id]; }

        var select = $("#" + params.Id).selectToAutocomplete({ "copy-attributes-to-text-field": true });
        var textAutocomplete = select.siblings("input[type='text']");
        var selectedCountries = select.siblings(".selected-countries");
        var allCountriesLinks = select.siblings(".all-countries-links");
        var selectedCountriesInfos = [];

        textAutocomplete.addClass("ignore-on-serialize");
        textAutocomplete.addClass("ignore-on-dom-events");

        textAutocomplete.on("autocompleteselect", function (event, ui) {
            var countryId = ui.item["real-value"];
            if (countryId != "" && !IsCountrySelected(countryId)) { InsertSelectedCountry(countryId); }
        });

        textAutocomplete.on("autocompleteclose", function (event, ui) {
            textAutocomplete.val("");
        });

        selectedCountries.on("click", ".selected-country-remove", function (event) {
            var countryId = $(event.currentTarget).parent(".selected-country").data("country-id");
            RemoveSelectedCountry(countryId);
        });

        if (params.IsWithSelectAllAndRemoveAllLinks) {
            allCountriesLinks.on("click", ".select-all-countries", SelectAllCountries);
            allCountriesLinks.on("click", ".remove-all-countries", RemoveAllCountries);
        }

        function IsCountrySelected(countryId) {
            return $.grep(selectedCountriesInfos, function (info) { return info.countryId == countryId; }).length !== 0;
        }

        function InsertSelectedCountry(countryId) {
            var countryName = select.children("option[value='" + countryId + "']").text();
            selectedCountriesInfos.push({ countryId: countryId, countryName: countryName });
            selectedCountriesInfos.sort(function (item1, item2) { return item1.countryName.localeCompare(item2.countryName); });
            RefreshSelectedCountries();
            select.triggerHandler("change");
        }

        function RemoveSelectedCountry(countryId) {
            selectedCountriesInfos = $.grep(selectedCountriesInfos, function (info) { return info.countryId != countryId; });
            selectedCountriesInfos.sort(function (item1, item2) { return item1.countryName.localeCompare(item2.countryName); });
            RefreshSelectedCountries();
            select.trigger("change");
        }

        function RemoveAllCountries() {
            selectedCountriesInfos = [];
            RefreshSelectedCountries();
            select.trigger("change");
        }

        function SelectAllCountries() {
            selectedCountriesInfos = [];
            select.children("option:not([value=''])").each(function () {
                selectedCountriesInfos.push({ countryId: $(this).val(), countryName: $(this).text() });
            });
            RefreshSelectedCountries();
            select.trigger("change");
        }

        function RefreshSelectedCountries() {
            selectedCountries.empty();
            $.each(selectedCountriesInfos, function (_, info) { selectedCountries.append(CreateSelectedCountry(info.countryId, info.countryName)); });
        }

        function CreateSelectedCountry(countryId, countryName) {
            return $("<div class='selected-country' data-country-id='" + countryId + "'> \
                          <input type='hidden' name='" + params.Name + "' value='" + countryId + "'>\
                          <span>" + countryName + "</span> \
                          <a class='selected-country-remove' href='javascript:void(0);' style='display: inline;'>(remove)</a> \
                      </div>");
        }

        var instance = {
            InsertSelectedCountries: InsertSelectedCountries,
            ClearSelectedCountries: ClearSelectedCountries,
            GetSelectedCountryIds: GetSelectedCountryIds,
            Remove: Remove
        };

        function InsertSelectedCountries(countryIds) {
            $.each(countryIds, function (_, countryId) {
                InsertSelectedCountry(countryId);
            });
        }

        function ClearSelectedCountries() {
            textAutocomplete.val("");
            selectedCountriesInfos = [];
            RefreshSelectedCountries();
        }

        function GetSelectedCountryIds() {
            var selectedCountryIds = [];
            $.each(selectedCountriesInfos, function (_, info) { selectedCountryIds.push(info.countryId); });
            return selectedCountryIds;
        }

        function Remove() {
            delete instances[params.Id];
        }

        return instances[params.Id] = instance;
    }
})(window.UNGM.EditorTemplates = window.UNGM.EditorTemplates || {});
;
"use strict";

(function (namespace, undefined) {

    namespace.Wizard = WizardFactory;

    var hash = UNGM.ViewModel.ElementHash();

    function WizardFactory(selector, area, type, options) {
        var root = $(selector);

        if (hash.value(root)) { return hash.value(root); }

        UNGM.initTabsIn(root);

        root.on("click", ".user-email-template-create", OnUserEmailTemplateCreate);
        root.on("click", ".user-email-template-select", OnUserEmailTemplateSelect);
        root.on("click", ".dynamicField", InsertDynamicFieldIntoActiveTabEmailBodyText);
        root.on("submit", ".user-email-template-form", OnUserEmailTemplateSubmit);
        root.on("click", ".user-email-template-preview-contact", OnPreviewSelect);
        root.on("click", ".user-email-template-confirm", Confirm);
        UNGM.tabContentReceivedCallback = OnTabContentLoaded;
        UNGM.DocumentPickerCallbacks = { documentPicked: AttachDocument, documentRemoved: DetachDocument };
        
        function OnUserEmailTemplateCreate(event) {
            root = $(event.currentTarget).closest(selector);
            SelectTemplate(null);
        }

        function OnUserEmailTemplateSelect(event) {
            root = $(event.currentTarget).closest(selector);
            var templateId = $(event.currentTarget).closest(".user-email-template").data("template-id");
            SelectTemplate(templateId);
        }

        function InsertDynamicFieldIntoActiveTabEmailBodyText(event) {
            root = $(event.currentTarget).closest(selector);
            var field = $(event.currentTarget).text();
            var richTextArea = root.find(".tab-user-email-template-form .tab:visible .rich-text-area");
            var id = richTextArea.attr("id");
            var editor = CKEDITOR.instances[id];
            editor.insertText(field);
        }

        function OnUserEmailTemplateSubmit(event) {
            event.preventDefault();
            root = $(event.currentTarget).closest(selector);
            var form = $(event.currentTarget);
            UpdateRichTextAreaDataFromCkeditor(form);
            if (IsUserEmailTemplateFormValid(form)) {
                $("#myUserEmailTemplatesInvalid").hide();
                UNGM.Throbber.Push();
                var url = form.attr("action");
                var data = form.serialize();
                $.post(url, data)
                    .done(function(templateId) {
                        var tabIndex = root.find(".tab-user-email-template-index");
                        tabIndex.data("reload", true);
                        SelectTemplate(templateId);
                    })
                    .always(function() { UNGM.Throbber.Pop(); });
            } 
            else {
                $("#myUserEmailTemplatesInvalid").show();
            }
        }

        function OnPreviewSelect(event) {
            var index = $(event.currentTarget).index();
            root = $(event.currentTarget).closest(selector);
            SelectPreview(index);
        }

        var callbacksOnConfirmed = [];
        function Confirm() {
            $.each(callbacksOnConfirmed, function (_, callback) { callback(selectedTemplateId, attachedDocumentIds); });
        }
        function AttachOnConfirmed(callback) {
            callbacksOnConfirmed.push(callback);
        }

        var selectedTemplateId = null;
        function SelectTemplate(templateId) {
            if (templateId === null) {
                selectedTemplateId = templateId;
                LoadCreateForm()
                DisableAttachments();
                DisablePreview();
            } else {
                if (selectedTemplateId !== templateId) {
                    selectedTemplateId = templateId;
                    LoadUpdateForm();
                    EnableAttachments();
                }
                EnablePreview();
            }
            UpdateSelectButton();
        }
        function LoadCreateForm() { return LoadForm("Shared/UserEmailTemplate/CreateForm?UserEmailTemplateArea=" + area + "&UserEmailTemplateType=" + type); }
        function LoadUpdateForm() { return LoadForm("Shared/UserEmailTemplate/UpdateForm?id=" + selectedTemplateId +"&UserEmailTemplateArea=" + area + "&UserEmailTemplateType=" + type); }
        function LoadForm(url) {
            var head = root.find(".tab-header-user-email-template-form");
            var body = root.find(".tab-user-email-template-form");
            body.data("url", url);
            body.data("reload", true);
            head.removeClass("tabDisabled");
            head.click();
        }

        var attachedDocumentIds = [];
        function EnableAttachments() {
            var head = root.find(".tab-header-user-email-template-attachments");
            UNGM.DocumentPickerCallbacks.documentPicked = AttachDocument;
            UNGM.DocumentPickerCallbacks.documentRemoved = DetachDocument;
            head.removeClass("tabDisabled");
        }
        function DisableAttachments() {
            var head = root.find(".tab-header-user-email-template-attachments");
            head.addClass("tabDisabled");
        }
        function AttachDocument(document) {
            attachedDocumentIds.push(document.Id);
        }
        function DetachDocument(documentId) {
            var index = $.inArray(documentId, attachedDocumentIds);
            attachedDocumentIds.splice(index, 1);
        }

        function EnablePreview() {
            var head = root.find(".tab-header-user-email-template-preview");
            var body = root.find(".tab-user-email-template-preview");
            var url = "Shared/UserEmailTemplate/Preview/" + selectedTemplateId;

            if (options.InstitutionalOrganizationId) { url += "?InstitutionalOrganizationId=" + options.InstitutionalOrganizationId; }
            if (options.BusinessSeminarId && options.VendorId) { url += "?BusinessSeminarId=" + options.BusinessSeminarId + "&VendorId=" + options.VendorId; }
            body.data("url", url);
            body.data("reload", true);
            head.removeClass("tabDisabled");
        }
        function DisablePreview() {
            var head = root.find(".tab-header-user-email-template-preview");
            head.addClass("tabDisabled");
        }

        function OnTabContentLoaded(tab) {
            UpdateSelectButton();
            ReplaceRichTextAreaWithCkeditor(tab);
            SelectFirstPreview(tab);
        }

        function UpdateSelectButton() {
            root.find(".user-email-template-select").show();
            root.find("[data-template-id='" + selectedTemplateId + "'] .user-email-template-select").hide();
        }

        function ReplaceRichTextAreaWithCkeditor(tab) {
            tab.find(".rich-text-area").each(function (_, element) {
                var richTextArea = $(element);
                var id = richTextArea.attr("id");
                var editor = CKEDITOR.instances[id];
                CKEDITOR.replace(id, { toolbar: UNGM.standardToolbarWithLinks });
            });
        }

        function UpdateRichTextAreaDataFromCkeditor(form) {
            form.find(".rich-text-area").each(function (_, element) {
                var richTextArea = $(element);
                var id = richTextArea.attr("id");
                var editor = CKEDITOR.instances[id];
                richTextArea.val(editor.getData());
            });
        }

        function SelectFirstPreview(tab) {
            $(tab).find(".user-email-template-preview-contact").first().click();
        }

        function IsUserEmailTemplateFormValid(form) {
            var texts = ["TextEn", "TextFr", "TextEs", "TextPt"];
            for (var i = 0; i < texts.length; ++i) {
                var text = texts[i];
                var hasSubject = form.find("[name='" + text + ".SubjectText']").val().trim() !== "";
                var hasBody = form.find("[name='" + text + ".BodyText']").val().trim() !== "";
                if (hasSubject && hasBody) { return true; }
            }
            return false;
        }

        function SelectPreview(index) {
            root.find(".user-email-template-preview-contact").removeClass("successnormalpadding").eq(index).addClass("successnormalpadding");
            root.find(".user-email-template-preview-content").hide().eq(index).show();
        }

        var instance = {
            AttachOnConfirmed: AttachOnConfirmed
        };

        return hash.value(root, instance);
    }
})(window.UNGM.UserEmailTemplate = window.UNGM.UserEmailTemplate || {});
;
"use strict";

(function (namespace, undefined) {

    namespace.Interpolate = InterpolateFactory;

    var INTERPOLATION_START_SYMBOL = "{{";
    var INTERPOLATION_BODY = "((.|[\n\r])*?)";
    var INTERPOLATION_END_SYMBOL = "}}";
    var INTERPOLATION_PATTERN = INTERPOLATION_START_SYMBOL + INTERPOLATION_BODY + INTERPOLATION_END_SYMBOL;
    var INTERPOLATION_REGEX = new RegExp(INTERPOLATION_PATTERN, "g");
    var INTERPOLATION_ESCAPED_START_SYMBOL = CreateEscapedRegExp(INTERPOLATION_START_SYMBOL.replace(/(.)/g, "\\$1"), "g");
    var INTERPOLATION_ESCAPED_END_SYMBOL = CreateEscapedRegExp(INTERPOLATION_END_SYMBOL.replace(/(.)/g, "\\$1"), "g");

    function InterpolateFactory(text, filters) {
        function Interpolate(object) {
            return text.replace(INTERPOLATION_REGEX, function (match, body) {
                try {
                    var unescapedBody = Unescape(body);
                    var interpolationFunction = ParseInterpolationFunction(unescapedBody);
                    return interpolationFunction(object);
                } catch (e) {
                    console.warn(e);
                    return match;
                }
            });
        }

        var _expressions = null
        Interpolate.expressions = function () {
            if (_expressions !== null) { return _expressions }
            _expressions = []
            for (var match = INTERPOLATION_REGEX.exec(text); match !== null; match = INTERPOLATION_REGEX.exec(text)) {
                var expression = $.trim(match[1].split('|')[0])
                _expressions.push(expression)
            }
            return _expressions
        }

        function ParseInterpolationFunction(body) {
            var tokens = $.trim(body).split(/\s*[|]\s*/);
            var expression = tokens[0];
            var filterTokens = tokens.slice(1);
            var filterFunctions = $.map(filterTokens, function (token) { return ParseInterpolationFilterFunction(token); });
            return function (object) {
                var value = EvaluateExpression(expression, object);
                $.each(filterFunctions, function (index, filterFunction) {
                    value = filterFunction(value);
                });
                return value;
            };
        }

        function ParseInterpolationFilterFunction(body) {
            var tokens = $.trim(body).split(/\s*[:]\s*/);
            var filterName = tokens[0]
            var filter = (filters || {})[filterName] || UNGM.ViewModel.Filter(filterName);
            if (typeof (filter) === 'function') {
                return function (value) {
                    return filter.apply(null, [value].concat(tokens.slice(1)));
                };
            } else {
                console.error('filter not found: ' + filterName);
                return function (value) { return value; };
            }
        }

        function EvaluateExpression(expression, object) {
            if (object.evaluate !== undefined) { return object.evaluate(expression) }

            try {
                expression = expression.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
                expression = expression.replace(/^\./, '');           // strip a leading dot
                var tokens = expression.split('.');
                var node = object;
                $.each(tokens, function (_index, token) {
                    if (token in node) {
                        node = node[token];
                    } else {
                        throw "token not found: " + expression;
                    }
                });
                return node;
            } catch (e) {
                console.warn(e);
                return undefined;
            }
        }

        function Unescape(body) {
            return body.replace(INTERPOLATION_ESCAPED_START_SYMBOL, INTERPOLATION_START_SYMBOL)
                .replace(INTERPOLATION_ESCAPED_END_SYMBOL, INTERPOLATION_END_SYMBOL);
        }

        return Interpolate
    }

    function CreateEscapedRegExp(text, option) {
        return new RegExp(text.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"), option);
    }

})(window.UNGM.ViewModel = window.UNGM.ViewModel || {});;
(function (namespace, undefined) {

    //
    // Account Registration
    //

    namespace.AccountRegistration = function (parameter) {
        this.FormSelector = parameter.FormSelector;
        this.EnglishNameSelector = parameter.EnglishNameSelector;
        this.CountrySelector = parameter.CountrySelector;
        this.TypeSelector = parameter.TypeSelector;
        this.TypeCommentSelector = parameter.TypeCommentSelector;
        this.TypeCommentHolder = parameter.TypeCommentHolder;
        this.TypeAllowingFreeText = parameter.TypeAllowingFreeText;
        this.PasswordSelector = parameter.PasswordSelector;
        this.ConfirmPasswordSelector = parameter.ConfirmPasswordSelector;
        this.ReferralSourceSelector = parameter.ReferralSourceSelector;
        this.ReferralSourceComent = parameter.ReferralSourceComent;
        this.CheckingAvailabilityHolder = parameter.CheckingAvailabilityHolder;
        this.CheckingAvailabilityHolderSelector = $(this.CheckingAvailabilityHolder);
        this.InstitutionalOrganizationExistsHolder = parameter.InstitutionalOrganizationExistsHolder;
        this.InstitutionalOrganizationExistsHolderSelector = $(this.InstitutionalOrganizationExistsHolder);
        this.LinkIsCombinationNameAndCountryUnique = parameter.LinkIsCombinationNameAndCountryUnique
        this.LinkActivatePending = parameter.LinkActivatePending;
        this.IsNameAndCountryValid = false;

        this.PasswordSelector.keyup(UNGM.ManageAccount.onPasswordKeyUp);
        this.TypeSelector.change(this.OnTypeChange().bind(this));
        this.EnglishNameSelector.focusout(this.IsCombinationOfInstitutionalOrganizationNameAndCountryUnique().bind(this));
        this.CountrySelector.change(this.IsCombinationOfInstitutionalOrganizationNameAndCountryUnique().bind(this));
        this.FormSelector.submit(this.ValidateForm().bind(this));
        UNGM.GoogleRecaptcha.successCallback = this.SubmitForm();
    }

    namespace.AccountRegistration.prototype.OnTypeChange = function () {
        return function (e) {
            var sender = $(e.currentTarget);
            var itemValue = sender.find('option:selected').val();
            if (itemValue == this.TypeAllowingFreeText) {
                this.TypeCommentHolder.show();
            } else {
                this.TypeCommentHolder.hide();
                this.TypeCommentSelector.val("");
            }
        }
    }

    namespace.AccountRegistration.prototype.IsCombinationOfInstitutionalOrganizationNameAndCountryUnique = function () {
        return function (e) {
            $this = this;
            var name = this.EnglishNameSelector.val();
            var countryId = this.CountrySelector.val();
            if (!this.EnglishNameSelector.valid()
                || !this.CountrySelector.valid()
                || name == undefined
                || countryId == undefined
                || name.length <= 0
                || countryId.length <= 0) {
                return false;
            }
            var sender = $(e.currentTarget);

            sender.siblings(this.CheckingAvailabilityHolder).show();

            $.ajax({
                url: UNGM.siteRoot + this.LinkIsCombinationNameAndCountryUnique,
                data: {
                    name: name,
                    countryId: countryId
                },
                type: 'GET',
                contentType: 'application/json',
                success: function (response) {
                    sender.siblings($this.CheckingAvailabilityHolder).hide()
                    if (response == false) {
                        UNGM.Validation.markElementInvalid($this.CountrySelector);
                        UNGM.Validation.markElementInvalid($this.EnglishNameSelector);
                        $this.IsNameAndCountryValid = false;
                        sender.siblings($this.InstitutionalOrganizationExistsHolder).show();
                    } else {
                        UNGM.Validation.markElementValid(sender);
                        $this.IsNameAndCountryValid = true;
                        $this.InstitutionalOrganizationExistsHolderSelector.hide();
                    }
                }
            });
        }
    }

    namespace.AccountRegistration.prototype.ValidateForm = function () {
        return function (e) {
            $this = this;
            e.preventDefault();
            var form = $(e.currentTarget);

            $this.EnglishNameSelector.focusout();
           
            if (!UNGM.EmailValidation.verified
                || !this.IsNameAndCountryValid
                || !form.valid()) {
                return false;
            }

            grecaptcha.execute();
            
            return true;
        }
    }

    namespace.AccountRegistration.prototype.SubmitForm = function () {
        return function (e) {
            $this = this;
            var form = $("form");

            UNGM.throbOver();

            $.ajax({
                url: form.attr("action"),
                type: 'POST',
                data: form.serialize(),
                success: function () {
                    window.location = UNGM.siteRoot + "Account/Registration/ActivatePending";
                }
            });
        }
    }
}(window.UNGM.InstitutionalOrganization = window.UNGM.InstitutionalOrganization || {}));;
window.UNGM.InstitutionalOrganization = window.UNGM.InstitutionalOrganization || {};
window.UNGM.InstitutionalOrganization.Registration = {
    isSubmissionStatusRegistered: null,
    init: function () {
        $(".tabs").off("submit").on("submit", "form", UNGM.InstitutionalOrganization.Registration.onFormSubmitted);
        $("#btnSubmitRegistration").off("click").on("click", UNGM.InstitutionalOrganization.Registration.submitRegistration);
    },
    onFormSubmitted: function (e) {
        e.preventDefault();
        var form = $(e.currentTarget);

        if (!form.valid() || form.find(".institutionalOrganizationExists:visible").length || form.find("#emailExists:visible").length) {
            return false;
        }

        if (!form.hasClass("contactForm") && UNGM.InstitutionalOrganization.Registration.isSubmissionStatusRegistered === true) {
            var sensitiveElements = form.find("#EnglishName, #InstitutionalOrganizationType, #CountryId, #StatuteDocumentId");
            for (i = 0; i < sensitiveElements.length; i++) {
                var element = $(sensitiveElements[i]);
                var oldValue = element.siblings(".oldValue, .OldDocumentId").val();
                var newValue = element.val();
                if (oldValue !== newValue) {
                    $.confirmWithNoCallback(
                        UNGM.InstitutionalOrganization.Registration.savingConfirmationTitle,
                        UNGM.InstitutionalOrganization.Registration.savingConfirmationMessage,
                        UNGM.InstitutionalOrganization.Registration.savingConfirmationBtnConfirm,
                        UNGM.InstitutionalOrganization.Registration.savingConfirmationBtnCancel,
                        function () { UNGM.InstitutionalOrganization.Registration.submitForm(form); },
                        function () { UNGM.InstitutionalOrganization.Registration.reloadTab(element.parents(".tab")); }
                    );
                    return false;
                }
            }
        }

        UNGM.InstitutionalOrganization.Registration.submitForm(form);
    },
    submitForm: function (form) {
        UNGM.Throbber.Push();
        $.ajax({
            url: form.attr("action"),
            type: "POST",
            data: form.serialize(),
            success: function (response) {
                if (form.hasClass("contactForm")) {
                    $("#dialog").dialog('destroy');
                    UNGM.InstitutionalOrganization.Registration.reloadTab("#ContactsContent");
                }
                // Needed due to the uniqueness constraint of the combination of country and english name
                if (form.attr('id') === "frmGeneralInfo") {
                    UNGM.InstitutionalOrganization.Registration.reloadTab("#CountryContent");
                }
                else if (form.attr('id') === "frmCountry") {
                    UNGM.InstitutionalOrganization.Registration.reloadTab("#GeneralInfoContent");
                }

                UNGM.InstitutionalOrganization.Registration.updateCompletion(response);
            },
            complete: UNGM.Throbber.Pop
        });
    },
    updateCompletion: function (response) {
        UNGM.InstitutionalOrganization.Registration.isSubmissionStatusRegistered = response.IsSubmissionStatusRegistered;
        if (response.CanSubmit) {
            $("#divSubmitRegistration").show();
            $.confirm(
                UNGM.InstitutionalOrganization.Registration.submitRegistrationTitle,
                UNGM.InstitutionalOrganization.Registration.submitRegistrationMessage,
                UNGM.InstitutionalOrganization.Registration.submitRegistrationBtnSubmit,
                UNGM.InstitutionalOrganization.Registration.submitRegistrationBtnSubmitLater,
                UNGM.InstitutionalOrganization.Registration.submitRegistration
            );
        }
        else {
            $("#divSubmitRegistration").hide();
        }
        UNGM.markTabComplete("#GeneralInfo", response.IsGeneralInfoComplete);
        UNGM.markTabComplete("#Address", response.IsAddressComplete);
        UNGM.markTabComplete("#Country", response.IsCountryComplete);
        UNGM.markTabComplete("#Contacts", response.IsContactsComplete);
        UNGM.markTabComplete("#SupportingInfo", response.IsSupportingInfoComplete);
    },
    getForm: function (e) {
        var button = $(e.currentTarget);
        UNGM.Throbber.Push();
        $.ajax({
            url: UNGM.siteRoot + button.data('form-url'),
            type: 'POST',
            success: UNGM.InstitutionalOrganization.Registration.onFormReceived,
            complete: UNGM.Throbber.Pop
        });
    },
    onFormReceived: function (data) {
        $("#dialog").html(data);
        $("#dialog").dialog({
            modal: true,
            title: $(data).data("title"),
            width: $(window).width() * 0.5,
            height: $(window).height() * 0.8,
            hide: { effect: 'fade', duration: 100 },
            show: { effect: 'fade', duration: 100 },
            open: function () {
                UNGM.markRequiredFields();
                $("#dialog").off("submit").on("submit", "form", UNGM.InstitutionalOrganization.Registration.onFormSubmitted);
            }
        });
    },
    deactivateContact: function (event) {
        $.confirm(UNGM.InstitutionalOrganization.Registration.deactivateContactTitle,
            UNGM.InstitutionalOrganization.Registration.deactivateContactMessage,
            UNGM.InstitutionalOrganization.Registration.deactivateContactYesButton,
            UNGM.InstitutionalOrganization.Registration.deactivateContactNoButton,
            function () {
                UNGM.Throbber.Push();
                $.post(UNGM.siteRoot + $(event.currentTarget).data("url"))
                    .done(function (result) {
                        if (result.RedirectToHome) {
                            window.location.href = UNGM.siteRoot;
                        } else {
                            UNGM.InstitutionalOrganization.Registration.reloadTab("#ContactsContent");
                        }
                    })
                    .always(function () { UNGM.Throbber.Pop(); });
            });
    },
    cancelInvitation: function (event) {
        $.confirm(UNGM.InstitutionalOrganization.Registration.cancelInvitationTitle,
            UNGM.InstitutionalOrganization.Registration.cancelInvitationMessage,
            UNGM.InstitutionalOrganization.Registration.cancelInvitationYesButton,
            UNGM.InstitutionalOrganization.Registration.cancelInvitationNoButton,
            function () {
                UNGM.Throbber.Push();
                $.post(UNGM.siteRoot + $(event.currentTarget).data("url"))
                    .done(function () { UNGM.InstitutionalOrganization.Registration.reloadTab("#ContactsContent"); })
                    .always(function () { UNGM.Throbber.Pop(); });
            });
    },
    resendInvitation: function (event) {
        $.confirm(UNGM.InstitutionalOrganization.Registration.resendInvitationTitle,
            UNGM.InstitutionalOrganization.Registration.resendInvitationMessage,
            UNGM.InstitutionalOrganization.Registration.resendInvitationYesButton,
            UNGM.InstitutionalOrganization.Registration.resendInvitationNoButton,
            function () {
                UNGM.Throbber.Push();
                $.post(UNGM.siteRoot + $(event.currentTarget).data("url"))
                    .done(function () { UNGM.InstitutionalOrganization.Registration.reloadTab("#ContactsContent"); })
                    .always(function () { UNGM.Throbber.Pop(); });
            });
    },
    reloadTab: function (elementSelector) {
        var tab = $(elementSelector);
        UNGM.Throbber.Push();
        $.ajax({
            url: UNGM.siteRoot + tab.data('url'),
            type: 'GET',
            success: function (data) {
                tab.html(data);
                if (tab.find("form").length) {
                    UNGM.Validation.initForElement(tab.find("form"));
                    UNGM.markRequiredFields();
                }
            },
            complete: UNGM.Throbber.Pop
        });

    },
    submitRegistration: function () {
        UNGM.Throbber.Push();
        $.ajax({
            url: UNGM.siteRoot + "/InstitutionalOrganization/Registration/SubmitRegistration",
            type: 'POST',
            success: function (response) {
                $("#divSubmittedSuccessMessage").show();
                UNGM.InstitutionalOrganization.Registration.updateCompletion(response);
                UNGM.InstitutionalOrganization.Registration.reloadTab("#GeneralInfoContent");                
                UNGM.InstitutionalOrganization.Registration.reloadTab("#CountryContent");
            },
            complete: UNGM.Throbber.Pop
        });
    },
    isCombinationOfEnglishNameAndCountryUnique: function (e) {
        var sender = $(e.currentTarget);
        var name = sender.closest("form").find("#EnglishName").val();
        var countryId = sender.closest("form").find("#CountryId").val();
        var institutionalOrganizationId = sender.closest("form").find("#Id").val();

        if (!sender.valid() || sender.val().length <= 0) {
            return false;
        }
        sender.siblings(".checkingAvailability").show();

        $.ajax({
            url: UNGM.siteRoot + "InstitutionalOrganization/Registration/IsCombinationOfInstitutionalOrganizationNameAndCountryUnique",
            data: {
                name: name,
                countryId: countryId,
                institutionalOrganizationId: institutionalOrganizationId
            },
            type: 'GET',
            contentType: 'application/json',
            success: function (response) {
                sender.siblings(".checkingAvailability").hide();
                if (response == false) {
                    UNGM.Validation.markElementInvalid(sender);
                    sender.siblings(".institutionalOrganizationExists").show();
                } else {
                    UNGM.Validation.markElementValid(sender);
                    $(".institutionalOrganizationExists").hide();
                }
            }
        });
    }
};
"use strict";

(function (namespace, undefined) {

    namespace.MultipleAgencyPicker = MultipleAgencyPickerFactory;

    var instances = {};

    function MultipleAgencyPickerFactory(params) {

        if (instances[params.Id] !== undefined) { return instances[params.Id]; }

        var select = $("#" + params.Id).selectToAutocomplete({ "copy-attributes-to-text-field": true });
        var textAutocomplete = select.siblings("input[type='text']");
        var selectedAgencies = select.siblings(".selected-agencies");
        var allAgenciesLinks = select.siblings(".all-agencies-links");
        var selectedAgenciesInfos = [];

        textAutocomplete.addClass("ignore-on-serialize");
        textAutocomplete.addClass("ignore-on-dom-events");

        textAutocomplete.on("autocompleteselect", function (event, ui) {
            var agencyId = ui.item["real-value"];
            if (agencyId != "" && !IsAgencySelected(agencyId)) { InsertSelectedAgency(agencyId , "true"); }
        });

        textAutocomplete.on("autocompleteclose", function (event, ui) {
            textAutocomplete.val("");
        });

        selectedAgencies.on("click", ".selected-agency-remove", function (event) {
            var agencyId = $(event.currentTarget).parent(".selected-agency").data("agency-id");
            RemoveSelectedAgency(agencyId);
        });

        if (params.IsWithSelectAllAndRemoveAllLinks) {
            allAgenciesLinks.on("click", ".select-all-agencies", SelectAllAgencies);
            allAgenciesLinks.on("click", ".remove-all-agencies", RemoveAllAgencies);
        }

        function IsAgencySelected(agencyId) {
            return $.grep(selectedAgenciesInfos, function (info) { return info.agencyId == agencyId; }).length !== 0;
        }

        function InsertSelectedAgency(agencyId, removable) {
            var agencyAbbreviation = select.children("option[value='" + agencyId + "']").text();
            selectedAgenciesInfos.push({ agencyId: agencyId, agencyAbbreviation: agencyAbbreviation, removable: removable });
            selectedAgenciesInfos.sort(function (item1, item2) { return item1.agencyAbbreviation.localeCompare(item2.agencyAbbreviation); });
            RefreshSelectedAgencies();
            select.triggerHandler("change");
        }

        function RemoveSelectedAgency(agencyId) {
            selectedAgenciesInfos = $.grep(selectedAgenciesInfos, function (info) { return info.agencyId != agencyId; });
            selectedAgenciesInfos.sort(function (item1, item2) { return item1.agencyAbbreviation.localeCompare(item2.agencyAbbreviation); });
            RefreshSelectedAgencies();
            select.trigger("change");
        }

        function RemoveAllAgencies() {
            selectedAgenciesInfos = [];
            RefreshSelectedAgencies();
            select.trigger("change");
        }

        function SelectAllAgencies() {
            selectedAgenciesInfos = [];
            select.children("option:not([value=''])").each(function () {
                selectedAgenciesInfos.push({ agencyId: $(this).val(), agencyAbbreviation: $(this).text() });
            });
            RefreshSelectedAgencies();
            select.trigger("change");
        }

        function RefreshSelectedAgencies() {
            selectedAgencies.empty();
            $.each(selectedAgenciesInfos, function (_, info) { selectedAgencies.append(CreateSelectedAgency(info.agencyId, info.agencyAbbreviation, info.removable)); });
        }

        function CreateSelectedAgency(agencyId, agencyAbbreviation, removable) {
            if (removable == "true") {
                return $("<div class='selected-agency' data-agency-id='" + agencyId + "'> \
                              <input type='hidden' name='" + params.Name + "' value='" + agencyId + "'>\
                              <span>" + agencyAbbreviation + "</span> \
                              <a class='selected-agency-remove' href='javascript:void(0);' style='display: inline;'>(remove)</a> \
                          </div>");
            } else {
                return $("<div class='selected-agency' data-agency-id='" + agencyId + "'> \
                              <input type='hidden' name='" + params.Name + "' value='" + agencyId + "'>\
                              <span>" + agencyAbbreviation + "</span> \
                          </div>");
            }
        }

        var instance = {
            InsertSelectedAgencies: InsertSelectedAgencies,
            ClearSelectedAgencies: ClearSelectedAgencies,
            GetSelectedAgencyIds: GetSelectedAgencyIds
        };

        function InsertSelectedAgencies(agencyIds) {
            $.each(agencyIds, function (_, agencyId) {
                InsertSelectedAgency(agencyId, params.Removable);
            });
        }

        function ClearSelectedAgencies() {
            textAutocomplete.val("");
            selectedAgenciesInfos = [];
            RefreshSelectedAgencies();
        }

        function GetSelectedAgencyIds() {
            var selectedAgencyIds = [];
            $.each(selectedAgenciesInfos, function (_, info) { selectedAgencyIds.push(info.agencyId); });
            return selectedAgencyIds;
        }

        return instances[params.Id] = instance;
    }
})(window.UNGM.EditorTemplates = window.UNGM.EditorTemplates || {});
;
'use strict';

(function(namespace, undefined) {

    namespace.Dialog = DialogFactory;

    function DialogFactory() {
        var dialog = {
            open: open
        }

        var _element = $('<div></div>');

        function open(url, options) {
            UNGM.Throbber.Push();
            options = options || {}
            $.get(url, options.data)
                .done(function(res) {
                    _element.html(res);
                    _element.dialog({
                            title: options.title,
                            modal: true,
                            width: options.width,
                            height: options.height,
                            hide: { effect: 'fade', duration: 100 },
                            show: { effect: 'fade', duration: 100 },
                            close: function(event, ui) { $(this).dialog('destroy').remove() }
                        })
                        .css("maxHeight", $(window).height() * 0.8);
                })
                .error(function (error) {
                    console.error(error);
                })
                .always(UNGM.Throbber.Pop);
        }

        return dialog;
    }

})((window.UNGM.Util = window.UNGM.Util || {}))
;
'use strict';

(function (namespace, undefined) {

    namespace.PdfDialog = PdfDialogFactory

    function PdfDialogFactory() {
        var pdfDialog = {
            open: open,
            errorMessage: errorMessage
        }

        var _element = $('<div></div>')
        var _errorMessage = "This document is not available."

        function open(pdfUrl, options) {
            if ((options || {}).skipCheck === true) {
                renderPdf(pdfUrl);
                openDialog();
            } else {
                UNGM.Throbber.Push();
                $.ajax({
                    url: pdfUrl,
                    type: 'HEAD',   // Gets only size of the header, not the actual file
                    global: false   // Disable global events, as UNGM.ajaxErrorHandler
                })
                .error(renderError)
                .done(function () { renderPdf(pdfUrl); })
                .always(function () { openDialog((options || {}).title); })
                .always(UNGM.Throbber.Pop)
            }
        }

        function errorMessage(message) {
            _errorMessage = message || _errorMessage
        }

        function renderError() {
            _element.empty()
            _element.html('<div class="info">' + _errorMessage + '</div>')
        }

        function renderPdf(pdfUrl) {
            _element.empty()
            var pdfViewer = UNGM.siteRoot + "Scripts/PDF/web/viewer.html?file="
            var iframeSrc = 'src="' + pdfViewer + pdfUrl + '"'
            var iframeStyle = 'style="width: ' + 0.6 * $(window).width() + 'px; height: ' + 0.7 * $(window).height() + 'px;"'
            var iframe = '<iframe ' + iframeSrc + ' ' + iframeStyle + '></iframe>'
            _element.html(iframe)
        }

        function openDialog(title) {
            _element.dialog({
                modal: true,
                title: title,
                width: 'auto',
                height: 'auto',
                hide: { effect: 'fade', duration: 100 },
                show: { effect: 'fade', duration: 100 }
            })
        }


        return pdfDialog
    }

})((window.UNGM.Util = window.UNGM.Util || {}))
;
//
// DocumentPickerDialog
//

(function (namespace, undefined) {

    namespace.DocumentPickerDialog = function (selector, area) {
        if (namespace.DocumentPickerDialog.prototype._singletonInstance) {
            return namespace.DocumentPickerDialog.prototype._singletonInstance;
        }
        namespace.DocumentPickerDialog.prototype._singletonInstance = this;

        var $this = this;
        this.element = $(selector);
        this.area = area;
        this.pickers = [];

        this.element.load(UNGM.siteRoot + this.area + "/Documents/List?isPicker=true", function () {
            UNGM.DocumentPicker = UNGM.DocumentPicker || {};
            UNGM.DocumentPicker.cofi = 0;
            UNGM.initCollapsables();
            $(".btnClose").click(function () { $this.element.dialog("close") });
            $.each($this.pickers, function (index, picker) { if (!picker.isMultipleDocumentPicker) { picker.Refresh(); } });
        });
    }

    namespace.DocumentPickerDialog.prototype.AttachDocumentPicker = function (picker) {
        this.pickers.push(picker);
    }

    namespace.DocumentPickerDialog.prototype.Open = function (picker) {
        var $this = this;
        UNGM.DocumentPicker.pickDocumentClicked = function (event) {
            var documentId = $(event.currentTarget).data("documentid");

            if (picker.isMultipleDocumentPicker) {
                var document = {
                    Id: documentId,
                    Filename: $(event.currentTarget).data("documentfilename"),
                    Description: $(event.currentTarget).data("description")
                }
                picker.AddDocument(document);
                $this.DisableUnavailableDocuments(picker.filesUsed);
            }
            else {
                $this.element.dialog("close");
                picker.ValidateAndSetValue(documentId);
            }
        };

        $this.element.dialog({
            modal: true,
            title: "Select file",
            width: $(window).width() * 0.8,
            height: $(window).height(),
            position: { my: 'top', at: 'top+' + ($(".top-bar") + 5), of: window },
            hide: { effect: 'fade', duration: 100 },
            show: { effect: 'fade', duration: 100 },
            open: function () {
                UNGM.initDatePickersIn($this.element);
                if (UNGM.Documents.initPagination) {
                    UNGM.Documents.initPagination();
                    UNGM.Documents.init();
                    $this.DisableUnavailableDocuments(picker.filesUsed);
                }
            }
        });
    }

    namespace.DocumentPickerDialog.prototype.GetFileName = function (id) {
        var element = this.element.find("[data-documentid='" + id + "']");
        return element.data('documentfilename');
    }

    namespace.DocumentPickerDialog.prototype.GetFilePath = function (id) {
        return UNGM.siteRoot + this.area + "/Documents/Download?docId=" + id;
    }

    namespace.DocumentPickerDialog.prototype.DisableUnavailableDocuments = function (filesUsed) {
        $(this.element).find(".btn.use").prop("disabled", false);
        if (filesUsed) {
            for (i = 0; i < filesUsed.length; i++) {
                var btnRow = $(this.element).find(".btn.use[data-documentid='" + filesUsed[i].Id + "']").parents(".tableRow");
                btnRow.find(".btn.use").prop("disabled", true);
                btnRow.find(":checkbox").prop("checked", false);
            }
        }
    }

}(window.UNGM.SharedScripts = window.UNGM.SharedScripts || {}));
;
//
// DocumentPicker
//

(function (namespace, undefined) {

    namespace.DocumentPicker = function (selector, errorMessage, dialog, resource) {
        this.element = $(selector);
        this.dialog = dialog;
        this.hidden = $("<input type='hidden'></input>");
        this.select = $("<div><a class='lnkShowDocumentSearch' href='javascript:;'>" + resource.select + "</a></div>");
        this.selected = $("<div style='display:none;'></div>");
        this.filename = $("<a class='lnkShowDocument' href='javascript:;'></a>");
        this.remove = $("<a class='lnkRemoveDocument' href='javascript:;'>" + resource.remove + "</a>");
        this.ErrorMessage = new UNGM.SharedScripts.ErrorMessage(errorMessage);

        this.element.append(this.hidden);
        this.element.append(this.select);
        this.element.append(this.selected);
        this.selected.append(this.filename);
        this.selected.append(this.remove);
        this.dialog.AttachDocumentPicker(this);

        this.select.click(this.Select(this));
        this.remove.click(this.Clear(this));
    }

    namespace.DocumentPicker.prototype.val = function (id, filename, callbackUrl) {
        if (callbackUrl) {
            this.CallbackUrl = callbackUrl;
        }

        if (typeof (id) !== 'undefined' && id != 0) {
            this.hidden.val(id);
            this.ErrorMessage.Hide();
            if (id) {
                this.filename.html(filename || this.dialog.GetFileName(id));
                this.filename.attr('href', this.dialog.GetFilePath(id));
                this.selected.fadeIn();
            } else {
                this.selected.fadeOut();
            }
            // hide the select link if file is selected
            this.ToggleSelectLink(!this.IsNotComplete());
        }

        return this.hidden.val();
    }

    namespace.DocumentPicker.prototype.Select = function ($this) {
        return function (event) {
            $this.dialog.Open($this);
        };
    }

    namespace.DocumentPicker.prototype.Clear = function ($this) {
        return function (event) {
            var removedDocumentId = $this.val();
            if ($this.CallbackUrl && ($this.CallbackUrl.OnRemovedUrl || $this.CallbackUrl.OnPreChangeConfirmationCallback || $this.CallbackUrl.OnChangeCallback)) {
                if ($this.CallbackUrl.OnRemovedUrl) {
                    UNGM.Throbber.Push();
                    $.ajax({
                        url: UNGM.siteRoot + $this.CallbackUrl.OnRemovedUrl,
                        type: 'POST',
                        success: function () {
                            $this.val(null);
                        },
                        error: function (xhr, err) {
                            alert("readyState: " + xhr.readyState + "\nstatus: " + xhr.status + "\nresponseText: " + xhr.responseText);
                        },
                        complete: function () {
                            UNGM.Throbber.Pop();
                        }
                    });
                }

                if ($this.CallbackUrl.OnPreChangeConfirmationCallback) {
                    $this.CallbackUrl.OnPreChangeConfirmationCallback(function () {
                        $this.val(null);
                        $this.CallbackUrl.OnChangeCallback.call();
                    });
                }
                else if ($this.CallbackUrl.OnChangeCallback) {
                    $this.val(null);
                    $this.CallbackUrl.OnChangeCallback.call();
                }
            }
            else {
                $this.val(null);
            }
        };
    }

    namespace.DocumentPicker.prototype.IsNotComplete = function () {
        return !this.hidden.val() || this.hidden.val() == "0";
    }

    namespace.DocumentPicker.prototype.ValidateAndSetValue = function (documentId) {
        var $this = this;
        if ($this.CallbackUrl) {
            if ($this.CallbackUrl.ValidationUrl) {
                UNGM.Throbber.Push();
                $this.ErrorMessage.Hide();
                $.ajax({
                    url: UNGM.siteRoot + $this.CallbackUrl.ValidationUrl.replace("{documentId}", documentId),
                    type: 'GET',
                    success: function (data) {
                        var response = JSON.parse(data);
                        if (response.IsDocumentValid) {
                            $this.val(documentId);
                            $this.ErrorMessage.Hide();
                        }
                        else {
                            $this.val(null);
                            $this.ErrorMessage.SetMessage(response.ErrorMessage);
                            $this.ErrorMessage.Show();
                        }
                    },
                    error: function (xhr, err) {
                        alert("readyState: " + xhr.readyState + "\nstatus: " + xhr.status + "\nresponseText: " + xhr.responseText);
                    },
                    complete: function () {
                        UNGM.Throbber.Pop();
                    }
                });
            }

            if ($this.CallbackUrl.OnSelectedUrl) {
                $.ajax({
                    url: UNGM.siteRoot + $this.CallbackUrl.OnSelectedUrl.replace("{documentId}", documentId),
                    type: 'POST',
                    success: function () {
                        $this.val(documentId);
                    },
                    error: function (xhr, err) {
                        alert("readyState: " + xhr.readyState + "\nstatus: " + xhr.status + "\nresponseText: " + xhr.responseText);
                    }
                });
            }

            if ($this.CallbackUrl.OnPreChangeConfirmationCallback) {
                $this.CallbackUrl.OnPreChangeConfirmationCallback(function () {
                    $this.val(documentId);
                    $this.CallbackUrl.OnChangeCallback.call();
                });
            }
            else if ($this.CallbackUrl.OnChangeCallback) {
                $this.val(documentId);
                $this.CallbackUrl.OnChangeCallback.call();
            }
        }
        else {
            $this.val(documentId);
        }
    }

    namespace.DocumentPicker.prototype.Refresh = function () {
        this.val(this.val());
    }

    namespace.DocumentPicker.prototype.ToggleReadonly = function (readonly) {
        this.ToggleSelectLink(readonly);
        this.ToggleRemoveLink(readonly);
    }

    namespace.DocumentPicker.prototype.ToggleSelectLink = function (readonly) {
        if (readonly || this.val() != 0) {
            this.select.hide();
        }
        else {
            this.select.show();
        }
    }

    namespace.DocumentPicker.prototype.ToggleRemoveLink = function (readonly) {
        if (readonly) {
            this.remove.hide();
        }
        else {
            this.remove.show();
        }
    }
}(window.UNGM.SharedScripts = window.UNGM.SharedScripts || {}));
;

//
// Multiple DocumentPicker
//

(function (namespace, undefined) {

    namespace.MultipleDocumentPicker = function (selector, errorMessage, dialog, resource) {
        this.element = $(selector);
        this.dialog = dialog;
        this.select = $("<div><a class='lnkShowDocumentSearch' href='javascript:;'>" + resource.select + "</a></div>");
        this.documentsList = $("<div class='docslist'></div>");
        this.isMultipleDocumentPicker = true;
        this.filesUsed = [];
        this.ErrorMessage = new UNGM.SharedScripts.ErrorMessage(errorMessage);

        this.element.append(this.select);
        this.element.append(this.documentsList);
        this.dialog.AttachDocumentPicker(this);

        this.select.click(this.Select(this));
    }

    namespace.MultipleDocumentPicker.prototype.AddDocument = function (doc) {
        if (doc) {
            this.filesUsed.push(doc);

            this.documentsList.append(this.RenderDocument(doc));
        }
    }

    namespace.MultipleDocumentPicker.prototype.RenderDocument = function (doc) {
        var elem = $("<div></div>").addClass("filterDiv").attr("data-documentid", doc.Id);
        var name = $("<span></span>").addClass("selectedDocumentFilename");
        var link = $("<a></a>").addClass("lnkShowDocument").attr("href", this.dialog.GetFilePath(doc.Id)).html(doc.Filename);
        var remove = $("<a></a>").addClass("lnkRemoveDocument").attr("data-documentid", doc.Id).attr("href", "javascript:;").html("(remove)");
        remove.on("click", this.Clear(this));

        elem.append(name).append(link).append(remove);
        return elem;
    }

    namespace.MultipleDocumentPicker.prototype.Select = function ($this) {
        return function (event) {
            $this.dialog.Open($this);
        };
    }

    namespace.MultipleDocumentPicker.prototype.Clear = function ($this) {
        return function (event) {
            var documentId = $(event.currentTarget).data("documentid");
            $(event.currentTarget).parent("div[data-documentid='" + documentId + "']").remove();
            $this.filesUsed = $.grep($this.filesUsed, function (item) { return item.Id != documentId; });
        };
    }

    namespace.MultipleDocumentPicker.prototype.IsNotComplete = function () {
        return this.filesUsed.length == 0;
    }

    namespace.MultipleDocumentPicker.prototype.GetSelectedFileIds = function () {
        return this.filesUsed.map(function (item) { return item.Id });
    }
}(window.UNGM.SharedScripts = window.UNGM.SharedScripts || {}));
;
//
// Validation Error Message
//

(function (namespace, undefined) {

    namespace.ErrorMessage = function (selector) {
        this.element = $(selector);
    }

    namespace.ErrorMessage.prototype.Toggle = function (show) {
        if (show) {
            this.Show();
        } else {
            this.Hide();
        }
    }

    namespace.ErrorMessage.prototype.Hide = function (show) {
        this.element.fadeOut();
    }

    namespace.ErrorMessage.prototype.Show = function (show) {
        this.element.fadeIn();
    }

    namespace.ErrorMessage.prototype.SetMessage = function (message) {
        this.element.html(message);
    }
}(window.UNGM.SharedScripts = window.UNGM.SharedScripts || {}));
;
'use strict';

uvm.module('UNGM.BusinessSeminar.Search', ['uvm'])
    .filter('entity_invitation_tag',
        function() {
            return function(value) {
                switch (value) {
                    case 1:
                        return "black";
                    case 2:
                        return "yellow";
                    case 4:
                        return "red";
                    case 8:
                        return "green";
                    default:
                        return "black";
                }
            }
        });;
'use strict';

uvm.module('UNGM.BusinessSeminar.Search')
    .controller('SearchController', SearchController);

SearchController.$inject = ['$http', '$delay', '$element', '$scope', '$confirm', '$stringResource'];

function SearchController($http, $delay, $element, $scope, $confirm, $stringResource) {
    var uvm = {
        config: {},
        businessSeminars: [],
        newSearch: newSearch,
        runSearch: $delay(runSearch, { preInvoke: function () { uvm.waypoint = false; } }),
        resetSearchFilters: resetSearchFilters,
        openBusinessSeminar: openBusinessSeminar,
        editBusinessSeminar: editBusinessSeminar,
        submitBusinessSeminar: submitBusinessSeminar,
        inviteVendors: inviteVendors,
        vendorAttendance: vendorAttendance,
        filter: {
            Page: 0,
            SortField: 'Id',
            SortAscending: false,
            CountryIds: [],
            StartDateFrom: '',
            StartDateTo: '',
            EndDateFrom: '',
            EndDateTo: '',
            Status: 0,
            VendorInvitationStatuses: 0
        },
        total: 0,
        waypoint: false
    };

    uvm.newSearch();

    function newSearch() {
        uvm.filter.Page = 0;
        uvm.runSearch();
    }

    function runSearch() {
        uvm.filter.CountryIds = UNGM.EditorTemplates.MultipleCountryPicker({ Id: 'CountryIds' }).GetSelectedCountryIds();
        $http.get(uvm.config.searchUrl, { data: uvm.filter })
            .done(function (result) {
                if (typeof (result) === 'string') { result = JSON.parse(result); }
                var isFirstPage = uvm.filter.Page === 0;
                uvm.filter.Page = uvm.filter.Page +1;
                uvm.businessSeminars = isFirstPage ? result.Items : uvm.businessSeminars.concat(result.Items);
                uvm.shown = uvm.businessSeminars.length;
                uvm.total = result.TotalCount;
                uvm.waypoint = uvm.businessSeminars.length < result.TotalCount;
            });
    }

    function resetSearchFilters() {
        UNGM.EditorTemplates.MultipleCountryPicker({ Id: 'CountryIds' }).ClearSelectedCountries();
        uvm.filter.SortField = 'Id';
        uvm.filter.SortAscending = false;
        uvm.filter.CountryIds = [];
        uvm.filter.StartDateFrom = '';
        uvm.filter.StartDateTo = '';
        uvm.filter.EndDateFrom = '';
        uvm.filter.EndDateTo = '';
        uvm.filter.Status = 0;
        uvm.filter.VendorInvitationStatuses = 0;
        uvm.newSearch();
    }

    function openBusinessSeminar(businessSeminarId) {
        var businessSeminar = $.grep(uvm.businessSeminars, function (businessSeminar) { return businessSeminar.Id === businessSeminarId; })[0];
        if (businessSeminar.KnowledgeCenterRelativeUrl) {
            window.open(UNGM.siteRoot + businessSeminar.KnowledgeCenterRelativeUrl, '_blank');
        } else {
            window.open(UNGM.siteRoot + 'Shared/BusinessSeminar/Detail/' + businessSeminarId, '_blank');
        }
    }

    function editBusinessSeminar($event, businessSeminarId) {
        $event.stopPropagation();
        UNGM.Throbber.Push();
        window.location.href = UNGM.siteRoot + 'InstitutionalOrganization/BusinessSeminar/Edit/' + businessSeminarId;
    }

    function submitBusinessSeminar($event, businessSeminarId) {
        $event.stopPropagation();
        $confirm({
            title: $stringResource.get('InstitutionalOrganization.BusinessSeminar.Index.SaveAndSubmitDialogTitle'),
            description: $stringResource.get('InstitutionalOrganization.BusinessSeminar.Index.SaveAndSubmitDialogMessage'),
            confirm: $stringResource.get('InstitutionalOrganization.BusinessSeminar.Index.SaveAndSubmitDialogSubmit'),
            cancel: $stringResource.get('InstitutionalOrganization.BusinessSeminar.Index.SaveAndSubmitDialogCancel')
        })
        .done(function () {
            var submitUrl = UNGM.siteRoot + 'InstitutionalOrganization/BusinessSeminar/Submit/' + businessSeminarId;
            $http.post(submitUrl, {})
                .done(function () {
                    $.each(uvm.businessSeminars, function (_index, item) {
                        if (item.Id === businessSeminarId) {
                            item.Status = 2; // Submitted
                        }
                    });
                });
        });
    }

    function inviteVendors($event, businessSeminarId) {
        $event.stopPropagation();
        UNGM.Throbber.Push();
        window.location.href = UNGM.siteRoot + 'Shared/BusinessSeminar/VendorsForInstitutionalOrganization/' + businessSeminarId;
    }

    function vendorAttendance($event, businessSeminarId) {
        $event.stopPropagation();
        UNGM.Throbber.Push();
        window.location.href = UNGM.siteRoot + 'Shared/BusinessSeminar/VendorsForInstitutionalOrganization/' + businessSeminarId;
    }

    return uvm;
};
'use strict';

uvm.module('UNGM.BusinessSeminar.Search')
    .controller('DetailController', DetailController);

DetailController.$inject = ['$element', '$http', '$scope', '$dialog', '$confirm', '$stringResource'];

function DetailController($element, $http, $scope, $dialog, $confirm, $stringResource) {

    var BUSINESS_SEMINAR_ONE_TO_ONE_MEETING_INVITER__ENTITY = 1;
    var BUSINESS_SEMINAR_ONE_TO_ONE_MEETING_INVITER__VENDOR = 2;

    var BUSINESS_SEMINAR_ONE_TO_ONE_MEETING_STATUS__PENDING = 1;
    var BUSINESS_SEMINAR_ONE_TO_ONE_MEETING_STATUS__ACCEPTED = 2;
    var BUSINESS_SEMINAR_ONE_TO_ONE_MEETING_STATUS__NATA = 4;

    var uvm = {
        evaluation: evaluationFactory(),
        entityInvitationResponse: {
            resourceUrl:
                UNGM.siteRoot +
                'Shared/BusinessSeminar/EntityInvitationResponse/' +
                '?id=' +
                $element.attr('data-uvm-business-seminar-id') +
                '&entityId=' +
                $element.attr('data-uvm-business-seminar-entity-id'),
            data: {
                entityId: parseInt($element.attr('data-uvm-business-seminar-entity-id')),
                status: parseInt($element.attr('data-uvm-business-seminar-entity-invitation-status')),
                historyLogs: []
            },
            form: {
                status: 8, // !== BusinessSeminarStatus.Approved as default value
                comment: ''
            },
            commentLimit: parseInt($element.attr('data-uvm-business-seminar-entity-invitation-comment-limit')),
            isExpanded: false,
            init: initEntityInvitationResponse,
            get: getEntityInvitationResponse,
            save: saveEntityInvitationResponse,
            commentMissing: entityInvitationResponseCommentMissing,
            commentExceedingLimit: entityInvitationResponseCommentExceedingLimit
        },
        entityContactsResponse: {
            resourceUrl:
                UNGM.siteRoot +
                'Shared/BusinessSeminar/EntityContacts/' +
                '?id=' +
                $element.attr('data-uvm-business-seminar-id') +
                '&entityId=' +
                $element.attr('data-uvm-business-seminar-entity-id'),
            data: {
                entityId: parseInt($element.attr('data-uvm-business-seminar-entity-id')),
                contacts: []
            },
            isExpanded: false,
            init: initEntityContactsResponse,
            get: getEntityContacts
        },
        oneToOneMeetingEntity: oneToOneMeetingEntityFactory(),
        expressionOfInterest: {
            isSuccessful: false,
            businessSeminarUrl: '',
            expressInterest: expressInterest,
            redirectToLogin: redirectToLogin
        }
    };

    function expressInterest() {
        $http.post(UNGM.siteRoot + "Shared/BusinessSeminar/VendorExpressionOfInterest/" + $element.attr('data-uvm-business-seminar-id'))
            .done(function () {
                uvm.expressionOfInterest.isSuccessful = true;
            });
    }

    function redirectToLogin() {
        window.location.href = UNGM.siteRoot + "Account/Account/Login?returnUrl=" + uvm.expressionOfInterest.businessSeminarUrl;
    }

    function evaluationFactory() {
        var evaluation = {
            resourceUrl: UNGM.siteRoot + 'Shared/BusinessSeminar/Evaluation/' + $element.attr('data-uvm-business-seminar-id'),
            data: {
                id: parseInt($element.attr('data-uvm-business-seminar-id')),
                status: 0,
                historyLogs: []
            },
            form: {
                status: 0,
                comment: ''
            },
            init: init,
            get: get,
            save: save,
            commentMissing: commentMissing
        };

        var initialized = false;
        function init() {
            if (!initialized) {
                evaluation.get();
                initialized = true;
            }
        }

        function get() {
            $http.get(evaluation.resourceUrl)
                .done(function (res) {
                    evaluation.data.status = res.Status;
                    evaluation.data.historyLogs = res.HistoryLogs;
                });
        }

        function save() {
            if (evaluation.commentMissing()) { return; }
            $confirm({
                title: $stringResource.get('Shared.BusinessSeminar.Evaluation.ConfirmDialogTitle'),
                description: $stringResource.get('Shared.BusinessSeminar.Evaluation.ConfirmDialogDescription'),
                confirm: $stringResource.get('Shared.BusinessSeminar.Evaluation.ConfirmDialogConfirm'),
                cancel: $stringResource.get('Shared.BusinessSeminar.Evaluation.ConfirmDialogCancel')
            }).done(function () {
                $http.post(evaluation.resourceUrl, evaluation.form)
                    .done(evaluation.get);
            });
        }

        function commentMissing() {
            return evaluation.form.status !== 4 // !== BusinessSeminarStatus.Approved
                && evaluation.form.comment.length === 0;
        }

        return evaluation;
    }

    var entityInvitationResponseInitalized = false;
    function initEntityInvitationResponse() {
        uvm.entityInvitationResponse.isExpanded = !uvm.entityInvitationResponse.isExpanded;
        uvm.entityContactsResponse.isExpanded = false;
        if (!entityInvitationResponseInitalized) {
            uvm.entityInvitationResponse.get();
            entityInvitationResponseInitalized = true;
        }
    }

    function getEntityInvitationResponse() {
        $http.get(uvm.entityInvitationResponse.resourceUrl)
            .done(function (res) {
                uvm.entityInvitationResponse.data.status = res.Status;
                uvm.entityInvitationResponse.data.historyLogs = res.HistoryLogs;
                uvm.entityInvitationResponse.form.status = 8; // !== BusinessSeminarStatus.Approved as default value
                uvm.entityInvitationResponse.form.comment = '';
            });
    }

    var entityContactsResponseInitalized = false;
    function initEntityContactsResponse() {
        uvm.entityContactsResponse.isExpanded = !uvm.entityContactsResponse.isExpanded;
        uvm.entityInvitationResponse.isExpanded = false;
        if (!entityContactsResponseInitalized) {
            uvm.entityContactsResponse.get();
            entityContactsResponseInitalized = true;
        }
    }

    function getEntityContacts() {
        $http.get(uvm.entityContactsResponse.resourceUrl)
            .done(function (res) {
                uvm.entityContactsResponse.data.contacts = res;
            });
    }

    function saveEntityInvitationResponse() {
        if (uvm.entityInvitationResponse.commentMissing() || uvm.entityInvitationResponse.commentExceedingLimit()) { return; }
        $confirm({
            title: $stringResource.get('Shared.BusinessSeminar.EntityInvitationResponse.ConfirmDialogTitle'),
            description: $stringResource.get('Shared.BusinessSeminar.EntityInvitationResponse.ConfirmDialogDescription'),
            confirm: $stringResource.get('Shared.BusinessSeminar.EntityInvitationResponse.ConfirmDialogConfirm'),
            cancel: $stringResource.get('Shared.BusinessSeminar.EntityInvitationResponse.ConfirmDialogCancel')
        }).done(function () {
            $http.post(uvm.entityInvitationResponse.resourceUrl, uvm.entityInvitationResponse.form)
                .done(uvm.entityInvitationResponse.get);
        });
    }

    function entityInvitationResponseCommentMissing() {
        return uvm.entityInvitationResponse.form.status === 4 // === BusinessSeminarEntityInvitationStatus.Declined
            && uvm.entityInvitationResponse.form.comment.length === 0;
    }

    function entityInvitationResponseCommentExceedingLimit() {
        return uvm.entityInvitationResponse.form.comment.length > uvm.entityInvitationResponse.commentLimit;
    }

    function oneToOneMeetingEntityFactory() {
        var oneToOneMeetingEntity = {
            config: {
                status: 0,
                inviter: 0
            },

            requestOneToOneMeeting: requestOneToOneMeeting,
            respondToOneToOneMeeting: respondToOneToOneMeeting,
            save: save,

            response: {
                url: UNGM.siteRoot + 'Shared/BusinessSeminar/OneToOneMeetingResponseFromVendorToBusinessSeminarEntity/' + $element.attr('data-uvm-business-seminar-id'),
                data: {
                    entityId: 0,
                    status: '',
                    comment: ''
                },

                submit: submitResponse,
                noDecisionSelectedMessageShown: false,
                setStatus: setStatus,
                commentMissing: commentMissingResponse
            },
        };

        function requestOneToOneMeeting(businessSeminarId, businessSeminarEntityId) {
            $confirm({
                title: $stringResource.get('Shared.BusinessSeminar.OneToOneMeetingEntity.ConfirmDialogTitle'),
                description: $stringResource.get('Shared.BusinessSeminar.OneToOneMeetingEntity.ConfirmDialogDescription'),
                confirm: $stringResource.get('Shared.BusinessSeminar.OneToOneMeetingEntity.ConfirmDialogConfirm'),
                cancel: $stringResource.get('Shared.BusinessSeminar.OneToOneMeetingEntity.ConfirmDialogCancel')
            }).done(function () {
                $http.post(UNGM.siteRoot + 'Shared/BusinessSeminar/OneToOneMeetingRequestFromVendorToBusinessSeminarEntity?id=' + businessSeminarId + '&entityId=' + businessSeminarEntityId)
                    .done(function () {
                        oneToOneMeetingEntity.config.inviter = BUSINESS_SEMINAR_ONE_TO_ONE_MEETING_INVITER__VENDOR;
                        oneToOneMeetingEntity.config.status = BUSINESS_SEMINAR_ONE_TO_ONE_MEETING_STATUS__PENDING;
                    });
            });
        }

        var dialogHolder = null;
        function respondToOneToOneMeeting(businessSeminarId, businessSeminarEntityId) {
            var url = UNGM.siteRoot + '/Shared/BusinessSeminar/OneToOneMeetingResponseFromVendorToBusinessSeminarEntity/' + businessSeminarId + '?entityId=' + businessSeminarEntityId;
            $dialog(url, {
                title: $stringResource.get("Shared.BusinessSeminar.OneToOneMeetingVendor.ConfirmDialogTitle"),
                width: "70%",
                scope: $scope
            }).done(function (dialog) { dialogHolder = dialog; });
        }
        
        function save() {
            $confirm({
                title: $stringResource.get('Shared.BusinessSeminar.OneToOneMeetingEntity.ConfirmDialogTitle'),
                description: $stringResource.get('Shared.BusinessSeminar.OneToOneMeetingEntity.ConfirmDialogDescription'),
                confirm: $stringResource.get('Shared.BusinessSeminar.OneToOneMeetingEntity.ConfirmDialogConfirm'),
                cancel: $stringResource.get('Shared.BusinessSeminar.OneToOneMeetingEntity.ConfirmDialogCancel')
            }).done(function () {
                $http.post(oneToOneMeetingEntity.requestUrl)
                    .done(function () {
                        oneToOneMeetingEntity.config.inviter = BUSINESS_SEMINAR_ONE_TO_ONE_MEETING_INVITER__VENDOR;
                        oneToOneMeetingEntity.config.status = BUSINESS_SEMINAR_ONE_TO_ONE_MEETING_STATUS__PENDING;
                    });
            });
        }

        function setStatus(status) {
            oneToOneMeetingEntity.response.data.status = parseInt(status);
            decisionSelectionMissing();
        }

        function decisionSelectionMissing() {
            oneToOneMeetingEntity.response.noDecisionSelectedMessageShown =
                oneToOneMeetingEntity.response.data.status !== BUSINESS_SEMINAR_ONE_TO_ONE_MEETING_STATUS__NATA &&
                oneToOneMeetingEntity.response.data.status !== BUSINESS_SEMINAR_ONE_TO_ONE_MEETING_STATUS__ACCEPTED;
        }


        function submitResponse() {
            decisionSelectionMissing();
            if (oneToOneMeetingEntity.response.noDecisionSelectedMessageShown || oneToOneMeetingEntity.response.commentMissing()) { return; }
            $http.post(oneToOneMeetingEntity.response.url, oneToOneMeetingEntity.response.data)
                .done(function () {
                    oneToOneMeetingEntity.config.status = oneToOneMeetingEntity.response.data.status;
                    dialogHolder.close();
                });
        }

        function commentMissingResponse() {
            return oneToOneMeetingEntity.response.data.status === BUSINESS_SEMINAR_ONE_TO_ONE_MEETING_STATUS__NATA
                && $.trim(oneToOneMeetingEntity.response.data.comment).length === 0;
        }

        return oneToOneMeetingEntity;
    }

    return uvm;
};
'use strict';

(function (undefined) {
    uvm.module('UNGM.BusinessSeminar.Search')
        .controller('VendorInvitationController', VendorInvitationController);

    VendorInvitationController.$inject = ['$http', '$scope', '$confirm'];

    function VendorInvitationController($http, $scope, $confirm) {
        var uvm = {
            data: {
                businessSeminarId: 0,
                isVendorInvitationAccepted: false,
                isCurrentUserVendorACompany: true,
                isBusinessSeminarOpenForAcceptingVendorInvitation: false,
                CompanyDescription: '',
                participatingContacts: [],
            },

            addParticipatingContactForm: {
                FirstName: '',
                LastName: '',
                Email: '',
                CompanyPosition: '',
            },

            $init: $init,

            isParticipatingContactEmailTaken: isParticipatingContactEmailTaken,
            addParticipatingContactFromVendorContact: addParticipatingContactFromVendorContact,
            submitAddParticipatingContactForm: submitAddParticipatingContactForm,
            removeParticipatingContact: removeParticipatingContact,
            submitAcceptVendorInvitationForm: submitAcceptVendorInvitationForm,
        };

        function $init() {
            getVendorContacts();
            getParticipatingContacts();
            getVendorType();
        }

        function isParticipatingContactEmailTaken(participatingContactEmail) {
            var matchingParticipatingContacts = $.grep(uvm.data.participatingContacts, function (participatingContact) {
                return participatingContact.Email === participatingContactEmail;
            });
            return matchingParticipatingContacts.length !== 0;
        }

        function addParticipatingContactFromVendorContact(vendorContactEmail) {
            if (isParticipatingContactEmailTaken(vendorContactEmail)) { return; }

            var participatingVendorContact = $.grep(uvm.data.vendorContacts, function (vendorContact) {
                return vendorContact.Email === vendorContactEmail;
            })[0];
            addParticipatingContact(participatingVendorContact);
        }

        function submitAddParticipatingContactForm($event) {
            var form = $($event.currentTarget).closest('form');
            if (!form.valid()) { return; }
            if (isParticipatingContactEmailTaken(uvm.addParticipatingContactForm.Email)) { return; }

            addParticipatingContact(uvm.addParticipatingContactForm);
        }

        function getVendorContacts() {
            var url = '/Shared/BusinessSeminar/VendorContacts';
            $http.get(url)
                .done(function (res) {
                    uvm.data.vendorContacts = res;
                });
        }

        function getParticipatingContacts() {
            var url = '/Shared/BusinessSeminar/VendorParticipatingContacts?businessSeminarId=' + uvm.data.businessSeminarId;
            $http.get(url)
                .done(function (res) {
                    uvm.data.participatingContacts = res;
                });
        }

        function getVendorType() {
            var url = '/Shared/BusinessSeminar/IsCurrentUserVendorACompany';
            $http.get(url)
                .done(function (res) {
                    uvm.data.isCurrentUserVendorACompany = res;
                });
        }

        function addParticipatingContact(participatingContact) {
            var url = '/Shared/BusinessSeminar/AddVendorParticipatingContact?businessSeminarId=' + uvm.data.businessSeminarId;
            $http.post(url, participatingContact)
                .always(function () {
                    uvm.addParticipatingContactForm = {
                        FirstName: '',
                        LastName: '',
                        Email: '',
                        CompanyPosition: '',
                    };
                    $scope.$emit('close');
                    getParticipatingContacts();
                });
        }

        function removeParticipatingContact(participatingContactEmail) {
            if (uvm.data.participatingContacts.length <= 1) { return; }
            $confirm('Shared.BusinessSeminar.VendorContact.RemoveParticipatingContactDialog')
                .done(function () {
                    var url = '/Shared/BusinessSeminar/RemoveVendorParticipatingContact?businessSeminarId=' + uvm.data.businessSeminarId;
                    $http.post(url, { participatingContactEmail: participatingContactEmail })
                        .always(function () {
                            getParticipatingContacts();
                        });
                })
        }

        function submitAcceptVendorInvitationForm($event) {
            var form = $($event.currentTarget).closest('form');
            if (!form.valid()) { return; }
            if (uvm.data.participatingContacts.length === 0) { return; }

            var url = '/Shared/BusinessSeminar/AcceptVendorInvitation?businessSeminarId=' + uvm.data.businessSeminarId;
            $http.post(url, { CompanyDescription: uvm.data.CompanyDescription })
                .done(function () {
                    uvm.data.isVendorInvitationAccepted = true;
                });
        }

        return uvm;
    }
})();
;
'use strict';

uvm.module('UNGM.KnowledgeCenterWorkingGroup.Membership', ['uvm']);
    ;
'use strict';

uvm.module('UNGM.KnowledgeCenterWorkingGroup.Membership')
    .controller('MembershipController', MembershipController);

MembershipController.$inject = ['$http', '$delay', '$element', '$scope', '$confirm', '$stringResource'];

function MembershipController($http, $delay, $element, $scope, $confirm, $stringResource) {
    var uvm = {        
        users: [],
        userIds: [],
        resetSearch: resetSearch,
        search: $delay(search, { preInvoke: function () { uvm.waypoint = false; } }),
        resetSearchFilters: resetSearchFilters,
        filter: {            
            SortField: 'Id',
            SortAscending: false,
            Id: $element.data('id'),
            Username: '',
            FirstName: '',
            Surname: '',
            Organization: '',            
            IsGroupAdmin: false,
            IsGroupEditor: false,
            IsGroupViewer: false,
            Roles: [],
            WorkingGroupNames: [],
            Skip: 0
        },
        knowledgeCenterWorkingGroupPermissions: {
            admin: '',
            editor: '',
            viewer: ''
        },
        isSelectAllGroupAdminChecked: false,
        isSelectAllGroupEditorChecked: false,
        isSelectAllGroupViewerChecked: false,
        total: 0,
        shown: false,
        waypoint: false,
        savePermission: savePermission,
        saveAllPermissions: saveAllPermissions,        
        url: UNGM.siteRoot + $element.data('uvm-user-search-url')
    };

    uvm.resetSearch();

    function resetSearch() {
        uvm.filter.Skip = 0;        
        uvm.search();
    }

    function search() {        
        $http.get(uvm.url, { data: uvm.filter })
            .done(function (result) {
                result = JSON.parse(result);

                var isFirstPage = uvm.filter.Skip == 0;
                uvm.users = isFirstPage ? result.users : uvm.users.concat(result.users);
                uvm.filter.Skip = uvm.users.length;
                uvm.shown = uvm.users.length;
                uvm.total = result.totalCount;
                uvm.waypoint = uvm.users.length < result.totalCount;
                updateSelectAllPermissionCheckboxes();
            });
    }

    function updateSelectAllPermissionCheckboxes() {
        var usersNotHavingGroupAdminPermission = $.grep(uvm.users, function (user, index) {
            return user.IsGroupAdmin == false;
        });

        var usersNotHavingGroupEditorPermission = $.grep(uvm.users, function (user, index) {
            return user.IsGroupEditor == false;
        });

        var usersNotHavingGroupViewerPermission = $.grep(uvm.users, function (user, index) {
            return user.IsGroupViewer == false;
        });
        
        uvm.isSelectAllGroupAdminChecked = usersNotHavingGroupAdminPermission.length > 0 ? false : true;
        uvm.isSelectAllGroupEditorChecked = usersNotHavingGroupEditorPermission.length > 0 ? false : true;
        uvm.isSelectAllGroupViewerChecked = usersNotHavingGroupViewerPermission.length > 0 ? false : true;
    }

    function resetSearchFilters() {
        uvm.filter.Username = '';
        uvm.filter.FirstName = '';
        uvm.filter.Surname = '';
        uvm.filter.Organization = '';        
        uvm.filter.IsGroupAdmin = false;
        uvm.filter.IsGroupEditor = false;
        uvm.filter.IsGroupViewer = false;
        uvm.filter.Roles = [];
        uvm.filter.WorkingGroupNames = [];
        uvm.filter.SortField = 'Id';
        uvm.filter.SortAscending = false;
        uvm.resetSearch();
    }

    function savePermission($event) {        
        var jsonString = JSON.stringify({
            id: uvm.filter.Id,
            isAssigned: $event.currentTarget.checked,
            userId: $($event.currentTarget).data("id"),            
            permission: $($event.currentTarget).data("permission")
        });
        var url = UNGM.siteRoot + 'UNUser/KnowledgeCenterWorkingGroup/SavePermission';
        $http.post(url, jsonString, { contentType: 'application/json' });
    }  

    function saveAllPermissions($event, permission) {
        var isChecked = $event.currentTarget.checked;
        $confirm({
            title: $stringResource.get('UNUser.KnowledgeCenterWorkingGroup.MembershipConfirmDialogTitle'),
            description: $stringResource.get('UNUser.KnowledgeCenterWorkingGroup.Membership.ConfirmDialogDescription') + uvm.users.length
                + $stringResource.get('UNUser.KnowledgeCenterWorkingGroup.Membership.ConfirmDialogSelectedUsers'),
            confirm: $stringResource.get('UNUser.KnowledgeCenterWorkingGroup.Membership.ConfirmDialogConfirm'),
            cancel: $stringResource.get('UNUser.KnowledgeCenterWorkingGroup.Membership.ConfirmDialogCancel')
        }).done(function () {
            var userIds = [];            
            $.each(uvm.users, function (key, user) {
                userIds.push(user.Id);
            });

            var jsonString = JSON.stringify({
                id: uvm.filter.Id,
                isAssigned: isChecked,
                permission: permission,
                userIds: userIds
            });

            var url = UNGM.siteRoot + "UNUser/KnowledgeCenterWorkingGroup/SaveAllPermissions";
            $http.post(url, jsonString, { contentType: 'application/json' });

            $.each(uvm.users, function (key, user) {
                if (permission == uvm.knowledgeCenterWorkingGroupPermissions.admin) {
                    user.IsGroupAdmin = isChecked;
                }
                else if (permission == uvm.knowledgeCenterWorkingGroupPermissions.editor) {
                    user.IsGroupEditor = isChecked;
                }
                else if (permission == uvm.knowledgeCenterWorkingGroupPermissions.viewer) {
                    user.IsGroupViewer = isChecked;
                }
            });
        }).fail(function () {
            $event.currentTarget.checked = !isChecked;
        });
    }
    return uvm;
};
UNGM.TenderAlertService = UNGM.TenderAlertService || {};
UNGM.TenderAlertService.RemindMeLater = {
    init: function () {
        $("input[name=Reason]").bind("change", UNGM.TenderAlertService.RemindMeLater.reasonChanged);
    },
    reasonChanged: function () {
        var item = $('input[name=Reason]:checked');
        if (item.val().toLowerCase() === "other") {
            $("#otherReason").show();
            UNGM.markRequiredFields(); 
        } else {
            $("#otherReason").hide();
        }
    },
    submit: function () {
        var form = $("#CreateReminderForm");
        if (form.valid()) {
            UNGM.throbOver(form);
            $.ajax({
                url: form.attr('action'),
                type: 'POST',
                data: form.serialize(),
                success:
                    function () {
                        UNGM.hideThrobber();
                        $.confirm(
                            UNGM.TenderAlertService.RemindMeLater.NotifyTitle,
                            UNGM.TenderAlertService.RemindMeLater.NotifyMessage,
                            UNGM.TenderAlertService.RemindMeLater.NotifyOK,
                            "",
                            function () {
                                UNGM.throbOver(form);
                                window.location.href = "/Public/TenderAlertService";
                            }
                        );
                    }
            });
        }
    }
}
;
'use strict';

(function () {
    uvm.module('UNGM.AccountRegistration', ['uvm']);
})();;
'use strict';

(function () {
    uvm.module('UNGM.AccountRegistration')
        .controller('VendorActivatePendingController', VendorActivatePendingController);

    VendorActivatePendingController.$inject = ['$element', '$http'];

    function VendorActivatePendingController($element, $http) {
        var uvm = {
            isEmailSent: false,
            account: {
                Email: "",
                RegistrationProgress: null
            },
            $init: $init,
            resendActivationEmail: resendActivationEmail
        };

        function $init() {
            $http.get(UNGM.siteRoot + 'Account/Registration/GetVendorAccountActivationStatus')
                .done(function (account) {
                    uvm.account = account;
                });
        }

        function resendActivationEmail() {
            $http.post(UNGM.siteRoot + 'Account/Registration/ResendVendorActivationEmail');
            uvm.isEmailSent = true;
        }

        return uvm;
    }
})();;
(function (undefined) {
    uvm.module('UNGM.ProcurementsByCategoriesWidget', ['uvm']);

    uvm.module('UNGM.ProcurementsByCategoriesWidget')
        .filter('in_USD', inUsdFilterFactory);

    function inUsdFilterFactory() {
        return function (value) {
            var number = parseFloat(value);
            if (isNaN(number)) { return ""; }
            if (number < 0) { return "-$" + Math.abs(number).toLocaleString("en", { minimumFractionDigits: 2, maximumFractionDigits: 2, useGrouping: true }); }
            return "$" + number.toLocaleString("en", { minimumFractionDigits: 2, maximumFractionDigits: 2, useGrouping: true });
        };
    }

    uvm.module('UNGM.ProcurementsByCategoriesWidget')
        .filter('in_percentage', inPercentageFilterFactory);

    function inPercentageFilterFactory() {
        return function (value) {
            var number = parseFloat(value);
            if (isNaN(number)) { return ""; }
            var string = number.toLocaleString("en", { minimumFractionDigits: 2, maximumFractionDigits: 2, useGrouping: true }) + "%";
            return (string == "0.00%") ? "< 0.01%" : string;
        };
    }
})();
;
(function (undefined) {
    uvm.module('UNGM.ProcurementsByCategoriesWidget')
        .controller('SearchController', SearchController);

    SearchController.$inject = ['$http', '$delay'];

    function SearchController($http, $delay) {
        var uvm = {
            config: {
                UngmUnspscIds: ''
            },
            filter: {
                UngmUnspscIds: [],
                Year: null,
                ASRUNSPSCLevelName: null,
            },

            result: {
                items: [],
            },

            $init: $init,
            search: $delay(search),
        };

        function $init() {
            uvm.filter.UngmUnspscIds = uvm.config.UngmUnspscIds.split(',');
            uvm.search();
        }

        function search() {
            $http.post('/Public/ASRDataArchive/ProcurementsByCategoriesWidgetResult', uvm.filter)
                .done(function (response) {
                    uvm.result.items = response;
                });
        }

        return uvm;
    }
})();
;
$.widget("ui.dialog", $.ui.dialog, {
    /*! jQuery UI - v1.10.2 - 2013-12-12
     *  http://bugs.jqueryui.com/ticket/9087#comment:27 - bugfix
     *  http://bugs.jqueryui.com/ticket/4727#comment:23 - bugfix
     *  allowInteraction fix to accommodate windowed editors
     */
    _allowInteraction: function (event) {
        if (this._super(event)) {
            return true;
        }

        // address interaction issues with general iframes with the dialog
        if (event.target.ownerDocument != this.document[0]) {
            return true;
        }

        // address interaction issues with dialog window
        if ($(event.target).closest(".cke_dialog").length) {
            return true;
        }

        // address interaction issues with iframe based drop downs in IE
        if ($(event.target).closest(".cke").length) {
            return true;
        }
    },
    /*! jQuery UI - v1.10.2 - 2013-10-28
     *  http://dev.ckeditor.com/ticket/10269 - bugfix
     *  moveToTop fix to accommodate windowed editors
     */
    _moveToTop: function (event, silent) {
        if (!event || !this.options.modal) {
            this._super(event, silent);
        }
    }
});
;
