/* Minification failed. Returning unminified contents.
(989,74-75): run-time error JS1195: Expected expression: >
(989,83-84): run-time error JS1003: Expected ':': )
(989,107-108): run-time error JS1004: Expected ';': )
(990,17-18): run-time error JS1195: Expected expression: :
(990,17-18): run-time error JS1195: Expected expression: :
(993,61-62): run-time error JS1004: Expected ';': {
(995,58-59): run-time error JS1195: Expected expression: >
(995,77-78): run-time error JS1004: Expected ';': )
(996,10-11): run-time error JS1195: Expected expression: )
(998,33-34): run-time error JS1004: Expected ';': {
(1001,5-6): run-time error JS1002: Syntax error: }
(1003,58-59): run-time error JS1004: Expected ';': {
(1020,47-48): run-time error JS1195: Expected expression: >
(1020,69-70): run-time error JS1004: Expected ';': )
(1032,73-74): run-time error JS1195: Expected expression: >
(1032,116-117): run-time error JS1195: Expected expression: >
(1032,129-130): run-time error JS1004: Expected ';': )
(1038,46-47): run-time error JS1195: Expected expression: >
(1038,81-82): run-time error JS1004: Expected ';': )
(1047,5-6): run-time error JS1002: Syntax error: }
(1049,10-19): run-time error JS1197: Too many errors. The file might not be a JavaScript file: onChanged
(1025,13-19): run-time error JS1018: 'return' statement outside of function: return
 */
var URL_API_BASE = "api/";
var URL_API = function (path) {
    return URL_API_BASE + path;
};

var EVENTS = {
    CATEGORY_LOADED: "CATEGORY_LOADED",
    PROGRAMME_LOADED: "PROGRAMME_LOADED",
    REGION_LOADED: "REGION_LOADED",
    COUNTRY_LOADED: "COUNTRY_LOADED",
    COUNTRY_CATEGORY_LOADED: "COUNTRY_CATEGORY_LOADED",
    COUNTRY_PROGRAMME_LOADED: "COUNTRY_PROGRAMME_LOADED"
};;
var APP = angular.module("WhoPBPortal", ["ngMessages", "ui.router", "ngAnimate", "ngSanitize", "sprintf", "ngMap", "bootstrap-tagsinput"]);
APP.config(["$stateProvider", "$urlRouterProvider", "$locationProvider", "$httpProvider", function ($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider) {
    $locationProvider.html5Mode(true);

    $stateProvider
        .state("404", {
            url: "/:biennium/404?message",
            templateUrl: "Views/NotFoundView.html",
            controller: "notFoundViewController"
        })
        .state("404-global", {
            url: "/404",
            templateUrl: "Views/NotFoundView.html",
            controller: "notFoundViewController"
        })
        .state("default", {
            url: "/",
            templateUrl: "Views/HomeView.html",
            controller: "homeViewController"
        })
        .state("home", {
            url: "/:biennium/home",
            templateUrl: "Views/HomeView.html",
            controller: "homeViewController"
        })

        // Categories states configuration
        .state("our-work", {
            url: "/:biennium/our-work",
            templateUrl: "Views/CategoriesViews/OurWorkView.html",
            controller: "ourWorkViewController"
        })
        .state("category", {
            url: "/:biennium/our-work/category/:categoryId",
            templateUrl: "Views/CategoriesViews/CategoryView.html",
            controller: "categoryViewController"
        })
        .state("category.about", {
            url: "/about",
            views: {
                "category-tabs": {
                    templateUrl: "Views/CategoriesViews/CategoryAboutView.html",
                    controller: "categoryAboutViewController"
                }
            }
        })
        .state("category.about.about", {
            url: "/about",
        })
        .state("category.about.programme-outcomes", {
            url: "/programme-outcomes",
        })
        .state("category.about.key-figures", {
            url: "/key-figures",
        })
        .state("category.about.key-figures-by-level", {
            url: "/key-figures-by-level",
        })
        .state("category.about.key-figures-by-location", {
            url: "/key-figures-by-location",
        })
        .state("category.flow", {
            url: "/flow",
            views: {
                "category-tabs": {
                    templateUrl: "Views/CategoriesViews/CategoryFinancialFlowView.html",
                    controller: "categoryFinancialFlowViewController"
                }
            }
        })
        .state("category.mtr", {
            url: "/mtr",
            views: {
                "category-tabs": {
                    templateUrl: "Views/CategoriesViews/CategoryMidTermReviewView.html",
                    controller: "categoryMidTermReviewViewController"
                }
            }
        })
        .state("category.eba", {
            url: "/eba",
            views: {
                "category-tabs": {
                    templateUrl: "Views/CategoriesViews/CategoryEndBienniumAssessmentView.html",
                    controller: "categoryEndBienniumAssessmentViewController"
                }
            }
        })

        // Programmes states configuration
        .state("programme", {
            url: "/:biennium/our-work/category/:categoryId/programme/:programmeId",
            templateUrl: "Views/ProgrammesViews/ProgrammeView.html",
            controller: "programmeViewController"
        })
        .state("programme.about", {
            url: "/about",
            views: {
                "programme-tabs": {
                    templateUrl: "Views/ProgrammesViews/ProgrammeAboutView.html",
                    controller: "programmeAboutViewController"
                }
            }
        })
        .state("programme.about.about", {
            url: "/about",
        })
        .state("programme.about.results-structure", {
            url: "/results-structure",
        })
        .state("programme.about.key-figures", {
            url: "/key-figures",
        })
        .state("programme.about.key-figures-by-level", {
            url: "/key-figures-by-level",
        })
        .state("programme.about.key-figures-by-geography", {
            url: "/key-figures-by-geography"
        })
        .state("programme.indicators", {
            url: "/indicators", 
            views: {
                "programme-tabs": {
                    templateUrl: "Views/ProgrammesViews/ProgrammeIndicatorsView.html",
                    controller: "programmeIndicatorsViewController"
                }
            }
        })
        .state("programme.flow", {
            url: "/flow",
            views: {
                "programme-tabs": {
                    templateUrl: "Views/ProgrammesViews/ProgrammeFinancialFlowView.html",
                    controller: "programmeFinancialFlowViewController"
                }
            }
        })
        .state("programme.mtr", {
            url: "/mtr",
            views: {
                "programme-tabs": {
                    templateUrl: "Views/ProgrammesViews/ProgrammeMidTermReviewView.html",
                    controller: "programmeMidTermReviewViewController"
                }
            }
        })
        .state("programme.eba", {
            url: "/eba",
            views: {
                "programme-tabs": {
                    templateUrl: "Views/ProgrammesViews/ProgrammeEndBienniumAssessmentView.html",
                    controller: "programmeEndBienniumAssessmentViewController"
                }
            }
        })
        .state("programme.ts", {
            url: "/ts",
            views: {
                "programme-tabs": {
                    templateUrl: "Views/ProgrammesViews/ProgrammeTopStoriesView.html",
                    controller: "programmeTopStoriesViewController"
                }
            }
        })

        // Category Human Resource
        .state("category-human-resource", {
            url: "/:biennium/our-work/category/:categoryId/human-resource",
            templateUrl: "Views/HumanResourcesViews/CategoryHumanResourceView.html",
            controller: "categoryHumanResourceViewController"
        })
        .state("category-human-resource.about", {
            url: "/about",
            views: {
                "human-resource-tabs": {
                    templateUrl: "Views/HumanResourcesViews/CategoryHumanResourceAboutView.html",
                    controller: "categoryHumanResourceAboutViewController"
                }
            }
        })
        .state("category-human-resource.about.about", {
            url: "/about"
        })
        .state("category-human-resource.about.programme-outcomes", {
            url: "/programme-outcomes"
        })
        .state("category-human-resource.about.key-figures", {
            url: "/key-figures",
        })
        .state("category-human-resource.about.key-figures-by-level", {
            url: "/key-figures-by-level",
        })
        .state("category-human-resource.about.key-figures-by-geography", {
            url: "/key-figures-by-geography",
        })
        .state("category-human-resource.flow", {
            url: "/flow",
            views: {
                "human-resource-tabs": {
                    templateUrl: "Views/HumanResourcesViews/CategoryHumanResourcesFlowView.html",
                    controller: "categoryHumanResourcesFlowViewController"
                }
            }
        })

        // Programme Human Resource
        .state("programme-human-resource", {
            url: "/:biennium/our-work/category/:categoryId/programme/:programmeId/human-resource",
            templateUrl: "Views/HumanResourcesViews/ProgrammeHumanResourceView.html",
            controller: "programmeHumanResourceViewController"
        })
        .state("programme-human-resource.about", {
            url: "/about",
            views: {
                "human-resource-tabs": {
                    templateUrl: "Views/HumanResourcesViews/ProgrammeHumanResourceAboutView.html",
                    controller: "programmeHumanResourceAboutViewController"
                }
            }
        })
        .state("programme-human-resource.about.about", {
            url: "/about"
        })
        .state("programme-human-resource.about.results-structure", {
            url: "/results-structure"
        })
        .state("programme-human-resource.about.key-figures", {
            url: "/key-figures",
        })
        .state("programme-human-resource.about.key-figures-by-level", {
            url: "/key-figures-by-level",
        })
        .state("programme-human-resource.about.key-figures-by-geography", {
            url: "/key-figures-by-geography",
        })
        .state("programme-human-resource.flow", {
            url: "/flow",
            views: {
                "human-resource-tabs": {
                    templateUrl: "Views/HumanResourcesViews/ProgrammeHumanResourcesFlowView.html",
                    controller: "programmeHumanResourcesFlowViewController"
                }
            }
        })

        // Budget & financing states configuration
        .state("budget-and-financing", {
            url: "/:biennium/budget-and-financing",
            controller: "budgetAndFinancingViewController",
            templateUrl: "Views/BudgetAndFinancingViews/BudgetAndFinancingView.html"
        })
        .state("budget-and-financing.gpw-overview", {
            url: "/gpw-overview",
            views: {
                "budget-and-financing-tabs": {
                    templateUrl: "Views/BudgetAndFinancingViews/GPWOverviewView.html",
                    controller: "gpwOverviewViewController"
                }
            }
        })
        .state("budget-and-financing.summary", {
            url: "/summary",
            views: {
                "budget-and-financing-tabs": {
                    templateUrl: "Views/BudgetAndFinancingViews/BudgetAndFinanceSummaryView.html",
                    controller: "budgetAndFinanceSummaryViewController"
                }
            }
        })
        .state("budget-and-financing.details", {
            url: "/details",
            views: {
                "budget-and-financing-tabs": {
                    templateUrl: "Views/BudgetAndFinancingViews/BudgetAndFinanceDetailsView.html",
                    controller: "budgetAndFinanceDetailsViewController"
                }
            }
        })
        .state("budget-and-financing.flow", {
            url: "/flow",
            views: {
                "budget-and-financing-tabs": {
                    templateUrl: "Views/BudgetAndFinancingViews/FinancialFlowView.html",
                    controller: "financialFlowViewController"
                }
            }
        })

        // Human Resources states configuration
        .state("human-resources", {
            url: "/:biennium/human-resources",
            controller: "humanResourcesViewController",
            templateUrl: "Views/HumanResourcesViews/HumanResourcesView.html"
        })

        // Workforce
        .state("human-resources.workforce", {
            url: "/workforce",
            views: {
                "human-resources-tabs": {
                    templateUrl: "Views/HumanResourcesViews/HumanResourcesWorkforceView.html",
                    controller: "humanResourcesWorkforceViewController"
                }
            }
        })
        .state("human-resources.workforce.by-job-category", {
            url: "/by-job-category"
        })
        .state("human-resources.workforce.by-appointment-type", {
            url: "/by-appointment-type"
        })
        .state("human-resources.workforce.by-contract-type", {
            url: "/by-contract-type"
        })
        .state("human-resources.workforce.by-region", {
            url: "/by-region"
        })
        .state("human-resources.workforce.by-org-level", {
            url: "/by-org-level"
        })
        .state("human-resources.workforce.by-gender", {
            url: "/by-gender"
        })

        // Gender Balance
        .state("human-resources.gender-parity", {
            url: "/gender-parity",
            views: {
                "human-resources-tabs": {
                    templateUrl: "Views/HumanResourcesViews/HumanResourcesGenderBalanceView.html",
                    controller: "humanResourcesGenderBalanceViewController"
                }
            }
        })
        .state("human-resources.gender-parity.by-job-category", {
            url: "/by-job-category"
        })
        .state("human-resources.gender-parity.by-appointment-type", {
            url: "/by-appointment-type"
        })
        .state("human-resources.gender-parity.by-region", {
            url: "/by-region"
        })
        .state("human-resources.gender-parity.by-org-level", {
            url: "/by-org-level"
        })

        // Geographical Distribution
        .state("human-resources.geographical-distribution", {
            url: "/geographical-distribution",
            views: {
                "human-resources-tabs": {
                    templateUrl: "Views/HumanResourcesViews/HumanResourcesGeographicalDistributionView.html",
                    controller: "humanResourcesGeographicalDistributionViewController"
                }
            }
        })
        .state("human-resources.geographical-distribution.country-staffing-profile", {
            url: "/country-staffing-profile"
        })
        .state("human-resources.geographical-distribution.country-staffing-profile-by-grade", {
            url: "/country-staffing-profile-by-grade"
        })

        // Regions states configuration
        .state("regions", {
            url: "/:biennium/regions",
            templateUrl: "Views/RegionsViews/RegionsView.html",
            controller: "regionsViewController"
        })
        .state("region", {
            url: "/:biennium/regions/:regionCode",
            templateUrl: "Views/RegionsViews/RegionView.html",
            controller: "regionViewController"
        })
        .state("region.about", {
            url: "/about",
            views: {
                "region-tabs": {
                    templateUrl: "Views/RegionsViews/RegionAboutView.html",
                    controller: "regionAboutViewController"
                }
            }
        })
        .state("region.about.about", {
            url: "/about",
        })
        .state("region.about.key-figures", {
            url: "/key-figures",
        })
        .state("region.about.key-figures-by-level", {
            url: "/key-figures-by-level",
        })
        .state("region.about.key-figures-by-geography", {
            url: "/key-figures-by-geography",
        })
        .state("region.flow", {
            url: "/flow",
            views: {
                "region-tabs": {
                    templateUrl: "Views/RegionsViews/RegionFinancialFlowView.html",
                    controller: "regionFinancialFlowViewController"
                }
            }
        })
        .state("region-hq", {
            url: "/:biennium/regions-hq",
            templateUrl: "Views/RegionsViews/RegionHqView.html",
            controller: "RegionHqViewController"
        })
        .state("region-hq.about", {
            url: "/about",
            views: {
                "region-tabs": {
                    templateUrl: "Views/RegionsViews/RegionHqAboutView.html",
                    controller: "regionHqAboutViewController"
                }
            }
        })
        .state("region-hq.about.about", {
            url: "/about",
        })
        .state("region-hq.about.key-figures", {
            url: "/key-figures",
        })
        .state("region-hq.about.key-figures-by-programme", {
            url: "/key-figures-by-programme",
        })
        .state("region-hq.about.key-figures-by-output", {
            url: "/key-figures-by-output",
        })
        .state("region-hq.flow", {
            url: "/flow",
            views: {
                "region-tabs": {
                    templateUrl: "Views/RegionsViews/RegionHqFinancialFlowView.html",
                    controller: "regionHqFinancialFlowViewController"
                }
            }
        })

        // Regions Human Resources
        .state("regions-human-resource", {
            url: "/:biennium/regions-human-resource/:regionCode",
            templateUrl: "Views/RegionsHRViews/RegionHRView.html",
            controller: "regionHRViewController"
        })
        .state("regions-human-resource.about", {
            url: "/about",
            views: {
                "region-tabs": {
                    templateUrl: "Views/RegionsHRViews/RegionHRAboutView.html",
                    controller: "regionHRAboutViewController"
                }
            }
        })
        .state("regions-human-resource.about.about", {
            url: "/about",
        })
        .state("regions-human-resource.about.key-figures", {
            url: "/key-figures",
        })
        .state("regions-human-resource.about.key-figures-by-level", {
            url: "/key-figures-by-level",
        })
        .state("regions-human-resource.about.key-figures-by-location", {
            url: "/key-figures-by-location",
        })
        .state("regions-human-resource.flow", {
            url: "/flow",
            views: {
                "region-tabs": {
                    templateUrl: "Views/RegionsHRViews/RegionHRFlowView.html",
                    controller: "regionHRFlowViewController"
                }
            }
        })

        // Region HR Headquarters
        .state("region-human-resource-hq", {
            url: "/:biennium/region-human-resource-hq",
            templateUrl: "Views/RegionsHRViews/RegionHRHqView.html",
            controller: "RegionHRHqViewController"
        })
        .state("region-human-resource-hq.about", {
            url: "/about",
            views: {
                "region-tabs": {
                    templateUrl: "Views/RegionsHRViews/RegionHRHqAboutView.html",
                    controller: "regionHRHqAboutViewController"
                }
            }
        })
        .state("region-human-resource-hq.about.about", {
            url: "/about",
        })
        .state("region-human-resource-hq.about.key-figures", {
            url: "/key-figures",
        })
        .state("region-human-resource-hq.about.key-figures-by-programme", {
            url: "/key-figures-by-programme",
        })
        .state("region-human-resource-hq.about.key-figures-by-output", {
            url: "/key-figures-by-output",
        })
        .state("region-human-resource-hq.flow", {
            url: "/flow",
            views: {
                "region-tabs": {
                    templateUrl: "Views/RegionsHRViews/RegionHRHqFlowView.html",
                    controller: "regionHRHqFlowViewController"
                }
            }
        })

        // Countries states configuration
        .state("country", {
            url: "/:biennium/country/:countryCode",
            templateUrl: "Views/CountryViews/CountryView.html",
            controller: "countryViewController"
        })
        .state("country.keyAchievements", {
            views: {
                "country-tabs": {
                    templateUrl: "Views/CountryViews/CountryKeyAchievementsView.html",
                    controller: "countryKeyAchievementsViewController"
                }
            }
        })
        .state("country.summary", {
            views: {
                "country-tabs": {
                    templateUrl: "Views/CountryViews/CountrySummaryView.html",
                    controller: "countrySummaryViewController"
                }
            }
        })
        .state("country.homepage", {
            views: {
                "country-tabs": {
                    templateUrl: "Views/CountryViews/CountryHomepageView.html",
                    controller: "countryHomepageViewController"
                }
            }
        })
        .state("country.financialFlow", {
            views: {
                "country-tabs": {
                    templateUrl: "Views/CountryViews/CountryFinancialFlowView.html",
                    controller: "countryFinancialFlowViewController"
                }
            }
        })

        // Country category states configuration
        .state("country-category", {
            url: "/:biennium/country-category/:countryCode/:categorySerialNo",
            templateUrl: "Views/CountryCategoryViews/CountryCategoryView.html",
            controller: "countryCategoryViewController"
        })
        .state("country-category.summary", {
            views: {
                "country-category-tabs": {
                    templateUrl: "Views/CountryCategoryViews/CountryCategorySummaryView.html",
                    controller: "countryCategorySummaryViewController"
                }
            }
        })
        .state("country-category.homepage", {
            views: {
                "country-category-tabs": {
                    templateUrl: "Views/CountryCategoryViews/CountryCategoryHomepageView.html",
                    controller: "countryCategoryHomepageViewController"
                }
            }
        })
        .state("country-category.financialFlow", {
            views: {
                "country-category-tabs": {
                    templateUrl: "Views/CountryCategoryViews/CountryCategoryFinancialFlowView.html",
                    controller: "countryCategoryFinancialFlowViewController"
                }
            }
        })

        // Country programme states configuration
        .state("country-programme", {
            url: "/:biennium/country-programme/:countryCode/:programmeSerialNo",
            templateUrl: "Views/CountryProgrammeViews/CountryProgrammeView.html",
            controller: "countryProgrammeViewController"
        })
        .state("country-programme.summary", {
            views: {
                "country-programme-tabs": {
                    templateUrl: "Views/CountryProgrammeViews/CountryProgrammeSummaryView.html",
                    controller: "countryProgrammeSummaryViewController"
                }
            }
        })
        .state("country-programme.homepage", {
            views: {
                "country-programme-tabs": {
                    templateUrl: "Views/CountryProgrammeViews/CountryProgrammeHomepageView.html",
                    controller: "countryProgrammeHomepageViewController"
                }
            }
        })
        .state("country-programme.financialFlow", {
            views: {
                "country-programme-tabs": {
                    templateUrl: "Views/CountryProgrammeViews/CountryProgrammeFinancialFlowView.html",
                    controller: "countryProgrammeFinancialFlowViewController"
                }
            }
        })

        // Countries HR states configuration
        .state("country-human-resource", {
            url: "/:biennium/country-human-resource/:countryCode",
            templateUrl: "Views/CountryHRViews/CountryHRView.html",
            controller: "countryHRViewController"
        })
        .state("country-human-resource.summary", {
            views: {
                "country-tabs": {
                    templateUrl: "Views/CountryHRViews/CountryHRSummaryView.html",
                    controller: "countryHRSummaryViewController"
                }
            }
        })
        .state("country-category-human-resource", {
            url: "/:biennium/country-category-human-resource/:countryCode/:categorySerialNo",
            templateUrl: "Views/CountryCategoryHRViews/CountryCategoryHRView.html",
            controller: "countryCategoryHRViewController"
        })
        .state("country-category-human-resource.summary", {
            views: {
                "country-category-tabs": {
                    templateUrl: "Views/CountryCategoryHRViews/CountryCategoryHRSummaryView.html",
                    controller: "countryCategoryHRSummaryViewController"
                }
            }
        })
        .state("country-programme-human-resource", {
            url: "/:biennium/country-programme-human-resource/:countryCode/:programmeSerialNo",
            templateUrl: "Views/CountryProgrammeHRViews/CountryProgrammeHRView.html",
            controller: "countryProgrammeHRViewController"
        })
        .state("country-programme-human-resource.summary", {
            views: {
                "country-programme-tabs": {
                    templateUrl: "Views/CountryProgrammeHRViews/CountryProgrammeHRSummaryView.html",
                    controller: "countryProgrammeHRSummaryViewController"
                }
            }
        })

        // Contributors stats configuration
        .state("contributors", {
            url: "/:biennium/contributors?name",
            templateUrl: "Views/ContributorsViews/ContributorsView.html",
            controller: "contributorsViewController"
        })
        .state("contributors.contributor", {
            url: "/contributor",
            views: {
                "contributors-tabs": {
                    templateUrl: "Views/ContributorsViews/ContributorView.html",
                    controller: "contributorViewController"
                }
            }
        })
    .state("contributors.overview", {
        url: "/overview",
        views: {
            "contributors-tabs": {
                templateUrl: "Views/ContributorsViews/ContributorsOverviewView.html",
                controller: "contributorsOverviewViewController"
            }
        }
    })
    .state("contributors.overview.ac", {
        url: "/ac"
    })
    .state("contributors.overview.vcs", {
        url: "/vcs"
    })
    .state("contributors.overview.vct", {
        url: "/vct"
    })
    .state("contributors.overview.cvc", {
        url: "/cvc"
    })
    .state("contributors.overview.pip", {
        url: "/pip"
    })
    .state("contributors.overview.cfe", {
        url: "/cfe"
    })
    .state("contributors.overview.pf", {
        url: "/pf"
    })
    .state("cvc", {
        url: "/:biennium/cvc",
        templateUrl: "Views/ContributorsViews/CvcView.html",
        controller: "cvcViewController"
    })
    .state("pip", {
        url: "/:biennium/pip",
        templateUrl: "Views/ContributorsViews/PipView.html",
        controller: "pipViewController"
    })
    .state("cfe", {
        url: "/:biennium/cfe",
        templateUrl: "Views/ContributorsViews/CfeView.html",
        controller: "cfeViewController"
    })
    .state("ks", {
        url: "/:biennium/ks",
        templateUrl: "Views/ContributorsViews/KsView.html",
        controller: "ksViewController"
    })

    // Indicators states configuration
    .state("indicators-default", {
        url: "/:biennium/indicators",
        templateUrl: "Views/IndicatorsViews/IndicatorsView.html",
        controller: "indicatorsViewController"
    })
    .state("indicators", {
        url: "/indicators",
        templateUrl: "Views/IndicatorsViews/IndicatorsView.html",
        controller: "indicatorsViewController"
    })
    .state("indicators-biennium-category", {
        url: "/indicators/biennium/:bienniumFilter/category/:categoryFilter",
        templateUrl: "Views/IndicatorsViews/IndicatorsView.html",
        controller: "indicatorsViewController"
    })
    .state("indicators-biennium-category-programme", {
        url: "/indicators/biennium/:bienniumFilter/category/:categoryFilter/programme/:programmeFilter",
        templateUrl: "Views/IndicatorsViews/IndicatorsView.html",
        controller: "indicatorsViewController"
    })

    // Who states configuration
    .state("iati", {
        url: "/iati",
        templateUrl: "Views/IATIAboutView.html"
    })
    .state("iati.about", {
        url: "/about",
        templateUrl: "Views/IATIAboutView.html"
    })
    .state("iati-files", {
        url: "/iati/files",
        templateUrl: "Views/IATIFilesView.html",
        controller: "iatiFilesController"
    })
    .state("iati-data", {
        url: "/:biennium/iati/data",
        templateUrl: "Views/IATIDataView.html",
        controller: "iatiDataController"
    })
    .state("documents", {
        url: "/:biennium/documents",
        templateUrl: "Views/DocumentsView.html",
        controller: "keyDocumentsController"
    })
    .state("resolutions-and-decisions", {
        url: "/resolutions-and-decisions",
        templateUrl: "Views/ResolutionsAndDecisionsView.html",
        controller: "resolutionsAndDecisionsController"
    })
    .state("contact-us", {
        url: "/:biennium/contact-us",
        templateUrl: "Views/ContactUsView.html",
        controller: "contactUsController"
    })
    .state("search", {
        url: "/:biennium/search",
        templateUrl: "Views/SearchResultsView.html"
    });

    $urlRouterProvider.otherwise(function ($injector, $location) {
        var $state = $injector.get("$state");
        var $rootScope = $injector.get("$rootScope");
        $state.go("404", {
            biennium: $rootScope.biennium
        });

        return $location.path();
    });


    $httpProvider.interceptors.push(["$rootScope", "$q", "$injector", "$window", function ($rootScope, $q, $injector, $window) {
        return {
            responseError: function (response) {
                var $state = $injector.get("$state");
                if (response.status == 404) {
                    $state.go("404", {
                        biennium: $rootScope.biennium,
                        message: response.data.message
                    });
                } else if (response.status == 503) {
                    $window.location.href = "views/maintenance.html";
                }

                return $q.reject(response);
            }
        };
    }]);
}]);

APP.run(["$rootScope", "$state", "$browser", "$location", "uiService", function ($rootScope, $state, $browser, $location, uiService) {
    $rootScope.biennium = BIENNIUMS.default;
    $rootScope.baseUrl = $browser.baseHref();
    $rootScope.scrollToTop = function () {
        $("html, body").animate({
            scrollTop: 0
        }, 600);
    };

    $rootScope.$on("$stateChangeSuccess", function (event, toState, toParams, fromState, fromParams) {
        uiService.activateTabsForCurrentState();

        var biennium = toParams.biennium;

        if (typeof biennium != "undefined" && biennium) {
            $rootScope.biennium = biennium;
        }

        if (toState && toState.url) {
            ga('set', 'page', $location.url());
            ga('send', 'pageview');
        }
    });

    $rootScope.$on("$stateChangeStart", function () {
        $rootScope.scrollToTop();
        uiService.closeMegaMenu();
    });
}]);

APP.filter('ifNullOrEmpty', function () {
    return function (input, defaultValue) {
        if (angular.isUndefined(input) || input === null || input === '') {
            return defaultValue;
        }

        return input;
    };
});

APP.filter('newLineToBr', function () {
  return function (text) {
    return text && text.replace ? text.replace(/\n/g, '<br/>') : text;
  };
});

//TODO: Move outside
APP.directive("postRender", ["$timeout", function ($timeout) {
    return {
        restrict: 'A',
        transclude: false,
        link: function (scope, element, attrs) {
            $timeout(scope.$eval(attrs.postRender), 0);
        }
    };
}]);

APP.directive("onClass", [function () {
    return {
        restrict: "A",
        link: function (scope, element, attrs) {
            var actionsForClasses = scope.$eval(attrs.onClass);
            scope.$watch(function () {
                return element.attr("class");
            }, function (newValue) {
                for (var elementClass in actionsForClasses) {
                    if (newValue && newValue.indexOf(elementClass) !== -1) {
                        scope.$eval(actionsForClasses[elementClass]);
                    }
                }
            });
        },
    };
}]);
;
var DataState = function () {
    this.isLoaded = false;
    this.data = null;
    this.error = null;
};

var REVIEW_CYCLE = {
    MTR: "MTR",
    EBA: "EBA"
};

FILTER_TYPE_MO = "MO";
FILTER_TYPE_LEVEL = "LEVEL";
FILTER_TYPE_BUDGET_SEGMENT = "BUDGET_SEGMENT";
FILTER_TYPE_SEGMENT = "SEGMENT";
FILTER_TYPE_CATEGORY = "CATEGORY";
FILTER_TYPE_PROGRAMME = "PROGRAMME";
FILTER_TYPE_BIENNIUM = "BIENNIUM";
FILTER_TYPE_CONTRACT = "CONTRACT_TYPE";
FILTER_TYPE_APPOINTMENT = "APPOINTMENT_TYPE";
FILTER_TYPE_JOB_CATEGORY = "JOB_CATEGORY";
FILTER_TYPE_GENDER = "GENDER";
FILTER_TYPE_MONTH = "MONTH";
FILTER_TYPE_GRADE_CATEGORY = "GRADE_CATEGORY";
FILTER_TYPE_REGION = "REGION";
FILTER_TYPE_COUNTRY = "COUNTRY";
FILTER_TYPE_STATUS = "STATUS";
FILTER_TYPE_PRIORITY = "PRIORITY";
FILTER_TYPE_GRADE = "GRADE";

var RelationsFilters = function (groupsTypes, config, eventsHandlers) {
    var self = this;

    self.config = config;
    self.eventsHandlers = eventsHandlers;
    self.all = null;
    self.groups = {};
    self.selected = {};
    groupsTypes = groupsTypes || [];
    groupsTypes.forEach(function (type) {
        self.groups[type] = [];
        self.selected[type] = null;
    });

    self.buildGroups = function (filters) {
        if (!filters) {
            return;
        }

        self.all = filters;
        groupsTypes.forEach(function (type) {
            self.groups[type] = [];
            self.selected[type] = null;
        });

        self.updateGroups();
    };

    self.updateGroups = function () {
        var relations = [];
        if (self.eventsHandlers && self.eventsHandlers.onRelationsBuild) {
            self.eventsHandlers.onRelationsBuild(relations);
        }

        groupsTypes.forEach(function (type) {
            self.groups[type] = getFiltersGroup(type, relations);
            var selectedFilter = self.selected[type];
            if (!selectedFilter) {
                selectedFilter = self.groups[type][0];
                self.selected[type] = selectedFilter;
            }

            if (selectedFilter.value !== "*") {
                relations.push(selectedFilter.value);
            }
        });
    };

    self.getSelectedValueForType = function (type) {
        var selectedFilter = self.selected[type];
        return selectedFilter ? selectedFilter.value : "*";
    };

    self.getSelectedValuesForType = function (type) {
        var selectedFilter = self.selected[type];
        return selectedFilter
                ? (Array.isArray(selectedFilter) ? selectedFilter.map(f => f.value) : selectedFilter.value)
                : "*";
    };

    self.setSelectedValuesForType = function (type, values) {
        var filters = self.all.filter(function (filter) {
            return filter.type == type && values.some(v => v == filter.value);
        });

        if (filters.length > 0) {
            self.selected[type] = filters;
        }
    };

    self.setSelectedValueForType = function(type, value) {
        value = value == "all" ? "*" : value;
        var filters = self.all.filter(function(filter) {
            return filter.type == type && filter.value == value;
        });

        if (filters.length > 0) {
            self.selected[type] = filters[0];
            self.onChanged(type);
        }
    };

    self.exists = function () {
        return !!self.all;
    };

    self.onMultiSelectChanged = function (value, checked) {
        var selectedFilter = self.all.find(i => i.$$hashKey == value);
        var type = selectedFilter.type;

        var typeIndex = $.inArray(type, groupsTypes);
        if (typeIndex < 0) {
            return;
        }

        var selectStr = "[name='" + type + "']";

        if (selectedFilter.value === "*") { // if selects 'All' deselect others, else don't allow to unselect itself
            if (checked) {
                $(selectStr).multiselect('deselect', self.all.filter(i => i.type == type && i.value != '*').map(i => i.$$hashKey));
            } else {
                $(selectStr).multiselect('select', value);
            }
            
        } else { // if selects others then deselect 'All', else select 'All'
            var allItem = self.all.filter(i => i.type == type && i.value === '*').map(i => i.$$hashKey);
            if (checked) {
                $(selectStr).multiselect('deselect', allItem);
            } else {
                if ($(selectStr + " option:selected").length == 0) {
                    $(selectStr).multiselect('select', allItem);
                }
            }
        }
    }

    self.onChanged = function (type) {
        var typeIndex = $.inArray(type, groupsTypes);
        if (typeIndex < 0) {
            return;
        }

        for (var i = typeIndex + 1; i < groupsTypes.length; i++) {
            var groupType = groupsTypes[i];
            self.selected[groupType] = null;
        }

        self.updateGroups();
    };

    function getFiltersGroup(type, relations) {
        if (self.all == null) {
            return [];
        }

        relations = relations || [];
        return self.all.filter(function (filter) {
            if (filter.type != type) {
                return false;
            }

            if (!isFilterAllEnabled(type) && filter.value === "*") {
                return false;
            }

            if (relations.length == 0 || filter.relationsValues.length == 0) {
                return true;
            }

            var hasRelations = true;
            relations.forEach(function (relation) {
                if ($.inArray(relation, filter.relationsValues) < 0) {
                    hasRelations = false;
                }
            });

            return hasRelations;
        });
    }

    function isFilterAllEnabled(type) {
        if (!self.config) {
            return true;
        }

        var configForType = self.config[type];
        if (configForType && typeof configForType.filterAllEnabled != "undefined") {
            return configForType.filterAllEnabled;
        }

        return true;
    }

};

APP.service("apiService", ["$http", "$rootScope", function ($http, $rootScope) {
    var self = this;

    this.loadDataByParams = function (action, params, callback, state) {
        state = typeof state == "undefined" ? null : state;
        state = state || new DataState();
            
        params = $.extend({
            biennium: $rootScope.biennium
        }, params);

        state.isLoaded = false;
        $http.get(URL_API(action), { params: params }).then(function (successResponse) {
            state.isLoaded = true;

            var responseData = successResponse.data;
            if (responseData.success) {
                state.data = responseData.data;
            }

            if (callback) {
                callback(state.data);
            }
        }, function (failureResponse) {
            state.isLoaded = true;
            state.error = failureResponse;

            console.log(failureResponse);

            if (callback) {
                callback(state.data);
            }
        });
    };

    this.loadData = function(action, callback, state) {
        self.loadDataByParams(action, {}, callback, state);
    };

    this.loadDataById = function (action, id, callback, state) {
        self.loadDataByParams(action, { id: id }, callback, state);
    };

    this.sendData = function (action, params, callback, state) {
        state = typeof state == "undefined" ? null : state;
        state = state || new DataState();

        state.isLoaded = false;
        $http.post(URL_API(action), params).then(function (successResponse) {
            state.isLoaded = true;

            if (callback) {
                callback(successResponse);
            }
        }, function (failureResponse) {
            state.isLoaded = true;
            state.error = failureResponse;

            if (callback) {
                callback(failureResponse);
            }
        });
    };

    this.isHumanResourcesEnabled = function(biennium) {
        return this.getBienniumMinYear(biennium) >= 2020;
    }

    this.isCFEEnabled = function (biennium) {
        return this.getBienniumMinYear(biennium) >= 2022;
    }

    this.getBienniumMinYear = function(biennium) {
        if (biennium) {
            var parts = biennium.split('-');
            if (parts.length > 0) {
                return parseInt(parts[0]);
            }
        }

        return 0;
    }
}]);

;
APP.service("categoriesService", ["apiService", "$http", "$rootScope", "$interpolate", function (apiService, $http, $rootScope, $interpolate) {
    var self = this;

    this.loadCategories = function (callback, state) {
        state = typeof state == "undefined" ? null : state;
        state = state || new DataState();

        var biennium = $rootScope.biennium;
        state.data = [];
        var cache = CACHE.getCacheForBiennium(biennium);
        if (cache.ourWork) {
            state.data = cache.ourWork;
            if (callback) {
                callback(state.data);
            }

            return;
        }

        state.isLoaded = false;
        var params = { biennium: biennium };
        $http.get(URL_API("categories"), { params: params }).then(function (successResponse) {
            state.isLoaded = true;

            var responseData = successResponse.data;
            if (responseData.success) {
                state.data = responseData.data;
                cache.ourWork = state.data;
            }

            if (callback) {
                callback(state.data);
            }
        }, function (failureResponse) {
            state.isLoaded = true;
            state.error = failureResponse;

            console.log(failureResponse);

            if (callback) {
                callback(state.data);
            }
        });
    };

    this.loadAbout = function (id, callback, state) {
        apiService.loadDataById("categories/about", id, callback, state);
    };

    this.loadBudgetSegments = function (callback, state) {
        apiService.loadData("categories/budget-segments", callback, state);
    };

    this.loadOutcomes = function (id, callback, state) {
        apiService.loadDataById("categories/about-outcomes", id, callback, state);
    };

    this.loadKeyFigures = function (id, callback, state) {
        apiService.loadDataById("categories/about-key-figures", id, callback, state);
    };

    this.loadKeyFiguresByLevel = function (id, callback, state) {
        apiService.loadDataById("categories/about-key-figures-level", id, callback, state);
    };

    this.loadKeyFiguresByGeography = function (id, callback, state) {
        apiService.loadDataById("categories/about-key-figures-geography", id, callback, state);
    };

    this.loadFinancingFlow = function (filters, callback, state) {
        apiService.loadDataByParams("categories/financing-flow", filters, callback, state);
    };

    this.loadReviewCycleTabs = function (filters, callback, state) {
        apiService.loadDataByParams("categories/review-cycle-tabs", filters, callback, state);
    };

    this.loadReviewCycleKeyFigures = function (filters, callback, state) {
        apiService.loadDataByParams("categories/review-cycle-key-figures", filters, callback, state);
    };

    this.getDetailedReportLink = function (categoryId, reviewCycle) {
        if (!categoryId || !reviewCycle || !CONFIG) {
            return null;
        }
        try {
            return $interpolate(CONFIG.categoryDetailedReportLink)({
                categoryId: categoryId,
                reviewCycle: reviewCycle.toLowerCase(),
                biennium: $rootScope.biennium
            });
        } catch (error) {
            console.error(error);
            return null;
        }
    };
}]);;
APP.service("categoryHumanResourceService", ["apiService", "$http", "$rootScope", "$interpolate", function (apiService, $http, $rootScope, $interpolate) {
    var self = this;

    this.loadCategories = function (callback, state) {
        state = typeof state == "undefined" ? null : state;
        state = state || new DataState();

        var biennium = $rootScope.biennium;
        state.data = [];
        var cache = CACHE.getCacheForBiennium(biennium);
        if (cache.ourWork) {
            state.data = cache.ourWork;
            if (callback) {
                callback(state.data);
            }

            return;
        }

        state.isLoaded = false;
        var params = { biennium: biennium };
        $http.get(URL_API("categories"), { params: params }).then(function (successResponse) {
            state.isLoaded = true;

            var responseData = successResponse.data;
            if (responseData.success) {
                state.data = responseData.data;
                cache.ourWork = state.data;
            }

            if (callback) {
                callback(state.data);
            }
        }, function (failureResponse) {
            state.isLoaded = true;
            state.error = failureResponse;

            console.log(failureResponse);

            if (callback) {
                callback(state.data);
            }
        });
    };

    this.loadAbout = function (id, callback, state) {
        apiService.loadDataById("categories/about", id, callback, state);
    };

    this.loadBudgetSegments = function (callback, state) {
        apiService.loadData("categories/budget-segments", callback, state);
    };

    this.loadOutcomes = function (id, callback, state) {
        apiService.loadDataById("categories/about-outcomes", id, callback, state);
    };

    this.loadKeyFigures = function (id, callback, state) {
        apiService.loadDataById("categoryHumanResource/about-key-figures", id, callback, state);
    };

    this.loadKeyFiguresByLevel = function (id, callback, state) {
        apiService.loadDataById("categoryHumanResource/about-key-figures-level", id, callback, state);
    };

    this.loadKeyFiguresByGeography = function (id, callback, state) {
        apiService.loadDataById("categoryHumanResource/about-key-figures-geography", id, callback, state);
    };

}]);;
APP.service("chartsService", ["$rootScope", "$timeout", function ($rootScope, $timeout) {
    var self = this;

    this.colors = ['#FFC000', '#376092', '#ED7D31', '#5B9BD5', '#A5A5A5', '#A9D18E'];
    this.colors2 = ['#FFC000', '#376092'];

    this.makeColumnChart = function (chartId, data, mappings) {
        if (!data || !mappings) {
            return;
        }

        var graphs = [];
        for (var graphField in mappings.graphs) {
            var graphMappings = mappings.graphs[graphField];
            var graph = {
                title: graphMappings.title,
                valueField: graphField,
                fillAlphas: 1,
                lineAlpha: 1,
                type: "column",
                showBalloon: !!graphMappings.showBalloon,
                newStack: graphMappings.newStack ? graphMappings.newStack : false,
                labelPosition: graphMappings.labelPosition ? graphMappings.labelPosition : "middle",
                labelFunction: function (value) {
                    if (!mappings.useOriginalLegendNumbers) {
                        return Number(value.values.value / 1000000).toLocaleString(undefined, { maximumFractionDigits: typeof (mappings.maximumFractionDigits) == "number" ? mappings.maximumFractionDigits : 1 }) + 'M';
                    }
                    return Number(value.values.value).toLocaleString() + (mappings.showPercentageValue ? (' (' + value.values.percents.toFixed(1) + '% )') : '');
                },
                labelText: graphMappings.showLabel ? '999' : '',
                color: graphMappings.labelColor ? graphMappings.labelColor : "#ffffff",
                fontSize: graphMappings.labelFontSize ? graphMappings.labelFontSize : 13,
                showAllValueLabels: false,
            };

            if (graphMappings.lineColor) {
                graph.lineColor = graphMappings.lineColor;
            }

            if (graphMappings.fillColors) {
                graph.fillColors = graphMappings.fillColors;
            }

            graphs.push(graph);
        }

        var category = mappings.category;
        var chartConfiguration = {
            type: 'serial',
            theme: "light",
            rotate: !!mappings.rotate,
            autoMargins: true,
            color: "#376092",
            fontFamily: "'Roboto Condensed'",
            fontSize: 18,
            responsive: {
                enabled: true
            },
            startEffect: "bounce",
            startDuration: 1,
            dataProvider: data,
            graphs: graphs,
            legend: {
                position: "bottom",
                autoMargins: true,
                maxcolumns: 2,
                align: "center",
                fontSize: 16,
                useGraphSettings: false,
                marginLeft: 0,
                marginRight: 0,
                valueText: '',
                equalWidths: false
            },
            valueAxes: [
                {
                    stackType: "regular",
                    axisAlpha: 0,
                    gridAlpha: 0.1,
                    labelsEnabled: true,
                    color: "#AAA",
                    inside: false,
                    labelFunction: function (value) {
                        if (!mappings.useOriginalLegendNumbers) {
                            return Number(value / 1000000).toLocaleString() + 'M';
                        }

                        return Number(value).toLocaleString();
                    },
                    unit: "K"
                }
            ],
            chartCursor: {
                fullWidth: true,
                oneBalloonOnly: true,
                cursorAlpha: 0.1,
                lineAlpha: 1,
                cursorColor: "#736dba",
                categoryBalloonColor: "#736dba",
                categoryBalloonEnabled: false
            },
            balloon: {
                
                //drop: true,
                adjustBorderColor: true,
                borderAlpha: 0,
                color: "#000000",
                cornerRadius: -100,
                pointerWidth: 0,
                fillColor: "#fff",
                fillAlpha: 1,
                showBullet: false,
                pointerOrientation: "top"
            },
            categoryField: category.field,
            categoryAxis: {
                position: "bottom",
                gridAlpha: 0,
                axisAlpha: 0,
                color: "#69727c",
                tickLength: 0,
                autoWrap: true,
                boldLabels: true,
                fontSize: 13,
                labelsEnabled: (category.labelsEnabled != null && typeof (category.labelsEnabled) != "undefined") ? category.labelsEnabled : true
            }
        };

        $timeout(function () {
            AmCharts.makeChart(chartId, chartConfiguration);
        }, 0);
    };

    this.makeColumn100Chart = function (chartId, data, mappings) {
        if (!data || !mappings) {
            return;
        }

        var graphs = [];
        for (var graphField in mappings.graphs) {
            var graphMappings = mappings.graphs[graphField];
            var graph = {
                title: graphMappings.title,
                valueField: graphField,
                fillAlphas: 1,
                lineAlpha: 1,
                type: "column",
                showBalloon: !!graphMappings.showBalloon,
                newStack: graphMappings.newStack ? graphMappings.newStack : false,
                labelPosition: graphMappings.labelPosition ? graphMappings.labelPosition : "middle",
                labelText: graphMappings.showLabel ? '[[percents]]%' : '',
                color: graphMappings.labelColor ? graphMappings.labelColor : "#ffffff",
                fontSize: graphMappings.labelFontSize ? graphMappings.labelFontSize : 13,
                showAllValueLabels: false,
            };

            if (graphMappings.lineColor) {
                graph.lineColor = graphMappings.lineColor;
            }

            if (graphMappings.fillColors) {
                graph.fillColors = graphMappings.fillColors;
            }

            graphs.push(graph);
        }

        var category = mappings.category;
        var chartConfiguration = {
            type: 'serial',
            theme: "light",
            rotate: !!mappings.rotate,
            autoMargins: true,
            color: "#376092",
            fontFamily: "'Roboto Condensed'",
            fontSize: 18,
            responsive: {
                enabled: true
            },
            startEffect: "bounce",
            startDuration: 1,
            dataProvider: data,
            graphs: graphs,
            legend: {
                position: "bottom",
                autoMargins: true,
                maxcolumns: 2,
                align: "center",
                fontSize: 16,
                useGraphSettings: false,
                marginLeft: 0,
                marginRight: 0,
                valueText: '',
                equalWidths: false
            },
            valueAxes: [
                {
                    stackType: "100%",
                    axisAlpha: 0,
                    gridAlpha: 0.1,
                    labelsEnabled: true,
                    color: "#AAA",
                    inside: false,
                    labelFunction: function (value) {
                        return value + '%';
                    },
                }
            ],
            chartCursor: {
                fullWidth: true,
                oneBalloonOnly: true,
                cursorAlpha: 0.1,
                lineAlpha: 1,
                cursorColor: "#736dba",
                categoryBalloonColor: "#736dba",
                categoryBalloonEnabled: false
            },
            balloon: {

                //drop: true,
                adjustBorderColor: true,
                borderAlpha: 0,
                color: "#000000",
                cornerRadius: -100,
                pointerWidth: 0,
                fillColor: "#fff",
                fillAlpha: 1,
                showBullet: false,
                pointerOrientation: "top"
            },
            categoryField: category.field,
            categoryAxis: {
                position: "bottom",
                gridAlpha: 0,
                axisAlpha: 0,
                color: "#69727c",
                tickLength: 0,
                autoWrap: true,
                boldLabels: true,
                fontSize: 13,
                labelsEnabled: (category.labelsEnabled != null && typeof (category.labelsEnabled) != "undefined") ? category.labelsEnabled : true
            }
        };

        $timeout(function () {
            AmCharts.makeChart(chartId, chartConfiguration);
        }, 0);
    };

    this.makePieChart = function (chartId, data, mappings) {
        if (!data || !mappings) {
            return;
        }

        var chartConfiguration = {
            type: "pie",
            titles: [
                {
                    text: mappings.title
                }
            ],
            dataProvider: data,
            valueField: mappings.valueField,
            titleField: mappings.titleField,
            theme: "light",
            precision: 1,
            angle: 0,
            depth3D: 0,
            startEffect: "bounce",
            startDuration: 1,
            addClassNames: true,

            legend: {
                position: "bottom",
                marginRight: 100,
                autoMargins: true,
                maxColumns: 5,
                equalWidths: false,
                align: "center",
                valueFunction: function (dataItem) {
                    if (typeof mappings.valuesEnabled != "undefined" && !mappings.valuesEnabled) {
                        return "";
                    } else if (mappings.useOriginalLegendNumbers) {
                        return dataItem.value;
                    }
                    else {
                        return Number(Math.round((dataItem.value / 1000000) * 100) / 100).toLocaleString();
                    }
                }
            },
            fontFamily: "'Roboto Condensed'",
            fontSize: 14,
            innerRadius: "20%",
            defs: {
                filter: [
                    {
                        id: "shadow",
                        width: "200%",
                        height: "200%",
                        feOffset: {
                            result: "offOut",
                            "in": "SourceAlpha",
                            dx: 0,
                            dy: 0
                        },
                        feGaussianBlur: {
                            result: "blurOut",
                            "in": "offOut",
                            stdDeviation: 5
                        },
                        feBlend: {
                            "in": "SourceGraphic",
                            in2: "blurOut",
                            mode: "normal"
                        }
                    }
                ]
            },
            "export": {
                enabled: true
            },
        };

        if (typeof mappings.labelsEnabled != "undefined") {
            chartConfiguration.labelsEnabled = mappings.labelsEnabled;
        }

        if (mappings.colors) {
            chartConfiguration.colors = mappings.colors;
        }

        if (mappings.radius) {
            chartConfiguration.radius = mappings.radius;
        }

        if (mappings.innerRadius) {
            chartConfiguration.innerRadius = mappings.innerRadius;
        }

        if (mappings.config) {
            chartConfiguration = $.extend(true, chartConfiguration, mappings.config);
        }

        if (typeof mappings.legendsEnabled != "undefined") {
            chartConfiguration.legend.enabled = mappings.legendsEnabled;
        }

        if (mappings.precision) {
            chartConfiguration.precision = mappings.precision;
        }

        if (mappings.labelText) {
            chartConfiguration.labelText = mappings.labelText;
        }

        if (mappings.labelRadius) {
            chartConfiguration.labelRadius = mappings.labelRadius;
        }

        $timeout(function () {
            AmCharts.makeChart(chartId, chartConfiguration);
        }, 100);
    };

    this.makeIndicatorChart = function (chartId, rawData) {
      if (!rawData || !chartId) {
        return;
      }

      var dataProvider = indicatorChartConstructDataProvider(rawData);
      if (dataProvider == null) {
        return;
      }

      // Trendlines
      var trendlines = [
        indicatorChartConstructTargetTrendline(rawData),
        indicatorChartConstructTarget2Trendline(rawData),
        indicatorChartConstructAchievedTrendline(rawData),
        indicatorChartConstructAchieved2Trendline(rawData)
      ];

      var valueSuffix = ((rawData.dataType || '').toLowerCase() == 'ratio') ? '%' : '';
      var valueUnits = (!rawData.valueUnits) ? '' : rawData.valueUnits.charAt(0).toUpperCase() + rawData.valueUnits.substr(1).toLowerCase();

      var bulletSizes = indicatorChartCalcBulletSizes(rawData);

        // Legend data
      var legendData = [];
      function addLegendData(value, title, color) {
        if (value != null) {
          legendData.push({
            title: buildLabel(title , value + valueSuffix),
            color: color
          });
        }
      }

      addLegendData(rawData.baselineValue, "Baseline", "#DBEEF9");
      addLegendData(rawData.achievedValue, "Result", "#008DC9");
      addLegendData(rawData.achieved2Value, "Result 2", "#008DC9");
      addLegendData(rawData.targetValue, "Target", "#939393");
      addLegendData(rawData.target2Value, "Target 2", "#939393");

        // Resize divs before making chart
      resizeIndicatorChart(chartId, dataProvider.length);

        var chart = AmCharts.makeChart(chartId, {
            "type": "serial",
            "addClassNames": true,
            "theme": "light",
            "autoMarginOffset": 30,
            "balloon": {
                "enabled": false,
                "adjustBorderColor": false,
                "horizontalPadding": 10,
                "verticalPadding": 8,
                "color": "#ffffff"
            },

            "dataProvider": dataProvider,

            "legend": {
                "position": "bottom",
                "markerType": "round",
                "markerSize": 20,
                "maxColumns": 3,
                "data": legendData,
                "valueWidth": 15
                //  width: "300px"
            },

            "valueAxes": [{
                "axisAlpha": 0,
                "position": "left",
                "minimum": 0, // y-axis must always starts at 0
                "maximum": 1.1 * Math.max(rawData.achievedValue, rawData.achieved2Value, rawData.baselineValue, rawData.targetValue, rawData.target2Value)
            }],

            "categoryField": "year",
            "categoryAxis": {
                "gridPosition": "start",
                "axisAlpha": 0,
                "autoWrap": true
            },

            "allLabels": [
                // Category axis
                {
                    "text": "Year",
                    "color": "#888",
                    "x": "!20",
                    "y": "!20",
                    "width": "50%",
                    "size": 15,
                    "bold": true,
                    "align": "right"
                },
                // Value axis
                {
                    "text": valueUnits,
                    "color": "#888",
                    "rotation": 270,
                    "x": "10",
                    "y": "10",
                    "width": "50%",
                    "size": 15,
                    "bold": true,
                    "align": "right"
                }],

            "trendLines": trendlines,

            "graphs": [
                {
                    "id": "target",
                    "title": "Target",
                    "valueField": "target",
                    "type": "line",

                    "balloonFunction": function (graphDataItem, graph) {
                      return buildLabel('TARGET', (graphDataItem.dataContext.targetText || graphDataItem.dataContext.target));
                    },

                    // line
                    "lineThickness": 5,
                    "lineAlpha": 0.8,
                    "lineColor": "rgb(147,147,147)",
                    "dashLength": 5,

                    // bullet
                    "bullet": "round",
                    "bulletSize": bulletSizes.target,
                    "labelText": "[[value]]" + valueSuffix,
                    "labelPosition": "middle",
                    "color": "#fff",
                }, 
                {
                    "id": "target2",
                    "title": "Target 2",
                    "valueField": "target2",
                    "type": "line",

                    "balloonFunction": function (graphDataItem, graph) {
                      return buildLabel('TARGET 2', (graphDataItem.dataContext.target2Text || graphDataItem.dataContext.target2));
                    },

                    // line
                    "lineThickness": 5,
                    "lineAlpha": 0.8,
                    "lineColor": "rgb(147,147,147)",
                    "dashLength": 5,

                    // bullet
                    "bullet": "round",
                    "bulletSize": bulletSizes.target2,
                    "labelText": "[[value]]" + valueSuffix,
                    "labelPosition": "middle",
                    "color": "#fff",
                },
                {
                    "id": "baseline",
                    "title": "Baseline",
                    "type": "line",
                    "valueField": "baseline",

                    "balloonColor": "#008dc9",
                    "balloonFunction": function (graphDataItem, graph) {
                      return buildLabel('BASELINE', (graphDataItem.dataContext.baselineText || graphDataItem.dataContext.baseline));
                    },

                    // line and dot color
                    "lineColor": "#DBEEF9",
                    "lineThickness": 5,
                    "lineAlpha": 0.8,

                    // bullet
                    "bullet": "round",
                    "bulletSize": bulletSizes.baseline,
                    "labelText": "[[value]]" + valueSuffix,
                    "labelPosition": "middle",
                    "color": "#196AAA", // text color
                    "bulletBorderColor": "#008dc9",
                    "bulletBorderThickness": 5,
                    "bulletBorderAlpha": 1,
                },
                {

                    "id": "achieved",
                    "title": "Result",
                    "type": "line",
                    "valueField": "achieved",

                    "balloonFunction": function (graphDataItem, graph) {
                      return buildLabel('RESULT', (graphDataItem.dataContext.achievedComment || graphDataItem.dataContext.achieved));
                    },

                    // line
                    "lineColor": "#008dc9",
                    "colorField": "color",
                    "lineThickness": 5,
                    "lineAlpha": 0.8,

                    // bullet
                    "bullet": "round",
                    "bulletSize": bulletSizes.achieved,
                    "labelText": "[[value]]" + valueSuffix,
                    "labelPosition": "middle",
                    "color": "#fff",
                },
                {

                    "id": "achieved2",
                    "title": "Result 2",
                    "type": "line",
                    "valueField": "achieved2",

                    "balloonFunction": function (graphDataItem, graph) {
                      return buildLabel('RESULT 2', (graphDataItem.dataContext.achieved2Comment || graphDataItem.dataContext.achieved2));
                    },

                    // line
                    "lineColor": "#008dc9",
                    "colorField": "color",
                    "lineThickness": 5,
                    "lineAlpha": 0.8,

                    // bullet
                    "bullet": "round",
                    "bulletSize": bulletSizes.achieved2,
                    "labelText": "[[value]]" + valueSuffix,
                    "labelPosition": "middle",
                    "color": "#fff"
                }

            ]
        });

        var $indicatorTooltip = $(sprintf("#%s-tooltip", chartId));

        chart.addListener("rollOverGraphItem", function (event) {
            var graphDataItem = event.item;
            var graph = graphDataItem.graph;
            if (!graphDataItem.$customTooltip) {
                graphDataItem.$customTooltip = $("<div></div>");
                graphDataItem.$customTooltip.appendTo($indicatorTooltip);
            }

            var $customTooltip = graphDataItem.$customTooltip;
            var text = graph.balloonFunction(graphDataItem, graph);
            if (text) {
                var rect = graphDataItem.bulletGraphics.node.getBoundingClientRect();
                $customTooltip.attr("data-original-title", text);
                $customTooltip.css("position", "fixed");
                $customTooltip.css("top", sprintf("%spx", window.pageYOffset + rect.top));
                $customTooltip.css("left", sprintf("%spx", window.pageXOffset + rect.left + rect.width / 2));
                $customTooltip.tooltip("show");
            }
        });

        chart.addListener("rollOutGraphItem", function (event) {
            if (event.item.$customTooltip) {
                event.item.$customTooltip.tooltip("hide");
            }
        });
    };

    // Returns amCharts dataProvider object for a serial type chart.
    //
    // rawData should have following structure:
    //var rawData = {
    //    indicatorText: null,
    //    dataType: null, // Either 'ratio', 'number', 'number of countries'
    //    valueUnits: null,
    //    baselineYear: null,
    //    baselineValue: null,
    //    achievedYear: null,
    //    achievedValue: null,
    //    targetYear: null,
    //    targetValue: null,
    //};
    function indicatorChartConstructDataProvider(rawData) {

      var years = [rawData.baselineYear, rawData.achievedYear, rawData.achieved2Year, rawData.targetYear, rawData.target2Year];
      years = $.grep(years, function (e) { return e != null; });
      if (years.length == 0) {
        console.log("Baseline, result and target years are all null.  Unable to create graph.");
        return null;
      }

      // **** Determine start and end year
        var startYear = Math.min.apply(this, years);
        var endYear = Math.max.apply(this, years);

        //  ** Pad years so it looks better if too narrow
        if (endYear - startYear <= 1)
            endYear += 1;

        // **** Validation
        // endYear must be GTE startYear
        if (endYear < startYear) {
            console.log("End year is less than start year.  Unable to create graph.");
            return null;
        }

        // **** Create dataProvider
        var dp = new Array();

        // ** Create a data item for each year
        for (var iYear = startYear; iYear <= endYear; iYear++) {
            var item = {};
            item.year = iYear.toString();
            dp.push(item);

            // baseline
            if (rawData.baselineYear == iYear) {
                item.baseline = rawData.baselineValue;
                item.baselineText = rawData.baselineValueText;
                //   item.valueType = "baseline";
            }

            if (rawData.achievedYear == iYear) {
                item.achieved = rawData.achievedValue;
                item.achievedComment = rawData.achievedComment;
                //  item.valueType = "achieved";
            }

            if (rawData.achieved2Year == iYear) {
                item.achieved2 = rawData.achieved2Value;
                item.achieved2Comment = rawData.achieved2Comment;
                //  item.valueType = "achieved";
            }

            // target? (can co-exist with baseline or result)
            if (rawData.targetYear == iYear) {
                item.target = rawData.targetValue;
                item.targetText = rawData.targetValueText;
                //   item.valueType = (item.valueType == null) ? "target" : item.valueType + " & " + "target";
            }

            if (rawData.target2Year == iYear) {
                item.target2 = rawData.target2Value;
                item.target2Text = rawData.target2ValueText;
                //   item.valueType = (item.valueType == null) ? "target" : item.valueType + " & " + "target";
            }
        }

        return dp;
    }

    // Returns amCharts target trend line.
    // Construct a trendline to target value from either baseline or achieved value
    function indicatorChartConstructTargetTrendline(rawData) {
        var result = {};

        // initial point
        if (rawData.baselineValue != null && rawData.baselineYear != null) {
            result.initialValue = rawData.baselineValue;
            result.initialCategory = rawData.baselineYear;
        }
        else if (rawData.achievedValue != null && rawData.achievedYear != null) {
            result.initialValue = rawData.achievedValue;
            result.initialCategory = rawData.achievedYear;
        }

        // final point
        if (rawData.targetValue != null && rawData.targetYear != null) {
            result.finalValue = rawData.targetValue;
            result.finalCategory = rawData.targetYear;
        }

        // Common properties
        result.lineThickness = 5;
        result.lineAlpha = 1.0; //0.8;
        result.lineColor = "rgb(147,147,147)";
        result.dashLength = 5;

        return result;
    }

    // Returns amCharts target trend line.
    // Construct a trendline to target value from either baseline or achieved value
    function indicatorChartConstructTarget2Trendline(rawData) {
        var result = {};

        // initial point
        if (rawData.baselineValue != null && rawData.baselineYear != null) {
            result.initialValue = rawData.baselineValue;
            result.initialCategory = rawData.baselineYear;
        }
        else if (rawData.achieved2Value != null && rawData.achieved2Year != null) {
            result.initialValue = rawData.achieved2Value;
            result.initialCategory = rawData.achieved2Year;
        }

        // final point
        if (rawData.target2Value != null && rawData.target2Year != null) {
            result.finalValue = rawData.target2Value;
            result.finalCategory = rawData.target2Year;
        }

        // Common properties
        result.lineThickness = 5;
        result.lineAlpha = 1.0; //0.8;
        result.lineColor = "rgb(147,147,147)";
        result.dashLength = 5;

        return result;
    }

    // Returns amCharts target trend line.
    // Construct a trendline baseline to achieved value if both baseline and achieved values exist
    function indicatorChartConstructAchievedTrendline(rawData) {
        var result = {};

        if (rawData.baselineValue != null && rawData.baselineYear != null &&
            rawData.achievedValue != null && rawData.achievedYear != null) {
            result.initialValue = rawData.baselineValue;
            result.initialCategory = rawData.baselineYear;

            result.finalValue = rawData.achievedValue;
            result.finalCategory = rawData.achievedYear;
        }

        // Common properties
        result.lineThickness = 5;
        result.lineAlpha = 0.8;
        result.lineColor = "#008dc9";
        //  result.dashLength = 5;

        return result;
    }

    // Returns amCharts target trend line.
    // Construct a trendline baseline to achieved value if both baseline and achieved values exist
    function indicatorChartConstructAchieved2Trendline(rawData) {
        var result = {};

        if (rawData.baselineValue != null && rawData.baselineYear != null &&
            rawData.achieved2Value != null && rawData.achieved2Year != null) {
            result.initialValue = rawData.baselineValue;
            result.initialCategory = rawData.baselineYear;

            result.finalValue = rawData.achieved2Value;
            result.finalCategory = rawData.achieved2Year;
        }

        // Common properties
        result.lineThickness = 5;
        result.lineAlpha = 0.8;
        result.lineColor = "#008dc9";
        //  result.dashLength = 5;

        return result;
    }

    // Resizes width of chart div based on number of years in data.
    // This should be called before the chart is created.
    function resizeIndicatorChart(chartId, yearsCount) {
        var chartDiv = document.getElementById(chartId);
        if (chartDiv == null) {
            console.log('In resizeChart, chartDiv cannot be determined from chartId ' + chartId);
            return;
        }

        // Settings
        var columnWidth = 90; // width of each year
        var otherWidth = 75; // width of legend, labels, etc.

        // Resize
      var plotWidth = otherWidth + columnWidth * yearsCount;
      plotWidth = Math.max(plotWidth, 500);
      chartDiv.style.width = plotWidth + 'px';

      return;
    }

  function indicatorChartCalcBulletSizes(rawData) {
    var items = [
      { name: 'baseline', year: rawData.baselineYear, value: rawData.baselineValue },
      { name: 'achieved', year: rawData.achievedYear, value: rawData.achievedValue },
      { name: 'achieved2', year: rawData.achieved2Year, value: rawData.achieved2Value },
      { name: 'target', year: rawData.targetYear, value: rawData.targetValue },
      { name: 'target2', year: rawData.target2Year, value: rawData.target2Value }
    ];
    items = $.grep(items, function (e) { return e.value != null; });

    var maxValue = Math.max.apply(this, $.map(items, function (e) { return e.value; }));
    var significantDifference = Math.ceil(maxValue / 100 * 5);

    const defaultSize = 40;
    const sizeIncrement = 20;
    var result = {
      baseline: defaultSize,
      achieved: defaultSize,
      achieved2: defaultSize,
      target: defaultSize,
      target2: defaultSize
    };

    for (var i = 1; i < items.length; i++) {
      var item1 = items[i];
      for (var j = 0; j < i; j++) {
        var item0 = items[j];
        if (item1.year == item0.year && Math.abs(item1.value - item0.value) <= significantDifference ) {
          result[item1.name] = result[item0.name] + sizeIncrement;
        }
      }
    }

    return result;
  }


    function buildLabel(title, comment) {
      return comment ? (title + ': ' + comment) : title;
    }

}]);;
APP.service("contributorsService", ["$http", "$rootScope", "apiService", function ($http, $rootScope, apiService) {
    var self = this;

    self.loadAll = function(callback, state) {
        apiService.loadDataByParams("contributors/all", {}, callback, state);
    };

    self.loadPip = function (callback, state) {
        apiService.loadDataByParams("contributors/pip", {}, callback, state);
    };

    self.loadCfe = function (callback, state) {
        apiService.loadDataByParams("contributors/cfe", {}, callback, state);
    };

    self.loadKs = function (callback, state) {
        apiService.loadDataByParams("contributors/ks", {}, callback, state);
    };

    self.loadCvc = function (callback, state) {
        apiService.loadDataByParams("contributors/cvc", {}, callback, state);
    };

    self.loadTopCvc = function (callback, state) {
        apiService.loadDataByParams("contributors/top-cvc", {}, callback, state);
    };

    self.loadCvcFinancingFlow = function (filters, callback, state) {
        apiService.loadDataByParams("contributors/cvc-financing-flow", filters, callback, state);
    };

    self.loadFinancingFlow = function (filters, callback, state) {
        apiService.loadDataByParams("contributors/financing-flow", filters, callback, state);
    };

    self.loadFund = function (groupCode, callback, state) {
        apiService.loadDataByParams("contributors/fund", {
            groupCode: groupCode
        }, callback, state);
    };

    self.loadCfeEmergencyEvents = function (callback, state) {
        apiService.loadDataByParams("contributors/cfe-emergency-events", {}, callback, state);
    };

}]);;
APP.service("countryCategoryService", ["apiService", "$http", "$rootScope", function (apiService, $http, $rootScope) {
    var self = this;

    self.loadAbout = function (id, callback, state) {
        apiService.loadDataById("countryCategory/about", id, callback, state);
    };

    self.loadKeyFigures = function (id, callback, state) {
        apiService.loadDataById("countryCategory/key-figures", id, callback, state);
    };

    self.loadCharts = function (id, callback, state) {
        apiService.loadDataById("countryCategory/charts", id, callback, state);
    };
}]);;
APP.service("countryHRService", ["apiService", "$http", "$rootScope", function(apiService, $http, $rootScope) {

    this.loadKeyFigures = function (id, callback, state) {
        apiService.loadDataById("countryHumanResource/key-figures", id, callback, state);
    };

    this.loadCategoryKeyFigures = function (id, callback, state) {
        apiService.loadDataById("countryHumanResource/category-key-figures", id, callback, state);
    };

    this.loadProgrammeKeyFigures = function (id, callback, state) {
        apiService.loadDataById("countryHumanResource/programme-key-figures", id, callback, state);
    };
}]);;
APP.service("countryProgrammeService", ["apiService", "$http", "$rootScope", function (apiService, $http, $rootScope) {
    var self = this;

    self.loadKeyFigures = function (id, callback, state) {
        apiService.loadDataById("countryProgramme/key-figures", id, callback, state);
    };

    self.loadCharts = function (id, callback, state) {
        apiService.loadDataById("countryProgramme/charts", id, callback, state);
    };
}]);;
APP.service("countryService", ["apiService", "$http", "$rootScope", function (apiService, $http, $rootScope) {
    var self = this;

    self.loadKeyFigures = function (id, callback, state) {
        apiService.loadDataById("country/key-figures", id, callback, state);
    };

    self.loadExpensesByTypeChartData = function (id, callback, state) {
        apiService.loadDataById("country/expenses-by-type", id, callback, state);
    };

    self.loadDocuments = function (id, callback, state) {
        apiService.loadDataById("country/documents", id, callback, state);
    };

    self.loadFinancingFlow = function (filters, callback, state) {
        apiService.loadDataByParams("country/financing-flow", filters, callback, state);
    };

    self.loadCountryReview = function (filters, callback, state) {
    	apiService.loadDataByParams("country/country-review", filters, callback, state);
    };
}]);;
APP.service("documentsService", ["apiService", "$http", "$rootScope", function (apiService, $http, $rootScope) {
    var self = this;

    self.loadSectionDocuments = function (callback, state) {
        apiService.loadData("documents/section-documents", callback, state);
    };
}]);;
APP.service("financingService", ["$http", "$rootScope", "apiService", function ($http, $rootScope, apiService) {
    var self = this;

    this.loadGpwOverview = function (callback) {
        var biennium = $rootScope.biennium;
        var gpwOverview = [];
        var cache = CACHE.getCacheForBiennium(biennium);
        if (cache.gpwOverview) {
            gpwOverview = cache.gpwOverview;
            if (callback) {
                callback(gpwOverview);
            }

            return;
        }

        var params = { biennium: biennium };
        $http.get(URL_API("financing/gpw"), { params: params }).then(function (successResponse) {
            var responseData = successResponse.data;
            if (responseData.success) {
                gpwOverview = responseData.data;
                cache.gpwOverview = gpwOverview;
            }

            if (callback) {
                callback(gpwOverview);
            }
        }, function (failureResponse) {
            console.log(failureResponse);

            if (callback) {
                callback(gpwOverview);
            }
        });
    };

    this.loadBudgetAndFinanceDetailsFilters = function (viewBy, callback) {
        var biennium = $rootScope.biennium;
        var filters = null;

        var params = {
            biennium: biennium,
            viewBy: viewBy
        };

        $http.get(URL_API("financing/details-filters"), { params: params }).then(function (successResponse) {
            var responseData = successResponse.data;
            if (responseData.success) {
                filters = responseData.data;
            }

            if (callback) {
                callback(filters);
            }
        }, function (failureResponse) {
            console.log(failureResponse);

            if (callback) {
                callback(filters);
            }
        });
    };

    this.loadDetails = function (filters, callback) {
        var details = null;
        var biennium = $rootScope.biennium;
        $.extend(filters, {
            biennium: biennium
        });

        $http.get(URL_API("financing/details"), { params: filters }).then(function (successResponse) {
            var responseData = successResponse.data;
            if (responseData.success) {
                details = responseData.data;
            }

            if (callback) {
                callback(details);
            }
        }, function (failureResponse) {
            console.log(failureResponse);

            if (callback) {
                callback(details);
            }
        });
    };

    this.loadFlowDonorCategoryRegionProgramme = function(filters, callback, state) {
        apiService.loadDataByParams("financing/flow", filters, callback, state);
    };

    this.loadBudgetAndFinanceSummaryFilters = function (callback, state) {
        apiService.loadData("financing/summary-filters", callback, state);
    };

    this.loadBudgetAndFinanceSummary = function (filters, callback, state) {
        apiService.loadDataByParams("financing/summary", filters, callback, state);
    };
}]);;
APP.service("humanResourcesService", ["$http", "$rootScope", "apiService",
    function ($http, $rootScope, apiService) {

        this.getDefaultPeriod = function (callback) {
            var period = null;

            $http.get(URL_API("humanResources/default-period")).then(function (successResponse) {
                var responseData = successResponse.data;
                if (responseData.success) {
                    period = responseData.data;
                }

                if (callback) {
                    callback(period);
                }
            }, function (failureResponse) {
                console.log(failureResponse);

                if (callback) {
                    callback(period);
                }
            });
        };

        this.loadWorkforceFilters = function (callback) {
            var biennium = $rootScope.biennium;
            var filters = null;

            var params = {
                biennium: biennium,
            };

            $http.get(URL_API("humanResources/workforce-filters"), { params: params }).then(function (successResponse) {
                var responseData = successResponse.data;
                if (responseData.success) {
                    filters = responseData.data;
                }

                if (callback) {
                    callback(filters);
                }
            }, function (failureResponse) {
                console.log(failureResponse);

                if (callback) {
                    callback(filters);
                }
            });
        };

        this.loadWorkforceCountDataByType = function (type, filters, callback) {
            var biennium = $rootScope.biennium;
            var data = null;

            filters.biennium = biennium;
            filters.dataType = type;

            $http.get(URL_API("humanResources/workforce-count"), { params: filters }).then(function (successResponse) {
                var responseData = successResponse.data;
                if (responseData.success) {
                    data = responseData.data;
                }

                if (callback) {
                    callback(data);
                }
            }, function (failureResponse) {
                console.log(failureResponse);

                if (callback) {
                    callback(data);
                }
            });
        };

        this.loadGenderBalanceFilters = function (callback) {
            var biennium = $rootScope.biennium;
            var filters = null;

            var params = {
                biennium: biennium,
            };

            $http.get(URL_API("humanResources/gender-balance-filters"), { params: params }).then(function (successResponse) {
                var responseData = successResponse.data;
                if (responseData.success) {
                    filters = responseData.data;
                }

                if (callback) {
                    callback(filters);
                }
            }, function (failureResponse) {
                console.log(failureResponse);

                if (callback) {
                    callback(filters);
                }
            });
        };

        this.loadGenderBalanceTotal = function(filters, callback) {
            var biennium = $rootScope.biennium;
            var data = null;

            filters.biennium = biennium;

            $http.get(URL_API("humanResources/gender-balance-total"), { params: filters }).then(function (successResponse) {
                var responseData = successResponse.data;
                if (responseData.success) {
                    data = responseData.data;
                }

                if (callback) {
                    callback(data);
                }
            }, function (failureResponse) {
                console.log(failureResponse);

                if (callback) {
                    callback(data);
                }
            });
        }

        this.loadGenderBalanceCountDataByType = function (type, filters, callback) {
            var biennium = $rootScope.biennium;
            var data = null;

            filters.biennium = biennium;
            filters.dataType = type;

            $http.get(URL_API("humanResources/gender-balance-count"), { params: filters }).then(function (successResponse) {
                var responseData = successResponse.data;
                if (responseData.success) {
                    data = responseData.data;
                }

                if (callback) {
                    callback(data);
                }
            }, function (failureResponse) {
                console.log(failureResponse);

                if (callback) {
                    callback(data);
                }
            });
        };

        this.loadHrFlowFilters = function (type, callback) {
            var params = {
                biennium: $rootScope.biennium,
                type: type
            };
            apiService.loadDataByParams("humanResources/flow-filters", params, callback, null);
        };

        this.loadHrFlow = function (flowChartParams, callback, state) {
            apiService.loadDataByParams("humanResources/flow", flowChartParams, callback, state);
        }

        this.loadHrProgrammeFlow = function (flowChartParams, callback, state) {
            apiService.loadDataByParams("humanResources/programme-flow", flowChartParams, callback, state);
        }

        this.loadGeographicalDistributionFilters = function (callback) {
            var params = {
                biennium: $rootScope.biennium,
            };
            apiService.loadDataByParams("humanResources/gd-filters", params, callback, null);
        };

        this.loadGeographicalDistributionCommonData = function (params, callback) {
            apiService.loadDataByParams("humanResources/gd-common-data", params, callback, null);
        }

        this.loadGeographicalDistributionByCountry = function (params, callback) {
            apiService.loadDataByParams("humanResources/gd-by-country", params, callback, null);
        }

        this.loadCountryStaffingProfileData = function (params, callback) {
            apiService.loadDataByParams("humanResources/country-staffing-data", params, callback, null);
        }

        this.loadCountryStaffingProfileDataByGrade = function (params, callback) {
            apiService.loadDataByParams("humanResources/country-staffing-data-by-grade", params, callback, null);
        }

    }]);;
APP.service("iatiFilesService", ["apiService", "$http", "$rootScope", function (apiService, $http, $rootScope) {
    var self = this;

    self.loadIatiFiles = function (id, callback, state) {
      apiService.loadDataById("iatiFiles/iati-files", id, callback, state);
    };

    self.loadAllIatiFiles = function (callback, state) {
        apiService.loadData("iatiFiles/all-iati-files", callback, state);
    };

    //self.loadIatiFileContent = function (id, callback, state) {
    //    apiService.loadDataById("iatiFiles/iati-file-content", id, callback, state);
    //};
}]);;
APP.service("modalDialogsService", ["$compile", "$templateRequest", "$rootScope", "$controller", function ($compile, $templateRequest, $rootScope, $controller) {
    var self = this;

    this.show = function (options) {
        $templateRequest(options.templateUrl, true).then(function (modalTemplate) {
            var modalScope = $rootScope.$new();
            if (options.scope) {
                modalScope = $.extend(modalScope, options.scope);
            }

            if (options.controller) {
                $controller(options.controller, { "$scope": modalScope });
            }

            var modalElement = $compile(modalTemplate)(modalScope)[0];
            document.body.appendChild(modalElement);

            $(modalElement).on("hidden.bs.modal", function () {
                document.body.removeChild(modalElement);
            });

            $(modalElement).modal("show");
        }, function (error) {
            console.log(error);
        });
    };
}]);;
APP.service("programmeBudgetService", ["$http", "$rootScope", "apiService", function ($http, $rootScope, apiService) {
    var self = this;

    this.loadCategories = function(callback, state) {
        apiService.loadData("budget/categories", callback, state);
    };

    this.loadRegions = function (callback) {
        var biennium = $rootScope.biennium;
        var regions = [];
        var cache = CACHE.getCacheForBiennium(biennium);
        if (cache.regions) {
            regions = cache.regions;
            if (callback) {
                callback(regions);
            }

            return;
        }

        var params = { biennium: biennium };
        $http.get(URL_API("budget/regions"), { params: params }).then(function (successResponse) {
            var responseData = successResponse.data;
            if (responseData.success) {
                regions = responseData.data;
                cache.regions = regions;
            }

            if (callback) {
                callback(regions);
            }
        }, function (failureResponse) {
            console.log(failureResponse);

            if (callback) {
                callback(regions);
            }
        });
    };

    this.loadStatuses = function (callback) {
        var statuses = {
            categoriesGroups: [],
            categories: []
        };

        var biennium = $rootScope.biennium;
        var cache = CACHE.getCacheForBiennium(biennium);
        if (cache.statuses) {
            statuses = cache.statuses;
            if (callback) {
                callback(statuses);
            }

            return;
        }

        var params = { biennium: biennium };
        $http.get(URL_API("budget/statuses"), { params: params }).then(function (successResponse) {
            var responseData = successResponse.data;
            if (responseData.success) {
                statuses = responseData.data;
                statuses.categoriesGroups = [];

                var items = statuses.categories;
                var itemsGroup = null;
                for (var i = 0; i < items.length; i++) {
                    if (itemsGroup && itemsGroup.length >= 4) {
                        itemsGroup = null;
                    }

                    if (!itemsGroup) {
                        itemsGroup = [];
                        statuses.categoriesGroups.push(itemsGroup);
                    }

                    itemsGroup.push(items[i]);
                }

                cache.statuses = statuses;
            }

            if (callback) {
                callback(statuses);
            }
        }, function (failureResponse) {
            console.log(failureResponse);
            if (callback) {
                callback(statuses);
            }
        });
    };

    this.loadLastContributions = function (callback) {
        var lastContributions = [];
        var biennium = $rootScope.biennium;
        var cache = CACHE.getCacheForBiennium(biennium);
        if (cache.lastContributions) {
            lastContributions = cache.lastContributions;
            if (callback) {
                callback(lastContributions);
            }

            return;
        }

        var params = { biennium: biennium };
        $http.get(URL_API("budget/contributions"), { params: params }).then(function (successResponse) {
            var responseData = successResponse.data;
            if (responseData.success) {
                lastContributions = responseData.data;
                cache.lastContributions = lastContributions;
            }

            if (callback) {
                callback(lastContributions);
            }
        }, function (failureResponse) {
            console.log(failureResponse);
            if (callback) {
                callback(lastContributions);
            }
        });
    };

    this.loadCvcInfo = function (callback, state) {
        apiService.loadDataByParams("budget/cvc-info", {}, callback, state);
    };

    this.loadActiveQuarter = function(callback, state) {
        apiService.loadDataByParams("budget/active-quarter", {}, callback, state);
    };

    this.loadBudgetSettings = function (callback, state) {
        apiService.loadDataByParams("budget/budget-settings", {}, callback, state);
    };

    this.loadDocuments = function(callback) {
        var documents = [];
        var biennium = $rootScope.biennium;
        var cache = CACHE.getCacheForBiennium(biennium);
        if (cache.documents) {
            documents = cache.documents;
            if (callback) {
                callback(documents);
            }

            return;
        }

        var params = { biennium: biennium };
        $http.get(URL_API("budget/documents"), { params: params }).then(function (successResponse) {
            var responseData = successResponse.data;
            if (responseData.success) {
                documents = responseData.data;
                cache.documents = documents;
            }

            if (callback) {
                callback(documents);
            }
        }, function (failureResponse) {
            console.log(failureResponse);
            if (callback) {
                callback(documents);
            }
        });
    };
}]);;
APP.service("programmeHumanResourceService", ["apiService", "$http", "$rootScope", "$interpolate", function (apiService, $http, $rootScope, $interpolate) {
    var self = this;

    this.loadCategories = function (callback, state) {
        state = typeof state == "undefined" ? null : state;
        state = state || new DataState();

        var biennium = $rootScope.biennium;
        state.data = [];
        var cache = CACHE.getCacheForBiennium(biennium);
        if (cache.ourWork) {
            state.data = cache.ourWork;
            if (callback) {
                callback(state.data);
            }

            return;
        }

        state.isLoaded = false;
    };

    this.loadAbout = function (id, callback, state) {
        apiService.loadDataById("programmes/about", id, callback, state);
    };

    this.loadOutputs = function (id, callback, state) {
        apiService.loadDataById("programmeHumanResource/about-outputs", id, callback, state);
    };

    this.loadKeyFigures = function (id, callback, state) {
        apiService.loadDataById("programmeHumanResource/about-key-figures", id, callback, state);
    };

    this.loadKeyFiguresByLevel = function (id, callback, state) {
        apiService.loadDataById("programmeHumanResource/about-key-figures-level", id, callback, state);
    };

    this.loadKeyFiguresByGeography = function (id, callback, state) {
        apiService.loadDataById("programmeHumanResource/about-key-figures-geography", id, callback, state);
    };

}]);;
APP.service("programmesService", ["apiService", "$http", "$rootScope", "$interpolate", function (apiService, $http, $rootScope, $interpolate) {
    var self = this;

    this.loadAbout = function (id, callback, state) {
        apiService.loadDataById("programmes/about", id, callback, state);
    };

    this.loadResults = function (id, callback, state) {
        apiService.loadDataById("programmes/results", id, callback, state);
    };

    this.loadDeliverables = function (id, callback, state) {
        apiService.loadDataById("programmes/deliverables", id, callback, state);
    };

    this.loadKeyFigures = function (id, callback, state) {
        apiService.loadDataById("programmes/about-key-figures", id, callback, state);
    };

    this.loadKeyFiguresByLevel = function (id, callback, state) {
        apiService.loadDataById("programmes/about-key-figures-level", id, callback, state);
    };

    this.loadKeyFiguresByGeography = function (id, callback, state) {
        apiService.loadDataById("programmes/about-key-figures-geography", id, callback, state);
    };

    this.loadFinancingFlow = function (filters, callback, state) {
        apiService.loadDataByParams("programmes/financing-flow", filters, callback, state);
    };

    this.loadTopStories = function(id, callback, state) {
        apiService.loadDataById("programmes/top-stories", id, callback, state);
    };

    this.loadReviewCycleTabs = function (filters, callback, state) {
        apiService.loadDataByParams("programmes/review-cycle-tabs", filters, callback, state);
    };

    this.loadReviewCycleKeyFigures = function (filters, callback, state) {
        apiService.loadDataByParams("programmes/review-cycle-key-figures", filters, callback, state);
    };

    this.loadReviewCycleProgress = function (filters, callback, state) {
        apiService.loadDataByParams("programmes/review-cycle-progress", filters, callback, state);
    };

    this.loadReviewCycleRating = function (filters, callback, state) {
        apiService.loadDataByParams("programmes/review-cycle-rating", filters, callback, state);
    };

    this.loadReviewCycleIndicators = function(filters, callback, state) {
        apiService.loadDataByParams("programmes/review-cycle-indicators", filters, callback, state);
    };

    this.loadIndicatorsGraph = function(filters, callback, state) {
        apiService.loadDataByParams("programmes/indicators-graph", filters, callback, state);
    }; 

    this.loadIndicatorCountriesDetails= function (filters, callback, state) {
        apiService.loadDataByParams("programmes/indicator-countries-details", filters, callback, state);
    };

    this.loadProgrammeInfo = function (id, callback, state) {
    	apiService.loadDataById("programmes/programme-info", id, callback, state);
    };

    this.loadIndicatorsFilters = function(callback, state) {
        apiService.loadData("programmes/indicators-filters", callback, state);
    };

    this.loadIndicators = function (filters, callback, state) {
        apiService.loadDataByParams("programmes/indicators", filters, callback, state);
    };

    this.getDetailedReportLink = function (programmeId, reviewCycle) {
        if (!programmeId || !reviewCycle || !CONFIG) {
            return null;
        }

        try {
            return $interpolate(CONFIG.programmeDetailedReportLink)({
                programmeId: programmeId,
                reviewCycle: reviewCycle.toLowerCase(),
                biennium: $rootScope.biennium
            });
        } catch (error) {
            console.error(error);
            return null;
        }
    };
}]);;
APP.service("regionsHRService", ["apiService", "$http", "$rootScope", function(apiService, $http, $rootScope) {
    this.loadAbout = function (id, callback, state) {
        apiService.loadDataById("regions/about", id, callback, state);
    };

    this.loadKeyFiguresFilters = function (id, callback, state) {
        apiService.loadDataById("regionsHumanResource/key-figures-filters", id, callback, state);
    };

    this.loadKeyFiguresByLevelFilters = function (id, callback, state) {
        apiService.loadDataById("regionsHumanResource/key-figures-level-filters", id, callback, state);
    };

    this.loadKeyFiguresByGeographyFilters = function (id, callback, state) {
        apiService.loadDataById("regionsHumanResource/key-figures-geography-filters", id, callback, state);
    };

    this.loadKeyFigures = function (id, filters, callback, state) {
        apiService.loadDataByParams("regionsHumanResource/about-key-figures", {
            id: id,
            snpMonth: filters.snpMonth,
            segment: filters.segment,
            categoryId: filters.categoryId,
        }, callback, state);
    };

    this.loadKeyFiguresByLevel = function (id, filters, callback, state) {
        apiService.loadDataByParams("regionsHumanResource/about-key-figures-level", {
            id: id,
            snpMonth: filters.snpMonth,
            segment: filters.segment,
            categoryId: filters.categoryId,
            programmeId: filters.programmeId,
        }, callback, state);
    };

    this.loadKeyFiguresByGeography = function (id, filters, callback, state) {
        apiService.loadDataByParams("regionsHumanResource/about-key-figures-geography", {
            id: id,
            snpMonth: filters.snpMonth,
            segment: filters.segment,
            categoryId: filters.categoryId,
            programmeId: filters.programmeId,
        }, callback, state);
    };

    this.loadHqKeyFiguresFilters = function (id, callback, state) {
        apiService.loadDataById("regionsHumanResource/hq-key-figures-filters", id, callback, state);
    };

    this.loadHqKeyFiguresByProgrammeFilters = function (id, callback, state) {
        apiService.loadDataById("regionsHumanResource/hq-key-figures-programme-filters", id, callback, state);
    };

    this.loadHqKeyFiguresByOutputFilters = function (id, callback, state) {
        apiService.loadDataById("regionsHumanResource/hq-key-figures-output-filters", id, callback, state);
    };

    this.loadHqKeyFigures = function (id, filters, callback, state) {
        apiService.loadDataByParams("regionsHumanResource/hq-about-key-figures", {
            id: id,
            snpMonth: filters.snpMonth,
            segment: filters.segment,
        }, callback, state);
    };

    this.loadHqKeyFiguresByProgramme = function (id, filters, callback, state) {
        apiService.loadDataByParams("regionsHumanResource/hq-about-key-figures-programme", {
            id: id,
            snpMonth: filters.snpMonth,
            segment: filters.segment,
            categoryId: filters.categoryId
        }, callback, state);
    };

    this.loadHqKeyFiguresByOutput = function (id, filters, callback, state) {
        apiService.loadDataByParams("regionsHumanResource/hq-about-key-figures-output", {
            id: id,
            snpMonth: filters.snpMonth,
            segment: filters.segment,
            categoryId: filters.categoryId,
            programmeId: filters.programmeId,
        }, callback, state);
    };

    this.loadHrFlowFilters = function (regionCode, callback) {
        var params = {
            biennium: $rootScope.biennium,
            regionId: regionCode
        };
        apiService.loadDataByParams("regionsHumanResource/flow-filters", params, callback, null);
    };

    this.loadHrFlow = function (flowChartParams, callback, state) {
        apiService.loadDataByParams("regionsHumanResource/flow", flowChartParams, callback, state);
    }
}]);;
var RegionFilters = function (eventsHandlers) {
    var self = this;

    this.eventsHandlers = eventsHandlers;
    this.all = null;
    this.groups = {
        segments: [],
        categories: [],
        programmes: []
    };

    this.selected = {
        segment: null,
        category: null,
        programme: null
    };

    this.buildGroups = function (filters) {
        if (!filters) {
            return;
        }

        this.all = filters;
        this.updateGroups();
    };

    this.updateGroups = function() {
        this.groups.segments = getFiltersGroup("SEGMENT", null, null);
        if (this.selected.segment == null) {
            this.selected.segment = this.groups.segments[0];
        }

        this.groups.categories = getFiltersGroup("CATEGORY", this.selected.segment, null);
        if (this.selected.category == null) {
            this.selected.category = this.groups.categories[0];
        }

        this.groups.programmes = getFiltersGroup("PROGRAMME", this.selected.segment, this.selected.category);
        if (this.selected.programme == null) {
            this.selected.programme = this.groups.programmes[0];
        }
    };

    this.getSelected = function () {
        var selectedSegment = this.selected.segment;
        var selectedCategory = this.selected.category;
        var selectedProgramme = this.selected.programme;

        var filters = {
            segment: selectedSegment ? selectedSegment.value : "*",
            categoryId: selectedCategory ? selectedCategory.value : "*",
            programmeId: selectedProgramme ? selectedProgramme.value : "*",
        };

        return filters;
    };

    this.exists = function() {
        return !!this.all;
    };

    this.onSegmentChanged = function () {
        this.selected.category = null;
        this.selected.programme = null;
        this.updateGroups();

        if (this.eventsHandlers && this.eventsHandlers.onSegmentChanged) {
            this.eventsHandlers.onSegmentChanged();
        }
    };

    this.onCategoryChanged = function () {
        this.selected.programme = null;
        this.updateGroups();

        if (this.eventsHandlers && this.eventsHandlers.onCategoryChanged) {
            this.eventsHandlers.onCategoryChanged();
        }
    };

    function getFiltersGroup(type, segment, category) {
        if (self.all == null) {
            return [];
        }

        var segmentValue = segment ? segment.value : "*";
        var categoryValue = category ? category.value : "*";

        return self.all.filter(function (filter) {
            if (filter.type != type) {
                return false;
            }

            if (filter.value === "*") {
                return true;
            }

            if (segmentValue !== "*" && filter.segmentValue !== segmentValue) {
                return false;
            }

            if (categoryValue !== "*" && filter.categoryValue !== categoryValue) {
                return false;
            }

            return true;
        });
    }
};

APP.service("regionsService", ["apiService", "$http", "$rootScope", function (apiService, $http, $rootScope) {
    var self = this;

    this.loadAbout = function (id, callback, state) {
        apiService.loadDataById("regions/about", id, callback, state);
    };

    this.loadKeyFigures = function (id, filters, callback, state) {
        apiService.loadDataByParams("regions/about-key-figures", {
            id: id,
            budgetSegment: filters.budgetSegment,
            segment: filters.segment,
            categoryId: filters.categoryId,
            programmeId: filters.programmeId
        }, callback, state);
    };

    this.loadKeyFiguresByCategory = function (id, filters, callback, state) {
        apiService.loadDataByParams("regions/about-key-figures-category", {
            id: id,
            budgetSegment: filters.budgetSegment,
            segment: filters.segment,
            categoryId: filters.categoryId,
            programmeId: filters.programmeId
        }, callback, state);
    };

    this.loadKeyFiguresByProgramme = function (id, filters, callback, state) {
        apiService.loadDataByParams("regions/about-key-figures-programme", {
            id: id,
            budgetSegment: filters.budgetSegment,
            segment: filters.segment,
            categoryId: filters.categoryId,
            programmeId: filters.programmeId
        }, callback, state);
    };

    this.loadKeyFiguresFilters = function (id, callback, state) {
        apiService.loadDataById("regions/about-key-figures-filters", id, callback, state);
    };

    this.loadKeyFiguresByLevel = function (id, filters, callback, state) {
        apiService.loadDataByParams("regions/about-key-figures-level", {
            id: id,
            budgetSegment: filters.budgetSegment,
            segment: filters.segment,
            categoryId: filters.categoryId,
            programmeId: filters.programmeId
        }, callback, state);
    };

    this.loadKeyFiguresByGeography = function (id, filters, callback, state) {
        apiService.loadDataByParams("regions/about-key-figures-geography", {
            id: id,
            budgetSegment: filters.budgetSegment,
            segment: filters.segment,
            categoryId: filters.categoryId,
            programmeId: filters.programmeId
        }, callback, state);
    };

    this.loadRegions = function (callback, state) {
        apiService.loadDataByParams("regions/all", {}, callback, state);
    };

    this.loadRegionsMaps = function (callback, state) {
        apiService.loadDataByParams("regions/maps", {}, callback, state);
    };

    this.loadHqKeyFigures = function (callback, state) {
        apiService.loadDataByParams("regions/hq-about-key-figures", {}, callback, state);
    };

    this.loadHqKeyFiguresByProgrammeFilters = function (callback, state) {
        apiService.loadDataByParams("regions/hq-about-key-figures-programme-filters", {}, callback, state);
    };

    this.loadHqKeyFiguresByProgramme = function (id, filters, callback, state) {
        apiService.loadDataByParams("regions/hq-about-key-figures-programme", {
            id: id,
            segment: filters.segment,
            categoryId: filters.categoryId,
            programmeId: filters.programmeId
        }, callback, state);
    };

    this.loadHqKeyFiguresByOutput = function (id, filters, callback, state) {
        apiService.loadDataByParams("regions/hq-about-key-figures-output", {
            id: id,
            segment: filters.segment,
            categoryId: filters.categoryId,
            programmeId: filters.programmeId
        }, callback, state);
    };

    this.loadOutputContributors = function(id, filters, callback, state) {
        apiService.loadDataByParams("regions/hq-output-contributors", {
            id: id,
            segment: filters.segment,
            categoryId: filters.categoryId,
            programmeId: filters.programmeId
        }, callback, state);
    };

    this.loadOutputExpenditures = function (id, filters, callback, state) {
        apiService.loadDataByParams("regions/hq-output-expenditures", {
            id: id,
            segment: filters.segment,
            categoryId: filters.categoryId,
            programmeId: filters.programmeId
        }, callback, state);
    };

    this.loadFinancingFlow = function (filters, callback, state) {
        apiService.loadDataByParams("regions/financing-flow", filters, callback, state);
    };

    this.loadHqFinancingFlow = function (filters, callback, state) {
        apiService.loadDataByParams("regions/hq-financing-flow", filters, callback, state);
    };
}]);;
APP.service("resolutionsAndDecisionsService", ["apiService", function (apiService) {
  var self = this;

  self.loadFilters = function (callback, state) {
    apiService.loadData("resolutionsAndDecisions/filters", callback, state);
  };

  self.search = function (filters, callback, state) {
    apiService.loadDataByParams("resolutionsAndDecisions/search", filters, callback, state);
  };
}]);;
APP.service("textsService", ["$rootScope",
  function ($rootScope) {
    var self = this;

    self.getTexts = function () {
      var texts = {};
      var initFunc =
        ["2014-15", "2016-17", "2018-19"].includes($rootScope.biennium) ? initPre2020texts :
        ["2020-21"].includes($rootScope.biennium) ? init2020texts :
        initSince2022texts;
      initFunc(texts);
      return texts;
    };

    function initPre2020texts(texts) {
      texts['Category'] = 'Category';
      texts['Categories'] = 'Categories';
      texts['Programme'] = 'Programme';
      texts['Programme outcomes'] = 'Programme Outcomes';
      texts['Programmes and outcomes'] = 'Programmes and outcomes';
      texts['Deliverables for Outputs'] = 'Deliverables for Outputs';
      texts['Programme area'] = 'Programme area';
      texts['Figures by Category'] = 'Figures by Category';
      texts['Figures by Programme'] = 'Figures by Programme';
      texts['Category figures by level'] = 'Category figures by level';
      texts['Programme figures by level'] = 'Programme figures by level';
      texts['Category expenses'] = 'Category expenses';
      texts['Category expenses by type'] = 'Category expenses by type';
      texts['Select Category'] = 'Select Category';
      texts['Category key figures'] = 'Category key figures';
      texts['Click category photo or name to zoom into programmes'] = 'Click category photo or name to zoom into programmes';
      texts['Category Summary'] = 'Category Summary';
      texts['Contributions to the following programmes and outputs:'] = 'Contributions to the following programmes and outputs:';
      texts['Category key figures by programme area'] = 'Category key figures by programme area';
      texts['Programme expenses'] = 'Programme expenses';
      texts['Programme expenses by type'] = 'Programme expenses by type';
      texts['All categories'] = 'All categories';
      texts['All programmes'] = 'All programmes';
      texts['Budget by Programme (M)'] = 'Budget by Programme (M)';
      texts['Financing by Category'] = 'Financing by Category';

      texts['home/1'] = "This is WHO's new Programme Budget Portal, providing further details of the Organization’s work, financing and implementation progress.";
      texts['home/2'] = "With quarterly updates this portal will present a better breakdown of our work, navigating through the different categories, programmes and outputs through which WHO’s work is delivered. Countries now specify the financial details at output level in order to meet WHO’s requirement for IATI compliance.";

      texts['budget-and-financing.gpw-title'] = "Financing of General Programme of Work 2014 - 2019";
      texts['budget-and-financing.gpw-overview'] = "Shows the overview of the Budget from 2014-2015 to 2018-2019 and related financing.  The Base segment of the budget is the WHA approved amount. For Polio, Outbreak and Crisis response, and Special programmes (TDR & HRP) this amount has been increased based on the financing and event driven nature of these programmes as per the respective WHA resolutions. All funding reflected in this section shows the distributed amounts across the Organization's results and structure.";
    }

    function init2020texts(texts) {
      texts['Category'] = 'Strategic Objective';
      texts['Categories'] = 'Strategic Objectives';
      texts['Programme'] = 'Outcome';
      texts['Programme outcomes'] = 'Outcomes';
      texts['Programmes and outcomes'] = 'Outcomes';
      texts['Deliverables for Outputs'] = 'Outputs';
      texts['Programme area'] = 'Outcome';
      texts['Figures by Category'] = 'Figures by Strategic Objective';
      texts['Figures by Programme'] = 'Figures by Outcome';
      texts['Category figures by level'] = 'Strategic Objective figures by level';
      texts['Programme figures by level'] = 'Outcome figures by level';
      texts['Category expenses'] = 'Strategic objective expenses';
      texts['Category expenses by type'] = 'Strategic objective expenses by type';
      texts['Select Category'] = 'Select Strategic Objective';
      texts['Category key figures'] = 'Strategic objective key figures';
      texts['Click category photo or name to zoom into programmes'] = 'Click strategic objective photo or name to zoom into outcomes';
      texts['Category Summary'] = 'Strategic Objective Summary';
      texts['Contributions to the following programmes and outputs:'] = 'Contributions to the following outcomes and outputs:';
      texts['Category key figures by programme area'] = 'Strategic objective key figures by outcome';
      texts['Programme expenses'] = 'Outcome expenses';
      texts['Programme expenses by type'] = 'Outcome expenses by type';
      texts['All categories'] = 'All strategic objectives';
      texts['All programmes'] = 'All outcomes';
      texts['Budget by Programme (M)'] = 'Budget by Outcome (M)';
      texts['Financing by Category'] = 'Financing by Strategic Objective';

      texts['home/1'] = "Welcome to WHO's Programme Budget Portal, providing further details of the Organization’s work, financing and implementation progress across its General Programme of Work.";
      texts['home/2'] = "With quarterly updates this portal presents a better breakdown of our work, navigating through the different strategic priorities, global outcomes and outputs through which WHO’s work is delivered. Countries specify any financial details at activity (output) level in order to meet WHO’s requirement for IATI compliance.";

      texts['budget-and-financing.gpw-title'] = "Financing of General Programme of Work 2020 - 2025";
      texts['budget-and-financing.gpw-overview'] = "Shows the overview of the Budget from 2020-2021 to 2024-2025 and related financing. The Base segment of the budget is the WHA approved amount. For Polio, Outbreak and Crisis response, and Special programmes (TDR, HRP and PIP Framework) this amount has been increased based on the financing and event driven nature of these programmes as per the respective WHA resolutions. All funding reflected in this section shows the distributed amounts across the Organization's results and structure.";
      texts['Staff by Outcome'] = 'Staff by Outcome';
      texts['Outcome'] = 'Outcome';
      texts['Strategic objective staff headcount by appointment type'] = 'Strategic objective staff headcount by appointment type';
      texts['View By'] = 'View By';
      texts['Figures by Output'] = 'Figures by Output';
      texts['Staff by Output'] = 'Staff by Output';
      texts['Output'] = 'Output';
    }

    function initSince2022texts(texts) {
      init2020texts(texts);
      texts['home/2'] = "With monthly updates this portal presents a better breakdown of our work around the different strategic priorities, global outcomes and outputs through which WHO’s work is delivered. WHO organizational entities (Country Offices, Regional Offices and Headquarters' divisions) specify any financial details at activity (output) level in order to meet WHO’s requirement for IATI compliance.";
    }

  }]);;
APP.service("uiService", ["$timeout", "$rootScope", "$state", function ($timeout, $rootScope, $state) {
    var self = this;

    self.initTooltips = function () {
        $timeout(function () {
            $(".tooltips").tooltip();
        }, 0);
    };

    self.initCombobox = function (comboboxId) {
        $timeout(function () {
            $(sprintf("#%s", comboboxId)).combobox();
        });
    };

    self.initSelect2Combobox = function (comboboxId) {
        $timeout(function () {
            $(sprintf("#%s", comboboxId)).select2({
                dropdownAutoWidth: true,
                width: '100%'
            });
        });
    };

    self.initMultiselect = function (filters) {
        $(".c-multiselect").multiselect({
            buttonContainer: '<div class="button-container"></div>',
            templates: {
                button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"><span class="multiselect-selected-text"></span></button>',
                popupContainer: '<div class="multiselect-container dropdown-menu"></div>',
                filter: '<div class="multiselect-filter d-flex align-items-center"><i class="fas fa-sm fa-search text-muted"></i><input type="search" class="multiselect-search form-control" /></div>',
                buttonGroup: '<button type="button" class="multiselect-buttons custom-select" style="display:flex;"></button>',
                buttonGroupReset: '<button type="button" class="multiselect-reset btn btn-secondary btn-block"></button>',
                option: '<div class="multiselect-option dropdown-item"></div>',
                divider: '<div class="dropdown-divider"></div>',
                optionGroup: '<button type="button" class="multiselect-group dropdown-item"></button>',
                resetButton: '<div class="multiselect-reset text-center p-2"><button type="button" class="btn btn-sm btn-block btn-outline-secondary"></button></div>'
            },
            onChange: function (option, checked) {
                if (option.length && option.length > 0) {
                    filters.onMultiSelectChanged(option.val(), checked);
                }
            },
        });
    }

    self.initBootstrapDropdowns = function () {
        $timeout(function () {
            $(".dropdown-toggle").dropdown();
        });
    };

    self.initMegaMenu = function () {
        $timeout(function () {
            LayoutMegaMenu.init();
        });
    };

    self.closeMegaMenu = function () {
        $timeout(function () {
            var menuControl = $(".c-hor-nav-toggler");
            var menuControlTarget = menuControl.data("target");
            $(menuControlTarget).removeClass("c-shown");
        });
    };

    self.initTabs = function () {
        $timeout(function () {
            $(".nav-tabs").tab();
        });
    };

    self.activateTabsForState = function (state) {
        if (!state || !state.name) {
            return;
        }

        $timeout(function () {
            $(sprintf(".nav li[active-state='%s'] a", state.name)).tab("show");
        });
    };

    self.activateTabsForCurrentState = function () {
        var includes = $state.$current.includes;
        if (!includes) {
            return;
        }

        $timeout(function () {
            for (var stateName in includes) {
                if (includes[stateName]) {
                    $(sprintf(".nav li[active-state='%s'] a", stateName)).tab("show");
                }
            }
        });
    };

    self.showFirstTab = function (tabsId) {
        $timeout(function () {
            $(sprintf("#%s a:first", tabsId)).tab("show");
        }, 0);
    };

    self.showGlobalPreloader = function () {
        NProgress.start();
    };

    self.hideGlobalPreloader = function () {
        NProgress.done();
    };
}]);;
APP.service("whoService", ["apiService", "$http", "$rootScope", function (apiService, $http, $rootScope) {
    var self = this;

    self.loadIatiFiles = function (callback, state) {
        alert(1);
        apiService.loadDataById("who/who-iati-files", '0', callback, state);
    };
}]);;
APP.controller("homeViewController", ["$scope", "$rootScope", "$http", "$interval", "$timeout", "modalDialogsService",
    "programmeBudgetService", "uiService", "textsService", function ($scope, $rootScope, $http, $interval, $timeout, modalDialogsService,
        programmeBudgetService, uiService, textsService) {
    var self = this;

    uiService.initTooltips();

    $scope.regions = [];
    $scope.statuses = [];
    $scope.lastContributions = [];
    $scope.documents = [];
    $scope.topCvc = null;
    $scope.texts = textsService.getTexts();

    $scope.initSliders = function() {
        $timeout(function () {
            LayoutRevoSliders.init();
        });
    };

    $scope.selectBiennium = function () {
        modalDialogsService.show({
            templateUrl: "Views/ModalViews/SelectBienniumModalView.html",
            controller: "selectBienniumModalViewController"
        });
    };

    $scope.loadRegions = function () {
        programmeBudgetService.loadRegions(function(regions) {
            $scope.regions = regions;
            $timeout(function() {
                $("#regions-tabs a:first").tab("show");
            }, 0);
        });
    };

    $scope.loadStatuses = function () {
        programmeBudgetService.loadStatuses(function (statuses) {
            $scope.statuses = statuses;
            $timeout(function () {
                LayoutProgressBar.init();
            }, 0);
        });
    };

    $scope.loadContributions = function () {
        programmeBudgetService.loadLastContributions(function (lastContributions) {
            $scope.lastContributions = lastContributions;
            $timeout(function () {
                ContentOwlcarousel.init();
            }, 0);
        });
    };

    $scope.loadDocuments = function() {
        programmeBudgetService.loadDocuments(function(documents) {
            $scope.documents = documents;
        });
    };

    $scope.loadCvcInfo = function () {
        programmeBudgetService.loadCvcInfo(function (cvcInfo) {
            $scope.cvcInfo = cvcInfo;
        });
    };

    $scope.loadActiveQuarter = function() {
        programmeBudgetService.loadActiveQuarter(function (activeQuarter) {
            $rootScope.budget.quarter = activeQuarter;
        });
    };

    $scope.loadBudgetSettings = function () {
        programmeBudgetService.loadBudgetSettings(function (settings) {
            $rootScope.budget.isMtrActive = settings.isMtrActive;
            $rootScope.budget.mtrQuarter = settings.mtrQuarter;
            $rootScope.budget.isEbaActive = settings.isEbaActive;
            $rootScope.budget.ebaQuarter = settings.ebaQuarter;
        });
    };

}]);;
APP.controller("masterViewController", ["$scope", "$rootScope", "modalDialogsService", "programmeBudgetService", "uiService", "apiService",
    function ($scope, $rootScope, modalDialogsService, programmeBudgetService, uiService, apiService) {
    $rootScope.categories = null;
    $rootScope.pbCategories = null;
    $rootScope.sgpCategories = null;
    $rootScope.budget = BUDGET;

    $rootScope.$watch("biennium", function () {
        $scope.loadCategories();
    });

    $scope.selectBiennium = function () {
        modalDialogsService.show({
            templateUrl: "Views/ModalViews/SelectBienniumModalView.html",
            controller: "selectBienniumModalViewController"
        });
    };

    $scope.loadCategories = function() {
        programmeBudgetService.loadCategories(function(categories) {
            $rootScope.categories = categories;
            $rootScope.pbCategories = categories.filter(function (item) { return item.budgetSegment == 'Programme Budget'; });
            $rootScope.sgpCategories = categories.filter(function (item) { return item.budgetSegment == 'Special Global Projects'; });
        });
    };

    $scope.initMegaMenu = function() {
        uiService.initMegaMenu();
    };

    $scope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams, options) {
        uiService.showGlobalPreloader();
    });

    $scope.$on("$stateChangeSuccess", function (event, toState, toParams, fromState, fromParams) {
        uiService.hideGlobalPreloader();
    });

    $scope.isHumanResourcesVisible = function () {
        return apiService.isHumanResourcesEnabled($scope.biennium);
    };

    $scope.isCFEPageVisible = function () {
       return apiService.isCFEEnabled($scope.biennium);
    };
}]);;
APP.controller("notFoundViewController", ["$scope", "$state", function ($scope, $state) {
    $scope.message = $state.params.message;
}]);;
APP.controller("budgetAndFinanceDetailsViewController", ["$scope", "$state", "$timeout", "financingService", "chartsService", "textsService",
  function ($scope, $state, $timeout, financingService, chartsService, textsService) {
    var VIEW_BY_CATEGORY = "ByCategory";
    var VIEW_BY_OFFICE = "ByOffice";

    var self = this;

    $scope.details = null;
    $scope.texts = textsService.getTexts();

    $scope.selectedViewBy = VIEW_BY_CATEGORY;
    $scope.quarters = [];
    $scope.budgetSegments = [];
    $scope.selectedQuarter = null;
    $scope.selectedBudgetSegment = null;
    $scope.byCategoryFilters = new RelationsFilters([FILTER_TYPE_MO, FILTER_TYPE_LEVEL, FILTER_TYPE_SEGMENT], null, {
        onRelationsBuild: function (relations) {
            relations.push($scope.selectedBudgetSegment);
        }
    });
    $scope.byMajorOfficeFilters = new RelationsFilters([FILTER_TYPE_LEVEL, FILTER_TYPE_SEGMENT, FILTER_TYPE_CATEGORY], null, {
        onRelationsBuild: function (relations) {
            relations.push($scope.selectedBudgetSegment);
        }
    });

    $scope.loadFilters = function () {
        financingService.loadBudgetAndFinanceDetailsFilters($scope.selectedViewBy, function(filters) {
            $scope.quarters = filters.quarters;
            if ($scope.quarters && $scope.quarters.length > 0) {
                $scope.selectedQuarter = $scope.quarters[$scope.quarters.length - 1].id;
            }

            $scope.budgetSegments = filters.budgetSegments;
            if ($scope.budgetSegments && $scope.budgetSegments.length > 0) {
                $scope.selectedBudgetSegment = $scope.budgetSegments[0].value;
            }

            $scope.byCategoryFilters.buildGroups(filters.filters);
            $scope.byMajorOfficeFilters.buildGroups(filters.filters);
            $scope.loadDetails();
        });
    };

    $scope.loadDetails = function () {
        var filters = null;
        if ($scope.selectedViewBy === VIEW_BY_CATEGORY) {
            filters = $scope.byCategoryFilters;
        } else if ($scope.selectedViewBy === VIEW_BY_OFFICE) {
            filters = $scope.byMajorOfficeFilters;
        } else {
            throw new Error("Unknown value for parameter 'selectedViewBy'");
        }

        financingService.loadDetails({
            viewBy: $scope.selectedViewBy,
            quarter: $scope.selectedQuarter,
            budgetSegment: $scope.selectedBudgetSegment,
            office: filters.getSelectedValueForType(FILTER_TYPE_MO),
            segment: filters.getSelectedValueForType(FILTER_TYPE_SEGMENT),
            level: filters.getSelectedValueForType(FILTER_TYPE_LEVEL),
            category: filters.getSelectedValueForType(FILTER_TYPE_CATEGORY),
        }, function(details) {
            $scope.details = details;

            if ($scope.selectedViewBy === VIEW_BY_CATEGORY) {
                self.showByCategoryDetails();
            } else if ($scope.selectedViewBy === VIEW_BY_OFFICE) {
                self.showByOfficeDetails();
            }
        });
    };

    self.showByCategoryDetails = function() {
        chartsService.makeColumnChart("ChartBienniumFinancingDetailsCategory", $scope.details, {
            graphs: {
                voluntaryContributionsSpecified: {
                    lineColor: "#376092",
                    fillColors: "#376092",
                    title: "Voluntary Contributions Specified",
                },
                voluntaryContributionsThematic: {
                    lineColor: "#ff0000",
                    fillColors: "#ff0000",
                    title: "Voluntary Contributions Thematic",
                },
                flexibleFunds: {
                    lineColor: "#ffc000",
                    fillColors: "#ffc000",
                    title: "Flexible Funds",
                },
                projectedFunding: {
                    lineColor: "#8eb4e3",
                    fillColors: "#8eb4e3",
                    title: "Projected",
                },
                shortfall: {
                    lineColor: "#bfbfbf",
                    fillColors: "#bfbfbf",
                    title: "Shortfall"
                }
            },

            category: {
                field: "categoryLongText",
            }
        });
    };

    self.showByOfficeDetails = function () {
        chartsService.makeColumnChart("ChartBienniumFinancingDetailsMO", $scope.details, {
            graphs: {
                voluntaryContributionsSpecified: {
                    lineColor: "#376092",
                    fillColors: "#376092",
                    title: "Voluntary Contributions Specified",
                },
                voluntaryContributionsThematic: {
                    lineColor: "#ff0000",
                    fillColors: "#ff0000",
                    title: "Voluntary Contributions Thematic",
                },
                flexibleFunds: {
                    lineColor: "#ffc000",
                    fillColors: "#ffc000",
                    title: "Flexible Funds",
                },
                projectedFunding: {
                    lineColor: "#8eb4e3",
                    fillColors: "#8eb4e3",
                    title: "Projected",
                },
                shortfall: {
                    lineColor: "#bfbfbf",
                    fillColors: "#bfbfbf",
                    title: "Shortfall"
                }
            },

            category: {
                field: "majorOffice",
            }
        });
    };
}]);;
APP.controller("budgetAndFinanceSummaryViewController", ["$scope", "$state", "$timeout", "financingService", "chartsService", function ($scope, $state, $timeout, financingService, chartsService) {
    $scope.budgetAndFinanceSummaryState = new DataState();
    $scope.filtersState = new DataState();
    $scope.selectedQuarter = null;
    $scope.selectedBudgetSegment = null;

    $scope.loadFilters = function() {
        financingService.loadBudgetAndFinanceSummaryFilters(function (filters) {
            if (filters && filters.quarters && filters.quarters.length > 0) {
                $scope.selectedQuarter = filters.quarters[filters.quarters.length - 1].id;
            }

            if (filters && filters.budgetSegments && filters.budgetSegments.length > 0) {
                $scope.selectedBudgetSegment = filters.budgetSegments[0].value;
            }

            $scope.loadBudgetAndFinanceSummary();
        }, $scope.filtersState);
    };

    $scope.loadBudgetAndFinanceSummary = function () {
        financingService.loadBudgetAndFinanceSummary({
            quarter: $scope.selectedQuarter,
            budgetSegment: $scope.selectedBudgetSegment
        }, function () {
            $scope.showBudgetAndFinanceSummary();
        }, $scope.budgetAndFinanceSummaryState);
    };

    $scope.showBudgetAndFinanceSummary = function () {
        if (!$scope.budgetAndFinanceSummaryState.isLoaded) {
            return;
        }

        chartsService.makeColumnChart("ChartBienniumFinancingSummary", $scope.budgetAndFinanceSummaryState.data, {
            graphs: {
                voluntaryContributionsSpecified: {
                    lineColor: "#376092",
                    fillColors: "#376092",
                    title: "Voluntary Contributions Specified",
                },
                voluntaryContributionsThematic: {
                    lineColor: "#ff0000",
                    fillColors: "#ff0000",
                    title: "Voluntary Contributions Thematic",
                },
                flexibleFunds: {
                    lineColor: "#ffc000",
                    fillColors: "#ffc000",
                    title: "Flexible Funds",
                },
                projectedFunding: {
                    lineColor: "#8eb4e3",
                    fillColors: "#8eb4e3",
                    title: "Projected",
                },
                shortfall: {
                    lineColor: "#bfbfbf",
                    fillColors: "#bfbfbf",
                    title: "Shortfall"
                }
            },

            category: {
                field: "segment",
            }
        });
    };
}]);;
APP.controller("budgetAndFinancingViewController", ["$scope", "$state", "uiService", function ($scope, $state, uiService) {
    console.log($state);
    if ($state.current.name == "budget-and-financing") {
        $state.go("budget-and-financing.gpw-overview");
    }

    uiService.initTooltips();
}]);;
APP.controller("financialFlowViewController", ["$scope", "$state", "$timeout", "financingService", "textsService",
  function ($scope, $state, $timeout, financingService, textsService) {
    $scope.texts = textsService.getTexts();

    $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
        DATA_TYPE_DONOR_CATEGORY,
        DATA_TYPE_MAJOR_OFFICE,
        DATA_TYPE_PROGRAMME,
        DATA_TYPE_COUNTRY,
        DATA_TYPE_DONOR
    ]);

    $scope.flowChartParams = {
        donorCategoryId: null,
        donorId: null,
        regionId: null,
        countryId: null,
        programmeId: null
    };

    $scope.onFlowChartInit = function (flowChartStateManager) {
        flowChartStateManager.startLoading();
        financingService.loadFlowDonorCategoryRegionProgramme($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };

    $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
        $scope.flowChartParams = flowChartState.params.flowChartParams;
        $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
    };

    $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
        flowChartStateManager.startLoading();

        if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
            $scope.flowChartParams.donorCategoryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
        } else if (node.dataType == DATA_TYPE_DONOR) {
            $scope.flowChartParams.donorId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
        } else if (node.dataType == DATA_TYPE_MAJOR_OFFICE) {
            $scope.flowChartParams.regionId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_MAJOR_OFFICE);
        } else if (node.dataType == DATA_TYPE_COUNTRY) {
            $scope.flowChartParams.countryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_COUNTRY);
        } else if (node.dataType == DATA_TYPE_PROGRAMME) {
            $scope.flowChartParams.programmeId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_PROGRAMME);
        }

        financingService.loadFlowDonorCategoryRegionProgramme($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };
}]);;
APP.controller("gpwOverviewViewController", ["$scope", "$state", "$timeout", "financingService", "chartsService", "textsService", function ($scope, $state, $timeout, financingService, chartsService, textsService) {
    $scope.gpwOverview = null;
    $scope.selectedQuarter = null;
    $scope.financingOverviews = null;
    $scope.texts = textsService.getTexts();

    $scope.loadGpwOverview = function () {
        financingService.loadGpwOverview(function (gpwOverview) {
            $scope.gpwOverview = gpwOverview;

            var quarters = $scope.gpwOverview.quarters;
            if (quarters && quarters.length > 0) {
                $scope.selectedQuarter = quarters[quarters.length-1].id;
                $scope.showGpwOverview();
            }
        });
    };

    $scope.showGpwOverview = function () {
        var selectedQuarter = $scope.selectedQuarter;
        var gpwOverview = $scope.gpwOverview;
        if (!selectedQuarter || !gpwOverview) {
            return;
        }

        $scope.financingOverviews = gpwOverview.financingOverviews[selectedQuarter];
        if (!$scope.financingOverviews) {
            return;
        }
        $scope.financingOverviews.sort((i1, i2) => i2.biennium < i1.biennium);

        chartsService.makeColumnChart("ChartGPWFinancing", $scope.financingOverviews, {
            graphs: {
                voluntaryContributionsSpecified: {
                    lineColor: "#376092",
                    fillColors: "#376092",
                    title: "Voluntary Contributions Specified",
                },
                voluntaryContributionsThematic: {
                    lineColor: "#ff0000",
                    fillColors: "#ff0000",
                    title: "Voluntary Contributions Thematic",
                },
                flexibleFunds: {
                    lineColor: "#ffc000",
                    fillColors: "#ffc000",
                    title: "Flexible Funds",
                },
                projectedFunding: {
                    lineColor: "#8eb4e3",
                    fillColors: "#8eb4e3",
                    title: "Projected",
                }
            },

            category: {
                field: "biennium",
            }
        });
    };
}]);;
APP.controller("categoryAboutViewController", ["$scope", "$timeout", "$state", "$stateParams", "categoriesService", "textsService",
  function ($scope, $timeout, $state, $stateParams, categoriesService, textsService) {
    var self = this;

    if ($state.current.name == "category.about") {
        $state.go("category.about.about");
    }

    $scope.categoryId = $stateParams.categoryId;
    $scope.aboutState = new DataState();
    $scope.about = null;
    $scope.outcomes = null;
    $scope.keyFigures = null;
    $scope.keyFiguresByLevel = null;
    $scope.keyFiguresByGeography = null;
    $scope.texts = textsService.getTexts();

    $scope.init = function () {
        switch ($state.current.name) {
            case "category.about.programme-outcomes":
                $scope.loadProgrammeOutcomes();
                break;
            case "category.about.key-figures":
                $scope.loadKeyFigures();
                break;
            case "category.about.key-figures-by-level":
                $scope.loadKeyFiguresByLevel();
                break;
            case "category.about.key-figures-by-geography":
                $scope.loadKeyFiguresByGeography();
                break;
            default:
                $scope.loadAbout();
        }
    };

    $scope.loadAbout = function () {
        categoriesService.loadAbout($scope.categoryId, function (about) {
            $scope.about = about;
            $scope.$emit(EVENTS.CATEGORY_LOADED, about);
        }, $scope.aboutState);
    };

    $scope.loadProgrammeOutcomes = function () {
        categoriesService.loadOutcomes($scope.categoryId, function (outcomes) {
            $scope.outcomes = outcomes;
        });
    };

    $scope.loadKeyFigures = function () {
        categoriesService.loadKeyFigures($scope.categoryId, function (keyFigures) {
            $scope.keyFigures = keyFigures;
        });
    };

    $scope.loadKeyFiguresByLevel = function () {
        categoriesService.loadKeyFiguresByLevel($scope.categoryId, function (keyFiguresByLevel) {
            $scope.keyFiguresByLevel = keyFiguresByLevel;
        });
    };

    $scope.loadKeyFiguresByGeography = function () {
        categoriesService.loadKeyFiguresByGeography($scope.categoryId, function (keyFiguresByGeography) {
            $scope.keyFiguresByGeography = keyFiguresByGeography;
            if ($scope.keyFiguresByGeography == null) {
                return;
            }

            $timeout(function () {
                $("#RegionTabs a:first").tab("show");
            }, 0);
        });
    };

    $scope.showCountryBudgetPieChart = function (regionCode, countries) {
        console.log(countries);
    };
}]);;
APP.controller("categoryEndBienniumAssessmentViewController", ["$scope", "$rootScope", "$state", "$timeout", "categoriesService", "chartsService", "modalDialogsService", "uiService", function ($scope, $rootScope, $state, $timeout, categoriesService, chartsService, modalDialogsService, uiService) {
    $scope.categoryId = $state.params.categoryId;
    $scope.categoryDetailedReportLink = categoriesService.getDetailedReportLink($scope.categoryId, REVIEW_CYCLE.EBA);
    $scope.tabs = null;
    $scope.keyFiguresState = new DataState();

    $scope.loadTabs = function () {
        categoriesService.loadReviewCycleTabs({
            id: $scope.categoryId,
            reviewCycle: REVIEW_CYCLE.EBA
        }, function (tabs) {
            $scope.tabs = tabs;
            uiService.showFirstTab("category-eba-tabs");
        });
    };

    $scope.loadKeyFigures = function () {
        categoriesService.loadReviewCycleKeyFigures({
            id: $scope.categoryId,
            reviewCycle: REVIEW_CYCLE.EBA
        }, function (data) {
            chartsService.makeColumnChart("EBAChartBudgetExpenseFunding", data.byRegion, {
                graphs: {
                    approvedBudget: {
                        lineColor: "#4F81BD",
                        fillColors: "#4F81BD",
                        title: "WHA Approved Budget",
                        newStack: true,
                    },
                    totalFinancing: {
                        lineColor: "#79933C",
                        fillColors: "#79933C",
                        title: "Funds available",
                        newStack: true,
                    },
                    expTotal: {
                        lineColor: "#F79646",
                        fillColors: "#F79646",
                        title: "Expenditures",
                        newStack: true,
                    },
                },

                category: {
                    field: "moName",
                }
            });
        }, $scope.keyFiguresState);
    };

  $scope.showFinancialOverview = function () {
        modalDialogsService.show({
            templateUrl: "Views/ModalViews/ReviewCycleFinancialOverview.html",
            scope: {
                title: sprintf("Results Report: Financial Overview for Category: %s", $scope.keyFiguresState.data.categoryLongText),
                data: $scope.keyFiguresState.data.financialOverview
            }
        });
    };
}]);;
APP.controller("categoryFinancialFlowViewController", ["$scope", "$state", "$timeout", "categoriesService", "textsService",
  function ($scope, $state, $timeout, categoriesService, textsService) {

    $scope.texts = textsService.getTexts();

    $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
        DATA_TYPE_DONOR_CATEGORY,
        DATA_TYPE_MAJOR_OFFICE,
        DATA_TYPE_PROGRAMME,
        DATA_TYPE_COUNTRY,
        DATA_TYPE_DONOR
    ]);

    $scope.flowChartParams = {
        categoryId: $state.params.categoryId,
        donorCategoryId: null,
        donorId: null,
        regionId: null,
        countryId: null,
        programmeId: null
    };

    $scope.onFlowChartInit = function (flowChartStateManager) {
        flowChartStateManager.startLoading();
        categoriesService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };

    $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
        $scope.flowChartParams = flowChartState.params.flowChartParams;
        $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
    };

    $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
        flowChartStateManager.startLoading();

        if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
            $scope.flowChartParams.donorCategoryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
        } else if (node.dataType == DATA_TYPE_DONOR) {
            $scope.flowChartParams.donorId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
        } else if (node.dataType == DATA_TYPE_MAJOR_OFFICE) {
            $scope.flowChartParams.regionId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_MAJOR_OFFICE);
        } else if (node.dataType == DATA_TYPE_COUNTRY) {
            $scope.flowChartParams.countryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_COUNTRY);
        } else if (node.dataType == DATA_TYPE_PROGRAMME) {
            $scope.flowChartParams.programmeId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_PROGRAMME);
        }

        categoriesService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };
}]);;
APP.controller("categoryMidTermReviewViewController", ["$scope", "$rootScope", "$state", "$timeout", "categoriesService", "chartsService", "modalDialogsService", "uiService", function ($scope, $rootScope, $state, $timeout, categoriesService, chartsService, modalDialogsService, uiService) {
    $scope.staticContent = STATIC_CONTENT;
    $scope.categoryId = $state.params.categoryId;
    $scope.categoryDetailedReportLink = categoriesService.getDetailedReportLink($scope.categoryId, REVIEW_CYCLE.MTR);
    $scope.tabs = null;
    $scope.keyFiguresState = new DataState();
    $scope.categoryResultPdfUrl = null;
    
    $scope.$on("$destroy", function () {
    });

    $scope.canLoadResults = function () {
        if (!$scope.staticContent) {
            return false;
        }

        var resultPdf = $scope.staticContent.get($rootScope.biennium, "CategoryResultPdf", $scope.categoryId);
        if (!resultPdf) {
            return false;
        }

        return true;
    }

    $scope.loadResults = function () {
        if (!$scope.canLoadResults()) {
            return;
        }

        var resultPdf = $scope.staticContent.get($rootScope.biennium, "CategoryResultPdf", $scope.categoryId);
        $scope.categoryResultPdfUrl = $scope.staticContent.pdfViewerUrl + encodeURI(resultPdf.url);
    };

    $scope.loadTabs = function () {
        categoriesService.loadReviewCycleTabs({
            id: $scope.categoryId,
            reviewCycle: REVIEW_CYCLE.MTR
        }, function (tabs) {
            $scope.tabs = tabs;
            uiService.showFirstTab("category-mtr-tabs");
        });
    };

    $scope.loadKeyFigures = function () {
        categoriesService.loadReviewCycleKeyFigures({
            id: $scope.categoryId,
            reviewCycle: REVIEW_CYCLE.MTR
        }, function (data) {
            chartsService.makeColumnChart("MTRChartBudgetExpenseFunding", data.byRegion, {
                graphs: {
                    approvedBudget: {
                        lineColor: "#4F81BD",
                        fillColors: "#4F81BD",
                        title: "WHA Approved Budget",
                        newStack: true,
                    },
                    totalFinancing: {
                        lineColor: "#79933C",
                        fillColors: "#79933C",
                        title: "Funds available",
                        newStack: true,
                    },
                    expTotal: {
                        lineColor: "#F79646",
                        fillColors: "#F79646",
                        title: "Expenditures",
                        newStack: true,
                    },
                },

                category: {
                    field: "moName",
                }
            });
        }, $scope.keyFiguresState);
    };

    $scope.showFinancialOverview = function () {
        modalDialogsService.show({
            templateUrl: "Views/ModalViews/ReviewCycleFinancialOverview.html",
            scope: {
                title: sprintf("Mid Term Review: Financial Overview for Category: %s", $scope.keyFiguresState.data.categoryLongText),
                data: $scope.keyFiguresState.data.financialOverview
            }
        });
    };
}]);;
APP.controller("categoryViewController", ["$scope", "$rootScope", "$timeout", "$state", "$interpolate", "categoriesService", "textsService", function ($scope, $rootScope, $timeout, $state, $interpolate, categoriesService, textsService) {
	$scope.categoryId = $state.params.categoryId;
	$scope.categoryResultsReportLink = getResultsReportLink($scope.categoryId);
  $scope.texts = textsService.getTexts();

    if ($state.current.name == "category") {
        $state.go("category.about");
    }

    $scope.$on(EVENTS.CATEGORY_LOADED, function(event, data) {
        $rootScope.category = data;
    });

    function getResultsReportLink(categoryId) {
    	if (!categoryId || !CONFIG) {
    		return null;
    	}

    	try {
    		return $interpolate(CONFIG.categoryResultsReportLink)({
    			categoryId: categoryId,
                biennium: $rootScope.biennium,
            });
    	} catch (error) {
    		console.error(error);
    		return null;
    	}
    };
}]);;
APP.controller("ourWorkViewController", ["$scope", "$timeout", "categoriesService", function ($scope, $timeout, categoriesService) {
    $scope.budgetSegments = [];

    $scope.loadBudgetSegments = function () {
        categoriesService.loadBudgetSegments(function (budgetSegments) {
            $scope.budgetSegments = budgetSegments;

            $timeout(function () {
                LayoutProgressBar.init();
            }, 0);
        });
    };
}]);;
APP.controller("cfeViewController", ["$scope", "$timeout", "contributorsService", "uiService", "chartsService", "textsService",
    function ($scope, $timeout, contributorsService, uiService, chartsService, textsService) {
    $scope.texts = textsService.getTexts();

    $scope.contributorsState = new DataState();
    $scope.emergencyEventsState = new DataState();
    uiService.initTooltips();

    $scope.loadContributors = function () {
        contributorsService.loadCfe(function (cfe) {
            $("#ChartFundingByContributor").css('height', cfe.length * 30 + 30);
            chartsService.makeColumnChart("ChartFundingByContributor", cfe, {
                graphs: {
                    cfeAmount: {
                        lineColor: "#3D3F96",
                        fillColors: "#3D3F96",
                        title: "CFE Amount",
                        showLabel: true,
                        showBalloon: true,
                        labelPosition: "right",
                        labelColor: "#000000"
                    }
                },

                category: {
                    field: "donorName"
                },

                rotate: true,
                maximumFractionDigits: 2
            });
        }, $scope.contributorsState);
    };

    $scope.loadEmergencyEvents = function () {
        contributorsService.loadCfeEmergencyEvents(function (data) {
            $("#ChartFundingByEvents").css('height', data.length * 30 + 30);
            chartsService.makeColumnChart("ChartFundingByEvents", data, {
                graphs: {
                    amount: {
                        lineColor: "#3D3F96",
                        fillColors: "#3D3F96",
                        title: "Amount",
                        showLabel: true,
                        showBalloon: true,
                        labelPosition: "right",
                        labelColor: "#000000"
                    }
                },

                category: {
                    field: "eventName"
                },

                rotate: true,
                //useOriginalLegendNumbers: true,
                maximumFractionDigits: 2
            });
        }, $scope.emergencyEventsState);
    };

}]);;
APP.controller("contributorsOverviewViewController", ["$scope", "$timeout", "$state", "contributorsService", function ($scope, $timeout, $state, contributorsService) {
    if ($state.current.name == "contributors.overview") {
        $state.go("contributors.overview.ac");
    }

    $scope.acState = new DataState();
    $scope.vcsState = new DataState();
    $scope.vctState = new DataState();
    $scope.pfState = new DataState();
    $scope.cvcState = new DataState();
    $scope.pipState = new DataState();
    $scope.cfeState = new DataState();

    $scope.init = function () {
        switch ($state.current.name) {
            case "contributors.overview.vcs":
                $scope.loadFund('VCS', $scope.vcsState);
                break;
            case "contributors.overview.vct":
                $scope.loadFund('VCT', $scope.vctState);
                break;
            case "contributors.overview.cvc":
                $scope.loadFund('CVC', $scope.cvcState);
                break;
            case "contributors.overview.pip":
                $scope.loadFund('PIP', $scope.pipState);
                break;
            case "contributors.overview.pf":
                $scope.loadFund('PF', $scope.pfState);
                break;
            case "contributors.overview.cfe":
                $scope.loadFund('CFE', $scope.cfeState);
                break;
            default:
                $scope.loadFund('AC', $scope.acState);
        }
    };

    $scope.loadFund = function(groupCode, state) {
        if (!groupCode || !state) {
            return;
        }

        contributorsService.loadFund(groupCode, function() {
        }, state);
    };
}]);;
APP.controller("contributorsViewController", ["$scope", "$timeout", "$state", "contributorsService", function ($scope, $timeout, $state, contributorsService) {
    if ($state.current.name == "contributors") {
        $state.go("contributors.contributor");
    }
}]);;
APP.controller("contributorViewController", ["$scope", "$timeout", "$state", "$location", "contributorsService", "uiService", "textsService", function ($scope, $timeout, $state, $location, contributorsService, uiService, textsService) {
    $scope.texts = textsService.getTexts();
    $scope.contributorName = $state.params.name;
    $scope.contributorsState = new DataState();
    $scope.selectedContributor = null;
    $scope.contributorsColors = null;

    $scope.flowChartStateManager = null;
    $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
        DATA_TYPE_DONOR_CATEGORY,
        DATA_TYPE_MAJOR_OFFICE,
        DATA_TYPE_PROGRAMME,
        DATA_TYPE_COUNTRY,
        DATA_TYPE_DONOR
    ]);

    $scope.flowChartParams = {
        donorName: $scope.contributorName,
        donorCategoryId: null,
        donorId: null,
        regionId: null,
        countryId: null,
        programmeId: null
    };

    uiService.initTooltips();

    $scope.loadContributors = function () {
        contributorsService.loadAll(function () {
            $scope.showContributorAndFunds();
            $scope.loadFlow();
        }, $scope.contributorsState);
    };

    $scope.showContributorAndFunds = function () {
        var contributors = $scope.contributorsState.data;
        if (!contributors || contributors.length <= 0) {
            return;
        }

        if ($scope.contributorName && !$scope.selectedContributor) {
            $scope.selectedContributor = _.find(contributors, function (c) {
                return c.donorName.toUpperCase() === $scope.contributorName.toUpperCase();
            });
        }

        if (!$scope.selectedContributor) {
            $scope.selectedContributor = contributors[0];
        }

        var contributor = $scope.selectedContributor;
        var funds = [
            { name: "Assessed contributions", color: "#c8d046", value: contributor.assessedAmount, valueFormatted: contributor.assessedAmountFormatted },
            { name: "Voluntary contributions - Specified", color: "#5dc09c", value: contributor.vcsAmount, valueFormatted: contributor.vcsAmountFormatted },
            { name: "Voluntary contributions - Thematic", color: "#ff0000", value: contributor.vctAmount, valueFormatted: contributor.vctAmountFormatted },
            { name: "Core voluntary contributions", color: "#ac3773", value: contributor.cvcAmount, valueFormatted: contributor.cvcAmountFormatted },
            { name: "PIP Contributions", color: "#ac6537", value: contributor.pipAmount, valueFormatted: contributor.pipAmountFormatted },
            { name: "Contingency Fund for Emergencies", color: "#376092", value: contributor.cfeAmount, valueFormatted: contributor.cfeAmountFormatted },
            { name: "Projected funding*", color: "#5893dd", value: contributor.projectedAmount, valueFormatted: contributor.projectedAmountFormatted }
        ];

        $scope.selectedContributor.funds = funds;

        $scope.contributorsColors = [];
        $scope.selectedContributor.funds.forEach(function (fund) {
            $scope.contributorsColors.push(fund.color);
        });

        uiService.initSelect2Combobox("select-contributors");

        var isAllSelected = $scope.selectedContributor == contributors[0];
        $location.search('name', isAllSelected ? null : $scope.selectedContributor.donorName);
    };

    $scope.loadFlow = function () {
        var selectedContributor = $scope.selectedContributor;
        var flowChartStateManager = $scope.flowChartStateManager;
        if (!selectedContributor || !flowChartStateManager) {
            return;
        }

        flowChartStateManager.startLoading();
        $scope.flowChartParams = {
            donorName: ~selectedContributor.donorName.toUpperCase().indexOf("ALL CONTRIBUTORS") ? null : selectedContributor.donorName,
            donorCategoryId: null,
            donorId: null,
            regionId: null,
            countryId: null,
            programmeId: null
        };

        $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
            DATA_TYPE_DONOR_CATEGORY,
            DATA_TYPE_MAJOR_OFFICE,
            DATA_TYPE_PROGRAMME,
            DATA_TYPE_COUNTRY,
            DATA_TYPE_DONOR
        ]);

        contributorsService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            }, true);

            flowChartStateManager.endLoading();
        });
    };

    $scope.onFlowChartInit = function (flowChartStateManager) {
        $scope.flowChartStateManager = flowChartStateManager;
    };

    $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
        $scope.flowChartParams = flowChartState.params.flowChartParams;
        $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
    };

    $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
        flowChartStateManager.startLoading();

        if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
            $scope.flowChartParams.donorCategoryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
        } else if (node.dataType == DATA_TYPE_DONOR) {
            $scope.flowChartParams.donorId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
            selectContributorByName(node.name);
        } else if (node.dataType == DATA_TYPE_MAJOR_OFFICE) {
            $scope.flowChartParams.regionId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_MAJOR_OFFICE);
        } else if (node.dataType == DATA_TYPE_COUNTRY) {
            $scope.flowChartParams.countryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_COUNTRY);
        } else if (node.dataType == DATA_TYPE_PROGRAMME) {
            $scope.flowChartParams.programmeId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_PROGRAMME);
        }

        contributorsService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };

    function selectContributorByName(contributorName) {
        if (!contributorName || !$scope.contributorsState.data) {
            return;
        }

        contributorName = contributorName.toUpperCase();
        var contributor = _.find($scope.contributorsState.data, function (item) {
            return item.donorName && item.donorName.toUpperCase() === contributorName;
        });

        if (contributor) {
            $("#select-contributors").val(contributor.$$hashKey).trigger("change");
        }
    }
}]);;
APP.controller("cvcViewController", ["$scope", "$timeout", "contributorsService", "uiService", "chartsService", "textsService", function ($scope, $timeout, contributorsService, uiService, chartsService, textsService) {
    $scope.texts = textsService.getTexts();
    $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
        DATA_TYPE_MAJOR_OFFICE,
        DATA_TYPE_PROGRAMME,
        DATA_TYPE_COUNTRY
    ]);

    $scope.flowChartParams = {
        donorCategoryId: null,
        donorId: null,
        regionId: null,
        countryId: null,
        programmeId: null
    };

    $scope.contributorsState = new DataState();
    uiService.initTooltips();

    $scope.loadContributors = function () {
        contributorsService.loadCvc(function (cvc) {
            $("#ChartFundingByContributor").css('height', cvc.length * 30 + 30);
            chartsService.makeColumnChart("ChartFundingByContributor", cvc, {
                graphs: {
                    cvcAmount: {
                        lineColor: "#3D3F96",
                        fillColors: "#3D3F96",
                        title: "CVC Amount",
                        showLabel: true,
                        showBalloon: true,
                        labelPosition: "right",
                        labelColor: "#000000"
                    },
                },

                category: {
                    field: "donorName",
                },

                rotate: true,
                maximumFractionDigits: 2
            });
        }, $scope.contributorsState);
    };

    $scope.onFlowChartInit = function (flowChartStateManager) {
        flowChartStateManager.startLoading();
        contributorsService.loadCvcFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };

    $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
        $scope.flowChartParams = flowChartState.params.flowChartParams;
        $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
    };

    $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
        flowChartStateManager.startLoading();

        if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
            $scope.flowChartParams.donorCategoryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
        } else if (node.dataType == DATA_TYPE_DONOR) {
            $scope.flowChartParams.donorId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
        } else if (node.dataType == DATA_TYPE_MAJOR_OFFICE) {
            $scope.flowChartParams.regionId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_MAJOR_OFFICE);
        } else if (node.dataType == DATA_TYPE_COUNTRY) {
            $scope.flowChartParams.countryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_COUNTRY);
        } else if (node.dataType == DATA_TYPE_PROGRAMME) {
            $scope.flowChartParams.programmeId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_PROGRAMME);
        }

        contributorsService.loadCvcFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };
}]);;
APP.controller("ksViewController", ["$scope", "$timeout", "contributorsService", "uiService", "chartsService", "categoriesService", function ($scope, $timeout, contributorsService, uiService, chartsService, categoriesService) {

    $scope.summaryState = new DataState();
    uiService.initTooltips();

    $scope.loadContributors = function () {
        contributorsService.loadKs(function (summary) {
            $("#ChartFundingByContributor").css('height', summary.length * 30 + 30);
            chartsService.makeColumnChart("ChartFundingByContributor", summary.total, {
                graphs: {
                    amount: {
                        lineColor: "#3D3F96",
                        fillColors: "#3D3F96",
                        title: "Amount",
                        showLabel: true,
                        showBalloon: true,
                        labelPosition: "right",
                        labelColor: "#000000"
                    },
                },

                category: {
                    field: "donorName",
                },

                rotate: true,
                maximumFractionDigits: 2
            });
        }, $scope.summaryState);
    };
}]);;
APP.controller("pipViewController", ["$scope", "$timeout", "contributorsService", "uiService", "chartsService", "categoriesService", "textsService", function ($scope, $timeout, contributorsService, uiService, chartsService, categoriesService, textsService) {
    $scope.texts = textsService.getTexts();
    $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
        DATA_TYPE_DONOR_CATEGORY,
        DATA_TYPE_MAJOR_OFFICE,
        DATA_TYPE_PROGRAMME,
        DATA_TYPE_COUNTRY,
        DATA_TYPE_DONOR
    ]);

    var pipAwardsNumbers = [60478, 61722, 60856];
    $scope.flowChartParams = {
        categoryId: null,
        donorCategoryId: null,
        donorId: null,
        regionId: null,
        countryId: null,
        programmeId: null,
        programmeSerialNo: null,
        awardNumbers: pipAwardsNumbers
    };

    $scope.contributorsState = new DataState();
    uiService.initTooltips();

    $scope.loadContributors = function () {
        contributorsService.loadPip(function (pip) {
            $("#ChartFundingByContributor").css('height', pip.length * 30 + 30);
            chartsService.makeColumnChart("ChartFundingByContributor", pip, {
                graphs: {
                    pipAmount: {
                        lineColor: "#3D3F96",
                        fillColors: "#3D3F96",
                        title: "PIP Amount",
                        showLabel: true,
                        showBalloon: true,
                        labelPosition: "right",
                        labelColor: "#000000"
                    },
                },

                category: {
                    field: "donorName",
                },

                rotate: true,
                maximumFractionDigits: 2
            });
        }, $scope.contributorsState);
    };

    $scope.onFlowChartInit = function (flowChartStateManager) {
        flowChartStateManager.startLoading();
        categoriesService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };

    $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
        $scope.flowChartParams = flowChartState.params.flowChartParams;
        $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
    };

    $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
        flowChartStateManager.startLoading();

        if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
            $scope.flowChartParams.donorCategoryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
        } else if (node.dataType == DATA_TYPE_DONOR) {
            $scope.flowChartParams.donorId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
        } else if (node.dataType == DATA_TYPE_MAJOR_OFFICE) {
            $scope.flowChartParams.regionId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_MAJOR_OFFICE);
        } else if (node.dataType == DATA_TYPE_COUNTRY) {
            $scope.flowChartParams.countryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_COUNTRY);
        } else if (node.dataType == DATA_TYPE_PROGRAMME) {
            $scope.flowChartParams.programmeId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_PROGRAMME);
        }

        categoriesService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };
}]);;
APP.controller("countryCategoryFinancialFlowViewController", ["$scope", "$state", "$timeout", "countryService", "textsService",
  function ($scope, $state, $timeout, countryService, textsService) {
    $scope.texts = textsService.getTexts();

    $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
        DATA_TYPE_DONOR_CATEGORY,
        DATA_TYPE_DONOR,
        DATA_TYPE_CATEGORY,
        DATA_TYPE_PROGRAMME,
        DATA_TYPE_OUTPUT
    ]);

    $scope.flowChartParams = {
        countryCode: $state.params.countryCode,
        categorySerialNo: $state.params.categorySerialNo,
        donorCategoryId: null,
        donorId: null,
        categoryId: null,
        programmeId: null,
        outputId: null,
    };

    $scope.onFlowChartInit = function (flowChartStateManager) {
        flowChartStateManager.startLoading();
        countryService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };

    $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
        $scope.flowChartParams = flowChartState.params.flowChartParams;
        $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
    };

    $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
        flowChartStateManager.startLoading();

        if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
            $scope.flowChartParams.donorCategoryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
        } else if (node.dataType == DATA_TYPE_DONOR) {
            $scope.flowChartParams.donorId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
        } else if (node.dataType == DATA_TYPE_CATEGORY) {
            $scope.flowChartParams.categoryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_CATEGORY);
        } else if (node.dataType == DATA_TYPE_PROGRAMME) {
            $scope.flowChartParams.programmeId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_PROGRAMME);
        } else if (node.dataType == DATA_TYPE_OUTPUT) {
            $scope.flowChartParams.outputId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_OUTPUT);
        }

        countryService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };
}]);;
APP.controller("countryCategoryHomepageViewController", ["$scope", "$timeout", "$state", "$sce", function ($scope, $timeout, $state, $sce) {
    $scope.iframeUrl = $sce.trustAsResourceUrl("https://www.who.int/countries/" + $state.params.countryCode.toLowerCase());
}]);;
APP.controller("countryCategorySummaryViewController", ["$scope", "$timeout", "$state", "countryCategoryService", "chartsService", "textsService",
  function ($scope, $timeout, $state, countryCategoryService, chartsService, textsService) {
    $scope.countryCode = $state.params.countryCode;
    $scope.categorySerialNo = $state.params.categorySerialNo;
    $scope.about = null;
    $scope.keyFigures = null;
    $scope.charts = null;
    $scope.texts = textsService.getTexts();

    $scope.loadAbout = function () {
        countryCategoryService.loadAbout({ countryCode: $scope.countryCode, categorySerialNo: $scope.categorySerialNo }, function (about) {
            $scope.$emit(EVENTS.COUNTRY_CATEGORY_LOADED, about);
            $scope.about = about;
        });
    }

    $scope.loadKeyFigures = function () {
        countryCategoryService.loadKeyFigures({ countryCode: $scope.countryCode, categorySerialNo: $scope.categorySerialNo }, function (keyFigures) {
            $scope.keyFigures = keyFigures;
        });
    };

    $scope.loadCharts = function () {
        countryCategoryService.loadCharts({ countryCode: $scope.countryCode, categorySerialNo: $scope.categorySerialNo }, function (charts) {
            $scope.charts = charts;
            $scope.showKeyFiguresChart();
            $scope.showExpensesByTypeChart();
        });
    };

    $scope.showKeyFiguresChart = function () {
        chartsService.makeColumnChart("ChartProgramme", $scope.charts.keyFiguresChart, {
            graphs: {
                budgetStaff: {
                    lineColor: "#3D3F96",
                    fillColors: "#3D3F96",
                    title: "Staff",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                budgetActivity: {
                    lineColor: "#4F81BD",
                    fillColors: "#4F81BD",
                    title: "Activity",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                financing: {
                    lineColor: "#79933C",
                    fillColors: "#79933C",
                    title: "Financing",
                    newStack: true,
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                expTotal: {
                    lineColor: "#F79646",
                    fillColors: "#F79646",
                    title: "Expenses",
                    newStack: true,
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                }
            },

            category: {
                field: "programmeLongText"
            }
        });
    }

    $scope.showExpensesByTypeChart = function () {
        chartsService.makeColumnChart("ChartExpensesByType", $scope.charts.programmeExpensesChart, {
            graphs: {
                staff: {
                    title: "Staff and other personnel costs",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                medical: {
                    title: "Medical supplies and materials",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                travel: {
                    title: "Travel",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                contracting: {
                    title: "Contracting services",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                transfers: {
                    title: "Transfers and grants to counterparts",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                equipment: {
                    title: "Equipment, vehicles and furniture",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                }
            },

            category: {
                field: "programmeLongText"
            }
        });
    }
}]);;
APP.controller("countryCategoryViewController", ["$scope", "$timeout", "$state", "textsService",
  function ($scope, $timeout, $state, textsService) {
    $scope.countryCode = $state.params.countryCode;
    $scope.categorySerialNo = $state.params.categorySerialNo;
    $scope.countryShortCode = null;
    $scope.country = null;
    $scope.regionName = null;
    $scope.categoryName = null;
    $scope.texts = textsService.getTexts();

    $state.go("country-category.summary");

      $scope.$on(EVENTS.COUNTRY_CATEGORY_LOADED, function (event, data) {
        $scope.country = data ? data.countryName : $scope.countryCode;
        $scope.countryShortCode = data ? data.countryIso2Code : $scope.countryCode.substring(0, 2);
        $scope.regionName = data ? data.regionName : "";
        $scope.categorySerialNo = data ? data.categorySerialNo : "";
        $scope.categoryName = data ? data.categoryLongText : "";
    });
}]);;
APP.controller("countryCategoryHRSummaryViewController", ["$rootScope", "$scope", "$timeout", "$state", "countryHRService", "textsService", "humanResourcesService",
    function ($rootScope, $scope, $timeout, $state, service, textsService, humanResourcesService) {
        $scope.countryCode = $state.params.countryCode;
        $scope.categorySerialNo = $state.params.categorySerialNo;
        $scope.quarters = null;
        $scope.keyFigures = null;
        $scope.texts = textsService.getTexts();
        $scope.loadKeyFigures = function () {
            service.loadCategoryKeyFigures({ countryCode: $scope.countryCode, categorySerialNo: $scope.categorySerialNo }, function (keyFigures) {
                $scope.quarters = keyFigures.quarters;
                $scope.keyFigures = keyFigures;
                $scope.quarters.forEach(quarter => {
                    keyFigures.byProgramme[quarter.id].forEach(item => {
                        item.nameLink = $state.href('country-programme-human-resource',
                            {
                                biennium: $rootScope.biennium,
                                countryCode: item.countryIso3Code,
                                programmeSerialNo: item.programmeSerialNo
                            });
                    });
                });
                $scope.$emit(EVENTS.COUNTRY_CATEGORY_LOADED, keyFigures);
                setDefaultPeriod();
            });
        };

        var setDefaultPeriod = function () {
            humanResourcesService.getDefaultPeriod(function (period) {
                $scope.selectedQuarter = period;
                if ($scope.quarters && $scope.quarters.length > 0) {
                    var periodExists = $scope.quarters.find(x => x.id == period);
                    if (!periodExists) {
                        $scope.selectedQuarter = $scope.quarters[$scope.quarters.length - 1].id;
                    }
                }
            });
        };

    }]);

APP.config(['$compileProvider', function ($compileProvider) {
    $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|sms|tel|data):/);
}]);;
APP.controller("countryCategoryHRViewController", ["$scope", "$timeout", "$state", "textsService",
    function ($scope, $timeout, $state, textsService) {
        $scope.countryCode = $state.params.countryCode;
        $scope.categorySerialNo = $state.params.categorySerialNo;
        $scope.countryShortCode = null;
        $scope.country = null;
        $scope.regionName = null;
        $scope.categoryName = null;
        $scope.texts = textsService.getTexts();

        $state.go("country-category-human-resource.summary");

        $scope.$on(EVENTS.COUNTRY_CATEGORY_LOADED, function (event, data) {
            $scope.country = data ? data.countryName : $scope.countryCode;
            $scope.countryShortCode = data ? data.countryIso2Code : $scope.countryCode.substring(0, 2);
            $scope.regionName = data ? data.regionName : "";
            $scope.categorySerialNo = data ? data.categorySerialNo : "";
            $scope.categoryName = data ? data.categoryLongText : "";
        });
    }]);;
APP.controller("countryFinancialFlowViewController", ["$scope", "$state", "$timeout", "countryService", "textsService", function ($scope, $state, $timeout, countryService, textsService) {
    $scope.texts = textsService.getTexts();
    $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
        DATA_TYPE_DONOR_CATEGORY,
        DATA_TYPE_DONOR,
        DATA_TYPE_CATEGORY,
        DATA_TYPE_PROGRAMME,
        DATA_TYPE_OUTPUT
    ]);

    $scope.flowChartParams = {
        countryCode: $state.params.countryCode,
        donorCategoryId: null,
        donorId: null,
        categoryId: null,
        programmeId: null,
        outputId: null,
    };

    $scope.onFlowChartInit = function (flowChartStateManager) {
        flowChartStateManager.startLoading();
        countryService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };

    $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
        $scope.flowChartParams = flowChartState.params.flowChartParams;
        $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
    };

    $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
        flowChartStateManager.startLoading();

        if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
            $scope.flowChartParams.donorCategoryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
        } else if (node.dataType == DATA_TYPE_DONOR) {
            $scope.flowChartParams.donorId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
        } else if (node.dataType == DATA_TYPE_CATEGORY) {
            $scope.flowChartParams.categoryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_CATEGORY);
        } else if (node.dataType == DATA_TYPE_PROGRAMME) {
            $scope.flowChartParams.programmeId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_PROGRAMME);
        } else if (node.dataType == DATA_TYPE_OUTPUT) {
            $scope.flowChartParams.outputId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_OUTPUT);
        }

        countryService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };
}]);;
APP.controller("countryHomepageViewController", ["$scope", "$timeout", "$state", "$sce", function ($scope, $timeout, $state, $sce) {
    $scope.iframeUrl = $sce.trustAsResourceUrl("https://www.who.int/countries/" + $state.params.countryCode.toLowerCase());
}]);;
APP.controller("countryKeyAchievementsViewController", ["$scope", "$state", "countryService", "$sce", function ($scope, $state, countryService, $sce) {
	$scope.countryCode = $state.params.countryCode;
	$scope.countryKeyAchievements = null;

	$scope.trustAsHtml = function (html) {
		return $sce.trustAsHtml(html);
	}

	$scope.loadKeyAchievements = function() {
		countryService.loadCountryReview({ CountryCode: $scope.countryCode }, function (data) {
			$scope.countryKeyAchievements = data.reviewText;
			$scope.$emit(EVENTS.COUNTRY_LOADED, data);
		});
	};
}]);
APP.controller("countrySummaryViewController", ["$scope", "$timeout", "$state", "countryService", "iatiFilesService", "chartsService", "textsService",
  function ($scope, $timeout, $state, countryService, iatiFilesService, chartsService, textsService) {
    $scope.countryCode = $state.params.countryCode;
    $scope.keyFigures = null;
    $scope.expensesByType = null;
    $scope.documents = null;
    $scope.iatiFiles = null;
    $scope.texts = textsService.getTexts();

    $scope.loadKeyFigures = function () {
        countryService.loadKeyFigures($scope.countryCode, function (keyFigures) {
            var firstItem = keyFigures.items.length > 0 ? keyFigures.items[0] : null;
            $scope.$emit(EVENTS.COUNTRY_LOADED, firstItem);
            $scope.keyFigures = keyFigures.items;
        }
    )};

    $scope.loadExpensesByTypeChartData = function () {
        countryService.loadExpensesByTypeChartData($scope.countryCode, function (data) {
            $scope.expensesByType = data;
            $scope.showExpensesByTypeChart();
        });
    };

    $scope.loadDocuments = function () {
        countryService.loadDocuments($scope.countryCode, function (data) {
            $scope.documents = data;
        });
    };

    $scope.loadIatiData = function () {
        iatiFilesService.loadIatiFiles($scope.countryCode, function (data) {
            $scope.iatiFiles = data;
        });
    };

    $scope.showKeyFiguresChart = function() {
        chartsService.makeColumnChart("ChartCategory", $scope.keyFigures, {
            graphs: {
                budgetStaff: {
                    lineColor: "#3D3F96",
                    fillColors: "#3D3F96",
                    title: "Staff",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                budgetActivity: {
                    lineColor: "#4F81BD",
                    fillColors: "#4F81BD",
                    title: "Activity",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                financing: {
                    lineColor: "#79933C",
                    fillColors: "#79933C",
                    title: "Financing",
                    newStack: true,
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                expenditureTotal: {
                    lineColor: "#F79646",
                    fillColors: "#F79646",
                    title: "Expenses",
                    newStack: true,
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                }
            },

            category: {
                field: "categoryLongText",
            }
        });
    };

    $scope.showExpensesByTypeChart = function () {
        if (!$scope.expensesByType) {
            return;
        }

        var data = [];
        var graphs = {};
        $scope.expensesByType.forEach(function(group) {
            var item = {
                name: group.name
            };

            if (group.items) {
                group.items.forEach(function(groupItem) {
                    item[groupItem.expenseClass] = groupItem.expenseTotal;
                    graphs[groupItem.expenseClass] = {
                        title: groupItem.expenseClass,
                        labelFontSize: 11,
                        showLabel: true
                    };
                });
            }

            data.push(item);
        });

        chartsService.makeColumnChart("ChartExpensesByType", data, {
            graphs: graphs,
            category: {
                field: "name",
            }
        });
    };

    $scope.showCharts = function () {
        $scope.loadExpensesByTypeChartData();
        $scope.showKeyFiguresChart();
    };
}]);

APP.config(['$compileProvider', function ($compileProvider) {
    $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|sms|tel|data):/);
}]);;
APP.controller("countryViewController", ["$scope", "$rootScope", "$timeout", "$state", function ($scope, $rootScope, $timeout, $state) {
    $scope.countryCode = $state.params.countryCode;
    $scope.countryShortCode = null;
    $scope.country = null;
    $scope.regionCode = null;
    $scope.regionName = null;

    if ($rootScope.budget.isEbaActive) {
    	$state.go("country.keyAchievements");
    }
    else
    	$state.go("country.summary");

    $scope.$on(EVENTS.COUNTRY_LOADED, function (event, data) {
        $scope.countryShortCode = data ? data.countryIso2Code : "";
        $scope.country = data ? data.countryName : $scope.countryCode;
        $scope.regionCode = data ? data.regionCode : "";
        $scope.regionName = data ? data.regionName : "";
    });
}]);;
APP.controller("countryHRSummaryViewController", ["$rootScope", "$scope", "$timeout", "$state", "countryHRService", "textsService", "humanResourcesService",
    function ($rootScope, $scope, $timeout, $state, service, textsService, humanResourcesService) {
    $scope.countryCode = $state.params.countryCode;
    $scope.quarters = null;
    $scope.keyFigures = null;
    $scope.texts = textsService.getTexts();

    $scope.loadKeyFigures = function () {
        service.loadKeyFigures($scope.countryCode, function (keyFigures) {
            $scope.quarters = keyFigures.quarters;
            $scope.quarters.forEach(quarter => {
                keyFigures.byCategory[quarter.id].forEach(item => {
                    item.nameLink = $state.href('country-category-human-resource',
                        {
                            biennium: $rootScope.biennium,
                            countryCode: item.countryIso3Code,
                            categorySerialNo: item.categorySerialNo
                        });
                });
            });
            $scope.keyFigures = keyFigures;
            setDefaultPeriod();
        });
      };

    var setDefaultPeriod = function () {
        humanResourcesService.getDefaultPeriod(function (period) {
            $scope.selectedQuarter = period;
            if ($scope.quarters && $scope.quarters.length > 0) {
                var periodExists = $scope.quarters.find(x => x.id == period);
                if (!periodExists) {
                    $scope.selectedQuarter = $scope.quarters[$scope.quarters.length - 1].id;
                }
            }
            
            var firstItem = $scope.keyFigures.byCategory[$scope.selectedQuarter].length > 0 ? $scope.keyFigures.byCategory[$scope.selectedQuarter][0] : null;
            $scope.$emit(EVENTS.COUNTRY_LOADED, firstItem);
        });
    };

}]);

APP.config(['$compileProvider', function ($compileProvider) {
    $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|sms|tel|data):/);
}]);;
APP.controller("countryHRViewController", ["$scope", "$rootScope", "$timeout", "$state", function ($scope, $rootScope, $timeout, $state) {
    $scope.countryCode = $state.params.countryCode;
    $scope.countryShortCode = null;
    $scope.country = null;
    $scope.regionCode = null;
    $scope.regionName = null;

   	$state.go("country-human-resource.summary");

    $scope.$on(EVENTS.COUNTRY_LOADED, function (event, data) {
        $scope.countryShortCode = data ? data.countryIso2Code : "";
        $scope.country = data ? data.countryName : $scope.countryCode;
        $scope.regionCode = data ? data.regionCode : "";
        $scope.regionName = data ? data.regionName : "";
    });
}]);;
APP.controller("countryProgrammeFinancialFlowViewController", ["$scope", "$state", "$timeout", "countryService", function ($scope, $state, $timeout, countryService) {
    $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
        DATA_TYPE_DONOR_CATEGORY,
        DATA_TYPE_DONOR,
        DATA_TYPE_OUTPUT
    ]);

    $scope.flowChartParams = {
        countryCode: $state.params.countryCode,
        programmeSerialNo: $state.params.programmeSerialNo,
        donorCategoryId: null,
        donorId: null,
        categoryId: null,
        programmeId: null,
        outputId: null,
    };

    $scope.onFlowChartInit = function (flowChartStateManager) {
        flowChartStateManager.startLoading();
        countryService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };

    $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
        $scope.flowChartParams = flowChartState.params.flowChartParams;
        $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
    };

    $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
        flowChartStateManager.startLoading();

        if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
            $scope.flowChartParams.donorCategoryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
        } else if (node.dataType == DATA_TYPE_DONOR) {
            $scope.flowChartParams.donorId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
        } else if (node.dataType == DATA_TYPE_OUTPUT) {
            $scope.flowChartParams.outputId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_OUTPUT);
        }

        countryService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };
}]);;
APP.controller("countryProgrammeHomepageViewController", ["$scope", "$timeout", "$state", "$sce", function ($scope, $timeout, $state, $sce) {
    $scope.iframeUrl = $sce.trustAsResourceUrl("https://www.who.int/countries/" + $state.params.countryCode.toLowerCase());
}]);;
APP.controller("countryProgrammeSummaryViewController", ["$scope", "$timeout", "$state", "countryProgrammeService", "chartsService", function ($scope, $timeout, $state, countryProgrammeService, chartsService) {
    $scope.countryCode = $state.params.countryCode;
    $scope.programmeSerialNo = $state.params.programmeSerialNo;
    $scope.keyFigures = null;
    $scope.charts = null;

    $scope.loadKeyFigures = function () {
        countryProgrammeService.loadKeyFigures({ countryCode: $scope.countryCode, programmeSerialNo: $scope.programmeSerialNo }, function (keyFigures) {
            $scope.$emit(EVENTS.COUNTRY_PROGRAMME_LOADED, keyFigures);
            $scope.keyFigures = keyFigures;
        });
    };

    $scope.loadCharts = function () {
        countryProgrammeService.loadCharts({ countryCode: $scope.countryCode, programmeSerialNo: $scope.programmeSerialNo }, function (charts) {
            $scope.charts = charts;
            $scope.showKeyFiguresChart();
            $scope.showExpensesByTypeChart();
        });
    };

    $scope.clickTest = function(item) {
        alert(item);
    };

    $scope.showKeyFiguresChart = function() {
        chartsService.makeColumnChart("ChartProgramme", $scope.charts.keyFiguresChart, {
            graphs: {
                budgetStaff: {
                    lineColor: "#3D3F96",
                    fillColors: "#3D3F96",
                    title: "Staff",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                budgetActivity: {
                    lineColor: "#4F81BD",
                    fillColors: "#4F81BD",
                    title: "Activity",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                financing: {
                    lineColor: "#79933C",
                    fillColors: "#79933C",
                    title: "Financing",
                    newStack: true,
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                expTotal: {
                    lineColor: "#F79646",
                    fillColors: "#F79646",
                    title: "Expenses",
                    newStack: true,
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                }
            },

            category: {
                field: "outputLongText"
            }
        });
    };

    $scope.showExpensesByTypeChart = function() {
        chartsService.makeColumnChart("ChartExpensesByType", $scope.charts.programmeExpensesChart, {
            graphs: {
                staff: {
                    title: "Staff and other personnel costs",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                medical: {
                    title: "Medical supplies and materials",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                travel: {
                    title: "Travel",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                contracting: {
                    title: "Contracting services",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                transfers: {
                    title: "Transfers and grants to counterparts",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                },
                equipment: {
                    title: "Equipment, vehicles and furniture",
                    labelColor: "#ffffff",
                    labelFontSize: 11,
                    showLabel: true
                }
            },

            category: {
                field: "outputLongText"
            }
        });
    };
}]);;
APP.controller("countryProgrammeViewController", ["$scope", "$timeout", "$state", function ($scope, $timeout, $state) {
    $scope.countryCode = $state.params.countryCode;
    $scope.countryShortCode = $scope.countryCode.substring(0, 2);
    $scope.country = null;
    $scope.regionName = null;
    $scope.categorySerialNo = null;
    $scope.categoryName = null;
    $scope.programmeSerialNo = $state.params.programmeSerialNo;
    $scope.programmeName = null;
    $state.go("country-programme.summary");

    $scope.$on(EVENTS.COUNTRY_PROGRAMME_LOADED, function (event, data) {
        $scope.country = data ? data.countryLongText : $scope.countryCode;
        $scope.countryShortCode = data ? data.countryIso2Code : $scope.countryCode.substring(0, 2);
        $scope.regionName = data ? data.regionLongText : "";
        $scope.categorySerialNo = data ? data.categorySerialNo : "";
        $scope.categoryName = data ? data.categoryLongText : "";
        $scope.programmeName = data ? data.programmeLongText : "";
    });
}]);;
APP.controller("countryProgrammeHRSummaryViewController", ["$rootScope", "$scope", "$timeout", "$state", "countryHRService", "textsService", "humanResourcesService",
    function ($rootScope, $scope, $timeout, $state, service, textsService, humanResourcesService) {
        $scope.countryCode = $state.params.countryCode;
        $scope.programmeSerialNo = $state.params.programmeSerialNo;
        $scope.quarters = null;
        $scope.keyFigures = null;
        $scope.texts = textsService.getTexts();
        $scope.loadKeyFigures = function () {
            service.loadProgrammeKeyFigures({ countryCode: $scope.countryCode, programmeSerialNo: $scope.programmeSerialNo }, function (keyFigures) {
                $scope.quarters = keyFigures.quarters;
                $scope.keyFigures = keyFigures;
                $scope.$emit(EVENTS.COUNTRY_PROGRAMME_LOADED, keyFigures);
                setDefaultPeriod();
            });
        };

        var setDefaultPeriod = function () {
            humanResourcesService.getDefaultPeriod(function (period) {
                $scope.selectedQuarter = period;
                if ($scope.quarters && $scope.quarters.length > 0) {
                    var periodExists = $scope.quarters.find(x => x.id == period);
                    if (!periodExists) {
                        $scope.selectedQuarter = $scope.quarters[$scope.quarters.length - 1].id;
                    }
                }
            });
        };

    }]);

APP.config(['$compileProvider', function ($compileProvider) {
    $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|sms|tel|data):/);
}]);;
APP.controller("countryProgrammeHRViewController", ["$scope", "$timeout", "$state", function ($scope, $timeout, $state) {
    $scope.countryCode = $state.params.countryCode;
    $scope.countryShortCode = $scope.countryCode.substring(0, 2);
    $scope.country = null;
    $scope.regionName = null;
    $scope.categorySerialNo = null;
    $scope.categoryName = null;
    $scope.programmeSerialNo = $state.params.programmeSerialNo;
    $scope.programmeName = null;
    $state.go("country-programme-human-resource.summary");

    $scope.$on(EVENTS.COUNTRY_PROGRAMME_LOADED, function (event, data) {
        $scope.country = data ? data.countryName : $scope.countryCode;
        $scope.countryShortCode = data ? data.countryIso2Code : $scope.countryCode.substring(0, 2);
        $scope.regionName = data ? data.regionName : "";
        $scope.categorySerialNo = data ? data.categorySerialNo : "";
        $scope.categoryName = data ? data.categoryLongText : "";
        $scope.programmeName = data ? data.programmeLongText : "";
    });
}]);;
APP.controller("categoryHumanResourceAboutViewController", ["$rootScope", "$scope", "$timeout", "$state", "$stateParams", "categoryHumanResourceService", "textsService", "humanResourcesService",
    function($rootScope, $scope, $timeout, $state, $stateParams, service, textsService, humanResourcesService) {
        var self = this;

        if ($state.current.name == "category-human-resource.about") {
            $state.go("category-human-resource.about.about");
        }

        $scope.categoryId = $stateParams.categoryId;
        $scope.biennium = $rootScope.biennium;
        $scope.aboutState = new DataState();
        $scope.about = null;
        $scope.outcomes = null;
        $scope.selectedQuarter = null;
        $scope.quarters = null;
        $scope.keyFigures = null;
        $scope.keyFiguresByLevel = null;
        $scope.keyFiguresByGeography = null;
        $scope.texts = textsService.getTexts();

        $scope.init = function() {
            switch ($state.current.name) {
            case "category-human-resource.about.programme-outcomes":
                $scope.loadProgrammeOutcomes();
                break;
            case "category-human-resource.about.key-figures":
                $scope.loadKeyFigures();
                break;
            case "category-human-resource.about.key-figures-by-level":
                $scope.loadKeyFiguresByLevel();
                break;
            case "category-human-resource.about.key-figures-by-geography":
                $scope.loadKeyFiguresByGeography();
                break;
            default:
                $scope.loadAbout();
            }
        };

        $scope.loadAbout = function() {
            $scope.quarters = null;
            service.loadAbout($scope.categoryId,
                function(about) {
                    $scope.about = about;
                    $scope.$emit(EVENTS.CATEGORY_LOADED, about);
                },
                $scope.aboutState);
        };

        $scope.loadProgrammeOutcomes = function() {
            $scope.quarters = null;
            service.loadOutcomes($scope.categoryId,
                function(outcomes) {
                    $scope.outcomes = outcomes;
                });
        };

        $scope.loadKeyFigures = function() {

            service.loadKeyFigures($scope.categoryId,
                function(keyFigures) {
                    $scope.quarters = keyFigures.quarters;
                    setDefaultPeriod();
                    $scope.keyFigures = keyFigures;

                    $scope.quarters.forEach(quarter => {
                        $scope.keyFigures.byProgramme[quarter.id].forEach(programme => {
                            programme.nameLink = $state.href('programme',
                                {
                                    biennium: $rootScope.biennium,
                                    categoryId: $scope.categoryId,
                                    programmeId: programme.programmeSerialNo
                                });
                        });
                    });
                });
        };

        $scope.loadKeyFiguresByLevel = function() {
            service.loadKeyFiguresByLevel($scope.categoryId,
                function(keyFiguresByLevel) {
                    $scope.quarters = keyFiguresByLevel.quarters;
                    setDefaultPeriod();
                    $scope.keyFiguresByLevel = keyFiguresByLevel;
                });
        };

        $scope.loadKeyFiguresByGeography = function() {
            service.loadKeyFiguresByGeography($scope.categoryId,
                function(keyFiguresByGeography) {
                    $scope.keyFiguresByGeography = keyFiguresByGeography;
                    if ($scope.keyFiguresByGeography == null) {
                        return;
                    }
                    $scope.quarters = keyFiguresByGeography.quarters;
                    setDefaultPeriod();

                    $scope.quarters.forEach(quarter => {
                        for (var key in $scope.keyFiguresByGeography.byCountry[quarter.id]) {
                            for (var i = 0; i < $scope.keyFiguresByGeography.byCountry[quarter.id][key].length; i++) {
                                var item = $scope.keyFiguresByGeography.byCountry[quarter.id][key][i];
                                item.nameLink = $state.href('country-human-resource',
                                    {
                                        biennium: $rootScope.biennium,
                                        countryCode: item.countryIso3Code,
                                    });
                            }
                        }
                    });


                    $timeout(function() {
                            $("#RegionTabs a:first").tab("show");
                        },
                        100);
                });
        };

        $scope.showCountryBudgetPieChart = function(regionCode, countries) {
            console.log(countries);
        };

        var setDefaultPeriod = function () {
            humanResourcesService.getDefaultPeriod(function (period) {
                $scope.selectedQuarter = period;
                if ($scope.quarters && $scope.quarters.length > 0) {
                    var periodExists = $scope.quarters.find(x => x.id == period);
                    if (!periodExists) {
                        $scope.selectedQuarter = $scope.quarters[$scope.quarters.length - 1].id;
                    }
                }
            });
        };

    }]);;
APP.controller("categoryHumanResourcesFlowViewController", ["$scope", "$state", "humanResourcesService", "textsService",
    function ($scope, $state, humanResourcesService, textsService) {
        $scope.texts = textsService.getTexts();

        $scope.filters = new RelationsFilters([FILTER_TYPE_MONTH]);

        $scope.flowChartClickableNodesTypes = {};

        $scope.flowChartParams = {};

        $scope.flowChartStateManager = {};

        $scope.initFlowChartParams = function() {
            $scope.flowChartParams = {
                donorCategoryId: null,
                donorId: null,
                regionId: null,
                countryId: null,
                programmeId: null,
                categorySerialNo: $state.params.categoryId,
                snpMonth: $scope.filters.getSelectedValueForType(FILTER_TYPE_MONTH)
            };

            $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
                DATA_TYPE_DONOR_CATEGORY,
                DATA_TYPE_MAJOR_OFFICE,
                DATA_TYPE_PROGRAMME,
                DATA_TYPE_COUNTRY,
                DATA_TYPE_DONOR
            ]);
        }

        $scope.loadFilters = function (callback) {
            humanResourcesService.loadHrFlowFilters('category', function (filters) {
                $scope.filters.buildGroups(filters);
                setDefaultPeriod(() => {
                    if (callback) {
                        callback();
                    }
                });
            });
        };

        $scope.onFilterChange = function () {
            $scope.flowChartStateManager.states = [];
            $scope.initFlowChartParams();

            $scope.flowChartInit($scope.flowChartStateManager);
        }

        $scope.flowChartInit = function (flowChartStateManager) {
            flowChartStateManager.startLoading();

            humanResourcesService.loadHrFlow($scope.flowChartParams, function (flow) {
                flowChartStateManager.pushState(flow, {
                    flowChartParams: $scope.flowChartParams,
                    flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
                });

                flowChartStateManager.endLoading();
            });
        }

        $scope.onFlowChartInit = function (flowChartStateManager) {
            $scope.initFlowChartParams();

            $scope.flowChartStateManager = flowChartStateManager;

            $scope.loadFilters(() => {
                $scope.flowChartInit(flowChartStateManager);
            });
        };

        $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
            $scope.flowChartParams = flowChartState.params.flowChartParams;
            $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
        };

        $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
            flowChartStateManager.startLoading();

            if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
                $scope.flowChartParams.donorCategoryId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
            } else if (node.dataType == DATA_TYPE_DONOR) {
                $scope.flowChartParams.donorId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
            } else if (node.dataType == DATA_TYPE_MAJOR_OFFICE) {
                $scope.flowChartParams.regionId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_MAJOR_OFFICE);
            } else if (node.dataType == DATA_TYPE_COUNTRY) {
                $scope.flowChartParams.countryId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_COUNTRY);
            } else if (node.dataType == DATA_TYPE_PROGRAMME) {
                $scope.flowChartParams.programmeId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_PROGRAMME);
            }

            humanResourcesService.loadHrFlow($scope.flowChartParams, function (flow) {
                flowChartStateManager.pushState(flow, {
                    flowChartParams: $scope.flowChartParams,
                    flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
                });

                flowChartStateManager.endLoading();
            });
        };

        var setDefaultPeriod = function (callback) {
            humanResourcesService.getDefaultPeriod(function (period) {
                $scope.filters.setSelectedValueForType(FILTER_TYPE_MONTH, period);
                var availableValues = $scope.filters.groups[FILTER_TYPE_MONTH];
                if (availableValues.length > 0) {
                    var periodExists = $scope.filters.selected[FILTER_TYPE_MONTH].value == period;
                    if (!periodExists) {
                        period = availableValues[availableValues.length - 1];
                        $scope.filters.setSelectedValueForType(FILTER_TYPE_MONTH, period);
                    }
                }
                $scope.flowChartParams.snpMonth = period;

                if (callback) {
                    callback();
                }
            });
        };
    }]);;
APP.controller("categoryHumanResourceViewController", ["$scope", "$rootScope", "$timeout", "$state", "$interpolate", "categoriesService", "textsService",
    function ($scope, $rootScope, $timeout, $state, $interpolate, categoriesService, textsService) {
    $scope.categoryId = $state.params.categoryId;
    $scope.texts = textsService.getTexts();

    if ($state.current.name == "category-human-resource") {
        $state.go("category-human-resource.about");
    }

    $scope.$on(EVENTS.CATEGORY_LOADED, function (event, data) {
        $rootScope.category = data;
    });

}]);;
APP.controller("humanResourceAboutViewController", ["$rootScope", "$scope", "$timeout", "$state", "$stateParams", "categoryHumanResourceService", "textsService", 
    function ($rootScope, $scope, $timeout, $state, $stateParams, service, textsService) {
        var self = this;

        if ($state.current.name == "human-resource.about") {
            $state.go("human-resource.about.about");
        }

        $scope.categoryId = $stateParams.categoryId;
        $scope.aboutState = new DataState();
        $scope.about = null;
        $scope.outcomes = null;
        $scope.keyFigures = null;
        $scope.keyFiguresByLevel = null;
        $scope.keyFiguresByGeography = null;
        $scope.texts = textsService.getTexts();

        $scope.init = function () {
            switch ($state.current.name) {
                case "human-resource.about.programme-outcomes":
                    $scope.loadProgrammeOutcomes();
                    break;
                case "human-resource.about.key-figures":
                    $scope.loadKeyFigures();
                    break;
                case "human-resource.about.key-figures-by-level":
                    $scope.loadKeyFiguresByLevel();
                    break;
                case "human-resource.about.key-figures-by-geography":
                    $scope.loadKeyFiguresByGeography();
                    break;
                default:
                    $scope.loadAbout();
            }
        };

        $scope.loadAbout = function () {
            service.loadAbout($scope.categoryId, function (about) {
                $scope.about = about;
                $scope.$emit(EVENTS.CATEGORY_LOADED, about);
            }, $scope.aboutState);
        };

        $scope.loadProgrammeOutcomes = function () {
            service.loadOutcomes($scope.categoryId, function (outcomes) {
                $scope.outcomes = outcomes;
            });
        };

        $scope.loadKeyFigures = function () {
            service.loadKeyFigures($scope.categoryId, function (keyFigures) {
                $scope.keyFigures = keyFigures;
                for (i = 0; i < keyFigures.byProgramme.length; i++) {
                    var programme = keyFigures.byProgramme[i];
                    programme.nameLink = $state.href('programme',
                        {
                            biennium: $rootScope.biennium,
                            categoryId: $scope.categoryId,
                            programmeId: programme.programmeSerialNo
                        });
                }
            });
        };

        $scope.loadKeyFiguresByLevel = function () {
            service.loadKeyFiguresByLevel($scope.categoryId, function (keyFiguresByLevel) {
                $scope.keyFiguresByLevel = keyFiguresByLevel;
            });
        };

        $scope.loadKeyFiguresByGeography = function () {
            service.loadKeyFiguresByGeography($scope.categoryId, function (keyFiguresByGeography) {
                $scope.keyFiguresByGeography = keyFiguresByGeography;
                if ($scope.keyFiguresByGeography == null) {
                    return;
                }

                for (var key in $scope.keyFiguresByGeography.byCountry) {
                    for (i = 0; i < $scope.keyFiguresByGeography.byCountry[key].length; i++) {
                        var item = $scope.keyFiguresByGeography.byCountry[key][i];
                        item.nameLink = $state.href('country',
                            {
                                biennium: $rootScope.biennium,
                                countryCode: item.countryIso3Code,
                            });
                    }
                }

                $timeout(function () {
                    $("#RegionTabs a:first").tab("show");
                }, 0);
            });
        };

        $scope.showCountryBudgetPieChart = function (regionCode, countries) {
            console.log(countries);
        };
    }]);;
APP.controller("humanResourcesFinancialFlowViewController", ["$scope", "$state", "$timeout", "humanResourcesService", "textsService",
    function ($scope, $state, $timeout, service, textsService) {
        $scope.texts = textsService.getTexts();

        $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
            DATA_TYPE_DONOR_CATEGORY,
            DATA_TYPE_MAJOR_OFFICE,
            DATA_TYPE_PROGRAMME,
            DATA_TYPE_COUNTRY,
            DATA_TYPE_DONOR
        ]);

        $scope.flowChartParams = {
            donorCategoryId: null,
            donorId: null,
            regionId: null,
            countryId: null,
            programmeId: null
        };

        $scope.onFlowChartInit = function (flowChartStateManager) {
            flowChartStateManager.startLoading();
            service.loadHrFlow($scope.flowChartParams, function (flow) {
                flowChartStateManager.pushState(flow, {
                    flowChartParams: $scope.flowChartParams,
                    flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
                });

                flowChartStateManager.endLoading();
            });
        };

        $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
            $scope.flowChartParams = flowChartState.params.flowChartParams;
            $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
        };

        $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
            flowChartStateManager.startLoading();

            if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
                $scope.flowChartParams.donorCategoryId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
            } else if (node.dataType == DATA_TYPE_DONOR) {
                $scope.flowChartParams.donorId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
            } else if (node.dataType == DATA_TYPE_MAJOR_OFFICE) {
                $scope.flowChartParams.regionId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_MAJOR_OFFICE);
            } else if (node.dataType == DATA_TYPE_COUNTRY) {
                $scope.flowChartParams.countryId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_COUNTRY);
            } else if (node.dataType == DATA_TYPE_PROGRAMME) {
                $scope.flowChartParams.programmeId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_PROGRAMME);
            }

            service.loadHrFlow($scope.flowChartParams, function (flow) {
                flowChartStateManager.pushState(flow, {
                    flowChartParams: $scope.flowChartParams,
                    flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
                });

                flowChartStateManager.endLoading();
            });
        };
    }]);;
APP.controller("humanResourcesGenderBalanceViewController", ["$scope", "$state", "$timeout", "humanResourcesService", "chartsService", "uiService", 
    function ($scope, $state, $timeout, humanResourcesService, chartsService, uiService) {

        var self = this;

        $scope.countsByTypeData = null;
        $scope.countsTableData = {};
        $scope.dataViewType = 'chart' | 'table';

        $scope.totalData = {};
        $scope.totalGenderBalanceValue = 0;

        $scope.filters = new RelationsFilters([FILTER_TYPE_MONTH, FILTER_TYPE_MO, FILTER_TYPE_LEVEL, FILTER_TYPE_CONTRACT,
            FILTER_TYPE_APPOINTMENT, FILTER_TYPE_JOB_CATEGORY, FILTER_TYPE_GRADE_CATEGORY, FILTER_TYPE_GRADE]);

        $scope.init = function () {
            $scope.dataViewType = 'chart';
            $scope.loadFilters();
        }

        $scope.loadFilters = function () {
            humanResourcesService.loadGenderBalanceFilters(function (filters) {
                $scope.filters.buildGroups(filters);

                setDefaultPeriod(() => {
                    $scope.filters.setSelectedValuesForType(FILTER_TYPE_GRADE_CATEGORY, ['P4 & above']);
                    $timeout(function () {
                        uiService.initMultiselect($scope.filters);
                    });
                    
                    $scope.loadCountData();
                });
            });
        };

        $scope.loadTotal = function () {
            var filters = buildSelectedFilters($scope.filters);
            humanResourcesService.loadGenderBalanceTotal(filters, function (data) {
                $scope.totalData = data;
                $scope.totalGenderBalanceValue = 0;
                $scope.totalData.forEach(item => {
                    $scope.totalGenderBalanceValue += item.value;
                });
            });
        };

        $scope.isTotalVisible = function() {
            return $state.current.name == "human-resources.gender-parity";
        }

        $scope.loadCountData = function () {
            $scope.loadTotal();
            switch ($state.current.name) {
              case "human-resources.gender-parity.by-appointment-type":
                $scope.loadCountByAppointmentType();
                break;
              case "human-resources.gender-parity.by-region":
                $scope.loadCountByRegion();
                break;
              case "human-resources.gender-parity.by-org-level":
                $scope.loadCountByOrgLevel();
                break;
              default:
                $scope.loadCountByJobCategory();
            }
        };

        $scope.loadCountByJobCategory = function () {
            var filters = buildSelectedFilters($scope.filters);
            $scope.countsByTypeData = null;
            humanResourcesService.loadGenderBalanceCountDataByType('job-category', filters, function (data) {
                $scope.countsByTypeData = data;

                $scope.bindCountsChart();
                $scope.bindCountsTable('Job Category');
            });
        };

        $scope.loadCountByAppointmentType = function () {
            var filters = buildSelectedFilters($scope.filters);
            humanResourcesService.loadGenderBalanceCountDataByType('appointment-type', filters, function (data) {
                $scope.countsByTypeData = data;

                $scope.bindCountsChart();
                $scope.bindCountsTable('Appointment Type');
            });
        };

        $scope.loadCountByRegion = function () {
            var filters = buildSelectedFilters($scope.filters);
            humanResourcesService.loadGenderBalanceCountDataByType('region', filters, function (data) {
                $scope.countsByTypeData = data;

                $scope.bindCountsChart();
                $scope.bindCountsTable('Region');
            });
        };

        $scope.loadCountByOrgLevel = function () {
            var filters = buildSelectedFilters($scope.filters);
            humanResourcesService.loadGenderBalanceCountDataByType('org-level', filters, function (data) {
                $scope.countsByTypeData = data;

                $scope.bindCountsChart();
                $scope.bindCountsTable('Organization Level');
            });
        };

        $scope.getChartColors = function() {
            return [...chartsService.colors2].reverse();
        }

        $scope.bindCountsChart = function () {
            
            if (!$scope.countsByTypeData) {
                return;
            }

            var categories = [];
            var genders = ['M', 'F'];
            var graphs = {};
            var chartData = [];

            $scope.countsByTypeData.forEach(item => {
                if (!categories.find(c => c == item.type)) {
                    categories.push(item.type);
                }
            });

            var i = 0;
            genders.forEach(gender => {
                graphs[gender] = {
                    title: gender,
                    showLabel: true,
                    lineColor: chartsService.colors2[i],
                    fillColors: chartsService.colors2[i]
                };
                i++;
            });

            categories.forEach(category => {
                var chartItem = {};
                chartItem.category = category;
                genders.forEach(gender => {
                    var categoryValue = $scope.countsByTypeData.find(i => i.gender == gender && i.type == category);
                    chartItem[gender] = categoryValue ? categoryValue.value : 0;
                });

                chartData.push(chartItem);
            });

            chartsService.makeColumn100Chart("сhartByType", chartData, {
                graphs: graphs,
                category: {
                    field: "category",
                }
            }, false);
        };

        $scope.bindCountsTable = function (itemsTitle) {

            if (!$scope.countsByTypeData) {
                return;
            }

            var types = [];
            var genders = ['F', 'M'];
            var tableData = {};

            $scope.countsByTypeData.forEach(item => {
                if (!types.find(type => type == item.type)) {
                    types.push(item.type);
                }
            });

            tableData.columnTitles = ['Female', 'Male', 'Female %'];
            tableData.items = [];

            types.forEach(type => {
                var tableItem = {};
                tableItem.label = type;
                tableItem.data = [];
                var totalValue = 0;
                var femaleValue = 0;
                genders.forEach(gender => {
                    var typeValue = $scope.countsByTypeData.find(i => i.gender == gender && i.type == type);
                    var value = typeValue ? typeValue.value : 0;
                    tableItem.data.push(value);
                    totalValue += value;
                    if (gender == 'F') {
                        femaleValue = value;
                    }
                });

                tableItem.data.push(totalValue > 0 ? (femaleValue * 100 / totalValue ).toFixed(1) : 0);

                tableData.items.push(tableItem);
            });

            //calc totals
            var totalItem = {};
            totalItem.label = 'Total';
            totalItem.data = [];
            var totalSumValue = 0;
            var femaleSumValue = 0;
            genders.forEach(gender => {
                var sumValue = 0;
                $scope.countsByTypeData.forEach(item => {
                    if (item.gender == gender) {
                        sumValue += item.value;
                    }
                });
                totalItem.data.push(sumValue);
                totalSumValue += sumValue;
                if (gender == 'F') {
                    femaleSumValue = sumValue;
                }
            });

            totalItem.data.push(totalSumValue > 0 ? (femaleSumValue * 100 / totalSumValue).toFixed(1) : 0);

            tableData.total = totalItem;
            tableData.itemsTitle = itemsTitle;

            $scope.countsTableData = tableData;
        }

        var buildSelectedFilters = function (relationsFilters) {
            return {
                snpMonth: relationsFilters.getSelectedValueForType(FILTER_TYPE_MONTH),
                region: relationsFilters.getSelectedValuesForType(FILTER_TYPE_MO),
                level: relationsFilters.getSelectedValuesForType(FILTER_TYPE_LEVEL),
                contractType: relationsFilters.getSelectedValuesForType(FILTER_TYPE_CONTRACT),
                appointmentType: relationsFilters.getSelectedValuesForType(FILTER_TYPE_APPOINTMENT),
                jobCategory: relationsFilters.getSelectedValuesForType(FILTER_TYPE_JOB_CATEGORY),
                gradeCategory: relationsFilters.getSelectedValuesForType(FILTER_TYPE_GRADE_CATEGORY),
                grade: relationsFilters.getSelectedValuesForType(FILTER_TYPE_GRADE),
            };
        };

        var setDefaultPeriod = function (callback) {
            humanResourcesService.getDefaultPeriod(function (period) {
                $scope.filters.setSelectedValueForType(FILTER_TYPE_MONTH, period);
                if ($scope.filters.groups[FILTER_TYPE_MONTH].length > 0) {
                    var periodExists = $scope.filters.selected[FILTER_TYPE_MONTH].value == period;
                    if (!periodExists) {
                        $scope.filters.selected[FILTER_TYPE_MONTH] = $scope.filters.groups[FILTER_TYPE_MONTH][$scope.filters.groups[FILTER_TYPE_MONTH].length - 1];
                    }
                }

                if (callback) {
                    callback();
                }
            });
        };

    }]);;
APP.controller("humanResourcesGeographicalDistributionViewController", ["$scope", "$state", "$timeout", "humanResourcesService", "uiService", 
    function ($scope, $state, $timeout, humanResourcesService, uiService) {

        const FILTER_TYPE_PROFILE_COUNTRY = "PROFILE_COUNTRY";

        var self = this;

        $scope.commonData = null;
        $scope.dataByCountry = null;
        $scope.countryStaffingProfileData = [];
        $scope.countryStaffingProfileDataByGrade = null;

        $scope.RECRUITMENT_PRIORITY_LABELS = {
            'A': 'Underrepresented Countries',
            'A*': 'Unrepresented Countries',
            'B1': 'Countries within their range but below midpoint',
            'B2': 'Countries ar or above midpoint of range',
            'B2*': 'Countries at the maximum of their range',
            'C': 'Countries above the maximum of their range'
        };

        $scope.REPRESENTATION_STATUS_COLORS = {
            'A'  : '#EBF1DE',
            'A*' : '#F2DCDB',
            'B1' : '#FDE9D9',
            'B2' : '#DAEEF3',
            'B2*': '#E4DFEC',
            'C'  : '#DDD9C4'
        };

        $scope.filters = new RelationsFilters([FILTER_TYPE_MONTH, FILTER_TYPE_REGION, FILTER_TYPE_COUNTRY, FILTER_TYPE_PROFILE_COUNTRY,
            FILTER_TYPE_STATUS, FILTER_TYPE_PRIORITY, FILTER_TYPE_JOB_CATEGORY]);

        $scope.init = function () {
            $scope.loadFilters();
        }

        $scope.loadFilters = function () {
            humanResourcesService.loadGeographicalDistributionFilters(function (filters) {
                $scope.filters.buildGroups(filters);
                setDefaultPeriod(() => {
                    $scope.filters.setSelectedValueForType(FILTER_TYPE_JOB_CATEGORY, 'Ungraded');
                    $timeout(function () {
                        uiService.initMultiselect($scope.filters);
                    });
                    $scope.loadData();
                });
            });
        };

        $scope.loadData = function () {
            switch ($state.current.name) {
                default:
                case "human-resources.geographical-distribution":
                    $scope.loadGeographicalDistribution();
                    break;
                case "human-resources.geographical-distribution.country-staffing-profile":
                    $scope.loadCountryStaffingProfile();
                    break;
                case "human-resources.geographical-distribution.country-staffing-profile-by-grade":
                    $scope.loadCountryStaffingProfileByGrade();
                    break;
            }
        };

        $scope.loadGeographicalDistribution = function () {
            var filters = buildGeoDistributionFilters($scope.filters);
            $scope.loadCommonTable(filters);
            $scope.loadDataByCountry(filters);
        };

        $scope.loadCommonTable = function (filters) {
            $scope.commonData = null;
            humanResourcesService.loadGeographicalDistributionCommonData(filters, function (data) {
                $scope.commonData = data;
            });
        };

        $scope.loadDataByCountry = function (filters) {
            $scope.dataByCountry = null;
            humanResourcesService.loadGeographicalDistributionByCountry(filters, function (data) {
                $scope.dataByCountry = data;
            });
        };

        $scope.loadCountryStaffingProfile = function () {
            var filters = buildStaffingProfileFilters($scope.filters);
            $scope.countryStaffingProfileData = null;
            humanResourcesService.loadCountryStaffingProfileData(filters, function (data) {
                $scope.countryStaffingProfileData = data;
            });
        };

        $scope.loadCountryStaffingProfileByGrade = function () {
            var filters = buildStaffingProfileByGradeFilters($scope.filters);
            $scope.countryStaffingProfileDataByGrade = null;
            humanResourcesService.loadCountryStaffingProfileDataByGrade(filters, function (data) {
                $scope.countryStaffingProfileDataByGrade = data;
            });
        };


        var buildGeoDistributionFilters = function (relationsFilters) {
            return {
                region: relationsFilters.getSelectedValuesForType(FILTER_TYPE_REGION),
                country: relationsFilters.getSelectedValuesForType(FILTER_TYPE_COUNTRY),
                status: relationsFilters.getSelectedValuesForType(FILTER_TYPE_STATUS),
                recruitmentPriority: relationsFilters.getSelectedValuesForType(FILTER_TYPE_PRIORITY),
                snpMonth: relationsFilters.getSelectedValueForType(FILTER_TYPE_MONTH),
            };
        };

        var buildStaffingProfileFilters = function (relationsFilters) {
            return {
                region: relationsFilters.getSelectedValuesForType(FILTER_TYPE_REGION),
                country: relationsFilters.getSelectedValuesForType(FILTER_TYPE_PROFILE_COUNTRY),
                snpMonth: relationsFilters.getSelectedValueForType(FILTER_TYPE_MONTH),
            };
        };

        var buildStaffingProfileByGradeFilters = function (relationsFilters) {
            return {
                region: relationsFilters.getSelectedValuesForType(FILTER_TYPE_REGION),
                country: relationsFilters.getSelectedValuesForType(FILTER_TYPE_PROFILE_COUNTRY),
                snpMonth: relationsFilters.getSelectedValueForType(FILTER_TYPE_MONTH),
                jobCategory: relationsFilters.getSelectedValueForType(FILTER_TYPE_JOB_CATEGORY),
            };
        };


        var setDefaultPeriod = function (callback) {
            humanResourcesService.getDefaultPeriod(function (period) {
                $scope.filters.setSelectedValueForType(FILTER_TYPE_MONTH, period);
                if ($scope.filters.groups[FILTER_TYPE_MONTH].length > 0) {
                    var periodExists = $scope.filters.selected[FILTER_TYPE_MONTH].value == period;
                    if (!periodExists) {
                        $scope.filters.selected[FILTER_TYPE_MONTH] = $scope.filters.groups[FILTER_TYPE_MONTH][$scope.filters.groups[FILTER_TYPE_MONTH].length - 1];
                    }
                }

                if (callback) {
                    callback();
                }
            });
        };

        $scope.showGeoDistribution = function () {
            return $state.current.name == "human-resources.geographical-distribution";
        }

        $scope.showStaffingProfile = function () {
            return $state.current.name == "human-resources.geographical-distribution.country-staffing-profile" || 
                $state.current.name == "human-resources.geographical-distribution.country-staffing-profile-by-grade" ;
        }

        $scope.showStaffingProfileByGrade = function () {
            return $state.current.name == "human-resources.geographical-distribution.country-staffing-profile-by-grade";
        }

        $scope.staffingProfileColumnTotal = function (col) {
            if ($scope.countryStaffingProfileData) {
                return $scope.countryStaffingProfileData.reduce((partialSum, a) => partialSum + a[col], 0);
            }
        }

    }]);;
APP.controller("humanResourcesViewController", ["$scope", "$state", "uiService", function ($scope, $state, uiService) {

    if ($state.current.name == "human-resources") {
        $state.go("human-resources.workforce");
    }

    uiService.initTooltips();
}]);;
APP.controller("humanResourcesWorkforceViewController", ["$scope", "$state", "$timeout", "humanResourcesService", "chartsService", "textsService", "uiService",
    function ($scope, $state, $timeout, humanResourcesService, chartsService, textsService, uiService) {

        var self = this;

        $scope.countsByTypeData = null;
        $scope.countsTableData = {};
        $scope.dataViewType = 'chart' | 'table';

        if ($state.current.name == "human-resources.workforce") {
            $state.go("human-resources.workforce.by-job-category");
        }

        $scope.filters = new RelationsFilters([FILTER_TYPE_MONTH, FILTER_TYPE_MO, FILTER_TYPE_LEVEL, FILTER_TYPE_CONTRACT,
            FILTER_TYPE_APPOINTMENT, FILTER_TYPE_JOB_CATEGORY, FILTER_TYPE_GENDER, FILTER_TYPE_GRADE]);

        $scope.init = function () {
            $scope.dataViewType = 'chart';
            $scope.loadFilters();
        }

        $scope.loadFilters = function () {
            humanResourcesService.loadWorkforceFilters(function (filters) {
                $scope.filters.buildGroups(filters);

                setDefaultPeriod(() => {
                    $timeout(function () {
                        uiService.initMultiselect($scope.filters);
                    });

                    $scope.loadCountData();
                });
            });
        };

        $scope.loadCountData = function () {
            switch ($state.current.name) {
            case "human-resources.workforce.by-appointment-type":
                $scope.loadCountByAppointmentType();
                break;
            case "human-resources.workforce.by-contract-type":
                $scope.loadCountByContractType();
                break;
            case "human-resources.workforce.by-region":
                $scope.loadCountByRegion();
                break;
            case "human-resources.workforce.by-org-level":
                $scope.loadCountByOrgLevel();
                break;
            case "human-resources.workforce.by-gender":
                $scope.loadCountByGender();
                break;
            default:
                $scope.loadCountByJobCategory();
            }
        };

        $scope.loadCountByJobCategory = function () {
            var filters = buildSelectedFilters($scope.filters);
            $scope.countsByTypeData = null;
            humanResourcesService.loadWorkforceCountDataByType('job-category', filters, function (data) {
                $scope.countsByTypeData = data;

                $scope.bindCountsChart('Job Category');
                $scope.bindCountsTable('Job Category', filters.snpMonth);
            });
        };

        $scope.loadCountByAppointmentType = function () {
            var filters = buildSelectedFilters($scope.filters);
            humanResourcesService.loadWorkforceCountDataByType('appointment-type', filters, function (data) {
                $scope.countsByTypeData = data;

                $scope.bindCountsChart('Appointment Type');
                $scope.bindCountsTable('Appointment Type', filters.snpMonth);
            });
        };

        $scope.loadCountByContractType = function () {
            var filters = buildSelectedFilters($scope.filters);
            humanResourcesService.loadWorkforceCountDataByType('contract-type', filters, function (data) {
                $scope.countsByTypeData = data;

                $scope.bindCountsChart('Contract Type');
                $scope.bindCountsTable('Contract Type', filters.snpMonth);
            });
        };

        $scope.loadCountByRegion = function () {
            var filters = buildSelectedFilters($scope.filters);
            humanResourcesService.loadWorkforceCountDataByType('region', filters, function (data) {
                $scope.countsByTypeData = data;

                $scope.bindCountsChart('Region');
                $scope.bindCountsTable('Region', filters.snpMonth);
            });
        };

        $scope.loadCountByOrgLevel = function () {
            var filters = buildSelectedFilters($scope.filters);
            humanResourcesService.loadWorkforceCountDataByType('org-level', filters, function (data) {
                $scope.countsByTypeData = data;

                $scope.bindCountsChart('Org Level');
                $scope.bindCountsTable('Org Level', filters.snpMonth);
            });
        };

        $scope.loadCountByGender = function () {
            var filters = buildSelectedFilters($scope.filters);
            humanResourcesService.loadWorkforceCountDataByType('gender', filters, function (data) {
                $scope.countsByTypeData = data;

                $scope.bindCountsChart('Gender');
                $scope.bindCountsTable('Gender', filters.snpMonth);
            });
        };

        $scope.bindCountsChart = function (type) {
            if (!$scope.countsByTypeData) {
                return;
            }

            var categories = [];
            var years = [];
            var graphs = {};
            var chartData = [];

            $scope.countsByTypeData.forEach(item => {
                if (!categories.find(c => c == item.type)) {
                    categories.push(item.type);
                }
                if (!years.find(year => year == item.year)) {
                    years.push(item.year);
                }
            });

            switch (type) {
                case "Job Category":
                case "Gender":
                    categories = categories.sort().reverse(); //desc
                    break;
                case "Org Level":
                    break;
                default:
                    categories = categories.sort(); // asc
                    break;
            }

            years = years.sort((i1, i2) => i2 - i1);

            for (var i = 0; i < years.length; i++) {
                var year = years[i];
                var chartItem = {};
                chartItem.year = i == 0 ? 'Staff as of Date' : ('Dec, ' + year);
                categories.forEach(category => {
                    var categoryValue = $scope.countsByTypeData.find(i => i.year == year && i.type == category);
                    chartItem[category] = categoryValue ? categoryValue.value : 0;
                });

                chartData.push(chartItem);
            }

            for (var i = 0; i < categories.length; i++) {
                var category = categories[i];

                graphs[category] = {
                    title: category,
                    showLabel: true,
                    lineColor: chartsService.colors[i],
                    fillColors: chartsService.colors[i],
                };
            }

            chartsService.makeColumnChart("chartByType", chartData, {
                graphs: graphs,
                useOriginalLegendNumbers: true,
                showPercentageValue: true,
                category: {
                    field: "year",
                }
            });
        };

        $scope.bindCountsTable = function (itemsTitle, snpMonth) {
            
            if (!$scope.countsByTypeData) {
                return;
            }

            var types = [];
            var years = [];
            var yearTitles = [];
            var yearsChanges = [];
            var tableData = {};
            var date = new Date();
            date.setMonth(parseInt(snpMonth.substring(6)) - 1);
            var currentFilterMonth = date.toLocaleString('en-us', {month: "short"});

            $scope.countsByTypeData.forEach(item => {
                if (!types.find(type => type == item.type)) {
                    types.push(item.type);
                }
                if (!years.find(year => year == item.year)) {
                    years.push(item.year);
                }
            });

            years = years.sort((i1, i2) => i2 - i1);
            var index = 0;
            years.forEach(year => {
                if (index == 0) {
                    yearTitles.push('Staff ' + '<br/> (' + currentFilterMonth + ' ' + year +  ')');
                }
                
                if (index > 0) {
                    yearTitles.push('Year ' + year + '<br/> (Dec)');
                    yearsChanges.push('% Change <br/> (Vs Dec ' + year + ')');
                }
                index++;
            });

            tableData.yearTitles = yearTitles;
            tableData.yearChangeTitles = yearsChanges;
            tableData.items = [];

            switch (itemsTitle) {
                case "Job Category":
                    types = types.sort().reverse(); //desc
                    break;
                case "Org Level":
                    break;
                default:
                    types = types.sort(); // asc
                    break;
            }
            

            types.forEach(type => {
                var tableItem = {};
                tableItem.label = type;
                tableItem.yearsData = [];
                tableItem.yearsChangesData = [];
                var index = 0;
                years.forEach(year => {
                    var typeValue = $scope.countsByTypeData.find(i => i.year == year && i.type == type);
                    var value = typeValue ? typeValue.value : 0;
                    tableItem.yearsData.push({ value: value });
                    
                    if (index > 0) {
                        var staffYear = years[0];
                        var categoryStaffValue = $scope.countsByTypeData.find(i => i.year == staffYear && i.type == type);
                        if (categoryStaffValue) {
                            var diffValue = categoryStaffValue.value - value;
                            tableItem.yearsChangesData.push({ value: (value > 0 ? (diffValue * 100 / value) : 0).toFixed(1) });
                        } else {
                            tableItem.yearsChangesData.push({ value: 0 });
                        }
                        
                    }
                    index++;
                });
                tableData.items.push(tableItem);
            });

            //calc totals
            var totalItem = {};
            totalItem.label = 'Total';
            totalItem.yearsData = [];
            totalItem.yearsChangesData = [];
            index = 0;
            years.forEach(year => {
                var sumValue = 0;
                $scope.countsByTypeData.forEach(item => {
                    if (item.year == year) {
                        sumValue += item.value;
                    }
                });
                totalItem.yearsData.push({ value: sumValue });

                if (index > 0) {
                    var staffYear = years[0];
                    var sumStaffValue = 0;
                    $scope.countsByTypeData.forEach(item => {
                         if (item.year == staffYear) {
                             sumStaffValue += item.value;
                         }
                    });
                    var diffValue = sumStaffValue - sumValue;
                    totalItem.yearsChangesData.push({ value: (sumValue > 0 ? (diffValue * 100 / sumValue) : 0).toFixed(1) });
                }
                index++;
            });
            tableData.total = totalItem;
            tableData.itemsTitle = itemsTitle;

            $scope.countsTableData = tableData;
        }

        $scope.appointmentTypeSelected = function () {
            return $state.current.name == 'human-resources.workforce.by-appointment-type';
        }

        var buildSelectedFilters = function (relationsFilters) {
            return {
                snpMonth: relationsFilters.getSelectedValueForType(FILTER_TYPE_MONTH),
                region: relationsFilters.getSelectedValuesForType(FILTER_TYPE_MO),
                level: relationsFilters.getSelectedValuesForType(FILTER_TYPE_LEVEL),
                contractType: relationsFilters.getSelectedValuesForType(FILTER_TYPE_CONTRACT),
                appointmentType: relationsFilters.getSelectedValuesForType(FILTER_TYPE_APPOINTMENT),
                jobCategory: relationsFilters.getSelectedValuesForType(FILTER_TYPE_JOB_CATEGORY),
                gender: relationsFilters.getSelectedValuesForType(FILTER_TYPE_GENDER),
                grade: relationsFilters.getSelectedValuesForType(FILTER_TYPE_GRADE)
            };
        };

        var setDefaultPeriod = function (callback) {
            humanResourcesService.getDefaultPeriod(function (period) {
                $scope.filters.setSelectedValueForType(FILTER_TYPE_MONTH, period);
                if ($scope.filters.groups[FILTER_TYPE_MONTH].length > 0) {
                    var periodExists = $scope.filters.selected[FILTER_TYPE_MONTH].value == period;
                    if (!periodExists) {
                        $scope.filters.selected[FILTER_TYPE_MONTH] = $scope.filters.groups[FILTER_TYPE_MONTH][$scope.filters.groups[FILTER_TYPE_MONTH].length - 1];
                    }
                }

                if (callback) {
                    callback();
                }
            });
        };
    }]);;
APP.controller("humanResourceViewController", ["$scope", "$rootScope", "$timeout", "$state", "$interpolate", "categoriesService", "textsService",
    function ($scope, $rootScope, $timeout, $state, $interpolate, categoriesService, textsService) {
    $scope.categoryId = $state.params.categoryId;
    $scope.categoryResultsReportLink = getResultsReportLink($scope.categoryId);
    $scope.texts = textsService.getTexts();

    if ($state.current.name == "human-resource") {
        $state.go("human-resource.about");
    }

    $scope.$on(EVENTS.CATEGORY_LOADED, function (event, data) {
        $rootScope.category = data;
    });

    function getResultsReportLink(categoryId) {
        if (!categoryId || !CONFIG) {
            return null;
        }

        try {
            return $interpolate(CONFIG.categoryResultsReportLink)({
                categoryId: categoryId,
                biennium: $rootScope.biennium
            });
        } catch (error) {
            console.error(error);
            return null;
        }
    };
}]);;
APP.controller("programmeHumanResourceAboutViewController", ["$rootScope", "$scope", "$timeout", "$state", "$stateParams", "programmeHumanResourceService", "textsService", "humanResourcesService",
    function ($rootScope, $scope, $timeout, $state, $stateParams, service, textsService, humanResourcesService) {
        var self = this;

        if ($state.current.name == "programme-human-resource.about") {
            $state.go("programme-human-resource.about.about");
        }

        $scope.programmeId = $state.params.programmeId;
        $scope.categoryId = $state.params.categoryId;
        $scope.aboutState = new DataState();
        $scope.about = null;
        $scope.outputs = null;
        $scope.selectedQuarter = null;
        $scope.quarters = null;
        $scope.keyFigures = null;
        $scope.keyFiguresByLevel = null;
        $scope.keyFiguresByGeography = null;
        $scope.texts = textsService.getTexts();

        $scope.init = function () {
            switch ($state.current.name) {
                case "programme-human-resource.about.results-structure":
                    $scope.loadProgrammeOutputs();
                    break;
                case "programme-human-resource.about.key-figures":
                    $scope.loadKeyFigures();
                    break;
                case "programme-human-resource.about.key-figures-by-level":
                    $scope.loadKeyFiguresByLevel();
                    break;
                case "programme-human-resource.about.key-figures-by-geography":
                    $scope.loadKeyFiguresByGeography();
                    break;
                default:
                    $scope.loadAbout();
            }
        };

        $scope.loadAbout = function () {
            $scope.quarters = null;
            service.loadAbout($scope.programmeId, function (about) {
                $scope.about = about;
                $scope.$emit(EVENTS.PROGRAMME_LOADED, about);
            }, $scope.aboutState);
        };

        $scope.loadProgrammeOutputs = function () {
            $scope.quarters = null;
            service.loadOutputs($scope.programmeId, function (outputs) {
                $scope.outputs = outputs;
            });
        };

        $scope.loadKeyFigures = function () {
            service.loadKeyFigures($scope.programmeId, function (keyFigures) {
                $scope.quarters = keyFigures.quarters;
                setDefaultPeriod();

                $scope.keyFigures = keyFigures;
            });
        };

        $scope.loadKeyFiguresByLevel = function () {
            service.loadKeyFiguresByLevel($scope.programmeId, function (keyFiguresByLevel) {
                $scope.quarters = keyFiguresByLevel.quarters;
                setDefaultPeriod();
                $scope.keyFiguresByLevel = keyFiguresByLevel;
            });
        };

        $scope.loadKeyFiguresByGeography = function () {
            service.loadKeyFiguresByGeography($scope.programmeId, function (keyFiguresByGeography) {
                $scope.keyFiguresByGeography = keyFiguresByGeography;
                if ($scope.keyFiguresByGeography == null) {
                    return;
                }
                $scope.quarters = keyFiguresByGeography.quarters;
                setDefaultPeriod();

                $scope.quarters.forEach(quarter => {
                    for (var key in $scope.keyFiguresByGeography.byCountry[quarter.id]) {
                        for (var i = 0; i < $scope.keyFiguresByGeography.byCountry[quarter.id][key].length; i++) {
                            var item = $scope.keyFiguresByGeography.byCountry[quarter.id][key][i];
                            item.nameLink = $state.href('country-human-resource',
                                {
                                    biennium: $rootScope.biennium,
                                    countryCode: item.countryIso3Code,
                                });
                        }
                    }
                });

                

                $timeout(function () {
                    $("#RegionTabs a:first").tab("show");
                }, 100);
            });
        };

        $scope.showCountryBudgetPieChart = function (regionCode, countries) {
            console.log(countries);
        };

        var setDefaultPeriod = function () {
            humanResourcesService.getDefaultPeriod(function (period) {
                $scope.selectedQuarter = period;
                if ($scope.quarters && $scope.quarters.length > 0) {
                    var periodExists = $scope.quarters.find(x => x.id == period);
                    if (!periodExists) {
                        $scope.selectedQuarter = $scope.quarters[$scope.quarters.length - 1].id;
                    }
                }
            });
        };

    }]);;
APP.controller("programmeHumanResourcesFlowViewController", ["$scope", "$state", "humanResourcesService", "textsService",
    function ($scope, $state, service, textsService) {
        $scope.texts = textsService.getTexts();

        $scope.filters = new RelationsFilters([FILTER_TYPE_MONTH]);

        $scope.flowChartClickableNodesTypes = {};

        $scope.flowChartParams = {};

        $scope.flowChartStateManager = {};

        $scope.initFlowChartParams = function() {
            $scope.flowChartParams = {
                donorCategoryId: null,
                donorId: null,
                regionId: null,
                countryId: null,
                outputId: null,
                programmeSerialNo: $state.params.programmeId,
                snpMonth: $scope.filters.getSelectedValueForType(FILTER_TYPE_MONTH)
            };

            $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
                DATA_TYPE_DONOR_CATEGORY,
                DATA_TYPE_MAJOR_OFFICE,
                DATA_TYPE_OUTPUT,
                DATA_TYPE_COUNTRY,
                DATA_TYPE_DONOR
            ]);

        }

        $scope.loadFilters = function (callback) {
            service.loadHrFlowFilters('programme', function (filters) {
                $scope.filters.buildGroups(filters);
                setDefaultPeriod(() => {
                    if (callback) {
                        callback();
                    }
                });
            });
        };

        $scope.onFilterChange = function () {
            $scope.flowChartStateManager.states = [];
            $scope.initFlowChartParams();

            $scope.flowChartInit($scope.flowChartStateManager);
        }

        $scope.flowChartInit = function (flowChartStateManager) {
            flowChartStateManager.startLoading();

            service.loadHrProgrammeFlow($scope.flowChartParams, function (flow) {
                flowChartStateManager.pushState(flow, {
                    flowChartParams: $scope.flowChartParams,
                    flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
                });

                flowChartStateManager.endLoading();
            });
        };

        $scope.onFlowChartInit = function (flowChartStateManager) {
            $scope.initFlowChartParams();

            $scope.flowChartStateManager = flowChartStateManager;

            $scope.loadFilters(() => {
                $scope.flowChartInit(flowChartStateManager);
            });
        };

        $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
            $scope.flowChartParams = flowChartState.params.flowChartParams;
            $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
        };

        $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
            flowChartStateManager.startLoading();

            if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
                $scope.flowChartParams.donorCategoryId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
            } else if (node.dataType == DATA_TYPE_DONOR) {
                $scope.flowChartParams.donorId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
            } else if (node.dataType == DATA_TYPE_MAJOR_OFFICE) {
                $scope.flowChartParams.regionId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_MAJOR_OFFICE);
            } else if (node.dataType == DATA_TYPE_COUNTRY) {
                $scope.flowChartParams.countryId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_COUNTRY);
            } else if (node.dataType == DATA_TYPE_OUTPUT) {
                $scope.flowChartParams.outputId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_OUTPUT);
            }

            service.loadHrProgrammeFlow($scope.flowChartParams, function (flow) {
                flowChartStateManager.pushState(flow, {
                    flowChartParams: $scope.flowChartParams,
                    flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
                });

                flowChartStateManager.endLoading();
            });
        };

        var setDefaultPeriod = function (callback) {
            service.getDefaultPeriod(function (period) {
                $scope.filters.setSelectedValueForType(FILTER_TYPE_MONTH, period);
                var availableValues = $scope.filters.groups[FILTER_TYPE_MONTH];
                if (availableValues.length > 0) {
                    var periodExists = $scope.filters.selected[FILTER_TYPE_MONTH].value == period;
                    if (!periodExists) {
                        period = availableValues[availableValues.length - 1];
                        $scope.filters.setSelectedValueForType(FILTER_TYPE_MONTH, period);
                    }
                }
                $scope.flowChartParams.snpMonth = period;

                if (callback) {
                    callback();
                }
            });
        };
    }]);;
APP.controller("programmeHumanResourceViewController", ["$scope", "$rootScope", "$timeout", "$state", "$interpolate", "categoriesService", "textsService",
    function ($scope, $rootScope, $timeout, $state, $interpolate, categoriesService, textsService) {
    $scope.programmeId = $state.params.programmeId;
    $scope.categoryId = $state.params.categoryId;
    $scope.texts = textsService.getTexts();

        if ($state.current.name == "programme-human-resource") {
            $state.go("programme-human-resource.about");
    }

    $scope.$on(EVENTS.PROGRAMME_LOADED, function (event, data) {
        $rootScope.programme = data;
    });

}]);;
APP.controller("indicatorsViewController", ["$scope", "$timeout", "$state", "programmesService", "modalDialogsService", "uiService", function ($scope, $timeout, $state, programmesService, modalDialogsService, uiService) {
    console.log($state);

    $scope.defaultFiltersValues = null;
    if ($state.params) {
        $scope.defaultFiltersValues = {
            biennium: $state.params.bienniumFilter || $state.params.biennium,
            categoryId: $state.params.categoryFilter,
            programmeId: $state.params.programmeFilter,
        };
    } 

    $scope.indicatorsState = new DataState();
    $scope.filters = new RelationsFilters([FILTER_TYPE_BIENNIUM, FILTER_TYPE_CATEGORY, FILTER_TYPE_PROGRAMME], {
        "BIENNIUM": {
            filterAllEnabled: false
        },
        "CATEGORY": {
            filterAllEnabled: false
        }
    });

    $scope.loadFilters = function () {
        programmesService.loadIndicatorsFilters(function (filters) {
            $scope.filters.buildGroups(filters);
            if ($scope.defaultFiltersValues) {
                if ($scope.defaultFiltersValues.biennium) {
                    $scope.filters.setSelectedValueForType("BIENNIUM", $scope.defaultFiltersValues.biennium);
                }

                if ($scope.defaultFiltersValues.categoryId) {
                    $scope.filters.setSelectedValueForType("CATEGORY", $scope.defaultFiltersValues.categoryId);
                }

                if ($scope.defaultFiltersValues.programmeId) {
                    $scope.filters.setSelectedValueForType("PROGRAMME", $scope.defaultFiltersValues.programmeId);
                }

                $scope.defaultFiltersValues = null;
            }

            $scope.loadIndicators();
        });
    };

    $scope.loadIndicators = function () {
        programmesService.loadIndicators({
            biennium: $scope.filters.getSelectedValueForType(FILTER_TYPE_BIENNIUM),
            categoryId: $scope.filters.getSelectedValueForType(FILTER_TYPE_CATEGORY),
            programmeId: $scope.filters.getSelectedValueForType(FILTER_TYPE_PROGRAMME),
        }, function (data) {
            uiService.initTooltips();
        }, $scope.indicatorsState);
    };

    $scope.showIndicatorDetails = function (data) {
        modalDialogsService.show({
            templateUrl: "Views/ModalViews/IndicatorDetails.html",
            scope: {
                data: data
            }
        });
    };

    $scope.showIndicatorCountryDetails = function (data) {
        modalDialogsService.show({
            templateUrl: "Views/ModalViews/IndicatorCountryDetails.html",
            scope: {
                isReady: false,
                data: data,
                countriesState: new DataState(),
                loadCountriesDetails: function () {
                    var self = this;
                    programmesService.loadIndicatorCountriesDetails({
                        biennium: $scope.filters.getSelectedValueForType(FILTER_TYPE_BIENNIUM),
                        id: this.data.indicatorCode
                    } , function () {
                        $timeout(function () {
                            $("#indicator-countries").DataTable({
                                aLengthMenu: [
                                    [10, 25, 50, 100, -1],
                                    [10, 25, 50, 100, "All"]
                                ],
                                bResetDisplay: false,
                            });
                            self.isReady = true;
                        });
                    }, this.countriesState);
                }
            }
        });
    };

}]);;
APP.controller("selectBienniumModalViewController", ["$scope", "$rootScope", function ($scope, $rootScope) {
  $scope.allBienniums = BIENNIUMS.all;
  $scope.currentBiennium = $rootScope.biennium;
}]);
;
APP.controller("programmeAboutViewController", ["$scope", "$timeout", "$state", "programmesService", "textsService", function ($scope, $timeout, $state, programmesService, textsService) {
    if ($state.current.name == "programme.about") {
        $state.go("programme.about.about");
    }

    $scope.programmeId = $state.params.programmeId;
    $scope.categoryId = $state.params.categoryId;
    $scope.about = null;
    $scope.aboutState = new DataState();
    $scope.results = null;
    $scope.deliverables = null;
    $scope.keyFigures = null;
    $scope.keyFiguresByLevel = null;
    $scope.keyFiguresByGeography = null;
    $scope.texts = textsService.getTexts();

    $scope.init = function () {
        switch ($state.current.name) {
            case "programme.about.results-structure":
                $scope.loadDeliverables();
                break;
            case "programme.about.key-figures":
                $scope.loadKeyFigures();
                break;
            case "programme.about.key-figures-by-level":
                $scope.loadKeyFiguresByLevel();
                break;
            case "programme.about.key-figures-by-geography":
                $scope.loadKeyFiguresByGeography();
                break;
            default:
                $scope.loadAbout();
        }
    };

    $scope.loadAbout = function () {
        programmesService.loadAbout($scope.programmeId, function (about) {
            $scope.about = about;
            $scope.$emit(EVENTS.PROGRAMME_LOADED, about);
        }, $scope.aboutState);
    };

    $scope.loadResults = function () {
        programmesService.loadResults($scope.programmeId, function (results) {
            $scope.results = results;
        });
    };

    $scope.loadDeliverables = function () {
        programmesService.loadDeliverables($scope.programmeId, function (deliverables) {
            $scope.deliverables = deliverables;
        });
    };

    $scope.loadKeyFigures = function () {
        programmesService.loadKeyFigures($scope.programmeId, function (keyFigures) {
            $scope.keyFigures = keyFigures;
        });
    };

    $scope.loadKeyFiguresByLevel = function () {
        programmesService.loadKeyFiguresByLevel($scope.programmeId, function (keyFiguresByLevel) {
            $scope.keyFiguresByLevel = keyFiguresByLevel;
        });
    };

    $scope.loadKeyFiguresByGeography = function () {
        programmesService.loadKeyFiguresByGeography($scope.programmeId, function(keyFiguresByGeography) {
            $scope.keyFiguresByGeography = keyFiguresByGeography;

            if ($scope.keyFiguresByGeography == null) {
                return;
            }

            $timeout(function () {
                $("#RegionTabs a:first").tab("show");
            }, 0);
        });
    };
}]);;
APP.controller("programmeEndBienniumAssessmentViewController", ["$scope", "$state", "$timeout", "programmesService", "chartsService", "modalDialogsService", "uiService", "$sce",
		function ($scope, $state, $timeout, programmesService, chartsService, modalDialogsService, uiService, $sce) {
		    $scope.categoryId = $state.params.categoryId;
		    $scope.programmeId = $state.params.programmeId;
		    $scope.programmeDetailedReportLink = programmesService.getDetailedReportLink($scope.programmeId, REVIEW_CYCLE.EBA);
		    $scope.tabs = null;
		    $scope.ratingState = new DataState();
		    $scope.indicatorsState = new DataState();
		    $scope.keyFiguresState = new DataState();
		    $scope.indicatorsGraphState = new DataState();


		    $scope.trustAsHtml = function (html) {
		        return $sce.trustAsHtml(html);
		    };

		    $scope.loadInfo = function () {
		        programmesService.loadProgrammeInfo($scope.programmeId, function (data) {
		            $scope.$emit(EVENTS.PROGRAMME_LOADED, data);
		        });
		    };

		    $scope.loadTabs = function () {
		        $scope.loadInfo();

		        programmesService.loadReviewCycleTabs({
		            id: $scope.programmeId,
		            reviewCycle: REVIEW_CYCLE.EBA
		        }, function (tabs) {
		            $scope.tabs = tabs;
		            uiService.showFirstTab("programme-eba-tabs");
		        });
		    };

		    $scope.loadRating = function () {
		        programmesService.loadReviewCycleRating({
		            id: $scope.programmeId,
		            reviewCycle: REVIEW_CYCLE.EBA
		        }, function (data) {
		            uiService.initTooltips();
		        }, $scope.ratingState);
		    };

		    $scope.loadIndicators = function () {
		        programmesService.loadReviewCycleIndicators({
		            id: $scope.programmeId,
		            reviewCycle: REVIEW_CYCLE.EBA
		        }, function (data) {
		            uiService.initTooltips();
		        }, $scope.indicatorsState);
		    };

		    $scope.loadIndicatorsGraph = function () {
		        programmesService.loadIndicatorsGraph({
		            id: $scope.programmeId,
		            reviewCycle: REVIEW_CYCLE.EBA
		        }, function (data) {
		            uiService.initTooltips();
		        }, $scope.indicatorsGraphState);
		    };

		    $scope.loadKeyFigures = function () {
		        programmesService.loadReviewCycleKeyFigures({
		            id: $scope.programmeId,
		            reviewCycle: REVIEW_CYCLE.EBA
		        }, function (data) {
		            chartsService.makeColumnChart("EBAChartBudgetExpenseFunding", data.byRegion, {
		                graphs: {
		                    approvedBudget: {
		                        lineColor: "#4F81BD",
		                        fillColors: "#4F81BD",
		                        title: "WHA Approved Budget",
		                        newStack: true,
		                    },
		                    totalFinancing: {
		                        lineColor: "#79933C",
		                        fillColors: "#79933C",
		                        title: "Funds available",
		                        newStack: true,
		                    },
		                    expTotal: {
		                        lineColor: "#F79646",
		                        fillColors: "#F79646",
		                        title: "Expenditures",
		                        newStack: true,
		                    },
		                },

		                category: {
		                    field: "moName",
		                }
		            });
		        }, $scope.keyFiguresState);
		    };

		    $scope.showFinancialOverview = function () {
		        modalDialogsService.show({
		            templateUrl: "Views/ModalViews/ReviewCycleFinancialOverview.html",
		            scope: {
		                title: sprintf("Results Report: Financial Overview for Programme: %s", $scope.keyFiguresState.data.programmeLongText),
		                data: $scope.keyFiguresState.data.financialOverview
		            }
		        });
		    };

		    $scope.showIndicatorDetails = function (data) {
		        modalDialogsService.show({
		            templateUrl: "Views/ModalViews/IndicatorDetails.html",
		            scope: {
		                data: data
		            }
		        });
		    };

		    $scope.showIndicatorCountryDetails = function (data) {
		        modalDialogsService.show({
		            templateUrl: "Views/ModalViews/IndicatorCountryDetails.html",
		            scope: {
		                isReady: false,
		                data: data,
		                countriesState: new DataState(),
		                loadCountriesDetails: function () {
		                    var self = this;
		                    programmesService.loadIndicatorCountriesDetails({
		                        id: this.data.indicatorCode
		                    }, function () {
		                        $timeout(function () {
		                            $("#indicator-countries").DataTable({
		                                aLengthMenu: [
                                            [10, 25, 50, 100, -1],
                                            [10, 25, 50, 100, "All"]
		                                ],
		                                bResetDisplay: false,
		                            });
		                            self.isReady = true;
		                        });
		                    }, this.countriesState);
		                }
		            }
		        });
		    };
		}]);;
APP.controller("programmeFinancialFlowViewController", ["$scope", "$state", "$timeout", "programmesService", function ($scope, $state, $timeout, programmesService) {
    $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
        DATA_TYPE_DONOR_CATEGORY,
        DATA_TYPE_MAJOR_OFFICE,
        DATA_TYPE_OUTPUT,
        DATA_TYPE_COUNTRY,
        DATA_TYPE_DONOR
    ]);

    $scope.flowChartParams = {
        programmeId: $state.params.programmeId,
        donorCategoryId: null,
        donorId: null,
        regionId: null,
        countryId: null,
        outputId: null
    };

    $scope.onFlowChartInit = function (flowChartStateManager) {
        flowChartStateManager.startLoading();
        programmesService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };

    $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
        $scope.flowChartParams = flowChartState.params.flowChartParams;
        $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
    };

    $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
        flowChartStateManager.startLoading();

        if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
            $scope.flowChartParams.donorCategoryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
        } else if (node.dataType == DATA_TYPE_DONOR) {
            $scope.flowChartParams.donorId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
        } else if (node.dataType == DATA_TYPE_MAJOR_OFFICE) {
            $scope.flowChartParams.regionId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_MAJOR_OFFICE);
        } else if (node.dataType == DATA_TYPE_COUNTRY) {
            $scope.flowChartParams.countryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_COUNTRY);
        } else if (node.dataType == DATA_TYPE_OUTPUT) {
            $scope.flowChartParams.outputId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_OUTPUT);
        }

        programmesService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };
}]);;
APP.controller("programmeIndicatorsViewController", ["$scope", "$state", "$timeout", "programmesService", "chartsService", "modalDialogsService", "uiService", "$sce",
    function ($scope, $state, $timeout, programmesService, chartsService, modalDialogsService, uiService, $sce) {
        $scope.categoryId = $state.params.categoryId;
        $scope.programmeId = $state.params.programmeId;
        $scope.indicatorsState = new DataState();
        $scope.indicatorsGraphState = new DataState();
        
        $scope.loadIndicators = function () {
            programmesService.loadReviewCycleIndicators({
                id: $scope.programmeId,
                reviewCycle: REVIEW_CYCLE.EBA
            }, function (data) {
                uiService.initTooltips();
            }, $scope.indicatorsState);
        };

        $scope.loadIndicatorsGraph = function () {
            programmesService.loadIndicatorsGraph({
                id: $scope.programmeId,
                reviewCycle: REVIEW_CYCLE.EBA
            }, function (data) {
                uiService.initTooltips();
            }, $scope.indicatorsGraphState);
        };

        $scope.showIndicatorDetails = function (data) {
            modalDialogsService.show({
                templateUrl: "Views/ModalViews/IndicatorDetails.html",
                scope: {
                    data: data
                }
            });
        };

        $scope.showIndicatorCountryDetails = function (data) {
            modalDialogsService.show({
                templateUrl: "Views/ModalViews/IndicatorCountryDetails.html",
                scope: {
                    isReady: false,
                    data: data,
                    countriesState: new DataState(),
                    loadCountriesDetails: function () {
                        var self = this;
                        programmesService.loadIndicatorCountriesDetails({
                            id: this.data.indicatorCode
                        }, function () {
                            $timeout(function () {
                                $("#indicator-countries").DataTable({
                                    aLengthMenu: [
                                        [10, 25, 50, 100, -1],
                                        [10, 25, 50, 100, "All"]
                                    ],
                                    bResetDisplay: false,
                                });
                                self.isReady = true;
                            });
                        }, this.countriesState);
                    }
                }
            });
        };
    }]);;
APP.controller("programmeMidTermReviewViewController", ["$scope", "$rootScope", "$state", "$timeout", "programmesService", "chartsService", "modalDialogsService", "uiService", function ($scope, $rootScope, $state, $timeout, programmesService, chartsService, modalDialogsService, uiService) {
    $scope.categoryId = $state.params.categoryId;
    $scope.programmeId = $state.params.programmeId;
    $scope.programmeDetailedReportLink = programmesService.getDetailedReportLink($scope.programmeId, REVIEW_CYCLE.MTR);
    $scope.tabs = null;
    $scope.progressState = new DataState();
    $scope.keyFiguresState = new DataState();

    $scope.loadTabs = function () {
        programmesService.loadReviewCycleTabs({
            id: $scope.programmeId,
            reviewCycle: REVIEW_CYCLE.MTR
        }, function (tabs) {
            $scope.tabs = tabs;
            uiService.showFirstTab("programme-mtr-tabs");
        });
    };

    $scope.loadProgress = function() {
        programmesService.loadReviewCycleProgress({
            id: $scope.programmeId,
            reviewCycle: REVIEW_CYCLE.MTR
        }, function (data) {
            uiService.initTooltips();
        }, $scope.progressState);
    };

    $scope.loadKeyFigures = function () {
        programmesService.loadReviewCycleKeyFigures({
            id: $scope.programmeId,
            reviewCycle: REVIEW_CYCLE.MTR
        }, function (data) {
            chartsService.makeColumnChart("MTRChartBudgetExpenseFunding", data.byRegion, {
                graphs: {
                    approvedBudget: {
                        lineColor: "#4F81BD",
                        fillColors: "#4F81BD",
                        title: "WHA Approved Budget",
                        newStack: true,
                    },
                    totalFinancing: {
                        lineColor: "#79933C",
                        fillColors: "#79933C",
                        title: "Funds available",
                        newStack: true,
                    },
                    expTotal: {
                        lineColor: "#F79646",
                        fillColors: "#F79646",
                        title: "Expenditures",
                        newStack: true,
                    },
                },

                category: {
                    field: "moName",
                }
            });
        }, $scope.keyFiguresState);
    };

    $scope.showFinancialOverview = function () {
        modalDialogsService.show({
            templateUrl: "Views/ModalViews/ReviewCycleFinancialOverview.html",
            scope: {
                title: sprintf("Mid Term Review: Financial Overview for Programme: %s", $scope.keyFiguresState.data.programmeLongText),
                data: $scope.keyFiguresState.data.financialOverview
            }
        });
    };
}]);;
APP.controller("programmeTopStoriesViewController", ["$scope", "$state", "programmesService", function ($scope, $state, programmesService) {
    $scope.programmeId = $state.params.programmeId;
    $scope.categoryId = $state.params.categoryId;
    $scope.topStoriesState = new DataState();
    $scope.selectedStory = null;

    $scope.loadTopStories = function () {
        programmesService.loadTopStories($scope.programmeId, function() {
            var stories = $scope.topStoriesState.data;
            if (stories && stories.length > 0) {
                $scope.selectedStory = stories[0];
            }

        }, $scope.topStoriesState);
    };

    $scope.showStory = function(story) {
        $scope.selectedStory = story;
    };
}]);;
APP.controller("programmeViewController", ["$scope", "$timeout", "$state", "$rootScope", "textsService", function ($scope, $timeout, $state, $rootScope, textsService) {
    $scope.programmeId = $state.params.programmeId;
    $scope.categoryId = $state.params.categoryId;
    $scope.texts = textsService.getTexts();

    if ($state.current.name == "programme") {
        if ($rootScope.budget.isEbaActive) {
            $state.go("programme.eba");
        } else {
            $state.go("programme.about");
        }
    }

    $scope.$on(EVENTS.PROGRAMME_LOADED, function (event, data) {
        $rootScope.programme = data;
    });
}]);;
APP.controller("regionAboutViewController", ["$scope", "$timeout", "$state", "$stateParams", "regionsService", "textsService",
    function ($scope, $timeout, $state, $stateParams, regionsService, textsService) {
    if ($state.current.name == "region.about") {
        $state.go("region.about.about");
    }

    $scope.regionCode = $stateParams.regionCode;
    $scope.about = null;
    $scope.aboutState = new DataState();
    $scope.texts = textsService.getTexts();

    $scope.keyFiguresAllFilters = null;

    $scope.keyFiguresByCategoryState = new DataState();
    $scope.keyFiguresByProgrammeState = new DataState();
    $scope.keyFiguresFilters = new RelationsFilters([FILTER_TYPE_BUDGET_SEGMENT, FILTER_TYPE_SEGMENT, FILTER_TYPE_CATEGORY], {
        "BUDGET_SEGMENT": {
            filterAllEnabled: false
        },
        "CATEGORY": {
            filterAllEnabled: false
        }
    });

    $scope.keyFiguresByLevel = null;
    $scope.keyFiguresByLevelFilters = new RelationsFilters([FILTER_TYPE_BUDGET_SEGMENT, FILTER_TYPE_SEGMENT, FILTER_TYPE_CATEGORY, FILTER_TYPE_PROGRAMME], {
        "BUDGET_SEGMENT": {
            filterAllEnabled: false
        }
    });

    $scope.keyFiguresByGeographyState = new DataState();
    $scope.keyFiguresByGeographyFilters = new RelationsFilters([FILTER_TYPE_BUDGET_SEGMENT, FILTER_TYPE_SEGMENT, FILTER_TYPE_CATEGORY, FILTER_TYPE_PROGRAMME], {
        "BUDGET_SEGMENT": {
            filterAllEnabled: false
        }
    });

    $scope.init = function() {
        switch ($state.current.name) {
            case "region.about.key-figures":
                $scope.loadKeyFiguresFilters();
                break;
            case "region.about.key-figures-by-level":
                $scope.loadKeyFiguresByLevelFilters();
                break;
            case "region.about.key-figures-by-geography":
                $scope.loadKeyFiguresByGeographyFilters();
                break;
            default:
                $scope.loadAbout();
        }
    };

    $scope.loadAbout = function () {
        regionsService.loadAbout($scope.regionCode, function (about) {
            $scope.about = about;
            $scope.$emit(EVENTS.REGION_LOADED, about);
        }, $scope.aboutState);
    };

    $scope.loadKeyFiguresFilters = function () {
        if ($scope.keyFiguresAllFilters) {
            $scope.keyFiguresFilters.buildGroups($scope.keyFiguresAllFilters);
            $scope.loadKeyFigures();

            return;
        }

        regionsService.loadKeyFiguresFilters($scope.regionCode, function (keyFiguresByLevelFilters) {
            $scope.keyFiguresAllFilters = keyFiguresByLevelFilters;
            $scope.keyFiguresFilters.buildGroups($scope.keyFiguresAllFilters);
            $scope.loadKeyFigures();
        });
    };

    $scope.loadKeyFigures = function () {
        $scope.loadKeyFiguresByCategory();
        $scope.loadKeyFiguresByProgramme();
    };

    $scope.loadKeyFiguresByCategory = function () {
        var filters = buildSelectedFilters($scope.keyFiguresFilters);
        regionsService.loadKeyFiguresByCategory($scope.regionCode, filters, function () {
        }, $scope.keyFiguresByCategoryState);
    };

    $scope.loadKeyFiguresByProgramme = function () {
        var filters = buildSelectedFilters($scope.keyFiguresFilters);
        regionsService.loadKeyFiguresByProgramme($scope.regionCode, filters, function () {
        }, $scope.keyFiguresByProgrammeState);
    };

    $scope.loadKeyFiguresByLevelFilters = function () {
        if ($scope.keyFiguresAllFilters) {
            $scope.keyFiguresByLevelFilters.buildGroups($scope.keyFiguresAllFilters);
            $scope.loadKeyFiguresByLevel();

            return;
        }

        regionsService.loadKeyFiguresFilters($scope.regionCode, function (keyFiguresByLevelFilters) {
            $scope.keyFiguresAllFilters = keyFiguresByLevelFilters;
            $scope.keyFiguresByLevelFilters.buildGroups($scope.keyFiguresAllFilters);
            $scope.loadKeyFiguresByLevel();
        });
    };

    $scope.loadKeyFiguresByLevel = function () {
        var filters = buildSelectedFilters($scope.keyFiguresByLevelFilters);
        regionsService.loadKeyFiguresByLevel($scope.regionCode, filters, function (keyFiguresByLevel) {
            $scope.keyFiguresByLevel = keyFiguresByLevel;
        });
    };

    $scope.loadKeyFiguresByGeographyFilters = function () {
        if ($scope.keyFiguresAllFilters) {
            $scope.keyFiguresByGeographyFilters.buildGroups($scope.keyFiguresAllFilters);
            $scope.loadKeyFiguresByGeography();

            return;
        }

        regionsService.loadKeyFiguresFilters($scope.regionCode, function (keyFiguresByLevelFilters) {
            $scope.keyFiguresAllFilters = keyFiguresByLevelFilters;
            $scope.keyFiguresByGeographyFilters.buildGroups($scope.keyFiguresAllFilters);
            $scope.loadKeyFiguresByGeography();
        });
    };

    $scope.loadKeyFiguresByGeography = function () {
        var filters = buildSelectedFilters($scope.keyFiguresByGeographyFilters);
        regionsService.loadKeyFiguresByGeography($scope.regionCode, filters, function (keyFiguresByGeography) {
        }, $scope.keyFiguresByGeographyState);
    };

    var buildSelectedFilters = function (relationsFilters) {
        return {
            budgetSegment: relationsFilters.getSelectedValueForType(FILTER_TYPE_BUDGET_SEGMENT),
            segment: relationsFilters.getSelectedValueForType(FILTER_TYPE_SEGMENT),
            categoryId: relationsFilters.getSelectedValueForType(FILTER_TYPE_CATEGORY),
            programmeId: relationsFilters.getSelectedValueForType(FILTER_TYPE_PROGRAMME),
        };
    };
}]);;
APP.controller("regionFinancialFlowViewController", ["$scope", "$state", "$timeout", "regionsService", "textsService",
  function ($scope, $state, $timeout, regionsService, textsService) {
    $scope.texts = textsService.getTexts();
    $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
        DATA_TYPE_DONOR_CATEGORY,
        DATA_TYPE_MAJOR_OFFICE,
        DATA_TYPE_CATEGORY,
        DATA_TYPE_PROGRAMME,
        DATA_TYPE_OUTPUT,
        DATA_TYPE_COUNTRY,
        DATA_TYPE_DONOR
    ]);

    $scope.flowChartParams = {
        regionId: $state.params.regionCode,
        donorCategoryId: null,
        donorId: null,
        categoryId: null,
        countryId: null,
        programmeId: null,
        outputId: null,
    };

    $scope.onFlowChartInit = function (flowChartStateManager) {
        flowChartStateManager.startLoading();
        regionsService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };

    $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
        $scope.flowChartParams = flowChartState.params.flowChartParams;
        $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
    };

    $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
        flowChartStateManager.startLoading();

        if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
            $scope.flowChartParams.donorCategoryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
        } else if (node.dataType == DATA_TYPE_DONOR) {
            $scope.flowChartParams.donorId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
        } else if (node.dataType == DATA_TYPE_CATEGORY) {
            $scope.flowChartParams.categoryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_CATEGORY);
        } else if (node.dataType == DATA_TYPE_PROGRAMME) {
            $scope.flowChartParams.programmeId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_PROGRAMME);
        } else if (node.dataType == DATA_TYPE_COUNTRY) {
            $scope.flowChartParams.countryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_COUNTRY);
        } else if (node.dataType == DATA_TYPE_OUTPUT) {
            $scope.flowChartParams.outputId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_OUTPUT);
        }

        regionsService.loadFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };
}]);;
APP.controller("regionHqAboutViewController", ["$scope", "$timeout", "$state", "regionsService", "chartsService", "modalDialogsService", "textsService",
  function ($scope, $timeout, $state, regionsService, chartsService, modalDialogsService, textsService) {
    if ($state.current.name == "region-hq.about") {
        $state.go("region-hq.about.about");
    };

    $scope.regionCode = "HQ";
    $scope.aboutState = new DataState();
    $scope.texts = textsService.getTexts();

    $scope.keyFiguresState = new DataState();
    $scope.selectedKeyFiguresFilter = null;
    $scope.keyFiguresByCategory = null;
    $scope.keyFiguresByCategoryExpendinture = null;

    $scope.keyFiguresByProgrammeState = new DataState();
    $scope.keyFiguresByProgrammeFilters = new RelationsFilters([FILTER_TYPE_SEGMENT, FILTER_TYPE_CATEGORY]);

    $scope.keyFiguresByOutputState = new DataState();
    $scope.keyFiguresByOutputFilters = new RelationsFilters([FILTER_TYPE_SEGMENT, FILTER_TYPE_CATEGORY, FILTER_TYPE_PROGRAMME]);
    $scope.outputContributersState = new DataState();
    $scope.outputExpendituresState = new DataState();

    $scope.init = function() {
        switch ($state.current.name) {
            case "region-hq.about.key-figures":
                $scope.loadKeyFigures();
                break;
            case "region-hq.about.key-figures-by-programme":
                $scope.loadKeyFiguresByProgrammeFilters();
                break;
            case "region-hq.about.key-figures-by-output":
                $scope.loadKeyFiguresByOutputFilters();
                break;
            default:
                $scope.loadAbout();
        }
    };

    $scope.loadAbout = function () {
        regionsService.loadAbout($scope.regionCode, function () {
        }, $scope.aboutState);
    };

    $scope.loadKeyFigures = function () {
        regionsService.loadHqKeyFigures(function () {
            var filters = $scope.keyFiguresState.data.filters;
            if (filters && filters.length > 0) {
                $scope.selectedKeyFiguresFilter = filters[0];
                $scope.showKeyFiguresByCategory();
            }
        }, $scope.keyFiguresState);
    };

    $scope.showKeyFiguresByCategory = function () {
        var selectedFilter = $scope.selectedKeyFiguresFilter;
        if (!selectedFilter) {
            return;
        }

        var dataByCategory = $scope.keyFiguresState.data.byCategory;
        if (dataByCategory) {
            $scope.keyFiguresByCategory = dataByCategory[selectedFilter.value];
        }

        var dataByCategoryExpenditure = $scope.keyFiguresState.data.byCategoryExpenditure;
        if (dataByCategoryExpenditure) {
            $scope.keyFiguresByCategoryExpendinture = dataByCategoryExpenditure[selectedFilter.value];
        }
    };

    $scope.loadKeyFiguresByProgrammeFilters = function () {
        if ($scope.keyFiguresByProgrammeFilters.exists()) {
            $scope.loadKeyFiguresByProgramme();

            return;
        }

        regionsService.loadHqKeyFiguresByProgrammeFilters(function (filters) {
            $scope.keyFiguresByProgrammeFilters.buildGroups(filters);
            $scope.loadKeyFiguresByProgramme();
        });
    };

    $scope.loadKeyFiguresByProgramme = function () {
        var filters = buildSelectedFilters($scope.keyFiguresByProgrammeFilters);
        regionsService.loadHqKeyFiguresByProgramme($scope.regionCode, filters, function () {
        }, $scope.keyFiguresByProgrammeState);
    };

    $scope.loadKeyFiguresByOutputFilters = function () {
        if ($scope.keyFiguresByOutputFilters.exists()) {
            $scope.loadKeyFiguresByOutput();

            return;
        }

        regionsService.loadKeyFiguresFilters($scope.regionCode, function (filters) {
            $scope.keyFiguresByOutputFilters.buildGroups(filters);
            $scope.loadKeyFiguresByOutput();
        });
    };

    $scope.loadKeyFiguresByOutput = function () {
        var filters = buildSelectedFilters($scope.keyFiguresByOutputFilters);
        regionsService.loadHqKeyFiguresByOutput($scope.regionCode, filters, function () {
        }, $scope.keyFiguresByOutputState);
    };

    $scope.loadOutputContributors = function (item) {
        if (!item) {
            return;
        }

        var filters = buildSelectedFilters($scope.keyFiguresByOutputFilters);
        regionsService.loadOutputContributors(item.outputShortText, filters, function () {
        }, $scope.outputContributersState);

        modalDialogsService.show({
            templateUrl: "Views/RegionsViews/ModalViews/RegionOutputContributorsModalView.html",
            scope: {
                contributersState: $scope.outputContributersState
            }
        });
    };

    $scope.loadOutputExpenditures = function (item) {
        if (!item) {
            return;
        }

        var filters = buildSelectedFilters($scope.keyFiguresByOutputFilters);
        regionsService.loadOutputExpenditures(item.outputShortText, filters, function () {
        }, $scope.outputExpendituresState);

        modalDialogsService.show({
            templateUrl: "Views/RegionsViews/ModalViews/RegionOutputExpendituresModalView.html",
            scope: {
                expendituresState: $scope.outputExpendituresState
            }
        });
    };

    var buildSelectedFilters = function (relationsFilters) {
        return {
            segment: relationsFilters.getSelectedValueForType(FILTER_TYPE_SEGMENT),
            categoryId: relationsFilters.getSelectedValueForType(FILTER_TYPE_CATEGORY),
            programmeId: relationsFilters.getSelectedValueForType(FILTER_TYPE_PROGRAMME),
        };
    };
}]);;
APP.controller("RegionHqViewController", ["$scope", "$timeout", "$state", "regionsService", "chartsService", function ($scope, $timeout, $state, regionsService, chartsService) {
    if ($state.current.name == "region-hq") {
        $state.go("region-hq.about.about");
    }
}]);;
APP.controller("regionHqFinancialFlowViewController", ["$scope", "$state", "$timeout", "regionsService", function ($scope, $state, $timeout, regionsService) {
    $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
        DATA_TYPE_DONOR_CATEGORY,
        DATA_TYPE_CATEGORY,
        DATA_TYPE_PROGRAMME,
        DATA_TYPE_OUTPUT,
        DATA_TYPE_COUNTRY,
        DATA_TYPE_DONOR
    ]);

    $scope.flowChartParams = {
        donorCategoryId: null,
        donorId: null,
        categoryId: null,
        programmeId: null,
        outputId: null,
    };

    $scope.onFlowChartInit = function (flowChartStateManager) {
        flowChartStateManager.startLoading();
        regionsService.loadHqFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };

    $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
        $scope.flowChartParams = flowChartState.params.flowChartParams;
        $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
    };

    $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
        flowChartStateManager.startLoading();

        if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
            $scope.flowChartParams.donorCategoryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
        } else if (node.dataType == DATA_TYPE_DONOR) {
            $scope.flowChartParams.donorId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
        } else if (node.dataType == DATA_TYPE_CATEGORY) {
            $scope.flowChartParams.categoryId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_CATEGORY);
        } else if (node.dataType == DATA_TYPE_PROGRAMME) {
            $scope.flowChartParams.programmeId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_PROGRAMME);
        }  else if (node.dataType == DATA_TYPE_OUTPUT) {
            $scope.flowChartParams.outputId = node.dataId;
            $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_OUTPUT);
        }

        regionsService.loadHqFinancingFlow($scope.flowChartParams, function (flow) {
            flowChartStateManager.pushState(flow, {
                flowChartParams: $scope.flowChartParams,
                flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
            });

            flowChartStateManager.endLoading();
        });
    };
}]);;
APP.controller("regionsViewController", ["$scope", "$state", "$timeout", "regionsService", function ($scope, $state, $timeout, regionsService) {
    $scope.regionsState = new DataState();
    $scope.selectedRegion = null;
    $scope.map = null;
    $scope.markers = {};
    $scope.geocoder = null;

    $scope.init = function() {
        $scope.loadRegions();
    };

    $scope.loadRegions = function () {
        regionsService.loadRegionsMaps(function () {
            initRegionsAccordion();
        }, $scope.regionsState);
    };

    $scope.onMapReady = function(map) {
        $scope.map = map;
        $scope.geocoder = new google.maps.Geocoder();
        $scope.showRegionOnMap($scope.selectedRegion);
    };

    $scope.showRegionOnMap = function (region) {
        $scope.selectedRegion = region;

        if (!$scope.map || !$scope.geocoder || !$scope.selectedRegion) {
            return;
        }

        var addAndShowMapMarker = function(lat, lng, zoom) {
            var latlng = new google.maps.LatLng(lat, lng);
            $scope.map.setCenter(latlng);
            $scope.map.setZoom(zoom || 13);

            var marker = $scope.markers[region.regionCode];
            if (!marker) {
                marker = new google.maps.Marker({
                    map: $scope.map,
                    position: latlng,
                    title: region.regionName,
                    icon: IMAGE_PIN_MAJOR_OFFICE
                });

                $scope.markers[region.regionCode] = marker;
            }
        };

        if (region.geoLatitude && region.geoLongitude) {
            addAndShowMapMarker(region.geoLatitude, region.geoLongitude, region.zoom);

            return;
        }

        $scope.geocoder.geocode({
            address: region.moPostalAddress
        }, function (results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                var latitude = results[0].geometry.location.lat();
                var longitude = results[0].geometry.location.lng();

                addAndShowMapMarker(latitude, longitude, region.zoom);
            }
        });
    };

    function initRegionsAccordion() {
        var regions = $scope.regionsState.data;
        if (regions && regions.length > 0) {
            $scope.selectedRegion = regions[0];
            $scope.showRegionOnMap($scope.selectedRegion);

            $timeout(function () {
                $("#regions-accordion .collapse:not(:first)").collapse({
                    toggle: true,
                    parent: "#regions-accordion"
                });
            }, 0);
        }
    }
}]);;
APP.controller("regionViewController", ["$scope", "$timeout", "$state", function ($scope, $timeout, $state) {
    $scope.region = null;
    $scope.regionCode = $state.params.regionCode;

    if ($state.current.name == "region") {
        $state.go("region.about.about");
    };

    $scope.$on(EVENTS.REGION_LOADED, function (event, data) {
        $scope.region = data;
    });
}]);;
APP.controller("regionHRAboutViewController", ["$rootScope", "$scope", "$timeout", "$state", "$stateParams", "regionsHRService", "textsService", "humanResourcesService",
    function ($rootScope, $scope, $timeout, $state, $stateParams, service, textsService, humanResourcesService) {
        if ($state.current.name == "regions-human-resource.about") {
            $state.go("regions-human-resource.about.about");
        }

        $scope.regionCode = $stateParams.regionCode;
        $scope.aboutState = new DataState();
        $scope.about = null;
        $scope.outcomes = null;
        $scope.keyFigures = null;
        $scope.keyFiguresByLevel = null;
        $scope.keyFiguresByGeography = null;
        $scope.texts = textsService.getTexts();

        $scope.keyFiguresFilters = new RelationsFilters([FILTER_TYPE_MONTH, FILTER_TYPE_SEGMENT, FILTER_TYPE_CATEGORY]);
        $scope.keyFiguresByLevelFilters = new RelationsFilters([FILTER_TYPE_MONTH, FILTER_TYPE_SEGMENT, FILTER_TYPE_CATEGORY, FILTER_TYPE_PROGRAMME]);
        $scope.keyFiguresByGeographyFilters = new RelationsFilters([FILTER_TYPE_MONTH, FILTER_TYPE_SEGMENT, FILTER_TYPE_CATEGORY, FILTER_TYPE_PROGRAMME]);

        $scope.init = function () {
            switch ($state.current.name) {
            case "regions-human-resource.about.key-figures":
                $scope.loadKeyFiguresFilters();
                break;
            case "regions-human-resource.about.key-figures-by-level":
                $scope.loadKeyFiguresByLevelFilters();
                break;
            case "regions-human-resource.about.key-figures-by-location":
                $scope.loadKeyFiguresByGeographyFilters();
                break;
            default:
                $scope.loadAbout();
            }
        };

        $scope.loadAbout = function () {
            resetData();

            service.loadAbout($scope.regionCode, function (about) {
                $scope.about = about;
                $scope.$emit(EVENTS.REGION_LOADED, about);
            }, $scope.aboutState);
        };

        $scope.loadKeyFiguresFilters = function () {
            resetData();

            service.loadKeyFiguresFilters($scope.regionCode, function (filters) {
                $scope.keyFiguresFilters.buildGroups(filters);
                
                setDefaultPeriod($scope.keyFiguresFilters, () => {
                    $scope.loadKeyFigures();
                });
                
            });
        }

        $scope.loadKeyFiguresByLevelFilters = function () {
            resetData();
            service.loadKeyFiguresByLevelFilters($scope.regionCode, function (filters) {
                $scope.keyFiguresByLevelFilters.buildGroups(filters);

                setDefaultPeriod($scope.keyFiguresByLevelFilters, () => {
                    $scope.loadKeyFiguresByLevel();
                });

            });
        }

        $scope.loadKeyFiguresByGeographyFilters = function () {
            resetData();
            service.loadKeyFiguresByGeographyFilters($scope.regionCode, function (filters) {
                $scope.keyFiguresByGeographyFilters.buildGroups(filters);

                setDefaultPeriod($scope.keyFiguresByGeographyFilters, () => {
                    $scope.loadKeyFiguresByGeography();
                });

            });
        }

        $scope.loadKeyFigures = function () {
            var filters = buildSelectedFilters($scope.keyFiguresFilters);

            service.loadKeyFigures($scope.regionCode, filters, function (keyFigures) {

                $scope.keyFigures = keyFigures;

                $scope.keyFigures.byProgramme.forEach(programme => {
                    programme.nameLink = $state.href('programme-human-resource',
                        {
                            biennium: $rootScope.biennium,
                            categoryId: programme.categorySerialNo,
                            programmeId: programme.programmeSerialNo
                        });
                });
            });
        };

        $scope.loadKeyFiguresByLevel = function () {
            var filters = buildSelectedLevelFilters($scope.keyFiguresByLevelFilters);
            service.loadKeyFiguresByLevel($scope.regionCode, filters, function (keyFiguresByLevel) {
                $scope.keyFiguresByLevel = keyFiguresByLevel;
            });
        };

        $scope.loadKeyFiguresByGeography = function () {
            var filters = buildSelectedGeographyFilters($scope.keyFiguresByGeographyFilters);
            service.loadKeyFiguresByGeography($scope.regionCode, filters, function (keyFiguresByGeography) {
                $scope.keyFiguresByGeography = keyFiguresByGeography;
                if ($scope.keyFiguresByGeography == null) {
                    return;
                }

                for (var i = 0; i < $scope.keyFiguresByGeography.byCountry.items.length; i++) {
                    var item = $scope.keyFiguresByGeography.byCountry.items[i];
                    item.nameLink = $state.href('country-human-resource',
                        {
                            biennium: $rootScope.biennium,
                            countryCode: item.countryIso3Code,
                        });
                }

                $timeout(function () {
                    $("#RegionTabs a:first").tab("show");
                }, 100);
            });
        };

        var setDefaultPeriod = function (filters, callback) {
            humanResourcesService.getDefaultPeriod(function (period) {
                filters.setSelectedValueForType(FILTER_TYPE_MONTH, period);
                if (filters.groups[FILTER_TYPE_MONTH].length > 0) {
                    var periodExists = filters.selected[FILTER_TYPE_MONTH].value == period;
                    if (!periodExists) {
                        filters.selected[FILTER_TYPE_MONTH] = filters.groups[FILTER_TYPE_MONTH][filters.groups[FILTER_TYPE_MONTH].length - 1];
                    }
                }

                if (callback) {
                    callback();
                }
            });
        };

        var buildSelectedFilters = function (relationsFilters) {
            return {
                snpMonth: relationsFilters.getSelectedValueForType(FILTER_TYPE_MONTH),
                segment: relationsFilters.getSelectedValueForType(FILTER_TYPE_SEGMENT),
                categoryId: relationsFilters.getSelectedValueForType(FILTER_TYPE_CATEGORY),
            };
        };

        var buildSelectedLevelFilters = function (relationsFilters) {
            return {
                snpMonth: relationsFilters.getSelectedValueForType(FILTER_TYPE_MONTH),
                segment: relationsFilters.getSelectedValueForType(FILTER_TYPE_SEGMENT),
                categoryId: relationsFilters.getSelectedValueForType(FILTER_TYPE_CATEGORY),
                programmeId: relationsFilters.getSelectedValueForType(FILTER_TYPE_PROGRAMME)
            };
        };

        var buildSelectedGeographyFilters = function (relationsFilters) {
            return {
                snpMonth: relationsFilters.getSelectedValueForType(FILTER_TYPE_MONTH),
                segment: relationsFilters.getSelectedValueForType(FILTER_TYPE_SEGMENT),
                categoryId: relationsFilters.getSelectedValueForType(FILTER_TYPE_CATEGORY),
                programmeId: relationsFilters.getSelectedValueForType(FILTER_TYPE_PROGRAMME)
            };
        };

        var resetData = function() {
            $scope.keyFigures = null;
            $scope.keyFiguresByLevel = null;
            $scope.keyFiguresByGeography = null;
        }
    }]);;
APP.controller("regionHRFlowViewController", ["$scope", "$state", "regionsHRService", "textsService", "humanResourcesService",
    function ($scope, $state, regionsHRService, textsService, humanResourcesService) {
        $scope.texts = textsService.getTexts();

        $scope.filters = new RelationsFilters([FILTER_TYPE_MONTH]);

        $scope.flowChartClickableNodesTypes = {};

        $scope.flowChartParams = {};

        $scope.flowChartStateManager = {};

        $scope.initFlowChartParams = function () {
            $scope.flowChartParams = {
                donorCategoryId: null,
                donorId: null,
                regionId: $state.params.regionCode,
                countryId: null,
                programmeId: null,
                snpMonth: $scope.filters.getSelectedValueForType(FILTER_TYPE_MONTH)
            };

            $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
                DATA_TYPE_DONOR_CATEGORY,
                DATA_TYPE_PROGRAMME,
                DATA_TYPE_CATEGORY,
                DATA_TYPE_COUNTRY,
                DATA_TYPE_DONOR
            ]);
        }

        $scope.loadFilters = function (callback) {
            regionsHRService.loadHrFlowFilters($state.params.regionCode, function (filters) {
                $scope.filters.buildGroups(filters);
                setDefaultPeriod(() => {
                    if (callback) {
                        callback();
                    }
                });
            });
        };

        $scope.onFilterChange = function () {
            $scope.flowChartStateManager.states = [];
            $scope.initFlowChartParams();

            $scope.flowChartInit($scope.flowChartStateManager);
        }

        $scope.flowChartInit = function (flowChartStateManager) {
            flowChartStateManager.startLoading();

            regionsHRService.loadHrFlow($scope.flowChartParams, function (flow) {
                flowChartStateManager.pushState(flow, {
                    flowChartParams: $scope.flowChartParams,
                    flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
                });

                flowChartStateManager.endLoading();
            });
        }

        $scope.onFlowChartInit = function (flowChartStateManager) {

            $scope.flowChartStateManager = flowChartStateManager;

            $scope.loadFilters(() => {
                $scope.initFlowChartParams();
                $scope.flowChartInit(flowChartStateManager);
            });
        };

        $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
            $scope.flowChartParams = flowChartState.params.flowChartParams;
            $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
        };

        $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
            flowChartStateManager.startLoading();

            if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
                $scope.flowChartParams.donorCategoryId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
            } else if (node.dataType == DATA_TYPE_DONOR) {
                $scope.flowChartParams.donorId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
            } else if (node.dataType == DATA_TYPE_MAJOR_OFFICE) {
                $scope.flowChartParams.regionId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_MAJOR_OFFICE);
            } else if (node.dataType == DATA_TYPE_COUNTRY) {
                $scope.flowChartParams.countryId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_COUNTRY);
            } else if (node.dataType == DATA_TYPE_PROGRAMME) {
                $scope.flowChartParams.programmeId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_PROGRAMME);
            } else if (node.dataType == DATA_TYPE_CATEGORY) {
                $scope.flowChartParams.categoryId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_CATEGORY);
            }

            regionsHRService.loadHrFlow($scope.flowChartParams, function (flow) {
                flowChartStateManager.pushState(flow, {
                    flowChartParams: $scope.flowChartParams,
                    flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
                });

                flowChartStateManager.endLoading();
            });
        };

        var setDefaultPeriod = function (callback) {
            humanResourcesService.getDefaultPeriod(function (period) {
                $scope.filters.setSelectedValueForType(FILTER_TYPE_MONTH, period);
                if ($scope.filters.groups[FILTER_TYPE_MONTH].length > 0) {
                    var periodExists = $scope.filters.selected[FILTER_TYPE_MONTH].value == period;
                    if (!periodExists) {
                        $scope.filters.selected[FILTER_TYPE_MONTH] = $scope.filters.groups[FILTER_TYPE_MONTH][$scope.filters.groups[FILTER_TYPE_MONTH].length - 1];
                    }
                }

                if (callback) {
                    callback();
                }
            });
        };
    }]);;
APP.controller("regionHRHqAboutViewController", ["$rootScope", "$scope", "$timeout", "$state", "regionsHRService", "chartsService", "modalDialogsService", "textsService", "humanResourcesService",
    function ($rootScope, $scope, $timeout, $state, regionsService, chartsService, modalDialogsService, textsService, humanResourcesService) {
        if ($state.current.name == "region-human-resource-hq.about") {
            $state.go("region-human-resource-hq.about.about");
        };

        $scope.regionCode = "HQ";
        $scope.aboutState = new DataState();
        $scope.about = null;
        $scope.outcomes = null;
        $scope.keyFigures = null;
        $scope.keyFiguresByProgramme = null;
        $scope.keyFiguresByOutput = null;
        $scope.texts = textsService.getTexts();

        $scope.keyFiguresFilters = new RelationsFilters([FILTER_TYPE_MONTH, FILTER_TYPE_SEGMENT]);
        $scope.keyFiguresByProgrammeFilters = new RelationsFilters([FILTER_TYPE_MONTH, FILTER_TYPE_SEGMENT, FILTER_TYPE_CATEGORY]);
        $scope.keyFiguresByOutputFilters = new RelationsFilters([FILTER_TYPE_MONTH, FILTER_TYPE_SEGMENT, FILTER_TYPE_CATEGORY, FILTER_TYPE_PROGRAMME]);

        $scope.init = function () {
            switch ($state.current.name) {
                case "region-human-resource-hq.about.key-figures":
                    $scope.loadKeyFiguresFilters();
                    break;
                case "region-human-resource-hq.about.key-figures-by-programme":
                    $scope.loadKeyFiguresByProgrammeFilters();
                    break;
                case "region-human-resource-hq.about.key-figures-by-output":
                    $scope.loadKeyFiguresByOutput();
                    break;
                default:
                    $scope.loadAbout();
            }
        };

        $scope.loadAbout = function () {
            resetData();
            regionsService.loadAbout($scope.regionCode, function () {
            }, $scope.aboutState);
        };

        $scope.loadKeyFiguresFilters = function () {
            resetData();

            regionsService.loadHqKeyFiguresFilters($scope.regionCode, function (filters) {
                $scope.keyFiguresFilters.buildGroups(filters);

                setDefaultPeriod($scope.keyFiguresFilters, () => {
                    $scope.loadKeyFigures();
                });

            });
        }

        $scope.loadKeyFiguresByProgrammeFilters = function () {
            resetData();

            regionsService.loadHqKeyFiguresByProgrammeFilters($scope.regionCode, function (filters) {
                $scope.keyFiguresByProgrammeFilters.buildGroups(filters);

                setDefaultPeriod($scope.keyFiguresByProgrammeFilters, () => {
                    $scope.loadKeyFiguresByProgramme();
                });
            });
        }

        $scope.loadKeyFiguresByOutputFilters = function () {
            resetData();

            regionsService.loadHqKeyFiguresByOutputFilters($scope.regionCode, function (filters) {
                $scope.keyFiguresByOutputFilters.buildGroups(filters);

                setDefaultPeriod($scope.keyFiguresByOutputFilters, () => {
                    $scope.loadKeyFiguresByOutput();
                });
            });
        }

        $scope.loadKeyFigures = function () {
            var filters = buildSelectedFilters($scope.keyFiguresFilters);
            regionsService.loadHqKeyFigures($scope.regionCode, filters, function (keyFigures) {
                $scope.keyFigures = keyFigures;
            });
        };

        $scope.loadKeyFiguresByProgramme = function () {
            var filters = buildSelectedByProgrammeFilters($scope.keyFiguresByProgrammeFilters);
            regionsService.loadHqKeyFiguresByProgramme($scope.regionCode, filters, function (keyFigures) {
                $scope.keyFiguresByProgramme = keyFigures;
            });
        };

        $scope.loadKeyFiguresByOutput = function () {
            var filters = buildSelectedByOutputFilters($scope.keyFiguresByOutputFilters);
            regionsService.loadHqKeyFiguresByOutput($scope.regionCode, filters, function (keyFigures) {
                $scope.keyFiguresByOutput = keyFigures;
            });
        };

        var setDefaultPeriod = function (filters, callback) {
            humanResourcesService.getDefaultPeriod(function (period) {
                filters.setSelectedValueForType(FILTER_TYPE_MONTH, period);
                if (filters.groups[FILTER_TYPE_MONTH].length > 0) {
                    var periodExists = filters.selected[FILTER_TYPE_MONTH].value == period;
                    if (!periodExists) {
                        filters.selected[FILTER_TYPE_MONTH] = filters.groups[FILTER_TYPE_MONTH][filters.groups[FILTER_TYPE_MONTH].length - 1];
                    }
                }

                if (callback) {
                    callback();
                }
            });
        };

        var resetData = function () {
            $scope.keyFigures = null;
            $scope.keyFiguresByProgramme = null;
            $scope.keyFiguresByOutput = null;
        }

        var buildSelectedFilters = function (relationsFilters) {
            return {
                snpMonth: relationsFilters.getSelectedValueForType(FILTER_TYPE_MONTH),
                segment: relationsFilters.getSelectedValueForType(FILTER_TYPE_SEGMENT),
            };
        };

        var buildSelectedByProgrammeFilters = function (relationsFilters) {
            return {
                snpMonth: relationsFilters.getSelectedValueForType(FILTER_TYPE_MONTH),
                segment: relationsFilters.getSelectedValueForType(FILTER_TYPE_SEGMENT),
                categoryId: relationsFilters.getSelectedValueForType(FILTER_TYPE_CATEGORY),
            };
        };

        var buildSelectedByOutputFilters = function (relationsFilters) {
            return {
                snpMonth: relationsFilters.getSelectedValueForType(FILTER_TYPE_MONTH),
                segment: relationsFilters.getSelectedValueForType(FILTER_TYPE_SEGMENT),
                categoryId: relationsFilters.getSelectedValueForType(FILTER_TYPE_CATEGORY),
                programmeId: relationsFilters.getSelectedValueForType(FILTER_TYPE_PROGRAMME),
            };
        };
    }]);;
APP.controller("regionHRHqFlowViewController", ["$scope", "$state", "regionsHRService", "textsService", "humanResourcesService",
    function ($scope, $state, regionsHRService, textsService, humanResourcesService) {
        $scope.texts = textsService.getTexts();

        $scope.filters = new RelationsFilters([FILTER_TYPE_MONTH]);

        $scope.flowChartClickableNodesTypes = {};

        $scope.flowChartParams = {};

        $scope.flowChartStateManager = {};
        $scope.regionCode = "HQ";

        $scope.initFlowChartParams = function () {
            $scope.flowChartParams = {
                donorCategoryId: null,
                donorId: null,
                regionId: $scope.regionCode,
                countryId: null,
                programmeId: null,
                snpMonth: $scope.filters.getSelectedValueForType(FILTER_TYPE_MONTH)
            };

            $scope.flowChartClickableNodesTypes = new FlowChartClickableNodesTypes([
                DATA_TYPE_DONOR_CATEGORY,
                DATA_TYPE_PROGRAMME,
                DATA_TYPE_CATEGORY,
                DATA_TYPE_COUNTRY,
                DATA_TYPE_DONOR
            ]);
        }

        $scope.loadFilters = function (callback) {
            regionsHRService.loadHrFlowFilters($scope.regionCode, function (filters) {
                $scope.filters.buildGroups(filters);
                setDefaultPeriod(() => {
                    if (callback) {
                        callback();
                    }
                });
            });
        };

        $scope.onFilterChange = function () {
            $scope.flowChartStateManager.states = [];
            $scope.initFlowChartParams();

            $scope.flowChartInit($scope.flowChartStateManager);
        }

        $scope.flowChartInit = function (flowChartStateManager) {
            flowChartStateManager.startLoading();

            regionsHRService.loadHrFlow($scope.flowChartParams, function (flow) {
                flowChartStateManager.pushState(flow, {
                    flowChartParams: $scope.flowChartParams,
                    flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
                });

                flowChartStateManager.endLoading();
            });
        }

        $scope.onFlowChartInit = function (flowChartStateManager) {

            $scope.flowChartStateManager = flowChartStateManager;

            $scope.loadFilters(() => {
                $scope.initFlowChartParams();
                $scope.flowChartInit(flowChartStateManager);
            });
        };

        $scope.onFlowChartStatePop = function (flowChartState, flowChartStateManager) {
            $scope.flowChartParams = flowChartState.params.flowChartParams;
            $scope.flowChartClickableNodesTypes = flowChartState.params.flowChartClickableNodesTypes;
        };

        $scope.onFlowChartNodeClick = function (node, flowChartStateManager) {
            flowChartStateManager.startLoading();

            if (node.dataType == DATA_TYPE_DONOR_CATEGORY) {
                $scope.flowChartParams.donorCategoryId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR_CATEGORY);
            } else if (node.dataType == DATA_TYPE_DONOR) {
                $scope.flowChartParams.donorId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_DONOR);
            } else if (node.dataType == DATA_TYPE_MAJOR_OFFICE) {
                $scope.flowChartParams.regionId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_MAJOR_OFFICE);
            } else if (node.dataType == DATA_TYPE_COUNTRY) {
                $scope.flowChartParams.countryId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_COUNTRY);
            } else if (node.dataType == DATA_TYPE_PROGRAMME) {
                $scope.flowChartParams.programmeId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_PROGRAMME);
            } else if (node.dataType == DATA_TYPE_CATEGORY) {
                $scope.flowChartParams.categoryId = node.dataId;
                $scope.flowChartClickableNodesTypes.remove(DATA_TYPE_CATEGORY);
            }

            regionsHRService.loadHrFlow($scope.flowChartParams, function (flow) {
                flowChartStateManager.pushState(flow, {
                    flowChartParams: $scope.flowChartParams,
                    flowChartClickableNodesTypes: $scope.flowChartClickableNodesTypes
                });

                flowChartStateManager.endLoading();
            });
        };

        var setDefaultPeriod = function (callback) {
            humanResourcesService.getDefaultPeriod(function (period) {
                $scope.filters.setSelectedValueForType(FILTER_TYPE_MONTH, period);
                if ($scope.filters.groups[FILTER_TYPE_MONTH].length > 0) {
                    var periodExists = $scope.filters.selected[FILTER_TYPE_MONTH].value == period;
                    if (!periodExists) {
                        $scope.filters.selected[FILTER_TYPE_MONTH] = $scope.filters.groups[FILTER_TYPE_MONTH][$scope.filters.groups[FILTER_TYPE_MONTH].length - 1];
                    }
                }

                if (callback) {
                    callback();
                }
            });
        };
    }]);;
APP.controller("RegionHRHqViewController", ["$scope", "$state", function ($scope, $state) {
    if ($state.current.name == "region-human-resources-hq") {
        $state.go("region-human-resources-hq.about.about");
    }
}]);;
APP.controller("regionHRViewController", ["$scope", "$timeout", "$state", function ($scope, $timeout, $state) {
    $scope.region = null;
    $scope.regionCode = $state.params.regionCode;

    if ($state.current.name == "regions-human-resource.about") {
        $state.go("regions-human-resource.about.about");
    };

    $scope.$on(EVENTS.REGION_LOADED, function (event, data) {
        $scope.region = data;
    });
}]);;
APP.controller("contactUsController", ["$scope", "$timeout", "$state", "apiService", function ($scope, $timeout, $state, apiService) {
    $scope.hideFeedbackLoader = true;
    $scope.feedback = {
        name: "",
        email: "",
        phone: "",
        comment: ""
    };

    $scope.showHeadquarterOnMap = function () {
        var mapbg = new GMaps({
            div: '#gmapbg',
            lat: 46.232927,
            lng: 6.134325,
            zoom: 11,
            scrollwheel: false
        });

        mapbg.addMarker({
            lat: 46.232927,
            lng: 6.134325,
            title: 'Headquarters',
            visible: true,
            icon: '/content/img/layout/logos/Logo-Stamp-SM.png',
            infoWindow: {
                content: '<h3>World Health Organization</h3><p>Avenue Appia 20, 1211, Geneve 27, Switzerland</p>'
            }
        });
    }

    var clearFeedbackForm = function () {
        $("#feedbackForm").find("input,textarea").val("");
    };

    $scope.sendMessage = function () {
        if ($scope.feedbackForm.$valid) {
            $scope.hideFeedbackLoader = null;
            apiService.sendData("contactUs/request-feedback", $scope.feedback, function (response) {
                $scope.hideFeedbackLoader = true;

                if (response && response.status === 200) {
                    $("#feedbackSuccessModal").modal();
                    clearFeedbackForm();
                } else {
                    $("#feedbackFailureModal").modal();
                }
            }, $state)};
        }
    }
]);;
APP.controller("iatiDataController", ["$scope", "$timeout", "$state", "iatiFilesService", function ($scope, $timeout, $state, iatiFilesService) {
}]);;
APP.controller("iatiFilesController", ["$scope", "$timeout", "$state", "iatiFilesService", function ($scope, $timeout, $state, iatiFilesService) {
    $scope.iatiFiles = null;

    $scope.loadIatiFiles = function () {
        iatiFilesService.loadAllIatiFiles(function (data) {
            $scope.iatiFiles = data;
        });
    }

    $scope.loadIatiFiles();
}]);

APP.config(['$compileProvider', function ($compileProvider) {
    $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|sms|tel|data):/);
}]);;
APP.controller("keyDocumentsController", ["$scope", "$timeout", "$state", "documentsService", function ($scope, $timeout, $state, documentsService) {
    $scope.documents = null;
    $scope.selectedSection = '';

    $scope.loadDocuments = function() {
        documentsService.loadSectionDocuments(function (documents) {
            $scope.documents = documents;
        });
    }

    $scope.showDocuments = function (section) {
        $scope.selectedSection = !section ? '' : section;
    }

    $scope.loadDocuments();
}]);

APP.filter('unique', function () {
    return function (collection, keyname) {
        var output = [],
            keys = [];

        angular.forEach(collection, function (item) {
            var key = item[keyname];
            if (keys.indexOf(key) === -1) {
                keys.push(key);
                output.push(item);
            }
        });

        return output;
    };
});;
APP.controller("resolutionsAndDecisionsController", ["$scope", "$state", "resolutionsAndDecisionsService",
  function ($scope, $state, resolutionsAndDecisionsService) {
    var self = this;

    $scope.data = null;
    $scope.filtersSource = null;

    $scope.selected = {
      meetingType: null,
      meetingName: null,
      documentCode: null,
      year: null,
      keyword: null
    };

    $scope.filters = {
      meetingTypes: [],
      meetingNames: [],
      documentCodes: [],
      years: []
    };

    $scope.loadFilters = function () {
      resolutionsAndDecisionsService.loadFilters(function (filters) {
        $scope.filtersSource = filters;
        $scope.updateFilters();
      });
    };

    $scope.updateFilters = function () {
      $scope.filters.years = $scope.filtersSource.years;
      $scope.filters.meetingTypes = $scope.filtersSource.meetingTypes;

      var sourceMeetingNames = $scope.filtersSource.meetingNames;
      if ($scope.selected.meetingType) {
        sourceMeetingNames = sourceMeetingNames.filter(function (x) {
          return x.meetingType == $scope.selected.meetingType;
        });
      }

      $scope.filters.meetingNames = sourceMeetingNames.map(function (x) {
        return x.meetingName;
      }).filter(function (value, index, self) {
        return self.indexOf(value) === index;
      });


      var sourceDocumentCodes = $scope.filtersSource.documentCodes;
      if ($scope.selected.meetingName) {
        sourceDocumentCodes = sourceDocumentCodes.filter(function (x) {
          return x.meetingName == $scope.selected.meetingName;
        });
      } else if ($scope.selected.meetingType) {
        sourceDocumentCodes = sourceDocumentCodes.filter(function (x) {
          return $scope.filters.meetingNames.indexOf(x.meetingName) > -1;
        });
      }

      $scope.filters.documentCodes = sourceDocumentCodes.map(function (x) {
        return x.documentCode;
      }).filter(function (value, index, self) {
        return self.indexOf(value) === index;
      });

      if ($scope.selected.documentCode) {
        if ($scope.filters.documentCodes.indexOf($scope.selected.documentCode) == -1) {
          $scope.selected.documentCode = null;
        }
      }

    };

    $scope.search = function () {
      resolutionsAndDecisionsService.search($scope.selected, function (data) {
        $scope.data = data;
      });
    };

  }
]);
;
APP.directive("budgetTable", ["$timeout", function ($timeout) {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/BudgetTable.html",
        scope: {
            items: "=",
            footer: "=",
            itemsTitle: "@",
            nameField: "@",
            staffField: "@",
            activityField: "@",
            totalField: "@",
            financingField: "@",
            financingPercentField: "@",
            expField: "@",
            onFinancingDetailsClick: "&",
            onExpDetailsClick: "&"
        }, 
        link: function (scope, element, attrs) {
        },
        controller: ["$scope", "uiService", function ($scope, uiService) {
            uiService.initTooltips();

            $scope.isFinancingDetailsEnabled = function () {
                return !!$scope.onFinancingDetailsClick();
            };

            $scope.isExpDetailsEnabled = function () {
                return !!$scope.onExpDetailsClick();
            };
        }]
    };
}]);;
APP.directive("categoriesMenuTypeSelect", [function () {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/CategoriesMenuTypeSelect.html",
        scope: {
            biennium: "=",
            categoryId: "="
        },
        link: function (scope, element, attrs) {
            
        },
        controller: ["$scope", "apiService", function ($scope, apiService) {
            $scope.isVisible = function () {
                return apiService.isHumanResourcesEnabled($scope.biennium);
            };
        }]
    };
}]);;
APP.directive("countriesBudgetTable", ["$timeout", function ($timeout) {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/CountriesBudgetTable.html",
        scope: {
            items: "=",
            footer: "=",
            itemsTitle: "@",
            nameField: "@",
            staffField: "@",
            activityField: "@",
            totalField: "@",
            financingField: "@",
            financingPercentField: "@",
            flagField: "@",
            expField: "@"
        }, 
        link: function (scope, element, attrs) {
        },
        controller: ["uiService", "$scope", "$rootScope", function (uiService, $scope, $rootScope) {
            $scope.biennium = $rootScope.biennium;
            uiService.initTooltips();
        }]
    };
}]);;
APP.directive("countryCategoryHrTable", ["$timeout", function ($timeout) {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/CountryCategoryHRTable.html",
        scope: {
            items: "=",
            itemsTitle: "@",
            nameField: "@",
            staffFTEField: "@",
            headCountField: "@",
            femaleField: "@",
            maleField: "@",
            femalePercentField: "@",
        },
        link: function (scope, element, attrs) {
            scope.$watch("items", function (items) {
                scope.data = Array.isArray(items) ? items : [items];
            });
        },
        controller: ["$scope", "uiService", function ($scope, uiService) {
            uiService.initTooltips();

            $scope.parseNumber = function (number) {
                if (number) {
                    return parseFloat(number).toFixed(0);
                }

                return null;
            };
        }]
    };
}]);;
APP.directive("countryCategoryProgrammeTable", ["$timeout", function ($timeout) {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/CountryCategoryProgrammeTable.html",
        scope: {
            items: "=",
            header: "@",
            itemsTitle: "@",
            total: "=",
            biennium: "@",
            countryCode: "@",
            nameField: "=categoryLongText",
            staffField: "=budgetStaffFormatted",
            activityField: "=budgetActivityFormatted",
            totalField: "=budgetTotalFormatted",
            financingField: "=financingFormatted",
            financingPercentField: "=financingPercent",
            expField: "=expenditureTotalFormatted"
        },
        link: function (scope, element, attrs) {
        },
        controller: ["uiService", function (uiService) {
            uiService.initTooltips();
        }]
    };
}]);;
APP.directive("countryCategoryTable", ["$timeout", function ($timeout) {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/CountryCategoryTable.html",
        scope: {
            items: "=",
            itemsTitle: "@",
            nameField: "=categoryLongText",
            staffField: "=budgetStaffFormatted",
            activityField: "=budgetActivityFormatted",
            totalField: "=budgetTotalFormatted",
            financingField: "=financingFormatted",
            financingPercentField: "=financingPercent",
            expField: "=expenditureTotalFormatted"
        },
        link: function (scope, element, attrs) {
        },
        controller: ["$scope", "uiService", function ($scope, uiService) {
            $scope.$watch("items", function () {
                uiService.initTooltips();
            });
        }]
    };
}]);;
APP.directive("countryProgrammeOutputTable", ["$timeout", function ($timeout) {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/CountryProgrammeOutputTable.html",
        scope: {
            items: "=",
            header: "@",
            itemsTitle: "@",
            total: "=",
            biennium: "@",
            countryCode: "@",
            categorySerialNo: "@",
            nameField: "=categoryLongText",
            staffField: "=budgetStaffFormatted",
            activityField: "=budgetActivityFormatted",
            totalField: "=budgetTotalFormatted",
            financingField: "=financingFormatted",
            financingPercentField: "=financingPercent",
            expField: "=expenditureTotalFormatted"
        },
        link: function (scope, element, attrs) {
        },
        controller: ["uiService", function (uiService) {
            uiService.initTooltips();
        }]
    };
}]);;
// #region Types

var NODE_TYPE_DISTRIBUTION_SOURCE = "DS";
var NODE_TYPE_DISTRIBUTION_TARGET = "DT";
var NODE_TYPE_EXPENDITURE_SOURCE = "ES";
var NODE_TYPE_EXPENDITURE_TARGET = "ET";
var ALL_NODE_TYPES = [
    NODE_TYPE_DISTRIBUTION_SOURCE,
    NODE_TYPE_DISTRIBUTION_TARGET,
    NODE_TYPE_EXPENDITURE_SOURCE,
    NODE_TYPE_EXPENDITURE_TARGET
];

var LINK_TYPE_DISTRIBUTION_TO_DISTRIBUTION = "D2D";
var LINK_TYPE_DISTRIBUTION_TO_EXPENDITURE = "D2E";
var LINK_TYPE_EXPENDITURE_TO_EXPENDITURE = "E2E";
var ALL_LINK_TYPES = [
    LINK_TYPE_DISTRIBUTION_TO_DISTRIBUTION,
    LINK_TYPE_DISTRIBUTION_TO_EXPENDITURE,
    LINK_TYPE_EXPENDITURE_TO_EXPENDITURE
];

var DATA_TYPE_DONOR_CATEGORY = "DC";
var DATA_TYPE_DONOR = "DN";
var DATA_TYPE_MAJOR_OFFICE = "MO";
var DATA_TYPE_PROGRAMME = "PM";
var DATA_TYPE_CATEGORY = "CG";
var DATA_TYPE_COUNTRY = "CT";
var DATA_TYPE_OUTPUT = "OP";

var FlowChartClickableNodesTypes = function (types) {
    this.types = types || [];
    this.remove = function (type) {
        if (this.types) {
            this.types = this.types.filter(function (t) {
                return t !== type;
            });
        }
    };

    this.isNodeClickable = function (node) {
        return node && this.types && $.inArray(node.dataType, this.types) >= 0;
    };
};

// #endregion

APP.directive("flowChart", ["$timeout", function ($timeout) {

    // #region Constants

    var NODE_WIDTH = 15;
    var NODE_PADDING = 65;
    var NODE_TITLE_MARGIN = 10;
    var NODE_TITLE_LINE_WIDTH = 200;
    var NODE_MIN_HEIGHT = 2;
    var NODES_MARGIN = { x: 200, y: 0 };
    var NODES_DISTANCE = 200;
    var NODES_MIN_OPACITY = 0.25;
    var LAYOUT_ITERATIONS = 32;
    var NODE_COLORS = {};
    NODE_COLORS[NODE_TYPE_DISTRIBUTION_SOURCE] = d3.rgb(49, 99, 168);
    NODE_COLORS[NODE_TYPE_DISTRIBUTION_TARGET] = d3.rgb(195, 62, 37);
    NODE_COLORS[NODE_TYPE_EXPENDITURE_SOURCE] = d3.rgb(195, 62, 37);
    NODE_COLORS[NODE_TYPE_EXPENDITURE_TARGET] = d3.rgb(81, 168, 44);

    // #endregion

    // #region Local variables

    var chartStateManager = null;
    var chartContext = null;
    var chartScope = null;
    var isChartEnable = false;

    // #endregion

    // #region Main methods

    function init(scope, timeout) {
        if (!scope) {
            return;
        }

        chartScope = scope;
        chartStateManager = {
            states: [],
            pushState: function (chartData, stateParams, cleanupHistory) {
                var self = this;
                buildChart(chartData);

                if (cleanupHistory) {
                    self.states = [];
                }

                self.states.push({
                    chartData: chartData,
                    params: angular.copy(stateParams)
                });

                chartScope.isBackEnable = self.states.length > 1;
                timeout(function () {
                    handleBackButton();
                });
            },
            popState: function () {
                var self = this;
                if (self.states.length <= 1) {
                    return;
                }

                self.states.pop();
                var lastState = self.states[self.states.length - 1];
                buildChart(lastState.chartData);
                var onChartStatePop = chartScope.onChartStatePop();
                if (onChartStatePop) {
                    onChartStatePop({
                        chartData: lastState.chartData,
                        params: angular.copy(lastState.params)
                    }, self);
                }

                chartScope.isBackEnable = self.states.length > 1;
                timeout(function () {
                    handleBackButton();
                });
            },
            startLoading: function () {
                isChartEnable = false;
                showPreloader();
            },
            endLoading: function () {
                isChartEnable = true;
                hidePreloader();
            }
        };

        chartScope.chartStateManager = chartStateManager;
        chartScope.isBackEnable = false;
        chartScope.onBackClick = function () {
            if (isChartEnable) {
                chartStateManager.popState();
                scrollToTop();
            }
        };

        var onChartInit = chartScope.onChartInit();
        if (onChartInit) {
            onChartInit(chartScope.chartStateManager);
        }

        if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) {
            $(window).bind("touchend touchcancel touchleave", function (e) {
                handleBackButton();
            });
        } else {
            $(window).scroll(function () {
                handleBackButton();
            });
        }
    }

    function buildChart(chartData) {
        try {
            chartContext = buildChartContext(chartData);
            if (!chartContext) {
                return;
            }

            layoutChart();
            renderChart();
        } catch (error) {
            console.log(error);
        }
    };

    function buildChartContext(chartData) {
        var chartDiv = d3.select("#" + chartScope.chartId);
        chartDiv.selectAll("*").remove();

        if (!chartData) {
            return null;
        }

        function NodeMap() {
            this.byId = {};
            this.byType = {};
            this.byTypeAndId = {};
            this.byTypeAndDataId = {};
            var map = this;

            ALL_NODE_TYPES.forEach(function (type) {
                map.byType[type] = [];
                map.byTypeAndId[type] = {};
                map.byTypeAndDataId[type] = {};
            });
        };

        var nodesMap = new NodeMap();
        chartData.nodes.forEach(function (node) {
            nodesMap.byId[node.id] = node;
            nodesMap.byType[node.type].push(node);
            nodesMap.byTypeAndId[node.type][node.id] = node;
            nodesMap.byTypeAndDataId[node.type][node.dataId] = node;
        });

        chartData.links = chartData.links.map(function (link) {
            if (typeof link.source != "object") {
                link.source = nodesMap.byId[link.source];
            }

            if (typeof link.target != "object") {
                link.target = nodesMap.byId[link.target];
            }

            return link;
        });

        var nodeMaxPercentByType = {};
        ALL_NODE_TYPES.forEach(function (type) {
            var percent = 0.0;
            nodesMap.byType[type].forEach(function (node) {
                percent = Math.max(node.percent, percent);
            });

            nodeMaxPercentByType[type] = percent;
        });

        return {
            chartData: chartData,
            chartSankey: null,
            chartSvg: chartDiv.append("svg"),
            chartWidth: chartScope.chartWidth,
            chartHeight: chartScope.chartHeight,
            nodesMap: nodesMap,
            nodeMaxPercentByType: nodeMaxPercentByType,
            isNodeClickable: function (node) {
                return chartScope.clickableNodesTypes && chartScope.clickableNodesTypes.isNodeClickable(node);
            },
        };
    };

    // #endregion

    // #region Layout methods

    function layoutChart() {
        var chartData = chartContext.chartData;

        // Calculate chart size
        var chartWidth = chartContext.chartWidth;
        var chartHeight = chartContext.chartHeight;
        if (!chartHeight) {
            var largestNodes = null;
            ALL_NODE_TYPES.forEach(function (type) {
                var nodes = chartContext.nodesMap.byType[type];
                if (largestNodes == null || nodes.length > largestNodes.length) {
                    largestNodes = nodes;
                }
            });

            chartHeight = 0;
            largestNodes.forEach(function (node) {
                chartHeight += Math.max(NODE_MIN_HEIGHT, node.percent) + NODE_PADDING;
            });
        }

        // Setup svg
        chartContext.chartSvg
            .attr({
                width: +chartWidth + NODES_MARGIN.x * 2,
                height: +chartHeight + NODES_MARGIN.y * 2
            });

        // Layout sankey chart
        chartContext.chartSankey = d3.sankey()
            .nodeWidth(NODE_WIDTH)
            .nodePadding(NODE_PADDING)
            .size([chartWidth, chartHeight]);

        chartContext.chartSankey
            .nodes(chartData.nodes)
            .links(chartData.links)
            .layout(LAYOUT_ITERATIONS);

        var nodesMap = chartContext.nodesMap;
        // Apply alignment between distribution target and expenditure source nodes 
        nodesMap.byType[NODE_TYPE_EXPENDITURE_SOURCE].forEach(function (node) {
            node.y = nodesMap.byTypeAndDataId[NODE_TYPE_DISTRIBUTION_TARGET][node.dataId].y;
        });

        // Apply nodes constraints 
        chartData.nodes.forEach(function (node) {
            var targetLinks = node.targetLinks;
            if (targetLinks && targetLinks.length > 0) {
                var sourceNode = targetLinks[0].source;
                node.x = sourceNode.x + NODES_DISTANCE + NODE_WIDTH;
            } else {
                node.x = 0;
            }
        });

        // Apply nodes custom configuration
        chartData.nodes.forEach(function (node) {
            // Node margins
            node.x += NODES_MARGIN.x;
            node.y += NODES_MARGIN.y;

            // Node color
            node.color = NODE_COLORS[node.type];

            // Node opacity
            var nodePercent = node.percent;
            var nodeMaxPercent = chartContext.nodeMaxPercentByType[node.type];
            if (node.type == NODE_TYPE_EXPENDITURE_SOURCE && node.targetLinks && node.targetLinks.length > 0) {
                nodePercent = node.targetLinks[0].source.percent;
                nodeMaxPercent = chartContext.nodeMaxPercentByType[NODE_TYPE_DISTRIBUTION_TARGET];
            }

            node.opacity = Math.max(NODES_MIN_OPACITY, nodePercent / nodeMaxPercent);
        });
    };

    // #endregion

    // #region Render methods

    function renderChart() {
        var svgMainGroup = chartContext.chartSvg.append("g");
        renderChartLinks(svgMainGroup);
        renderChartNodes(svgMainGroup);

        var svgMainGroupBBox = svgMainGroup.node().getBBox();
        svgMainGroup.attr("transform", function () { 
            var x = svgMainGroupBBox.width > 430 ? Math.max((chartContext.chartSvg.attr("width") - svgMainGroupBBox.width) / 2, 0) : 0;
            var y = Math.max((chartContext.chartSvg.attr("height") - svgMainGroupBBox.height) / 2, 0);

            return sprintf("translate(%s, %s)", x, y);
        });
    };

    function renderChartLinks(svgMainGroup) {
        var linksGroup = svgMainGroup.append("g");
        var links = linksGroup.selectAll(".link")
            .data(chartContext.chartData.links)
            .enter();

        var paths = links.append("path")
            .attr({
                "class": "link",
                d: chartContext.chartSankey.link(),
                id: function (link) {
                    return link.id;
                }
            })
            .style("stroke-width", function (d) {
                return Math.max(2, d.dy);
            })
            .on({
                mouseover: onLinkMouseOver,
                mouseout: onLinkMouseOut,
            });;

        paths.append("title")
            .text(function (link) {
                return link.subtitle;
            });

        paths.each(function (link) {
            if (link.type == LINK_TYPE_DISTRIBUTION_TO_EXPENDITURE) {
                var pathBBox = this.getBBox();
                var pathText = linksGroup.append("text")
                    .attr("x", pathBBox.x + pathBBox.width / 2)
                    .attr("text-anchor", "middle")
                    .text(link.title);

                var pathTextBBox = pathText.node().getBBox();
                pathText.attr("y", pathBBox.y + Math.min(pathTextBBox.height / 2, link.target.dy / 2) - NODE_TITLE_MARGIN);
                pathText.call(wrapText, NODE_TITLE_LINE_WIDTH);
            }
        });
    };

    function renderChartNodes(svgMainGroup) {
        var nodes = svgMainGroup.append("g")
            .selectAll(".node")
            .data(chartContext.chartData.nodes)
            .enter()
            .append("g")
            .classed("clickable", chartContext.isNodeClickable)
            .classed("node", true)
            .attr({
                transform: function (node) {
                    return sprintf("translate(%(x)f, %(y)f)", node);
                }
            })
            .on({
                mouseover: onNodeMouseOver,
                mouseout: onNodeMouseOut,
                click: function (node) {
                    onNodeClick(node, chartContext);
                }
            });

        nodes.append("rect")
            .attr({
                width: chartContext.chartSankey.nodeWidth(),
                height: function (node) {
                    return Math.max(NODE_MIN_HEIGHT, node.dy);
                }
            })
            .style({
                fill: function (node) {
                    return node.color;
                },
                stroke: function (node) {
                    return node.color;
                },
                opacity: function (node) {
                    return node.opacity;
                }
            });

        nodes.append("title")
            .text(function (node) {
                return node.subtitle;
            });

        nodes.append("text")
            .attr("text-anchor", function (node) {
                return node.type == NODE_TYPE_EXPENDITURE_TARGET
                    ? "start"
                    : "end";
            })
            .attr("transform", null)
            .text(function (node) {
                return (node.type == NODE_TYPE_DISTRIBUTION_SOURCE || node.type == NODE_TYPE_EXPENDITURE_TARGET)
                    ? node.title
                    : null;
            })
            .attr("x", function (node) {
                return node.type == NODE_TYPE_DISTRIBUTION_SOURCE
                    ? -NODE_TITLE_MARGIN
                    : chartContext.chartSankey.nodeWidth() + NODE_TITLE_MARGIN;
            })
            .attr("y", function (node) {
                return 0;
            })
            .call(wrapText, NODE_TITLE_LINE_WIDTH)
            .call(function (selector) {
                selector.each(function (node) {
                    var textBBox = this.getBBox();
                    if (!textBBox) {
                        return;
                    }

                    var y = (node.dy / 2 - textBBox.height / 2) + NODE_TITLE_MARGIN;
                    var text = d3.select(this);
                    text.attr("y", y);
                    text.selectAll("tspan").attr("y", y);
                });
            });
    };

    function wrapText(selector, width) {
        selector.each(function () {
            var text = d3.select(this),
                words = text.text().split(/\s+/).reverse(),
                word,
                line = [],
                lineNumber = 0,
                lineHeight = 1.1, // ems
                x = text.attr("x"),
                y = text.attr("y"),
                dy = 0.35,
                tspan = text.text(null).append("tspan")
                    .attr("x", x)
                    .attr("y", y)
                    .attr("dy", dy + "em");

            while (word = words.pop()) {
                line.push(word);
                tspan.text(line.join(" "));
                if (tspan.node().getComputedTextLength() > width) {
                    line.pop();
                    tspan.text(line.join(" "));
                    line = [word];
                    tspan = text.append("tspan")
                            .attr("x", x)
                            .attr("y", y)
                            .attr("dy", ++lineNumber * lineHeight + dy + "em")
                            .text(word);
                }
            }
        });
    };

    // #endregion

    // #region Events methods

    function onNodeMouseOver(node) {
        if (!isChartEnable) {
            return;
        }

        highlightNodes([node], function (targetNode) {
            return targetNode.sourceLinks;
        }, function (targetLink) {
            return targetLink.target;
        });

        highlightNodes([node], function (sourceNode) {
            return sourceNode.targetLinks;
        }, function (sourceLink) {
            return sourceLink.source;
        });
    }

    function onNodeMouseOut(node) {
        resetHighlightedNodes();
    }

    function onNodeClick(node) {
        if (!isChartEnable || !chartContext.isNodeClickable(node)) {
            return;
        }

        var onNodeClickHandler = chartScope.onNodeClick();
        if (onNodeClickHandler) {
            onNodeClickHandler(node, chartStateManager);
        }
    }

    function onLinkMouseOver(link) {
        if (!isChartEnable) {
            return;
        }

        highlightLink(link);
        highlightNodes([link.target], function (targetNode) {
            return targetNode.sourceLinks;
        }, function (targetLink) {
            return targetLink.target;
        });

        highlightNodes([link.source], function (sourceNode) {
            return sourceNode.targetLinks;
        }, function (sourceLink) {
            return sourceLink.source;
        });
    }

    function onLinkMouseOut(link) {
        resetHighlightedNodes();
    }

    // #endregion 

    // #region Helper methods

    function highlightNodes(nodes, linksSelector, nodeSelector) {
        function addActiveClass(activeNodes, activeLinksSelector, activeNodeSelector) {
            if (!activeNodes) {
                return;
            }

            activeNodes.forEach(function (nodeToActivate) {
                var links = activeLinksSelector(nodeToActivate);
                if (links) {
                    links.forEach(function (link) {
                        highlightLink(link);
                        addActiveClass([activeNodeSelector(link)], activeLinksSelector, activeNodeSelector);
                    });
                }
            });
        }

        addActiveClass(nodes, linksSelector, nodeSelector);
        d3.selectAll("svg .link:not(.active)").classed("disabled", true);
    }

    function highlightLink(link) {
        d3.select("#" + link.id)
            .classed("active", true)
            .classed("disabled", false);
    }

    function resetHighlightedNodes() {
        var chartSvg = chartContext.chartSvg;
        chartSvg.selectAll(".active").classed("active", false);
        chartSvg.selectAll(".disabled").classed("disabled", false);
    }

    function showPreloader() {
        $("#preloader-" + chartScope.chartId).show();
        $("#" + chartScope.chartId).removeClass("disabled").addClass("disabled");

        scrollToTop();
    }

    function hidePreloader() {
        $("#preloader-" + chartScope.chartId).hide();
        $("#" + chartScope.chartId).removeClass("disabled");
    }

    function showNoData() {
    }

    function hideNoData() {
    }

    function scrollToTop() {
        var autoscrollEnabled = chartScope.autoscrollEnabled();
        if (typeof autoscrollEnabled != "undefined" && !autoscrollEnabled) {
            return;
        }

        var top = 0;
        var offset = $("#" + chartScope.chartId).offset();
        if (offset) {
            top = Math.max(0, offset.top - 300);
        }

        $("html, body").animate({
            scrollTop: top
        }, 600);
    }

    function handleBackButton() {
        if (!chartScope) {
            return;
        }

        var $chartRoot = $(sprintf('[chart-id="%(chartId)s"]', chartScope));
        var $chartDiv = $chartRoot.find(".flow-chart");
        var $chartBackButton = $chartRoot.find(".btn-zoom-out");
        if (!$chartDiv || !$chartBackButton || $chartDiv.length == 0 || $chartBackButton.length == 0) {
            return;
        }

        var chartDivRect = $chartDiv[0].getBoundingClientRect();
        var chartBackButtonRect = $chartBackButton[0].getBoundingClientRect();
        if (chartDivRect.top - 100 > chartBackButtonRect.top) {
            $chartBackButton.fadeTo(0, 0);
        } else {
            $chartBackButton.fadeTo(0, 1);
        }
    }

    // #endregion

    return {
        restrict: "E",
        templateUrl: "Views/Directives/FlowChart.html",
        scope: {
            chartStateManager: "=?",
            clickableNodesTypes: "=?",
            chartId: "@",
            chartWidth: "@",
            chartHeight: "@",
            chartLeftTitle: "@",
            chartMiddleTitle: "@",
            chartRightTitle: "@",
            isBackEnable: "=?",
            autoscrollEnabled: "&",
            onChartInit: "&",
            onChartStatePop: "&",
            onNodeClick: "&",
            onBackClick: "&?",
        },
        link: function (scope, element, attrs) {
        },
        controller: ["$scope", function ($scope) {
            init($scope, $timeout);
        }]
    };
}]);

;
APP.directive("humanResourceFiguresTable", ["$timeout", function ($timeout) {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/HumanResourceFiguresTable.html",
        scope: {
            items: "=",
            footer: "=",
            itemsTitle: "@",
            nameField: "@",
            staffFTEField: "@",
            headCountField: "@",
            femaleField: "@",
            maleField: "@",
            femalePercentField: "@",
            showFlag: "@"
        },
        link: function (scope, element, attrs) {
            scope.$watch("items", function (items) {
                scope.data = Array.isArray(items) ? items : [items];
            });
        },
        controller: ["$scope", "uiService", function ($scope, uiService) {
            uiService.initTooltips();

            $scope.parseNumber = function (number) {
                if (number) {
                    return parseFloat(number).toFixed(0);
                }

                return null;
            };
        }]
    };
}]);;
APP.directive("humanResourcesCountsTable", ["$timeout", function ($timeout) {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/HumanResourcesCountsTable.html",
        scope: {
            data: "=",
            footer: "=",
            itemsTitle: "=",
        },
        link: function (scope, element, attrs) {
            scope.$watch("items", function (data) {
                scope.data = data;
            });
        },
        controller: ["$scope", "uiService", function ($scope, uiService) {
            uiService.initTooltips();

            $scope.parseNumber = function (number) {
                if (number) {
                    return parseFloat(number).toFixed(0);
                }

                return null;
            };
        }]
    };
}]);;
APP.directive("humanResourcesGenderBalanceCountsTable", ["$timeout", function ($timeout) {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/HumanResourcesGenderBalanceCountsTable.html",
        scope: {
            data: "=",
            footer: "=",
            itemsTitle: "=",
        },
        link: function (scope, element, attrs) {
            scope.$watch("items", function (data) {
                scope.data = data;
            });
        },
        controller: ["$scope", "uiService", function ($scope, uiService) {
            uiService.initTooltips();
        }]
    };
}]);;
APP.directive("indicator", ["chartsService", function (chartsService) {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/Indicator.html",
        scope: {
            chartId: "=",
            chartData: "="
        },
        link: function (scope, element, attrs) {
            scope.getChartId = function() {
                return scope.chartId ? scope.chartId.replace(/\./g, "-") : scope.chartId;
            };

            scope.$watch("chartData", function (newValue) {
                chartsService.makeIndicatorChart(scope.getChartId(), newValue);
            });
        }
    };
}]);;
var IMAGE_PIN_MAJOR_OFFICE = "content/img/layout/logos/Logo-Stamp-SM.png";

var MAP_MEDIATOR = function () {
    return {
        isMapLoaded: false,
        isMapApiLoaded: false,

        loadMapAndApi: function (key, version, callback) {
            if (callback) {
                $(this).one(MAP_MEDIATOR_EVENTS.MAP_API_READY, function() {
                    callback();
                });
            }

            if (google.maps) {
                this.isMapApiLoaded = true;
                this.isMapLoaded = true;
            }

            if (this.isMapApiLoaded) {
                $(this).trigger(MAP_MEDIATOR_EVENTS.MAP_API_READY);

                return;
            }

            if (this.isMapLoaded) {
                return;
            }

            this.isMapLoaded = true;

            var mapScript = document.createElement("script");
            mapScript.type = "text/javascript";
            document.getElementsByTagName("head")[0].appendChild(mapScript);
            mapScript.src = sprintf("http://maps.googleapis.com/maps/api/js?key=%s&v=%s&callback=MAP_MEDIATOR.loadMapCallback", key, version);
        },

        loadMapCallback: function () {
            this.isMapApiLoaded = true;
            $(this).trigger(MAP_MEDIATOR_EVENTS.MAP_API_READY);
        }
    };
}();

var MAP_MEDIATOR_EVENTS = {
    MAP_API_READY: "MAP_API_READY",
};

APP.directive("googleMap", ["$timeout", function ($timeout) {
    function init(scope) {
        MAP_MEDIATOR.loadMapAndApi(scope.mapKey, scope.mapVersion, function() {
            $timeout(function() {
                buildMap(scope);
            }, 500);
        });
    }

    function buildMap(scope) {
        var map = new google.maps.Map(document.getElementById(scope.mapId), {
            zoom: 13,
            center: scope.mapCenter(),
            styles: [{ "featureType": "administrative", "elementType": "labels.text.fill", "stylers": [{ "color": "#444444" }] }, { "featureType": "landscape", "elementType": "all", "stylers": [{ "color": "#f2f2f2" }] }, { "featureType": "poi", "elementType": "all", "stylers": [{ "visibility": "off" }] }, { "featureType": "road", "elementType": "all", "stylers": [{ "saturation": -100 }, { "lightness": 45 }] }, { "featureType": "road.highway", "elementType": "all", "stylers": [{ "visibility": "simplified" }] }, { "featureType": "road.arterial", "elementType": "labels.icon", "stylers": [{ "visibility": "off" }] }, { "featureType": "transit", "elementType": "all", "stylers": [{ "visibility": "off" }] }, { "featureType": "water", "elementType": "all", "stylers": [{ "color": "#46bcec" }, { "visibility": "on" }] }]
        });

        var onMapReady = scope.onMapReady();
        if (onMapReady) {
            onMapReady(map);
        }
    }

    return {
        restrict: "E",
        templateUrl: "Views/Directives/Map.html",
        scope: {
            mapId: "@",
            mapKey: "@",
            mapVersion: "@",
            mapCenter: "&",
            onMapReady: "&"
        },
        link: function (scope, element, attrs) {
            init(scope);
        }
    };
}]);;
APP.directive("multiselectFilters", ["$timeout", function ($timeout) {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/MultiselectFilters.html",
        scope: {
            filters: "=",
            type: "@",

            onFilterChange: "&",
        },
        link: function (scope, element, attrs) {
        },
        controller: ["$scope", "textsService", function ($scope, textsService) {
            $scope.onChange = function () {
                var onFilterChange = $scope.onFilterChange();
                if (onFilterChange) {
                    onFilterChange();
                }
            };

            var texts = textsService.getTexts();
            $scope.getOptionName = function (filter) {
                var name = filter.name;
                return texts[name] || name;
            };

        }]
    };
}]);;
APP.directive("pieChart", ["chartsService", function (chartsService) {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/PieChart.html",
        scope: {
            chartId: "@",
            chartHeight: "@",
            chartData: "=",
            chartConfig: "&",
            labelsEnabled: "&",
            valuesEnabled: "&",
            legendsEnabled: "&",
            radius: "&",
            innerRadius: "@",
            colors: "&",
            title: "@",
            valueField: "@",
            titleField: "@",
            useOriginalLegendNumbers: "@",
            precision: "@",
            labelText: "@",
            labelRadius: "@"
        },
        link: function (scope, element, attrs) {
            scope.$watch("chartData", function (newValue) {
                chartsService.makePieChart(scope.chartId, newValue || [], {
                    title: scope.title,
                    valueField: scope.valueField,
                    titleField: scope.titleField,
                    labelsEnabled: scope.labelsEnabled(),
                    valuesEnabled: scope.valuesEnabled(),
                    legendsEnabled: scope.legendsEnabled(),
                    config: scope.chartConfig(),
                    radius: scope.radius(),
                    innerRadius: scope.innerRadius,
                    colors: scope.colors(),
                    useOriginalLegendNumbers: scope.useOriginalLegendNumbers,
                    precision: scope.precision,
                    labelText: scope.labelText,
                    labelRadius: scope.labelRadius,
                });
            });
        }
    };
}]);;
APP.directive("preloader", ["$timeout", function ($timeout) {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/Preloader.html",
        link: function (scope, element, attrs) {
            var contentId = attrs.contentId;
            var showContent = function() {
                $timeout(function () {
                    element.hide();
                    angular.element("#" + contentId).show();
                }, 0);
            };

            var hideContent = function () {
                $timeout(function () {
                    element.show();
                    angular.element("#" + contentId).hide();
                }, 0);
            };

            var watchFor = function(attribute) {
                if (typeof attribute != "undefined" && attribute && attribute.length > 0) {
                    scope.$watch(attribute, function (newValue) {
                        if (typeof newValue != "undefined" && newValue) {
                            showContent();
                        } else {
                            hideContent();
                        }
                    });
                }
            };

            watchFor(attrs.watchData);
            watchFor(attrs.watchState);
        }
    };
}]);;
APP.directive("programmeBudgetHumanResourceTabs", [function () {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/ProgrammeBudgetHumanResourceTabs.html",
        scope: {
            biennium: "=",
            programmeBudgetLink: "@",
            humanResourceLink: "@",
            programmeBudgetActiveState: "@",
            humanResourceActiveState: "@"
        },
        link: function (scope, element, attrs) {
        },
        controller: ["$scope", "apiService", function ($scope, apiService) {
            
            $scope.isVisible = function () {
                return apiService.isHumanResourcesEnabled($scope.biennium);
            };
        }]
    };
}]);;
APP.directive("programmeMenuTypeSelect", [function () {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/ProgrammeMenuTypeSelect.html",
        scope: {
            biennium: "=",
            categoryId: "=",
            programmeId: "="
        },
        link: function (scope, element, attrs) {
            
        },
        controller: ["$scope", "apiService", function ($scope, apiService) {
            $scope.isVisible = function () {
                return apiService.isHumanResourcesEnabled($scope.biennium);
            };
        }]
    };
}]);;
APP.directive("relationsFilters", ["$timeout", function ($timeout) {
    return {
        restrict: "E",
        templateUrl: "Views/Directives/RelationsFilters.html",
        scope: {
            filters: "=",
            type: "@",
            onFilterChange: "&",
        },
        link: function (scope, element, attrs) {
        },
        controller: ["$scope", "textsService", function ($scope, textsService) {
            $scope.onChange = function () {
                var onFilterChange = $scope.onFilterChange();
                if (onFilterChange) {
                    onFilterChange();
                }
            };

          var texts = textsService.getTexts();
          $scope.getOptionName = function (filter) {
            var name = filter.name;
            return texts[name] || name;
          };

        }]
    };
}]);;
