diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c46da4d77ffa..443120267530 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -41,6 +41,10 @@ browser/widevine @simonhong browser/ui/views/infobars/brave_wayback_machine_* @simonhong components/brave_wayback_machine @simonhong +# NTP sponsored images +browser/ntp_sponsored_images @simonhong @petemill +components/ntp_sponsored_images @simonhong @petemill + # Licensing of third-party components common/licenses/ @fmarier components/brave_new_tab_ui/data/backgrounds.ts @fmarier diff --git a/.storybook/config.js b/.storybook/config.js index b90e3ce0058d..bb63c977f0ba 100644 --- a/.storybook/config.js +++ b/.storybook/config.js @@ -32,6 +32,11 @@ addParameters({ function loadStories() { initLocale(locale) + window.loadTimeData = { + getString (key) { + return locale[key] || key + } + } const req = require.context('../components/', true, /\/stories\/.*\.tsx$/) req.keys().forEach(filename => req(filename)) } diff --git a/.storybook/locale.ts b/.storybook/locale.ts index c582a38b0471..5359e5993508 100644 --- a/.storybook/locale.ts +++ b/.storybook/locale.ts @@ -215,6 +215,19 @@ const locale: Record = { rewardsRestoreText4: 'Enter your recovery key or', rewardsSummary: 'Rewards Summary', rewardsWhy: 'Why Brave Rewards…', + rewardsWidgetBraveRewards: 'Brave Rewards', + rewardsWidgetBat: 'BAT', + rewardsWidgetEstimatedEarnings: 'Estimated earnings so far this month', + rewardsWidgetAdsOptInDescription: 'Earn tokens by viewing privacy-respecting ads.', + rewardsWidgetMonthlyTips: 'Tips and contributions this month', + rewardsWidgetNotificationTitle: 'Way to Go!', + rewardsWidgetBrandedNotificationTitle: 'You\'re getting paid to view this background image.', + rewardsWidgetBrandedNotificationDescription: '$1Learn more$2 about sponsored images in Brave Rewards.', + rewardsWidgetAboutRewards: '$1Learn more$2 about Brave Rewards', + rewardsWidgetTurnOnAds: 'Turn on Brave Ads', + rewardsWidgetTurnOnRewards: 'Turn on Rewards', + rewardsWidgetServiceText: 'By turning on Rewards, you agree to the $1Terms of Service$2.', + rewardsWidgetTurnOnLearnMore: 'Learn more', saveAd: 'Save', saveAsFile: 'Save', saved: 'Saved', diff --git a/browser/BUILD.gn b/browser/BUILD.gn index 8e33c9c64fc6..8bcca50b0838 100644 --- a/browser/BUILD.gn +++ b/browser/BUILD.gn @@ -135,6 +135,7 @@ source_set("browser_process") { "//brave/components/brave_webtorrent/browser/buildflags", "//brave/components/content_settings/core/browser", "//brave/components/greaselion/browser/buildflags", + "//brave/components/ntp_sponsored_images/browser", "//brave/components/ntp_tiles", "//brave/components/p3a", "//brave/components/resources", @@ -165,6 +166,7 @@ source_set("browser_process") { "//extensions/buildflags", "//brave/chromium_src:browser", "themes", + "ntp_sponsored_images", "//services/network/public/cpp", "//services/service_manager/embedder", "//third_party/widevine/cdm:buildflags", diff --git a/browser/brave_browser_process_impl.cc b/browser/brave_browser_process_impl.cc index 344ff01cecec..b89c4337a8fd 100644 --- a/browser/brave_browser_process_impl.cc +++ b/browser/brave_browser_process_impl.cc @@ -20,6 +20,7 @@ #include "brave/browser/tor/buildflags.h" #include "brave/browser/ui/brave_browser_command_controller.h" #include "brave/common/pref_names.h" +#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h" #include "brave/components/brave_component_updater/browser/local_data_files_service.h" #include "brave/components/brave_shields/browser/ad_block_custom_filters_service.h" #include "brave/components/brave_shields/browser/ad_block_regional_service_manager.h" @@ -28,11 +29,13 @@ #include "brave/components/brave_shields/browser/https_everywhere_service.h" #include "brave/components/brave_shields/browser/referrer_whitelist_service.h" #include "brave/components/brave_shields/browser/tracking_protection_service.h" +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_images_service.h" #include "brave/components/p3a/buildflags.h" #include "brave/components/p3a/brave_histogram_rewrite.h" #include "brave/components/p3a/brave_p3a_service.h" #include "brave/services/network/public/cpp/system_request_handler.h" #include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/webui/components_ui.h" #include "chrome/common/buildflags.h" #include "chrome/common/chrome_paths.h" #include "components/component_updater/component_updater_service.h" @@ -74,6 +77,8 @@ #include "chrome/browser/ui/browser.h" #endif +using ntp_sponsored_images::NTPSponsoredImagesService; + namespace { // Initializes callback for SystemRequestHandler @@ -127,6 +132,9 @@ BraveBrowserProcessImpl::BraveBrowserProcessImpl(StartupData* startup_data) void BraveBrowserProcessImpl::Init() { BrowserProcessImpl::Init(); + brave_component_updater::BraveOnDemandUpdater::GetInstance()-> + RegisterOnDemandUpdateCallback( + base::BindRepeating(&ComponentsUI::OnDemandUpdate)); UpdateBraveDarkMode(); pref_change_registrar_.Add( kBraveDarkMode, @@ -207,6 +215,17 @@ BraveBrowserProcessImpl::ad_block_regional_service_manager() { return ad_block_regional_service_manager_.get(); } +NTPSponsoredImagesService* +BraveBrowserProcessImpl::ntp_sponsored_images_service() { + if (!ntp_sponsored_images_service_) { + ntp_sponsored_images_service_ = + std::make_unique( + component_updater()); + } + + return ntp_sponsored_images_service_.get(); +} + brave_shields::AutoplayWhitelistService* BraveBrowserProcessImpl::autoplay_whitelist_service() { if (!autoplay_whitelist_service_) { diff --git a/browser/brave_browser_process_impl.h b/browser/brave_browser_process_impl.h index c331f2dd5820..97b7240e8d40 100644 --- a/browser/brave_browser_process_impl.h +++ b/browser/brave_browser_process_impl.h @@ -50,11 +50,16 @@ class GreaselionDownloadService; #endif } // namespace greaselion +namespace ntp_sponsored_images { +class NTPSponsoredImagesService; +} // namespace ntp_sponsored_images + namespace extensions { class BraveTorClientUpdater; } using brave_component_updater::BraveComponent; +using ntp_sponsored_images::NTPSponsoredImagesService; class BraveBrowserProcessImpl : public BrowserProcessImpl { public: @@ -91,6 +96,7 @@ class BraveBrowserProcessImpl : public BrowserProcessImpl { BraveWidevineBundleManager* brave_widevine_bundle_manager(); #endif brave::BraveStatsUpdater* brave_stats_updater(); + NTPSponsoredImagesService* ntp_sponsored_images_service(); private: // BrowserProcessImpl overrides: @@ -145,6 +151,7 @@ class BraveBrowserProcessImpl : public BrowserProcessImpl { std::unique_ptr brave_widevine_bundle_manager_; #endif scoped_refptr brave_p3a_service_; + std::unique_ptr ntp_sponsored_images_service_; SEQUENCE_CHECKER(sequence_checker_); diff --git a/browser/browser_context_keyed_service_factories.cc b/browser/browser_context_keyed_service_factories.cc index 36c52de8959d..294b459e6bcc 100644 --- a/browser/browser_context_keyed_service_factories.cc +++ b/browser/browser_context_keyed_service_factories.cc @@ -12,6 +12,7 @@ #include "brave/components/brave_ads/browser/ads_service_factory.h" #include "brave/components/brave_rewards/browser/rewards_service_factory.h" #include "brave/components/greaselion/browser/buildflags/buildflags.h" +#include "brave/browser/ntp_sponsored_images/view_counter_service_factory.h" #if BUILDFLAG(ENABLE_GREASELION) #include "brave/browser/greaselion/greaselion_service_factory.h" @@ -33,6 +34,7 @@ void EnsureBrowserContextKeyedServiceFactoriesBuilt() { #endif TorProfileServiceFactory::GetInstance(); SearchEngineProviderServiceFactory::GetInstance(); + ntp_sponsored_images::ViewCounterServiceFactory::GetInstance(); #if !defined(OS_ANDROID) BookmarkPrefsServiceFactory::GetInstance(); diff --git a/browser/component_updater/brave_component_updater_delegate.cc b/browser/component_updater/brave_component_updater_delegate.cc index 50833501d065..3299b855febb 100644 --- a/browser/component_updater/brave_component_updater_delegate.cc +++ b/browser/component_updater/brave_component_updater_delegate.cc @@ -10,11 +10,12 @@ #include "base/sequenced_task_runner.h" #include "base/task/post_task.h" #include "brave/browser/component_updater/brave_component_installer.h" -#include "chrome/browser/ui/webui/components_ui.h" +#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h" #include "chrome/browser/browser_process.h" #include "components/component_updater/component_updater_service.h" using brave_component_updater::BraveComponent; +using brave_component_updater::BraveOnDemandUpdater; namespace brave { @@ -46,7 +47,7 @@ bool BraveComponentUpdaterDelegate::Unregister( void BraveComponentUpdaterDelegate::OnDemandUpdate( const std::string& component_id) { - ComponentsUI::OnDemandUpdate(component_id); + BraveOnDemandUpdater::GetInstance()->OnDemandUpdate(component_id); } scoped_refptr diff --git a/browser/ntp_sponsored_images/BUILD.gn b/browser/ntp_sponsored_images/BUILD.gn new file mode 100644 index 000000000000..02a27a4886a7 --- /dev/null +++ b/browser/ntp_sponsored_images/BUILD.gn @@ -0,0 +1,18 @@ +source_set("ntp_sponsored_images") { + sources = [ + "view_counter_service_factory.cc", + "view_counter_service_factory.h", + ] + + deps = [ + "//base", + "//brave/components/ntp_sponsored_images/browser", + "//brave/common", + "//brave/components/brave_ads/browser", + "//chrome/common", + "//content/public/browser", + "//components/keyed_service/content", + "//components/prefs", + "//components/pref_registry", + ] +} diff --git a/browser/ntp_sponsored_images/view_counter_service_factory.cc b/browser/ntp_sponsored_images/view_counter_service_factory.cc new file mode 100644 index 000000000000..6e184d93b31b --- /dev/null +++ b/browser/ntp_sponsored_images/view_counter_service_factory.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2020 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. + +#include "brave/browser/ntp_sponsored_images/view_counter_service_factory.h" + +#include "brave/browser/brave_browser_process_impl.h" +#include "brave/common/pref_names.h" +#include "brave/components/brave_ads/browser/ads_service.h" +#include "brave/components/brave_ads/browser/ads_service_factory.h" +#include "brave/components/ntp_sponsored_images/browser/features.h" +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_images_service.h" +#include "brave/components/ntp_sponsored_images/browser/view_counter_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/chrome_features.h" +#include "content/public/browser/browser_context.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" +#include "components/pref_registry/pref_registry_syncable.h" +#include "components/prefs/pref_service.h" + +namespace ntp_sponsored_images { + +// static +ViewCounterService* ViewCounterServiceFactory::GetForProfile(Profile* profile) { + if (base::FeatureList::IsEnabled(features::kBraveNTPBrandedWallpaper)) + return static_cast( + GetInstance()->GetServiceForBrowserContext(profile, true)); + + return nullptr; +} + +// static +ViewCounterServiceFactory* ViewCounterServiceFactory::GetInstance() { + return base::Singleton::get(); +} + +ViewCounterServiceFactory::ViewCounterServiceFactory() + : BrowserContextKeyedServiceFactory( + "ViewCounterService", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(brave_ads::AdsServiceFactory::GetInstance()); +} + +ViewCounterServiceFactory::~ViewCounterServiceFactory() {} + +KeyedService* ViewCounterServiceFactory::BuildServiceInstanceFor( + content::BrowserContext* browser_context) const { + Profile* profile = Profile::FromBrowserContext(browser_context); + + bool is_supported_locale = false; + auto* ads_service = brave_ads::AdsServiceFactory::GetForProfile(profile); + if (!ads_service) { + LOG(ERROR) << "Ads service was disabled at build time!"; + } else { + is_supported_locale = ads_service->IsSupportedLocale(); + } + + auto* service = g_brave_browser_process->ntp_sponsored_images_service(); + service->AddDataSource(browser_context); + + ViewCounterService* instance = new ViewCounterService( + service, + profile->GetPrefs(), + is_supported_locale); + return instance; +} + +void ViewCounterServiceFactory::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) { + registry->RegisterBooleanPref( + kBrandedWallpaperNotificationDismissed, false); + registry->RegisterBooleanPref( + kNewTabPageShowBrandedBackgroundImage, true); +} + +bool ViewCounterServiceFactory::ServiceIsCreatedWithBrowserContext() const { + return true; +} + +} // namespace ntp_sponsored_images diff --git a/browser/ntp_sponsored_images/view_counter_service_factory.h b/browser/ntp_sponsored_images/view_counter_service_factory.h new file mode 100644 index 000000000000..70d4d17f7e25 --- /dev/null +++ b/browser/ntp_sponsored_images/view_counter_service_factory.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2019 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_BROWSER_NTP_SPONSORED_IMAGES_VIEW_COUNTER_SERVICE_FACTORY_H_ +#define BRAVE_BROWSER_NTP_SPONSORED_IMAGES_VIEW_COUNTER_SERVICE_FACTORY_H_ + +#include "base/macros.h" +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +class Profile; + +namespace content { +class BrowserContext; +} + +namespace user_prefs { +class PrefRegistrySyncable; +} + +namespace ntp_sponsored_images { + +class ViewCounterService; + +class ViewCounterServiceFactory : public BrowserContextKeyedServiceFactory { + public: + static ViewCounterService* GetForProfile(Profile* profile); + static ViewCounterServiceFactory* GetInstance(); + + private: + friend struct base::DefaultSingletonTraits; + + ViewCounterServiceFactory(); + ~ViewCounterServiceFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + bool ServiceIsCreatedWithBrowserContext() const override; + void RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) override; + + DISALLOW_COPY_AND_ASSIGN(ViewCounterServiceFactory); +}; + +} // namespace ntp_sponsored_images + +#endif // BRAVE_BROWSER_NTP_SPONSORED_IMAGES_VIEW_COUNTER_SERVICE_FACTORY_H_ diff --git a/browser/ui/BUILD.gn b/browser/ui/BUILD.gn index 4611fd20a704..6e2c412c6716 100644 --- a/browser/ui/BUILD.gn +++ b/browser/ui/BUILD.gn @@ -195,6 +195,7 @@ source_set("ui") { "//brave/components/brave_adblock_ui:generated_resources", "//brave/components/webcompat_reporter/ui:generated_resources", "//brave/components/brave_new_tab_ui:generated_resources", + "//brave/components/brave_ads/browser", "//brave/components/brave_rewards/browser", "//brave/components/brave_rewards/resources", "//brave/components/brave_shields/browser", diff --git a/browser/ui/webui/brave_new_tab_message_handler.cc b/browser/ui/webui/brave_new_tab_message_handler.cc index 48a69db91e4a..84f628190751 100644 --- a/browser/ui/webui/brave_new_tab_message_handler.cc +++ b/browser/ui/webui/brave_new_tab_message_handler.cc @@ -6,24 +6,54 @@ #include "brave/browser/ui/webui/brave_new_tab_message_handler.h" #include +#include +#include -#include "base/bind.h" #include "base/values.h" #include "brave/browser/profiles/profile_util.h" #include "brave/browser/search_engines/search_engine_provider_util.h" #include "brave/browser/ui/webui/brave_new_tab_ui.h" +#include "brave/browser/ntp_sponsored_images/view_counter_service_factory.h" #include "brave/common/pref_names.h" +#include "brave/components/brave_ads/browser/ads_service.h" +#include "brave/components/brave_ads/browser/ads_service_factory.h" +#include "brave/components/ntp_sponsored_images/browser/features.h" +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_images_data.h" +#include "brave/components/ntp_sponsored_images/browser/view_counter_service.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/common/chrome_features.h" #include "content/public/browser/web_ui_data_source.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_service.h" +using ntp_sponsored_images::features::kBraveNTPBrandedWallpaper; +using ntp_sponsored_images::NTPSponsoredImagesData; +using ntp_sponsored_images::ViewCounterService; + namespace { bool IsPrivateNewTab(Profile* profile) { return brave::IsTorProfile(profile) || profile->IsIncognitoProfile(); } +base::DictionaryValue GetBrandedWallpaperDictionary( + NTPSponsoredImagesData* wallpaper, + size_t wallpaper_image_index) { + DCHECK(wallpaper_image_index >= 0 && + wallpaper_image_index < wallpaper->wallpaper_image_urls().size()); + + base::DictionaryValue data; + data.SetString("wallpaperImageUrl", + wallpaper->wallpaper_image_urls()[wallpaper_image_index]); + auto logo_data = std::make_unique(); + logo_data->SetString("image", wallpaper->logo_image_url()); + logo_data->SetString("companyName", wallpaper->logo_company_name); + logo_data->SetString("alt", wallpaper->logo_alt_text); + logo_data->SetString("destinationUrl", wallpaper->logo_destination_url); + data.SetDictionary("logo", std::move(logo_data)); + return data; +} + base::DictionaryValue GetStatsDictionary(PrefService* prefs) { base::DictionaryValue stats_data; stats_data.SetInteger( @@ -46,6 +76,9 @@ base::DictionaryValue GetPreferencesDictionary(PrefService* prefs) { pref_data.SetBoolean( "showBackgroundImage", prefs->GetBoolean(kNewTabPageShowBackgroundImage)); + pref_data.SetBoolean( + "brandedWallpaperOptIn", + prefs->GetBoolean(kNewTabPageShowBrandedBackgroundImage)); pref_data.SetBoolean( "showClock", prefs->GetBoolean(kNewTabPageShowClock)); @@ -58,6 +91,9 @@ base::DictionaryValue GetPreferencesDictionary(PrefService* prefs) { pref_data.SetBoolean( "showRewards", prefs->GetBoolean(kNewTabPageShowRewards)); + pref_data.SetBoolean( + "isBrandedWallpaperNotificationDismissed", + prefs->GetBoolean(kBrandedWallpaperNotificationDismissed)); return pref_data; } @@ -78,6 +114,20 @@ BraveNewTabMessageHandler* BraveNewTabMessageHandler::Create( // Initial Values // Should only contain data that is static // + auto* ads_service_ = brave_ads::AdsServiceFactory::GetForProfile(profile); + // For safety, default |is_ads_supported_locale_| to true. Better to have + // false positive than falsen egative, + // in which case we would not show "opt out" toggle. + bool is_ads_supported_locale_ = true; + if (!ads_service_) { + LOG(ERROR) << "Ads service is not initialized!"; + } else { + is_ads_supported_locale_ = ads_service_->IsSupportedLocale(); + } + source->AddBoolean( + "featureFlagBraveNTPBrandedWallpaper", + base::FeatureList::IsEnabled(kBraveNTPBrandedWallpaper) && + is_ads_supported_locale_); // Private Tab info if (IsPrivateNewTab(profile)) { source->AddBoolean( @@ -126,6 +176,16 @@ void BraveNewTabMessageHandler::RegisterMessages() { base::BindRepeating( &BraveNewTabMessageHandler::HandleSaveNewTabPagePref, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "registerNewTabPageView", + base::BindRepeating( + &BraveNewTabMessageHandler::HandleRegisterNewTabPageView, + base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "getBrandedWallpaperData", + base::BindRepeating( + &BraveNewTabMessageHandler::HandleGetBrandedWallpaperData, + base::Unretained(this))); } void BraveNewTabMessageHandler::OnJavascriptAllowed() { @@ -162,6 +222,9 @@ void BraveNewTabMessageHandler::OnJavascriptAllowed() { pref_change_registrar_.Add(kNewTabPageShowBackgroundImage, base::Bind(&BraveNewTabMessageHandler::OnPreferencesChanged, base::Unretained(this))); + pref_change_registrar_.Add(kNewTabPageShowBrandedBackgroundImage, + base::Bind(&BraveNewTabMessageHandler::OnPreferencesChanged, + base::Unretained(this))); pref_change_registrar_.Add(kNewTabPageShowClock, base::Bind(&BraveNewTabMessageHandler::OnPreferencesChanged, base::Unretained(this))); @@ -174,6 +237,9 @@ void BraveNewTabMessageHandler::OnJavascriptAllowed() { pref_change_registrar_.Add(kNewTabPageShowRewards, base::Bind(&BraveNewTabMessageHandler::OnPreferencesChanged, base::Unretained(this))); + pref_change_registrar_.Add(kBrandedWallpaperNotificationDismissed, + base::Bind(&BraveNewTabMessageHandler::OnPreferencesChanged, + base::Unretained(this))); } void BraveNewTabMessageHandler::OnJavascriptDisallowed() { @@ -229,6 +295,8 @@ void BraveNewTabMessageHandler::HandleSaveNewTabPagePref( std::string settingsKey; if (settingsKeyInput == "showBackgroundImage") { settingsKey = kNewTabPageShowBackgroundImage; + } else if (settingsKeyInput == "brandedWallpaperOptIn") { + settingsKey = kNewTabPageShowBrandedBackgroundImage; } else if (settingsKeyInput == "showClock") { settingsKey = kNewTabPageShowClock; } else if (settingsKeyInput == "showTopSites") { @@ -237,6 +305,8 @@ void BraveNewTabMessageHandler::HandleSaveNewTabPagePref( settingsKey = kNewTabPageShowStats; } else if (settingsKeyInput == "showRewards") { settingsKey = kNewTabPageShowRewards; + } else if (settingsKeyInput == "isBrandedWallpaperNotificationDismissed") { + settingsKey = kBrandedWallpaperNotificationDismissed; } else { LOG(ERROR) << "Invalid setting key"; return; @@ -244,6 +314,32 @@ void BraveNewTabMessageHandler::HandleSaveNewTabPagePref( prefs->SetBoolean(settingsKey, settingsValueBool); } +void BraveNewTabMessageHandler::HandleRegisterNewTabPageView( + const base::ListValue* args) { + AllowJavascript(); + + // Decrement original value only if there's actual branded content + if (auto* service = + ntp_sponsored_images::ViewCounterServiceFactory::GetForProfile(profile_)) + service->RegisterPageView(); +} + +void BraveNewTabMessageHandler::HandleGetBrandedWallpaperData( + const base::ListValue* args) { + AllowJavascript(); + + auto* service = + ntp_sponsored_images::ViewCounterServiceFactory::GetForProfile(profile_); + if (!service || !service->ShouldShowBrandedWallpaper()) { + ResolveJavascriptCallback(args->GetList()[0], base::Value()); + return; + } + auto data = GetBrandedWallpaperDictionary( + service->current_wallpaper(), + service->GetWallpaperImageIndexToDisplay()); + ResolveJavascriptCallback(args->GetList()[0], data); +} + void BraveNewTabMessageHandler::OnPrivatePropertiesChanged() { PrefService* prefs = profile_->GetPrefs(); auto data = GetPrivatePropertiesDictionary(prefs); diff --git a/browser/ui/webui/brave_new_tab_message_handler.h b/browser/ui/webui/brave_new_tab_message_handler.h index 32f0960e8251..1d4dc9d6cb11 100644 --- a/browser/ui/webui/brave_new_tab_message_handler.h +++ b/browser/ui/webui/brave_new_tab_message_handler.h @@ -35,6 +35,8 @@ class BraveNewTabMessageHandler : public content::WebUIMessageHandler { void HandleSaveNewTabPagePref(const base::ListValue* args); void HandleToggleAlternativeSearchEngineProvider( const base::ListValue* args); + void HandleRegisterNewTabPageView(const base::ListValue* args); + void HandleGetBrandedWallpaperData(const base::ListValue* args); void OnStatsChanged(); void OnPreferencesChanged(); diff --git a/browser/ui/webui/brave_webui_source.cc b/browser/ui/webui/brave_webui_source.cc index b29418fa1e59..d9141704783b 100644 --- a/browser/ui/webui/brave_webui_source.cc +++ b/browser/ui/webui/brave_webui_source.cc @@ -79,7 +79,14 @@ void CustomizeWebUIHTMLSource(const std::string &name, { "ntp-14.webp", IDR_BRAVE_NEW_TAB_BACKGROUND14 }, { "ntp-15.webp", IDR_BRAVE_NEW_TAB_BACKGROUND15 }, { "ntp-16.webp", IDR_BRAVE_NEW_TAB_BACKGROUND16 }, - + { "ntp-dummy-brandedwallpaper/wallpaper1.jpg", + IDR_BRAVE_NEW_TAB_DUMMY_BRANDED_WALLPAPER_BG_1 }, + { "ntp-dummy-brandedwallpaper/wallpaper2.jpg", + IDR_BRAVE_NEW_TAB_DUMMY_BRANDED_WALLPAPER_BG_2 }, + { "ntp-dummy-brandedwallpaper/wallpaper3.jpg", + IDR_BRAVE_NEW_TAB_DUMMY_BRANDED_WALLPAPER_BG_3 }, + { "ntp-dummy-brandedwallpaper/logo.png", + IDR_BRAVE_NEW_TAB_DUMMY_BRANDED_WALLPAPER_LOGO }, // private tab { "c168145d6bf1abf2c0322636366f7dbe.svg", IDR_BRAVE_PRIVATE_TAB_TOR_IMG }, // NOLINT { "dbdc336ccc651b8a7c925b3482d6e65a.svg", IDR_BRAVE_PRIVATE_TAB_IMG } @@ -157,7 +164,7 @@ void CustomizeWebUIHTMLSource(const std::string &name, { "showClock", IDS_BRAVE_NEW_TAB_SHOW_CLOCK }, { "showTopSites", IDS_BRAVE_NEW_TAB_SHOW_TOP_SITES }, { "showRewards", IDS_BRAVE_NEW_TAB_SHOW_REWARDS }, - + { "brandedWallpaperOptIn", IDS_BRAVE_NEW_TAB_BRANDED_WALLPAPER_OPT_IN }, // Private Tab - General { "learnMore", IDS_BRAVE_PRIVATE_NEW_TAB_LEARN_MORE }, { "done", IDS_BRAVE_PRIVATE_NEW_TAB_DONE }, @@ -194,29 +201,33 @@ void CustomizeWebUIHTMLSource(const std::string &name, { "boxTorButton", IDS_BRAVE_PRIVATE_NEW_TAB_BOX_TOR_BUTTON }, // Rewards widget - { "rewardsWidgetAnd", IDS_BRAVE_UI_AND }, { "rewardsWidgetBap", IDS_BRAVE_UI_BAP_REWARDS_TEXT }, { "rewardsWidgetBat", IDS_BRAVE_UI_BAT_REWARDS_TEXT }, { "rewardsWidgetBraveRewards", IDS_BRAVE_UI_BRAVE_REWARDS }, - { "rewardsWidgetPrivacyPolicy", IDS_BRAVE_UI_PRIVACY_POLICY }, - { "rewardsWidgetTermsOfService", IDS_BRAVE_UI_TERMS_OF_SERVICE }, { "rewardsWidgetTurnOnAds", IDS_BRAVE_UI_TURN_ON_ADS }, { "rewardsWidgetClaimMyRewards", IDS_REWARDS_WIDGET_CLAIM_MY_REWARDS }, { "rewardsWidgetWalletFailedButton", IDS_BRAVE_UI_WALLET_FAILED_BUTTON }, // NOLINT { "rewardsWidgetAboutRewards", IDS_REWARDS_WIDGET_ABOUT_REWARDS }, { "rewardsWidgetServiceText", IDS_REWARDS_WIDGET_SERVICE_TEXT }, { "rewardsWidgetEstimatedEarnings", IDS_REWARDS_WIDGET_ESTIMATED_EARNINGS }, // NOLINT + { "rewardsWidgetAdsOptInDescription", IDS_REWARDS_WIDGET_ADS_OPT_IN_DESCRIPTION }, // NOLINT { "rewardsWidgetMonthlyTips", IDS_REWARDS_WIDGET_MONTHLY_TIPS }, { "rewardsWidgetTurningOn", IDS_REWARDS_WIDGET_TURNING_ON }, { "rewardsWidgetTurnOnRewards", IDS_REWARDS_WIDGET_TURN_ON_REWARDS }, // NOLINT { "rewardsWidgetReEnableTitle", IDS_REWARDS_WIDGET_REENABLE_TITLE }, // NOLINT + { "rewardsWidgetTurnOnLearnMore", IDS_REWARDS_WIDGET_TURN_ON_LEARN_MORE }, // NOLINT { "rewardsWidgetEnableTitle", IDS_REWARDS_WIDGET_ENABLE_TITLE }, + { "rewardsWidgetEnableBrandedWallpaperTitle", IDS_REWARDS_WIDGET_ENABLE_BRANDED_WALLPAPER_TITLE }, // NOLINT { "rewardsWidgetReEnableSubTitle", IDS_REWARDS_WIDGET_REENABLE_SUBTITLE }, // NOLINT { "rewardsWidgetEnableSubTitle", IDS_REWARDS_WIDGET_ENABLE_SUBTITLE }, // NOLINT + { "rewardsWidgetEnableBrandedWallpaperSubTitle", IDS_REWARDS_WIDGET_ENABLE_BRANDED_WALLPAPER_SUBTITLE }, // NOLINT { "rewardsWidgetAdsNotSupported", IDS_BRAVE_REWARDS_LOCAL_ADS_NOT_SUPPORTED_REGION }, // NOLINT { "rewardsWidgetNotificationTitle", IDS_REWARDS_WIDGET_NOTIFICATION_TITLE }, // NOLINT { "rewardsWidgetNotificationTextAds", IDS_REWARDS_WIDGET_NOTIFICATION_TEXT_ADS }, // NOLINT - { "rewardsWidgetNotificationTextUGP", IDS_REWARDS_WIDGET_NOTIFICATION_TEXT_UGP } // NOLINT + { "rewardsWidgetNotificationTextUGP", IDS_REWARDS_WIDGET_NOTIFICATION_TEXT_UGP }, // NOLINT + { "rewardsWidgetBrandedNotificationTitle", IDS_REWARDS_WIDGET_BRANDED_NOTIFICATION_TITLE }, // NOLINT + { "rewardsWidgetBrandedNotificationDescription", IDS_REWARDS_WIDGET_BRANDED_NOTIFICATION_DESCRIPTION }, // NOLINT + { "rewardsWidgetBrandedNotificationHideAction", IDS_REWARDS_WIDGET_BRANDED_NOTIFICATION_HIDE_ACTION }, // NOLINT } }, { std::string("welcome"), { diff --git a/chromium_src/chrome/browser/about_flags.cc b/chromium_src/chrome/browser/about_flags.cc index e910bb11f61f..1341d18f9f33 100644 --- a/chromium_src/chrome/browser/about_flags.cc +++ b/chromium_src/chrome/browser/about_flags.cc @@ -3,6 +3,21 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "brave/components/ntp_sponsored_images/browser/features.h" + +using ntp_sponsored_images::features::kBraveNTPBrandedWallpaper; +using ntp_sponsored_images::features::kBraveNTPBrandedWallpaperDemo; + +#define BRAVE_FEATURE_ENTRIES \ + {"brave-ntp-branded-wallpaper", \ + flag_descriptions::kBraveNTPBrandedWallpaperName, \ + flag_descriptions::kBraveNTPBrandedWallpaperDescription, kOsAll, \ + FEATURE_VALUE_TYPE(kBraveNTPBrandedWallpaper)}, \ + {"brave-ntp-branded-wallpaper-demo", \ + flag_descriptions::kBraveNTPBrandedWallpaperDemoName, \ + flag_descriptions::kBraveNTPBrandedWallpaperDemoDescription, kOsAll, \ + FEATURE_VALUE_TYPE(kBraveNTPBrandedWallpaperDemo)}, + #define SetFeatureEntryEnabled SetFeatureEntryEnabled_ChromiumImpl #include "../../../../chrome/browser/about_flags.cc" // NOLINT #include "../../../../components/flags_ui/flags_state.cc" // NOLINT diff --git a/chromium_src/chrome/browser/flag_descriptions.cc b/chromium_src/chrome/browser/flag_descriptions.cc new file mode 100644 index 000000000000..79f904867d3c --- /dev/null +++ b/chromium_src/chrome/browser/flag_descriptions.cc @@ -0,0 +1,19 @@ +/* Copyright (c) 2019 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "../../../../chrome/browser/flag_descriptions.cc" + +namespace flag_descriptions { +const char kBraveNTPBrandedWallpaperName[] = + "New Tab Page Branded Wallpapers"; +const char kBraveNTPBrandedWallpaperDescription[] = + "Allow New Tab Page Branded Wallpapers and user preference."; +const char kBraveNTPBrandedWallpaperDemoName[] = + "New Tab Page Demo Branded Wallpaper"; +const char kBraveNTPBrandedWallpaperDemoDescription[] = + "Force dummy data for the Branded Wallpaper New Tab Page Experience. " + "View rate and user opt-in conditionals will still be followed to decide " + "when to display the Branded Wallpaper."; +} // namespace flag_descriptions diff --git a/chromium_src/chrome/browser/flag_descriptions.h b/chromium_src/chrome/browser/flag_descriptions.h new file mode 100644 index 000000000000..99bd22fcb7e5 --- /dev/null +++ b/chromium_src/chrome/browser/flag_descriptions.h @@ -0,0 +1,18 @@ +/* Copyright (c) 2019 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_CHROMIUM_SRC_CHROME_BROWSER_FLAG_DESCRIPTIONS_H_ +#define BRAVE_CHROMIUM_SRC_CHROME_BROWSER_FLAG_DESCRIPTIONS_H_ + +#include "../../../../chrome/browser/flag_descriptions.h" + +namespace flag_descriptions { +extern const char kBraveNTPBrandedWallpaperName[]; +extern const char kBraveNTPBrandedWallpaperDescription[]; +extern const char kBraveNTPBrandedWallpaperDemoName[]; +extern const char kBraveNTPBrandedWallpaperDemoDescription[]; +} + +#endif // BRAVE_CHROMIUM_SRC_CHROME_BROWSER_FLAG_DESCRIPTIONS_H_ diff --git a/common/pref_names.cc b/common/pref_names.cc index 84eb79a3edcc..036611653ee3 100644 --- a/common/pref_names.cc +++ b/common/pref_names.cc @@ -61,10 +61,14 @@ const char kHideBraveRewardsButton[] = "brave.hide_brave_rewards_button"; const char kIPFSCompanionEnabled[] = "brave.ipfs_companion_enabled"; const char kNewTabPageShowBackgroundImage[] = "brave.new_tab_page.show_background_image"; +const char kNewTabPageShowBrandedBackgroundImage[] = + "brave.new_tab_page.show_branded_background_image"; const char kNewTabPageShowClock[] = "brave.new_tab_page.show_clock"; const char kNewTabPageShowTopSites[] = "brave.new_tab_page.show_top_sites"; const char kNewTabPageShowStats[] = "brave.new_tab_page.show_stats"; const char kNewTabPageShowRewards[] = "brave.new_tab_page.show_rewards"; +const char kBrandedWallpaperNotificationDismissed[] = + "brave.branded_wallpaper_notification_dismissed"; const char kBraveEnabledMediaRouter[] = "brave.enable_media_router"; const char kBraveWalletAES256GCMSivNonce[] = "brave.wallet.aes_256_gcm_siv_nonce"; diff --git a/common/pref_names.h b/common/pref_names.h index a30029215685..262a5f52794a 100644 --- a/common/pref_names.h +++ b/common/pref_names.h @@ -52,10 +52,12 @@ extern const char kHangoutsEnabled[]; extern const char kHideBraveRewardsButton[]; extern const char kIPFSCompanionEnabled[]; extern const char kNewTabPageShowBackgroundImage[]; +extern const char kNewTabPageShowBrandedBackgroundImage[]; extern const char kNewTabPageShowClock[]; extern const char kNewTabPageShowTopSites[]; extern const char kNewTabPageShowStats[]; extern const char kNewTabPageShowRewards[]; +extern const char kBrandedWallpaperNotificationDismissed[]; extern const char kBraveEnabledMediaRouter[]; extern const char kBraveWalletAES256GCMSivNonce[]; extern const char kBraveWalletEncryptedSeed[]; diff --git a/components/brave_ads/browser/locale_helper.cc b/components/brave_ads/browser/locale_helper.cc index c7c1f41695be..4d61a321ac39 100644 --- a/components/brave_ads/browser/locale_helper.cc +++ b/components/brave_ads/browser/locale_helper.cc @@ -5,8 +5,16 @@ #include "brave/components/brave_ads/browser/locale_helper.h" +#include +#include +#include "base/strings/string_util.h" +#include "base/strings/string_split.h" + namespace brave_ads { +const char kDefaultCountryCode[] = "US"; +const char kDefaultLocale[] = "en-US"; + LocaleHelper* g_locale_helper_for_testing = nullptr; LocaleHelper::LocaleHelper() = default; @@ -22,6 +30,29 @@ const std::string LocaleHelper::GetLocale() const { return kDefaultLocale; } +const std::string LocaleHelper::GetCountryCode( + const std::string& locale) { + std::vector locale_components = base::SplitString(locale, ".", + base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + + if (locale_components.size() == 0) { + return kDefaultCountryCode; + } + + auto normalized_locale = locale_components.front(); + std::replace(normalized_locale.begin(), normalized_locale.end(), '-', '_'); + + std::vector components = base::SplitString( + normalized_locale, "_", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); + + if (components.size() != 2) { + return kDefaultCountryCode; + } + + auto country_code = components.at(1); + return country_code; +} + LocaleHelper* LocaleHelper::GetInstance() { if (g_locale_helper_for_testing) { return g_locale_helper_for_testing; diff --git a/components/brave_ads/browser/locale_helper.h b/components/brave_ads/browser/locale_helper.h index 5a122db96983..e256d9852ef2 100644 --- a/components/brave_ads/browser/locale_helper.h +++ b/components/brave_ads/browser/locale_helper.h @@ -14,7 +14,8 @@ namespace brave_ads { -const char kDefaultLocale[] = "en-US"; +extern const char kDefaultCountryCode[]; +extern const char kDefaultLocale[]; class LocaleHelper { public: @@ -26,6 +27,8 @@ class LocaleHelper { // Should return the language based upon the tagging conventions of RFC 4646 virtual const std::string GetLocale() const; + static const std::string GetCountryCode(const std::string& locale); + protected: LocaleHelper(); virtual ~LocaleHelper(); diff --git a/components/brave_ads/browser/locale_helper_android.cc b/components/brave_ads/browser/locale_helper_android.cc index 6ff32f461e16..c92d35a78760 100644 --- a/components/brave_ads/browser/locale_helper_android.cc +++ b/components/brave_ads/browser/locale_helper_android.cc @@ -3,12 +3,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include -#include #include "brave/components/brave_ads/browser/locale_helper_android.h" + #include "base/android/locale_utils.h" -#include "base/strings/string_util.h" -#include "base/strings/string_split.h" namespace brave_ads { @@ -24,27 +21,4 @@ LocaleHelper* LocaleHelper::GetInstanceImpl() { return LocaleHelperAndroid::GetInstanceImpl(); } -const std::string LocaleHelperAndroid::GetCountryCode( - const std::string& locale) { - std::vector locale_components = base::SplitString(locale, ".", - base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - - if (locale_components.size() == 0) { - return kDefaultCountryCode; - } - - auto normalized_locale = locale_components.front(); - std::replace(normalized_locale.begin(), normalized_locale.end(), '-', '_'); - - std::vector components = base::SplitString( - normalized_locale, "_", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); - - if (components.size() != 2) { - return kDefaultCountryCode; - } - - auto country_code = components.at(1); - return country_code; -} - } // namespace brave_ads diff --git a/components/brave_ads/browser/locale_helper_android.h b/components/brave_ads/browser/locale_helper_android.h index 79881e8c0766..51adba33cdaa 100644 --- a/components/brave_ads/browser/locale_helper_android.h +++ b/components/brave_ads/browser/locale_helper_android.h @@ -12,15 +12,10 @@ namespace brave_ads { - static char kDefaultCountryCode[] = "US"; - class LocaleHelperAndroid : public LocaleHelper { public: static LocaleHelperAndroid* GetInstanceImpl(); - static const std::string GetCountryCode( - const std::string& locale); - private: LocaleHelperAndroid() = default; ~LocaleHelperAndroid() override = default; diff --git a/components/brave_component_updater/browser/BUILD.gn b/components/brave_component_updater/browser/BUILD.gn index d7418994bc3d..d61c6c6dd6f0 100644 --- a/components/brave_component_updater/browser/BUILD.gn +++ b/components/brave_component_updater/browser/BUILD.gn @@ -4,6 +4,8 @@ source_set("browser") { sources = [ "brave_component.cc", "brave_component.h", + "brave_on_demand_updater.cc", + "brave_on_demand_updater.h", "dat_file_util.cc", "dat_file_util.h", "local_data_files_service.cc", diff --git a/components/brave_component_updater/browser/brave_on_demand_updater.cc b/components/brave_component_updater/browser/brave_on_demand_updater.cc new file mode 100644 index 000000000000..d87eef03d43d --- /dev/null +++ b/components/brave_component_updater/browser/brave_on_demand_updater.cc @@ -0,0 +1,32 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h" + +#include + +#include "base/memory/singleton.h" + +namespace brave_component_updater { + +BraveOnDemandUpdater* BraveOnDemandUpdater::GetInstance() { + return base::Singleton::get(); +} + +BraveOnDemandUpdater::BraveOnDemandUpdater() {} + +BraveOnDemandUpdater::~BraveOnDemandUpdater() {} + +void BraveOnDemandUpdater::OnDemandUpdate(const std::string& id) { + DCHECK(!on_demand_update_callback_.is_null()); + on_demand_update_callback_.Run(id); +} + +void BraveOnDemandUpdater::RegisterOnDemandUpdateCallback(Callback callback) { + on_demand_update_callback_ = callback; +} + + +} // namespace brave_component_updater diff --git a/components/brave_component_updater/browser/brave_on_demand_updater.h b/components/brave_component_updater/browser/brave_on_demand_updater.h new file mode 100644 index 000000000000..17db0dcb3d1e --- /dev/null +++ b/components/brave_component_updater/browser/brave_on_demand_updater.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_BRAVE_COMPONENT_UPDATER_BROWSER_BRAVE_ON_DEMAND_UPDATER_H_ +#define BRAVE_COMPONENTS_BRAVE_COMPONENT_UPDATER_BROWSER_BRAVE_ON_DEMAND_UPDATER_H_ + +#include + +#include "base/callback.h" +#include "base/macros.h" + +namespace base { +template +struct DefaultSingletonTraits; +} // namespace base + +namespace brave_component_updater { + +class BraveOnDemandUpdater { + public: + using Callback = base::RepeatingCallback; + static BraveOnDemandUpdater* GetInstance(); + + ~BraveOnDemandUpdater(); + void OnDemandUpdate(const std::string& id); + + void RegisterOnDemandUpdateCallback(Callback callback); + + private: + friend struct base::DefaultSingletonTraits; + BraveOnDemandUpdater(); + + Callback on_demand_update_callback_; + + DISALLOW_COPY_AND_ASSIGN(BraveOnDemandUpdater); +}; + +} // namespace brave_component_updater + +#endif // BRAVE_COMPONENTS_BRAVE_COMPONENT_UPDATER_BROWSER_BRAVE_ON_DEMAND_UPDATER_H_ diff --git a/components/brave_new_tab_ui/actions/new_tab_actions.ts b/components/brave_new_tab_ui/actions/new_tab_actions.ts index 593e7f62c020..a4f8f6b5e55f 100644 --- a/components/brave_new_tab_ui/actions/new_tab_actions.ts +++ b/components/brave_new_tab_ui/actions/new_tab_actions.ts @@ -77,19 +77,15 @@ export const setInitialData = (initialData: InitialData) => export const createWallet = () => action(types.CREATE_WALLET, {}) -export const onEnabledMain = (enabledMain: boolean) => action(types.ON_ENABLED_MAIN, { - enabledMain +export const onEnabledMain = (enabledMain: boolean, enabledAds?: boolean) => action(types.ON_ENABLED_MAIN, { + enabledMain, + enabledAds }) export const onAdsEnabled = (enabled: boolean) => action(types.ON_ADS_ENABLED, { enabled }) -export const onRewardsSettingSave = (key: string, value: any) => action(types.ON_REWARDS_SETTING_SAVE, { - key, - value -}) - export const onWalletInitialized = (result: NewTab.RewardsResult) => action(types.ON_WALLET_INITIALIZED, { result }) diff --git a/components/brave_new_tab_ui/api/brandedWallpaper.ts b/components/brave_new_tab_ui/api/brandedWallpaper.ts new file mode 100644 index 000000000000..52f194e1d150 --- /dev/null +++ b/components/brave_new_tab_ui/api/brandedWallpaper.ts @@ -0,0 +1,12 @@ +// Copyright (c) 2020 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. + +export function getBrandedWallpaper (): Promise { + return window.cr.sendWithPromise('getBrandedWallpaperData') +} + +export function registerViewCount (): Promise { + return window.cr.sendWithPromise('registerNewTabPageView') +} diff --git a/components/brave_new_tab_ui/api/initialData.ts b/components/brave_new_tab_ui/api/initialData.ts index 630161eab46c..92256961362f 100644 --- a/components/brave_new_tab_ui/api/initialData.ts +++ b/components/brave_new_tab_ui/api/initialData.ts @@ -7,12 +7,14 @@ import * as preferencesAPI from './preferences' import * as statsAPI from './stats' import * as privateTabDataAPI from './privateTabData' import * as topSitesAPI from './topSites' +import * as brandedWallpaper from './brandedWallpaper' export type InitialData = { preferences: preferencesAPI.Preferences stats: statsAPI.Stats privateTabData: privateTabDataAPI.PrivateTabData - topSites: topSitesAPI.TopSitesData + topSites: topSitesAPI.TopSitesData, + brandedWallpaperData: undefined | NewTab.BrandedWallpaper } export type PreInitialRewardsData = { @@ -28,22 +30,32 @@ export type InitialRewardsData = { balance: NewTab.RewardsBalance } +const isIncognito: boolean = chrome.extension.inIncognitoContext + // Gets all data required for the first render of the page export async function getInitialData (): Promise { try { console.timeStamp('Getting initial data...') - const [preferences, stats, privateTabData, topSites] = await Promise.all([ + const [ + preferences, + stats, + privateTabData, + topSites, + brandedWallpaperData + ] = await Promise.all([ preferencesAPI.getPreferences(), statsAPI.getStats(), privateTabDataAPI.getPrivateTabData(), - topSitesAPI.getTopSites() + topSitesAPI.getTopSites(), + !isIncognito ? brandedWallpaper.getBrandedWallpaper() : Promise.resolve(undefined) ]) console.timeStamp('Got all initial data.') return { preferences, stats, privateTabData, - topSites + topSites, + brandedWallpaperData } } catch (e) { console.error(e) diff --git a/components/brave_new_tab_ui/api/preferences.ts b/components/brave_new_tab_ui/api/preferences.ts index e62b49a8cbc8..6400c6e220e1 100644 --- a/components/brave_new_tab_ui/api/preferences.ts +++ b/components/brave_new_tab_ui/api/preferences.ts @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Brave Authors. All rights reserved. +// Copyright (c) 2020 The Brave Authors. All rights reserved. // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, // you can obtain one at http://mozilla.org/MPL/2.0/. @@ -12,10 +12,12 @@ export type Preferences = { showBackgroundImage: boolean + brandedWallpaperOptIn: boolean showStats: boolean showClock: boolean showTopSites: boolean showRewards: boolean + isBrandedWallpaperNotificationDismissed: boolean } type PreferencesUpdatedHandler = (prefData: Preferences) => void @@ -48,6 +50,14 @@ export function saveShowRewards (value: boolean): void { sendSavePref('showRewards', value) } +export function saveBrandedWallpaperOptIn (value: boolean): void { + sendSavePref('brandedWallpaperOptIn', value) +} + +export function saveIsBrandedWallpaperNotificationDismissed (value: boolean): void { + sendSavePref('isBrandedWallpaperNotificationDismissed', value) +} + export function addChangeListener (listener: PreferencesUpdatedHandler): void { window.cr.addWebUIListener('preferences-changed', listener) } diff --git a/components/brave_new_tab_ui/apiEventsToStore.ts b/components/brave_new_tab_ui/apiEventsToStore.ts index 987bf8af397a..f94d6e890294 100644 --- a/components/brave_new_tab_ui/apiEventsToStore.ts +++ b/components/brave_new_tab_ui/apiEventsToStore.ts @@ -90,7 +90,9 @@ chrome.braveRewards.onWalletInitialized.addListener((result: any | NewTab.Reward }) chrome.braveRewards.onEnabledMain.addListener((enabledMain: boolean) => { - getActions().onEnabledMain(enabledMain) + chrome.braveRewards.getAdsEnabled((enabledAds: boolean) => { + getActions().onEnabledMain(enabledMain, enabledAds) + }) }) chrome.braveRewards.onAdsEnabled.addListener((enabled: boolean) => { diff --git a/components/brave_new_tab_ui/components/default/brandedWallpaper/logo-style.ts b/components/brave_new_tab_ui/components/default/brandedWallpaper/logo-style.ts new file mode 100644 index 000000000000..2fe0ae28d9c5 --- /dev/null +++ b/components/brave_new_tab_ui/components/default/brandedWallpaper/logo-style.ts @@ -0,0 +1,27 @@ +import styled from 'styled-components' + +export const Indicator = styled('div')` + position: absolute; + top: 7px; + right: 7px; + visibility: hidden; + width: 20px; + color: white; +` + +export const Anchor = styled('a')` + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + display: block; + cursor: pointer; + &:hover ${Indicator} { + visibility: visible; + } +` + +export const Image = styled('img')` + width: 170px; +` diff --git a/components/brave_new_tab_ui/components/default/brandedWallpaper/logo.tsx b/components/brave_new_tab_ui/components/default/brandedWallpaper/logo.tsx new file mode 100644 index 000000000000..8ebd560b95b3 --- /dev/null +++ b/components/brave_new_tab_ui/components/default/brandedWallpaper/logo.tsx @@ -0,0 +1,27 @@ +// Copyright (c) 2020 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. + +import * as React from 'react' +import { OpenNewIcon } from 'brave-ui/components/icons' + +import createWidget from '../widget/index' +import * as Styled from './logo-style' + +interface Props { + data: NewTab.BrandedWallpaperLogo +} + +function Logo ({ data }: Props) { + return ( + <> + + + + + + ) +} + +export default createWidget(Logo) diff --git a/components/brave_new_tab_ui/components/default/clock/style.ts b/components/brave_new_tab_ui/components/default/clock/style.ts index d415a13d07ab..3436fc9e01d3 100644 --- a/components/brave_new_tab_ui/components/default/clock/style.ts +++ b/components/brave_new_tab_ui/components/default/clock/style.ts @@ -16,8 +16,8 @@ export const StyledClock = styled<{}, 'div'>('div')` export const StyledTime = styled<{}, 'span'>('span')` box-sizing: border-box; - font-size: 90px; - font-weight: 200; + font-size: 78px; + font-weight: 300; color: inherit; display: inline-flex; ` diff --git a/components/brave_new_tab_ui/components/default/grid/index.ts b/components/brave_new_tab_ui/components/default/grid/index.ts deleted file mode 100644 index 16ec3338d45e..000000000000 --- a/components/brave_new_tab_ui/components/default/grid/index.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -import styled from 'brave-ui/theme' - -export const Header = styled<{}, 'header'>('header')` - box-sizing: border-box; - display: grid; - margin: 46px 0 0; - grid-template-columns: fit-content(100%) auto fit-content(100%); - grid-template-rows: fit-content(100%) fit-content(100%) fit-content(100%) fit-content(100%); - grid-template-areas: - "stats . clock" - ". . clock" - "topsites . rewards" - ". . ." - "notification notification notification"; - - > *:nth-child(1) { - grid-area: stats; - margin: 0 46px 0 46px; - } - - > *:nth-child(2) { - grid-area: clock; - margin: 0 46px 0 46px; - } - - > *:nth-child(3) { - grid-area: rewards; - } - - > *:nth-child(4) { - grid-area: topsites; - margin: 0 46px 0 46px; - justify-self: start; - align-items: normal; - } - - > *:nth-child(5) { - grid-area: notification; - justify-self: center; - } - - @media screen and (max-width: 1150px) { - grid-row-gap: 4px; - grid-template-areas: - "clock clock clock" - "stats stats stats" - "topsites topsites topsites" - "rewards rewards rewards" - "notification notification notification"; - - - > *:nth-child(1) { - margin: auto; - text-align: center; - } - - > *:nth-child(2) { - margin: auto; - text-align: center; - } - - > *:nth-child(3) { - margin: auto; - justify-content: center; - } - - > *:nth-child(4) { - margin: auto; - justify-content: center; - } - } -` -export const Main = styled<{}, 'main'>('main')` - box-sizing: border-box; -` - -export const Footer = styled<{}, 'footer'>('footer')` - box-sizing: border-box; - display: grid; - height: 100%; - margin: 70px; - align-items: center; - grid-template-columns: 1fr auto; - grid-template-rows: auto; - grid-gap: 0; - grid-template-areas: "credits actions"; - - > *:first-child { - grid-area: credits; - } - - > *:nth-child(2) { - grid-area: actions; - } - - @media screen and (max-width: 1150px) { - margin: 70px 40px; - - grid-template-areas: - "credits actions"; - - > *:nth-child(2) { - justify-content: flex-end; - } - } - - @media screen and (max-width: 390px) { - grid-template-areas: - "credits" - "actions"; - - > *:first-child { - text-align: center; - } - - > *:nth-child(2) { - justify-content: center; - } - } -` diff --git a/components/brave_new_tab_ui/components/default/index.ts b/components/brave_new_tab_ui/components/default/index.ts index 82223b352736..6e5bacd72d3f 100644 --- a/components/brave_new_tab_ui/components/default/index.ts +++ b/components/brave_new_tab_ui/components/default/index.ts @@ -4,7 +4,6 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ import { StatsContainer, StatsItem } from './stats' -import { Header, Main, Footer } from './grid' import { SettingsMenu, SettingsRow, SettingsText, SettingsTitle, SettingsWrapper } from './settings' import { ListWidget, Tile, TileActionsContainer, TileAction, TileFavicon } from './topSites' import { SiteRemovalNotification, SiteRemovalText, SiteRemovalAction } from './notification' @@ -17,9 +16,6 @@ export * from './page' export { StatsContainer, StatsItem, - Header, - Main, - Footer, ListWidget, Tile, TileActionsContainer, diff --git a/components/brave_new_tab_ui/components/default/notification/index.ts b/components/brave_new_tab_ui/components/default/notification/index.ts index a69324ced313..c3ee7cca8d77 100644 --- a/components/brave_new_tab_ui/components/default/notification/index.ts +++ b/components/brave_new_tab_ui/components/default/notification/index.ts @@ -9,11 +9,11 @@ export const SiteRemovalNotification = styled<{}, 'header'>('header')` border-radius: 8px; box-shadow: 2px 2px 6px rgba(0,0,0,0.3); background-color: #fff; - width: 500px; + width: 100%; + max-width: 500px; height: 100px; align-items: center; padding: 30px 60px; - margin: 80px 0 0 0; display: flex; justify-content: space-between; ` diff --git a/components/brave_new_tab_ui/components/default/page/index.ts b/components/brave_new_tab_ui/components/default/page/index.ts index a91731ef2461..98e6c26e72c5 100644 --- a/components/brave_new_tab_ui/components/default/page/index.ts +++ b/components/brave_new_tab_ui/components/default/page/index.ts @@ -2,21 +2,143 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -import styled from 'brave-ui/theme' +import styled, { css } from 'brave-ui/theme' + +const breakpointLargeBlocks = '980px' +const breakpointEveryBlock = '870px' + +const singleColumnSmallViewport = css` + @media screen and (max-width: ${breakpointEveryBlock}) { + text-align: center; + } +` +interface PageProps { + showClock: boolean + showStats: boolean + showRewards: boolean + showTopSites: boolean + showBrandedWallpaper: boolean +} + +function getItemRowCount (p: PageProps): number { + let right = (p.showClock ? 1 : 0) + (p.showRewards ? 2 : 0) + let left = (p.showStats ? 1 : 0) + (p.showTopSites ? 1 : 0) + // Has space for branded logo to sit next to something on right? + if (p.showBrandedWallpaper && left >= right) { + left++ + } + return Math.max(left, right) + 1 // extra 1 for footer +} + +export const Page = styled('div')` + /* Increase the explicit row count when adding new widgets + so that the footer goes in the correct location always, + yet can still merge upwards to previous rows. */ + --ntp-item-row-count: ${getItemRowCount}; + --ntp-extra-footer-rows: ${p => p.showBrandedWallpaper ? 1 : 0}; + --ntp-space-rows: 0; + --ntp-page-rows: calc(var(--ntp-item-row-count) + var(--ntp-space-rows)); + --ntp-item-justify: start; + @media screen and (max-width: ${breakpointLargeBlocks}) { + --ntp-space-rows: 1; + } + @media screen and (max-width: ${breakpointEveryBlock}) { + --ntp-item-justify: center; + } -export const Page = styled<{}, 'div'>('div')` -webkit-font-smoothing: antialiased; box-sizing: border-box; position: relative; z-index: 3; top: 0; left: 0; - display: flex; + display: grid; + grid-template-rows: repeat(calc(var(--ntp-page-rows) - 1), min-content) auto; + grid-template-columns: min-content auto min-content; + grid-auto-flow: row dense; + padding: 12px; + overflow: hidden; flex: 1; flex-direction: column; justify-content: space-between; height: 100%; min-height: 100vh; + align-items: flex-start; + + @media screen and (max-width: ${breakpointEveryBlock}) { + display: flex; + flex-direction: column; + align-items: center; + } +` + +export const GridItemStats = styled('section')` + grid-column: 1 / span 2; + ${singleColumnSmallViewport} +` + +export const GridItemClock = styled('section')` + grid-column: 3; + justify-self: center; + ${singleColumnSmallViewport} +` + +export const GridItemRewards = styled('section')` + grid-column: 3 / span 1; + grid-row-end: span 2; + @media screen and (max-width: ${breakpointLargeBlocks}) { + grid-column: 2 / span 2; + justify-self: end; + } +` + +export const GridItemTopSites = styled('section')` + grid-column: 1 / span 2; + ${singleColumnSmallViewport} +` + +export const GridItemNotification = styled('section')` + position: fixed; + left: 50%; + top: 0; + transform: translateX(-50%); +` + +export const GridItemCredits = styled('section')` + grid-column: 1 / span 1; + grid-row: calc(-2 - var(--ntp-extra-footer-rows)) / span calc(1 + var(--ntp-extra-footer-rows)); + /* grid-row: calc(-1 - var(--ntp-left-item-count)); + grid-row-end: span calc(1 + var(--ntp-left-item-count)); */ + align-self: end; + margin: 0 0 36px 36px; + @media screen and (max-width: ${breakpointEveryBlock}) { + align-self: center; + margin: 0; + } +` + +export const GridItemNavigation = styled('section')` + grid-column: 3 / span 1; + grid-row: -2 / span 1; + align-self: end; + margin: 0 24px 24px 0; + @media screen and (max-width: ${breakpointEveryBlock}) { + margin: 0; + align-self: unset; + } +` + +export const Footer = styled<{}, 'footer'>('footer')` + display: contents; + + @media screen and (max-width: ${breakpointEveryBlock}) { + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: flex-end; + flex-wrap: wrap; + } ` interface ImageLoadProps { @@ -96,15 +218,19 @@ export const Link = styled<{}, 'a'>('a')` ` export const PhotoName = styled<{}, 'div'>('div')` + align-self: flex-end; -webkit-font-smoothing: antialiased; box-sizing: border-box; font-size: 12px; font-family: Muli, sans-serif; color: rgba(255, 255, 255, 0.6); + white-space: nowrap; ` export const Navigation = styled<{}, 'nav'>('nav')` + align-self: flex-end; display: flex; + justify-content: flex-end; ` interface IconButtonProps { diff --git a/components/brave_new_tab_ui/components/default/rewards/brandedWallpaperNotification.tsx b/components/brave_new_tab_ui/components/default/rewards/brandedWallpaperNotification.tsx new file mode 100644 index 000000000000..ac712cfe66d6 --- /dev/null +++ b/components/brave_new_tab_ui/components/default/rewards/brandedWallpaperNotification.tsx @@ -0,0 +1,97 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this file, +* You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import * as React from 'react' + +import { + Title, + SubTitle, + SubTitleLink, + CloseIcon, + NotificationWrapper, + OrphanedNotificationWrapper, + NotificationAction, + NotificationButton +} from './style' +import { CloseStrokeIcon } from 'brave-ui/components/icons' +import { getLocale, splitStringForTag } from '../../../../common/locale' + +interface NotificationProps { + onDismissNotification: () => void + brandedWallpaperData?: NewTab.BrandedWallpaper + isOrphan?: boolean + onEnableAds?: () => void + onHideSponsoredImages: () => void + order: number +} + +export default class BrandedWallpaperRewardsNotification extends React.PureComponent { + + renderPostAdsOptInContent () { + const text = getLocale('rewardsWidgetBrandedNotificationDescription') + const { beforeTag, duringTag, afterTag } = splitStringForTag(text, '$1', '$2') + return ( + <> + + {getLocale('rewardsWidgetBrandedNotificationTitle')} + + + {beforeTag} + + {duringTag} + + {afterTag} + + + {getLocale('rewardsWidgetBrandedNotificationHideAction')} + + + ) + } + + renderPreAdsOptInContent () { + const text = getLocale('rewardsWidgetEnableBrandedWallpaperSubTitle') + const { beforeTag, duringTag, afterTag } = splitStringForTag(text, '$1', '$2') + return ( + <> + + {getLocale('rewardsWidgetEnableBrandedWallpaperTitle')} + + + {beforeTag} + + {duringTag} + + {afterTag} + + + {getLocale('rewardsWidgetTurnOnAds')} + + + ) + } + + render () { + const { brandedWallpaperData, isOrphan } = this.props + if (!brandedWallpaperData) { + console.error('Asked to render a branded wallpaper but there was no data!') + return null + } + const styleVars = { '--notification-counter': this.props.order } as React.CSSProperties + const Wrapper = isOrphan ? OrphanedNotificationWrapper : NotificationWrapper + return ( + + + + + { this.props.onEnableAds + ? this.renderPreAdsOptInContent() + : this.renderPostAdsOptInContent() + } + + ) + } +} diff --git a/components/brave_new_tab_ui/components/default/rewards/confetti.png b/components/brave_new_tab_ui/components/default/rewards/confetti.png new file mode 100644 index 000000000000..4e6e32c2724b Binary files /dev/null and b/components/brave_new_tab_ui/components/default/rewards/confetti.png differ diff --git a/components/brave_new_tab_ui/components/default/rewards/index.tsx b/components/brave_new_tab_ui/components/default/rewards/index.tsx index 666c4dc0ef84..c2a35166ea0c 100644 --- a/components/brave_new_tab_ui/components/default/rewards/index.tsx +++ b/components/brave_new_tab_ui/components/default/rewards/index.tsx @@ -5,11 +5,14 @@ import * as React from 'react' import createWidget from '../widget/index' import { convertBalance } from '../../../../brave_rewards/resources/page/utils' -import { getLocale } from '../../../../common/locale' +import { getLocale, splitStringForTag } from '../../../../common/locale' import { WidgetWrapper, + WidgetLayer, + NotificationsList, BatIcon, + CloseIcon, RewardsTitle, Footer, ServiceText, @@ -17,20 +20,22 @@ import { LearnMoreText, PreOptInInfo, Title, + LearnMoreTextButton, SubTitle, + SubTitleLink, PreOptInAction, TurnOnButton, AmountItem, - AmountInformation, AmountDescription, Amount, ConvertedAmount, - LearnMoreLink, + AmountUSD, TurnOnAdsButton, UnsupportedMessage } from './style' import Notification from './notification' -import { BatColorIcon } from 'brave-ui/components/icons' +import BrandedWallpaperNotification from './brandedWallpaperNotification' +import { BatColorIcon, CloseStrokeIcon } from 'brave-ui/components/icons' export interface RewardsProps { enabledAds: boolean @@ -45,10 +50,16 @@ export interface RewardsProps { adsEstimatedEarnings: number onlyAnonWallet?: boolean adsSupported?: boolean + isShowingBrandedWallpaper: boolean + isNotification?: boolean + showBrandedWallpaperNotification: boolean + brandedWallpaperData?: NewTab.BrandedWallpaper onCreateWallet: () => void onEnableAds: () => void onEnableRewards: () => void onDismissNotification: (id: string) => void + onDismissBrandedWallpaperNotification: () => void + onDisableBrandedWallpaper: () => void } const enum AmountItemType { @@ -91,7 +102,8 @@ class Rewards extends React.PureComponent { renderPreOptIn = () => { const { enabledMain, - walletCreated + walletCreated, + isShowingBrandedWallpaper } = this.props if (enabledMain && walletCreated) { @@ -99,23 +111,37 @@ class Rewards extends React.PureComponent { } const hasEnabled = !enabledMain && walletCreated + let titleText: string | JSX.Element + let subTitleText: string | JSX.Element + if (isShowingBrandedWallpaper) { + titleText = getLocale('rewardsWidgetEnableBrandedWallpaperTitle') + const text = getLocale('rewardsWidgetEnableBrandedWallpaperSubTitle') + const { beforeTag, duringTag, afterTag } = splitStringForTag(text, '$1', '$2') + subTitleText = ( + <> + {beforeTag} + + {duringTag} + + {afterTag} + + ) + } else if (hasEnabled) { + titleText = getLocale('rewardsWidgetReEnableTitle') + subTitleText = getLocale('rewardsWidgetReEnableSubTitle') + } else { + titleText = getLocale('rewardsWidgetEnableTitle') + subTitleText = getLocale('rewardsWidgetEnableSubTitle') + } return ( <> - { - hasEnabled - ? getLocale('rewardsWidgetReEnableTitle') - : getLocale('rewardsWidgetEnableTitle') - } + {titleText} - { - hasEnabled - ? getLocale('rewardsWidgetReEnableSubTitle') - : getLocale('rewardsWidgetEnableSubTitle') - } + {subTitleText} @@ -125,6 +151,9 @@ class Rewards extends React.PureComponent { > {this.getButtonText()} + + {getLocale('rewardsWidgetTurnOnLearnMore')} + ) @@ -150,14 +179,7 @@ class Rewards extends React.PureComponent { const batFormatString = onlyAnonWallet ? getLocale('rewardsWidgetBap') : getLocale('rewardsWidgetBat') return ( - - - { - type === AmountItemType.ADS - ? getLocale('rewardsWidgetEstimatedEarnings') - : getLocale('rewardsWidgetMonthlyTips') - } - + { showEnableAds ? @@ -167,12 +189,12 @@ class Rewards extends React.PureComponent { } { !showEnableAds && !(type === AmountItemType.ADS && !adsSupported) - ? + ?
{amount.toFixed(1)} - {`${batFormatString} ${converted} USD`} + {batFormatString}{converted} USD - +
: null } { @@ -182,6 +204,15 @@ class Rewards extends React.PureComponent { : null } + + { + type === AmountItemType.ADS + ? showEnableAds + ? getLocale('rewardsWidgetAdsOptInDescription') + : getLocale('rewardsWidgetEstimatedEarnings') + : getLocale('rewardsWidgetMonthlyTips') + } +
) } @@ -205,71 +236,125 @@ class Rewards extends React.PureComponent { } renderLearnMore = () => { + const text = getLocale('rewardsWidgetAboutRewards') + const { beforeTag, duringTag, afterTag } = splitStringForTag(text, '$1', '$2') return ( - - {getLocale('learnMore')} - - {getLocale('rewardsWidgetAboutRewards')} + {beforeTag} + + {duringTag} + + {afterTag} ) } renderPrivacyPolicy = () => { + const text = getLocale('rewardsWidgetServiceText') + const { beforeTag, duringTag, afterTag } = splitStringForTag(text, '$1', '$2') return ( <> - {getLocale('rewardsWidgetServiceText')} {getLocale('rewardsWidgetTermsOfService')} {getLocale('rewardsWidgetAnd')} {getLocale('rewardsWidgetPrivacyPolicy')}. + {beforeTag}{duringTag}{afterTag} ) } - renderNotifications = () => { - const { promotions, onDismissNotification } = this.props + renderNotifications = (singleOrphaned = false) => { + let { + promotions, + onDismissNotification, + enabledAds, + onEnableAds + } = this.props - if (!promotions) { - return null - } + // Uncomment for demo promotion notifications data: + // + // const showDummyPromotion = true + // if (showDummyPromotion) { + // promotions = [{ + // type: 1, + // promotionId: '1234' + // }] + // } + // TODO(petemill): If we want a true 'single' mode then + // only show a single notification from any source. + // For now, this only happens for the branded wallpaper notification. + promotions = singleOrphaned ? [] : (promotions || []) + const Wrapper = singleOrphaned ? React.Fragment : NotificationsList return ( - <> + {promotions.map((promotion: NewTab.Promotion, index) => { return ( ) })} - + { this.props.showBrandedWallpaperNotification && + + } + ) } + dismissNotification (notificationType: string) { + this.props.onDismissNotification(notificationType) + } + render () { const { enabledMain, - walletCreated + walletCreated, + isNotification } = this.props + // Handle isNotification: + // - if rewards isn't on, we ourselves are a notification + // - if rewards is on, only show a single notification (the last one) + // (intended for branded wallpaper notification). + if (isNotification) { + if (enabledMain) { + return this.renderNotifications(true) + } + } + return ( - - - - - - {getLocale('rewardsWidgetBraveRewards')} - - {this.renderPreOptIn()} - {this.renderRewardsInfo()} -
- { - enabledMain && walletCreated - ? this.renderLearnMore() - : this.renderPrivacyPolicy() + + + {isNotification && + + + } -
+ + + + + {getLocale('rewardsWidgetBraveRewards')} + + {this.renderPreOptIn()} + {this.renderRewardsInfo()} +
+ { + enabledMain && walletCreated + ? this.renderLearnMore() + : this.renderPrivacyPolicy() + } +
+ { enabledMain ? this.renderNotifications() diff --git a/components/brave_new_tab_ui/components/default/rewards/notification.tsx b/components/brave_new_tab_ui/components/default/rewards/notification.tsx index cc13711ec3d2..234317db4a01 100644 --- a/components/brave_new_tab_ui/components/default/rewards/notification.tsx +++ b/components/brave_new_tab_ui/components/default/rewards/notification.tsx @@ -8,7 +8,6 @@ import { Title, SubTitle, CloseIcon, - Content, NotificationWrapper, NotificationButton } from './style' @@ -18,6 +17,7 @@ import { getLocale } from '../../../../common/locale' interface NotificationProps { promotion: NewTab.Promotion onDismissNotification: (id: string) => void + order: number } export default class RewardsNotification extends React.PureComponent { @@ -32,13 +32,16 @@ export default class RewardsNotification extends React.PureComponent + - - + <Title isGrant={true}> {getLocale('rewardsWidgetNotificationTitle')} @@ -51,7 +54,6 @@ export default class RewardsNotification extends React.PureComponent {getLocale('rewardsWidgetClaimMyRewards')} - ) } diff --git a/components/brave_new_tab_ui/components/default/rewards/style.ts b/components/brave_new_tab_ui/components/default/rewards/style.ts deleted file mode 100644 index 0e99fc44562d..000000000000 --- a/components/brave_new_tab_ui/components/default/rewards/style.ts +++ /dev/null @@ -1,166 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -import styled from 'styled-components' -import palette from 'brave-ui/theme/colors' - -interface StyleProps { - isLast?: boolean -} - -export const WidgetWrapper = styled<{}, 'div'>('div')` - color: white; - padding: 10px 15px; - border-radius: 6px; - position: relative; - font-family: Muli, sans-serif; - overflow-x: hidden; - background-image: linear-gradient(140deg, #392DD1 0%, #8E2995 100%); -` - -export const Footer = styled<{}, 'div'>('div')` - max-width: 275px; - margin-top: 25px; -` - -export const BatIcon = styled<{}, 'div'>('div')` - width: 30px; - height: 30px; - display: inline-block; -` - -export const RewardsTitle = styled<{}, 'span'>('span')` - top: -9px; - left: 8px; - font-size: 14px; - font-weight: 500; - position: relative; - font-family: Poppins, sans-serif; -` - -export const ServiceText = styled<{}, 'span'>('span')` - color: #fff; - font-size: 10px; - letter-spacing: 0; - line-height: 18px; -` - -export const ServiceLink = styled<{}, 'a'>('a')` - color: #B0DBFF; - font-weight: 600; - text-decoration: none; -` - -export const LearnMoreLink = styled(ServiceLink)` - margin-right: 5px; -` - -export const LearnMoreText = styled<{}, 'div'>('div')` - font-size: 14px; - margin-top: 50px; -` - -export const PreOptInInfo = styled<{}, 'div'>('div')` - margin-top: 20px; -` - -export const Title = styled<{}, 'span'>('span')` - font-size: 19px; - display: block; - font-family: Poppins, sans-serif; -` - -export const SubTitle = styled<{}, 'span'>('span')` - font-size: 14px; - display: block; - margin-top: 10px; - max-width: 250px; -` - -export const PreOptInAction = styled<{}, 'div'>('div')` - margin-top: 30px; - text-align: center -` - -export const TurnOnButton = styled<{}, 'button'>('button')` - border-radius: 20px; - background: white; - color: ${palette.blurple500}; - font-weight: bold; - font-size: 14px; - padding: 7px 60px; - margin: 0 auto; - cursor: pointer; - border: none; -` - -export const TurnOnAdsButton = styled(TurnOnButton)` - width: 100%; - margin-top: 8px; - display: block; -` - -export const AmountItem = styled('div')` - margin-top: ${p => p.isLast ? 20 : 10}px; - margin-bottom: ${p => p.isLast ? -10 : 0}px; -` - -export const AmountInformation = styled<{}, 'div'>('div')` - margin-top: 5px; -` - -export const Amount = styled<{}, 'span'>('span')` - font-size: 32px; - margin-right: 10px; - font-family: Poppins, sans-serif; -` - -export const ConvertedAmount = styled<{}, 'span'>('span')` - font-size: 14px; -` - -export const AmountDescription = styled<{}, 'span'>('span')` - font-size: 14px; - color: #fff; - margin-bottom: 5px; -` - -export const NotificationWrapper = styled(WidgetWrapper)` - position: absolute; - bottom: 0; - left: 0; - width: 100%; - min-height: 210px; - box-shadow: 5px 10px 8px 10px #000; -` - -export const NotificationButton = styled(TurnOnButton)` - background: #1DB100; - color: #fff; - border: none; - font-weight: bold; - padding-top: 10px; - padding-bottom: 10px; - margin-top: 50px; - font-size: 13px; -` - -export const Content = styled<{}, 'div'>('div')` - margin-top: 30px; -` - -export const CloseIcon = styled<{}, 'div'>('div')` - color: #fff; - width: 20px; - height: 20px; - float: right; - cursor: pointer; -` - -export const UnsupportedMessage = styled<{}, 'div'>('div')` - color: rgba(255, 255, 255, 0.70); - font-size: 14px; - max-width: 235px; - margin-top: 8px; -` diff --git a/components/brave_new_tab_ui/components/default/rewards/style.tsx b/components/brave_new_tab_ui/components/default/rewards/style.tsx new file mode 100644 index 000000000000..a3eb192d7cbe --- /dev/null +++ b/components/brave_new_tab_ui/components/default/rewards/style.tsx @@ -0,0 +1,284 @@ +// Copyright (c) 2020 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. + +import * as React from 'react' +import styled, { css } from 'brave-ui/theme' +import palette from 'brave-ui/theme/colors' +import confettiImageUrl from './confetti.png' + +interface StyleProps { + isLast?: boolean + isActionPrompt: boolean +} + +interface WidgetProps { + isEnabled: boolean +} + +const Base = styled('div')` + overflow-x: hidden; + color: white; + font-family: Muli, sans-serif; + width: 284px; +` + +export const WidgetWrapper = styled(Base)` + position: relative; + /* Show a 1x1 grid with all items overlapping. + This makes sure that our layered notifications increase the height of the + whole widget. Absolute positioning would not accomplish that. */ + display: grid; + background: ${p => p.isEnabled ? palette.neutral900 : 'linear-gradient(140deg, #392DD1 0%, #8E2995 100%)'}; + border-radius: 6px; +` + +export const WidgetLayer = styled(Base)` + padding: 16px 22px 22px 22px; + grid-row: 1 / 2; + grid-column: 1 / 2; +` + +export const Footer = styled<{}, 'div'>('div')` + max-width: 275px; + margin-top: 25px; +` + +export const BatIcon = styled<{}, 'div'>('div')` + width: 27px; + height: 27px; + margin-right: 8px; +` + +export const RewardsTitle = styled<{}, 'div'>('div')` + margin-top: 6px; + display: flex; + justify-content: flex-start; + align-items: center; + font-size: 22px; + font-weight: 600; + font-family: Poppins, sans-serif; +` + +export const ServiceText = styled<{}, 'span'>('span')` + color: #fff; + font-size: 12px; + letter-spacing: 0; + line-height: 1.5; +` + +export const ServiceLink = styled<{}, 'a'>('a')` + color: ${p => p.theme.color.brandBrave}; + font-weight: 600; + text-decoration: none; +` + +export const LearnMoreText = styled<{}, 'div'>('div')` + font-size: 12px; + margin-top: 34px; +` + +export const PreOptInInfo = styled<{}, 'div'>('div')` + margin-top: 14px; +` + +export const Title = styled<{ isGrant?: boolean}, 'span'>('span')` + font-size: ${p => p.isGrant ? 16 : 14}px; + display: block; + font-family: ${p => p.theme.fontFamily.heading}; + line-height: 1.5; + font-weight: 500; +` + +export const LearnMoreTextButton = styled('a')` + display: block; + margin-top: 16px; + font-size: 14px; + line-height: 1.5; + font-weight: 600; + font-family: ${p => p.theme.fontFamily.body}; + color: white; + text-decoration: none; + :hover { + text-decoration: underline; + } +` + +export const SubTitle = styled<{}, 'span'>('span')` + font-size: 14px; + display: block; + margin-top: 15px; + max-width: 250px; + line-height: 1.4; +` + +export const SubTitleLink = styled<{}, 'a'>('a')` + color: ${p => p.theme.color.brandBrave}; + text-decoration: none; + cursor: pointer; + &:hover { + text-decoration: underline; + } +` + +export const PreOptInAction = styled<{}, 'div'>('div')` + margin-top: 30px; + text-align: center; +` + +export const TurnOnButton = styled<{}, 'button'>('button')` + --rewards-widget-button-extra-space: 2px; + margin: 0 auto; + border: solid 1px ${palette.grey400}; + border-radius: 20px; + background: transparent; + padding: calc(8px + var(--rewards-widget-button-extra-space)) 20px; + color: ${palette.neutral000}; + font-weight: 700; + font-size: 16px; + cursor: pointer; + word-break: break-word; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + :focus { + outline: none; + box-shadow: 0 0 0 1px ${p => p.theme.color.brandBrave}; + } +` + +const ActionButton = styled(TurnOnButton)` + --rewards-widget-button-extra-space: 0px; + margin: 0 auto; + border: none; + background: ${palette.blurple500}; + color: #fff; + font-weight: 700; + :hover { + background: ${palette.blurple600}; + } +` + +const StyledButtonIcon = styled('svg')` + margin-right: 6px; +` + +interface CoinsButtonProps { + onClick?: () => void + className?: string +} + +const CoinsButton: React.FunctionComponent = ({ onClick, className, children }) => { + return ( + + + + + {children} + + ) +} + +export const TurnOnAdsButton = styled(CoinsButton)` + margin-bottom: 8px; +` + +export const NotificationButton = styled(CoinsButton)` + margin-top: 25px; +` + +export const AmountItem = styled('div')` + margin-top: ${p => p.isLast ? 18 : 12}px; + margin-bottom: ${p => p.isLast ? -10 : 0}px; + ${p => p.isActionPrompt && css` + text-align: center; + border-bottom: solid 1px ${palette.grey800}; + padding-bottom: 16px; + `} +` + +export const Amount = styled<{}, 'span'>('span')` + font-size: 32px; + margin-right: 10px; + font-family: Poppins, sans-serif; +` + +export const ConvertedAmount = styled<{}, 'span'>('span')` + font-size: 14px; +` + +export const AmountDescription = styled<{}, 'div'>('div')` + font-size: 14px; + color: ${palette.grey300}; +` + +export const AmountUSD = styled('span')` + margin-left: 12px; +` + +export const NotificationsList = styled('div')` + grid-row: 1 / 2; + grid-column: 1 / 2; + display: grid; + margin-top: 10px; +` + +const BaseNotificationWrapper = styled(WidgetLayer)<{isGrant?: boolean}>` + background-color: ${palette.neutral800}; + ${p => p.isGrant && css` + background-image: url(${confettiImageUrl}); + background-position: bottom center; + background-size: contain; + background-repeat: no-repeat; + `} + border-radius: 6px; +` + +export const NotificationWrapper = styled(BaseNotificationWrapper)` + /* Stack the notifications offset from each other + and make sure they all cover the panel */ + --ntp-rewards-notification-offset: calc(var(--notification-counter, 0) * 48px); + margin-top: var(--ntp-rewards-notification-offset); + min-height: calc(100% - var(--ntp-rewards-notification-offset)); + width: 100%; + box-shadow: 0px 0px 16px 0px rgba(0, 0, 0, 0.5); +` + +export const OrphanedNotificationWrapper = styled(BaseNotificationWrapper)` +` + +export const NotificationAction = styled<{}, 'a'>('a')` + margin-top: 20px; + max-width: 250px; + display: block; + cursor: pointer; + font-size: 14px; + color: ${p => palette.blurple300}; + font-weight: 700; + text-decoration: none; + &:hover { + text-decoration: underline; + } +` + +export const CloseIcon = styled<{}, 'div'>('div')` + color: #fff; + width: 13px; + height: 13px; + float: right; + cursor: pointer; + margin-top: 2px; +` + +export const UnsupportedMessage = styled<{}, 'div'>('div')` + color: rgba(255, 255, 255, 0.70); + font-size: 14px; + max-width: 235px; + margin-top: 8px; +` diff --git a/components/brave_new_tab_ui/components/default/settings/index.ts b/components/brave_new_tab_ui/components/default/settings/index.ts index ae9255a665be..2f99a0b35e38 100644 --- a/components/brave_new_tab_ui/components/default/settings/index.ts +++ b/components/brave_new_tab_ui/components/default/settings/index.ts @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -import styled from 'styled-components' +import styled, { css } from 'styled-components' interface Props { textDirection: string @@ -29,13 +29,20 @@ export const SettingsTitle = styled<{}, 'div'>('div')` margin-bottom: 17px; ` -export const SettingsRow = styled<{}, 'div'>('div')` +interface SettingsRowProps { + isChildSetting?: boolean +} + +export const SettingsRow = styled('div')` box-sizing: border-box; display: grid; grid-template-columns: 1fr 36px; margin-top: 10px; height: 30px; width: 320px; + ${p => p.isChildSetting && css` + padding-left: 15px; + `} ` export const SettingsText = styled<{}, 'span'>('span')` diff --git a/components/brave_new_tab_ui/components/default/stats/style.ts b/components/brave_new_tab_ui/components/default/stats/style.ts index 5435d6e915df..744cce604f9e 100644 --- a/components/brave_new_tab_ui/components/default/stats/style.ts +++ b/components/brave_new_tab_ui/components/default/stats/style.ts @@ -6,30 +6,23 @@ import styled from 'brave-ui/theme' export const StyledStatsItemContainer = styled<{}, 'ul'>('ul')` -webkit-font-smoothing: antialiased; - display: grid; - grid-template-columns: repeat(3, fit-content(100%)); - grid-gap: 25px 50px; + display: inline-flex; + flex-wrap: wrap; + justify-content: var(--ntp-item-justify, start); + align-items: start; font-weight: 400; margin: 0; padding: 0; color: inherit; font-size: inherit; font-family: inherit; - - @media screen and (max-width: 700px) { - grid-template-columns: repeat(2, fit-content(100%)); - } - - - @media screen and (max-width: 390px) { - grid-template-columns: repeat(1, fit-content(100%)); - } ` export const StyledStatsItem = styled<{}, 'li'>('li')` list-style-type: none; font-size: inherit; font-family: inherit; + margin: 10px 16px; &:first-child { color: #FB542B; @@ -39,22 +32,24 @@ export const StyledStatsItem = styled<{}, 'li'>('li')` } &:last-child { color: #FFFFFF; + margin-right: 0; } ` export const StyledStatsItemCounter = styled<{}, 'span'>('span')` color: inherit; font-family: ${p => p.theme.fontFamily.heading}; - font-size: 46px; + font-size: 40px; font-weight: 400; width: 7ch; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; + word-wrap: none; ` export const StyledStatsItemText = styled<{}, 'span'>('span')` - font-size: 20px; + font-size: 24px; font-family: ${p => p.theme.fontFamily.heading}; margin-left: 4px; display: inline; @@ -62,8 +57,9 @@ export const StyledStatsItemText = styled<{}, 'span'>('span')` ` export const StyledStatsItemDescription = styled<{}, 'div'>('div')` - font-size: 14px; - font-weight: 400; + font-size: 16px; + font-weight: 500; color: #FFFFFF; + margin-top: 8px; font-family: ${p => p.theme.fontFamily.heading}; ` diff --git a/components/brave_new_tab_ui/components/default/topSites/index.ts b/components/brave_new_tab_ui/components/default/topSites/index.ts index d89ab27eb379..b0fc8f9660aa 100644 --- a/components/brave_new_tab_ui/components/default/topSites/index.ts +++ b/components/brave_new_tab_ui/components/default/topSites/index.ts @@ -10,13 +10,12 @@ interface ListProps { } export const List = styled('div')` + justify-self: start; + align-items: normal; height: 100%; display: grid; grid-template-columns: repeat(${p => Math.min(p.blockNumber, 6).toString()}, 92px); - - @media screen and (max-width: 1150px) { - justify-content: center; - } + justify-content: var(--ntp-item-justify, start); @media screen and (max-width: 700px) { grid-template-columns: repeat(${p => Math.min(p.blockNumber, 3).toString()}, 92px); @@ -102,6 +101,7 @@ export const TileFavicon = styled<{}, 'img'>('img')` display: block; height: 72px; padding: 16px; + object-fit: contain; ` export const ListWidget = createWidget(List) diff --git a/components/brave_new_tab_ui/components/default/widget/index.tsx b/components/brave_new_tab_ui/components/default/widget/index.tsx index fcdc8cbbb090..2a6b7a2ece78 100644 --- a/components/brave_new_tab_ui/components/default/widget/index.tsx +++ b/components/brave_new_tab_ui/components/default/widget/index.tsx @@ -6,10 +6,12 @@ import * as React from 'react' import { StyledWidget, StyledWidgetContainer } from './styles' import WidgetMenu from './widgetMenu' +type HideWidgetFunction = () => void + export interface WidgetProps { - showWidget: boolean menuPosition: 'right' | 'left' - hideWidget: () => void + hideWidget?: HideWidgetFunction + preventFocus?: boolean textDirection: string } @@ -35,25 +37,30 @@ const createWidget =

(WrappedComponent: React.ComponentType

} render () { - const { showWidget, menuPosition, hideWidget, textDirection } = this.props + const { menuPosition, hideWidget, textDirection, preventFocus } = this.props const { widgetMenuPersist } = this.state + return ( - - + + + {hideWidget && !preventFocus && + } ) } diff --git a/components/brave_new_tab_ui/components/default/widget/styles.ts b/components/brave_new_tab_ui/components/default/widget/styles.ts index f5d3eaa0d575..c6e3024e586f 100644 --- a/components/brave_new_tab_ui/components/default/widget/styles.ts +++ b/components/brave_new_tab_ui/components/default/widget/styles.ts @@ -2,39 +2,40 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -import styled from 'styled-components' +import styled, { css } from 'styled-components' -interface WidgetContainerProps { - showWidget: boolean +interface WidgetPositionProps { menuPosition: 'right' | 'left' +} +interface WidgetContainerProps extends WidgetPositionProps { textDirection: string } export const StyledWidgetContainer = styled('div')` - display: ${p => p.showWidget ? 'flex' : 'none'}; + display: inline-flex; + /* For debug: */ + /* outline: 1px solid rgba(0, 185, 0, .6); */ align-items: center; flex-direction: ${p => p.menuPosition === 'right' ? 'row' : 'row-reverse'}; height: fit-content; - - @media screen and (max-width: 1150px) { - flex-direction: row; - padding: ${p => p.textDirection === 'ltr' - ? `0 0 0 48px` - : `0 48px 0 0`} - } + min-width: 0; ` interface WidgetVisibilityProps { widgetMenuPersist: boolean + preventFocus?: boolean } export const StyledWidget = styled('div')` padding: 24px; - - ${StyledWidgetContainer}:hover & { - border-radius: 16px; - background: rgba(33, 37, 41, 0.48); - } + max-width: 100%; + position: relative; + ${p => !p.preventFocus && css` + ${StyledWidgetContainer}:hover & { + border-radius: 16px; + background: rgba(33, 37, 41, 0.48); + } + `} // Also hover when menu button has been clicked ${ p => p.widgetMenuPersist && ` @@ -44,7 +45,7 @@ export const StyledWidget = styled('div')` ` -export const StyledWidgetMenuContainer = styled('div')` +export const StyledWidgetMenuContainer = styled('div')` visibility: hidden; pointer-events: none; position: relative; @@ -60,6 +61,12 @@ export const StyledWidgetMenuContainer = styled('d pointer-events: auto; `} + // Float in gutter + ${p => p.menuPosition === 'left' ? css` + margin-left: -48px; + ` : css` + margin-right: -48px; + `} ` interface WidgetMenuProps { diff --git a/components/brave_new_tab_ui/components/default/widget/widgetMenu.tsx b/components/brave_new_tab_ui/components/default/widget/widgetMenu.tsx index c353bf3172f5..79f3c136ec23 100644 --- a/components/brave_new_tab_ui/components/default/widget/widgetMenu.tsx +++ b/components/brave_new_tab_ui/components/default/widget/widgetMenu.tsx @@ -68,6 +68,7 @@ export default class WidgetMenu extends React.PureComponent { const { showMenu } = this.state return ( diff --git a/components/brave_new_tab_ui/constants/new_tab_types.ts b/components/brave_new_tab_ui/constants/new_tab_types.ts index e60f0c72d241..42f5df992c76 100644 --- a/components/brave_new_tab_ui/constants/new_tab_types.ts +++ b/components/brave_new_tab_ui/constants/new_tab_types.ts @@ -26,7 +26,6 @@ export const enum types { ON_ADS_ENABLED = '@@newtab/ON_ADS_ENABLED', ON_ADS_ESTIMATED_EARNINGS = '@@newtab/ON_ADS_ESTIMATED_EARNINGS', ON_ENABLED_MAIN = '@@newtab/ON_ENABLED_MAIN', - ON_REWARDS_SETTING_SAVE = '@@newtab_panel/ON_SETTING_SAVE', ON_WALLET_INITIALIZED = '@@newtab/ON_WALLET_INITIALIZED', ON_BALANCE_REPORT = '@@newtab/ON_BALANCE_REPORT', ON_PROMOTIONS = '@@newtab/ON_PROMOTIONS', diff --git a/components/brave_new_tab_ui/containers/app.tsx b/components/brave_new_tab_ui/containers/app.tsx index dd00465968a6..261723cfb9ec 100644 --- a/components/brave_new_tab_ui/containers/app.tsx +++ b/components/brave_new_tab_ui/containers/app.tsx @@ -20,6 +20,9 @@ interface Props { newTabData: NewTab.State } +const onDismissBrandedWallpaperNotification = + PreferencesAPI.saveIsBrandedWallpaperNotificationDismissed.bind(null, true) + class DefaultPage extends React.Component { render () { const { newTabData, actions } = this.props @@ -40,6 +43,8 @@ class DefaultPage extends React.Component { saveShowStats={PreferencesAPI.saveShowStats} saveShowTopSites={PreferencesAPI.saveShowTopSites} saveShowRewards={PreferencesAPI.saveShowRewards} + onDismissBrandedWallpaperNotification={onDismissBrandedWallpaperNotification} + saveBrandedWallpaperOptIn={PreferencesAPI.saveBrandedWallpaperOptIn} /> ) } diff --git a/components/brave_new_tab_ui/containers/newTab/footerInfo.tsx b/components/brave_new_tab_ui/containers/newTab/footerInfo.tsx index 3d45d9ef6027..4c7d79c8d995 100644 --- a/components/brave_new_tab_ui/containers/newTab/footerInfo.tsx +++ b/components/brave_new_tab_ui/containers/newTab/footerInfo.tsx @@ -6,6 +6,7 @@ import * as React from 'react' // Feature-specific components import { Link, Navigation, IconLink, PhotoName } from '../../components/default' +import * as S from '../../components/default/page' // Icons import { SettingsAdvancedIcon, BookmarkBook, HistoryIcon } from 'brave-ui/components/icons' @@ -26,11 +27,14 @@ interface Props { toggleShowStats: () => void toggleShowTopSites: () => void toggleShowRewards: () => void + toggleBrandedWallpaperOptIn: () => void showBackgroundImage: boolean showClock: boolean showStats: boolean showTopSites: boolean showRewards: boolean + brandedWallpaperOptIn: boolean + allowBrandedWallpaperUI: boolean } export default class FooterInfo extends React.PureComponent { @@ -47,52 +51,61 @@ export default class FooterInfo extends React.PureComponent { toggleShowClock, toggleShowStats, toggleShowTopSites, + toggleBrandedWallpaperOptIn, showBackgroundImage, showClock, showStats, showTopSites, + brandedWallpaperOptIn, + allowBrandedWallpaperUI, toggleShowRewards, showRewards } = this.props return ( <> -

- {showPhotoInfo && backgroundImageInfo && - - {`${getLocale('photoBy')} `} - - {backgroundImageInfo.author} - - } -
- - - - - - - - - - - - + { showPhotoInfo && backgroundImageInfo && + + + {`${getLocale('photoBy')} `} + + {backgroundImageInfo.author} + + + + } + + + + + + + + + + + + + + ) } diff --git a/components/brave_new_tab_ui/containers/newTab/index.tsx b/components/brave_new_tab_ui/containers/newTab/index.tsx index d1bad01c2c6a..dd9018a9be89 100644 --- a/components/brave_new_tab_ui/containers/newTab/index.tsx +++ b/components/brave_new_tab_ui/containers/newTab/index.tsx @@ -5,23 +5,19 @@ import * as React from 'react' import { DragDropContext } from 'react-dnd' import HTML5Backend from 'react-dnd-html5-backend' -import { - Page, - Header, - ClockWidget as Clock, - ListWidget as List, - Footer, - App, - PosterBackground, - Gradient, - RewardsWidget as Rewards -} from '../../components/default' // Components import Stats from './stats' import Block from './block' import FooterInfo from './footerInfo' import SiteRemovalNotification from './notification' +import { + ClockWidget as Clock, + ListWidget as List, + RewardsWidget as Rewards +} from '../../components/default' +import * as Page from '../../components/default/page' +import BrandedWallpaperLogo from '../../components/default/brandedWallpaper/logo' interface Props { newTabData: NewTab.State @@ -31,6 +27,8 @@ interface Props { saveShowTopSites: (value: boolean) => void saveShowStats: (value: boolean) => void saveShowRewards: (value: boolean) => void + saveBrandedWallpaperOptIn: (value: boolean) => void + onDismissBrandedWallpaperNotification: () => void } interface State { @@ -39,6 +37,22 @@ interface State { backgroundHasLoaded: boolean } +function GetBackgroundImageSrc (props: Props) { + if (!props.newTabData.showBackgroundImage) { + return undefined + } + if (props.newTabData.brandedWallpaperData) { + const wallpaperData = props.newTabData.brandedWallpaperData + if (wallpaperData && wallpaperData.wallpaperImageUrl) { + return wallpaperData.wallpaperImageUrl + } + } + if (props.newTabData.backgroundImage && props.newTabData.backgroundImage.source) { + return props.newTabData.backgroundImage.source + } + return undefined +} + class NewTabPage extends React.Component { state = { onlyAnonWallet: false, @@ -46,30 +60,33 @@ class NewTabPage extends React.Component { backgroundHasLoaded: false } + imageSource?: string = undefined + componentDidMount () { // if a notification is open at component mounting time, close it this.props.actions.onHideSiteRemovalNotification() + this.imageSource = GetBackgroundImageSrc(this.props) this.trackCachedImage() } componentDidUpdate (prevProps: Props) { - if (!prevProps.newTabData.showBackgroundImage && - this.props.newTabData.showBackgroundImage) { + const oldImageSource = GetBackgroundImageSrc(prevProps) + const newImageSource = GetBackgroundImageSrc(this.props) + this.imageSource = newImageSource + if (newImageSource && oldImageSource !== newImageSource) { this.trackCachedImage() } - if (prevProps.newTabData.showBackgroundImage && - !this.props.newTabData.showBackgroundImage) { + if (oldImageSource && + !newImageSource) { // reset loaded state this.setState({ backgroundHasLoaded: false }) } } trackCachedImage () { - if (this.props.newTabData.showBackgroundImage && - this.props.newTabData.backgroundImage && - this.props.newTabData.backgroundImage.source) { + if (this.imageSource) { const imgCache = new Image() - imgCache.src = this.props.newTabData.backgroundImage.source + imgCache.src = this.imageSource console.timeStamp('image start loading...') imgCache.onload = () => { console.timeStamp('image loaded') @@ -138,12 +155,22 @@ class NewTabPage extends React.Component { ) } + disableBrandedWallpaper = () => { + this.props.saveBrandedWallpaperOptIn(false) + } + + toggleShowBrandedWallpaper = () => { + this.props.saveBrandedWallpaperOptIn( + !this.props.newTabData.brandedWallpaperOptIn + ) + } + enableAds = () => { chrome.braveRewards.saveAdsSetting('adsEnabled', 'true') } enableRewards = () => { - this.props.actions.onRewardsSettingSave('enabledMain', '1') + chrome.braveRewards.saveSetting('enabledMain', '1') } createWallet = () => { @@ -162,111 +189,163 @@ class NewTabPage extends React.Component { this.setState({ showSettingsMenu: !this.state.showSettingsMenu }) } + renderRewardsContent () { + const { newTabData } = this.props + const { + rewardsState, + showRewards: rewardsWidgetOn, + isBrandedWallpaperNotificationDismissed + } = newTabData + const isShowingBrandedWallpaper = newTabData.brandedWallpaperData ? true : false + const shouldShowBrandedWallpaperNotification = isShowingBrandedWallpaper && !isBrandedWallpaperNotificationDismissed + const shouldShowRewardsWidget = rewardsWidgetOn || shouldShowBrandedWallpaperNotification + return shouldShowRewardsWidget && ( + + + + ) + } + render () { const { newTabData, actions } = this.props const { showSettingsMenu } = this.state - const { rewardsState } = newTabData if (!newTabData) { return null } + const hasImage = this.imageSource !== undefined + const isShowingBrandedWallpaper = newTabData.brandedWallpaperData ? true : false + const showTopSites = !!this.props.newTabData.gridSites.length && newTabData.showTopSites + const rewardsContent = this.renderRewardsContent() + return ( - - + - {newTabData.showBackgroundImage && newTabData.backgroundImage && - + {hasImage && + } - - {newTabData.showBackgroundImage && - + {hasImage && + } - -
+ + {newTabData.showStats && + + + } + {newTabData.showClock && + - - {this.props.newTabData.gridSites.length ? - { - this.props.newTabData.gridSites.map((site: NewTab.Site) => - - ) - } - : null} + + } + {showTopSites && + { - this.props.newTabData.showSiteRemovalNotification - ? - : null + this.props.newTabData.gridSites.map((site: NewTab.Site) => + + ) } -
-
+ + } + { + this.props.newTabData.showSiteRemovalNotification + ? + + + : null + } + {rewardsContent} + + {isShowingBrandedWallpaper && newTabData.brandedWallpaperData && + newTabData.brandedWallpaperData.logo && + + + } -
-
-
+ + + ) } } diff --git a/components/brave_new_tab_ui/containers/newTab/settings.tsx b/components/brave_new_tab_ui/containers/newTab/settings.tsx index 9632cd100f25..20a8b271a7ae 100644 --- a/components/brave_new_tab_ui/containers/newTab/settings.tsx +++ b/components/brave_new_tab_ui/containers/newTab/settings.tsx @@ -21,10 +21,13 @@ export interface Props { toggleShowStats: () => void toggleShowTopSites: () => void toggleShowRewards: () => void + toggleBrandedWallpaperOptIn: () => void showBackgroundImage: boolean showStats: boolean showClock: boolean showTopSites: boolean + brandedWallpaperOptIn: boolean + allowBrandedWallpaperUI: boolean showRewards: boolean } @@ -55,20 +58,30 @@ export default class Settings extends React.PureComponent { } } + toggleShowBackgroundImage = () => { + this.props.toggleShowBackgroundImage() + // Re opt-in to branded wallpaper if re-enabling background image + if (!this.props.showBackgroundImage && !this.props.brandedWallpaperOptIn) { + this.props.toggleBrandedWallpaperOptIn() + } + } + render () { const { textDirection, showSettingsMenu, - toggleShowBackgroundImage, toggleShowClock, toggleShowStats, toggleShowTopSites, toggleShowRewards, + toggleBrandedWallpaperOptIn, showBackgroundImage, showStats, showClock, showTopSites, showRewards, + brandedWallpaperOptIn, + allowBrandedWallpaperUI, onClick } = this.props return ( @@ -80,11 +93,22 @@ export default class Settings extends React.PureComponent { {getLocale('showBackgroundImage')} + {allowBrandedWallpaperUI && + + {getLocale('brandedWallpaperOptIn')} + + + } {getLocale('showRewards')} void + +function performSideEffect (fn: SideEffectFunction): void { + window.setTimeout(() => fn(sideEffectState), 0) } export const newTabReducer: Reducer = (state: NewTab.State | undefined, action: any) => { @@ -43,8 +48,13 @@ export const newTabReducer: Reducer = (state: NewTab.S ...initialDataPayload.preferences, stats: initialDataPayload.stats, ...initialDataPayload.privateTabData, - topSites: initialDataPayload.topSites + topSites: initialDataPayload.topSites, + brandedWallpaperData: initialDataPayload.brandedWallpaperData } + // TODO(petemill): only get backgroundImage if no sponsored background this time. + // ...We would also have to set the value at the action + // the branded wallpaper is turned off. Since this is a cheap string API + // (no image will be downloaded), we can afford to leave this here for now. if (initialDataPayload.preferences.showBackgroundImage) { state.backgroundImage = backgroundAPI.randomBackgroundImage() } @@ -59,8 +69,15 @@ export const newTabReducer: Reducer = (state: NewTab.S // UI render time. // We at least schedule to run after the reducer has finished // and the resulting new state is available. - addToDispatchQueue(() => { + performSideEffect(async function (state) { gridAPI.calculateGridSites(state) + if (!state.isIncognito) { + try { + await registerViewCount() + } catch (e) { + console.error('Error calling registerViewCount', e) + } + } }) break case types.BOOKMARK_ADDED: @@ -102,7 +119,7 @@ export const newTabReducer: Reducer = (state: NewTab.S // UI render time. // We at least schedule to run after the reducer has finished // and the resulting new state is available. - addToDispatchQueue(() => { + performSideEffect((state) => { gridAPI.calculateGridSites(state) }) break @@ -126,7 +143,7 @@ export const newTabReducer: Reducer = (state: NewTab.S // UI render time. // We at least schedule to run after the reducer has finished // and the resulting new state is available. - addToDispatchQueue(() => { + performSideEffect((state) => { gridAPI.calculateGridSites(state) }) break @@ -148,7 +165,7 @@ export const newTabReducer: Reducer = (state: NewTab.S // UI render time. // We at least schedule to run after the reducer has finished // and the resulting new state is available. - addToDispatchQueue(() => { + performSideEffect((state) => { gridAPI.calculateGridSites(state) }) break @@ -170,7 +187,7 @@ export const newTabReducer: Reducer = (state: NewTab.S // UI render time. // We at least schedule to run after the reducer has finished // and the resulting new state is available. - addToDispatchQueue(() => { + performSideEffect((state) => { gridAPI.calculateGridSites(state) }) break @@ -190,7 +207,7 @@ export const newTabReducer: Reducer = (state: NewTab.S // UI render time. // We at least schedule to run after the reducer has finished // and the resulting new state is available. - addToDispatchQueue(() => { + performSideEffect((state) => { gridAPI.calculateGridSites(state) }) break @@ -236,15 +253,23 @@ export const newTabReducer: Reducer = (state: NewTab.S case types.NEW_TAB_PREFERENCES_UPDATED: const preferences = payload as Preferences - const shouldChangeBackgroundImage = - !state.showBackgroundImage && preferences.showBackgroundImage - state = { + const newState = { ...state, ...preferences } + // Remove branded wallpaper when opting out or turning wallpapers off + const hasTurnedBrandedWallpaperOff = !preferences.brandedWallpaperOptIn && state.brandedWallpaperData + const hasTurnedWallpaperOff = !preferences.showBackgroundImage && state.showBackgroundImage + if (hasTurnedBrandedWallpaperOff || (state.brandedWallpaperData && hasTurnedWallpaperOff)) { + newState.brandedWallpaperData = undefined + } + // Get a new wallpaper image if turning that feature on + const shouldChangeBackgroundImage = + !state.showBackgroundImage && preferences.showBackgroundImage if (shouldChangeBackgroundImage) { - state.backgroundImage = backgroundAPI.randomBackgroundImage() + newState.backgroundImage = backgroundAPI.randomBackgroundImage() } + state = newState break case types.CREATE_WALLET: @@ -256,6 +281,9 @@ export const newTabReducer: Reducer = (state: NewTab.S case types.ON_ENABLED_MAIN: state = { ...state } state.rewardsState.enabledMain = payload.enabledMain + if (payload.enabledAds !== undefined) { + state.rewardsState.enabledAds = payload.enabledAds + } break case types.ON_WALLET_INITIALIZED: { @@ -283,17 +311,6 @@ export const newTabReducer: Reducer = (state: NewTab.S break } - case types.ON_REWARDS_SETTING_SAVE: - const key = action.payload.key - const value = action.payload.value - - if (key) { - state = { ...state } - state.rewardsState[key] = !!value - chrome.braveRewards.saveSetting(key, value) - } - break - case types.ON_ADS_ENABLED: state = { ...state } state.rewardsState.enabledAds = payload.enabled @@ -312,7 +329,6 @@ export const newTabReducer: Reducer = (state: NewTab.S case types.DISMISS_NOTIFICATION: state = { ...state } - const dismissedNotifications = state.rewardsState.dismissedNotifications dismissedNotifications.push(payload.id) state.rewardsState.dismissedNotifications = dismissedNotifications @@ -438,6 +454,7 @@ export const newTabReducer: Reducer = (state: NewTab.S storage.debouncedSave(state) } + sideEffectState = state return state } diff --git a/components/brave_new_tab_ui/storage.ts b/components/brave_new_tab_ui/storage.ts index 0a793cd39f28..61390ed6ffa0 100644 --- a/components/brave_new_tab_ui/storage.ts +++ b/components/brave_new_tab_ui/storage.ts @@ -10,11 +10,14 @@ const keyName = 'new-tab-data' const defaultState: NewTab.State = { initialDataLoaded: false, textDirection: window.loadTimeData.getString('textdirection'), + featureFlagBraveNTPBrandedWallpaper: window.loadTimeData.getBoolean('featureFlagBraveNTPBrandedWallpaper'), showBackgroundImage: false, showStats: false, showClock: false, showTopSites: false, showRewards: false, + brandedWallpaperOptIn: false, + isBrandedWallpaperNotificationDismissed: true, topSites: [], ignoredTopSites: [], pinnedTopSites: [], diff --git a/components/brave_new_tab_ui/stories/default.tsx b/components/brave_new_tab_ui/stories/default.tsx index e83a36b47f31..67ae44fc07ef 100644 --- a/components/brave_new_tab_ui/stories/default.tsx +++ b/components/brave_new_tab_ui/stories/default.tsx @@ -4,7 +4,7 @@ import * as React from 'react' import { storiesOf } from '@storybook/react' -import { withKnobs, select } from '@storybook/addon-knobs/react' +import { withKnobs, select, boolean } from '@storybook/addon-knobs/react' // Components import NewTabPage from './default/index' @@ -15,6 +15,9 @@ storiesOf('New Tab/Default', module) return ( ) }) diff --git a/components/brave_new_tab_ui/stories/default/data/brandedWallpaper.ts b/components/brave_new_tab_ui/stories/default/data/brandedWallpaper.ts new file mode 100644 index 000000000000..23bd4be97254 --- /dev/null +++ b/components/brave_new_tab_ui/stories/default/data/brandedWallpaper.ts @@ -0,0 +1,12 @@ +import wallpaperImageUrl from '../../../../img/newtab/dummy-branded-wallpaper/background-1.jpg' +import brandingImageUrl from '../../../../img/newtab/dummy-branded-wallpaper/logo.png' + +export default { + wallpaperImageUrl, + logo: { + image: brandingImageUrl, + companyName: 'Technikke', + alt: 'Technikke: For music lovers.', + destinationUrl: 'https://brave.com' + } +} diff --git a/components/brave_new_tab_ui/stories/default/footerInfo.tsx b/components/brave_new_tab_ui/stories/default/footerInfo.tsx index 823813fd2c95..3fad7fdf3494 100644 --- a/components/brave_new_tab_ui/stories/default/footerInfo.tsx +++ b/components/brave_new_tab_ui/stories/default/footerInfo.tsx @@ -6,6 +6,7 @@ import * as React from 'react' // Feature-specific components import { Link, Navigation, IconLink, PhotoName } from '../../components/default' +import * as S from '../../components/default/page' // Icons import { SettingsAdvancedIcon, BookmarkBook, HistoryIcon } from 'brave-ui/components/icons' @@ -26,11 +27,13 @@ interface Props { toggleShowStats: () => void toggleShowTopSites: () => void toggleShowRewards: () => void + toggleBrandedWallpaperOptIn: () => void showBackgroundImage: boolean showClock: boolean showStats: boolean showTopSites: boolean showRewards: boolean + brandedWallpaperOptIn: boolean } export default class FooterInfo extends React.PureComponent { @@ -48,45 +51,52 @@ export default class FooterInfo extends React.PureComponent { toggleShowStats, toggleShowTopSites, toggleShowRewards, + toggleBrandedWallpaperOptIn, showBackgroundImage, showClock, showStats, showTopSites, - showRewards + showRewards, + brandedWallpaperOptIn } = this.props return ( <> -
- {showPhotoInfo && + {showPhotoInfo && + {`${getLocale('photoBy')} `} {backgroundImageInfo.author} - } -
- - - - - - + + + } + + + + + + + + ) } diff --git a/components/brave_new_tab_ui/stories/default/index.tsx b/components/brave_new_tab_ui/stories/default/index.tsx index 4076d5e20472..0c98a12de517 100644 --- a/components/brave_new_tab_ui/stories/default/index.tsx +++ b/components/brave_new_tab_ui/stories/default/index.tsx @@ -5,16 +5,19 @@ import * as React from 'react' // Feature-specific components -import { Page, Header, Footer, App, PosterBackground, Gradient, ClockWidget as Clock, RewardsWidget as Rewards } from '../../components/default' +import { ClockWidget as Clock, RewardsWidget as Rewards } from '../../components/default' +import * as Page from '../../components/default/page' +import BrandedWallpaperLogo from '../../components/default/brandedWallpaper/logo' import TopSitesList from './topSites/topSitesList' import Stats from './stats' -import SiteRemovalNotification from './siteRemovalNotification' +import TopSitesNotification from '../../containers/newTab/notification' import FooterInfo from './footerInfo' // Assets import { getRandomBackgroundData } from './helpers' import { images } from './data/background' +import dummyBrandedWallpaper from './data/brandedWallpaper' const generateRandomBackgroundData = getRandomBackgroundData(images) interface State { @@ -23,7 +26,9 @@ interface State { showStats: boolean showClock: boolean showTopSites: boolean + showTopSitesNotification: boolean showRewards: boolean + brandedWallpaperOptIn: boolean adsEstimatedEarnings: number balance: NewTab.RewardsBalance promotions: NewTab.Promotion[] @@ -33,11 +38,16 @@ interface State { walletCreated: boolean walletCreating: boolean walletCreateFailed: boolean - walletCorrupted: boolean + walletCorrupted: boolean, + isBrandedWallpaperNotificationDismissed: boolean + brandedWallpaper?: NewTab.BrandedWallpaper } interface Props { textDirection: string + showBrandedWallpaper: boolean + showTopSitesNotification: boolean + isAdsOn: boolean } export default class NewTabPage extends React.PureComponent { @@ -49,7 +59,9 @@ export default class NewTabPage extends React.PureComponent { showStats: true, showClock: true, showTopSites: true, + showTopSitesNotification: props.showTopSitesNotification, showRewards: true, + brandedWallpaperOptIn: true, adsEstimatedEarnings: 5, enabledAds: false, enabledMain: false, @@ -63,12 +75,13 @@ export default class NewTabPage extends React.PureComponent { walletCreated: false, walletCreating: false, walletCreateFailed: false, - walletCorrupted: false + walletCorrupted: false, + isBrandedWallpaperNotificationDismissed: false } } doNothing = (s: string) => { - /* no-op */ + console.log('doNothing called:', s) } toggleShowBackgroundImage = () => { @@ -107,6 +120,18 @@ export default class NewTabPage extends React.PureComponent { this.setState({ enabledMain: true }) } + toggleBrandedWallpaperOptIn = () => { + this.setState({ brandedWallpaperOptIn: !this.state.brandedWallpaperOptIn }) + } + + hideSiteRemovalNotification = () => { + this.setState({ showTopSitesNotification: false }) + } + + onDismissBrandedWallpaperNotification = () => { + this.setState({ isBrandedWallpaperNotificationDismissed: true }) + } + createWallet = () => { this.setState({ walletCreating: true }) setTimeout(() => { @@ -116,10 +141,19 @@ export default class NewTabPage extends React.PureComponent { }, 1000) } + componentDidUpdate (prevProps: Props) { + if (!prevProps.showBrandedWallpaper && this.props.showBrandedWallpaper) { + const brandedWallpaper = dummyBrandedWallpaper + this.setState({ brandedWallpaper }) + } + if (prevProps.showTopSitesNotification !== this.props.showTopSitesNotification) { + this.setState({ showTopSitesNotification: this.props.showTopSitesNotification }) + } + } + render () { const { showSettingsMenu, showBackgroundImage, showClock, showStats, showTopSites, showRewards } = this.state const { - enabledAds, enabledMain, adsEstimatedEarnings, walletCorrupted, @@ -132,33 +166,81 @@ export default class NewTabPage extends React.PureComponent { } = this.state const { textDirection } = this.props + const enabledAds = this.state.enabledAds && this.props.isAdsOn + + const hasImage = showBackgroundImage + let imageSource + if (hasImage) { + imageSource = this.state.brandedWallpaper ? this.state.brandedWallpaper.wallpaperImageUrl : generateRandomBackgroundData.source + } + + const showBrandedWallpaper = this.props.showBrandedWallpaper && + this.state.brandedWallpaperOptIn + return ( - - - {showBackgroundImage && } - - {showBackgroundImage && } - -
+ + + {hasImage && + + } + + {hasImage && + + } + + {showStats && + + + } + {showClock && + + + } + {showTopSites && + + + } + {this.state.showTopSitesNotification && + + { console.log('undo site ignored') }, + onUndoAllSiteIgnored: () => { console.log('undo all site ignored') }, + onHideSiteRemovalNotification: () => this.hideSiteRemovalNotification() + }} + /> + + } + {(showRewards || (showBrandedWallpaper && !this.state.isBrandedWallpaperNotificationDismissed)) && + { onCreateWallet={this.createWallet} onEnableRewards={this.enableRewards} textDirection={textDirection} - showWidget={showRewards} menuPosition={'left'} hideWidget={this.toggleShowRewards} onDismissNotification={this.doNothing} + onDismissBrandedWallpaperNotification={this.onDismissBrandedWallpaperNotification} totalContribution={totalContribution} + showBrandedWallpaperNotification={!this.state.isBrandedWallpaperNotificationDismissed} + brandedWallpaperData={this.state.brandedWallpaper} + isShowingBrandedWallpaper={showBrandedWallpaper} + isNotification={!showRewards} + preventFocus={!showRewards} + onDisableBrandedWallpaper={this.doNothing.bind(this, 'onDisableBrandedWallpaper')} /> - -
-
+ + } + + {showBrandedWallpaper && this.state.brandedWallpaper && + this.state.brandedWallpaper.logo && + + + + } -
-
-
+ + + ) } } diff --git a/components/brave_new_tab_ui/stories/default/settings.tsx b/components/brave_new_tab_ui/stories/default/settings.tsx index 71dbb404b115..ef89cd10571e 100644 --- a/components/brave_new_tab_ui/stories/default/settings.tsx +++ b/components/brave_new_tab_ui/stories/default/settings.tsx @@ -21,10 +21,12 @@ interface Props { toggleShowStats: () => void toggleShowTopSites: () => void toggleShowRewards: () => void + toggleBrandedWallpaperOptIn: () => void showBackgroundImage: boolean showStats: boolean showClock: boolean showTopSites: boolean + brandedWallpaperOptIn: boolean showRewards: boolean } @@ -64,11 +66,13 @@ export default class Settings extends React.PureComponent { toggleShowStats, toggleShowTopSites, toggleShowRewards, + toggleBrandedWallpaperOptIn, showBackgroundImage, showStats, showClock, showTopSites, showRewards, + brandedWallpaperOptIn, onClick } = this.props return ( @@ -85,6 +89,15 @@ export default class Settings extends React.PureComponent { size='small' />
+ + {getLocale('brandedWallpaperOptIn')} + + {getLocale('showBraveStats')} { - render () { - return ( - - {getLocale('thumbRemoved')} - {getLocale('undoRemoved')} - {getLocale('restoreAll')} - - - ) - } -} diff --git a/components/common/locale.ts b/components/common/locale.ts index d4efb16d3b96..853197ac8387 100644 --- a/components/common/locale.ts +++ b/components/common/locale.ts @@ -26,3 +26,35 @@ export const getLocale = (key: string, replacements?: Record) => return returnVal } + +interface SplitStringForTagResult { + beforeTag: string + duringTag?: string + afterTag?: string +} + +/** + * Allows an html or xml tag to be injected in to a string by extracting + * the components of the string before, during and after the tag. + * Usage: + * splitStringForTag('my string with some $1bold text$2', '$1', '$2') + * + * @export + * @param {string} text + * @param {string} tagOpenPlaceholder + * @param {string} tagClosePlaceholder + * @returns {SplitStringForTagResult} + */ +export function splitStringForTag (text: string, tagOpenPlaceholder: string, tagClosePlaceholder: string): SplitStringForTagResult { + const tagStartIndex: number = text.indexOf(tagOpenPlaceholder) + const tagEndIndex: number = text.lastIndexOf(tagClosePlaceholder) + const isValid = (tagStartIndex !== -1 && tagEndIndex !== -1) + const beforeTag = !isValid ? text : text.substring(0, tagStartIndex) + const duringTag = isValid ? text.substring(tagStartIndex + tagOpenPlaceholder.length, tagEndIndex) : undefined + const afterTag = isValid ? text.substring(tagEndIndex + tagClosePlaceholder.length) : undefined + return { + beforeTag, + duringTag, + afterTag + } +} diff --git a/components/definitions/newTab.d.ts b/components/definitions/newTab.d.ts index 72e86a8e9d52..fd7253c1780d 100644 --- a/components/definitions/newTab.d.ts +++ b/components/definitions/newTab.d.ts @@ -1,4 +1,21 @@ +// Copyright (c) 2019 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. + declare namespace NewTab { + + export type BrandedWallpaperLogo = { + image: string + companyName: string + alt: string + destinationUrl: string + } + + export interface BrandedWallpaper { + wallpaperImageUrl: string + logo: BrandedWallpaperLogo + } export interface ApplicationState { newTabData: State | undefined } @@ -52,6 +69,7 @@ declare namespace NewTab { export interface EphemeralState { initialDataLoaded: boolean textDirection: string + featureFlagBraveNTPBrandedWallpaper: boolean isIncognito: boolean useAlternativePrivateSearchEngine: boolean isTor: boolean @@ -64,7 +82,10 @@ declare namespace NewTab { showClock: boolean showTopSites: boolean showRewards: boolean - stats: Stats + brandedWallpaperOptIn: boolean + isBrandedWallpaperNotificationDismissed: boolean + stats: Stats, + brandedWallpaperData?: BrandedWallpaper } export interface RewardsWidgetState { diff --git a/components/img/newtab/dummy-branded-wallpaper/background-1.jpg b/components/img/newtab/dummy-branded-wallpaper/background-1.jpg new file mode 100644 index 000000000000..6ce4a41e205e Binary files /dev/null and b/components/img/newtab/dummy-branded-wallpaper/background-1.jpg differ diff --git a/components/img/newtab/dummy-branded-wallpaper/background-2.jpg b/components/img/newtab/dummy-branded-wallpaper/background-2.jpg new file mode 100644 index 000000000000..cb6ddd8d92c8 Binary files /dev/null and b/components/img/newtab/dummy-branded-wallpaper/background-2.jpg differ diff --git a/components/img/newtab/dummy-branded-wallpaper/background-3.jpg b/components/img/newtab/dummy-branded-wallpaper/background-3.jpg new file mode 100644 index 000000000000..9ebddca9edc9 Binary files /dev/null and b/components/img/newtab/dummy-branded-wallpaper/background-3.jpg differ diff --git a/components/img/newtab/dummy-branded-wallpaper/logo.png b/components/img/newtab/dummy-branded-wallpaper/logo.png new file mode 100644 index 000000000000..3ea23c0ef449 Binary files /dev/null and b/components/img/newtab/dummy-branded-wallpaper/logo.png differ diff --git a/components/ntp_sponsored_images/browser/BUILD.gn b/components/ntp_sponsored_images/browser/BUILD.gn new file mode 100644 index 000000000000..bf31857e44df --- /dev/null +++ b/components/ntp_sponsored_images/browser/BUILD.gn @@ -0,0 +1,38 @@ +static_library("browser") { + sources = [ + "features.cc", + "features.h", + "ntp_sponsored_image_source.cc", + "ntp_sponsored_image_source.h", + "ntp_sponsored_images_component_installer.cc", + "ntp_sponsored_images_component_installer.h", + "ntp_sponsored_images_data.cc", + "ntp_sponsored_images_data.h", + "ntp_sponsored_images_service.cc", + "ntp_sponsored_images_service.h", + "regional_component_data.cc", + "regional_component_data.h", + "switches.cc", + "switches.h", + "url_constants.cc", + "url_constants.h", + "view_counter_model.cc", + "view_counter_model.h", + "view_counter_service.cc", + "view_counter_service.h", + ] + + deps = [ + "//base", + "//brave/components/brave_ads/browser", + "//brave/components/brave_ads/common", + "//brave/components/brave_rewards/common", + "//brave/components/brave_component_updater/browser", + "//components/component_updater", + "//components/keyed_service/core", + "//components/prefs", + "//content/public/browser", + "//content/public/common", + "//crypto", + ] +} diff --git a/components/ntp_sponsored_images/browser/features.cc b/components/ntp_sponsored_images/browser/features.cc new file mode 100644 index 000000000000..36e6cc226060 --- /dev/null +++ b/components/ntp_sponsored_images/browser/features.cc @@ -0,0 +1,23 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/components/ntp_sponsored_images/browser/features.h" + +#include "base/feature_list.h" +#include "build/build_config.h" +#include "ui/base/ui_base_features.h" + +namespace ntp_sponsored_images { +namespace features { + +const base::Feature kBraveNTPBrandedWallpaper{ + "BraveNTPBrandedWallpaperName", + base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kBraveNTPBrandedWallpaperDemo{ + "BraveNTPBrandedWallpaperDemoName", + base::FEATURE_DISABLED_BY_DEFAULT}; + +} // namespace features +} // namespace ntp_sponsored_images diff --git a/components/ntp_sponsored_images/browser/features.h b/components/ntp_sponsored_images/browser/features.h new file mode 100644 index 000000000000..d1563e279dd9 --- /dev/null +++ b/components/ntp_sponsored_images/browser/features.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_FEATURES_H_ +#define BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_FEATURES_H_ + +namespace base { +struct Feature; +} // namespace base + +namespace ntp_sponsored_images { +namespace features { +extern const base::Feature kBraveNTPBrandedWallpaper; +extern const base::Feature kBraveNTPBrandedWallpaperDemo; +} // namespace features +} // namespace ntp_sponsored_images + +#endif // BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_FEATURES_H_ diff --git a/components/ntp_sponsored_images/browser/ntp_sponsored_image_source.cc b/components/ntp_sponsored_images/browser/ntp_sponsored_image_source.cc new file mode 100644 index 000000000000..c1dfa30d6f75 --- /dev/null +++ b/components/ntp_sponsored_images/browser/ntp_sponsored_image_source.cc @@ -0,0 +1,136 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_image_source.h" + +#include + +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted_memory.h" +#include "base/strings/stringprintf.h" +#include "base/task/post_task.h" +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_images_service.h" +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_images_data.h" +#include "brave/components/ntp_sponsored_images/browser/url_constants.h" +#include "content/public/browser/browser_thread.h" + +namespace ntp_sponsored_images { + +namespace { + +base::Optional ReadFileToString(const base::FilePath& path) { + std::string contents; + if (!base::ReadFileToString(path, &contents)) + return base::Optional(); + return contents; +} + +} // namespace + +NTPSponsoredImageSource::NTPSponsoredImageSource( + NTPSponsoredImagesService* service) + : service_(service), + weak_factory_(this) { +} + +NTPSponsoredImageSource::~NTPSponsoredImageSource() = default; + +std::string NTPSponsoredImageSource::GetSource() { + return kBrandedWallpaperHost; +} + +void NTPSponsoredImageSource::StartDataRequest( + const std::string& path, + const content::WebContents::Getter& wc_getter, + const GotDataCallback& callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + if (!IsValidPath(path)) { + scoped_refptr bytes; + std::move(callback).Run(std::move(bytes)); + return; + } + + auto* images_data = service_->GetSponsoredImagesData(); + if (!images_data) { + base::PostTask(FROM_HERE, + base::BindOnce(std::move(callback), + scoped_refptr())); + return; + } + + base::FilePath image_file_path; + if (IsLogoPath(path)) { + image_file_path = images_data->logo_image_file; + } else { + DCHECK(IsWallpaperPath(path)); + image_file_path = + images_data->wallpaper_image_files[GetWallpaperIndexFromPath(path)]; + } + + base::PostTaskAndReplyWithResult( + FROM_HERE, {base::ThreadPool(), base::MayBlock()}, + base::BindOnce(&ReadFileToString, image_file_path), + base::BindOnce(&NTPSponsoredImageSource::OnGotImageFile, + weak_factory_.GetWeakPtr(), + callback)); +} + +void NTPSponsoredImageSource::OnGotImageFile( + const GotDataCallback& callback, + base::Optional input) { + if (!input) + return; + + scoped_refptr bytes; + bytes = new base::RefCountedBytes( + reinterpret_cast(input->c_str()), input->length()); + std::move(callback).Run(std::move(bytes)); +} + +std::string NTPSponsoredImageSource::GetMimeType(const std::string& path) { + if (IsLogoPath(path)) + return "image/png"; + return "image/jpg"; +} + +bool NTPSponsoredImageSource::IsValidPath(const std::string& path) const { + if (IsLogoPath(path)) + return true; + + if (IsWallpaperPath(path)) + return true; + + return false; +} + +bool NTPSponsoredImageSource::IsWallpaperPath(const std::string& path) const { + return GetWallpaperIndexFromPath(path) != -1; +} + +bool NTPSponsoredImageSource::IsLogoPath(const std::string& path) const { + return path.compare(kLogoPath) == 0; +} + +int NTPSponsoredImageSource::GetWallpaperIndexFromPath( + const std::string& path) const { + auto* images_data = service_->GetSponsoredImagesData(); + if (!images_data) + return -1; + + const int wallpaper_count = images_data->wallpaper_image_files.size(); + for (int i = 0; i < wallpaper_count; ++i) { + const std::string generated_path = + base::StringPrintf("%s%d.jpg", kWallpaperPathPrefix, i); + if (path.compare(generated_path) == 0) + return i; + } + + return -1; +} + +} // namespace ntp_sponsored_images diff --git a/components/ntp_sponsored_images/browser/ntp_sponsored_image_source.h b/components/ntp_sponsored_images/browser/ntp_sponsored_image_source.h new file mode 100644 index 000000000000..60d419115da0 --- /dev/null +++ b/components/ntp_sponsored_images/browser/ntp_sponsored_image_source.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_NTP_SPONSORED_IMAGE_SOURCE_H_ +#define BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_NTP_SPONSORED_IMAGE_SOURCE_H_ + +#include + +#include "base/memory/weak_ptr.h" +#include "base/optional.h" +#include "content/public/browser/url_data_source.h" + +namespace ntp_sponsored_images { + +class NTPSponsoredImagesService; + +// This serves branded image data. +class NTPSponsoredImageSource : public content::URLDataSource { + public: + explicit NTPSponsoredImageSource(NTPSponsoredImagesService* service); + + ~NTPSponsoredImageSource() override; + + NTPSponsoredImageSource(const NTPSponsoredImageSource&) = delete; + NTPSponsoredImageSource& operator=( + const NTPSponsoredImageSource&) = delete; + + private: + // content::URLDataSource overrides: + std::string GetSource() override; + void StartDataRequest(const std::string& path, + const content::WebContents::Getter& wc_getter, + const GotDataCallback& callback) override; + std::string GetMimeType(const std::string& path) override; + + void OnGotImageFile(const GotDataCallback& callback, + base::Optional input); + bool IsValidPath(const std::string& path) const; + bool IsLogoPath(const std::string& path) const; + bool IsWallpaperPath(const std::string& path) const; + int GetWallpaperIndexFromPath(const std::string& path) const; + + NTPSponsoredImagesService* service_; // not owned + base::WeakPtrFactory weak_factory_; +}; + +} // namespace ntp_sponsored_images + +#endif // BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_NTP_SPONSORED_IMAGE_SOURCE_H_ diff --git a/components/ntp_sponsored_images/browser/ntp_sponsored_images_component_installer.cc b/components/ntp_sponsored_images/browser/ntp_sponsored_images_component_installer.cc new file mode 100644 index 000000000000..dd72bbec2469 --- /dev/null +++ b/components/ntp_sponsored_images/browser/ntp_sponsored_images_component_installer.cc @@ -0,0 +1,147 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_images_component_installer.h" + +#include +#include +#include + +#include "base/base64.h" +#include "base/bind.h" +#include "base/callback.h" +#include "brave/components/brave_ads/browser/locale_helper.h" +#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h" +#include "brave/components/ntp_sponsored_images/browser/regional_component_data.h" +#include "components/component_updater/component_installer.h" +#include "components/component_updater/component_updater_service.h" +#include "crypto/sha2.h" + +using brave_component_updater::BraveOnDemandUpdater; + +namespace { + +constexpr char kNTPSponsoredImagesDisplayName[] = "NTP sponsored images"; +constexpr char kNTPSponsoredImagesBaseDirectory[] = "NTPSponsoredImages"; +constexpr size_t kHashSize = 32; + +class NTPSponsoredImagesComponentInstallerPolicy + : public component_updater::ComponentInstallerPolicy { + public: + explicit NTPSponsoredImagesComponentInstallerPolicy( + const RegionalComponentData& regional_component_data, + OnComponentReadyCallback callback); + ~NTPSponsoredImagesComponentInstallerPolicy() override; + + // component_updater::ComponentInstallerPolicy + bool SupportsGroupPolicyEnabledComponentUpdates() const override; + bool RequiresNetworkEncryption() const override; + update_client::CrxInstaller::Result OnCustomInstall( + const base::DictionaryValue& manifest, + const base::FilePath& install_dir) override; + void OnCustomUninstall() override; + bool VerifyInstallation(const base::DictionaryValue& manifest, + const base::FilePath& install_dir) const override; + void ComponentReady(const base::Version& version, + const base::FilePath& path, + std::unique_ptr manifest) override; + base::FilePath GetRelativeInstallDir() const override; + void GetHash(std::vector* hash) const override; + std::string GetName() const override; + update_client::InstallerAttributes GetInstallerAttributes() const override; + std::vector GetMimeTypes() const override; + + private: + OnComponentReadyCallback ready_callback_; + uint8_t component_hash_[kHashSize]; + + DISALLOW_COPY_AND_ASSIGN(NTPSponsoredImagesComponentInstallerPolicy); +}; + +NTPSponsoredImagesComponentInstallerPolicy:: +NTPSponsoredImagesComponentInstallerPolicy( + const RegionalComponentData& data, OnComponentReadyCallback callback) + : ready_callback_(callback) { + // Generate hash from public key. + std::string decoded_public_key; + base::Base64Decode(data.component_base64_public_key, &decoded_public_key); + crypto::SHA256HashString(decoded_public_key, component_hash_, kHashSize); +} + +NTPSponsoredImagesComponentInstallerPolicy:: +~NTPSponsoredImagesComponentInstallerPolicy() {} + +bool NTPSponsoredImagesComponentInstallerPolicy:: + SupportsGroupPolicyEnabledComponentUpdates() const { + return true; +} + +bool NTPSponsoredImagesComponentInstallerPolicy:: + RequiresNetworkEncryption() const { + return false; +} + +update_client::CrxInstaller::Result +NTPSponsoredImagesComponentInstallerPolicy::OnCustomInstall( + const base::DictionaryValue& manifest, + const base::FilePath& install_dir) { + return update_client::CrxInstaller::Result(0); +} + +void NTPSponsoredImagesComponentInstallerPolicy::OnCustomUninstall() {} + +void NTPSponsoredImagesComponentInstallerPolicy::ComponentReady( + const base::Version& version, + const base::FilePath& path, + std::unique_ptr manifest) { + ready_callback_.Run(path); +} + +bool NTPSponsoredImagesComponentInstallerPolicy::VerifyInstallation( + const base::DictionaryValue& manifest, + const base::FilePath& install_dir) const { + return true; +} + +base::FilePath NTPSponsoredImagesComponentInstallerPolicy:: + GetRelativeInstallDir() const { + return base::FilePath::FromUTF8Unsafe(kNTPSponsoredImagesBaseDirectory); +} + +void NTPSponsoredImagesComponentInstallerPolicy::GetHash( + std::vector* hash) const { + hash->assign(component_hash_, component_hash_ + kHashSize); +} + +std::string NTPSponsoredImagesComponentInstallerPolicy::GetName() const { + return kNTPSponsoredImagesDisplayName; +} + +update_client::InstallerAttributes +NTPSponsoredImagesComponentInstallerPolicy::GetInstallerAttributes() const { + return update_client::InstallerAttributes(); +} + +std::vector + NTPSponsoredImagesComponentInstallerPolicy::GetMimeTypes() const { + return std::vector(); +} + +} // namespace + +void OnRegistered(const std::string& component_id) { + BraveOnDemandUpdater::GetInstance()->OnDemandUpdate(component_id); +} + +void RegisterNTPSponsoredImagesComponent( + component_updater::ComponentUpdateService* cus, + const RegionalComponentData& data, + OnComponentReadyCallback callback) { + auto installer = base::MakeRefCounted( + std::make_unique( + data, callback)); + installer->Register(cus, + base::BindOnce(&OnRegistered, data.component_id)); +} diff --git a/components/ntp_sponsored_images/browser/ntp_sponsored_images_component_installer.h b/components/ntp_sponsored_images/browser/ntp_sponsored_images_component_installer.h new file mode 100644 index 000000000000..6ed7492a00a7 --- /dev/null +++ b/components/ntp_sponsored_images/browser/ntp_sponsored_images_component_installer.h @@ -0,0 +1,25 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_NTP_SPONSORED_IMAGES_COMPONENT_INSTALLER_H_ +#define BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_NTP_SPONSORED_IMAGES_COMPONENT_INSTALLER_H_ + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "brave/components/ntp_sponsored_images/browser/regional_component_data.h" + +namespace component_updater { +class ComponentUpdateService; +} // namespace component_updater + +using OnComponentReadyCallback = + base::RepeatingCallback; + +void RegisterNTPSponsoredImagesComponent( + component_updater::ComponentUpdateService* cus, + const RegionalComponentData& regional_component_data, + OnComponentReadyCallback callback); + +#endif // BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_NTP_SPONSORED_IMAGES_COMPONENT_INSTALLER_H_ diff --git a/components/ntp_sponsored_images/browser/ntp_sponsored_images_data.cc b/components/ntp_sponsored_images/browser/ntp_sponsored_images_data.cc new file mode 100644 index 000000000000..cd8423685e8f --- /dev/null +++ b/components/ntp_sponsored_images/browser/ntp_sponsored_images_data.cc @@ -0,0 +1,50 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_images_data.h" + +#include "base/strings/stringprintf.h" +#include "brave/components/ntp_sponsored_images/browser/url_constants.h" +#include "content/public/common/url_constants.h" + +namespace ntp_sponsored_images { + +namespace { +const std::string default_url_prefix = // NOLINT + base::StringPrintf("%s://%s/", + content::kChromeUIScheme, + kBrandedWallpaperHost); +} // namespace + +NTPSponsoredImagesData::NTPSponsoredImagesData() + : url_prefix(default_url_prefix) {} + +NTPSponsoredImagesData& NTPSponsoredImagesData::operator=( + const NTPSponsoredImagesData& data) = default; +NTPSponsoredImagesData::NTPSponsoredImagesData( + NTPSponsoredImagesData&& data) = default; +NTPSponsoredImagesData::NTPSponsoredImagesData( + const NTPSponsoredImagesData& data) = default; +NTPSponsoredImagesData::~NTPSponsoredImagesData() = default; + +bool NTPSponsoredImagesData::IsValid() const { + return wallpaper_image_files.size() > 0; +} + +std::string NTPSponsoredImagesData::logo_image_url() const { + return url_prefix + kLogoPath; +} + +std::vector NTPSponsoredImagesData::wallpaper_image_urls() const { + std::vector wallpaper_image_urls; + for (size_t i = 0; i < wallpaper_image_files.size(); i++) { + const std::string wallpaper_image_url = url_prefix + base::StringPrintf( + "%s%zu.jpg", kWallpaperPathPrefix, i); + wallpaper_image_urls.push_back(wallpaper_image_url); + } + return wallpaper_image_urls; +} + +} // namespace ntp_sponsored_images diff --git a/components/ntp_sponsored_images/browser/ntp_sponsored_images_data.h b/components/ntp_sponsored_images/browser/ntp_sponsored_images_data.h new file mode 100644 index 000000000000..93cb52f51797 --- /dev/null +++ b/components/ntp_sponsored_images/browser/ntp_sponsored_images_data.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_NTP_SPONSORED_IMAGES_DATA_H_ +#define BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_NTP_SPONSORED_IMAGES_DATA_H_ + +#include +#include + +#include "base/files/file_path.h" + +namespace ntp_sponsored_images { + +struct NTPSponsoredImagesData { + NTPSponsoredImagesData(); + NTPSponsoredImagesData(const NTPSponsoredImagesData& data); + NTPSponsoredImagesData& operator=(const NTPSponsoredImagesData& data); + NTPSponsoredImagesData(NTPSponsoredImagesData&& data); + ~NTPSponsoredImagesData(); + + bool IsValid() const; + std::string logo_image_url() const; + std::vector wallpaper_image_urls() const; + + base::FilePath logo_image_file; + std::string logo_alt_text; + std::string logo_destination_url; + std::string logo_company_name; + std::vector wallpaper_image_files; + std::string url_prefix; +}; + +} // namespace ntp_sponsored_images + +#endif // BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_NTP_SPONSORED_IMAGES_DATA_H_ diff --git a/components/ntp_sponsored_images/browser/ntp_sponsored_images_service.cc b/components/ntp_sponsored_images/browser/ntp_sponsored_images_service.cc new file mode 100644 index 000000000000..5b168763924f --- /dev/null +++ b/components/ntp_sponsored_images/browser/ntp_sponsored_images_service.cc @@ -0,0 +1,168 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_images_service.h" + +#include + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/json/json_reader.h" +#include "base/task/post_task.h" +#include "base/values.h" +#include "brave/components/brave_ads/browser/locale_helper.h" +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_images_component_installer.h" +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_images_data.h" +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_image_source.h" +#include "brave/components/ntp_sponsored_images/browser/regional_component_data.h" +#include "brave/components/ntp_sponsored_images/browser/switches.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/url_data_source.h" + +namespace ntp_sponsored_images { + +namespace { + +constexpr char kPhotoJsonFilename[] = "photo.json"; + +constexpr char kLogoImageURLPath[] = "logo.imageUrl"; +constexpr char kLogoAltPath[] = "logo.alt"; +constexpr char kLogoCompanyNamePath[] = "logo.companyName"; +constexpr char kLogoDestinationURLPath[] = "logo.destinationUrl"; +constexpr char kWallpapersPath[] = "wallpapers"; +constexpr char kWallpaperImageURLPath[] = "imageUrl"; + +std::string ReadPhotosManifest(const base::FilePath& photos_manifest_path) { + std::string contents; + bool success = base::ReadFileToString(photos_manifest_path, &contents); + if (!success || contents.empty()) { + DVLOG(2) << "ReadPhotosManifest: cannot " + << "read photo.json file " << photos_manifest_path; + } + return contents; +} + +} // namespace + +NTPSponsoredImagesService::NTPSponsoredImagesService( + component_updater::ComponentUpdateService* cus) + : weak_factory_(this) { + // Flag override for testing or demo purposes + base::FilePath forced_local_path( + base::CommandLine::ForCurrentProcess()->GetSwitchValueNative( + switches::kNTPBrandedDataPathForTesting)); + if (!forced_local_path.empty()) { + LOG(ERROR) + << "NTP Sponsored Image package will be loaded from local path at: " + << forced_local_path.LossyDisplayName(); + OnComponentReady(forced_local_path); + return; + } + + // Early return for test. + if (!cus) + return; + + const std::string locale = + brave_ads::LocaleHelper::GetInstance()->GetLocale(); + if (const auto& data = GetRegionalComponentData( + brave_ads::LocaleHelper::GetCountryCode(locale))) { + RegisterNTPSponsoredImagesComponent(cus, data.value(), + base::BindRepeating(&NTPSponsoredImagesService::OnComponentReady, + weak_factory_.GetWeakPtr())); + } +} + +NTPSponsoredImagesService::~NTPSponsoredImagesService() {} + +void NTPSponsoredImagesService::AddObserver(Observer* observer) { + observer_list_.AddObserver(observer); +} + +void NTPSponsoredImagesService::RemoveObserver(Observer* observer) { + observer_list_.RemoveObserver(observer); +} + +bool NTPSponsoredImagesService::HasObserver(Observer* observer) { + return observer_list_.HasObserver(observer); +} + +void NTPSponsoredImagesService::AddDataSource( + content::BrowserContext* browser_context) { + content::URLDataSource::Add(browser_context, + std::make_unique(this)); +} + +NTPSponsoredImagesData* +NTPSponsoredImagesService::GetSponsoredImagesData() const { + return images_data_.get(); +} + +void NTPSponsoredImagesService::OnComponentReady( + const base::FilePath& installed_dir) { + // image list is no longer valid after the component has been updated + images_data_.reset(); + NotifyObservers(); + + photos_manifest_path_ = installed_dir.AppendASCII(kPhotoJsonFilename); + base::PostTaskAndReplyWithResult( + FROM_HERE, {base::ThreadPool(), base::MayBlock()}, + base::BindOnce(&ReadPhotosManifest, photos_manifest_path_), + base::BindOnce(&NTPSponsoredImagesService::OnGetPhotoJsonData, + weak_factory_.GetWeakPtr())); +} + +void NTPSponsoredImagesService::OnGetPhotoJsonData( + const std::string& photo_json) { + base::Optional photo_value = base::JSONReader::Read(photo_json); + if (photo_value) { + images_data_.reset(new NTPSponsoredImagesData); + + // Resources are stored with json file in the same directory. + base::FilePath base_dir = photos_manifest_path_.DirName(); + + if (auto* logo_image_url = photo_value->FindStringPath(kLogoImageURLPath)) { + images_data_->logo_image_file = + base_dir.AppendASCII(*logo_image_url); + } + + if (auto* logo_alt_text = photo_value->FindStringPath(kLogoAltPath)) { + images_data_->logo_alt_text = *logo_alt_text; + } + + if (auto* logo_company_name = + photo_value->FindStringPath(kLogoCompanyNamePath)) { + images_data_->logo_company_name = *logo_company_name; + } + + if (auto* logo_destination_url = + photo_value->FindStringPath(kLogoDestinationURLPath)) { + images_data_->logo_destination_url = *logo_destination_url; + } + + if (auto* wallpaper_image_urls = + photo_value->FindListPath(kWallpapersPath)) { + for (const auto& value : wallpaper_image_urls->GetList()) { + images_data_->wallpaper_image_files.push_back( + base_dir.AppendASCII( + *value.FindStringPath(kWallpaperImageURLPath))); + } + } + NotifyObservers(); + } +} + +void NTPSponsoredImagesService::NotifyObservers() { + for (auto& observer : observer_list_) { + observer.OnUpdated(images_data_.get()); + } +} + +void NTPSponsoredImagesService::ResetImagesDataForTest() { + images_data_.reset(); +} + +} // namespace ntp_sponsored_images diff --git a/components/ntp_sponsored_images/browser/ntp_sponsored_images_service.h b/components/ntp_sponsored_images/browser/ntp_sponsored_images_service.h new file mode 100644 index 000000000000..a099259dd950 --- /dev/null +++ b/components/ntp_sponsored_images/browser/ntp_sponsored_images_service.h @@ -0,0 +1,76 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_NTP_SPONSORED_IMAGES_SERVICE_H_ +#define BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_NTP_SPONSORED_IMAGES_SERVICE_H_ + +#include +#include + +#include "base/files/file_path.h" +#include "base/gtest_prod_util.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" + +FORWARD_DECLARE_TEST(NTPSponsoredImagesServiceTest, InternalDataTest); + +namespace component_updater { +class ComponentUpdateService; +} + +namespace content { +class BrowserContext; +} // namespace content + +class NTPSponsoredImagesServiceTest; + +namespace ntp_sponsored_images { + +struct NTPSponsoredImagesData; + +class NTPSponsoredImagesService { + public: + class Observer { + public: + // Called whenever ntp sponsored images component is updated. + virtual void OnUpdated(NTPSponsoredImagesData* data) = 0; + protected: + virtual ~Observer() {} + }; + + explicit NTPSponsoredImagesService( + component_updater::ComponentUpdateService* cus); + ~NTPSponsoredImagesService(); + + NTPSponsoredImagesService(const NTPSponsoredImagesService&) = delete; + NTPSponsoredImagesService& operator=( + const NTPSponsoredImagesService&) = delete; + + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + bool HasObserver(Observer* observer); + + // This should be called by client because this service is context neutral. + void AddDataSource(content::BrowserContext* browser_context); + + NTPSponsoredImagesData* GetSponsoredImagesData() const; + + private: + FRIEND_TEST_ALL_PREFIXES(::NTPSponsoredImagesServiceTest, InternalDataTest); + + void OnComponentReady(const base::FilePath& installed_dir); + void OnGetPhotoJsonData(const std::string& photo_json); + void NotifyObservers(); + + void ResetImagesDataForTest(); + + base::FilePath photos_manifest_path_; + base::ObserverList::Unchecked observer_list_; + std::unique_ptr images_data_; + base::WeakPtrFactory weak_factory_; +}; + +} // namespace ntp_sponsored_images +#endif // BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_NTP_SPONSORED_IMAGES_SERVICE_H_ diff --git a/components/ntp_sponsored_images/browser/ntp_sponsored_images_service_unittest.cc b/components/ntp_sponsored_images/browser/ntp_sponsored_images_service_unittest.cc new file mode 100644 index 000000000000..5c04da2cf920 --- /dev/null +++ b/components/ntp_sponsored_images/browser/ntp_sponsored_images_service_unittest.cc @@ -0,0 +1,88 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_images_data.h" +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_images_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ntp_sponsored_images::NTPSponsoredImagesService; +using ntp_sponsored_images::NTPSponsoredImagesData; + +class TestObserver : public NTPSponsoredImagesService::Observer { + public: + TestObserver() = default; + ~TestObserver() override = default; + + void OnUpdated(NTPSponsoredImagesData* data) override { + called_ = true; + data_ = data; + } + + NTPSponsoredImagesData* data_; + bool called_ = false; +}; + +TEST(NTPSponsoredImagesServiceTest, BasicTest) { + NTPSponsoredImagesService service(nullptr); + + // By default manager doesn't have data. + EXPECT_EQ(service.GetSponsoredImagesData(), nullptr); +} + +TEST(NTPSponsoredImagesServiceTest, InternalDataTest) { + TestObserver observer; + NTPSponsoredImagesService service(nullptr); + service.AddObserver(&observer); + + // Check with json file with empty object. + service.ResetImagesDataForTest(); + service.OnGetPhotoJsonData("{}"); + auto* data = service.GetSponsoredImagesData(); + EXPECT_NE(data, nullptr); + EXPECT_FALSE(data->IsValid()); + service.NotifyObservers(); + EXPECT_TRUE(observer.called_); + EXPECT_TRUE(observer.data_->logo_alt_text.empty()); + + const std::string test_json_string = R"( + { + "logo": { + "imageUrl": "logo.png", + "alt": "Technikke: For music lovers", + "destinationUrl": "https://www.brave.com/", + "companyName": "Technikke" + }, + "wallpapers": [ + { + "imageUrl": "background-1.jpg", + "focalPoint": {} + }, + { + "imageUrl": "background-2.jpg", + "focalPoint": {} + }, + { + "imageUrl": "background-3.jpg", + "focalPoint": {} + } + ] + })"; + service.ResetImagesDataForTest(); + service.OnGetPhotoJsonData(test_json_string); + data = service.GetSponsoredImagesData(); + EXPECT_TRUE(data); + EXPECT_TRUE(data->IsValid()); + // Above json data has 3 wallpapers. + const size_t image_count = 3; + EXPECT_EQ(image_count, data->wallpaper_image_urls().size()); + observer.called_ = false; + service.NotifyObservers(); + EXPECT_TRUE(observer.called_); + EXPECT_FALSE(observer.data_->logo_alt_text.empty()); + + service.RemoveObserver(&observer); +} diff --git a/components/ntp_sponsored_images/browser/regional_component_data.cc b/components/ntp_sponsored_images/browser/regional_component_data.cc new file mode 100644 index 000000000000..e6369698654e --- /dev/null +++ b/components/ntp_sponsored_images/browser/regional_component_data.cc @@ -0,0 +1,752 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/components/ntp_sponsored_images/browser/regional_component_data.h" + +// This list should be synced with the list of generateNTPSponsoredImages.js +// and packageNTPSponsoredImagesComponents.js in brave-core-crx-packager. +base::Optional GetRegionalComponentData( + const std::string& region) { + static const RegionalComponentData regional_data[] = { + { "AF", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3UQJgNrn5qHNcHU3OKhhK34FT0AN8V0+KJwBany7O3RbDh/JmZ28F18wAmq8r06XIFlxxm0vTA3YDtQ+kPzMCdk5fBIZKwVL7ikFEVV3vd3Opu26GRuJ+s9qEbgErHPGC0SHAse3FWhGKVw+sOI6bKOPUtRgmPJFeb8azE/qdVFvP8L0z3Ny0PSlu+wTb3klT0c3LVpA/2WSQLCfAZ8zvwCFDlsIoH/3oFRKgDpoWztIT15D1dfx/mAg8+j3KC8kjUBYBOHOjmTdjjQUrav4hcn39tIw83QovAEXEjy3aywSSrHP7Lg81gzJkR1EdufC+PCWPT6NQEUYSQPbNaxCtwIDAQAB", // NOLINT + "gleegaillfpdpepmcfpafbofocjleapf" }, + { "AL", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyA5vYKDcLJhD4V1nn5yqKwrdUQIS488roIpXfHFdjZ208L9ZTjDfGrIJ9ZGMfMMcjEF4kGsYYTi9ze/VH94aJpAMeVRZkj5oUYbXLAHl2Mfm2KjRda+d0OPFsgzkEmdJGBOxvuZLGZdeLCyflMCD/0oGzA8GkCNDgMODG1Leu8qAtF9ZDcD5DLmfVI7P8sJbP6yrz7fzi9P6HFWpu9jr+WTZ/PERm9GrDX68KoilgirzFr04Xhu9LGyOt966xWasiGSUumE7xewVmhFPEkgBsFn7fjd70o6nDUMgqRA5iZqDEbTHRIMHAawkjUghk55EU/8dnIAi7J9bKMRjunajswIDAQAB", // NOLINT + "kombghaljmieaghiiiidkphpjclhphap" }, + { "DZ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+XUfS45EtuOfn0uY7hGA0Td7y7hzILMOcA3yncXyZ5yOAaDiKR3UwcWc0AC6aV/JSWitMsOcbHD6IB6HcADSzbJuIZDS9Jb04LkkH1wQrJjeRxadu6nleCIu5D+fTuDg4MVIuoCUSQ3G2sTS779tODhKjgj5g1zyRcIqKy1MYQkV9GUS+1qD1Sxctus00czNhNwRSqQADEq8lVSjpRc9sVUALWy3gpETgc4/t85OuIvd8xd+ohi/L8Cp/N8EEql1U00sLZVTGKoLSwJ9prOCD0IY+EndjNEMrRdhMjqsZESr/4Iyhk6JTCyXei71m9Lf3DPXFOgANh8pNKq3hE+cGwIDAQAB", // NOLINT + "ogjffiompmmmddfilcjajlhhcpclnbae" }, + { "AS", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs8MYpHTCj/eJHSVovPZkP79CRiQCy26uMMy/KQvuNZL+/xGp8CpoTH2FRlLWJmK2uPJQVYg7j8eshC+coJaLDp/XwiayPmS8BBGtjYADB6nidAxFVbaoB5kxRjGcPjlkjAAOLglvKZswYpGdnL4Zgc+1B3IGCaAWR3spJFXHgI8jrp2iwiyEi5BPu4VMWqAPYn+IPhc1YJjWz0dI+qe94y0xWI1uM8BwDuvgB/s1ueGJUs0A7XLEoPGSGe0n1cWL5AfIO8P3KpR75AexU9J0H1BsmbHHaOHmAb/N93U82cNza7SwyJKRDV7F4nQvjXFuZ66gsjcXcqgPsXksucc1UwIDAQAB", // NOLINT + "bphfcnolppmlcpdlilmllbcfahljckei" }, + { "AD", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2B5wzX9c/Ljwxc0ccMKp+QNIl+J7YrOlLfiRiFTyzECaCXk+JDpIqtpva5dAYqzKA6vAXTOWiXwnp3SZQwLupNXXagbMrEGA2N7jdFsAMYTy0+brjliQ36eQE+eGC3tSu3GkQQXTmoO1nJDIOM/TR6ZR+oqCExFL+mRdYGAzJIjD8htnvhnlXUzNJv6dD8wSJCyvZIglu1+MsAaK7C168uF7XEWxO85TJilFKVxuGKbyz+7Fdyg034ZEyH64aJyjvPEIqFhV29YyO5l/BF6wU9LiCVnBXK+hTwasQqRS1ZRqT0si5JCRnMAGEAnrkIX4NTsntupC+AwO0Lh3M4uHaQIDAQAB", // NOLINT + "mglpdepifkigjonbfccemggjfelcbinj" }, + { "AO", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw+BzsBN4q4r65PaADD61hWgxL7EFbgmJrF8vsIOe9ct2EUTXlKt2FigmT6bceS/ih90/EdweTcFMWwwM3r7w6h6fNtj4xyLegdO/WGcu+RiFwnjKRv3tKt/x5rwmU0R0uBu8XPBe4rlBzOpta/H2PlHI6/l6dEThjEhHoWs4QlJIsEgc8kli3uJcrNgxLg10aNc7cl5Rs8vjVmh45IsQApuK/5cqt8BWKZl2jr+IZvEqTrtW3I1r+JDwLBHc61wevTJ6VeLIzOC13lnxONEFvBRcSbT6wfstb2xl+ccgyCPt5cNqZtPBiKdTRjrcEm8ufziL+CHXA780bNghFeyXwwIDAQAB", // NOLINT + "jkiciljmaebialdknljhnikaecldobmn" }, + { "AI", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwwtFni/ZWCrqEK3WIs+Ov2negReaSn4uZnXQWED7elTyA5l5h5TOimB/hfYGgwk/Z4T0n4aCtvIqS5wx6Nby1NPTWC1CBADV8xCwxxvgffCQNROt0IVN0G59STKnXW2xD7B0vbVEwNtoF7m4XPNUoLGh5hk2/Nl5Ag3zR4tu2yMXt3mddGunQoCwZNDcRQAHj7lcsZMkvA9s6+ea2EBL5HNjhRbTe1YYV9Ea0qX/+R8kYiLEGt6cvTERizFkhGCMvOfIDdC+PVsNZSFVSrreIyvcQJRurRvG5uT5XYYzhTFnLO1R0sSQfVfk44xCVx7/mkJ1oXk5QWYHD6PTb8nh6wIDAQAB", // NOLINT + "coplhpkeciemmkgpnjohdbokjlmcadjj" }, + { "AQ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq6X6sTEpKh5uFCf5zndbQNRtOJWCD9pVoq1rjCAuZqFnDt0Dv7zsBvo0s5bgK6JXmYjk8qnm9eWnnWyo9rnxXp8+pXBOoTBT+mxqTiiKig95Ym7oX0HrjgFW4UjdvJHJyu90o++Evi5amaJEBi3tgidlRzKTZVYpho/JLFTg2mOQnog3E3Ru7tnziD5/jXb462GX70Kx/bdt0CNGNX76wQNUJRxOHStHqox2UhGNTkY/5J2eVtySN4u0BBHyIGfNCz516UQ8eVJpekEh7/abYrNmHWkjH0i4r1vvQIlEvhquHUhpuvH4PIWKfn+naW7qUsdFrdDORadRTiG2nURBrwIDAQAB", // NOLINT + "jcapfegeepaefadjpiameimfkpnepdje" }, + { "AG", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5vVKZkt+WVheiTM7avU8VaBoST26XMryhskaWemNA5asHiwiOpFLvyLLFCCP18KsAW+liGQcRinI8uaJINPMvmBdLuYgy2EgkzqT9VuomkxRAOMNHBjObCtP5jA6jAGMNr9oU/pIWsbBDcHvYWCy5cksSOBExCcMgf81DF0Og6NKeTFJFMg6EpShJecUdt7qlrxh52CrH9IZ+fwzzf4ymPqZ85NHcBok9mqdzC56em802f5Die0D6wqyFPh0WQeJVrE38Lq1peCBWGa4oAk93FdLQiyrhDt4ONcvkPDh4TfRjozX1OIdxhDDwS0t2pd7fzjssZ52BXCUAbzw/A0GSwIDAQAB", // NOLINT + "ekhcdkkpoiofhmhadhkcefhpgkcnfkmi" }, + { "AR", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9ejDprywjeh2RQ5egbxm03ZvZbLfuMe1L9SnW26fn1amnO4T+m45VIRUNx5wLhexxU2jukT2Uv5WWGwT+l0u+GjQgBBnhPxMsVBC21Tgjx7HYjRorAV4y2aLQ/J/GxpKcvt20OGmKArihH0yl8CzndUoNkq/cB8N6CVPfhiRQEQckRsIHzILDU+6yqzevW6MolehT7kPlvkLzFLbV9EUTuxwj4r/SzjFCVxEw/87oCJU8+6nR4CXhYkEsVQvtc/DE3y5ne3Exs7TZgm6bB9KMkmCNVqBNOWXe1A+2Q87nGiqgNJiuCVDKCEmlm3IfwL+gjyM9qChwvmpD+FtmmdFBwIDAQAB", // NOLINT + "golcdmhaefcpmdoofahgnhnfldidgjfl" }, + { "AM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAssSDVJqEUd1T5vwYKhG3NKV1g64Wi1I7WyK2hO2S7kYQT+98EyHuEf4UyifxTHnPguXGq0Rq4TpR7zjkT3n2fnuc8Pkn3KUjip11dE2GOOQMFK8MPBbl0xjQyUQBmIdzLo4BvEbScKur9wvjFgedKVuGErKRgHKx/uwSvZ+3H9vc6tOV+T3Rn8vZhM28fNqb1wVpAoFBZDBJ8X4ioEEIJWLRmaVwl6xPrasRxnvvJgmjKsO8AZFiTlmFSFKgujUdWpB8Jfxz/WAmubn41WkuOmCPgZDQlA7XnUGRmXq0fWlAwdCHbDo2zBOjb5U2IEBgkGxqHX1ULzSWW2V8sle+FQIDAQAB", // NOLINT + "hpfodmjdafhbmnnnmnabfchdgmgogpfa" }, + { "AW", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAylukasY0X+vqTbdXaYBhlFaQAKRspZQ4PtM4jcW/lcdwd78I69K1nt5OEZ0Glf4Mg+ALaacuqxLVrtHZcMraXV/TctDmOLyCTIphF5/65ANgAd5T6Hk5dtFsEOGiI0ux8nXfgEM//HC4/qlLM3pQiKBc2Wt5rg5zv9dY9DSVbXB2LnqEGv7WZ2E7X5Wd6tCR0cIH6nGnZgA0MaHJSB4gm/JA0u/CjYlUFpKJT0YnKGdnkQeh7tcrqp6EskYdtALg0v3cnNmR+XS5veWlcII84x8gBm7PHAxNQFFMF0Mub8PZJdUPzazcJJRX/3gCHaz0Ei2C1PfP9IrL7EU2XwmAfQIDAQAB", // NOLINT + "jpnjhmgcogafphjdjaljkoiahdebfooi" }, + { "AU", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqYW/+c7cJp1E/di0zpHA+yOCM9MCBNfRsYjBsKKnpYWQ9kwTH828VTz+LwDKwitko1jG1Juz4q7FbgTY9Te2vz/k3JJC1BFWRFfe7rUQvrUpMGgDjL3/amwEmasahOKzebOVYP6sRgoZVhvILm+gVD7U2OLqTH1eeEA3ildS0X+IZnceiSrwN9yM7K7EjQJ1qYNKOWx6NXxcIQyBmCZmrG8gCxbY3IaJVT8KiXn2IMUZkN40V8LiOGfMyO4ExAiBK2+mogH8c/FS3I0NqeFLghUmAtxIE6MvIcwrqeY8u26KoQKk32vLh2gYjkj1GoEfgdNW9pJ4M1uvoy9IwC5LnQIDAQAB", // NOLINT + "hlcinbnbfgoealjpgmoacabdkapmjjfj" }, + { "AT", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1y8EZFFqqzUTlt2MA1goPt3IZclAzo01qC2k35p8RWmLyz8LDNTD4GvdgVKRlJ4g4elvUc7+dpfw6X9JqJuA0b2tr5GzFvniDoEZU1sv35p3CGDZnSEOKNLUvQDJbwI0ydXRnwhr670KvkBBhR6W9KGkjhzgDoNRpdL1LTjpzaR092jRpbUJzw5W+inBZtkanF+w/qz9DVHAJC0km4wJPj4Eic9KNnevepKgPmkvcNKcyk3N4oqX04tB0h/7RuscArTZickx0lmMQI/kY4huNsymB/jj6As1dqP4UcF7LXuwHtKpmJ+jdI2N4wfKhqdE8PTckcf0a47SNgu5vkgLJwIDAQAB", // NOLINT + "oflogggibieonhbjhjogmnbanpedikbg" }, + { "AZ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwDcdST+Aj+1EAUAAeG2izTb7Yib1MijfkNUuud3bOxGJTh+YK9NT3mBbe5YhY681dCxiOQRmuOznCPU+3xR2H0dCPpuSzjYSKp1zKwm6+2hzkvK82Eyo5eGuxbq70yBJQvEjcxUOivNlgGwTFXCkYUALr6hSLTvPlB3vMErUjT9bn3qP4wHrnj8LJF467+Uk//eleLIIm4EhDrQ8ejvPM3RhTlDeX3EMNl5e5rjglIFhN6XoNbESIVxAYdWNRGgi4tPDfsC4ZiKUyZBby+T7+2fMQywsuoZvAJ3b78b2y7BHTvhfFp6019Zd/xkSqw34UXfURAzgMmJDCKrQ6I5y+wIDAQAB", // NOLINT + "knelngmkdbakbkmmjmmeaickchockibe" }, + { "BS", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxoTWZeY52sIRQyp9Xj+AZWWRhO+0qxtLND4xxTUtwMG8wt+o6UFw+SoOPl79OCpkrkYyA8h52oA6DGxs6gY12hwIUegEDMzC5xZYLWOQgAuPItgKbRstJI7gU1fV31ZzIGxRbcMTBcY7UPZNSHOdFnKrB4Hyfsbf+ksgtNwEKUJgODHRhgfAfOJgRtJa9aDMAtec8wfBcBpLPYfPRQYJLRgznCnhL8Ur+eo5H02YtNepXRzVSfb1C9npjh05tJGNUOS8apgrWt0wXzLtUm8f184qQXQf1aABY/K94enLwH8Zap2xUe2nTmNTqAF737qPWtSn/97hOV8jrGseBa5SKwIDAQAB", // NOLINT + "dfhghnjmdgnnfcpgnepafgpcjkhngmoc" }, + { "BH", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0/zC0CetVecpqw8WOoPd1IA/p5UL6iR2zXqQobqpecfIbIcHNB00AOarHTFzkrufk9/WoYxwWi4sH8SOHlBXFN1Syx4b8w8DOE8yZDg4Oc0cSdV21vEejEymEEUu3to5uH0Cagn1Gb0q9XZVosQnWyYk+3+3sIpuBcrCNzhBfa/5IPPzHHQO9O15q+08MRXjAuXBfsp8NpMA48PjEmOOY6gzxOQntJr2haD9lL9fQ4E7YBdlHBzxPsj0Pn08hg+mvk1zAat98hthrR8atN3wynrAGvW7KKaL7AzKAIFxkhrcw+bgaXe8t4Z+w/GaBqPSKAKsV2pUwCuN7bdgaQ9r/wIDAQAB", // NOLINT + "emonjpkcejmieeeloijoefolelhbkkkl" }, + { "BD", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwTe1gwsJ+v/aWCsMqXmjMSgNrNgDViT0SOHwEhQDtHalW08vhi6JC+o56d0uTugdpXQ/lg7DOrcpuc0wdY8ePpzWS/c/X7WYcgbUWJJebyk+Tkvt9Yk2F1q/tIE09DNqEwJS1rq2g2tL+Mng4lXqpmYTTCpAeiGPe7SJ9z4iaz3uAtG4d7WJ3RAGBGvg10+SoNMcWGc/086EWbYD79rrC/0X6du5CaNAn2zO4dR0VlY/A3VFKYp7qgUge42vCgCjMMWvsamhuDTQVDf1xVjK3o/3sbgHKE0gkTHUxxRTHjBi1Erg2oI7gdRVFDGJD6ryOXSVzmoai5L44XpItqeL8wIDAQAB", // NOLINT + "iaaclhdpadloaihmmkaiggpfellmiepp" }, + { "BB", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvCGEO20C1GE6gCeG9Uu9Ou2QHLZ8/XM828hd1rzRKAxRtLtz4TaOCWpJReIU/2jxfI8oTuvHgsTNVtVbFcOHXF+FUcoatwMx+K23yYj4lF4wy/sp5Yw7vOe0gS67sVLkl2A1cjS15TskdvyIEJo/qCW8vTZ5rWrlQYLOXQ51eHcL/D29yIxdocckcOyJmtqNNatNGyRCP/ONPHsBCJD97X8k6MvoL3PzAecSVXrIge13ZWg59QEW8jFseX8F5QLp0aIIhmfy86kRB44x3rhI3FKUS+6BKPmCi4ojF+POX+LnyQAgENBNHIBP2ZMcWmERh35kWASCgCTkuYwgrumEGQIDAQAB", // NOLINT + "hgmbchfjiedpmdimfmjdigolfoanolpf" }, + { "BY", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3z75wUmEbwK8PbjHB2MYl9QPa8/A0xPS369Guaqhls3Pw2KPoJm8il92A25oiaorBE+QMD7rpEWsz3mAbcAoS7ZBwpNQylILI+8ne+md/RGvwYPV+CzJ/++tKmzvZPHrabNHDBqrTtilasSMVPB8CIe5TrhmDLnWzt88CXHra0hD/zawVrVZvrRRrgtbpAO0XVS//SXwOmvP4SUfskwmbMWKkqBz7BJt34GIq1OYj0he8BCbG96Bhj0JJsvWucHptF9zmt5eUFJlWSm64paIYVbwBlVx0DmlH5pkyMwdfM2fIX/LTJsbjRQvY4a37eVgrUzJ0eXeYInNdrZw8EyKjwIDAQAB", // NOLINT + "imalfcnjbalbidejpneinaifplfbjhbj" }, + { "BE", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAujM1+HVT7IMIcNhxhGyTRwrkk+SLevzD9uXWmF5SaM8qwgCaXUSsZfdeIr8aFWO1ZyErxYRRif10IaL36zIYkJ71c8xEYmWOR9rEKvbSYAJMXN6KKU/HUJEabrI5CqMl5nrZdcoYywF5LKcpPOI08VO2RUnixbBOD1dUJMwzxhcwSEifxoVEqDs0TsGHwm6Ux3lj5kHUMQR327ipp5oZm0/zauaj4d6LRMTTFeEkjjL7Cnd8k/pGwjpEoW//gQhnBNvLRzr7Hx3PSG1A6DCtimEwTfcIJbB6IF6R5ygUCDFzIqtZaeQwGSSEYQ7f33JAzsPkHjCOGpIRwtEDALJBgQIDAQAB", // NOLINT + "gigpfioocjkgbjgoonldcifaeajkbdln" }, + { "BZ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnJpT0a4025vEupZeCibH0uGzQkfS5W+FnnYu93J47GRHpVUbKaQ2NbheK5Npk3jLe/DjXTnkkaj5XfH6nLAqyrmz7XycmAyORPlxHAFiglXkB2Xi1jIJiL2ewCxAJurvtftU41Wplz78q55+Axf6wrr17YUmq8HHCp9aag21PJhtC2h5lekVJLXPr2i9meQ6L+b1uz27diHJj75KGA0xPa/1Mk2gI0K7ILsN2izTREngFhB0D0PxlAp2YX4ZWdtEEVnzOLrw6Z8T9EPXBVT1b7RR2zpkMuWh5GSy4EFFEF0gg6/D4ArIM2e9ryvTFgDlIvTNAwEUe0+0xAZh255ciQIDAQAB", // NOLINT + "ccpopkeoigcjgpikdkdfigbgcaaoffam" }, + { "BJ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtuigQgudXtMLRf/I/C/yYHLqiGT1yEQLDsUTSCmrVpn3k09xr5HaMC6avPZyA0B8yrmnT12yhv58bw1aMT7Vc0ASN7RwGpaa6ch5JV5yVzhpTOL3FtLjCC+6EYrTE83eIWEcIDibBzQ0tts6t2WqrDcc1e0IHl164VTXYE8A7Zc9o8q98iCnYZ74DrWVUCn/Nv27S5MnWEkHdMeGw4iC54EnVTP8/NS5iP1kCRu5RYnGx5eTU8oU33aAJgg45UmxiI6iYSpHgEmPGJqOddIf/uiO6/S/tC8n1t37NRCq/qlk01OnLf+iYVWDrW/7Sl+xGQSg0wR6kWkPuYlj5l8N0QIDAQAB", // NOLINT + "ipdpmogknbckihddljiolpcohfgjjdek" }, + { "BM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxFXTLGa9hWpEMSmTR4BQGI+0LYQLwEvxTp6urGOOU5KgK3Ps0iqhX1IGmUZIVQoP/vVDzoH/4Kqvm3+PZIpkwBnxqb9q7hLTwJn9m32FlFCic7HOekSTFNyU52RtgHCHEjL2auSWtafNv0ftHYZhEbd+7s1IPtY54grAdIu3YjItwTvW99oY1GVScBxudIW+QfLThz4i612m/9nksycbJ6O+JQB3aXFTcv2NQ016MRSnxvc/QU6mdxa88vFCDNClZQAy1vrMyKg4rQPcEUOwsAIazDPWEO8fZwZyFxS7SbGP6AORu5bQOr4nR0EycCxygCc+GvdCTVtmL5CbYrdBnwIDAQAB", // NOLINT + "ppkljclpmejopbjbgcgkegoanbefikah" }, + { "BT", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqImUnaeSgHh1bxGJZ4U81NTqCmjxg2cMzFSUNy9rrNdKDE6YGHGjMdhJr/Qa5XcaQb3FQimRmiAu0qKv+QhRXI/+kLfQf3PAJz+1PxN0LFn9FyJ8Vj9SJC+2QfupsrpR6Y0qhafHe3/ZW9PZoKJZIZMLm5RnGOpD0whveM2hMhKs6Tr2V6yH6EMwp7j5AeC8ogTjJemK5vcejQ9PUvRItkou1kPhOnrwVp+mTSmJgMCoV1AcoMcYRFrw5ShtZs0Pj6C4rCuAjMBX83Sy2ANviw6gT7MLX3QGat6X6OyQdMiA8sEHlf63wbJ6wyZgEMxP/kXeFt+fs7fHfNUjzEgwdwIDAQAB", // NOLINT + "cpdopmjcamdaednfhmlhfnndnlofggdh" }, + { "BO", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvD4Ioy3MX/9FjigdDSplP2zgZWo7CpfjYrUyAcO9mMi7wW7WZq2U0TzU61Q+CbZV4DOY9VMhZYHzKT2oQs4pRHlhsHwK6QNXSERfB2nSiuZWwQfhpAxUzks+9WiL0ji2dCDXi7ub/XYg9GmYO1SZJoWD53CazMolUSxtqYqCOCSS1lIoIe2TNX9qWzRAmCrK/YoZtPFZ4qP5gVi267/dgiNiDWQMK4vtkyk0w0LxSiV6q61nAKUJhufa4CAen101gPHYvzakU7r/jIvP4uiQ32vhD8qLo+T0Va3p6kz+vlrfhuSIRGRvyrvAXfz7B8Qcw43qtJtoxpnanKRIkihEzQIDAQAB", // NOLINT + "pgjaooobjjmndfmpblabkaplljlieglg" }, + { "BQ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwI20FVIK67pSyh5YamaPZad5eQylG/61V0BLhNdm8Z+e4sfYeucVfJ8DiRfHKHHt+VCl4LmuuPnR6tIC51fJ5ygyRizgTKP0bUEWtQqJUcIXZTwwHOVG1cjkH7nSb5LuRr8kzdZzkaYdSY0mJ6w4in2rUIT0kA58ANvK/B1yI6v3GJiP4Wxf7kLY5twyh23tZxz7ssyg3Nssil8RmQQt5NAjIKOCf/6lF+GrfBM9Z2Z0UKEVHltC0jgcgHAAcgeopaoJUL82fiGt66qT/VlRERO6+CHuOHSS+tptwhDXeWjA+9Sr3X/Q3etxNe/9VEQcyKftYuNX25laOAKHfAj7GwIDAQAB", // NOLINT + "bjhobgmbedlgaojaedfmmjfemndlgime" }, + { "BA", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtbEZDT1V5r8oDVXXCmwO4a010P6x1O69YAdyt96KHtsR85xp0QWsvZ8KphBoox3vk4D3Ic9wZVPDSLDFdayk79yb16NRaBgu9FGhMolFV2kXLqkU7sxCkkO+rAigXlveNs8ARu1RUsvABxCxbMkITB1J897lcgnl62eJNB++QHXENXZ4OtXsQHkFEGCYGPE1Rh4KQyatstoRsspBpVS0SYXhceWv0KKUyssQsPjv6Eri8+OYOoZXvQdHX2FDcSn3CLf4qMK6v20kql5yhMhmUh444jmyDBOFsQWOYmYbIzUVKkcXNRkWJgegLxcLENaCYDGJeTcP1K1w9p6PYc35/QIDAQAB", // NOLINT + "poohfphagakpddhilcnkaiggjfciegop" }, + { "BW", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnE7hKoj4UfjfOZ0jHbQz/yrVe8Hn5FKGbS4nIf1Id+FbAmlXMrFw/MKS/+owizKvdM4pvU7GsL3FLP1P2I/7JXCRowDip0aEIlCFGIBiUbOu6x5aOX0CCch24EZwI2XMCxPHkqPGoMb8n2metMEaH/D8rpXGk5i4BHKrhq2MXBYbLFHTYMU6HASp3QboZfR+maw9/MTWnYLwoclegTWnikR1CM1J3ntn+aDzuEXfDjuUI65UtiB7+ztwWXsMDGDeDdauZT4sC3V5JnXsJ8JPl51c1F9nc5nlEx3oNXG8p0tHJKd6yl+blqO4qJHMYR4x3IJqMmFIhgadYDEfUuodxQIDAQAB", // NOLINT + "bmeholbapnlcoednoegmneljfpklicad" }, + { "BV", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6nCYYgawYloMaO5xyhijfqUVf6wuVDrWildBePi0hLWniwyILyqm9BMF9mW9SNmjMb1rQDpuRp/Wrnowq5EIdTtSHITti+vdzNmSRapJBcWlWXOoiyiDBk5jabOt0RPM30ncLhxeiKTnFA8HeZ9YBHfQzglPiXgPOGbDARTQw4E+1nwvT/M5r0nivKfIh6qBV6T04isGQiDikMlsG0Ajs9bhQzAgm2pgxtnFNR8ZL8wZ7eZAl1YFtFkUoK91B6DP4ZeqJf4qYo7aVfuBZjTH5E/YN4yA660wbXZa459A4fH83T26ekgWJgOCXcCjMyz7CEsBJG27j+uibYoDsIDuCwIDAQAB", // NOLINT + "nfkijacohghenplbbgakfkcellnmaekg" }, + { "BR", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsSeZgq6A4+qOsFpmGRVXNZA47wDAozhzNiHwzkahOX7I2c/rA9sl794556Bzx7Bq4+antImQodPC1Gtibo+rQLgZqplx7TZNdFwqZrDmTu+crLaQFXv0FXGbgzxOkAXAa/HIsIT7FdNR6Q4lDCNxeZLuqC+RuAbwp+qJ64e+1n8to0T1Svx5rlE6aLvrIJp8nyju3Qn2mMsr0YKe84mXsC1o4GkJvfOkwywX7qnmKeZUbZJ3unFsBfIcibDrFA35xeyS4uMgvd2xoQdHxfBkkhLOyRZZrozLx07LUD1jMTb5f+PY3ZRWT6SUVC2UMiKsGHwZVWevYLgJyNwkxNXTJQIDAQAB", // NOLINT + "bpndlkddhgpmjengabcakadpcabgflca" }, + { "IO", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAypGLxc1Sa2qsL9yC/pQ4bqUVpHsMRLt3EPQ86mSRGEFUmaUze4Sge3H79OZ9QgBSoezQnCqW4jknxV6SMHZJqBEjCiQkTp/nLqKMLFxJggjHvbN9GDOoAHXvXxIYAuWj6qu5+V5EvwbC3vRRb74RSNXE/k/0zpTVnLOdwOoocz5vHC1OzU9/UkfWLFW+9aBSRUK0/V+mkHhxFnpwf7QxrBDwcZbBadGj7Gz0K3/oFySDEhdOb+zfcow3s2e18poakARtvmyOc5yIOMsXi4DxgnU8jJaJDAA3KxQGC2KKQNPiMfhP7awRmnKYs6ptzw0zevN+YJ08lcULf+ck4tsDRQIDAQAB", // NOLINT + "kekmeeaeagidecnpjahopnmdfgfbijph" }, + { "BN", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ttssYz6PDmnM7YLOc0u0Y5y50sj/piUda1G2LS18oNeDeVKKt5iAG9MbH4Wn76tqH6mvjPvD+0IF/YoJR5Xl3M0jWYFzvTNIO7IZeIEl16kqaz0K1X2sXjf9STdP8GvSDH7ToFpOQjkHUh/XK8b6Gq+W7on/b0sgpeHMh09+3MSGupIEF6whAHjALNXdAeLUGN51hfr3Mzu7TSdg7KWcVXBcp2/OnoFBkRoKGc7Nqq7rnygdxUQDgzm5Gr7ogD1v2RbAtNk746fusEzdF9C3Fe+DBwBd8hEAxasttvaMem3NdJAYDn9OxGkXxOcCXSRulTvDoeDC0cDL3rReH+vZQIDAQAB", // NOLINT + "famnkdbokigkljenljlobcemgphkjpgl" }, + { "BG", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmz/75BNCw4nW3jSbGY7tya3/uXScYyfoWGlHukJcUA+a7pach3XegruqEgk4oIThj/AJI477zG/XO3pVt2xY+DURyATkITEDifDR0vwiC8mFEtisMQC0AK142e/PepU/63pxoXoU8tptolD2lm/ysKRlAqBe+ef34olj19A3ZF6QU2ITgovigEtfBcjdI82ij9HSEXE28wy8pd/ARfsObEP/kwKhq6uuqxQnfDww7ILZ8dfenV8pXzjNtK+Djjm1rHlezQ4B4BuWub6Xee34iOPUk3m4PvXguraBEWFJgAvFsGNZD2Upx1ThmPKcLKsfrNkDtQONdrQEW6jyPrAFHQIDAQAB", // NOLINT + "bnbiclbgphecbbhoomjpeoamfeidoifa" }, + { "BF", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5Gh0c5DIPoqPXzP323fLP0cHvgWLrgeYjqgpNJM51XujQ9C4l7okrzABsuxIsCvgC2OCyiKYc47/tr98kTFKGyDiiUHUPyJSnhXfi984Xm9Xlfjc9OpCH7yWYZjvoT7iNx21QNyYnXMc629Bi9D5QmUA8493tibSuVTXW521nL9C52AOFgeVebUhG8Png/dkiapq2Z3HEXP4gV/1fM3W7zu9AleX2qD6yxT13+xugi2DsIxcZeFTJsMSA9NcgskTRUbFxJlCXQ/JNpaBoFvccs1Aeeklp3n9CpBo6X+NvUhNVqA2rkqlM6npTvlfASgrFlwBGY2ksvkfK6NVsuDMvQIDAQAB", // NOLINT + "fdacbnkjeigjidplkaaggedjlloekblk" }, + { "BI", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu+/W2+V02psOPkSKkFZ4mqKi0KtceeBGMqswYvOOrFCTkJjap9GwhORVqC8r6gKMqa5lYNqz8UdeHUyHZPj7miYXuBs2VF8bZDCP9gLhC0ijTSU0jCQeYJuL8j6LUAi7od54Mx7N+sSgQJNGNj7mTZGkNGNHvgOoRJYJn0RtMuusNZQP+9gkQEIG47Ltk70TqhNyrDMGwzBdYv1a8LXpd6wwZXAUL3+7y/wPOxwT8YpDvUyVRGOduDNboD1EOm1UBQSKLWLwea9fYskWtRFcHh40NkXOoX5UfMcNgeNu0T1W1HD+MiqUHB8bUZ0BBXcO065zWms0KKQUHhZOj+wa1wIDAQAB", // NOLINT + "medapmfmbambbblcihgifohkhojmamjc" }, + { "KH", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+EBXNXd+mXz+Bvgr4yNblFJ8Tw1S/lCRPEOMWW2BXxy10/Hl1sSm7L4PQLcpk3NRam1nIYBWNdq2/fwz5VamUZTRldM8zruL1oVJ32BW6C6EDRlJmzVq91m9vDcHWdgurayaGWazg4GEanAvoeKHQh3prKX7N3bHeMurEBoVv/CAVCeFNmRuTtXAkuQgnip5/J+891DYWcv8ecQ/UdQtZEgBE8v3IrIOrm1Pfro8RcTq6pQ+acup11wAPhaM4HTeanAZMwcQnn6KLsWxsCPw3hIgK3giljueYkAZJHsvE7L0R4zS0vMGWhFcbtSs8vRQT9dI3pfCMlkdxOgmYJ92cQIDAQAB", // NOLINT + "ecapdjlipchhokcojioljgeobphnakej" }, + { "CM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArQm6avoDcM7vhpIQZTCRE/EDaKNotv1NpDpb5CXd2VR1BJUCZiz/F3W107jR/w1SMjJZh2U/pmRqJP5eH36ttdkuk6LgH720fvO4PcysfWOur3effmFqp+mtuyuibfOJ3CnVBiY/wXXhMG3eoV4cF290uh+P5jYPm4lJM3n8CwCPy7MaVktIEHx8Hu3bYdDiymtku6oZnQ4cASfyIU+1L2CWsRfjCcdEC+ywdd45HxsKHB4zsGQ9kp/LnJjUx1UaKz8sMPyEFfRHzswcXaZ9DFb48azRIA1q568NTE438cgPEFonWb+zfcO0289ZXCdiTJYXLajRS6zi6T7R+8T93wIDAQAB", // NOLINT + "appbgepigbjhiefaebnleabkjepchkpk" }, + { "CA", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2IhrWy2VyE58LoS17LaJfJCpvXXEI79fLoLdJ0gDpF5Vhd5+zkHUn5IauVNUAcOD3wxrAloLFoXtIn20kJB7dxhW8HsW3PAvJz1nQOU0sQ/LGxwaJGm/0UsC+TeWsyVeDjkzeIFfjjGXmJ+FjfS7Riboc2rN2G+W/q9padrrrO7PCEpTFfodwliW6fQShn0uqasC5xShtetLPdtq38obxtns81+Owk7vpzdXwxwbLxe3adwhX+EseX0dQD4/YfpkEY8m57fnJV8dEhK/K17PIVEyFec+qWtL5rXf8Ov1bRLxSeeVamJyKIKRMx/pRVPByHo1XSRIKj+1L0I+gLAWJQIDAQAB", // NOLINT + "jiacfhmaoegmmahbioiihgpfnjnklmoe" }, + { "CV", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx6G+QwIuEiA1FhoweiYnVQPHMNw315DZ1F2y0TEGDXyUR8PGtPeNBPDhqiJoc5O3xX0UjUboAnFxueluzIrkBw1Slll6rEmkEnhEjDh+Ff9ssF6OwILboj1B1ogSR4Rtb6/TQoIkLSC/shYKoTbYSeoOJP2X8s+QMA9VThZ/95N7h4vFKjJ4QXgmALiysfHBy46emra+5U8EE3HzYj+7MYHdFinW8+OzNKV3SKi2s/s6zbZJ7rqz1dDqzbjteW4Zv0V6Z0Yx2gYXCjsHQGlVbAgnpKRjenD83lVR1acEdZuEZQY10iC7yxtwn5ZqLFMdfBpjo8ZgGXdCyz0wfR68LQIDAQAB", // NOLINT + "jegnidalfgfnefeialkccpgejnejncnm" }, + { "KY", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0EPcM/eCBXicL5eOJwfAAKsGXD0uSrQzxwl0Qc1wp7DbQUtyd3IW3w/5AXi/HyfSVSw1YNMj+1732TsMA5u2u4IYhgE9TSens7Tk3WGNop/El7ZPLKdHk93CDCYNdyGPEAeXWXJS5Q0wPkOnB/3I258JlI9r3XUE+AZY5aXLtl8DcCVLwVhg/NG7cPqM7TN3TXAJ5x69mPcL9xyhzxiGOWPk/dVGf1xilRQG/f6JkT3d1ikkvsImu+GRe5pJ3slzUTO+uAe9ddfCTsEg8+DlAgBo0/CNXCzPn7QVwNvUbVKTnnBHh48H27Xk5gs6jI4jeMhCmx5VhgS5f/T0LNL6KQIDAQAB", // NOLINT + "fofioopnpjaehpmilccbldepabdpcioj" }, + { "CF", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxFyM5dTv+zHTP9xwxOVBozJkpuj3jJY9kpp9rw/gxmkY2kfMvcgnjdUu++eBue8v8wFnvh+5J99NMIIsSfRMh+rf330hH4QN7Qw4S8L6KjeOwQKBAlXSel//Twk7c+LbqgqWx2Xu+GdjBXmi0pZBy9x3hx4v/7EN6BcBHNpfi2a7DkZSuFMaWhauKgUM+q7kH/ZrDLgOAFnWnPa+iNr8TXBAQ2aTiH5S01K4RYetJyxkJyMClllH50GgXSKivmwouTPGccKTZ29OhcffLIdmVsrdvxhSzBdUdkzhl0tyZvzJsjggqxWEJioLYAeQsCgrixJTc5esmTuI43414w7/aQIDAQAB", // NOLINT + "lfgffimgfmeolmhpiinkngllbdndgdod" }, + { "TD", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo8H93q2wNt5HzexlZEIWTgVFZHOGe87ZK+HTxiszq/xb3rFJU0zUDhw3kNXKpLaKeX0Gzip7KmWingH2Zm2nF7phZYDhuFgWDvNKp1FJ95D+Pp0xNHVrlb9uXoo7g4j51N5bxoC8LznGs+BdUgCE/20jojZTSGK8w6TC87jyHgjG1CuvgP0lxEkrDQP7fRKs733AmC/V6Ob449pMso6l3qrbb4cRXJ6F+CuCVaB3en4lzOIPCGoyuXpEnlM5mVSV84fnnlW4v6xObaibMAYW2pj+Lp2g4/Kw5oVsC0DwRQxurjK3VMk+PSA3PKxpQsfwpIstcO2GLAHeUhmjmm/t7wIDAQAB", // NOLINT + "eecjkfadllefecghpjhmgdflieboefgj" }, + { "CL", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArGc22rdGT6MHAYWMy3tPHU8mmapks3L82WR8wGvkjC7QMiwUHHjAhiEODL6PTgSMWjy4wRRV1AgdZMzfRGMfQOEjJ5oXrE3Ul5/Z0U7opZNEDiqrloiXUvuxqcqzHuR+8kLgkg08BzfEfLVfJf5kZWvMwxDG8HOuacSv8ndRs/OLOnjb2TyExztB6WIz66levg/ba/G9S1QWqOk8NeTODcF0gDxX5YXvs+gSeUKGf8FzxNT0KqKezcwkxgSuo7kTRd7D/KRA+e50fXccjr7/GYNv+EhiknBVHWDN2WsQi30S9y4VjRioNCWf1h4A7aDuRJldLrzuYoLzHYR6qutKPwIDAQAB", // NOLINT + "ehldeebhpkbahclfninajgpllfdegecf" }, + { "CN", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4uFBz3/4wYST9/TOVSLM77aYDD0o8KwCup9C51+m+YAuyYVnpyFYj3njxCtlyRlTPlKLnLCm/oLNSjm6L3VY1nlZr9GAMEYX9zO/976ZwU4YFwe5MOs7fvL3rrbGsLSCNUnavSScaZxlg2qQGu8i/8oRa8PV11EDHPgwRkt94ZRDNSwSRaR1WmD4xu2DH2HN7t7KAUB4x1ikd5eVTFdNxhECOLMdK5goBuqHhd5N4u+Rk4cu1daojCQiQ0o31G3X9QnOHGVAGoDyYUVHw35jdjkq0asJiyMHUXps/1YIDdT7abuVHfVHuVCSARSg4gI3aTagV60Zw2vbdBtmh1b45QIDAQAB", // NOLINT + "nnccjlhhhhbnfelgbodjgnibibohhkai" }, + { "CX", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5DatAv/VW2cU2K2AvuxFYEBsIj5kLYfj7MNeHwVgj6pG/21UwxcBEpxktuYkmtePZa+37U0uUYxcdxsu9i3+h+aGR6gkcIFdcGQZBWutwHVR9qg42L3xsMIJYh960Q7MvSfzKJDZLgASJLh4ezHfmzQ21Izs/9LBZ6SoPKPMlcehgExOTANfTOJwtOoZ/2ZUJaSteDn5dR2BUOIDfoYZj9clPFSvXTjpXxU6OHyUyKM8o3tU4GK3/DPtgkrLfNYGKHqvtYfFET2Z3VtNqNiz7eKqfue29kCrodFHkROnZ8bAQD6g0PzbyN2fw2xUZKE3N+U+YjZQNmeq7ElowKRYlwIDAQAB", // NOLINT + "dbeompaakmnidjdggflacnlmjallljhi" }, + { "CC", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAowsgnq2hFh9nJse/ZTxGP3y6Ob2BuMPyPyjuSHrfxrKpeEhLRdcm62IL66atNBy7dKDDtgvFmkUZ5Rgry1EU0tNb9wA7Y3GBlcsg7VqI/qLdFx3jl4X2dNLYiHUTvjIcSYOjgSGLblc6m8PxtufBgRUzCju0FGjOy8D0YqfvIUbLUYph1ho3k/tWhdJyyYlwrKfHk5oABJX+ATFElmhYNyVRu1mFrmQI3CNJSrQBDhzd/j3vcAc6igKyiKT677A2wAW6Gr4bVVIXZ9t22baCOLI9SDMJUR6OvNNYYq4uoPdMEdZqSvGCl06pQxl1BfKD6BYX2hOqAZ92oYIoU9RNgQIDAQAB", // NOLINT + "dacfhehglojdhonfaopikfkgeepndaeg" }, + { "CO", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2+ptZWl+3HvQxKs7iZiodvfh2z/VKqv4I0tBQRmO5B040o6UQ3lcqSocAZ0M+lzFhv3jY/qxO30DWHLN4bTWMz4PZrHC7w3H4KqFWei7Cg9qgzc3pFsL+YMhe1r5nHFJUyuoSrms6rzhbPcfFovnQXY/2+d+8u8Tv5RLWi7prrzeubsCCa/LNwk2m2HnKn/3zpPY2P/gJsVT5EofEKh+5b6BIHmdVqyi/ViGkwJ5mn7tTKkPHSNMubj64o+6DlnB2C4KAG8489vfkxv/wj5OL5Uzc8uJbVMVNGE+lNNeM7IY/66/lKZb2rQHSmLptZYoVFd+qDhBpAv+7jogzEooywIDAQAB", // NOLINT + "ogdjnhmejccgjdnclbeghpffmecndeai" }, + { "KM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0PxxmtOI3tf6GwVY4m5PpsJn6YkcXD5esDaYxBriOtcAkENfe6Zcfv7hNK4dQbKhiDvCnvJRPTFYWmWJRx9IPazRBHkKTr1UVeKdpXVDNkjQ5B9iZ6gjPp/cWKJIfhqhxx7I71wQ000sGG6tn5t7WisJtt9YgSozVEjU9s/JxK5SqpqyDLxFbZmwjyvz08aPzt7819AFBsQGJPquYQ2Vfo/SIjaAOeLSzBOSf7YutW3GGX5i/8ivI/nLmZ/s9k86D4pEoqiQsdQpMTao6CbQoU5QxT9MN5zbuRuo5bBFvgtcGO7DNDjj8cpxAFpPIYWdLa8wj1/1+cer4oRHrx9aswIDAQAB", // NOLINT + "kgljhejnebjlgcpbbkjhokejfgdobjek" }, + { "CG", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvEhKXxmdOdw7n/dU8yZxlaxHJmUDoq0G/ksD99MDMX6omefY6kAR3unW3I4zJcjAqaoxnryGDaDUqIjt0Hzy4VnFBFuCVol7pKZ5a7OoYvJZBcv5KMH+u/T9rP/2GfSM9P8YPgis/cOuIUFw/tPRnGdMO++qlO0LjaReNmSm4pi8wY0/YCw6jIuEnZzhbJk3lvGn0tW5qqGU0DcI1mV4B+h+RiKIZcU1jUy0QdL2dsxdvZeUV7E93uGOlTMngXapanlWl5p76rkjg0FszLhZUhP2VilOEbjkC+Jwi8M/jxBS2H5dCZgvyYGLPB4nucySaUBsqYakiRh/f9NhUpryawIDAQAB", // NOLINT + "mchimlbhhknohnnafkjmbehacahehlim" }, + { "CD", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2iUAaSAkOccINei2JHXup1+U4U7fHcn8pGV29RCleURwy8uSs79NPX4+oPFgMYtdvntnhiZOV/PMK1WPGPIjoWy3YrH2p98Ls4S19CDbsjafdj6Nnj3QnS+wwrZB6yff5w1uAzrluoS2rzswedVH0UbIKRhpROP69ma4FX163+fyOtnw3Cwd8TQ5+HJC21tS56zDqVCJyrLNqSB4SKeG1i8r2UZVLo9/XOUg6IilVIcMD0CvqT7naf9FaWkZva24aUdUn8cLuwBpE/j7FZh/xT8BACYb2wAr2EJrKfQKaJ9PJvnG5smKDEkVQzoJ19P7v9Is4DFoHKo/hZ9WYAxPzQIDAQAB", // NOLINT + "flaaimlcdhpdepjkkkmnipajangdlkee" }, + { "CK", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArV9htvYJ0Yza3P4lR8xboZWpWJDawpZOv8Lv/kNX99naVZdEhng2ssNeFxAQ3OPRz86tPVZSgHSUkw5RqE+DYK81X4aLY57qza3nmDShgSAgZO5TrkzhL/N6hgmM7tBbjuKJws2zI4YiSNiQpxR/6rBRrGZNytP77ENKhbt22XiOitGF8zay/auKiMlGjSQjRPCRo/6mQpL24rp/FaIQApw1twtas31hQk+TYdsAQRS4WTo4HEGlhtVSVFnwh3zn3OovyrnNr+esk4mAZFSD26JFtXNb31VIaYlbLqHwuKNfQdg3xxu82xoWtuxDbRbidEqWEfGQA7VtgAC2OP4ucwIDAQAB", // NOLINT + "gecgflnfffpjnpnijlllboakfgenpmhk" }, + { "CR", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn2FKQa97+bB597pT1zlX278+ZWz5H33Wc03nmwxhPi5HJGuyK7jHNjizsP/CDHoYUGaXPEiMorayANe+TtvjaX49cyg4kt5APDDz4y23D+CPY2XK1nwoH67Sml+GfGvdrrkMcDy+b+MCt2kJMHien7PxM+Q9wN38nqJigPqExREos0FMDgpFT+am6LVFNDIvJp6VSVrS+dQLkQsQLzpc2L3egNilUL/5j0kSAiB749z5WjKPDPUT6GryTPijIw9ifNoq0UbnqPo1uZ46dEjf48XQqKe07wixMreasqnRxegTVkO/XtSE2zKfQskV2a/WewaRHstfBGcnu9U0iH1LBwIDAQAB", // NOLINT + "hhbebcdhndppaeoepejhlhnmpcnekngl" }, + { "HR", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxFr9lNZO+Hib+btiF9mRGI9PLGerfbzjCgMusjT9cgJhKH9Kh/nuM17M+bNDzrLZ+mlZQFPaQcGSFxRNfvCJmhiAPiB3WhfdDxZBKHNCObXURCGfyqJG/T9vcQrn40qGpbLIBD9A5ete8EoQmq3i+vQslhKOM9L1bH4EPpcKFv+XkSUnWnzQ1jWoWsdw+0YsXjJBFhhc4ligCmINImkXLbk5nX95AUJlWtc0praU+IvSTYd04yD2zOq+lkrzh1E4JNUsvuHeKCn7pQiK6HMKd09tTTPnhubZ2AnXMfVKnWn9UHVd6sk0O/x3cFgigzl9aKdFKM5jb9ofOsS8VQGK4QIDAQAB", // NOLINT + "pneohhcppdekibeejiedgkkcfdkndgdg" }, + { "CW", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8fLP/2JH6t+l0qZId7zXgB7881unPyj3jkZdJax9tryUzSjNtAyvMTX0q6f//farvlXaMv/cnIGAVPGi7z7h5kfBz+ZW6YyKRni/0mr7T30w0jFhautE8XOdjDWCuSJvM7hd4h7QVoSEXyipskWbmUxYRrP1Z1WDxz5GyJKiBC6Ey77OtsqUx7xSVR9dWiUPXSaUiV+/Y9LysJNSx6+hF0wcUY05uSoLOvMmDoZpWj3C49i8Ne7AyLgaygKfrUbJiog/ZZnYsaP+B43T7JXOZ36OijN/Kuqh7nTD9rpkVbwIQZdC7FC/zPssoCaBrXdgPX8FNSfXr/1a+t6UR7Qi9QIDAQAB", // NOLINT + "ofnccilfbddeljmbfhljciclcblkjmnb" }, + { "CY", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1R+zISqTC3pHHJfT+G8ar/XBUq1G11Re8Q10xXGvRuowgInY6OKbUedm1IQ/yJ+KAPo73DaLlaBlLNrBKYqgWSa0J52++bVwMeKb09T6zTnMmUj0CB+vAEQG7lrnaaknBBDr3eyAoOQPIdoTDxOD2hQH2PXfbp6YNYUohAPe2UUwPd9dDmEmqveGQ/jdzickqzvCvo4zGYDMzhSmVeHei8/pQpGRcjptLC1id8gMlX+AlaFQARg375GFfqEI3qvIBKx+vogtAfJoHnOOYaPiKzNRq/tRVEFeYQoeixsyIWnwgWyXcR1HM/kxnfLKudxagfwu5BiE5HLQJPiuW6fbJwIDAQAB", // NOLINT + "gfofccccokkebgagdmdkmlfbicpchoin" }, + { "CZ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuXdYaHWhsi82FfprmpfJeSiOpBBV3I3VclarSGMF4YOVkUOD+LCQIsFgWAu2HHSDryBt1pC9wPkFBASbZfH/oJJUKbT8z755o3Tfd33sngPeUwXMwQ5gKvrH2aKoI9xya96mx6ely1DWZLk1o34abhEQjR45OOz+2lJNsL9D0bFSgCG4tjiHr/MtPKJUe8IcWFdTx2MLlzJhWj7JE8mmnpvVF5PpH5Q69wOJDC0LEHLY+N5jB5773wofMUEAW6cBNNaib+DdXOA+YvBpC8XK8xfUZ+PHGNblr/9pCJQhBMOZo6I/6a3bmylIE5/Blk5WxANswojN+EnH7KBtb2XIUQIDAQAB", // NOLINT + "efkihffiamafhbhefjaljejgdpkelpal" }, + { "CI", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoljf/PejuDdwgurTLLO2jHB1OuUIV+aLqw9M1hSszPOKmH3V0TnkLDcWwLm0H9/IEQZKpNimK+P0p6OdIXFgnB0BHtl2fc5XsbNeQ16O2wvsYJkx7HaVucm4Fksrh+tuUK4FAi2lc6Nukr0JDyoH8i0zZJ3o4U7LS2URnohz/aUAY5e2r67gmnNyCo+BgXyLXo9K+iDuJiuWE7yeQdRcmWdM09yhNkY6xHgJZHxbz3yBIoW/DwQtZYaerw7HjWHFaW7xVL/PpwuxedrJYANtJdpmfUIhT2STRFTpR+g6oinDUwPqAoqiULvvlkyKTr7TpM8ypW9/sd2btHOnzNKLVwIDAQAB", // NOLINT + "eeeobilfiogkeldbhjfndlokkehhffap" }, + { "DK", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqqARwjC0O2OKSCCwSEOc7gFwVJmXQc6dCKMvgkqGg7xw9tBMyQGoh4xdvzL44LhLgAGf3Kl0fT7b/8SJFjCrw3qvVUrkrPZ09y/tFoT1e7DKXOGHEsf7pYeqI8/InaQZjy17EhnvyOQoKFWADQHtfCc8A1zOrEFHi+3sz9Pr+49kIp1PHJroKYI5BYC8v3pgR5xOLpUxGOlVAmhvXTKVoY/i4Upq5EfH8xrVuJf0+jWL0wkzPntzpT8UOMLqyLoNjK9WE6urqQKnPvFrFlO9MSpEuBEYGU5Tcy9PiEb2FTJHvAjx1Zu8prg66xuRJo0caYIKvF049x4M9ogYHa3nSQIDAQAB", // NOLINT + "npgnikmkgjfghbkdbfijbiekedekaebh" }, + { "DJ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4ILEnrPTtpFqffwpevt6blF2dEHeStF/SxP1y+4kOYX9Jig8R0QiA6dC4i9qv0bca9cbgVfmeb90WI/MR6dOZZTpud0rOOEF8iliE1RzCHWbrmAPhf72cNIjWwecWTrroOUnxTM250BTVea8vf/ABCEAWFm2HwVbGNKm4aq+Eqoo829WAvjkAWZgV8RtK1BUXeJCxXvn204S6c8s1fcClxdPXnVsXvk6HpP83iIpP26KdG4hh+BbvTfGQNF+a5WuHGWBUG8l6OOMS5DzzUiBhZvGhlJ2peczDzaE1jULn2w/U3NJ1ymxZ0w/u+g3vrSCvS4hdY05X+iguj5Eq4AOnwIDAQAB", // NOLINT + "lbpdgpikingcgnbggafhehiceagglpml" }, + { "DM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0CP84nmNQjq8FYhZqZ4lR6X0C5YXJUDy530MLQxuvPK06iPCDUChxXvxB6CrtSpIPw5lTHolLB52V6BtqUch2WKvAEMIRCzgKSSQWXBrbrCn+pwsf8+sFvVMcV0PwfWGTQGIv6lnRY3y0ewLCYZN/mKTJWoX3LJGG/QZceKDqZy9c5sAiwUTQUzqWeGtXukOoAYWiy/PpYnKrmd4e3ZvXJxsU/Do9xKAUdmFak6KOK2FvPkq2R+/lYQHbJynVB1Lq/q2svfKiiAZtD/TUqNuE/w1YE7VQ2monsu0s6kmSV4oO4vlJDczaRfkUXM/00mQrJ+jJSTe1qQLkKT3onFhiQIDAQAB", // NOLINT + "deendeplpgolpjphhkcpbohddfkkjoni" }, + { "DO", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5QO8I4e8ixBw6gCdyIBNjhUke+jIKYHBWZWgQKeDMVTVS366ix59aNnpzybFFe6172jlprp/pkrG6nz8CCtzYUGDvAAFfh7479kAPYgpn6pD8opMms8vGvUlFwJBidiq/ElxnalYA6J6BZ/MaBmR7bAflsqUbebwQSyQVb0IR6cqbn2qirGY3Ab6ZlkgtxloFEsomhoZQKbnuNNvbGqd31Gt1GyO/hMSD5a5sjbqwZqft/JcQLiBsXbS5dDDTynvQREynsQ+s6ekwAh2g9XFPNlU2VkMbOLzqtupwYsYSgAF6UHYS/hIuYYANyxugApaaCzTZ6q8uvYTmZ5Eg/8FswIDAQAB", // NOLINT + "mnjeoloelleocpclogeagnemaiijohph" }, + { "EC", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuJoAUmJeAwRexz4/zb3UpcQGV4Ozs6dLvoWzw+dEJZTceNFGDWmVZHYUjD7cXA/GitIhIBEcEK36EblDR5Y+Dq24k1/1vaoP/CaH9zTLdsyae+2bnnO6/ohEA5a+Sxs9Z4tAWhsl7dUfBGtyTlrzD3MzpLYaJ1aQwAPqFaEJ5u6u6W1zyv8icSrqAGfPq33ObndVS1/ayVEHqMELxfMDFk+RtsjL2F7VEAL0T2v67XLITs/WGbWfCbXbZU9nYnN/6MkVgr/eE2JarS/Lsj+kWQF9aVh/iQr9dV00qYFThuZ0RuZFXRti2m6vGtFa7Yhg843qxvstIm5TdaCw+n1S8QIDAQAB", // NOLINT + "oencalbobamnpldcfkjdpkhhmghfapbn" }, + { "EG", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1LGd3B7Xkq2ljLX6q9NXxxNk8wSOXAmlOCPC6X/Ui6Q1R1ofAaT6k2sYqqr8ujvB+yvbDNrYZVfXAgw7RGzwmoeEb2nVOJcsjQ1igwzCo+W4VTb4iWehuN+h8efKnK8DL2Omr2cuOUp9VS6fQlZqsUWpqPYqrE5NxvlvpxMqp/Pji1SX7Uw2BPZol9PJ/ImrWYyhJ1I1ulaCh/IeMktRggsNj1PYFbOpah7U88xMo0WZFfCm/B9qz5n9hSkm3XBCuw5euEWm4jY7zDAWHRDsH0zHq8O6M17Qb0Ba+2Ev+mCv0GTW+c7ofkZoG+wySDIDFQXl4BUAfl2iwoEsfRnoUwIDAQAB", // NOLINT + "ipidgafgmbgjnepnnnhgjdjmegihckkn" }, + { "SV", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsGp+1HMbtvy1rrx9O2BvCDB5fjN5Fi/XA+Htq69G5fqDqK5zoGHxEdZnbN5Enf/em69nxXhq6DxjVyhfa5E9pFD003ZwIueJCLNbyFa6F9IxdrCfEDszwt792yyNJNC63wyXpulfzxcVx+vacUh2/Dc5e75NUmc1bLH9OlqHew6aAInxh8mLSSKGvFAlUlw0dK9aHgw1H3NQ/7tj+MXrCldy33oXrVRvH96yUY4JvBcrXAXdbBb10COSwLuVl+GJ1+Y7Io/EQ4Y88NhVuKR7WnpIIiAyGT5C8MBUhGdJ+9usB5Dw4vV5XwVYDc6+Ab2Dl7KD7iKHzmwJibigQZIAyQIDAQAB", // NOLINT + "lfdaibahpdfgpanbmofomfhdpbpbncel" }, + { "GQ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxyizxCYpZzWjfOARwitQ4Bb09jLoUMJGtIo6hia1EGEZI+hIOsmz4RnYIvb4Zy/M59fs1qfV6k4ega+N2VP0htih7+XoGke3hVffZrAoav9/NLXLybsdkqKF2/uzry6uUM5clswP4Td9DC4jba0BKIupspI130PyDdYYEHTj5wqSopoQOnnTY4nlHJ5jkLc+vMk9jtxC9RY0ii7RrgGBkhvMB4V7E039P32lFEEj9j1hD67dU6S9/l5LutM5tT8nUdGxcbOv+H+TkwYBX6c/WNDZlBPRzdOvQNazGh/q/bN91DYE3GmLInhhP5f0YyLvmHUTMjN3ExMPd48S50BzKwIDAQAB", // NOLINT + "oilcgogigpdbdipldnplmmffbigmibdi" }, + { "ER", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtJgV27b6oj8YJ2kvcl1wn/GIdzZ9qob1NZ7NCOZFP6m02Ds/30HKcy9xTqFBW4PjatWGdXpJAcV3gjpOn+6BRq1qbmvAm+DBi2CUMbQ2DHgFGxjVl37W15yD95wSIszgWZ6zltWS8z2DAjr9iKAnPqgvDxAE4wBv5RgAlH+ZcYBGQbJaQooRTnRBKUPN5TaEc6AHuvD3AkFNcR4Mdafht8h+IU7e6R6yCb6DT9PEz9E6pZEcTcEBIJefBpeEHtJqXViv40dv7LKpG/9LvJK3yjWBaHGcgLf8EoutiTBxehC7jksqDmsZYzZGWOplarhjotdPhgB5DLaM+rmY/wAIJwIDAQAB", // NOLINT + "ikiaamalanfbnkiabmfbodoaigddcnoi" }, + { "EE", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyDzvS82+92yRzV31k+cRDc7SiJz9+AGfxDMrUR4c/wpSFGuALinJsk53Dk2itJYYjT8FuUp05iD1cDYikxxOr53gW/rdMqg0J1hbpeJxopUa/11z1hMjgGvsyRKHrG+gi+KUQNj1KjT1YMyK1VWCIHuuQPbimiWWSHwhCQUDHM5GgXaVJdz9OkyZU9YE3Bx/p34c3CZ09NGxhGTEUv11jYy4WnbrEm2MYcSAq7sQY0v+Eitg23wUEM/rmkpOMWiX9oEazTRHA2E9X0OmcCCwkh/VbdSHpjmlCQ5drk8L6B/WpvTM6aqJsWs93sgAD8wOab2gXuUMHEyyXCfL3SQXiQIDAQAB", // NOLINT + "ppgcncbdmemfhjjpcnbnoeipceedhdlm" }, + { "ET", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtH0KX7bu0CWGHph7CDh60YeyukV/JJGDEJgS0vwAqchNovjTigNFcKzp4Ms07eQRuPTye5OabhkREi5iSRKBF8gG4MV+tnQj6MBDffMGDWDxkBiLEhzFYZxcwdZ6L9/tGqGca0/9zIQMe4/146rXeNMX9uqifXBeL5MrsYmCT1MePY7j1/hZhUjHhhBeEP5CHEXoJtqcd/qJx+rGsU//oxFVTKv+/fqdVUwUrUzE4XA8ZURI/GAF7zTD6/6nSBNnnRfpwxhdqhwqVv66kU5VAeQTBjSgzKFr9ryHEC2DD2SjHsKfUadzsUle7ExXSS9LJGzVGUH2RitONp7kTt3E1QIDAQAB", // NOLINT + "hnmjglgamhnldenlnpfbbooefcpjglep" }, + { "FK", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAurwTAx5N5VpjVyfOKMjZqLG7enWJKX3MHlBySJtWNjZ11Jo2TKIopRXfUSK3UTyS6jda9Qw60OJ6MH8CWbk+wWaC2sMMLMpPg4FMZiEdLzTX5Sda3GFStxAAhbeZxXe2fiyYiR/EEINUTObMdtzClO8aBMPaxleqimYghwAML/MlvqU/u4GJmjWJfWPYhWp4MafzrETQAYq6Rswi1yCFh4VwRGmbKkReA9H2H4Qao7hav42TXzc7LiIOiXtUg0iuzAV+mq7KvS4xSCHg6LkHjkZVLLnpVezor9hN3bMiaS/vgDhkdHJtzIQAVEOZM+/vRI86FyrR4WCoLIiUImIOhQIDAQAB", // NOLINT + "mcljmnmgdfblbbfnmfafjbohbmebdikl" }, + { "FO", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnkazdGik3R9r7JMD5DTK5Ytx+CN21uGkmAtZOGq8XqUDGunmvgBR56Ey1qyMMlBU+MZifkqBfJOdxd82k5zIbJFfvfboQtdiAFPkml3LTVU7ymyLKdsiifBKMKthVa9RQ4CqPx4PwToIUhVtjjpqdRO0MMwr9GLoO/h18mEZhkNk441l4vRKpdRjT6k7rWWenxorPlv/bL2Q9KPbBJ8RnJzmV5sD4DIgJetRsnScLeXn9MzrSBIBZG/L3h+cfW9aYUidYK5faNYRKiEwwTEi2zShrsewdxm+Bmv5zjflX+vcw6dLGKGI6ntOlhPUzorwGgcqJcv3AzCDUNViqkcsiwIDAQAB", // NOLINT + "emhijfjhjoahlpfpdfgjmnmmkbhjgkhg" }, + { "FJ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqMCEtrEgGmnsIocpxy+3s7rOidg3qO/0pInnLIoq08sipFf23cqWbAsE4ajYC/v0eOeiI9UdMcJIDiet1QQ5AOxXRdeM4/6N7vFjVB+z1su6bGr6Qr7b49W/L4ZqqXgzshFgvhDa71gL86gsLcbAGapVFPc4NHdQOdB6ggrMWyk8Cfq/MYKwoXWWLnERKgjmfda2qpZ5kMX0SP5ELVivN9jcqpWT2zedDgMHjQxFNbOi2qCzcRlqijWdD1NTHSnQstemPZDQE6jCFpVuSbIgLAQXjDxAJYAGE89YnT6e3T1JP+3kQQ/t6U+9jYv4tvKPsTocMmBRsfj8IPIpn+Hm4QIDAQAB", // NOLINT + "njbhmagcfomdnbkedcokomndcfmcchmn" }, + { "FI", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs50uDMPQdlVr0nxcX9VLnrRZZxCQBKOG3C7LfShQcLgYjWALgUHtOc8GrF0+3BZ8asVEalE8GLR0XhO7/CXc5QpouRoN1afAYXYfT3dfdNzqYC43gx8FlL//7kyTbgcYUpDk5Gv3AD0hCTeZxE7iXPMb/+omVLnXthnN3MJE7wDkXbhkx4lonYr5Nvh53N5plCUeQ5RpOlXYApwgVyGoe88VdVCWI4FTXuBLfoAJ1v86SG2YyCleWhKQNQ/3FbEgsMBBDMbf3pdhtVh6+fnosMcv8ZDI2BxKPflrVqkRJKo/HdI+oPySV4pGd5KPvcqHntPJj5cMSF2MWDLOY+IYsQIDAQAB", // NOLINT + "ecajlggcmpeepanklgklbpdoeibmahen" }, + { "FR", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt1rAth2QEiT/A894fZPX3drYc0i2yWZ31aeR45QcvXyDa1BqEs2GUxN6AOoQtY3HDmcmn6GfBFQKywHscddDPyxRyX4nop3yzWI6An0Ykn5B37drTf34hVcte9PnC1SWNwOyfXZft1eRG/sLMkAqTPE3gwP5GItVHMAZ3pN9dxmVtjanECJ50WKo4XdwTfYYU0ws0aqyYTfYaZemYlAQvBncMVqTcNo4sEEwSCbXzjL4fiL39s0X6h1Z5jOC0hfLoG+/2x4YtvP2ULRTPlX4Ab4DOcRh4mGStrRyr2cO5LDfjiWc6uQ5fcZFo7IaFmQRB56+/WkznFlb/RXWdlYnsQIDAQAB", // NOLINT + "lcenblphbmngnohghkhpojmpflebkcpd" }, + { "GF", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxZbSsS6PWfrrDx/CQV9vGsfgkV9LXtBAavCT5iOyP0s/Is8jG0g0TJNT6MR+WKTqF2xgngmLWPijhlUnjwb3+8SVvYjTeCKYOsRdDAf2dfaGBzL4FS4H+XGZKZAxqOwihznY3MeUPgiDwddqJgy6YSm9OFYDnCUpxb51B0K1XIVWbnIo+s27N6OxxMfGdtuiqlfCs2p4ZBIl6FIIk2RR8Jp3MIRSG56Fne3LEqW1bDNGoUTg+IouxAMLDTlp6rWC2xOYV9tJMQ8hvObET1vjRNett6VyCfCScXnBNuHfM3Ykh+lYoellhn9Pd0fIw273UUT9qJyuyTAMX+uIa+vxvQIDAQAB", // NOLINT + "ipaieocdpcobgjdcpoglfjigpjamlmjl" }, + { "PF", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuh83NARn255fVMvcKpfkBWs26vpQkZ6aPK+belMHuc6TyUHmmlfeNRfaDe8M/eJyvcvjtiSpXTI/zZXpkFfqOPmuhtLqrjPlWP0OD/rNsdJN8BAyywcyLSd4bV7K/wF3o91dy7SXOzwZszUKl4JdQIQQvUSmU7sEKxzKh/WcW/Xf92xUt3cjLugRzq+r9fP/Gh82xMCtG1xNEVvAZfSMHNlxr7EmgnvIkC/hJ/Hx+vjzrcG/5b/eZdiFqyi/h1IrCEeYeOrthUg5qp5URpzwMGx/JjJYM2oSENP0+BBfTEUJtMy34DTuX1CX6gq/gQaT9TTPEUD8r2kioEmpEDcmbwIDAQAB", // NOLINT + "knhjlplmefnmdnnaohlpjemlhongoggc" }, + { "TF", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuIXKkwwfOtldLbje+8hNuk4odOy5A/wcoRW4LG4TBzYk4tyzrWrwkS3URgs7yyE3KVwJTOeAz2gmBIKPd0Sl/KNi1HTPyNLEwrBEBTXzyOM7csNYvZ3NQJrebaE5EEHb4+9yqm/6jh44ABhK3B7y9ZqjUNSiQ0uaefxbWvawC+crv7imkwHhsiXjta7oOK19KEp03jMZaCVl7qmtD6v/+ejg3kXSJuGuYwce+zqR0qEvd6QwSU5SEtSZa0SuXemQ00fu7On/do9/KVcF2EB3AVH+WYgCR64+orRJGxHsELuKtg2BHQWMS71XcYYI59Ktqzk57Uq83IVFsVR7edR+vwIDAQAB", // NOLINT + "njcglaockinpggkdikhogommihdofdlb" }, + { "GA", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8PYN0GFJ8uYBFl6Pxo+a1Vu4vuCp6s796WFOPoGWFa1RmH3qNrgv9rKRxAMiUap2fu1sxN5gyodmzN3TotgflAFME1pD0AUlbtJoLG9mZKSqgTNLnIJxfb2JLZfIQ6TIIMVfmgYOC9Mv1Mywapg9suuWzgHVl591d5WnsfIk9c5WBVsg4OjTcE0BXOKvO3PMJJwTgrJiJF2ocmJ1rKM3sSB5jvuFsiFXW1yLAYCvpNcqYWfk5890XoI1oiRv2pqJhdHIKfC+fCOR8rJ0HsjFzGnwWUhs6tRvczY/AMR6Inhr019kZtMUXSjufxWCiTRIMOPPF1KqJLDeUujEabfPhwIDAQAB", // NOLINT + "ggfglmplaojemolgcbckpgadcaljijdl" }, + { "GM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApw4dUfe8IRAdiUjHeJIR7KJMDQg+IwUbhCqch9Ds2tef3G9f/thch+FY6ANYOuv1gJibWd6KKbpyDnrWudtj+aKDXTb3REUisAKdU45uG02kyxzYlEQ88ptPBTHSTZXDCeNlMd0lvC+MKKziZ+t+y7MeWLZwBaAL4b8cmVz0cAsgN98o5lt0Ht+qUxr8Ip2a2NDNEMt++3Ch+ngb5Kk0aoapiGdr+lwnmXxUidudWJbd9GBwuXvyR+g2+wnUYAkJ6XXtprCuISogDpeIFMZco2VXbC1lV0+BNmj7I+4JqEmVq6VNszJryJ8gHPHgumIqrSRjxNYIJG/kl1JKSypFqwIDAQAB", // NOLINT + "gcddkmmiekkgdjjhckemiiagoekfpjcn" }, + { "GE", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwnXeCzWERBiBbuWPuJaE4sMktoJeRva9Tuh0ZxgF2q1WVAHwG/OJR8TOQKiVdVTRPxlTGh4N8K47K7uZyOJIl6KbqMYg8nK/BL8VQsmlOIy+vYyXIStNIpwjL5w9iRytThd2CGTCLFjubuOxzfHZcIR1lIbXuwlC2su0wA26FRiI/zN8Zs8G1TlBwrytfY74rsty4k8ahLiKJ2Px/n2pC5QQyesdydJXhRhon/bYprclntLv8BK7WDrYhqk3NFGc87I9FxEB5OA49nQo4lpbqdvF1KFnljftY0tspHmVVDGZT59HQogOK0SoTZImNOHmw6kd/XJ6JM1l2Plx6o8FvwIDAQAB", // NOLINT + "pldjcdmpjcopmodlfjplijaacnccpggc" }, + { "DE", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArAe+hjqaMo36d6BULH3a8iPNvr9kpaD7uuhiPM15xBDEp3iP4OJe+mvFKYfQbD+2H+w736mkWeyCqrZl2xOSn+zDZaoYsy3kiQUkgXD+XcJugkv42SuuPSvzKgS4s/ST8f4iID5ftB1GKg64ed7MwCWZZZqxwufE+OJImTvs1s6rPjwhqSb8wY/RG21Bk0nAYk+DHB2Aks8E3SRo474SjaiEpDYEFD8iFYVb0U9h+pPZqBl/SmPPK66sftYV8tVVLL5Ew2zq1gLeOH0+aYgRY8owIC8XjyUJ3uwDwdPOsd/sbKE4iK5lF62/enMFBzKcTuOBvz87sby9xQfEpRpqgwIDAQAB", // NOLINT + "obbokncgfcbepeipkhpdepjjoncelefj" }, + { "GH", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2eot/NOvEgbR++CxukooXA+kgc7dRDrqVUpCVByuBOeFK/YGj7MEHBfhM2uSM1GIc6HknF5ejs31FEy0lCsSOgN/Gmq82vucKtSu7sTz7oBpRFm4euPI0tqZJjC1is1kbS89LT6BSnGi1zFlAiSgapAaTtQGEt/SGVURwfm1yu3WqtwK91Co6N4oBhQJ9nRlkjXLfDEPge9wqT8+j5y72bPsvfFvMyXr51nO96DPW9EXN/44Bi9thPREpeyMnrYXzVJIsTQSSxPfhNKIskG7wdB/H+22/dN2FeFYzNngBmFnnpas+4+U8n3fEKhU88k3AXiuM3aIgwU9Tk/pm4gGQIDAQAB", // NOLINT + "ebklifmmpanffcmbfdncfeghkcofaojc" }, + { "GI", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs4C2WagOjDG+0EcJFmHPtOyl3++g6ADZ1loRPFW/Pra7eEJoX5voy2xP5Rad52xYXpGECkfSb1KbhUVjG71P4kpT830Y1OUGABwBe7e6omgpIDFovqaTFnMbLOkCgWYdv21JGrL67yxGMCFWgcXYBlg+GZ3U5pLpqoVvt89F+ZTjFRbgHE/rIC569I8ToSeiB/iSSk5okcf0gdXLLcT9Oc5zBVgsgcKrUDe26aONr9Wcj3J1hl6qnosZzgZaYixpXgDxKoLmHcp7BfihXCyXcBtS60TnkG2Bdb88A/+0IURkdlgTb99kMKrBtQi/hLGWwnOi8gNs93jFNf6cOgqS2wIDAQAB", // NOLINT + "dbecmlnlbldpfndheomkblhnlccmpgij" }, + { "GR", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArClsykiefLRs4I4uZ5qaaYSLL/UxqvTwLkMhgJfUhQt6ihHlOaYfyIwzVnTwj8JTLKSqRf5VJYZzJrVkCSvfsKSltl9JzyY9yb2MHGHp6MxJH7FVHUjTZZqp8glALCO7adf3DfmZ5lYcEOTWKoWKQ6tu51jkFniZAMDMJRGswf1Sx49Tv94hxyMq+v2epFLol3ULFHMRkJhTkS0nINWVVkFogc4ZsdKhAvztKHfnfU8HQFiF69pebTt3dTnIsEaHhldA9LqxlNVMBJLzwCeYseMI1rcLFaan204s9nM9FqZcZSAht9WtEE+f9iT22+joGzrvXBkzmnYcaJUCAAhRRQIDAQAB", // NOLINT + "nckpknljimkeefilndhgljafclhkjcfj" }, + { "GL", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs8PmutcEF6yqPXF+vQHJcnsVQV3bXLp3eY3HUQkdieBioNrn6zwp7TsiILjzUCfeoms+zXDgaPTxvxBV2aStBALVS6At7ifjYuJXNkKk6tnRi9Yf/M9oh76ifdUhJp6mgON3vjM3m0YwJdeL0+nFNYsl1y/8CwMc9uoXlbR2ak74EIfalPQF9QI3IHiKMzJo1MAo4rCWug7PJsWP72XQvWq23X73anV23sdhzuj9aPUDDly6gvt6djYO6Ve9YrXxJsCHnv8r36/zaId7qMGhUvcipFnFYAcy7cmdzOPZn4+6wCiUIVornYclqtWrGJzwToipJcPBSbU8cHQjNDvBOQIDAQAB", // NOLINT + "infokpompigfnijaonlfaghdmmaooikp" }, + { "GD", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8gV60HbR41WdR4zGbjveSLn1aTW5zW/WTFXXFlCdzRGHpjuA9kXR6Whsevlu1g7X4+v6XWBVa8SFMJeC/FfydgSSXZaHiWWPy1LaQPmnCKJpkc9pQPR7vv+MHGQtlo3F6kdnq6SpR7QEH3L9pgTzkUcUG5zFu6z0+ubcVeYCedXMrm6cvtiEAQjd89uEE23XVcch6cSOhSxpsayy7YWFrwROVTDMrr5FkTBnisWtpL3P8x6op5DEj/jxeXCAG+eETi6qhgy/sTYYzl/XPok+ppNiFufSJFc61RZdVVHGPK/lMorfvBtgeJR03KO1G8JsqZj+yY2cFtKc1qxX3H9LswIDAQAB", // NOLINT + "fbgomlmaikpnnpoiljnippknkndefljj" }, + { "GP", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx0x9itBotrcDj1/Bsz6j3tyzdpo2zMPoevJ+rMBHMCuOioDz63uioqdQzrl7vJRgHfhhjj3KvSXnErUvzqYsOEeYntsz/i8PSOi6zdkui84Roir9/6mOXn5o6xFx2NZGTuzI1fS7PQfsFYJiDABYQnsRkzLwQPwLqoqZz4iwawld79T/bor+7L9ope3VJlgWY6Yh7BoF0yMZDpcHP9F2U6IRl7DAllcHeqYKxIz50LaGE/T3Il5NEsasnlBZTBU9hyqnmN8cEfaCE8d50o1pELnRI71EU/eo5DpSvTmIpvkpMBrvgVrLwEM2oy8hH+q++dR/uow0KUVAOdmvxp15FwIDAQAB", // NOLINT + "bkdgjhgilnehdigmbhgphjgffijfomig" }, + { "GU", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3GVbgygY19/pyuo2+or37XiABp6GFLhUc4J8IowAe8sCNBtM3hK8tiNcV3rJR8HYQYaOsaw5wUOtUXTq1WQOaqjf7xSmqt3RCLKf6immiZnl0CAWxA1Rd6kyuSRqUtUfHYcKOQGtJWXMmS8G8MBqmHeyrAKvUGgPV4HjfAWgeya9297PUIEqRwQ7t/OMn5xs0dOSWAx5SDQN9S1onK6XgpwaMZcjN+OpZ+o9Koz8AbTO3f7l3omw6sbtfOzaKWBN58jWyWEn8JP51dvcoSp6f+D4jL/LIwCNUqFH+nxXqdvGOTo8G3+ILTXrTwtUfb5xlHa+NXZQCT+lMjdw8OhuewIDAQAB", // NOLINT + "bdgcdjempdmhobmkphcmffbieckpijkn" }, + { "GT", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArax16WnTgiOmDq1oy47x5Rj53L1yDjY6SuKrAltW3dVucgo8Z7bK1MvA6YpNTvDL/4G0ANBI+LLCceJyFSS4dHIhxC1XnXUZQ5JLo4q/mQl19FTAzC2BuNeLPXp7cCwtdzpQhsAFnDOWAegXp0iCXwIL75wk6MfOEM6i4XBAF+QNy2vOvU4kmd3X3j1bqTkSuobNm4HlQh4v1qRGkEQnzib4X4f/JepVT9DeJ6YQ2UoJVMc1rXwP8PCF2Yf59TsswFloUm4Dc/L4ztaoCg8gVIbfGXJiej0VkdAXp2gW2Zs47RERSu6tXRzEFGydg7XMG7gaOUimLZGcmu90TxlZmQIDAQAB", // NOLINT + "jcpjhnpjejmlociccaeacejdgccojkim" }, + { "GG", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuSHzrBz0TVVnkEwEwbu3N4Mqu480s2cLNFdD1Nz6x8Ie/9+omUcXiZtX065yprtX5QcdB6fOiFXULvVElx0Re6CJHYXOSrO9xHR0ncrqEpJFiEjWiQ5APq612aqTJV9aRJPycqmvZJVPmnVwGaGoGVb6V4GVTXphnjqVFJN07OAATKe1g42g81NJSgORY+n0nxCTk4tg7638sq7I6FfqQ+Wqi9aV8N0wj2sjpwxegYsMOq4l3z3vSzII5fQ6cCl+OVVgOw3k9JTIRS8AG1iICXYA7FrvOthFHY9FbAz20i7iqP+T7qVY8P7boynXdxs4ODDDfcizlSpJEpHRFGFi+wIDAQAB", // NOLINT + "lgcoilldbfnhohgcaojjcledkednpjjk" }, + { "GN", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsmjvMWvVb7VPFd7drippcUqjThYe0ba5bRvIwvJLK8W9e3EBUrn2xZt868M+bVhFMN1AJyNaF7voW5ndW0Zm37VTw9a5onitnlw2DcNE4OJ/F5qwM2BZlsHK92iV5aDCkMidd4zT0QUOZDWW2R/S6Jjlv7F7qjJJqXZM/NGrY26U6vxmm1T51kppynZgnOIPh+/NQiSB6AB2gPQwu80sLfGQZMZCTBkFMlLkgQ83bQi04NPVMwGU0vWIak2og7OVdMKhFJrv1XhBbiEzZnMIHxAuTuzVly/jWR+J1O9gcMkmy1/lS8396UqUQGL0xu/Q6qV6dbqKkK0L6vatsyd11QIDAQAB", // NOLINT + "omhkhfpkolckffcaofhjhmdfnaonjpje" }, + { "GW", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6JBUq/bJXwTmdic1DtbJ1XasFP79LqORAKhIpOycxrq6Ooiu+OFsiy63U2XQ22RrwynAM7V8GvVTVsaYQVnfJfI6Rapg7vng88HWWWQMtLa8IFZsT/ZQazjWVuI+QQhhJxTFLsod4jXU1H5L0Aju5Ha1IZS17/F/fV6V7ks8UgXQR8fN58fK3pzmcIlzy2rCZ1PMnZ4W+K8LrXBumZ/bDe8h43kiKKMFiZuQ6q+uOzd1CRpymtetRT74bVnYf9W5Pyri45gztbeIwF3ZHWzlyilyEPAKyeAukNpDB5EJ6XniMiByxaURA6ypNUPFQlHcn+nIH+3v6jKtRrnk+QeGDQIDAQAB", // NOLINT + "njjnffhegicgangjmjjpcfafdpjhnibd" }, + { "GY", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA79uJdZzV7MUt9ed9Ug1xLrGfhXAImJYZ3lMFa4KqyeqAxxcCrQ8ok4tKXkmUAHzHuaasYg944MvKkA5l8XlGrDSEIRwQE6HxCD5c9lzM90RSz7smnTC6+MNUnVtMu521GKyF56rlmJF3KK8KfPvkotCmtDg4Ppx5l9S3pGGajVHdV4cpWbY/uIC1hY2c5898rOdz7QDOQNtQXVpj5IM52wDrjrsUIMyYpY521d1urO07dYEKU73ARxART1y+623b7E8glEU5fE6xFA1RJ4t2aT5OPuZ8F1J8KtdftB3NPfcJo7dxx7GNr3krSLlLEISvHaReo/rNnJSIDL2GmINizQIDAQAB", // NOLINT + "bnpckhmlfcpbcadfjpimkehpdhojndaj" }, + { "HT", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy7OC8+BsqsW+XHLLbdWRXJpNlMFQfGBCEKzoHNt/ao2jR2pI1iZlUDpFZhQSFwEl1dWM/IMuTJI4DIBqTgaxEnlXtMe7p0x9aicEZR5GBMiZTJWhZ4YxsSYjfxprg3CYBfVEAHty/8uEzr5+C3Q0rDiTR2eSBRJJdABFtXSTXhVaBRyVVFW8UQ4pOKiv+g95gLT8rAOSMQTTKgv3nGQNp1EPhxj3ZxnkPpy9fAPqI6p7UlBVSOPdsm3Nv8L4QiCqsg6SW4treSMB0QpZS86ZBLbQS3YDG05aVuuhP2xqNn7w5Z2jsYBGgqnRcEzkER19VRyblJgMp6V3lXBhHz737QIDAQAB", // NOLINT + "dnkddiaaijeblfemkmapkdaofdjoaphl" }, + { "HM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQ7JMMPrKrJfOwFJr8GbmTSahaAK/Gg2VHoflkPYs9uOUUUFmPUVEADA6xQx3a0FZHLwOstMAucwkOQs4ilStLOv1t4rBYzsZcxFu+RCrR76B73XnmGwmCox6Jsaw3KyihdEloKaK4CiobqeH+D4qceX5lSajUQV/g20MX380hEyRN5h2pHz6NbA9yGERKJQOJ1oqXa02vZcUP4H5CmO1n/5tDFioI8FkHNAeRW+B0+voVpKsUj7qMl0S/yWpvajpFd6mgRh7pqSccY93zOJCvx1doGeiQa2DwTvBzE9yCGgR7+squMeHQcODlwRX3XGhgp/cRcLzDKlFKneEgYNoQIDAQAB", // NOLINT + "cdmjnfigiegleindjolmcihoibonjmme" }, + { "VA", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxwHNzmQ9Gth1yBY9SMIyhC1TeKAkAJ0NQcXLZBdUYzXTNf5EeuLQPX/BDOs8Xi0sEt+rhoWVpisPYRKekNKHixRQyJ0MkcnRISRUdmoKznAxif/miAVXvtqmyTMaFufEsYDCnzUYjpow4KaZ94KXb8vJ7PN7DlxwFNSfzr3ygOCwiHdKyT2UhkItUWUFnoLiYV77iBaFDmzozIDujGgAeElFP0Y6/AnDdSpehnCrWzVSKb9SGwrCbcjw5A1Kz6+2ND1N4kjg5StnjMLG0r1Pra/y4cSBnbgnbg1yTdoT1ahKF27bsWS9utdP5T0yzre6OqPJzb7BfS6vsmkqxAjLdQIDAQAB", // NOLINT + "lhkfbdoeodicokpodalgjhmiffoahjok" }, + { "HN", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqjf5eop6rthX0B8XAp/2shj/jxSpfw6nk0zx1/UUg41mppZfliCtZiOAuDSYGRtgNp0ncowd8l0qNmp1WG9QLyMmzMt5tqGngnEyvmrky4gqlP2WmDCVrF5jnQZU7JhjoZpgXBQ+jVdizrW4Mo/43f/tORO9hgBH/lvoGAXEwwmLanK1Vhp24oxdyu87JZLZKiTPRC02zXNTcmMbh/QbHp6KSkVMSjrUsVaAaixdQHQ8ayxaHC11EH6XvB+JP5syGmQjeM4ECGA9PRWHVl/XB3/X05K3F4QIcYhC8tGfLeFHa1PyN8rYwf2YWEQDkWT6YAeX3orx4ar4EsiwSQQUzwIDAQAB", // NOLINT + "lihgomdncagbnnhdcbmhppkcegekdanf" }, + { "HK", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuLUfgtynvgVjbKKioe5k/tswQSY6CUhRl5YZhB61x4M2CzpEonYuQPllHd71d9iXZNYGuDfaSlVB7cWYYoxEt9QkXaUC6HFOh+gIbY8wqArlIBdEMiseD5i7dgSfrCXNGZfxnHF4XDDLWQpK/McCBA3v4Y/brtmqMmTaMJfo1Q6GNqOJfGPH7xZnwdq+oJugEEnulwsKeGcd0FDXfAiUFx02j0og647pGNvhyZ+ymBWFgv3FW9ksC7kQDUoM8SacECXETSrf8UcuShd7lXzS5v0KY8PEXOfUNqHcyyt9+6J2daEKjOlhpxkV2Ryl8ox7pRymVUwJlEpW1n9Oqo8ZLQIDAQAB", // NOLINT + "pmaifhippbnnjinngjdjloefcgkijdbh" }, + { "HU", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5ELmfDmsLZifqnUsZzEhplUPP6+ozDW7FAO25zQGmanhIQpAY9J1hrWYvj1wSoz4sOI1EquHFhmkr64aKnfUEoYQ7kCQ+zUuW/Om5SuMKQrMbUHNvzUX6wZIzN7FvbJQ14JWMVJp4b18/A5N/fTtHgZGiQCj7d0i7e/COAiM4SZBh56rGotjcRzUzBsIw8WRTUAETh6RJ+/9Hu3Q8d9YEocRtTCF/nej9mckOjwY3HuYmyfDX0bVATa01nn2iu3p94wn4gzc72CowMEd8gbJgCdz5pVLoEWXom0caA5g0tpnOF1n4q74SW2PY7lujH3JanqEFPbh3HTqV8bpc34LkwIDAQAB", // NOLINT + "kppohgoddjnfnckjhoohbgnmfpclidjh" }, + { "IS", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtV+wxvtUbkXxRA+Yrid41DW88LXsK0tKftEhUvaRiM84s0MWvr2gNUzATAqrU/vK5kAsUM9HGL5dk4Ff1EsQ5W4GzTm6h5+i/deyI2ZEQA5c96K8xsnmv+RMXl7LY2p291Ysx8MJRPqc6jb7pI3X0Jz/BXNDIjPeGfphw6kmQw4D16c7n0mQerSTfUkzJer10nV7wouDllkzjjBElOtnee90apfICy/w+b2d9ONtlP0jqL32fVH/nzatGvp4XCbpDLQf2jyHMnGIlJko6jiplLbTI00/wIOK/7bichZ7cmzm/INjTTlcdh2NlsMd1kHxc11pG24UMD3z8pE+TjmxPQIDAQAB", // NOLINT + "efdhdoankffmhdackjamnaepphobncmb" }, + { "IN", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtaCstzq3rpuPlnYGKRw63qlJjHzI3a/I7tAU26Qsqr41s3S62dRQvSwmNR8c9jHG6wCStvlY4lyj3WGAF884qGjh4KFI341IJCP96br7dTqAUazfQlzAhQh3jP3PAO3qcmmYbg0nQa1oNCBp2skHTEl6J3dU4VEtujJ4F4tp0e2StjPvkqx1vqrU/MKtYv5Zhb+fSAhVIaI3OiNc2rDgqy57f7G/AqhjF6cUdwclSxYZFFgGr8l9QY93REK0nsA3+qnOGdMHXSjl8HK9nacDNmflQp1QHk83I3M2UCTnD/LpEneb4T3vKC/uIBvJzSCqxwR2PmENkS4VhrZlT/6LzQIDAQAB", // NOLINT + "ckcgdbohephpcbegllbicpadgbifppfo" }, + { "ID", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr8aaPwGDqIAXnV9Tz4P+gznQbf1AmZBim3yM5Ck8tnVx84redejQmZeiHK0i0Flps5zaNXkILSL9UsXChh8UJDnpOcTs53SwbtUsqQ8Jy+w5KVB6Xy1IXJbjtIjO/cWrh7pH6cjqkYNgZzdjFuTovC5eft2Bqf/KvKXc1t1sxKM0I+8upYV+sI0PYxvFl1ex9uGdBTQOBZpQCSI4bTH7e2SF0c1tID/MMEdK1jWy/ebA8gidgTsFofr6GYnvnklvjrOOgygm6jl1AE2CeRdhisMd70geTXVgKJAaueNd9FykcX2xDcbHzcP7m2c8tklcG2gM6sfv6FHslD/c/fGBxQIDAQAB", // NOLINT + "cpppcmbaikbaogkpaomdljdlkhmffgco" }, + { "IQ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtAwnyXrjShsgXDy2/km1Jj9pQHSIijSqbj6WUYGcPO0yhZ1KBUo/UK7XyFXc6OwA2etuMzwMnK+TH0wYWVCnU9oX1ubOVfXe5HBa9fLJ1wVHqo3DvBrDjADzcBKlpYASdfW7AR67voHrOeardlKFBIuiBaVrJvVUg+M4UB4FilJEV6CfYwc5zHz1NQEtpssCwSBUvMNVNbly0JtNk4yYTpQ6WTEsIH3JjWo2AO9cRZgdKTNAUYZvxNB5ag61RGtksxbNKAKytopeg5k9DjqpNztUgsgWCKSVyA4PIKuCddnSu2uCG5v8Mw/aj5NpKBTly6kl6S0pnh4hra9BgMUyXQIDAQAB", // NOLINT + "nfjmnceonmfeegkenjninlbdgdlcbefb" }, + { "IE", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvQ6koLYANxSSEmdV2l3hBt4MkhSxl+Sh8sDHwChQPE0RevCcg0OniixQPTpt4hbp91k773a+MLtvl1JXJP6QrbPet2WpiVkmPRopwhMstjRdR6r80pRGb8HF94uGm/kXUxzcbKbsbwkwS5lwr9Y+VpOBsvFUbLb9enLZaTYNrucXyzrliLYGkN097p+Y44KtaquNgoRnLXadePXsvbqH6z8Wck4WzMu779b+TE9kZ9fA0PKERfPt7rN7RDwF3SLD7SgX4DCMgL2s+hSw7wNfQatpm6A3SVpdzgnc4+qbImziqZJYDI1ve9/u7gSp8do4MfwBfU2Hi8O6ikdGZMKzlQIDAQAB", // NOLINT + "iobjkiknhhkhkgepehpkpogckaebmhlh" }, + { "IM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy/Mfl8qfEDVzYxLGLvXqhCwR2lJHDIMGZE8EDPwNSWNjs0SLGQ/pXVc33BUUrHviM0+7rnotk2aWSZOhCOuClb0BZtu3YeXjmOsXvQ3I7jV/jOkWacSTuSbde7Ho+QWTZfGUM5MvaURer6agXF9Btu/ssqZ2LxuUB3JcbVvrHPf/kh0zi1/pVpQVVArcNsrfKuyRRqQZWkBhlsMvLr0TaQlalaHPON9BCIIMXcU5CCPp4EzxJD0X/pPE+ATWtySFVxV+vNlqMAIXLcW8Id2v/TVzPxTA4Oe/yELvrtPL2VLcGp4A9OxrN+N5dM8brp5jfJqFiyNFZFKpQZ97x61GMwIDAQAB", // NOLINT + "memplkoamoiphbhkcijfnbgfeilemhgf" }, + { "IL", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0JDcnccO/qHPquPWij+gJ4Ed9fvaza+j9kn9+TV8c2z43ftjChfvDgxQKDc5XzvoKeydLhMGHQwKY1+GnkPO//CdeuxI/tcByLxLa7ZDfntcCU3+H6MckYQsljWbsN2nrluRpZn9eCagnhn5CulbDkdCz2cSW6IXCjyNMVGb4/D88ru9/pOK+hWzQBDMsriC15+nfqSx58FvRnhv0xmQ+jsR9uAH+X9OPf4MmAtcWA6YS/r2w0lVn4e+2Ofi0wQMlFo5zi42bTJTOWlyLKEk+Cjt1JC63oucW+V3JCvlFRi6HGI8XK8ywalfxSQbnOl0H7Qm04mz+QcmlwGMc7cYiQIDAQAB", // NOLINT + "nhjdpioohdbmgmdifcpekmfjnahnkeoe" }, + { "IT", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+EYOuUHr5iPHpBcXPzF0TPMsGSve48HpsWQ3fI4siu/aYRj9hmBKg3NqIF261ZANNnbECq/ln2TD48NQJHITkETH3bvzjjUIQ6z+IrikMLNJs3nsgAZMGdEJ/XnW/Ke/UKXLwn9QYb1nABfAzI8gBCVmuU9MMT6v0J6688bp+n3VL5+W4jEw6XYcT6Q9d9vlfNYQ8T9l7hjfPGuzEKLcClqWwhlKfIVNg/iY7nE8raOUNjjJMec0/ekcbUxD/ZQC5EV83r/+ldjYrF5yWkACjwC/zwaI/YgdDEYqIovPuB4miDHLPPAXSD5eubdfIidVVdEnD6NMH12bdGZKOwDWxwIDAQAB", // NOLINT + "kiofdgfkdiedmmaepkhahkbodobjgdnb" }, + { "JM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzsPmz1stalSE/J5TnfofwaR5UgUo6f5+uxrMMoZM4J/tOzPorvSKDH7fhH69F5a82MQwf8QG1Ob/wqHbsDECJYed2ZvbJccZycIHW6VRbo0OHepuWfx7AKAkEgcynLqwrwMqrc1WFxTY7XUTN3ihnNIVUfPRiOcjLvcSXqwUtHoPwqKUFXiKhaXahiSLx7JxelDbgeLnaLga9zpczA9fqABvSdWW3D8qLmQ00TAhbjIAh3GI1whCkeOubF8+5r9TDVCt0IbGnUwFw0PUtqDvFxpsIcJTiBsbt9UoGN05BIfEYe/4NV5OWe6BHp4TsI5SF+doGSLZ36JbQqdXY+IlKwIDAQAB", // NOLINT + "mgnoiojfoaflegbbofehcakchbhoggba" }, + { "JP", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0/joa4B/GP1+rqkOjF6Ofnjv7wqcsikRLJlAL2/m/C4td1/9xqQh2vswr64f76YCtqGE7FebkHCu+6Mntx59JSXish4FXuJD2jjyrB85lvGU/KE5NoU65BJVc9M8CBXhNrNNVYNpfYxMun06q6qJwd6d4yylvdU5DZYFS1vHHjJXGJUv3xyvnY2eNQ1Y7Nh9iyhiaK0rolX8ROFEUjkXByE+f5JrxBFpHSuCEGoycyWAYWbcilEk8w+Pvl8X6Kh8RPfd4ioDkENXfrrkp0GyjC37LT0+nfRIgEhY5OtF1nBqXlVxqO327rD8d2eHtEq40eyGF4EmAfZjgbQvtVvwowIDAQAB", // NOLINT + "ffopfgphnhgdkbnogedcfofdpfghgfbp" }, + { "JE", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvs3B4nVxWwefVjiGqIfmhOB5HUyZB1AODIGQKDqLMt3m/wz4nj2beZrwAfJMYb5Uio009rW7kDgy/JHHX2uS1NywFQ+SMV3gjugtz58lypfZUC3n96yqXOro5JrUbItptnUTmL2UzCLZcKlcwJytJgMx2Z3z/RJfKh4qry/zt2HTCp6FJjzngOAol1kG9Tuq+11JKzOGj/zTQPMHLMUH0RRUjQE1fxEY7ornIYjSldcXQu8t3LPNCS+YWL738IKG58K93MjtdQFJvw7DlB1IYZeAxWzh0NwecUZKtxBiVWyfJ8LAesqt8yamtlIwYCVSP2dTBL4wt+PPeW2rkRIaewIDAQAB", // NOLINT + "ecpfbmamfglkigljekblgafemnpeaoil" }, + { "JO", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1p0xntOfgC1BgOdgAEHo7eCG7+BP7PR+WSuqWh9ZjMVq7sK2N3B9MjwhQsndz5Iza+8COqTOnm+a7ESkBo3u7Oq0YIZkl5h5FTkituItfAS9OChRSeysQTbRelgjSufPBW4EN0zX+L5FrQWdr3Jr4xzhjjvI9e2C+R2ZP6JrlR3lsJn4sAzK34k19uhl51XzvlPasIz8/P2YA+3mJL3ITLrwjVxLfE7Y5D73EkTCo1cgm0z6CCUwA8jtrmPs0lRYnuTbDw5ZdPYhEGYWB/YHGpkHyPWAOuVdRA5if4IOf10AQ09Y+nXwFZZH3uD+0fw7FziymBR77aRf+t5TftVVcQIDAQAB", // NOLINT + "bfoadhpfgblolihgcpcgjkpffmdchiia" }, + { "KZ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvPyBO1IHCvs7aMeRmltHkV5JNVSn3gCRmezjt9NGzW2DEwqKsY3M6x+mQlyGH+GefN0+Fj3qQzRdnHpAxp3yDtd7YZCLgtchagb013RfEaE7ijYxgyrSPkgVfJxIYsCWdjnIruZHHbij02DE4iHQvMQR9nMsRQ2M3+xEkd1GkEkGvGk6XNh4eqpxatb07oSvZLsV0g8EKVaRBqq4NHMe2B2wzKNcP6MdevX2kC5MY2alrsLPuw0kvv7Mm+8RVGNGoke381hTDRXy2RDQ8A+8qD+aj8xR3TIyO0Jr64ySRzln5eK7yCUNEDmAPSLEpQv5rJsI05lnjAhyDgvh96R2iwIDAQAB", // NOLINT + "lfijadjooejmalmlgbhgjpcmmifcpedh" }, + { "KE", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsF9BZNhlRpOsSAKNBdsUi/o9c0lfvWEf5PBPl8vL3CSN7N+JGSMgfxHvqwJY1W0YPGjYml4ebXp6zVQtz0JRIpzqkn3v8RwCCKUqxYCAIasx6e4jVJKpwvgCHN5S6lyDgnmaw1UKvFs9H+67gYS9PwuPk63IqM9xUhADOIPdlV2npBYUU/Ei1xNIqcYLTm87eyQXEkuy3CxNSmxCznyhwiV739qR+zuYMzSNZrAAlMnIbVanagz/D/3ooWwTU786B/qYAvObQV/FejKVilAtczqaqPyL17h0aDy3aleQEvNT0fChFD7JEcCgGaQpIoem70VT9qm5HkUzq9SOIX7moQIDAQAB", // NOLINT + "aldljpemobhmeaaigaaiacepmbgfpdin" }, + { "KI", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnXRpju2X6RGTrYGUMdUxrl3eBsniZOFDce9Cfsn0+EHR4o1I4WoUnEkhHOycB/jX3/Nwq8INjEtU2zJ4ehcIcdupH7dvS1BiF53jIkcdo1F9f2sBB3hXD0OEaGfvz/I6jKeLnC2ncn3XFle85ImzrNbCh9bfhaQ4sJh6ScF3XsbPjzNuqg85/4dkeokH+9m6wfcypTe88meHuVwUk3AG0R94nFeiwmysJUJ/9qgLSoP+lkuVZcku+9/gocyb/zJn3ioohzhvA8cOoeZlhZxxCwjoEpfyZspvlT6wOfJF8I5RdgjuWlw55a2sWYiAyttFG/peVGLoiV4lK/Ix/yNGwQIDAQAB", // NOLINT + "aogdimippgnokgjljimifnjmmaballal" }, + { "KR", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3aGJo6Nkha+dDrov29LLHtMmkp5oshy8jHP24wgDG3cZKK7F4hItip1EFlWqdAPg44OUdmHIeZS2igrL6+5RvkHUKJ9mYy2ay/7L1+9j7BtF8lH7mf/L69+Sxlu1E1LfwoSMAzSf/HV+zX5wjZeVDjY2SlbyMLPKZ8uboGLrIJLDtwwDoCSpM0utd7sLhaAqGzkoL+5FvMatQUupsKjUq1Qnt11SU+TUb1ASUqf+ka11gN5Bo0dtnoP0MYo++YZC6W03WiYJ4a2z1Cu2iQoHzyEeUxHLy8UAGHD/GKgjKgIez0MNF9uMpNVP3IC4zIW6/BYa4SPxpKmNstEH/YXcgQIDAQAB", // NOLINT + "hlkoknhaanajaeaalmcabkidlfhdpdbn" }, + { "KW", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA47Wlw3UADhkliRhphAUImuAkdOCB3tbkWjih+fkCijmqRPydeADuiFgk4Q4PkldTMMmEXaLWUtIqf6u9dxf5yUz2EuyEk2rKkhwNO9WU1vUWmg76xS31Xn75hYG5eRCK9VOix3GaVNjZeukiYGJgOFwjvzixlcRwvsC/nlwzohTmFNcpSXU9geJBmqo099riHyfYjCeOEVOMk4+tUQEuKvd2/hxkELWTcUp79kjYqb+DmRxv4dzn/mw7CzcDFv9VO0lJ4bcUL/a913A7Gd7752OG7DlQWWK+MPsyX/l4qyjloqOHHCBY0YJqtHks/o9I2RDRVOylwux+0mzSZsX4JwIDAQAB", // NOLINT + "gllhkboalgpfglciadiojcieipacgeio" }, + { "KG", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2S1ZV6PcDK1nSC0fEimOs2RynVLg9jW/q8ZgNunAOcY0mA4HGEwIsnEyJyMd7vd/g9KPRR/BF8Mxg3YgG15gw/qSZ6pHQ1H7ebFyAZRD5R3l20WOPc7RW/UXhVKuw93voQhMT4OSkxzYViSJ6KRlAkNuVghQzFAV+joL1p/gsh6x8deAzW0kkRMCTf5DTKfTSJBH3px6oCZ3NAvvGOcFDbkK5uGu2BBqOJlC6As6w5uRQOXQzVm9PWa1kJSqvAjEb1HlqjsxXlrjjuVztLTAhreh6GqIdkdQSEuUZ0uSfuOYfaKuOv8CTub9Dsr5D8dqWM4FlJ7KmzXoRJ1TiHrp3wIDAQAB", // NOLINT + "aofncdnopompimdieabhooeabedfidmp" }, + { "LA", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxYgEnbM9Wqgj5lhq9dyFP0lUtRC0kat7mMdlNbzg6fVuinO93K83eNuPKxsXQtcjRItf53COnBp0bFen4pFuR4c4QlWy5uDrppuewAqcazdFY0Ss0W+hscbxfCPuUAPdSbADNj6h5CM4jdUgj8E0ScgfBhzY/ilDaxqu6Jl+ZguBYgZfKLZir5w0cTt6SDnKNSJSY0JDO4InansXAWSeDUq++psCTlhCb7UY+LI7Ii4g660uTg84kFo5IqSB5OLfm4qfeMvD5cuaLrVrgSRyx1Ok4dKq8V3/gEz5BS1Hc6GH3VGbWNVq8jFvFKneWIO3bGg7fLpCispRI5uybUNq4QIDAQAB", // NOLINT + "lgfjlomakojfdgefhhcjkbhhalihedik" }, + { "LV", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXv+YpB9VGhh9NoiuJJBGnxMX24Ss3TMaLBzxge1To8e0sPzwyvdwO2WS9ozm4VtSyOYhLhWpxXdRpN8gv8TOPnpz0a2qlWtrPwTeeEHAvNl8XtvmuJ55OUURKXZ6+E6qTokRfUb4GsiWxJJRg7hOOrSmGEvPTbTeXkkYs+C0hbpttFvnucawXpms6/NSepOZ1hUjzOShNSGIVkP/5OsYJitmLhRZf5/1h5riRX2bVmHzuiLeJJa6rXrfXwWBWC9Qzp0ExPmRFoBhlOriX4Yg8d62GmuAslKhh2zxYzqv9JXdSH30lfFwtZ8RiPFr65L2bgteSkWk3Ju0HMMXi2ZfQIDAQAB", // NOLINT + "fnmpofdfahkchdnijkpbejnjicncnmff" }, + { "LB", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtlzkY+L1kQKookptQBmwzSSn3p/E2EhPf12CVP/vlGMYyE3e8LmS/mYmK+qsOB2+kMWOPcLm4oBefpu2murJ6Heyo9NvqmgtfLQ1KMXXJoNBxhCRfm8OEZJfUYk/Pyp3Afx99iRXr5afTKy1viEp8BEuHg/7s+WqZdkVYy+ncFVNV/mwZ0m5NE+m7GXSlrUaXK1sb7No2fFEdBIY1TcVYXKymbplTC+bZctl4a6WoF9bExIPpmqiTnyL2yyqQpTzlTTeFe5Ar2wCDqotf+V3BbSLVG/FqCz9tq52IjqVY+531O72iQIbYmeocPXvv4Tv/ATkaaRqDUGh0pcqXagdrQIDAQAB", // NOLINT + "cmjipljcodnfmfchfoajjiheddoejkla" }, + { "LS", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu2zulBj3R+QeqjmGughH+YfafyxqfiG4D1gVj3yFHrSCfuLUJdjMrBILcdK8EXWU0dziVskQONfMFL+OtRJznNEugVTt1cbNFS1EmMooZzLHeClAQoJyLbnU2ThCttLY7ibnoFDwPo1xyVqGcmGFEGYWVqrrOaQ6T/n7FvwXVZp41fFzMLnu5aiLyjLRHh07ZhzR1zfXWuxotlTsn0uwn5H5dhhOjCTF+QCbgcZ/dL7PypN3UO1/Et7Z1dWCjAKG56N8vBdqSZ+bD2kLV3TrkjApBxkyrGVRWrIGce/qSsmIwg454iA3aKLuuQVmekspdFmDTbp1s9IZN2eFWumXcQIDAQAB", // NOLINT + "ekjdoohmhblddcdpekahlclohllhgbib" }, + { "LR", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmky0a9tFiFYGcS7WsPUjHC9RPjd4MkU7cq3cm83o1QzEltSF7T/cpELBmWy7xay4+MbcmksKKyNNMLbHAotjrFs7AWSmkyUNWBCLor+8Q1euLlBCiqoNqg/F35sMlRtIBcr0YNtrGO0ux9acaQfFmuj/YjqatoY1SSSv05lIsKvcHszSnEOYpmQiU8wuPQjtM36PDTt7VW9vgybyGIIGJ6KsK17YtDDBW93iu/AhzRb0A2v37sgRniev25vSTZ2bGhJhjtGP8qzg85nv5U0b1H3QDmbZa56iEDNOk2Ql8bLO19nK9cKYZzqRTOiG6KQhAmLnkDSovR/txY3WCJKGQwIDAQAB", // NOLINT + "gjjbolaeaegppccijdcgnafnipnjdpag" }, + { "LY", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy3PXgalRr9QxMdOcxtfTQade+M2OOYBSVueFlnEphlKR66gw8Mg214xhyMltGkJ/y0RkB1HSLCAXY1McNSsCHuB4XByCwqhmOssR8OHR+ol1nO034KRXSjLCP1vKu/8ato/aLFjGsUAbDNuntFBUBxOM9Cse79baen7w8BggIlqD4+z3wfgd525QDG2PO24Pqq5uwVy6NTB+k8OZK0xw21n6EKNpyT8HkzS/pzIsSo3RXWcUoM7PRLA3XHH3VBTDcFFROH1Ij7/c0NZjP5BZT1943ELSNNPIoaI5bUXOyfJiZkMn2Ys+3uYYEsnoMJQM0Bip+PwxM9c/nf2qA1/yQQIDAQAB", // NOLINT + "dkgigidnndmjldnjfifcbngknneihmfb" }, + { "LI", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv9c9TrCbargg98fJFyGgY4HylOHgzn0SROh01SkMHuT2DrRA4GLTa0K/2vIDtZSibXBH+qQM6H2mr60jhsdkF0UyoiZIsMltvSDkRVEC5yDetOxyWO1xdy3O+4vMc/Bweb5e52ENaxHV+ivayMjj6ynPCCVwhhKwk9vHO4M06/ADoH1+pUU4lDsAazWmpIjzH+pkg/8b96RcMTlD4kI1AkGhdcP/NtUEFZ3YlXF6GrkfRwwYNJ8EBP1ALe0ZEI5h1FPk0jVs4ePtB34CG4mno8rkZGDu/aB+vsujL2Wj52woz5+tBQ6flJZGUhKBwd8PCgYUYGh+sZPxFq7FuHSdFQIDAQAB", // NOLINT + "cnenckljacblcgomenkaifohoghpbbmf" }, + { "LT", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2T43Jvnc/8yiWrN0PjoGnRkZ8IjmPxSFhsejI87sOXFDw/c9bwwjKMuko0nibq7KFz4t7PBTPyOKAT2Uza/GSel76XFBnaPHJ4LVEPSEFlSnvVccPwRfROoVKPEPdWBZJ5+4nbQ7OqD9WhzeDZiRad/G3UIVwjpj4/RiwZxnUExihGjYiTm6wyil/cJkZEoXMIpTrhytZEekfmjZHvqdgFXtLqcKXaGX1nRkxxMYpKTeeBG9UILoNVJxVoXmip6MN9+RG2/fRPbEaEL4ej3D0vitiNIDwCoEYvjNRmgn7N2teytjaLnLB++2X3zukKyUqcTNkvQ3VF4UaaBNwDYd4wIDAQAB", // NOLINT + "peigikhkkjnlhlpbangknejbdpkgoaga" }, + { "LU", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvtmIKQK9ghyv09/tS6yCVaVAiNL0R6ohb2VH9uKwi0emNeaK+WF7T8itnxlPuv/NGrjXEFPNX3ZK5+AC7gfOFpi5Vseoi9EZ1ldJR5XDelWBHB110Fvtjv5oCKA85HicmApF/VdIJ+402fNypdDV/F9+29q30KDNgsnnygGZCypJu2/eVQq2BNAZcBSQKvzum4I98gtq8ZSshhr3hL3QH3I1iAt7/XnA79MJjVTz50AgWdkBk5PlemHvn07WDtsBnVS3oFvqkjZdnGWSpn8iWjHp9jbkhk2x3wuRCjW+Vwdzsp9eBD4PirAGPCH33E4DKPNP8+DWoYs3tyUu35l0UwIDAQAB", // NOLINT + "kchhmgajflbkfplcohkgefoaldfdkcnd" }, + { "MO", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu9Qbo7MNaLfOr6FJzu33I4c2ciCecgdHEYe+/NtRfzHn9br1OrTgE++0tfG6i1fGqST8KCDpPBYaQ7bSvvRqVlURjOd7yvqmCXdSwXyK5bzCL8FC1pphu2jVsLx59SrpRwawbqnTw4lVNUR5LZozk5aENO8Wi5A4o5TeRjMlP/6GQ34f1BpJl1kF4tmFDCvklRpE98AlU4ARpq8Koqa8DFYwCWm2YbhsQe659lxuqBIIgQq8+crUIptx+ksyxZmQ5z58SvZUSsJCEPX8TvGiONJm6Sf9GTAGI3ZPAdFd/lf/PAGavJLXVHoraNZfC4Z1XjGfm1eRFYJyCPI0Mfon3QIDAQAB", // NOLINT + "defghfpoioebbafmpihnapbphmfdppbp" }, + { "MK", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxaO8vJN8XjAHDEclyl2Ig8O14Nt2Vk+Uk0ztIPKvtu6Oa+VrwYDu8BJ0dEuxW59ZkIc5PDCXra7hf0pR3ACoG+Z/go+FVx2aVugR2iepIzt6HglMVoyGc2nmNuetPqRZjHiPzeP15biMLKlBDirs91ocbcB6OIzKjMCPyhII2d7jmBUnCUgRxkXGcaWieAOWxzDYAzrnsEADpU2An47s4YLLzGZxa5rFKa09pFl+dYaXY0n3zEO24yGoBHiJNgxZpMHy7vk4bcF53C3Bwnr5vUs1QFxDL2wC3crP52CEXsT1I7Dth+CDtIsSW5bA02Cx0UAugebNo//b+9s3qx4MVwIDAQAB", // NOLINT + "omgioecjmfcoebandkefmidmjmgmplcf" }, + { "MG", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzccBqyJzPATNGVRIXPcnPAGxSWMzA3Caz6+6NjkprqO3qz0TJxde6FVtxQqXhfWuOS+KEZxuG3fXZFoQ/A/qBrHR6xSPsGJKv/l94ZrLzm477qXpEfNbvbtAzNeov1ALwpxk71M5P2MOAiUkfKrufaQktN5f3eDzrB3fWiZ0uIEWsSBRfl00xsu6KUGrnDQzKDsvJHZHfVGBfpbXk2KQotVPoqE3YaSgxmThinu3knqn9aO4th5lNtQsgElo1eWl3Z8L2ubqgJAgR8ZPAxkrzyz9bTFc2ia3EQIdntLKi+SkbYDyd2aIviPoHVjUP/5a7ggpkMc77DV5g7l5lisg8QIDAQAB", // NOLINT + "kbcoekpbchmgnlkgnofbbmkbkhkmedcn" }, + { "MW", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8BLL14RIC0z3eHNFSg1MiPESD+h8ZVr9+xnK6va9Bks2634lkmrsMy2o+RV/tE/KXE1mV50YybtoFL7FFNwEaQKs7sONMKfoMd6sZeiTXFXTe+nCR7gzNRGntKkyxXE7uOCxt09oLKh2J7blcTf2X/aokeenbMi7lz0BRnD6sl1O+3ElXN9FgkHbeF6sTdJeCSETswedzPh0JNMAFuRHGDeX/+99IxLq4jWk2z5Opno4c0zw28ejxR7VeQl+IcS5KTevaPn0qhzTCh92Hci+3VpvmIhQAXpIAchR9I1hELWpoPyEQNUQArTVrYAv8IMf0J4efAbNt+aLUufLbVStkQIDAQAB", // NOLINT + "kcmliiocpdnpmgpaiajphclfkobkhclg" }, + { "MY", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoVcA7bxA2TzX90rBzWSYoIlOXYuQXlR8aUjAnM4U2Bq9y5RygvuxYWMFyfUp3Xb8B/qwJ6LPdm3vIBNc2uH+DT4My+/Jxztu5L71XND/SqBevxHSnQ7oa5Ti/HIqn4vgxHvl+hFkXcsTJAd2xz8wKhD4sQ67HP+VTfVk5O2rYIDbgmNMmf3fxapTLSHhylmjtI28KBmlElA7H2jru8Nb/8E1pcEYxs9nnQi4qlrsYKkZAySRhWq+FqCCxROhSNiBH3gtR+GsTW3Mos+8Dasuq0+zWGr4E7r5qeltkhRpg5KmITIiY50eApR3LQ3dDUHjYMi/eNOeqnoaWKb+RkyBeQIDAQAB", // NOLINT + "ahlfjnbdchljfacofcmdbkkhjljomeok" }, + { "MV", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3bC7ShtntESqi2769ScLgDeVYWGpyi3pbq8VBGAE1VxhZjdj/uuqnLSaKC67Qe8DWDx8E7qg3TnI2Ur5MZ6tsHO4Xq0Tr8iKINmielmbIKl+lfD9opptHNi8VLRwVi9B1YydL3m8wkl0NKdND/8EkhyW9Sp7LsaTUsi9Bp3jVVzFmSHh5fx9PibzycqzYGuTGyw25oLpKjSkDdpnXAdCVhBVmBuncMSsNG/WkEZR/z/gQO2D1Phi0hr9oLfbsuubnA5bmzb1HK6SMsmA+vrCeXcNL50KPAdD8/obLGYvNnKBzo89qf3QVdl03Tdqo505bs4gQnrpWfdAnEdR/UQotQIDAQAB", // NOLINT + "nedaocfgaldbnnaffpdhoeoamninnmom" }, + { "ML", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzwkyTd9dw+SW74ng0knECws8zu05RMD3U3DsT6LXWANseBynoyrJ8Rm7VKSjeqGS180AIzjsHLsD+63MWF/NSoHyLIFAKiHQC4BuMiQixxflcCM9tWJ1z/5eSvz6IlFObIzWPrBz5I8fOPeoWYVXtjNohjDH/T7w6Ni29XVOYvZM4EsIaqIAiEhvClA5z78pbzaTx/cAUpQAsSa8tcFW3UjucbxuHWlZy4+ibma0lrP8V1uXyJA+EupobMAR5TKF3HD1aTRJq7/gZ5lZpQ94bGtwOpg0uakPjhUf7TDS9LKBitdoKESxHcNPWR8oiQWofxc5IpDKk3cWTiPf8sVo2QIDAQAB", // NOLINT + "felpjmpmfcfhkdmmboignojkjhlmlcoh" }, + { "MT", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtpHf9BwNt1GVSWK13nbzFj2y0wQtp3RXVfaMdB/ymhVU7ghB9lkGAzf8J7l33hTb7A2kjz3QFYXH1XKH9WCtv/fXlB9S/YTZeNjfh1ep/1hZtTGwZd4+w99KE4OgbpgAHOYMSzUz3zLahFkTWP9c43i3G4oRz3kdvl1OnZ7K94nhY/9UZNxQalF8fWLuULZzg7A+qq0UqgeoAZqCfL696Ihk3NWFBKOtWtVHopi/+DmiPReSf0LPTVdEJV+uYyazbOlmczaUhDgSXM3DRT8GHGPJujrOEUfShb8BsOJN3NY39946r15JDQdtCEbcok3P+ACiyVDvy1L1PiDW8ttC+wIDAQAB", // NOLINT + "pimkphemgcgfhebibmeeodlpimapojja" }, + { "MH", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApn5Lv44ly8lQIkNAJQSDVvLF9ks0AjCJ+21Ml62QUskqlKawCA7gm3jldB+40IPdH5YkRaNZjDK/oXj3PsXYvdnXAGIpObMeYtx8YzXMXI3lrHxn9LIyRBPlMofVqfWyjE+qjgD0tdN9nLor5Pp2IFsQUwsO7hRqJnCgKJHzcg3o4DP2QIzU32U6NpYnLVFu2KJPx/7tr9wl31Q7dJXbr3TIQzJEVl3TTiu3hLg6j99WQhflKAVDiaWXfP1UQCAfUBYJXaD7PlIyNPEw6L8yyzXjlTtUQmlyb62NzHT72yHFx96RipfchNs1Oz+9oXLVfFJhH+3/0k+Ivwo+UeilowIDAQAB", // NOLINT + "nnnlomkdpodgnlelbdpdnknldmailfgk" }, + { "MQ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtuyjNCFhyE7yypervTB6BrrLJ49N5631Fuz1NCiEquDXs24bHNGbsfJeNnWxPPpF4ipTu/uSopNaVkmcPmxECMny7I4vXbxg9ND/k1nn3FvBUZxFNbOIVzr0sCGD/WcLFqklbF24z/h7d0Ic5rMdQ0AzDJ8/ZYcgLiIvaPwxbjL+nzFquTM9+ynTu7oWWOjFbFx/E+VZkZq962a6/+Nk/UEAi7yd2Kcs+UaC33Lgu3qXTn6WkLPWZvhjqX3VyUWLgMrRBWpAJO+rJpwfCfZXjk5NHIRS+soCi+MF48AmfrIHSW+2/wbkYG/xWpCWXlbJorqrZSX4tdCvk5l5JdlvEwIDAQAB", // NOLINT + "mpnkooglgldlpjkmdlfoalbbmmafaijb" }, + { "MR", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvS5HcNElZN98Lmu5BXYxbXxoTo+luSdC59q6awtAUsFvlayhYUZaXTp0jZV6SV4XhD3qhG4raE2O+tyA7vjJ/1LjlDxYJ5yz4Pp58CpEbcm5Z/Pyd48LgqkC481oLQgQO4GlYYZ+hkzXMcRBVcoQVqQe42f2g0Eu1GLYQa0WTnADRunrXiXcFJyGBfhhK8deEYSLBdhuEAk3ZlfRN0lwrwtZ6aLAynsMdlocq12Z5aZYlGeMGsR8SZ9o/98DtXis4x3Q7f+iI2lpQsfm6xnLyXUFxVq0QUyuZW0Uf3jOhyGQiXYz5UDc0TIsDP8s3yQGIOh4H6pHS6NGVe+BFXgbawIDAQAB", // NOLINT + "ojdeldgejmhpljngfbpnchooppmjanaj" }, + { "MU", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtaw1u7Qm9TVpf/FvQhW3qv6Hy3wCfsXR0qVjzdwIBqmOqnOsGlM+uEyrWmr/dKHxdZ3daEqLSZLSdIDlYdkPKMzayFeWu+V+BRWLlc9JmMW/+6VWjGJZ/JYL293plEQkfZ71f+UHHYStS6bY4bPWKs+FSH/I+qrJtQoJYGbSaJd/CShfQNXcUtsKV9Fk0OITNNgnRnh0J1G+RN+ulC+UvpZRxV4mRB9GN/y3WCuUa/Rk9ES77zS6IBomI9AjtxSl/W2k5ZTR6xPQaN3FWTi1SXKzHpkyhrvlIC5WHaP1eLtX1MnFZ38JLXSGkkoMWW8opumJk6IwbIOgEIPbGuerdQIDAQAB", // NOLINT + "aaglapnbdlbaikjcomenbehmbefnionb" }, + { "YT", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2MBISNKhVti1Yvxfg4dyMk30IUGTJ4pRQ7jKb9GVw4VGi+fe8UQ/hQVEBOItCBmrckdtr57naDdtFp/OHgKdtXl+oXIqRsQWe/dylVRbDPjaJXkPiyzitd9DGqyudBamteM7xAl55g6ldM8IOUomtoOopmB5WkeOd+zxr7SmNJvLkoAdIQNYgFZIl6IiTqMHS1fygxiMAJXrn9iDYmjIFNkk3ALgFbLKOdsClNJPhSHOSa3QYvqcSZp7bLubTJ2CibIzcl51RI6WGNa4WkbksLxLDeycnKofoP9lCZEBeomujHJWobXRjooHbP7jR8lrwwkavSW3PXBeBmGg5lBljwIDAQAB", // NOLINT + "pagdedoelfjanmfejmpbhegikifljkbd" }, + { "MX", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoUe16heEvY66QdbrmkAOi9jPPUwIMjnQJaN14mK0LYpY7ir8F2PsBkxEkn0l2u1zaPDZ9sQMvU2tLRrAidnW/bcsIpWJgMzA9ILBSpWDRWAfKu54W226M2ifnKzm9R3+GKpSV/VX46RMNFBYDrBypMrDhl/j8LKTrg7d1cgQoBRqGeO7Hj6diMdUpzfibpBogMY+pYI55P7dLuM+Gha2Gr1sxXNY7QDw/kIxwGof0aFtTHfmIqE1Fb9c2uFiFXPk/tSOad76iK5lfJwnCINw9FH0DW20t0CtSrkKgEBtkBXCE5DaxrgnfEjVFkN/dxwzynYzO8FYWDgBcqevkw9ZlQIDAQAB", // NOLINT + "fcggndnjiecfkiomngolonakcmagfomn" }, + { "FM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxKWgiJkXaNaicyJIiaxTaF9oHTTG6YDgs2GQmSx9JraNcUlc9fUSAbkCWOFBRgllc0je8LIUrRv/IHic4ahceR3Xpu6kCxNNVwA27X4QPlnybPvfe0HUAQ31g2F6jgdGL3rMfluPb+gd4ru49nNMr/ppsG3mfyyM5j5SedhupFjRBrZ8UoKL6Oqn7nJvfuPld9+nQxwhSH3dpAAju6dW+x30TqSkYcUTv7wBhXbwoQ+oauIHTMDTWGBXRmsWJTLPcPhHEYWrPQneeoEoH4ZWBREPYcPKRAwt/tuvAC2fx8Gea1/YjQdg6uMYHPSMSZ3V7x7gpGvJtqhwvX3JEIGanQIDAQAB", // NOLINT + "jaihoejfkgkcnnejhbekdppkebhagijh" }, + { "MD", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA99xQQBlKV53susLNc/p1dLdrrI7SntLQHK52X2EqonSommV5D+C7X9MAw8tZn+fCrauhL8adx7Izzc4tUqdUPlSqffcR4lAc/8zeo/kXiKnKRRJuDDNVnDcsOUJrnjWNV6RNXdPbNA43IMYn69ZpRBNHEZXT0FhRU6ELXRGmYbAVN7pBMUWwhif3AUbgUcHpgnq5G87UoYIyGO6w8P/+gJqM95bafJj4sMfXvTlh1rH6607RrGz9sii4mS8YWW5of9pH+Iua7rGjTibc4yLn334ugABqpdng9jhHIov4Kngnezn8tdG2BUHttLYQYVAFLtANjHjydq/fE1z2gaWLqQIDAQAB", // NOLINT + "fmnhfebikmmbpnhlmcpkkjgbppdpbjea" }, + { "MC", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArbqiodjePTDTf/iN5e+j9735E5orW3lTV5bTp2hsd9jVFz/k/ZVGx3F68lEfefYRK20fNGQu4oEscyLsxtWzEOfZdA0EDaMNVNt+U2WMOZA/HHZZflhdBrBxSPLvy0MBwRVkR+sbbpBUHOYDKt62xR4UtJ7u+8aAAj3UfbDIztbS09VMMrlxsY4as2X64L1ipqkwvj5Ftl7v+Rg8LY/QXQ3xVtlvz9vLSHyrQGwRckbSVysTStglDfMq7wMFcSDnK2X1/H71jhtMGLHqUGKWERZOtRAeuwCuRFX84RcSWYCzd47Mf4KQvoGhGc46/NXl6mFG/Gqs6aHrWmSMnMFayQIDAQAB", // NOLINT + "oofpepdfilfongncajkacijcdemjidnn" }, + { "MN", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzTi1tQFrwcNBxI4SNKyPBJmEsFiC6C4tjEcNIcaxItV7qXvRhlDOTg4Oo2AfOvlAkp8zvEQ4hl43irDudwf8rBfUG6xyJarOAJyI4fMAPoUfJSJbnT7896gUJLQtwHPwhxOdBjIOcEJgcp2rJeceB01Z3DXQYObByYkKz7STFK4BHJVR8PfJ/y9Pq/NicLdOaf7aOjy8ITkAvZL0BvxTYo7vquzo19pLqs3yiWdIqN7/Wh7QJVKGUFaqyZIgM8mN1O4uPLRVBt6s2ABezklnR4Cb19e+TJMpFOp+Gx1YnMjoBHZF2w6EDiISKnrViUC3bQxVR+dvvCr+XHLN/NSn5QIDAQAB", // NOLINT + "ppjldojmggfbnigkceidehpdomfambfn" }, + { "ME", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr9lNz9Y6ILmQC5+nsTNsxyr6qKhSQFFVyCEO+GVUZehPgvrw2GqWVaeVXtiDZvKIROiXZAxWfX2nILxV2iwyw7KmbqstrEfFG1yUSUjg8qejGV0O6KVKfKKNKu5sbXAoY7f5X1AorFCEEYdGB4+1rr0ix6cF63ehTmxgXa08snuykYzlmTIgq/ewbxESECGyxBPeUok16kHwMfO5sWHSdnXFGUAmEERdx9+CtzPzVZEkPR00vhcQl/d6sQNRUqkE2liofyx01tM5EJCiqiloCIqZkffdVVEqm3nIjI28X6B9nMEquzGcMODv07c/GPS2BWjRaFeBGTW+o3zhANS8qQIDAQAB", // NOLINT + "ecjlpaeejgdhfjgcfbfdmdiechfppdnn" }, + { "MS", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAstFZDnAJce7aoCCLdSM3aDKmFKEUK9Y/bDGMih02lMlkwUnFs5FdvOLBM6HCcoM75cDBrLzj1uSU4y4BhXl4KWyz5YKn10IFUgiz4FHtp9Inf1lM7jHSnuB9kRIR6PuYQVAOZFx4U6LijNqzxDSacFL+Ng8fYXQsTyvO3v5t1rs1NQ3boE/8AldsRkpq+mcC2EYTBKW90qDt79RVwhvQgle2kjG8S5n2TBtHqq9d2+gbI2jBue/MB5sMX2dOQhINir/Awf66YtxWahP6icM0V38QkevmiZW9YWp3CsShoV6eoaSZlnf0LVBuw2rN1u+ebM52xO6TNLQJW7epR1fLTQIDAQAB", // NOLINT + "bdimjgdfkgmacllplaoopenflakflopc" }, + { "MA", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwmogfBKEWynYxFNFAf5wv8IwqZYXmHHKAJUWn77X8/a6eLk54mrop/quqeQ7FgRZRzV/gTOoGBqeUytmPdeYBJrm56q+rQ3cw60t77UC+NHExT+B62oQ0gjyhI1rz/HJg95DcI7UWP5Er/mc//USoC+XUoIChWvGYrT4twCsnRr6vru4Lpz5vWd2bryNLetNbJkLZsl67KcnOhUKT+/XSRDenSXdLpqrbA+G6QYpjV0iO1z+ET3/egzrVMxnDspSr/0hEsIURLKwVeNQcWt9Zrx1J4+Py1pIMZu69tsNdMextZZTbNhIny5YAZg4hNMZFRd6vujgqJU+lr/EWVDbZwIDAQAB", // NOLINT + "jccnodpeafnigonfoeokpkfcapjjgfkg" }, + { "MZ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlayyIW/ZbnRlVPb5Q9QEdpeaGCiClC6vKAdgctm+QyVQ/WcCvU1auSpOadwRj8XcAN0YL8Idca8aeWqv2mSfCGAhEQu3jtZp5AORXdiijld5RkrJq1XHFoVSFk12x6NLHD2mw0dVwTSq0UHLpdGrXmU13gK3Lv8yGEGXd7mUouWgjb0Nhj87MwhwcKHDBo8kUeoND07q9oBnz3Sg4uvad6XOO8ariZsh6nwM62bpUF5ab1z7DNN05Wqw8I7glZsPITLT9BHwDc5o7qjf5VxdR3P1+fokTY7xFOQI4vdulJSRGSXR/Av6z9ZxJoGnoa1dgescedAhHL51MiQI2RoB4wIDAQAB", // NOLINT + "kmnhbdpklhabdphnjpcjhkpmjelbknkp" }, + { "MM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoIthKLHFa/QM2J1IRNZA8PtDeqlc1nQpSAyC37W+F5k2watzUh+RooAm+Kf78Sw/1dHVAX6Ci3CLaCYNfKzUkEb2nB/MAJFo1vSt/8YrOs6nZ8XFamaFYVZxZuf/Xk67Nura/JadO3ZUadp/8UUhtOjzIT/x+zGnyarscHyQZf3/MRw2GS8vf+SkrIpJ/EacVDlbcyHEiqtKc4lXlMBeyEhz7hakjz9SJRddPVoUv2dkJZlT0XNNxMiAM2mmdtBqrLK4DQPViMeHB1IRJDrBMiqxG5BKsaWQ0slwM7sqJB5V73BZTMS0dTRg2bs41tqymHhCQ/7L1/M7t1F+s0rXZwIDAQAB", // NOLINT + "pjdfbfmfdcbpfakchgckmkmccnnphdck" }, + { "NA", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnxVSO/MIfkZAwBJ43h8AkS9x9avDxezxg1l2Xn9KfSV2VmbiTS9C4SELc7R6/SKUKAxYeHO6UEc1mTktmGd9gbal6gTNrdtY6vxsLlkrpXSOeLKuSRWbtfS6sSkCMEz/0mOJemOTa7vIebdvsgODQdatls5P5AlOV2t3Xs37718xDb9l838jC1gKR6xiVY7vbM4Q0D6KesaJSVIOJ/k+S7j7M2BJ15FTdijzHm1palodkmVp4VaMWPLYiJ0oIQuYCmEQOyqcLsHTleBnnG3W1VSflyNY4M3A+JI7Q5REzwSnwdqdcL9O3PJ98QqhOkZyhAExWRwBseX1yDi2fg9LOQIDAQAB", // NOLINT + "cnaiikiphfpadgacdmggaghcgjldacbd" }, + { "NR", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAva8mgRsxS3Vm8ohRCesmo+3GMXwrJuKlW8Eq5udDbxwe3hUW1clvermKurbvfFAmOH+jXiT0BzdrHkl3aYzd5UURHV8EpfZPi25Brwbk//6VoOYVF7nTit88Z1oLhN8M/Jd7GUSCQxD5DC8wPOwdt9ws9tlsLX73SAuDyFcQGcMKVXoKFfMhQ2/qaMIXBJQMtpvN35vxKfktAP9UWYgnfiLUsVpeNU2gAHB081tLptt27wmuNWPLUZZO3Jg+RGa17yCRZscdspmjYvEX3LKCOnNi3W7Drwyr1j+VZn8kXjYF0+RhqJoQq5vc5PJm5wa+ERyWOtckmOrBFKlMfs845QIDAQAB", // NOLINT + "bbibiihicogkdejcfglnhoiionnobjjb" }, + { "NP", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyhqAQ4Kuw2wB5pUrkDGltDaas/PoQEQqxLfAMGb0GPQ09E3qlL9+vGewWNcMd1dadvZG+5AtF1KnZ1D5itZpx+80Kp5xYlSW/D0OqNDCnLTN6LvczNkqGecZPlZ6+KFFe1scaLq2NhCqx8uHKgLmEtR3K+D9vL+E5IqQUCJzc+cZvrtQJqOaU3IifgnVeOAKvjBPdIhKgGGK7J3lVC5VhGnqGu71YIEPF0RrlMav0RVg+Su+PQKijyjuyDDXbew5KBmMKCwX18Xz+Pl8yJVFlswhmInAdCgm+FmbqNInOFMpfj5FmHu5cYUH0yt3I87rJnSowW+TaJEDWhnwZG01lwIDAQAB", // NOLINT + "pbjpbeffocfhdbmhibmnflgkpnalpamn" }, + { "NL", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAybAq9klAnt08UTb9KRR821Robx/kGcv6zZv9xcmOlZ2yN2RVT2zA+clqL/p6OljFSU2P/fUNXdA0O54G5E++Y/X0tupgeNhQMHSjhnTHacRpd4Si9M/GBu2O++dH4bBHQO2eqI1Gg71gwzkktSdrunjMAxWvVGwhTo36/g/LAasvKyxlkKYM8tODvzfKhd8g2wy/2zKZu+iWmJNhxORKYyFnKb3sCUAl7deEh8+esalnE328WoLs88hmLSiUpl23KML9pvGiXTWogcXtGHRf2/KMOXXYW6U6M/RIxKE/IBsCtuE64gxHamQmPV3O/FLKYsfyHMyiyDzx8ATYtdieeQIDAQAB", // NOLINT + "hgokbmpjajigbckbjhklcifehhbkepnf" }, + { "NC", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw07SRgExVsT84yzyeB8aqV3W+b6R/03vXcpO9LbDs+ZoVvLbk8/KvA7LrGOaPntNwDDBzJwE5QBTVUAxd/3toeiH01iJHZJsdqyna+ojPfSx6H77+6bEXl2q+6fkhPP/NiI9MwvMkymcB+bADmhHHX76WfzFhVznpaUBJObN1NHE4oapDstKUEBfSj+qUlPlJHggSLeaI0+rht/IlmVoZb9MUsm4TYHxEQu5uXfK7pNnF14XqMtAudBWRcx806nY13dbtvkcHKqCdMa63i3qUuEhXPknnJr8f+Xz91650g3RFkXxsscEF/y2dn7IsXOuMkNe+o0WGf25h2FCr+/vcQIDAQAB", // NOLINT + "ohpdjcinbpaljkbdgondjfebibceljoc" }, + { "NZ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4fzUpI38yYgD1qUkt5LW+W4q54l5ZzaNwardtDmAk+uxFGcCgJ2EkleiDwEZ5qSdPXfQ4XTLN0VmXoMunMo6wGQJznXDot85AYxJeiaXPNBA5/9UeIrbUbDYzcP6L8CSuRlph5yEU4qLnMkr8XNtWnmYvR/HXh/OUPDoS2rOiVy4VEYpvpTwGbxvBSSrSQUywBNYQkdlCzVpA5UrCjPOthRxpi1c23alB1cWrz4m8A0DPF0zZXQPcSexZgkaYYneMnSGBV1x8OUVuqfzjL+9RP26mzGIJl0pE04Q3hUcwOVSxuKwR+gXX5Ywi3HL/YVlOZY/F45UJv4zie/ouKu4SQIDAQAB", // NOLINT + "ghefljidhailmmhogfjdcejhokkhncag" }, + { "NI", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvnX5kR5SNVczHUZbz4obucLKbArnvgcuYmTnZyxc/QKUlz6fQyIgmToceimJjyxGvFL2kekA4NB37JtkCSpFu4lCxELTbw5d8fRlwOhSu5Gu4z8ml3aiKSwWnxNUc7DwUkmjApqjqZdvWFTXEOalR3ZE7z5CHwD/+Ofi0Mj+HC5ATebap1Ej5TGoLNw3ZlhZaD2No2pzx0LrypD9OpcIzk5JHRHGW7oHX6VtsqP4URcLUjaSAkp/Xom0QzmjHM6BZ44QvCyv/Njt+y/X/4ritxZ3Xpxyni2XQeBdHsSu/BVGihd5H9GhJWuq2FuhMyWQBJ+aV/c1o+yCqT4UUUV04QIDAQAB", // NOLINT + "fijgoeegdoidnknopmkcfnhjkdfcbfge" }, + { "NE", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvknX8lf7AiSowbQfw2HuNj6J/B3ykdKUl0jJQsiAejM8Wvh5c54vCYXLx6qJFOZbP44aPdgfEqPZPuFBuD2vc2MQ2+Wp/OzE/FKSds2FBp5OEPZdDzQy5iVmshYq5ysj0rYcu9WnkFJ0MaH+asoA0XHC89XmHTQ7B8sS57+g8gPEpNIip/U0+hKvlRGbdGH4wywdGj7bCLZ1D1L1tta74UhLDdWEZ98Knal0KMpctz6CQTBqjk8tocGFFcB/uwd7kM1lfYv3wKon1MfpSKgQp3dcocoqPmFDHozAdvzNm5bI6I3BbmjvOyPMMvBxYueSfLnXTWErdqx9BMLIcFz5UwIDAQAB", // NOLINT + "bbpboomhknbcipijmpnbefnffcijdlmm" }, + { "NG", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo/RL97H5RIfRlBjdWlxKY+yBpwQC/0vJp3EMcIQT6AVZkzWjxmJifh07GJ40XauxHEG1YV4eo38e+AUKL3tY7UgdzXkgu4MOAeA/liOYG72+Vk18ovvIunxKI/GcyeadaWdMtjLRIzjWdTxHGWf4OT9ahf3/p9+7M9pMR5BlAP7CYCUVPYagdwwm21xNP2HQ7BSAS9Hh/p8bgDryKMlc5YTZ1zdfn7769YKxs3NgUpEg5T5L9cTGRiGLEvdoAM6qVx4YqOyoG2uVz+j9W+vegNlLNsMc8vxBGdPsAb1okkm0+A5daayNmvAckdYkek7BWbsDNk2zUbtbxGMP7g3BuwIDAQAB", // NOLINT + "ooejhiplfngjlejbjmjeejfflhcccekc" }, + { "NU", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0EpyghBdDx+Qfjt9LqKpKRqBreFKWPNkdimT4FoyKpVf2qEZkxwXcfADFLgXkxw/QlHOyKthLR3klVw5/ojnPtcldE/JQtH/QB8FeoZCbRb0QtVZyHbPirglNVL+iE/Wr4myF6MmRWjeSg5QItsHR7Qty4nkIWAyrGhbo/B0ki6tV+wrZO2EIBbQgeicdD1SVmcbS18t2XIm+mW1qagqDUO5ezA15F/ju0psSRovGAKB6dr3TYmMlNWAW66M6hLgZ1RQE4GF80zN+S6LEkOAE8zEJvcclaHn0ej7UGKDp/icToDu7TcYpBk6WIa38CHQnFeT+HF8aTz9QGAdtlJqdQIDAQAB", // NOLINT + "bocncfbemknelfeffpilbfcfgfkpelae" }, + { "NF", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuEB5JtzxBaWUJgsQe9zBmn+Fnn14uME210v1MU7h7OCvfkFh0PHVn4NZRfL6ZBo/faVDRTYIh2C2Gb+Lc4SNj2c35Whn7DBw3/2s29v6P6UPsg1sZjD6MQLbAt469hHxVyDCXUqoltpUFje1wLkqHprDK9iuCbhcdRddy0UfS4iJo5KlXj2uYflL620E2vESjhu2IZ03yxlshxCLkRh79iSxVrAENK7sSKMfwILPtDbATT9p1idsd7k19EHcweKnJlmIisAkoRzCjY0bJxIAqJ8lMxPjTHtCGmwqy5QoT23YqRjRdrX2qPv9ZOrdi5dvepyzZ/JpOb2NFFknm3MOJQIDAQAB", // NOLINT + "elljljbcdigajcbfdoiiiocmldmjnbhn" }, + { "MP", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsF6/ozCTmxJB0GjzlKnaSJxGZMSsVUHtllzv0H7b37Ss+Xkd8HqhqX5+HqCwK6FXlVJa/VhFVZnROvzM3RgRCjACU5meCsaNERhFhONeSJ9FiKtleKE/wG0saTRFQibisUYVTHTBFV41+NKOk/26A2Hw0myFnAsSEMWsXhzi+m0/rlkEFhANcVNfa1adoJ59wuNpw/MIwBYPd1xxj+kEP1EfrYP0sf2uGo5YSwyaxEOgVlVFyQ4XPRGVD8I1lB6O6OO30UHjP/IwTKcp09aRW/rBegmb69c2C0U9PCD+foCx4BeAbDcx0XT06O/ml4lM30Wqc+7Jtjyfh4LIMDi0yQIDAQAB", // NOLINT + "bojlkkiigikhkoogdmpmedamjfndhnlp" }, + { "NO", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApXthifemTQgtt+h49dGCUQ2RgR3uwkSDxseosxsVnyGvfexOco18gn8WFmwM3i+5PfY4r/N9faRJJApP9pkFBurJNxDl7a2eqMZP9UR9nf1SQsmcHpN3h1B9YiOX6gyE3OpftI4Xd8ctN/NO3K1u50REC7e5hwJ46OgzGjFajmBQOkOdWe7OnTiSk4I9MxPAo7GnCIF2T23F4P38VkTrTXN+hjlf+RJbSL/G0xBhGs6+gYKHd37v2Qk/PHNF0XFPh0vOdqJ14XpluXpH2tCmNcyjOQCBdyFSde6Rs5ZM4Rwg8p0IEPP75DCwbHiq8xD0M2zAcWYKG+2YNG3v3PhNIwIDAQAB", // NOLINT + "kgaofbdgbdahgofjpcdhcadjlcijhdfi" }, + { "OM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzjs0AkdzKPLNppVMA2idNgveVfP0zERoCBvsKZQUujoxVLPCWPsJNrEkHE06VrrzepPJjN9hI0OcRlLkLw+n7LoG8FtpuzwJwZnOHqa+u+ClRtdcKHOwlQi4bbgsRI+p5b4nFCp3Xk/iSlAfUD2yCOOacqN0XKShdR2KgVcz9pkLOuI500+CFpWS0RAbS4ClIewBw3hRNnoGAUUfoGL2BS6Dim7pWZcLOcEFh2vdZ3ZGJl6PcvISZzTbouJ1RprNA38fRWJkvNc3Md54tfWj3351CpRYJUm41pbS4qtusTdlrAdgV1xj4H0o+sGqeMIyff7jR4Kqh5esKuU1fgAgwIDAQAB", // NOLINT + "agbgphcinomjlhibdlplmlimaccedkcl" }, + { "PK", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsIq7Wq/Gm9lRphR0Bqvc2tOhdIGmADu1NpeFA+MP6bhYScXrC6NQgFIJa6ZaTqM8fUhZ2/Z3ZCON6kEomlqaXkIAckGwxOiZ6oJ5Ug87i05/ESFITFM4LRl5smTVHqLZ8lWXeX4AaNBS3KTJzxMP2csu09zdFycCnqfBohBHOE7TbMUlgAU3eAAqmXxO6iHMPdowKanV126MAT9chFRDMikSoSnVzCVR6IUG8/G4lWkyNq+EmEunF9RLrH7pdbmxCciUfaJ2/KPlOmplK/kGJbcnu5vnBt6piKzJLX3+Mit1jYEbDwpKAI0ZlxzKWJTgKY5QvM/eg8GxZMjJQTGdmQIDAQAB", // NOLINT + "gglfbihmpbgfficchcefondjjdalaefn" }, + { "PW", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5inGHRjJ5rhvNM12Abk5TCsc5DmVd7biLC1cDwPAvfPh+FYK7LSEYNelFB4FEUGhoLNGAMrzEBMhs44ufBakFZKZU9xIwd6TmepAeg3dbAyfCtvF+vO7K39qfAGIjn/F2X8Yx0vpAzluc9kkR39poHKojdnhqKT44BxMtClRDJGjEPxPmsjziMQgiuMqEFuT/gPdSkbZJXHxrlerlXuxRG8rViKzCcsB8aQvdjoe6xRJdNl97bDCeM7dUnGriMzzp5NLIo0ZFTJQA8gaPsNyE0jih9hiZ8dfnMVfP2B1whKyEQPqtiaWu5GKTgjHL8nyTw6uU0Np+gblDtpVS3ak7wIDAQAB", // NOLINT + "omflhiccdoghdddiokgemicehmmnpemn" }, + { "PS", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsxI2zS4gkzTATXVTj9Qig1CyuzZKEwS2ovb69xMGKKpYeYfEs8PLhaS/hzjlO27tGozzj5bI46kmdqYOJ/qUNW/JuGVi7H62OfMdikIAhBmfU2pd0PhfqnwdOeAh/CNhVWxBV4Zj4wXTbhLymTPW3Lf6rz/I855/uiSMHu0HzJcAV6eaxD1vrFjp4TWsOVbAXjeA+ecwkPPMq7pxdOfDW6aahkQvnLyZBMUW4QrRWjLrkuFrftV/TNNYfQ+7EYJcwdswycSX0ANGOiDhnRL7PNyvUxJpCBVzCEn5XCDSB3SNTjSBXiawOV56AfgMW51HYVantHQZo+OYgkaeXye4awIDAQAB", // NOLINT + "hopmkknfbpcoemopkennpbakcadnlnam" }, + { "PA", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvG1LfB6Q+OC50xl86fqZsg3SM43ONAyBR+bDrUzHtJOqrtYSOAHhgrbvKhTuSK+Yj0yRvbQpRrq0mIvXxl1qbxxdJNLvoyBVHp4/c9BMInjaZNL5HSBhLf6Q+utbd+WFpCRwq5siPWRM9CkraMyPn6le2ah8g4d25xlSmphNIN0TWcv4DxRq+bucACgyaxf9C59Ct5n/IMvMd0jeV62gybYwX7jmhY1rXSTfrPYSvk3ZJljL4p51vzGkL6AHATxc0W7dNwEskkSNAhEdOsUhRPw3I6MQdxxvBTGOc0WrEpyVjy+Rhn4Lcy94fKJwe8gPrLcp0/X4nSR+8Lg0U6ZuqwIDAQAB", // NOLINT + "pnebbecdpplkfeadflkibdacoejaceeb" }, + { "PG", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0lR48HndTTQQTnNGWrdSiRQLrR3T25b52y0hwo4SGoRU13brWiSX9F5r2DFlDwXwbeojWMz6Mj90VXz6LZ9oZyf1ZQZbXUJh0N5mpNDozZEojUMmmCRB2tpJ7H879hO6pB4v5WF+HW3MlQ/P59K0vkFFBJpcuvr1YsfK4wTSAP1N42VCFhSWjCcOJpeOl7FL5mrm/NLNlmavVebCA3IQwmrOKgWmvVoKomqg9Lf0YzxX4C0GnIsItzpaz52eRb6z0yfL994eh/nIRrdk8c+VGFE71TzX/Oj9IqGCh/MNlQXl9mINx8gJD735iC11gIRx1/chO+Id+cuJHnXbjKmHcwIDAQAB", // NOLINT + "bdcfnocbbooimgmbiheaddjlkcnkgldc" }, + { "PY", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw5ZRd7oMalkxVCf4wT3yQ/Zff2w0wqLtycm2pchHK2paafAxcDdn+E1N8WhaoTb6BU/vGkATP5J5DhvaOvQ6sRKGqM3Tq8WUgK+RHu4CTK9nvrvKzne19MELuYdb/WUox1pBmxJN1pWBC0vQQ13FWv9ee5h7HVlAnASCZy5gh0xk0ax+6Hd08vbzujLdDXkIbhzn/o1fAV19L5g7TgXS1LjD2Pblm8dpaupoikFMh8SW2/7fs3G70VPKihFOoMDDEJbGCnVV0k+Z0hMiyo9RRtQCuc/tSDCMqzpBdJ3xZqnNLjBK1K+267/PlkPRrX/akaGTtkmgbT9hJpIzebO6OQIDAQAB", // NOLINT + "ihhnipipjiepjbmnkigfokgapkampmel" }, + { "PE", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2x2BYyK10jfsCWHhG9KaA0uVzulKBqCfdfYI/BI+P1tCu6FfOmETvpGIjulH8rCBfoUapIJkp8T93hmcOXBtkDJsDqppTdxRQNbVfRIohHoP1WN+oWI1h27etjhxdrMyYiMQiD2n14sjiqdYmqmvRnqTHIdNGANBieEU3h+aqo4gRIUB2WjpikId9AMlBgP81ie6V3Buk/jHeXCJqqg5i1yJ7tyu2+9+ENAyelRg2ouhzooWvf/IWhd04V46cjq53tSVa4GVQx5KwXnRQ4HQUfjnNhk7ST8QUTHMdTe6uO1X4n0mRVTG7ED7Cx72V8UkZC5A07oPCdLoDgaXL8aa8QIDAQAB", // NOLINT + "ockebjceekmicdpngeehfmfhdmhjdhlh" }, + { "PH", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1bjOdyod9FMPhqJAmCdfHW+QG8nP26+992zrcfqtq+GHARkJwlbjkzZkZ02Ol43p/J/k6taNqBUawklWN0Dwv/C9cVSgLA9o3Uktwlv9APHi06rBoDMOE9aIYXlGW/XdVpKfsVf4ASof4sfNDvqoACCBIb1hoHnKHPtctdC13CFtHQ8D6Jppa93WYsmPcl35tuXARRR8R1nh9tTsXcUfWMXhcNGwI0cniXCZS+BhRPqF+cy7yMDW9DYfDoAxkVoekxrsYI76h0JX+ACCuqPphYlTJ1KAPOGhPZMFG/NFjWjHYQfQAPk7QHsOzvM4FAtoV5JdUXDfqiAwD40TMRYkGQIDAQAB", // NOLINT + "gnpenibjeonfpmokjgpndnckjaehmcfm" }, + { "PN", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvepA8KzXazz4sUPWz3N8t/G5exR4QsAQsDJ2Fl89CUcI6xuOA8TqHipvYtME4zPLimE0oqThyylIKqTyzHRdGqf5yX6akdSbP//QzcWyx/6JrFb+KDxWklfMXe8tTIaizRavU7Y95b0szTCnQ+1A0/rqVxsIM7PpbzXgerNKo3mG6VpWLcd951pV8xtl3r81RafaAcrPq14GjcByILzoDGGMvfj2LxC1spUGaYZ+Kkgf/CdxHISZGFFqO4DjcPZxUaW87QJeRVItbTihu4bisVVESqebvi84lIASGyq3eZNXggUiWjvGUSnMusNo+5EtkGQYk7MjXfpXUnDQxzcuOQIDAQAB", // NOLINT + "dgcfgapclmoehkaelkiepnfpbmmfcdpm" }, + { "PL", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvP18e+oWNEeZnG+UnlzeV6jXTnPqi+jMDCQj+QBIqByETSC7NunHgpaaQ8QevG073GtdETYvN3D+s2tvnr0ibZ8PTTV1yoPGdPQs57O+ID7HUz89lJAId8A9jSLeGDX/aVNER+STzbWSyHnph4qD2ftGOfIBeqRm+fWmEjaZfStpZvApmYsd767k5f8Y/DI0c9Ezibx8rUQB4OHm6GNz3oRyEYo/llja97RkwfYaUt4YhvcSnVVYD7UteYy/HaAmrOqsQ2WZca+daDGwZ7DQkn+j4A6CvfpmYOZKOtaVNH5AGL9rp9y0Pzf/y9XioqTnbK6TEPdbGMLkMyfrJ8GnSwIDAQAB", // NOLINT + "iodhafecfemgejckecbnmpobnhmoaoag" }, + { "PT", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAryLLNWEVSiwldWvuQNeXra2Ra4f096n2QT1L4uxqg8YyqL8wwON4qtjdIROy8guotBBivRaK5M97poy9Fku7cdjl0mKu2CnYWU3gVbxBKB6H9SPoj7XkkV7ND0o9uM9r4/fZAknfo+PpjNaZYG2VZ7D0OBjupRMc3tuJyhLWAeF1Jd7P2YKUwHDKdw1N49tbkz3naQTRWYsBQGwYd0xLGbaz2Te9NFomcNBicsNWeG7uz42fNBmqd6VOw+bWyvsKOgDRdB5DAXtBb75jp5FGOHYOqdPnwuwscYg/E/2Gw3t4yvqYOXyV9XyVEgUNjUE2grXbCjPQNqb1bJk3i99duQIDAQAB", // NOLINT + "hgpphbfdjokplapppecjacpijehenpkj" }, + { "PR", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw4OBsDltaNfiGqiPFSpei9keEflT0QdUS5txGT5sUEsoKz2Mze2BzvpNWLYwtaT09h7a26kKNCVC5+X7rkZxQf5IyNPPPZfdC+9epSc1LTg8ak+tAbx7IgyYkw5KqTV0QPpKTvhcHeR67vGP5EnZ1jwC8K7QurZq44hUlGx6gy4AGhZ85FziCifNX4VhfxmSliCqbt9w0mjtDOpgZr1xz7GbEg9QmHACxlxqeovPqZTnrzOBaU/OukbF56qQcOJtYqGNn3eBNBxNiSVf5NSa+yD5LKalJycD1Rshv71zjTuOEJeV17dqG6ntnuWFIP6Zs8bMaPUbJitQ0VKKhHoNxwIDAQAB", // NOLINT + "ihgdlbakpjjlgfblpbmgcgbiaikmfhgi" }, + { "QA", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzNvigt/ogcqnzNqweJNeHWRupf6GK8eU3H2KJhvfISacEppAcisaRTq1blu212eNRo7i1EYgeo39fg5dwmxUT/sFXvYkIXEESskJ5e5jDkSkJz3HKV+Sgs3NyLh8C+y9PY9YmsaoUR1pArAxL9Ds0R6H5VZU/YXyC8caWGyG5iHX/+rN2WnUXimGKAA+SuXzhkmQrQg/ygmaCJoSWFJzzHCEvGI7YdoW2RXS3qTahvoAkbE2h+kB0x+X5P4Cm4i4D1+RFeuF00HseYnVP4pfPP+jFm3+yUMay/jxiBx4zK4XM+h+pYn0zNuVSvTbxCWSGbG1h4BCHGPZ56oEcRfkdwIDAQAB", // NOLINT + "chdhpjgbnfnbfjklndpfaiglohgohnde" }, + { "RO", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoXvF4iN0m7gLHtseFEkTStLu/jKXdtPBQrokdqab2aAPW1X+V2sZQbwLSXtNDmZ+6mt6U+ar1XXHwjBEYUr9MjqtEriSkIeMIjcLEqagGOQSacI78T/P7IEZR72yC80fPpl9BqymNc9nvA98YT2NnqWY8uryoH8C3Msznq5b1n7iR3qn+Kq7ledJ3y8OLgfhJOXF/vKKDJZW11ZifLh9A7f0XUN5vIvfDh9vvnv+AJbnsG0hZL11CRACY+O525AO3BdE1kuuna7Tp85hl/J8u0zR+G3Y8/CzPx08j3A+9mdF66lrPSekfRFZqY9+7kFHaZZ0Z/dQLgVG30qrB0sEbwIDAQAB", // NOLINT + "lpebdnochobhopeiidkonjhkepamihmm" }, + { "RU", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApWVYpBPOPR736D6v3zr8mZuEVpphnOlEcxzdDCEuv1/Cwn7WjJSnwg01IU16rm222zTtAVSet2WN65K/Z9gHUv5SaYjVh5VI7aI5e5JqxYdRsQlNKUHgeLyxg8OoX0752SKjfIDSOuEA1sFu41/wTVuVsVk8tc2GxfB27I42f/rb3OyvtVvvupeW1A0gDWhjCk/IVU898j4BOFEGa4Wu/akzrGFIOLg+HP2tLIjDiXMAx8s9O9qIxn9ImwdvBuHwF/+f0OK2QXEwfXiXr356JK4S8a07DJJRWdPlDCBASkdPr4TjxpzC4LQVOahiOBXvlz2vUdMsp8bw1TyYSPvahQIDAQAB", // NOLINT + "ecpljfnmcepaelhgnakodhnhhmognpch" }, + { "RW", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1Z0Ghzpfqi9i48VfFHCG80FKD8bQNJbUFtDfvmVS9ZUN+g+NvVvAkD3HLtDy0ktdrF8659gv0MkosW4xCXPnOoJ5rnRSM70GvkfeADI2I3SwlLGwPKoLYiUZLAHClvBDSlxdQIGXuWS7tPUbZjWJ8CKtN43NHfbccIbNa/TVrlqEoz1tGbUN9V89FKguRMCXi/Khke/oCI2hekz58FgTLL8RZAhceWZN2xwjHUC72a+vYPxyhXOSyzA0wZv/yda5k9zTKMMU5uZIczJaibYj4MVcuddxvtIPsqTOh8kFBLgxh/lPMby1ZzgbwKXJOuuO1GGnPaVyRoqkrtD9qC+ciQIDAQAB", // NOLINT + "dclamjfkdcpjhbibaanlbjodeedbllhn" }, + { "RE", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1TByi4nzA6h1hOTKK1GKoSYdArnNHkKjupUWJHTtjGpOLwOCtg9MwIRJsAW9MXlvqJxGxuwXLrFhefHu7Maqfw+E76+krnLsMcx26UmpJXbugFAyZ8vp1N6uMQG6WhK1SlW+Nq2ORhXFpjHJNZH7guWOb+8UsitSAaN2wYmieAHkSvF1rW5/5bEn7Rnr/oVdAVs/RhE+wnq8R7qaOlCIJr/tzxiqbAKVdOLBENXhwXonkwQvN38718VPyNDUBDGB2USVUUmmfc8Pf/pLdLuvHDjF/RIUchAFgR6l0ELXvc1hEHQ7RInX4W10N71Pv00t7Ec3HFzb5wuSg33Y9mGsgQIDAQAB", // NOLINT + "bhlehnnpjgccdkdgibbhphjihimoapgb" }, + { "BL", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu6MhIvxEs3NTVVnGd3YGfnh/O3G+LHj8/e5QenMVAATEdHE66c/CWFAnBbj5PevY7cuNdMMGp1+4IpQ6bUTe5L/yWC3jZvjrCumfCu5gXtolsaBUTBDvMSIJUTUj+3HoSZI55/qpR01JCYuyn9wPhcjHmD7d3uqEyvuh87813TP/qepFiGpA4KYJn2XgoTPAsvbNVrdEwA2a2401fhEvThyaQiSR7RLVH3TbPK0uDCAVxHjViyCoSerBrxN6TygkvmJdo/SeaSCMWZtVaWpyfstg2V0qosyUmgsCf0G1dhVDCXHQ+2I+ybqejEYAIoD8SvjlKb5dZeMMp+UUDpgGTQIDAQAB", // NOLINT + "fmckajnhepmlikeddjmobdjmgeilmind" }, + { "SH", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA44dLj+KyKYu2uJacU1MWRAwYSLRoofssJHPKLNUcNkXTs/Y84IRDol2k/B/D0qFLYEl7LNa3iE8rIEJ0ZO7d/P77y3Jy5ImA+VYSP4FKPfzBcmyYVVvRccSxz8ZK6fL4TmVZ45fUS+CNhrh34jjSyQyOhRxfK/G0Geag3AzcxvrwFRre61Ad8ZZQQBXcHfKtpifeuCSScI6tiS8e2cwaKgkLsi7iKk/soDvpKRx/dn1poK94h0eIhN5106AcrnaALKUX2L+YLsakhzuCUQcOGV2ehJZrflVWL54Q7R9g91582c6RRaNzrgN2E2goHjvUgatjzgA6arYFFI4G96ix2QIDAQAB", // NOLINT + "ebhcpbhdpdhgihjahegbmfobdcieplfm" }, + { "KN", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy4T4zO5PNGJ7uz8VfmEUUVUkAm75BwsvhchRj8SYBbV2bj9gisef/2QKkCGgE3LNY2VPrYvEA3wvnjBLkYyg/tDJi1+3w3g7YrEMYNIDVxrMF8/wdrLNB9eFhmWJcJ1pDkIsYd0IPt+lgP43P7VmPMC4QFjDJ8nHWyvspn54DFUpstn+s9ssixkIRKDJjMyZhvvDPgwwy1G0wQYZ6KY15c+IWjvaUZmEbi8ixH47Y34RbOvq6khibei6HpCN9+V/xR4bq5LrPF5Hx3yyUcuaxX85TKTiLp6H3VukaChGwxTM1o48L9Kd/SSN/arTBLEm8F7/KHwBsoeSG32ivDZo2QIDAQAB", // NOLINT + "pfchicaflldhegodepfmabmpjjfkiiak" }, + { "LC", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1zz+UP37xCtGu/tN3LJAPWHtUM+8Tsk80opk3pdqLBQrQYTH3bS2wwX2At+8nzPVZ9M+eVVx1ga3rsYOZQEt6sL0fSNM5c//9683NpEGlRVS88SxWbKVx+RXrTtd1HQG6xWxnPHXgMWBQNm8CVDnGBWhdBEE07SpyW4kUuLfPJVWdjzcayYoqtdxiKcjGiKSJt6KsSsRIS1WOTBlkjrGQmYWYc606S/bK2/LmnGX4sNx74oXah4Owq7gdxXzBnbO5vZ2+wZYNtw4Ar8Yf38zvHqeG4KL3CenMnNQZz3XiUMkX2qTXgBvkvY9kYljG7tkS9tAZ67LZlZe5s2KdxJ8OwIDAQAB", // NOLINT + "iodabfdfebahecjeihekepbnlkkgnbeb" }, + { "MF", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsb0XA4fPU7GWyG/gmXj9MBfnfmXM4bar15wTRzoV1gVZzI/UsOemUdmS+uoQQnHv6/6mK1tgsuurr2mA83KHgyMquV7Db8ep115r5Vko43ozon+ToFRRD5R2wDlVXQQhU6DgFeCq5KiREOATU6Cr5lgSxNwb3Lur1HKcYzs9jjtBItPCGy3ty8VBo9Sgldh4kTK9+rjSw9alBDBMVz4QMhmXPz6KbZqea6J/zHOkHQpcboDylFTbmmn8APktR1fg/hpa+afdPVtyOXN3FcVoPhHaIX/1zbeLsNuENPjdQery4Q73QpYqHpsmiLBWOpqlpmZtlsCB1JghaY/etEVckwIDAQAB", // NOLINT + "mijjahipjbofhmhglgpkbplelmnfjbdg" }, + { "PM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtvKnNC9lCMBpJhwvYAddTK9Oy3IUIG3GXM22YS/71iZEnPYORgQH+ShImULzu8Tc2y/pVkjhpPxnEBNZbkrM/xPeCHzKCWNhYyTcFt2AAIcy1W+jSe2tO7LGbaiIh+LNx8UrbzoFqJTRC49Hj5+EiA+rousknjFIA7M6Zv6DI4OtGiS0MApdDlm5DcswEYDU9VzMGkk5zpaItpUfo7/8Twjt6fQ+Lbwj5SMHl1ZCjjBZHhXmKK6KaEkN3nP2SbYBms28DZKzgvBmfN9lrucB3jArFB/JaUB8CN/PsqKfXiW+Fb6DxM4LfSPvGI1jhN2BdXg2gua0d/sSki2k769t7QIDAQAB", // NOLINT + "bjfmhphpjfbdoohofibccdhkbenoncol" }, + { "VC", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAribA3+WmRMi4GN5bHso7Z88fOsPeDvlvUb5LtNMvPFlNop8YEo0Bh1R3IZhKdE4Hbjy6+j4ed8BeudDB0uB/DsMX4vB/lH7eY2tAGo7ARdHYekDjW+HvDBTHp2pmh4DkvnheFXLPAhiLAKXYdXJ+/7kJlbsVYqDZjo4S+Q+2ctuAX3eA/mFl3V2FZcUDyrePkywpwS7IdS1g21wXIgQDwSIc91YVTFP95CF6GEzCNBqT8Hs/4OcI830neITZt7BImD7ev4OQIX1qFGoVjYVQ+/wm/4yc1T3fBDI/mH6A+OW38zVrQsN/kiJDkL9d+VaG4jKPq1SP5GnhPzO0Y0HHEwIDAQAB", // NOLINT + "lideklmiddfklgmpfphehiiglomkahoc" }, + { "WS", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv1n2HQl244XqgC599pqqa4z1L1Z4w77rEgUwUj/b4xzyEBPzip4RLdSQcPTwsTk8VZaRBuuinTRWM8miR0pO6/XKL+fSAhC5eAyONrjYzxpuvUGs6IeJjsLTm8HancMaGBQ0e6bhu6xkMeGprop34bCFgeWAfo2KkZv1TrhNOmcKVpQuq91oxtNeHXN6jH7rOTYj1Pd0dSv6FsGuKywqy3PXzy+aKbQnmhg3YPbsnd7WE3Pw377mMC0zHbRiAZdo71FCRMm3MbZbdrABq36DQ5+gglwxKRgWy6JU+4ruyKS5djGM6qifX06fcu41YUslkjv/fwt9kYXQOUY3hy5BKwIDAQAB", // NOLINT + "ckenkcbpmnichcemlglhddojbcbgbfhl" }, + { "SM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/EVqDOZQzXQAb3qlxeSR6ke3rEf9JXjhFMEYcjtPkbUwU/I4aua6SD2mhT70WqOtoBC3actsCfE8uNcFSpOSb+Hft0Ry4fly3kP+HNaFq7u71oa9C5hRFRp+0ol8XJnkOIP0E8ZYBW1YULxY37t4EhSo8j4VhXcgyo2d0iTovnt0qc+aFE4Ioh0O+hUwKO+C5CnpgICZpQ94kS3n3234oD1w1CqioIYX1OikPe9eBCUpCY9ZO3BlMrK6DdqOOTJLNDY3+EHPvSlxPqyd4fY+8dqvXjmklXEsGLHAlHc+0AYO+cr8HqJJsEWde0MjRCU/Oc7x7yXb4ActzzCFXLx5QwIDAQAB", // NOLINT + "mcgikdgaolmikdhmpchbpffdfapaodip" }, + { "ST", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzYyvWj9TyrWzbPV6tzppOO+WeXYJYSfg42+p9Bn+iwFVHe5DqzF/IVhjifRlblA0WRN+xxUd7BMA70dwZbcLiI2h7NEosN+9iDnuRHkJDnqPvnxMe7CH7Z2uWnjjCsHyp+dwGCKKH2mzN/Md5pY6XLoIo9opzrsyCJ163MnesLJnFi0LS5SGLwyj93mMShpSxDI4mqi3dUB0AdtHlWlUYJpsKa49NdaeeKVvkjaGLVDjSoWXuIJDrEZxuMCZz9yeiGzhF12sveWawsjqF0K8RA7G+k5aPMdXRFK0FfEzmcfnB+pufAsUQqbBLpdVLWUAGt3hB8YUbKZ0WfI9mkpZQwIDAQAB", // NOLINT + "gblbonpdddippofkfckpbajkmjijchhp" }, + { "SA", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnsLSdu1aEecu+jiTN5nyAnqYATRzUN00mbGsDltHgWqGljntUIA92624YdUNqGW8/vTtMvGIf6MWlyHJANwHTlYjJcNjULFo7B6WvySnwWN+OGuDPWwhInh1R1P59cz3orLOjg/tAqEqB6RUiZvQ7F+/R3PDoK4pE4vgo3oXUIc+HcOt21kf/ZsEAjodx2AyqOf8glRoYU43b3WxY6HIufsSmXIUXCGg5eobSA6vAdaga9h/jxpM4/B0zme2RH/L+Iw0jA0ZD2imksuqZYAxNBJ6SxJC4ndCO5HJfDq0UB9GNVEzUaoDK2sM39SGr7jKPu+g/r4KMwxPBGDKgM9zHwIDAQAB", // NOLINT + "kbfidkbgkeenhlckfanhfgkddllpoeif" }, + { "SN", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0+12BqZYomdq2gY6tXE/7mPTnf/e6IPapfVQb20m0B+Ityuqkrw7kfQn/8TuinnabPlccDo0AjehZrGoPFNvMuMV8as1KXn0d9jQauDCsrtgJSW146w+JGbf58J+yYbRFwvkUzttBM1hluGlWtObyz5S8FaM84ryUvFL+tAZ89/I6cNRs5e0gNedNKQi8AHEYpZYFIPfIv/yqP8KJEYQyRr5jmtz3t1nngZHn3kN48xO0JZsw8eJnHx4JbKsHQNyiauD0AaTtXaOlXb0994myDmkByk1ZUyDqg32oN3WUeByqci8mzoNkhB/Dee3GY0M7UJRq/t4nMXjhA/9o4CLJQIDAQAB", // NOLINT + "kbmhgllbapalolfncmbdeflndajoeeoi" }, + { "RS", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxNE8zhpSSnH0Xy6MPb9frQfnb8+43VqfJeE8nDbiDQfTzCgLIWVqJ9QsJsvwMpOnkFg5kqVXZT0PpOf2l3cwQrx5esAbq3uHh1egrrwjGzXX/oZB88ooaEgMtbHZYDtjnD4GS32IfeqdGOTTbgc00ZPH/vm61FV1/Aaft2hqnWrNwrqUkiabiql2M1uOE9HH4nAokdQNVkA46NojrtoygcA9vMe24qHKRWTfYVRrnuX1IswtrLfTASYCULtm5qVQHbdX5KKanNLMJroxjTN309r+1XSLMHdKd0sCkEdBA/Cf/MOksJIeI8UlwNn4zTxam71hqp9ZKeHEd8FYldpKxQIDAQAB", // NOLINT + "nkegnmcaaingjdpfadapphceooopdkpj" }, + { "SC", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsU8jEkjvbA2uggH9kIa+cm9Xc/tkVYGKCT/FrA5qfuaRFdHbRLMwvdlRQIfRLW4DQb+2sqigfuPU9PSuJMdJ0EM6lr7SXftsZl5OEchQKg0wK9o3gP6477neQu5jXITHGPrwwJZEnPJflQ9B5jT8UVptLdxOrBAraW6+u9nRJOERa5KGqouyozlOItPJqL6MHkvnUDwDa7Cy6OwinzB1QlduXA0oTJrkaEn+pOuAqVN3+SkIVK9utqi/7NW7sC8e90R09TKwtkEeBH9PXROsrRLaxBD8krL4iqi5NyK4R5bZbgJrNJVmC4eQqIs96/YQQxXxzZPVBhrJqxQPn6voqQIDAQAB", // NOLINT + "aagbbnkiefghgmhgbgpjalhfpgocibfo" }, + { "SL", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtWmGfMZVwAOgEAIzln5E511uM8YWLMY//9Wdyz3f2m+425M8PflAXhjNdGrQS5IuTNe1cTVoAgKU7iLDGjRrPysfU5ixz5xvKltmiJL6gYT9vQuFmcHBFzKQEgt+ne35Cj/WOJ8gUQo3OOk4S1yNjbcegBiYkydRzxN3O2MdT2pclrv3VALZ4ufzDwrbYKfXfRW4KBmK5U3HicWg8tP054jM+8Bm2dBr2te4ueXcjyPeU/SFeQnxMN3FXYqmDj9NibFhE7LD4z136I+jWPo6CRRYQh0YnuZDifJKNQUbrhJQnU32VRRlhUsweLGvd1Aojp8D7gx01cNsS6NfoOQYyQIDAQAB", // NOLINT + "fgijehcjekpldjbnmjobnngfboeephai" }, + { "SG", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp1sSHoJuGhgtnkTQpmMTzYIfuszoaNEBOmPhu/c1LZ5VZhCH08ZtXV8y4hAVyJKcGxuziBVUTUxAnRKGwZWgs3+TL+Wm8q7xaZy8b/5tbxvq+SG6NqtZnoF4pAB4ral3dGC4MqR913Y/vmIQB4AgzdSL2NqY4DTenXJaQ9mXrsSGeyVBCf6a23i7VOMnHIydPQtLkFbA/AJjf/2s5W2wYTBGIjGxRXIW4i4+Bbt/oFrgIALwg64vgfLjWyi6lQN00wa9Kwl8Fxk4akKdBn2RGxglPYRzx3PdbCuTBCAanMmqa7+RvgZp+J/ccWw15tTlS4GljTZxop/nZe8P+PhvvQIDAQAB", // NOLINT + "mdaeadaiknjmeenpknpiogcfeoegegdk" }, + { "SX", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtWa5NByeLBSq36xAg8YUSAvBQmOIQ4MY6pTCC/oxq5uJcXkPDDhH7C5j3daGfPjnPbKH0vcH5Go7G5nojrq0b7BjO823cXfmLMSSPoqFxiwXv6sWfJ+XXcsREMzzQZnEm4Nsym3/kRVJKcSBH7XIAF0HKAlf242gx1/Sc3ubtnPPXbImKKhUaMubqODsBFDSkG4PnrVfbcOqyFSqhc2Y6YYRyC49s3XlT7eYqDSjUVW0OKLVMUkU/bNe96DjyE61DYjIBetDZpSHUOCtPQrimMLKuvUQPi9pK+80ylAflD4eA8gcDix+U9YOBtl/vvIZjcta2cnuYDPbEAD15qN0jwIDAQAB", // NOLINT + "cmabnpanffepldojenenfiiogcladjaa" }, + { "SK", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvQj9IBH5UJCoH4u6cy29bcXHP5acNTJm1OCk6806u6Y4ZBPzdTrsmUR0sO+BN/uvrPGNt+N6pg7XbcJ9fFgwzkWlMk6rA6D8kP7vQlrGAcDag7LEnfA8EQ8e8YUzwCKejBI4G3bIa8VnWwJX8se+4i1JkUsCJXzCWxyPawPWrGBxDpf0U012d1zif53uuxMzAKymhXbEPgw2O8e6MgazukvKjwHu9BrP7e1ldtxPA/ZE3diVH80m8uqitl2/aNWZb5qc8a0E8kqsU7r2ODVGWkQ6pY+lLL57k61fRtfthk6xzCRS5SIX9s96a7Z7izkQCldjcMxVQAwASyfrHBGdyQIDAQAB", // NOLINT + "mjgplcflbkgklplplbakkopkafojhbmk" }, + { "SI", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6YnJG5Z7nY40G6U5U5PNa/v7Sc9jsc9bISqR87wVHsB07c+l22jfDRTRFnFPWiWEoavoACwqaNf2XG5wDYPz3ljLMj3IVmgF5vZHN9OeyxUNIgpMEv+6VQLcPZWR+fEXIBhs+2eworDlg4e3tkpdq5wMHPyQH4PThLtMWcjv3vnUjEZmSOlY1yjUc+m2IDnFKHVLqMdWXZAOQFQCYmaW2forOb4thQ9PA5mB9HKy64BXgUGwyrbWqbhlIEwrdfhaNtFvdAL5Pp/YHfg3MgJCcV7MWfYJtu3RgoCXQX4p3D/U3YQgIG2vZ6oXE8dPFOoS6c+OJGo/Cb0kXkofgrFdRQIDAQAB", // NOLINT + "npnhbedoclaegaeblfcgahebjchphfgh" }, + { "SB", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApi/F1bxB6jM978FyRtIL4d8jwKjmUH9BD/9FMYS8a4RusDBkf9jSGW1pOacQNgIne5A876Jw8kSqhQNYq0tFVR7hkjIq4mtCLvFKa94E0LCy+OgA5M2hVUKgVwVwFj407NE0RBkPrCWafRgsCcdSDMN6Y9S8uC8tgNxGhe6Zb51yrBv0RXEjy63ZJVY+7Qc7fKxVVrQDLtsfHmfathpgAzt6D3FsbV8tUumYfP8uD0x8q5ZacZkfC+gd/lXAQwVUUcWFgTo/KRESuNaarSYaIHpIhZsv77BknlMvUnBIxgTN6TsMzdhcDJqoYdTwA4WFQ5xEvPwJW7ljPjoUBzlKvwIDAQAB", // NOLINT + "ojemlfnfkkapjkgjkodgmdbjjilbklek" }, + { "SO", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuQXm+f5kEc/DsRYGQ5wUbR8pYivb/YOtZpAOMvJtU9GjTkaHaTML1qPG6Abp2mQtodK+sesQX/Tq/Zs/VZ0VA89Fe04Jh0mx6I1KZkd6gAEwc+D8YKmV9fJiH7ORt7sEXqrdj/b3buG/jupEu9OjwylMLZJWn7Bdnhmww0CwACH4cjJhQOTLFqCLwPiQzqhcQGPJ5hJf4FOfRb4BEsPZdLbKxA2LB/0Zvg6j5fah1SeLi5TymObHi9VVh9oJiWe3y5NLF1BDb1X7J2dILoN5HC/r18/UDInniNvEwAvSLQl3TepN8nVjUOpFvRh7W32s4iFI8pjjhJQ/+3T25Oz+4QIDAQAB", // NOLINT + "nleodcmelklnmfpdmhfdhfkilhebklof" }, + { "ZA", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Liix3cMWyShkfd6FZWa2gQjnsZsLDyjhjRFfD/of8iS9GlNEEk2vF5J6jsZedPQU76d+SFif+GRAAl5XxjWIVa+12Nix+u4vtdGHpqJrSlMvy/azAvWRWkVynX2QD1wv1t0js3pDDzU8Uh+Nkl9JeijneXT46tUdgvtPJwgKjBOMpvpU3MaqFIScisBw7N7lahJTvxUd+WIS2umHd0s7pKLZ9x9w6eK7pxOPDGk/Z6vD2oMqff+9vneELCPwxr+WjrCCYPdObIug2HHbln6zqGMko90A3+8Q9jW5s5MriiSGsmFmf3ClUbfYSLrwnuo82DIdnx3XL3G+miLH+YKYwIDAQAB", // NOLINT + "ejdgeppfmiloeldijnhljdlamkkmbgko" }, + { "GS", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzpaCa6GMUr+r8zpu5WysCIWa1+F7I30JyabOsri83efTCWeZXzAzkjk1qOngGKQKcrtEz0cF1v5KxV7ndiyTFjnCQcasbqdMyrlAOaYTK3llE3jmg14AijvQvLS1yhPvkw11i5ow3COx1+77yN8RXbIXwzWExyds58fdmPE/VYpiZeIESaWG/Db2MVW8lrromJrSPxsHE8uvoN8Eo7kP1zn21W7taCEtiGwbkMw0KsG45ejnH6PzVE7kY5oaDvcBtBGUcdsOk1q7PJof5Hn8eNUE006NWU4R0L5/GZAS1rQnvCmssa1l7uDPp+ZK7ftqr0CQs2XYz9aaRhTMt6OTEQIDAQAB", // NOLINT + "hmlhmclooobdkbaebppokhclngjpmlin" }, + { "SS", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwWU9jL8nwxXMuGO3WjM1Tr0ezfs0Mf7YG5MUto2KwpxjQn0JP4J8iN/AeB9teeF/I8NN/TqTzLcBzODLdLXfmJczHb4nfamDwqE1fO4ztg1RM+e08Zxy5nfSpHVWz9+4aFZKnA64Ok1Vm9AlnhXOnlLfjx9sdMFA4i+bFDWMpiwR4ZfloCjRFO3QyPUEmml6xTxgZ9RJvA3nXW/oE06e8mrlIF63rBjFxr+cy0NhWqOTMMRROBBe3p4JVvEBzGF0iUZhjhxLn4vh/iQZ888OZGkRKNW7D1T7UarlAr9JuSs5MKjexu/854RzT1UVbSblNdHlvI3juhuQMUY9MkLGqwIDAQAB", // NOLINT + "mamecldloceieebkiiefggcainpcmbai" }, + { "ES", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAupXUnjpEbyFeULe4vMUZFe/bIlbu/LUbDpQ8IaEhuaV/ju8MD/Bu+vQ+YuWeLy8eUNr1yRXilRzT3a9K+2KHbKqmJ6aUaVgQABcFQggFyX6JiFQhO99Jh8+bwE5QPSoSw7takBsTkkJmYmHaTHCcysZcL6VpbuDkNCpf+Srj7M3HAdCmhUy+E1S0y5viJD2XZZ916pVt3YobUNexp/8gcndlbxWMObtM00NSbkDd5gqVwwwkaSJGPRwrqoTLoACW7sr1pzZFSoTc5BT//l1bOB8CEA1Oqj4/Cw6SjlZCFxSqUTpqJD3ZcdgxaxZ5mscOOQjRVKAdlAHaowNe5NKYWQIDAQAB", // NOLINT + "alkblaadjjijngaehljijdimckobegga" }, + { "LK", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyjVNy8TpEOzRhbJjlfgzow1p9WQqBELpJMP3fkceq6lwonxGIbXaKmEA9Jz4uQgRrBw+vQ/Erj1x71u5Af4doCurfDNW1MwWG1B8DfqOTPn2b94F/npTUklkpkyVyYhRXF6xm5fKKwzLpv312pprkU/2DrVgKOYbiZtA8tvxbTujvlu5W3LBWsFIDo4zR022Tm9NHYUqS8c5st+IxAGwmNS82+q8cJ9na2OYfahlJUU+4EqMu4wR9oaitLIdCzc/+ChkkN8AcqHet2ogu8wR3Xbb9VdLerrB4jQrlHxkUc6cSbbPv35qubwkqBCN22qPxltdacoiUADB0DTSJo8L0QIDAQAB", // NOLINT + "ghkeckejkogaohfggjajgphabnpljokj" }, + { "SD", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxe1bzvCWsrGjaf83fMhQXe8dchZ6KBsSLl9kp4vPFz55LJSN4RA/gMuoMKNCzoidbYWkJk4BHxi1aP11RakwQzIKp2mTHpGS6x/ymfOzhOxs+QyMMW9lzLfGG1oBtfNttKkTyWxVknpgPB34KDpZIm8Yug1K2UOlsLbM5Y50JRMJ0c/zRxXuLS1DD1Owbct04Fery9qr1PlH0qbilmagzNvaxZ+gg3HSFV/9B1ChuaeJ5KIiCO/d8aBNFoa/55ue3dsOoGxFjxRv6F+g0KN+8ST2EcC00s11LTpI3oE+3feT9IHOG2gwgurVrc9PDo1xGznbJHkL7R3E28lKwX/ThwIDAQAB", // NOLINT + "ahhkhoeilkcpohjclhhhceeednkabkao" }, + { "SR", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwJdTzwLGZk48TVoI2UeT5/oTWh/hsnosJxYADkRbSFyviVdvAxvLBSUzDq2Herla2lms1GNUcpEZPg8acUaZ4ON+L5t8Z3n7CsLj3K1u3SjkXF0uWN76aDb8g/JvCRP3Q5OdLvkj9fyVkyaz8FooHI9xkRYf4Ov9MDVvZjsr2yPOASbMd8tuian/P/2PzisCY2ZxnqomDNfEgvSFq2DgxXB7lBpVlunZuIp1zVzKOfyBjUEXSr14GqJoF48ZQvgJQ+q6B3a+XQBwY9x3WlpoqnT/nWnGlVRDoqQ++2KH382D5npjNj+4sH0UNdbTIjLFH6w1OHI4F0c5lGhtsz87TQIDAQAB", // NOLINT + "dpngefdlopbcigbdgnijfdahfedobpga" }, + { "SJ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmes1FwI5P6LbmZI98DLfmuqnXKnczeUFtYb2OFjUAQp89dX5oXk8IWnCHxzIbtK0qWX+ATNLlptsFQE+/LSzOPAYkczGPzS/EhpTwnOOWi9B86pzOoSteZR4lqI2MiCWgVUnN2dhbFNTC3N+gc2WI28xPb1ESgKK7N9dFvC9hTRfEsx/Wu9Z4FyDn7yqM2h3+sXr3+S53z1Iif3Jrr9Q+SCZAE6BqqhgQLfMjBg3dIs0R8LAWqkaYkZtwMA8L6daia+aXpWp/qXB2/S2NjqCdmpX02sIgzFhGp7zXEGAbRh+NT2EXlYip0xwCQf2so9aNdOzvhj5yRuKRz+53wr3YQIDAQAB", // NOLINT + "gkaakgaijaclagpbeebjlacfopicemhl" }, + { "SZ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3oHZxy/MSwX5bPe5SeHe4XMoc3XsPHDLW+GRZtZL1eaWfctyn3CDRIYu7OTzHf8VKUtowG+Dy486XNPScEoOKA17I80L8ypNlUDpz7B5kiLZzmMjmUMm+57WmNALOK8wmIkg/JwGtsOGYZyfW4ZGSrNhHfZ4oPLRB8mBUUhhSAefzs0IyG+OqSTqaN+BaIVKwzPWtYJeesQveCGIQR2Zzp36zQBqJ1bBk+PUkAjmN5DW7fYn1NHT2gkdYgeafZRUmlYcct3XH8TwYzrLPqlUpvV6dh9MyuFDvz9jMT355+Fl1l3+R0YfGMJuZVFgVu1HcDaGuqkt9juKcbFI8rK6DQIDAQAB", // NOLINT + "lglojajnkecpjoohdcjknlnmkmelghdg" }, + { "SE", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3ENAltYMrnUOOscfFxG7ZnLsrFQHvmT9PGLeslqaItfO5j5uwKiAgt8qhsz4OKLNyMDVBj6yM4PscDjJSWxeaOrzA8Zdvrv48Do1YI/t32KVLIyLEkcEYPl7IGbnXuJOHfyGW2727XdTbgVI+gSXitzNo+GlYDyZtUl8Fc+gRjSfCYlwTG1PcwBSh7CIt15YkKQRV/7P01gB2LnYm69CSoilm2099ZXsUh4gkzke4rHje+NDkQRE5nKddC6BCaBbnnMCE94NVg8jr/yXfIQlJ0TgaI9V5XArdC5DT5cfeXDDDVMo7iPJRGVDIVwljmc9xtgX3JuIlxXJpkEa3YAi3QIDAQAB", // NOLINT + "bbfficebgamjnjbonmopfidnldmebfmh" }, + { "CH", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzK+FXBRg8v9VJ6tsUIxWimz0KbEBfTxHJXgvppnKJLZPFlNde646Lovli0Yh0hG8dWq0+tPbAZLvtUAM3U+Pz+NKZ6sc9Ie/XTpgLeWSXgzbEniOJtBFWD3EY9M4m0GWYs/r0g0NBH/rfSlfcVgZ8MQlFMdpj/WxjD4J8Za5VRytrlpKajzn2fC7RyvWeA4/5XUvCnDRk7OJGHQrlHlS6+atroCsHIvjPu0fERe/+RAEcm7fWe02v+qeEfk6E8SguavAnDm3uMaE+6qdsf7zal640Ex5fyUdgrzf05ocYBzTECRb2jVjd3zMMfyplgmgIpB4iCRR/XOq7UJ+evxEXwIDAQAB", // NOLINT + "ejhkplcmfikggnbclnmlknehecbgkpoc" }, + { "TW", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Fabg97cpP0pReM0JqQ/rLWnNaxCqLHpE5R50CNMmxFy7t2321rKKg0VnkOc1T4YEXo3da1btXOjRvyXf3Tei4+K1HGnTyYTxhC4MeB3lXioK3Dyd6G2H/B1RD2O6VvGwGohTvrUy2S/9+MVkIeRtN+Zf66wIvLOjw513m7OOAotl8uq0eT8JSbyxVkKKZ1JwxvgXx470epEl+NHx3fEnUhK/2ipqoQXJx+5WkG4IymUKa4uoipQ4gMC+bX/fjsDpQqTnYd1r6tloWGkkEgo3HYXxcSfjVd4Bo6KtH2lyTA8TtskfAZ8VHIGTiJ5HJTYQIFPozb0m4vivC/HbisF1QIDAQAB", // NOLINT + "ingafkgoaeckllpagcegobjkcgjgjgcn" }, + { "TJ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7gq469j/xcTCXuOAbqaY8MAVrqQ1UMdnZB8Tx/OrOa1MFrBjXHYrUvJu+ViCSjTYwL3R8fnr0WSov4UMyoU3AxbM+0cUb172261BazL4e/Oywj08QaB4nZqZw7VlL+z+8E3qbpKknourg6MiD315T1YXavtu4Z3bAv6kbMYkdh3s7VIyaRARrEEOgseTM4T4/j12Q3WwoTW6NwH2WA4DYGUR2eswm9Y662r84EzFl7tss14SuLTuV/B48qCn4Iog+RfLwegmnxVO41gJFwF14cwAKMVlECuTT8xbJOuUjiGUMh9VRYiQAPQpm6f1Du/9S46T4AZdOq4Af9Of0WoucQIDAQAB", // NOLINT + "jalidfnaieeenmfadiepmgdeaieokfek" }, + { "TZ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3ttl9zPXRkBXhymRHpbDvJgegmBp7TSe4tvz06cURBoiHdzBL9/dGX7xvL4ThsKbcGHdED5q0CbSg5L6B7FCkY6quT3ZnSNZ0Dyt7aL8w9UyVUIDvE15tysolO45dM3ag1uuau5vIB9gXlsEp4euJbYSwUUKdMR7Iu0iWNnV7iWYCBweH45QequTpAH9wUAIvCni2OXHyZ0DA6meD01P10POIsWfY7HI9nXLCwH7yKFw+v5v/mNUuBfoFQmE1yB82D6YGe9RXZ+2qWT3hoyjywbRfh5chwIuXlOl2ZU8v9PcV0GG+FcJ9YD8wdGit7fppZFhidi+DasiJDCcbElXYQIDAQAB", // NOLINT + "egpihmededanahfjakaigfggofjemigg" }, + { "TH", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsgN42D1dvTGPE3IPi6F7zZv2IkoQb/mTHw/ZuzNXJIH3ht4HHJa+aFE8c0t/NKz7uw3YJbP8s3IU4UKX/89v6IU/bTaEZYqRKE6R7x2vG20AmpBEG7K3U3rpFkQRqrifjAqgjFN1P4D/eRNlKDbT9WjeiElwYNq/XbDy40OSMfvSrnWf90mrxfeZ53Ny250Jvfii3KpunvvGKomt4+aqfFzam4E3z/jPCUkJ6lw5zmy9bSz9wlKSFeeb4JHYQFI6dscWy6dY9S5heFvzNtD/jZ60CWNFf7AN+tUR9fvjCKHOYrx/5C1OJf1pBhkrcr05yhV3nTzezCCc2VlSc/yBDwIDAQAB", // NOLINT + "ooaakjidfnpkficcldikjhacfjclenom" }, + { "TL", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAypgn6KYiAXj0j1j+AYkhjyDP+5zr6V25zsBIS4JR2r4N3tZh/e+p6ndAgQpVskrcxnnRa49O4z+iPAekCJNmNq6asp3nx5lNwSQMcinWTwzmPJwzTxDKccJTdTOPV+wsneH5WiW4qJaMp4xByCgpYIJuZoYtz6ZFBecuAgnraYdfHK047FL0JHoP8TvPHVLdqyCJqijg0A7m4HX2J0LoZP+RJUhXUW6kbYaSZ68FUfc96kXitpG7ogZRO++AozMygpJ9xz0c7+9C24HAysRe8I9QlkWh0nR6PgcVgR3xQLEOR1eCQHJ++aFuz7M3xtgKiOxyyzBLmHvqIkvx+bflRwIDAQAB", // NOLINT + "kfljjfjoekodjhpoopmakmnelhjhlpjc" }, + { "TG", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA93QRnOFG98Jq8/hn9zaadTnE/AOXfF6plmR7bUKpme4CQpw99uulCv0s5FijS+VQZp2pxkj/JTvhpV8+KYmW6d/q+fAXf9p7nQ/osxThvskMyYgdqIHvBUiAL6VlNSsl/8QdyhpPQTD6dpf0gTaJedrP2Z8cvgvCk0HsqYfkTf7JPE4QUM6u/5qcP3BfOvPN4lvq2VlCJtv8APqaGu1vyaThZKH8uweHMj2HTC1aXa9p4SHfZduuzKNYCC/6Y6bSgn64gLm3nU9EvFHIDCyXSZz0nIOi3Pbe0kaIGJLOk83Uc8ID8jd5Hlaz2lu5wBUWEarbUkXEVADxtDfSzWxJuwIDAQAB", // NOLINT + "hbkbakaligmmpbejgnjlcmajjihhamol" }, + { "TK", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwse+mLej6eTl1QEt4y1yCJ5I3vsLSWyvwbZ6MD8xhzRoOYj9AjxJQreCkbts8bcmXQspXghgVN2N0F08bymmqpjcisYR9474dfm5KCX7Kj8UDgsLYVyzTs+gQQZ7cCM02p73yM6WJoBnhefQcWmXlSPDmqYzIVbM2tnw+LIpKcnlRgfXqyc4cfQF3wC5fK1okbQ6SxDIo+iNV1KskG9gHqrxaCwD0Hx9qsQodrBUBuixq/7I2pItQRRfbmDRpXfX4O2WRw55LC0vTpHM6ERtPEF78CEKvXvDfBCRev3UgjxWD16zOGiYKVfYcwiJg/ENEC0yY1APAjRVgCCCPJBChQIDAQAB", // NOLINT + "ejlhmndnokmakghdlabbhkobmmcjclll" }, + { "TO", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp/vfByGSE9HkHsVic/pi7e9zExj42qUQmFyWC+heuWyD9yek9rPDO3I66sK8KpZViCgEJDBGPgp8LFkigLQRo/R/v7EQAauPwAewsp/sNIWqFZf53UcSh9DQ1VZ8iI4mk48iuzrGmUdkOonuDpS6trtRJzlrkRU4timamcH+5zkvYUvQcO/1xlg8zOC5rW+LtnabNev7KxilzNMMrfcAfbbw3caxOyMOPbj04J/DxZzJkLggKplgk/7vBkJ9sx6NILKP2G7ZoPlfD3ha4ZNicK2I/kaPMT2ZMwmevgE4oqdWFQrHz5EBGIQ+CTuSA7wYfgmOHgaGWtTr9Vxv7TN4AQIDAQAB", // NOLINT + "pjhegemhnmpbdegelahelfnjfplpacop" }, + { "TT", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwKQgY+4qymOsBQ7oALzVCYVvJZbeUm3ZvpxfqCP3uhv7ehoDNiL9FpTxppIeJj/cRTz5+FD5Q/trLHmhBHwU3+9xtNI8yesEMXO77f1n4gG7r7SOjcgWX755tSTLUGks2CUdd14gly+SPFCLc4IAZ39pGYYk/DqihKgkJ8JQNPmDZRmbsjYUjzavtMjRymrsW10Xf2aWoKrL1KVIZQkSkXKXKJAHkSW0z5i6Ng4Rxun0Iuq8TMj6GxG49cFpNOXtAa6gaC074kpMJq+WNNTQhyxkc6B0zhpaaHRkcVoLIW98zfcxqp3Gg2PDl7JV4Qaw8dSMm14L+wjT0fwVXcDG6wIDAQAB", // NOLINT + "pknhdofcobabipekoiedphjhoopfclfc" }, + { "TN", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwfObXFYvOMWorlCa/k/6EFXKViVWpYtnsznpxjS+RQsFVOojKhA5spm82Un59eKckxguSjuKa6LVRWPzcGa8O7pfgpea7bGnbhmTalSAy0rS5W3/+L+4tvJxxbambX3QOq5wP5mZBoU9vzRhm1Ym0kFFhpV+vAeYvccQfo14BJMsOnD89BclH5xmNz0848UgjOysB+g9wDQ5G3SwUUhXRlPsfJLzizJCtxB/MhyiBVyNmy4pze7L07sW68NzGFnrMSkeiRrX4+1GR9wXGQIcyD9Ee5TlOW1o9MUa4/0QVBP5BFHY6MTDXN7M9zTYXLzwYukQN30fBIPk+mLHNIUeXwIDAQAB", // NOLINT + "fdopinddpncodhidjbejocckeddjejdo" }, + { "TR", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv5xDV5r3BRVEp/hTbeiuRoqMlMSy1yT/H9ONv8y9+opF6MGwuIIMyzbH3R9ycNSTvRVr4zgCvSaJ4/F0tuK3eS7G2Un8IsOT7D0KFp6FK1iSQRV47yfSWM6h3IdAaa+DSuG8SnKDXXCZdW8iaS47xYQur81EteqghpaChSZbEqAcUpy5UGy3ErjQLhtXu0jQtA1IXcK4vgUrqzCAL6xHnbo4Qr6h1OBLIHnSYvSACUflK86/x2mlZgIuM7L1xohhex0jYd1BnBYTEDAAWj8UvCq/JONJNWODbpJi/pkw5IV/siIf1aYiqPLTzXHF2RCk0tJ/9rrSNsIBFUF3//22wwIDAQAB", // NOLINT + "dglngbgepdcmodilimpbpekobgiinpdg" }, + { "TM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3V1mZWlNVg208cNnS1k08zVU/g+9GntOuv9+DdVJphcbra1RPugcB7weH2YjPd2/ubMc/Z31FXIFBWwsg4IIyRxBkEUoF0iKd5Zoeqp07/TdfTNsr7xSGXbf7T/XeWOnQMwwo8YfdCmADhEq0Lu6036fMUqlN9LwtOyRklsmn+GuMznVvWjz+E545akFL89zPwMzCk1dnRwTXjw0bTfSTRiBDVHonwRMcJLEHeK2gvgZK6NgfFFA9Ms3moGZnpX2cjwLn5izoesKcleBwua7TsGE18Emh4AFjqT4AHbJcIIweKuS2/FPwXQ59xtIStx5VpbS5pRg94HOzt3bA7GwQQIDAQAB", // NOLINT + "nedabhbnbfeigbngjbjilpeggfkofola" }, + { "TC", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu8Dq41m65TxSwwYsPWH7tqDYxk/d18rs8FW44SeeRmTqJclx5J9qdYSdBTPYSLC0xk6WsrP0hsjKJXbJ5F9IJsJpvCgoxl/77ho42xTtgz4ai9qT3azgalR3z8n4jgcM+Sx05IlPolU2tZ/4Gg2/VLLeN3GFG4g98U0GVdM5J5cOtscYt5d8DLNegaLNOEnC/dh7vgzlicVF9viAIviJeum5HPBaV4lJkBlv4vigWGAfL8uq2ftIZ+w4D6eRBXqNp+uXfI9gTaB5w73KeBHQuawUD+5sSIomCMRH0cmppRJljbbZIwOy/Pjmb04N+M2b+7CzpMiZ8di2P1c/qLWUQwIDAQAB", // NOLINT + "keooabejaabjknldhonajmoncdhajmmh" }, + { "TV", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm/g0JKxBy0KqaNIiz3KX2uC6NKMbmXUN68v4b3jiYn3Sf1cEYnojVZfA8sdxaa0myBae1cvMsqT1yQT/zeFHMIRLWlzJb8D/QZEpdK9eaq8w4Z+8bn+BeqdWA3W77ulXZTFBrOD5al7GkgDN3EJKrrtl2UghFSgls/X3jtzDu8lT5e0rs562PCB0n2AoFvxamxGfwpfVXARh4KiVEsR5h15C6tKNn9qWOgR9EYcqctHF0ICguDW0eRneC6QkPrOqsCWqihUeZY4eZiGFZQ91t9CCzt1xcEyyPNfALfrOykjkfklvhuf7hWR/9WHagGMY3JaKghzB6ZHrBv0FvwNJgwIDAQAB", // NOLINT + "ggfhbkgnakddamabgoiecfboeebefknf" }, + { "UG", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvSXjYYzK/xpruIS+f+x1tse7ebHZAsa5Axtk0ghLK34JJVlwwLIlFNp/OMmtq1N9AsO4vFQh75WjUU8vmbbAttE8lwgGoMaQIRlxbkh+77pGRbCaj5XPHPMV3+hfVoxb00Sg+8BJVer1Kqtgtsv9yrDAcJVkKmyLEfCP4PiYmURtnxqt1XMHH5FDu+QQ8pCSz4n7YFwAerkT/WUBd0aW6r/GcpJyvhSDQ0FTu+Jvn/iG0r3cq94mCGwT+AL1oB6gc3G15x9ckLec/BXkoZkEs0l0SWhOJ82dl02QBGbnQwoQSaFNpmjhrX3n0NxhuZkNxxDEEnbn08Igg5zONcppYQIDAQAB", // NOLINT + "jpgghpnjjgikhmmnlghhjafgojleobnk" }, + { "UA", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz5SOW26A08K328RrkMMZZcf8SzspsBcnWl0DfQNMdOZzpU3qIPK5h/2k16DOfTCto7diPfoAXM2XQ6VdwBBOzVbjFuJ+p9c1QLqMpWH4df+TaMtvJ23vfggAhH66R0K+wFaTDl+s77iQYGmQydbZ7BfYuVF07M8THxLS+I/+L8r3gRCFoZbG/vSlLp1iaCPi2Q27WpSWWBptbxjAtvvzPx25iNoJKHuiHY8ZnfLAe9gekqaL1v+Dfp+LyXn9nWr2dhZqJ1ktlGEemKhOD4s2tN02o2xQL5T5fvC6o8uI6u1bQA+gwy9UIpdl1j3z76t/DcIMsXmOuzFB0GSEHZM16QIDAQAB", // NOLINT + "ckkjalncahmcpnlaekccmldjfkkcfbcj" }, + { "AE", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAss21zhpZTGuZmrcK0cbPjo2Kix8c8yNfdLh/RWDcVn6UtL0gmQKrDmqL/TKxvGCWAZ0msS83kQCfJWtEppPlXbWEC0rHozlwGaPTifPMZeYwwBzGG8PZE+MZVrSzAy0dC2QgL0Cm9aMJeEy+RUyvV96MmE/Xdzr3TxlYApPYoPYR6Mctg8VykBCBn4ONWs48ROSRtHk1xrL/uU4kSAVhxOIVOw7hnq4LzVLtmkm4vbptxwcXPNXmIbO7/tQ/izVNPHc8UF1E9dSQQm2yDrKU+SpNnmYiAfa0Tq+NjP+D4TlybDH4J4DV2wIGcWDtCapEigYR4fCAm/QEmFtUZyCWSwIDAQAB", // NOLINT + "oihifbodpjninnbmeepdapdmmhjojnda" }, + { "GB", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2LqZaGPvWc9IDVNQ5Qg9oet/Pp4Qj/Ihm3HfXx+aYyrAoS1raI52rZv+qtVE8iCN+H5Vy35S4tKifwflUvxD+BRVLsxdjrCMPU/PcEoBAPB0WfNrJwWYSnT4r+Y8PHBh/ujHyk3IHZKkT3gAIh6SZ0MJszHqnDxwuDdpuR66HOfuy+oJ1SlaC4fzfFuUlSbobY/Ho56+Y5QE9yvXCwHIlqFLip04TKO+KrjfwS/+PP0ewq78OP+I0qrv3dw2zNGijJDJk8Zw1Lj9D/BHI1HSNy1RdNM3Vk9ufT3TXg/pR9fq0CLCLkiedu3qdmdu/T6zblbF8zgh+Ehuh7mk2MHgMwIDAQAB", // NOLINT + "mjpbonbjgpinifgnneajcbigekbpfige" }, + { "US", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3RFwHEUBupykC4398KzN4KwHMF3vo3YkiygxgTDc5gzhwW48mBxcO9HDyor/PqcTijgyPsquFZ2VSGgvO7UYGlZsLl4ZjqXhYNVgj9StxlcBzofGaKDSY2lf9hFpvR1u45BQdKkv0ujo7rbU/wWnjMoqiTshgkdLc0hRczdHDAYpj8LxNi3Qk1+OMuzh7R4dyM/xC0gE4nUSjvWH9SxK7eAnK0TgwQp76b3DyG6ubKN+L6OL4z6iR24wFYXnQJMdvgs7X0Yge3M6E0j/6IyVFA19nsjJ9pw5xNiJLgNtGfOed0RAHh32EtQZ3BgDeD+OSBEK+U1+iaYQYGm+POxwHQIDAQAB", // NOLINT + "gccbbckogglekeggclmmekihdgdpdgoe" }, + { "UM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuJsznC7v6Z3xBVV8aMBBysb3Uc7mzADFb1npnA67MEyswEkRo39LzrLJstWWk+FlqBdaJKBUVuiOG16fVneHTbd+mYI+8Ohw6k+MXeMAlG7GnFy9xxRTyPS37e91g0wL8Y8ewVozoFXmvHtaKY92J44Pt0SEmZtTBimOd1rSkCSLuPjLvFry/R8HI+BtC2sVyzibK3CSolJht1C3omVkBNClPAhnQt4pfuYvueexbac16B7BFY2zPuNOh5z5EkclxsebZ659+3BEHt1wwCv6WOfPhjttzLYZ7ox8eSMzh+q59867m+R8B7HeFbwQDV7AHa8+QvQTioZQzXqoq3Q2WQIDAQAB", // NOLINT + "pokcffijikcgjnbjneohomgdgbplibdm" }, + { "UY", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnSGDCPTG6g13ypRlCMsbheFN3APWH3FGXYhUVy7d0daV2mRUMvwnG0QN/moIa44Utlv2cgtqOgwBA15jVBeLdaWR+s1SWpx269/HnfbmxApB7ySMJ/zlSq4Ft+H6oMNtqDPs/p4SSr62WELRX7nOoMvNUFCZI0LN3yhHbbRAEnrY3C+SWHwrJJZACxv7hQCWIZy1rs1SwYq5uzloM3+qo1uTN5voY8zNY10WYQOI7utdO6l5b3RFxvckXdPFaqUElEPiqxIbCjGy7fj74Bfw/60tTLhy4bfTBQET2rObj2WVhpo6874VsVJW1B6peZdQfpDbV/apOqpH1Coyvd+FtQIDAQAB", // NOLINT + "canclnelbbnoepofhgidlgfngfalilgi" }, + { "UZ", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3Gahx4gT8PZvdWsFnvlflbk3Yti6e1KcgM19XbAdXqRvNdyIiG161U6ynd7c5BTrqv8jdCfVy+XsPDbY/956c5LirS/+VMMEXVKuplPiR+vO3qeE7ZFsoj1tiG2tunfu0kiXOmAiDevdLA67IWeghSAryLFhMkbComcXbPTRKPyZNLSzBob37h64HhYAZYXf16pR67JfHdiOKXehKzc1Y4gfGlvPau7p9RH0ODqMYAn1Q0GNZx1foOq39jvqJcINQtp8W5gqpwXRxsIxxFrd5u/OwlaIDT86ACGv43Jr1JhLyDtSmmhArUIV4+PSzonpollVdNmIPx0XsdkPjRD8GwIDAQAB", // NOLINT + "cdjipkfhmebenoigmldamjlmmafpocce" }, + { "VU", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsOUPkUC6gbp/t2NH90nChxWGLfHqhSVvCG0PgZ/LIe76aeFfe80MMJOFkfMl1Y0H4uxzNkI8PJI98T4jUobfGIKgRtn3OK2XBZloRIJ8WdalzsXwNv5Q8Ifwn+kZIMXsJl0gQP1UWe38A8sUMV7dvPJDE1EjazRWi1kpNbvbiWS9MkyF/9Ee7MHK9EBTgahiQjqY8gX3QUy15cO8fuWgp03UVrVBn0jm1KkyksC0mERrPgwDjkByD+i54M7TksnS+x5n1RwoodTLsD9fARtQhxTKfae+sVhK0WRL5oMxn5R4vg64Q1x8OyRzcYGLsxSwoqBUJRn65iobBBFf28iR5QIDAQAB", // NOLINT + "jlofmicpfnhpdeokpjgfmkmgnjeolmne" }, + { "VE", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAybrsPHtBqBdvflcHY3FVzgRkQ7L+gLuWzX8gOWIxqM14UJ00VJvGEJ03QMFQez6ieR/AZbJAeCjHElz3zWMsPSFu9KNX+ATG7MkZFxv9OVjTy/GgMWf0e3WcX3S6W9HZbyiE+K5CX/ia5J4wOZtx9BMkT97kdzeXYyeeZ9u0uJsFbSBzYj/21Vp47vPS3IwV5Qx5fVQkrqaC73ECLtyKA6kt3dQaNMDi/+Kp1ad2BNkoNKq+03MTaJDoj2d2ZjVYCtDxQbP/Wo4aosx85bkxtR1j2Z5J0tZ541WdGyP7yJ+GEB+q7T2rbCsHnFat58D6Auf0bZwKDaCXdisWrK8JqQIDAQAB", // NOLINT + "ebcoibiagpkgmjjidmmdanbckdofjhic" }, + { "VN", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6QxDFFJlGicLZtwLMMgxINeoORV/lGjyFf5yC4u/Nf7c5A0ju7Gv7YtJwsTddITdlDDRkiYfg6bSbRFm7qaxpki1UFfb4cnc9N8F/XTGTkBoeRi78zGSzSFAUGfl23bb9epLRwPu1rpNkHlYvtsPvsg3INjNFMYgL6Q3IPjJNra3ZmBSrkauJQLv9/DGAgJLRRcLkGKP1PjNbUemsNuKHiLSjjrB0SBA/UaUqduwPdWaIurjpBpb1f9V0mYdF3T1SDGFVBWwA81eekFVZxqOmFec81OLIiAYQmBfAUSlKtLUJRNzDynv43WTuPoMPp8NuAcYgRsqznKn4bzXBVcyJQIDAQAB", // NOLINT + "pphbpmdlkglnolofoaifgkbmkdoobalm" }, + { "VG", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqa6ICTvKKvmrCiIde+3YWet4FBy18xFnfPgtSBQ9C4EhkECR0F9UsXv3yDlG47mlDquadbxhMnXM5UpnvTz5AFXZjoeZJVOpvY1YwV35SW4EB3FaMHAhIuUqo9JN9EiUnbpwpIE/FKt7Yhanwy4ziZM/SwTpFFt3+8nhXbpbJeUHVf/gvt1xKlEmvYzkBdiWA7ueRI7nexVoYgl1n4yC75g7V+kQNPPHCXYY6PFUhHgfKtR/2f0FD9OThL26QhLYQ2OfbSXgDa/LblhdEO2I+lDqCTAXt6A5NJIaPQRDTaKjOMfRPajy5PzYekFGPyU1YEjEytDEhMiqe4n/7P+3SQIDAQAB", // NOLINT + "figjhageemejcpjifmahldhnnielokpe" }, + { "VI", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1AKA+EpwR+vf/5YThI1rwYR6MaNYIeGZ9Hy+NdRm0PkPmfLoJM+JXIMDvtWcwG8TJfkMeCtntj5v3H5oNpu0+LxVWZ5zcRPBSnfeol9OLXaFCHZHVvQC20HXL/2W3g7hkL4iUWk5KV8gER3oLTnLHRY4rHncmbMUtIsuRbIR8lMmHNly1LLlH3HbP6E4Crw7haLOl0m4qtsco1RJxiTO1UW1wde+Ta54wAK6bUsUhhCRkW3Rt8ZT1wa9dIJ++CtsgiP69eJEZMqEaq+qpOXGuzM+P+s/neDRARUYFm8wB5mVqNijaP7G7zz14J5A4daAzTgv53B8SqWCvsDSEC0y9wIDAQAB", // NOLINT + "peccmioepjcacinpipdamkkigebajimd" }, + { "WF", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtQeWHrwNXOrR60DnE9sPspkYKUD9HWo6LmSHeTBgUVL7qmWHgl7B7uPlJ8D7x9vmrNa6s1BQmdSf8py3bJh+EllI+e0eFTnRgFaU0PWVhngYiamjC9kGKAE52gp87jb6tLNCY/MetZsZYQe3r648QtltPtGQCpesemadU+l01F6Fqa/Prf3/rBUOpRrkig55cjE28V1PBsZOe19M8jKg9giNl7CyoJOpPjfwvBsFgmwMlNfdKh/rDlqKudHm+mfz+aJNtJ8zf0Nm0Zgj2KMjYZHzi5dXv8GmLojHkQKoU7K7v4Go/PAEwj5SA6dzriowElTKGaTv0G1M60mZyE6CIQIDAQAB", // NOLINT + "degjpeocmpbkemeeiikpcppjgioomlag" }, + { "EH", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzRYQzaeQxzi/YRJ+PoRH6V0Dpf6d66Yc+ShsfqZDt3UsMkf19B22FPrjwMV6gjq+dyOz3CYgGC6MkRePMqRybSUzn/8cW5QSdUwyDlIUFVN+BQWQ1uA7ZfG6O41Z9Am4EteuMoPU9M+FblnG/Qs83f2z3xzQiD/F+rqlKNJFQnk3QG+OD/2b75Ur/a84AaFkxb8v035My181NRpF6iXdeWX1vaYlRqbxpCMFDek9pNoBVLK3LC5KvhkKccy6tTYsam3l4mZrIVWTtC9Og46jxsM0AHbi10WcoIzOQXikQXL4YgBp/BXnaRWH4lWpY3DSKZlo9ue7LKxQbtehhiqLMwIDAQAB", // NOLINT + "cpfnaghehmghdmfpooifapjnmiaaepnn" }, + { "YE", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz9YZCLtsLZ747Uxp6oMBvz6Qm0emud2o0gFTFYzsRDGuMH7PMRS+UGsdIWv915Esgn48CohpW8N8AKeOJActIzZ8EZdCh2CygGDb2vYHi+1z6gC/fatNalMWGi8CO2r6FxwnJa8XSjQ9LBXTQ3eNUXnrAX3aK8HctYnjSfXB/FElQVwOOwyfu2+tqyu/aK7Rlp72l9T4kga7cUraBER+UMHbOqVKKDMAMNZsLxjvOcdUNRTpoZsROrtsgPf8WpfDFvMVTN10yAxpZjjzO6/rO+7CIrMsARZ6NviRCmgSExihGUHE9d2mXYIcoNC+D3s4IQpXgTmctAwNJrRjcAoxvQIDAQAB", // NOLINT + "jefjjbedohlgdbmaiigogpmgonkleigg" }, + { "ZM", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn8LTAORwUmEHDIkPKTfpWOPxcbXDO1srw4Ot1g8sxlJWLFtBeAwn3DF+TczAYkGp9q0p2/xXCcxYk+fyd1ZwkRpywLeEm+dUFONXoFTsd762+s2grhIrHDmo5XU47CfeeIe0slXY1fH079wxyUm6WiwTKRqLWDk8Fdeok1hA1eWn/WD/ncnIAS1QfKEcpqpqajaTCZVW4oTc+0QA9hZeWaRxzUdaQ9AxbG+Ude67A9GhHOheSg9P9GRcWqv95NEaLa7W/abKdYT5RONw3HLe2bwfTLfR99J5v2ksOoeVdgPG/7jSW5iTRPBrd76ubY0tqsoa8UR33KTCBVe9tFs2pQIDAQAB", // NOLINT + "bigdhcddjgnchmabfgijdflcfbgilkff" }, + { "ZW", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2IkpF282zaSaCgmEtS9m+XpLz177vx2I5na6bJIDlh0hFhzm+WYsBoe1k5tndsMhp9y41KqnJi4g2lrKTSQxASBPwsRAuofFoJeFHTsX6NRAg2g1sitvfj2aAn26LVaa1IAqkefX/CVywMKpYaihteVyggh53epXrwropPBNWwSY7Ro5dJaEmY7EzsBWWjWOR0UtffYlv1CdM8mr2YDubZw6lSreBEtU0TSHcE8u/X9cIgDggHzPA2p9GtAAgAgNl8/ZqztVHupeC08rPRQfPDmKcAGKlMXE3jb1IoJhNvo+j6jDTAC+jb6JOpCTvzkEXp2ZvC3dEPCO3rqEh+FJHwIDAQAB", // NOLINT + "gjnpmnbnfgdhagnnjcnaidipccghdkeo" }, + }; + + for (const auto& data : regional_data) { + if (data.region == region) + return data; + } + return base::nullopt; +} diff --git a/components/ntp_sponsored_images/browser/regional_component_data.h b/components/ntp_sponsored_images/browser/regional_component_data.h new file mode 100644 index 000000000000..6c2fc1f0c7fd --- /dev/null +++ b/components/ntp_sponsored_images/browser/regional_component_data.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_REGIONAL_COMPONENT_DATA_H_ +#define BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_REGIONAL_COMPONENT_DATA_H_ + +#include + +#include "base/optional.h" + +struct RegionalComponentData { + std::string region; + std::string component_base64_public_key; + std::string component_id; +}; + +base::Optional GetRegionalComponentData( + const std::string& region); + +#endif // BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_REGIONAL_COMPONENT_DATA_H_ diff --git a/components/ntp_sponsored_images/browser/switches.cc b/components/ntp_sponsored_images/browser/switches.cc new file mode 100644 index 000000000000..870c477d0b1c --- /dev/null +++ b/components/ntp_sponsored_images/browser/switches.cc @@ -0,0 +1,20 @@ +// Copyright (c) 2020 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. + +#include "brave/components/ntp_sponsored_images/browser/switches.h" + +#include "base/command_line.h" + +namespace ntp_sponsored_images { + +namespace switches { + +// Allows forcing sponsored images to use a local directory to find +// the photo json rule file and associated images. +const char kNTPBrandedDataPathForTesting[] = "ntp-branded-data-path"; + +} // namespace switches + +} // namespace ntp_sponsored_images diff --git a/components/ntp_sponsored_images/browser/switches.h b/components/ntp_sponsored_images/browser/switches.h new file mode 100644 index 000000000000..fcb97bd54509 --- /dev/null +++ b/components/ntp_sponsored_images/browser/switches.h @@ -0,0 +1,21 @@ +// Copyright (c) 2020 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_SWITCHES_H_ +#define BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_SWITCHES_H_ + +namespace ntp_sponsored_images { + +namespace switches { + +// Allows forcing sponsored images to use a local directory to find +// the photo json rule file and associated images. +extern const char kNTPBrandedDataPathForTesting[]; + +} // namespace switches + +} // namespace ntp_sponsored_images + +#endif // BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_SWITCHES_H_ diff --git a/components/ntp_sponsored_images/browser/url_constants.cc b/components/ntp_sponsored_images/browser/url_constants.cc new file mode 100644 index 000000000000..f7e46bcb48c0 --- /dev/null +++ b/components/ntp_sponsored_images/browser/url_constants.cc @@ -0,0 +1,14 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/components/ntp_sponsored_images/browser/url_constants.h" + +namespace ntp_sponsored_images { + +const char kBrandedWallpaperHost[] = "branded-wallpaper"; +const char kLogoPath[] = "logo.png"; +const char kWallpaperPathPrefix[] = "wallpaper-"; + +} // namespace ntp_sponsored_images diff --git a/components/ntp_sponsored_images/browser/url_constants.h b/components/ntp_sponsored_images/browser/url_constants.h new file mode 100644 index 000000000000..18a817dfd583 --- /dev/null +++ b/components/ntp_sponsored_images/browser/url_constants.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_URL_CONSTANTS_H_ +#define BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_URL_CONSTANTS_H_ + +namespace ntp_sponsored_images { + +extern const char kBrandedWallpaperHost[]; +extern const char kLogoPath[]; +extern const char kWallpaperPathPrefix[]; + +} // namespace ntp_sponsored_images + +#endif // BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_URL_CONSTANTS_H_ diff --git a/components/ntp_sponsored_images/browser/view_counter_model.cc b/components/ntp_sponsored_images/browser/view_counter_model.cc new file mode 100644 index 000000000000..1c957e682941 --- /dev/null +++ b/components/ntp_sponsored_images/browser/view_counter_model.cc @@ -0,0 +1,50 @@ +// Copyright (c) 2020 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. + +#include "brave/components/ntp_sponsored_images/browser/view_counter_model.h" + +#include "base/logging.h" + +namespace ntp_sponsored_images { + +namespace { + +constexpr int kInitialCountToBrandedWallpaper = 1; +constexpr int kRegularCountToBrandedWallpaper = 3; + +} // namespace + +ViewCounterModel::ViewCounterModel() + : count_to_branded_wallpaper_(kInitialCountToBrandedWallpaper) { +} + +ViewCounterModel::~ViewCounterModel() = default; + +bool ViewCounterModel::ShouldShowBrandedWallpaper() const { + return count_to_branded_wallpaper_ == 0; +} + +void ViewCounterModel::ResetCurrentWallpaperImageIndex() { + current_wallpaper_image_index_ = 0; +} + +void ViewCounterModel::RegisterPageView() { + DCHECK_NE(-1, total_image_count_); + + // When count is `0` then UI is free to show + // the branded wallpaper, until the next time `RegisterPageView` + // is called. + // We select the appropriate image index for the scheduled + // view of the branded wallpaper. + count_to_branded_wallpaper_--; + if (count_to_branded_wallpaper_ < 0) { + // Reset count and increse image index for next time. + count_to_branded_wallpaper_ = kRegularCountToBrandedWallpaper; + current_wallpaper_image_index_++; + current_wallpaper_image_index_ %= total_image_count_; + } +} + +} // namespace ntp_sponsored_images diff --git a/components/ntp_sponsored_images/browser/view_counter_model.h b/components/ntp_sponsored_images/browser/view_counter_model.h new file mode 100644 index 000000000000..304aa1b75abd --- /dev/null +++ b/components/ntp_sponsored_images/browser/view_counter_model.h @@ -0,0 +1,37 @@ +// Copyright (c) 2020 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_VIEW_COUNTER_MODEL_H_ +#define BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_VIEW_COUNTER_MODEL_H_ + +namespace ntp_sponsored_images { + +class ViewCounterModel { + public: + ViewCounterModel(); + ~ViewCounterModel(); + + ViewCounterModel(const ViewCounterModel&) = delete; + ViewCounterModel& operator=(const ViewCounterModel&) = delete; + + int current_wallpaper_image_index() const { + return current_wallpaper_image_index_; + } + + void set_total_image_count(int count) { total_image_count_ = count; } + + bool ShouldShowBrandedWallpaper() const; + void RegisterPageView(); + void ResetCurrentWallpaperImageIndex(); + + private: + int current_wallpaper_image_index_ = 0; + int count_to_branded_wallpaper_; + int total_image_count_ = -1; +}; + +} // namespace ntp_sponsored_images + +#endif // BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_VIEW_COUNTER_MODEL_H_ diff --git a/components/ntp_sponsored_images/browser/view_counter_model_unittest.cc b/components/ntp_sponsored_images/browser/view_counter_model_unittest.cc new file mode 100644 index 000000000000..b8355ddf1d8c --- /dev/null +++ b/components/ntp_sponsored_images/browser/view_counter_model_unittest.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2020 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. + +#include "brave/components/ntp_sponsored_images/browser/view_counter_model.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ntp_sponsored_images::ViewCounterModel; + +TEST(ViewCounterTest, BasicTest) { + ViewCounterModel model; + model.set_total_image_count(3); + + // First loading. + EXPECT_FALSE(model.ShouldShowBrandedWallpaper()); + model.RegisterPageView(); + + // Second loading. + // Image at index 0 should be displayed now. + EXPECT_TRUE(model.ShouldShowBrandedWallpaper()); + EXPECT_EQ(0, model.current_wallpaper_image_index()); + model.RegisterPageView(); + + // Loading 3 times. + for (int i = 0; i < 3; ++i) { + EXPECT_FALSE(model.ShouldShowBrandedWallpaper()); + EXPECT_EQ(1, model.current_wallpaper_image_index()); + model.RegisterPageView(); + } + + // Image at index 1 should be displayed now. + EXPECT_TRUE(model.ShouldShowBrandedWallpaper()); + EXPECT_EQ(1, model.current_wallpaper_image_index()); + model.RegisterPageView(); + + // Loading 3 times again. + for (int i = 0; i < 3; ++i) { + EXPECT_FALSE(model.ShouldShowBrandedWallpaper()); + EXPECT_EQ(2, model.current_wallpaper_image_index()); + model.RegisterPageView(); + } + + // Image at index 2 should be displayed now. + EXPECT_TRUE(model.ShouldShowBrandedWallpaper()); + EXPECT_EQ(2, model.current_wallpaper_image_index()); + model.RegisterPageView(); + + // Loading 3 times again. + for (int i = 0; i < 3; ++i) { + EXPECT_FALSE(model.ShouldShowBrandedWallpaper()); + EXPECT_EQ(0, model.current_wallpaper_image_index()); + model.RegisterPageView(); + } + + // Image at index 0 should be displayed now. + EXPECT_TRUE(model.ShouldShowBrandedWallpaper()); + EXPECT_EQ(0, model.current_wallpaper_image_index()); + model.RegisterPageView(); +} diff --git a/components/ntp_sponsored_images/browser/view_counter_service.cc b/components/ntp_sponsored_images/browser/view_counter_service.cc new file mode 100644 index 000000000000..bdcf5cf6b927 --- /dev/null +++ b/components/ntp_sponsored_images/browser/view_counter_service.cc @@ -0,0 +1,132 @@ +// Copyright (c) 2020 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. + +#include "brave/components/ntp_sponsored_images/browser/view_counter_service.h" + +#include +#include + +#include "base/bind.h" +#include "base/feature_list.h" +#include "base/files/file_path.h" +#include "brave/common/pref_names.h" +#include "brave/components/brave_ads/common/pref_names.h" +#include "brave/components/brave_rewards/common/pref_names.h" +#include "brave/components/ntp_sponsored_images/browser/features.h" +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_images_data.h" +#include "components/prefs/pref_change_registrar.h" +#include "components/prefs/pref_service.h" + +namespace ntp_sponsored_images { + +namespace { + +NTPSponsoredImagesData* GetDemoWallpaper() { + static auto demo = std::make_unique(); + demo->url_prefix = "chrome://newtab/ntp-dummy-brandedwallpaper/"; + demo->wallpaper_image_files = { + base::FilePath(FILE_PATH_LITERAL("wallpaper1.jpg")), + base::FilePath(FILE_PATH_LITERAL("wallpaper2.jpg")), + base::FilePath(FILE_PATH_LITERAL("wallpaper3.jpg")), + }; + demo->logo_alt_text = "Technikke: For music lovers."; + demo->logo_company_name = "Technikke"; + demo->logo_destination_url = "https://brave.com"; + return demo.get(); +} + +} // namespace + +ViewCounterService::ViewCounterService( + NTPSponsoredImagesService* service, + PrefService* prefs, + bool is_supported_locale) + : service_(service), + prefs_(prefs), + is_supported_locale_(is_supported_locale) { + // If we have a wallpaper, store it as private var. + // Set demo wallpaper if a flag is set. + if (!base::FeatureList::IsEnabled(features::kBraveNTPBrandedWallpaperDemo)) { + service_->AddObserver(this); + } + + if (current_wallpaper()) { + model_.set_total_image_count( + current_wallpaper()->wallpaper_image_files.size()); + } + + pref_change_registrar_.Init(prefs_); + pref_change_registrar_.Add(brave_rewards::prefs::kBraveRewardsEnabled, + base::BindRepeating(&ViewCounterService::OnPreferenceChanged, + base::Unretained(this))); + pref_change_registrar_.Add(brave_ads::prefs::kEnabled, + base::BindRepeating(&ViewCounterService::OnPreferenceChanged, + base::Unretained(this))); +} + +ViewCounterService::~ViewCounterService() = default; + +NTPSponsoredImagesData* ViewCounterService::current_wallpaper() { + if (base::FeatureList::IsEnabled(features::kBraveNTPBrandedWallpaperDemo)) { + return GetDemoWallpaper(); + } else { + return service_->GetSponsoredImagesData(); + } +} + +void ViewCounterService::Shutdown() { + if (service_ && service_->HasObserver(this)) + service_->RemoveObserver(this); +} + +void ViewCounterService::OnUpdated( + NTPSponsoredImagesData* data) { + DCHECK( + !base::FeatureList::IsEnabled(features::kBraveNTPBrandedWallpaperDemo)); + DCHECK(service_); + + // Data is updated, so change our stored data and reset any indexes. + // But keep view counter until branded content is seen. + model_.ResetCurrentWallpaperImageIndex(); + model_.set_total_image_count(data ? data->wallpaper_image_files.size() : 0); +} + + +void ViewCounterService::OnPreferenceChanged( + const std::string& pref_name) { + ResetNotificationState(); +} + +void ViewCounterService::ResetNotificationState() { + prefs_->SetBoolean(kBrandedWallpaperNotificationDismissed, false); +} + +void ViewCounterService::RegisterPageView() { + // Don't do any counting if we will never be showing the data + // since we want the count to start at the point of data being available + // or the user opt-in status changing. + if (IsBrandedWallpaperActive()) { + model_.RegisterPageView(); + } +} + +bool ViewCounterService::IsBrandedWallpaperActive() { + return (is_supported_locale_ && IsOptedIn() && current_wallpaper()); +} + +bool ViewCounterService::ShouldShowBrandedWallpaper() { + return IsBrandedWallpaperActive() && model_.ShouldShowBrandedWallpaper(); +} + +size_t ViewCounterService::GetWallpaperImageIndexToDisplay() { + return model_.current_wallpaper_image_index(); +} + +bool ViewCounterService::IsOptedIn() { + return prefs_->GetBoolean(kNewTabPageShowBrandedBackgroundImage) && + prefs_->GetBoolean(kNewTabPageShowBackgroundImage); +} + +} // namespace ntp_sponsored_images diff --git a/components/ntp_sponsored_images/browser/view_counter_service.h b/components/ntp_sponsored_images/browser/view_counter_service.h new file mode 100644 index 000000000000..d39e1c5dddbe --- /dev/null +++ b/components/ntp_sponsored_images/browser/view_counter_service.h @@ -0,0 +1,75 @@ +// Copyright (c) 2020 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_VIEW_COUNTER_SERVICE_H_ +#define BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_VIEW_COUNTER_SERVICE_H_ + +#include +#include + +#include "components/keyed_service/core/keyed_service.h" +#include "components/prefs/pref_change_registrar.h" +#include "brave/components/ntp_sponsored_images/browser/ntp_sponsored_images_service.h" +#include "brave/components/ntp_sponsored_images/browser/view_counter_model.h" + +class PrefService; + +namespace ntp_sponsored_images { + +struct NTPSponsoredImagesData; + +class ViewCounterService + : public KeyedService, + public NTPSponsoredImagesService::Observer { + public: + explicit ViewCounterService( + NTPSponsoredImagesService* service, + PrefService* prefs, + bool is_supported_locale); + ~ViewCounterService() override; + + ViewCounterService(const ViewCounterService&) = delete; + ViewCounterService& operator=( + const ViewCounterService&) = delete; + + // Lets the counter know that a New Tab Page view has occured. + // This should always be called as it will evaluate whether the user has + // opted-in or data is available. + void RegisterPageView(); + // Do we have a branded wallpaper to show and has the user + // opted-in to showing it at some time. + bool IsBrandedWallpaperActive(); + // Should we show the branded wallpaper right now, in addition + // to the result from `IsBrandedWallpaperActive()`. + bool ShouldShowBrandedWallpaper(); + // Gets the current data for branded wallpaper, if there + // is a wallpaper active. Does not consider user opt-in + // status, or consider whether the wallpaper should be shown. + NTPSponsoredImagesData* current_wallpaper(); + size_t GetWallpaperImageIndexToDisplay(); + + private: + void OnPreferenceChanged(const std::string& pref_name); + + // KeyedService + void Shutdown() override; + + // NTPSponsoredImagesService::Observer + void OnUpdated(NTPSponsoredImagesData* data) override; + + bool GetBrandedWallpaperFromDataSource(); + bool IsOptedIn(); + void ResetNotificationState(); + + NTPSponsoredImagesService* service_ = nullptr; // not owned + PrefService* prefs_ = nullptr; // not owned + bool is_supported_locale_ = false; + PrefChangeRegistrar pref_change_registrar_; + ViewCounterModel model_; +}; + +} // namespace ntp_sponsored_images + +#endif // BRAVE_COMPONENTS_NTP_SPONSORED_IMAGES_BROWSER_VIEW_COUNTER_SERVICE_H_ diff --git a/components/resources/brave_components_resources.grd b/components/resources/brave_components_resources.grd index 7d11f09d89c0..ef908ab59cd4 100644 --- a/components/resources/brave_components_resources.grd +++ b/components/resources/brave_components_resources.grd @@ -55,7 +55,11 @@ - + + + + + diff --git a/components/resources/brave_components_strings.grd b/components/resources/brave_components_strings.grd index 4c308d745a08..10c3b3e819bf 100644 --- a/components/resources/brave_components_strings.grd +++ b/components/resources/brave_components_strings.grd @@ -139,11 +139,12 @@ View and Manage Bookmarks View your browsing history Dashboard Settings - Show background image + Show Background Image Show Brave Stats Show Clock Show Top Sites Show Brave Rewards + Show Sponsored Images Learn more @@ -637,20 +638,59 @@ Brave Software Inc. does not process, store, or access any of the personal information that you provide to Uphold when you establish an account with them. - about Brave Rewards - By clicking 'Turn on Rewards', you indicate that you have read and agree to the + $1Learn more$2 about Brave Rewards + + By turning on Rewards, you agree to the $1Terms of Service$2. + Estimated earnings so far this month + + Earn tokens by viewing privacy-respecting ads. + Tips and contributions this month Claim my Rewards Turning on... Turn on Rewards Feeling Generous? + + Learn more + Ready to start earning? + + Get paid to view this sponsored background image. + Try Brave Rewards again and pay it forward as you earn! - Turn on Brave Rewards and get paid to view privacy respecting ads. + Turn on Brave Rewards and get paid to view privacy-respecting ads. + + Turn on Brave Rewards to claim your share. You can also choose to $1hide sponsored images$2. + Way to Go! Your rewards from Ads are here You have a UGP grant from Brave. + + You’re getting paid to view this background image. + + + $1Learn more$2 about sponsored images in Brave Rewards. + + + Hide sponsored images + diff --git a/patches/chrome-browser-about_flags.cc.patch b/patches/chrome-browser-about_flags.cc.patch index 58e60ac172d2..ad02de37cff0 100644 --- a/patches/chrome-browser-about_flags.cc.patch +++ b/patches/chrome-browser-about_flags.cc.patch @@ -21,3 +21,11 @@ index 656fd33db3f6553a409351701c9839725452df59..0a164040f1a8d76a5eeee517644af14f const FeatureEntry::Choice kPassiveListenersChoices[] = { {flags_ui::kGenericExperimentChoiceDefault, "", ""}, +@@ -4688,6 +4688,7 @@ const FeatureEntry kFeatureEntries[] = { + // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag + // Histograms" in tools/metrics/histograms/README.md (run the + // AboutFlagsHistogramTest unit test to verify this process). ++ BRAVE_FEATURE_ENTRIES + }; + + class FlagsStateSingleton { diff --git a/test/BUILD.gn b/test/BUILD.gn index 9ef1892dca6b..2a4c9d8330c5 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -95,6 +95,7 @@ test("brave_unit_tests") { "//brave/components/brave_shields/browser/ad_block_regional_service_unittest.cc", "//brave/components/brave_shields/browser/adblock_stub_response_unittest.cc", "//brave/components/brave_shields/browser/https_everywhere_recently_used_cache_unittest.cpp", + "//brave/components/ntp_sponsored_images/browser/view_counter_model_unittest.cc", "//brave/components/rappor/log_uploader_unittest.cc", "//brave/components/translate/core/browser/translate_language_list_unittest.cc", "//brave/third_party/libaddressinput/chromium/chrome_metadata_source_unittest.cc", @@ -106,6 +107,7 @@ test("brave_unit_tests") { deps = [ "//brave/browser/safebrowsing", + "//brave/components/ntp_sponsored_images/browser", "//brave/vendor/brave_base", "//chrome:browser_dependencies", "//chrome:child_dependencies", @@ -156,6 +158,7 @@ test("brave_unit_tests") { "//brave/common/importer/brave_mock_importer_bridge.cc", "//brave/common/importer/brave_mock_importer_bridge.h", "//brave/components/invalidation/push_client_channel_unittest.cc", + "//brave/components/ntp_sponsored_images/browser/ntp_sponsored_images_service_unittest.cc", "//chrome/browser/push_messaging/push_messaging_app_identifier_unittest.cc", "//chrome/browser/push_messaging/push_messaging_notification_manager_unittest.cc", "//chrome/browser/push_messaging/push_messaging_service_unittest.cc",