diff options
Diffstat (limited to 'toolkit/modules')
205 files changed, 62301 insertions, 0 deletions
diff --git a/toolkit/modules/AboutPagesUtils.sys.mjs b/toolkit/modules/AboutPagesUtils.sys.mjs new file mode 100644 index 0000000000..2b2ee3c956 --- /dev/null +++ b/toolkit/modules/AboutPagesUtils.sys.mjs @@ -0,0 +1,32 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 sts=2 expandtab + * 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 const AboutPagesUtils = {}; + +ChromeUtils.defineLazyGetter(AboutPagesUtils, "visibleAboutUrls", () => { + const urls = []; + const rx = /@mozilla.org\/network\/protocol\/about;1\?what\=(.*)$/; + for (const cid in Cc) { + const result = cid.match(rx); + if (!result) { + continue; + } + const [, aboutType] = result; + try { + const am = Cc[cid].getService(Ci.nsIAboutModule); + const uri = Services.io.newURI(`about:${aboutType}`); + const flags = am.getURIFlags(uri); + if (!(flags & Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT)) { + urls.push(`about:${aboutType}`); + } + } catch (e) { + // getService() might have thrown if the component doesn't actually + // implement nsIAboutModule + } + } + urls.sort(); + return urls; +}); diff --git a/toolkit/modules/ActorManagerParent.sys.mjs b/toolkit/modules/ActorManagerParent.sys.mjs new file mode 100644 index 0000000000..3bf7299abe --- /dev/null +++ b/toolkit/modules/ActorManagerParent.sys.mjs @@ -0,0 +1,734 @@ +/* vim: set ts=2 sw=2 sts=2 et tw=80: */ +/* 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/. */ + +/** + * This module handles JavaScript-implemented JSWindowActors, registered through DOM IPC + * infrastructure, and are fission-compatible. + */ + +import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +/** + * Fission-compatible JSProcess implementations. + * Each actor options object takes the form of a ProcessActorOptions dictionary. + * Detailed documentation of these options is in dom/docs/ipc/jsactors.rst, + * available at https://firefox-source-docs.mozilla.org/dom/ipc/jsactors.html + */ +let JSPROCESSACTORS = { + AsyncPrefs: { + parent: { + esModuleURI: "resource://gre/modules/AsyncPrefs.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/modules/AsyncPrefs.sys.mjs", + }, + }, + + ContentPrefs: { + parent: { + esModuleURI: "resource://gre/modules/ContentPrefServiceParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/modules/ContentPrefServiceChild.sys.mjs", + }, + }, + + ExtensionContent: { + child: { + esModuleURI: "resource://gre/modules/ExtensionContent.sys.mjs", + }, + includeParent: true, + }, + + ProcessConduits: { + parent: { + esModuleURI: "resource://gre/modules/ConduitsParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/modules/ConduitsChild.sys.mjs", + }, + }, +}; + +/** + * Fission-compatible JSWindowActor implementations. + * Each actor options object takes the form of a WindowActorOptions dictionary. + * Detailed documentation of these options is in dom/docs/ipc/jsactors.rst, + * available at https://firefox-source-docs.mozilla.org/dom/ipc/jsactors.html + */ +let JSWINDOWACTORS = { + AboutCertViewer: { + parent: { + esModuleURI: "resource://gre/modules/AboutCertViewerParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/modules/AboutCertViewerChild.sys.mjs", + + events: { + DOMDocElementInserted: { capture: true }, + }, + }, + + matches: ["about:certificate"], + }, + + AboutHttpsOnlyError: { + parent: { + esModuleURI: "resource://gre/actors/AboutHttpsOnlyErrorParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/AboutHttpsOnlyErrorChild.sys.mjs", + events: { + DOMDocElementInserted: {}, + }, + }, + matches: ["about:httpsonlyerror?*"], + allFrames: true, + }, + + AboutTranslations: { + parent: { + esModuleURI: "resource://gre/actors/AboutTranslationsParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/AboutTranslationsChild.sys.mjs", + events: { + // Run the actor before any content of the page appears to inject functions. + DOMDocElementInserted: {}, + DOMContentLoaded: {}, + // Used to show and hide the translations button. + pageshow: { mozSystemGroup: true }, + pagehide: { mozSystemGroup: true }, + }, + }, + matches: ["about:translations"], + remoteTypes: ["privilegedabout"], + enablePreference: "browser.translations.enable", + }, + + AudioPlayback: { + parent: { + esModuleURI: "resource://gre/actors/AudioPlaybackParent.sys.mjs", + }, + + child: { + esModuleURI: "resource://gre/actors/AudioPlaybackChild.sys.mjs", + observers: ["audio-playback"], + }, + + allFrames: true, + }, + + AutoComplete: { + parent: { + esModuleURI: "resource://gre/actors/AutoCompleteParent.sys.mjs", + // These two messages are also used, but are currently synchronous calls + // through the per-process message manager. + // "FormAutoComplete:GetSelectedIndex", + // "FormAutoComplete:SelectBy" + }, + + child: { + esModuleURI: "resource://gre/actors/AutoCompleteChild.sys.mjs", + }, + + allFrames: true, + }, + + Autoplay: { + parent: { + esModuleURI: "resource://gre/actors/AutoplayParent.sys.mjs", + }, + + child: { + esModuleURI: "resource://gre/actors/AutoplayChild.sys.mjs", + events: { + GloballyAutoplayBlocked: {}, + }, + }, + + allFrames: true, + }, + + AutoScroll: { + parent: { + esModuleURI: "resource://gre/actors/AutoScrollParent.sys.mjs", + }, + + child: { + esModuleURI: "resource://gre/actors/AutoScrollChild.sys.mjs", + events: { + mousedown: { capture: true, mozSystemGroup: true }, + }, + }, + + allFrames: true, + }, + + BackgroundThumbnails: { + child: { + esModuleURI: "resource://gre/actors/BackgroundThumbnailsChild.sys.mjs", + events: { + DOMDocElementInserted: { capture: true }, + }, + }, + messageManagerGroups: ["thumbnails"], + }, + + BrowserElement: { + parent: { + esModuleURI: "resource://gre/actors/BrowserElementParent.sys.mjs", + }, + + child: { + esModuleURI: "resource://gre/actors/BrowserElementChild.sys.mjs", + events: { + DOMWindowClose: {}, + }, + }, + + allFrames: true, + }, + + Conduits: { + parent: { + esModuleURI: "resource://gre/modules/ConduitsParent.sys.mjs", + }, + + child: { + esModuleURI: "resource://gre/modules/ConduitsChild.sys.mjs", + }, + + allFrames: true, + }, + + Controllers: { + parent: { + esModuleURI: "resource://gre/actors/ControllersParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/ControllersChild.sys.mjs", + }, + + allFrames: true, + }, + + CookieBanner: { + parent: { + esModuleURI: "resource://gre/actors/CookieBannerParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/CookieBannerChild.sys.mjs", + events: { + DOMContentLoaded: {}, + load: { capture: true }, + }, + }, + // Only need handle cookie banners for HTTP/S scheme. + matches: ["https://*/*", "http://*/*"], + // Only handle banners for browser tabs (including sub-frames). + messageManagerGroups: ["browsers"], + // Cookie banners can be shown in sub-frames so we need to include them. + allFrames: true, + // Holds lazy pref getters. + _prefs: {}, + // Remember current register state to avoid duplicate calls to register / + // unregister. + _isRegistered: false, + onAddActor(register, unregister) { + // Register / unregister on pref changes. + let onPrefChange = () => { + if ( + this._prefs["cookiebanners.bannerClicking.enabled"] && + (this._prefs["cookiebanners.service.mode"] != 0 || + this._prefs["cookiebanners.service.mode.privateBrowsing"] != 0) + ) { + if (!this._isRegistered) { + register(); + this._isRegistered = true; + } + } else if (this._isRegistered) { + unregister(); + this._isRegistered = false; + } + }; + + // Add lazy pref getters with pref observers so we can dynamically enable + // or disable the actor. + [ + "cookiebanners.bannerClicking.enabled", + "cookiebanners.service.mode", + "cookiebanners.service.mode.privateBrowsing", + ].forEach(prefName => { + XPCOMUtils.defineLazyPreferenceGetter( + this._prefs, + prefName, + prefName, + null, + onPrefChange + ); + }); + + // Check initial state. + onPrefChange(); + }, + }, + + ExtFind: { + child: { + esModuleURI: "resource://gre/actors/ExtFindChild.sys.mjs", + }, + + allFrames: true, + }, + + FindBar: { + parent: { + esModuleURI: "resource://gre/actors/FindBarParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/FindBarChild.sys.mjs", + events: { + keypress: { mozSystemGroup: true }, + }, + }, + + allFrames: true, + messageManagerGroups: ["browsers", "test"], + }, + + // This is the actor that responds to requests from the find toolbar and + // searches for matches and highlights them. + Finder: { + child: { + esModuleURI: "resource://gre/actors/FinderChild.sys.mjs", + }, + + allFrames: true, + }, + + FormHistory: { + parent: { + esModuleURI: "resource://gre/actors/FormHistoryParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/FormHistoryChild.sys.mjs", + events: { + DOMFormBeforeSubmit: {}, + }, + }, + + allFrames: true, + }, + + InlineSpellChecker: { + parent: { + esModuleURI: "resource://gre/actors/InlineSpellCheckerParent.sys.mjs", + }, + + child: { + esModuleURI: "resource://gre/actors/InlineSpellCheckerChild.sys.mjs", + }, + + allFrames: true, + }, + + KeyPressEventModelChecker: { + child: { + esModuleURI: + "resource://gre/actors/KeyPressEventModelCheckerChild.sys.mjs", + events: { + CheckKeyPressEventModel: { capture: true, mozSystemGroup: true }, + }, + }, + + allFrames: true, + }, + + LoginManager: { + parent: { + esModuleURI: "resource://gre/modules/LoginManagerParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/modules/LoginManagerChild.sys.mjs", + events: { + DOMFormBeforeSubmit: {}, + DOMFormHasPassword: {}, + DOMFormHasPossibleUsername: {}, + DOMInputPasswordAdded: {}, + }, + }, + + allFrames: true, + messageManagerGroups: ["browsers", "webext-browsers", ""], + }, + + ManifestMessages: { + child: { + esModuleURI: "resource://gre/modules/ManifestMessagesChild.sys.mjs", + }, + }, + + NetError: { + parent: { + esModuleURI: "resource://gre/actors/NetErrorParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/NetErrorChild.sys.mjs", + events: { + DOMDocElementInserted: {}, + click: {}, + }, + }, + + matches: ["about:certerror?*", "about:neterror?*"], + allFrames: true, + }, + + PictureInPictureLauncher: { + parent: { + esModuleURI: "resource://gre/modules/PictureInPicture.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/PictureInPictureChild.sys.mjs", + events: { + MozTogglePictureInPicture: { capture: true }, + }, + }, + + allFrames: true, + }, + + PictureInPicture: { + parent: { + esModuleURI: "resource://gre/modules/PictureInPicture.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/PictureInPictureChild.sys.mjs", + }, + + allFrames: true, + }, + + PictureInPictureToggle: { + parent: { + esModuleURI: "resource://gre/modules/PictureInPicture.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/PictureInPictureChild.sys.mjs", + events: { + UAWidgetSetupOrChange: {}, + contextmenu: { capture: true }, + }, + }, + + allFrames: true, + }, + + PopupBlocking: { + parent: { + esModuleURI: "resource://gre/actors/PopupBlockingParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/PopupBlockingChild.sys.mjs", + events: { + DOMPopupBlocked: { capture: true }, + // Only listen for the `pageshow` event after the actor has already been + // created for some other reason. + pageshow: { createActor: false }, + }, + }, + allFrames: true, + }, + + Printing: { + parent: { + esModuleURI: "resource://gre/actors/PrintingParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/PrintingChild.sys.mjs", + events: { + PrintingError: { capture: true }, + printPreviewUpdate: { capture: true }, + }, + }, + }, + + PrintingSelection: { + child: { + esModuleURI: "resource://gre/actors/PrintingSelectionChild.sys.mjs", + }, + allFrames: true, + }, + + PurgeSessionHistory: { + child: { + esModuleURI: "resource://gre/actors/PurgeSessionHistoryChild.sys.mjs", + }, + allFrames: true, + }, + + ReportBrokenSite: { + parent: { + esModuleURI: "resource://gre/actors/ReportBrokenSiteParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/ReportBrokenSiteChild.sys.mjs", + }, + matches: [ + "http://*/*", + "https://*/*", + "about:certerror?*", + "about:neterror?*", + ], + messageManagerGroups: ["browsers"], + allFrames: true, + }, + + // This actor is available for all pages that one can + // view the source of, however it won't be created until a + // request to view the source is made via the message + // 'ViewSource:LoadSource' or 'ViewSource:LoadSourceWithSelection'. + ViewSource: { + child: { + esModuleURI: "resource://gre/actors/ViewSourceChild.sys.mjs", + }, + + allFrames: true, + }, + + // This actor is for the view-source page itself. + ViewSourcePage: { + parent: { + esModuleURI: "resource://gre/actors/ViewSourcePageParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/ViewSourcePageChild.sys.mjs", + events: { + pageshow: { capture: true }, + click: {}, + }, + }, + + matches: ["view-source:*"], + allFrames: true, + }, + + WebChannel: { + parent: { + esModuleURI: "resource://gre/actors/WebChannelParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/WebChannelChild.sys.mjs", + events: { + WebChannelMessageToChrome: { capture: true, wantUntrusted: true }, + }, + }, + + allFrames: true, + }, + + Thumbnails: { + child: { + esModuleURI: "resource://gre/actors/ThumbnailsChild.sys.mjs", + }, + }, + + // Determines if a page can be translated, and coordinates communication with the + // translations engine. + Translations: { + parent: { + esModuleURI: "resource://gre/actors/TranslationsParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/TranslationsChild.sys.mjs", + events: { + DOMContentLoaded: {}, + }, + }, + matches: [ + "http://*/*", + "https://*/*", + "file:///*", + + // The actor is explicitly loaded by this page, + // so it needs to be allowed for it. + "about:translations", + ], + enablePreference: "browser.translations.enable", + }, + + // A single process that controls all of the translations. + TranslationsEngine: { + parent: { + esModuleURI: "resource://gre/actors/TranslationsEngineParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/TranslationsEngineChild.sys.mjs", + events: { + DOMContentLoaded: { createActor: true }, + }, + }, + includeChrome: true, + matches: ["chrome://global/content/translations/translations-engine.html"], + enablePreference: "browser.translations.enable", + }, + + UAWidgets: { + child: { + esModuleURI: "resource://gre/actors/UAWidgetsChild.sys.mjs", + events: { + UAWidgetSetupOrChange: {}, + UAWidgetTeardown: {}, + }, + }, + + includeChrome: true, + allFrames: true, + }, + + UnselectedTabHover: { + parent: { + esModuleURI: "resource://gre/actors/UnselectedTabHoverParent.sys.mjs", + }, + child: { + esModuleURI: "resource://gre/actors/UnselectedTabHoverChild.sys.mjs", + events: { + "UnselectedTabHover:Enable": {}, + "UnselectedTabHover:Disable": {}, + }, + }, + + allFrames: true, + }, +}; + +/** + * Note that turning on page data collection for snapshots currently disables + * collection of generic page info for normal history entries. See bug 1740234. + */ +if (!Services.prefs.getBoolPref("browser.pagedata.enabled", false)) { + JSWINDOWACTORS.ContentMeta = { + parent: { + esModuleURI: "resource://gre/actors/ContentMetaParent.sys.mjs", + }, + + child: { + esModuleURI: "resource://gre/actors/ContentMetaChild.sys.mjs", + events: { + DOMContentLoaded: {}, + DOMMetaAdded: { createActor: false }, + }, + }, + + messageManagerGroups: ["browsers"], + }; +} + +if (AppConstants.platform != "android") { + // Note that GeckoView has another implementation in mobile/android/actors. + JSWINDOWACTORS.Select = { + parent: { + esModuleURI: "resource://gre/actors/SelectParent.sys.mjs", + }, + + child: { + esModuleURI: "resource://gre/actors/SelectChild.sys.mjs", + events: { + mozshowdropdown: {}, + "mozshowdropdown-sourcetouch": {}, + mozhidedropdown: { mozSystemGroup: true }, + }, + }, + + includeChrome: true, + allFrames: true, + }; + + // Note that GeckoView handles MozOpenDateTimePicker in GeckoViewPrompt. + JSWINDOWACTORS.DateTimePicker = { + parent: { + esModuleURI: "resource://gre/actors/DateTimePickerParent.sys.mjs", + }, + + child: { + esModuleURI: "resource://gre/actors/DateTimePickerChild.sys.mjs", + events: { + MozOpenDateTimePicker: {}, + MozUpdateDateTimePicker: {}, + MozCloseDateTimePicker: {}, + }, + }, + + includeChrome: true, + allFrames: true, + }; +} + +export var ActorManagerParent = { + _addActors(actors, kind) { + let register, unregister; + switch (kind) { + case "JSProcessActor": + register = ChromeUtils.registerProcessActor; + unregister = ChromeUtils.unregisterProcessActor; + break; + case "JSWindowActor": + register = ChromeUtils.registerWindowActor; + unregister = ChromeUtils.unregisterWindowActor; + break; + default: + throw new Error("Invalid JSActor kind " + kind); + } + for (let [actorName, actor] of Object.entries(actors)) { + // The actor defines its own register/unregister logic. + if (actor.onAddActor) { + actor.onAddActor( + () => register(actorName, actor), + () => unregister(actorName, actor) + ); + continue; + } + + // If enablePreference is set, only register the actor while the + // preference is set to true. + if (actor.enablePreference) { + let actorNameProp = actorName + "_Preference"; + XPCOMUtils.defineLazyPreferenceGetter( + this, + actorNameProp, + actor.enablePreference, + false, + (prefName, prevValue, isEnabled) => { + if (isEnabled) { + register(actorName, actor); + } else { + unregister(actorName, actor); + } + if (actor.onPreferenceChanged) { + actor.onPreferenceChanged(prefName, prevValue, isEnabled); + } + } + ); + if (!this[actorNameProp]) { + continue; + } + } + + register(actorName, actor); + } + }, + + addJSProcessActors(actors) { + this._addActors(actors, "JSProcessActor"); + }, + addJSWindowActors(actors) { + this._addActors(actors, "JSWindowActor"); + }, +}; + +ActorManagerParent.addJSProcessActors(JSPROCESSACTORS); +ActorManagerParent.addJSWindowActors(JSWINDOWACTORS); diff --git a/toolkit/modules/AppConstants.sys.mjs b/toolkit/modules/AppConstants.sys.mjs new file mode 100644 index 0000000000..36b26056ec --- /dev/null +++ b/toolkit/modules/AppConstants.sys.mjs @@ -0,0 +1,482 @@ +#filter substitution +#include @TOPOBJDIR@/source-repo.h +#include @TOPOBJDIR@/buildid.h +/* 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/. */ + +const lazy = {}; +ChromeUtils.defineModuleGetter(lazy, "AddonManager", "resource://gre/modules/AddonManager.jsm"); + +// Immutable for export. +export var AppConstants = Object.freeze({ + // See this wiki page for more details about channel specific build + // defines: https://wiki.mozilla.org/Platform/Channel-specific_build_defines + NIGHTLY_BUILD: +#ifdef NIGHTLY_BUILD + true, +#else + false, +#endif + + RELEASE_OR_BETA: +#ifdef RELEASE_OR_BETA + true, +#else + false, +#endif + + EARLY_BETA_OR_EARLIER: +#ifdef EARLY_BETA_OR_EARLIER + true, +#else + false, +#endif + + IS_ESR: +#ifdef MOZ_ESR + true, +#else + false, +#endif + + ACCESSIBILITY: +#ifdef ACCESSIBILITY + true, +#else + false, +#endif + + // Official corresponds, roughly, to whether this build is performed + // on Mozilla's continuous integration infrastructure. You should + // disable developer-only functionality when this flag is set. + MOZILLA_OFFICIAL: +#ifdef MOZILLA_OFFICIAL + true, +#else + false, +#endif + + MOZ_OFFICIAL_BRANDING: +#ifdef MOZ_OFFICIAL_BRANDING + true, +#else + false, +#endif + + MOZ_DEV_EDITION: +#ifdef MOZ_DEV_EDITION + true, +#else + false, +#endif + + MOZ_SERVICES_SYNC: +#ifdef MOZ_SERVICES_SYNC + true, +#else + false, +#endif + + MOZ_SERVICES_HEALTHREPORT: +#ifdef MOZ_SERVICES_HEALTHREPORT + true, +#else + false, +#endif + + MOZ_DATA_REPORTING: +#ifdef MOZ_DATA_REPORTING + true, +#else + false, +#endif + + MOZ_SANDBOX: +#ifdef MOZ_SANDBOX + true, +#else + false, +#endif + + MOZ_TELEMETRY_REPORTING: +#ifdef MOZ_TELEMETRY_REPORTING + true, +#else + false, +#endif + + MOZ_TELEMETRY_ON_BY_DEFAULT: +#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT + true, +#else + false, +#endif + + MOZ_UPDATER: +#ifdef MOZ_UPDATER + true, +#else + false, +#endif + + MOZ_SWITCHBOARD: +#ifdef MOZ_SWITCHBOARD + true, +#else + false, +#endif + + MOZ_WEBRTC: +#ifdef MOZ_WEBRTC + true, +#else + false, +#endif + + MOZ_WIDGET_GTK: +#ifdef MOZ_WIDGET_GTK + true, +#else + false, +#endif + + MOZ_WMF_CDM: +#ifdef MOZ_WMF_CDM + true, +#else + false, +#endif + + XP_UNIX: +#ifdef XP_UNIX + true, +#else + false, +#endif + +// NOTE! XP_LINUX has to go after MOZ_WIDGET_ANDROID otherwise Android +// builds will be misidentified as linux. + platform: +#ifdef MOZ_WIDGET_GTK + "linux", +#elif XP_WIN + "win", +#elif XP_MACOSX + "macosx", +#elif MOZ_WIDGET_ANDROID + "android", +#elif XP_LINUX + "linux", +#else + "other", +#endif + +// Most of our frontend code assumes that any desktop Unix platform +// is "linux". Add the distinction for code that needs it. + unixstyle: +#ifdef XP_LINUX + "linux", +#elif XP_OPENBSD + "openbsd", +#elif XP_NETBSD + "netbsd", +#elif XP_FREEBSD + "freebsd", +#elif XP_SOLARIS + "solaris", +#else + "other", +#endif + + isPlatformAndVersionAtLeast(platform, version) { + let platformVersion = Services.sysinfo.getProperty("version"); + return platform == this.platform && + Services.vc.compare(platformVersion, version) >= 0; + }, + + isPlatformAndVersionAtMost(platform, version) { + let platformVersion = Services.sysinfo.getProperty("version"); + return platform == this.platform && + Services.vc.compare(platformVersion, version) <= 0; + }, + + MOZ_CRASHREPORTER: +#ifdef MOZ_CRASHREPORTER + true, +#else + false, +#endif + + MOZ_NORMANDY: +#ifdef MOZ_NORMANDY + true, +#else + false, +#endif + + MOZ_MAINTENANCE_SERVICE: +#ifdef MOZ_MAINTENANCE_SERVICE + true, +#else + false, +#endif + + MOZ_BACKGROUNDTASKS: +#ifdef MOZ_BACKGROUNDTASKS + true, +#else + false, +#endif + + MOZ_UPDATE_AGENT: +#ifdef MOZ_UPDATE_AGENT + true, +#else + false, +#endif + + MOZ_BITS_DOWNLOAD: +#ifdef MOZ_BITS_DOWNLOAD + true, +#else + false, +#endif + + DEBUG: +#ifdef DEBUG + true, +#else + false, +#endif + + ASAN: +#ifdef MOZ_ASAN + true, +#else + false, +#endif + + ASAN_REPORTER: +#ifdef MOZ_ASAN_REPORTER + true, +#else + false, +#endif + + TSAN: +#ifdef MOZ_TSAN + true, +#else + false, +#endif + + MOZ_SYSTEM_NSS: +#ifdef MOZ_SYSTEM_NSS + true, +#else + false, +#endif + + MOZ_PLACES: +#ifdef MOZ_PLACES + true, +#else + false, +#endif + + MOZ_REQUIRE_SIGNING: +#ifdef MOZ_REQUIRE_SIGNING + true, +#else + false, +#endif + + get MOZ_UNSIGNED_SCOPES() { + let result = 0; +#ifdef MOZ_UNSIGNED_APP_SCOPE + result |= lazy.AddonManager.SCOPE_APPLICATION; +#endif +#ifdef MOZ_UNSIGNED_SYSTEM_SCOPE + result |= lazy.AddonManager.SCOPE_SYSTEM; +#endif + return result; + }, + + MOZ_ALLOW_ADDON_SIDELOAD: +#ifdef MOZ_ALLOW_ADDON_SIDELOAD + true, +#else + false, +#endif + + MOZ_WEBEXT_WEBIDL_ENABLED: +#ifdef MOZ_WEBEXT_WEBIDL_ENABLED + true, +#else + false, +#endif + + MENUBAR_CAN_AUTOHIDE: +#ifdef MENUBAR_CAN_AUTOHIDE + true, +#else + false, +#endif + + MOZ_ANDROID_HISTORY: +#ifdef MOZ_ANDROID_HISTORY + true, +#else + false, +#endif + + MOZ_GECKO_PROFILER: +#ifdef MOZ_GECKO_PROFILER + true, +#else + false, +#endif + + DLL_PREFIX: "@DLL_PREFIX@", + DLL_SUFFIX: "@DLL_SUFFIX@", + + MOZ_APP_NAME: "@MOZ_APP_NAME@", + MOZ_APP_BASENAME: "@MOZ_APP_BASENAME@", + // N.b.: you almost certainly want brandShortName/brand-short-name: + // MOZ_APP_DISPLAYNAME should only be used for static user-visible + // fields (e.g., DLL properties, Mac Bundle name, or similar). + MOZ_APP_DISPLAYNAME_DO_NOT_USE: "@MOZ_APP_DISPLAYNAME@", + MOZ_APP_VERSION: "@MOZ_APP_VERSION@", + MOZ_APP_VERSION_DISPLAY: "@MOZ_APP_VERSION_DISPLAY@", + MOZ_BUILDID: "@MOZ_BUILDID@", + MOZ_BUILD_APP: "@MOZ_BUILD_APP@", + MOZ_MACBUNDLE_ID: "@MOZ_MACBUNDLE_ID@", + MOZ_MACBUNDLE_NAME: "@MOZ_MACBUNDLE_NAME@", + MOZ_UPDATE_CHANNEL: "@MOZ_UPDATE_CHANNEL@", + MOZ_WIDGET_TOOLKIT: "@MOZ_WIDGET_TOOLKIT@", + ANDROID_PACKAGE_NAME: "@ANDROID_PACKAGE_NAME@", + + DEBUG_JS_MODULES: "@DEBUG_JS_MODULES@", + + MOZ_BING_API_CLIENTID: "@MOZ_BING_API_CLIENTID@", + MOZ_BING_API_KEY: "@MOZ_BING_API_KEY@", + MOZ_GOOGLE_LOCATION_SERVICE_API_KEY: "@MOZ_GOOGLE_LOCATION_SERVICE_API_KEY@", + MOZ_GOOGLE_SAFEBROWSING_API_KEY: "@MOZ_GOOGLE_SAFEBROWSING_API_KEY@", + MOZ_MOZILLA_API_KEY: "@MOZ_MOZILLA_API_KEY@", + + BROWSER_CHROME_URL: "@BROWSER_CHROME_URL@", + + OMNIJAR_NAME: "@OMNIJAR_NAME@", + + // URL to the hg revision this was built from (e.g. + // "https://hg.mozilla.org/mozilla-central/rev/6256ec9113c1") + // On unofficial builds, this is an empty string. +#ifndef MOZ_SOURCE_URL +#define MOZ_SOURCE_URL +#endif + SOURCE_REVISION_URL: "@MOZ_SOURCE_URL@", + + HAVE_USR_LIB64_DIR: +#ifdef HAVE_USR_LIB64_DIR + true, +#else + false, +#endif + + HAVE_SHELL_SERVICE: +#ifdef HAVE_SHELL_SERVICE + true, +#else + false, +#endif + + MOZ_CODE_COVERAGE: +#ifdef MOZ_CODE_COVERAGE + true, +#else + false, +#endif + + TELEMETRY_PING_FORMAT_VERSION: @TELEMETRY_PING_FORMAT_VERSION@, + + MOZ_NEW_NOTIFICATION_STORE: +#ifdef MOZ_NEW_NOTIFICATION_STORE + true, +#else + false, +#endif + + ENABLE_WEBDRIVER: +#ifdef ENABLE_WEBDRIVER + true, +#else + false, +#endif + + REMOTE_SETTINGS_SERVER_URL: +#ifdef MOZ_THUNDERBIRD + "https://thunderbird-settings.thunderbird.net/v1", +#else + "https://firefox.settings.services.mozilla.com/v1", +#endif + + REMOTE_SETTINGS_VERIFY_SIGNATURE: +#ifdef MOZ_THUNDERBIRD + false, +#else + true, +#endif + + REMOTE_SETTINGS_DEFAULT_BUCKET: +#ifdef MOZ_THUNDERBIRD + "thunderbird", +#else + "main", +#endif + + MOZ_GLEAN_ANDROID: +#ifdef MOZ_GLEAN_ANDROID + true, +#else + false, +#endif + + MOZ_JXL: +#ifdef MOZ_JXL + true, +#else + false, +#endif + + MOZ_CAN_FOLLOW_SYSTEM_TIME: +#ifdef XP_WIN + true, +#elif XP_MACOSX + true, +#elif MOZ_WIDGET_GTK + #ifdef MOZ_ENABLE_DBUS + true, + #else + false, + #endif +#else + false, +#endif + + MOZ_SYSTEM_POLICIES: +#ifdef MOZ_SYSTEM_POLICIES + true, +#else + false, +#endif + + // Returns true for CN region build when distibution id set as 'MozillaOnline' + isChinaRepack() { + return ( + Services.prefs + .getDefaultBranch("") + .getCharPref("distribution.id", "default") === "MozillaOnline" + ); + }, +}); diff --git a/toolkit/modules/AppMenuNotifications.sys.mjs b/toolkit/modules/AppMenuNotifications.sys.mjs new file mode 100644 index 0000000000..77487437ac --- /dev/null +++ b/toolkit/modules/AppMenuNotifications.sys.mjs @@ -0,0 +1,184 @@ +/* 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/. */ + +function AppMenuNotification(id, mainAction, secondaryAction, options = {}) { + this.id = id; + this.mainAction = mainAction; + this.secondaryAction = secondaryAction; + this.options = options; + this.dismissed = this.options.dismissed || false; +} + +export var AppMenuNotifications = { + _notifications: [], + _hasInitialized: false, + + get notifications() { + return Array.from(this._notifications); + }, + + _lazyInit() { + if (!this._hasInitialized) { + Services.obs.addObserver(this, "xpcom-shutdown"); + Services.obs.addObserver(this, "appMenu-notifications-request"); + } + }, + + uninit() { + Services.obs.removeObserver(this, "xpcom-shutdown"); + Services.obs.removeObserver(this, "appMenu-notifications-request"); + }, + + observe(subject, topic, status) { + switch (topic) { + case "xpcom-shutdown": + this.uninit(); + break; + case "appMenu-notifications-request": + if (this._notifications.length) { + Services.obs.notifyObservers(null, "appMenu-notifications", "init"); + } + break; + } + }, + + get activeNotification() { + if (this._notifications.length) { + const doorhanger = this._notifications.find( + n => !n.dismissed && !n.options.badgeOnly + ); + return doorhanger || this._notifications[0]; + } + + return null; + }, + + showNotification(id, mainAction, secondaryAction, options = {}) { + let notification = new AppMenuNotification( + id, + mainAction, + secondaryAction, + options + ); + let existingIndex = this._notifications.findIndex(n => n.id == id); + if (existingIndex != -1) { + this._notifications.splice(existingIndex, 1); + } + + // We don't want to clobber doorhanger notifications just to show a badge, + // so don't dismiss any of them and the badge will show once the doorhanger + // gets resolved. + if (!options.badgeOnly && !options.dismissed) { + this._notifications.forEach(n => { + n.dismissed = true; + }); + } + + // Since notifications are generally somewhat pressing, the ideal case is that + // we never have two notifications at once. However, in the event that we do, + // it's more likely that the older notification has been sitting around for a + // bit, and so we don't want to hide the new notification behind it. Thus, + // we want our notifications to behave like a stack instead of a queue. + this._notifications.unshift(notification); + + this._lazyInit(); + this._updateNotifications(); + return notification; + }, + + showBadgeOnlyNotification(id) { + return this.showNotification(id, null, null, { badgeOnly: true }); + }, + + removeNotification(id) { + let notifications; + if (typeof id == "string") { + notifications = this._notifications.filter(n => n.id == id); + } else { + // If it's not a string, assume RegExp + notifications = this._notifications.filter(n => id.test(n.id)); + } + // _updateNotifications can be expensive if it forces attachment of XBL + // bindings that haven't been used yet, so return early if we haven't found + // any notification to remove, as callers may expect this removeNotification + // method to be a no-op for non-existent notifications. + if (!notifications.length) { + return; + } + + notifications.forEach(n => { + this._removeNotification(n); + }); + this._updateNotifications(); + }, + + dismissNotification(id) { + let notifications; + if (typeof id == "string") { + notifications = this._notifications.filter(n => n.id == id); + } else { + // If it's not a string, assume RegExp + notifications = this._notifications.filter(n => id.test(n.id)); + } + + notifications.forEach(n => { + n.dismissed = true; + if (n.options.onDismissed) { + n.options.onDismissed(); + } + }); + this._updateNotifications(); + }, + + callMainAction(win, notification, fromDoorhanger) { + let action = notification.mainAction; + this._callAction(win, notification, action, fromDoorhanger); + }, + + callSecondaryAction(win, notification) { + let action = notification.secondaryAction; + this._callAction(win, notification, action, true); + }, + + _callAction(win, notification, action, fromDoorhanger) { + let dismiss = true; + if (action) { + try { + action.callback(win, fromDoorhanger); + } catch (error) { + console.error(error); + } + + dismiss = action.dismiss; + } + + if (dismiss) { + notification.dismissed = true; + } else { + this._removeNotification(notification); + } + + this._updateNotifications(); + }, + + _removeNotification(notification) { + // This notification may already be removed, in which case let's just ignore. + let notifications = this._notifications; + if (!notifications) { + return; + } + + var index = notifications.indexOf(notification); + if (index == -1) { + return; + } + + // Remove the notification + notifications.splice(index, 1); + }, + + _updateNotifications() { + Services.obs.notifyObservers(null, "appMenu-notifications", "update"); + }, +}; diff --git a/toolkit/modules/AsanReporter.sys.mjs b/toolkit/modules/AsanReporter.sys.mjs new file mode 100644 index 0000000000..172e24ca4b --- /dev/null +++ b/toolkit/modules/AsanReporter.sys.mjs @@ -0,0 +1,166 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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 { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + Log: "resource://gre/modules/Log.sys.mjs", +}); + +// Define our prefs +const PREF_CLIENT_ID = "asanreporter.clientid"; +const PREF_API_URL = "asanreporter.apiurl"; +const PREF_AUTH_TOKEN = "asanreporter.authtoken"; +const PREF_LOG_LEVEL = "asanreporter.loglevel"; + +// Reporter product map +const REPORTER_PRODUCT = { + firefox: "mozilla-central-asan-nightly", + thunderbird: "comm-central-asan-daily", +}; + +const LOGGER_NAME = "asanreporter"; + +let logger; + +export const AsanReporter = { + init() { + if (this.initialized) { + return; + } + this.initialized = true; + + // Setup logging + logger = lazy.Log.repository.getLogger(LOGGER_NAME); + logger.addAppender( + new lazy.Log.ConsoleAppender(new lazy.Log.BasicFormatter()) + ); + logger.addAppender( + new lazy.Log.DumpAppender(new lazy.Log.BasicFormatter()) + ); + logger.level = Services.prefs.getIntPref( + PREF_LOG_LEVEL, + lazy.Log.Level.Info + ); + + logger.info("Starting up..."); + + // Install a handler to observe tab crashes, so we can report those right + // after they happen instead of relying on the user to restart the browser. + Services.obs.addObserver(this, "ipc:content-shutdown"); + + processDirectory(); + }, + + observe(aSubject, aTopic, aData) { + if (aTopic == "ipc:content-shutdown") { + aSubject.QueryInterface(Ci.nsIPropertyBag2); + if (!aSubject.get("abnormal")) { + return; + } + processDirectory(); + } + }, +}; + +async function processDirectory() { + const asanDumpDir = PathUtils.join(PathUtils.profileDir, "asan"); + const children = await IOUtils.getChildren(asanDumpDir); + + const results = children.filter(function (path) { + const name = PathUtils.filename(path); + return name.startsWith("ff_asan_log.") && !name.includes("submitted"); + }); + + logger.info(`Processing ${results.length} reports...`); + for (const result of results) { + try { + await submitReport(result); + logger.info(`Successfully submitted ${result.path}`); + } catch (e) { + logger.error(`Failed to submit ${result.path}. Reason: ${e}`); + } + } + + logger.info("Done processing reports."); +} + +async function submitReport(reportFile) { + logger.info("Processing " + reportFile); + const data = await IOUtils.read(reportFile); + await submitToServer(data); + // Mark as submitted only if we successfully submitted it to the server. + await IOUtils.move(reportFile, `${reportFile}.submitted`); +} + +function submitToServer(data) { + return new Promise(function (resolve, reject) { + logger.debug("Setting up XHR request"); + let client = Services.prefs.getStringPref(PREF_CLIENT_ID); + let api_url = Services.prefs.getStringPref(PREF_API_URL); + let auth_token = Services.prefs.getStringPref(PREF_AUTH_TOKEN, null); + + let decoder = new TextDecoder(); + + if (!client) { + client = "unknown"; + } + + let versionArr = [ + Services.appinfo.version, + Services.appinfo.appBuildID, + AppConstants.SOURCE_REVISION_URL || "unknown", + ]; + + // Concatenate all relevant information as our server only + // has one field available for version information. + let product_version = versionArr.join("-"); + let os = AppConstants.platform; + let reporter_product = REPORTER_PRODUCT[AppConstants.MOZ_APP_NAME]; + + let reportObj = { + rawStdout: "", + rawStderr: "", + rawCrashData: decoder.decode(data), + // Hardcode platform as there is no other reasonable platform for ASan + platform: "x86-64", + product: reporter_product, + product_version, + os, + client, + tool: "asan-nightly-program", + }; + + var xhr = new XMLHttpRequest(); + xhr.open("POST", api_url, true); + xhr.setRequestHeader("Content-Type", "application/json"); + + // For internal testing purposes, an auth_token can be specified + if (auth_token) { + xhr.setRequestHeader("Authorization", "Token " + auth_token); + } else { + // Prevent privacy leaks + xhr.channel.loadFlags |= Ci.nsIRequest.LOAD_ANONYMOUS; + } + + xhr.onreadystatechange = function () { + if (xhr.readyState == 4) { + if (xhr.status == "201") { + logger.debug("XHR: OK"); + resolve(xhr); + } else { + logger.debug( + "XHR: Status: " + xhr.status + " Response: " + xhr.responseText + ); + reject(xhr); + } + } + }; + + xhr.send(JSON.stringify(reportObj)); + }); +} diff --git a/toolkit/modules/AsyncPrefs.sys.mjs b/toolkit/modules/AsyncPrefs.sys.mjs new file mode 100644 index 0000000000..5fa0ea6d6f --- /dev/null +++ b/toolkit/modules/AsyncPrefs.sys.mjs @@ -0,0 +1,159 @@ +/* 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/. */ + +const kInChildProcess = + Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT; + +const kAllowedPrefs = new Set([ + // NB: please leave the testing prefs at the top, and sort the rest alphabetically if you add + // anything. + "testing.allowed-prefs.some-bool-pref", + "testing.allowed-prefs.some-char-pref", + "testing.allowed-prefs.some-int-pref", + + "browser.contentblocking.report.hide_vpn_banner", + "browser.contentblocking.report.show_mobile_app", + + "browser.shopping.experience2023.optedIn", + "browser.shopping.experience2023.active", + "browser.shopping.experience2023.ads.userEnabled", + "browser.shopping.experience2023.autoOpen.enabled", + "browser.shopping.experience2023.autoOpen.userEnabled", + "browser.shopping.experience2023.showKeepSidebarClosedMessage", + "browser.shopping.experience2023.sidebarClosedCount", + + "narrate.rate", + "narrate.voice", + + "reader.font_size", + "reader.font_type", + "reader.color_scheme", + "reader.content_width", + "reader.line_height", + + "security.tls.version.enable-deprecated", + "security.xfocsp.errorReporting.automatic", + + "network.trr.display_fallback_warning", +]); + +const kPrefTypeMap = new Map([ + ["boolean", Services.prefs.PREF_BOOL], + ["number", Services.prefs.PREF_INT], + ["string", Services.prefs.PREF_STRING], +]); + +function maybeReturnErrorForReset(pref) { + if (!kAllowedPrefs.has(pref)) { + return `Resetting pref ${pref} from content is not allowed.`; + } + return false; +} + +function maybeReturnErrorForSet(pref, value) { + if (!kAllowedPrefs.has(pref)) { + return `Setting pref ${pref} from content is not allowed.`; + } + + let valueType = typeof value; + if (!kPrefTypeMap.has(valueType)) { + return `Can't set pref ${pref} to value of type ${valueType}.`; + } + let prefType = Services.prefs.getPrefType(pref); + if ( + prefType != Services.prefs.PREF_INVALID && + prefType != kPrefTypeMap.get(valueType) + ) { + return `Can't set pref ${pref} to a value with type ${valueType} that doesn't match the pref's type ${prefType}.`; + } + return false; +} + +export class AsyncPrefsChild extends JSProcessActorChild { + set(pref, value) { + let error = maybeReturnErrorForSet(pref, value); + if (error) { + return Promise.reject(error); + } + + return this.sendQuery("AsyncPrefs:SetPref", { + pref, + value, + }); + } + + reset(pref) { + let error = maybeReturnErrorForReset(pref); + if (error) { + return Promise.reject(error); + } + + return this.sendQuery("AsyncPrefs:ResetPref", { pref }); + } +} + +export var AsyncPrefs = { + set(pref, value) { + if (kInChildProcess) { + return ChromeUtils.domProcessChild + .getActor("AsyncPrefs") + .set(pref, value); + } + return AsyncPrefsParent.set(pref, value); + }, + + reset(pref, value) { + if (kInChildProcess) { + return ChromeUtils.domProcessChild.getActor("AsyncPrefs").reset(pref); + } + return AsyncPrefsParent.reset(pref); + }, +}; + +const methodForType = { + number: "setIntPref", + boolean: "setBoolPref", + string: "setCharPref", +}; + +export class AsyncPrefsParent extends JSProcessActorParent { + static set(pref, value) { + let error = maybeReturnErrorForSet(pref, value); + if (error) { + return Promise.reject(error); + } + let methodToUse = methodForType[typeof value]; + try { + Services.prefs[methodToUse](pref, value); + } catch (ex) { + console.error(ex); + return Promise.reject(ex.message); + } + + return Promise.resolve(value); + } + + static reset(pref) { + let error = maybeReturnErrorForReset(pref); + if (error) { + return Promise.reject(error); + } + + try { + Services.prefs.clearUserPref(pref); + } catch (ex) { + console.error(ex); + return Promise.reject(ex.message); + } + + return Promise.resolve(); + } + + receiveMessage(msg) { + if (msg.name == "AsyncPrefs:SetPref") { + return AsyncPrefsParent.set(msg.data.pref, msg.data.value); + } + return AsyncPrefsParent.reset(msg.data.pref); + } +} diff --git a/toolkit/modules/BinarySearch.sys.mjs b/toolkit/modules/BinarySearch.sys.mjs new file mode 100644 index 0000000000..7697b77044 --- /dev/null +++ b/toolkit/modules/BinarySearch.sys.mjs @@ -0,0 +1,70 @@ +/* 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 var BinarySearch = Object.freeze({ + /** + * Returns the index of the given target in the given array or -1 if the + * target is not found. + * + * See search() for a description of this function's parameters. + * + * @return The index of `target` in `array` or -1 if `target` is not found. + */ + indexOf(comparator, array, target) { + let [found, idx] = this.search(comparator, array, target); + return found ? idx : -1; + }, + + /** + * Returns the index within the given array where the given target may be + * inserted to keep the array ordered. + * + * See search() for a description of this function's parameters. + * + * @return The index in `array` where `target` may be inserted to keep `array` + * ordered. + */ + insertionIndexOf(comparator, array, target) { + return this.search(comparator, array, target)[1]; + }, + + /** + * Searches for the given target in the given array. + * + * @param comparator + * A function that takes two arguments and compares them, returning a + * negative number if the first should be ordered before the second, + * zero if the first and second have the same ordering, or a positive + * number if the second should be ordered before the first. The first + * argument is always `target`, and the second argument is a value + * from the array. + * @param array + * An array whose elements are ordered by `comparator`. + * @param target + * The value to search for. + * @return An array with two elements. If `target` is found, the first + * element is true, and the second element is its index in the array. + * If `target` is not found, the first element is false, and the + * second element is the index where it may be inserted to keep the + * array ordered. + */ + search(comparator, array, target) { + let low = 0; + let high = array.length - 1; + while (low <= high) { + // Thanks to http://jsperf.com/code-review-1480 for this tip. + let mid = (low + high) >> 1; + let cmp = comparator(target, array[mid]); + if (cmp == 0) { + return [true, mid]; + } + if (cmp < 0) { + high = mid - 1; + } else { + low = mid + 1; + } + } + return [false, low]; + }, +}); diff --git a/toolkit/modules/BrowserTelemetryUtils.sys.mjs b/toolkit/modules/BrowserTelemetryUtils.sys.mjs new file mode 100644 index 0000000000..c3035974e8 --- /dev/null +++ b/toolkit/modules/BrowserTelemetryUtils.sys.mjs @@ -0,0 +1,70 @@ +/* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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 var BrowserTelemetryUtils = { + recordSiteOriginTelemetry(aWindows, aIsGeckoView) { + Services.tm.idleDispatchToMainThread(() => { + this._recordSiteOriginTelemetry(aWindows, aIsGeckoView); + }); + }, + + computeSiteOriginCount(aWindows, aIsGeckoView) { + // Geckoview and Desktop work differently. On desktop, aBrowser objects + // holds an array of tabs which we can use to get the <browser> objects. + // In Geckoview, it is apps' responsibility to keep track of the tabs, so + // there isn't an easy way for us to get the tabs. + let tabs = []; + if (aIsGeckoView) { + // To get all active windows; Each tab has its own window + tabs = aWindows; + } else { + for (const win of aWindows) { + tabs = tabs.concat(win.gBrowser.tabs); + } + } + + let topLevelBCs = []; + + for (const tab of tabs) { + let browser; + if (aIsGeckoView) { + browser = tab.browser; + } else { + browser = tab.linkedBrowser; + } + + if (browser.browsingContext) { + // This is the top level browsingContext + topLevelBCs.push(browser.browsingContext); + } + } + + return CanonicalBrowsingContext.countSiteOrigins(topLevelBCs); + }, + + _recordSiteOriginTelemetry(aWindows, aIsGeckoView) { + let currentTime = Date.now(); + + // default is 5 minutes + if (!this.min_interval) { + this.min_interval = Services.prefs.getIntPref( + "telemetry.number_of_site_origin.min_interval", + 300000 + ); + } + + let originCount = this.computeSiteOriginCount(aWindows, aIsGeckoView); + + // Discard the first load because most of the time the first load only has 1 + // tab and 1 window open, so it is useless to report it. + if (!this._lastRecordSiteOrigin) { + this._lastRecordSiteOrigin = currentTime; + } else if (currentTime >= this._lastRecordSiteOrigin + this.min_interval) { + this._lastRecordSiteOrigin = currentTime; + + Glean.geckoview.documentSiteOrigins.accumulateSamples([originCount]); + } + }, +}; diff --git a/toolkit/modules/BrowserUtils.sys.mjs b/toolkit/modules/BrowserUtils.sys.mjs new file mode 100644 index 0000000000..1963b9728e --- /dev/null +++ b/toolkit/modules/BrowserUtils.sys.mjs @@ -0,0 +1,614 @@ +/* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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 { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; + +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + ReaderMode: "resource://gre/modules/ReaderMode.sys.mjs", + Region: "resource://gre/modules/Region.sys.mjs", +}); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "INVALID_SHAREABLE_SCHEMES", + "services.sync.engine.tabs.filteredSchemes", + "", + null, + val => { + return new Set(val.split("|")); + } +); +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "FXVIEW_SEARCH_ENABLED", + "browser.firefox-view.search.enabled" +); + +ChromeUtils.defineLazyGetter(lazy, "gLocalization", () => { + return new Localization(["toolkit/global/browser-utils.ftl"], true); +}); + +function stringPrefToSet(prefVal) { + return new Set( + prefVal + .toLowerCase() + .split(/\s*,\s*/g) // split on commas, ignoring whitespace + .filter(v => !!v) // discard any falsey values + ); +} + +export var BrowserUtils = { + /** + * Return or create a principal with the content of one, and the originAttributes + * of an existing principal (e.g. on a docshell, where the originAttributes ought + * not to change, that is, we should keep the userContextId, privateBrowsingId, + * etc. the same when changing the principal). + * + * @param principal + * The principal whose content/null/system-ness we want. + * @param existingPrincipal + * The principal whose originAttributes we want, usually the current + * principal of a docshell. + * @return an nsIPrincipal that matches the content/null/system-ness of the first + * param, and the originAttributes of the second. + */ + principalWithMatchingOA(principal, existingPrincipal) { + // Don't care about system principals: + if (principal.isSystemPrincipal) { + return principal; + } + + // If the originAttributes already match, just return the principal as-is. + if (existingPrincipal.originSuffix == principal.originSuffix) { + return principal; + } + + let secMan = Services.scriptSecurityManager; + if (principal.isContentPrincipal) { + return secMan.principalWithOA( + principal, + existingPrincipal.originAttributes + ); + } + + if (principal.isNullPrincipal) { + return secMan.createNullPrincipal(existingPrincipal.originAttributes); + } + throw new Error( + "Can't change the originAttributes of an expanded principal!" + ); + }, + + /** + * Returns true if |mimeType| is text-based, or false otherwise. + * + * @param mimeType + * The MIME type to check. + */ + mimeTypeIsTextBased(mimeType) { + return ( + mimeType.startsWith("text/") || + mimeType.endsWith("+xml") || + mimeType.endsWith("+json") || + mimeType == "application/x-javascript" || + mimeType == "application/javascript" || + mimeType == "application/json" || + mimeType == "application/xml" + ); + }, + + /** + * Returns true if we can show a find bar, including FAYT, for the specified + * document location. The location must not be in a blocklist of specific + * "about:" pages for which find is disabled. + * + * This can be called from the parent process or from content processes. + */ + canFindInPage(location) { + return ( + !location.startsWith("about:preferences") && + !location.startsWith("about:logins") && + !(location.startsWith("about:firefoxview") && lazy.FXVIEW_SEARCH_ENABLED) + ); + }, + + isFindbarVisible(docShell) { + const FINDER_SYS_MJS = "resource://gre/modules/Finder.sys.mjs"; + return ( + Cu.isESModuleLoaded(FINDER_SYS_MJS) && + ChromeUtils.importESModule(FINDER_SYS_MJS).Finder.isFindbarVisible( + docShell + ) + ); + }, + + /** + * Returns a Promise which resolves when the given observer topic has been + * observed. + * + * @param {string} topic + * The topic to observe. + * @param {function(nsISupports, string)} [test] + * An optional test function which, when called with the + * observer's subject and data, should return true if this is the + * expected notification, false otherwise. + * @returns {Promise<object>} + */ + promiseObserved(topic, test = () => true) { + return new Promise(resolve => { + let observer = (subject, topic, data) => { + if (test(subject, data)) { + Services.obs.removeObserver(observer, topic); + resolve({ subject, data }); + } + }; + Services.obs.addObserver(observer, topic); + }); + }, + + formatURIStringForDisplay(uriString, options = {}) { + try { + return this.formatURIForDisplay(Services.io.newURI(uriString), options); + } catch (ex) { + return uriString; + } + }, + + formatURIForDisplay(uri, options = {}) { + let { showInsecureHTTP = false } = options; + switch (uri.scheme) { + case "view-source": + let innerURI = uri.spec.substring("view-source:".length); + return this.formatURIStringForDisplay(innerURI, options); + case "http": + // Fall through. + case "https": + let host = uri.displayHostPort; + if (!showInsecureHTTP && host.startsWith("www.")) { + host = Services.eTLD.getSchemelessSite(uri); + } + if (showInsecureHTTP && uri.scheme == "http") { + return "http://" + host; + } + return host; + case "about": + return "about:" + uri.filePath; + case "blob": + try { + let url = new URL(uri.specIgnoringRef); + // _If_ we find a non-null origin, report that. + if (url.origin && url.origin != "null") { + return this.formatURIStringForDisplay(url.origin, options); + } + // otherwise, fall through... + } catch (ex) { + console.error("Invalid blob URI passed to formatURIForDisplay: ", ex); + } + /* For blob URIs without an origin, fall through and use the data URI + * logic (shows just "(data)", localized). */ + case "data": + return lazy.gLocalization.formatValueSync("browser-utils-url-data"); + case "moz-extension": + let policy = WebExtensionPolicy.getByURI(uri); + return lazy.gLocalization.formatValueSync( + "browser-utils-url-extension", + { extension: policy?.name.trim() || uri.spec } + ); + case "chrome": + case "resource": + case "jar": + case "file": + default: + try { + let url = uri.QueryInterface(Ci.nsIURL); + // Just the filename if we have one: + if (url.fileName) { + return url.fileName; + } + // We won't get a filename for a path that looks like: + // /foo/bar/baz/ + // So try the directory name: + if (url.directory) { + let parts = url.directory.split("/"); + // Pop off any empty bits at the end: + let last; + while (!last && parts.length) { + last = parts.pop(); + } + if (last) { + return last; + } + } + } catch (ex) { + console.error(ex); + } + } + return uri.asciiHost || uri.spec; + }, + + // Given a URL returns a (possibly transformed) URL suitable for sharing, or null if + // no such URL can be obtained. + getShareableURL(url) { + if (!url) { + return null; + } + + // Carve out an exception for about:reader. + if (url.spec.startsWith("about:reader?")) { + url = Services.io.newURI(lazy.ReaderMode.getOriginalUrl(url.spec)); + } + // Disallow sharing URLs with more than 65535 characters. + if (url.spec.length > 65535) { + return null; + } + // Use the same preference as synced tabs to disable what kind + // of tabs we can send to another device + return lazy.INVALID_SHAREABLE_SCHEMES.has(url.scheme) ? null : url; + }, + + /** + * Extracts linkNode and href for a click event. + * + * @param event + * The click event. + * @return [href, linkNode, linkPrincipal]. + * + * @note linkNode will be null if the click wasn't on an anchor + * element. This includes SVG links, because callers expect |node| + * to behave like an <a> element, which SVG links (XLink) don't. + */ + hrefAndLinkNodeForClickEvent(event) { + // We should get a window off the event, and bail if not: + let content = event.view || event.composedTarget?.ownerGlobal; + if (!content?.HTMLAnchorElement) { + return null; + } + function isHTMLLink(aNode) { + // Be consistent with what nsContextMenu.js does. + return ( + (content.HTMLAnchorElement.isInstance(aNode) && aNode.href) || + (content.HTMLAreaElement.isInstance(aNode) && aNode.href) || + content.HTMLLinkElement.isInstance(aNode) + ); + } + + let node = event.composedTarget; + while (node && !isHTMLLink(node)) { + node = node.flattenedTreeParentNode; + } + + if (node) { + return [node.href, node, node.ownerDocument.nodePrincipal]; + } + + // If there is no linkNode, try simple XLink. + let href, baseURI; + node = event.composedTarget; + while (node && !href) { + if ( + node.nodeType == content.Node.ELEMENT_NODE && + (node.localName == "a" || + node.namespaceURI == "http://www.w3.org/1998/Math/MathML") + ) { + href = + node.getAttribute("href") || + node.getAttributeNS("http://www.w3.org/1999/xlink", "href"); + if (href) { + baseURI = node.ownerDocument.baseURIObject; + break; + } + } + node = node.flattenedTreeParentNode; + } + + // In case of XLink, we don't return the node we got href from since + // callers expect <a>-like elements. + // Note: makeURI() will throw if aUri is not a valid URI. + return [ + href ? Services.io.newURI(href, null, baseURI).spec : null, + null, + node && node.ownerDocument.nodePrincipal, + ]; + }, + + /** + * whereToOpenLink() looks at an event to decide where to open a link. + * + * The event may be a mouse event (click, double-click, middle-click) or keypress event (enter). + * + * On Windows, the modifiers are: + * Ctrl new tab, selected + * Shift new window + * Ctrl+Shift new tab, in background + * Alt save + * + * Middle-clicking is the same as Ctrl+clicking (it opens a new tab). + * + * Exceptions: + * - Alt is ignored for menu items selected using the keyboard so you don't accidentally save stuff. + * (Currently, the Alt isn't sent here at all for menu items, but that will change in bug 126189.) + * - Alt is hard to use in context menus, because pressing Alt closes the menu. + * - Alt can't be used on the bookmarks toolbar because Alt is used for "treat this as something draggable". + * - The button is ignored for the middle-click-paste-URL feature, since it's always a middle-click. + * + * @param e {Event|Object} Event or JSON Object + * @param ignoreButton {Boolean} + * @param ignoreAlt {Boolean} + * @returns {"current" | "tabshifted" | "tab" | "save" | "window"} + */ + whereToOpenLink(e, ignoreButton, ignoreAlt) { + // This method must treat a null event like a left click without modifier keys (i.e. + // e = { shiftKey:false, ctrlKey:false, metaKey:false, altKey:false, button:0 }) + // for compatibility purposes. + if (!e) { + return "current"; + } + + e = this.getRootEvent(e); + + var shift = e.shiftKey; + var ctrl = e.ctrlKey; + var meta = e.metaKey; + var alt = e.altKey && !ignoreAlt; + + // ignoreButton allows "middle-click paste" to use function without always opening in a new window. + let middle = !ignoreButton && e.button == 1; + let middleUsesTabs = Services.prefs.getBoolPref( + "browser.tabs.opentabfor.middleclick", + true + ); + let middleUsesNewWindow = Services.prefs.getBoolPref( + "middlemouse.openNewWindow", + false + ); + + // Don't do anything special with right-mouse clicks. They're probably clicks on context menu items. + + // See also nsWindowWatcher::GetWindowOpenLocation in + // toolkit/components/windowwatcher/nsWindowWatcher.cpp + + var metaKey = AppConstants.platform == "macosx" ? meta : ctrl; + if (metaKey || (middle && middleUsesTabs)) { + return shift ? "tabshifted" : "tab"; + } + + if (alt && Services.prefs.getBoolPref("browser.altClickSave", false)) { + return "save"; + } + + if (shift || (middle && !middleUsesTabs && middleUsesNewWindow)) { + return "window"; + } + + return "current"; + }, + + // Utility function to check command events for potential middle-click events + // from checkForMiddleClick and unwrap them. + getRootEvent(aEvent) { + // Part of the fix for Bug 1523813. + // Middle-click events arrive here wrapped in different numbers (1-2) of + // command events, depending on the button originally clicked. + if (!aEvent) { + return aEvent; + } + let tempEvent = aEvent; + while (tempEvent.sourceEvent) { + if (tempEvent.sourceEvent.button == 1) { + aEvent = tempEvent.sourceEvent; + break; + } + tempEvent = tempEvent.sourceEvent; + } + return aEvent; + }, + + /** + * An enumeration of the promotion types that can be passed to shouldShowPromo + */ + PromoType: { + DEFAULT: 0, // invalid + VPN: 1, + RELAY: 2, + FOCUS: 3, + PIN: 4, + COOKIE_BANNERS: 5, + }, + + /** + * Should a given promo be shown to the user now, based on things including: + * + * current region + * home region + * where ads for a particular thing are allowed + * where they are illegal + * in what regions is the thing being promoted supported? + * whether there is an active enterprise policy + * settings of specific preferences related to this promo + * + * @param {BrowserUtils.PromoType} promoType - What promo are we checking on? + * + * @return {boolean} - should we display this promo now or not? + */ + shouldShowPromo(promoType) { + switch (promoType) { + case this.PromoType.VPN: + case this.PromoType.FOCUS: + case this.PromoType.PIN: + case this.PromoType.RELAY: + case this.PromoType.COOKIE_BANNERS: + break; + default: + throw new Error("Unknown promo type: ", promoType); + } + + const info = PromoInfo[promoType]; + const promoEnabled = + !info.enabledPref || Services.prefs.getBoolPref(info.enabledPref, true); + + const homeRegion = lazy.Region.home || ""; + const currentRegion = lazy.Region.current || ""; + + let inSupportedRegion = true; + if ("supportedRegions" in info.lazyStringSetPrefs) { + const supportedRegions = + info.lazyStringSetPrefs.supportedRegions.lazyValue; + inSupportedRegion = + supportedRegions.has(currentRegion.toLowerCase()) || + supportedRegions.has(homeRegion.toLowerCase()); + } + + const avoidAdsRegions = + info.lazyStringSetPrefs.disallowedRegions?.lazyValue; + + // Don't show promo if there's an active enterprise policy + const noActivePolicy = + info.showForEnterprise || + !Services.policies || + Services.policies.status !== Services.policies.ACTIVE; + + // Promos may add custom checks that must pass. + const passedExtraCheck = !info.extraCheck || info.extraCheck(); + + return ( + promoEnabled && + !avoidAdsRegions?.has(homeRegion.toLowerCase()) && + !avoidAdsRegions?.has(currentRegion.toLowerCase()) && + !info.illegalRegions.includes(homeRegion.toLowerCase()) && + !info.illegalRegions.includes(currentRegion.toLowerCase()) && + inSupportedRegion && + noActivePolicy && + passedExtraCheck + ); + }, + + /** + * @deprecated in favor of shouldShowPromo + */ + shouldShowVPNPromo() { + return this.shouldShowPromo(this.PromoType.VPN); + }, + + // Return true if Send to Device emails are supported for user's locale + sendToDeviceEmailsSupported() { + const userLocale = Services.locale.appLocaleAsBCP47.toLowerCase(); + return this.emailSupportedLocales.has(userLocale); + }, +}; + +/** + * A table of promos used by shouldShowPromo to decide whether or not to show. + * Each entry defines the criteria for a given promo, and also houses lazy + * getters for specified string set preferences. + */ +let PromoInfo = { + [BrowserUtils.PromoType.VPN]: { + enabledPref: "browser.vpn_promo.enabled", + lazyStringSetPrefs: { + supportedRegions: { + name: "browser.contentblocking.report.vpn_regions", + default: + "ca,my,nz,sg,gb,gg,im,io,je,uk,vg,as,mp,pr,um,us,vi,de,fr,at,be,ch,es,it,ie,nl,se,fi,bg,cy,cz,dk,ee,hr,hu,lt,lu,lv,mt,pl,pt,ro,si,sk", + }, + disallowedRegions: { + name: "browser.vpn_promo.disallowed_regions", + default: "ae,by,cn,cu,iq,ir,kp,om,ru,sd,sy,tm,tr", + }, + }, + //See https://github.com/search?q=repo%3Amozilla%2Fbedrock+VPN_EXCLUDED_COUNTRY_CODES&type=code + illegalRegions: [ + "ae", + "by", + "cn", + "cu", + "iq", + "ir", + "kp", + "om", + "ru", + "sd", + "sy", + "tm", + "tr", + ], + }, + [BrowserUtils.PromoType.FOCUS]: { + enabledPref: "browser.promo.focus.enabled", + lazyStringSetPrefs: { + // there are no particular limitions to where it is "supported", + // so we leave out the supported pref + disallowedRegions: { + name: "browser.promo.focus.disallowed_regions", + default: "cn", + }, + }, + illegalRegions: ["cn"], + }, + [BrowserUtils.PromoType.PIN]: { + enabledPref: "browser.promo.pin.enabled", + lazyStringSetPrefs: {}, + illegalRegions: [], + }, + [BrowserUtils.PromoType.RELAY]: { + lazyStringSetPrefs: {}, + illegalRegions: [], + // Returns true if user is using the FxA "production" instance, or returns + // false for custom FxA instance (such as accounts.firefox.com.cn for the + // China repack) which doesn't support authentication for addons like Relay. + extraCheck: () => + !Services.prefs.getCharPref("identity.fxaccounts.autoconfig.uri", "") && + [ + "identity.fxaccounts.remote.root", + "identity.fxaccounts.auth.uri", + "identity.fxaccounts.remote.oauth.uri", + "identity.fxaccounts.remote.profile.uri", + "identity.fxaccounts.remote.pairing.uri", + "identity.sync.tokenserver.uri", + ].every(pref => !Services.prefs.prefHasUserValue(pref)), + }, + [BrowserUtils.PromoType.COOKIE_BANNERS]: { + enabledPref: "browser.promo.cookiebanners.enabled", + lazyStringSetPrefs: {}, + illegalRegions: [], + showForEnterprise: true, + }, +}; + +/* + * Finish setting up the PromoInfo data structure by attaching lazy prefs getters + * as specified in the structure. (the object for each pref in the lazyStringSetPrefs + * gets a `lazyValue` property attached to it). + */ +for (let promo of Object.values(PromoInfo)) { + for (let prefObj of Object.values(promo.lazyStringSetPrefs)) { + XPCOMUtils.defineLazyPreferenceGetter( + prefObj, + "lazyValue", + prefObj.name, + prefObj.default, + null, + stringPrefToSet + ); + } +} + +XPCOMUtils.defineLazyPreferenceGetter( + BrowserUtils, + "navigationRequireUserInteraction", + "browser.navigation.requireUserInteraction", + false +); + +XPCOMUtils.defineLazyPreferenceGetter( + BrowserUtils, + "emailSupportedLocales", + "browser.send_to_device_locales", + "de,en-GB,en-US,es-AR,es-CL,es-ES,es-MX,fr,id,pl,pt-BR,ru,zh-TW", + null, + stringPrefToSet +); diff --git a/toolkit/modules/CanonicalJSON.sys.mjs b/toolkit/modules/CanonicalJSON.sys.mjs new file mode 100644 index 0000000000..07ee318895 --- /dev/null +++ b/toolkit/modules/CanonicalJSON.sys.mjs @@ -0,0 +1,66 @@ +/* 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 var CanonicalJSON = { + /** + * Return the canonical JSON form of the passed source, sorting all the object + * keys recursively. Note that this method will cause an infinite loop if + * cycles exist in the source (bug 1265357). + * + * @param source + * The elements to be serialized. + * + * The output will have all unicode chars escaped with the unicode codepoint + * as lowercase hexadecimal. + * + * @usage + * CanonicalJSON.stringify(listOfRecords); + **/ + stringify: function stringify(source, jsescFn) { + if (typeof jsescFn != "function") { + const { jsesc } = ChromeUtils.importESModule( + "resource://gre/modules/third_party/jsesc/jsesc.mjs" + ); + jsescFn = jsesc; + } + if (Array.isArray(source)) { + const jsonArray = source.map(x => (typeof x === "undefined" ? null : x)); + return ( + "[" + jsonArray.map(item => stringify(item, jsescFn)).join(",") + "]" + ); + } + + if (typeof source === "number") { + if (source === 0) { + return Object.is(source, -0) ? "-0" : "0"; + } + } + + // Leverage jsesc library, mainly for unicode escaping. + const toJSON = input => jsescFn(input, { lowercaseHex: true, json: true }); + + if (typeof source !== "object" || source === null) { + return toJSON(source); + } + + // Dealing with objects, ordering keys. + const sortedKeys = Object.keys(source).sort(); + const lastIndex = sortedKeys.length - 1; + return ( + sortedKeys.reduce((serial, key, index) => { + const value = source[key]; + // JSON.stringify drops keys with an undefined value. + if (typeof value === "undefined") { + return serial; + } + const jsonValue = value && value.toJSON ? value.toJSON() : value; + const suffix = index !== lastIndex ? "," : ""; + const escapedKey = toJSON(key); + return ( + serial + escapedKey + ":" + stringify(jsonValue, jsescFn) + suffix + ); + }, "{") + "}" + ); + }, +}; diff --git a/toolkit/modules/CertUtils.sys.mjs b/toolkit/modules/CertUtils.sys.mjs new file mode 100644 index 0000000000..2fc3b5cb50 --- /dev/null +++ b/toolkit/modules/CertUtils.sys.mjs @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ +const Ce = Components.Exception; + +/** + * Reads a set of expected certificate attributes from preferences. The returned + * array can be passed to validateCert or checkCert to validate that a + * certificate matches the expected attributes. The preferences should look like + * this: + * prefix.1.attribute1 + * prefix.1.attribute2 + * prefix.2.attribute1 + * etc. + * Each numeric branch contains a set of required attributes for a single + * certificate. Having multiple numeric branches means that multiple + * certificates would be accepted by validateCert. + * + * @param aPrefBranch + * The prefix for all preferences, should end with a ".". + * @return An array of JS objects with names / values corresponding to the + * expected certificate's attribute names / values. + */ +function readCertPrefs(aPrefBranch) { + if (!Services.prefs.getBranch(aPrefBranch).getChildList("").length) { + return null; + } + + let certs = []; + let counter = 1; + while (true) { + let prefBranchCert = Services.prefs.getBranch(aPrefBranch + counter + "."); + let prefCertAttrs = prefBranchCert.getChildList(""); + if (!prefCertAttrs.length) { + break; + } + + let certAttrs = {}; + for (let prefCertAttr of prefCertAttrs) { + certAttrs[prefCertAttr] = prefBranchCert.getCharPref(prefCertAttr); + } + + certs.push(certAttrs); + counter++; + } + + return certs; +} + +/** + * Verifies that an nsIX509Cert matches the expected certificate attribute + * values. + * + * @param aCertificate + * The nsIX509Cert to compare to the expected attributes. + * @param aCerts + * An array of JS objects with names / values corresponding to the + * expected certificate's attribute names / values. If this is null or + * an empty array then no checks are performed. + * @throws NS_ERROR_ILLEGAL_VALUE if a certificate attribute name from the + * aCerts param does not exist or the value for a certificate attribute + * from the aCerts param is different than the expected value or + * aCertificate wasn't specified and aCerts is not null or an empty + * array. + */ +function validateCert(aCertificate, aCerts) { + // If there are no certificate requirements then just exit + if (!aCerts || !aCerts.length) { + return; + } + + if (!aCertificate) { + const missingCertErr = "A required certificate was not present."; + console.error(missingCertErr); + throw new Ce(missingCertErr, Cr.NS_ERROR_ILLEGAL_VALUE); + } + + var errors = []; + for (var i = 0; i < aCerts.length; ++i) { + var error = false; + var certAttrs = aCerts[i]; + for (var name in certAttrs) { + if (!(name in aCertificate)) { + error = true; + errors.push( + "Expected attribute '" + name + "' not present in certificate." + ); + break; + } + if (aCertificate[name] != certAttrs[name]) { + error = true; + errors.push( + "Expected certificate attribute '" + + name + + "' " + + "value incorrect, expected: '" + + certAttrs[name] + + "', got: '" + + aCertificate[name] + + "'." + ); + break; + } + } + + if (!error) { + break; + } + } + + if (error) { + errors.forEach(Cu.reportError.bind(Cu)); + const certCheckErr = + "Certificate checks failed. See previous errors for details."; + console.error(certCheckErr); + throw new Ce(certCheckErr, Cr.NS_ERROR_ILLEGAL_VALUE); + } +} + +/** + * Checks if the connection must be HTTPS and if so, only allows built-in + * certificates and validates application specified certificate attribute + * values. + * See bug 340198 and bug 544442. + * + * @param aChannel + * The nsIChannel that will have its certificate checked. + * @param aAllowNonBuiltInCerts (optional) + * When true certificates that aren't builtin are allowed. When false + * or not specified the certificate must be a builtin certificate. + * @param aCerts (optional) + * An array of JS objects with names / values corresponding to the + * channel's expected certificate's attribute names / values. If it + * isn't null or not specified the the scheme for the channel's + * originalURI must be https. + * @throws NS_ERROR_UNEXPECTED if a certificate is expected and the URI scheme + * is not https. + * NS_ERROR_ILLEGAL_VALUE if a certificate attribute name from the + * aCerts param does not exist or the value for a certificate attribute + * from the aCerts param is different than the expected value. + * NS_ERROR_ABORT if the certificate issuer is not built-in. + */ +function checkCert(aChannel, aAllowNonBuiltInCerts, aCerts) { + if (!aChannel.originalURI.schemeIs("https")) { + // Require https if there are certificate values to verify + if (aCerts) { + throw new Ce( + "SSL is required and URI scheme is not https.", + Cr.NS_ERROR_UNEXPECTED + ); + } + return; + } + + let secInfo = aChannel.securityInfo; + let cert = secInfo.serverCert; + + validateCert(cert, aCerts); + + if (aAllowNonBuiltInCerts === true) { + return; + } + const certNotBuiltInErr = "Certificate issuer is not built-in."; + if (!secInfo.isBuiltCertChainRootBuiltInRoot) { + throw new Ce(certNotBuiltInErr, Cr.NS_ERROR_ABORT); + } +} + +/** + * This class implements nsIChannelEventSink. Its job is to perform extra checks + * on the certificates used for some connections when those connections + * redirect. + * + * @param aAllowNonBuiltInCerts (optional) + * When true certificates that aren't builtin are allowed. When false + * or not specified the certificate must be a builtin certificate. + */ +function BadCertHandler(aAllowNonBuiltInCerts) { + this.allowNonBuiltInCerts = aAllowNonBuiltInCerts; +} +BadCertHandler.prototype = { + // nsIChannelEventSink + asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) { + if (this.allowNonBuiltInCerts) { + callback.onRedirectVerifyCallback(Cr.NS_OK); + return; + } + + // make sure the certificate of the old channel checks out before we follow + // a redirect from it. See bug 340198. + // Don't call checkCert for internal redirects. See bug 569648. + if (!(flags & Ci.nsIChannelEventSink.REDIRECT_INTERNAL)) { + checkCert(oldChannel); + } + + callback.onRedirectVerifyCallback(Cr.NS_OK); + }, + + // nsIInterfaceRequestor + getInterface(iid) { + return this.QueryInterface(iid); + }, + + // nsISupports + QueryInterface: ChromeUtils.generateQI([ + "nsIChannelEventSink", + "nsIInterfaceRequestor", + ]), +}; + +export var CertUtils = { + BadCertHandler, + checkCert, + readCertPrefs, + validateCert, +}; diff --git a/toolkit/modules/ClipboardContextMenu.sys.mjs b/toolkit/modules/ClipboardContextMenu.sys.mjs new file mode 100644 index 0000000000..011bfe64d7 --- /dev/null +++ b/toolkit/modules/ClipboardContextMenu.sys.mjs @@ -0,0 +1,214 @@ +/* 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/. */ + +const lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + PromptUtils: "resource://gre/modules/PromptUtils.sys.mjs", +}); + +export var ClipboardContextMenu = { + MENU_POPUP_ID: "clipboardReadPasteMenuPopup", + + // EventListener interface. + handleEvent(aEvent) { + switch (aEvent.type) { + case "command": { + this.onCommand(); + break; + } + case "popuphiding": { + this.onPopupHiding(); + break; + } + case "keydown": { + this.onKeyDown(aEvent); + break; + } + } + }, + + _pasteMenuItemClicked: false, + + onCommand() { + // onPopupHiding is responsible for returning result by calling onComplete + // function. + this._pasteMenuItemClicked = true; + }, + + onPopupHiding() { + // Remove the listeners before potentially sending the async message + // below, because that might throw. + this._removeMenupopupEventListeners(); + this._clearDelayTimer(); + this._stopWatchingForSpammyActivation(); + + this._menupopup = null; + this._menuitem = null; + + let propBag = lazy.PromptUtils.objectToPropBag({ + ok: this._pasteMenuItemClicked, + }); + this._pendingRequest.resolve(propBag); + + // A result has already been responded to. Reset the state to properly + // handle further click or dismiss events. + this._pasteMenuItemClicked = false; + this._pendingRequest = null; + }, + + _lastBeepTime: 0, + + onKeyDown(aEvent) { + if (!this._menuitem.disabled) { + return; + } + + let accesskey = this._menuitem.getAttribute("accesskey"); + if ( + aEvent.key == accesskey.toLowerCase() || + aEvent.key == accesskey.toUpperCase() + ) { + if (Date.now() - this._lastBeepTime > 1000) { + Cc["@mozilla.org/sound;1"].getService(Ci.nsISound).beep(); + this._lastBeepTime = Date.now(); + } + this._refreshDelayTimer(); + } + }, + + _menupopup: null, + _menuitem: null, + _pendingRequest: null, + + confirmUserPaste(aWindowContext) { + return new Promise((resolve, reject) => { + if (!aWindowContext) { + reject( + Components.Exception("Null window context.", Cr.NS_ERROR_INVALID_ARG) + ); + return; + } + + let { document } = aWindowContext.browsingContext.topChromeWindow; + if (!document) { + reject( + Components.Exception( + "Unable to get chrome document.", + Cr.NS_ERROR_FAILURE + ) + ); + return; + } + + if (this._pendingRequest) { + reject( + Components.Exception( + "There is an ongoing request.", + Cr.NS_ERROR_FAILURE + ) + ); + return; + } + + this._pendingRequest = { resolve, reject }; + this._menupopup = this._getMenupopup(document); + this._menuitem = this._menupopup.firstElementChild; + this._addMenupopupEventListeners(); + + let mouseXInCSSPixels = {}; + let mouseYInCSSPixels = {}; + document.ownerGlobal.windowUtils.getLastOverWindowPointerLocationInCSSPixels( + mouseXInCSSPixels, + mouseYInCSSPixels + ); + + this._menuitem.disabled = true; + this._startWatchingForSpammyActivation(); + // `openPopup` is a no-op if the popup is already opened. + // That property is used when `navigator.clipboard.readText()` or + // `navigator.clipboard.read()`is called from two different frames, e.g. + // an iframe and the top level frame. In that scenario, the two frames + // correspond to different `navigator.clipboard` instances. When + // `readText()` or `read()` is called from both frames, an actor pair is + // instantiated for each of them. Both actor parents will call `openPopup` + // on the same `_menupopup` object. If the popup is already open, + // `openPopup` is a no-op. When the popup is clicked or dismissed both + // actor parents will receive the corresponding event. + this._menupopup.openPopup( + null, + "overlap" /* options */, + mouseXInCSSPixels.value, + mouseYInCSSPixels.value, + true /* isContextMenu */ + ); + + this._refreshDelayTimer(document); + }); + }, + + _addMenupopupEventListeners() { + this._menupopup.addEventListener("command", this); + this._menupopup.addEventListener("popuphiding", this); + }, + + _removeMenupopupEventListeners() { + this._menupopup.removeEventListener("command", this); + this._menupopup.removeEventListener("popuphiding", this); + }, + + _createMenupopup(aChromeDoc) { + let menuitem = aChromeDoc.createXULElement("menuitem"); + menuitem.id = "clipboardReadPasteMenuItem"; + aChromeDoc.l10n.setAttributes(menuitem, "text-action-paste"); + + let menupopup = aChromeDoc.createXULElement("menupopup"); + menupopup.id = this.MENU_POPUP_ID; + menupopup.appendChild(menuitem); + return menupopup; + }, + + _getMenupopup(aChromeDoc) { + let menupopup = aChromeDoc.getElementById(this.MENU_POPUP_ID); + if (menupopup == null) { + menupopup = this._createMenupopup(aChromeDoc); + const parent = + aChromeDoc.querySelector("popupset") || aChromeDoc.documentElement; + parent.appendChild(menupopup); + } + + return menupopup; + }, + + _startWatchingForSpammyActivation() { + let doc = this._menuitem.ownerDocument; + Services.els.addSystemEventListener(doc, "keydown", this, true); + }, + + _stopWatchingForSpammyActivation() { + let doc = this._menuitem.ownerDocument; + Services.els.removeSystemEventListener(doc, "keydown", this, true); + }, + + _delayTimer: null, + + _clearDelayTimer() { + if (this._delayTimer) { + let window = this._menuitem.ownerGlobal; + window.clearTimeout(this._delayTimer); + this._delayTimer = null; + } + }, + + _refreshDelayTimer() { + this._clearDelayTimer(); + + let window = this._menuitem.ownerGlobal; + let delay = Services.prefs.getIntPref("security.dialog_enable_delay"); + this._delayTimer = window.setTimeout(() => { + this._menuitem.disabled = false; + this._stopWatchingForSpammyActivation(); + this._delayTimer = null; + }, delay); + }, +}; diff --git a/toolkit/modules/Color.sys.mjs b/toolkit/modules/Color.sys.mjs new file mode 100644 index 0000000000..d00902ef6b --- /dev/null +++ b/toolkit/modules/Color.sys.mjs @@ -0,0 +1,109 @@ +/* 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/. */ + +/** + * A list of minimum contrast ratio's per WCAG 2.0 conformance level. + * Please refer to section 1.4.3 of the WCAG 2.0 spec at http://www.w3.org/TR/WCAG20/. + * Simply put: + * A = Large text only. + * AA = Regular sized text or large text in enhanced contrast mode. + * AAA = Regular sized text in enhanced contrast mode. + * + * @type {Object} + */ +const CONTRAST_RATIO_LEVELS = { + A: 3, + AA: 4.5, + AAA: 7, +}; + +/** + * For text legibility on any background color, you need to determine which text + * color - black or white - will yield the highest contrast ratio. + * Since you're always comparing `contrastRatio(bgcolor, black) > + * contrastRatio(bgcolor, white) ? <use black> : <use white>`, we can greatly + * simplify the calculation to the following constant. + * + * @type {Number} + */ +const CONTRAST_BRIGHTTEXT_THRESHOLD = Math.sqrt(1.05 * 0.05) - 0.05; + +/** + * Color class, which describes a color. + * In the future, this object may be extended to allow for conversions between + * different color formats and notations, support transparency. + * + * @param {Number} r Red color component + * @param {Number} g Green color component + * @param {Number} b Blue color component + */ +export class Color { + constructor(r, g, b) { + this.r = r; + this.g = g; + this.b = b; + } + + /** + * Formula from W3C's WCAG 2.0 spec's relative luminance, section 1.4.1, + * http://www.w3.org/TR/WCAG20/. + * + * @return {Number} Relative luminance, represented as number between 0 and 1. + */ + get relativeLuminance() { + let colorArr = [this.r, this.g, this.b].map(color => { + color = parseInt(color, 10); + if (color <= 10) { + return color / 255 / 12.92; + } + return Math.pow((color / 255 + 0.055) / 1.055, 2.4); + }); + return colorArr[0] * 0.2126 + colorArr[1] * 0.7152 + colorArr[2] * 0.0722; + } + + /** + * @return {Boolean} TRUE if you need to use a bright color (e.g. 'white'), when + * this color is set as the background. + */ + get useBrightText() { + return this.relativeLuminance <= CONTRAST_BRIGHTTEXT_THRESHOLD; + } + + /** + * Get the contrast ratio between the current color and a second other color. + * A common use case is to express the difference between a foreground and a + * background color in numbers. + * Formula from W3C's WCAG 2.0 spec's contrast ratio, section 1.4.1, + * http://www.w3.org/TR/WCAG20/. + * + * @param {Color} otherColor Color instance to calculate the contrast with + * @return {Number} Contrast ratios can range from 1 to 21, commonly written + * as 1:1 to 21:1. + */ + contrastRatio(otherColor) { + if (!(otherColor instanceof Color)) { + throw new TypeError("The first argument should be an instance of Color"); + } + + let luminance = this.relativeLuminance; + let otherLuminance = otherColor.relativeLuminance; + return ( + (Math.max(luminance, otherLuminance) + 0.05) / + (Math.min(luminance, otherLuminance) + 0.05) + ); + } + + /** + * Method to check if the contrast ratio between two colors is high enough to + * be discernable. + * + * @param {Color} otherColor Color instance to calculate the contrast with + * @param {String} [level] WCAG conformance level that maps to the minimum + * required contrast ratio. Defaults to 'AA' + * @return {Boolean} + */ + isContrastRatioAcceptable(otherColor, level = "AA") { + return this.contrastRatio(otherColor) > CONTRAST_RATIO_LEVELS[level]; + } +} diff --git a/toolkit/modules/Console.sys.mjs b/toolkit/modules/Console.sys.mjs new file mode 100644 index 0000000000..5fb4f750f4 --- /dev/null +++ b/toolkit/modules/Console.sys.mjs @@ -0,0 +1,753 @@ +/* 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/. */ + +/** + * Define a 'console' API to roughly match the implementation provided by + * Firebug. + * This module helps cases where code is shared between the web and Firefox. + * See also Browser.jsm for an implementation of other web constants to help + * sharing code between the web and firefox; + * + * The API is only be a rough approximation for 3 reasons: + * - The Firebug console API is implemented in many places with differences in + * the implementations, so there isn't a single reference to adhere to + * - The Firebug console is a rich display compared with dump(), so there will + * be many things that we can't replicate + * - The primary use of this API is debugging and error logging so the perfect + * implementation isn't always required (or even well defined) + */ + +var gTimerRegistry = new Map(); + +/** + * String utility to ensure that strings are a specified length. Strings + * that are too long are truncated to the max length and the last char is + * set to "_". Strings that are too short are padded with spaces. + * + * @param {string} aStr + * The string to format to the correct length + * @param {number} aMaxLen + * The maximum allowed length of the returned string + * @param {number} aMinLen (optional) + * The minimum allowed length of the returned string. If undefined, + * then aMaxLen will be used + * @param {object} aOptions (optional) + * An object allowing format customization. Allowed customizations: + * 'truncate' - can take the value "start" to truncate strings from + * the start as opposed to the end or "center" to truncate + * strings in the center. + * 'align' - takes an alignment when padding is needed for MinLen, + * either "start" or "end". Defaults to "start". + * @return {string} + * The original string formatted to fit the specified lengths + */ +function fmt(aStr, aMaxLen, aMinLen, aOptions) { + if (aMinLen == null) { + aMinLen = aMaxLen; + } + if (aStr == null) { + aStr = ""; + } + if (aStr.length > aMaxLen) { + if (aOptions && aOptions.truncate == "start") { + return "_" + aStr.substring(aStr.length - aMaxLen + 1); + } else if (aOptions && aOptions.truncate == "center") { + let start = aStr.substring(0, aMaxLen / 2); + + let end = aStr.substring(aStr.length - aMaxLen / 2 + 1); + return start + "_" + end; + } + return aStr.substring(0, aMaxLen - 1) + "_"; + } + if (aStr.length < aMinLen) { + let padding = Array(aMinLen - aStr.length + 1).join(" "); + aStr = aOptions.align === "end" ? padding + aStr : aStr + padding; + } + return aStr; +} + +/** + * Utility to extract the constructor name of an object. + * Object.toString gives: "[object ?????]"; we want the "?????". + * + * @param {object} aObj + * The object from which to extract the constructor name + * @return {string} + * The constructor name + */ +function getCtorName(aObj) { + if (aObj === null) { + return "null"; + } + if (aObj === undefined) { + return "undefined"; + } + if (aObj.constructor && aObj.constructor.name) { + return aObj.constructor.name; + } + // If that fails, use Objects toString which sometimes gives something + // better than 'Object', and at least defaults to Object if nothing better + return Object.prototype.toString.call(aObj).slice(8, -1); +} + +/** + * Indicates whether an object is a JS or `Components.Exception` error. + * + * @param {object} aThing + The object to check + * @return {boolean} + Is this object an error? + */ +function isError(aThing) { + return ( + aThing && + ((typeof aThing.name == "string" && aThing.name.startsWith("NS_ERROR_")) || + getCtorName(aThing).endsWith("Error")) + ); +} + +/** + * A single line stringification of an object designed for use by humans + * + * @param {any} aThing + * The object to be stringified + * @param {boolean} aAllowNewLines + * @return {string} + * A single line representation of aThing, which will generally be at + * most 80 chars long + */ +function stringify(aThing, aAllowNewLines) { + if (aThing === undefined) { + return "undefined"; + } + + if (aThing === null) { + return "null"; + } + + if (isError(aThing)) { + return "Message: " + aThing; + } + + if (typeof aThing == "object") { + let type = getCtorName(aThing); + if (Element.isInstance(aThing)) { + return debugElement(aThing); + } + type = type == "Object" ? "" : type + " "; + let json; + try { + json = JSON.stringify(aThing); + } catch (ex) { + // Can't use a real ellipsis here, because cmd.exe isn't unicode-enabled + json = "{" + Object.keys(aThing).join(":..,") + ":.., }"; + } + return type + json; + } + + if (typeof aThing == "function") { + return aThing.toString().replace(/\s+/g, " "); + } + + let str = aThing.toString(); + if (!aAllowNewLines) { + str = str.replace(/\n/g, "|"); + } + return str; +} + +/** + * Create a simple debug representation of a given element. + * + * @param {Element} aElement + * The element to debug + * @return {string} + * A simple single line representation of aElement + */ +function debugElement(aElement) { + return ( + "<" + + aElement.tagName + + (aElement.id ? "#" + aElement.id : "") + + (aElement.className && aElement.className.split + ? "." + aElement.className.split(" ").join(" .") + : "") + + ">" + ); +} + +/** + * A multi line stringification of an object, designed for use by humans + * + * @param {any} aThing + * The object to be stringified + * @return {string} + * A multi line representation of aThing + */ +function log(aThing) { + if (aThing === null) { + return "null\n"; + } + + if (aThing === undefined) { + return "undefined\n"; + } + + if (typeof aThing == "object") { + let reply = ""; + let type = getCtorName(aThing); + if (type == "Map") { + reply += "Map\n"; + for (let [key, value] of aThing) { + reply += logProperty(key, value); + } + } else if (type == "Set") { + let i = 0; + reply += "Set\n"; + for (let value of aThing) { + reply += logProperty("" + i, value); + i++; + } + } else if (isError(aThing)) { + reply += " Message: " + aThing + "\n"; + if (aThing.stack) { + reply += " Stack:\n"; + var frame = aThing.stack; + while (frame) { + reply += " " + frame + "\n"; + frame = frame.caller; + } + } + } else if (Element.isInstance(aThing)) { + reply += " " + debugElement(aThing) + "\n"; + } else { + let keys = Object.getOwnPropertyNames(aThing); + if (keys.length) { + reply += type + "\n"; + keys.forEach(function (aProp) { + reply += logProperty(aProp, aThing[aProp]); + }); + } else { + reply += type + "\n"; + let root = aThing; + let logged = []; + while (root != null) { + let properties = Object.keys(root); + properties.sort(); + properties.forEach(function (property) { + if (!(property in logged)) { + logged[property] = property; + reply += logProperty(property, aThing[property]); + } + }); + + root = Object.getPrototypeOf(root); + if (root != null) { + reply += " - prototype " + getCtorName(root) + "\n"; + } + } + } + } + + return reply; + } + + return " " + aThing.toString() + "\n"; +} + +/** + * Helper for log() which converts a property/value pair into an output + * string + * + * @param {string} aProp + * The name of the property to include in the output string + * @param {object} aValue + * Value assigned to aProp to be converted to a single line string + * @return {string} + * Multi line output string describing the property/value pair + */ +function logProperty(aProp, aValue) { + let reply = ""; + if (aProp == "stack" && typeof value == "string") { + let trace = parseStack(aValue); + reply += formatTrace(trace); + } else { + reply += " - " + aProp + " = " + stringify(aValue) + "\n"; + } + return reply; +} + +const LOG_LEVELS = { + all: Number.MIN_VALUE, + debug: 2, + log: 3, + info: 3, + clear: 3, + trace: 3, + timeEnd: 3, + time: 3, + assert: 3, + group: 3, + groupEnd: 3, + profile: 3, + profileEnd: 3, + dir: 3, + dirxml: 3, + warn: 4, + error: 5, + off: Number.MAX_VALUE, +}; + +/** + * Helper to tell if a console message of `aLevel` type + * should be logged in stdout and sent to consoles given + * the current maximum log level being defined in `console.maxLogLevel` + * + * @param {string} aLevel + * Console message log level + * @param {string} aMaxLevel {string} + * String identifier (See LOG_LEVELS for possible + * values) that allows to filter which messages + * are logged based on their log level + * @return {boolean} + * Should this message be logged or not? + */ +function shouldLog(aLevel, aMaxLevel) { + return LOG_LEVELS[aMaxLevel] <= LOG_LEVELS[aLevel]; +} + +/** + * Parse a stack trace, returning an array of stack frame objects, where + * each has filename/lineNumber/functionName members + * + * @param {string} aStack + * The serialized stack trace + * @return {object[]} + * Array of { file: "...", line: NNN, call: "..." } objects + */ +function parseStack(aStack) { + let trace = []; + aStack.split("\n").forEach(function (line) { + if (!line) { + return; + } + let at = line.lastIndexOf("@"); + let posn = line.substring(at + 1); + trace.push({ + filename: posn.split(":")[0], + lineNumber: posn.split(":")[1], + functionName: line.substring(0, at), + }); + }); + return trace; +} + +/** + * Format a frame coming from Components.stack such that it can be used by the + * Browser Console, via ConsoleAPIStorage notifications. + * + * @param {object} aFrame + * The stack frame from which to begin the walk. + * @param {number=0} aMaxDepth + * Maximum stack trace depth. Default is 0 - no depth limit. + * @return {object[]} + * An array of {filename, lineNumber, functionName, language} objects. + * These objects follow the same format as other ConsoleAPIStorage + * messages. + */ +function getStack(aFrame, aMaxDepth = 0) { + if (!aFrame) { + aFrame = Components.stack.caller; + } + let trace = []; + while (aFrame) { + trace.push({ + filename: aFrame.filename, + lineNumber: aFrame.lineNumber, + functionName: aFrame.name, + language: aFrame.language, + }); + if (aMaxDepth == trace.length) { + break; + } + aFrame = aFrame.caller; + } + return trace; +} + +/** + * Take the output from parseStack() and convert it to nice readable + * output + * + * @param {object[]} aTrace + * Array of trace objects as created by parseStack() + * @return {string} Multi line report of the stack trace + */ +function formatTrace(aTrace) { + let reply = ""; + aTrace.forEach(function (frame) { + reply += + fmt(frame.filename, 20, 20, { truncate: "start" }) + + " " + + fmt(frame.lineNumber, 5, 5) + + " " + + fmt(frame.functionName, 75, 0, { truncate: "center" }) + + "\n"; + }); + return reply; +} + +/** + * Create a new timer by recording the current time under the specified name. + * + * @param {string} aName + * The name of the timer. + * @param {number} [aTimestamp=Date.now()] + * Optional timestamp that tells when the timer was originally started. + * @return {object} + * The name property holds the timer name and the started property + * holds the time the timer was started. In case of error, it returns + * an object with the single property "error" that contains the key + * for retrieving the localized error message. + */ +function startTimer(aName, aTimestamp) { + let key = aName.toString(); + if (!gTimerRegistry.has(key)) { + gTimerRegistry.set(key, aTimestamp || Date.now()); + } + return { name: aName, started: gTimerRegistry.get(key) }; +} + +/** + * Stop the timer with the specified name and retrieve the elapsed time. + * + * @param {string} aName + * The name of the timer. + * @param {number} [aTimestamp=Date.now()] + * Optional timestamp that tells when the timer was originally stopped. + * @return {object} + * The name property holds the timer name and the duration property + * holds the number of milliseconds since the timer was started. + */ +function stopTimer(aName, aTimestamp) { + let key = aName.toString(); + let duration = (aTimestamp || Date.now()) - gTimerRegistry.get(key); + gTimerRegistry.delete(key); + return { name: aName, duration }; +} + +/** + * Dump a new message header to stdout by taking care of adding an eventual + * prefix + * + * @param {object} aConsole + * ConsoleAPI instance + * @param {string} aLevel + * The string identifier for the message log level + * @param {string} aMessage + * The string message to print to stdout + */ +function dumpMessage(aConsole, aLevel, aMessage) { + aConsole.dump( + "console." + + aLevel + + ": " + + (aConsole.prefix ? aConsole.prefix + ": " : "") + + aMessage + + "\n" + ); +} + +/** + * Create a function which will output a concise level of output when used + * as a logging function + * + * @param {string} aLevel + * A prefix to all output generated from this function detailing the + * level at which output occurred + * @return {function} + * A logging function + * @see createMultiLineDumper() + */ +function createDumper(aLevel) { + return function () { + if (!shouldLog(aLevel, this.maxLogLevel)) { + return; + } + let args = Array.prototype.slice.call(arguments, 0); + let frame = getStack(Components.stack.caller, 1)[0]; + sendConsoleAPIMessage(this, aLevel, frame, args); + let data = args.map(function (arg) { + return stringify(arg, true); + }); + dumpMessage(this, aLevel, data.join(" ")); + }; +} + +/** + * Create a function which will output more detailed level of output when + * used as a logging function + * + * @param {string} aLevel + * A prefix to all output generated from this function detailing the + * level at which output occurred + * @return {function} + * A logging function + * @see createDumper() + */ +function createMultiLineDumper(aLevel) { + return function () { + if (!shouldLog(aLevel, this.maxLogLevel)) { + return; + } + dumpMessage(this, aLevel, ""); + let args = Array.prototype.slice.call(arguments, 0); + let frame = getStack(Components.stack.caller, 1)[0]; + sendConsoleAPIMessage(this, aLevel, frame, args); + args.forEach(function (arg) { + this.dump(log(arg)); + }, this); + }; +} + +/** + * Send a Console API message. This function will send a notification through + * the nsIConsoleAPIStorage service. + * + * @param {object} aConsole + * The instance of ConsoleAPI performing the logging. + * @param {string} aLevel + * Message severity level. This is usually the name of the console method + * that was called. + * @param {object} aFrame + * The youngest stack frame coming from Components.stack, as formatted by + * getStack(). + * @param {array} aArgs + * The arguments given to the console method. + * @param {object} aOptions + * Object properties depend on the console method that was invoked: + * - timer: for time() and timeEnd(). Holds the timer information. + * - groupName: for group(), groupCollapsed() and groupEnd(). + * - stacktrace: for trace(). Holds the array of stack frames as given by + * getStack(). + */ +function sendConsoleAPIMessage(aConsole, aLevel, aFrame, aArgs, aOptions = {}) { + let consoleEvent = { + ID: "jsm", + innerID: aConsole.innerID || aFrame.filename, + consoleID: aConsole.consoleID, + level: aLevel, + filename: aFrame.filename, + lineNumber: aFrame.lineNumber, + functionName: aFrame.functionName, + timeStamp: Date.now(), + arguments: aArgs, + prefix: aConsole.prefix, + chromeContext: true, + }; + + consoleEvent.wrappedJSObject = consoleEvent; + + switch (aLevel) { + case "trace": + consoleEvent.stacktrace = aOptions.stacktrace; + break; + case "time": + case "timeEnd": + consoleEvent.timer = aOptions.timer; + break; + case "group": + case "groupCollapsed": + case "groupEnd": + try { + consoleEvent.groupName = Array.prototype.join.call(aArgs, " "); + } catch (ex) { + console.error(ex); + console.error(ex.stack); + return; + } + break; + } + + let ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"].getService( + Ci.nsIConsoleAPIStorage + ); + if (ConsoleAPIStorage) { + ConsoleAPIStorage.recordEvent("jsm", consoleEvent); + } +} + +/** + * This creates a console object that somewhat replicates Firebug's console + * object + * + * @param {object} aConsoleOptions + * Optional dictionary with a set of runtime console options: + * - prefix {string} : An optional prefix string to be printed before + * the actual logged message + * - maxLogLevel {string} : String identifier (See LOG_LEVELS for + * possible values) that allows to filter which + * messages are logged based on their log level. + * If falsy value, all messages will be logged. + * If wrong value that doesn't match any key of + * LOG_LEVELS, no message will be logged + * - maxLogLevelPref {string} : String pref name which contains the + * level to use for maxLogLevel. If the pref doesn't + * exist or gets removed, the maxLogLevel will default + * to the value passed to this constructor (or "all" + * if it wasn't specified). + * - dump {function} : An optional function to intercept all strings + * written to stdout + * - innerID {string}: An ID representing the source of the message. + * Normally the inner ID of a DOM window. + * - consoleID {string} : String identified for the console, this will + * be passed through the console notifications + * @return {object} + * A console API instance object + */ +export function ConsoleAPI(aConsoleOptions = {}) { + // Normalize console options to set default values + // in order to avoid runtime checks on each console method call. + this.dump = aConsoleOptions.dump || dump; + this.prefix = aConsoleOptions.prefix || ""; + this.maxLogLevel = aConsoleOptions.maxLogLevel; + this.innerID = aConsoleOptions.innerID || null; + this.consoleID = aConsoleOptions.consoleID || ""; + + // Setup maxLogLevelPref watching + let updateMaxLogLevel = () => { + if ( + Services.prefs.getPrefType(aConsoleOptions.maxLogLevelPref) == + Services.prefs.PREF_STRING + ) { + this._maxLogLevel = Services.prefs + .getCharPref(aConsoleOptions.maxLogLevelPref) + .toLowerCase(); + } else { + this._maxLogLevel = this._maxExplicitLogLevel; + } + }; + + if (aConsoleOptions.maxLogLevelPref) { + updateMaxLogLevel(); + Services.prefs.addObserver( + aConsoleOptions.maxLogLevelPref, + updateMaxLogLevel + ); + } + + // Bind all the functions to this object. + for (let prop in this) { + if (typeof this[prop] === "function") { + this[prop] = this[prop].bind(this); + } + } +} + +ConsoleAPI.prototype = { + /** + * The last log level that was specified via the constructor or setter. This + * is used as a fallback if the pref doesn't exist or is removed. + */ + _maxExplicitLogLevel: null, + /** + * The current log level via all methods of setting (pref or via the API). + */ + _maxLogLevel: null, + debug: createMultiLineDumper("debug"), + assert: createDumper("assert"), + log: createDumper("log"), + info: createDumper("info"), + warn: createDumper("warn"), + error: createMultiLineDumper("error"), + exception: createMultiLineDumper("error"), + + trace: function Console_trace() { + if (!shouldLog("trace", this.maxLogLevel)) { + return; + } + let args = Array.prototype.slice.call(arguments, 0); + let trace = getStack(Components.stack.caller); + sendConsoleAPIMessage(this, "trace", trace[0], args, { stacktrace: trace }); + dumpMessage(this, "trace", "\n" + formatTrace(trace)); + }, + clear: function Console_clear() {}, + + dir: createMultiLineDumper("dir"), + dirxml: createMultiLineDumper("dirxml"), + group: createDumper("group"), + groupEnd: createDumper("groupEnd"), + + time: function Console_time() { + if (!shouldLog("time", this.maxLogLevel)) { + return; + } + let args = Array.prototype.slice.call(arguments, 0); + let frame = getStack(Components.stack.caller, 1)[0]; + let timer = startTimer(args[0]); + sendConsoleAPIMessage(this, "time", frame, args, { timer }); + dumpMessage(this, "time", "'" + timer.name + "' @ " + new Date()); + }, + + timeEnd: function Console_timeEnd() { + if (!shouldLog("timeEnd", this.maxLogLevel)) { + return; + } + let args = Array.prototype.slice.call(arguments, 0); + let frame = getStack(Components.stack.caller, 1)[0]; + let timer = stopTimer(args[0]); + sendConsoleAPIMessage(this, "timeEnd", frame, args, { timer }); + dumpMessage( + this, + "timeEnd", + "'" + timer.name + "' " + timer.duration + "ms" + ); + }, + + profile(profileName) { + if (!shouldLog("profile", this.maxLogLevel)) { + return; + } + Services.obs.notifyObservers( + { + wrappedJSObject: { + action: "profile", + arguments: [profileName], + chromeContext: true, + }, + }, + "console-api-profiler" + ); + dumpMessage(this, "profile", `'${profileName}'`); + }, + + profileEnd(profileName) { + if (!shouldLog("profileEnd", this.maxLogLevel)) { + return; + } + Services.obs.notifyObservers( + { + wrappedJSObject: { + action: "profileEnd", + arguments: [profileName], + chromeContext: true, + }, + }, + "console-api-profiler" + ); + dumpMessage(this, "profileEnd", `'${profileName}'`); + }, + + get maxLogLevel() { + return this._maxLogLevel || "all"; + }, + + set maxLogLevel(aValue) { + this._maxLogLevel = this._maxExplicitLogLevel = aValue; + }, + + shouldLog(aLevel) { + return shouldLog(aLevel, this.maxLogLevel); + }, +}; diff --git a/toolkit/modules/ContentDOMReference.sys.mjs b/toolkit/modules/ContentDOMReference.sys.mjs new file mode 100644 index 0000000000..ef4896ff67 --- /dev/null +++ b/toolkit/modules/ContentDOMReference.sys.mjs @@ -0,0 +1,177 @@ +/* vim: set ts=2 sw=2 sts=2 et tw=80: */ +/* 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/. */ + +/** + * This module holds weak references to DOM elements that exist within the + * current content process, and converts them to a unique identifier that can be + * passed between processes. The identifer, if received by the same content process + * that issued it, can then be converted back into the DOM element (presuming the + * element hasn't had all of its other references dropped). + * + * The hope is that this module can eliminate the need for passing CPOW references + * between processes during runtime. + */ + +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const lazy = {}; + +XPCOMUtils.defineLazyServiceGetter( + lazy, + "finalizationService", + "@mozilla.org/toolkit/finalizationwitness;1", + "nsIFinalizationWitnessService" +); + +/** + * @typedef {number} ElementID + * @typedef {Object} ElementIdentifier + */ + +const FINALIZATION_TOPIC = "content-dom-reference-finalized"; + +// A WeakMap which ties finalization witness objects to the lifetime of the DOM +// nodes they're meant to witness. When the DOM node in the map key is +// finalized, the WeakMap stops holding the finalization witness in its value +// alive, which alerts our observer that the element has been destroyed. +const finalizerRoots = new WeakMap(); + +/** + * An identifier generated by ContentDOMReference is a unique pair of BrowsingContext + * ID and a numeric ID. gRegistry maps BrowsingContext's to an object with the following + * properties: + * + * IDToElement: + * A Map of IDs to WeakReference's to the elements they refer to. + * + * elementToID: + * A WeakMap from a DOM element to an ID that refers to it. + */ +var gRegistry = new WeakMap(); + +export var ContentDOMReference = { + _init() { + Services.obs.addObserver(this, FINALIZATION_TOPIC); + }, + + observe(subject, topic, data) { + if (topic !== FINALIZATION_TOPIC) { + throw new Error("Unexpected observer topic"); + } + + let identifier = JSON.parse(data); + this._revoke(identifier); + }, + + /** + * Generate and return an identifier for a given DOM element. + * + * @param {Element} element The DOM element to generate the identifier for. + * @return {ElementIdentifier} The identifier for the DOM element that can be passed between + * processes as a message. + */ + get(element) { + if (!element) { + throw new Error( + "Can't create a ContentDOMReference identifier for " + + "non-existant nodes." + ); + } + + let browsingContext = BrowsingContext.getFromWindow(element.ownerGlobal); + let mappings = gRegistry.get(browsingContext); + if (!mappings) { + mappings = { + IDToElement: new Map(), + elementToID: new WeakMap(), + }; + gRegistry.set(browsingContext, mappings); + } + + let id = mappings.elementToID.get(element); + if (id) { + // We already had this element registered, so return the pre-existing ID. + return { browsingContextId: browsingContext.id, id }; + } + + // We must be registering a new element at this point. + id = Math.random(); + mappings.elementToID.set(element, id); + mappings.IDToElement.set(id, Cu.getWeakReference(element)); + + let identifier = { browsingContextId: browsingContext.id, id }; + + finalizerRoots.set( + element, + lazy.finalizationService.make( + FINALIZATION_TOPIC, + JSON.stringify(identifier) + ) + ); + + return identifier; + }, + + /** + * Resolves an identifier back into the DOM Element that it was generated from. + * + * @param {ElementIdentifier} The identifier generated via ContentDOMReference.get for a + * DOM element. + * @return {Element} The DOM element that the identifier was generated for, or + * null if the element does not still exist. + */ + resolve(identifier) { + let browsingContext = BrowsingContext.get(identifier.browsingContextId); + let { id } = identifier; + return this._resolveIDToElement(browsingContext, id); + }, + + /** + * Removes an identifier from the registry so that subsequent attempts + * to resolve it will result in null. This is done automatically when the + * target node is GCed. + * + * @param {ElementIdentifier} The identifier to revoke, issued by ContentDOMReference.get for + * a DOM element. + */ + _revoke(identifier) { + let browsingContext = BrowsingContext.get(identifier.browsingContextId); + let { id } = identifier; + + let mappings = gRegistry.get(browsingContext); + if (!mappings) { + return; + } + + mappings.IDToElement.delete(id); + }, + + /** + * Private helper function that resolves a BrowsingContext and ID (the + * pair that makes up an identifier) to a DOM element. + * + * @param {BrowsingContext} browsingContext The BrowsingContext that was hosting + * the DOM element at the time that the identifier was generated. + * @param {ElementID} id The ID generated for the DOM element. + * + * @return {Element} The DOM element that the identifier was generated for, or + * null if the element does not still exist. + */ + _resolveIDToElement(browsingContext, id) { + let mappings = gRegistry.get(browsingContext); + if (!mappings) { + return null; + } + + let weakReference = mappings.IDToElement.get(id); + if (!weakReference) { + return null; + } + + return weakReference.get(); + }, +}; + +ContentDOMReference._init(); diff --git a/toolkit/modules/CreditCard.sys.mjs b/toolkit/modules/CreditCard.sys.mjs new file mode 100644 index 0000000000..622c371d76 --- /dev/null +++ b/toolkit/modules/CreditCard.sys.mjs @@ -0,0 +1,525 @@ +/* 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/. */ + +// The list of known and supported credit card network ids ("types") +// This list mirrors the networks from dom/payments/BasicCardPayment.cpp +// and is defined by https://www.w3.org/Payments/card-network-ids +const SUPPORTED_NETWORKS = Object.freeze([ + "amex", + "cartebancaire", + "diners", + "discover", + "jcb", + "mastercard", + "mir", + "unionpay", + "visa", +]); + +// This lists stores lower cased variations of popular credit card network +// names for matching against strings. +export const NETWORK_NAMES = { + "american express": "amex", + "master card": "mastercard", + "union pay": "unionpay", +}; + +// Based on https://en.wikipedia.org/wiki/Payment_card_number +// +// Notice: +// - CarteBancaire (`4035`, `4360`) is now recognized as Visa. +// - UnionPay (`63--`) is now recognized as Discover. +// This means that the order matters. +// First we'll try to match more specific card, +// and if that doesn't match we'll test against the more generic range. +const CREDIT_CARD_IIN = [ + { type: "amex", start: 34, end: 34, len: 15 }, + { type: "amex", start: 37, end: 37, len: 15 }, + { type: "cartebancaire", start: 4035, end: 4035, len: 16 }, + { type: "cartebancaire", start: 4360, end: 4360, len: 16 }, + // We diverge from Wikipedia here, because Diners card + // support length of 14-19. + { type: "diners", start: 300, end: 305, len: [14, 19] }, + { type: "diners", start: 3095, end: 3095, len: [14, 19] }, + { type: "diners", start: 36, end: 36, len: [14, 19] }, + { type: "diners", start: 38, end: 39, len: [14, 19] }, + { type: "discover", start: 6011, end: 6011, len: [16, 19] }, + { type: "discover", start: 622126, end: 622925, len: [16, 19] }, + { type: "discover", start: 624000, end: 626999, len: [16, 19] }, + { type: "discover", start: 628200, end: 628899, len: [16, 19] }, + { type: "discover", start: 64, end: 65, len: [16, 19] }, + { type: "jcb", start: 3528, end: 3589, len: [16, 19] }, + { type: "mastercard", start: 2221, end: 2720, len: 16 }, + { type: "mastercard", start: 51, end: 55, len: 16 }, + { type: "mir", start: 2200, end: 2204, len: 16 }, + { type: "unionpay", start: 62, end: 62, len: [16, 19] }, + { type: "unionpay", start: 81, end: 81, len: [16, 19] }, + { type: "visa", start: 4, end: 4, len: 16 }, +].sort((a, b) => b.start - a.start); + +export class CreditCard { + /** + * A CreditCard object represents a credit card, with + * number, name, expiration, network, and CCV. + * The number is the only required information when creating + * an object, all other members are optional. The number + * is validated during construction and will throw if invalid. + * + * @param {string} name, optional + * @param {string} number + * @param {string} expirationString, optional + * @param {string|number} expirationMonth, optional + * @param {string|number} expirationYear, optional + * @param {string} network, optional + * @param {string|number} ccv, optional + * @param {string} encryptedNumber, optional + * @throws if number is an invalid credit card number + */ + constructor({ + name, + number, + expirationString, + expirationMonth, + expirationYear, + network, + ccv, + encryptedNumber, + }) { + this._name = name; + this._unmodifiedNumber = number; + this._encryptedNumber = encryptedNumber; + this._ccv = ccv; + this.number = number; + let { month, year } = CreditCard.normalizeExpiration({ + expirationString, + expirationMonth, + expirationYear, + }); + this._expirationMonth = month; + this._expirationYear = year; + this.network = network; + } + + set name(value) { + this._name = value; + } + + set expirationMonth(value) { + if (typeof value == "undefined") { + this._expirationMonth = undefined; + return; + } + this._expirationMonth = CreditCard.normalizeExpirationMonth(value); + } + + get expirationMonth() { + return this._expirationMonth; + } + + set expirationYear(value) { + if (typeof value == "undefined") { + this._expirationYear = undefined; + return; + } + this._expirationYear = CreditCard.normalizeExpirationYear(value); + } + + get expirationYear() { + return this._expirationYear; + } + + set expirationString(value) { + let { month, year } = CreditCard.parseExpirationString(value); + this.expirationMonth = month; + this.expirationYear = year; + } + + set ccv(value) { + this._ccv = value; + } + + get number() { + return this._number; + } + + /** + * Sets the number member of a CreditCard object. If the number + * is not valid according to the Luhn algorithm then the member + * will get set to the empty string before throwing an exception. + * + * @param {string} value + * @throws if the value is an invalid credit card number + */ + set number(value) { + if (value) { + let normalizedNumber = CreditCard.normalizeCardNumber(value); + // Based on the information on wiki[1], the shortest valid length should be + // 12 digits (Maestro). + // [1] https://en.wikipedia.org/wiki/Payment_card_number + normalizedNumber = normalizedNumber.match(/^\d{12,}$/) + ? normalizedNumber + : ""; + this._number = normalizedNumber; + } else { + this._number = ""; + } + + if (value && !this.isValidNumber()) { + this._number = ""; + throw new Error("Invalid credit card number"); + } + } + + get network() { + return this._network; + } + + set network(value) { + this._network = value || undefined; + } + + // Implements the Luhn checksum algorithm as described at + // http://wikipedia.org/wiki/Luhn_algorithm + // Number digit lengths vary with network, but should fall within 12-19 range. [2] + // More details at https://en.wikipedia.org/wiki/Payment_card_number + isValidNumber() { + if (!this._number) { + return false; + } + + // Remove dashes and whitespace + const number = CreditCard.normalizeCardNumber(this._number); + + const len = number.length; + if (len < 12 || len > 19) { + return false; + } + + if (!/^\d+$/.test(number)) { + return false; + } + + let total = 0; + for (let i = 0; i < len; i++) { + let ch = parseInt(number[len - i - 1], 10); + if (i % 2 == 1) { + // Double it, add digits together if > 10 + ch *= 2; + if (ch > 9) { + ch -= 9; + } + } + total += ch; + } + return total % 10 == 0; + } + + /** + * Normalizes a credit card number. + * @param {string} number + * @return {string | null} + * @memberof CreditCard + */ + static normalizeCardNumber(number) { + if (!number) { + return null; + } + return number.replace(/[\-\s]/g, ""); + } + + /** + * Attempts to match the number against known network identifiers. + * + * @param {string} ccNumber Credit card number with no spaces or special characters in it. + * + * @returns {string|null} + */ + static getType(ccNumber) { + if (!ccNumber) { + return null; + } + + for (let i = 0; i < CREDIT_CARD_IIN.length; i++) { + const range = CREDIT_CARD_IIN[i]; + if (typeof range.len == "number") { + if (range.len != ccNumber.length) { + continue; + } + } else if ( + ccNumber.length < range.len[0] || + ccNumber.length > range.len[1] + ) { + continue; + } + + const prefixLength = Math.floor(Math.log10(range.start)) + 1; + const prefix = parseInt(ccNumber.substring(0, prefixLength), 10); + if (prefix >= range.start && prefix <= range.end) { + return range.type; + } + } + return null; + } + + /** + * Attempts to retrieve a card network identifier based + * on a name. + * + * @param {string|undefined|null} name + * + * @returns {string|null} + */ + static getNetworkFromName(name) { + if (!name) { + return null; + } + let lcName = name.trim().toLowerCase().normalize("NFKC"); + if (SUPPORTED_NETWORKS.includes(lcName)) { + return lcName; + } + for (let term in NETWORK_NAMES) { + if (lcName.includes(term)) { + return NETWORK_NAMES[term]; + } + } + return null; + } + + /** + * Returns true if the card number is valid and the + * expiration date has not passed. Otherwise false. + * + * @returns {boolean} + */ + isValid() { + if (!this.isValidNumber()) { + return false; + } + + let currentDate = new Date(); + let currentYear = currentDate.getFullYear(); + if (this._expirationYear > currentYear) { + return true; + } + + // getMonth is 0-based, so add 1 because credit cards are 1-based + let currentMonth = currentDate.getMonth() + 1; + return ( + this._expirationYear == currentYear && + this._expirationMonth >= currentMonth + ); + } + + get maskedNumber() { + return CreditCard.getMaskedNumber(this._number); + } + + get longMaskedNumber() { + return CreditCard.getLongMaskedNumber(this._number); + } + + /** + * Get credit card display label. It should display masked numbers, the + * cardholder's name, and the expiration date, separated by a commas. + * In addition, the card type is provided in the accessibility label. + */ + static getLabelInfo({ number, name, month, year, type }) { + let formatSelector = ["number"]; + if (name) { + formatSelector.push("name"); + } + if (month && year) { + formatSelector.push("expiration"); + } + let stringId = `credit-card-label-${formatSelector.join("-")}-2`; + return { + id: stringId, + args: { + number: CreditCard.getMaskedNumber(number), + name, + month: month?.toString(), + year: year?.toString(), + type, + }, + }; + } + + /** + * + * Please use getLabelInfo above, as it allows for localization. + * @deprecated + */ + static getLabel({ number, name }) { + let parts = []; + + if (number) { + parts.push(CreditCard.getMaskedNumber(number)); + } + if (name) { + parts.push(name); + } + return parts.join(", "); + } + + static normalizeExpirationMonth(month) { + month = parseInt(month, 10); + if (isNaN(month) || month < 1 || month > 12) { + return undefined; + } + return month; + } + + static normalizeExpirationYear(year) { + year = parseInt(year, 10); + if (isNaN(year) || year < 0) { + return undefined; + } + if (year < 100) { + year += 2000; + } + return year; + } + + static parseExpirationString(expirationString) { + let rules = [ + { + regex: /(?:^|\D)(\d{2})(\d{2})(?!\d)/, + }, + { + regex: /(?:^|\D)(\d{4})[-/](\d{1,2})(?!\d)/, + yearIndex: 0, + monthIndex: 1, + }, + { + regex: /(?:^|\D)(\d{1,2})[-/](\d{4})(?!\d)/, + yearIndex: 1, + monthIndex: 0, + }, + { + regex: /(?:^|\D)(\d{1,2})[-/](\d{1,2})(?!\d)/, + }, + { + regex: /(?:^|\D)(\d{2})(\d{2})(?!\d)/, + }, + ]; + + expirationString = expirationString.replaceAll(" ", ""); + for (let rule of rules) { + let result = rule.regex.exec(expirationString); + if (!result) { + continue; + } + + let year, month; + const parsedResults = [parseInt(result[1], 10), parseInt(result[2], 10)]; + if (!rule.yearIndex || !rule.monthIndex) { + month = parsedResults[0]; + if (month > 12) { + year = parsedResults[0]; + month = parsedResults[1]; + } else { + year = parsedResults[1]; + } + } else { + year = parsedResults[rule.yearIndex]; + month = parsedResults[rule.monthIndex]; + } + + if (month >= 1 && month <= 12 && (year < 100 || year > 2000)) { + return { month, year }; + } + } + return { month: undefined, year: undefined }; + } + + static normalizeExpiration({ + expirationString, + expirationMonth, + expirationYear, + }) { + // Only prefer the string version if missing one or both parsed formats. + let parsedExpiration = {}; + if (expirationString && (!expirationMonth || !expirationYear)) { + parsedExpiration = CreditCard.parseExpirationString(expirationString); + } + return { + month: CreditCard.normalizeExpirationMonth( + parsedExpiration.month || expirationMonth + ), + year: CreditCard.normalizeExpirationYear( + parsedExpiration.year || expirationYear + ), + }; + } + + static formatMaskedNumber(maskedNumber) { + return "*".repeat(4) + maskedNumber.substr(-4); + } + + static getMaskedNumber(number) { + return "*".repeat(4) + " " + number.substr(-4); + } + + static getLongMaskedNumber(number) { + return "*".repeat(number.length - 4) + number.substr(-4); + } + + static getCreditCardLogo(network) { + const PATH = "chrome://formautofill/content/"; + const THIRD_PARTY_PATH = PATH + "third-party/"; + switch (network) { + case "amex": + return THIRD_PARTY_PATH + "cc-logo-amex.png"; + case "cartebancaire": + return THIRD_PARTY_PATH + "cc-logo-cartebancaire.png"; + case "diners": + return THIRD_PARTY_PATH + "cc-logo-diners.svg"; + case "discover": + return THIRD_PARTY_PATH + "cc-logo-discover.png"; + case "jcb": + return THIRD_PARTY_PATH + "cc-logo-jcb.svg"; + case "mastercard": + return THIRD_PARTY_PATH + "cc-logo-mastercard.svg"; + case "mir": + return THIRD_PARTY_PATH + "cc-logo-mir.svg"; + case "unionpay": + return THIRD_PARTY_PATH + "cc-logo-unionpay.svg"; + case "visa": + return THIRD_PARTY_PATH + "cc-logo-visa.svg"; + default: + return PATH + "icon-credit-card-generic.svg"; + } + } + + /* + * Validates the number according to the Luhn algorithm. This + * method does not throw an exception if the number is invalid. + */ + static isValidNumber(number) { + try { + new CreditCard({ number }); + } catch (ex) { + return false; + } + return true; + } + + static isValidNetwork(network) { + return SUPPORTED_NETWORKS.includes(network); + } + + static getSupportedNetworks() { + return SUPPORTED_NETWORKS; + } + + /** + * Localised names for supported networks are available in + * `browser/preferences/formAutofill.ftl`. + */ + static getNetworkL10nId(network) { + return this.isValidNetwork(network) + ? `autofill-card-network-${network}` + : null; + } +} diff --git a/toolkit/modules/DateTimePickerPanel.sys.mjs b/toolkit/modules/DateTimePickerPanel.sys.mjs new file mode 100644 index 0000000000..8d67cb0d8e --- /dev/null +++ b/toolkit/modules/DateTimePickerPanel.sys.mjs @@ -0,0 +1,309 @@ +/* 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 var DateTimePickerPanel = class { + constructor(element) { + this.element = element; + + this.TIME_PICKER_WIDTH = "13em"; + this.TIME_PICKER_HEIGHT = "22em"; + this.DATE_PICKER_WIDTH = "24em"; + this.DATE_PICKER_HEIGHT = "27em"; + } + + get dateTimePopupFrame() { + let frame = this.element.querySelector("#dateTimePopupFrame"); + if (!frame) { + frame = this.element.ownerDocument.createXULElement("iframe"); + frame.id = "dateTimePopupFrame"; + this.element.appendChild(frame); + } + return frame; + } + + openPicker(type, rect, detail) { + if (type == "datetime-local") { + type = "date"; + } + this.type = type; + this.pickerState = {}; + // TODO: Resize picker according to content zoom level + this.element.style.fontSize = "10px"; + switch (type) { + case "time": { + this.detail = detail; + this.dateTimePopupFrame.addEventListener("load", this, true); + this.dateTimePopupFrame.setAttribute( + "src", + "chrome://global/content/timepicker.xhtml" + ); + this.dateTimePopupFrame.style.width = this.TIME_PICKER_WIDTH; + this.dateTimePopupFrame.style.height = this.TIME_PICKER_HEIGHT; + break; + } + case "date": { + this.detail = detail; + this.dateTimePopupFrame.addEventListener("load", this, true); + this.dateTimePopupFrame.setAttribute( + "src", + "chrome://global/content/datepicker.xhtml" + ); + this.dateTimePopupFrame.style.width = this.DATE_PICKER_WIDTH; + this.dateTimePopupFrame.style.height = this.DATE_PICKER_HEIGHT; + break; + } + } + this.element.openPopupAtScreenRect( + "after_start", + rect.left, + rect.top, + rect.width, + rect.height, + false, + false + ); + } + + closePicker(clear) { + if (clear) { + this.element.dispatchEvent(new CustomEvent("DateTimePickerValueCleared")); + } else { + this.setInputBoxValue(true); + } + this.pickerState = {}; + this.type = undefined; + this.dateTimePopupFrame.removeEventListener("load", this, true); + this.dateTimePopupFrame.contentDocument.removeEventListener( + "message", + this + ); + this.dateTimePopupFrame.setAttribute("src", ""); + this.element.hidePopup(); + } + + setPopupValue(data) { + switch (this.type) { + case "time": { + this.postMessageToPicker({ + name: "PickerSetValue", + detail: data.value, + }); + break; + } + case "date": { + const { year, month, day } = data.value; + this.postMessageToPicker({ + name: "PickerSetValue", + detail: { + year, + // Month value from input box starts from 1 instead of 0 + month: month == undefined ? undefined : month - 1, + day, + }, + }); + break; + } + } + } + + initPicker(detail) { + let locale = new Services.intl.Locale( + Services.locale.webExposedLocales[0], + { + calendar: "gregory", + } + ).toString(); + + // Workaround for bug 1418061, while we wait for resolution of + // http://bugs.icu-project.org/trac/ticket/13592: drop the PT region code, + // because it results in "abbreviated" day names that are too long; + // the region-less "pt" locale has shorter forms that are better here. + locale = locale.replace(/^pt-PT/i, "pt"); + + const dir = Services.locale.isAppLocaleRTL ? "rtl" : "ltr"; + + switch (this.type) { + case "time": { + const { hour, minute } = detail.value; + const format = detail.format || "12"; + + this.postMessageToPicker({ + name: "PickerInit", + detail: { + hour, + minute, + format, + locale, + min: detail.min, + max: detail.max, + step: detail.step, + }, + }); + break; + } + case "date": { + const { year, month, day } = detail.value; + const { firstDayOfWeek, weekends } = this.getCalendarInfo(locale); + + const monthDisplayNames = new Services.intl.DisplayNames(locale, { + type: "month", + style: "short", + calendar: "gregory", + }); + const monthStrings = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map( + month => monthDisplayNames.of(month) + ); + + const weekdayDisplayNames = new Services.intl.DisplayNames(locale, { + type: "weekday", + style: "abbreviated", + calendar: "gregory", + }); + const weekdayStrings = [ + // Weekdays starting Sunday (7) to Saturday (6). + 7, 1, 2, 3, 4, 5, 6, + ].map(weekday => weekdayDisplayNames.of(weekday)); + + this.postMessageToPicker({ + name: "PickerInit", + detail: { + year, + // Month value from input box starts from 1 instead of 0 + month: month == undefined ? undefined : month - 1, + day, + firstDayOfWeek, + weekends, + monthStrings, + weekdayStrings, + locale, + dir, + min: detail.min, + max: detail.max, + step: detail.step, + stepBase: detail.stepBase, + }, + }); + break; + } + } + } + + /** + * @param {Boolean} passAllValues: Pass spinner values regardless if they've been set/changed or not + */ + setInputBoxValue(passAllValues) { + switch (this.type) { + case "time": { + const { hour, minute, isHourSet, isMinuteSet, isDayPeriodSet } = + this.pickerState; + const isAnyValueSet = isHourSet || isMinuteSet || isDayPeriodSet; + if (passAllValues && isAnyValueSet) { + this.sendPickerValueChanged({ hour, minute }); + } else { + this.sendPickerValueChanged({ + hour: isHourSet || isDayPeriodSet ? hour : undefined, + minute: isMinuteSet ? minute : undefined, + }); + } + break; + } + case "date": { + this.sendPickerValueChanged(this.pickerState); + break; + } + } + } + + sendPickerValueChanged(value) { + switch (this.type) { + case "time": { + this.element.dispatchEvent( + new CustomEvent("DateTimePickerValueChanged", { + detail: { + hour: value.hour, + minute: value.minute, + }, + }) + ); + break; + } + case "date": { + this.element.dispatchEvent( + new CustomEvent("DateTimePickerValueChanged", { + detail: { + year: value.year, + // Month value from input box starts from 1 instead of 0 + month: value.month == undefined ? undefined : value.month + 1, + day: value.day, + }, + }) + ); + break; + } + } + } + + getCalendarInfo(locale) { + const calendarInfo = Services.intl.getCalendarInfo(locale); + + // Day of week from calendarInfo starts from 1 as Monday to 7 as Sunday, + // so they need to be mapped to JavaScript convention with 0 as Sunday + // and 6 as Saturday + function toDateWeekday(day) { + return day === 7 ? 0 : day; + } + + let firstDayOfWeek = toDateWeekday(calendarInfo.firstDayOfWeek), + weekend = calendarInfo.weekend; + + let weekends = weekend.map(toDateWeekday); + + return { + firstDayOfWeek, + weekends, + }; + } + + handleEvent(aEvent) { + switch (aEvent.type) { + case "load": { + this.initPicker(this.detail); + this.dateTimePopupFrame.contentWindow.addEventListener("message", this); + break; + } + case "message": { + this.handleMessage(aEvent); + break; + } + } + } + + handleMessage(aEvent) { + if ( + !this.dateTimePopupFrame.contentDocument.nodePrincipal.isSystemPrincipal + ) { + return; + } + + switch (aEvent.data.name) { + case "PickerPopupChanged": { + this.pickerState = aEvent.data.detail; + this.setInputBoxValue(); + break; + } + case "ClosePopup": { + this.closePicker(aEvent.data.detail); + break; + } + } + } + + postMessageToPicker(data) { + if ( + this.dateTimePopupFrame.contentDocument.nodePrincipal.isSystemPrincipal + ) { + this.dateTimePopupFrame.contentWindow.postMessage(data, "*"); + } + } +}; diff --git a/toolkit/modules/DeferredTask.sys.mjs b/toolkit/modules/DeferredTask.sys.mjs new file mode 100644 index 0000000000..d515c16e2b --- /dev/null +++ b/toolkit/modules/DeferredTask.sys.mjs @@ -0,0 +1,352 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */ +/* 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/. */ + +/** + * Sets up a function or an asynchronous task whose execution can be triggered + * after a defined delay. Multiple attempts to run the task before the delay + * has passed are coalesced. The task cannot be re-entered while running, but + * can be executed again after a previous run finished. + * + * A common use case occurs when a data structure should be saved into a file + * every time the data changes, using asynchronous calls, and multiple changes + * to the data may happen within a short time: + * + * let saveDeferredTask = new DeferredTask(async function() { + * await OS.File.writeAtomic(...); + * // Any uncaught exception will be reported. + * }, 2000); + * + * // The task is ready, but will not be executed until requested. + * + * The "arm" method can be used to start the internal timer that will result in + * the eventual execution of the task. Multiple attempts to arm the timer don't + * introduce further delays: + * + * saveDeferredTask.arm(); + * + * // The task will be executed in 2 seconds from now. + * + * await waitOneSecond(); + * saveDeferredTask.arm(); + * + * // The task will be executed in 1 second from now. + * + * The timer can be disarmed to reset the delay, or just to cancel execution: + * + * saveDeferredTask.disarm(); + * saveDeferredTask.arm(); + * + * // The task will be executed in 2 seconds from now. + * + * When the internal timer fires and the execution of the task starts, the task + * cannot be canceled anymore. It is however possible to arm the timer again + * during the execution of the task, in which case the task will need to finish + * before the timer is started again, thus guaranteeing a time of inactivity + * between executions that is at least equal to the provided delay. + * + * The "finalize" method can be used to ensure that the task terminates + * properly. The promise it returns is resolved only after the last execution + * of the task is finished. To guarantee that the task is executed for the + * last time, the method prevents any attempt to arm the timer again. + * + * If the timer is already armed when the "finalize" method is called, then the + * task is executed immediately. If the task was already running at this point, + * then one last execution from start to finish will happen again, immediately + * after the current execution terminates. If the timer is not armed, the + * "finalize" method only ensures that any running task terminates. + * + * For example, during shutdown, you may want to ensure that any pending write + * is processed, using the latest version of the data if the timer is armed: + * + * AsyncShutdown.profileBeforeChange.addBlocker( + * "Example service: shutting down", + * () => saveDeferredTask.finalize() + * ); + * + * Instead, if you are going to delete the saved data from disk anyways, you + * might as well prevent any pending write from starting, while still ensuring + * that any write that is currently in progress terminates, so that the file is + * not in use anymore: + * + * saveDeferredTask.disarm(); + * saveDeferredTask.finalize().then(() => OS.File.remove(...)) + * .then(null, Components.utils.reportError); + */ + +// Globals + +const Timer = Components.Constructor( + "@mozilla.org/timer;1", + "nsITimer", + "initWithCallback" +); + +// DeferredTask + +/** + * Sets up a task whose execution can be triggered after a delay. + * + * @param aTaskFn + * Function to execute. If the function returns a promise, the task is + * not considered complete until that promise resolves. This + * task is never re-entered while running. + * @param aDelayMs + * Time between executions, in milliseconds. Multiple attempts to run + * the task before the delay has passed are coalesced. This time of + * inactivity is guaranteed to pass between multiple executions of the + * task, except on finalization, when the task may restart immediately + * after the previous execution finished. + * @param aIdleTimeoutMs + * The maximum time to wait for an idle slot on the main thread after + * aDelayMs have elapsed. If omitted, waits indefinitely for an idle + * callback. + */ +export var DeferredTask = function (aTaskFn, aDelayMs, aIdleTimeoutMs) { + this._taskFn = aTaskFn; + this._delayMs = aDelayMs; + this._timeoutMs = aIdleTimeoutMs; + this._caller = new Error().stack.split("\n", 2)[1]; + let markerString = `delay: ${aDelayMs}ms`; + if (aIdleTimeoutMs) { + markerString += `, idle timeout: ${aIdleTimeoutMs}`; + } + ChromeUtils.addProfilerMarker( + "DeferredTask", + { captureStack: true }, + markerString + ); +}; + +DeferredTask.prototype = { + /** + * Function to execute. + */ + _taskFn: null, + + /** + * Time between executions, in milliseconds. + */ + _delayMs: null, + + /** + * Indicates whether the task is currently requested to start again later, + * regardless of whether it is currently running. + */ + get isArmed() { + return this._armed; + }, + _armed: false, + + /** + * Indicates whether the task is currently running. This is always true when + * read from code inside the task function, but can also be true when read + * from external code, in case the task is an asynchronous function. + */ + get isRunning() { + return !!this._runningPromise; + }, + + /** + * Promise resolved when the current execution of the task terminates, or null + * if the task is not currently running. + */ + _runningPromise: null, + + /** + * nsITimer used for triggering the task after a delay, or null in case the + * task is running or there is no task scheduled for execution. + */ + _timer: null, + + /** + * Actually starts the timer with the delay specified on construction. + */ + _startTimer() { + let callback, timer; + if (this._timeoutMs === 0) { + callback = () => this._timerCallback(); + } else { + callback = () => { + this._startIdleDispatch(() => { + // _timer could have changed by now: + // - to null if disarm() or finalize() has been called. + // - to a new nsITimer if disarm() was called, followed by arm(). + // In either case, don't invoke _timerCallback any more. + if (this._timer === timer) { + this._timerCallback(); + } + }, this._timeoutMs); + }; + } + timer = new Timer(callback, this._delayMs, Ci.nsITimer.TYPE_ONE_SHOT); + this._timer = timer; + }, + + /** + * Dispatches idle task. Can be overridden for testing by test_DeferredTask. + */ + _startIdleDispatch(callback, timeout) { + ChromeUtils.idleDispatch(callback, { timeout }); + }, + + /** + * Requests the execution of the task after the delay specified on + * construction. Multiple calls don't introduce further delays. If the task + * is running, the delay will start when the current execution finishes. + * + * The task will always be executed on a different tick of the event loop, + * even if the delay specified on construction is zero. Multiple "arm" calls + * within the same tick of the event loop are guaranteed to result in a single + * execution of the task. + * + * @note By design, this method doesn't provide a way for the caller to detect + * when the next execution terminates, or collect a result. In fact, + * doing that would often result in duplicate processing or logging. If + * a special operation or error logging is needed on completion, it can + * be better handled from within the task itself, for example using a + * try/catch/finally clause in the task. The "finalize" method can be + * used in the common case of waiting for completion on shutdown. + */ + arm() { + if (this._finalized) { + throw new Error("Unable to arm timer, the object has been finalized."); + } + + this._armed = true; + + // In case the timer callback is running, do not create the timer now, + // because this will be handled by the timer callback itself. Also, the + // timer is not restarted in case it is already running. + if (!this._runningPromise && !this._timer) { + this._startTimer(); + } + }, + + /** + * Cancels any request for a delayed the execution of the task, though the + * task itself cannot be canceled in case it is already running. + * + * This method stops any currently running timer, thus the delay will restart + * from its original value in case the "arm" method is called again. + */ + disarm() { + this._armed = false; + if (this._timer) { + // Calling the "cancel" method and discarding the timer reference makes + // sure that the timer callback will not be called later, even if the + // timer thread has already posted the timer event on the main thread. + this._timer.cancel(); + this._timer = null; + } + }, + + /** + * Ensures that any pending task is executed from start to finish, while + * preventing any attempt to arm the timer again. + * + * - If the task is running and the timer is armed, then one last execution + * from start to finish will happen again, immediately after the current + * execution terminates, then the returned promise will be resolved. + * - If the task is running and the timer is not armed, the returned promise + * will be resolved when the current execution terminates. + * - If the task is not running and the timer is armed, then the task is + * started immediately, and the returned promise resolves when the new + * execution terminates. + * - If the task is not running and the timer is not armed, the method returns + * a resolved promise. + * + * @return {Promise} + * @resolves After the last execution of the task is finished. + * @rejects Never. + */ + finalize() { + if (this._finalized) { + throw new Error("The object has been already finalized."); + } + this._finalized = true; + + // If the timer is armed, it means that the task is not running but it is + // scheduled for execution. Cancel the timer and run the task immediately, + // so we don't risk blocking async shutdown longer than necessary. + if (this._timer) { + this.disarm(); + this._timerCallback(); + } + + // Wait for the operation to be completed, or resolve immediately. + if (this._runningPromise) { + return this._runningPromise; + } + return Promise.resolve(); + }, + _finalized: false, + + /** + * Whether the DeferredTask has been finalized, and it cannot be armed anymore. + */ + get isFinalized() { + return this._finalized; + }, + + /** + * Timer callback used to run the delayed task. + */ + _timerCallback() { + let runningDeferred = Promise.withResolvers(); + + // All these state changes must occur at the same time directly inside the + // timer callback, to prevent race conditions and to ensure that all the + // methods behave consistently even if called from inside the task. This + // means that the assignment of "this._runningPromise" must complete before + // the task gets a chance to start. + this._timer = null; + this._armed = false; + this._runningPromise = runningDeferred.promise; + + runningDeferred.resolve( + (async () => { + // Execute the provided function asynchronously. + await this._runTask(); + + // Now that the task has finished, we check the state of the object to + // determine if we should restart the task again. + if (this._armed) { + if (!this._finalized) { + this._startTimer(); + } else { + // Execute the task again immediately, for the last time. The isArmed + // property should return false while the task is running, and should + // remain false after the last execution terminates. + this._armed = false; + await this._runTask(); + } + } + + // Indicate that the execution of the task has finished. This happens + // synchronously with the previous state changes in the function. + this._runningPromise = null; + })().catch(console.error) + ); + }, + + /** + * Executes the associated task and catches exceptions. + */ + async _runTask() { + let startTime = Cu.now(); + try { + await this._taskFn(); + } catch (ex) { + console.error(ex); + } finally { + ChromeUtils.addProfilerMarker( + "DeferredTask", + { startTime }, + this._caller + ); + } + }, +}; diff --git a/toolkit/modules/Deprecated.sys.mjs b/toolkit/modules/Deprecated.sys.mjs new file mode 100644 index 0000000000..c8f5aeba01 --- /dev/null +++ b/toolkit/modules/Deprecated.sys.mjs @@ -0,0 +1,81 @@ +/* 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/. */ + +const PREF_DEPRECATION_WARNINGS = "devtools.errorconsole.deprecation_warnings"; + +// A flag that indicates whether deprecation warnings should be logged. +var logWarnings = Services.prefs.getBoolPref(PREF_DEPRECATION_WARNINGS); + +Services.prefs.addObserver( + PREF_DEPRECATION_WARNINGS, + function (aSubject, aTopic, aData) { + logWarnings = Services.prefs.getBoolPref(PREF_DEPRECATION_WARNINGS); + } +); + +/** + * Build a callstack log message. + * + * @param nsIStackFrame aStack + * A callstack to be converted into a string log message. + */ +function stringifyCallstack(aStack) { + // If aStack is invalid, use Components.stack (ignoring the last frame). + if (!aStack || !(aStack instanceof Ci.nsIStackFrame)) { + aStack = Components.stack.caller; + } + + let frame = aStack.caller; + let msg = ""; + // Get every frame in the callstack. + while (frame) { + if (frame.filename || frame.lineNumber || frame.name) { + msg += frame.filename + " " + frame.lineNumber + " " + frame.name + "\n"; + } + frame = frame.caller; + } + return msg; +} + +export var Deprecated = { + /** + * Log a deprecation warning. + * + * @param string aText + * Deprecation warning text. + * @param string aUrl + * A URL pointing to documentation describing deprecation + * and the way to address it. + * @param nsIStackFrame aStack + * An optional callstack. If it is not provided a + * snapshot of the current JavaScript callstack will be + * logged. + */ + warning(aText, aUrl, aStack) { + if (!logWarnings) { + return; + } + + // If URL is not provided, report an error. + if (!aUrl) { + console.error( + "Error in Deprecated.warning: warnings must " + + "provide a URL documenting this deprecation." + ); + return; + } + + let textMessage = + "DEPRECATION WARNING: " + + aText + + "\nYou may find more details about this deprecation at: " + + aUrl + + "\n" + + // Append a callstack part to the deprecation message. + stringifyCallstack(aStack); + + // Report deprecation warning. + console.error(textMessage); + }, +}; diff --git a/toolkit/modules/E10SUtils.sys.mjs b/toolkit/modules/E10SUtils.sys.mjs new file mode 100644 index 0000000000..34da17fc50 --- /dev/null +++ b/toolkit/modules/E10SUtils.sys.mjs @@ -0,0 +1,849 @@ +/* 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 { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const lazy = {}; + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "useSeparateFileUriProcess", + "browser.tabs.remote.separateFileUriProcess", + false +); +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "useSeparatePrivilegedAboutContentProcess", + "browser.tabs.remote.separatePrivilegedContentProcess", + false +); +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "separatePrivilegedMozillaWebContentProcess", + "browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", + false +); +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "separatedMozillaDomains", + "browser.tabs.remote.separatedMozillaDomains", + "", + false, + val => val.split(",") +); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "useCrossOriginOpenerPolicy", + "browser.tabs.remote.useCrossOriginOpenerPolicy", + false +); +XPCOMUtils.defineLazyServiceGetter( + lazy, + "serializationHelper", + "@mozilla.org/network/serialization-helper;1", + "nsISerializationHelper" +); +XPCOMUtils.defineLazyServiceGetter( + lazy, + "extProtService", + "@mozilla.org/uriloader/external-protocol-service;1", + "nsIExternalProtocolService" +); + +function getOriginalReaderModeURI(aURI) { + try { + let searchParams = new URLSearchParams(aURI.query); + if (searchParams.has("url")) { + return Services.io.newURI(searchParams.get("url")); + } + } catch (e) {} + return null; +} + +const NOT_REMOTE = null; + +// These must match the similar ones in RemoteTypes.h, ProcInfo.h, ChromeUtils.webidl and ChromeUtils.cpp +const WEB_REMOTE_TYPE = "web"; +const FISSION_WEB_REMOTE_TYPE = "webIsolated"; +const WEB_REMOTE_COOP_COEP_TYPE_PREFIX = "webCOOP+COEP="; +const FILE_REMOTE_TYPE = "file"; +const EXTENSION_REMOTE_TYPE = "extension"; +const PRIVILEGEDABOUT_REMOTE_TYPE = "privilegedabout"; +const PRIVILEGEDMOZILLA_REMOTE_TYPE = "privilegedmozilla"; +const SERVICEWORKER_REMOTE_TYPE = "webServiceWorker"; + +// This must start with the WEB_REMOTE_TYPE above. +const DEFAULT_REMOTE_TYPE = WEB_REMOTE_TYPE; + +// This list is duplicated between Navigator.cpp and here because navigator +// is not accessible in this context. Please update both if the list changes. +const kSafeSchemes = [ + "bitcoin", + "ftp", + "ftps", + "geo", + "im", + "irc", + "ircs", + "magnet", + "mailto", + "matrix", + "mms", + "news", + "nntp", + "openpgp4fpr", + "sftp", + "sip", + "sms", + "smsto", + "ssh", + "tel", + "urn", + "webcal", + "wtai", + "xmpp", +]; + +const STANDARD_SAFE_PROTOCOLS = kSafeSchemes; + +// Note that even if the scheme fits the criteria for a web-handled scheme +// (ie it is compatible with the checks registerProtocolHandler uses), it may +// not be web-handled - it could still be handled via the OS by another app. +function hasPotentiallyWebHandledScheme({ scheme }) { + // Note that `scheme` comes from a URI object so is already lowercase. + if (kSafeSchemes.includes(scheme)) { + return true; + } + if (!scheme.startsWith("web+") || scheme.length < 5) { + return false; + } + // Check the rest of the scheme only consists of ascii a-z chars + return /^[a-z]+$/.test(scheme.substr("web+".length)); +} + +function validatedWebRemoteType( + aPreferredRemoteType, + aTargetUri, + aCurrentUri, + aResultPrincipal, + aRemoteSubframes, + aOriginAttributes = {} +) { + // To load into the Privileged Mozilla Content Process you must be https, + // and be an exact match or a subdomain of an allowlisted domain. + if ( + lazy.separatePrivilegedMozillaWebContentProcess && + aTargetUri.asciiHost && + aTargetUri.scheme == "https" && + lazy.separatedMozillaDomains.some(function (val) { + return ( + aTargetUri.asciiHost == val || aTargetUri.asciiHost.endsWith("." + val) + ); + }) + ) { + return PRIVILEGEDMOZILLA_REMOTE_TYPE; + } + + // If we're in the parent and we were passed a web-handled scheme, + // transform it now to avoid trying to load it in the wrong process. + if (aRemoteSubframes && hasPotentiallyWebHandledScheme(aTargetUri)) { + if ( + Services.appinfo.processType != Services.appinfo.PROCESS_TYPE_DEFAULT && + Services.appinfo.remoteType.startsWith(FISSION_WEB_REMOTE_TYPE + "=") + ) { + // If we're in a child process, assume we're OK to load this non-web + // URL for now. We'll either load it externally or re-evaluate once + // we know the "real" URL to which we'll redirect. + return Services.appinfo.remoteType; + } + + // This doesn't work (throws) in the child - see + // https://bugzilla.mozilla.org/show_bug.cgi?id=1589082 + // Even if it did, it'd cause sync IPC + // ( https://bugzilla.mozilla.org/show_bug.cgi?id=1589085 ), and this code + // can get called several times per page load so that seems like something + // we'd want to avoid. + let handlerInfo = lazy.extProtService.getProtocolHandlerInfo( + aTargetUri.scheme + ); + try { + if (!handlerInfo.alwaysAskBeforeHandling) { + let app = handlerInfo.preferredApplicationHandler; + app.QueryInterface(Ci.nsIWebHandlerApp); + // If we get here, the default handler is a web app. + // Target to the origin of that web app: + let uriStr = app.uriTemplate.replace(/%s/, aTargetUri.spec); + aTargetUri = Services.io.newURI(uriStr); + } + } catch (ex) { + // It's not strange for this to throw, we just ignore it and fall through. + } + } + + // If the domain is allow listed to allow it to use file:// URIs, then we have + // to run it in a file content process, in case it uses file:// sub-resources. + const sm = Services.scriptSecurityManager; + if (sm.inFileURIAllowlist(aTargetUri)) { + return FILE_REMOTE_TYPE; + } + + // If we're within a fission window, extract site information from the URI in + // question, and use it to generate an isolated origin. + if (aRemoteSubframes) { + let originAttributes = {}; + // Only use specific properties of OriginAttributes in our remoteType + let { userContextId, privateBrowsingId, geckoViewSessionContextId } = + aOriginAttributes; + originAttributes = { + userContextId, + privateBrowsingId, + geckoViewSessionContextId, + }; + + // Get a principal to use for isolation. + let targetPrincipal; + if (aResultPrincipal) { + targetPrincipal = sm.principalWithOA(aResultPrincipal, originAttributes); + } else { + targetPrincipal = sm.createContentPrincipal(aTargetUri, originAttributes); + } + + // If this is a special webCOOP+COEP= remote type that matches the + // principal's siteOrigin, we don't want to override it with webIsolated= + // as it's already isolated. + if ( + aPreferredRemoteType && + aPreferredRemoteType.startsWith( + `${WEB_REMOTE_COOP_COEP_TYPE_PREFIX}${targetPrincipal.siteOrigin}` + ) + ) { + return aPreferredRemoteType; + } + + return `${FISSION_WEB_REMOTE_TYPE}=${targetPrincipal.siteOrigin}`; + // else fall through and probably return WEB_REMOTE_TYPE + } + + if (!aPreferredRemoteType) { + return WEB_REMOTE_TYPE; + } + + if (aPreferredRemoteType.startsWith(WEB_REMOTE_TYPE)) { + return aPreferredRemoteType; + } + + return WEB_REMOTE_TYPE; +} + +export var E10SUtils = { + DEFAULT_REMOTE_TYPE, + NOT_REMOTE, + WEB_REMOTE_TYPE, + WEB_REMOTE_COOP_COEP_TYPE_PREFIX, + FILE_REMOTE_TYPE, + EXTENSION_REMOTE_TYPE, + PRIVILEGEDABOUT_REMOTE_TYPE, + PRIVILEGEDMOZILLA_REMOTE_TYPE, + FISSION_WEB_REMOTE_TYPE, + SERVICEWORKER_REMOTE_TYPE, + STANDARD_SAFE_PROTOCOLS, + + /** + * @param aURI The URI of the about page + * @return The instance of the nsIAboutModule related to this uri + */ + getAboutModule(aURL) { + // Needs to match NS_GetAboutModuleName + let moduleName = aURL.pathQueryRef.replace(/[#?].*/, "").toLowerCase(); + let contract = "@mozilla.org/network/protocol/about;1?what=" + moduleName; + try { + return Cc[contract].getService(Ci.nsIAboutModule); + } catch (e) { + // Either the about module isn't defined or it is broken. In either case + // ignore it. + return null; + } + }, + + useCrossOriginOpenerPolicy() { + return lazy.useCrossOriginOpenerPolicy; + }, + + _log: null, + _uriStr: function uriStr(aUri) { + return aUri ? aUri.spec : "undefined"; + }, + + log: function log() { + if (!this._log) { + this._log = console.createInstance({ + prefix: "ProcessSwitch", + maxLogLevel: "Error", // Change to "Debug" the process switching code + }); + + this._log.debug("Setup logger"); + } + + return this._log; + }, + + /** + * Serialize csp data. + * + * @param {nsIContentSecurity} csp. The csp to serialize. + * @return {String} The base64 encoded csp data. + */ + serializeCSP(csp) { + let serializedCSP = null; + + try { + if (csp) { + serializedCSP = lazy.serializationHelper.serializeToString(csp); + } + } catch (e) { + this.log().error(`Failed to serialize csp '${csp}' ${e}`); + } + return serializedCSP; + }, + + /** + * Deserialize a base64 encoded csp (serialized with + * Utils::serializeCSP). + * + * @param {String} csp_b64 A base64 encoded serialized csp. + * @return {nsIContentSecurityPolicy} A deserialized csp. + */ + deserializeCSP(csp_b64) { + if (!csp_b64) { + return null; + } + + try { + let csp = lazy.serializationHelper.deserializeObject(csp_b64); + csp.QueryInterface(Ci.nsIContentSecurityPolicy); + return csp; + } catch (e) { + this.log().error(`Failed to deserialize csp_b64 '${csp_b64}' ${e}`); + } + return null; + }, + + canLoadURIInRemoteType( + aURL, + aRemoteSubframes, + aRemoteType = DEFAULT_REMOTE_TYPE, + aOriginAttributes = {} + ) { + // aRemoteType cannot be undefined, as that would cause it to default to + // `DEFAULT_REMOTE_TYPE`. This means any falsy remote types are + // intentionally `NOT_REMOTE`. + + return ( + aRemoteType == + this.getRemoteTypeForURI( + aURL, + true, + aRemoteSubframes, + aRemoteType, + null, + aOriginAttributes + ) + ); + }, + + getRemoteTypeForURI( + aURL, + aMultiProcess, + aRemoteSubframes, + aPreferredRemoteType = DEFAULT_REMOTE_TYPE, + aCurrentUri, + aOriginAttributes = {} + ) { + if (!aMultiProcess) { + return NOT_REMOTE; + } + + // loadURI in browser.js treats null as about:blank + if (!aURL) { + aURL = "about:blank"; + } + + let uri; + try { + uri = Services.uriFixup.getFixupURIInfo(aURL).preferredURI; + } catch (e) { + // If we have an invalid URI, it's still possible that it might get + // fixed-up into a valid URI later on. However, we don't want to return + // aPreferredRemoteType here, in case the URI gets fixed-up into + // something that wouldn't normally run in that process. + return DEFAULT_REMOTE_TYPE; + } + + return this.getRemoteTypeForURIObject(uri, { + multiProcess: aMultiProcess, + remoteSubFrames: aRemoteSubframes, + preferredRemoteType: aPreferredRemoteType, + currentURI: aCurrentUri, + originAttributes: aOriginAttributes, + }); + }, + + getRemoteTypeForURIObject(aURI, options) { + let { + multiProcess = Services.appinfo.browserTabsRemoteAutostart, + remoteSubFrames = Services.appinfo.fissionAutostart, + preferredRemoteType = DEFAULT_REMOTE_TYPE, + currentURI = null, + resultPrincipal = null, + originAttributes = {}, + } = options; + if (!multiProcess) { + return NOT_REMOTE; + } + + switch (aURI.scheme) { + case "javascript": + // javascript URIs can load in any, they apply to the current document. + return preferredRemoteType; + + case "data": + case "blob": + // We need data: and blob: URIs to load in any remote process, because + // they need to be able to load in whatever is the current process + // unless it is non-remote. In that case we don't want to load them in + // the parent process, so we load them in the default remote process, + // which is sandboxed and limits any risk. + return preferredRemoteType == NOT_REMOTE + ? DEFAULT_REMOTE_TYPE + : preferredRemoteType; + + case "file": + return lazy.useSeparateFileUriProcess + ? FILE_REMOTE_TYPE + : DEFAULT_REMOTE_TYPE; + + case "about": + let module = this.getAboutModule(aURI); + // If the module doesn't exist then an error page will be loading, that + // should be ok to load in any process + if (!module) { + return preferredRemoteType; + } + + let flags = module.getURIFlags(aURI); + if (flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_EXTENSION_PROCESS) { + return WebExtensionPolicy.useRemoteWebExtensions + ? EXTENSION_REMOTE_TYPE + : NOT_REMOTE; + } + + if (flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD) { + if ( + flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS && + (lazy.useSeparatePrivilegedAboutContentProcess || + aURI.filePath == "logins" || + // Force about:welcome and about:home into the privileged content process to + // workaround code coverage test failures which result from the + // workaround in bug 161269. Once that bug is fixed for real, + // the about:welcome and about:home case below can be removed. + aURI.filePath == "welcome" || + aURI.filePath == "home") + ) { + return PRIVILEGEDABOUT_REMOTE_TYPE; + } + + // When loading about:reader, try to display the document in the same + // web remote type as the document it's loading. + if (aURI.filePath == "reader") { + let readerModeURI = getOriginalReaderModeURI(aURI); + if (readerModeURI) { + let innerRemoteType = this.getRemoteTypeForURIObject( + readerModeURI, + options + ); + if ( + innerRemoteType && + innerRemoteType.startsWith(WEB_REMOTE_TYPE) + ) { + return innerRemoteType; + } + } + } + + return DEFAULT_REMOTE_TYPE; + } + + // If the about page can load in parent or child, it should be safe to + // load in any remote type. + if (flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD) { + return preferredRemoteType; + } + + return NOT_REMOTE; + + case "chrome": + let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService( + Ci.nsIXULChromeRegistry + ); + if (chromeReg.mustLoadURLRemotely(aURI)) { + return DEFAULT_REMOTE_TYPE; + } + + if ( + chromeReg.canLoadURLRemotely(aURI) && + preferredRemoteType != NOT_REMOTE + ) { + return DEFAULT_REMOTE_TYPE; + } + + return NOT_REMOTE; + + case "moz-extension": + // Extension iframes should load in the same process + // as their outer frame, but that's handled elsewhere. + return WebExtensionPolicy.useRemoteWebExtensions + ? EXTENSION_REMOTE_TYPE + : NOT_REMOTE; + + case "imap": + case "mailbox": + case "news": + case "nntp": + case "snews": + // Protocols used by Thunderbird to display email messages. + return NOT_REMOTE; + + default: + // WebExtensions may set up protocol handlers for protocol names + // beginning with ext+. These may redirect to http(s) pages or to + // moz-extension pages. We can't actually tell here where one of + // these pages will end up loading but Talos tests use protocol + // handlers that redirect to extension pages that rely on this + // behavior so a pageloader frame script is injected correctly. + // Protocols that redirect to http(s) will just flip back to a + // regular content process after the redirect. + if (aURI.scheme.startsWith("ext+")) { + return WebExtensionPolicy.useRemoteWebExtensions + ? EXTENSION_REMOTE_TYPE + : NOT_REMOTE; + } + + // For any other nested URIs, we use the innerURI to determine the + // remote type. In theory we should use the innermost URI, but some URIs + // have fake inner URIs (e.g. about URIs with inner moz-safe-about) and + // if such URIs are wrapped in other nested schemes like view-source:, + // we don't want to "skip" past "about:" by going straight to the + // innermost URI. Any URIs like this will need to be handled in the + // cases above, so we don't still end up using the fake inner URI here. + if (aURI instanceof Ci.nsINestedURI) { + let innerURI = aURI.QueryInterface(Ci.nsINestedURI).innerURI; + return this.getRemoteTypeForURIObject(innerURI, options); + } + + var log = this.log(); + log.debug("validatedWebRemoteType()"); + log.debug(` aPreferredRemoteType: ${preferredRemoteType}`); + log.debug(` aTargetUri: ${this._uriStr(aURI)}`); + log.debug(` aCurrentUri: ${this._uriStr(currentURI)}`); + var remoteType = validatedWebRemoteType( + preferredRemoteType, + aURI, + currentURI, + resultPrincipal, + remoteSubFrames, + originAttributes + ); + log.debug(` validatedWebRemoteType() returning: ${remoteType}`); + return remoteType; + } + }, + + makeInputStream(data) { + if (typeof data == "string") { + let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( + Ci.nsISupportsCString + ); + stream.data = data; + return stream; // XPConnect will QI this to nsIInputStream for us. + } + + let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( + Ci.nsISupportsCString + ); + stream.data = data.content; + + if (data.headers) { + let mimeStream = Cc[ + "@mozilla.org/network/mime-input-stream;1" + ].createInstance(Ci.nsIMIMEInputStream); + + mimeStream.setData(stream); + for (let [name, value] of data.headers) { + mimeStream.addHeader(name, value); + } + return mimeStream; + } + + return stream; // XPConnect will QI this to nsIInputStream for us. + }, + + /** + * Serialize principal data. + * + * @param {nsIPrincipal} principal The principal to serialize. + * @return {String} The serialized principal data. + */ + serializePrincipal(principal) { + let serializedPrincipal = null; + + try { + if (principal) { + serializedPrincipal = + Services.scriptSecurityManager.principalToJSON(principal); + } + } catch (e) { + this.log().error(`Failed to serialize principal '${principal}' ${e}`); + } + + return serializedPrincipal; + }, + + /** + * Deserialize a principal (serialized with serializePrincipal). + * + * @param {String} serializedPincipal A serialized principal. + * @return {nsIPrincipal} A deserialized principal. + */ + deserializePrincipal(serializedPincipal, fallbackPrincipalCallback = null) { + if (!serializedPincipal) { + if (!fallbackPrincipalCallback) { + this.log().warn( + "No principal passed to deserializePrincipal and no fallbackPrincipalCallback" + ); + return null; + } + + return fallbackPrincipalCallback(); + } + + try { + let principal; + // The current JSON representation of principal is not stored as base64. We start by checking + // if the serialized data starts with '{' to determine if we're using the new JSON representation. + // If it doesn't we try the two legacy formats, old JSON and nsISerializable. + if (serializedPincipal.startsWith("{")) { + principal = + Services.scriptSecurityManager.JSONToPrincipal(serializedPincipal); + } else { + // Both the legacy and legacy JSON representation of principals are stored as base64 + // The legacy JSON kind are the only ones that will start with "{" when decoded. + // We check here for the legacy JSON serialized, if it doesn't start with that continue using nsISerializable. + // JSONToPrincipal accepts a *non* base64 encoded string and returns a principal or a null. + let tmpa = atob(serializedPincipal); + if (tmpa.startsWith("{")) { + principal = Services.scriptSecurityManager.JSONToPrincipal(tmpa); + } else { + principal = + lazy.serializationHelper.deserializeObject(serializedPincipal); + } + } + principal.QueryInterface(Ci.nsIPrincipal); + return principal; + } catch (e) { + this.log().error( + `Failed to deserialize serializedPincipal '${serializedPincipal}' ${e}` + ); + } + if (!fallbackPrincipalCallback) { + this.log().warn( + "No principal passed to deserializePrincipal and no fallbackPrincipalCallback" + ); + return null; + } + return fallbackPrincipalCallback(); + }, + + /** + * Serialize cookieJarSettings. + * + * @param {nsICookieJarSettings} cookieJarSettings The cookieJarSettings to + * serialize. + * @return {String} The base64 encoded cookieJarSettings data. + */ + serializeCookieJarSettings(cookieJarSettings) { + let serialized = null; + if (cookieJarSettings) { + try { + serialized = + lazy.serializationHelper.serializeToString(cookieJarSettings); + } catch (e) { + this.log().error( + `Failed to serialize cookieJarSettings '${cookieJarSettings}' ${e}` + ); + } + } + return serialized; + }, + + /** + * Deserialize a base64 encoded cookieJarSettings + * + * @param {String} cookieJarSettings_b64 A base64 encoded serialized cookieJarSettings. + * @return {nsICookieJarSettings} A deserialized cookieJarSettings. + */ + deserializeCookieJarSettings(cookieJarSettings_b64) { + let deserialized = null; + if (cookieJarSettings_b64) { + try { + deserialized = lazy.serializationHelper.deserializeObject( + cookieJarSettings_b64 + ); + deserialized.QueryInterface(Ci.nsICookieJarSettings); + } catch (e) { + this.log().error( + `Failed to deserialize cookieJarSettings_b64 '${cookieJarSettings_b64}' ${e}` + ); + } + } + return deserialized; + }, + + wrapHandlingUserInput(aWindow, aIsHandling, aCallback) { + var handlingUserInput; + try { + handlingUserInput = aWindow.windowUtils.setHandlingUserInput(aIsHandling); + aCallback(); + } finally { + handlingUserInput.destruct(); + } + }, + + /** + * Serialize referrerInfo. + * + * @param {nsIReferrerInfo} The referrerInfo to serialize. + * @return {String} The base64 encoded referrerInfo. + */ + serializeReferrerInfo(referrerInfo) { + let serialized = null; + if (referrerInfo) { + try { + serialized = lazy.serializationHelper.serializeToString(referrerInfo); + } catch (e) { + this.log().error( + `Failed to serialize referrerInfo '${referrerInfo}' ${e}` + ); + } + } + return serialized; + }, + /** + * Deserialize a base64 encoded referrerInfo + * + * @param {String} referrerInfo_b64 A base64 encoded serialized referrerInfo. + * @return {nsIReferrerInfo} A deserialized referrerInfo. + */ + deserializeReferrerInfo(referrerInfo_b64) { + let deserialized = null; + if (referrerInfo_b64) { + try { + deserialized = + lazy.serializationHelper.deserializeObject(referrerInfo_b64); + deserialized.QueryInterface(Ci.nsIReferrerInfo); + } catch (e) { + this.log().error( + `Failed to deserialize referrerInfo_b64 '${referrerInfo_b64}' ${e}` + ); + } + } + return deserialized; + }, + + /** + * Returns the pids for a remote browser and its remote subframes. + */ + getBrowserPids(aBrowser, aRemoteSubframes) { + if (!aBrowser.isRemoteBrowser || !aBrowser.frameLoader) { + return []; + } + let tabPid = aBrowser.frameLoader.remoteTab.osPid; + let pids = new Set(); + if (aRemoteSubframes) { + let stack = [aBrowser.browsingContext]; + while (stack.length) { + let bc = stack.pop(); + stack.push(...bc.children); + if (bc.currentWindowGlobal) { + let pid = bc.currentWindowGlobal.osPid; + if (pid != tabPid) { + pids.add(pid); + } + } + } + } + return [tabPid, ...pids]; + }, + + /** + * The suffix after a `=` in a remoteType is dynamic, and used to control the + * process pool to use. The C++ version of this method is mozilla::dom::RemoteTypePrefix(). + */ + remoteTypePrefix(aRemoteType) { + return aRemoteType.split("=")[0]; + }, + + /** + * There are various types of remote types that are for web content processes, but + * they all start with "web". The C++ version of this method is + * mozilla::dom::IsWebRemoteType(). + */ + isWebRemoteType(aRemoteType) { + return aRemoteType.startsWith(WEB_REMOTE_TYPE); + }, + + /** + * Assemble or predict originAttributes from available arguments. + */ + predictOriginAttributes({ + window, + browser, + userContextId, + geckoViewSessionContextId, + privateBrowsingId, + }) { + if (browser) { + if (browser.browsingContext) { + return browser.browsingContext.originAttributes; + } + if (!window) { + window = browser.contentDocument?.defaultView; + } + if (!userContextId) { + userContextId = browser.getAttribute("usercontextid") || 0; + } + if (!geckoViewSessionContextId) { + geckoViewSessionContextId = + browser.getAttribute("geckoViewSessionContextId") || ""; + } + } + + if (window && !privateBrowsingId) { + privateBrowsingId = window.browsingContext.usePrivateBrowsing ? 1 : 0; + } + return { privateBrowsingId, userContextId, geckoViewSessionContextId }; + }, +}; + +ChromeUtils.defineLazyGetter( + E10SUtils, + "SERIALIZED_SYSTEMPRINCIPAL", + function () { + return btoa( + E10SUtils.serializePrincipal( + Services.scriptSecurityManager.getSystemPrincipal() + ) + ); + } +); diff --git a/toolkit/modules/EventEmitter.sys.mjs b/toolkit/modules/EventEmitter.sys.mjs new file mode 100644 index 0000000000..90900e22e5 --- /dev/null +++ b/toolkit/modules/EventEmitter.sys.mjs @@ -0,0 +1,203 @@ +/* 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 EventEmitter() {} + +let loggingEnabled = Services.prefs.getBoolPref("toolkit.dump.emit"); +Services.prefs.addObserver("toolkit.dump.emit", { + observe: () => { + loggingEnabled = Services.prefs.getBoolPref("toolkit.dump.emit"); + }, +}); + +/** + * Decorate an object with event emitter functionality. + * + * @param Object objectToDecorate + * Bind all public methods of EventEmitter to + * the objectToDecorate object. + */ +EventEmitter.decorate = function (objectToDecorate) { + let emitter = new EventEmitter(); + objectToDecorate.on = emitter.on.bind(emitter); + objectToDecorate.off = emitter.off.bind(emitter); + objectToDecorate.once = emitter.once.bind(emitter); + objectToDecorate.emit = emitter.emit.bind(emitter); +}; + +function describeNthCaller(n) { + let caller = Components.stack; + // Do one extra iteration to skip this function. + while (n >= 0) { + --n; + caller = caller.caller; + } + + let func = caller.name; + let file = caller.filename; + if (file.includes(" -> ")) { + file = caller.filename.split(/ -> /)[1]; + } + let path = file + ":" + caller.lineNumber; + + return func + "() -> " + path; +} + +EventEmitter.prototype = { + /** + * Connect a listener. + * + * @param string event + * The event name to which we're connecting. + * @param function listener + * Called when the event is fired. + */ + on(event, listener) { + if (!this._eventEmitterListeners) { + this._eventEmitterListeners = new Map(); + } + if (!this._eventEmitterListeners.has(event)) { + this._eventEmitterListeners.set(event, []); + } + this._eventEmitterListeners.get(event).push(listener); + }, + + /** + * Listen for the next time an event is fired. + * + * @param string event + * The event name to which we're connecting. + * @param function listener + * (Optional) Called when the event is fired. Will be called at most + * one time. + * @return promise + * A promise which is resolved when the event next happens. The + * resolution value of the promise is the first event argument. If + * you need access to second or subsequent event arguments (it's rare + * that this is needed) then use listener + */ + once(event, listener) { + return new Promise(resolve => { + let handler = (_, first, ...rest) => { + this.off(event, handler); + if (listener) { + listener(event, first, ...rest); + } + resolve(first); + }; + + handler._originalListener = listener; + this.on(event, handler); + }); + }, + + /** + * Remove a previously-registered event listener. Works for events + * registered with either on or once. + * + * @param string event + * The event name whose listener we're disconnecting. + * @param function listener + * The listener to remove. + */ + off(event, listener) { + if (!this._eventEmitterListeners) { + return; + } + let listeners = this._eventEmitterListeners.get(event); + if (listeners) { + this._eventEmitterListeners.set( + event, + listeners.filter(l => { + return l !== listener && l._originalListener !== listener; + }) + ); + } + }, + + /** + * Emit an event. All arguments to this method will + * be sent to listener functions. + */ + emit(event) { + this.logEvent(event, arguments); + + if ( + !this._eventEmitterListeners || + !this._eventEmitterListeners.has(event) + ) { + return; + } + + let originalListeners = this._eventEmitterListeners.get(event); + for (let listener of this._eventEmitterListeners.get(event)) { + // If the object was destroyed during event emission, stop + // emitting. + if (!this._eventEmitterListeners) { + break; + } + + // If listeners were removed during emission, make sure the + // event handler we're going to fire wasn't removed. + if ( + originalListeners === this._eventEmitterListeners.get(event) || + this._eventEmitterListeners.get(event).some(l => l === listener) + ) { + try { + listener.apply(null, arguments); + } catch (ex) { + console.error(ex); + } + } + } + }, + + logEvent(event, args) { + if (!loggingEnabled) { + return; + } + + let description = describeNthCaller(2); + + let argOut = "("; + if (args.length === 1) { + argOut += event; + } + + let out = "EMITTING: "; + + // We need this try / catch to prevent any dead object errors. + try { + for (let i = 1; i < args.length; i++) { + if (i === 1) { + argOut = "(" + event + ", "; + } else { + argOut += ", "; + } + + let arg = args[i]; + argOut += arg; + + if (arg && arg.nodeName) { + argOut += " (" + arg.nodeName; + if (arg.id) { + argOut += "#" + arg.id; + } + if (arg.className) { + argOut += "." + arg.className; + } + argOut += ")"; + } + } + } catch (e) { + // Object is dead so the toolbox is most likely shutting down, + // do nothing. + } + + argOut += ")"; + out += "emit" + argOut + " from " + description + "\n"; + + dump(out); + }, +}; diff --git a/toolkit/modules/FileUtils.sys.mjs b/toolkit/modules/FileUtils.sys.mjs new file mode 100644 index 0000000000..bea38e3161 --- /dev/null +++ b/toolkit/modules/FileUtils.sys.mjs @@ -0,0 +1,148 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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 var FileUtils = { + MODE_RDONLY: 0x01, + MODE_WRONLY: 0x02, + MODE_RDWR: 0x04, + MODE_CREATE: 0x08, + MODE_APPEND: 0x10, + MODE_TRUNCATE: 0x20, + + PERMS_FILE: 0o644, + PERMS_DIRECTORY: 0o755, + + /** + * Gets a directory at the specified hierarchy under a nsIDirectoryService + * key. + * @param key + * The Directory Service Key to start from + * @param pathArray + * An array of path components to locate beneath the directory + * specified by |key| + * @return nsIFile object for the location specified. + */ + getDir: function FileUtils_getDir(key, pathArray) { + var dir = Services.dirsvc.get(key, Ci.nsIFile); + for (var i = 0; i < pathArray.length; ++i) { + dir.append(pathArray[i]); + } + return dir; + }, + + /** + * Opens a file output stream for writing. + * @param file + * The file to write to. + * @param modeFlags + * (optional) File open flags. Can be undefined. + * @returns nsIFileOutputStream to write to. + * @note The stream is initialized with the DEFER_OPEN behavior flag. + * See nsIFileOutputStream. + */ + openFileOutputStream: function FileUtils_openFileOutputStream( + file, + modeFlags + ) { + var fos = Cc["@mozilla.org/network/file-output-stream;1"].createInstance( + Ci.nsIFileOutputStream + ); + return this._initFileOutputStream(fos, file, modeFlags); + }, + + /** + * Opens an atomic file output stream for writing. + * @param file + * The file to write to. + * @param modeFlags + * (optional) File open flags. Can be undefined. + * @returns nsIFileOutputStream to write to. + * @note The stream is initialized with the DEFER_OPEN behavior flag. + * See nsIFileOutputStream. + * OpeanAtomicFileOutputStream is generally better than openSafeFileOutputStream + * baecause flushing is not needed in most of the issues. + */ + openAtomicFileOutputStream: function FileUtils_openAtomicFileOutputStream( + file, + modeFlags + ) { + var fos = Cc[ + "@mozilla.org/network/atomic-file-output-stream;1" + ].createInstance(Ci.nsIFileOutputStream); + return this._initFileOutputStream(fos, file, modeFlags); + }, + + /** + * Opens a safe file output stream for writing. + * @param file + * The file to write to. + * @param modeFlags + * (optional) File open flags. Can be undefined. + * @returns nsIFileOutputStream to write to. + * @note The stream is initialized with the DEFER_OPEN behavior flag. + * See nsIFileOutputStream. + */ + openSafeFileOutputStream: function FileUtils_openSafeFileOutputStream( + file, + modeFlags + ) { + var fos = Cc[ + "@mozilla.org/network/safe-file-output-stream;1" + ].createInstance(Ci.nsIFileOutputStream); + return this._initFileOutputStream(fos, file, modeFlags); + }, + + _initFileOutputStream: function FileUtils__initFileOutputStream( + fos, + file, + modeFlags + ) { + if (modeFlags === undefined) { + modeFlags = this.MODE_WRONLY | this.MODE_CREATE | this.MODE_TRUNCATE; + } + fos.init(file, modeFlags, this.PERMS_FILE, fos.DEFER_OPEN); + return fos; + }, + + /** + * Closes an atomic file output stream. + * @param stream + * The stream to close. + */ + closeAtomicFileOutputStream: function FileUtils_closeAtomicFileOutputStream( + stream + ) { + if (stream instanceof Ci.nsISafeOutputStream) { + try { + stream.finish(); + return; + } catch (e) {} + } + stream.close(); + }, + + /** + * Closes a safe file output stream. + * @param stream + * The stream to close. + */ + closeSafeFileOutputStream: function FileUtils_closeSafeFileOutputStream( + stream + ) { + if (stream instanceof Ci.nsISafeOutputStream) { + try { + stream.finish(); + return; + } catch (e) {} + } + stream.close(); + }, + + File: Components.Constructor( + "@mozilla.org/file/local;1", + Ci.nsIFile, + "initWithPath" + ), +}; diff --git a/toolkit/modules/FindBarContent.sys.mjs b/toolkit/modules/FindBarContent.sys.mjs new file mode 100644 index 0000000000..8b34d93f9d --- /dev/null +++ b/toolkit/modules/FindBarContent.sys.mjs @@ -0,0 +1,117 @@ +// vim: set ts=2 sw=2 sts=2 tw=80: +// 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 { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +/* Please keep in sync with toolkit/content/widgets/findbar.js */ +const FIND_NORMAL = 0; +const FIND_TYPEAHEAD = 1; +const FIND_LINKS = 2; + +export class FindBarContent { + constructor(actor) { + this.actor = actor; + + this.findMode = 0; + this.inQuickFind = false; + + this.addedEventListener = false; + } + + start(event) { + this.inPassThrough = true; + } + + startQuickFind(event, autostart = false) { + if (!this.addedEventListener) { + this.addedEventListener = true; + Services.els.addSystemEventListener( + this.actor.document.defaultView, + "mouseup", + this, + false + ); + } + + let mode = FIND_TYPEAHEAD; + if ( + event.charCode == "'".charAt(0) || + (autostart && FindBarContent.typeAheadLinksOnly) + ) { + mode = FIND_LINKS; + } + + // Set findMode immediately (without waiting for child->parent->child roundtrip) + // to ensure we pass any further keypresses, too. + this.findMode = mode; + this.passKeyToParent(event); + } + + updateState(data) { + this.findMode = data.findMode; + this.inQuickFind = data.hasQuickFindTimeout; + if (data.isOpenAndFocused) { + this.inPassThrough = false; + } + } + + handleEvent(event) { + switch (event.type) { + case "keypress": + this.onKeypress(event); + break; + case "mouseup": + this.onMouseup(event); + break; + } + } + + onKeypress(event) { + if (this.inPassThrough) { + this.passKeyToParent(event); + } else if ( + this.findMode != FIND_NORMAL && + this.inQuickFind && + event.charCode + ) { + this.passKeyToParent(event); + } + } + + passKeyToParent(event) { + event.preventDefault(); + // These are the properties required to dispatch another 'real' event + // to the findbar in the parent in _dispatchKeypressEvent in findbar.xml . + // If you make changes here, verify that that method can still do its job. + const kRequiredProps = [ + "type", + "bubbles", + "cancelable", + "ctrlKey", + "altKey", + "shiftKey", + "metaKey", + "keyCode", + "charCode", + ]; + let fakeEvent = {}; + for (let prop of kRequiredProps) { + fakeEvent[prop] = event[prop]; + } + this.actor.sendAsyncMessage("Findbar:Keypress", fakeEvent); + } + + onMouseup(event) { + if (this.findMode != FIND_NORMAL) { + this.actor.sendAsyncMessage("Findbar:Mouseup", {}); + } + } +} + +XPCOMUtils.defineLazyPreferenceGetter( + FindBarContent, + "typeAheadLinksOnly", + "accessibility.typeaheadfind.linksonly" +); diff --git a/toolkit/modules/Finder.sys.mjs b/toolkit/modules/Finder.sys.mjs new file mode 100644 index 0000000000..0b54b2caad --- /dev/null +++ b/toolkit/modules/Finder.sys.mjs @@ -0,0 +1,858 @@ +// vim: set ts=2 sw=2 sts=2 tw=80: +// 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 { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +import { Rect } from "resource://gre/modules/Geometry.sys.mjs"; + +import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + FinderIterator: "resource://gre/modules/FinderIterator.sys.mjs", + PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", +}); + +XPCOMUtils.defineLazyServiceGetter( + lazy, + "ClipboardHelper", + "@mozilla.org/widget/clipboardhelper;1", + "nsIClipboardHelper" +); + +const kSelectionMaxLen = 150; +const kMatchesCountLimitPref = "accessibility.typeaheadfind.matchesCountLimit"; + +const activeFinderRoots = new WeakSet(); + +export function Finder(docShell) { + this._fastFind = Cc["@mozilla.org/typeaheadfind;1"].createInstance( + Ci.nsITypeAheadFind + ); + this._fastFind.init(docShell); + + this._currentFoundRange = null; + this._docShell = docShell; + this._listeners = []; + this._previousLink = null; + this._searchString = null; + this._highlighter = null; + + docShell + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebProgress) + .addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION); + docShell.domWindow.addEventListener( + "unload", + this.onLocationChange.bind(this, { isTopLevel: true }) + ); +} + +Finder.isFindbarVisible = function (docShell) { + return activeFinderRoots.has(docShell.browsingContext.top); +}; + +Finder.prototype = { + get iterator() { + if (!this._iterator) { + this._iterator = new lazy.FinderIterator(); + } + return this._iterator; + }, + + destroy() { + if (this._iterator) { + this._iterator.reset(); + } + let window = this._getWindow(); + if (this._highlighter && window) { + // if we clear all the references before we hide the highlights (in both + // highlighting modes), we simply can't use them to find the ranges we + // need to clear from the selection. + this._highlighter.hide(window); + this._highlighter.clear(window); + this.highlighter.removeScrollMarks(); + } + this.listeners = []; + this._docShell + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebProgress) + .removeProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION); + this._listeners = []; + this._currentFoundRange = + this._fastFind = + this._docShell = + this._previousLink = + this._highlighter = + null; + }, + + addResultListener(aListener) { + if (!this._listeners.includes(aListener)) { + this._listeners.push(aListener); + } + }, + + removeResultListener(aListener) { + this._listeners = this._listeners.filter(l => l != aListener); + }, + + _setResults(options, mode) { + if (typeof options.storeResult != "boolean") { + options.storeResult = true; + } + + if (options.storeResult) { + this._searchString = options.searchString; + this.clipboardSearchString = options.searchString; + } + + let foundLink = this._fastFind.foundLink; + let linkURL = null; + if (foundLink) { + linkURL = Services.textToSubURI.unEscapeURIForUI(foundLink.href); + } + + options.linkURL = linkURL; + options.rect = this._getResultRect(); + options.searchString = this._searchString; + + this._outlineLink(options.drawOutline); + + for (let l of this._listeners) { + try { + l.onFindResult(options); + } catch (ex) {} + } + }, + + get searchString() { + if (!this._searchString && this._fastFind.searchString) { + this._searchString = this._fastFind.searchString; + } + return this._searchString; + }, + + get clipboardSearchString() { + return GetClipboardSearchString( + this._getWindow().docShell.QueryInterface(Ci.nsILoadContext) + ); + }, + + set clipboardSearchString(aSearchString) { + if (!lazy.PrivateBrowsingUtils.isContentWindowPrivate(this._getWindow())) { + SetClipboardSearchString(aSearchString); + } + }, + + set caseSensitive(aSensitive) { + if (this._fastFind.caseSensitive === aSensitive) { + return; + } + this._fastFind.caseSensitive = aSensitive; + this.iterator.reset(); + }, + + set matchDiacritics(aMatchDiacritics) { + if (this._fastFind.matchDiacritics === aMatchDiacritics) { + return; + } + this._fastFind.matchDiacritics = aMatchDiacritics; + this.iterator.reset(); + }, + + set entireWord(aEntireWord) { + if (this._fastFind.entireWord === aEntireWord) { + return; + } + this._fastFind.entireWord = aEntireWord; + this.iterator.reset(); + }, + + get highlighter() { + if (this._highlighter) { + return this._highlighter; + } + + const { FinderHighlighter } = ChromeUtils.importESModule( + "resource://gre/modules/FinderHighlighter.sys.mjs" + ); + return (this._highlighter = new FinderHighlighter(this)); + }, + + get matchesCountLimit() { + if (typeof this._matchesCountLimit == "number") { + return this._matchesCountLimit; + } + + this._matchesCountLimit = + Services.prefs.getIntPref(kMatchesCountLimitPref) || 0; + return this._matchesCountLimit; + }, + + _lastFindResult: null, + + /** + * Used for normal search operations, highlights the first match. + * This method is used only for compatibility with non-remote browsers. + * + * @param aSearchString String to search for. + * @param aLinksOnly Only consider nodes that are links for the search. + * @param aDrawOutline Puts an outline around matched links. + */ + fastFind(aSearchString, aLinksOnly, aDrawOutline) { + this._lastFindResult = this._fastFind.find( + aSearchString, + aLinksOnly, + Ci.nsITypeAheadFind.FIND_INITIAL, + false + ); + let searchString = this._fastFind.searchString; + + let results = { + searchString, + result: this._lastFindResult, + findBackwards: false, + findAgain: false, + drawOutline: aDrawOutline, + linksOnly: aLinksOnly, + useSubFrames: true, + }; + + this._setResults(results); + this.updateHighlightAndMatchCount(results); + + return this._lastFindResult; + }, + + /** + * Repeat the previous search. Should only be called after a previous + * call to Finder.fastFind. + * This method is used only for compatibility with non-remote browsers. + * + * @param aSearchString String to search for. + * @param aFindBackwards Controls the search direction: + * true: before current match, false: after current match. + * @param aLinksOnly Only consider nodes that are links for the search. + * @param aDrawOutline Puts an outline around matched links. + */ + findAgain(aSearchString, aFindBackwards, aLinksOnly, aDrawOutline) { + let mode = aFindBackwards + ? Ci.nsITypeAheadFind.FIND_PREVIOUS + : Ci.nsITypeAheadFind.FIND_NEXT; + this._lastFindResult = this._fastFind.find( + aFindBackwards, + aLinksOnly, + mode, + false + ); + let searchString = this._fastFind.searchString; + + let results = { + searchString, + result: this._lastFindResult, + findBackwards: aFindBackwards, + findAgain: true, + drawOutline: aDrawOutline, + linksOnly: aLinksOnly, + useSubFrames: true, + }; + this._setResults(results); + this.updateHighlightAndMatchCount(results); + + return this._lastFindResult; + }, + + /** + * Used for normal search operations, highlights the first or + * subsequent match depending on the mode. + * + * Options are: + * searchString String to search for. + * findAgain True if this a find again operation. + * mode Search mode from nsITypeAheadFind. + * linksOnly Only consider nodes that are links for the search. + * drawOutline Puts an outline around matched links. + * useSubFrames True to iterate over subframes. + * caseSensitive True for case sensitive searching. + * entireWord True to match entire words. + * matchDiacritics True to match diacritics. + */ + find(options) { + this.caseSensitive = options.caseSensitive; + this.entireWord = options.entireWord; + this.matchDiacritics = options.matchDiacritics; + + this._lastFindResult = this._fastFind.find( + options.searchString, + options.linksOnly, + options.mode, + !options.useSubFrames + ); + let searchString = this._fastFind.searchString; + let results = { + searchString, + result: this._lastFindResult, + findBackwards: + options.mode == Ci.nsITypeAheadFind.FIND_PREVIOUS || + options.mode == Ci.nsITypeAheadFind.FIND_LAST, + findAgain: options.findAgain, + drawOutline: options.drawOutline, + linksOnly: options.linksOnly, + entireWord: this._fastFind.entireWord, + useSubFrames: options.useSubFrames, + }; + this._setResults(results, options.mode); + return new Promise(resolve => resolve(results)); + }, + + /** + * Forcibly set the search string of the find clipboard to the currently + * selected text in the window, on supported platforms (i.e. OSX). + */ + setSearchStringToSelection() { + let searchInfo = this.getActiveSelectionText(); + + // If an empty string is returned or a subframe is focused, don't + // assign the search string. + if (searchInfo.selectedText) { + this.clipboardSearchString = searchInfo.selectedText; + } + + return searchInfo; + }, + + async highlight(aHighlight, aWord, aLinksOnly, aUseSubFrames = true) { + return this.highlighter.highlight( + aHighlight, + aWord, + aLinksOnly, + false, + aUseSubFrames + ); + }, + + async updateHighlightAndMatchCount(aArgs) { + this._lastFindResult = aArgs; + + if ( + !this.iterator.continueRunning({ + caseSensitive: this._fastFind.caseSensitive, + entireWord: this._fastFind.entireWord, + linksOnly: aArgs.linksOnly, + matchDiacritics: this._fastFind.matchDiacritics, + word: aArgs.searchString, + useSubFrames: aArgs.useSubFrames, + }) + ) { + this.iterator.stop(); + } + + let highlightPromise = this.highlighter.update( + aArgs, + aArgs.useSubFrames ? false : aArgs.foundInThisFrame + ); + let matchCountPromise = this.requestMatchesCount( + aArgs.searchString, + aArgs.linksOnly, + aArgs.useSubFrames + ); + + let results = await Promise.all([highlightPromise, matchCountPromise]); + + this.highlighter.updateScrollMarks(); + + if (results[1]) { + return Object.assign(results[1], results[0]); + } else if (results[0]) { + return results[0]; + } + + return null; + }, + + getInitialSelection() { + let initialSelection = this.getActiveSelectionText().selectedText; + this._getWindow().setTimeout(() => { + for (let l of this._listeners) { + try { + l.onCurrentSelection(initialSelection, true); + } catch (ex) {} + } + }, 0); + }, + + getActiveSelectionText() { + let focusedWindow = {}; + let focusedElement = Services.focus.getFocusedElementForWindow( + this._getWindow(), + true, + focusedWindow + ); + focusedWindow = focusedWindow.value; + + let selText; + + // If this is a remote subframe, return an empty string but + // indiciate which browsing context was focused. + if ( + focusedElement && + "frameLoader" in focusedElement && + BrowsingContext.isInstance(focusedElement.browsingContext) + ) { + return { + focusedChildBrowserContextId: focusedElement.browsingContext.id, + selectedText: "", + }; + } + + if (focusedElement && focusedElement.editor) { + // The user may have a selection in an input or textarea. + selText = focusedElement.editor.selectionController + .getSelection(Ci.nsISelectionController.SELECTION_NORMAL) + .toString(); + } else { + // Look for any selected text on the actual page. + selText = focusedWindow.getSelection().toString(); + } + + if (!selText) { + return { selectedText: "" }; + } + + // Process our text to get rid of unwanted characters. + selText = selText.trim().replace(/\s+/g, " "); + let truncLength = kSelectionMaxLen; + if (selText.length > truncLength) { + let truncChar = selText.charAt(truncLength).charCodeAt(0); + if (truncChar >= 0xdc00 && truncChar <= 0xdfff) { + truncLength++; + } + selText = selText.substr(0, truncLength); + } + + return { selectedText: selText }; + }, + + enableSelection() { + this._fastFind.setSelectionModeAndRepaint( + Ci.nsISelectionController.SELECTION_ON + ); + this._restoreOriginalOutline(); + }, + + removeSelection(keepHighlight) { + this._fastFind.collapseSelection(); + this.enableSelection(); + let window = this._getWindow(); + if (keepHighlight) { + this.highlighter.clearCurrentOutline(window); + } else { + this.highlighter.clear(window); + this.highlighter.removeScrollMarks(); + } + }, + + focusContent() { + // Allow Finder listeners to cancel focusing the content. + for (let l of this._listeners) { + try { + if ("shouldFocusContent" in l && !l.shouldFocusContent()) { + return; + } + } catch (ex) { + console.error(ex); + } + } + + let fastFind = this._fastFind; + try { + // Try to find the best possible match that should receive focus and + // block scrolling on focus since find already scrolls. Further + // scrolling is due to user action, so don't override this. + if (fastFind.foundLink) { + Services.focus.setFocus( + fastFind.foundLink, + Services.focus.FLAG_NOSCROLL + ); + } else if (fastFind.foundEditable) { + Services.focus.setFocus( + fastFind.foundEditable, + Services.focus.FLAG_NOSCROLL + ); + fastFind.collapseSelection(); + } else { + this._getWindow().focus(); + } + } catch (e) {} + }, + + onFindbarClose() { + this.enableSelection(); + this.highlighter.highlight(false); + this.highlighter.removeScrollMarks(); + this.iterator.reset(); + activeFinderRoots.delete(this._docShell.browsingContext.top); + }, + + onFindbarOpen() { + activeFinderRoots.add(this._docShell.browsingContext.top); + }, + + onModalHighlightChange(useModalHighlight) { + if (this._highlighter) { + this._highlighter.onModalHighlightChange(useModalHighlight); + } + }, + + onHighlightAllChange(highlightAll) { + if (this._highlighter) { + this._highlighter.onHighlightAllChange(highlightAll); + } + if (this._iterator) { + this._iterator.reset(); + } + }, + + keyPress(aEvent) { + let controller = this._getSelectionController(this._getWindow()); + let accelKeyPressed = + AppConstants.platform == "macosx" ? aEvent.metaKey : aEvent.ctrlKey; + + switch (aEvent.keyCode) { + case aEvent.DOM_VK_RETURN: + if (this._fastFind.foundLink) { + let view = this._fastFind.foundLink.ownerGlobal; + this._fastFind.foundLink.dispatchEvent( + new view.MouseEvent("click", { + view, + cancelable: true, + bubbles: true, + ctrlKey: aEvent.ctrlKey, + altKey: aEvent.altKey, + shiftKey: aEvent.shiftKey, + metaKey: aEvent.metaKey, + }) + ); + } + break; + case aEvent.DOM_VK_TAB: + let direction = Services.focus.MOVEFOCUS_FORWARD; + if (aEvent.shiftKey) { + direction = Services.focus.MOVEFOCUS_BACKWARD; + } + Services.focus.moveFocus(this._getWindow(), null, direction, 0); + break; + case aEvent.DOM_VK_PAGE_UP: + controller.scrollPage(false); + break; + case aEvent.DOM_VK_PAGE_DOWN: + controller.scrollPage(true); + break; + case aEvent.DOM_VK_UP: + if (accelKeyPressed) { + controller.completeScroll(false); + } else { + controller.scrollLine(false); + } + break; + case aEvent.DOM_VK_DOWN: + if (accelKeyPressed) { + controller.completeScroll(true); + } else { + controller.scrollLine(true); + } + break; + } + }, + + _notifyMatchesCount(aWord, result = this._currentMatchesCountResult) { + // The `_currentFound` property is only used for internal bookkeeping. + delete result._currentFound; + result.searchString = aWord; + result.limit = this.matchesCountLimit; + if (result.total == result.limit) { + result.total = -1; + } + + for (let l of this._listeners) { + try { + l.onMatchesCountResult(result); + } catch (ex) {} + } + + this._currentMatchesCountResult = null; + return result; + }, + + async requestMatchesCount(aWord, aLinksOnly, aUseSubFrames = true) { + if ( + this._lastFindResult == Ci.nsITypeAheadFind.FIND_NOTFOUND || + this.searchString == "" || + !aWord || + !this.matchesCountLimit + ) { + return this._notifyMatchesCount(aWord, { + total: 0, + current: 0, + }); + } + + this._currentFoundRange = this._fastFind.getFoundRange(); + + let params = { + caseSensitive: this._fastFind.caseSensitive, + entireWord: this._fastFind.entireWord, + linksOnly: aLinksOnly, + matchDiacritics: this._fastFind.matchDiacritics, + word: aWord, + useSubFrames: aUseSubFrames, + }; + if (!this.iterator.continueRunning(params)) { + this.iterator.stop(); + } + + await this.iterator.start( + Object.assign(params, { + finder: this, + limit: this.matchesCountLimit, + listener: this, + useCache: true, + useSubFrames: aUseSubFrames, + }) + ); + + // Without a valid result, there's nothing to notify about. This happens + // when the iterator was started before and won the race. + if (!this._currentMatchesCountResult) { + return null; + } + + return this._notifyMatchesCount(aWord); + }, + + // FinderIterator listener implementation + + onIteratorRangeFound(range) { + let result = this._currentMatchesCountResult; + if (!result) { + return; + } + + ++result.total; + if (!result._currentFound) { + ++result.current; + result._currentFound = + this._currentFoundRange && + range.startContainer == this._currentFoundRange.startContainer && + range.startOffset == this._currentFoundRange.startOffset && + range.endContainer == this._currentFoundRange.endContainer && + range.endOffset == this._currentFoundRange.endOffset; + } + }, + + onIteratorReset() {}, + + onIteratorRestart({ word, linksOnly, useSubFrames }) { + this.requestMatchesCount(word, linksOnly, useSubFrames); + }, + + onIteratorStart() { + this._currentMatchesCountResult = { + total: 0, + current: 0, + _currentFound: false, + }; + }, + + _getWindow() { + if (!this._docShell) { + return null; + } + return this._docShell.domWindow; + }, + + /** + * Get the bounding selection rect in CSS px relative to the origin of the + * top-level content document. + */ + _getResultRect() { + let topWin = this._getWindow(); + let win = this._fastFind.currentWindow; + if (!win) { + return null; + } + + let selection = win.getSelection(); + if (!selection.rangeCount || selection.isCollapsed) { + // The selection can be into an input or a textarea element. + let nodes = win.document.querySelectorAll("input, textarea"); + for (let node of nodes) { + if (node.editor) { + try { + let sc = node.editor.selectionController; + selection = sc.getSelection( + Ci.nsISelectionController.SELECTION_NORMAL + ); + if (selection.rangeCount && !selection.isCollapsed) { + break; + } + } catch (e) { + // If this textarea is hidden, then its selection controller might + // not be intialized. Ignore the failure. + } + } + } + } + + if (!selection.rangeCount || selection.isCollapsed) { + return null; + } + + let utils = topWin.windowUtils; + + let scrollX = {}, + scrollY = {}; + utils.getScrollXY(false, scrollX, scrollY); + + for (let frame = win; frame != topWin; frame = frame.parent) { + let rect = frame.frameElement.getBoundingClientRect(); + let left = frame.getComputedStyle(frame.frameElement).borderLeftWidth; + let top = frame.getComputedStyle(frame.frameElement).borderTopWidth; + scrollX.value += rect.left + parseInt(left, 10); + scrollY.value += rect.top + parseInt(top, 10); + } + let rect = Rect.fromRect(selection.getRangeAt(0).getBoundingClientRect()); + return rect.translate(scrollX.value, scrollY.value); + }, + + _outlineLink(aDrawOutline) { + let foundLink = this._fastFind.foundLink; + + // Optimization: We are drawing outlines and we matched + // the same link before, so don't duplicate work. + if (foundLink == this._previousLink && aDrawOutline) { + return; + } + + this._restoreOriginalOutline(); + + if (foundLink && aDrawOutline) { + // Backup original outline + this._tmpOutline = foundLink.style.outline; + this._tmpOutlineOffset = foundLink.style.outlineOffset; + + // Draw pseudo focus rect + // XXX Should we change the following style for FAYT pseudo focus? + // XXX Shouldn't we change default design if outline is visible + // already? + // Don't set the outline-color, we should always use initial value. + foundLink.style.outline = "1px dotted"; + foundLink.style.outlineOffset = "0"; + + this._previousLink = foundLink; + } + }, + + _restoreOriginalOutline() { + // Removes the outline around the last found link. + if (this._previousLink) { + this._previousLink.style.outline = this._tmpOutline; + this._previousLink.style.outlineOffset = this._tmpOutlineOffset; + this._previousLink = null; + } + }, + + _getSelectionController(aWindow) { + // display: none iframes don't have a selection controller, see bug 493658 + try { + if (!aWindow.innerWidth || !aWindow.innerHeight) { + return null; + } + } catch (e) { + // If getting innerWidth or innerHeight throws, we can't get a selection + // controller. + return null; + } + + // Yuck. See bug 138068. + let docShell = aWindow.docShell; + + let controller = docShell + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsISelectionDisplay) + .QueryInterface(Ci.nsISelectionController); + return controller; + }, + + // Start of nsIWebProgressListener implementation. + + onLocationChange(aWebProgress, aRequest, aLocation, aFlags) { + if (!aWebProgress.isTopLevel) { + return; + } + // Ignore events that don't change the document. + if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) { + return; + } + + // Avoid leaking if we change the page. + this._lastFindResult = this._previousLink = this._currentFoundRange = null; + this.highlighter.onLocationChange(); + this.iterator.reset(); + }, + + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsISupportsWeakReference", + ]), +}; + +export function GetClipboardSearchString(aLoadContext) { + let searchString = ""; + if ( + !Services.clipboard.isClipboardTypeSupported( + Services.clipboard.kFindClipboard + ) + ) { + return searchString; + } + + try { + let trans = Cc["@mozilla.org/widget/transferable;1"].createInstance( + Ci.nsITransferable + ); + trans.init(aLoadContext); + trans.addDataFlavor("text/plain"); + + Services.clipboard.getData(trans, Ci.nsIClipboard.kFindClipboard); + + let data = {}; + trans.getTransferData("text/plain", data); + if (data.value) { + data = data.value.QueryInterface(Ci.nsISupportsString); + searchString = data.toString(); + } + } catch (ex) {} + + return searchString; +} + +export function SetClipboardSearchString(aSearchString) { + if ( + !aSearchString || + !Services.clipboard.isClipboardTypeSupported( + Services.clipboard.kFindClipboard + ) + ) { + return; + } + + lazy.ClipboardHelper.copyStringToClipboard( + aSearchString, + Ci.nsIClipboard.kFindClipboard + ); +} diff --git a/toolkit/modules/FinderHighlighter.sys.mjs b/toolkit/modules/FinderHighlighter.sys.mjs new file mode 100644 index 0000000000..2ebf718bd0 --- /dev/null +++ b/toolkit/modules/FinderHighlighter.sys.mjs @@ -0,0 +1,2137 @@ +/* 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/. */ + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + Color: "resource://gre/modules/Color.sys.mjs", + Rect: "resource://gre/modules/Geometry.sys.mjs", +}); +ChromeUtils.defineLazyGetter(lazy, "kDebug", () => { + const kDebugPref = "findbar.modalHighlight.debug"; + return ( + Services.prefs.getPrefType(kDebugPref) && + Services.prefs.getBoolPref(kDebugPref) + ); +}); + +const kContentChangeThresholdPx = 5; +const kBrightTextSampleSize = 5; +// This limit is arbitrary and doesn't scale for low-powered machines or +// high-powered machines. Netbooks will probably need a much lower limit, for +// example. Though getting something out there is better than nothing. +const kPageIsTooBigPx = 500000; +const kModalHighlightRepaintLoFreqMs = 100; +const kModalHighlightRepaintHiFreqMs = 16; +const kHighlightAllPref = "findbar.highlightAll"; +const kModalHighlightPref = "findbar.modalHighlight"; +const kFontPropsCSS = [ + "color", + "font-family", + "font-kerning", + "font-size", + "font-size-adjust", + "font-stretch", + "font-variant", + "font-weight", + "line-height", + "letter-spacing", + "text-emphasis", + "text-orientation", + "text-transform", + "word-spacing", +]; +const kFontPropsCamelCase = kFontPropsCSS.map(prop => { + let parts = prop.split("-"); + return ( + parts.shift() + + parts.map(part => part.charAt(0).toUpperCase() + part.slice(1)).join("") + ); +}); +const kRGBRE = /^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*/i; +// This uuid is used to prefix HTML element IDs in order to make them unique and +// hard to clash with IDs content authors come up with. +const kModalIdPrefix = "cedee4d0-74c5-4f2d-ab43-4d37c0f9d463"; +const kModalOutlineId = kModalIdPrefix + "-findbar-modalHighlight-outline"; +const kOutlineBoxColor = "255,197,53"; +const kOutlineBoxBorderSize = 1; +const kOutlineBoxBorderRadius = 2; +const kModalStyles = { + outlineNode: [ + ["background-color", `rgb(${kOutlineBoxColor})`], + ["background-clip", "padding-box"], + ["border", `${kOutlineBoxBorderSize}px solid rgba(${kOutlineBoxColor},.7)`], + ["border-radius", `${kOutlineBoxBorderRadius}px`], + ["box-shadow", `0 2px 0 0 rgba(0,0,0,.1)`], + ["color", "#000"], + ["display", "flex"], + [ + "margin", + `-${kOutlineBoxBorderSize}px 0 0 -${kOutlineBoxBorderSize}px !important`, + ], + ["overflow", "hidden"], + ["pointer-events", "none"], + ["position", "absolute"], + ["white-space", "nowrap"], + ["will-change", "transform"], + ["z-index", 2], + ], + outlineNodeDebug: [["z-index", 2147483647]], + outlineText: [ + ["margin", "0 !important"], + ["padding", "0 !important"], + ["vertical-align", "top !important"], + ], + maskNode: [ + ["background", "rgba(0,0,0,.25)"], + ["pointer-events", "none"], + ["position", "absolute"], + ["z-index", 1], + ], + maskNodeTransition: [["transition", "background .2s ease-in"]], + maskNodeDebug: [ + ["z-index", 2147483646], + ["top", 0], + ["left", 0], + ], + maskNodeBrightText: [["background", "rgba(255,255,255,.25)"]], +}; +const kModalOutlineAnim = { + keyframes: [ + { transform: "scaleX(1) scaleY(1)" }, + { transform: "scaleX(1.5) scaleY(1.5)", offset: 0.5, easing: "ease-in" }, + { transform: "scaleX(1) scaleY(1)" }, + ], + duration: 50, +}; +const kNSHTML = "http://www.w3.org/1999/xhtml"; +const kRepaintSchedulerStopped = 1; +const kRepaintSchedulerPaused = 2; +const kRepaintSchedulerRunning = 3; + +function mockAnonymousContentNode(domNode) { + return { + setTextContentForElement(id, text) { + (domNode.querySelector("#" + id) || domNode).textContent = text; + }, + getAttributeForElement(id, attrName) { + let node = domNode.querySelector("#" + id) || domNode; + if (!node.hasAttribute(attrName)) { + return undefined; + } + return node.getAttribute(attrName); + }, + setAttributeForElement(id, attrName, attrValue) { + (domNode.querySelector("#" + id) || domNode).setAttribute( + attrName, + attrValue + ); + }, + removeAttributeForElement(id, attrName) { + let node = domNode.querySelector("#" + id) || domNode; + if (!node.hasAttribute(attrName)) { + return; + } + node.removeAttribute(attrName); + }, + remove() { + try { + domNode.remove(); + } catch (ex) {} + }, + setAnimationForElement(id, keyframes, duration) { + return (domNode.querySelector("#" + id) || domNode).animate( + keyframes, + duration + ); + }, + setCutoutRectsForElement(id, rects) { + // no-op for now. + }, + }; +} + +let gWindows = new WeakMap(); + +/** + * FinderHighlighter class that is used by Finder.sys.mjs to take care of the + * 'Highlight All' feature, which can highlight all find occurrences in a page. + * + * @param {Finder} finder Finder.sys.mjs instance + * @param {boolean} useTop check and use top-level windows for rectangle + * computation, if possible. + */ +export function FinderHighlighter(finder, useTop = false) { + this._highlightAll = Services.prefs.getBoolPref(kHighlightAllPref); + this._modal = Services.prefs.getBoolPref(kModalHighlightPref); + this._useSubFrames = false; + this._useTop = useTop; + this._marksListener = null; + this._testing = false; + this.finder = finder; +} + +FinderHighlighter.prototype = { + get iterator() { + return this.finder.iterator; + }, + + enableTesting(enable) { + this._testing = enable; + }, + + // Get the top-most window when allowed. When out-of-process frames are used, + // this will usually be the same as the passed-in window. The checkUseTop + // argument can be used to instead check the _useTop flag which can be used + // to enable rectangle coordinate checks. + getTopWindow(window, checkUseTop) { + if (this._useSubFrames || (checkUseTop && this._useTop)) { + try { + return window.top; + } catch (ex) {} + } + + return window; + }, + + useModal() { + // Modal highlighting is currently only enabled when there are no + // out-of-process subframes. + return this._modal && this._useSubFrames; + }, + + /** + * Each window is unique, globally, and the relation between an active + * highlighting session and a window is 1:1. + * For each window we track a number of properties which _at least_ consist of + * - {Boolean} detectedGeometryChange Whether the geometry of the found ranges' + * rectangles has changed substantially + * - {Set} dynamicRangesSet Set of ranges that may move around, depending + * on page layout changes and user input + * - {Map} frames Collection of frames that were encountered + * when inspecting the found ranges + * - {Map} modalHighlightRectsMap Collection of ranges and their corresponding + * Rects and texts + * + * @param {nsIDOMWindow} window + * @return {Object} + */ + getForWindow(window, propName = null) { + if (!gWindows.has(window)) { + gWindows.set(window, { + detectedGeometryChange: false, + dynamicRangesSet: new Set(), + frames: new Map(), + lastWindowDimensions: { width: 0, height: 0 }, + modalHighlightRectsMap: new Map(), + previousRangeRectsAndTexts: { rectList: [], textList: [] }, + repaintSchedulerState: kRepaintSchedulerStopped, + }); + } + return gWindows.get(window); + }, + + /** + * Notify all registered listeners that the 'Highlight All' operation finished. + * + * @param {Boolean} highlight Whether highlighting was turned on + */ + notifyFinished(highlight) { + for (let l of this.finder._listeners) { + try { + l.onHighlightFinished(highlight); + } catch (ex) {} + } + }, + + /** + * Toggle highlighting all occurrences of a word in a page. This method will + * be called recursively for each (i)frame inside a page. + * + * @param {Booolean} highlight Whether highlighting should be turned on + * @param {String} word Needle to search for and highlight when found + * @param {Boolean} linksOnly Only consider nodes that are links for the search + * @param {Boolean} drawOutline Whether found links should be outlined. + * @param {Boolean} useSubFrames Whether to iterate over subframes. + * @yield {Promise} that resolves once the operation has finished + */ + async highlight(highlight, word, linksOnly, drawOutline, useSubFrames) { + let window = this.finder._getWindow(); + let dict = this.getForWindow(window); + let controller = this.finder._getSelectionController(window); + let doc = window.document; + this._found = false; + this._useSubFrames = useSubFrames; + + let result = { searchString: word, highlight, found: false }; + + if (!controller || !doc || !doc.documentElement) { + // Without the selection controller, + // we are unable to (un)highlight any matches + return result; + } + + if (highlight) { + let params = { + allowDistance: 1, + caseSensitive: this.finder._fastFind.caseSensitive, + entireWord: this.finder._fastFind.entireWord, + linksOnly, + word, + finder: this.finder, + listener: this, + matchDiacritics: this.finder._fastFind.matchDiacritics, + useCache: true, + useSubFrames, + window, + }; + if ( + this.iterator.isAlreadyRunning(params) || + (this.useModal() && + this.iterator._areParamsEqual(params, dict.lastIteratorParams)) + ) { + return result; + } + + if (!this.useModal()) { + dict.visible = true; + } + await this.iterator.start(params); + if (this._found) { + this.finder._outlineLink(drawOutline); + } + } else { + this.hide(window); + + // Removing the highlighting always succeeds, so return true. + this._found = true; + } + + result.found = this._found; + this.notifyFinished(result); + return result; + }, + + // FinderIterator listener implementation + + onIteratorRangeFound(range) { + this.highlightRange(range); + this._found = true; + }, + + onIteratorReset() {}, + + onIteratorRestart() { + this.clear(this.finder._getWindow()); + }, + + onIteratorStart(params) { + let window = this.finder._getWindow(); + let dict = this.getForWindow(window); + // Save a clean params set for use later in the `update()` method. + dict.lastIteratorParams = params; + if (!this.useModal()) { + this.hide(window, this.finder._fastFind.getFoundRange()); + } + this.clear(window); + }, + + /** + * Add a range to the find selection, i.e. highlight it, and if it's inside an + * editable node, track it. + * + * @param {Range} range Range object to be highlighted + */ + highlightRange(range) { + let node = range.startContainer; + let editableNode = this._getEditableNode(node); + let window = node.ownerGlobal; + let controller = this.finder._getSelectionController(window); + if (editableNode) { + controller = editableNode.editor.selectionController; + } + + if (this.useModal()) { + this._modalHighlight(range, controller, window); + } else { + let findSelection = controller.getSelection( + Ci.nsISelectionController.SELECTION_FIND + ); + findSelection.addRange(range); + // Check if the range is inside an (i)frame. + if (window != this.getTopWindow(window)) { + let dict = this.getForWindow(this.getTopWindow(window)); + // Add this frame to the list, so that we'll be able to find it later + // when we need to clear its selection(s). + dict.frames.set(window, {}); + } + } + + if (editableNode) { + // Highlighting added, so cache this editor, and hook up listeners + // to ensure we deal properly with edits within the highlighting + this._addEditorListeners(editableNode.editor); + } + }, + + /** + * If modal highlighting is enabled, show the dimmed background that will overlay + * the page. + * + * @param {nsIDOMWindow} window The dimmed background will overlay this window. + * Optional, defaults to the finder window. + */ + show(window = null) { + window = this.getTopWindow(window || this.finder._getWindow()); + let dict = this.getForWindow(window); + if (!this.useModal() || dict.visible) { + return; + } + + dict.visible = true; + + this._maybeCreateModalHighlightNodes(window); + this._addModalHighlightListeners(window); + }, + + /** + * Clear all highlighted matches. If modal highlighting is enabled and + * the outline + dimmed background is currently visible, both will be hidden. + * + * @param {nsIDOMWindow} window The dimmed background will overlay this window. + * Optional, defaults to the finder window. + * @param {Range} skipRange A range that should not be removed from the + * find selection. + * @param {Event} event When called from an event handler, this will + * be the triggering event. + */ + hide(window, skipRange = null, event = null) { + try { + window = this.getTopWindow(window); + } catch (ex) { + console.error(ex); + return; + } + let dict = this.getForWindow(window); + + let isBusySelecting = dict.busySelecting; + dict.busySelecting = false; + // Do not hide on anything but a left-click. + if ( + event && + event.type == "click" && + (event.button !== 0 || + event.altKey || + event.ctrlKey || + event.metaKey || + event.shiftKey || + event.relatedTarget || + isBusySelecting || + (event.target.localName == "a" && event.target.href)) + ) { + return; + } + + this._clearSelection( + this.finder._getSelectionController(window), + skipRange + ); + for (let frame of dict.frames.keys()) { + this._clearSelection( + this.finder._getSelectionController(frame), + skipRange + ); + } + + // Next, check our editor cache, for editors belonging to this + // document + if (this._editors) { + let doc = window.document; + for (let x = this._editors.length - 1; x >= 0; --x) { + if (this._editors[x].document == doc) { + this._clearSelection(this._editors[x].selectionController, skipRange); + // We don't need to listen to this editor any more + this._unhookListenersAtIndex(x); + } + } + } + + if (dict.modalRepaintScheduler) { + window.clearTimeout(dict.modalRepaintScheduler); + dict.modalRepaintScheduler = null; + dict.repaintSchedulerState = kRepaintSchedulerStopped; + } + dict.lastWindowDimensions = { width: 0, height: 0 }; + + this._removeRangeOutline(window); + this._removeHighlightAllMask(window); + this._removeModalHighlightListeners(window); + + dict.visible = false; + }, + + /** + * Called by the Finder after a find result comes in; update the position and + * content of the outline to the newly found occurrence. + * To make sure that the outline covers the found range completely, all the + * CSS styles that influence the text are copied and applied to the outline. + * + * @param {Object} data Dictionary coming from Finder that contains the + * following properties: + * {Number} result One of the nsITypeAheadFind.FIND_* constants + * indicating the result of a search operation. + * {Boolean} findBackwards If TRUE, the search was performed backwards, + * FALSE if forwards. + * {Boolean} findAgain If TRUE, the search was performed using the same + * search string as before. + * {String} linkURL If a link was hit, this will contain a URL string. + * {Rect} rect An object with top, left, width and height + * coordinates of the current selection. + * {String} searchString The string the search was performed with. + * {Boolean} storeResult Indicator if the search string should be stored + * by the consumer of the Finder. + */ + async update(data, foundInThisFrame) { + let window = this.finder._getWindow(); + let dict = this.getForWindow(window); + let foundRange = this.finder._fastFind.getFoundRange(); + + if ( + data.result == Ci.nsITypeAheadFind.FIND_NOTFOUND || + !data.searchString || + (foundInThisFrame && !foundRange) + ) { + this.hide(window); + return; + } + + this._useSubFrames = data.useSubFrames; + if (!this.useModal()) { + if (this._highlightAll) { + dict.previousFoundRange = dict.currentFoundRange; + dict.currentFoundRange = foundRange; + let params = this.iterator.params; + if ( + dict.visible && + this.iterator._areParamsEqual(params, dict.lastIteratorParams) + ) { + return; + } + if (!dict.visible && !params) { + params = { word: data.searchString, linksOnly: data.linksOnly }; + } + if (params) { + await this.highlight( + true, + params.word, + params.linksOnly, + params.drawOutline, + data.useSubFrames + ); + } + } + return; + } + + dict.animateOutline = true; + // Immediately finish running animations, if any. + this._finishOutlineAnimations(dict); + + if (foundRange !== dict.currentFoundRange || data.findAgain) { + dict.previousFoundRange = dict.currentFoundRange; + dict.currentFoundRange = foundRange; + + if (!dict.visible) { + this.show(window); + } else { + this._maybeCreateModalHighlightNodes(window); + } + } + + if (this._highlightAll) { + await this.highlight( + true, + data.searchString, + data.linksOnly, + data.drawOutline, + data.useSubFrames + ); + } + }, + + /** + * Invalidates the list by clearing the map of highlighted ranges that we + * keep to build the mask for. + */ + clear(window = null) { + if (!window || !this.getTopWindow(window)) { + return; + } + + let dict = this.getForWindow(this.getTopWindow(window)); + this._finishOutlineAnimations(dict); + dict.dynamicRangesSet.clear(); + dict.frames.clear(); + dict.modalHighlightRectsMap.clear(); + dict.brightText = null; + }, + + /** + * Removes the outline from a single window. This is done when + * switching the current search to a new frame. + */ + clearCurrentOutline(window = null) { + let dict = this.getForWindow(this.getTopWindow(window)); + this._finishOutlineAnimations(dict); + this._removeRangeOutline(window); + }, + + // Update the tick marks that should appear on the page's scrollbar(s). + updateScrollMarks() { + // Only show scrollbar marks when normal highlighting is enabled. + if (this.useModal() || !this._highlightAll) { + this.removeScrollMarks(); + return; + } + + let marks = new Set(); // Use a set so duplicate values are removed. + let window = this.finder._getWindow(); + // Show the marks on the horizontal scrollbar for vertical writing modes. + let onHorizontalScrollbar = !window + .getComputedStyle(window.document.body || window.document.documentElement) + .writingMode.startsWith("horizontal"); + let yStart = window.scrollY - window.scrollMinY; + let xStart = window.scrollX - window.scrollMinX; + + let hasRanges = false; + if (window) { + let controllers = [this.finder._getSelectionController(window)]; + let editors = this.editors; + if (editors) { + // Add the selection controllers from any input fields. + controllers.push(...editors.map(editor => editor.selectionController)); + } + + for (let controller of controllers) { + let findSelection = controller.getSelection( + Ci.nsISelectionController.SELECTION_FIND + ); + + let rangeCount = findSelection.rangeCount; + if (rangeCount > 0) { + hasRanges = true; + } + + // No need to calculate the mark positions if there is no visible scrollbar. + if (window.scrollMaxY > window.scrollMinY && !onHorizontalScrollbar) { + // Use the body's scrollHeight if available. + let scrollHeight = + window.document.body?.scrollHeight || + window.document.documentElement.scrollHeight; + let yAdj = (window.scrollMaxY - window.scrollMinY) / scrollHeight; + + for (let r = 0; r < rangeCount; r++) { + let rect = findSelection.getRangeAt(r).getBoundingClientRect(); + let yPos = Math.round((yStart + rect.y + rect.height / 2) * yAdj); // use the midpoint + marks.add(yPos); + } + } else if ( + window.scrollMaxX > window.scrollMinX && + onHorizontalScrollbar + ) { + // Use the body's scrollWidth if available. + let scrollWidth = + window.document.body?.scrollWidth || + window.document.documentElement.scrollWidth; + let xAdj = (window.scrollMaxX - window.scrollMinX) / scrollWidth; + + for (let r = 0; r < rangeCount; r++) { + let rect = findSelection.getRangeAt(r).getBoundingClientRect(); + let xPos = Math.round((xStart + rect.x + rect.width / 2) * xAdj); + marks.add(xPos); + } + } + } + } + + if (hasRanges) { + // Assign the marks to the window and add a listener for the MozScrolledAreaChanged + // event which fires whenever the scrollable area's size is updated. + this.setScrollMarks(window, Array.from(marks), onHorizontalScrollbar); + + if (!this._marksListener) { + this._marksListener = event => { + this.updateScrollMarks(); + }; + + window.addEventListener( + "MozScrolledAreaChanged", + this._marksListener, + true + ); + window.addEventListener("resize", this._marksListener); + } + } else if (this._marksListener) { + // No results were found so remove any existing ones and the MozScrolledAreaChanged listener. + this.removeScrollMarks(); + } + }, + + removeScrollMarks() { + let window; + try { + window = this.finder._getWindow(); + } catch (ex) { + // An exception can happen after changing remoteness but this + // would have deleted the marks anyway. + return; + } + + if (this._marksListener) { + window.removeEventListener( + "MozScrolledAreaChanged", + this._marksListener, + true + ); + window.removeEventListener("resize", this._marksListener); + this._marksListener = null; + } + this.setScrollMarks(window, []); + }, + + /** + * Set the scrollbar marks for a current search. If testing mode is enabled, fire a + * find-scrollmarks-changed event at the window. + * + * @param window window to set the scrollbar marks on + * @param marks array of integer scrollbar mark positions + * @param onHorizontalScrollbar whether to display the marks on the horizontal scrollbar + */ + setScrollMarks(window, marks, onHorizontalScrollbar = false) { + window.setScrollMarks(marks, onHorizontalScrollbar); + + // Fire an event containing the found mark values if testing mode is enabled. + if (this._testing) { + window.dispatchEvent( + new CustomEvent("find-scrollmarks-changed", { + detail: { + marks: Array.from(marks), + onHorizontalScrollbar, + }, + }) + ); + } + }, + + /** + * When the current page is refreshed or navigated away from, the CanvasFrame + * contents is not valid anymore, i.e. all anonymous content is destroyed. + * We need to clear the references we keep, which'll make sure we redraw + * everything when the user starts to find in page again. + */ + onLocationChange() { + let window = this.finder._getWindow(); + if (!window || !this.getTopWindow(window)) { + return; + } + this.hide(window); + this.clear(window); + this._removeRangeOutline(window); + + gWindows.delete(this.getTopWindow(window)); + }, + + /** + * When `kModalHighlightPref` pref changed during a session, this callback is + * invoked. When modal highlighting is turned off, we hide the CanvasFrame + * contents. + * + * @param {Boolean} useModalHighlight + */ + onModalHighlightChange(useModalHighlight) { + let window = this.finder._getWindow(); + if (window && this.useModal() && !useModalHighlight) { + this.hide(window); + this.clear(window); + } + this._modal = useModalHighlight; + this.updateScrollMarks(); + }, + + /** + * When 'Highlight All' is toggled during a session, this callback is invoked + * and when it's turned off, the found occurrences will be removed from the mask. + * + * @param {Boolean} highlightAll + */ + onHighlightAllChange(highlightAll) { + this._highlightAll = highlightAll; + if (!highlightAll) { + let window = this.finder._getWindow(); + if (!this.useModal()) { + this.hide(window); + } + this.clear(window); + this._scheduleRepaintOfMask(window); + } + + this.updateScrollMarks(); + }, + + /** + * Utility; removes all ranges from the find selection that belongs to a + * controller. Optionally skips a specific range. + * + * @param {nsISelectionController} controller + * @param {Range} restoreRange + */ + _clearSelection(controller, restoreRange = null) { + if (!controller) { + return; + } + let sel = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND); + sel.removeAllRanges(); + if (restoreRange) { + sel = controller.getSelection(Ci.nsISelectionController.SELECTION_NORMAL); + sel.addRange(restoreRange); + controller.setDisplaySelection( + Ci.nsISelectionController.SELECTION_ATTENTION + ); + controller.repaintSelection(Ci.nsISelectionController.SELECTION_NORMAL); + } + }, + + /** + * Utility; get the nsIDOMWindowUtils for a window. + * + * @param {nsIDOMWindow} window Optional, defaults to the finder window. + * @return {nsIDOMWindowUtils} + */ + _getDWU(window = null) { + return (window || this.finder._getWindow()).windowUtils; + }, + + /** + * Utility; returns the bounds of the page relative to the viewport. + * If the pages is part of a frameset or inside an iframe of any kind, its + * offset is accounted for. + * Geometry.sys.mjs takes care of the DOMRect calculations. + * + * @param {nsIDOMWindow} window Window to read the boundary rect from + * @param {Boolean} [includeScroll] Whether to ignore the scroll offset, + * which is useful for comparing DOMRects. + * Optional, defaults to `true` + * @return {Rect} + */ + _getRootBounds(window, includeScroll = true) { + let dwu = this._getDWU(this.getTopWindow(window, true)); + let cssPageRect = lazy.Rect.fromRect(dwu.getRootBounds()); + let scrollX = {}; + let scrollY = {}; + if (includeScroll && window == this.getTopWindow(window, true)) { + dwu.getScrollXY(false, scrollX, scrollY); + cssPageRect.translate(scrollX.value, scrollY.value); + } + + // If we're in a frame, update the position of the rect (top/ left). + let currWin = window; + while (currWin != this.getTopWindow(window, true)) { + let frameOffsets = this._getFrameElementOffsets(currWin); + cssPageRect.translate(frameOffsets.x, frameOffsets.y); + + // Since the frame is an element inside a parent window, we'd like to + // learn its position relative to it. + let el = currWin.browsingContext.embedderElement; + currWin = currWin.parent; + dwu = this._getDWU(currWin); + let parentRect = lazy.Rect.fromRect(dwu.getBoundsWithoutFlushing(el)); + + if (includeScroll) { + dwu.getScrollXY(false, scrollX, scrollY); + parentRect.translate(scrollX.value, scrollY.value); + // If the current window is an iframe with scrolling="no" and its parent + // is also an iframe the scroll offsets from the parents' documentElement + // (inverse scroll position) needs to be subtracted from the parent + // window rect. + if ( + el.getAttribute("scrolling") == "no" && + currWin != this.getTopWindow(window, true) + ) { + let docEl = currWin.document.documentElement; + parentRect.translate(-docEl.scrollLeft, -docEl.scrollTop); + } + } + + cssPageRect.translate(parentRect.left, parentRect.top); + } + let frameOffsets = this._getFrameElementOffsets(currWin); + cssPageRect.translate(frameOffsets.x, frameOffsets.y); + + return cssPageRect; + }, + + /** + * (I)Frame elements may have a border and/ or padding set, which is not + * included in the bounds returned by nsDOMWindowUtils#getRootBounds() for the + * window it hosts. + * This method fetches this offset of the frame element to the respective window. + * + * @param {nsIDOMWindow} window Window to read the boundary rect from + * @return {Object} Simple object that contains the following two properties: + * - {Number} x Offset along the horizontal axis. + * - {Number} y Offset along the vertical axis. + */ + _getFrameElementOffsets(window) { + let frame = window.frameElement; + if (!frame) { + return { x: 0, y: 0 }; + } + + // Getting style info is super expensive, causing reflows, so let's cache + // frame border widths and padding values aggressively. + let dict = this.getForWindow(this.getTopWindow(window, true)); + let frameData = dict.frames.get(window); + if (!frameData) { + dict.frames.set(window, (frameData = {})); + } + if (frameData.offset) { + return frameData.offset; + } + + let style = frame.ownerGlobal.getComputedStyle(frame); + // We only need to left sides, because ranges are offset from point 0,0 in + // the top-left corner. + let borderOffset = [ + parseInt(style.borderLeftWidth, 10) || 0, + parseInt(style.borderTopWidth, 10) || 0, + ]; + let paddingOffset = [ + parseInt(style.paddingLeft, 10) || 0, + parseInt(style.paddingTop, 10) || 0, + ]; + return (frameData.offset = { + x: borderOffset[0] + paddingOffset[0], + y: borderOffset[1] + paddingOffset[1], + }); + }, + + /** + * Utility; fetch the full width and height of the current window, excluding + * scrollbars. + * + * @param {nsiDOMWindow} window The current finder window. + * @return {Object} The current full page dimensions with `width` and `height` + * properties + */ + _getWindowDimensions(window) { + // First we'll try without flushing layout, because it's way faster. + let dwu = this._getDWU(window); + let { width, height } = dwu.getRootBounds(); + + if (!width || !height) { + // We need a flush after all :'( + width = window.innerWidth + window.scrollMaxX - window.scrollMinX; + height = window.innerHeight + window.scrollMaxY - window.scrollMinY; + + let scrollbarHeight = {}; + let scrollbarWidth = {}; + dwu.getScrollbarSize(false, scrollbarWidth, scrollbarHeight); + width -= scrollbarWidth.value; + height -= scrollbarHeight.value; + } + + return { width, height }; + }, + + /** + * Utility; get all available font styles as applied to the content of a given + * range. The CSS properties we look for can be found in `kFontPropsCSS`. + * + * @param {Range} range Range to fetch style info from. + * @return {Object} Dictionary consisting of the styles that were found. + */ + _getRangeFontStyle(range) { + let node = range.startContainer; + while (node.nodeType != 1) { + node = node.parentNode; + } + let style = node.ownerGlobal.getComputedStyle(node); + let props = {}; + for (let prop of kFontPropsCamelCase) { + if (prop in style && style[prop]) { + props[prop] = style[prop]; + } + } + return props; + }, + + /** + * Utility; transform a dictionary object as returned by `_getRangeFontStyle` + * above into a HTML style attribute value. + * + * @param {Object} fontStyle + * @return {String} + */ + _getHTMLFontStyle(fontStyle) { + let style = []; + for (let prop of Object.getOwnPropertyNames(fontStyle)) { + let idx = kFontPropsCamelCase.indexOf(prop); + if (idx == -1) { + continue; + } + style.push(`${kFontPropsCSS[idx]}: ${fontStyle[prop]}`); + } + return style.join("; "); + }, + + /** + * Transform a style definition array as defined in `kModalStyles` into a CSS + * string that can be used to set the 'style' property of a DOM node. + * + * @param {Array} stylePairs Two-dimensional array of style pairs + * @param {...Array} [additionalStyles] Optional set of style pairs that will + * augment or override the styles defined + * by `stylePairs` + * @return {String} + */ + _getStyleString(stylePairs, ...additionalStyles) { + let baseStyle = new Map(stylePairs); + for (let additionalStyle of additionalStyles) { + for (let [prop, value] of additionalStyle) { + baseStyle.set(prop, value); + } + } + return [...baseStyle] + .map(([cssProp, cssVal]) => `${cssProp}: ${cssVal}`) + .join("; "); + }, + + /** + * Checks whether a CSS RGB color value can be classified as being 'bright'. + * + * @param {String} cssColor RGB color value in the default format rgb[a](r,g,b) + * @return {Boolean} + */ + _isColorBright(cssColor) { + cssColor = cssColor.match(kRGBRE); + if (!cssColor || !cssColor.length) { + return false; + } + cssColor.shift(); + return !new lazy.Color(...cssColor).useBrightText; + }, + + /** + * Detects if the overall text color in the page can be described as bright. + * This is done according to the following algorithm: + * 1. With the entire set of ranges that we have found thusfar; + * 2. Get an odd-numbered `sampleSize`, with a maximum of `kBrightTextSampleSize` + * ranges, + * 3. Slice the set of ranges into `sampleSize` number of equal parts, + * 4. Grab the first range for each slice and inspect the brightness of the + * color of its text content. + * 5. When the majority of ranges are counted as contain bright colored text, + * the page is considered to contain bright text overall. + * + * @param {Object} dict Dictionary of properties belonging to the + * currently active window. The page text color property + * will be recorded in `dict.brightText` as `true` or `false`. + */ + _detectBrightText(dict) { + let sampleSize = Math.min( + dict.modalHighlightRectsMap.size, + kBrightTextSampleSize + ); + let ranges = [...dict.modalHighlightRectsMap.keys()]; + let rangesCount = ranges.length; + // Make sure the sample size is an odd number. + if (sampleSize % 2 == 0) { + // Make the previously or currently found range weigh heavier. + if (dict.previousFoundRange || dict.currentFoundRange) { + ranges.push(dict.previousFoundRange || dict.currentFoundRange); + ++sampleSize; + ++rangesCount; + } else { + --sampleSize; + } + } + let brightCount = 0; + for (let i = 0; i < sampleSize; ++i) { + let range = ranges[Math.floor((rangesCount / sampleSize) * i)]; + let fontStyle = this._getRangeFontStyle(range); + if (this._isColorBright(fontStyle.color)) { + ++brightCount; + } + } + + dict.brightText = brightCount >= Math.ceil(sampleSize / 2); + }, + + /** + * Checks if a range is inside a DOM node that's positioned in a way that it + * doesn't scroll along when the document is scrolled and/ or zoomed. This + * is the case for 'fixed' and 'sticky' positioned elements, elements inside + * (i)frames and elements that have their overflow styles set to 'auto' or + * 'scroll'. + * + * @param {Range} range Range that be enclosed in a dynamic container + * @return {Boolean} + */ + _isInDynamicContainer(range) { + const kFixed = new Set(["fixed", "sticky", "scroll", "auto"]); + let node = range.startContainer; + while (node.nodeType != 1) { + node = node.parentNode; + } + let document = node.ownerDocument; + let window = document.defaultView; + let dict = this.getForWindow(this.getTopWindow(window)); + + // Check if we're in a frameset (including iframes). + if (window != this.getTopWindow(window)) { + if (!dict.frames.has(window)) { + dict.frames.set(window, {}); + } + return true; + } + + do { + let style = window.getComputedStyle(node); + if ( + kFixed.has(style.position) || + kFixed.has(style.overflow) || + kFixed.has(style.overflowX) || + kFixed.has(style.overflowY) + ) { + return true; + } + node = node.parentNode; + } while (node && node != document.documentElement); + + return false; + }, + + /** + * Read and store the rectangles that encompass the entire region of a range + * for use by the drawing function of the highlighter. + * + * @param {Range} range Range to fetch the rectangles from + * @param {Object} [dict] Dictionary of properties belonging to + * the currently active window + * @return {Set} Set of rects that were found for the range + */ + _getRangeRectsAndTexts(range, dict = null) { + let window = range.startContainer.ownerGlobal; + let bounds; + // If the window is part of a frameset, try to cache the bounds query. + if (dict && dict.frames.has(window)) { + let frameData = dict.frames.get(window); + bounds = frameData.bounds; + if (!bounds) { + bounds = frameData.bounds = this._getRootBounds(window); + } + } else { + bounds = this._getRootBounds(window); + } + + let topBounds = this._getRootBounds(this.getTopWindow(window, true), false); + let rects = []; + // A range may consist of multiple rectangles, we can also do these kind of + // precise cut-outs. range.getBoundingClientRect() returns the fully + // encompassing rectangle, which is too much for our purpose here. + let { rectList, textList } = range.getClientRectsAndTexts(); + for (let rect of rectList) { + rect = lazy.Rect.fromRect(rect); + rect.x += bounds.x; + rect.y += bounds.y; + // If the rect is not even visible from the top document, we can ignore it. + if (rect.intersects(topBounds)) { + rects.push(rect); + } + } + return { rectList: rects, textList }; + }, + + /** + * Read and store the rectangles that encompass the entire region of a range + * for use by the drawing function of the highlighter and store them in the + * cache. + * + * @param {Range} range Range to fetch the rectangles from + * @param {Boolean} [checkIfDynamic] Whether we should check if the range + * is dynamic as per the rules in + * `_isInDynamicContainer()`. Optional, + * defaults to `true` + * @param {Object} [dict] Dictionary of properties belonging to + * the currently active window + * @return {Set} Set of rects that were found for the range + */ + _updateRangeRects(range, checkIfDynamic = true, dict = null) { + let window = range.startContainer.ownerGlobal; + let rectsAndTexts = this._getRangeRectsAndTexts(range, dict); + + // Only fetch the rect at this point, if not passed in as argument. + dict = dict || this.getForWindow(this.getTopWindow(window)); + let oldRectsAndTexts = dict.modalHighlightRectsMap.get(range); + dict.modalHighlightRectsMap.set(range, rectsAndTexts); + // Check here if we suddenly went down to zero rects from more than zero before, + // which indicates that we should re-iterate the document. + if ( + oldRectsAndTexts && + oldRectsAndTexts.rectList.length && + !rectsAndTexts.rectList.length + ) { + dict.detectedGeometryChange = true; + } + if (checkIfDynamic && this._isInDynamicContainer(range)) { + dict.dynamicRangesSet.add(range); + } + return rectsAndTexts; + }, + + /** + * Re-read the rectangles of the ranges that we keep track of separately, + * because they're enclosed by a position: fixed container DOM node or (i)frame. + * + * @param {Object} dict Dictionary of properties belonging to the currently + * active window + */ + _updateDynamicRangesRects(dict) { + // Reset the frame bounds cache. + for (let frameData of dict.frames.values()) { + frameData.bounds = null; + } + for (let range of dict.dynamicRangesSet) { + this._updateRangeRects(range, false, dict); + } + }, + + /** + * Update the content, position and style of the yellow current found range + * outline that floats atop the mask with the dimmed background. + * Rebuild it, if necessary, This will deactivate the animation between + * occurrences. + * + * @param {Object} dict Dictionary of properties belonging to the currently + * active window + */ + _updateRangeOutline(dict) { + let range = dict.currentFoundRange; + if (!range) { + return; + } + + let fontStyle = this._getRangeFontStyle(range); + // Text color in the outline is determined by kModalStyles. + delete fontStyle.color; + + let rectsAndTexts = this._updateRangeRects(range, true, dict); + let outlineAnonNode = dict.modalHighlightOutline; + let rectCount = rectsAndTexts.rectList.length; + let previousRectCount = dict.previousRangeRectsAndTexts.rectList.length; + // (re-)Building the outline is conditional and happens when one of the + // following conditions is met: + // 1. No outline nodes were built before, or + // 2. When the amount of rectangles to draw is different from before, or + // 3. When there's more than one rectangle to draw, because it's impossible + // to animate that consistently with AnonymousContent nodes. + let rebuildOutline = + !outlineAnonNode || rectCount !== previousRectCount || rectCount != 1; + dict.previousRangeRectsAndTexts = rectsAndTexts; + + let window = this.getTopWindow(range.startContainer.ownerGlobal); + let document = window.document; + // First see if we need to and can remove the previous outline nodes. + if (rebuildOutline) { + this._removeRangeOutline(window); + } + + // Abort when there's no text to highlight OR when it's the exact same range + // as the previous call and isn't inside a dynamic container. + if ( + !rectsAndTexts.textList.length || + (!rebuildOutline && + dict.previousUpdatedRange == range && + !dict.dynamicRangesSet.has(range)) + ) { + return; + } + + let outlineBox; + if (rebuildOutline) { + // Create the main (yellow) highlight outline box. + outlineBox = document.createElementNS(kNSHTML, "div"); + outlineBox.setAttribute("id", kModalOutlineId); + } + + const kModalOutlineTextId = kModalOutlineId + "-text"; + let i = 0; + for (let rect of rectsAndTexts.rectList) { + let text = rectsAndTexts.textList[i]; + + // Next up is to check of the outline box' borders will not overlap with + // rects that we drew before or will draw after this one. + // We're taking the width of the border into account, which is + // `kOutlineBoxBorderSize` pixels. + // When left and/ or right sides will overlap with the current, previous + // or next rect, make sure to make the necessary adjustments to the style. + // These adjustments will override the styles as defined in `kModalStyles.outlineNode`. + let intersectingSides = new Set(); + let previous = rectsAndTexts.rectList[i - 1]; + if (previous && rect.left - previous.right <= 2 * kOutlineBoxBorderSize) { + intersectingSides.add("left"); + } + let next = rectsAndTexts.rectList[i + 1]; + if (next && next.left - rect.right <= 2 * kOutlineBoxBorderSize) { + intersectingSides.add("right"); + } + let borderStyles = [...intersectingSides].map(side => [ + "border-" + side, + 0, + ]); + if (intersectingSides.size) { + borderStyles.push([ + "margin", + `-${kOutlineBoxBorderSize}px 0 0 ${ + intersectingSides.has("left") ? 0 : -kOutlineBoxBorderSize + }px !important`, + ]); + borderStyles.push([ + "border-radius", + (intersectingSides.has("left") ? 0 : kOutlineBoxBorderRadius) + + "px " + + (intersectingSides.has("right") ? 0 : kOutlineBoxBorderRadius) + + "px " + + (intersectingSides.has("right") ? 0 : kOutlineBoxBorderRadius) + + "px " + + (intersectingSides.has("left") ? 0 : kOutlineBoxBorderRadius) + + "px", + ]); + } + + let outlineStyle = this._getStyleString( + kModalStyles.outlineNode, + [ + ["top", rect.top + "px"], + ["left", rect.left + "px"], + ["height", rect.height + "px"], + ["width", rect.width + "px"], + ], + borderStyles, + lazy.kDebug ? kModalStyles.outlineNodeDebug : [] + ); + fontStyle.lineHeight = rect.height + "px"; + let textStyle = + this._getStyleString(kModalStyles.outlineText) + + "; " + + this._getHTMLFontStyle(fontStyle); + + if (rebuildOutline) { + let textBoxParent = outlineBox.appendChild( + document.createElementNS(kNSHTML, "div") + ); + textBoxParent.setAttribute("id", kModalOutlineId + i); + textBoxParent.setAttribute("style", outlineStyle); + + let textBox = document.createElementNS(kNSHTML, "span"); + textBox.setAttribute("id", kModalOutlineTextId + i); + textBox.setAttribute("style", textStyle); + textBox.textContent = text; + textBoxParent.appendChild(textBox); + } else { + // Set the appropriate properties on the existing nodes, which will also + // activate the transitions. + outlineAnonNode.setAttributeForElement( + kModalOutlineId + i, + "style", + outlineStyle + ); + outlineAnonNode.setAttributeForElement( + kModalOutlineTextId + i, + "style", + textStyle + ); + outlineAnonNode.setTextContentForElement(kModalOutlineTextId + i, text); + } + + ++i; + } + + if (rebuildOutline) { + dict.modalHighlightOutline = lazy.kDebug + ? mockAnonymousContentNode( + (document.body || document.documentElement).appendChild(outlineBox) + ) + : document.insertAnonymousContent(outlineBox); + } + + if (dict.animateOutline && !this._isPageTooBig(dict)) { + let animation; + dict.animations = new Set(); + for (let i = rectsAndTexts.rectList.length - 1; i >= 0; --i) { + animation = dict.modalHighlightOutline.setAnimationForElement( + kModalOutlineId + i, + Cu.cloneInto(kModalOutlineAnim.keyframes, window), + kModalOutlineAnim.duration + ); + animation.onfinish = function () { + dict.animations.delete(this); + }; + dict.animations.add(animation); + } + } + dict.animateOutline = false; + dict.ignoreNextContentChange = true; + + dict.previousUpdatedRange = range; + }, + + /** + * Finish any currently playing animations on the found range outline node. + * + * @param {Object} dict Dictionary of properties belonging to the currently + * active window + */ + _finishOutlineAnimations(dict) { + if (!dict.animations) { + return; + } + for (let animation of dict.animations) { + animation.finish(); + } + }, + + /** + * Safely remove the outline AnoymousContent node from the CanvasFrame. + * + * @param {nsIDOMWindow} window + */ + _removeRangeOutline(window) { + let dict = this.getForWindow(window); + if (!dict.modalHighlightOutline) { + return; + } + + if (lazy.kDebug) { + dict.modalHighlightOutline.remove(); + } else { + try { + window.document.removeAnonymousContent(dict.modalHighlightOutline); + } catch (ex) {} + } + + dict.modalHighlightOutline = null; + }, + + /** + * Add a range to the list of ranges to highlight on, or cut out of, the dimmed + * background. + * + * @param {Range} range Range object that should be inspected + * @param {nsIDOMWindow} window Window object, whose DOM tree is being traversed + */ + _modalHighlight(range, controller, window) { + this._updateRangeRects(range); + + this.show(window); + // We don't repaint the mask right away, but pass it off to a render loop of + // sorts. + this._scheduleRepaintOfMask(window); + }, + + /** + * Lazily insert the nodes we need as anonymous content into the CanvasFrame + * of a window. + * + * @param {nsIDOMWindow} window Window to draw in. + */ + _maybeCreateModalHighlightNodes(window) { + window = this.getTopWindow(window); + let dict = this.getForWindow(window); + if (dict.modalHighlightOutline) { + if (!dict.modalHighlightAllMask) { + // Make sure to at least show the dimmed background. + this._repaintHighlightAllMask(window, false); + this._scheduleRepaintOfMask(window); + } else { + this._scheduleRepaintOfMask(window, { contentChanged: true }); + } + return; + } + + let document = window.document; + // A hidden document doesn't accept insertAnonymousContent calls yet. + if (document.hidden) { + let onVisibilityChange = () => { + document.removeEventListener("visibilitychange", onVisibilityChange); + this._maybeCreateModalHighlightNodes(window); + }; + document.addEventListener("visibilitychange", onVisibilityChange); + return; + } + + // Make sure to at least show the dimmed background. + this._repaintHighlightAllMask(window, false); + }, + + /** + * Build and draw the mask that takes care of the dimmed background that + * overlays the current page and the mask that cuts out all the rectangles of + * the ranges that were found. + * + * @param {nsIDOMWindow} window Window to draw in. + * @param {Boolean} [paintContent] + */ + _repaintHighlightAllMask(window, paintContent = true) { + window = this.getTopWindow(window); + let dict = this.getForWindow(window); + + const kMaskId = kModalIdPrefix + "-findbar-modalHighlight-outlineMask"; + if (!dict.modalHighlightAllMask) { + let document = window.document; + let maskNode = document.createElementNS(kNSHTML, "div"); + maskNode.setAttribute("id", kMaskId); + dict.modalHighlightAllMask = lazy.kDebug + ? mockAnonymousContentNode( + (document.body || document.documentElement).appendChild(maskNode) + ) + : document.insertAnonymousContent(maskNode); + } + + // Make sure the dimmed mask node takes the full width and height that's available. + let { width, height } = (dict.lastWindowDimensions = + this._getWindowDimensions(window)); + if (typeof dict.brightText != "boolean" || dict.updateAllRanges) { + this._detectBrightText(dict); + } + let maskStyle = this._getStyleString( + kModalStyles.maskNode, + [ + ["width", width + "px"], + ["height", height + "px"], + ], + dict.brightText ? kModalStyles.maskNodeBrightText : [], + paintContent ? kModalStyles.maskNodeTransition : [], + lazy.kDebug ? kModalStyles.maskNodeDebug : [] + ); + dict.modalHighlightAllMask.setAttributeForElement( + kMaskId, + "style", + maskStyle + ); + + this._updateRangeOutline(dict); + + let allRects = []; + // When the user's busy scrolling the document, don't bother cutting out rectangles, + // because they're not going to keep up with scrolling speed anyway. + if (!dict.busyScrolling && (paintContent || dict.modalHighlightAllMask)) { + // No need to update dynamic ranges separately when we already about to + // update all of them anyway. + if (!dict.updateAllRanges) { + this._updateDynamicRangesRects(dict); + } + + let DOMRect = window.DOMRect; + for (let [range, rectsAndTexts] of dict.modalHighlightRectsMap) { + if (!this.finder._fastFind.isRangeVisible(range, false)) { + continue; + } + + if (dict.updateAllRanges) { + rectsAndTexts = this._updateRangeRects(range); + } + + // If a geometry change was detected, we bail out right away here, because + // the current set of ranges has been invalidated. + if (dict.detectedGeometryChange) { + return; + } + + for (let rect of rectsAndTexts.rectList) { + allRects.push(new DOMRect(rect.x, rect.y, rect.width, rect.height)); + } + } + dict.updateAllRanges = false; + } + + // We may also want to cut out zero rects, which effectively clears out the mask. + dict.modalHighlightAllMask.setCutoutRectsForElement(kMaskId, allRects); + + // The reflow observer may ignore the reflow we cause ourselves here. + dict.ignoreNextContentChange = true; + }, + + /** + * Safely remove the mask AnoymousContent node from the CanvasFrame. + * + * @param {nsIDOMWindow} window + */ + _removeHighlightAllMask(window) { + window = this.getTopWindow(window); + let dict = this.getForWindow(window); + if (!dict.modalHighlightAllMask) { + return; + } + + // If the current window isn't the one the content was inserted into, this + // will fail, but that's fine. + if (lazy.kDebug) { + dict.modalHighlightAllMask.remove(); + } else { + try { + window.document.removeAnonymousContent(dict.modalHighlightAllMask); + } catch (ex) {} + } + dict.modalHighlightAllMask = null; + }, + + /** + * Check if the width or height of the current document is too big to handle + * for certain operations. This allows us to degrade gracefully when we expect + * the performance to be negatively impacted due to drawing-intensive operations. + * + * @param {Object} dict Dictionary of properties belonging to the currently + * active window + * @return {Boolean} + */ + _isPageTooBig(dict) { + let { height, width } = dict.lastWindowDimensions; + return height >= kPageIsTooBigPx || width >= kPageIsTooBigPx; + }, + + /** + * Doing a full repaint each time a range is delivered by the highlight iterator + * is way too costly, thus we pipe the frequency down to every + * `kModalHighlightRepaintLoFreqMs` milliseconds. If there are dynamic ranges + * found (see `_isInDynamicContainer()` for the definition), the frequency + * will be upscaled to `kModalHighlightRepaintHiFreqMs`. + * + * @param {nsIDOMWindow} window + * @param {Object} options Dictionary of painter hints that contains the + * following properties: + * {Boolean} contentChanged Whether the documents' content changed in the + * meantime. This happens when the DOM is updated + * whilst the page is loaded. + * {Boolean} scrollOnly TRUE when the page has scrolled in the meantime, + * which means that the dynamically positioned + * elements need to be repainted. + * {Boolean} updateAllRanges Whether to recalculate the rects of all ranges + * that were found up until now. + */ + _scheduleRepaintOfMask( + window, + { contentChanged = false, scrollOnly = false, updateAllRanges = false } = {} + ) { + if (!this.useModal()) { + return; + } + + window = this.getTopWindow(window); + let dict = this.getForWindow(window); + // Bail out early if the repaint scheduler is paused or when we're supposed + // to ignore the next paint (i.e. content change). + if ( + dict.repaintSchedulerState == kRepaintSchedulerPaused || + (contentChanged && dict.ignoreNextContentChange) + ) { + dict.ignoreNextContentChange = false; + return; + } + + let hasDynamicRanges = !!dict.dynamicRangesSet.size; + let pageIsTooBig = this._isPageTooBig(dict); + let repaintDynamicRanges = + (scrollOnly || contentChanged) && hasDynamicRanges && !pageIsTooBig; + + // Determine scroll behavior and keep that state around. + let startedScrolling = !dict.busyScrolling && scrollOnly; + // When the user started scrolling the document, hide the other highlights. + if (startedScrolling) { + dict.busyScrolling = startedScrolling; + this._repaintHighlightAllMask(window); + } + // Whilst scrolling, suspend the repaint scheduler, but only when the page is + // too big or the find results contains ranges that are inside dynamic + // containers. + if (dict.busyScrolling && (pageIsTooBig || hasDynamicRanges)) { + dict.ignoreNextContentChange = true; + this._updateRangeOutline(dict); + // NB: we're not using `kRepaintSchedulerPaused` on purpose here, otherwise + // we'd break the `busyScrolling` detection (re-)using the timer. + if (dict.modalRepaintScheduler) { + window.clearTimeout(dict.modalRepaintScheduler); + dict.modalRepaintScheduler = null; + } + } + + // When we request to repaint unconditionally, we mean to call + // `_repaintHighlightAllMask()` right after the timeout. + if (!dict.unconditionalRepaintRequested) { + dict.unconditionalRepaintRequested = + !contentChanged || repaintDynamicRanges; + } + // Some events, like a resize, call for recalculation of all the rects of all ranges. + if (!dict.updateAllRanges) { + dict.updateAllRanges = updateAllRanges; + } + + if (dict.modalRepaintScheduler) { + return; + } + + let timeoutMs = + hasDynamicRanges && !dict.busyScrolling + ? kModalHighlightRepaintHiFreqMs + : kModalHighlightRepaintLoFreqMs; + dict.modalRepaintScheduler = window.setTimeout(() => { + dict.modalRepaintScheduler = null; + dict.repaintSchedulerState = kRepaintSchedulerStopped; + dict.busyScrolling = false; + + let pageContentChanged = dict.detectedGeometryChange; + if (!pageContentChanged && !pageIsTooBig) { + let { width: previousWidth, height: previousHeight } = + dict.lastWindowDimensions; + let { width, height } = (dict.lastWindowDimensions = + this._getWindowDimensions(window)); + pageContentChanged = + dict.detectedGeometryChange || + Math.abs(previousWidth - width) > kContentChangeThresholdPx || + Math.abs(previousHeight - height) > kContentChangeThresholdPx; + } + dict.detectedGeometryChange = false; + // When the page has changed significantly enough in size, we'll restart + // the iterator with the same parameters as before to find us new ranges. + if (pageContentChanged && !pageIsTooBig) { + this.iterator.restart(this.finder); + } + + if ( + dict.unconditionalRepaintRequested || + (dict.modalHighlightRectsMap.size && pageContentChanged) + ) { + dict.unconditionalRepaintRequested = false; + this._repaintHighlightAllMask(window); + } + }, timeoutMs); + dict.repaintSchedulerState = kRepaintSchedulerRunning; + }, + + /** + * Add event listeners to the content which will cause the modal highlight + * AnonymousContent to be re-painted or hidden. + * + * @param {nsIDOMWindow} window + */ + _addModalHighlightListeners(window) { + window = this.getTopWindow(window); + let dict = this.getForWindow(window); + if (dict.highlightListeners) { + return; + } + + dict.highlightListeners = [ + this._scheduleRepaintOfMask.bind(this, window, { contentChanged: true }), + this._scheduleRepaintOfMask.bind(this, window, { updateAllRanges: true }), + this._scheduleRepaintOfMask.bind(this, window, { scrollOnly: true }), + this.hide.bind(this, window, null), + () => (dict.busySelecting = true), + () => { + if (window.document.hidden) { + dict.repaintSchedulerState = kRepaintSchedulerPaused; + } else if (dict.repaintSchedulerState == kRepaintSchedulerPaused) { + dict.repaintSchedulerState = kRepaintSchedulerRunning; + this._scheduleRepaintOfMask(window); + } + }, + ]; + let target = this.iterator._getDocShell(window).chromeEventHandler; + target.addEventListener("MozAfterPaint", dict.highlightListeners[0]); + target.addEventListener("resize", dict.highlightListeners[1]); + target.addEventListener("scroll", dict.highlightListeners[2], { + capture: true, + passive: true, + }); + target.addEventListener("click", dict.highlightListeners[3]); + target.addEventListener("selectstart", dict.highlightListeners[4]); + window.document.addEventListener( + "visibilitychange", + dict.highlightListeners[5] + ); + }, + + /** + * Remove event listeners from content. + * + * @param {nsIDOMWindow} window + */ + _removeModalHighlightListeners(window) { + window = this.getTopWindow(window); + let dict = this.getForWindow(window); + if (!dict.highlightListeners) { + return; + } + + let target = this.iterator._getDocShell(window).chromeEventHandler; + target.removeEventListener("MozAfterPaint", dict.highlightListeners[0]); + target.removeEventListener("resize", dict.highlightListeners[1]); + target.removeEventListener("scroll", dict.highlightListeners[2], { + capture: true, + passive: true, + }); + target.removeEventListener("click", dict.highlightListeners[3]); + target.removeEventListener("selectstart", dict.highlightListeners[4]); + window.document.removeEventListener( + "visibilitychange", + dict.highlightListeners[5] + ); + + dict.highlightListeners = null; + }, + + /** + * For a given node returns its editable parent or null if there is none. + * It's enough to check if node is a text node and its parent's parent is + * an input or textarea. + * + * @param node the node we want to check + * @returns the first node in the parent chain that is editable, + * null if there is no such node + */ + _getEditableNode(node) { + if ( + node.nodeType === node.TEXT_NODE && + node.parentNode && + node.parentNode.parentNode && + (ChromeUtils.getClassName(node.parentNode.parentNode) === + "HTMLInputElement" || + ChromeUtils.getClassName(node.parentNode.parentNode) === + "HTMLTextAreaElement") + ) { + return node.parentNode.parentNode; + } + return null; + }, + + /** + * Add ourselves as an nsIEditActionListener and nsIDocumentStateListener for + * a given editor + * + * @param editor the editor we'd like to listen to + */ + _addEditorListeners(editor) { + if (!this._editors) { + this._editors = []; + this._stateListeners = []; + } + + let existingIndex = this._editors.indexOf(editor); + if (existingIndex == -1) { + let x = this._editors.length; + this._editors[x] = editor; + this._stateListeners[x] = this._createStateListener(); + this._editors[x].addEditActionListener(this); + this._editors[x].addDocumentStateListener(this._stateListeners[x]); + } + }, + + /** + * Helper method to unhook listeners, remove cached editors + * and keep the relevant arrays in sync + * + * @param idx the index into the array of editors/state listeners + * we wish to remove + */ + _unhookListenersAtIndex(idx) { + this._editors[idx].removeEditActionListener(this); + this._editors[idx].removeDocumentStateListener(this._stateListeners[idx]); + this._editors.splice(idx, 1); + this._stateListeners.splice(idx, 1); + if (!this._editors.length) { + delete this._editors; + delete this._stateListeners; + } + }, + + /** + * Remove ourselves as an nsIEditActionListener and + * nsIDocumentStateListener from a given cached editor + * + * @param editor the editor we no longer wish to listen to + */ + _removeEditorListeners(editor) { + // editor is an editor that we listen to, so therefore must be + // cached. Find the index of this editor + let idx = this._editors.indexOf(editor); + if (idx == -1) { + return; + } + // Now unhook ourselves, and remove our cached copy + this._unhookListenersAtIndex(idx); + }, + + /* + * nsIEditActionListener logic follows + * + * We implement this interface to allow us to catch the case where + * the findbar found a match in a HTML <input> or <textarea>. If the + * user adjusts the text in some way, it will no longer match, so we + * want to remove the highlight, rather than have it expand/contract + * when letters are added or removed. + */ + + /** + * Helper method used to check whether a selection intersects with + * some highlighting + * + * @param selectionRange the range from the selection to check + * @param findRange the highlighted range to check against + * @returns true if they intersect, false otherwise + */ + _checkOverlap(selectionRange, findRange) { + if (!selectionRange || !findRange) { + return false; + } + // The ranges overlap if one of the following is true: + // 1) At least one of the endpoints of the deleted selection + // is in the find selection + // 2) At least one of the endpoints of the find selection + // is in the deleted selection + if ( + findRange.isPointInRange( + selectionRange.startContainer, + selectionRange.startOffset + ) + ) { + return true; + } + if ( + findRange.isPointInRange( + selectionRange.endContainer, + selectionRange.endOffset + ) + ) { + return true; + } + if ( + selectionRange.isPointInRange( + findRange.startContainer, + findRange.startOffset + ) + ) { + return true; + } + if ( + selectionRange.isPointInRange(findRange.endContainer, findRange.endOffset) + ) { + return true; + } + + return false; + }, + + /** + * Helper method to determine if an edit occurred within a highlight + * + * @param selection the selection we wish to check + * @param node the node we want to check is contained in selection + * @param offset the offset into node that we want to check + * @returns the range containing (node, offset) or null if no ranges + * in the selection contain it + */ + _findRange(selection, node, offset) { + let rangeCount = selection.rangeCount; + let rangeidx = 0; + let foundContainingRange = false; + let range = null; + + // Check to see if this node is inside one of the selection's ranges + while (!foundContainingRange && rangeidx < rangeCount) { + range = selection.getRangeAt(rangeidx); + if (range.isPointInRange(node, offset)) { + foundContainingRange = true; + break; + } + rangeidx++; + } + + if (foundContainingRange) { + return range; + } + + return null; + }, + + // Start of nsIEditActionListener implementations + + WillDeleteText(textNode, offset, length) { + let editor = this._getEditableNode(textNode).editor; + let controller = editor.selectionController; + let fSelection = controller.getSelection( + Ci.nsISelectionController.SELECTION_FIND + ); + let range = this._findRange(fSelection, textNode, offset); + + if (range) { + // Don't remove the highlighting if the deleted text is at the + // end of the range + if (textNode != range.endContainer || offset != range.endOffset) { + // Text within the highlight is being removed - the text can + // no longer be a match, so remove the highlighting + fSelection.removeRange(range); + if (fSelection.rangeCount == 0) { + this._removeEditorListeners(editor); + } + } + } + }, + + DidInsertText(textNode, offset, aString) { + let editor = this._getEditableNode(textNode).editor; + let controller = editor.selectionController; + let fSelection = controller.getSelection( + Ci.nsISelectionController.SELECTION_FIND + ); + let range = this._findRange(fSelection, textNode, offset); + + if (range) { + // If the text was inserted before the highlight + // adjust the highlight's bounds accordingly + if (textNode == range.startContainer && offset == range.startOffset) { + range.setStart( + range.startContainer, + range.startOffset + aString.length + ); + } else if (textNode != range.endContainer || offset != range.endOffset) { + // The edit occurred within the highlight - any addition of text + // will result in the text no longer being a match, + // so remove the highlighting + fSelection.removeRange(range); + if (fSelection.rangeCount == 0) { + this._removeEditorListeners(editor); + } + } + } + }, + + WillDeleteRanges(rangesToDelete) { + let { editor } = this._getEditableNode(rangesToDelete[0].startContainer); + let controller = editor.selectionController; + let fSelection = controller.getSelection( + Ci.nsISelectionController.SELECTION_FIND + ); + + let shouldDelete = {}; + let numberOfDeletedSelections = 0; + let numberOfMatches = fSelection.rangeCount; + + // We need to test if any ranges to be deleted + // are in any of the ranges of the find selection + // Usually both selections will only contain one range, however + // either may contain more than one. + + for (let fIndex = 0; fIndex < numberOfMatches; fIndex++) { + shouldDelete[fIndex] = false; + let fRange = fSelection.getRangeAt(fIndex); + + for (let selRange of rangesToDelete) { + if (shouldDelete[fIndex]) { + continue; + } + + let doesOverlap = this._checkOverlap(selRange, fRange); + if (doesOverlap) { + shouldDelete[fIndex] = true; + numberOfDeletedSelections++; + } + } + } + + // OK, so now we know what matches (if any) are in the selection + // that is being deleted. Time to remove them. + if (!numberOfDeletedSelections) { + return; + } + + for (let i = numberOfMatches - 1; i >= 0; i--) { + if (shouldDelete[i]) { + fSelection.removeRange(fSelection.getRangeAt(i)); + } + } + + // Remove listeners if no more highlights left + if (!fSelection.rangeCount) { + this._removeEditorListeners(editor); + } + }, + + /* + * nsIDocumentStateListener logic follows + * + * When attaching nsIEditActionListeners, there are no guarantees + * as to whether the findbar or the documents in the browser will get + * destructed first. This leads to the potential to either leak, or to + * hold on to a reference an editable element's editor for too long, + * preventing it from being destructed. + * + * However, when an editor's owning node is being destroyed, the editor + * sends out a DocumentWillBeDestroyed notification. We can use this to + * clean up our references to the object, to allow it to be destroyed in a + * timely fashion. + */ + + /** + * Unhook ourselves when one of our state listeners has been called. + * This can happen in 4 cases: + * 1) The document the editor belongs to is navigated away from, and + * the document is not being cached + * + * 2) The document the editor belongs to is expired from the cache + * + * 3) The tab containing the owning document is closed + * + * 4) The <input> or <textarea> that owns the editor is explicitly + * removed from the DOM + * + * @param the listener that was invoked + */ + _onEditorDestruction(aListener) { + // First find the index of the editor the given listener listens to. + // The listeners and editors arrays must always be in sync. + // The listener will be in our array of cached listeners, as this + // method could not have been called otherwise. + let idx = 0; + while (this._stateListeners[idx] != aListener) { + idx++; + } + + // Unhook both listeners + this._unhookListenersAtIndex(idx); + }, + + /** + * Creates a unique document state listener for an editor. + * + * It is not possible to simply have the findbar implement the + * listener interface itself, as it wouldn't have sufficient information + * to work out which editor was being destroyed. Therefore, we create new + * listeners on the fly, and cache them in sync with the editors they + * listen to. + */ + _createStateListener() { + return { + findbar: this, + + QueryInterface: ChromeUtils.generateQI(["nsIDocumentStateListener"]), + + NotifyDocumentWillBeDestroyed() { + this.findbar._onEditorDestruction(this); + }, + + // Unimplemented + notifyDocumentStateChanged(aDirty) {}, + }; + }, +}; diff --git a/toolkit/modules/FinderIterator.sys.mjs b/toolkit/modules/FinderIterator.sys.mjs new file mode 100644 index 0000000000..f09873bed8 --- /dev/null +++ b/toolkit/modules/FinderIterator.sys.mjs @@ -0,0 +1,770 @@ +/* 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 { clearTimeout, setTimeout } from "resource://gre/modules/Timer.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + NLP: "resource://gre/modules/NLP.sys.mjs", + Rect: "resource://gre/modules/Geometry.sys.mjs", +}); + +const kDebug = false; +const kIterationSizeMax = 100; +const kTimeoutPref = "findbar.iteratorTimeout"; + +/** + * FinderIterator. See the documentation for the `start()` method to + * learn more. + */ +export class FinderIterator { + constructor() { + this._listeners = new Map(); + this._currentParams = null; + this._catchingUp = new Set(); + this._previousParams = null; + this._previousRanges = []; + this._spawnId = 0; + this._timer = null; + this.ranges = []; + this.running = false; + this.useSubFrames = false; + } + + _timeout = Services.prefs.getIntPref(kTimeoutPref); + + // Expose `kIterationSizeMax` to the outside world for unit tests to use. + get kIterationSizeMax() { + return kIterationSizeMax; + } + + get params() { + if (!this._currentParams && !this._previousParams) { + return null; + } + return Object.assign({}, this._currentParams || this._previousParams); + } + + /** + * Start iterating the active Finder docShell, using the options below. When + * it already started at the request of another consumer, we first yield the + * results we already collected before continuing onward to yield fresh results. + * We make sure to pause every `kIterationSizeMax` iterations to make sure we + * don't block the host process too long. In the case of a break like this, we + * yield `undefined`, instead of a range. + * Upon re-entrance after a break, we check if `stop()` was called during the + * break and if so, we stop iterating. + * Results are also passed to the `listener.onIteratorRangeFound` callback + * method, along with a flag that specifies if the result comes from the cache + * or is fresh. The callback also adheres to the `limit` flag. + * The returned promise is resolved when 1) the limit is reached, 2) when all + * the ranges have been found or 3) when `stop()` is called whilst iterating. + * + * @param {Number} [options.allowDistance] Allowed edit distance between the + * current word and `options.word` + * when the iterator is already running + * @param {Boolean} options.caseSensitive Whether to search in case sensitive + * mode + * @param {Boolean} options.entireWord Whether to search in entire-word mode + * @param {Finder} options.finder Currently active Finder instance + * @param {Number} [options.limit] Limit the amount of results to be + * passed back. Optional, defaults to no + * limit. + * @param {Boolean} [options.linksOnly] Only yield ranges that are inside a + * hyperlink (used by QuickFind). + * Optional, defaults to `false`. + * @param {Object} options.listener Listener object that implements the + * following callback functions: + * - onIteratorRangeFound({Range} range); + * - onIteratorReset(); + * - onIteratorRestart({Object} iterParams); + * - onIteratorStart({Object} iterParams); + * @param {Boolean} options.matchDiacritics Whether to search in + * diacritic-matching mode + * @param {Boolean} [options.useCache] Whether to allow results already + * present in the cache or demand fresh. + * Optional, defaults to `false`. + * @param {Boolean} [options.useSubFrames] Whether to iterate over subframes. + * Optional, defaults to `false`. + * @param {String} options.word Word to search for + * @return {Promise} + */ + start({ + allowDistance, + caseSensitive, + entireWord, + finder, + limit, + linksOnly, + listener, + matchDiacritics, + useCache, + word, + useSubFrames, + }) { + // Take care of default values for non-required options. + if (typeof allowDistance != "number") { + allowDistance = 0; + } + if (typeof limit != "number") { + limit = -1; + } + if (typeof linksOnly != "boolean") { + linksOnly = false; + } + if (typeof useCache != "boolean") { + useCache = false; + } + if (typeof useSubFrames != "boolean") { + useSubFrames = false; + } + + // Validate the options. + if (typeof caseSensitive != "boolean") { + throw new Error("Missing required option 'caseSensitive'"); + } + if (typeof entireWord != "boolean") { + throw new Error("Missing required option 'entireWord'"); + } + if (typeof matchDiacritics != "boolean") { + throw new Error("Missing required option 'matchDiacritics'"); + } + if (!finder) { + throw new Error("Missing required option 'finder'"); + } + if (!word) { + throw new Error("Missing required option 'word'"); + } + if (typeof listener != "object" || !listener.onIteratorRangeFound) { + throw new TypeError("Missing valid, required option 'listener'"); + } + + // If the listener was added before, make sure the promise is resolved before + // we replace it with another. + if (this._listeners.has(listener)) { + let { onEnd } = this._listeners.get(listener); + if (onEnd) { + onEnd(); + } + } + + let window = finder._getWindow(); + let resolver; + let promise = new Promise(resolve => (resolver = resolve)); + let iterParams = { + caseSensitive, + entireWord, + linksOnly, + matchDiacritics, + useCache, + window, + word, + useSubFrames, + }; + + this._listeners.set(listener, { limit, onEnd: resolver }); + + // If we're not running anymore and we're requesting the previous result, use it. + if (!this.running && this._previousResultAvailable(iterParams)) { + this._yieldPreviousResult(listener, window); + return promise; + } + + if (this.running) { + // Double-check if we're not running the iterator with a different set of + // parameters, otherwise report an error with the most common reason. + if ( + !this._areParamsEqual(this._currentParams, iterParams, allowDistance) + ) { + if (kDebug) { + console.error( + `We're currently iterating over '${this._currentParams.word}', not '${word}'\n` + + new Error().stack + ); + } + this._listeners.delete(listener); + resolver(); + return promise; + } + + // if we're still running, yield the set we have built up this far. + this._yieldIntermediateResult(listener, window); + + return promise; + } + + // Start! + this.running = true; + this._currentParams = iterParams; + this._findAllRanges(finder, ++this._spawnId); + + return promise; + } + + /** + * Stop the currently running iterator as soon as possible and optionally cache + * the result for later. + * + * @param {Boolean} [cachePrevious] Whether to save the result for later. + * Optional. + */ + stop(cachePrevious = false) { + if (!this.running) { + return; + } + + if (this._timer) { + clearTimeout(this._timer); + this._timer = null; + } + if (this._runningFindResolver) { + this._runningFindResolver(); + this._runningFindResolver = null; + } + + if (cachePrevious) { + this._previousRanges = [].concat(this.ranges); + this._previousParams = Object.assign({}, this._currentParams); + } else { + this._previousRanges = []; + this._previousParams = null; + } + + this._catchingUp.clear(); + this._currentParams = null; + this.ranges = []; + this.running = false; + + for (let [, { onEnd }] of this._listeners) { + onEnd(); + } + } + + /** + * Stops the iteration that currently running, if it is, and start a new one + * with the exact same params as before. + * + * @param {Finder} finder Currently active Finder instance + */ + restart(finder) { + // Capture current iterator params before we stop the show. + let iterParams = this.params; + if (!iterParams) { + return; + } + this.stop(); + + // Restart manually. + this.running = true; + this._currentParams = iterParams; + + this._findAllRanges(finder, ++this._spawnId); + this._notifyListeners("restart", iterParams); + } + + /** + * Reset the internal state of the iterator. Typically this would be called + * when the docShell is not active anymore, which makes the current and cached + * previous result invalid. + * If the iterator is running, it will be stopped as soon as possible. + */ + reset() { + if (this._timer) { + clearTimeout(this._timer); + this._timer = null; + } + if (this._runningFindResolver) { + this._runningFindResolver(); + this._runningFindResolver = null; + } + + this._catchingUp.clear(); + this._currentParams = this._previousParams = null; + this._previousRanges = []; + this.ranges = []; + this.running = false; + + this._notifyListeners("reset"); + for (let [, { onEnd }] of this._listeners) { + onEnd(); + } + this._listeners.clear(); + } + + /** + * Check if the currently running iterator parameters are the same as the ones + * passed through the arguments. When `true`, we can keep it running as-is and + * the consumer should stop the iterator when `false`. + * + * @param {Boolean} options.caseSensitive Whether to search in case sensitive + * mode + * @param {Boolean} options.entireWord Whether to search in entire-word mode + * @param {Boolean} options.linksOnly Whether to search for the word to be + * present in links only + * @param {Boolean} options.matchDiacritics Whether to search in + * diacritic-matching mode + * @param {String} options.word The word being searched for + * @param (Boolean) options.useSubFrames Whether to search subframes + * @return {Boolean} + */ + continueRunning({ + caseSensitive, + entireWord, + linksOnly, + matchDiacritics, + word, + useSubFrames, + }) { + return ( + this.running && + this._currentParams.caseSensitive === caseSensitive && + this._currentParams.entireWord === entireWord && + this._currentParams.linksOnly === linksOnly && + this._currentParams.matchDiacritics === matchDiacritics && + this._currentParams.word == word && + this._currentParams.useSubFrames == useSubFrames + ); + } + + /** + * The default mode of operation of the iterator is to not accept duplicate + * listeners, resolve the promise of the older listeners and replace it with + * the new listener. + * Consumers may opt-out of this behavior by using this check and not call + * start(). + * + * @param {Object} paramSet Property bag with the same signature as you would + * pass into `start()` + * @return {Boolean} + */ + isAlreadyRunning(paramSet) { + return ( + this.running && + this._areParamsEqual(this._currentParams, paramSet) && + this._listeners.has(paramSet.listener) + ); + } + + /** + * Safely notify all registered listeners that an event has occurred. + * + * @param {String} callback Name of the callback to invoke + * @param {mixed} [params] Optional argument that will be passed to the + * callback + * @param {Iterable} [listeners] Set of listeners to notify. Optional, defaults + * to `this._listeners.keys()`. + */ + _notifyListeners(callback, params, listeners = this._listeners.keys()) { + callback = + "onIterator" + callback.charAt(0).toUpperCase() + callback.substr(1); + for (let listener of listeners) { + try { + listener[callback](params); + } catch (ex) { + console.error("FinderIterator Error: ", ex); + } + } + } + + /** + * Internal; check if an iteration request is available in the previous result + * that we cached. + * + * @param {Boolean} options.caseSensitive Whether to search in case sensitive + * mode + * @param {Boolean} options.entireWord Whether to search in entire-word mode + * @param {Boolean} options.linksOnly Whether to search for the word to be + * present in links only + * @param {Boolean} options.matchDiacritics Whether to search in + * diacritic-matching mode + * @param {Boolean} options.useCache Whether the consumer wants to use the + * cached previous result at all + * @param {String} options.word The word being searched for + * @return {Boolean} + */ + _previousResultAvailable({ + caseSensitive, + entireWord, + linksOnly, + matchDiacritics, + useCache, + word, + }) { + return !!( + useCache && + this._areParamsEqual(this._previousParams, { + caseSensitive, + entireWord, + linksOnly, + matchDiacritics, + word, + }) && + this._previousRanges.length + ); + } + + /** + * Internal; compare if two sets of iterator parameters are equivalent. + * + * @param {Object} paramSet1 First set of params (left hand side) + * @param {Object} paramSet2 Second set of params (right hand side) + * @param {Number} [allowDistance] Allowed edit distance between the two words. + * Optional, defaults to '0', which means 'no + * distance'. + * @return {Boolean} + */ + _areParamsEqual(paramSet1, paramSet2, allowDistance = 0) { + return ( + !!paramSet1 && + !!paramSet2 && + paramSet1.caseSensitive === paramSet2.caseSensitive && + paramSet1.entireWord === paramSet2.entireWord && + paramSet1.linksOnly === paramSet2.linksOnly && + paramSet1.matchDiacritics === paramSet2.matchDiacritics && + paramSet1.window === paramSet2.window && + paramSet1.useSubFrames === paramSet2.useSubFrames && + lazy.NLP.levenshtein(paramSet1.word, paramSet2.word) <= allowDistance + ); + } + + /** + * Internal; iterate over a predefined set of ranges that have been collected + * before. + * Also here, we make sure to pause every `kIterationSizeMax` iterations to + * make sure we don't block the host process too long. In the case of a break + * like this, we yield `undefined`, instead of a range. + * + * @param {Object} listener Listener object + * @param {Array} rangeSource Set of ranges to iterate over + * @param {nsIDOMWindow} window The window object is only really used + * for access to `setTimeout` + * @param {Boolean} [withPause] Whether to pause after each `kIterationSizeMax` + * number of ranges yielded. Optional, defaults + * to `true`. + * @yield {Range} + */ + async _yieldResult(listener, rangeSource, window, withPause = true) { + // We keep track of the number of iterations to allow a short pause between + // every `kIterationSizeMax` number of iterations. + let iterCount = 0; + let { limit, onEnd } = this._listeners.get(listener); + let ranges = rangeSource.slice(0, limit > -1 ? limit : undefined); + for (let range of ranges) { + try { + range.startContainer; + } catch (ex) { + // Don't yield dead objects, so use the escape hatch. + if (ex.message.includes("dead object")) { + return; + } + } + + // Pass a flag that is `true` when we're returning the result from a + // cached previous iteration. + listener.onIteratorRangeFound(range, !this.running); + await range; + + if (withPause && ++iterCount >= kIterationSizeMax) { + iterCount = 0; + // Make sure to save the current limit for later. + this._listeners.set(listener, { limit, onEnd }); + // Sleep for the rest of this cycle. + await new Promise(resolve => window.setTimeout(resolve, 0)); + // After a sleep, the set of ranges may have updated. + ranges = rangeSource.slice(0, limit > -1 ? limit : undefined); + } + + if (limit !== -1 && --limit === 0) { + // We've reached our limit; no need to do more work. + this._listeners.delete(listener); + onEnd(); + return; + } + } + + // Save the updated limit globally. + this._listeners.set(listener, { limit, onEnd }); + } + + /** + * Internal; iterate over the set of previously found ranges. Meanwhile it'll + * mark the listener as 'catching up', meaning it will not receive fresh + * results from a running iterator. + * + * @param {Object} listener Listener object + * @param {nsIDOMWindow} window The window object is only really used + * for access to `setTimeout` + * @yield {Range} + */ + async _yieldPreviousResult(listener, window) { + this._notifyListeners("start", this.params, [listener]); + this._catchingUp.add(listener); + await this._yieldResult(listener, this._previousRanges, window); + this._catchingUp.delete(listener); + let { onEnd } = this._listeners.get(listener); + if (onEnd) { + onEnd(); + } + } + + /** + * Internal; iterate over the set of already found ranges. Meanwhile it'll + * mark the listener as 'catching up', meaning it will not receive fresh + * results from the running iterator. + * + * @param {Object} listener Listener object + * @param {nsIDOMWindow} window The window object is only really used + * for access to `setTimeout` + * @yield {Range} + */ + async _yieldIntermediateResult(listener, window) { + this._notifyListeners("start", this.params, [listener]); + this._catchingUp.add(listener); + await this._yieldResult(listener, this.ranges, window, false); + this._catchingUp.delete(listener); + } + + /** + * Internal; see the documentation of the start() method above. + * + * @param {Finder} finder Currently active Finder instance + * @param {Number} spawnId Since `stop()` is synchronous and this method + * is not, this identifier is used to learn if + * it's supposed to still continue after a pause. + * @yield {Range} + */ + async _findAllRanges(finder, spawnId) { + if (this._timeout) { + if (this._timer) { + clearTimeout(this._timer); + } + if (this._runningFindResolver) { + this._runningFindResolver(); + } + + let timeout = this._timeout; + let searchTerm = this._currentParams.word; + // Wait a little longer when the first or second character is typed into + // the findbar. + if (searchTerm.length == 1) { + timeout *= 4; + } else if (searchTerm.length == 2) { + timeout *= 2; + } + await new Promise(resolve => { + this._runningFindResolver = resolve; + this._timer = setTimeout(resolve, timeout); + }); + this._timer = this._runningFindResolver = null; + // During the timeout, we could have gotten the signal to stop iterating. + // Make sure we do here. + if (!this.running || spawnId !== this._spawnId) { + return; + } + } + + this._notifyListeners("start", this.params); + + let { linksOnly, useSubFrames, window } = this._currentParams; + // First we collect all frames we need to search through, whilst making sure + // that the parent window gets dibs. + let frames = [window]; + if (useSubFrames) { + frames.push(...this._collectFrames(window, finder)); + } + let iterCount = 0; + for (let frame of frames) { + for (let range of this._iterateDocument(this._currentParams, frame)) { + // Between iterations, for example after a sleep of one cycle, we could + // have gotten the signal to stop iterating. Make sure we do here. + if (!this.running || spawnId !== this._spawnId) { + return; + } + + // Deal with links-only mode here. + if (linksOnly && !this._rangeStartsInLink(range)) { + continue; + } + + this.ranges.push(range); + + // Call each listener with the range we just found. + for (let [listener, { limit, onEnd }] of this._listeners) { + if (this._catchingUp.has(listener)) { + continue; + } + + listener.onIteratorRangeFound(range); + + if (limit !== -1 && --limit === 0) { + // We've reached our limit; no need to do more work for this listener. + this._listeners.delete(listener); + onEnd(); + continue; + } + + // Save the updated limit globally. + this._listeners.set(listener, { limit, onEnd }); + } + + await range; + + if (++iterCount >= kIterationSizeMax) { + iterCount = 0; + // Sleep for the rest of this cycle. + await new Promise(resolve => window.setTimeout(resolve, 0)); + } + } + } + + // When the iterating has finished, make sure we reset and save the state + // properly. + this.stop(true); + } + + /** + * Internal; basic wrapper around nsIFind that provides a generator yielding + * a range each time an occurence of `word` string is found. + * + * @param {Boolean} options.caseSensitive Whether to search in case + * sensitive mode + * @param {Boolean} options.entireWord Whether to search in entire-word + * mode + * @param {Boolean} options.matchDiacritics Whether to search in + * diacritic-matching mode + * @param {String} options.word The word to search for + * @param {nsIDOMWindow} window The window to search in + * @yield {Range} + */ + *_iterateDocument( + { caseSensitive, entireWord, matchDiacritics, word }, + window + ) { + let doc = window.document; + let body = doc.body || doc.documentElement; + + if (!body) { + return; + } + + let searchRange = doc.createRange(); + searchRange.selectNodeContents(body); + + let startPt = searchRange.cloneRange(); + startPt.collapse(true); + + let endPt = searchRange.cloneRange(); + endPt.collapse(false); + + let retRange = null; + + let nsIFind = Cc["@mozilla.org/embedcomp/rangefind;1"] + .createInstance() + .QueryInterface(Ci.nsIFind); + nsIFind.caseSensitive = caseSensitive; + nsIFind.entireWord = entireWord; + nsIFind.matchDiacritics = matchDiacritics; + + while ((retRange = nsIFind.Find(word, searchRange, startPt, endPt))) { + yield retRange; + startPt = retRange.cloneRange(); + startPt.collapse(false); + } + } + + /** + * Internal; helper method for the iterator that recursively collects all + * visible (i)frames inside a window. + * + * @param {nsIDOMWindow} window The window to extract the (i)frames from + * @param {Finder} finder The Finder instance + * @return {Array} Stack of frames to iterate over + */ + _collectFrames(window, finder) { + let frames = []; + if (!("frames" in window) || !window.frames.length) { + return frames; + } + + // Casting `window.frames` to an Iterator doesn't work, so we're stuck with + // a plain, old for-loop. + let dwu = window.windowUtils; + for (let i = 0, l = window.frames.length; i < l; ++i) { + let frame = window.frames[i]; + // Don't count matches in hidden frames; get the frame element rect and + // check if it's empty. We shan't flush! + let frameEl = frame && frame.frameElement; + if ( + !frameEl || + lazy.Rect.fromRect(dwu.getBoundsWithoutFlushing(frameEl)).isEmpty() + ) { + continue; + } + // All conditions pass, so push the current frame and its children on the + // stack. + frames.push(frame, ...this._collectFrames(frame, finder)); + } + + return frames; + } + + /** + * Internal; helper method to extract the docShell reference from a Window or + * Range object. + * + * @param {Range} windowOrRange Window object to query. May also be a + * Range, from which the owner window will + * be queried. + * @return {nsIDocShell} + */ + _getDocShell(windowOrRange) { + let window = windowOrRange; + // Ranges may also be passed in, so fetch its window. + if (ChromeUtils.getClassName(windowOrRange) === "Range") { + window = windowOrRange.startContainer.ownerGlobal; + } + return window.docShell; + } + + /** + * Internal; determines whether a range is inside a link. + * + * @param {Range} range the range to check + * @return {Boolean} True if the range starts in a link + */ + _rangeStartsInLink(range) { + let isInsideLink = false; + let node = range.startContainer; + + if (node.nodeType == node.ELEMENT_NODE) { + if (node.hasChildNodes) { + let childNode = node.item(range.startOffset); + if (childNode) { + node = childNode; + } + } + } + + const XLink_NS = "http://www.w3.org/1999/xlink"; + const HTMLAnchorElement = (node.ownerDocument || node).defaultView + .HTMLAnchorElement; + do { + if (HTMLAnchorElement.isInstance(node)) { + isInsideLink = node.hasAttribute("href"); + break; + } else if ( + typeof node.hasAttributeNS == "function" && + node.hasAttributeNS(XLink_NS, "href") + ) { + isInsideLink = node.getAttributeNS(XLink_NS, "type") == "simple"; + break; + } + + node = node.parentNode; + } while (node); + + return isInsideLink; + } +} diff --git a/toolkit/modules/FinderParent.sys.mjs b/toolkit/modules/FinderParent.sys.mjs new file mode 100644 index 0000000000..8c8437f5e9 --- /dev/null +++ b/toolkit/modules/FinderParent.sys.mjs @@ -0,0 +1,654 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- +// vim: set ts=2 sw=2 sts=2 et tw=80: */ +// 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/. + +const kModalHighlightPref = "findbar.modalHighlight"; +const kSoundEnabledPref = "accessibility.typeaheadfind.enablesound"; +const kNotFoundSoundPref = "accessibility.typeaheadfind.soundURL"; + +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + GetClipboardSearchString: "resource://gre/modules/Finder.sys.mjs", + RFPHelper: "resource://gre/modules/RFPHelper.sys.mjs", + Rect: "resource://gre/modules/Geometry.sys.mjs", +}); + +const kPrefLetterboxing = "privacy.resistFingerprinting.letterboxing"; + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "isLetterboxingEnabled", + kPrefLetterboxing, + false +); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "isSoundEnabled", + kSoundEnabledPref, + false +); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "notFoundSoundURL", + kNotFoundSoundPref, + "" +); + +export function FinderParent(browser) { + this._listeners = new Set(); + this._searchString = ""; + this._foundSearchString = null; + this._lastFoundBrowsingContext = null; + + // The correct states of these will be updated when the findbar is opened. + this._caseSensitive = false; + this._entireWord = false; + this._matchDiacritics = false; + + this.swapBrowser(browser); +} + +let gSound = null; + +FinderParent.prototype = { + get browsingContext() { + return this._browser.browsingContext; + }, + + get useRemoteSubframes() { + return this._browser.ownerGlobal.docShell.nsILoadContext.useRemoteSubframes; + }, + + swapBrowser(aBrowser) { + this._browser = aBrowser; + // Ideally listeners would have removed themselves but that doesn't happen + // right now + this._listeners.clear(); + }, + + addResultListener(aListener) { + this._listeners.add(aListener); + }, + + removeResultListener(aListener) { + this._listeners.delete(aListener); + }, + + callListeners(aCallback, aArgs) { + for (let l of this._listeners) { + // Don't let one callback throwing stop us calling the rest + try { + l[aCallback].apply(l, aArgs); + } catch (e) { + if (!l[aCallback]) { + console.error( + `Missing ${aCallback} callback on RemoteFinderListener` + ); + } else { + console.error(e); + } + } + } + }, + + getLastFoundBrowsingContext(aList) { + // If a search was already performed, returned the last + // browsing context where the result was found. However, + // ensure that this browsing context is still valid, and + // if not, return null. + if ( + aList.includes(this._lastFoundBrowsingContext) && + !this._lastFoundBrowsingContext.isUnderHiddenEmbedderElement + ) { + return this._lastFoundBrowsingContext; + } + + this._lastFoundBrowsingContext = null; + return null; + }, + + sendMessageToContext(aMessageName, aArgs = {}) { + // If there is a last found browsing context, use that. Otherwise, + // use the top-level browsing context. + let browsingContext = null; + if (this._lastFoundBrowsingContext) { + let list = this.gatherBrowsingContexts(this.browsingContext); + let lastBrowsingContext = this.getLastFoundBrowsingContext(list); + if (lastBrowsingContext) { + browsingContext = lastBrowsingContext; + } + } + + if (!browsingContext) { + browsingContext = this.browsingContext; + } + + let windowGlobal = browsingContext.currentWindowGlobal; + if (windowGlobal) { + let actor = windowGlobal.getActor("Finder"); + actor.sendAsyncMessage(aMessageName, aArgs); + } + }, + + sendQueryToContext(aMessageName, aArgs, aBrowsingContext) { + let windowGlobal = aBrowsingContext.currentWindowGlobal; + if (windowGlobal) { + let actor = windowGlobal.getActor("Finder"); + return actor.sendQuery(aMessageName, aArgs).then( + result => result, + r => {} + ); + } + + return Promise.resolve({}); + }, + + sendMessageToAllContexts(aMessageName, aArgs = {}) { + let list = this.gatherBrowsingContexts(this.browsingContext); + for (let browsingContext of list) { + let windowGlobal = browsingContext.currentWindowGlobal; + if (windowGlobal) { + let actor = windowGlobal.getActor("Finder"); + actor.sendAsyncMessage(aMessageName, aArgs); + } + } + }, + + gatherBrowsingContexts(aBrowsingContext) { + if (aBrowsingContext.isUnderHiddenEmbedderElement) { + return []; + } + + let list = [aBrowsingContext]; + + for (let child of aBrowsingContext.children) { + list.push(...this.gatherBrowsingContexts(child)); + } + + return list; + }, + + // If the modal highlighter is on, and there are no out-of-process child + // frames, send a message only to the top-level frame and set the useSubFrames + // flag, so that the finder iterator iterates over subframes. If there is + // an out-of-process subframe, modal highlighting is disabled. + needSubFrameSearch(aList) { + let useSubFrames = false; + + let useModalHighlighter = Services.prefs.getBoolPref(kModalHighlightPref); + let hasOutOfProcessChild = false; + if (useModalHighlighter) { + if (this.useRemoteSubframes) { + return false; + } + + for (let browsingContext of aList) { + if ( + browsingContext != this.browsingContext && + browsingContext.currentWindowGlobal.isProcessRoot + ) { + hasOutOfProcessChild = true; + } + } + + if (!hasOutOfProcessChild) { + aList.splice(0); + aList.push(this.browsingContext); + useSubFrames = true; + } + } + + return useSubFrames; + }, + + onResultFound(aResponse) { + this._foundSearchString = aResponse.searchString; + // The rect stops being a Geometry.sys.mjs:Rect over IPC. + if (aResponse.rect) { + aResponse.rect = lazy.Rect.fromRect(aResponse.rect); + } + + this.callListeners("onFindResult", [aResponse]); + }, + + get searchString() { + return this._foundSearchString; + }, + + get clipboardSearchString() { + return lazy.GetClipboardSearchString(this._browser.loadContext); + }, + + set caseSensitive(aSensitive) { + this._caseSensitive = aSensitive; + this.sendMessageToAllContexts("Finder:CaseSensitive", { + caseSensitive: aSensitive, + }); + }, + + set entireWord(aEntireWord) { + this._entireWord = aEntireWord; + this.sendMessageToAllContexts("Finder:EntireWord", { + entireWord: aEntireWord, + }); + }, + + set matchDiacritics(aMatchDiacritics) { + this._matchDiacritics = aMatchDiacritics; + this.sendMessageToAllContexts("Finder:MatchDiacritics", { + matchDiacritics: aMatchDiacritics, + }); + }, + + async setSearchStringToSelection() { + return this.setToSelection("Finder:SetSearchStringToSelection", false); + }, + + async getInitialSelection() { + return this.setToSelection("Finder:GetInitialSelection", true); + }, + + async setToSelection(aMessage, aInitial) { + let browsingContext = this.browsingContext; + + // Iterate over focused subframe descendants until one is found + // that has the selection. + let result; + do { + result = await this.sendQueryToContext(aMessage, {}, browsingContext); + if (!result || !result.focusedChildBrowserContextId) { + break; + } + + browsingContext = BrowsingContext.get( + result.focusedChildBrowserContextId + ); + } while (browsingContext); + + if (result) { + this.callListeners("onCurrentSelection", [result.selectedText, aInitial]); + } + + return result; + }, + + async doFind(aFindNext, aArgs) { + let rootBC = this.browsingContext; + let highlightList = this.gatherBrowsingContexts(rootBC); + + let canPlayNotFoundSound = + aArgs.searchString.length > this._searchString.length; + + this._searchString = aArgs.searchString; + + let initialBC = this.getLastFoundBrowsingContext(highlightList); + if (!initialBC) { + initialBC = rootBC; + aFindNext = false; + } + + // Make a copy of the list starting from the + // browsing context that was last searched from. The original + // list will be used for the highlighter where the search + // order doesn't matter. + let searchList = []; + for (let c = 0; c < highlightList.length; c++) { + if (highlightList[c] == initialBC) { + searchList = highlightList.slice(c); + searchList.push(...highlightList.slice(0, c)); + break; + } + } + + let mode = Ci.nsITypeAheadFind.FIND_INITIAL; + if (aFindNext) { + mode = aArgs.findBackwards + ? Ci.nsITypeAheadFind.FIND_PREVIOUS + : Ci.nsITypeAheadFind.FIND_NEXT; + } + aArgs.findAgain = aFindNext; + + aArgs.caseSensitive = this._caseSensitive; + aArgs.matchDiacritics = this._matchDiacritics; + aArgs.entireWord = this._entireWord; + + aArgs.useSubFrames = this.needSubFrameSearch(searchList); + if (aArgs.useSubFrames) { + // Use the single frame for the highlight list as well. + highlightList = searchList; + // The typeaheadfind component will play the sound in this case. + canPlayNotFoundSound = false; + } + + if (canPlayNotFoundSound) { + this.initNotFoundSound(); + } + + // Add the initial browsing context twice to allow looping around. + searchList = [...searchList, initialBC]; + + if (aArgs.findBackwards) { + searchList.reverse(); + } + + let response = null; + let wrapped = false; + let foundBC = null; + + for (let c = 0; c < searchList.length; c++) { + let currentBC = searchList[c]; + aArgs.mode = mode; + + // A search has started for a different string, so + // ignore further searches of the old string. + if (this._searchString != aArgs.searchString) { + return; + } + + response = await this.sendQueryToContext("Finder:Find", aArgs, currentBC); + + // This can happen if the tab is closed while the find is in progress. + if (!response) { + break; + } + + // If the search term was found, stop iterating. + if (response.result != Ci.nsITypeAheadFind.FIND_NOTFOUND) { + if ( + this._lastFoundBrowsingContext && + this._lastFoundBrowsingContext != currentBC + ) { + // If the new result is in a different frame than the previous result, + // clear the result from the old frame. If it is the same frame, the + // previous result will be cleared by the find component. + this.removeSelection(true); + } + this._lastFoundBrowsingContext = currentBC; + + // Set the wrapped result flag if needed. + if (wrapped) { + response.result = Ci.nsITypeAheadFind.FIND_WRAPPED; + } + + foundBC = currentBC; + break; + } + + if (aArgs.findBackwards && currentBC == rootBC) { + wrapped = true; + } else if ( + !aArgs.findBackwards && + c + 1 < searchList.length && + searchList[c + 1] == rootBC + ) { + wrapped = true; + } + + mode = aArgs.findBackwards + ? Ci.nsITypeAheadFind.FIND_LAST + : Ci.nsITypeAheadFind.FIND_FIRST; + } + + if (response) { + response.useSubFrames = aArgs.useSubFrames; + // Update the highlight in all browsing contexts. This needs to happen separately + // once it is clear whether a match was found or not. + this.updateHighlightAndMatchCount({ + list: highlightList, + message: "Finder:UpdateHighlightAndMatchCount", + args: response, + foundBrowsingContextId: foundBC ? foundBC.id : -1, + doHighlight: true, + doMatchCount: true, + }); + + // Use the last result found. + this.onResultFound(response); + + if ( + canPlayNotFoundSound && + response.result == Ci.nsITypeAheadFind.FIND_NOTFOUND && + !aFindNext && + !response.entireWord + ) { + this.playNotFoundSound(); + } + } + }, + + fastFind(aSearchString, aLinksOnly, aDrawOutline) { + this.doFind(false, { + searchString: aSearchString, + findBackwards: false, + linksOnly: aLinksOnly, + drawOutline: aDrawOutline, + }); + }, + + findAgain(aSearchString, aFindBackwards, aLinksOnly, aDrawOutline) { + this.doFind(true, { + searchString: aSearchString, + findBackwards: aFindBackwards, + linksOnly: aLinksOnly, + drawOutline: aDrawOutline, + }); + }, + + highlight(aHighlight, aWord, aLinksOnly) { + let list = this.gatherBrowsingContexts(this.browsingContext); + let args = { + highlight: aHighlight, + linksOnly: aLinksOnly, + searchString: aWord, + }; + + args.useSubFrames = this.needSubFrameSearch(list); + + let lastBrowsingContext = this.getLastFoundBrowsingContext(list); + this.updateHighlightAndMatchCount({ + list, + message: "Finder:Highlight", + args, + foundBrowsingContextId: lastBrowsingContext ? lastBrowsingContext.id : -1, + doHighlight: true, + doMatchCount: false, + }); + }, + + requestMatchesCount(aSearchString, aLinksOnly) { + let list = this.gatherBrowsingContexts(this.browsingContext); + let args = { searchString: aSearchString, linksOnly: aLinksOnly }; + + args.useSubFrames = this.needSubFrameSearch(list); + + let lastBrowsingContext = this.getLastFoundBrowsingContext(list); + this.updateHighlightAndMatchCount({ + list, + message: "Finder:MatchesCount", + args, + foundBrowsingContextId: lastBrowsingContext ? lastBrowsingContext.id : -1, + doHighlight: false, + doMatchCount: true, + }); + }, + + updateHighlightAndMatchCount(options) { + let promises = []; + let found = options.args.result != Ci.nsITypeAheadFind.FIND_NOTFOUND; + for (let browsingContext of options.list) { + options.args.foundInThisFrame = + options.foundBrowsingContextId != -1 && + found && + browsingContext.id == options.foundBrowsingContextId; + + // Don't wait for the result + let promise = this.sendQueryToContext( + options.message, + options.args, + browsingContext + ); + promises.push(promise); + } + + Promise.all(promises).then(responses => { + if (options.doHighlight) { + let sendNotification = false; + let highlight = false; + let found = false; + for (let response of responses) { + if (!response) { + break; + } + + sendNotification = true; + if (response.found) { + found = true; + } + highlight = response.highlight; + } + + if (sendNotification) { + this.callListeners("onHighlightFinished", [ + { searchString: options.args.searchString, highlight, found }, + ]); + } + } + + if (options.doMatchCount) { + let sendNotification = false; + let current = 0; + let total = 0; + let limit = 0; + for (let response of responses) { + // A null response can happen if another search was started + // and this one became invalid. + if (!response || !("total" in response)) { + break; + } + + sendNotification = true; + + if ( + options.args.useSubFrames || + (options.foundBrowsingContextId >= 0 && + response.browsingContextId == options.foundBrowsingContextId) + ) { + current = total + response.current; + } + total += response.total; + limit = response.limit; + } + + if (sendNotification) { + this.callListeners("onMatchesCountResult", [ + { searchString: options.args.searchString, current, total, limit }, + ]); + } + } + }); + }, + + enableSelection() { + this.sendMessageToContext("Finder:EnableSelection"); + }, + + removeSelection(aKeepHighlight) { + this.sendMessageToContext("Finder:RemoveSelection", { + keepHighlight: aKeepHighlight, + }); + }, + + focusContent() { + // Allow Finder listeners to cancel focusing the content. + for (let l of this._listeners) { + try { + if ("shouldFocusContent" in l && !l.shouldFocusContent()) { + return; + } + } catch (ex) { + console.error(ex); + } + } + + this._browser.focus(); + this.sendMessageToContext("Finder:FocusContent"); + }, + + onFindbarClose() { + this._lastFoundBrowsingContext = null; + this.sendMessageToAllContexts("Finder:FindbarClose"); + + if (lazy.isLetterboxingEnabled) { + let window = this._browser.ownerGlobal; + lazy.RFPHelper.contentSizeUpdated(window); + } + }, + + onFindbarOpen() { + this.sendMessageToAllContexts("Finder:FindbarOpen"); + + if (lazy.isLetterboxingEnabled) { + let window = this._browser.ownerGlobal; + lazy.RFPHelper.contentSizeUpdated(window); + } + }, + + onModalHighlightChange(aUseModalHighlight) { + this.sendMessageToAllContexts("Finder:ModalHighlightChange", { + useModalHighlight: aUseModalHighlight, + }); + }, + + onHighlightAllChange(aHighlightAll) { + this.sendMessageToAllContexts("Finder:HighlightAllChange", { + highlightAll: aHighlightAll, + }); + }, + + keyPress(aEvent) { + this.sendMessageToContext("Finder:KeyPress", { + keyCode: aEvent.keyCode, + ctrlKey: aEvent.ctrlKey, + metaKey: aEvent.metaKey, + altKey: aEvent.altKey, + shiftKey: aEvent.shiftKey, + }); + }, + + initNotFoundSound() { + if (!gSound && lazy.isSoundEnabled && lazy.notFoundSoundURL) { + try { + gSound = Cc["@mozilla.org/sound;1"].getService(Ci.nsISound); + gSound.init(); + } catch (ex) {} + } + }, + + playNotFoundSound() { + if (!lazy.isSoundEnabled || !lazy.notFoundSoundURL) { + return; + } + + this.initNotFoundSound(); + if (!gSound) { + return; + } + + let soundUrl = lazy.notFoundSoundURL; + if (soundUrl == "beep") { + gSound.beep(); + } else { + if (soundUrl == "default") { + soundUrl = "chrome://global/content/notfound.wav"; + } + gSound.play(Services.io.newURI(soundUrl)); + } + }, +}; diff --git a/toolkit/modules/FirstStartup.sys.mjs b/toolkit/modules/FirstStartup.sys.mjs new file mode 100644 index 0000000000..b31e7ffa07 --- /dev/null +++ b/toolkit/modules/FirstStartup.sys.mjs @@ -0,0 +1,109 @@ +/* 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 { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + Normandy: "resource://normandy/Normandy.sys.mjs", + TaskScheduler: "resource://gre/modules/TaskScheduler.sys.mjs", +}); + +const PREF_TIMEOUT = "first-startup.timeout"; + +/** + * Service for blocking application startup, to be used on the first install. The intended + * use case is for `FirstStartup` to be invoked when the application is called by an installer, + * such as the Windows Stub Installer, to allow the application to do some first-install tasks + * such as performance tuning and downloading critical data. + * + * In this scenario, the installer does not exit until the first application window appears, + * which gives the user experience of the application starting up quickly on first install. + */ +export var FirstStartup = { + NOT_STARTED: 0, + IN_PROGRESS: 1, + TIMED_OUT: 2, + SUCCESS: 3, + UNSUPPORTED: 4, + + _state: 0, // NOT_STARTED, + /** + * Initialize and run first-startup services. This will always run synchronously + * and spin the event loop until either all required services have + * completed, or until a timeout is reached. + * + * In the latter case, services are expected to run post-UI instead as usual. + */ + init() { + this._state = this.IN_PROGRESS; + const timeout = Services.prefs.getIntPref(PREF_TIMEOUT, 30000); // default to 30 seconds + let startingTime = Cu.now(); + let initialized = false; + + let promises = []; + + let normandyInitEndTime = null; + if (AppConstants.MOZ_NORMANDY) { + promises.push( + lazy.Normandy.init({ runAsync: false }).finally(() => { + normandyInitEndTime = Cu.now(); + }) + ); + } + + let deleteTasksEndTime = null; + if (AppConstants.MOZ_UPDATE_AGENT) { + // It's technically possible for a previous installation to leave an old + // OS-level scheduled task around. Start fresh. + promises.push( + lazy.TaskScheduler.deleteAllTasks() + .catch(() => {}) + .finally(() => { + deleteTasksEndTime = Cu.now(); + }) + ); + } + + if (promises.length) { + Promise.all(promises).then(() => (initialized = true)); + + this.elapsed = 0; + Services.tm.spinEventLoopUntil("FirstStartup.sys.mjs:init", () => { + this.elapsed = Math.round(Cu.now() - startingTime); + if (this.elapsed >= timeout) { + this._state = this.TIMED_OUT; + return true; + } else if (initialized) { + this._state = this.SUCCESS; + return true; + } + return false; + }); + } else { + this._state = this.UNSUPPORTED; + } + + if (AppConstants.MOZ_NORMANDY) { + Glean.firstStartup.normandyInitTime.set( + Math.ceil(normandyInitEndTime || Cu.now() - startingTime) + ); + } + + if (AppConstants.MOZ_UPDATE_AGENT) { + Glean.firstStartup.deleteTasksTime.set( + Math.ceil(deleteTasksEndTime || Cu.now() - startingTime) + ); + } + + Glean.firstStartup.statusCode.set(this._state); + Glean.firstStartup.elapsed.set(this.elapsed); + GleanPings.firstStartup.submit(); + }, + + get state() { + return this._state; + }, +}; diff --git a/toolkit/modules/FormLikeFactory.sys.mjs b/toolkit/modules/FormLikeFactory.sys.mjs new file mode 100644 index 0000000000..4950ee0f82 --- /dev/null +++ b/toolkit/modules/FormLikeFactory.sys.mjs @@ -0,0 +1,191 @@ +/* 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/. */ + +/** + * A factory to generate FormLike objects that represent a set of related fields + * which aren't necessarily marked up with a <form> element. FormLike's emulate + * the properties of an HTMLFormElement which are relevant to form tasks. + */ +export let FormLikeFactory = { + _propsFromForm: ["action", "autocomplete", "ownerDocument"], + + /** + * Create a FormLike object from a <form>. + * + * @param {HTMLFormElement} aForm + * @return {FormLike} + * @throws Error if aForm isn't an HTMLFormElement + */ + createFromForm(aForm) { + if (!HTMLFormElement.isInstance(aForm)) { + throw new Error("createFromForm: aForm must be a HTMLFormElement"); + } + + let formLike = { + elements: [...aForm.elements], + rootElement: aForm, + }; + + for (let prop of this._propsFromForm) { + formLike[prop] = aForm[prop]; + } + + this._addToJSONProperty(formLike); + + return formLike; + }, + + /** + * Create a FormLike object from an <input>/<select> in a document. + * + * If the field is in a <form>, construct the FormLike from the form. + * Otherwise, create a FormLike with a rootElement (wrapper) according to + * heuristics. Currently all <input>/<select> not in a <form> are one FormLike + * but this shouldn't be relied upon as the heuristics may change to detect + * multiple "forms" (e.g. registration and login) on one page with a <form>. + * + * Note that two FormLikes created from the same field won't return the same FormLike object. + * Use the `rootElement` property on the FormLike as a key instead. + * + * @param {HTMLInputElement|HTMLSelectElement} aField - an <input> or <select> field in a document + * @return {FormLike} + * @throws Error if aField isn't a password or username field in a document + */ + createFromField(aField) { + if ( + (!HTMLInputElement.isInstance(aField) && + !HTMLSelectElement.isInstance(aField)) || + !aField.ownerDocument + ) { + throw new Error("createFromField requires a field in a document"); + } + + let rootElement = this.findRootForField(aField); + if (HTMLFormElement.isInstance(rootElement)) { + return this.createFromForm(rootElement); + } + + let doc = aField.ownerDocument; + + let formLike = { + action: doc.baseURI, + autocomplete: "on", + ownerDocument: doc, + rootElement, + }; + + // FormLikes can be created when fields are inserted into the DOM. When + // many, many fields are inserted one after the other, we create many + // FormLikes, and computing the elements list becomes more and more + // expensive. Making the elements list lazy means that it'll only + // be computed when it's eventually needed (if ever). + ChromeUtils.defineLazyGetter(formLike, "elements", function () { + let elements = []; + for (let el of this.rootElement.querySelectorAll("input, select")) { + // Exclude elements inside the rootElement that are already in a <form> as + // they will be handled by their own FormLike. + if (!el.form) { + elements.push(el); + } + } + + return elements; + }); + + this._addToJSONProperty(formLike); + return formLike; + }, + + /** + * Find the closest <form> if any when aField is inside a ShadowRoot. + * + * @param {HTMLInputElement} aField - a password or username field in a document + * @return {HTMLFormElement|null} + */ + closestFormIgnoringShadowRoots(aField) { + let form = aField.closest("form"); + let current = aField; + while (!form) { + let shadowRoot = current.getRootNode(); + if (!ShadowRoot.isInstance(shadowRoot)) { + break; + } + let host = shadowRoot.host; + form = host.closest("form"); + current = host; + } + return form; + }, + + /** + * Determine the Element that encapsulates the related fields. For example, if + * a page contains a login form and a checkout form which are "submitted" + * separately, and the username field is passed in, ideally this would return + * an ancestor Element of the username and password fields which doesn't + * include any of the checkout fields. + * + * @param {HTMLInputElement|HTMLSelectElement} aField - a field in a document + * @return {HTMLElement} - the root element surrounding related fields + */ + findRootForField(aField) { + let form = aField.form || this.closestFormIgnoringShadowRoots(aField); + if (form) { + return form; + } + + return aField.ownerDocument.documentElement; + }, + + /** + * Add a `toJSON` property to a FormLike so logging which ends up going + * through dump doesn't include usless garbage from DOM objects. + */ + _addToJSONProperty(aFormLike) { + function prettyElementOutput(aElement) { + let idText = aElement.id ? "#" + aElement.id : ""; + let classText = ""; + for (let className of aElement.classList) { + classText += "." + className; + } + return `<${aElement.nodeName + idText + classText}>`; + } + + Object.defineProperty(aFormLike, "toJSON", { + value: () => { + let cleansed = {}; + for (let key of Object.keys(aFormLike)) { + let value = aFormLike[key]; + let cleansedValue = value; + + switch (key) { + case "elements": { + cleansedValue = []; + for (let element of value) { + cleansedValue.push(prettyElementOutput(element)); + } + break; + } + + case "ownerDocument": { + cleansedValue = { + location: { + href: value.location.href, + }, + }; + break; + } + + case "rootElement": { + cleansedValue = prettyElementOutput(value); + break; + } + } + + cleansed[key] = cleansedValue; + } + return cleansed; + }, + }); + }, +}; diff --git a/toolkit/modules/GMPExtractor.worker.js b/toolkit/modules/GMPExtractor.worker.js new file mode 100644 index 0000000000..2c10daaf50 --- /dev/null +++ b/toolkit/modules/GMPExtractor.worker.js @@ -0,0 +1,83 @@ +/* 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/. */ + +"use strict"; + +const FILE_ENTRY = "201: "; + +onmessage = async function (msg) { + try { + let extractedPaths = []; + // Construct a jar URI from the file URI so we can use the JAR URI scheme + // handling to navigate inside the zip file. + let jarPath = "jar:" + msg.data.zipURI + "!/"; + let jarResponse = await fetch(jarPath); + let dirListing = await jarResponse.text(); + let lines = dirListing.split("\n"); + let reader = new FileReader(); + for (let line of lines) { + if (!line.startsWith(FILE_ENTRY)) { + // Not a file entry, skip. + continue; + } + let lineSplits = line.split(" "); + let fileName = lineSplits[1]; + // We don't need these types of files. + if ( + fileName == "verified_contents.json" || + fileName == "icon-128x128.png" || + fileName.startsWith("_") + ) { + continue; + } + let filePath = jarPath + fileName; + let filePathResponse = await fetch(filePath); + let fileContents = await filePathResponse.blob(); + let fileData = await new Promise(resolve => { + reader.onloadend = function () { + resolve(reader.result); + }; + reader.readAsArrayBuffer(fileContents); + }); + let installToDirPath = PathUtils.join( + await PathUtils.getProfileDir(), + ...msg.data.relativeInstallPath + ); + await IOUtils.makeDirectory(installToDirPath); + // Do not extract into directories. Extract all files to the same + // directory. + let destPath = PathUtils.join(installToDirPath, fileName); + await IOUtils.write(destPath, new Uint8Array(fileData), { + tmpPath: destPath + ".tmp", + }); + // Ensure files are writable and executable. Otherwise, we may be + // unable to execute or uninstall them. + await IOUtils.setPermissions(destPath, 0o700); + if (IOUtils.delMacXAttr) { + // If we're on MacOS Firefox will add the quarantine xattr to files it + // downloads. In this case we want to clear that xattr so we can load + // the CDM. + try { + await IOUtils.delMacXAttr(destPath, "com.apple.quarantine"); + } catch (e) { + // Failed to remove the attribute. This could be because the profile + // exists on a file system without xattr support. + // + // Don't fail the extraction here, as in this case it's likely we + // didn't set quarantine on these files in the first place. + } + } + extractedPaths.push(destPath); + } + postMessage({ + result: "success", + extractedPaths, + }); + } catch (e) { + postMessage({ + result: "fail", + exception: e.message, + }); + } +}; diff --git a/toolkit/modules/GMPInstallManager.sys.mjs b/toolkit/modules/GMPInstallManager.sys.mjs new file mode 100644 index 0000000000..41f57b9a63 --- /dev/null +++ b/toolkit/modules/GMPInstallManager.sys.mjs @@ -0,0 +1,850 @@ +/* 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/. */ + +// 1 day default +const DEFAULT_SECONDS_BETWEEN_CHECKS = 60 * 60 * 24; + +import { Log } from "resource://gre/modules/Log.sys.mjs"; +import { + GMPPrefs, + GMPUtils, + GMP_PLUGIN_IDS, + WIDEVINE_L1_ID, + WIDEVINE_L3_ID, +} from "resource://gre/modules/GMPUtils.sys.mjs"; + +import { ProductAddonChecker } from "resource://gre/modules/addons/ProductAddonChecker.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + CertUtils: "resource://gre/modules/CertUtils.sys.mjs", + FileUtils: "resource://gre/modules/FileUtils.sys.mjs", + ServiceRequest: "resource://gre/modules/ServiceRequest.sys.mjs", + UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs", +}); + +function getScopedLogger(prefix) { + // `PARENT_LOGGER_ID.` being passed here effectively links this logger + // to the parentLogger. + return Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP", prefix + " "); +} + +const LOCAL_GMP_SOURCES = [ + { + id: "gmp-gmpopenh264", + src: "chrome://global/content/gmp-sources/openh264.json", + installByDefault: true, + }, + { + id: "gmp-widevinecdm", + src: "chrome://global/content/gmp-sources/widevinecdm.json", + installByDefault: true, + }, + { + id: "gmp-widevinecdm-l1", + src: "chrome://global/content/gmp-sources/widevinecdm_l1.json", + installByDefault: false, + }, +]; + +function downloadJSON(uri) { + let log = getScopedLogger("GMPInstallManager.checkForAddons"); + log.info("fetching config from: " + uri); + return new Promise((resolve, reject) => { + let xmlHttp = new lazy.ServiceRequest({ mozAnon: true }); + + xmlHttp.onload = function (aResponse) { + resolve(JSON.parse(this.responseText)); + }; + + xmlHttp.onerror = function (e) { + reject("Fetching " + uri + " results in error code: " + e.target.status); + }; + + xmlHttp.open("GET", uri); + xmlHttp.overrideMimeType("application/json"); + xmlHttp.send(); + }); +} + +/** + * If downloading from the network fails (AUS server is down), + * load the sources from local build configuration. + */ +function downloadLocalConfig(sources) { + if (!sources.length) { + return Promise.resolve({ addons: [] }); + } + + let log = getScopedLogger("GMPInstallManager.downloadLocalConfig"); + return Promise.all( + sources.map(conf => { + return downloadJSON(conf.src).then(addons => { + let platforms = addons.vendors[conf.id].platforms; + let target = Services.appinfo.OS + "_" + lazy.UpdateUtils.ABI; + let details = null; + + while (!details) { + if (!(target in platforms)) { + // There was no matching platform so return false, this addon + // will be filtered from the results below + log.info("no details found for: " + target); + return false; + } + // Field either has the details of the binary or is an alias + // to another build target key that does + if (platforms[target].alias) { + target = platforms[target].alias; + } else { + details = platforms[target]; + } + } + + log.info("found plugin: " + conf.id); + return { + id: conf.id, + URL: details.fileUrl, + hashFunction: addons.hashFunction, + hashValue: details.hashValue, + version: addons.vendors[conf.id].version, + size: details.filesize, + usedFallback: true, + }; + }); + }) + ).then(addons => { + // Some filters may not match this platform so + // filter those out + return { addons: addons.filter(x => x !== false) }; + }); +} + +/** + * Provides an easy API for downloading and installing GMP Addons + */ +export function GMPInstallManager() {} + +/** + * Temp file name used for downloading + */ +GMPInstallManager.prototype = { + /** + * Obtains a URL with replacement of vars + */ + async _getURL() { + let log = getScopedLogger("GMPInstallManager._getURL"); + // Use the override URL if it is specified. The override URL is just like + // the normal URL but it does not check the cert. + let url = GMPPrefs.getString(GMPPrefs.KEY_URL_OVERRIDE, ""); + if (url) { + log.info("Using override url: " + url); + } else { + url = GMPPrefs.getString(GMPPrefs.KEY_URL); + log.info("Using url: " + url); + } + + url = await lazy.UpdateUtils.formatUpdateURL(url); + + log.info("Using url (with replacement): " + url); + return url; + }, + + /** + * Records telemetry results on if fetching update.xml from Balrog succeeded + * when content signature was used to verify the response from Balrog. + * @param didGetAddonList + * A boolean indicating if an update.xml containing the addon list was + * successfully fetched (true) or not (false). + * @param err + * The error that was thrown (if it exists) for the failure case. This + * is expected to have a addonCheckerErr member which provides further + * information on why the addon checker failed. + */ + recordUpdateXmlTelemetryForContentSignature(didGetAddonList, err = null) { + let log = getScopedLogger( + "GMPInstallManager.recordUpdateXmlTelemetryForContentSignature" + ); + try { + let updateResultHistogram = Services.telemetry.getHistogramById( + "MEDIA_GMP_UPDATE_XML_FETCH_RESULT" + ); + + // The non-glean telemetry used here will be removed in future and just + // the glean data will be gathered. + if (didGetAddonList) { + updateResultHistogram.add("content_sig_ok"); + Glean.gmp.updateXmlFetchResult.content_sig_success.add(1); + return; + } + // All remaining cases are failure cases. + updateResultHistogram.add("content_sig_fail"); + if (!err?.addonCheckerErr) { + // Unknown error case. If this is happening we should audit error paths + // to identify why we're not getting an error, or not getting it + // labelled. + Glean.gmp.updateXmlFetchResult.content_sig_unknown_error.add(1); + return; + } + const errorToHistogramMap = { + [ProductAddonChecker.NETWORK_REQUEST_ERR]: + "content_sig_net_request_error", + [ProductAddonChecker.NETWORK_TIMEOUT_ERR]: "content_sig_net_timeout", + [ProductAddonChecker.ABORT_ERR]: "content_sig_abort", + [ProductAddonChecker.VERIFICATION_MISSING_DATA_ERR]: + "content_sig_missing_data", + [ProductAddonChecker.VERIFICATION_FAILED_ERR]: "content_sig_failed", + [ProductAddonChecker.VERIFICATION_INVALID_ERR]: "content_sig_invalid", + [ProductAddonChecker.XML_PARSE_ERR]: "content_sig_xml_parse_error", + }; + let metricID = + errorToHistogramMap[err.addonCheckerErr] ?? "content_sig_unknown_error"; + let metric = Glean.gmp.updateXmlFetchResult[metricID]; + metric.add(1); + } catch (e) { + // We don't expect this path to be hit, but we don't want telemetry + // failures to break GMP updates, so catch any issues here and let the + // update machinery continue. + log.error( + `Failed to record telemetry result of getProductAddonList, got error: ${e}` + ); + } + }, + + /** + * Records telemetry results on if fetching update.xml from Balrog succeeded + * when cert pinning was used to verify the response from Balrog. This + * should be removed once we're no longer using cert pinning. + * @param didGetAddonList + * A boolean indicating if an update.xml containing the addon list was + * successfully fetched (true) or not (false). + * @param err + * The error that was thrown (if it exists) for the failure case. This + * is expected to have a addonCheckerErr member which provides further + * information on why the addon checker failed. + */ + recordUpdateXmlTelemetryForCertPinning(didGetAddonList, err = null) { + let log = getScopedLogger( + "GMPInstallManager.recordUpdateXmlTelemetryForCertPinning" + ); + try { + let updateResultHistogram = Services.telemetry.getHistogramById( + "MEDIA_GMP_UPDATE_XML_FETCH_RESULT" + ); + + // The non-glean telemetry used here will be removed in future and just + // the glean data will be gathered. + if (didGetAddonList) { + updateResultHistogram.add("cert_pinning_ok"); + Glean.gmp.updateXmlFetchResult.cert_pin_success.add(1); + return; + } + // All remaining cases are failure cases. + updateResultHistogram.add("cert_pinning_fail"); + if (!err?.addonCheckerErr) { + // Unknown error case. If this is happening we should audit error paths + // to identify why we're not getting an error, or not getting it + // labelled. + Glean.gmp.updateXmlFetchResult.cert_pin_unknown_error.add(1); + return; + } + const errorToHistogramMap = { + [ProductAddonChecker.NETWORK_REQUEST_ERR]: "cert_pin_net_request_error", + [ProductAddonChecker.NETWORK_TIMEOUT_ERR]: "cert_pin_net_timeout", + [ProductAddonChecker.ABORT_ERR]: "cert_pin_abort", + [ProductAddonChecker.VERIFICATION_MISSING_DATA_ERR]: + "cert_pin_missing_data", + [ProductAddonChecker.VERIFICATION_FAILED_ERR]: "cert_pin_failed", + [ProductAddonChecker.VERIFICATION_INVALID_ERR]: "cert_pin_invalid", + [ProductAddonChecker.XML_PARSE_ERR]: "cert_pin_xml_parse_error", + }; + let metricID = + errorToHistogramMap[err.addonCheckerErr] ?? "cert_pin_unknown_error"; + let metric = Glean.gmp.updateXmlFetchResult[metricID]; + metric.add(1); + } catch (e) { + // We don't expect this path to be hit, but we don't want telemetry + // failures to break GMP updates, so catch any issues here and let the + // update machinery continue. + log.error( + `Failed to record telemetry result of getProductAddonList, got error: ${e}` + ); + } + }, + + /** + * Performs an addon check. + * @return a promise which will be resolved or rejected. + * The promise is resolved with an object with properties: + * addons: array of addons + * usedFallback: whether the data was collected from online or + * from fallback data within the build + * The promise is rejected with an object with properties: + * target: The XHR request object + * status: The HTTP status code + * type: Sometimes specifies type of rejection + */ + async checkForAddons() { + let log = getScopedLogger("GMPInstallManager.checkForAddons"); + if (this._deferred) { + log.error("checkForAddons already called"); + return Promise.reject({ type: "alreadycalled" }); + } + + if (!GMPPrefs.getBool(GMPPrefs.KEY_UPDATE_ENABLED, true)) { + log.info("Updates are disabled via media.gmp-manager.updateEnabled"); + return { usedFallback: true, addons: [] }; + } + + this._deferred = Promise.withResolvers(); + let deferredPromise = this._deferred.promise; + + // Should content signature checking of Balrog replies be used? If so this + // will be done instead of the older cert pinning method. + let checkContentSignature = GMPPrefs.getBool( + GMPPrefs.KEY_CHECK_CONTENT_SIGNATURE, + true + ); + + let allowNonBuiltIn = true; + let certs = null; + // Only check certificates if we're not using a custom URL, and only if + // we're not checking a content signature. + if ( + !Services.prefs.prefHasUserValue(GMPPrefs.KEY_URL_OVERRIDE) && + !checkContentSignature + ) { + allowNonBuiltIn = !GMPPrefs.getString( + GMPPrefs.KEY_CERT_REQUIREBUILTIN, + true + ); + if (GMPPrefs.getBool(GMPPrefs.KEY_CERT_CHECKATTRS, true)) { + certs = lazy.CertUtils.readCertPrefs(GMPPrefs.KEY_CERTS_BRANCH); + } + } + + let url = await this._getURL(); + + log.info( + `Fetching product addon list url=${url}, allowNonBuiltIn=${allowNonBuiltIn}, certs=${certs}, checkContentSignature=${checkContentSignature}` + ); + + let success = true; + let res; + try { + res = await ProductAddonChecker.getProductAddonList( + url, + allowNonBuiltIn, + certs, + checkContentSignature + ); + + if (checkContentSignature) { + this.recordUpdateXmlTelemetryForContentSignature(true); + } else { + this.recordUpdateXmlTelemetryForCertPinning(true); + } + } catch (err) { + success = false; + if (checkContentSignature) { + this.recordUpdateXmlTelemetryForContentSignature(false, err); + } else { + this.recordUpdateXmlTelemetryForCertPinning(false, err); + } + } + + try { + if (!success) { + log.info("Falling back to local config"); + let fallbackSources = LOCAL_GMP_SOURCES.filter(function (gmpSource) { + return gmpSource.installByDefault; + }); + res = await downloadLocalConfig(fallbackSources); + } + } catch (err) { + this._deferred.reject(err); + delete this._deferred; + return deferredPromise; + } + + let addons; + if (res && res.addons) { + addons = res.addons.map(a => new GMPAddon(a)); + } else { + addons = []; + } + + // We need to merge in the addons that are only available via fallback that + // the user has requested be forced installed regardless of our update + // server configuration. + try { + let forcedSources = LOCAL_GMP_SOURCES.filter(function (gmpSource) { + return GMPPrefs.getBool( + GMPPrefs.KEY_PLUGIN_FORCE_INSTALL, + false, + gmpSource.id + ); + }); + + let forcedConfigs = await downloadLocalConfig( + forcedSources.filter(function (gmpSource) { + return !addons.find(gmpAddon => gmpAddon.id == gmpSource.id); + }) + ); + + let forcedAddons = forcedConfigs.addons.map( + config => new GMPAddon(config) + ); + + log.info("Forced " + forcedAddons.length + " addons."); + addons = addons.concat(forcedAddons); + } catch (err) { + log.info("Failed to force addons: " + err); + } + + this._deferred.resolve({ addons }); + delete this._deferred; + return deferredPromise; + }, + /** + * Installs the specified addon and calls a callback when done. + * @param gmpAddon The GMPAddon object to install + * @return a promise which will be resolved or rejected + * The promise will resolve with an array of paths that were extracted + * The promise will reject with an error object: + * target: The XHR request object + * status: The HTTP status code + * type: A string to represent the type of error + * downloaderr, verifyerr or previouserrorencountered + */ + installAddon(gmpAddon) { + if (this._deferred) { + let log = getScopedLogger("GMPInstallManager.installAddon"); + log.error("previous error encountered"); + return Promise.reject({ type: "previouserrorencountered" }); + } + this.gmpDownloader = new GMPDownloader(gmpAddon); + return this.gmpDownloader.start(); + }, + _getTimeSinceLastCheck() { + let now = Math.round(Date.now() / 1000); + // Default to 0 here because `now - 0` will be returned later if that case + // is hit. We want a large value so a check will occur. + let lastCheck = GMPPrefs.getInt(GMPPrefs.KEY_UPDATE_LAST_CHECK, 0); + // Handle clock jumps, return now since we want it to represent + // a lot of time has passed since the last check. + if (now < lastCheck) { + return now; + } + return now - lastCheck; + }, + get _isEMEEnabled() { + return GMPPrefs.getBool(GMPPrefs.KEY_EME_ENABLED, true); + }, + _isAddonEnabled(aAddon) { + return GMPPrefs.getBool(GMPPrefs.KEY_PLUGIN_ENABLED, true, aAddon); + }, + _isAddonUpdateEnabled(aAddon) { + return ( + this._isAddonEnabled(aAddon) && + GMPPrefs.getBool(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, true, aAddon) + ); + }, + _updateLastCheck() { + let now = Math.round(Date.now() / 1000); + GMPPrefs.setInt(GMPPrefs.KEY_UPDATE_LAST_CHECK, now); + }, + _versionchangeOccurred() { + let savedBuildID = GMPPrefs.getString(GMPPrefs.KEY_BUILDID, ""); + let buildID = Services.appinfo.platformBuildID || ""; + if (savedBuildID == buildID) { + return false; + } + GMPPrefs.setString(GMPPrefs.KEY_BUILDID, buildID); + return true; + }, + /** + * Wrapper for checkForAddons and installAddon. + * Will only install if not already installed and will log the results. + * This will only install/update the OpenH264 and EME plugins + * @return a promise which will be resolved if all addons could be installed + * successfully, rejected otherwise. + */ + async simpleCheckAndInstall() { + let log = getScopedLogger("GMPInstallManager.simpleCheckAndInstall"); + + if (this._versionchangeOccurred()) { + log.info( + "A version change occurred. Ignoring " + + "media.gmp-manager.lastCheck to check immediately for " + + "new or updated GMPs." + ); + } else { + let secondsBetweenChecks = GMPPrefs.getInt( + GMPPrefs.KEY_SECONDS_BETWEEN_CHECKS, + DEFAULT_SECONDS_BETWEEN_CHECKS + ); + let secondsSinceLast = this._getTimeSinceLastCheck(); + log.info( + "Last check was: " + + secondsSinceLast + + " seconds ago, minimum seconds: " + + secondsBetweenChecks + ); + if (secondsBetweenChecks > secondsSinceLast) { + log.info("Will not check for updates."); + return { status: "too-frequent-no-check" }; + } + } + + try { + let { addons } = await this.checkForAddons(); + this._updateLastCheck(); + log.info("Found " + addons.length + " addons advertised."); + let addonsToInstall = addons.filter(function (gmpAddon) { + log.info("Found addon: " + gmpAddon.toString()); + + if (!gmpAddon.isValid) { + log.info("Addon |" + gmpAddon.id + "| is invalid."); + return false; + } + + if (GMPUtils.isPluginHidden(gmpAddon)) { + log.info("Addon |" + gmpAddon.id + "| has been hidden."); + return false; + } + + if (gmpAddon.isInstalled) { + log.info("Addon |" + gmpAddon.id + "| already installed."); + return false; + } + + // Do not install from fallback if already installed as it + // may be a downgrade + if (gmpAddon.usedFallback && gmpAddon.isUpdate) { + log.info( + "Addon |" + + gmpAddon.id + + "| not installing updates based " + + "on fallback." + ); + return false; + } + + let addonUpdateEnabled = false; + if (GMP_PLUGIN_IDS.includes(gmpAddon.id)) { + if (!this._isAddonEnabled(gmpAddon.id)) { + log.info( + "GMP |" + gmpAddon.id + "| has been disabled; skipping check." + ); + } else if (!this._isAddonUpdateEnabled(gmpAddon.id)) { + log.info( + "Auto-update is off for " + gmpAddon.id + ", skipping check." + ); + } else { + addonUpdateEnabled = true; + } + } else { + // Currently, we only support installs of OpenH264 and EME plugins. + log.info( + "Auto-update is off for unknown plugin '" + + gmpAddon.id + + "', skipping check." + ); + } + + return addonUpdateEnabled; + }, this); + + if (!addonsToInstall.length) { + let now = Math.round(Date.now() / 1000); + GMPPrefs.setInt(GMPPrefs.KEY_UPDATE_LAST_EMPTY_CHECK, now); + log.info("No new addons to install, returning"); + return { status: "nothing-new-to-install" }; + } + + let installResults = []; + let failureEncountered = false; + for (let addon of addonsToInstall) { + try { + await this.installAddon(addon); + installResults.push({ + id: addon.id, + result: "succeeded", + }); + } catch (e) { + failureEncountered = true; + installResults.push({ + id: addon.id, + result: "failed", + }); + } + } + if (failureEncountered) { + // eslint-disable-next-line no-throw-literal + throw { status: "failed", results: installResults }; + } + return { status: "succeeded", results: installResults }; + } catch (e) { + log.error("Could not check for addons", e); + throw e; + } + }, + + /** + * Makes sure everything is cleaned up + */ + uninit() { + let log = getScopedLogger("GMPInstallManager.uninit"); + if (this._request) { + log.info("Aborting request"); + this._request.abort(); + } + if (this._deferred) { + log.info("Rejecting deferred"); + this._deferred.reject({ type: "uninitialized" }); + } + log.info("Done cleanup"); + }, + + /** + * If set to true, specifies to leave the temporary downloaded zip file. + * This is useful for tests. + */ + overrideLeaveDownloadedZip: false, +}; + +/** + * Used to construct a single GMP addon + * GMPAddon objects are returns from GMPInstallManager.checkForAddons + * GMPAddon objects can also be used in calls to GMPInstallManager.installAddon + * + * @param addon The ProductAddonChecker `addon` object + */ +export function GMPAddon(addon) { + let log = getScopedLogger("GMPAddon.constructor"); + this.usedFallback = false; + for (let name of Object.keys(addon)) { + this[name] = addon[name]; + } + log.info("Created new addon: " + this.toString()); +} + +GMPAddon.prototype = { + /** + * Returns a string representation of the addon + */ + toString() { + return ( + this.id + + " (" + + "isValid: " + + this.isValid + + ", isInstalled: " + + this.isInstalled + + ", hashFunction: " + + this.hashFunction + + ", hashValue: " + + this.hashValue + + (this.size !== undefined ? ", size: " + this.size : "") + + ")" + ); + }, + /** + * If all the fields aren't specified don't consider this addon valid + * @return true if the addon is parsed and valid + */ + get isValid() { + return ( + this.id && + this.URL && + this.version && + this.hashFunction && + !!this.hashValue + ); + }, + get isInstalled() { + return ( + this.version && + !!this.hashValue && + GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_VERSION, "", this.id) === + this.version && + GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_HASHVALUE, "", this.id) === + this.hashValue + ); + }, + get isEME() { + return this.id == WIDEVINE_L1_ID || this.id == WIDEVINE_L3_ID; + }, + get isOpenH264() { + return this.id == "gmp-gmpopenh264"; + }, + /** + * @return true if the addon has been previously installed and this is + * a new version, if this is a fresh install return false + */ + get isUpdate() { + return ( + this.version && + GMPPrefs.getBool(GMPPrefs.KEY_PLUGIN_VERSION, false, this.id) + ); + }, +}; + +/** + * Constructs a GMPExtractor object which is used to extract a GMP zip + * into the specified location. + * @param zipPath The path on disk of the zip file to extract + * @param relativePath The relative path components inside the profile directory + * to extract the zip to. + */ +export function GMPExtractor(zipPath, relativeInstallPath) { + this.zipPath = zipPath; + this.relativeInstallPath = relativeInstallPath; +} + +GMPExtractor.prototype = { + /** + * Installs the this.zipPath contents into the directory used to store GMP + * addons for the current platform. + * + * @return a promise which will be resolved or rejected + * See GMPInstallManager.installAddon for resolve/rejected info + */ + install() { + this._deferred = Promise.withResolvers(); + let deferredPromise = this._deferred; + let { zipPath, relativeInstallPath } = this; + // Escape the zip path since the worker will use it as a URI + let zipFile = new lazy.FileUtils.File(zipPath); + let zipURI = Services.io.newFileURI(zipFile).spec; + let worker = new ChromeWorker( + "resource://gre/modules/GMPExtractor.worker.js" + ); + worker.onmessage = function (msg) { + let log = getScopedLogger("GMPExtractor"); + worker.terminate(); + if (msg.data.result != "success") { + log.error("Failed to extract zip file: " + zipURI); + log.error("Exception: " + msg.data.exception); + return deferredPromise.reject({ + target: this, + status: msg.data.exception, + type: "exception", + }); + } + log.info("Successfully extracted zip file: " + zipURI); + return deferredPromise.resolve(msg.data.extractedPaths); + }; + worker.postMessage({ zipURI, relativeInstallPath }); + return this._deferred.promise; + }, +}; + +/** + * Constructs an object which downloads and initiates an install of + * the specified GMPAddon object. + * @param gmpAddon The addon to install. + */ +export function GMPDownloader(gmpAddon) { + this._gmpAddon = gmpAddon; +} + +GMPDownloader.prototype = { + /** + * Starts the download process for an addon. + * @return a promise which will be resolved or rejected + * See GMPInstallManager.installAddon for resolve/rejected info + */ + start() { + let log = getScopedLogger("GMPDownloader"); + let gmpAddon = this._gmpAddon; + let now = Math.round(Date.now() / 1000); + GMPPrefs.setInt(GMPPrefs.KEY_PLUGIN_LAST_INSTALL_START, now, gmpAddon.id); + + if (!gmpAddon.isValid) { + log.info("gmpAddon is not valid, will not continue"); + return Promise.reject({ + target: this, + type: "downloaderr", + }); + } + // If the HTTPS-Only Mode is enabled, every insecure request gets upgraded + // by default. This upgrade has to be prevented for openh264 downloads since + // the server doesn't support https:// + const downloadOptions = { + httpsOnlyNoUpgrade: gmpAddon.isOpenH264, + }; + return ProductAddonChecker.downloadAddon(gmpAddon, downloadOptions).then( + zipPath => { + let now = Math.round(Date.now() / 1000); + GMPPrefs.setInt(GMPPrefs.KEY_PLUGIN_LAST_DOWNLOAD, now, gmpAddon.id); + log.info( + `install to directory path: ${gmpAddon.id}/${gmpAddon.version}` + ); + let gmpInstaller = new GMPExtractor(zipPath, [ + gmpAddon.id, + gmpAddon.version, + ]); + let installPromise = gmpInstaller.install(); + return installPromise.then( + extractedPaths => { + // Success, set the prefs + let now = Math.round(Date.now() / 1000); + GMPPrefs.setInt(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, now, gmpAddon.id); + // Remember our ABI, so that if the profile is migrated to another + // platform or from 32 -> 64 bit, we notice and don't try to load the + // unexecutable plugin library. + let abi = GMPUtils._expectedABI(gmpAddon); + log.info("Setting ABI to '" + abi + "' for " + gmpAddon.id); + GMPPrefs.setString(GMPPrefs.KEY_PLUGIN_ABI, abi, gmpAddon.id); + // We use the combination of the hash and version to ensure we are + // up to date. + GMPPrefs.setString( + GMPPrefs.KEY_PLUGIN_HASHVALUE, + gmpAddon.hashValue, + gmpAddon.id + ); + // Setting the version pref signals installation completion to consumers, + // if you need to set other prefs etc. do it before this. + GMPPrefs.setString( + GMPPrefs.KEY_PLUGIN_VERSION, + gmpAddon.version, + gmpAddon.id + ); + return extractedPaths; + }, + reason => { + GMPPrefs.setString( + GMPPrefs.KEY_PLUGIN_LAST_INSTALL_FAIL_REASON, + reason, + gmpAddon.id + ); + let now = Math.round(Date.now() / 1000); + GMPPrefs.setInt( + GMPPrefs.KEY_PLUGIN_LAST_INSTALL_FAILED, + now, + gmpAddon.id + ); + throw reason; + } + ); + }, + reason => { + GMPPrefs.setString( + GMPPrefs.KEY_PLUGIN_LAST_DOWNLOAD_FAIL_REASON, + reason, + gmpAddon.id + ); + let now = Math.round(Date.now() / 1000); + GMPPrefs.setInt( + GMPPrefs.KEY_PLUGIN_LAST_DOWNLOAD_FAILED, + now, + gmpAddon.id + ); + throw reason; + } + ); + }, +}; diff --git a/toolkit/modules/GMPUtils.sys.mjs b/toolkit/modules/GMPUtils.sys.mjs new file mode 100644 index 0000000000..211c30a45f --- /dev/null +++ b/toolkit/modules/GMPUtils.sys.mjs @@ -0,0 +1,263 @@ +/* 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 { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; + +const lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs", +}); + +// GMP IDs +export const OPEN_H264_ID = "gmp-gmpopenh264"; +export const WIDEVINE_L1_ID = "gmp-widevinecdm-l1"; +export const WIDEVINE_L3_ID = "gmp-widevinecdm"; + +export const GMP_PLUGIN_IDS = [OPEN_H264_ID, WIDEVINE_L1_ID, WIDEVINE_L3_ID]; + +export var GMPUtils = { + /** + * Checks whether or not a given plugin is hidden. Hidden plugins are neither + * downloaded nor displayed in the addons manager. + * @param aPlugin + * The plugin to check. + */ + isPluginHidden(aPlugin) { + if (!this._isPluginSupported(aPlugin) || !this._isPluginVisible(aPlugin)) { + return true; + } + + if (!aPlugin.isEME) { + return false; + } + + if (!GMPPrefs.getBool(GMPPrefs.KEY_EME_ENABLED, true)) { + return true; + } + + return false; + }, + + /** + * Checks whether or not a given plugin is supported by the current OS. + * @param aPlugin + * The plugin to check. + */ + _isPluginSupported(aPlugin) { + if (this._isPluginForceSupported(aPlugin)) { + return true; + } + if (aPlugin.id == WIDEVINE_L1_ID) { + // The Widevine L1 plugin is currently only available for Windows x64. + return ( + AppConstants.MOZ_WMF_CDM && + AppConstants.platform == "win" && + lazy.UpdateUtils.ABI.match(/x64/) + ); + } + if (aPlugin.id == WIDEVINE_L1_ID || aPlugin.id == WIDEVINE_L3_ID) { + // The Widevine plugin is available for Windows versions Vista and later, + // Mac OSX, and Linux. + return ( + AppConstants.platform == "win" || + AppConstants.platform == "macosx" || + AppConstants.platform == "linux" + ); + } + + return true; + }, + + /** + * Checks whether or not a given plugin is visible in the addons manager + * UI and the "enable DRM" notification box. This can be used to test + * plugins that aren't yet turned on in the mozconfig. + * @param aPlugin + * The plugin to check. + */ + _isPluginVisible(aPlugin) { + return GMPPrefs.getBool(GMPPrefs.KEY_PLUGIN_VISIBLE, false, aPlugin.id); + }, + + /** + * Checks whether or not a given plugin is forced-supported. This is used + * in automated tests to override the checks that prevent GMPs running on an + * unsupported platform. + * @param aPlugin + * The plugin to check. + */ + _isPluginForceSupported(aPlugin) { + return GMPPrefs.getBool( + GMPPrefs.KEY_PLUGIN_FORCE_SUPPORTED, + false, + aPlugin.id + ); + }, + + _isWindowsOnARM64() { + return ( + AppConstants.platform == "win" && lazy.UpdateUtils.ABI.match(/aarch64/) + ); + }, + + _expectedABI(aPlugin) { + let defaultABI = lazy.UpdateUtils.ABI; + let expectedABIs = [defaultABI]; + if (aPlugin.id == WIDEVINE_L3_ID && this._isWindowsOnARM64()) { + // On Windows on aarch64, we may use either the x86 or the ARM64 plugin + // as we are still shipping the former to release. + expectedABIs.push(defaultABI.replace(/aarch64/g, "x86")); + } + return expectedABIs.join(","); + }, +}; + +/** + * Manages preferences for GMP addons + */ +export var GMPPrefs = { + KEY_EME_ENABLED: "media.eme.enabled", + KEY_PLUGIN_ENABLED: "media.{0}.enabled", + KEY_PLUGIN_LAST_DOWNLOAD: "media.{0}.lastDownload", + KEY_PLUGIN_LAST_DOWNLOAD_FAILED: "media.{0}.lastDownloadFailed", + KEY_PLUGIN_LAST_DOWNLOAD_FAIL_REASON: "media.{0}.lastDownloadFailReason", + KEY_PLUGIN_LAST_INSTALL_FAILED: "media.{0}.lastInstallFailed", + KEY_PLUGIN_LAST_INSTALL_FAIL_REASON: "media.{0}.lastInstallFailReason", + KEY_PLUGIN_LAST_INSTALL_START: "media.{0}.lastInstallStart", + KEY_PLUGIN_LAST_UPDATE: "media.{0}.lastUpdate", + KEY_PLUGIN_HASHVALUE: "media.{0}.hashValue", + KEY_PLUGIN_VERSION: "media.{0}.version", + KEY_PLUGIN_AUTOUPDATE: "media.{0}.autoupdate", + KEY_PLUGIN_VISIBLE: "media.{0}.visible", + KEY_PLUGIN_ABI: "media.{0}.abi", + KEY_PLUGIN_FORCE_SUPPORTED: "media.{0}.forceSupported", + KEY_PLUGIN_FORCE_INSTALL: "media.{0}.forceInstall", + KEY_PLUGIN_ALLOW_X64_ON_ARM64: "media.{0}.allow-x64-plugin-on-arm64", + KEY_URL: "media.gmp-manager.url", + KEY_URL_OVERRIDE: "media.gmp-manager.url.override", + KEY_CERT_CHECKATTRS: "media.gmp-manager.cert.checkAttributes", + KEY_CERT_REQUIREBUILTIN: "media.gmp-manager.cert.requireBuiltIn", + KEY_CHECK_CONTENT_SIGNATURE: "media.gmp-manager.checkContentSignature", + KEY_UPDATE_LAST_CHECK: "media.gmp-manager.lastCheck", + KEY_UPDATE_LAST_EMPTY_CHECK: "media.gmp-manager.lastEmptyCheck", + KEY_SECONDS_BETWEEN_CHECKS: "media.gmp-manager.secondsBetweenChecks", + KEY_UPDATE_ENABLED: "media.gmp-manager.updateEnabled", + KEY_APP_DISTRIBUTION: "distribution.id", + KEY_APP_DISTRIBUTION_VERSION: "distribution.version", + KEY_BUILDID: "media.gmp-manager.buildID", + KEY_CERTS_BRANCH: "media.gmp-manager.certs.", + KEY_PROVIDER_ENABLED: "media.gmp-provider.enabled", + KEY_LOG_BASE: "media.gmp.log.", + KEY_LOGGING_LEVEL: "media.gmp.log.level", + KEY_LOGGING_DUMP: "media.gmp.log.dump", + + /** + * Obtains the specified string preference in relation to the specified plugin. + * @param aKey The preference key value to use. + * @param aDefaultValue The default value if no preference exists. + * @param aPlugin The plugin to scope the preference to. + * @return The obtained preference value, or the defaultValue if none exists. + */ + getString(aKey, aDefaultValue, aPlugin) { + if ( + aKey === this.KEY_APP_DISTRIBUTION || + aKey === this.KEY_APP_DISTRIBUTION_VERSION + ) { + return Services.prefs.getDefaultBranch(null).getCharPref(aKey, "default"); + } + return Services.prefs.getStringPref( + this.getPrefKey(aKey, aPlugin), + aDefaultValue + ); + }, + + /** + * Obtains the specified int preference in relation to the specified plugin. + * @param aKey The preference key value to use. + * @param aDefaultValue The default value if no preference exists. + * @param aPlugin The plugin to scope the preference to. + * @return The obtained preference value, or the defaultValue if none exists. + */ + getInt(aKey, aDefaultValue, aPlugin) { + return Services.prefs.getIntPref( + this.getPrefKey(aKey, aPlugin), + aDefaultValue + ); + }, + + /** + * Obtains the specified bool preference in relation to the specified plugin. + * @param aKey The preference key value to use. + * @param aDefaultValue The default value if no preference exists. + * @param aPlugin The plugin to scope the preference to. + * @return The obtained preference value, or the defaultValue if none exists. + */ + getBool(aKey, aDefaultValue, aPlugin) { + return Services.prefs.getBoolPref( + this.getPrefKey(aKey, aPlugin), + aDefaultValue + ); + }, + + /** + * Sets the specified string preference in relation to the specified plugin. + * @param aKey The preference key value to use. + * @param aVal The value to set. + * @param aPlugin The plugin to scope the preference to. + */ + setString(aKey, aVal, aPlugin) { + Services.prefs.setStringPref(this.getPrefKey(aKey, aPlugin), aVal); + }, + + /** + * Sets the specified bool preference in relation to the specified plugin. + * @param aKey The preference key value to use. + * @param aVal The value to set. + * @param aPlugin The plugin to scope the preference to. + */ + setBool(aKey, aVal, aPlugin) { + Services.prefs.setBoolPref(this.getPrefKey(aKey, aPlugin), aVal); + }, + + /** + * Sets the specified int preference in relation to the specified plugin. + * @param aKey The preference key value to use. + * @param aVal The value to set. + * @param aPlugin The plugin to scope the preference to. + */ + setInt(aKey, aVal, aPlugin) { + Services.prefs.setIntPref(this.getPrefKey(aKey, aPlugin), aVal); + }, + + /** + * Checks whether or not the specified preference is set in relation to the + * specified plugin. + * @param aKey The preference key value to use. + * @param aPlugin The plugin to scope the preference to. + * @return true if the preference is set, false otherwise. + */ + isSet(aKey, aPlugin) { + return Services.prefs.prefHasUserValue(this.getPrefKey(aKey, aPlugin)); + }, + + /** + * Resets the specified preference in relation to the specified plugin to its + * default. + * @param aKey The preference key value to use. + * @param aPlugin The plugin to scope the preference to. + */ + reset(aKey, aPlugin) { + Services.prefs.clearUserPref(this.getPrefKey(aKey, aPlugin)); + }, + + /** + * Scopes the specified preference key to the specified plugin. + * @param aKey The preference key value to use. + * @param aPlugin The plugin to scope the preference to. + * @return A preference key scoped to the specified plugin. + */ + getPrefKey(aKey, aPlugin) { + return aKey.replace("{0}", aPlugin || ""); + }, +}; diff --git a/toolkit/modules/Geometry.sys.mjs b/toolkit/modules/Geometry.sys.mjs new file mode 100644 index 0000000000..1eae68ce2a --- /dev/null +++ b/toolkit/modules/Geometry.sys.mjs @@ -0,0 +1,389 @@ +/* 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/. */ + +/** + * Simple Point class. + * + * Any method that takes an x and y may also take a point. + */ +export function Point(x, y) { + this.set(x, y); +} + +Point.prototype = { + clone: function clone() { + return new Point(this.x, this.y); + }, + + set: function set(x, y) { + this.x = x; + this.y = y; + return this; + }, + + equals: function equals(x, y) { + return this.x == x && this.y == y; + }, + + toString: function toString() { + return "(" + this.x + "," + this.y + ")"; + }, + + map: function map(f) { + this.x = f.call(this, this.x); + this.y = f.call(this, this.y); + return this; + }, + + add: function add(x, y) { + this.x += x; + this.y += y; + return this; + }, + + subtract: function subtract(x, y) { + this.x -= x; + this.y -= y; + return this; + }, + + scale: function scale(s) { + this.x *= s; + this.y *= s; + return this; + }, + + isZero() { + return this.x == 0 && this.y == 0; + }, +}; + +(function () { + function takePointOrArgs(f) { + return function (arg1, arg2) { + if (arg2 === undefined) { + return f.call(this, arg1.x, arg1.y); + } + return f.call(this, arg1, arg2); + }; + } + + for (let f of ["add", "subtract", "equals", "set"]) { + Point.prototype[f] = takePointOrArgs(Point.prototype[f]); + } +})(); + +/** + * Rect is a simple data structure for representation of a rectangle supporting + * many basic geometric operations. + * + * NOTE: Since its operations are closed, rectangles may be empty and will report + * non-positive widths and heights in that case. + */ + +export function Rect(x, y, w, h) { + this.left = x; + this.top = y; + this.right = x + w; + this.bottom = y + h; +} + +Rect.fromRect = function fromRect(r) { + return new Rect(r.left, r.top, r.right - r.left, r.bottom - r.top); +}; + +Rect.prototype = { + get x() { + return this.left; + }, + get y() { + return this.top; + }, + get width() { + return this.right - this.left; + }, + get height() { + return this.bottom - this.top; + }, + set x(v) { + let diff = this.left - v; + this.left = v; + this.right -= diff; + }, + set y(v) { + let diff = this.top - v; + this.top = v; + this.bottom -= diff; + }, + set width(v) { + this.right = this.left + v; + }, + set height(v) { + this.bottom = this.top + v; + }, + + isEmpty: function isEmpty() { + return this.left >= this.right || this.top >= this.bottom; + }, + + setRect(x, y, w, h) { + this.left = x; + this.top = y; + this.right = x + w; + this.bottom = y + h; + + return this; + }, + + setBounds(l, t, r, b) { + this.top = t; + this.left = l; + this.bottom = b; + this.right = r; + + return this; + }, + + equals: function equals(other) { + return ( + other != null && + ((this.isEmpty() && other.isEmpty()) || + (this.top == other.top && + this.left == other.left && + this.bottom == other.bottom && + this.right == other.right)) + ); + }, + + clone: function clone() { + return new Rect( + this.left, + this.top, + this.right - this.left, + this.bottom - this.top + ); + }, + + center: function center() { + if (this.isEmpty()) { + throw new Error("Empty rectangles do not have centers"); + } + return new Point( + this.left + (this.right - this.left) / 2, + this.top + (this.bottom - this.top) / 2 + ); + }, + + copyFrom(other) { + this.top = other.top; + this.left = other.left; + this.bottom = other.bottom; + this.right = other.right; + + return this; + }, + + translate(x, y) { + this.left += x; + this.right += x; + this.top += y; + this.bottom += y; + + return this; + }, + + toString() { + return ( + "[" + this.x + "," + this.y + "," + this.width + "," + this.height + "]" + ); + }, + + /** return a new rect that is the union of that one and this one */ + union(other) { + return this.clone().expandToContain(other); + }, + + contains(other) { + if (other.isEmpty()) { + return true; + } + if (this.isEmpty()) { + return false; + } + + return ( + other.left >= this.left && + other.right <= this.right && + other.top >= this.top && + other.bottom <= this.bottom + ); + }, + + intersect(other) { + return this.clone().restrictTo(other); + }, + + intersects(other) { + if (this.isEmpty() || other.isEmpty()) { + return false; + } + + let x1 = Math.max(this.left, other.left); + let x2 = Math.min(this.right, other.right); + let y1 = Math.max(this.top, other.top); + let y2 = Math.min(this.bottom, other.bottom); + return x1 < x2 && y1 < y2; + }, + + /** Restrict area of this rectangle to the intersection of both rectangles. */ + restrictTo: function restrictTo(other) { + if (this.isEmpty() || other.isEmpty()) { + return this.setRect(0, 0, 0, 0); + } + + let x1 = Math.max(this.left, other.left); + let x2 = Math.min(this.right, other.right); + let y1 = Math.max(this.top, other.top); + let y2 = Math.min(this.bottom, other.bottom); + // If width or height is 0, the intersection was empty. + return this.setRect(x1, y1, Math.max(0, x2 - x1), Math.max(0, y2 - y1)); + }, + + /** Expand this rectangle to the union of both rectangles. */ + expandToContain: function expandToContain(other) { + if (this.isEmpty()) { + return this.copyFrom(other); + } + if (other.isEmpty()) { + return this; + } + + let l = Math.min(this.left, other.left); + let r = Math.max(this.right, other.right); + let t = Math.min(this.top, other.top); + let b = Math.max(this.bottom, other.bottom); + return this.setRect(l, t, r - l, b - t); + }, + + /** + * Expands to the smallest rectangle that contains original rectangle and is bounded + * by lines with integer coefficients. + */ + expandToIntegers: function round() { + this.left = Math.floor(this.left); + this.top = Math.floor(this.top); + this.right = Math.ceil(this.right); + this.bottom = Math.ceil(this.bottom); + return this; + }, + + scale: function scale(xscl, yscl) { + this.left *= xscl; + this.right *= xscl; + this.top *= yscl; + this.bottom *= yscl; + return this; + }, + + map: function map(f) { + this.left = f.call(this, this.left); + this.top = f.call(this, this.top); + this.right = f.call(this, this.right); + this.bottom = f.call(this, this.bottom); + return this; + }, + + /** Ensure this rectangle is inside the other, if possible. Preserves w, h. */ + translateInside: function translateInside(other) { + let offsetX = 0; + if (this.left <= other.left) { + offsetX = other.left - this.left; + } else if (this.right > other.right) { + offsetX = other.right - this.right; + } + + let offsetY = 0; + if (this.top <= other.top) { + offsetY = other.top - this.top; + } else if (this.bottom > other.bottom) { + offsetY = other.bottom - this.bottom; + } + + return this.translate(offsetX, offsetY); + }, + + /** Subtract other area from this. Returns array of rects whose union is this-other. */ + subtract: function subtract(other) { + let r = new Rect(0, 0, 0, 0); + let result = []; + other = other.intersect(this); + if (other.isEmpty()) { + return [this.clone()]; + } + + // left strip + r.setBounds(this.left, this.top, other.left, this.bottom); + if (!r.isEmpty()) { + result.push(r.clone()); + } + // inside strip + r.setBounds(other.left, this.top, other.right, other.top); + if (!r.isEmpty()) { + result.push(r.clone()); + } + r.setBounds(other.left, other.bottom, other.right, this.bottom); + if (!r.isEmpty()) { + result.push(r.clone()); + } + // right strip + r.setBounds(other.right, this.top, this.right, this.bottom); + if (!r.isEmpty()) { + result.push(r.clone()); + } + + return result; + }, + + /** + * Blends two rectangles together. + * @param rect Rectangle to blend this one with + * @param scalar Ratio from 0 (returns a clone of this rect) to 1 (clone of rect). + * @return New blended rectangle. + */ + blend: function blend(rect, scalar) { + return new Rect( + this.left + (rect.left - this.left) * scalar, + this.top + (rect.top - this.top) * scalar, + this.width + (rect.width - this.width) * scalar, + this.height + (rect.height - this.height) * scalar + ); + }, + + /** + * Grows or shrinks the rectangle while keeping the center point. + * Accepts single multipler, or separate for both axes. + */ + inflate: function inflate(xscl, yscl) { + let xAdj = (this.width * xscl - this.width) / 2; + let s = arguments.length > 1 ? yscl : xscl; + let yAdj = (this.height * s - this.height) / 2; + this.left -= xAdj; + this.right += xAdj; + this.top -= yAdj; + this.bottom += yAdj; + return this; + }, + + /** + * Grows or shrinks the rectangle by fixed amount while keeping the center point. + * Accepts single fixed amount + */ + inflateFixed: function inflateFixed(fixed) { + this.left -= fixed; + this.right += fixed; + this.top -= fixed; + this.bottom += fixed; + return this; + }, +}; diff --git a/toolkit/modules/HiddenFrame.sys.mjs b/toolkit/modules/HiddenFrame.sys.mjs new file mode 100644 index 0000000000..23ad57c0d3 --- /dev/null +++ b/toolkit/modules/HiddenFrame.sys.mjs @@ -0,0 +1,119 @@ +/* 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/. */ + +const XUL_PAGE = Services.io.newURI("chrome://global/content/win.xhtml"); + +const gAllHiddenFrames = new Set(); + +let cleanupRegistered = false; +function ensureCleanupRegistered() { + if (!cleanupRegistered) { + cleanupRegistered = true; + Services.obs.addObserver(function () { + for (let hiddenFrame of gAllHiddenFrames) { + hiddenFrame.destroy(); + } + }, "xpcom-shutdown"); + } +} + +/** + * An hidden frame object. It takes care of creating a windowless browser and + * passing the window containing a blank XUL <window> back. + */ +export function HiddenFrame() {} + +HiddenFrame.prototype = { + _frame: null, + _browser: null, + _listener: null, + _webProgress: null, + _deferred: null, + + /** + * Gets the |contentWindow| of the hidden frame. Creates the frame if needed. + * @returns Promise Returns a promise which is resolved when the hidden frame has finished + * loading. + */ + get() { + if (!this._deferred) { + this._deferred = Promise.withResolvers(); + this._create(); + } + + return this._deferred.promise; + }, + + /** + * Fetch a sync ref to the window inside the frame (needed for the add-on SDK). + */ + getWindow() { + this.get(); + return this._browser.document.ownerGlobal; + }, + + destroy() { + if (this._browser) { + if (this._listener) { + this._webProgress.removeProgressListener(this._listener); + this._listener = null; + this._webProgress = null; + } + this._frame = null; + this._deferred = null; + + gAllHiddenFrames.delete(this); + this._browser.close(); + this._browser = null; + } + }, + + _create() { + ensureCleanupRegistered(); + let chromeFlags = Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW; + if (Services.appinfo.fissionAutostart) { + chromeFlags |= Ci.nsIWebBrowserChrome.CHROME_FISSION_WINDOW; + } + this._browser = Services.appShell.createWindowlessBrowser( + true, + chromeFlags + ); + this._browser.QueryInterface(Ci.nsIInterfaceRequestor); + gAllHiddenFrames.add(this); + this._webProgress = this._browser.getInterface(Ci.nsIWebProgress); + this._listener = { + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsIWebProgressListener2", + "nsISupportsWeakReference", + ]), + }; + this._listener.onStateChange = (wbp, request, stateFlags, status) => { + if (!request) { + return; + } + if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) { + this._webProgress.removeProgressListener(this._listener); + this._listener = null; + this._webProgress = null; + // Get the window reference via the document. + this._frame = this._browser.document.ownerGlobal; + this._deferred.resolve(this._frame); + } + }; + this._webProgress.addProgressListener( + this._listener, + Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT + ); + let docShell = this._browser.docShell; + let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal(); + docShell.createAboutBlankDocumentViewer(systemPrincipal, systemPrincipal); + let browsingContext = this._browser.browsingContext; + browsingContext.useGlobalHistory = false; + let loadURIOptions = { + triggeringPrincipal: systemPrincipal, + }; + this._browser.loadURI(XUL_PAGE, loadURIOptions); + }, +}; diff --git a/toolkit/modules/IgnoreLists.sys.mjs b/toolkit/modules/IgnoreLists.sys.mjs new file mode 100644 index 0000000000..1b7282d77f --- /dev/null +++ b/toolkit/modules/IgnoreLists.sys.mjs @@ -0,0 +1,93 @@ +/* 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/. */ + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + RemoteSettings: "resource://services-settings/remote-settings.sys.mjs", + RemoteSettingsClient: + "resource://services-settings/RemoteSettingsClient.sys.mjs", +}); + +const SETTINGS_IGNORELIST_KEY = "hijack-blocklists"; + +class IgnoreListsManager { + async init() { + if (!this._ignoreListSettings) { + this._ignoreListSettings = lazy.RemoteSettings(SETTINGS_IGNORELIST_KEY); + } + } + + async getAndSubscribe(listener) { + await this.init(); + + // Trigger a get of the initial value. + const settings = await this._getIgnoreList(); + + // Listen for future updates after we first get the values. + this._ignoreListSettings.on("sync", listener); + + return settings; + } + + unsubscribe(listener) { + if (!this._ignoreListSettings) { + return; + } + + this._ignoreListSettings.off("sync", listener); + } + + async _getIgnoreList() { + if (this._getSettingsPromise) { + return this._getSettingsPromise; + } + + const settings = await (this._getSettingsPromise = + this._getIgnoreListSettings()); + delete this._getSettingsPromise; + return settings; + } + + /** + * Obtains the current ignore list from remote settings. This includes + * verifying the signature of the ignore list within the database. + * + * If the signature in the database is invalid, the database will be wiped + * and the stored dump will be used, until the settings next update. + * + * Note that this may cause a network check of the certificate, but that + * should generally be quick. + * + * @param {boolean} [firstTime] + * Internal boolean to indicate if this is the first time check or not. + * @returns {array} + * An array of objects in the database, or an empty array if none + * could be obtained. + */ + async _getIgnoreListSettings(firstTime = true) { + let result = []; + try { + result = await this._ignoreListSettings.get({ + verifySignature: true, + }); + } catch (ex) { + if ( + ex instanceof lazy.RemoteSettingsClient.InvalidSignatureError && + firstTime + ) { + // The local database is invalid, try and reset it. + await this._ignoreListSettings.db.clear(); + // Now call this again. + return this._getIgnoreListSettings(false); + } + // Don't throw an error just log it, just continue with no data, and hopefully + // a sync will fix things later on. + console.error(ex); + } + return result; + } +} + +export const IgnoreLists = new IgnoreListsManager(); diff --git a/toolkit/modules/IndexedDB.sys.mjs b/toolkit/modules/IndexedDB.sys.mjs new file mode 100644 index 0000000000..b3b9f81b8d --- /dev/null +++ b/toolkit/modules/IndexedDB.sys.mjs @@ -0,0 +1,431 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* 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/. */ + +/** + * @file + * + * This module provides Promise-based wrappers around ordinarily + * IDBRequest-based IndexedDB methods and classes. + */ + +/** + * Wraps the given request object, and returns a Promise which resolves when + * the requests succeeds or rejects when it fails. + * + * @param {IDBRequest} request + * An IndexedDB request object to wrap. + * @returns {Promise} + */ +function wrapRequest(request) { + return new Promise((resolve, reject) => { + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }); +} + +/** + * Forwards a set of getter properties from a wrapper class to the wrapped + * object. + * + * @param {function} cls + * The class constructor for which to forward the getters. + * @param {string} target + * The name of the property which contains the wrapped object to which + * to forward the getters. + * @param {Array<string>} props + * A list of property names to forward. + */ +function forwardGetters(cls, target, props) { + for (let prop of props) { + Object.defineProperty(cls.prototype, prop, { + get() { + return this[target][prop]; + }, + }); + } +} + +/** + * Forwards a set of getter and setter properties from a wrapper class to the + * wrapped object. + * + * @param {function} cls + * The class constructor for which to forward the properties. + * @param {string} target + * The name of the property which contains the wrapped object to which + * to forward the properties. + * @param {Array<string>} props + * A list of property names to forward. + */ +function forwardProps(cls, target, props) { + for (let prop of props) { + Object.defineProperty(cls.prototype, prop, { + get() { + return this[target][prop]; + }, + set(value) { + this[target][prop] = value; + }, + }); + } +} + +/** + * Wraps a set of IDBRequest-based methods via {@link wrapRequest} and + * forwards them to the equivalent methods on the wrapped object. + * + * @param {function} cls + * The class constructor for which to forward the methods. + * @param {string} target + * The name of the property which contains the wrapped object to which + * to forward the methods. + * @param {Array<string>} methods + * A list of method names to forward. + */ +function wrapMethods(cls, target, methods) { + for (let method of methods) { + cls.prototype[method] = function (...args) { + return wrapRequest(this[target][method](...args)); + }; + } +} + +/** + * Forwards a set of methods from a wrapper class to the wrapped object. + * + * @param {function} cls + * The class constructor for which to forward the getters. + * @param {string} target + * The name of the property which contains the wrapped object to which + * to forward the methods. + * @param {Array<string>} methods + * A list of method names to forward. + */ +function forwardMethods(cls, target, methods) { + for (let method of methods) { + cls.prototype[method] = function (...args) { + return this[target][method](...args); + }; + } +} + +class Cursor { + constructor(cursorRequest, source) { + this.cursorRequest = cursorRequest; + this.source = source; + this.cursor = null; + } + + get done() { + return !this.cursor; + } + + // This method is used internally to wait the cursor's IDBRequest to have been + // completed and the internal cursor has been updated (used when we initially + // create the cursor from Cursed.openCursor/openKeyCursor, and in the method + // of this class defined by defineCursorUpdateMethods). + async awaitRequest() { + this.cursor = await wrapRequest(this.cursorRequest); + return this; + } +} + +/** + * Define the Cursor class methods that update the cursor (continue, continuePrimaryKey + * and advance) as async functions that call the related IDBCursor methods and + * await the cursor's IDBRequest to be completed. + * + * @param {function} cls + * The class constructor for which to define the cursor update methods. + * @param {Array<string>} methods + * A list of "cursor update" method names to define. + */ +function defineCursorUpdateMethods(cls, methods) { + for (let method of methods) { + cls.prototype[method] = async function (...args) { + const promise = this.awaitRequest(); + this.cursor[method](...args); + await promise; + }; + } +} + +defineCursorUpdateMethods(Cursor, [ + "advance", + "continue", + "continuePrimaryKey", +]); + +forwardGetters(Cursor, "cursor", ["direction", "key", "primaryKey"]); +wrapMethods(Cursor, "cursor", ["delete", "update"]); + +class CursorWithValue extends Cursor {} + +forwardGetters(CursorWithValue, "cursor", ["value"]); + +class Cursed { + constructor(cursed) { + this.cursed = cursed; + } + + openCursor(...args) { + const cursor = new CursorWithValue(this.cursed.openCursor(...args), this); + return cursor.awaitRequest(); + } + + openKeyCursor(...args) { + const cursor = new Cursor(this.cursed.openKeyCursor(...args), this); + return cursor.awaitRequest(); + } +} + +wrapMethods(Cursed, "cursed", [ + "count", + "get", + "getAll", + "getAllKeys", + "getKey", +]); + +class Index extends Cursed { + constructor(index, objectStore) { + super(index); + + this.objectStore = objectStore; + this.index = index; + } +} + +forwardGetters(Index, "index", [ + "isAutoLocale", + "keyPath", + "locale", + "multiEntry", + "name", + "unique", +]); + +class ObjectStore extends Cursed { + constructor(store) { + super(store); + + this.store = store; + } + + createIndex(...args) { + return new Index(this.store.createIndex(...args), this); + } + + index(...args) { + return new Index(this.store.index(...args), this); + } +} + +wrapMethods(ObjectStore, "store", ["add", "clear", "delete", "put"]); + +forwardMethods(ObjectStore, "store", ["deleteIndex"]); + +class Transaction { + constructor(transaction) { + this.transaction = transaction; + + this._completionPromise = new Promise((resolve, reject) => { + transaction.oncomplete = resolve; + transaction.onerror = () => { + reject(transaction.error); + }; + transaction.onabort = () => { + const error = + transaction.error || + new DOMException("The operation has been aborted", "AbortError"); + reject(error); + }; + }); + } + + objectStore(name) { + return new ObjectStore(this.transaction.objectStore(name)); + } + + /** + * Returns a Promise which resolves when the transaction completes, or + * rejects when a transaction error or abort occurs. + * + * @returns {Promise} + */ + promiseComplete() { + return this._completionPromise; + } +} + +forwardGetters(Transaction, "transaction", [ + "db", + "mode", + "error", + "objectStoreNames", +]); + +forwardMethods(Transaction, "transaction", ["abort"]); + +export class IndexedDB { + /** + * Opens the database with the given name, and returns a Promise which + * resolves to an IndexedDB instance when the operation completes. + * + * @param {string} dbName + * The name of the database to open. + * @param {object} options + * The options with which to open the database. + * @param {integer} options.version + * The schema version with which the database needs to be opened. If + * the database does not exist, or its current schema version does + * not match, the `onupgradeneeded` function will be called. + * @param {function} [onupgradeneeded] + * A function which will be called with an IndexedDB object as its + * first parameter when the database needs to be created, or its + * schema needs to be upgraded. If this function is not provided, the + * {@link #onupgradeneeded} method will be called instead. + * + * @returns {Promise<IndexedDB>} + */ + static open(dbName, options, onupgradeneeded = null) { + let request = indexedDB.open(dbName, options); + return this._wrapOpenRequest(request, onupgradeneeded); + } + + /** + * Opens the database for a given principal and with the given name, returns + * a Promise which resolves to an IndexedDB instance when the operation completes. + * + * @param {nsIPrincipal} principal + * The principal to open the database for. + * @param {string} dbName + * The name of the database to open. + * @param {object} options + * The options with which to open the database. + * @param {integer} options.version + * The schema version with which the database needs to be opened. If + * the database does not exist, or its current schema version does + * not match, the `onupgradeneeded` function will be called. + * @param {function} [onupgradeneeded] + * A function which will be called with an IndexedDB object as its + * first parameter when the database needs to be created, or its + * schema needs to be upgraded. If this function is not provided, the + * {@link #onupgradeneeded} method will be called instead. + * + * @returns {Promise<IndexedDB>} + */ + static openForPrincipal(principal, dbName, options, onupgradeneeded = null) { + const request = indexedDB.openForPrincipal(principal, dbName, options); + return this._wrapOpenRequest(request, onupgradeneeded); + } + + static _wrapOpenRequest(request, onupgradeneeded = null) { + request.onupgradeneeded = event => { + let db = new this(request.result); + if (onupgradeneeded) { + onupgradeneeded(db, event); + } else { + db.onupgradeneeded(event); + } + }; + + return wrapRequest(request).then(db => new this(db)); + } + + constructor(db) { + this.db = db; + } + + onupgradeneeded() {} + + /** + * Opens a transaction for the given object stores. + * + * @param {Array<string>} storeNames + * The names of the object stores for which to open a transaction. + * @param {string} [mode = "readonly"] + * The mode in which to open the transaction. + * @param {function} [callback] + * An optional callback function. If provided, the function will be + * called with the Transaction, and a Promise will be returned, which + * will resolve to the callback's return value when the transaction + * completes. + * @returns {Transaction|Promise} + */ + transaction(storeNames, mode, callback = null) { + let transaction = new Transaction(this.db.transaction(storeNames, mode)); + + if (callback) { + let result = new Promise(resolve => { + resolve(callback(transaction)); + }); + return transaction.promiseComplete().then(() => result); + } + + return transaction; + } + + /** + * Opens a transaction for a single object store, and returns that object + * store. + * + * @param {string} storeName + * The name of the object store to open. + * @param {string} [mode = "readonly"] + * The mode in which to open the transaction. + * @param {function} [callback] + * An optional callback function. If provided, the function will be + * called with the ObjectStore, and a Promise will be returned, which + * will resolve to the callback's return value when the transaction + * completes. + * @returns {ObjectStore|Promise} + */ + objectStore(storeName, mode, callback = null) { + let transaction = this.transaction([storeName], mode); + let objectStore = transaction.objectStore(storeName); + + if (callback) { + let result = new Promise(resolve => { + resolve(callback(objectStore)); + }); + return transaction.promiseComplete().then(() => result); + } + + return objectStore; + } + + createObjectStore(...args) { + return new ObjectStore(this.db.createObjectStore(...args)); + } +} + +for (let method of ["cmp", "deleteDatabase"]) { + IndexedDB[method] = function (...args) { + return indexedDB[method](...args); + }; +} + +forwardMethods(IndexedDB, "db", [ + "addEventListener", + "close", + "deleteObjectStore", + "hasEventListener", + "removeEventListener", +]); + +forwardGetters(IndexedDB, "db", ["name", "objectStoreNames", "version"]); + +forwardProps(IndexedDB, "db", [ + "onabort", + "onclose", + "onerror", + "onversionchange", +]); diff --git a/toolkit/modules/InlineSpellChecker.sys.mjs b/toolkit/modules/InlineSpellChecker.sys.mjs new file mode 100644 index 0000000000..7d70c6a89a --- /dev/null +++ b/toolkit/modules/InlineSpellChecker.sys.mjs @@ -0,0 +1,626 @@ +/* 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/. */ + +const MAX_UNDO_STACK_DEPTH = 1; + +export function InlineSpellChecker(aEditor) { + this.init(aEditor); + this.mAddedWordStack = []; // We init this here to preserve it between init/uninit calls +} + +InlineSpellChecker.prototype = { + // Call this function to initialize for a given editor + init(aEditor) { + this.uninit(); + this.mEditor = aEditor; + try { + this.mInlineSpellChecker = this.mEditor.getInlineSpellChecker(true); + // note: this might have been NULL if there is no chance we can spellcheck + } catch (e) { + this.mInlineSpellChecker = null; + } + }, + + initFromRemote(aSpellInfo, aWindowGlobalParent) { + if (this.mRemote) { + // We shouldn't get here, but let's just recover instead of bricking the + // menu by throwing exceptions: + console.error(new Error("Unexpected remote spellchecker present!")); + try { + this.mRemote.uninit(); + } catch (ex) { + console.error(ex); + } + this.mRemote = null; + } + this.uninit(); + + if (!aSpellInfo) { + return; + } + this.mInlineSpellChecker = this.mRemote = new RemoteSpellChecker( + aSpellInfo, + aWindowGlobalParent + ); + this.mOverMisspelling = aSpellInfo.overMisspelling; + this.mMisspelling = aSpellInfo.misspelling; + }, + + // call this to clear state + uninit() { + if (this.mRemote) { + this.mRemote.uninit(); + this.mRemote = null; + } + + this.mEditor = null; + this.mInlineSpellChecker = null; + this.mOverMisspelling = false; + this.mMisspelling = ""; + this.mMenu = null; + this.mSuggestionItems = []; + this.mDictionaryMenu = null; + this.mDictionaryItems = []; + this.mWordNode = null; + }, + + // for each UI event, you must call this function, it will compute the + // word the cursor is over + initFromEvent(rangeParent, rangeOffset) { + this.mOverMisspelling = false; + + if (!rangeParent || !this.mInlineSpellChecker) { + return; + } + + var selcon = this.mEditor.selectionController; + var spellsel = selcon.getSelection(selcon.SELECTION_SPELLCHECK); + if (spellsel.rangeCount == 0) { + return; + } // easy case - no misspellings + + var range = this.mInlineSpellChecker.getMisspelledWord( + rangeParent, + rangeOffset + ); + if (!range) { + return; + } // not over a misspelled word + + this.mMisspelling = range.toString(); + this.mOverMisspelling = true; + this.mWordNode = rangeParent; + this.mWordOffset = rangeOffset; + }, + + // returns false if there should be no spellchecking UI enabled at all, true + // means that you can at least give the user the ability to turn it on. + get canSpellCheck() { + // inline spell checker objects will be created only if there are actual + // dictionaries available + if (this.mRemote) { + return this.mRemote.canSpellCheck; + } + return this.mInlineSpellChecker != null; + }, + + get initialSpellCheckPending() { + if (this.mRemote) { + return this.mRemote.spellCheckPending; + } + return !!( + this.mInlineSpellChecker && + !this.mInlineSpellChecker.spellChecker && + this.mInlineSpellChecker.spellCheckPending + ); + }, + + // Whether spellchecking is enabled in the current box + get enabled() { + if (this.mRemote) { + return this.mRemote.enableRealTimeSpell; + } + return ( + this.mInlineSpellChecker && this.mInlineSpellChecker.enableRealTimeSpell + ); + }, + set enabled(isEnabled) { + if (this.mRemote) { + this.mRemote.setSpellcheckUserOverride(isEnabled); + } else if (this.mInlineSpellChecker) { + this.mEditor.setSpellcheckUserOverride(isEnabled); + } + }, + + // returns true if the given event is over a misspelled word + get overMisspelling() { + return this.mOverMisspelling; + }, + + // this prepends up to "maxNumber" suggestions at the given menu position + // for the word under the cursor. Returns the number of suggestions inserted. + addSuggestionsToMenuOnParent(menu, insertBefore, maxNumber) { + if (this.mRemote) { + // This is used on parent process only. + // If you want to add suggestions to context menu, get suggestions then + // use addSuggestionsToMenu instead. + return 0; + } + if (!this.mInlineSpellChecker || !this.mOverMisspelling) { + return 0; + } + + let spellchecker = this.mInlineSpellChecker.spellChecker; + let spellSuggestions = []; + + try { + if (!spellchecker.CheckCurrentWord(this.mMisspelling)) { + return 0; + } + + for (let i = 0; i < maxNumber; i++) { + let suggestion = spellchecker.GetSuggestedWord(); + if (!suggestion.length) { + // no more data + break; + } + spellSuggestions.push(suggestion); + } + } catch (e) { + return 0; + } + return this._addSuggestionsToMenu(menu, insertBefore, spellSuggestions); + }, + + addSuggestionsToMenu(menu, insertBefore, spellSuggestions) { + if ( + !this.mRemote && + (!this.mInlineSpellChecker || !this.mOverMisspelling) + ) { + return 0; + } // nothing to do + + if (!spellSuggestions?.length) { + return 0; + } + + return this._addSuggestionsToMenu(menu, insertBefore, spellSuggestions); + }, + + _addSuggestionsToMenu(menu, insertBefore, spellSuggestions) { + this.mMenu = menu; + this.mSuggestionItems = []; + + for (let suggestion of spellSuggestions) { + var item = menu.ownerDocument.createXULElement("menuitem"); + this.mSuggestionItems.push(item); + item.setAttribute("label", suggestion); + item.setAttribute("value", suggestion); + item.addEventListener( + "command", + this.replaceMisspelling.bind(this, suggestion), + true + ); + item.setAttribute("class", "spell-suggestion"); + menu.insertBefore(item, insertBefore); + } + return spellSuggestions.length; + }, + + // undoes the work of addSuggestionsToMenu for the same menu + // (call from popup hiding) + clearSuggestionsFromMenu() { + for (var i = 0; i < this.mSuggestionItems.length; i++) { + this.mMenu.removeChild(this.mSuggestionItems[i]); + } + this.mSuggestionItems = []; + }, + + sortDictionaryList(list) { + var sortedList = []; + var names = Services.intl.getLocaleDisplayNames(undefined, list); + for (var i = 0; i < list.length; i++) { + sortedList.push({ localeCode: list[i], displayName: names[i] }); + } + let comparer = new Services.intl.Collator().compare; + sortedList.sort((a, b) => comparer(a.displayName, b.displayName)); + return sortedList; + }, + + async languageMenuListener(evt) { + let curlangs = new Set(); + if (this.mRemote) { + curlangs = new Set(this.mRemote.currentDictionaries); + } else if (this.mInlineSpellChecker) { + let spellchecker = this.mInlineSpellChecker.spellChecker; + try { + curlangs = new Set(spellchecker.getCurrentDictionaries()); + } catch (e) {} + } + + let localeCodes = new Set(curlangs); + let localeCode = evt.target.dataset.localeCode; + if (localeCodes.has(localeCode)) { + localeCodes.delete(localeCode); + } else { + localeCodes.add(localeCode); + } + let dictionaries = Array.from(localeCodes); + await this.selectDictionaries(dictionaries); + if (this.mRemote) { + // Store the new set in case the menu doesn't close. + this.mRemote.currentDictionaries = dictionaries; + } + // Notify change of dictionary, especially for Thunderbird, + // which is otherwise not notified any more. + let view = this.mDictionaryMenu.ownerGlobal; + let spellcheckChangeEvent = new view.CustomEvent("spellcheck-changed", { + detail: { dictionaries }, + }); + this.mDictionaryMenu.ownerDocument.dispatchEvent(spellcheckChangeEvent); + }, + + // returns the number of dictionary languages. If insertBefore is NULL, this + // does an append to the given menu + addDictionaryListToMenu(menu, insertBefore) { + this.mDictionaryMenu = menu; + this.mDictionaryItems = []; + + if (!this.enabled) { + return 0; + } + + let list; + let curlangs = new Set(); + if (this.mRemote) { + list = this.mRemote.dictionaryList; + curlangs = new Set(this.mRemote.currentDictionaries); + } else if (this.mInlineSpellChecker) { + let spellchecker = this.mInlineSpellChecker.spellChecker; + list = spellchecker.GetDictionaryList(); + try { + curlangs = new Set(spellchecker.getCurrentDictionaries()); + } catch (e) {} + } + + let sortedList = this.sortDictionaryList(list); + this.languageMenuListenerBind = this.languageMenuListener.bind(this); + menu.addEventListener("command", this.languageMenuListenerBind, true); + + for (let i = 0; i < sortedList.length; i++) { + let item = menu.ownerDocument.createXULElement("menuitem"); + + item.setAttribute( + "id", + "spell-check-dictionary-" + sortedList[i].localeCode + ); + // XXX: Once Fluent has dynamic references, we could also lazily + // inject regionNames/languageNames FTL and localize using + // `l10n-id` here. + item.setAttribute("label", sortedList[i].displayName); + item.setAttribute("type", "checkbox"); + item.setAttribute("selection-type", "multiple"); + if (sortedList.length > 1) { + item.setAttribute("closemenu", "none"); + } + this.mDictionaryItems.push(item); + item.dataset.localeCode = sortedList[i].localeCode; + if (curlangs.has(sortedList[i].localeCode)) { + item.setAttribute("checked", "true"); + } + if (insertBefore) { + menu.insertBefore(item, insertBefore); + } else { + menu.appendChild(item); + } + } + return list.length; + }, + + // undoes the work of addDictionaryListToMenu for the menu + // (call on popup hiding) + clearDictionaryListFromMenu() { + this.mDictionaryMenu?.removeEventListener( + "command", + this.languageMenuListenerBind, + true + ); + for (var i = 0; i < this.mDictionaryItems.length; i++) { + this.mDictionaryMenu.removeChild(this.mDictionaryItems[i]); + } + this.mDictionaryItems = []; + }, + + // callback for selecting a dictionary + async selectDictionaries(localeCodes) { + if (this.mRemote) { + this.mRemote.selectDictionaries(localeCodes); + return; + } + if (!this.mInlineSpellChecker) { + return; + } + var spellchecker = this.mInlineSpellChecker.spellChecker; + await spellchecker.setCurrentDictionaries(localeCodes); + this.mInlineSpellChecker.spellCheckRange(null); // causes recheck + }, + + // callback for selecting a suggested replacement + replaceMisspelling(suggestion) { + if (this.mRemote) { + this.mRemote.replaceMisspelling(suggestion); + return; + } + if (!this.mInlineSpellChecker || !this.mOverMisspelling) { + return; + } + this.mInlineSpellChecker.replaceWord( + this.mWordNode, + this.mWordOffset, + suggestion + ); + }, + + // callback for enabling or disabling spellchecking + toggleEnabled() { + if (this.mRemote) { + this.mRemote.toggleEnabled(); + } else { + this.mEditor.setSpellcheckUserOverride( + !this.mInlineSpellChecker.enableRealTimeSpell + ); + } + }, + + // callback for adding the current misspelling to the user-defined dictionary + addToDictionary() { + // Prevent the undo stack from growing over the max depth + if (this.mAddedWordStack.length == MAX_UNDO_STACK_DEPTH) { + this.mAddedWordStack.shift(); + } + + this.mAddedWordStack.push(this.mMisspelling); + if (this.mRemote) { + this.mRemote.addToDictionary(); + } else { + this.mInlineSpellChecker.addWordToDictionary(this.mMisspelling); + } + }, + // callback for removing the last added word to the dictionary LIFO fashion + undoAddToDictionary() { + if (this.mAddedWordStack.length) { + var word = this.mAddedWordStack.pop(); + if (this.mRemote) { + this.mRemote.undoAddToDictionary(word); + } else { + this.mInlineSpellChecker.removeWordFromDictionary(word); + } + } + }, + canUndo() { + // Return true if we have words on the stack + return !!this.mAddedWordStack.length; + }, + ignoreWord() { + if (this.mRemote) { + this.mRemote.ignoreWord(); + } else { + this.mInlineSpellChecker.ignoreWord(this.mMisspelling); + } + }, +}; + +export var SpellCheckHelper = { + // Set when over a non-read-only <textarea> or editable <input> + // (that allows text entry of some kind, so not e.g. <input type=checkbox>) + EDITABLE: 0x1, + + // Set when over an <input> element of any type. + INPUT: 0x2, + + // Set when over any <textarea>. + TEXTAREA: 0x4, + + // Set when over any text-entry <input>. + TEXTINPUT: 0x8, + + // Set when over an <input> that can be used as a keyword field. + KEYWORD: 0x10, + + // Set when over an element that otherwise would not be considered + // "editable" but is because content editable is enabled for the document. + CONTENTEDITABLE: 0x20, + + // Set when over an <input type="number"> or other non-text field. + NUMERIC: 0x40, + + // Set when over an <input type="password"> field. + PASSWORD: 0x80, + + // Set when spellcheckable. Replaces `EDITABLE`/`CONTENTEDITABLE` combination + // specifically for spellcheck. + SPELLCHECKABLE: 0x100, + + isTargetAKeywordField(aNode, window) { + if (!window.HTMLInputElement.isInstance(aNode)) { + return false; + } + + var form = aNode.form; + if (!form || aNode.type == "password") { + return false; + } + + var method = form.method.toUpperCase(); + + // These are the following types of forms we can create keywords for: + // + // method encoding type can create keyword + // GET * YES + // * YES + // POST YES + // POST application/x-www-form-urlencoded YES + // POST text/plain NO (a little tricky to do) + // POST multipart/form-data NO + // POST everything else YES + return ( + method == "GET" || + method == "" || + (form.enctype != "text/plain" && form.enctype != "multipart/form-data") + ); + }, + + // Returns the computed style attribute for the given element. + getComputedStyle(aElem, aProp) { + return aElem.ownerGlobal.getComputedStyle(aElem).getPropertyValue(aProp); + }, + + isEditable(element, window) { + var flags = 0; + if (window.HTMLInputElement.isInstance(element)) { + flags |= this.INPUT; + if (element.mozIsTextField(false) || element.type == "number") { + flags |= this.TEXTINPUT; + if (!element.readOnly) { + flags |= this.EDITABLE; + } + + if (element.type == "number") { + flags |= this.NUMERIC; + } + + // Allow spellchecking UI on all text and search inputs. + if ( + !element.readOnly && + (element.type == "text" || element.type == "search") + ) { + flags |= this.SPELLCHECKABLE; + } + if (this.isTargetAKeywordField(element, window)) { + flags |= this.KEYWORD; + } + if (element.type == "password") { + flags |= this.PASSWORD; + } + } + } else if (window.HTMLTextAreaElement.isInstance(element)) { + flags |= this.TEXTINPUT | this.TEXTAREA; + if (!element.readOnly) { + flags |= this.SPELLCHECKABLE | this.EDITABLE; + } + } + + if (!(flags & this.SPELLCHECKABLE)) { + var win = element.ownerGlobal; + if (win) { + var isSpellcheckable = false; + try { + var editingSession = win.docShell.editingSession; + if ( + editingSession.windowIsEditable(win) && + this.getComputedStyle(element, "-moz-user-modify") == "read-write" + ) { + isSpellcheckable = true; + } + } catch (ex) { + // If someone built with composer disabled, we can't get an editing session. + } + + if (isSpellcheckable) { + flags |= this.CONTENTEDITABLE | this.SPELLCHECKABLE; + } + } + } + + return flags; + }, +}; + +function RemoteSpellChecker(aSpellInfo, aWindowGlobalParent) { + this._spellInfo = aSpellInfo; + this._suggestionGenerator = null; + this._actor = aWindowGlobalParent.getActor("InlineSpellChecker"); + this._actor.registerDestructionObserver(this); +} + +RemoteSpellChecker.prototype = { + get canSpellCheck() { + return this._spellInfo.canSpellCheck; + }, + get spellCheckPending() { + return this._spellInfo.initialSpellCheckPending; + }, + get overMisspelling() { + return this._spellInfo.overMisspelling; + }, + get enableRealTimeSpell() { + return this._spellInfo.enableRealTimeSpell; + }, + get suggestions() { + return this._spellInfo.spellSuggestions; + }, + + get currentDictionaries() { + return this._spellInfo.currentDictionaries; + }, + set currentDictionaries(dicts) { + this._spellInfo.currentDictionaries = dicts; + }, + get dictionaryList() { + return this._spellInfo.dictionaryList.slice(); + }, + + selectDictionaries(localeCodes) { + this._actor.selectDictionaries({ localeCodes }); + }, + + replaceMisspelling(suggestion) { + this._actor.replaceMisspelling({ suggestion }); + }, + + toggleEnabled() { + this._actor.toggleEnabled(); + }, + addToDictionary() { + // This is really ugly. There is an nsISpellChecker somewhere in the + // parent that corresponds to our current element's spell checker in the + // child, but it's hard to access it. However, we know that + // addToDictionary adds the word to the singleton personal dictionary, so + // we just do that here. + // NB: We also rely on the fact that we only ever pass an empty string in + // as the "lang". + + let dictionary = Cc[ + "@mozilla.org/spellchecker/personaldictionary;1" + ].getService(Ci.mozIPersonalDictionary); + dictionary.addWord(this._spellInfo.misspelling); + this._actor.recheckSpelling(); + }, + undoAddToDictionary(word) { + let dictionary = Cc[ + "@mozilla.org/spellchecker/personaldictionary;1" + ].getService(Ci.mozIPersonalDictionary); + dictionary.removeWord(word); + this._actor.recheckSpelling(); + }, + ignoreWord() { + let dictionary = Cc[ + "@mozilla.org/spellchecker/personaldictionary;1" + ].getService(Ci.mozIPersonalDictionary); + dictionary.ignoreWord(this._spellInfo.misspelling); + this._actor.recheckSpelling(); + }, + uninit() { + if (this._actor) { + this._actor.uninit(); + this._actor.unregisterDestructionObserver(this); + } + }, + + actorDestroyed() { + // The actor lets us know if it gets destroyed, so we don't + // later try to call `.uninit()` on it. + this._actor = null; + }, +}; diff --git a/toolkit/modules/InlineSpellCheckerContent.sys.mjs b/toolkit/modules/InlineSpellCheckerContent.sys.mjs new file mode 100644 index 0000000000..b910a54304 --- /dev/null +++ b/toolkit/modules/InlineSpellCheckerContent.sys.mjs @@ -0,0 +1,119 @@ +/* vim: set ts=2 sw=2 sts=2 tw=80: */ +/* 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 { + InlineSpellChecker, + SpellCheckHelper, +} from "resource://gre/modules/InlineSpellChecker.sys.mjs"; + +export var InlineSpellCheckerContent = { + _spellChecker: null, + _actor: null, + + async initContextMenu(event, editFlags, actor) { + this._actor = actor; + this._actor.registerDestructionObserver(this); + + let spellChecker; + if (!(editFlags & (SpellCheckHelper.TEXTAREA | SpellCheckHelper.INPUT))) { + // Get the editor off the window. + let win = event.target.ownerGlobal; + let editingSession = win.docShell.editingSession; + spellChecker = this._spellChecker = new InlineSpellChecker( + editingSession.getEditorForWindow(win) + ); + } else { + // Use the element's editor. + spellChecker = this._spellChecker = new InlineSpellChecker( + event.composedTarget.editor + ); + } + + this._spellChecker.initFromEvent(event.rangeParent, event.rangeOffset); + + if (!spellChecker.canSpellCheck) { + return { + canSpellCheck: false, + initialSpellCheckPending: true, + enableRealTimeSpell: false, + }; + } + + if (!spellChecker.mInlineSpellChecker.enableRealTimeSpell) { + return { + canSpellCheck: true, + initialSpellCheckPending: spellChecker.initialSpellCheckPending, + enableRealTimeSpell: false, + }; + } + + if (spellChecker.initialSpellCheckPending) { + return { + canSpellCheck: true, + initialSpellCheckPending: true, + enableRealTimeSpell: true, + }; + } + + let realSpellChecker = spellChecker.mInlineSpellChecker.spellChecker; + let dictionaryList = realSpellChecker.GetDictionaryList(); + let spellSuggestions = await this._generateSpellSuggestions(); + + return { + canSpellCheck: spellChecker.canSpellCheck, + initialSpellCheckPending: spellChecker.initialSpellCheckPending, + enableRealTimeSpell: spellChecker.enabled, + overMisspelling: spellChecker.overMisspelling, + misspelling: spellChecker.mMisspelling, + spellSuggestions, + currentDictionaries: + spellChecker.mInlineSpellChecker.spellChecker.getCurrentDictionaries(), + dictionaryList, + }; + }, + + uninitContextMenu() { + if (this._actor) { + this._actor.unregisterDestructionObserver(this); + } + this._actor = null; + this._spellChecker = null; + }, + + actorDestroyed() { + this.uninitContextMenu(); + }, + + async _generateSpellSuggestions() { + let spellChecker = this._spellChecker.mInlineSpellChecker.spellChecker; + let suggestions = null; + try { + suggestions = await spellChecker.suggest( + this._spellChecker.mMisspelling, + 5 + ); + } catch (e) { + return []; + } + + return suggestions; + }, + + selectDictionaries(localeCodes) { + this._spellChecker.selectDictionaries(localeCodes); + }, + + replaceMisspelling(suggestion) { + this._spellChecker.replaceMisspelling(suggestion); + }, + + toggleEnabled() { + this._spellChecker.toggleEnabled(); + }, + + recheck() { + this._spellChecker.mInlineSpellChecker.enableRealTimeSpell = true; + }, +}; diff --git a/toolkit/modules/Integration.sys.mjs b/toolkit/modules/Integration.sys.mjs new file mode 100644 index 0000000000..4ae4f89099 --- /dev/null +++ b/toolkit/modules/Integration.sys.mjs @@ -0,0 +1,281 @@ +/* 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/. */ + +/* + * Implements low-overhead integration between components of the application. + * This may have different uses depending on the component, including: + * + * - Providing product-specific implementations registered at startup. + * - Using alternative implementations during unit tests. + * - Allowing add-ons to change specific behaviors. + * + * Components may define one or more integration points, each defined by a + * root integration object whose properties and methods are the public interface + * and default implementation of the integration point. For example: + * + * const DownloadIntegration = { + * getTemporaryDirectory() { + * return "/tmp/"; + * }, + * + * getTemporaryFile(name) { + * return this.getTemporaryDirectory() + name; + * }, + * }; + * + * Other parts of the application may register overrides for some or all of the + * defined properties and methods. The component defining the integration point + * does not have to be loaded at this stage, because the name of the integration + * point is the only information required. For example, if the integration point + * is called "downloads": + * + * Integration.downloads.register(base => ({ + * getTemporaryDirectory() { + * return base.getTemporaryDirectory.call(this) + "subdir/"; + * }, + * })); + * + * When the component defining the integration point needs to call a method on + * the integration object, instead of using it directly the component would use + * the "getCombined" method to retrieve an object that includes all overrides. + * For example: + * + * let combined = Integration.downloads.getCombined(DownloadIntegration); + * Assert.is(combined.getTemporaryFile("file"), "/tmp/subdir/file"); + * + * Overrides can be registered at startup or at any later time, so each call to + * "getCombined" may return a different object. The simplest way to create a + * reference to the combined object that stays updated to the latest version is + * to define the root object in a JSM and use the "defineModuleGetter" method. + * + * *** Registration *** + * + * Since the interface is not declared formally, the registrations can happen + * at startup without loading the component, so they do not affect performance. + * + * Hovever, this module does not provide a startup registry, this means that the + * code that registers and implements the override must be loaded at startup. + * + * If performance for the override code is a concern, you can take advantage of + * the fact that the function used to create the override is called lazily, and + * include only a stub loader for the final code in an existing startup module. + * + * The registration of overrides should be repeated for each process where the + * relevant integration methods will be called. + * + * *** Accessing base methods and properties *** + * + * Overrides are included in the prototype chain of the combined object in the + * same order they were registered, where the first is closest to the root. + * + * When defining overrides, you do not need to manipulate the prototype chain of + * the objects you create, because their properties and methods are moved to a + * new object with the correct prototype. If you do, however, you can call base + * properties and methods using the "super" keyword. For example: + * + * Integration.downloads.register(base => { + * let newObject = { + * getTemporaryDirectory() { + * return super.getTemporaryDirectory() + "subdir/"; + * }, + * }; + * Object.setPrototypeOf(newObject, base); + * return newObject; + * }); + * + * *** State handling *** + * + * Storing state directly on the combined integration object using the "this" + * reference is not recommended. When a new integration is registered, own + * properties stored on the old combined object are copied to the new combined + * object using a shallow copy, but the "this" reference for new invocations + * of the methods will be different. + * + * If the root object defines a property that always points to the same object, + * for example a "state" property, you can safely use it across registrations. + * + * Integration overrides provided by restartless add-ons should not use the + * "this" reference to store state, to avoid conflicts with other add-ons. + * + * *** Interaction with XPCOM *** + * + * Providing the combined object as an argument to any XPCOM method will + * generate a console error message, and will throw an exception where possible. + * For example, you cannot register observers directly on the combined object. + * This helps preventing mistakes due to the fact that the combined object + * reference changes when new integration overrides are registered. + */ + +/** + * Maps integration point names to IntegrationPoint objects. + */ +const gIntegrationPoints = new Map(); + +/** + * This Proxy object creates IntegrationPoint objects using their name as key. + * The objects will be the same for the duration of the process. For example: + * + * Integration.downloads.register(...); + * Integration["addon-provided-integration"].register(...); + */ +export var Integration = new Proxy( + {}, + { + get(target, name) { + let integrationPoint = gIntegrationPoints.get(name); + if (!integrationPoint) { + integrationPoint = new IntegrationPoint(); + gIntegrationPoints.set(name, integrationPoint); + } + return integrationPoint; + }, + } +); + +/** + * Individual integration point for which overrides can be registered. + */ +var IntegrationPoint = function () { + this._overrideFns = new Set(); + this._combined = { + // eslint-disable-next-line mozilla/use-chromeutils-generateqi + QueryInterface() { + let ex = new Components.Exception( + "Integration objects should not be used with XPCOM because" + + " they change when new overrides are registered.", + Cr.NS_ERROR_NO_INTERFACE + ); + console.error(ex); + throw ex; + }, + }; +}; + +IntegrationPoint.prototype = { + /** + * Ordered set of registered functions defining integration overrides. + */ + _overrideFns: null, + + /** + * Combined integration object. When this reference changes, properties + * defined directly on this object are copied to the new object. + * + * Initially, the only property of this object is a "QueryInterface" method + * that throws an exception, to prevent misuse as a permanent XPCOM listener. + */ + _combined: null, + + /** + * Indicates whether the integration object is current based on the list of + * registered integration overrides. + */ + _combinedIsCurrent: false, + + /** + * Registers new overrides for the integration methods. For example: + * + * Integration.nameOfIntegrationPoint.register(base => ({ + * asyncMethod: Task.async(function* () { + * return yield base.asyncMethod.apply(this, arguments); + * }), + * })); + * + * @param overrideFn + * Function returning an object defining the methods that should be + * overridden. Its only parameter is an object that contains the base + * implementation of all the available methods. + * + * @note The override function is called every time the list of registered + * override functions changes. Thus, it should not have any side + * effects or do any other initialization. + */ + register(overrideFn) { + this._overrideFns.add(overrideFn); + this._combinedIsCurrent = false; + }, + + /** + * Removes a previously registered integration override. + * + * Overrides don't usually need to be unregistered, unless they are added by a + * restartless add-on, in which case they should be unregistered when the + * add-on is disabled or uninstalled. + * + * @param overrideFn + * This must be the same function object passed to "register". + */ + unregister(overrideFn) { + this._overrideFns.delete(overrideFn); + this._combinedIsCurrent = false; + }, + + /** + * Retrieves the dynamically generated object implementing the integration + * methods. Platform-specific code and add-ons can override methods of this + * object using the "register" method. + */ + getCombined(root) { + if (this._combinedIsCurrent) { + return this._combined; + } + + // In addition to enumerating all the registered integration overrides in + // order, we want to keep any state that was previously stored in the + // combined object using the "this" reference in integration methods. + let overrideFnArray = [...this._overrideFns, () => this._combined]; + + let combined = root; + for (let overrideFn of overrideFnArray) { + try { + // Obtain a new set of methods from the next override function in the + // list, specifying the current combined object as the base argument. + let override = overrideFn(combined); + + // Retrieve a list of property descriptors from the returned object, and + // use them to build a new combined object whose prototype points to the + // previous combined object. + let descriptors = {}; + for (let name of Object.getOwnPropertyNames(override)) { + descriptors[name] = Object.getOwnPropertyDescriptor(override, name); + } + combined = Object.create(combined, descriptors); + } catch (ex) { + // Any error will result in the current override being skipped. + console.error(ex); + } + } + + this._combinedIsCurrent = true; + return (this._combined = combined); + }, + + /** + * Defines a getter to retrieve the dynamically generated object implementing + * the integration methods, loading the root implementation lazily from the + * specified sys.mjs module. For example: + * + * Integration.test.defineModuleGetter(this, "TestIntegration", + * "resource://testing-common/TestIntegration.sys.mjs"); + * + * @param targetObject + * The object on which the lazy getter will be defined. + * @param name + * The name of the getter to define. + * @param moduleUrl + * The URL used to obtain the module. + */ + defineESModuleGetter(targetObject, name, moduleUrl) { + let moduleHolder = {}; + // eslint-disable-next-line mozilla/lazy-getter-object-name + ChromeUtils.defineESModuleGetters(moduleHolder, { + [name]: moduleUrl, + }); + Object.defineProperty(targetObject, name, { + get: () => this.getCombined(moduleHolder[name]), + configurable: true, + enumerable: true, + }); + }, +}; diff --git a/toolkit/modules/JSONFile.sys.mjs b/toolkit/modules/JSONFile.sys.mjs new file mode 100644 index 0000000000..39a8e89cc4 --- /dev/null +++ b/toolkit/modules/JSONFile.sys.mjs @@ -0,0 +1,495 @@ +/* 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/. */ + +/** + * Handles serialization of the data and persistence into a file. + * + * This modules handles the raw data stored in JavaScript serializable objects, + * and contains no special validation or query logic, that is handled entirely + * by "storage.js" instead. + * + * The data can be manipulated only after it has been loaded from disk. The + * load process can happen asynchronously, through the "load" method, or + * synchronously, through "ensureDataReady". After any modification, the + * "saveSoon" method must be called to flush the data to disk asynchronously. + * + * The raw data should be manipulated synchronously, without waiting for the + * event loop or for promise resolution, so that the saved file is always + * consistent. This synchronous approach also simplifies the query and update + * logic. For example, it is possible to find an object and modify it + * immediately without caring whether other code modifies it in the meantime. + * + * An asynchronous shutdown observer makes sure that data is always saved before + * the browser is closed. The data cannot be modified during shutdown. + * + * The file is stored in JSON format, without indentation, using UTF-8 encoding. + */ + +// Globals + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs", + FileUtils: "resource://gre/modules/FileUtils.sys.mjs", + NetUtil: "resource://gre/modules/NetUtil.sys.mjs", +}); + +ChromeUtils.defineLazyGetter(lazy, "gTextDecoder", function () { + return new TextDecoder(); +}); + +const FileInputStream = Components.Constructor( + "@mozilla.org/network/file-input-stream;1", + "nsIFileInputStream", + "init" +); + +/** + * Delay between a change to the data and the related save operation. + */ +const kSaveDelayMs = 1500; + +/** + * Cleansed basenames of the filenames that telemetry can be recorded for. + * Keep synchronized with 'objects' from Events.yaml. + */ +const TELEMETRY_BASENAMES = new Set(["logins", "autofillprofiles"]); + +// JSONFile + +/** + * Handles serialization of the data and persistence into a file. + * + * @param config An object containing following members: + * - path: String containing the file path where data should be saved. + * - sanitizedBasename: Sanitized string identifier used for logging, + * shutdown debugging, and telemetry. Defaults to + * basename of given `path`, sanitized. + * - dataPostProcessor: Function triggered when data is just loaded. The + * data object will be passed as the first argument + * and should be returned no matter it's modified or + * not. Its failure leads to the failure of load() + * and ensureDataReady(). + * - saveDelayMs: Number indicating the delay (in milliseconds) between a + * change to the data and the related save operation. The + * default value will be applied if omitted. + * - beforeSave: Promise-returning function triggered just before the + * data is written to disk. This can be used to create any + * intermediate directories before saving. The file will + * not be saved if the promise rejects or the function + * throws an exception. + * - finalizeAt: An `IOUtils` phase or barrier client that should + * automatically finalize the file when triggered. Defaults + * to `profileBeforeChange`; exposed as an option for + * testing. + * - compression: A compression algorithm to use when reading and + * writing the data. + * - backupTo: A string value indicating where writeAtomic should create + * a backup before writing to json files. Note that using this + * option currently ensures that we automatically restore backed + * up json files in load() and ensureDataReady() when original + * files are missing or corrupt. + */ +export function JSONFile(config) { + this.path = config.path; + this.sanitizedBasename = + config.sanitizedBasename ?? + PathUtils.filename(this.path) + .replace(/\.json(.lz4)?$/, "") + .replaceAll(/[^a-zA-Z0-9_.]/g, ""); + + if (typeof config.dataPostProcessor === "function") { + this._dataPostProcessor = config.dataPostProcessor; + } + if (typeof config.beforeSave === "function") { + this._beforeSave = config.beforeSave; + } + + if (config.saveDelayMs === undefined) { + config.saveDelayMs = kSaveDelayMs; + } + this._saver = new lazy.DeferredTask(() => this._save(), config.saveDelayMs); + + this._options = {}; + if (config.compression) { + this._options.decompress = this._options.compress = true; + } + + if (config.backupTo) { + this._options.backupFile = this._options.backupTo = config.backupTo; + } + + this._finalizeAt = config.finalizeAt || IOUtils.profileBeforeChange; + this._finalizeInternalBound = this._finalizeInternal.bind(this); + this._finalizeAt.addBlocker( + `JSON store: writing data for '${this.sanitizedBasename}'`, + this._finalizeInternalBound, + () => ({ sanitizedBasename: this.sanitizedBasename }) + ); + + Services.telemetry.setEventRecordingEnabled("jsonfile", true); +} + +JSONFile.prototype = { + /** + * String containing the file path where data should be saved. + */ + path: "", + + /** + * Sanitized identifier used for logging, shutdown debugging, and telemetry. + */ + sanitizedBasename: "", + + /** + * True when data has been loaded. + */ + dataReady: false, + + /** + * DeferredTask that handles the save operation. + */ + _saver: null, + + /** + * Internal data object. + */ + _data: null, + + /** + * Internal fields used during finalization. + */ + _finalizeAt: null, + _finalizePromise: null, + _finalizeInternalBound: null, + + /** + * Serializable object containing the data. This is populated directly with + * the data loaded from the file, and is saved without modifications. + * + * The raw data should be manipulated synchronously, without waiting for the + * event loop or for promise resolution, so that the saved file is always + * consistent. + */ + get data() { + if (!this.dataReady) { + throw new Error("Data is not ready."); + } + return this._data; + }, + + /** + * Sets the loaded data to a new object. This will overwrite any persisted + * data on the next save. + */ + set data(data) { + this._data = data; + this.dataReady = true; + }, + + /** + * Loads persistent data from the file to memory. + * + * @return {Promise} + * @resolves When the operation finished successfully. + * @rejects JavaScript exception when dataPostProcessor fails. It never fails + * if there is no dataPostProcessor. + */ + async load() { + if (this.dataReady) { + return; + } + + let data = {}; + + try { + data = await IOUtils.readJSON(this.path, this._options); + + // If synchronous loading happened in the meantime, exit now. + if (this.dataReady) { + return; + } + } catch (ex) { + // If an exception occurs because the file does not exist or it cannot be read, + // we do two things. + // 1. For consumers of JSONFile.sys.mjs that have configured a `backupTo` path option, + // we try to look for and use backed up json files first. If the backup + // is also not found or if the backup is unreadable, we then start with an empty file. + // 2. If a consumer does not configure a `backupTo` path option, we just start + // with an empty file. + + // In the event that the file exists, but an exception is thrown because it cannot be read, + // we store it as a .corrupt file for debugging purposes. + + let errorNo = ex.winLastError || ex.unixErrno; + this._recordTelemetry("load", errorNo ? errorNo.toString() : ""); + if (!(DOMException.isInstance(ex) && ex.name == "NotFoundError")) { + console.error(ex); + + // Move the original file to a backup location, ignoring errors. + try { + let uniquePath = await IOUtils.createUniqueFile( + PathUtils.parent(this.path), + PathUtils.filename(this.path) + ".corrupt", + 0o600 + ); + await IOUtils.move(this.path, uniquePath); + this._recordTelemetry("load", "invalid_json"); + } catch (e2) { + console.error(e2); + } + } + + if (this._options.backupFile) { + // Restore the original file from the backup here so fresh writes to empty + // json files don't happen at any time in the future compromising the backup + // in the process. + try { + await IOUtils.copy(this._options.backupFile, this.path); + } catch (e) { + if (!(DOMException.isInstance(e) && e.name == "NotFoundError")) { + console.error(e); + } + } + + try { + // We still read from the backup file here instead of the original file in case + // access to the original file is blocked, e.g. by anti-virus software on the + // user's computer. + data = await IOUtils.readJSON( + this._options.backupFile, + this._options + ); + // If synchronous loading happened in the meantime, exit now. + if (this.dataReady) { + return; + } + this._recordTelemetry("load", "used_backup"); + } catch (e3) { + if (!(DOMException.isInstance(e3) && e3.name == "NotFoundError")) { + console.error(e3); + } + } + } + + // In some rare cases it's possible for data to have been added to + // our database between the call to IOUtils.read and when we've been + // notified that there was a problem with it. In that case, leave the + // synchronously-added data alone. + if (this.dataReady) { + return; + } + } + + this._processLoadedData(data); + }, + + /** + * Loads persistent data from the file to memory, synchronously. An exception + * can be thrown only if dataPostProcessor exists and fails. + */ + ensureDataReady() { + if (this.dataReady) { + return; + } + + let data = {}; + + try { + // This reads the file and automatically detects the UTF-8 encoding. + let inputStream = new FileInputStream( + new lazy.FileUtils.File(this.path), + lazy.FileUtils.MODE_RDONLY, + lazy.FileUtils.PERMS_FILE, + 0 + ); + try { + let bytes = lazy.NetUtil.readInputStream( + inputStream, + inputStream.available() + ); + data = JSON.parse(lazy.gTextDecoder.decode(bytes)); + } finally { + inputStream.close(); + } + } catch (ex) { + // If an exception occurs because the file does not exist or it cannot be read, + // we do two things. + // 1. For consumers of JSONFile.sys.mjs that have configured a `backupTo` path option, + // we try to look for and use backed up json files first. If the backup + // is also not found or if the backup is unreadable, we then start with an empty file. + // 2. If a consumer does not configure a `backupTo` path option, we just start + // with an empty file. + + // In the event that the file exists, but an exception is thrown because it cannot be read, + // we store it as a .corrupt file for debugging purposes. + if ( + !( + ex instanceof Components.Exception && + ex.result == Cr.NS_ERROR_FILE_NOT_FOUND + ) + ) { + console.error(ex); + // Move the original file to a backup location, ignoring errors. + try { + let originalFile = new lazy.FileUtils.File(this.path); + let backupFile = originalFile.clone(); + backupFile.leafName += ".corrupt"; + backupFile.createUnique( + Ci.nsIFile.NORMAL_FILE_TYPE, + lazy.FileUtils.PERMS_FILE + ); + backupFile.remove(false); + originalFile.moveTo(backupFile.parent, backupFile.leafName); + } catch (e2) { + console.error(e2); + } + } + + if (this._options.backupFile) { + // Restore the original file from the backup here so fresh writes to empty + // json files don't happen at any time in the future compromising the backup + // in the process. + try { + let basename = PathUtils.filename(this.path); + let backupFile = new lazy.FileUtils.File(this._options.backupFile); + backupFile.copyTo(null, basename); + } catch (e) { + if (e.result != Cr.NS_ERROR_FILE_NOT_FOUND) { + console.error(e); + } + } + + try { + // We still read from the backup file here instead of the original file in case + // access to the original file is blocked, e.g. by anti-virus software on the + // user's computer. + // This reads the file and automatically detects the UTF-8 encoding. + let inputStream = new FileInputStream( + new lazy.FileUtils.File(this._options.backupFile), + lazy.FileUtils.MODE_RDONLY, + lazy.FileUtils.PERMS_FILE, + 0 + ); + try { + let bytes = lazy.NetUtil.readInputStream( + inputStream, + inputStream.available() + ); + data = JSON.parse(lazy.gTextDecoder.decode(bytes)); + } finally { + inputStream.close(); + } + } catch (e3) { + if (e3.result != Cr.NS_ERROR_FILE_NOT_FOUND) { + console.error(e3); + } + } + } + } + + this._processLoadedData(data); + }, + + /** + * Called when the data changed, this triggers asynchronous serialization. + */ + saveSoon() { + return this._saver.arm(); + }, + + /** + * Saves persistent data from memory to the file. + * + * If an error occurs, the previous file is not deleted. + * + * @return {Promise} + * @resolves When the operation finished successfully. + * @rejects JavaScript exception. + */ + async _save() { + // Create or overwrite the file. + if (this._beforeSave) { + await Promise.resolve(this._beforeSave()); + } + + try { + await IOUtils.writeJSON( + this.path, + this._data, + Object.assign({ tmpPath: this.path + ".tmp" }, this._options) + ); + } catch (ex) { + if (typeof this._data.toJSONSafe == "function") { + // If serialization fails, try fallback safe JSON converter. + await IOUtils.writeUTF8( + this.path, + this._data.toJSONSafe(), + Object.assign({ tmpPath: this.path + ".tmp" }, this._options) + ); + } + } + }, + + /** + * Synchronously work on the data just loaded into memory. + */ + _processLoadedData(data) { + if (this._finalizePromise) { + // It's possible for `load` to race with `finalize`. In that case, don't + // process or set the loaded data. + return; + } + this.data = this._dataPostProcessor ? this._dataPostProcessor(data) : data; + }, + + _recordTelemetry(method, value) { + if (!TELEMETRY_BASENAMES.has(this.sanitizedBasename)) { + // Avoid recording so we don't log an error in the console. + return; + } + + Services.telemetry.recordEvent( + "jsonfile", + method, + this.sanitizedBasename, + value + ); + }, + + /** + * Finishes persisting data to disk and resets all state for this file. + * + * @return {Promise} + * @resolves When the object is finalized. + */ + _finalizeInternal() { + if (this._finalizePromise) { + // Finalization already in progress; return the pending promise. This is + // possible if `finalize` is called concurrently with shutdown. + return this._finalizePromise; + } + this._finalizePromise = (async () => { + await this._saver.finalize(); + this._data = null; + this.dataReady = false; + })(); + return this._finalizePromise; + }, + + /** + * Ensures that all data is persisted to disk, and prevents future calls to + * `saveSoon`. This is called automatically on shutdown, but can also be + * called explicitly when the file is no longer needed. + */ + async finalize() { + if (this._finalizePromise) { + throw new Error(`The file ${this.path} has already been finalized`); + } + // Wait for finalization before removing the shutdown blocker. + await this._finalizeInternal(); + this._finalizeAt.removeBlocker(this._finalizeInternalBound); + }, +}; diff --git a/toolkit/modules/JsonSchema.sys.mjs b/toolkit/modules/JsonSchema.sys.mjs new file mode 100644 index 0000000000..62b063ec8a --- /dev/null +++ b/toolkit/modules/JsonSchema.sys.mjs @@ -0,0 +1,176 @@ +/* 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/. */ + +/** + * A facade around @cfworker/json-schema that provides additional formats and + * convenience methods whil executing inside a sandbox. + */ + +const sandbox = new Cu.Sandbox(null, { + wantComponents: false, + wantGlobalProperties: ["URL"], +}); + +Services.scriptloader.loadSubScript( + "chrome://global/content/third_party/cfworker/json-schema.js", + sandbox +); + +/** + * A JSON Schema string format for URLs intended to go through Services.urlFormatter. + */ +Cu.exportFunction( + function validateMozUrlFormat(input) { + try { + const formatted = Services.urlFormatter.formatURL(input); + return Cu.waiveXrays(sandbox.fastFormat).uri(formatted); + } catch { + return false; + } + }, + sandbox.fastFormat, + { defineAs: "moz-url-format" } +); + +// initialBaseURI defaults to github.com/cfworker, which will be confusing. +Cu.evalInSandbox( + `this.initialBaseURI = initialBaseURI = new URL("http://mozilla.org");`, + sandbox +); + +/** + * A JSONSchema validator that performs validation inside a sandbox. + */ +class Validator { + #inner; + #draft; + + /** + * Create a new validator. + * + * @param {object} schema The schema to validate with. + * @param {object} options Options for the validator. + * @param {string} options.draft The draft to validate against. Should be one + * of "4", "6", "7", "2019-09", or "2020-12". + * + * If the |$schema| key is present in the + * |schema|, it will be used to auto-detect the + * correct version. Otherwise, 2019-09 will be + * used. + * @param {boolean} options.shortCircuit Whether or not the validator should + * return after a single error occurs. + */ + constructor( + schema, + { draft = detectSchemaDraft(schema), shortCircuit = true } = {} + ) { + this.#draft = draft; + this.#inner = Cu.waiveXrays( + new sandbox.Validator(Cu.cloneInto(schema, sandbox), draft, shortCircuit) + ); + } + + /** + * Validate the instance against the known schemas. + * + * @param {object} instance The instance to validate. + * + * @return {object} An object with |valid| and |errors| keys that indicates + * the success of validation. + */ + validate(instance) { + return this.#inner.validate(Cu.cloneInto(instance, sandbox)); + } + + /** + * Add a schema to the validator. + * + * @param {object} schema A JSON schema object. + * @param {string} id An optional ID to identify the schema if it does not + * provide an |$id| field. + */ + addSchema(schema, id) { + const draft = detectSchemaDraft(schema, undefined); + if (draft && this.#draft != draft) { + console.error( + `Adding a draft "${draft}" schema to a draft "${ + this.#draft + }" validator.` + ); + } + this.#inner.addSchema(Cu.cloneInto(schema, sandbox), id); + } +} + +/** + * A wrapper around validate that provides some options as an object + * instead of positional arguments. + * + * @param {object} instance The instance to validate. + * @param {object} schema The JSON schema to validate against. + * @param {object} options Options for the validator. + * @param {string} options.draft The draft to validate against. Should + * be one of "4", "6", "7", "2019-09", or "2020-12". + * + * If the |$schema| key is present in the |schema|, it + * will be used to auto-detect the correct version. + * Otherwise, 2019-09 will be used. + * @param {boolean} options.shortCircuit Whether or not the validator should + * return after a single error occurs. + * + * @returns {object} An object with |valid| and |errors| keys that indicates the + * success of validation. + */ +function validate( + instance, + schema, + { draft = detectSchemaDraft(schema), shortCircuit = true } = {} +) { + const clonedSchema = Cu.cloneInto(schema, sandbox); + + return sandbox.validate( + Cu.cloneInto(instance, sandbox), + clonedSchema, + draft, + sandbox.dereference(clonedSchema), + shortCircuit + ); +} + +function detectSchemaDraft(schema, defaultDraft = "2019-09") { + const { $schema } = schema; + + if (typeof $schema === "undefined") { + return defaultDraft; + } + + switch ($schema) { + case "http://json-schema.org/draft-04/schema#": + return "4"; + + case "http://json-schema.org/draft-06/schema#": + return "6"; + + case "http://json-schema.org/draft-07/schema#": + return "7"; + + case "https://json-schema.org/draft/2019-09/schema": + return "2019-09"; + + case "https://json-schema.org/draft/2020-12/schema": + return "2020-12"; + + default: + console.error( + `Unexpected $schema "${$schema}", defaulting to ${defaultDraft}.` + ); + return defaultDraft; + } +} + +export const JsonSchema = { + Validator, + validate, + detectSchemaDraft, +}; diff --git a/toolkit/modules/KeywordUtils.sys.mjs b/toolkit/modules/KeywordUtils.sys.mjs new file mode 100644 index 0000000000..2765dd3ce9 --- /dev/null +++ b/toolkit/modules/KeywordUtils.sys.mjs @@ -0,0 +1,119 @@ +/* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs", +}); + +export var KeywordUtils = { + /** + * Replaces %s or %S in the provided url or postData with the given parameter, + * acccording to the best charset for the given url. + * + * @return [url, postData] + * @throws if nor url nor postData accept a param, but a param was provided. + */ + async parseUrlAndPostData(url, postData, param) { + let hasGETParam = /%s/i.test(url); + let decodedPostData = postData ? unescape(postData) : ""; + let hasPOSTParam = /%s/i.test(decodedPostData); + + if (!hasGETParam && !hasPOSTParam) { + if (param) { + // If nor the url, nor postData contain parameters, but a parameter was + // provided, return the original input. + throw new Error( + "A param was provided but there's nothing to bind it to" + ); + } + return [url, postData]; + } + + let charset = ""; + const re = /^(.*)\&mozcharset=([a-zA-Z][_\-a-zA-Z0-9]+)\s*$/; + let matches = url.match(re); + if (matches) { + [, url, charset] = matches; + } else { + // Try to fetch a charset from History. + try { + // Will return an empty string if character-set is not found. + let pageInfo = await lazy.PlacesUtils.history.fetch(url, { + includeAnnotations: true, + }); + if ( + pageInfo && + pageInfo.annotations.has(lazy.PlacesUtils.CHARSET_ANNO) + ) { + charset = pageInfo.annotations.get(lazy.PlacesUtils.CHARSET_ANNO); + } + } catch (ex) { + // makeURI() throws if url is invalid. + console.error(ex); + } + } + + // encodeURIComponent produces UTF-8, and cannot be used for other charsets. + // escape() works in those cases, but it doesn't uri-encode +, @, and /. + // Therefore we need to manually replace these ASCII characters by their + // encodeURIComponent result, to match the behavior of nsEscape() with + // url_XPAlphas. + let encodedParam = ""; + if (charset && charset != "UTF-8") { + try { + let converter = Cc[ + "@mozilla.org/intl/scriptableunicodeconverter" + ].createInstance(Ci.nsIScriptableUnicodeConverter); + converter.charset = charset; + encodedParam = converter.ConvertFromUnicode(param) + converter.Finish(); + } catch (ex) { + encodedParam = param; + } + encodedParam = escape(encodedParam).replace( + /[+@\/]+/g, + encodeURIComponent + ); + } else { + // Default charset is UTF-8 + encodedParam = encodeURIComponent(param); + } + + url = url.replace(/%s/g, encodedParam).replace(/%S/g, param); + if (hasPOSTParam) { + postData = decodedPostData + .replace(/%s/g, encodedParam) + .replace(/%S/g, param); + } + return [url, postData]; + }, + + /** + * Returns a set of parameters if a keyword is registered and the search + * string can be bound to it. + * + * @param {string} keyword The typed keyword. + * @param {string} searchString The full search string, including the keyword. + * @returns { entry, url, postData } + */ + async getBindableKeyword(keyword, searchString) { + let entry = await lazy.PlacesUtils.keywords.fetch(keyword); + if (!entry) { + return {}; + } + + try { + let [url, postData] = await this.parseUrlAndPostData( + entry.url.href, + entry.postData, + searchString + ); + return { entry, url, postData }; + } catch (ex) { + return {}; + } + }, +}; diff --git a/toolkit/modules/LayoutUtils.sys.mjs b/toolkit/modules/LayoutUtils.sys.mjs new file mode 100644 index 0000000000..3358093f9c --- /dev/null +++ b/toolkit/modules/LayoutUtils.sys.mjs @@ -0,0 +1,65 @@ +/* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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 var LayoutUtils = { + /** + * For a given DOM element, returns its position in screen coordinates of CSS units + * (<https://developer.mozilla.org/en-US/docs/Web/CSS/CSSOM_View/Coordinate_systems#screen>). + */ + getElementBoundingScreenRect(aElement) { + let rect = aElement.getBoundingClientRect(); + let win = aElement.ownerGlobal; + + const { x, y, width, height } = this._rectToClientRect(win, rect); + return win.windowUtils.toScreenRectInCSSUnits(x, y, width, height); + }, + + /** + * Similar to getElementBoundingScreenRect using window and rect, + * returns screen coordinates in screen units. + */ + rectToScreenRect(win, rect) { + const { x, y, width, height } = this._rectToClientRect(win, rect); + return win.ownerGlobal.windowUtils.toScreenRect(x, y, width, height); + }, + + _rectToClientRect(win, rect) { + // We need to compensate the position for ancestor iframes in the same + // process that might shift things over. Those might have different CSS + // pixel scales, so we compute the position in device pixels and then go + // back to css pixels at the end. + let winDpr = win.devicePixelRatio; + let x = rect.left * winDpr; + let y = rect.top * winDpr; + + let parentFrame = win.browsingContext?.embedderElement; + while (parentFrame) { + win = parentFrame.ownerGlobal; + let cstyle = win.getComputedStyle(parentFrame); + + let framerect = parentFrame.getBoundingClientRect(); + let xDelta = + framerect.left + + parseFloat(cstyle.borderLeftWidth) + + parseFloat(cstyle.paddingLeft); + let yDelta = + framerect.top + + parseFloat(cstyle.borderTopWidth) + + parseFloat(cstyle.paddingTop); + + x += xDelta * win.devicePixelRatio; + y += yDelta * win.devicePixelRatio; + + parentFrame = win.browsingContext?.embedderElement; + } + + return { + x: x / winDpr, + y: y / winDpr, + width: rect.width, + height: rect.height, + }; + }, +}; diff --git a/toolkit/modules/LightweightThemeConsumer.sys.mjs b/toolkit/modules/LightweightThemeConsumer.sys.mjs new file mode 100644 index 0000000000..cf388eb3d3 --- /dev/null +++ b/toolkit/modules/LightweightThemeConsumer.sys.mjs @@ -0,0 +1,719 @@ +/* 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 { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const lazy = {}; +// Get the theme variables from the app resource directory. +// This allows per-app variables. +ChromeUtils.defineESModuleGetters(lazy, { + NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs", + PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", + ThemeContentPropertyList: "resource:///modules/ThemeVariableMap.sys.mjs", + ThemeVariableMap: "resource:///modules/ThemeVariableMap.sys.mjs", +}); + +// Whether the content and chrome areas should always use the same color +// scheme (unless user-overridden). Thunderbird uses this. +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "BROWSER_THEME_UNIFIED_COLOR_SCHEME", + "browser.theme.unified-color-scheme", + false +); + +const DEFAULT_THEME_ID = "default-theme@mozilla.org"; + +const toolkitVariableMap = [ + [ + "--lwt-accent-color", + { + lwtProperty: "accentcolor", + processColor(rgbaChannels, element) { + if (!rgbaChannels || rgbaChannels.a == 0) { + return "white"; + } + // Remove the alpha channel + const { r, g, b } = rgbaChannels; + return `rgb(${r}, ${g}, ${b})`; + }, + }, + ], + [ + "--lwt-text-color", + { + lwtProperty: "textcolor", + processColor(rgbaChannels, element) { + if (!rgbaChannels) { + rgbaChannels = { r: 0, g: 0, b: 0 }; + } + // Remove the alpha channel + const { r, g, b } = rgbaChannels; + return `rgba(${r}, ${g}, ${b})`; + }, + }, + ], + [ + "--arrowpanel-background", + { + lwtProperty: "popup", + }, + ], + [ + "--arrowpanel-color", + { + lwtProperty: "popup_text", + }, + ], + [ + "--arrowpanel-border-color", + { + lwtProperty: "popup_border", + }, + ], + [ + "--toolbar-field-background-color", + { + lwtProperty: "toolbar_field", + fallbackColor: "rgba(255, 255, 255, 0.8)", + }, + ], + [ + "--toolbar-bgcolor", + { + lwtProperty: "toolbarColor", + }, + ], + [ + "--toolbar-color", + { + lwtProperty: "toolbar_text", + }, + ], + [ + "--toolbar-field-color", + { + lwtProperty: "toolbar_field_text", + fallbackColor: "black", + }, + ], + [ + "--toolbar-field-border-color", + { + lwtProperty: "toolbar_field_border", + fallbackColor: "transparent", + }, + ], + [ + "--toolbar-field-focus-background-color", + { + lwtProperty: "toolbar_field_focus", + fallbackProperty: "toolbar_field", + fallbackColor: "white", + processColor(rgbaChannels, element, propertyOverrides) { + if (!rgbaChannels) { + return null; + } + // Ensure minimum opacity as this is used behind address bar results. + const min_opacity = 0.9; + let { r, g, b, a } = rgbaChannels; + if (a < min_opacity) { + propertyOverrides.set( + "toolbar_field_text_focus", + _isColorDark(r, g, b) ? "white" : "black" + ); + return `rgba(${r}, ${g}, ${b}, ${min_opacity})`; + } + return `rgba(${r}, ${g}, ${b}, ${a})`; + }, + }, + ], + [ + "--toolbar-field-focus-color", + { + lwtProperty: "toolbar_field_text_focus", + fallbackProperty: "toolbar_field_text", + fallbackColor: "black", + }, + ], + [ + "--toolbar-field-focus-border-color", + { + lwtProperty: "toolbar_field_border_focus", + }, + ], + [ + "--lwt-toolbar-field-highlight", + { + lwtProperty: "toolbar_field_highlight", + processColor(rgbaChannels, element) { + if (!rgbaChannels) { + return null; + } + const { r, g, b, a } = rgbaChannels; + return `rgba(${r}, ${g}, ${b}, ${a})`; + }, + }, + ], + [ + "--lwt-toolbar-field-highlight-text", + { + lwtProperty: "toolbar_field_highlight_text", + }, + ], + // The following 3 are given to the new tab page by contentTheme.js. They are + // also exposed here, in the browser chrome, so popups anchored on top of the + // new tab page can use them to avoid clashing with the new tab page content. + [ + "--newtab-background-color", + { + lwtProperty: "ntp_background", + processColor(rgbaChannels) { + if (!rgbaChannels) { + return null; + } + const { r, g, b } = rgbaChannels; + // Drop alpha channel + return `rgb(${r}, ${g}, ${b})`; + }, + }, + ], + [ + "--newtab-background-color-secondary", + { lwtProperty: "ntp_card_background" }, + ], + ["--newtab-text-primary-color", { lwtProperty: "ntp_text" }], +]; + +export function LightweightThemeConsumer(aDocument) { + this._doc = aDocument; + this._win = aDocument.defaultView; + this._winId = this._win.docShell.outerWindowID; + + Services.obs.addObserver(this, "lightweight-theme-styling-update"); + + this.darkThemeMediaQuery = this._win.matchMedia("(-moz-system-dark-theme)"); + this.darkThemeMediaQuery.addListener(this); + + const { LightweightThemeManager } = ChromeUtils.importESModule( + "resource://gre/modules/LightweightThemeManager.sys.mjs" + ); + this._update(LightweightThemeManager.themeData); + + this._win.addEventListener("unload", this, { once: true }); +} + +LightweightThemeConsumer.prototype = { + _lastData: null, + + observe(aSubject, aTopic, aData) { + if (aTopic != "lightweight-theme-styling-update") { + return; + } + + let data = aSubject.wrappedJSObject; + if (data.window && data.window !== this._winId) { + return; + } + + this._update(data); + }, + + handleEvent(aEvent) { + if (aEvent.target == this.darkThemeMediaQuery) { + this._update(this._lastData); + return; + } + + switch (aEvent.type) { + case "unload": + Services.obs.removeObserver(this, "lightweight-theme-styling-update"); + Services.ppmm.sharedData.delete(`theme/${this._winId}`); + this._win = this._doc = null; + if (this.darkThemeMediaQuery) { + this.darkThemeMediaQuery.removeListener(this); + this.darkThemeMediaQuery = null; + } + break; + } + }, + + _update(themeData) { + this._lastData = themeData; + + const hasDarkTheme = !!themeData.darkTheme; + let updateGlobalThemeData = true; + let useDarkTheme = (() => { + if (!hasDarkTheme) { + return false; + } + + if (this.darkThemeMediaQuery?.matches) { + return themeData.darkTheme.id != DEFAULT_THEME_ID; + } + + // If enabled, apply the dark theme variant to private browsing windows. + if ( + !lazy.NimbusFeatures.majorRelease2022.getVariable( + "feltPrivacyPBMDarkTheme" + ) || + !lazy.PrivateBrowsingUtils.isWindowPrivate(this._win) || + lazy.PrivateBrowsingUtils.permanentPrivateBrowsing + ) { + return false; + } + // When applying the dark theme for a PBM window we need to skip calling + // _determineToolbarAndContentTheme, because it applies the color scheme + // globally for all windows. Skipping this method also means we don't + // switch the content theme to dark. + // + // TODO: On Linux we most likely need to apply the dark theme, but on + // Windows and macOS we should be able to render light and dark windows + // with the default theme at the same time. + updateGlobalThemeData = false; + return true; + })(); + + // If this is a per-window dark theme, set the color scheme override so + // child BrowsingContexts, such as embedded prompts, get themed + // appropriately. + // If not, reset the color scheme override field. This is required to reset + // the color scheme on theme switch. + if (this._win.browsingContext == this._win.browsingContext.top) { + if (useDarkTheme && !updateGlobalThemeData) { + this._win.browsingContext.prefersColorSchemeOverride = "dark"; + } else { + this._win.browsingContext.prefersColorSchemeOverride = "none"; + } + } + + let theme = useDarkTheme ? themeData.darkTheme : themeData.theme; + if (!theme) { + theme = { id: DEFAULT_THEME_ID }; + } + + let active = (this._active = Object.keys(theme).length); + + let root = this._doc.documentElement; + + if (active && theme.headerURL) { + root.setAttribute("lwtheme-image", "true"); + } else { + root.removeAttribute("lwtheme-image"); + } + + let hasTheme = theme.id != DEFAULT_THEME_ID || useDarkTheme; + + this._setExperiment(active, themeData.experiment, theme.experimental); + _setImage(this._win, root, active, "--lwt-header-image", theme.headerURL); + _setImage( + this._win, + root, + active, + "--lwt-additional-images", + theme.additionalBackgrounds + ); + _setProperties(root, active, theme, hasTheme); + + if (hasTheme) { + if (updateGlobalThemeData) { + _determineToolbarAndContentTheme( + this._doc, + theme, + hasDarkTheme, + useDarkTheme + ); + } + root.setAttribute("lwtheme", "true"); + } else { + _determineToolbarAndContentTheme(this._doc, null); + root.removeAttribute("lwtheme"); + } + + _setDarkModeAttributes(this._doc, root, theme._processedColors, hasTheme); + + let contentThemeData = _getContentProperties(this._doc, active, theme); + Services.ppmm.sharedData.set(`theme/${this._winId}`, contentThemeData); + // We flush sharedData because contentThemeData can be responsible for + // painting large background surfaces. If this data isn't delivered to the + // content process before about:home is painted, we will paint a default + // background and then replace it when sharedData syncs, causing flashing. + Services.ppmm.sharedData.flush(); + + this._win.dispatchEvent(new CustomEvent("windowlwthemeupdate")); + }, + + _setExperiment(active, experiment, properties) { + const root = this._doc.documentElement; + if (this._lastExperimentData) { + const { stylesheet, usedVariables } = this._lastExperimentData; + if (stylesheet) { + stylesheet.remove(); + } + if (usedVariables) { + for (const [variable] of usedVariables) { + _setProperty(root, false, variable); + } + } + } + + this._lastExperimentData = {}; + + if (!active || !experiment) { + return; + } + + let usedVariables = []; + if (properties.colors) { + for (const property in properties.colors) { + const cssVariable = experiment.colors[property]; + const value = _rgbaToString( + _cssColorToRGBA(root.ownerDocument, properties.colors[property]) + ); + usedVariables.push([cssVariable, value]); + } + } + + if (properties.images) { + for (const property in properties.images) { + const cssVariable = experiment.images[property]; + usedVariables.push([ + cssVariable, + `url(${properties.images[property]})`, + ]); + } + } + if (properties.properties) { + for (const property in properties.properties) { + const cssVariable = experiment.properties[property]; + usedVariables.push([cssVariable, properties.properties[property]]); + } + } + for (const [variable, value] of usedVariables) { + _setProperty(root, true, variable, value); + } + this._lastExperimentData.usedVariables = usedVariables; + + if (experiment.stylesheet) { + /* Stylesheet URLs are validated using WebExtension schemas */ + let stylesheetAttr = `href="${experiment.stylesheet}" type="text/css"`; + let stylesheet = this._doc.createProcessingInstruction( + "xml-stylesheet", + stylesheetAttr + ); + this._doc.insertBefore(stylesheet, root); + this._lastExperimentData.stylesheet = stylesheet; + } + }, +}; + +function _getContentProperties(doc, active, data) { + if (!active) { + return {}; + } + let properties = {}; + for (let property in data) { + if (lazy.ThemeContentPropertyList.includes(property)) { + properties[property] = _cssColorToRGBA(doc, data[property]); + } + } + if (data.experimental) { + for (const property in data.experimental.colors) { + if (lazy.ThemeContentPropertyList.includes(property)) { + properties[property] = _cssColorToRGBA( + doc, + data.experimental.colors[property] + ); + } + } + for (const property in data.experimental.images) { + if (lazy.ThemeContentPropertyList.includes(property)) { + properties[property] = `url(${data.experimental.images[property]})`; + } + } + for (const property in data.experimental.properties) { + if (lazy.ThemeContentPropertyList.includes(property)) { + properties[property] = data.experimental.properties[property]; + } + } + } + return properties; +} + +function _setImage(aWin, aRoot, aActive, aVariableName, aURLs) { + if (aURLs && !Array.isArray(aURLs)) { + aURLs = [aURLs]; + } + _setProperty( + aRoot, + aActive, + aVariableName, + aURLs && aURLs.map(v => `url(${aWin.CSS.escape(v)})`).join(", ") + ); +} + +function _setProperty(elem, active, variableName, value) { + if (active && value) { + elem.style.setProperty(variableName, value); + } else { + elem.style.removeProperty(variableName); + } +} + +function _isToolbarDark(aDoc, aColors) { + // We prefer looking at toolbar background first (if it's opaque) because + // some text colors can be dark enough for our heuristics, but still + // contrast well enough with a dark background, see bug 1743010. + if (aColors.toolbarColor) { + let color = _cssColorToRGBA(aDoc, aColors.toolbarColor); + if (color.a == 1) { + return _isColorDark(color.r, color.g, color.b); + } + } + if (aColors.toolbar_text) { + let color = _cssColorToRGBA(aDoc, aColors.toolbar_text); + return !_isColorDark(color.r, color.g, color.b); + } + // It'd seem sensible to try looking at the "frame" background (accentcolor), + // but we don't because some themes that use background images leave it to + // black, see bug 1741931. + // + // Fall back to black as per the textcolor processing above. + let color = _cssColorToRGBA(aDoc, aColors.textcolor || "black"); + return !_isColorDark(color.r, color.g, color.b); +} + +function _determineToolbarAndContentTheme( + aDoc, + aTheme, + aHasDarkTheme = false, + aIsDarkTheme = false +) { + const kDark = 0; + const kLight = 1; + const kSystem = 2; + + const colors = aTheme?._processedColors; + function colorSchemeValue(aColorScheme) { + if (!aColorScheme) { + return null; + } + switch (aColorScheme) { + case "light": + return kLight; + case "dark": + return kDark; + case "system": + return kSystem; + case "auto": + default: + break; + } + return null; + } + + let toolbarTheme = (function () { + if (!aTheme) { + return kSystem; + } + let themeValue = colorSchemeValue(aTheme.color_scheme); + if (themeValue !== null) { + return themeValue; + } + if (aHasDarkTheme) { + return aIsDarkTheme ? kDark : kLight; + } + return _isToolbarDark(aDoc, colors) ? kDark : kLight; + })(); + + let contentTheme = (function () { + if (lazy.BROWSER_THEME_UNIFIED_COLOR_SCHEME) { + return toolbarTheme; + } + if (!aTheme) { + return kSystem; + } + let themeValue = colorSchemeValue( + aTheme.content_color_scheme || aTheme.color_scheme + ); + if (themeValue !== null) { + return themeValue; + } + return kSystem; + })(); + + Services.prefs.setIntPref("browser.theme.toolbar-theme", toolbarTheme); + Services.prefs.setIntPref("browser.theme.content-theme", contentTheme); +} + +/** + * Sets dark mode attributes on root, if required. We must do this here, + * instead of in each color's processColor function, because multiple colors + * are considered. + * @param {Document} doc + * @param {Element} root + * @param {object} colors + * The `_processedColors` object from the object created for our theme. + * @param {boolean} hasTheme + */ +function _setDarkModeAttributes(doc, root, colors, hasTheme) { + { + let textColor = _cssColorToRGBA(doc, colors.textcolor); + if (textColor && !_isColorDark(textColor.r, textColor.g, textColor.b)) { + root.setAttribute("lwtheme-brighttext", "true"); + } else { + root.removeAttribute("lwtheme-brighttext"); + } + } + + if (hasTheme) { + root.setAttribute( + "lwt-toolbar", + _isToolbarDark(doc, colors) ? "dark" : "light" + ); + } else { + root.removeAttribute("lwt-toolbar"); + } + + const setAttribute = function ( + attribute, + textPropertyName, + backgroundPropertyName + ) { + let dark = _determineIfColorPairIsDark( + doc, + colors, + textPropertyName, + backgroundPropertyName + ); + if (dark === null) { + root.removeAttribute(attribute); + } else { + root.setAttribute(attribute, dark ? "dark" : "light"); + } + }; + + setAttribute("lwt-tab-selected", "tab_text", "tab_selected"); + setAttribute("lwt-toolbar-field", "toolbar_field_text", "toolbar_field"); + setAttribute( + "lwt-toolbar-field-focus", + "toolbar_field_text_focus", + "toolbar_field_focus" + ); + setAttribute("lwt-popup", "popup_text", "popup"); + setAttribute("lwt-sidebar", "sidebar_text", "sidebar"); +} + +/** + * Determines if a themed color pair should be considered to have a dark color + * scheme. We consider both the background and foreground (i.e. usually text) + * colors because some text colors can be dark enough for our heuristics, but + * still contrast well enough with a dark background + * @param {Document} doc + * @param {object} colors + * @param {string} foregroundElementId + * The key for the foreground element in `colors`. + * @param {string} backgroundElementId + * The key for the background element in `colors`. + * @returns {boolean | null} True if the element should be considered dark, false + * if light, null for preferred scheme. + */ +function _determineIfColorPairIsDark( + doc, + colors, + textPropertyName, + backgroundPropertyName +) { + if (!colors[backgroundPropertyName] && !colors[textPropertyName]) { + // Handles the system theme. + return null; + } + + let color = _cssColorToRGBA(doc, colors[backgroundPropertyName]); + if (color && color.a == 1) { + return _isColorDark(color.r, color.g, color.b); + } + + color = _cssColorToRGBA(doc, colors[textPropertyName]); + if (!color) { + // Handles the case where a theme only provides a background color and it is + // semi-transparent. + return null; + } + + return !_isColorDark(color.r, color.g, color.b); +} + +function _setProperties(root, active, themeData, hasTheme) { + let propertyOverrides = new Map(); + let doc = root.ownerDocument; + + // Copy the theme into _processedColors. We'll replace values with processed + // colors if necessary. We copy because some colors (such as those used in + // content) are not processed here, but are referenced in places that check + // _processedColors. Copying means _processedColors will contain irrelevant + // properties like `id`. There aren't too many, so that's OK. + themeData._processedColors = { ...themeData }; + for (let map of [toolkitVariableMap, lazy.ThemeVariableMap]) { + for (let [cssVarName, definition] of map) { + const { + lwtProperty, + fallbackProperty, + fallbackColor, + optionalElementID, + processColor, + isColor = true, + } = definition; + let elem = optionalElementID + ? doc.getElementById(optionalElementID) + : root; + let val = propertyOverrides.get(lwtProperty) || themeData[lwtProperty]; + if (isColor) { + val = _cssColorToRGBA(doc, val); + if (!val && fallbackProperty) { + val = _cssColorToRGBA(doc, themeData[fallbackProperty]); + } + if (!val && hasTheme && fallbackColor) { + val = _cssColorToRGBA(doc, fallbackColor); + } + if (processColor) { + val = processColor(val, elem, propertyOverrides); + } else { + val = _rgbaToString(val); + } + } + + // Add processed color to themeData. + themeData._processedColors[lwtProperty] = val; + + _setProperty(elem, active, cssVarName, val); + } + } +} + +const kInvalidColor = { r: 0, g: 0, b: 0, a: 1 }; + +function _cssColorToRGBA(doc, cssColor) { + if (!cssColor) { + return null; + } + return ( + doc.defaultView.InspectorUtils.colorToRGBA(cssColor, doc) || kInvalidColor + ); +} + +function _rgbaToString(parsedColor) { + if (!parsedColor) { + return null; + } + let { r, g, b, a } = parsedColor; + if (a == 1) { + return `rgb(${r}, ${g}, ${b})`; + } + return `rgba(${r}, ${g}, ${b}, ${a})`; +} + +function _isColorDark(r, g, b) { + return 0.2125 * r + 0.7154 * g + 0.0721 * b <= 127; +} diff --git a/toolkit/modules/Log.sys.mjs b/toolkit/modules/Log.sys.mjs new file mode 100644 index 0000000000..62cd80b15c --- /dev/null +++ b/toolkit/modules/Log.sys.mjs @@ -0,0 +1,746 @@ +/* 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 { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const INTERNAL_FIELDS = new Set(["_level", "_message", "_time", "_namespace"]); + +/* + * Dump a message everywhere we can if we have a failure. + */ +function dumpError(text) { + dump(text + "\n"); + // TODO: Bug 1801091 - Figure out how to replace this. + // eslint-disable-next-line mozilla/no-cu-reportError + Cu.reportError(text); +} + +export var Log = { + Level: { + Fatal: 70, + Error: 60, + Warn: 50, + Info: 40, + Config: 30, + Debug: 20, + Trace: 10, + All: -1, // We don't want All to be falsy. + Desc: { + 70: "FATAL", + 60: "ERROR", + 50: "WARN", + 40: "INFO", + 30: "CONFIG", + 20: "DEBUG", + 10: "TRACE", + "-1": "ALL", + }, + Numbers: { + FATAL: 70, + ERROR: 60, + WARN: 50, + INFO: 40, + CONFIG: 30, + DEBUG: 20, + TRACE: 10, + ALL: -1, + }, + }, + + get repository() { + delete Log.repository; + Log.repository = new LoggerRepository(); + return Log.repository; + }, + set repository(value) { + delete Log.repository; + Log.repository = value; + }, + + _formatError(e) { + let result = String(e); + if (e.fileName) { + let loc = [e.fileName]; + if (e.lineNumber) { + loc.push(e.lineNumber); + } + if (e.columnNumber) { + loc.push(e.columnNumber); + } + result += `(${loc.join(":")})`; + } + return `${result} ${Log.stackTrace(e)}`; + }, + + // This is for back compatibility with services/common/utils.js; we duplicate + // some of the logic in ParameterFormatter + exceptionStr(e) { + if (!e) { + return String(e); + } + if (e instanceof Ci.nsIException) { + return `${e} ${Log.stackTrace(e)}`; + } else if (isError(e)) { + return Log._formatError(e); + } + // else + let message = e.message || e; + return `${message} ${Log.stackTrace(e)}`; + }, + + stackTrace(e) { + if (!e) { + return Components.stack.caller.formattedStack.trim(); + } + // Wrapped nsIException + if (e.location) { + let frame = e.location; + let output = []; + while (frame) { + // Works on frames or exceptions, munges file:// URIs to shorten the paths + // FIXME: filename munging is sort of hackish. + let str = "<file:unknown>"; + + let file = frame.filename || frame.fileName; + if (file) { + str = file.replace(/^(?:chrome|file):.*?([^\/\.]+(\.\w+)+)$/, "$1"); + } + + if (frame.lineNumber) { + str += ":" + frame.lineNumber; + } + + if (frame.name) { + str = frame.name + "()@" + str; + } + + if (str) { + output.push(str); + } + frame = frame.caller; + } + return `Stack trace: ${output.join("\n")}`; + } + // Standard JS exception + if (e.stack) { + let stack = e.stack; + return ( + "JS Stack trace: " + + stack.trim().replace(/@[^@]*?([^\/\.]+(\.\w+)+:)/g, "@$1") + ); + } + + if (e instanceof Ci.nsIStackFrame) { + return e.formattedStack.trim(); + } + return "No traceback available"; + }, +}; + +/* + * LogMessage + * Encapsulates a single log event's data + */ +class LogMessage { + constructor(loggerName, level, message, params) { + this.loggerName = loggerName; + this.level = level; + /* + * Special case to handle "log./level/(object)", for example logging a caught exception + * without providing text or params like: catch(e) { logger.warn(e) } + * Treating this as an empty text with the object in the 'params' field causes the + * object to be formatted properly by BasicFormatter. + */ + if ( + !params && + message && + typeof message == "object" && + typeof message.valueOf() != "string" + ) { + this.message = null; + this.params = message; + } else { + // If the message text is empty, or a string, or a String object, normal handling + this.message = message; + this.params = params; + } + + // The _structured field will correspond to whether this message is to + // be interpreted as a structured message. + this._structured = this.params && this.params.action; + this.time = Date.now(); + } + + get levelDesc() { + if (this.level in Log.Level.Desc) { + return Log.Level.Desc[this.level]; + } + return "UNKNOWN"; + } + + toString() { + let msg = `${this.time} ${this.level} ${this.message}`; + if (this.params) { + msg += ` ${JSON.stringify(this.params)}`; + } + return `LogMessage [${msg}]`; + } +} + +/* + * Logger + * Hierarchical version. Logs to all appenders, assigned or inherited + */ + +class Logger { + constructor(name, repository) { + if (!repository) { + repository = Log.repository; + } + this._name = name; + this.children = []; + this.ownAppenders = []; + this.appenders = []; + this._repository = repository; + + this._levelPrefName = null; + this._levelPrefValue = null; + this._level = null; + this._parent = null; + } + + get name() { + return this._name; + } + + get level() { + if (this._levelPrefName) { + // We've been asked to use a preference to configure the logs. If the + // pref has a value we use it, otherwise we continue to use the parent. + const lpv = this._levelPrefValue; + if (lpv) { + const levelValue = Log.Level[lpv]; + if (levelValue) { + // stash it in _level just in case a future value of the pref is + // invalid, in which case we end up continuing to use this value. + this._level = levelValue; + return levelValue; + } + } else { + // in case the pref has transitioned from a value to no value, we reset + // this._level and fall through to using the parent. + this._level = null; + } + } + if (this._level != null) { + return this._level; + } + if (this.parent) { + return this.parent.level; + } + dumpError("Log warning: root logger configuration error: no level defined"); + return Log.Level.All; + } + set level(level) { + if (this._levelPrefName) { + // I guess we could honor this by nuking this._levelPrefValue, but it + // almost certainly implies confusion, so we'll warn and ignore. + dumpError( + `Log warning: The log '${this.name}' is configured to use ` + + `the preference '${this._levelPrefName}' - you must adjust ` + + `the level by setting this preference, not by using the ` + + `level setter` + ); + return; + } + this._level = level; + } + + get parent() { + return this._parent; + } + set parent(parent) { + if (this._parent == parent) { + return; + } + // Remove ourselves from parent's children + if (this._parent) { + let index = this._parent.children.indexOf(this); + if (index != -1) { + this._parent.children.splice(index, 1); + } + } + this._parent = parent; + parent.children.push(this); + this.updateAppenders(); + } + + manageLevelFromPref(prefName) { + if (prefName == this._levelPrefName) { + // We've already configured this log with an observer for that pref. + return; + } + if (this._levelPrefName) { + dumpError( + `The log '${this.name}' is already configured with the ` + + `preference '${this._levelPrefName}' - ignoring request to ` + + `also use the preference '${prefName}'` + ); + return; + } + this._levelPrefName = prefName; + XPCOMUtils.defineLazyPreferenceGetter(this, "_levelPrefValue", prefName); + } + + updateAppenders() { + if (this._parent) { + let notOwnAppenders = this._parent.appenders.filter(function (appender) { + return !this.ownAppenders.includes(appender); + }, this); + this.appenders = notOwnAppenders.concat(this.ownAppenders); + } else { + this.appenders = this.ownAppenders.slice(); + } + + // Update children's appenders. + for (let i = 0; i < this.children.length; i++) { + this.children[i].updateAppenders(); + } + } + + addAppender(appender) { + if (this.ownAppenders.includes(appender)) { + return; + } + this.ownAppenders.push(appender); + this.updateAppenders(); + } + + removeAppender(appender) { + let index = this.ownAppenders.indexOf(appender); + if (index == -1) { + return; + } + this.ownAppenders.splice(index, 1); + this.updateAppenders(); + } + + _unpackTemplateLiteral(string, params) { + if (!Array.isArray(params)) { + // Regular log() call. + return [string, params]; + } + + if (!Array.isArray(string)) { + // Not using template literal. However params was packed into an array by + // the this.[level] call, so we need to unpack it here. + return [string, params[0]]; + } + + // We're using template literal format (logger.warn `foo ${bar}`). Turn the + // template strings into one string containing "${0}"..."${n}" tokens, and + // feed it to the basic formatter. The formatter will treat the numbers as + // indices into the params array, and convert the tokens to the params. + + if (!params.length) { + // No params; we need to set params to undefined, so the formatter + // doesn't try to output the params array. + return [string[0], undefined]; + } + + let concat = string[0]; + for (let i = 0; i < params.length; i++) { + concat += `\${${i}}${string[i + 1]}`; + } + return [concat, params]; + } + + log(level, string, params) { + if (this.level > level) { + return; + } + + // Hold off on creating the message object until we actually have + // an appender that's responsible. + let message; + let appenders = this.appenders; + for (let appender of appenders) { + if (appender.level > level) { + continue; + } + if (!message) { + [string, params] = this._unpackTemplateLiteral(string, params); + message = new LogMessage(this._name, level, string, params); + } + appender.append(message); + } + } + + fatal(string, ...params) { + this.log(Log.Level.Fatal, string, params); + } + error(string, ...params) { + this.log(Log.Level.Error, string, params); + } + warn(string, ...params) { + this.log(Log.Level.Warn, string, params); + } + info(string, ...params) { + this.log(Log.Level.Info, string, params); + } + config(string, ...params) { + this.log(Log.Level.Config, string, params); + } + debug(string, ...params) { + this.log(Log.Level.Debug, string, params); + } + trace(string, ...params) { + this.log(Log.Level.Trace, string, params); + } +} + +/* + * LoggerRepository + * Implements a hierarchy of Loggers + */ + +class LoggerRepository { + constructor() { + this._loggers = {}; + this._rootLogger = null; + } + + get rootLogger() { + if (!this._rootLogger) { + this._rootLogger = new Logger("root", this); + this._rootLogger.level = Log.Level.All; + } + return this._rootLogger; + } + set rootLogger(logger) { + throw new Error("Cannot change the root logger"); + } + + _updateParents(name) { + let pieces = name.split("."); + let cur, parent; + + // find the closest parent + // don't test for the logger name itself, as there's a chance it's already + // there in this._loggers + for (let i = 0; i < pieces.length - 1; i++) { + if (cur) { + cur += "." + pieces[i]; + } else { + cur = pieces[i]; + } + if (cur in this._loggers) { + parent = cur; + } + } + + // if we didn't assign a parent above, there is no parent + if (!parent) { + this._loggers[name].parent = this.rootLogger; + } else { + this._loggers[name].parent = this._loggers[parent]; + } + + // trigger updates for any possible descendants of this logger + for (let logger in this._loggers) { + if (logger != name && logger.indexOf(name) == 0) { + this._updateParents(logger); + } + } + } + + /** + * Obtain a named Logger. + * + * The returned Logger instance for a particular name is shared among + * all callers. In other words, if two consumers call getLogger("foo"), + * they will both have a reference to the same object. + * + * @return Logger + */ + getLogger(name) { + if (name in this._loggers) { + return this._loggers[name]; + } + this._loggers[name] = new Logger(name, this); + this._updateParents(name); + return this._loggers[name]; + } + + /** + * Obtain a Logger that logs all string messages with a prefix. + * + * A common pattern is to have separate Logger instances for each instance + * of an object. But, you still want to distinguish between each instance. + * Since Log.repository.getLogger() returns shared Logger objects, + * monkeypatching one Logger modifies them all. + * + * This function returns a new object with a prototype chain that chains + * up to the original Logger instance. The new prototype has log functions + * that prefix content to each message. + * + * @param name + * (string) The Logger to retrieve. + * @param prefix + * (string) The string to prefix each logged message with. + */ + getLoggerWithMessagePrefix(name, prefix) { + let log = this.getLogger(name); + + let proxy = Object.create(log); + proxy.log = (level, string, params) => { + if (Array.isArray(string) && Array.isArray(params)) { + // Template literal. + // We cannot change the original array, so create a new one. + string = [prefix + string[0]].concat(string.slice(1)); + } else { + string = prefix + string; // Regular string. + } + return log.log(level, string, params); + }; + return proxy; + } +} + +/* + * Formatters + * These massage a LogMessage into whatever output is desired. + */ + +// Basic formatter that doesn't do anything fancy. +class BasicFormatter { + constructor(dateFormat) { + if (dateFormat) { + this.dateFormat = dateFormat; + } + this.parameterFormatter = new ParameterFormatter(); + } + + /** + * Format the text of a message with optional parameters. + * If the text contains ${identifier}, replace that with + * the value of params[identifier]; if ${}, replace that with + * the entire params object. If no params have been substituted + * into the text, format the entire object and append that + * to the message. + */ + formatText(message) { + let params = message.params; + if (typeof params == "undefined") { + return message.message || ""; + } + // Defensive handling of non-object params + // We could add a special case for NSRESULT values here... + let pIsObject = typeof params == "object" || typeof params == "function"; + + // if we have params, try and find substitutions. + if (this.parameterFormatter) { + // have we successfully substituted any parameters into the message? + // in the log message + let subDone = false; + let regex = /\$\{(\S*?)\}/g; + let textParts = []; + if (message.message) { + textParts.push( + message.message.replace(regex, (_, sub) => { + // ${foo} means use the params['foo'] + if (sub) { + if (pIsObject && sub in message.params) { + subDone = true; + return this.parameterFormatter.format(message.params[sub]); + } + return "${" + sub + "}"; + } + // ${} means use the entire params object. + subDone = true; + return this.parameterFormatter.format(message.params); + }) + ); + } + if (!subDone) { + // There were no substitutions in the text, so format the entire params object + let rest = this.parameterFormatter.format(message.params); + if (rest !== null && rest != "{}") { + textParts.push(rest); + } + } + return textParts.join(": "); + } + return undefined; + } + + format(message) { + return ( + message.time + + "\t" + + message.loggerName + + "\t" + + message.levelDesc + + "\t" + + this.formatText(message) + ); + } +} + +/** + * Test an object to see if it is a Mozilla JS Error. + */ +function isError(aObj) { + return ( + aObj && + typeof aObj == "object" && + "name" in aObj && + "message" in aObj && + "fileName" in aObj && + "lineNumber" in aObj && + "stack" in aObj + ); +} + +/* + * Parameter Formatters + * These massage an object used as a parameter for a LogMessage into + * a string representation of the object. + */ + +class ParameterFormatter { + constructor() { + this._name = "ParameterFormatter"; + } + + format(ob) { + try { + if (ob === undefined) { + return "undefined"; + } + if (ob === null) { + return "null"; + } + // Pass through primitive types and objects that unbox to primitive types. + if ( + (typeof ob != "object" || typeof ob.valueOf() != "object") && + typeof ob != "function" + ) { + return ob; + } + if (ob instanceof Ci.nsIException) { + return `${ob} ${Log.stackTrace(ob)}`; + } else if (isError(ob)) { + return Log._formatError(ob); + } + // Just JSONify it. Filter out our internal fields and those the caller has + // already handled. + return JSON.stringify(ob, (key, val) => { + if (INTERNAL_FIELDS.has(key)) { + return undefined; + } + return val; + }); + } catch (e) { + dumpError( + `Exception trying to format object for log message: ${Log.exceptionStr( + e + )}` + ); + } + // Fancy formatting failed. Just toSource() it - but even this may fail! + try { + return ob.toSource(); + } catch (_) {} + try { + return String(ob); + } catch (_) { + return "[object]"; + } + } +} + +/* + * Appenders + * These can be attached to Loggers to log to different places + * Simply subclass and override doAppend to implement a new one + */ + +class Appender { + constructor(formatter) { + this.level = Log.Level.All; + this._name = "Appender"; + this._formatter = formatter || new BasicFormatter(); + } + + append(message) { + if (message) { + this.doAppend(this._formatter.format(message)); + } + } + + toString() { + return `${this._name} [level=${this.level}, formatter=${this._formatter}]`; + } +} + +/* + * DumpAppender + * Logs to standard out + */ + +class DumpAppender extends Appender { + constructor(formatter) { + super(formatter); + this._name = "DumpAppender"; + } + + doAppend(formatted) { + dump(formatted + "\n"); + } +} + +/* + * ConsoleAppender + * Logs to the javascript console + */ + +class ConsoleAppender extends Appender { + constructor(formatter) { + super(formatter); + this._name = "ConsoleAppender"; + } + + // XXX this should be replaced with calls to the Browser Console + append(message) { + if (message) { + let m = this._formatter.format(message); + if (message.level > Log.Level.Warn) { + // TODO: Bug 1801091 - Figure out how to replace this. + // eslint-disable-next-line mozilla/no-cu-reportError + Cu.reportError(m); + return; + } + this.doAppend(m); + } + } + + doAppend(formatted) { + Services.console.logStringMessage(formatted); + } +} + +Object.assign(Log, { + LogMessage, + Logger, + LoggerRepository, + + BasicFormatter, + + Appender, + DumpAppender, + ConsoleAppender, + + ParameterFormatter, +}); diff --git a/toolkit/modules/NLP.sys.mjs b/toolkit/modules/NLP.sys.mjs new file mode 100644 index 0000000000..e2de0f245c --- /dev/null +++ b/toolkit/modules/NLP.sys.mjs @@ -0,0 +1,78 @@ +/* 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/. */ + +/** + * NLP, which stands for Natural Language Processing, is a module that provides + * an entry point to various methods to interface with human language. + * + * At least, that's the goal. Eventually. Right now, the find toolbar only really + * needs the Levenshtein distance algorithm. + */ +export var NLP = { + /** + * Calculate the Levenshtein distance between two words. + * The implementation of this method was heavily inspired by + * http://locutus.io/php/strings/levenshtein/index.html + * License: MIT. + * + * @param {String} word1 Word to compare against + * @param {String} word2 Word that may be different + * @param {Number} costIns The cost to insert a character + * @param {Number} costRep The cost to replace a character + * @param {Number} costDel The cost to delete a character + * @return {Number} + */ + levenshtein(word1 = "", word2 = "", costIns = 1, costRep = 1, costDel = 1) { + if (word1 === word2) { + return 0; + } + + let l1 = word1.length; + let l2 = word2.length; + if (!l1) { + return l2 * costIns; + } + if (!l2) { + return l1 * costDel; + } + + let p1 = new Array(l2 + 1); + let p2 = new Array(l2 + 1); + + let i1, i2, c0, c1, c2, tmp; + + for (i2 = 0; i2 <= l2; i2++) { + p1[i2] = i2 * costIns; + } + + for (i1 = 0; i1 < l1; i1++) { + p2[0] = p1[0] + costDel; + + for (i2 = 0; i2 < l2; i2++) { + c0 = p1[i2] + (word1[i1] === word2[i2] ? 0 : costRep); + c1 = p1[i2 + 1] + costDel; + + if (c1 < c0) { + c0 = c1; + } + + c2 = p2[i2] + costIns; + + if (c2 < c0) { + c0 = c2; + } + + p2[i2 + 1] = c0; + } + + tmp = p1; + p1 = p2; + p2 = tmp; + } + + c0 = p1[l2]; + + return c0; + }, +}; diff --git a/toolkit/modules/NewTabUtils.sys.mjs b/toolkit/modules/NewTabUtils.sys.mjs new file mode 100644 index 0000000000..00067ada12 --- /dev/null +++ b/toolkit/modules/NewTabUtils.sys.mjs @@ -0,0 +1,2363 @@ +/* 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/. */ + +// Android tests don't import these properly, so guard against that +let shortURL = {}; +let searchShortcuts = {}; +let didSuccessfulImport = false; +try { + shortURL = ChromeUtils.importESModule( + "resource://activity-stream/lib/ShortURL.sys.mjs" + ); + searchShortcuts = ChromeUtils.importESModule( + "resource://activity-stream/lib/SearchShortcuts.sys.mjs" + ); + didSuccessfulImport = true; +} catch (e) { + // The test failed to import these files +} + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + BinarySearch: "resource://gre/modules/BinarySearch.sys.mjs", + PageThumbs: "resource://gre/modules/PageThumbs.sys.mjs", + PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs", + Pocket: "chrome://pocket/content/Pocket.sys.mjs", + pktApi: "chrome://pocket/content/pktApi.sys.mjs", +}); + +let BrowserWindowTracker; +try { + BrowserWindowTracker = ChromeUtils.importESModule( + "resource:///modules/BrowserWindowTracker.sys.mjs" + ).BrowserWindowTracker; +} catch (e) { + // BrowserWindowTracker is used to determine devicePixelRatio in + // _addFavicons. We fallback to the value 2 if we can't find a window, + // so it's safe to do nothing with this here. +} + +ChromeUtils.defineLazyGetter(lazy, "gCryptoHash", function () { + return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash); +}); + +// Boolean preferences that control newtab content +const PREF_NEWTAB_ENABLED = "browser.newtabpage.enabled"; + +// The maximum number of results PlacesProvider retrieves from history. +const HISTORY_RESULTS_LIMIT = 100; + +// The maximum number of links Links.getLinks will return. +const LINKS_GET_LINKS_LIMIT = 100; + +// The gather telemetry topic. +const TOPIC_GATHER_TELEMETRY = "gather-telemetry"; + +// Some default frecency threshold for Activity Stream requests +const ACTIVITY_STREAM_DEFAULT_FRECENCY = 150; + +// Some default query limit for Activity Stream requests +const ACTIVITY_STREAM_DEFAULT_LIMIT = 12; + +// Some default seconds ago for Activity Stream recent requests +const ACTIVITY_STREAM_DEFAULT_RECENT = 5 * 24 * 60 * 60; + +// The fallback value for the width of smallFavicon in pixels. +// This value will be multiplied by the current window's devicePixelRatio. +// If devicePixelRatio cannot be found, it will be multiplied by 2. +const DEFAULT_SMALL_FAVICON_WIDTH = 16; + +const POCKET_UPDATE_TIME = 24 * 60 * 60 * 1000; // 1 day +const POCKET_INACTIVE_TIME = 7 * 24 * 60 * 60 * 1000; // 1 week +const PREF_POCKET_LATEST_SINCE = "extensions.pocket.settings.latestSince"; + +/** + * Calculate the MD5 hash for a string. + * @param aValue + * The string to convert. + * @return The base64 representation of the MD5 hash. + */ +function toHash(aValue) { + let value = new TextEncoder().encode(aValue); + lazy.gCryptoHash.init(lazy.gCryptoHash.MD5); + lazy.gCryptoHash.update(value, value.length); + return lazy.gCryptoHash.finish(true); +} + +/** + * Singleton that provides storage functionality. + */ +ChromeUtils.defineLazyGetter(lazy, "Storage", function () { + return new LinksStorage(); +}); + +function LinksStorage() { + // Handle migration of data across versions. + try { + if (this._storedVersion < this._version) { + // This is either an upgrade, or version information is missing. + if (this._storedVersion < 1) { + // Version 1 moved data from DOM Storage to prefs. Since migrating from + // version 0 is no more supported, we just reportError a dataloss later. + throw new Error("Unsupported newTab storage version"); + } + // Add further migration steps here. + } else { + // This is a downgrade. Since we cannot predict future, upgrades should + // be backwards compatible. We will set the version to the old value + // regardless, so, on next upgrade, the migration steps will run again. + // For this reason, they should also be able to run multiple times, even + // on top of an already up-to-date storage. + } + } catch (ex) { + // Something went wrong in the update process, we can't recover from here, + // so just clear the storage and start from scratch (dataloss!). + console.error( + "Unable to migrate the newTab storage to the current version. " + + "Restarting from scratch.\n", + ex + ); + this.clear(); + } + + // Set the version to the current one. + this._storedVersion = this._version; +} + +LinksStorage.prototype = { + get _version() { + return 1; + }, + + get _prefs() { + return Object.freeze({ + pinnedLinks: "browser.newtabpage.pinned", + blockedLinks: "browser.newtabpage.blocked", + }); + }, + + get _storedVersion() { + if (this.__storedVersion === undefined) { + // When the pref is not set, the storage version is unknown, so either: + // - it's a new profile + // - it's a profile where versioning information got lost + // In this case we still run through all of the valid migrations, + // starting from 1, as if it was a downgrade. As previously stated the + // migrations should already support running on an updated store. + this.__storedVersion = Services.prefs.getIntPref( + "browser.newtabpage.storageVersion", + 1 + ); + } + return this.__storedVersion; + }, + set _storedVersion(aValue) { + Services.prefs.setIntPref("browser.newtabpage.storageVersion", aValue); + this.__storedVersion = aValue; + }, + + /** + * Gets the value for a given key from the storage. + * @param aKey The storage key (a string). + * @param aDefault A default value if the key doesn't exist. + * @return The value for the given key. + */ + get: function Storage_get(aKey, aDefault) { + let value; + try { + let prefValue = Services.prefs.getStringPref(this._prefs[aKey]); + value = JSON.parse(prefValue); + } catch (e) {} + return value || aDefault; + }, + + /** + * Sets the storage value for a given key. + * @param aKey The storage key (a string). + * @param aValue The value to set. + */ + set: function Storage_set(aKey, aValue) { + // Page titles may contain unicode, thus use complex values. + Services.prefs.setStringPref(this._prefs[aKey], JSON.stringify(aValue)); + }, + + /** + * Removes the storage value for a given key. + * @param aKey The storage key (a string). + */ + remove: function Storage_remove(aKey) { + Services.prefs.clearUserPref(this._prefs[aKey]); + }, + + /** + * Clears the storage and removes all values. + */ + clear: function Storage_clear() { + for (let key in this._prefs) { + this.remove(key); + } + }, +}; + +/** + * Singleton that serves as a registry for all open 'New Tab Page's. + */ +var AllPages = { + /** + * The array containing all active pages. + */ + _pages: [], + + /** + * Cached value that tells whether the New Tab Page feature is enabled. + */ + _enabled: null, + + /** + * Adds a page to the internal list of pages. + * @param aPage The page to register. + */ + register: function AllPages_register(aPage) { + this._pages.push(aPage); + this._addObserver(); + }, + + /** + * Removes a page from the internal list of pages. + * @param aPage The page to unregister. + */ + unregister: function AllPages_unregister(aPage) { + let index = this._pages.indexOf(aPage); + if (index > -1) { + this._pages.splice(index, 1); + } + }, + + /** + * Returns whether the 'New Tab Page' is enabled. + */ + get enabled() { + if (this._enabled === null) { + this._enabled = Services.prefs.getBoolPref(PREF_NEWTAB_ENABLED); + } + + return this._enabled; + }, + + /** + * Enables or disables the 'New Tab Page' feature. + */ + set enabled(aEnabled) { + if (this.enabled != aEnabled) { + Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, !!aEnabled); + } + }, + + /** + * Returns the number of registered New Tab Pages (i.e. the number of open + * about:newtab instances). + */ + get length() { + return this._pages.length; + }, + + /** + * Updates all currently active pages but the given one. + * @param aExceptPage The page to exclude from updating. + * @param aReason The reason for updating all pages. + */ + update(aExceptPage, aReason = "") { + for (let page of this._pages.slice()) { + if (aExceptPage != page) { + page.update(aReason); + } + } + }, + + /** + * Implements the nsIObserver interface to get notified when the preference + * value changes or when a new copy of a page thumbnail is available. + */ + observe: function AllPages_observe(aSubject, aTopic, aData) { + if (aTopic == "nsPref:changed") { + // Clear the cached value. + switch (aData) { + case PREF_NEWTAB_ENABLED: + this._enabled = null; + break; + } + } + // and all notifications get forwarded to each page. + this._pages.forEach(function (aPage) { + aPage.observe(aSubject, aTopic, aData); + }, this); + }, + + /** + * Adds a preference and new thumbnail observer and turns itself into a + * no-op after the first invokation. + */ + _addObserver: function AllPages_addObserver() { + Services.prefs.addObserver(PREF_NEWTAB_ENABLED, this, true); + Services.obs.addObserver(this, "page-thumbnail:create", true); + this._addObserver = function () {}; + }, + + QueryInterface: ChromeUtils.generateQI([ + "nsIObserver", + "nsISupportsWeakReference", + ]), +}; + +/** + * Singleton that keeps track of all pinned links and their positions in the + * grid. + */ +var PinnedLinks = { + /** + * The cached list of pinned links. + */ + _links: null, + + /** + * The array of pinned links. + */ + get links() { + if (!this._links) { + this._links = lazy.Storage.get("pinnedLinks", []); + } + + return this._links; + }, + + /** + * Pins a link at the given position. + * @param aLink The link to pin. + * @param aIndex The grid index to pin the cell at. + * @return true if link changes, false otherwise + */ + pin: function PinnedLinks_pin(aLink, aIndex) { + // Clear the link's old position, if any. + this.unpin(aLink); + + // change pinned link into a history link + let changed = this._makeHistoryLink(aLink); + this.links[aIndex] = aLink; + this.save(); + return changed; + }, + + /** + * Unpins a given link. + * @param aLink The link to unpin. + */ + unpin: function PinnedLinks_unpin(aLink) { + let index = this._indexOfLink(aLink); + if (index == -1) { + return; + } + let links = this.links; + links[index] = null; + // trim trailing nulls + let i = links.length - 1; + while (i >= 0 && links[i] == null) { + i--; + } + links.splice(i + 1); + this.save(); + }, + + /** + * Saves the current list of pinned links. + */ + save: function PinnedLinks_save() { + lazy.Storage.set("pinnedLinks", this.links); + }, + + /** + * Checks whether a given link is pinned. + * @params aLink The link to check. + * @return whether The link is pinned. + */ + isPinned: function PinnedLinks_isPinned(aLink) { + return this._indexOfLink(aLink) != -1; + }, + + /** + * Resets the links cache. + */ + resetCache: function PinnedLinks_resetCache() { + this._links = null; + }, + + /** + * Finds the index of a given link in the list of pinned links. + * @param aLink The link to find an index for. + * @return The link's index. + */ + _indexOfLink: function PinnedLinks_indexOfLink(aLink) { + for (let i = 0; i < this.links.length; i++) { + let link = this.links[i]; + if (link && link.url == aLink.url) { + return i; + } + } + + // The given link is unpinned. + return -1; + }, + + /** + * Transforms link into a "history" link + * @param aLink The link to change + * @return true if link changes, false otherwise + */ + _makeHistoryLink: function PinnedLinks_makeHistoryLink(aLink) { + if (!aLink.type || aLink.type == "history") { + return false; + } + aLink.type = "history"; + return true; + }, + + /** + * Replaces existing link with another link. + * @param aUrl The url of existing link + * @param aLink The replacement link + */ + replace: function PinnedLinks_replace(aUrl, aLink) { + let index = this._indexOfLink({ url: aUrl }); + if (index == -1) { + return; + } + this.links[index] = aLink; + this.save(); + }, +}; + +/** + * Singleton that keeps track of all blocked links in the grid. + */ +var BlockedLinks = { + /** + * A list of objects that are observing blocked link changes. + */ + _observers: [], + + /** + * The cached list of blocked links. + */ + _links: null, + + /** + * Registers an object that will be notified when the blocked links change. + */ + addObserver(aObserver) { + this._observers.push(aObserver); + }, + + /** + * Remove the observers. + */ + removeObservers() { + this._observers = []; + }, + + /** + * The list of blocked links. + */ + get links() { + if (!this._links) { + this._links = lazy.Storage.get("blockedLinks", {}); + } + + return this._links; + }, + + /** + * Blocks a given link. Adjusts siteMap accordingly, and notifies listeners. + * @param aLink The link to block. + */ + block: function BlockedLinks_block(aLink) { + this._callObservers("onLinkBlocked", aLink); + this.links[toHash(aLink.url)] = 1; + this.save(); + + // Make sure we unpin blocked links. + PinnedLinks.unpin(aLink); + }, + + /** + * Unblocks a given link. Adjusts siteMap accordingly, and notifies listeners. + * @param aLink The link to unblock. + */ + unblock: function BlockedLinks_unblock(aLink) { + if (this.isBlocked(aLink)) { + delete this.links[toHash(aLink.url)]; + this.save(); + this._callObservers("onLinkUnblocked", aLink); + } + }, + + /** + * Saves the current list of blocked links. + */ + save: function BlockedLinks_save() { + lazy.Storage.set("blockedLinks", this.links); + }, + + /** + * Returns whether a given link is blocked. + * @param aLink The link to check. + */ + isBlocked: function BlockedLinks_isBlocked(aLink) { + return toHash(aLink.url) in this.links; + }, + + /** + * Checks whether the list of blocked links is empty. + * @return Whether the list is empty. + */ + isEmpty: function BlockedLinks_isEmpty() { + return !Object.keys(this.links).length; + }, + + /** + * Resets the links cache. + */ + resetCache: function BlockedLinks_resetCache() { + this._links = null; + }, + + _callObservers(methodName, ...args) { + for (let obs of this._observers) { + if (typeof obs[methodName] == "function") { + try { + obs[methodName](...args); + } catch (err) { + console.error(err); + } + } + } + }, +}; + +/** + * Singleton that serves as the default link provider for the grid. It queries + * the history to retrieve the most frequently visited sites. + */ +var PlacesProvider = { + /** + * Set this to change the maximum number of links the provider will provide. + */ + maxNumLinks: HISTORY_RESULTS_LIMIT, + + /** + * Must be called before the provider is used. + */ + init: function PlacesProvider_init() { + this._placesObserver = new PlacesWeakCallbackWrapper( + this.handlePlacesEvents.bind(this) + ); + PlacesObservers.addListener( + ["page-visited", "page-title-changed", "pages-rank-changed"], + this._placesObserver + ); + }, + + /** + * Gets the current set of links delivered by this provider. + * @param aCallback The function that the array of links is passed to. + */ + getLinks: function PlacesProvider_getLinks(aCallback) { + let options = lazy.PlacesUtils.history.getNewQueryOptions(); + options.maxResults = this.maxNumLinks; + + // Sort by frecency, descending. + options.sortingMode = + Ci.nsINavHistoryQueryOptions.SORT_BY_FRECENCY_DESCENDING; + + let links = []; + + let callback = { + handleResult(aResultSet) { + let row; + + while ((row = aResultSet.getNextRow())) { + let url = row.getResultByIndex(1); + if (LinkChecker.checkLoadURI(url)) { + let title = row.getResultByIndex(2); + let frecency = row.getResultByIndex(12); + let lastVisitDate = row.getResultByIndex(5); + links.push({ + url, + title, + frecency, + lastVisitDate, + type: "history", + }); + } + } + }, + + handleError(aError) { + // Should we somehow handle this error? + aCallback([]); + }, + + handleCompletion(aReason) { + // The Places query breaks ties in frecency by place ID descending, but + // that's different from how Links.compareLinks breaks ties, because + // compareLinks doesn't have access to place IDs. It's very important + // that the initial list of links is sorted in the same order imposed by + // compareLinks, because Links uses compareLinks to perform binary + // searches on the list. So, ensure the list is so ordered. + let i = 1; + let outOfOrder = []; + while (i < links.length) { + if (Links.compareLinks(links[i - 1], links[i]) > 0) { + outOfOrder.push(links.splice(i, 1)[0]); + } else { + i++; + } + } + for (let link of outOfOrder) { + i = lazy.BinarySearch.insertionIndexOf( + Links.compareLinks, + links, + link + ); + links.splice(i, 0, link); + } + + aCallback(links); + }, + }; + + // Execute the query. + let query = lazy.PlacesUtils.history.getNewQuery(); + lazy.PlacesUtils.history.asyncExecuteLegacyQuery(query, options, callback); + }, + + /** + * Registers an object that will be notified when the provider's links change. + * @param aObserver An object with the following optional properties: + * * onLinkChanged: A function that's called when a single link + * changes. It's passed the provider and the link object. Only the + * link's `url` property is guaranteed to be present. If its `title` + * property is present, then its title has changed, and the + * property's value is the new title. If any sort properties are + * present, then its position within the provider's list of links may + * have changed, and the properties' values are the new sort-related + * values. Note that this link may not necessarily have been present + * in the lists returned from any previous calls to getLinks. + * * onManyLinksChanged: A function that's called when many links + * change at once. It's passed the provider. You should call + * getLinks to get the provider's new list of links. + */ + addObserver: function PlacesProvider_addObserver(aObserver) { + this._observers.push(aObserver); + }, + + _observers: [], + + handlePlacesEvents(aEvents) { + for (let event of aEvents) { + switch (event.type) { + case "page-visited": { + if (event.visitCount == 1 && event.lastKnownTitle) { + this._callObservers("onLinkChanged", { + url: event.url, + title: event.lastKnownTitle, + }); + } + break; + } + case "page-title-changed": { + this._callObservers("onLinkChanged", { + url: event.url, + title: event.title, + }); + break; + } + case "pages-rank-changed": { + this._callObservers("onManyLinksChanged"); + break; + } + } + } + }, + + _callObservers: function PlacesProvider__callObservers(aMethodName, aArg) { + for (let obs of this._observers) { + if (obs[aMethodName]) { + try { + obs[aMethodName](this, aArg); + } catch (err) { + console.error(err); + } + } + } + }, +}; + +/** + * Queries history to retrieve the most frecent sites. Emits events when the + * history changes. + */ +var ActivityStreamProvider = { + THUMB_FAVICON_SIZE: 96, + + /** + * Shared adjustment for selecting potentially blocked links. + */ + _adjustLimitForBlocked({ ignoreBlocked, numItems }) { + // Just use the usual number if blocked links won't be filtered out + if (ignoreBlocked) { + return numItems; + } + // Additionally select the number of blocked links in case they're removed + return Object.keys(BlockedLinks.links).length + numItems; + }, + + /** + * Shared sub-SELECT to get the guid of a bookmark of the current url while + * avoiding LEFT JOINs on moz_bookmarks. This avoids gettings tags. The guid + * could be one of multiple possible guids. Assumes `moz_places h` is in FROM. + */ + _commonBookmarkGuidSelect: `( + SELECT guid + FROM moz_bookmarks b + WHERE fk = h.id + AND type = :bookmarkType + AND ( + SELECT id + FROM moz_bookmarks p + WHERE p.id = b.parent + AND p.parent <> :tagsFolderId + ) NOTNULL + ) AS bookmarkGuid`, + + /** + * Shared WHERE expression filtering out undesired pages, e.g., hidden, + * unvisited, and non-http/s urls. Assumes moz_places is in FROM / JOIN. + * + * NB: SUBSTR(url) is used even without an index instead of url_hash because + * most desired pages will match http/s, so it will only run on the ~10s of + * rows matched. If url_hash were to be used, it should probably *not* be used + * by the query optimizer as we primarily want it optimized for the other + * conditions, e.g., most frecent first. + */ + _commonPlacesWhere: ` + AND hidden = 0 + AND last_visit_date > 0 + AND (SUBSTR(url, 1, 6) == "https:" + OR SUBSTR(url, 1, 5) == "http:") + `, + + /** + * Shared parameters for getting correct bookmarks and LIMITed queries. + */ + _getCommonParams(aOptions, aParams = {}) { + return Object.assign( + { + bookmarkType: lazy.PlacesUtils.bookmarks.TYPE_BOOKMARK, + limit: this._adjustLimitForBlocked(aOptions), + tagsFolderId: lazy.PlacesUtils.tagsFolderId, + }, + aParams + ); + }, + + /** + * Shared columns for Highlights related queries. + */ + _highlightsColumns: [ + "bookmarkGuid", + "description", + "guid", + "preview_image_url", + "title", + "url", + ], + + /** + * Shared post-processing of Highlights links. + */ + _processHighlights(aLinks, aOptions, aType) { + // Filter out blocked if necessary + if (!aOptions.ignoreBlocked) { + aLinks = aLinks.filter( + link => + !BlockedLinks.isBlocked( + link.pocket_id ? { url: link.open_url } : link + ) + ); + } + + // Limit the results to the requested number and set a type corresponding to + // which query selected it + return aLinks.slice(0, aOptions.numItems).map(item => + Object.assign(item, { + type: aType, + }) + ); + }, + + /** + * From an Array of links, if favicons are present, convert to data URIs + * + * @param {Array} aLinks + * an array containing objects with favicon data and mimeTypes + * + * @returns {Array} an array of links with favicons as data uri + */ + _faviconBytesToDataURI(aLinks) { + return aLinks.map(link => { + if (link.favicon) { + let encodedData = btoa(String.fromCharCode.apply(null, link.favicon)); + link.favicon = `data:${link.mimeType};base64,${encodedData}`; + delete link.mimeType; + } + + if (link.smallFavicon) { + let encodedData = btoa( + String.fromCharCode.apply(null, link.smallFavicon) + ); + link.smallFavicon = `data:${link.smallFaviconMimeType};base64,${encodedData}`; + delete link.smallFaviconMimeType; + } + + return link; + }); + }, + + /** + * Get favicon data (and metadata) for a uri. Fetches both the largest favicon + * available, for Activity Stream; and a normal-sized favicon, for the Urlbar. + * + * @param {nsIURI} aUri Page to check for favicon data + * @param {number} preferredFaviconWidth + * The preferred width of the of the normal-sized favicon in pixels. + * @returns A promise of an object (possibly empty) containing the data. + */ + async _loadIcons(aUri, preferredFaviconWidth) { + let iconData = {}; + // Fetch the largest icon available. + let faviconData; + try { + faviconData = await lazy.PlacesUtils.promiseFaviconData( + aUri, + this.THUMB_FAVICON_SIZE + ); + Object.assign(iconData, { + favicon: faviconData.data, + faviconLength: faviconData.dataLen, + faviconRef: faviconData.uri.ref, + faviconSize: faviconData.size, + mimeType: faviconData.mimeType, + }); + } catch (e) { + // Return early because fetching the largest favicon is the primary + // purpose of NewTabUtils. + return null; + } + + // Also fetch a smaller icon. + try { + faviconData = await lazy.PlacesUtils.promiseFaviconData( + aUri, + preferredFaviconWidth + ); + Object.assign(iconData, { + smallFavicon: faviconData.data, + smallFaviconLength: faviconData.dataLen, + smallFaviconRef: faviconData.uri.ref, + smallFaviconSize: faviconData.size, + smallFaviconMimeType: faviconData.mimeType, + }); + } catch (e) { + // Do nothing with the error since we still have the large favicon fields. + } + + return iconData; + }, + + /** + * Computes favicon data for each url in a set of links + * + * @param {Array} links + * an array containing objects without favicon data or mimeTypes yet + * + * @returns {Promise} Returns a promise with the array of links with the largest + * favicon available (as a byte array), mimeType, byte array + * length, and favicon size (width) + */ + _addFavicons(aLinks) { + let win; + if (BrowserWindowTracker) { + win = BrowserWindowTracker.getTopWindow(); + } + // We fetch two copies of a page's favicon: the largest available, for + // Activity Stream; and a smaller size appropriate for the Urlbar. + const preferredFaviconWidth = + DEFAULT_SMALL_FAVICON_WIDTH * (win ? win.devicePixelRatio : 2); + // Each link in the array needs a favicon for it's page - so we fire off a + // promise for each link to compute the favicon data and attach it back to + // the original link object. We must wait until all favicons for the array + // of links are computed before returning + return Promise.all( + aLinks.map( + link => + // eslint-disable-next-line no-async-promise-executor + new Promise(async resolve => { + // Never add favicon data for pocket items + if (link.type === "pocket") { + resolve(link); + return; + } + let iconData; + try { + let linkUri = Services.io.newURI(link.url); + iconData = await this._loadIcons(linkUri, preferredFaviconWidth); + + // Switch the scheme to try again with the other + if (!iconData) { + linkUri = linkUri + .mutate() + .setScheme(linkUri.scheme === "https" ? "http" : "https") + .finalize(); + iconData = await this._loadIcons( + linkUri, + preferredFaviconWidth + ); + } + } catch (e) { + // We just won't put icon data on the link + } + + // Add the icon data to the link if we have any + resolve(Object.assign(link, iconData)); + }) + ) + ); + }, + + /** + * Helper function which makes the call to the Pocket API to fetch the user's + * saved Pocket items. + */ + fetchSavedPocketItems(requestData) { + const latestSince = + Services.prefs.getStringPref(PREF_POCKET_LATEST_SINCE, 0) * 1000; + + // Do not fetch Pocket items for users that have been inactive for too long, or are not logged in + if ( + !lazy.pktApi.isUserLoggedIn() || + Date.now() - latestSince > POCKET_INACTIVE_TIME + ) { + return Promise.resolve(null); + } + + return new Promise((resolve, reject) => { + lazy.pktApi.retrieve(requestData, { + success(data) { + resolve(data); + }, + error(error) { + reject(error); + }, + }); + }); + }, + + /** + * Get the most recently Pocket-ed items from a user's Pocket list. See: + * https://getpocket.com/developer/docs/v3/retrieve for details + * + * @param {Object} aOptions + * {int} numItems: The max number of pocket items to fetch + */ + async getRecentlyPocketed(aOptions) { + const pocketSecondsAgo = + Math.floor(Date.now() / 1000) - ACTIVITY_STREAM_DEFAULT_RECENT; + const requestData = { + detailType: "complete", + count: aOptions.numItems, + since: pocketSecondsAgo, + }; + let data; + try { + data = await this.fetchSavedPocketItems(requestData); + if (!data) { + return []; + } + } catch (e) { + console.error(e); + return []; + } + /* Extract relevant parts needed to show this card as a highlight: + * url, preview image, title, description, and the unique item_id + * necessary for Pocket to identify the item + */ + let items = Object.values(data.list) + // status "0" means not archived or deleted + .filter(item => item.status === "0") + .map(item => ({ + date_added: item.time_added * 1000, + description: item.excerpt, + preview_image_url: item.image && item.image.src, + title: item.resolved_title, + url: item.resolved_url, + pocket_id: item.item_id, + open_url: item.open_url, + })); + + // Append the query param to let Pocket know this item came from highlights + for (let item of items) { + let url = new URL(item.open_url); + url.searchParams.append("src", "fx_new_tab"); + item.open_url = url.href; + } + + return this._processHighlights(items, aOptions, "pocket"); + }, + + /** + * Get most-recently-created visited bookmarks for Activity Stream. + * + * @param {Object} aOptions + * {num} bookmarkSecondsAgo: Maximum age of added bookmark. + * {bool} ignoreBlocked: Do not filter out blocked links. + * {int} numItems: Maximum number of items to return. + */ + async getRecentBookmarks(aOptions) { + const options = Object.assign( + { + bookmarkSecondsAgo: ACTIVITY_STREAM_DEFAULT_RECENT, + ignoreBlocked: false, + numItems: ACTIVITY_STREAM_DEFAULT_LIMIT, + }, + aOptions || {} + ); + + const sqlQuery = ` + SELECT + b.guid AS bookmarkGuid, + description, + h.guid, + preview_image_url, + b.title, + b.dateAdded / 1000 AS date_added, + url + FROM moz_bookmarks b + JOIN moz_bookmarks p + ON p.id = b.parent + JOIN moz_places h + ON h.id = b.fk + WHERE b.dateAdded >= :dateAddedThreshold + AND b.title NOTNULL + AND b.type = :bookmarkType + AND p.parent <> :tagsFolderId + ${this._commonPlacesWhere} + ORDER BY b.dateAdded DESC + LIMIT :limit + `; + + return this._processHighlights( + await this.executePlacesQuery(sqlQuery, { + columns: [...this._highlightsColumns, "date_added"], + params: this._getCommonParams(options, { + dateAddedThreshold: + (Date.now() - options.bookmarkSecondsAgo * 1000) * 1000, + }), + }), + options, + "bookmark" + ); + }, + + /** + * Get total count of all bookmarks. + * Note: this includes default bookmarks + * + * @return {int} The number bookmarks in the places DB. + */ + async getTotalBookmarksCount() { + let sqlQuery = ` + SELECT count(*) FROM moz_bookmarks b + JOIN moz_bookmarks t ON t.id = b.parent + AND t.parent <> :tags_folder + WHERE b.type = :type_bookmark + `; + + const result = await this.executePlacesQuery(sqlQuery, { + params: { + tags_folder: lazy.PlacesUtils.tagsFolderId, + type_bookmark: lazy.PlacesUtils.bookmarks.TYPE_BOOKMARK, + }, + }); + + return result[0][0]; + }, + + /** + * Get most-recently-visited history with metadata for Activity Stream. + * + * @param {Object} aOptions + * {bool} ignoreBlocked: Do not filter out blocked links. + * {int} numItems: Maximum number of items to return. + */ + async getRecentHistory(aOptions) { + const options = Object.assign( + { + ignoreBlocked: false, + numItems: ACTIVITY_STREAM_DEFAULT_LIMIT, + }, + aOptions || {} + ); + + const sqlQuery = ` + SELECT + ${this._commonBookmarkGuidSelect}, + description, + guid, + preview_image_url, + title, + url + FROM moz_places h + WHERE description NOTNULL + AND preview_image_url NOTNULL + ${this._commonPlacesWhere} + ORDER BY last_visit_date DESC + LIMIT :limit + `; + + return this._processHighlights( + await this.executePlacesQuery(sqlQuery, { + columns: this._highlightsColumns, + params: this._getCommonParams(options), + }), + options, + "history" + ); + }, + + /* + * Gets the top frecent sites for Activity Stream. + * + * @param {Object} aOptions + * {bool} ignoreBlocked: Do not filter out blocked links. + * {int} numItems: Maximum number of items to return. + * {int} topsiteFrecency: Minimum amount of frecency for a site. + * {bool} onePerDomain: Dedupe the resulting list. + * {bool} includeFavicon: Include favicons if available. + * {string} hideWithSearchParam: URLs that contain this search param will be + * excluded from the returned links. This value should be either undefined + * or a string with one of the following forms: + * - undefined: Fall back to the value of pref + * `browser.newtabpage.activity-stream.hideTopSitesWithSearchParam` + * - "" (empty) - Disable this feature + * - "key" - Search param named "key" with any or no value + * - "key=" - Search param named "key" with no value + * - "key=value" - Search param named "key" with value "value" + * + * @returns {Promise} Returns a promise with the array of links as payload. + */ + async getTopFrecentSites(aOptions) { + const options = Object.assign( + { + ignoreBlocked: false, + numItems: ACTIVITY_STREAM_DEFAULT_LIMIT, + topsiteFrecency: ACTIVITY_STREAM_DEFAULT_FRECENCY, + onePerDomain: true, + includeFavicon: true, + hideWithSearchParam: Services.prefs.getCharPref( + "browser.newtabpage.activity-stream.hideTopSitesWithSearchParam", + "" + ), + }, + aOptions || {} + ); + + // Double the item count in case the host is deduped between with www or + // not-www (i.e., 2 hosts) and an extra buffer for multiple pages per host. + const origNumItems = options.numItems; + if (options.onePerDomain) { + options.numItems *= 2 * 10; + } + + // Keep this query fast with frecency-indexed lookups (even with excess + // rows) and shift the more complex logic to post-processing afterwards + const sqlQuery = ` + SELECT + ${this._commonBookmarkGuidSelect}, + frecency, + guid, + last_visit_date / 1000 AS lastVisitDate, + rev_host, + title, + url, + "history" as type + FROM moz_places h + WHERE frecency >= :frecencyThreshold + ${this._commonPlacesWhere} + ORDER BY frecency DESC + LIMIT :limit + `; + + let links = await this.executePlacesQuery(sqlQuery, { + columns: [ + "bookmarkGuid", + "frecency", + "guid", + "lastVisitDate", + "title", + "url", + "type", + ], + params: this._getCommonParams(options, { + frecencyThreshold: options.topsiteFrecency, + }), + }); + + // Determine if the other link is "better" (larger frecency, more recent, + // lexicographically earlier url) + function isOtherBetter(link, other) { + if (other.frecency === link.frecency) { + if (other.lastVisitDate === link.lastVisitDate) { + return other.url < link.url; + } + return other.lastVisitDate > link.lastVisitDate; + } + return other.frecency > link.frecency; + } + + // Update a host Map with the better link + function setBetterLink(map, link, hostMatcher, combiner = () => {}) { + const host = hostMatcher(link.url)[1]; + if (map.has(host)) { + const other = map.get(host); + if (isOtherBetter(link, other)) { + link = other; + } + combiner(link, other); + } + map.set(host, link); + } + + // Convert all links that are supposed to be a seach shortcut to its canonical URL + if ( + didSuccessfulImport && + Services.prefs.getBoolPref( + `browser.newtabpage.activity-stream.${searchShortcuts.SEARCH_SHORTCUTS_EXPERIMENT}` + ) + ) { + links.forEach(link => { + let searchProvider = searchShortcuts.getSearchProvider( + shortURL.shortURL(link) + ); + if (searchProvider) { + link.url = searchProvider.url; + } + }); + } + + // Remove links that contain the hide-with search param. + if (options.hideWithSearchParam) { + let [key, value] = options.hideWithSearchParam.split("="); + links = links.filter(link => { + try { + let { searchParams } = new URL(link.url); + return value === undefined + ? !searchParams.has(key) + : !searchParams.getAll(key).includes(value); + } catch (error) {} + return true; + }); + } + + // Remove any blocked links. + if (!options.ignoreBlocked) { + links = links.filter(link => !BlockedLinks.isBlocked(link)); + } + + if (options.onePerDomain) { + // De-dup the links. + const exactHosts = new Map(); + for (const link of links) { + // First we want to find the best link for an exact host + setBetterLink(exactHosts, link, url => url.match(/:\/\/([^\/]+)/)); + } + + // Clean up exact hosts to dedupe as non-www hosts + const hosts = new Map(); + for (const link of exactHosts.values()) { + setBetterLink( + hosts, + link, + url => url.match(/:\/\/(?:www\.)?([^\/]+)/), + // Combine frecencies when deduping these links + (targetLink, otherLink) => { + targetLink.frecency = link.frecency + otherLink.frecency; + } + ); + } + + links = [...hosts.values()]; + } + // Pick out the top links using the same comparer as before + links = links.sort(isOtherBetter).slice(0, origNumItems); + + if (!options.includeFavicon) { + return links; + } + // Get the favicons as data URI for now (until we use the favicon protocol) + return this._faviconBytesToDataURI(await this._addFavicons(links)); + }, + + /** + * Gets a specific bookmark given some info about it + * + * @param {Obj} aInfo + * An object with one and only one of the following properties: + * - url + * - guid + * - parentGuid and index + */ + async getBookmark(aInfo) { + let bookmark = await lazy.PlacesUtils.bookmarks.fetch(aInfo); + if (!bookmark) { + return null; + } + let result = {}; + result.bookmarkGuid = bookmark.guid; + result.bookmarkTitle = bookmark.title; + result.lastModified = bookmark.lastModified.getTime(); + result.url = bookmark.url.href; + return result; + }, + + /** + * Count the number of visited urls grouped by day + */ + getUserMonthlyActivity() { + let sqlQuery = ` + SELECT count(*), + strftime('%Y-%m-%d', visit_date/1000000.0, 'unixepoch', 'localtime') as date_format + FROM moz_historyvisits + WHERE visit_date > 0 + AND visit_date > strftime('%s','now','localtime','start of day','-27 days','utc') * 1000000 + GROUP BY date_format + ORDER BY date_format ASC + `; + + return this.executePlacesQuery(sqlQuery); + }, + + /** + * Executes arbitrary query against places database + * + * @param {String} aQuery + * SQL query to execute + * @param {Object} [optional] aOptions + * aOptions.columns - an array of column names. if supplied the return + * items will consists of objects keyed on column names. Otherwise + * array of raw values is returned in the select order + * aOptions.param - an object of SQL binding parameters + * + * @returns {Promise} Returns a promise with the array of retrieved items + */ + async executePlacesQuery(aQuery, aOptions = {}) { + let { columns, params } = aOptions; + let items = []; + let queryError = null; + let conn = await lazy.PlacesUtils.promiseDBConnection(); + await conn.executeCached(aQuery, params, (aRow, aCancel) => { + try { + let item = null; + // if columns array is given construct an object + if (columns && Array.isArray(columns)) { + item = {}; + columns.forEach(column => { + item[column] = aRow.getResultByName(column); + }); + } else { + // if no columns - make an array of raw values + item = []; + for (let i = 0; i < aRow.numEntries; i++) { + item.push(aRow.getResultByIndex(i)); + } + } + items.push(item); + } catch (e) { + queryError = e; + aCancel(); + } + }); + if (queryError) { + throw new Error(queryError); + } + return items; + }, +}; + +/** + * A set of actions which influence what sites shown on the Activity Stream page + */ +var ActivityStreamLinks = { + _savedPocketStories: null, + _pocketLastUpdated: 0, + _pocketLastLatest: 0, + + /** + * Block a url + * + * @param {Object} aLink + * The link which contains a URL to add to the block list + */ + blockURL(aLink) { + BlockedLinks.block(aLink); + // If we're blocking a pocket item, invalidate the cache too + if (aLink.pocket_id) { + this._savedPocketStories = null; + } + }, + + onLinkBlocked(aLink) { + Services.obs.notifyObservers(null, "newtab-linkBlocked", aLink.url); + }, + + /** + * Adds a bookmark and opens up the Bookmark Dialog to show feedback that + * the bookmarking action has been successful + * + * @param {Object} aData + * aData.url The url to bookmark + * aData.title The title of the page to bookmark + * @param {Window} aBrowserWindow + * The current browser chrome window + * + * @returns {Promise} Returns a promise set to an object representing the bookmark + */ + addBookmark(aData, aBrowserWindow) { + const { url, title } = aData; + return aBrowserWindow.PlacesCommandHook.bookmarkLink(url, title); + }, + + /** + * Removes a bookmark + * + * @param {String} aBookmarkGuid + * The bookmark guid associated with the bookmark to remove + * + * @returns {Promise} Returns a promise at completion. + */ + deleteBookmark(aBookmarkGuid) { + return lazy.PlacesUtils.bookmarks.remove(aBookmarkGuid); + }, + + /** + * Removes a history link and unpins the URL if previously pinned + * + * @param {String} aUrl + * The url to be removed from history + * + * @returns {Promise} Returns a promise set to true if link was removed + */ + deleteHistoryEntry(aUrl) { + const url = aUrl; + PinnedLinks.unpin({ url }); + return lazy.PlacesUtils.history.remove(url); + }, + + /** + * Helper function which makes the call to the Pocket API to delete an item from + * a user's saved to Pocket feed. Also, invalidate the Pocket stories cache + * + * @param {Integer} aItemID + * The unique pocket ID used to find the item to be deleted + * + *@returns {Promise} Returns a promise at completion + */ + deletePocketEntry(aItemID) { + this._savedPocketStories = null; + return new Promise((success, error) => + lazy.pktApi.deleteItem(aItemID, { success, error }) + ); + }, + + /** + * Helper function which makes the call to the Pocket API to archive an item from + * a user's saved to Pocket feed. Also, invalidate the Pocket stories cache + * + * @param {Integer} aItemID + * The unique pocket ID used to find the item to be archived + * + *@returns {Promise} Returns a promise at completion + */ + archivePocketEntry(aItemID) { + this._savedPocketStories = null; + return new Promise((success, error) => + lazy.pktApi.archiveItem(aItemID, { success, error }) + ); + }, + + /** + * Helper function which makes the call to the Pocket API to save an item to + * a user's saved to Pocket feed if they are logged in. Also, invalidate the + * Pocket stories cache + * + * @param {String} aUrl + * The URL belonging to the story being saved + * @param {String} aTitle + * The title belonging to the story being saved + * @param {Browser} aBrowser + * The target browser to show the doorhanger in + * + *@returns {Promise} Returns a promise at completion + */ + addPocketEntry(aUrl, aTitle, aBrowser) { + // If the user is not logged in, show the panel to prompt them to log in + if (!lazy.pktApi.isUserLoggedIn()) { + lazy.Pocket.savePage(aBrowser, aUrl, aTitle); + return Promise.resolve(null); + } + + // If the user is logged in, just save the link to Pocket and Activity Stream + // will update the page + this._savedPocketStories = null; + return new Promise((success, error) => { + lazy.pktApi.addLink(aUrl, { + title: aTitle, + success, + error, + }); + }); + }, + + /** + * Get the Highlights links to show on Activity Stream + * + * @param {Object} aOptions + * {bool} excludeBookmarks: Don't add bookmark items. + * {bool} excludeHistory: Don't add history items. + * {bool} excludePocket: Don't add Pocket items. + * {bool} withFavicons: Add favicon data: URIs, when possible. + * {int} numItems: Maximum number of (bookmark or history) items to return. + * + * @return {Promise} Returns a promise with the array of links as the payload + */ + async getHighlights(aOptions = {}) { + aOptions.numItems = aOptions.numItems || ACTIVITY_STREAM_DEFAULT_LIMIT; + const results = []; + + // First get bookmarks if we want them + if (!aOptions.excludeBookmarks) { + results.push( + ...(await ActivityStreamProvider.getRecentBookmarks(aOptions)) + ); + } + + // Add the Pocket items if we need more and want them + if (aOptions.numItems - results.length > 0 && !aOptions.excludePocket) { + const latestSince = ~~Services.prefs.getStringPref( + PREF_POCKET_LATEST_SINCE, + 0 + ); + // Invalidate the cache, get new stories, and update timestamps if: + // 1. we do not have saved to Pocket stories already cached OR + // 2. it has been too long since we last got Pocket stories OR + // 3. there has been a paged saved to pocket since we last got new stories + if ( + !this._savedPocketStories || + Date.now() - this._pocketLastUpdated > POCKET_UPDATE_TIME || + this._pocketLastLatest < latestSince + ) { + this._savedPocketStories = + await ActivityStreamProvider.getRecentlyPocketed(aOptions); + this._pocketLastUpdated = Date.now(); + this._pocketLastLatest = latestSince; + } + results.push(...this._savedPocketStories); + } + + // Add in history if we need more and want them + if (aOptions.numItems - results.length > 0 && !aOptions.excludeHistory) { + // Use the same numItems as bookmarks above in case we remove duplicates + const history = await ActivityStreamProvider.getRecentHistory(aOptions); + + // Only include a url once in the result preferring the bookmark + const bookmarkUrls = new Set(results.map(({ url }) => url)); + for (const page of history) { + if (!bookmarkUrls.has(page.url)) { + results.push(page); + + // Stop adding pages once we reach the desired maximum + if (results.length === aOptions.numItems) { + break; + } + } + } + } + + if (aOptions.withFavicons) { + return ActivityStreamProvider._faviconBytesToDataURI( + await ActivityStreamProvider._addFavicons(results) + ); + } + + return results; + }, + + /** + * Get the top sites to show on Activity Stream + * + * @return {Promise} Returns a promise with the array of links as the payload + */ + async getTopSites(aOptions = {}) { + return ActivityStreamProvider.getTopFrecentSites(aOptions); + }, +}; + +/** + * Singleton that provides access to all links contained in the grid (including + * the ones that don't fit on the grid). A link is a plain object that looks + * like this: + * + * { + * url: "http://www.mozilla.org/", + * title: "Mozilla", + * frecency: 1337, + * lastVisitDate: 1394678824766431, + * } + */ +var Links = { + /** + * The maximum number of links returned by getLinks. + */ + maxNumLinks: LINKS_GET_LINKS_LIMIT, + + /** + * A mapping from each provider to an object { sortedLinks, siteMap, linkMap }. + * sortedLinks is the cached, sorted array of links for the provider. + * siteMap is a mapping from base domains to URL count associated with the domain. + * The count does not include blocked URLs. siteMap is used to look up a + * user's top sites that can be targeted with a suggested tile. + * linkMap is a Map from link URLs to link objects. + */ + _providers: new Map(), + + /** + * The properties of link objects used to sort them. + */ + _sortProperties: ["frecency", "lastVisitDate", "url"], + + /** + * List of callbacks waiting for the cache to be populated. + */ + _populateCallbacks: [], + + /** + * A list of objects that are observing links updates. + */ + _observers: [], + + /** + * Registers an object that will be notified when links updates. + */ + addObserver(aObserver) { + this._observers.push(aObserver); + }, + + /** + * Adds a link provider. + * @param aProvider The link provider. + */ + addProvider: function Links_addProvider(aProvider) { + this._providers.set(aProvider, null); + aProvider.addObserver(this); + }, + + /** + * Removes a link provider. + * @param aProvider The link provider. + */ + removeProvider: function Links_removeProvider(aProvider) { + if (!this._providers.delete(aProvider)) { + throw new Error("Unknown provider"); + } + }, + + /** + * Populates the cache with fresh links from the providers. + * @param aCallback The callback to call when finished (optional). + * @param aForce When true, populates the cache even when it's already filled. + */ + populateCache: function Links_populateCache(aCallback, aForce) { + let callbacks = this._populateCallbacks; + + // Enqueue the current callback. + callbacks.push(aCallback); + + // There was a callback waiting already, thus the cache has not yet been + // populated. + if (callbacks.length > 1) { + return; + } + + function executeCallbacks() { + while (callbacks.length) { + let callback = callbacks.shift(); + if (callback) { + try { + callback(); + } catch (e) { + // We want to proceed even if a callback fails. + } + } + } + } + + let numProvidersRemaining = this._providers.size; + for (let [provider /* , links */] of this._providers) { + this._populateProviderCache( + provider, + () => { + if (--numProvidersRemaining == 0) { + executeCallbacks(); + } + }, + aForce + ); + } + + this._addObserver(); + }, + + /** + * Gets the current set of links contained in the grid. + * @return The links in the grid. + */ + getLinks: function Links_getLinks() { + let pinnedLinks = Array.from(PinnedLinks.links); + let links = this._getMergedProviderLinks(); + + let sites = new Set(); + for (let link of pinnedLinks) { + if (link) { + sites.add(NewTabUtils.extractSite(link.url)); + } + } + + // Filter blocked and pinned links and duplicate base domains. + links = links.filter(function (link) { + let site = NewTabUtils.extractSite(link.url); + if (site == null || sites.has(site)) { + return false; + } + sites.add(site); + + return !BlockedLinks.isBlocked(link) && !PinnedLinks.isPinned(link); + }); + + // Try to fill the gaps between pinned links. + for (let i = 0; i < pinnedLinks.length && links.length; i++) { + if (!pinnedLinks[i]) { + pinnedLinks[i] = links.shift(); + } + } + + // Append the remaining links if any. + if (links.length) { + pinnedLinks = pinnedLinks.concat(links); + } + + for (let link of pinnedLinks) { + if (link) { + link.baseDomain = NewTabUtils.extractSite(link.url); + } + } + return pinnedLinks; + }, + + /** + * Resets the links cache. + */ + resetCache: function Links_resetCache() { + for (let provider of this._providers.keys()) { + this._providers.set(provider, null); + } + }, + + /** + * Compares two links. + * @param aLink1 The first link. + * @param aLink2 The second link. + * @return A negative number if aLink1 is ordered before aLink2, zero if + * aLink1 and aLink2 have the same ordering, or a positive number if + * aLink1 is ordered after aLink2. + * + * @note compareLinks's this object is bound to Links below. + */ + compareLinks: function Links_compareLinks(aLink1, aLink2) { + for (let prop of this._sortProperties) { + if (!(prop in aLink1) || !(prop in aLink2)) { + throw new Error("Comparable link missing required property: " + prop); + } + } + return ( + aLink2.frecency - aLink1.frecency || + aLink2.lastVisitDate - aLink1.lastVisitDate || + aLink1.url.localeCompare(aLink2.url) + ); + }, + + _incrementSiteMap(map, link) { + if (NewTabUtils.blockedLinks.isBlocked(link)) { + // Don't count blocked URLs. + return; + } + let site = NewTabUtils.extractSite(link.url); + map.set(site, (map.get(site) || 0) + 1); + }, + + _decrementSiteMap(map, link) { + if (NewTabUtils.blockedLinks.isBlocked(link)) { + // Blocked URLs are not included in map. + return; + } + let site = NewTabUtils.extractSite(link.url); + let previousURLCount = map.get(site); + if (previousURLCount === 1) { + map.delete(site); + } else { + map.set(site, previousURLCount - 1); + } + }, + + /** + * Update the siteMap cache based on the link given and whether we need + * to increment or decrement it. We do this by iterating over all stored providers + * to find which provider this link already exists in. For providers that + * have this link, we will adjust siteMap for them accordingly. + * + * @param aLink The link that will affect siteMap + * @param increment A boolean for whether to increment or decrement siteMap + */ + _adjustSiteMapAndNotify(aLink, increment = true) { + for (let [, /* provider */ cache] of this._providers) { + // We only update siteMap if aLink is already stored in linkMap. + if (cache.linkMap.get(aLink.url)) { + if (increment) { + this._incrementSiteMap(cache.siteMap, aLink); + continue; + } + this._decrementSiteMap(cache.siteMap, aLink); + } + } + this._callObservers("onLinkChanged", aLink); + }, + + onLinkBlocked(aLink) { + this._adjustSiteMapAndNotify(aLink, false); + }, + + onLinkUnblocked(aLink) { + this._adjustSiteMapAndNotify(aLink); + }, + + populateProviderCache(provider, callback) { + if (!this._providers.has(provider)) { + throw new Error( + "Can only populate provider cache for existing provider." + ); + } + + return this._populateProviderCache(provider, callback, false); + }, + + /** + * Calls getLinks on the given provider and populates our cache for it. + * @param aProvider The provider whose cache will be populated. + * @param aCallback The callback to call when finished. + * @param aForce When true, populates the provider's cache even when it's + * already filled. + */ + _populateProviderCache(aProvider, aCallback, aForce) { + let cache = this._providers.get(aProvider); + let createCache = !cache; + if (createCache) { + cache = { + // Start with a resolved promise. + populatePromise: new Promise(resolve => resolve()), + }; + this._providers.set(aProvider, cache); + } + // Chain the populatePromise so that calls are effectively queued. + cache.populatePromise = cache.populatePromise.then(() => { + return new Promise(resolve => { + if (!createCache && !aForce) { + aCallback(); + resolve(); + return; + } + aProvider.getLinks(links => { + // Filter out null and undefined links so we don't have to deal with + // them in getLinks when merging links from providers. + links = links.filter(link => !!link); + cache.sortedLinks = links; + cache.siteMap = links.reduce((map, link) => { + this._incrementSiteMap(map, link); + return map; + }, new Map()); + cache.linkMap = links.reduce((map, link) => { + map.set(link.url, link); + return map; + }, new Map()); + aCallback(); + resolve(); + }); + }); + }); + }, + + /** + * Merges the cached lists of links from all providers whose lists are cached. + * @return The merged list. + */ + _getMergedProviderLinks: function Links__getMergedProviderLinks() { + // Build a list containing a copy of each provider's sortedLinks list. + let linkLists = []; + for (let provider of this._providers.keys()) { + let links = this._providers.get(provider); + if (links && links.sortedLinks) { + linkLists.push(links.sortedLinks.slice()); + } + } + + return this.mergeLinkLists(linkLists); + }, + + mergeLinkLists: function Links_mergeLinkLists(linkLists) { + if (linkLists.length == 1) { + return linkLists[0]; + } + + function getNextLink() { + let minLinks = null; + for (let links of linkLists) { + if ( + links.length && + (!minLinks || Links.compareLinks(links[0], minLinks[0]) < 0) + ) { + minLinks = links; + } + } + return minLinks ? minLinks.shift() : null; + } + + let finalLinks = []; + for ( + let nextLink = getNextLink(); + nextLink && finalLinks.length < this.maxNumLinks; + nextLink = getNextLink() + ) { + finalLinks.push(nextLink); + } + + return finalLinks; + }, + + /** + * Called by a provider to notify us when a single link changes. + * @param aProvider The provider whose link changed. + * @param aLink The link that changed. If the link is new, it must have all + * of the _sortProperties. Otherwise, it may have as few or as + * many as is convenient. + * @param aIndex The current index of the changed link in the sortedLinks + cache in _providers. Defaults to -1 if the provider doesn't know the index + * @param aDeleted Boolean indicating if the provider has deleted the link. + */ + onLinkChanged: function Links_onLinkChanged( + aProvider, + aLink, + aIndex = -1, + aDeleted = false + ) { + if (!("url" in aLink)) { + throw new Error("Changed links must have a url property"); + } + + let links = this._providers.get(aProvider); + if (!links) { + // This is not an error, it just means that between the time the provider + // was added and the future time we call getLinks on it, it notified us of + // a change. + return; + } + + let { sortedLinks, siteMap, linkMap } = links; + let existingLink = linkMap.get(aLink.url); + let insertionLink = null; + let updatePages = false; + + if (existingLink) { + // Update our copy's position in O(lg n) by first removing it from its + // list. It's important to do this before modifying its properties. + if (this._sortProperties.some(prop => prop in aLink)) { + let idx = aIndex; + if (idx < 0) { + idx = this._indexOf(sortedLinks, existingLink); + } else if (this.compareLinks(aLink, sortedLinks[idx]) != 0) { + throw new Error("aLink should be the same as sortedLinks[idx]"); + } + + if (idx < 0) { + throw new Error("Link should be in _sortedLinks if in _linkMap"); + } + sortedLinks.splice(idx, 1); + + if (aDeleted) { + updatePages = true; + linkMap.delete(existingLink.url); + this._decrementSiteMap(siteMap, existingLink); + } else { + // Update our copy's properties. + Object.assign(existingLink, aLink); + + // Finally, reinsert our copy below. + insertionLink = existingLink; + } + } + // Update our copy's title in O(1). + if ("title" in aLink && aLink.title != existingLink.title) { + existingLink.title = aLink.title; + updatePages = true; + } + } else if (this._sortProperties.every(prop => prop in aLink)) { + // Before doing the O(lg n) insertion below, do an O(1) check for the + // common case where the new link is too low-ranked to be in the list. + if (sortedLinks.length && sortedLinks.length == aProvider.maxNumLinks) { + let lastLink = sortedLinks[sortedLinks.length - 1]; + if (this.compareLinks(lastLink, aLink) < 0) { + return; + } + } + // Copy the link object so that changes later made to it by the caller + // don't affect our copy. + insertionLink = {}; + for (let prop in aLink) { + insertionLink[prop] = aLink[prop]; + } + linkMap.set(aLink.url, insertionLink); + this._incrementSiteMap(siteMap, aLink); + } + + if (insertionLink) { + let idx = this._insertionIndexOf(sortedLinks, insertionLink); + sortedLinks.splice(idx, 0, insertionLink); + if (sortedLinks.length > aProvider.maxNumLinks) { + let lastLink = sortedLinks.pop(); + linkMap.delete(lastLink.url); + this._decrementSiteMap(siteMap, lastLink); + } + updatePages = true; + } + + if (updatePages) { + AllPages.update(null, "links-changed"); + } + }, + + /** + * Called by a provider to notify us when many links change. + */ + onManyLinksChanged: function Links_onManyLinksChanged(aProvider) { + this._populateProviderCache( + aProvider, + () => { + AllPages.update(null, "links-changed"); + }, + true + ); + }, + + _indexOf: function Links__indexOf(aArray, aLink) { + return this._binsearch(aArray, aLink, "indexOf"); + }, + + _insertionIndexOf: function Links__insertionIndexOf(aArray, aLink) { + return this._binsearch(aArray, aLink, "insertionIndexOf"); + }, + + _binsearch: function Links__binsearch(aArray, aLink, aMethod) { + return lazy.BinarySearch[aMethod](this.compareLinks, aArray, aLink); + }, + + /** + * Implements the nsIObserver interface to get notified about browser history + * sanitization. + */ + observe: function Links_observe(aSubject, aTopic, aData) { + // Make sure to update open about:newtab instances. If there are no opened + // pages we can just wait for the next new tab to populate the cache again. + if (AllPages.length && AllPages.enabled) { + this.populateCache(function () { + AllPages.update(); + }, true); + } else { + this.resetCache(); + } + }, + + _callObservers(methodName, ...args) { + for (let obs of this._observers) { + if (typeof obs[methodName] == "function") { + try { + obs[methodName](this, ...args); + } catch (err) { + console.error(err); + } + } + } + }, + + /** + * Adds a sanitization observer and turns itself into a no-op after the first + * invokation. + */ + _addObserver: function Links_addObserver() { + Services.obs.addObserver(this, "browser:purge-session-history", true); + this._addObserver = function () {}; + }, + + QueryInterface: ChromeUtils.generateQI([ + "nsIObserver", + "nsISupportsWeakReference", + ]), +}; + +Links.compareLinks = Links.compareLinks.bind(Links); + +/** + * Singleton used to collect telemetry data. + * + */ +var Telemetry = { + /** + * Initializes object. + */ + init: function Telemetry_init() { + Services.obs.addObserver(this, TOPIC_GATHER_TELEMETRY); + }, + + uninit: function Telemetry_uninit() { + Services.obs.removeObserver(this, TOPIC_GATHER_TELEMETRY); + }, + + /** + * Collects data. + */ + _collect: function Telemetry_collect() { + let probes = [ + { histogram: "NEWTAB_PAGE_ENABLED", value: AllPages.enabled }, + { + histogram: "NEWTAB_PAGE_PINNED_SITES_COUNT", + value: PinnedLinks.links.length, + }, + { + histogram: "NEWTAB_PAGE_BLOCKED_SITES_COUNT", + value: Object.keys(BlockedLinks.links).length, + }, + ]; + + probes.forEach(function Telemetry_collect_forEach(aProbe) { + Services.telemetry.getHistogramById(aProbe.histogram).add(aProbe.value); + }); + }, + + /** + * Listens for gather telemetry topic. + */ + observe: function Telemetry_observe(aSubject, aTopic, aData) { + this._collect(); + }, +}; + +/** + * Singleton that checks if a given link should be displayed on about:newtab + * or if we should rather not do it for security reasons. URIs that inherit + * their caller's principal will be filtered. + */ +var LinkChecker = { + _cache: {}, + + get flags() { + return ( + Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL | + Ci.nsIScriptSecurityManager.DONT_REPORT_ERRORS + ); + }, + + checkLoadURI: function LinkChecker_checkLoadURI(aURI) { + if (!(aURI in this._cache)) { + this._cache[aURI] = this._doCheckLoadURI(aURI); + } + + return this._cache[aURI]; + }, + + _doCheckLoadURI: function Links_doCheckLoadURI(aURI) { + try { + // about:newtab is currently privileged. In any case, it should be + // possible for tiles to point to pretty much everything - but not + // to stuff that inherits the system principal, so we check: + let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal(); + Services.scriptSecurityManager.checkLoadURIStrWithPrincipal( + systemPrincipal, + aURI, + this.flags + ); + return true; + } catch (e) { + // We got a weird URI or one that would inherit the caller's principal. + return false; + } + }, +}; + +var ExpirationFilter = { + init: function ExpirationFilter_init() { + lazy.PageThumbs.addExpirationFilter(this); + }, + + filterForThumbnailExpiration: + function ExpirationFilter_filterForThumbnailExpiration(aCallback) { + if (!AllPages.enabled) { + aCallback([]); + return; + } + + Links.populateCache(function () { + let urls = []; + + // Add all URLs to the list that we want to keep thumbnails for. + for (let link of Links.getLinks().slice(0, 25)) { + if (link && link.url) { + urls.push(link.url); + } + } + + aCallback(urls); + }); + }, +}; + +/** + * Singleton that provides the public API of this JSM. + */ +export var NewTabUtils = { + _initialized: false, + + /** + * Extract a "site" from a url in a way that multiple urls of a "site" returns + * the same "site." + * @param aUrl Url spec string + * @return The "site" string or null + */ + extractSite: function Links_extractSite(url) { + let host; + try { + // Note that nsIURI.asciiHost throws NS_ERROR_FAILURE for some types of + // URIs, including jar and moz-icon URIs. + host = Services.io.newURI(url).asciiHost; + } catch (ex) { + return null; + } + + // Strip off common subdomains of the same site (e.g., www, load balancer) + return host.replace(/^(m|mobile|www\d*)\./, ""); + }, + + init: function NewTabUtils_init() { + if (this.initWithoutProviders()) { + PlacesProvider.init(); + Links.addProvider(PlacesProvider); + BlockedLinks.addObserver(Links); + BlockedLinks.addObserver(ActivityStreamLinks); + } + }, + + initWithoutProviders: function NewTabUtils_initWithoutProviders() { + if (!this._initialized) { + this._initialized = true; + ExpirationFilter.init(); + Telemetry.init(); + return true; + } + return false; + }, + + uninit: function NewTabUtils_uninit() { + if (this.initialized) { + Telemetry.uninit(); + BlockedLinks.removeObservers(); + } + }, + + getProviderLinks(aProvider) { + let cache = Links._providers.get(aProvider); + if (cache && cache.sortedLinks) { + return cache.sortedLinks; + } + return []; + }, + + isTopSiteGivenProvider(aSite, aProvider) { + let cache = Links._providers.get(aProvider); + if (cache && cache.siteMap) { + return cache.siteMap.has(aSite); + } + return false; + }, + + isTopPlacesSite(aSite) { + return this.isTopSiteGivenProvider(aSite, PlacesProvider); + }, + + /** + * Restores all sites that have been removed from the grid. + */ + restore: function NewTabUtils_restore() { + lazy.Storage.clear(); + Links.resetCache(); + PinnedLinks.resetCache(); + BlockedLinks.resetCache(); + + Links.populateCache(function () { + AllPages.update(); + }, true); + }, + + /** + * Undoes all sites that have been removed from the grid and keep the pinned + * tabs. + * @param aCallback the callback method. + */ + undoAll: function NewTabUtils_undoAll(aCallback) { + lazy.Storage.remove("blockedLinks"); + Links.resetCache(); + BlockedLinks.resetCache(); + Links.populateCache(aCallback, true); + }, + + links: Links, + allPages: AllPages, + pinnedLinks: PinnedLinks, + blockedLinks: BlockedLinks, + activityStreamLinks: ActivityStreamLinks, + activityStreamProvider: ActivityStreamProvider, +}; diff --git a/toolkit/modules/OSKeyStore.sys.mjs b/toolkit/modules/OSKeyStore.sys.mjs new file mode 100644 index 0000000000..3d759ed740 --- /dev/null +++ b/toolkit/modules/OSKeyStore.sys.mjs @@ -0,0 +1,382 @@ +/* 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/. */ + +/** + * Helpers for using OS Key Store. + */ + +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; + +const lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs", +}); +XPCOMUtils.defineLazyServiceGetter( + lazy, + "nativeOSKeyStore", + "@mozilla.org/security/oskeystore;1", + Ci.nsIOSKeyStore +); +XPCOMUtils.defineLazyServiceGetter( + lazy, + "osReauthenticator", + "@mozilla.org/security/osreauthenticator;1", + Ci.nsIOSReauthenticator +); + +// Skip reauth during tests, only works in non-official builds. +const TEST_ONLY_REAUTH = "toolkit.osKeyStore.unofficialBuildOnlyLogin"; + +export var OSKeyStore = { + /** + * On macOS this becomes part of the name label visible on Keychain Acesss as + * "Firefox Encrypted Storage" (where "Firefox" is the MOZ_APP_BASENAME). + * Unfortunately, since this is the index into the keystore, we can't + * localize it without some really unfortunate side effects, like users + * losing access to stored information when they change their locale. + * This is a limitation of the interface exposed by macOS. Notably, both + * Chrome and Safari suffer the same shortcoming. + */ + STORE_LABEL: AppConstants.MOZ_APP_BASENAME + " Encrypted Storage", + + /** + * Consider the module is initialized as locked. OS might unlock without a + * prompt. + * @type {Boolean} + */ + _isLocked: true, + + _pendingUnlockPromise: null, + + /** + * @returns {boolean} True if logged in (i.e. decrypt(reauth = false) will + * not retrigger a dialog) and false if not. + * User might log out elsewhere in the OS, so even if this + * is true a prompt might still pop up. + */ + get isLoggedIn() { + return !this._isLocked; + }, + + /** + * @returns {boolean} True if there is another login dialog existing and false + * otherwise. + */ + get isUIBusy() { + return !!this._pendingUnlockPromise; + }, + + canReauth() { + // We have no support on linux (bug 1527745) + if (AppConstants.platform == "win" || AppConstants.platform == "macosx") { + lazy.log.debug( + "canReauth, returning true, this._testReauth:", + this._testReauth + ); + return true; + } + lazy.log.debug("canReauth, returning false"); + return false; + }, + + /** + * If the test pref exists, this method will dispatch a observer message and + * resolves to simulate successful reauth, or rejects to simulate failed reauth. + * + * @returns {Promise<undefined>} Resolves when sucessful login, rejects when + * login fails. + */ + async _reauthInTests() { + // Skip this reauth because there is no way to mock the + // native dialog in the testing environment, for now. + lazy.log.debug("_reauthInTests: _testReauth: ", this._testReauth); + switch (this._testReauth) { + case "pass": + Services.obs.notifyObservers( + null, + "oskeystore-testonly-reauth", + "pass" + ); + return { authenticated: true, auth_details: "success" }; + case "cancel": + Services.obs.notifyObservers( + null, + "oskeystore-testonly-reauth", + "cancel" + ); + throw new Components.Exception( + "Simulating user cancelling login dialog", + Cr.NS_ERROR_FAILURE + ); + default: + throw new Components.Exception( + "Unknown test pref value", + Cr.NS_ERROR_FAILURE + ); + } + }, + + /** + * Ensure the store in use is logged in. It will display the OS + * login prompt or do nothing if it's logged in already. If an existing login + * prompt is already prompted, the result from it will be used instead. + * + * Note: This method must set _pendingUnlockPromise before returning the + * promise (i.e. the first |await|), otherwise we'll risk re-entry. + * This is why there aren't an |await| in the method. The method is marked as + * |async| to communicate that it's async. + * + * @param {boolean|string} reauth If set to a string, prompt the reauth login dialog, + * showing the string on the native OS login dialog. + * Otherwise `false` will prevent showing the prompt. + * @param {string} dialogCaption The string will be shown on the native OS + * login dialog as the dialog caption (usually Product Name). + * @param {Window?} parentWindow The window of the caller, used to center the + * OS prompt in the middle of the application window. + * @param {boolean} generateKeyIfNotAvailable Makes key generation optional + * because it will currently cause more + * problems for us down the road on macOS since the application + * that creates the Keychain item is the only one that gets + * access to the key in the future and right now that key isn't + * specific to the channel or profile. This means if a user uses + * both DevEdition and Release on the same OS account (not + * unreasonable for a webdev.) then when you want to simply + * re-auth the user for viewing passwords you may also get a + * KeyChain prompt to allow the app to access the stored key even + * though that's not at all relevant for the re-auth. We skip the + * code here so that we can postpone deciding on how we want to + * handle this problem (multiple channels) until we actually use + * the key storage. If we start creating keys on macOS by running + * this code we'll potentially have to do extra work to cleanup + * the mess later. + * @returns {Promise<Object>} Object with the following properties: + * authenticated: {boolean} Set to true if the user successfully authenticated. + * auth_details: {String?} Details of the authentication result. + */ + async ensureLoggedIn( + reauth = false, + dialogCaption = "", + parentWindow = null, + generateKeyIfNotAvailable = true + ) { + if ( + (typeof reauth != "boolean" && typeof reauth != "string") || + reauth === true || + reauth === "" + ) { + throw new Error( + "reauth is required to either be `false` or a non-empty string" + ); + } + + if (this._pendingUnlockPromise) { + lazy.log.debug("ensureLoggedIn: Has a pending unlock operation"); + return this._pendingUnlockPromise; + } + lazy.log.debug( + "ensureLoggedIn: Creating new pending unlock promise. reauth: ", + reauth + ); + + let unlockPromise; + if (typeof reauth == "string") { + // Only allow for local builds + if ( + lazy.UpdateUtils.getUpdateChannel(false) == "default" && + this._testReauth + ) { + unlockPromise = this._reauthInTests(); + } else if (this.canReauth()) { + // On Windows, this promise rejects when the user cancels login dialog, see bug 1502121. + // On macOS this resolves to false, so we would need to check it. + unlockPromise = lazy.osReauthenticator + .asyncReauthenticateUser(reauth, dialogCaption, parentWindow) + .then(reauthResult => { + let auth_details_extra = {}; + if (reauthResult.length > 3) { + auth_details_extra.auto_admin = "" + !!reauthResult[2]; + auth_details_extra.require_signon = "" + !!reauthResult[3]; + } + if (!reauthResult[0]) { + throw new Components.Exception( + "User canceled OS reauth entry", + Cr.NS_ERROR_FAILURE, + null, + auth_details_extra + ); + } + let result = { + authenticated: true, + auth_details: "success", + auth_details_extra, + }; + if (reauthResult.length > 1 && reauthResult[1]) { + result.auth_details += "_no_password"; + } + return result; + }); + } else { + lazy.log.debug( + "ensureLoggedIn: Skipping reauth on unsupported platforms" + ); + unlockPromise = Promise.resolve({ + authenticated: true, + auth_details: "success_unsupported_platform", + }); + } + } else { + unlockPromise = Promise.resolve({ authenticated: true }); + } + + if (generateKeyIfNotAvailable) { + unlockPromise = unlockPromise.then(async reauthResult => { + if ( + !(await lazy.nativeOSKeyStore.asyncSecretAvailable(this.STORE_LABEL)) + ) { + lazy.log.debug( + "ensureLoggedIn: Secret unavailable, attempt to generate new secret." + ); + let recoveryPhrase = await lazy.nativeOSKeyStore.asyncGenerateSecret( + this.STORE_LABEL + ); + // TODO We should somehow have a dialog to ask the user to write this down, + // and another dialog somewhere for the user to restore the secret with it. + // (Intentionally not printing it out in the console) + lazy.log.debug( + "ensureLoggedIn: Secret generated. Recovery phrase length: " + + recoveryPhrase.length + ); + } + return reauthResult; + }); + } + + unlockPromise = unlockPromise.then( + reauthResult => { + lazy.log.debug("ensureLoggedIn: Logged in"); + this._pendingUnlockPromise = null; + this._isLocked = false; + + return reauthResult; + }, + err => { + lazy.log.debug("ensureLoggedIn: Not logged in", err); + this._pendingUnlockPromise = null; + this._isLocked = true; + + return { + authenticated: false, + auth_details: "fail", + auth_details_extra: err.data?.QueryInterface(Ci.nsISupports) + .wrappedJSObject, + }; + } + ); + + this._pendingUnlockPromise = unlockPromise; + + return this._pendingUnlockPromise; + }, + + /** + * Decrypts cipherText. + * + * Note: In the event of an rejection, check the result property of the Exception + * object. Handles NS_ERROR_ABORT as user has cancelled the action (e.g., + * don't show that dialog), apart from other errors (e.g., gracefully + * recover from that and still shows the dialog.) + * + * @param {string} cipherText Encrypted string including the algorithm details. + * @param {boolean|string} reauth If set to a string, prompt the reauth login dialog. + * The string may be shown on the native OS + * login dialog. Empty strings and `true` are disallowed. + * @returns {Promise<string>} resolves to the decrypted string, or rejects otherwise. + */ + async decrypt(cipherText, reauth = false) { + if (!(await this.ensureLoggedIn(reauth)).authenticated) { + throw Components.Exception( + "User canceled OS unlock entry", + Cr.NS_ERROR_ABORT + ); + } + let bytes = await lazy.nativeOSKeyStore.asyncDecryptBytes( + this.STORE_LABEL, + cipherText + ); + return String.fromCharCode.apply(String, bytes); + }, + + /** + * Encrypts a string and returns cipher text containing algorithm information used for decryption. + * + * @param {string} plainText Original string without encryption. + * @returns {Promise<string>} resolves to the encrypted string (with algorithm), otherwise rejects. + */ + async encrypt(plainText) { + if (!(await this.ensureLoggedIn()).authenticated) { + throw Components.Exception( + "User canceled OS unlock entry", + Cr.NS_ERROR_ABORT + ); + } + + // Convert plain text into a UTF-8 binary string + plainText = unescape(encodeURIComponent(plainText)); + + // Convert it to an array + let textArr = []; + for (let char of plainText) { + textArr.push(char.charCodeAt(0)); + } + + let rawEncryptedText = await lazy.nativeOSKeyStore.asyncEncryptBytes( + this.STORE_LABEL, + textArr + ); + + // Mark the output with a version number. + return rawEncryptedText; + }, + + /** + * Resolve when the login dialogs are closed, immediately if none are open. + * + * An existing MP dialog will be focused and will request attention. + * + * @returns {Promise<boolean>} + * Resolves with whether the user is logged in to MP. + */ + async waitForExistingDialog() { + if (this.isUIBusy) { + return this._pendingUnlockPromise; + } + return this.isLoggedIn; + }, + + /** + * Remove the store. For tests. + */ + async cleanup() { + return lazy.nativeOSKeyStore.asyncDeleteSecret(this.STORE_LABEL); + }, +}; + +ChromeUtils.defineLazyGetter(lazy, "log", () => { + let { ConsoleAPI } = ChromeUtils.importESModule( + "resource://gre/modules/Console.sys.mjs" + ); + return new ConsoleAPI({ + maxLogLevelPref: "toolkit.osKeyStore.loglevel", + prefix: "OSKeyStore", + }); +}); + +XPCOMUtils.defineLazyPreferenceGetter( + OSKeyStore, + "_testReauth", + TEST_ONLY_REAUTH, + "" +); diff --git a/toolkit/modules/ObjectUtils.sys.mjs b/toolkit/modules/ObjectUtils.sys.mjs new file mode 100644 index 0000000000..e0fbeead12 --- /dev/null +++ b/toolkit/modules/ObjectUtils.sys.mjs @@ -0,0 +1,199 @@ +/* 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/. */ + +// Portions of this file are originally from narwhal.js (http://narwhaljs.org) +// Copyright (c) 2009 Thomas Robinson <280north.com> +// MIT license: http://opensource.org/licenses/MIT + +// Used only to cause test failures. + +var pSlice = Array.prototype.slice; + +export var ObjectUtils = { + /** + * This tests objects & values for deep equality. + * + * We check using the most exact approximation of equality between two objects + * to keep the chance of false positives to a minimum. + * `JSON.stringify` is not designed to be used for this purpose; objects may + * have ambiguous `toJSON()` implementations that would influence the test. + * + * @param a (mixed) Object or value to be compared. + * @param b (mixed) Object or value to be compared. + * @return Boolean Whether the objects are deep equal. + */ + deepEqual(a, b) { + return _deepEqual(a, b); + }, + + /** + * A thin wrapper on an object, designed to prevent client code from + * accessing non-existent properties because of typos. + * + * // Without `strict` + * let foo = { myProperty: 1 }; + * foo.MyProperty; // undefined + * + * // With `strict` + * let strictFoo = ObjectUtils.strict(foo); + * strictFoo.myProperty; // 1 + * strictFoo.MyProperty; // TypeError: No such property "MyProperty" + * + * Note that `strict` has no effect in non-DEBUG mode. + */ + strict(obj) { + return _strict(obj); + }, + + /** + * Returns `true` if `obj` is an array without elements, an object without + * enumerable properties, or a falsy primitive; `false` otherwise. + */ + isEmpty(obj) { + if (!obj) { + return true; + } + if (typeof obj != "object") { + return false; + } + if (Array.isArray(obj)) { + return !obj.length; + } + for (let key in obj) { + return false; + } + return true; + }, +}; + +// ... Start of previously MIT-licensed code. +// This deepEqual implementation is originally from narwhal.js (http://narwhaljs.org) +// Copyright (c) 2009 Thomas Robinson <280north.com> +// MIT license: http://opensource.org/licenses/MIT + +function _deepEqual(a, b) { + // The numbering below refers to sections in the CommonJS spec. + + // 7.1 All identical values are equivalent, as determined by ===. + if (a === b) { + return true; + // 7.2 If the b value is a Date object, the a value is + // equivalent if it is also a Date object that refers to the same time. + } + let aIsDate = instanceOf(a, "Date"); + let bIsDate = instanceOf(b, "Date"); + if (aIsDate || bIsDate) { + if (!aIsDate || !bIsDate) { + return false; + } + if (isNaN(a.getTime()) && isNaN(b.getTime())) { + return true; + } + return a.getTime() === b.getTime(); + // 7.3 If the b value is a RegExp object, the a value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } + let aIsRegExp = instanceOf(a, "RegExp"); + let bIsRegExp = instanceOf(b, "RegExp"); + if (aIsRegExp || bIsRegExp) { + return ( + aIsRegExp && + bIsRegExp && + a.source === b.source && + a.global === b.global && + a.multiline === b.multiline && + a.lastIndex === b.lastIndex && + a.ignoreCase === b.ignoreCase + ); + // 7.4 Other pairs that do not both pass typeof value == "object", + // equivalence is determined by ==. + } + if (typeof a != "object" || typeof b != "object") { + return a == b; + } + // 7.5 For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + return objEquiv(a, b); +} + +function instanceOf(object, type) { + return Object.prototype.toString.call(object) == "[object " + type + "]"; +} + +function isUndefinedOrNull(value) { + return value === null || value === undefined; +} + +function isArguments(object) { + return instanceOf(object, "Arguments"); +} + +function objEquiv(a, b) { + if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) { + return false; + } + // An identical 'prototype' property. + if ((a.prototype || undefined) != (b.prototype || undefined)) { + return false; + } + // Object.keys may be broken through screwy arguments passing. Converting to + // an array solves the problem. + if (isArguments(a)) { + if (!isArguments(b)) { + return false; + } + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b); + } + let ka, kb; + try { + ka = Object.keys(a); + kb = Object.keys(b); + } catch (e) { + // Happens when one is a string literal and the other isn't + return false; + } + // Having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length != kb.length) { + return false; + } + // The same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + // Equivalent values for every corresponding key, and possibly expensive deep + // test + for (let key of ka) { + if (!_deepEqual(a[key], b[key])) { + return false; + } + } + return true; +} + +// ... End of previously MIT-licensed code. + +function _strict(obj) { + if (typeof obj != "object") { + throw new TypeError("Expected an object"); + } + + return new Proxy(obj, { + get(target, name) { + if (name in obj) { + return obj[name]; + } + + let error = new TypeError(`No such property: "${name}"`); + Promise.reject(error); // Cause an xpcshell/mochitest failure. + throw error; + }, + }); +} diff --git a/toolkit/modules/OsEnvironment.sys.mjs b/toolkit/modules/OsEnvironment.sys.mjs new file mode 100644 index 0000000000..c16cbf3187 --- /dev/null +++ b/toolkit/modules/OsEnvironment.sys.mjs @@ -0,0 +1,94 @@ +/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */ +/* 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 { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs", + WindowsVersionInfo: + "resource://gre/modules/components-utils/WindowsVersionInfo.sys.mjs", +}); + +export let OsEnvironment = { + /** + * This is a policy object used to override behavior for testing. + */ + Policy: { + getAllowedAppSources: () => + lazy.WindowsRegistry.readRegKey( + Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer", + "AicEnabled" + ), + windowsVersionHasAppSourcesFeature: () => { + let windowsVersion = parseFloat(Services.sysinfo.getProperty("version")); + if (isNaN(windowsVersion)) { + throw new Error("Unable to parse Windows version"); + } + if (windowsVersion < 10) { + return false; + } + + // The App Sources feature was added in Windows 10, build 15063. + const { buildNumber } = lazy.WindowsVersionInfo.get(); + return buildNumber >= 15063; + }, + }, + + reportAllowedAppSources() { + if (AppConstants.platform != "win") { + // This is currently a windows-only feature. + return; + } + + const appSourceScalar = "os.environment.allowed_app_sources"; + + let haveAppSourcesFeature; + try { + haveAppSourcesFeature = + OsEnvironment.Policy.windowsVersionHasAppSourcesFeature(); + } catch (ex) { + console.error(ex); + Services.telemetry.scalarSet(appSourceScalar, "Error"); + return; + } + if (!haveAppSourcesFeature) { + Services.telemetry.scalarSet(appSourceScalar, "NoSuchFeature"); + return; + } + + let allowedAppSources; + try { + allowedAppSources = OsEnvironment.Policy.getAllowedAppSources(); + } catch (ex) { + console.error(ex); + Services.telemetry.scalarSet(appSourceScalar, "Error"); + return; + } + if (allowedAppSources === undefined) { + // A return value of undefined means that the registry value didn't + // exist. Windows treats a missing registry entry the same as if the + // value is "Anywhere". Make sure to differentiate a missing registry + // entry from one containing an empty string, since it is unclear how + // Windows would treat such a setting, but it may not be the same as + // if the value is missing. + allowedAppSources = "Anywhere"; + } + + const expectedValues = [ + "Anywhere", + "Recommendations", + "PreferStore", + "StoreOnly", + ]; + if (!expectedValues.includes(allowedAppSources)) { + allowedAppSources = "Error"; + } + + Services.telemetry.scalarSet(appSourceScalar, allowedAppSources); + }, +}; diff --git a/toolkit/modules/PermissionsUtils.sys.mjs b/toolkit/modules/PermissionsUtils.sys.mjs new file mode 100644 index 0000000000..593cf97b04 --- /dev/null +++ b/toolkit/modules/PermissionsUtils.sys.mjs @@ -0,0 +1,107 @@ +// 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/. + +var gImportedPrefBranches = new Set(); + +function importPrefBranch(aPrefBranch, aPermission, aAction) { + let list = Services.prefs.getChildList(aPrefBranch); + + for (let pref of list) { + let origins = Services.prefs.getCharPref(pref, ""); + + if (!origins) { + continue; + } + + origins = origins.split(","); + + for (let origin of origins) { + let principals = []; + try { + principals = [ + Services.scriptSecurityManager.createContentPrincipalFromOrigin( + origin + ), + ]; + } catch (e) { + // This preference used to contain a list of hosts. For back-compat + // reasons, we convert these hosts into http:// and https:// permissions + // on default ports. + try { + let httpURI = Services.io.newURI("http://" + origin); + let httpsURI = Services.io.newURI("https://" + origin); + + principals = [ + Services.scriptSecurityManager.createContentPrincipal(httpURI, {}), + Services.scriptSecurityManager.createContentPrincipal(httpsURI, {}), + ]; + } catch (e2) {} + } + + for (let principal of principals) { + try { + Services.perms.addFromPrincipal(principal, aPermission, aAction); + } catch (e) {} + } + } + + Services.prefs.setCharPref(pref, ""); + } +} + +export var PermissionsUtils = { + /** + * Import permissions from perferences to the Permissions Manager. After being + * imported, all processed permissions will be set to an empty string. + * Perferences are only processed once during the application's + * lifetime - it's safe to call this multiple times without worrying about + * doing unnecessary work, as the preferences branch will only be processed + * the first time. + * + * @param aPrefBranch Preferences branch to import from. The preferences + * under this branch can specify whitelist (ALLOW_ACTION) + * or blacklist (DENY_ACTION) additions using perference + * names of the form: + * * <BRANCH>.whitelist.add.<ID> + * * <BRANCH>.blacklist.add.<ID> + * Where <ID> can be any valid preference name. + * The value is expected to be a comma separated list of + * host named. eg: + * * something.example.com + * * foo.exmaple.com,bar.example.com + * + * @param aPermission Permission name to be passsed to the Permissions + * Manager. + */ + importFromPrefs(aPrefBranch, aPermission) { + if (!aPrefBranch.endsWith(".")) { + aPrefBranch += "."; + } + + // Ensure we only import this pref branch once. + if (gImportedPrefBranches.has(aPrefBranch)) { + return; + } + + importPrefBranch( + aPrefBranch + "whitelist.add", + aPermission, + Services.perms.ALLOW_ACTION + ); + importPrefBranch( + aPrefBranch + "blacklist.add", + aPermission, + Services.perms.DENY_ACTION + ); + + gImportedPrefBranches.add(aPrefBranch); + }, +}; + +// For test use only. +export const PermissionsTestUtils = { + clearImportedPrefBranches() { + gImportedPrefBranches.clear(); + }, +}; diff --git a/toolkit/modules/PopupNotifications.sys.mjs b/toolkit/modules/PopupNotifications.sys.mjs new file mode 100644 index 0000000000..2f4893a2f6 --- /dev/null +++ b/toolkit/modules/PopupNotifications.sys.mjs @@ -0,0 +1,2030 @@ +/* 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 { PrivateBrowsingUtils } from "resource://gre/modules/PrivateBrowsingUtils.sys.mjs"; +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const NOTIFICATION_EVENT_DISMISSED = "dismissed"; +const NOTIFICATION_EVENT_REMOVED = "removed"; +const NOTIFICATION_EVENT_SHOWING = "showing"; +const NOTIFICATION_EVENT_SHOWN = "shown"; +const NOTIFICATION_EVENT_SWAPPING = "swapping"; + +const ICON_SELECTOR = ".notification-anchor-icon"; +const ICON_ATTRIBUTE_SHOWING = "showing"; +const ICON_ANCHOR_ATTRIBUTE = "popupnotificationanchor"; + +const PREF_SECURITY_DELAY = "security.notification_enable_delay"; +const FULLSCREEN_TRANSITION_TIME_SHOWN_OFFSET_MS = 2000; + +// Enumerated values for the POPUP_NOTIFICATION_STATS telemetry histogram. +const TELEMETRY_STAT_OFFERED = 0; +const TELEMETRY_STAT_ACTION_1 = 1; +const TELEMETRY_STAT_ACTION_2 = 2; +// const TELEMETRY_STAT_ACTION_3 = 3; +const TELEMETRY_STAT_ACTION_LAST = 4; +// const TELEMETRY_STAT_DISMISSAL_CLICK_ELSEWHERE = 5; +const TELEMETRY_STAT_REMOVAL_LEAVE_PAGE = 6; +// const TELEMETRY_STAT_DISMISSAL_CLOSE_BUTTON = 7; +const TELEMETRY_STAT_OPEN_SUBMENU = 10; +const TELEMETRY_STAT_LEARN_MORE = 11; + +const TELEMETRY_STAT_REOPENED_OFFSET = 20; +const lazy = {}; + +XPCOMUtils.defineLazyPreferenceGetter(lazy, "buttonDelay", PREF_SECURITY_DELAY); + +var popupNotificationsMap = new WeakMap(); +var gNotificationParents = new WeakMap(); + +function getAnchorFromBrowser(aBrowser, aAnchorID) { + let attrPrefix = aAnchorID ? aAnchorID.replace("notification-icon", "") : ""; + let anchor = + aBrowser.getAttribute(attrPrefix + ICON_ANCHOR_ATTRIBUTE) || + aBrowser[attrPrefix + ICON_ANCHOR_ATTRIBUTE] || + aBrowser.getAttribute(ICON_ANCHOR_ATTRIBUTE) || + aBrowser[ICON_ANCHOR_ATTRIBUTE]; + if (anchor) { + if (ChromeUtils.getClassName(anchor) == "XULElement") { + return anchor; + } + return aBrowser.ownerDocument.getElementById(anchor); + } + return null; +} + +/** + * Given a DOM node inside a <popupnotification>, return the parent <popupnotification>. + */ +function getNotificationFromElement(aElement) { + return aElement.closest("popupnotification"); +} + +/** + * Notification object describes a single popup notification. + * + * @see PopupNotifications.show() + */ +function Notification( + id, + message, + anchorID, + mainAction, + secondaryActions, + browser, + owner, + options +) { + this.id = id; + this.message = message; + this.anchorID = anchorID; + this.mainAction = mainAction; + this.secondaryActions = secondaryActions || []; + this.browser = browser; + this.owner = owner; + this.options = options || {}; + + this._dismissed = false; + // Will become a boolean when manually toggled by the user. + this._checkboxChecked = null; + this.wasDismissed = false; + this.recordedTelemetryStats = new Set(); + this.isPrivate = PrivateBrowsingUtils.isWindowPrivate( + this.browser.ownerGlobal + ); + this.timeCreated = this.owner.window.performance.now(); +} + +Notification.prototype = { + id: null, + message: null, + anchorID: null, + mainAction: null, + secondaryActions: null, + browser: null, + owner: null, + options: null, + timeShown: null, + + /** + * Indicates whether the notification is currently dismissed. + */ + set dismissed(value) { + this._dismissed = value; + if (value) { + // Keep the dismissal into account when recording telemetry. + this.wasDismissed = true; + } + }, + get dismissed() { + return this._dismissed; + }, + + /** + * Removes the notification and updates the popup accordingly if needed. + */ + remove: function Notification_remove() { + this.owner.remove(this); + }, + + get anchorElement() { + let iconBox = this.owner.iconBox; + + let anchorElement = getAnchorFromBrowser(this.browser, this.anchorID); + if (!iconBox) { + return anchorElement; + } + + if (!anchorElement && this.anchorID) { + anchorElement = iconBox.querySelector("#" + this.anchorID); + } + + // Use a default anchor icon if it's available + if (!anchorElement) { + anchorElement = + iconBox.querySelector("#default-notification-icon") || iconBox; + } + + return anchorElement; + }, + + reshow() { + this.owner._reshowNotifications(this.anchorElement, this.browser); + }, + + /** + * Adds a value to the specified histogram, that must be keyed by ID. + */ + _recordTelemetry(histogramId, value) { + if (this.isPrivate && !this.options.recordTelemetryInPrivateBrowsing) { + // The reason why we don't record telemetry in private windows is because + // the available actions can be different from regular mode. The main + // difference is that all of the persistent permission options like + // "Always remember" aren't there, so they really need to be handled + // separately to avoid skewing results. For notifications with the same + // choices, there would be no reason not to record in private windows as + // well, but it's just simpler to use the same check for everything. + return; + } + let histogram = Services.telemetry.getKeyedHistogramById(histogramId); + histogram.add("(all)", value); + histogram.add(this.id, value); + }, + + /** + * Adds an enumerated value to the POPUP_NOTIFICATION_STATS histogram, + * ensuring that it is recorded at most once for each distinct Notification. + * + * Statistics for reopened notifications are recorded in separate buckets. + * + * @param value + * One of the TELEMETRY_STAT_ constants. + */ + _recordTelemetryStat(value) { + if (this.wasDismissed) { + value += TELEMETRY_STAT_REOPENED_OFFSET; + } + if (!this.recordedTelemetryStats.has(value)) { + this.recordedTelemetryStats.add(value); + this._recordTelemetry("POPUP_NOTIFICATION_STATS", value); + } + }, +}; + +/** + * The PopupNotifications object manages popup notifications for a given browser + * window. + * @param tabbrowser + * window's TabBrowser. Used to observe tab switching events and + * for determining the active browser element. + * @param panel + * The <xul:panel/> element to use for notifications. The panel is + * populated with <popupnotification> children and displayed it as + * needed. + * @param iconBox + * Reference to a container element that should be hidden or + * unhidden when notifications are hidden or shown. It should be the + * parent of anchor elements whose IDs are passed to show(). + * It is used as a fallback popup anchor if notifications specify + * invalid or non-existent anchor IDs. + * @param options + * An optional object with the following optional properties: + * { + * shouldSuppress: + * If this function returns true, then all notifications are + * suppressed for this window. This state is checked on construction + * and when the "anchorVisibilityChange" method is called. + * getVisibleAnchorElement(anchorElement): + * A function which takes an anchor element as input and should return + * either the anchor if it's visible, a fallback anchor element, or if + * no fallback exists, a null element. + * } + */ +export function PopupNotifications(tabbrowser, panel, iconBox, options = {}) { + if (!tabbrowser) { + throw new Error("Invalid tabbrowser"); + } + if (iconBox && ChromeUtils.getClassName(iconBox) != "XULElement") { + throw new Error("Invalid iconBox"); + } + if (ChromeUtils.getClassName(panel) != "XULPopupElement") { + throw new Error("Invalid panel"); + } + + this._shouldSuppress = options.shouldSuppress || (() => false); + this._suppress = this._shouldSuppress(); + + this._getVisibleAnchorElement = options.getVisibleAnchorElement; + + this.window = tabbrowser.ownerGlobal; + this.panel = panel; + this.tabbrowser = tabbrowser; + this.iconBox = iconBox; + + // panel itself has a listener in the bubble phase and this listener + // needs to be called after that, so use bubble phase here. + this.panel.addEventListener("popuphidden", this); + this.panel.addEventListener("popuppositioned", this); + this.panel.classList.add("popup-notification-panel", "panel-no-padding"); + + // This listener will be attached to the chrome window whenever a notification + // is showing, to allow the user to dismiss notifications using the escape key. + this._handleWindowKeyPress = aEvent => { + if (aEvent.keyCode != aEvent.DOM_VK_ESCAPE) { + return; + } + + // Esc key cancels the topmost notification, if there is one. + let notification = this.panel.firstElementChild; + if (!notification) { + return; + } + + let doc = this.window.document; + let focusedElement = Services.focus.focusedElement; + + // If the chrome window has a focused element, let it handle the ESC key instead. + if ( + !focusedElement || + focusedElement == doc.body || + focusedElement == this.tabbrowser.selectedBrowser || + // Ignore focused elements inside the notification. + notification.contains(focusedElement) + ) { + let escAction = notification.notification.options.escAction; + this._onButtonEvent(aEvent, escAction, "esc-press", notification); + // Without this preventDefault call, the event will be sent to the content page + // and our event listener might be called again after receiving a reply from + // the content process, which could accidentally dismiss another notification. + aEvent.preventDefault(); + } + }; + + let documentElement = this.window.document.documentElement; + let locationBarHidden = documentElement + .getAttribute("chromehidden") + .includes("location"); + let isFullscreen = !!this.window.document.fullscreenElement; + + this.panel.setAttribute("followanchor", !locationBarHidden && !isFullscreen); + + // There are no anchor icons in DOM fullscreen mode, but we would + // still like to show the popup notification. To avoid an infinite + // loop of showing and hiding, we have to disable followanchor + // (which hides the element without an anchor) in fullscreen. + this.window.addEventListener( + "MozDOMFullscreen:Entered", + () => { + this.panel.setAttribute("followanchor", "false"); + }, + true + ); + this.window.addEventListener( + "MozDOMFullscreen:Exited", + () => { + this.panel.setAttribute("followanchor", !locationBarHidden); + }, + true + ); + + Services.obs.addObserver(this, "fullscreen-transition-start"); + Services.obs.addObserver(this, "pointer-lock-entered"); + + this.window.addEventListener("unload", () => { + Services.obs.removeObserver(this, "fullscreen-transition-start"); + Services.obs.removeObserver(this, "pointer-lock-entered"); + }); + + this.window.addEventListener("activate", this, true); + if (this.tabbrowser.tabContainer) { + this.tabbrowser.tabContainer.addEventListener("TabSelect", this, true); + + this.tabbrowser.tabContainer.addEventListener("TabClose", aEvent => { + // If the tab was just closed and we have notifications associated with it, + // then the notifications were closed because of the tab removal. We need to + // record this event in telemetry and fire the removal callback. + this.nextRemovalReason = TELEMETRY_STAT_REMOVAL_LEAVE_PAGE; + let notifications = this._getNotificationsForBrowser( + aEvent.target.linkedBrowser + ); + for (let notification of notifications) { + this._fireCallback( + notification, + NOTIFICATION_EVENT_REMOVED, + this.nextRemovalReason + ); + notification._recordTelemetryStat(this.nextRemovalReason); + } + }); + } +} + +PopupNotifications.prototype = { + window: null, + panel: null, + tabbrowser: null, + + _iconBox: null, + set iconBox(iconBox) { + // Remove the listeners on the old iconBox, if needed + if (this._iconBox) { + this._iconBox.removeEventListener("click", this); + this._iconBox.removeEventListener("keypress", this); + } + this._iconBox = iconBox; + if (iconBox) { + iconBox.addEventListener("click", this); + iconBox.addEventListener("keypress", this); + } + }, + get iconBox() { + return this._iconBox; + }, + + observe(subject, topic) { + // These observers apply to all windows. + if ( + topic == "fullscreen-transition-start" || + topic == "pointer-lock-entered" + ) { + // Extend security delay if the panel is open. + if (this.isPanelOpen) { + let notification = this.panel.firstChild?.notification; + if (notification) { + this._extendSecurityDelay([notification]); + } + } + } + }, + + /** + * Retrieve one or many Notification object/s associated with the browser/ID pair. + * @param {string|string[]} id + * The Notification ID or an array of IDs to search for. + * @param [browser] + * The browser whose notifications should be searched. If null, the + * currently selected browser's notifications will be searched. + * + * @returns {Notification|Notification[]|null} If passed a single id, returns the corresponding Notification object, or null if no such + * notification exists. + * If passed an id array, returns an array of Notification objects which match the ids. + */ + getNotification: function PopupNotifications_getNotification(id, browser) { + let notifications = this._getNotificationsForBrowser( + browser || this.tabbrowser.selectedBrowser + ); + if (Array.isArray(id)) { + return notifications.filter(x => id.includes(x.id)); + } + return notifications.find(x => x.id == id) || null; + }, + + /** + * Adds a new popup notification. + * @param browser + * The <xul:browser> element associated with the notification. Must not + * be null. + * @param id + * A unique ID that identifies the type of notification (e.g. + * "geolocation"). Only one notification with a given ID can be visible + * at a time. If a notification already exists with the given ID, it + * will be replaced. + * @param message + * A string containing the text to be displayed as the notification + * header. The string may optionally contain one or two "<>" as a + * placeholder which is later replaced by a host name or an addon name + * that is formatted to look bold, in which case the options.name + * property (as well as options.secondName if passing a "<>" and a "{}" + * placeholder) needs to be specified. "<>" will be considered as the + * first and "{}" as the second placeholder. + * @param anchorID + * The ID of the element that should be used as this notification + * popup's anchor. May be null, in which case the notification will be + * anchored to the iconBox. + * @param mainAction + * A JavaScript object literal describing the notification button's + * action. If present, it must have the following properties: + * - label (string): the button's label. + * - accessKey (string): the button's accessKey. + * - callback (function): a callback to be invoked when the button is + * pressed, is passed an object that contains the following fields: + * - checkboxChecked: (boolean) If the optional checkbox is checked. + * - source: (string): the source of the action that initiated the + * callback, either: + * - "button" if popup buttons were directly activated, or + * - "esc-press" if the user pressed the escape key, or + * - "menucommand" if a menu was activated. + * - [optional] dismiss (boolean): If this is true, the notification + * will be dismissed instead of removed after running the callback. + * - [optional] disabled (boolean): If this is true, the button + * will be disabled. + * If null, the notification will have a default "OK" action button + * that can be used to dismiss the popup and secondaryActions will be ignored. + * @param secondaryActions + * An optional JavaScript array describing the notification's alternate + * actions. The array should contain objects with the same properties + * as mainAction. These are used to populate the notification button's + * dropdown menu. + * @param options + * An options JavaScript object holding additional properties for the + * notification. The following properties are currently supported: + * persistence: An integer. The notification will not automatically + * dismiss for this many page loads. + * timeout: A time in milliseconds. The notification will not + * automatically dismiss before this time. + * persistWhileVisible: + * A boolean. If true, a visible notification will always + * persist across location changes. + * persistent: A boolean. If true, the notification will always + * persist even across tab and app changes (but not across + * location changes), until the user accepts or rejects + * the request. The notification will never be implicitly + * dismissed. + * dismissed: Whether the notification should be added as a dismissed + * notification. Dismissed notifications can be activated + * by clicking on their anchorElement. + * autofocus: Whether the notification should be autofocused on + * showing, stealing focus from any other focused element. + * eventCallback: + * Callback to be invoked when the notification changes + * state. The callback's first argument is a string + * identifying the state change: + * "dismissed": notification has been dismissed by the + * user (e.g. by clicking away or switching + * tabs) + * "removed": notification has been removed (due to + * location change or user action) + * "showing": notification is about to be shown + * (this can be fired multiple times as + * notifications are dismissed and re-shown) + * If the callback returns true, the notification + * will be dismissed. + * "shown": notification has been shown (this can be fired + * multiple times as notifications are dismissed + * and re-shown) + * "swapping": the docshell of the browser that created + * the notification is about to be swapped to + * another browser. A second parameter contains + * the browser that is receiving the docshell, + * so that the event callback can transfer stuff + * specific to this notification. + * If the callback returns true, the notification + * will be moved to the new browser. + * If the callback isn't implemented, returns false, + * or doesn't return any value, the notification + * will be removed. + * neverShow: Indicate that no popup should be shown for this + * notification. Useful for just showing the anchor icon. + * removeOnDismissal: + * Notifications with this parameter set to true will be + * removed when they would have otherwise been dismissed + * (i.e. any time the popup is closed due to user + * interaction). + * hideClose: Indicate that the little close button in the corner of + * the panel should be hidden. + * checkbox: An object that allows you to add a checkbox and + * control its behavior with these fields: + * label: + * (required) Label to be shown next to the checkbox. + * checked: + * (optional) Whether the checkbox should be checked + * by default. Defaults to false. + * checkedState: + * (optional) An object that allows you to customize + * the notification state when the checkbox is checked. + * disableMainAction: + * (optional) Whether the mainAction is disabled. + * Defaults to false. + * warningLabel: + * (optional) A (warning) text that is shown below the + * checkbox. Pass null to hide. + * uncheckedState: + * (optional) An object that allows you to customize + * the notification state when the checkbox is not checked. + * Has the same attributes as checkedState. + * popupIconClass: + * A string. A class (or space separated list of classes) + * that will be applied to the icon in the popup so that + * several notifications using the same panel can use + * different icons. + * popupIconURL: + * A string. URL of the image to be displayed in the popup. + * learnMoreURL: + * A string URL. Setting this property will make the + * prompt display a "Learn More" link that, when clicked, + * opens the URL in a new tab. + * displayURI: + * The nsIURI of the page the notification came + * from. If present, this will be displayed above the message. + * If the nsIURI represents a file, the path will be displayed, + * otherwise the hostPort will be displayed. + * name: + * An optional string formatted to look bold and used in the + * notifiation description header text. Usually a host name or + * addon name. + * secondName: + * An optional string formatted to look bold and used in the + * notification description header text. Usually a host name or + * addon name. This is similar to name, and only used in case + * where message contains a "<>" and a "{}" placeholder. "<>" + * is considered the first and "{}" is considered the second + * placeholder. + * escAction: + * An optional string indicating the action to take when the + * Esc key is pressed. This should be set to the name of the + * command to run. If not provided, "secondarybuttoncommand" + * will be used. + * extraAttr: + * An optional string value which will be given to the + * extraAttr attribute on the notification's anchorElement + * popupOptions: + * An optional object containing popup options passed to + * `openPopup()` when defined. + * recordTelemetryInPrivateBrowsing: + * An optional boolean indicating whether popup telemetry + * should be recorded in private browsing windows. By default, + * telemetry is NOT recorded in PBM, because the available + * options for persistent permission notifications are + * different between normal and PBM windows, potentially + * skewing the data. But for notifications that do not differ + * in PBM, this option can be used to ensure that popups in + * both PBM and normal windows record the same interactions. + * @returns the Notification object corresponding to the added notification. + */ + show: function PopupNotifications_show( + browser, + id, + message, + anchorID, + mainAction, + secondaryActions, + options + ) { + function isInvalidAction(a) { + return ( + !a || !(typeof a.callback == "function") || !a.label || !a.accessKey + ); + } + + if (!browser) { + throw new Error("PopupNotifications_show: invalid browser"); + } + if (!id) { + throw new Error("PopupNotifications_show: invalid ID"); + } + if (mainAction && isInvalidAction(mainAction)) { + throw new Error("PopupNotifications_show: invalid mainAction"); + } + if (secondaryActions && secondaryActions.some(isInvalidAction)) { + throw new Error("PopupNotifications_show: invalid secondaryActions"); + } + + let notification = new Notification( + id, + message, + anchorID, + mainAction, + secondaryActions, + browser, + this, + options + ); + + if (options) { + let escAction = options.escAction; + if ( + escAction != "buttoncommand" && + escAction != "secondarybuttoncommand" + ) { + escAction = "secondarybuttoncommand"; + } + notification.options.escAction = escAction; + } + + if (options && options.dismissed) { + notification.dismissed = true; + } + + let existingNotification = this.getNotification(id, browser); + if (existingNotification) { + this._remove(existingNotification); + } + + let notifications = this._getNotificationsForBrowser(browser); + notifications.push(notification); + + let isActiveBrowser = this._isActiveBrowser(browser); + let isActiveWindow = Services.focus.activeWindow == this.window; + + if (isActiveBrowser) { + if (isActiveWindow) { + // Autofocus if the notification requests focus. + if (options && !options.dismissed && options.autofocus) { + this.panel.removeAttribute("noautofocus"); + } else { + this.panel.setAttribute("noautofocus", "true"); + } + + // show panel now + this._update( + notifications, + new Set([notification.anchorElement]), + true + ); + } else { + // indicate attention and update the icon if necessary + if (!notification.dismissed) { + this.window.getAttention(); + } + this._updateAnchorIcons( + notifications, + this._getAnchorsForNotifications( + notifications, + notification.anchorElement + ) + ); + this._notify("backgroundShow"); + } + } else { + // Notify observers that we're not showing the popup (useful for testing) + this._notify("backgroundShow"); + } + + return notification; + }, + + /** + * Returns true if the notification popup is currently being displayed. + */ + get isPanelOpen() { + let panelState = this.panel.state; + + return panelState == "showing" || panelState == "open"; + }, + + /** + * Called by the consumer to indicate that the open panel should + * temporarily be hidden while the given panel is showing. + */ + suppressWhileOpen(panel) { + this._hidePanel().catch(console.error); + panel.addEventListener("popuphidden", aEvent => { + this._update(); + }); + }, + + /** + * Called by the consumer to indicate that a browser's location has changed, + * so that we can update the active notifications accordingly. + */ + locationChange: function PopupNotifications_locationChange(aBrowser) { + if (!aBrowser) { + throw new Error("PopupNotifications_locationChange: invalid browser"); + } + + let notifications = this._getNotificationsForBrowser(aBrowser); + + this.nextRemovalReason = TELEMETRY_STAT_REMOVAL_LEAVE_PAGE; + + notifications = notifications.filter(function (notification) { + // The persistWhileVisible option allows an open notification to persist + // across location changes + if (notification.options.persistWhileVisible && this.isPanelOpen) { + if ( + "persistence" in notification.options && + notification.options.persistence + ) { + notification.options.persistence--; + } + return true; + } + + // The persistence option allows a notification to persist across multiple + // page loads + if ( + "persistence" in notification.options && + notification.options.persistence + ) { + notification.options.persistence--; + return true; + } + + // The timeout option allows a notification to persist until a certain time + if ( + "timeout" in notification.options && + Date.now() <= notification.options.timeout + ) { + return true; + } + + notification._recordTelemetryStat(this.nextRemovalReason); + this._fireCallback( + notification, + NOTIFICATION_EVENT_REMOVED, + this.nextRemovalReason + ); + return false; + }, this); + + this._setNotificationsForBrowser(aBrowser, notifications); + + if (this._isActiveBrowser(aBrowser)) { + this.anchorVisibilityChange(); + } + }, + + /** + * Called by the consumer to indicate that the visibility of the notification + * anchors may have changed, but the location has not changed. This also + * checks whether all notifications are suppressed for this window. + * + * Calling this method may result in the "showing" and "shown" events for + * visible notifications to be invoked even if the anchor has not changed. + */ + anchorVisibilityChange() { + let suppress = this._shouldSuppress(); + if (!suppress) { + // If notifications are not suppressed, always update the visibility. + this._suppress = false; + let notifications = this._getNotificationsForBrowser( + this.tabbrowser.selectedBrowser + ); + this._update( + notifications, + this._getAnchorsForNotifications( + notifications, + getAnchorFromBrowser(this.tabbrowser.selectedBrowser) + ) + ); + return; + } + + // Notifications are suppressed, ensure that the panel is hidden. + if (!this._suppress) { + this._suppress = true; + this._hidePanel().catch(console.error); + } + }, + + /** + * Removes one or many Notifications. + * @param {Notification|Notification[]} notification - The Notification object/s to remove. + * @param {Boolean} [isCancel] - Whether to signal, in the notification event, that removal + * should be treated as cancel. This is currently used to cancel permission requests + * when their Notifications are removed. + */ + remove: function PopupNotifications_remove(notification, isCancel = false) { + let notificationArray = Array.isArray(notification) + ? notification + : [notification]; + let activeBrowser; + + notificationArray.forEach(n => { + this._remove(n, isCancel); + if (!activeBrowser && this._isActiveBrowser(n.browser)) { + activeBrowser = n.browser; + } + }); + + if (activeBrowser) { + let browserNotifications = + this._getNotificationsForBrowser(activeBrowser); + this._update(browserNotifications); + } + }, + + handleEvent(aEvent) { + switch (aEvent.type) { + case "popuphidden": + this._onPopupHidden(aEvent); + break; + case "activate": + case "popuppositioned": + if (this.isPanelOpen) { + for (let elt of this.panel.children) { + elt.notification.timeShown = Math.max( + this.window.performance.now(), + elt.notification.timeShown ?? 0 + ); + } + break; + } + // fall through + case "TabSelect": + // setTimeout(..., 0) needed, otherwise openPopup from "activate" event + // handler results in the popup being hidden again for some reason... + this.window.setTimeout(() => { + this._suppress = this._shouldSuppress(); + this._update(); + }, 0); + break; + case "click": + case "keypress": + this._onIconBoxCommand(aEvent); + break; + } + }, + + // Utility methods + + _ignoreDismissal: null, + _currentAnchorElement: null, + + /** + * Gets notifications for the currently selected browser. + */ + get _currentNotifications() { + return this.tabbrowser.selectedBrowser + ? this._getNotificationsForBrowser(this.tabbrowser.selectedBrowser) + : []; + }, + + _remove: function PopupNotifications_removeHelper( + notification, + isCancel = false + ) { + // This notification may already be removed, in which case let's just fail + // silently. + let notifications = this._getNotificationsForBrowser(notification.browser); + if (!notifications) { + return; + } + + var index = notifications.indexOf(notification); + if (index == -1) { + return; + } + + if (this._isActiveBrowser(notification.browser)) { + notification.anchorElement.removeAttribute(ICON_ATTRIBUTE_SHOWING); + } + + // remove the notification + notifications.splice(index, 1); + this._fireCallback( + notification, + NOTIFICATION_EVENT_REMOVED, + this.nextRemovalReason, + isCancel + ); + }, + + /** + * Dismisses the notification without removing it. + * + * @param {Event} the event associated with the user interaction that + * caused the dismissal + * @param {boolean} whether to disable persistent status. Normally, + * persistent prompts can not be dismissed. You can + * use this argument to force dismissal. + */ + _dismiss: function PopupNotifications_dismiss( + event, + disablePersistent = false + ) { + if (disablePersistent) { + let notificationEl = getNotificationFromElement(event.target); + if (notificationEl) { + notificationEl.notification.options.persistent = false; + } + } + + let browser = + this.panel.firstElementChild && + this.panel.firstElementChild.notification.browser; + this.panel.hidePopup(); + if (browser) { + browser.focus(); + } + }, + + /** + * Hides the notification popup. + */ + _hidePanel: function PopupNotifications_hide() { + if (this.panel.state == "closed") { + return Promise.resolve(); + } + if (this._ignoreDismissal) { + return this._ignoreDismissal.promise; + } + let deferred = Promise.withResolvers(); + this._ignoreDismissal = deferred; + this.panel.hidePopup(); + return deferred.promise; + }, + + /** + * Removes all notifications from the notification popup. + */ + _clearPanel() { + let popupnotification; + while ((popupnotification = this.panel.lastElementChild)) { + this.panel.removeChild(popupnotification); + + // If this notification was provided by the chrome document rather than + // created ad hoc, move it back to where we got it from. + let originalParent = gNotificationParents.get(popupnotification); + if (originalParent) { + popupnotification.notification = null; + + // Re-hide the notification such that it isn't rendered in the chrome + // document. _refreshPanel will unhide it again when needed. + popupnotification.hidden = true; + + originalParent.appendChild(popupnotification); + } + } + }, + + /** + * Formats the notification description message before we display it + * and splits it into three parts if the message contains "<>" as + * placeholder. + * + * param notification + * The Notification object which contains the message to format. + * + * @returns a Javascript object that has the following properties: + * start: A start label string containing the first part of the message. + * It may contain the whole string if the description message + * does not have "<>" as a placeholder. For example, local + * file URIs with description messages that don't display hostnames. + * name: A string that is formatted to look bold. It replaces the + * placeholder with the options.name property from the notification + * object which is usually an addon name or a host name. + * end: The last part of the description message. + */ + _formatDescriptionMessage(n) { + let text = {}; + let array = n.message.split(/<>|{}/); + text.start = array[0] || ""; + text.name = n.options.name || ""; + text.end = array[1] || ""; + if (array.length == 3) { + text.secondName = n.options.secondName || ""; + text.secondEnd = array[2] || ""; + + // name and secondName should be in logical positions. Swap them in case + // the second placeholder came before the first one in the original string. + if (n.message.indexOf("{}") < n.message.indexOf("<>")) { + let tmp = text.name; + text.name = text.secondName; + text.secondName = tmp; + } + } else if (array.length > 3) { + console.error( + "Unexpected array length encountered in " + + "_formatDescriptionMessage: ", + array.length + ); + } + return text; + }, + + _refreshPanel: function PopupNotifications_refreshPanel(notificationsToShow) { + this._clearPanel(); + + notificationsToShow.forEach(function (n) { + let doc = this.window.document; + + // Append "-notification" to the ID to try to avoid ID conflicts with other stuff + // in the document. + let popupnotificationID = n.id + "-notification"; + + // If the chrome document provides a popupnotification with this id, use + // that. Otherwise create it ad-hoc. + let popupnotification = doc.getElementById(popupnotificationID); + if (popupnotification) { + gNotificationParents.set( + popupnotification, + popupnotification.parentNode + ); + } else { + popupnotification = doc.createXULElement("popupnotification"); + } + + // Create the notification description element. + let desc = this._formatDescriptionMessage(n); + popupnotification.setAttribute("label", desc.start); + popupnotification.setAttribute("name", desc.name); + popupnotification.setAttribute("endlabel", desc.end); + if ("secondName" in desc && "secondEnd" in desc) { + popupnotification.setAttribute("secondname", desc.secondName); + popupnotification.setAttribute("secondendlabel", desc.secondEnd); + } else { + popupnotification.removeAttribute("secondname"); + popupnotification.removeAttribute("secondendlabel"); + } + + if (n.options.hintText) { + popupnotification.setAttribute("hinttext", n.options.hintText); + } else { + popupnotification.removeAttribute("hinttext"); + } + + popupnotification.setAttribute("id", popupnotificationID); + popupnotification.setAttribute("popupid", n.id); + popupnotification.setAttribute( + "oncommand", + "PopupNotifications._onCommand(event);" + ); + popupnotification.setAttribute( + "closebuttoncommand", + `PopupNotifications._dismiss(event, true);` + ); + + popupnotification.toggleAttribute( + "hasicon", + !!(n.options.popupIconURL || n.options.popupIconClass) + ); + + if (n.mainAction) { + popupnotification.setAttribute("buttonlabel", n.mainAction.label); + popupnotification.setAttribute( + "buttonaccesskey", + n.mainAction.accessKey + ); + popupnotification.setAttribute( + "buttoncommand", + "PopupNotifications._onButtonEvent(event, 'buttoncommand');" + ); + popupnotification.setAttribute( + "dropmarkerpopupshown", + "PopupNotifications._onButtonEvent(event, 'dropmarkerpopupshown');" + ); + popupnotification.setAttribute( + "learnmoreclick", + "PopupNotifications._onButtonEvent(event, 'learnmoreclick');" + ); + popupnotification.setAttribute( + "menucommand", + "PopupNotifications._onMenuCommand(event);" + ); + } else { + // Enable the default button to let the user close the popup if the close button is hidden + popupnotification.setAttribute( + "buttoncommand", + "PopupNotifications._onButtonEvent(event, 'buttoncommand');" + ); + popupnotification.toggleAttribute("buttonhighlight", true); + popupnotification.removeAttribute("buttonlabel"); + popupnotification.removeAttribute("buttonaccesskey"); + popupnotification.removeAttribute("dropmarkerpopupshown"); + popupnotification.removeAttribute("learnmoreclick"); + popupnotification.removeAttribute("menucommand"); + } + + let classes = "popup-notification-icon"; + if (n.options.popupIconClass) { + classes += " " + n.options.popupIconClass; + } + popupnotification.setAttribute("iconclass", classes); + + if (n.options.popupIconURL) { + popupnotification.setAttribute("icon", n.options.popupIconURL); + } else { + popupnotification.removeAttribute("icon"); + } + + if (n.options.learnMoreURL) { + popupnotification.setAttribute("learnmoreurl", n.options.learnMoreURL); + } else { + popupnotification.removeAttribute("learnmoreurl"); + } + + if (n.options.displayURI) { + let uri; + try { + if (n.options.displayURI instanceof Ci.nsIFileURL) { + uri = n.options.displayURI.pathQueryRef; + } else { + try { + uri = n.options.displayURI.hostPort; + } catch (e) { + uri = n.options.displayURI.spec; + } + } + popupnotification.setAttribute("origin", uri); + } catch (e) { + console.error(e); + popupnotification.removeAttribute("origin"); + } + } else { + popupnotification.removeAttribute("origin"); + } + + if (n.options.hideClose) { + popupnotification.setAttribute("closebuttonhidden", "true"); + } + + popupnotification.notification = n; + let menuitems = []; + + if (n.mainAction && n.secondaryActions && n.secondaryActions.length) { + let telemetryStatId = TELEMETRY_STAT_ACTION_2; + + let secondaryAction = n.secondaryActions[0]; + popupnotification.setAttribute( + "secondarybuttonlabel", + secondaryAction.label + ); + popupnotification.setAttribute( + "secondarybuttonaccesskey", + secondaryAction.accessKey + ); + popupnotification.setAttribute( + "secondarybuttoncommand", + "PopupNotifications._onButtonEvent(event, 'secondarybuttoncommand');" + ); + + for (let i = 1; i < n.secondaryActions.length; i++) { + let action = n.secondaryActions[i]; + let item = doc.createXULElement("menuitem"); + item.setAttribute("label", action.label); + item.setAttribute("accesskey", action.accessKey); + item.notification = n; + item.action = action; + + menuitems.push(item); + + // We can only record a limited number of actions in telemetry. If + // there are more, the latest are all recorded in the last bucket. + item.action.telemetryStatId = telemetryStatId; + if (telemetryStatId < TELEMETRY_STAT_ACTION_LAST) { + telemetryStatId++; + } + } + popupnotification.setAttribute("secondarybuttonhidden", "false"); + } else { + popupnotification.setAttribute("secondarybuttonhidden", "true"); + } + popupnotification.setAttribute( + "dropmarkerhidden", + n.secondaryActions.length < 2 ? "true" : "false" + ); + + let checkbox = n.options.checkbox; + if (checkbox && checkbox.label) { + let checked = + n._checkboxChecked != null ? n._checkboxChecked : !!checkbox.checked; + popupnotification.checkboxState = { + checked, + label: checkbox.label, + }; + + if (checked) { + this._setNotificationUIState( + popupnotification, + checkbox.checkedState + ); + } else { + this._setNotificationUIState( + popupnotification, + checkbox.uncheckedState + ); + } + } else { + popupnotification.checkboxState = null; + // Reset the UI state to avoid previous state bleeding into this prompt. + this._setNotificationUIState(popupnotification); + } + + this.panel.appendChild(popupnotification); + + // The popupnotification may be hidden if we got it from the chrome + // document rather than creating it ad hoc. + popupnotification.show(); + + popupnotification.menupopup.textContent = ""; + popupnotification.menupopup.append(...menuitems); + }, this); + }, + + _setNotificationUIState(notification, state = {}) { + let mainAction = notification.notification.mainAction; + if ( + (mainAction && mainAction.disabled) || + state.disableMainAction || + notification.hasAttribute("invalidselection") + ) { + notification.setAttribute("mainactiondisabled", "true"); + } else { + notification.removeAttribute("mainactiondisabled"); + } + if (state.warningLabel) { + notification.setAttribute("warninglabel", state.warningLabel); + notification.removeAttribute("warninghidden"); + } else { + notification.setAttribute("warninghidden", "true"); + } + }, + + _extendSecurityDelay(notifications) { + let now = this.window.performance.now(); + notifications.forEach(n => { + n.timeShown = now + FULLSCREEN_TRANSITION_TIME_SHOWN_OFFSET_MS; + }); + }, + + _showPanel: function PopupNotifications_showPanel( + notificationsToShow, + anchorElement + ) { + this.panel.hidden = false; + + notificationsToShow = notificationsToShow.filter(n => { + if (anchorElement != n.anchorElement) { + return false; + } + + let dismiss = this._fireCallback(n, NOTIFICATION_EVENT_SHOWING); + if (dismiss) { + n.dismissed = true; + } + return !dismiss; + }); + if (!notificationsToShow.length) { + return; + } + + let notificationIds = notificationsToShow.map(n => n.id); + + this._refreshPanel(notificationsToShow); + + // The element the PopupNotification should anchor to might not be visible. + // Check its visibility using a callback that returns the same anchor + // element if its visible, or a fallback option that is visible. + // If no fallbacks are visible, it should return null. + if (this._getVisibleAnchorElement) { + anchorElement = this._getVisibleAnchorElement(anchorElement); + } + // In case _getVisibleAnchorElement provided a non-visible element. + if (!anchorElement?.checkVisibility()) { + // We only ever show notifications for the current browser, + // so we can just use the current tab. + anchorElement = this.tabbrowser.selectedTab; + if (!anchorElement?.checkVisibility()) { + // If we're in an entirely chromeless environment, set the anchorElement + // to null and let openPopup show the notification at (0,0) later. + anchorElement = null; + } + } + + // Remember the time the notification was shown for the security delay. + notificationsToShow.forEach( + n => + (n.timeShown = Math.max( + this.window.performance.now(), + n.timeShown ?? 0 + )) + ); + + if (this.isPanelOpen && this._currentAnchorElement == anchorElement) { + notificationsToShow.forEach(function (n) { + this._fireCallback(n, NOTIFICATION_EVENT_SHOWN); + }, this); + + // Make sure we update the noautohide attribute on the panel, in case it changed. + if (notificationsToShow.some(n => n.options.persistent)) { + this.panel.setAttribute("noautohide", "true"); + } else { + this.panel.removeAttribute("noautohide"); + } + + // Let tests know that the panel was updated and what notifications it was + // updated with so that tests can wait for the correct notifications to be + // added. + let event = new this.window.CustomEvent("PanelUpdated", { + detail: notificationIds, + }); + this.panel.dispatchEvent(event); + return; + } + + // If the panel is already open but we're changing anchors, we need to hide + // it first. Otherwise it can appear in the wrong spot. (_hidePanel is + // safe to call even if the panel is already hidden.) + this._hidePanel().then(() => { + this._currentAnchorElement = anchorElement; + + if (notificationsToShow.some(n => n.options.persistent)) { + this.panel.setAttribute("noautohide", "true"); + } else { + this.panel.removeAttribute("noautohide"); + } + + notificationsToShow.forEach(function (n) { + // Record that the notification was actually displayed on screen. + // Notifications that were opened a second time or that were originally + // shown with "options.dismissed" will be recorded in a separate bucket. + n._recordTelemetryStat(TELEMETRY_STAT_OFFERED); + }, this); + + // We're about to open the panel while in a full screen transition or + // during pointer lock. Extend the security delay to avoid clickjacking. + if ( + this.window.isInFullScreenTransition || + this.window.PointerLock?.isActive + ) { + this._extendSecurityDelay(notificationsToShow); + } + + let target = this.panel; + if (target.parentNode) { + // NOTIFICATION_EVENT_SHOWN should be fired for the panel before + // anyone listening for popupshown on the panel gets run. Otherwise, + // the panel will not be initialized when the popupshown event + // listeners run. + // By targeting the panel's parent and using a capturing listener, we + // can have our listener called before others waiting for the panel to + // be shown (which probably expect the panel to be fully initialized) + target = target.parentNode; + } + if (this._popupshownListener) { + target.removeEventListener( + "popupshown", + this._popupshownListener, + true + ); + } + this._popupshownListener = function (e) { + target.removeEventListener( + "popupshown", + this._popupshownListener, + true + ); + this._popupshownListener = null; + + notificationsToShow.forEach(function (n) { + this._fireCallback(n, NOTIFICATION_EVENT_SHOWN); + }, this); + // These notifications are used by tests to know when all the processing + // required to display the panel has happened. + this.panel.dispatchEvent(new this.window.CustomEvent("Shown")); + let event = new this.window.CustomEvent("PanelUpdated", { + detail: notificationIds, + }); + this.panel.dispatchEvent(event); + }; + this._popupshownListener = this._popupshownListener.bind(this); + target.addEventListener("popupshown", this._popupshownListener, true); + + let popupOptions = notificationsToShow.findLast( + n => n.options?.popupOptions + )?.options?.popupOptions; + if (popupOptions) { + this.panel.openPopup(anchorElement, popupOptions); + } else { + this.panel.openPopup(anchorElement, "bottomleft topleft", 0, 0); + } + }); + }, + + /** + * Updates the notification state in response to window activation or tab + * selection changes. + * + * @param notifications an array of Notification instances. if null, + * notifications will be retrieved off the current + * browser tab + * @param anchors is a XUL element or a Set of XUL elements that the + * notifications panel(s) will be anchored to. + * @param dismissShowing if true, dismiss any currently visible notifications + * if there are no notifications to show. Otherwise, + * currently displayed notifications will be left alone. + */ + _update: function PopupNotifications_update( + notifications, + anchors = new Set(), + dismissShowing = false + ) { + if (ChromeUtils.getClassName(anchors) == "XULElement") { + anchors = new Set([anchors]); + } + + if (!notifications) { + notifications = this._currentNotifications; + } + + let haveNotifications = !!notifications.length; + if (!anchors.size && haveNotifications) { + anchors = this._getAnchorsForNotifications(notifications); + } + + let useIconBox = !!this.iconBox; + if (useIconBox && anchors.size) { + for (let anchor of anchors) { + if (anchor.parentNode == this.iconBox) { + continue; + } + useIconBox = false; + break; + } + } + + // Filter out notifications that have been dismissed, unless they are + // persistent. Also check if we should not show any notification. + let notificationsToShow = []; + if (!this._suppress) { + notificationsToShow = notifications.filter( + n => (!n.dismissed || n.options.persistent) && !n.options.neverShow + ); + } + + if (useIconBox) { + // Hide icons of the previous tab. + this._hideIcons(); + } + + if (haveNotifications) { + // Also filter out notifications that are for a different anchor. + notificationsToShow = notificationsToShow.filter(function (n) { + return anchors.has(n.anchorElement); + }); + + if (useIconBox) { + this._showIcons(notifications); + this.iconBox.hidden = false; + // Make sure that panels can only be attached to anchors of shown + // notifications inside an iconBox. + anchors = this._getAnchorsForNotifications(notificationsToShow); + } else if (anchors.size) { + this._updateAnchorIcons(notifications, anchors); + } + } + + if (notificationsToShow.length) { + let anchorElement = anchors.values().next().value; + if (anchorElement) { + this._showPanel(notificationsToShow, anchorElement); + } + + // Setup a capturing event listener on the whole window to catch the + // escape key while persistent notifications are visible. + this.window.addEventListener( + "keypress", + this._handleWindowKeyPress, + true + ); + } else { + // Notify observers that we're not showing the popup (useful for testing) + this._notify("updateNotShowing"); + + // Close the panel if there are no notifications to show. + // When called from PopupNotifications.show() we should never close the + // panel, however. It may just be adding a dismissed notification, in + // which case we want to continue showing any existing notifications. + if (!dismissShowing) { + this._dismiss(); + } + + // Only hide the iconBox if we actually have no notifications (as opposed + // to not having any showable notifications) + if (!haveNotifications) { + if (useIconBox) { + this.iconBox.hidden = true; + } else if (anchors.size) { + for (let anchorElement of anchors) { + anchorElement.removeAttribute(ICON_ATTRIBUTE_SHOWING); + } + } + } + + // Stop listening to keyboard events for notifications. + this.window.removeEventListener( + "keypress", + this._handleWindowKeyPress, + true + ); + } + }, + + _updateAnchorIcons: function PopupNotifications_updateAnchorIcons( + notifications, + anchorElements + ) { + for (let anchorElement of anchorElements) { + anchorElement.setAttribute(ICON_ATTRIBUTE_SHOWING, "true"); + } + }, + + _showIcons: function PopupNotifications_showIcons(aCurrentNotifications) { + for (let notification of aCurrentNotifications) { + let anchorElm = notification.anchorElement; + if (anchorElm) { + anchorElm.setAttribute(ICON_ATTRIBUTE_SHOWING, "true"); + + if (notification.options.extraAttr) { + anchorElm.setAttribute("extraAttr", notification.options.extraAttr); + } else { + anchorElm.removeAttribute("extraAttr"); + } + } + } + }, + + _hideIcons: function PopupNotifications_hideIcons() { + let icons = this.iconBox.querySelectorAll(ICON_SELECTOR); + for (let icon of icons) { + icon.removeAttribute(ICON_ATTRIBUTE_SHOWING); + } + }, + + /** + * Gets and sets notifications for the browser. + */ + _getNotificationsForBrowser: function PopupNotifications_getNotifications( + browser + ) { + let notifications = popupNotificationsMap.get(browser); + if (!notifications) { + // Initialize the WeakMap for the browser so callers can reference/manipulate the array. + notifications = []; + popupNotificationsMap.set(browser, notifications); + } + return notifications; + }, + _setNotificationsForBrowser: function PopupNotifications_setNotifications( + browser, + notifications + ) { + popupNotificationsMap.set(browser, notifications); + return notifications; + }, + + _getAnchorsForNotifications: + function PopupNotifications_getAnchorsForNotifications( + notifications, + defaultAnchor + ) { + let anchors = new Set(); + for (let notification of notifications) { + if (notification.anchorElement) { + anchors.add(notification.anchorElement); + } + } + if (defaultAnchor && !anchors.size) { + anchors.add(defaultAnchor); + } + return anchors; + }, + + _isActiveBrowser(browser) { + // We compare on frameLoader instead of just comparing the + // selectedBrowser and browser directly because browser tabs in + // Responsive Design Mode put the actual web content into a + // mozbrowser iframe and proxy property read/write and method + // calls from the tab to that iframe. This is so that attempts + // to reload the tab end up reloading the content in + // Responsive Design Mode, and not the Responsive Design Mode + // viewer itself. + // + // This means that PopupNotifications can come up from a browser + // in Responsive Design Mode, but the selectedBrowser will not match + // the browser being passed into this function, despite the browser + // actually being within the selected tab. We workaround this by + // comparing frameLoader instead, which is proxied from the outer + // <xul:browser> to the inner mozbrowser <iframe>. + return this.tabbrowser.selectedBrowser.frameLoader == browser.frameLoader; + }, + + _onIconBoxCommand: function PopupNotifications_onIconBoxCommand(event) { + // Left click, space or enter only + let type = event.type; + if (type == "click" && event.button != 0) { + return; + } + + if ( + type == "keypress" && + !( + event.charCode == event.DOM_VK_SPACE || + event.keyCode == event.DOM_VK_RETURN + ) + ) { + return; + } + + if (!this._currentNotifications.length) { + return; + } + + event.stopPropagation(); + + // Get the anchor that is the immediate child of the icon box + let anchor = event.target; + while (anchor && anchor.parentNode != this.iconBox) { + anchor = anchor.parentNode; + } + + if (!anchor) { + return; + } + + // If the panel is not closed, and the anchor is different, immediately mark all + // active notifications for the previous anchor as dismissed + if (this.panel.state != "closed" && anchor != this._currentAnchorElement) { + this._dismissOrRemoveCurrentNotifications(); + } + + // Avoid reshowing notifications that are already shown and have not been dismissed. + if (this.panel.state == "closed" || anchor != this._currentAnchorElement) { + // As soon as the panel is shown, focus the first element in the selected notification. + this.panel.addEventListener( + "popupshown", + () => + this.window.document.commandDispatcher.advanceFocusIntoSubtree( + this.panel + ), + { once: true } + ); + + this._reshowNotifications(anchor); + } else { + // Focus the first element in the selected notification. + this.window.document.commandDispatcher.advanceFocusIntoSubtree( + this.panel + ); + } + }, + + _reshowNotifications: function PopupNotifications_reshowNotifications( + anchor, + browser + ) { + // Mark notifications anchored to this anchor as un-dismissed + browser = browser || this.tabbrowser.selectedBrowser; + let notifications = this._getNotificationsForBrowser(browser); + notifications.forEach(function (n) { + if (n.anchorElement == anchor) { + n.dismissed = false; + } + }); + + if (this._isActiveBrowser(browser)) { + // ...and then show them. + this._update(notifications, anchor); + } + }, + + _swapBrowserNotifications: + function PopupNotifications_swapBrowserNoficications( + ourBrowser, + otherBrowser + ) { + // When swaping browser docshells (e.g. dragging tab to new window) we need + // to update our notification map. + + let ourNotifications = this._getNotificationsForBrowser(ourBrowser); + let other = otherBrowser.ownerGlobal.PopupNotifications; + if (!other) { + if (ourNotifications.length) { + console.error( + "unable to swap notifications: otherBrowser doesn't support notifications" + ); + } + return; + } + let otherNotifications = other._getNotificationsForBrowser(otherBrowser); + if (ourNotifications.length < 1 && otherNotifications.length < 1) { + // No notification to swap. + return; + } + + otherNotifications = otherNotifications.filter(n => { + if (this._fireCallback(n, NOTIFICATION_EVENT_SWAPPING, ourBrowser)) { + n.browser = ourBrowser; + n.owner = this; + return true; + } + other._fireCallback( + n, + NOTIFICATION_EVENT_REMOVED, + this.nextRemovalReason + ); + return false; + }); + + ourNotifications = ourNotifications.filter(n => { + if (this._fireCallback(n, NOTIFICATION_EVENT_SWAPPING, otherBrowser)) { + n.browser = otherBrowser; + n.owner = other; + return true; + } + this._fireCallback( + n, + NOTIFICATION_EVENT_REMOVED, + this.nextRemovalReason + ); + return false; + }); + + this._setNotificationsForBrowser(otherBrowser, ourNotifications); + other._setNotificationsForBrowser(ourBrowser, otherNotifications); + + if (otherNotifications.length) { + this._update(otherNotifications); + } + if (ourNotifications.length) { + other._update(ourNotifications); + } + }, + + _fireCallback: function PopupNotifications_fireCallback(n, event, ...args) { + try { + if (n.options.eventCallback) { + return n.options.eventCallback.call(n, event, ...args); + } + } catch (error) { + console.error(error); + } + return undefined; + }, + + _onPopupHidden: function PopupNotifications_onPopupHidden(event) { + if (event.target != this.panel) { + return; + } + + // It's possible that a popupnotification set `aria-describedby` on the + // panel element in its eventCallback function. If so, we'll clear that out + // before showing the next notification. + this.panel.removeAttribute("aria-describedby"); + + // We may have removed the "noautofocus" attribute before showing the panel + // if the notification specified it wants to autofocus on first show. + // When the panel is closed, we have to restore the attribute to its default + // value, so we don't autofocus it if it's subsequently opened from a different code path. + this.panel.setAttribute("noautofocus", "true"); + + // Handle the case where the panel was closed programmatically. + if (this._ignoreDismissal) { + this._ignoreDismissal.resolve(); + this._ignoreDismissal = null; + return; + } + + this._dismissOrRemoveCurrentNotifications(); + + this._clearPanel(); + + this._update(); + }, + + _dismissOrRemoveCurrentNotifications() { + let browser = + this.panel.firstElementChild && + this.panel.firstElementChild.notification.browser; + if (!browser) { + return; + } + + let notifications = this._getNotificationsForBrowser(browser); + // Mark notifications as dismissed and call dismissal callbacks + for (let nEl of this.panel.children) { + let notificationObj = nEl.notification; + // Never call a dismissal handler on a notification that's been removed. + if (!notifications.includes(notificationObj)) { + return; + } + + // Record the time of the first notification dismissal if the main action + // was not triggered in the meantime. + let timeSinceShown = + this.window.performance.now() - notificationObj.timeShown; + if ( + !notificationObj.wasDismissed && + !notificationObj.recordedTelemetryMainAction + ) { + notificationObj._recordTelemetry( + "POPUP_NOTIFICATION_DISMISSAL_MS", + timeSinceShown + ); + } + + // Do not mark the notification as dismissed or fire NOTIFICATION_EVENT_DISMISSED + // if the notification is removed. + if (notificationObj.options.removeOnDismissal) { + notificationObj._recordTelemetryStat(this.nextRemovalReason); + this._remove(notificationObj); + } else { + notificationObj.dismissed = true; + this._fireCallback(notificationObj, NOTIFICATION_EVENT_DISMISSED); + } + } + }, + + _onCheckboxCommand(event) { + let notificationEl = getNotificationFromElement(event.originalTarget); + let checked = notificationEl.checkbox.checked; + let notification = notificationEl.notification; + + // Save checkbox state to be able to persist it when re-opening the doorhanger. + notification._checkboxChecked = checked; + + if (checked) { + this._setNotificationUIState( + notificationEl, + notification.options.checkbox.checkedState + ); + } else { + this._setNotificationUIState( + notificationEl, + notification.options.checkbox.uncheckedState + ); + } + event.stopPropagation(); + }, + + _onCommand(event) { + // Ignore events from buttons as they are submitting and so don't need checks + if (event.originalTarget.localName == "button") { + return; + } + let notificationEl = getNotificationFromElement(event.target); + + let notification = notificationEl.notification; + if (!notification.options.checkbox) { + this._setNotificationUIState(notificationEl); + return; + } + + if (notificationEl.checkbox.checked) { + this._setNotificationUIState( + notificationEl, + notification.options.checkbox.checkedState + ); + } else { + this._setNotificationUIState( + notificationEl, + notification.options.checkbox.uncheckedState + ); + } + }, + + _onButtonEvent(event, type, source = "button", notificationEl = null) { + if (!notificationEl) { + notificationEl = getNotificationFromElement(event.originalTarget); + } + + if (!notificationEl) { + throw new Error( + "PopupNotifications._onButtonEvent: couldn't find notification element" + ); + } + + if (!notificationEl.notification) { + throw new Error( + "PopupNotifications._onButtonEvent: couldn't find notification" + ); + } + + let notification = notificationEl.notification; + + // Receiving a button event means the notification should have been shown. + // Make sure that timeShown is always set to ensure we don't break the + // security delay calculation below. + if (!notification.timeShown) { + console.warn( + "_onButtonEvent: notification.timeShown is unset. Setting to now.", + notification + ); + notification.timeShown = this.window.performance.now(); + } + + if (type == "dropmarkerpopupshown") { + notification._recordTelemetryStat(TELEMETRY_STAT_OPEN_SUBMENU); + return; + } + + if (type == "learnmoreclick") { + notification._recordTelemetryStat(TELEMETRY_STAT_LEARN_MORE); + return; + } + + if (type == "buttoncommand") { + // Record the total timing of the main action since the notification was + // created, even if the notification was dismissed in the meantime. + let timeSinceCreated = + this.window.performance.now() - notification.timeCreated; + if (!notification.recordedTelemetryMainAction) { + notification.recordedTelemetryMainAction = true; + notification._recordTelemetry( + "POPUP_NOTIFICATION_MAIN_ACTION_MS", + timeSinceCreated + ); + } + } + + if (type == "buttoncommand" || type == "secondarybuttoncommand") { + if (Services.focus.activeWindow != this.window) { + Services.console.logStringMessage( + "PopupNotifications._onButtonEvent: " + + "Button click happened before the window was focused" + ); + this.window.focus(); + return; + } + + let now = this.window.performance.now(); + let timeSinceShown = now - notification.timeShown; + if (timeSinceShown < lazy.buttonDelay) { + Services.console.logStringMessage( + "PopupNotifications._onButtonEvent: " + + "Button click happened before the security delay: " + + timeSinceShown + + "ms" + ); + notification.timeShown = Math.max(now, notification.timeShown); + return; + } + } + + let action = notification.mainAction; + let telemetryStatId = TELEMETRY_STAT_ACTION_1; + + if (type == "secondarybuttoncommand") { + action = notification.secondaryActions[0]; + telemetryStatId = TELEMETRY_STAT_ACTION_2; + } + + notification._recordTelemetryStat(telemetryStatId); + + if (action) { + try { + action.callback.call(undefined, { + checkboxChecked: notificationEl.checkbox.checked, + source, + event, + }); + } catch (error) { + console.error(error); + } + + if (action.dismiss) { + this._dismiss(); + return; + } + } + + this._remove(notification); + this._update(); + }, + + _onMenuCommand: function PopupNotifications_onMenuCommand(event) { + let target = event.originalTarget; + if (!target.action || !target.notification) { + throw new Error( + "menucommand target has no associated action/notification" + ); + } + + let notificationEl = getNotificationFromElement(target); + event.stopPropagation(); + + target.notification._recordTelemetryStat(target.action.telemetryStatId); + + try { + target.action.callback.call(undefined, { + checkboxChecked: notificationEl.checkbox.checked, + source: "menucommand", + }); + } catch (error) { + console.error(error); + } + + if (target.action.dismiss) { + this._dismiss(); + return; + } + + this._remove(target.notification); + this._update(); + }, + + _notify: function PopupNotifications_notify(topic) { + Services.obs.notifyObservers(null, "PopupNotifications-" + topic); + }, +}; diff --git a/toolkit/modules/Preferences.sys.mjs b/toolkit/modules/Preferences.sys.mjs new file mode 100644 index 0000000000..619087aa67 --- /dev/null +++ b/toolkit/modules/Preferences.sys.mjs @@ -0,0 +1,451 @@ +/* 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/. */ + +// The minimum and maximum integers that can be set as preferences. +// The range of valid values is narrower than the range of valid JS values +// because the native preferences code treats integers as NSPR PRInt32s, +// which are 32-bit signed integers on all platforms. +const MAX_INT = 0x7fffffff; // Math.pow(2, 31) - 1 +const MIN_INT = -0x80000000; + +export function Preferences(args) { + this._cachedPrefBranch = null; + if (isObject(args)) { + if (args.branch) { + this._branchStr = args.branch; + } + if (args.defaultBranch) { + this._defaultBranch = args.defaultBranch; + } + if (args.privacyContext) { + this._privacyContext = args.privacyContext; + } + } else if (args) { + this._branchStr = args; + } +} + +/** + * Get the value of a pref, if any; otherwise return the default value. + * + * @param prefName {String|Array} + * the pref to get, or an array of prefs to get + * + * @param defaultValue + * the default value, if any, for prefs that don't have one + * + * @param valueType + * the XPCOM interface of the pref's complex value type, if any + * + * @returns the value of the pref, if any; otherwise the default value + */ +Preferences.get = function (prefName, defaultValue, valueType = null) { + if (Array.isArray(prefName)) { + return prefName.map(v => this.get(v, defaultValue)); + } + + return this._get(prefName, defaultValue, valueType); +}; + +Preferences._get = function (prefName, defaultValue, valueType) { + switch (this._prefBranch.getPrefType(prefName)) { + case Ci.nsIPrefBranch.PREF_STRING: + if (valueType) { + let ifaces = ["nsIFile", "nsIPrefLocalizedString"]; + if (ifaces.includes(valueType.name)) { + return this._prefBranch.getComplexValue(prefName, valueType).data; + } + } + return this._prefBranch.getStringPref(prefName); + + case Ci.nsIPrefBranch.PREF_INT: + return this._prefBranch.getIntPref(prefName); + + case Ci.nsIPrefBranch.PREF_BOOL: + return this._prefBranch.getBoolPref(prefName); + + case Ci.nsIPrefBranch.PREF_INVALID: + return defaultValue; + + default: + // This should never happen. + throw new Error( + `Error getting pref ${prefName}; its value's type is ` + + `${this._prefBranch.getPrefType(prefName)}, which I don't ` + + `know how to handle.` + ); + } +}; + +/** + * Set a preference to a value. + * + * You can set multiple prefs by passing an object as the only parameter. + * In that case, this method will treat the properties of the object + * as preferences to set, where each property name is the name of a pref + * and its corresponding property value is the value of the pref. + * + * @param prefName {String|Object} + * the name of the pref to set; or an object containing a set + * of prefs to set + * + * @param prefValue {String|Number|Boolean} + * the value to which to set the pref + * + * Note: Preferences cannot store non-integer numbers or numbers outside + * the signed 32-bit range -(2^31-1) to 2^31-1, If you have such a number, + * store it as a string by calling toString() on the number before passing + * it to this method, i.e.: + * Preferences.set("pi", 3.14159.toString()) + * Preferences.set("big", Math.pow(2, 31).toString()). + */ +Preferences.set = function (prefName, prefValue) { + if (isObject(prefName)) { + for (let [name, value] of Object.entries(prefName)) { + this.set(name, value); + } + return; + } + + this._set(prefName, prefValue); +}; + +Preferences._set = function (prefName, prefValue) { + let prefType; + if (typeof prefValue != "undefined" && prefValue != null) { + prefType = prefValue.constructor.name; + } + + switch (prefType) { + case "String": + this._prefBranch.setStringPref(prefName, prefValue); + break; + + case "Number": + // We throw if the number is outside the range, since the result + // will never be what the consumer wanted to store, but we only warn + // if the number is non-integer, since the consumer might not mind + // the loss of precision. + if (prefValue > MAX_INT || prefValue < MIN_INT) { + throw new Error( + `you cannot set the ${prefName} pref to the number ` + + `${prefValue}, as number pref values must be in the signed ` + + `32-bit integer range -(2^31-1) to 2^31-1. To store numbers ` + + `outside that range, store them as strings.` + ); + } + this._prefBranch.setIntPref(prefName, prefValue); + if (prefValue % 1 != 0) { + console.error( + "Warning: setting the ", + prefName, + " pref to the non-integer number ", + prefValue, + " converted it " + + "to the integer number " + + this.get(prefName) + + "; to retain fractional precision, store non-integer " + + "numbers as strings." + ); + } + break; + + case "Boolean": + this._prefBranch.setBoolPref(prefName, prefValue); + break; + + default: + throw new Error( + `can't set pref ${prefName} to value '${prefValue}'; ` + + `it isn't a String, Number, or Boolean` + ); + } +}; + +/** + * Whether or not the given pref has a value. This is different from isSet + * because it returns true whether the value of the pref is a default value + * or a user-set value, while isSet only returns true if the value + * is a user-set value. + * + * @param prefName {String|Array} + * the pref to check, or an array of prefs to check + * + * @returns {Boolean|Array} + * whether or not the pref has a value; or, if the caller provided + * an array of pref names, an array of booleans indicating whether + * or not the prefs have values + */ +Preferences.has = function (prefName) { + if (Array.isArray(prefName)) { + return prefName.map(this.has, this); + } + + return ( + this._prefBranch.getPrefType(prefName) != Ci.nsIPrefBranch.PREF_INVALID + ); +}; + +/** + * Whether or not the given pref has a user-set value. This is different + * from |has| because it returns true only if the value of the pref is a user- + * set value, while |has| returns true if the value of the pref is a default + * value or a user-set value. + * + * @param prefName {String|Array} + * the pref to check, or an array of prefs to check + * + * @returns {Boolean|Array} + * whether or not the pref has a user-set value; or, if the caller + * provided an array of pref names, an array of booleans indicating + * whether or not the prefs have user-set values + */ +Preferences.isSet = function (prefName) { + if (Array.isArray(prefName)) { + return prefName.map(this.isSet, this); + } + + return this.has(prefName) && this._prefBranch.prefHasUserValue(prefName); +}; + +/** + * Whether or not the given pref has a user-set value. Use isSet instead, + * which is equivalent. + * @deprecated + */ +Preferences.modified = function (prefName) { + return this.isSet(prefName); +}; + +Preferences.reset = function (prefName) { + if (Array.isArray(prefName)) { + prefName.map(v => this.reset(v)); + return; + } + + this._prefBranch.clearUserPref(prefName); +}; + +/** + * Lock a pref so it can't be changed. + * + * @param prefName {String|Array} + * the pref to lock, or an array of prefs to lock + */ +Preferences.lock = function (prefName) { + if (Array.isArray(prefName)) { + prefName.map(this.lock, this); + } + + this._prefBranch.lockPref(prefName); +}; + +/** + * Unlock a pref so it can be changed. + * + * @param prefName {String|Array} + * the pref to lock, or an array of prefs to lock + */ +Preferences.unlock = function (prefName) { + if (Array.isArray(prefName)) { + prefName.map(this.unlock, this); + } + + this._prefBranch.unlockPref(prefName); +}; + +/** + * Whether or not the given pref is locked against changes. + * + * @param prefName {String|Array} + * the pref to check, or an array of prefs to check + * + * @returns {Boolean|Array} + * whether or not the pref has a user-set value; or, if the caller + * provided an array of pref names, an array of booleans indicating + * whether or not the prefs have user-set values + */ +Preferences.locked = function (prefName) { + if (Array.isArray(prefName)) { + return prefName.map(this.locked, this); + } + + return this._prefBranch.prefIsLocked(prefName); +}; + +/** + * Start observing a pref. + * + * The callback can be a function or any object that implements nsIObserver. + * When the callback is a function and thisObject is provided, it gets called + * as a method of thisObject. + * + * @param prefName {String} + * the name of the pref to observe + * + * @param callback {Function|Object} + * the code to notify when the pref changes; + * + * @param thisObject {Object} [optional] + * the object to use as |this| when calling a Function callback; + * + * @returns the wrapped observer + */ +Preferences.observe = function (prefName, callback, thisObject) { + let fullPrefName = this._branchStr + (prefName || ""); + + let observer = new PrefObserver(fullPrefName, callback, thisObject); + Preferences._prefBranch.addObserver(fullPrefName, observer, true); + observers.push(observer); + + return observer; +}; + +/** + * Stop observing a pref. + * + * You must call this method with the same prefName, callback, and thisObject + * with which you originally registered the observer. However, you don't have + * to call this method on the same exact instance of Preferences; you can call + * it on any instance. For example, the following code first starts and then + * stops observing the "foo.bar.baz" preference: + * + * let observer = function() {...}; + * Preferences.observe("foo.bar.baz", observer); + * new Preferences("foo.bar.").ignore("baz", observer); + * + * @param prefName {String} + * the name of the pref being observed + * + * @param callback {Function|Object} + * the code being notified when the pref changes + * + * @param thisObject {Object} [optional] + * the object being used as |this| when calling a Function callback + */ +Preferences.ignore = function (prefName, callback, thisObject) { + let fullPrefName = this._branchStr + (prefName || ""); + + // This seems fairly inefficient, but I'm not sure how much better we can + // make it. We could index by fullBranch, but we can't index by callback + // or thisObject, as far as I know, since the keys to JavaScript hashes + // (a.k.a. objects) can apparently only be primitive values. + let [observer] = observers.filter( + v => + v.prefName == fullPrefName && + v.callback == callback && + v.thisObject == thisObject + ); + + if (observer) { + Preferences._prefBranch.removeObserver(fullPrefName, observer); + observers.splice(observers.indexOf(observer), 1); + } else { + console.error( + `Attempt to stop observing a preference "${prefName}" that's not being observed` + ); + } +}; + +Preferences.resetBranch = function (prefBranch = "") { + this.reset(this._prefBranch.getChildList(prefBranch)); +}; + +/** + * A string identifying the branch of the preferences tree to which this + * instance provides access. + * @private + */ +Preferences._branchStr = ""; + +/** + * The cached preferences branch object this instance encapsulates, or null. + * Do not use! Use _prefBranch below instead. + * @private + */ +Preferences._cachedPrefBranch = null; + +/** + * The preferences branch object for this instance. + * @private + */ +Object.defineProperty(Preferences, "_prefBranch", { + get: function _prefBranch() { + if (!this._cachedPrefBranch) { + let prefSvc = Services.prefs; + this._cachedPrefBranch = this._defaultBranch + ? prefSvc.getDefaultBranch(this._branchStr) + : prefSvc.getBranch(this._branchStr); + } + return this._cachedPrefBranch; + }, + enumerable: true, + configurable: true, +}); + +// Constructor-based access (Preferences.get(...) and set) is preferred over +// instance-based access (new Preferences().get(...) and set) and when using the +// root preferences branch, as it's desirable not to allocate the extra object. +// But both forms are acceptable. +Preferences.prototype = Preferences; + +/** + * A cache of pref observers. + * + * We use this to remove observers when a caller calls Preferences::ignore. + * + * All Preferences instances share this object, because we want callers to be + * able to remove an observer using a different Preferences object than the one + * with which they added it. That means we have to identify the observers + * in this object by their complete pref name, not just their name relative to + * the root branch of the Preferences object with which they were created. + */ +var observers = []; + +function PrefObserver(prefName, callback, thisObject) { + this.prefName = prefName; + this.callback = callback; + this.thisObject = thisObject; +} + +PrefObserver.prototype = { + QueryInterface: ChromeUtils.generateQI([ + "nsIObserver", + "nsISupportsWeakReference", + ]), + + observe(subject, topic, data) { + // The pref service only observes whole branches, but we only observe + // individual preferences, so we check here that the pref that changed + // is the exact one we're observing (and not some sub-pref on the branch). + if (data != this.prefName) { + return; + } + + if (typeof this.callback == "function") { + let prefValue = Preferences.get(this.prefName); + + if (this.thisObject) { + this.callback.call(this.thisObject, prefValue); + } else { + this.callback(prefValue); + } + } else { + // typeof this.callback == "object" (nsIObserver) + this.callback.observe(subject, topic, data); + } + }, +}; + +function isObject(val) { + // We can't check for |val.constructor == Object| here, since the value + // might be from a different context whose Object constructor is not the same + // as ours, so instead we match based on the name of the constructor. + return ( + typeof val != "undefined" && + val != null && + typeof val == "object" && + val.constructor.name == "Object" + ); +} diff --git a/toolkit/modules/PrivateBrowsingUtils.sys.mjs b/toolkit/modules/PrivateBrowsingUtils.sys.mjs new file mode 100644 index 0000000000..487a43bfbf --- /dev/null +++ b/toolkit/modules/PrivateBrowsingUtils.sys.mjs @@ -0,0 +1,71 @@ +/* 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/. */ + +const kAutoStartPref = "browser.privatebrowsing.autostart"; + +// This will be set to true when the PB mode is autostarted from the command +// line for the current session. +var gTemporaryAutoStartMode = false; + +export var PrivateBrowsingUtils = { + get enabled() { + return Services.policies.isAllowed("privatebrowsing"); + }, + + // Rather than passing content windows to this function, please use + // isBrowserPrivate since it works with e10s. + isWindowPrivate: function pbu_isWindowPrivate(aWindow) { + if (!aWindow.isChromeWindow) { + dump( + "WARNING: content window passed to PrivateBrowsingUtils.isWindowPrivate. " + + "Use isContentWindowPrivate instead (but only for frame scripts).\n" + + new Error().stack + ); + } + + return this.privacyContextFromWindow(aWindow).usePrivateBrowsing; + }, + + // This should be used only in frame scripts. + isContentWindowPrivate: function pbu_isWindowPrivate(aWindow) { + return this.privacyContextFromWindow(aWindow).usePrivateBrowsing; + }, + + isBrowserPrivate(aBrowser) { + let chromeWin = aBrowser.ownerGlobal; + if (chromeWin.gMultiProcessBrowser || !aBrowser.contentWindow) { + // In e10s we have to look at the chrome window's private + // browsing status since the only alternative is to check the + // content window, which is in another process. If the browser + // is lazy or is running in windowless configuration then the + // content window doesn't exist. + return this.isWindowPrivate(chromeWin); + } + return this.privacyContextFromWindow(aBrowser.contentWindow) + .usePrivateBrowsing; + }, + + privacyContextFromWindow: function pbu_privacyContextFromWindow(aWindow) { + return aWindow.docShell.QueryInterface(Ci.nsILoadContext); + }, + + get permanentPrivateBrowsing() { + try { + return ( + gTemporaryAutoStartMode || Services.prefs.getBoolPref(kAutoStartPref) + ); + } catch (e) { + // The pref does not exist + return false; + } + }, + + // These should only be used from internal code + enterTemporaryAutoStartMode: function pbu_enterTemporaryAutoStartMode() { + gTemporaryAutoStartMode = true; + }, + get isInTemporaryAutoStartMode() { + return gTemporaryAutoStartMode; + }, +}; diff --git a/toolkit/modules/ProcessType.sys.mjs b/toolkit/modules/ProcessType.sys.mjs new file mode 100644 index 0000000000..4c30068755 --- /dev/null +++ b/toolkit/modules/ProcessType.sys.mjs @@ -0,0 +1,34 @@ +/* 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 const ProcessType = Object.freeze({ + /** + * Converts a key string to a fluent ID defined in processTypes.ftl. + */ + kProcessTypeMap: { + // Keys defined in xpcom/build/GeckoProcessTypes.h + default: "process-type-default", + gpu: "process-type-gpu", + tab: "process-type-tab", + rdd: "process-type-rdd", + socket: "process-type-socket", + utility: "process-type-utility", + + // Keys defined in dom/ipc/RemoteType.h + extension: "process-type-extension", + file: "process-type-file", + prealloc: "process-type-prealloc", + privilegedabout: "process-type-privilegedabout", + privilegedmozilla: "process-type-privilegedmozilla", + web: "process-type-web", + webIsolated: "process-type-webisolated", + webServiceWorker: "process-type-webserviceworker", + }, + + kFallback: "process-type-unknown", + + fluentNameFromProcessTypeString(type) { + return this.kProcessTypeMap[type] || this.kFallback; + }, +}); diff --git a/toolkit/modules/ProfileAge.sys.mjs b/toolkit/modules/ProfileAge.sys.mjs new file mode 100644 index 0000000000..ea824f5a91 --- /dev/null +++ b/toolkit/modules/ProfileAge.sys.mjs @@ -0,0 +1,202 @@ +/* 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 { Log } from "resource://gre/modules/Log.sys.mjs"; + +const FILE_TIMES = "times.json"; + +/** + * Traverse the contents of the profile directory, finding the oldest file + * and returning its creation timestamp. + */ +async function getOldestProfileTimestamp(profilePath, log) { + let start = Date.now(); + let oldest = start + 1000; + log.debug("Iterating over profile " + profilePath); + + try { + for (const childPath of await IOUtils.getChildren(profilePath)) { + try { + let info = await IOUtils.stat(childPath); + let timestamp; + if (info.creationTime !== undefined) { + timestamp = info.creationTime; + } else { + // We only support file creation times on Mac and Windows. We have to + // settle for mtime on Linux. + log.debug("No birth date. Using mtime."); + timestamp = info.lastModified; + } + + log.debug(`Using date: ${childPath} = ${timestamp}`); + if (timestamp < oldest) { + oldest = timestamp; + } + } catch (e) { + // Never mind. + log.debug("Stat failure", e); + } + } + } catch (reason) { + throw new Error("Unable to fetch oldest profile entry: " + reason); + } + + return oldest; +} + +/** + * Profile access to times.json (eg, creation/reset time). + * This is separate from the provider to simplify testing and enable extraction + * to a shared location in the future. + */ +class ProfileAgeImpl { + constructor(profile, times) { + this._profilePath = profile; + this._times = times; + this._log = Log.repository.getLogger("Toolkit.ProfileAge"); + + if ("firstUse" in this._times && this._times.firstUse === null) { + // Indicates that this is a new profile that needs a first use timestamp. + this._times.firstUse = Date.now(); + this.writeTimes(); + } + } + + get profilePath() { + if (!this._profilePath) { + this._profilePath = Services.dirsvc.get("ProfD", Ci.nsIFile).path; + } + + return this._profilePath; + } + + /** + * There are two ways we can get our creation time: + * + * 1. From the on-disk JSON file. + * 2. By calculating it from the filesystem. + * + * If we have to calculate, we write out the file; if we have + * to touch the file, we persist in-memory. + * + * @return a promise that resolves to the profile's creation time. + */ + get created() { + // This can be an expensive operation so make sure we only do it once. + if (this._created) { + return this._created; + } + + if (!this._times.created) { + this._created = this.computeAndPersistCreated(); + } else { + this._created = Promise.resolve(this._times.created); + } + + return this._created; + } + + /** + * Returns a promise to the time of first use of the profile. This may be + * undefined if the first use time is unknown. + */ + get firstUse() { + if ("firstUse" in this._times) { + return Promise.resolve(this._times.firstUse); + } + return Promise.resolve(undefined); + } + + /** + * Return a promise representing the writing the current times to the profile. + */ + async writeTimes() { + try { + await IOUtils.writeJSON( + PathUtils.join(this.profilePath, FILE_TIMES), + this._times + ); + } catch (e) { + if ( + !DOMException.isInstance(e) || + e.name !== "AbortError" || + e.message !== "IOUtils: Shutting down and refusing additional I/O tasks" + ) { + throw e; + } + } + } + + /** + * Calculates the created time by scanning the profile directory, sets it in + * the current set of times and persists it to the profile. Returns a promise + * that resolves when all of that is complete. + */ + async computeAndPersistCreated() { + let oldest = await getOldestProfileTimestamp(this.profilePath, this._log); + this._times.created = oldest; + await this.writeTimes(); + return oldest; + } + + /** + * Record (and persist) when a profile reset happened. We just store a + * single value - the timestamp of the most recent reset - but there is scope + * to keep a list of reset times should our health-reporter successor + * be able to make use of that. + * Returns a promise that is resolved once the file has been written. + */ + recordProfileReset(time = Date.now()) { + this._times.reset = time; + return this.writeTimes(); + } + + /* Returns a promise that resolves to the time the profile was reset, + * or undefined if not recorded. + */ + get reset() { + if ("reset" in this._times) { + return Promise.resolve(this._times.reset); + } + return Promise.resolve(undefined); + } +} + +// A Map from profile directory to a promise that resolves to the ProfileAgeImpl. +const PROFILES = new Map(); + +async function initProfileAge(profile) { + let timesPath = PathUtils.join(profile, FILE_TIMES); + + try { + let times = await IOUtils.readJSON(timesPath); + return new ProfileAgeImpl(profile, times || {}); + } catch (e) { + // Indicates that the file was missing or broken. In this case we want to + // record the first use time as now. The constructor will set this and write + // times.json + return new ProfileAgeImpl(profile, { firstUse: null }); + } +} + +/** + * Returns a promise that resolves to an instance of ProfileAgeImpl. Will always + * return the same instance for every call for the same profile. + * + * @param {string} profile The path to the profile directory. + * @return {Promise<ProfileAgeImpl>} Resolves to the ProfileAgeImpl. + */ +export function ProfileAge(profile) { + if (!profile) { + profile = PathUtils.profileDir; + } + + if (PROFILES.has(profile)) { + return PROFILES.get(profile); + } + + let promise = initProfileAge(profile); + PROFILES.set(profile, promise); + return promise; +} diff --git a/toolkit/modules/PropertyListUtils.sys.mjs b/toolkit/modules/PropertyListUtils.sys.mjs new file mode 100644 index 0000000000..1acfe980f7 --- /dev/null +++ b/toolkit/modules/PropertyListUtils.sys.mjs @@ -0,0 +1,869 @@ +/* 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/. */ + +/** + * Module for reading Property Lists (.plist) files + * ------------------------------------------------ + * This module functions as a reader for Apple Property Lists (.plist files). + * It supports both binary and xml formatted property lists. It does not + * support the legacy ASCII format. Reading of Cocoa's Keyed Archives serialized + * to binary property lists isn't supported either. + * + * Property Lists objects are represented by standard JS and Mozilla types, + * namely: + * + * XML type Cocoa Class Returned type(s) + * -------------------------------------------------------------------------- + * <true/> / <false/> NSNumber TYPE_PRIMITIVE boolean + * <integer> / <real> NSNumber TYPE_PRIMITIVE number + * TYPE_INT64 String [1] + * Not Available NSNull TYPE_PRIMITIVE null [2] + * TYPE_PRIMITIVE undefined [3] + * <date/> NSDate TYPE_DATE Date + * <data/> NSData TYPE_UINT8_ARRAY Uint8Array + * <array/> NSArray TYPE_ARRAY Array + * Not Available NSSet TYPE_ARRAY Array [2][4] + * <dict/> NSDictionary TYPE_DICTIONARY Map + * + * Use PropertyListUtils.getObjectType to detect the type of a Property list + * object. + * + * ------------- + * 1) Property lists supports storing U/Int64 numbers, while JS can only handle + * numbers that are in this limits of float-64 (±2^53). For numbers that + * do not outbound this limits, simple primitive number are always used. + * Otherwise, a String object. + * 2) About NSNull and NSSet values: While the xml format has no support for + * representing null and set values, the documentation for the binary format + * states that it supports storing both types. However, the Cocoa APIs for + * serializing property lists do not seem to support either types (test with + * NSPropertyListSerialization::propertyList:isValidForFormat). Furthermore, + * if an array or a dictionary (Map) contains a NSNull or a NSSet value, they cannot + * be serialized to a property list. + * As for usage within OS X, not surprisingly there's no known usage of + * storing either of these types in a property list. It seems that, for now, + * Apple is keeping the features of binary and xml formats in sync, probably as + * long as the XML format is not officially deprecated. + * 3) Not used anywhere. + * 4) About NSSet representation: For the time being, we represent those + * theoretical NSSet objects the same way NSArray is represented. + * While this would most certainly work, it is not the right way to handle + * it. A more correct representation for a set is a js generator, which would + * read the set lazily and has no indices semantics. + */ + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + ctypes: "resource://gre/modules/ctypes.sys.mjs", +}); + +export var PropertyListUtils = Object.freeze({ + /** + * Asynchronously reads a file as a property list. + * + * @param aFile (Blob/nsIFile) + * the file to be read as a property list. + * @param aCallback + * If the property list is read successfully, aPropertyListRoot is set + * to the root object of the property list. + * Use getPropertyListObjectType to detect its type. + * If it's not read successfully, aPropertyListRoot is set to null. + * The reaon for failure is reported to the Error Console. + */ + read: function PLU_read(aFile, aCallback) { + if (!(aFile instanceof Ci.nsIFile || File.isInstance(aFile))) { + throw new Error("aFile is not a file object"); + } + if (typeof aCallback != "function") { + throw new Error("Invalid value for aCallback"); + } + + // We guarantee not to throw directly for any other exceptions, and always + // call aCallback. + Services.tm.dispatchToMainThread(() => { + let self = this; + function readDOMFile(aFile) { + let fileReader = new FileReader(); + let onLoadEnd = function () { + let root = null; + try { + fileReader.removeEventListener("loadend", onLoadEnd); + if (fileReader.readyState != fileReader.DONE) { + throw new Error( + "Could not read file contents: " + fileReader.error + ); + } + + root = self._readFromArrayBufferSync(fileReader.result); + } finally { + aCallback(root); + } + }; + fileReader.addEventListener("loadend", onLoadEnd); + fileReader.readAsArrayBuffer(aFile); + } + + try { + if (aFile instanceof Ci.nsIFile) { + if (!aFile.exists()) { + throw new Error("The file pointed by aFile does not exist"); + } + + File.createFromNsIFile(aFile).then(function (aFile) { + readDOMFile(aFile); + }); + return; + } + readDOMFile(aFile); + } catch (ex) { + aCallback(null); + throw ex; + } + }); + }, + + /** + * DO NOT USE ME. Once Bug 718189 is fixed, this method won't be public. + * + * Synchronously read an ArrayBuffer contents as a property list. + */ + _readFromArrayBufferSync: function PLU__readFromArrayBufferSync(aBuffer) { + if (BinaryPropertyListReader.prototype.canProcess(aBuffer)) { + return new BinaryPropertyListReader(aBuffer).root; + } + + // Convert the buffer into an XML tree. + let domParser = new DOMParser(); + let bytesView = new Uint8Array(aBuffer); + try { + let doc = domParser.parseFromBuffer(bytesView, "application/xml"); + return new XMLPropertyListReader(doc).root; + } catch (ex) { + throw new Error("aBuffer cannot be parsed as a DOM document: " + ex); + } + }, + + TYPE_PRIMITIVE: 0, + TYPE_DATE: 1, + TYPE_UINT8_ARRAY: 2, + TYPE_ARRAY: 3, + TYPE_DICTIONARY: 4, + TYPE_INT64: 5, + + /** + * Get the type in which the given property list object is represented. + * Check the header for the mapping between the TYPE* constants to js types + * and objects. + * + * @return one of the TYPE_* constants listed above. + * @note this method is merely for convenience. It has no magic to detect + * that aObject is indeed a property list object created by this module. + */ + getObjectType: function PLU_getObjectType(aObject) { + if (aObject === null || typeof aObject != "object") { + return this.TYPE_PRIMITIVE; + } + + // Given current usage, we could assume that aObject was created in the + // scope of this module, but in future, this util may be used as part of + // serializing js objects to a property list - in which case the object + // would most likely be created in the caller's scope. + let global = Cu.getGlobalForObject(aObject); + + if (aObject instanceof global.Map) { + return this.TYPE_DICTIONARY; + } + if (Array.isArray(aObject)) { + return this.TYPE_ARRAY; + } + if (aObject instanceof global.Date) { + return this.TYPE_DATE; + } + if (aObject instanceof global.Uint8Array) { + return this.TYPE_UINT8_ARRAY; + } + if (aObject instanceof global.String && "__INT_64_WRAPPER__" in aObject) { + return this.TYPE_INT64; + } + + throw new Error("aObject is not as a property list object."); + }, + + /** + * Wraps a 64-bit stored in the form of a string primitive as a String object, + * which we can later distiguish from regular string values. + * @param aPrimitive + * a number in the form of either a primitive string or a primitive number. + * @return a String wrapper around aNumberStr that can later be identified + * as holding 64-bit number using getObjectType. + */ + wrapInt64: function PLU_wrapInt64(aPrimitive) { + if (typeof aPrimitive != "string" && typeof aPrimitive != "number") { + throw new Error("aPrimitive should be a string primitive"); + } + + // The function converts string or number to object + // So eslint rule is disabled + // eslint-disable-next-line no-new-wrappers + let wrapped = new String(aPrimitive); + Object.defineProperty(wrapped, "__INT_64_WRAPPER__", { value: true }); + return wrapped; + }, +}); + +/** + * Here's the base structure of binary-format property lists. + * 1) Header - magic number + * - 6 bytes - "bplist" + * - 2 bytes - version number. This implementation only supports version 00. + * 2) Objects Table + * Variable-sized objects, see _readObject for how various types of objects + * are constructed. + * 3) Offsets Table + * The offset of each object in the objects table. The integer size is + * specified in the trailer. + * 4) Trailer + * - 6 unused bytes + * - 1 byte: the size of integers in the offsets table + * - 1 byte: the size of object references for arrays, sets and + * dictionaries. + * - 8 bytes: the number of objects in the objects table + * - 8 bytes: the index of the root object's offset in the offsets table. + * - 8 bytes: the offset of the offsets table. + * + * Note: all integers are stored in big-endian form. + */ + +/** + * Reader for binary-format property lists. + * + * @param aBuffer + * ArrayBuffer object from which the binary plist should be read. + */ +function BinaryPropertyListReader(aBuffer) { + this._dataView = new DataView(aBuffer); + + const JS_MAX_INT = Math.pow(2, 53); + this._JS_MAX_INT_SIGNED = lazy.ctypes.Int64(JS_MAX_INT); + this._JS_MAX_INT_UNSIGNED = lazy.ctypes.UInt64(JS_MAX_INT); + this._JS_MIN_INT = lazy.ctypes.Int64(-JS_MAX_INT); + + try { + this._readTrailerInfo(); + this._readObjectsOffsets(); + } catch (ex) { + throw new Error("Could not read aBuffer as a binary property list"); + } + this._objects = []; +} + +BinaryPropertyListReader.prototype = { + /** + * Checks if the given ArrayBuffer can be read as a binary property list. + * It can be called on the prototype. + */ + canProcess: function BPLR_canProcess(aBuffer) { + return ( + Array.from(new Uint8Array(aBuffer, 0, 8)) + .map(c => String.fromCharCode(c)) + .join("") == "bplist00" + ); + }, + + get root() { + return this._readObject(this._rootObjectIndex); + }, + + _readTrailerInfo: function BPLR__readTrailer() { + // The first 6 bytes of the 32-bytes trailer are unused + let trailerOffset = this._dataView.byteLength - 26; + [this._offsetTableIntegerSize, this._objectRefSize] = + this._readUnsignedInts(trailerOffset, 1, 2); + + [this._numberOfObjects, this._rootObjectIndex, this._offsetTableOffset] = + this._readUnsignedInts(trailerOffset + 2, 8, 3); + }, + + _readObjectsOffsets: function BPLR__readObjectsOffsets() { + this._offsetTable = this._readUnsignedInts( + this._offsetTableOffset, + this._offsetTableIntegerSize, + this._numberOfObjects + ); + }, + + _readSignedInt64: function BPLR__readSignedInt64(aByteOffset) { + let lo = this._dataView.getUint32(aByteOffset + 4); + let hi = this._dataView.getInt32(aByteOffset); + let int64 = lazy.ctypes.Int64.join(hi, lo); + if ( + lazy.ctypes.Int64.compare(int64, this._JS_MAX_INT_SIGNED) == 1 || + lazy.ctypes.Int64.compare(int64, this._JS_MIN_INT) == -1 + ) { + return PropertyListUtils.wrapInt64(int64.toString()); + } + + return parseInt(int64.toString(), 10); + }, + + _readReal: function BPLR__readReal(aByteOffset, aRealSize) { + if (aRealSize == 4) { + return this._dataView.getFloat32(aByteOffset); + } + if (aRealSize == 8) { + return this._dataView.getFloat64(aByteOffset); + } + + throw new Error("Unsupported real size: " + aRealSize); + }, + + OBJECT_TYPE_BITS: { + SIMPLE: parseInt("0000", 2), + INTEGER: parseInt("0001", 2), + REAL: parseInt("0010", 2), + DATE: parseInt("0011", 2), + DATA: parseInt("0100", 2), + ASCII_STRING: parseInt("0101", 2), + UNICODE_STRING: parseInt("0110", 2), + UID: parseInt("1000", 2), + ARRAY: parseInt("1010", 2), + SET: parseInt("1100", 2), + DICTIONARY: parseInt("1101", 2), + }, + + ADDITIONAL_INFO_BITS: { + // Applies to OBJECT_TYPE_BITS.SIMPLE + NULL: parseInt("0000", 2), + FALSE: parseInt("1000", 2), + TRUE: parseInt("1001", 2), + FILL_BYTE: parseInt("1111", 2), + // Applies to OBJECT_TYPE_BITS.DATE + DATE: parseInt("0011", 2), + // Applies to OBJECT_TYPE_BITS.DATA, ASCII_STRING, UNICODE_STRING, ARRAY, + // SET and DICTIONARY. + LENGTH_INT_SIZE_FOLLOWS: parseInt("1111", 2), + }, + + /** + * Returns an object descriptor in the form of two integers: object type and + * additional info. + * + * @param aByteOffset + * the descriptor's offset. + * @return [objType, additionalInfo] - the object type and additional info. + * @see OBJECT_TYPE_BITS and ADDITIONAL_INFO_BITS + */ + _readObjectDescriptor: function BPLR__readObjectDescriptor(aByteOffset) { + // The first four bits hold the object type. For some types, additional + // info is held in the other 4 bits. + let byte = this._readUnsignedInts(aByteOffset, 1, 1)[0]; + return [(byte & 0xf0) >> 4, byte & 0x0f]; + }, + + _readDate: function BPLR__readDate(aByteOffset) { + // That's the reference date of NSDate. + let date = new Date("1 January 2001, GMT"); + + // NSDate values are float values, but setSeconds takes an integer. + date.setMilliseconds(this._readReal(aByteOffset, 8) * 1000); + return date; + }, + + /** + * Reads a portion of the buffer as a string. + * + * @param aByteOffset + * The offset in the buffer at which the string starts + * @param aNumberOfChars + * The length of the string to be read (that is the number of + * characters, not bytes). + * @param aUnicode + * Whether or not it is a unicode string. + * @return the string read. + * + * @note this is tested to work well with unicode surrogate pairs. Because + * all unicode characters are read as 2-byte integers, unicode surrogate + * pairs are read from the buffer in the form of two integers, as required + * by String.fromCharCode. + */ + _readString: function BPLR__readString( + aByteOffset, + aNumberOfChars, + aUnicode + ) { + let codes = this._readUnsignedInts( + aByteOffset, + aUnicode ? 2 : 1, + aNumberOfChars + ); + return codes.map(c => String.fromCharCode(c)).join(""); + }, + + /** + * Reads an array of unsigned integers from the buffer. Integers larger than + * one byte are read in big endian form. + * + * @param aByteOffset + * The offset in the buffer at which the array starts. + * @param aIntSize + * The size of each int in the array. + * @param aLength + * The number of ints in the array. + * @param [optional] aBigIntAllowed (default: false) + * Whether or not to accept integers which outbounds JS limits for + * numbers (±2^53) in the form of a String. + * @return an array of integers (number primitive and/or Strings for large + * numbers (see header)). + * @throws if aBigIntAllowed is false and one of the integers in the array + * cannot be represented by a primitive js number. + */ + _readUnsignedInts: function BPLR__readUnsignedInts( + aByteOffset, + aIntSize, + aLength, + aBigIntAllowed + ) { + let uints = []; + for ( + let offset = aByteOffset; + offset < aByteOffset + aIntSize * aLength; + offset += aIntSize + ) { + if (aIntSize == 1) { + uints.push(this._dataView.getUint8(offset)); + } else if (aIntSize == 2) { + uints.push(this._dataView.getUint16(offset)); + } else if (aIntSize == 3) { + let int24 = Uint8Array(4); + int24[3] = 0; + int24[2] = this._dataView.getUint8(offset); + int24[1] = this._dataView.getUint8(offset + 1); + int24[0] = this._dataView.getUint8(offset + 2); + uints.push(Uint32Array(int24.buffer)[0]); + } else if (aIntSize == 4) { + uints.push(this._dataView.getUint32(offset)); + } else if (aIntSize == 8) { + let lo = this._dataView.getUint32(offset + 4); + let hi = this._dataView.getUint32(offset); + let uint64 = lazy.ctypes.UInt64.join(hi, lo); + if ( + lazy.ctypes.UInt64.compare(uint64, this._JS_MAX_INT_UNSIGNED) == 1 + ) { + if (aBigIntAllowed === true) { + uints.push(PropertyListUtils.wrapInt64(uint64.toString())); + } else { + throw new Error("Integer too big to be read as float 64"); + } + } else { + uints.push(parseInt(uint64, 10)); + } + } else { + throw new Error("Unsupported size: " + aIntSize); + } + } + + return uints; + }, + + /** + * Reads from the buffer the data object-count and the offset at which the + * first object starts. + * + * @param aObjectOffset + * the object's offset. + * @return [offset, count] - the offset in the buffer at which the first + * object in data starts, and the number of objects. + */ + _readDataOffsetAndCount: function BPLR__readDataOffsetAndCount( + aObjectOffset + ) { + // The length of some objects in the data can be stored in two ways: + // * If it is small enough, it is stored in the second four bits of the + // object descriptors. + // * Otherwise, those bits are set to 1111, indicating that the next byte + // consists of the integer size of the data-length (also stored in the form + // of an object descriptor). The length follows this byte. + let [, maybeLength] = this._readObjectDescriptor(aObjectOffset); + if (maybeLength != this.ADDITIONAL_INFO_BITS.LENGTH_INT_SIZE_FOLLOWS) { + return [aObjectOffset + 1, maybeLength]; + } + + let [, intSizeInfo] = this._readObjectDescriptor(aObjectOffset + 1); + + // The int size is 2^intSizeInfo. + let intSize = Math.pow(2, intSizeInfo); + let dataLength = this._readUnsignedInts(aObjectOffset + 2, intSize, 1)[0]; + return [aObjectOffset + 2 + intSize, dataLength]; + }, + + /** + * Read array from the buffer and wrap it as a js array. + * @param aObjectOffset + * the offset in the buffer at which the array starts. + * @param aNumberOfObjects + * the number of objects in the array. + * @return a js array. + */ + _wrapArray: function BPLR__wrapArray(aObjectOffset, aNumberOfObjects) { + let refs = this._readUnsignedInts( + aObjectOffset, + this._objectRefSize, + aNumberOfObjects + ); + + let array = new Array(aNumberOfObjects); + let readObjectBound = this._readObject.bind(this); + + // Each index in the returned array is a lazy getter for its object. + Array.prototype.forEach.call( + refs, + function (ref, objIndex) { + Object.defineProperty(array, objIndex, { + get() { + delete array[objIndex]; + return (array[objIndex] = readObjectBound(ref)); + }, + configurable: true, + enumerable: true, + }); + }, + this + ); + return array; + }, + + /** + * Reads dictionary from the buffer and wraps it as a Map object. + * @param aObjectOffset + * the offset in the buffer at which the dictionary starts + * @param aNumberOfObjects + * the number of keys in the dictionary + * @return Map-style dictionary. + */ + _wrapDictionary(aObjectOffset, aNumberOfObjects) { + // A dictionary in the binary format is stored as a list of references to + // key-objects, followed by a list of references to the value-objects for + // those keys. The size of each list is aNumberOfObjects * this._objectRefSize. + let dict = new Proxy(new Map(), LazyMapProxyHandler()); + if (aNumberOfObjects == 0) { + return dict; + } + + let keyObjsRefs = this._readUnsignedInts( + aObjectOffset, + this._objectRefSize, + aNumberOfObjects + ); + let valObjsRefs = this._readUnsignedInts( + aObjectOffset + aNumberOfObjects * this._objectRefSize, + this._objectRefSize, + aNumberOfObjects + ); + for (let i = 0; i < aNumberOfObjects; i++) { + let key = this._readObject(keyObjsRefs[i]); + let readBound = this._readObject.bind(this, valObjsRefs[i]); + + dict.setAsLazyGetter(key, readBound); + } + return dict; + }, + + /** + * Reads an object at the spcified index in the object table + * @param aObjectIndex + * index at the object table + * @return the property list object at the given index. + */ + _readObject: function BPLR__readObject(aObjectIndex) { + // If the object was previously read, return the cached object. + if (this._objects[aObjectIndex] !== undefined) { + return this._objects[aObjectIndex]; + } + + let objOffset = this._offsetTable[aObjectIndex]; + let [objType, additionalInfo] = this._readObjectDescriptor(objOffset); + let value; + switch (objType) { + case this.OBJECT_TYPE_BITS.SIMPLE: { + switch (additionalInfo) { + case this.ADDITIONAL_INFO_BITS.NULL: + value = null; + break; + case this.ADDITIONAL_INFO_BITS.FILL_BYTE: + value = undefined; + break; + case this.ADDITIONAL_INFO_BITS.FALSE: + value = false; + break; + case this.ADDITIONAL_INFO_BITS.TRUE: + value = true; + break; + default: + throw new Error("Unexpected value!"); + } + break; + } + + case this.OBJECT_TYPE_BITS.INTEGER: { + // The integer is sized 2^additionalInfo. + let intSize = Math.pow(2, additionalInfo); + + // For objects, 64-bit integers are always signed. Negative integers + // are always represented by a 64-bit integer. + if (intSize == 8) { + value = this._readSignedInt64(objOffset + 1); + } else { + value = this._readUnsignedInts(objOffset + 1, intSize, 1, true)[0]; + } + break; + } + + case this.OBJECT_TYPE_BITS.REAL: { + // The real is sized 2^additionalInfo. + value = this._readReal(objOffset + 1, Math.pow(2, additionalInfo)); + break; + } + + case this.OBJECT_TYPE_BITS.DATE: { + if (additionalInfo != this.ADDITIONAL_INFO_BITS.DATE) { + throw new Error("Unexpected value"); + } + + value = this._readDate(objOffset + 1); + break; + } + + case this.OBJECT_TYPE_BITS.DATA: { + let [offset, bytesCount] = this._readDataOffsetAndCount(objOffset); + value = new Uint8Array(this._readUnsignedInts(offset, 1, bytesCount)); + break; + } + + case this.OBJECT_TYPE_BITS.ASCII_STRING: { + let [offset, charsCount] = this._readDataOffsetAndCount(objOffset); + value = this._readString(offset, charsCount, false); + break; + } + + case this.OBJECT_TYPE_BITS.UNICODE_STRING: { + let [offset, unicharsCount] = this._readDataOffsetAndCount(objOffset); + value = this._readString(offset, unicharsCount, true); + break; + } + + case this.OBJECT_TYPE_BITS.UID: { + // UIDs are only used in Keyed Archives, which are not yet supported. + throw new Error("Keyed Archives are not supported"); + } + + case this.OBJECT_TYPE_BITS.ARRAY: + case this.OBJECT_TYPE_BITS.SET: { + // Note: For now, we fallback to handle sets the same way we handle + // arrays. See comments in the header of this file. + + // The bytes following the count are references to objects (indices). + // Each reference is an unsigned int with size=this._objectRefSize. + let [offset, objectsCount] = this._readDataOffsetAndCount(objOffset); + value = this._wrapArray(offset, objectsCount); + break; + } + + case this.OBJECT_TYPE_BITS.DICTIONARY: { + let [offset, objectsCount] = this._readDataOffsetAndCount(objOffset); + value = this._wrapDictionary(offset, objectsCount); + break; + } + + default: { + throw new Error("Unknown object type: " + objType); + } + } + + return (this._objects[aObjectIndex] = value); + }, +}; + +/** + * Reader for XML property lists. + * + * @param aDOMDoc + * the DOM document to be read as a property list. + */ +function XMLPropertyListReader(aDOMDoc) { + let docElt = aDOMDoc.documentElement; + if (!docElt || docElt.localName != "plist" || !docElt.firstElementChild) { + throw new Error("aDoc is not a property list document"); + } + + this._plistRootElement = docElt.firstElementChild; +} + +XMLPropertyListReader.prototype = { + get root() { + return this._readObject(this._plistRootElement); + }, + + /** + * Convert a dom element to a property list object. + * @param aDOMElt + * a dom element in a xml tree of a property list. + * @return a js object representing the property list object. + */ + _readObject: function XPLR__readObject(aDOMElt) { + switch (aDOMElt.localName) { + case "true": + return true; + case "false": + return false; + case "string": + case "key": + return aDOMElt.textContent; + case "integer": + return this._readInteger(aDOMElt); + case "real": { + let number = parseFloat(aDOMElt.textContent.trim()); + if (isNaN(number)) { + throw new Error("Could not parse float value"); + } + return number; + } + case "date": + return new Date(aDOMElt.textContent); + case "data": + // Strip spaces and new lines. + let base64str = aDOMElt.textContent.replace(/\s*/g, ""); + let decoded = atob(base64str); + return new Uint8Array(Array.from(decoded, c => c.charCodeAt(0))); + case "dict": + return this._wrapDictionary(aDOMElt); + case "array": + return this._wrapArray(aDOMElt); + default: + throw new Error("Unexpected tagname"); + } + }, + + _readInteger: function XPLR__readInteger(aDOMElt) { + // The integer may outbound js's max/min integer value. We recognize this + // case by comparing the parsed number to the original string value. + // In case of an outbound, we fallback to return the number as a string. + let numberAsString = aDOMElt.textContent.toString(); + let parsedNumber = parseInt(numberAsString, 10); + if (isNaN(parsedNumber)) { + throw new Error("Could not parse integer value"); + } + + if (parsedNumber.toString() == numberAsString) { + return parsedNumber; + } + + return PropertyListUtils.wrapInt64(numberAsString); + }, + + _wrapDictionary: function XPLR__wrapDictionary(aDOMElt) { + // <dict> + // <key>my true bool</key> + // <true/> + // <key>my string key</key> + // <string>My String Key</string> + // </dict> + if (aDOMElt.children.length % 2 != 0) { + throw new Error("Invalid dictionary"); + } + let dict = new Proxy(new Map(), LazyMapProxyHandler()); + for (let i = 0; i < aDOMElt.children.length; i += 2) { + let keyElem = aDOMElt.children[i]; + let valElem = aDOMElt.children[i + 1]; + + if (keyElem.localName != "key") { + throw new Error("Invalid dictionary"); + } + + let keyName = this._readObject(keyElem); + let readBound = this._readObject.bind(this, valElem); + + dict.setAsLazyGetter(keyName, readBound); + } + return dict; + }, + + _wrapArray: function XPLR__wrapArray(aDOMElt) { + // <array> + // <string>...</string> + // <integer></integer> + // <dict> + // .... + // </dict> + // </array> + + // Each element in the array is a lazy getter for its property list object. + let array = []; + let readObjectBound = this._readObject.bind(this); + Array.prototype.forEach.call(aDOMElt.children, function (elem, elemIndex) { + Object.defineProperty(array, elemIndex, { + get() { + delete array[elemIndex]; + return (array[elemIndex] = readObjectBound(elem)); + }, + configurable: true, + enumerable: true, + }); + }); + return array; + }, +}; + +/** + * Simple handler method to proxy calls to dict/Map objects to implement the + * setAsLazyGetter API. With this, a value can be set as a function that will + * evaluate its value and only be called when it's first retrieved. + * @member _lazyGetters + * Set() object to hold keys invoking LazyGetter. + * @method get + * Trap for getting property values. Ensures that if a lazyGetter is present + * as value for key, then the function is evaluated, the value is cached, + * and its value will be returned. + * @param target + * Target object. (dict/Map) + * @param name + * Name of operation to be invoked on target. + * @param key + * Key to be set, retrieved or deleted. Keys are checked for laziness. + * @return Returns value of "name" property of target by default. Otherwise returns + * updated target. + */ +function LazyMapProxyHandler() { + return { + _lazyGetters: new Set(), + get(target, name) { + switch (name) { + case "setAsLazyGetter": + return (key, value) => { + this._lazyGetters.add(key); + target.set(key, value); + }; + case "get": + return key => { + if (this._lazyGetters.has(key)) { + target.set(key, target.get(key)()); + this._lazyGetters.delete(key); + } + return target.get(key); + }; + case "delete": + return key => { + if (this._lazyGetters.has(key)) { + this._lazyGetters.delete(key); + } + return target.delete(key); + }; + case "has": + return key => target.has(key); + default: + return target[name]; + } + }, + }; +} diff --git a/toolkit/modules/Region.sys.mjs b/toolkit/modules/Region.sys.mjs new file mode 100644 index 0000000000..a07e73f378 --- /dev/null +++ b/toolkit/modules/Region.sys.mjs @@ -0,0 +1,887 @@ +/* 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 { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; + +import { RemoteSettings } from "resource://services-settings/remote-settings.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + LocationHelper: "resource://gre/modules/LocationHelper.sys.mjs", + setTimeout: "resource://gre/modules/Timer.sys.mjs", +}); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "wifiScanningEnabled", + "browser.region.network.scan", + true +); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "networkTimeout", + "browser.region.timeout", + 5000 +); + +// Retry the region lookup every hour on failure, a failure +// is likely to be a service failure so this gives the +// service some time to restore. Setting to 0 disabled retries. +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "retryTimeout", + "browser.region.retry-timeout", + 60 * 60 * 1000 +); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "loggingEnabled", + "browser.region.log", + false +); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "cacheBustEnabled", + "browser.region.update.enabled", + false +); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "updateDebounce", + "browser.region.update.debounce", + 60 * 60 * 24 +); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "lastUpdated", + "browser.region.update.updated", + 0 +); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "localGeocodingEnabled", + "browser.region.local-geocoding", + false +); + +XPCOMUtils.defineLazyServiceGetter( + lazy, + "timerManager", + "@mozilla.org/updates/timer-manager;1", + "nsIUpdateTimerManager" +); + +const log = console.createInstance({ + prefix: "Region.sys.mjs", + maxLogLevel: lazy.loggingEnabled ? "All" : "Warn", +}); + +const REGION_PREF = "browser.search.region"; +const COLLECTION_ID = "regions"; +const GEOLOCATION_TOPIC = "geolocation-position-events"; + +// Prefix for all the region updating related preferences. +const UPDATE_PREFIX = "browser.region.update"; + +// The amount of time (in seconds) we need to be in a new +// location before we update the home region. +// Currently set to 2 weeks. +const UPDATE_INTERVAL = 60 * 60 * 24 * 14; + +const MAX_RETRIES = 3; + +// If the user never uses geolocation, schedule a periodic +// update to check the current location (in seconds). +const UPDATE_CHECK_NAME = "region-update-timer"; +const UPDATE_CHECK_INTERVAL = 60 * 60 * 24 * 7; + +// Let child processes read the current home value +// but dont trigger redundant updates in them. +let inChildProcess = + Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; + +/** + * This module keeps track of the users current region (country). + * so the SearchService and other consumers can apply region + * specific customisations. + */ +class RegionDetector { + // The users home location. + _home = null; + // The most recent location the user was detected. + _current = null; + // The RemoteSettings client used to sync region files. + _rsClient = null; + // Keep track of the wifi data across listener events. + _wifiDataPromise = null; + // Keep track of how many times we have tried to fetch + // the users region during failure. + _retryCount = 0; + // Let tests wait for init to complete. + _initPromise = null; + // Topic for Observer events fired by Region.sys.mjs. + REGION_TOPIC = "browser-region-updated"; + // Values for telemetry. + TELEMETRY = { + SUCCESS: 0, + NO_RESULT: 1, + TIMEOUT: 2, + ERROR: 3, + }; + + /** + * Read currently stored region data and if needed trigger background + * region detection. + */ + async init() { + if (this._initPromise) { + return this._initPromise; + } + if (lazy.cacheBustEnabled && !inChildProcess) { + Services.tm.idleDispatchToMainThread(() => { + lazy.timerManager.registerTimer( + UPDATE_CHECK_NAME, + () => this._updateTimer(), + UPDATE_CHECK_INTERVAL + ); + }); + } + let promises = []; + this._home = Services.prefs.getCharPref(REGION_PREF, null); + if (!this._home && !inChildProcess) { + promises.push(this._idleDispatch(() => this._fetchRegion())); + } + if (lazy.localGeocodingEnabled && !inChildProcess) { + promises.push(this._idleDispatch(() => this._setupRemoteSettings())); + } + return (this._initPromise = Promise.all(promises)); + } + + /** + * Get the region we currently consider the users home. + * + * @returns {string} + * The users current home region. + */ + get home() { + return this._home; + } + + /** + * Get the last region we detected the user to be in. + * + * @returns {string} + * The users current region. + */ + get current() { + return this._current; + } + + /** + * Fetch the users current region. + * + * @returns {string} + * The country_code defining users current region. + */ + async _fetchRegion() { + if (this._retryCount >= MAX_RETRIES) { + return null; + } + let startTime = Date.now(); + let telemetryResult = this.TELEMETRY.SUCCESS; + let result = null; + + try { + result = await this._getRegion(); + } catch (err) { + telemetryResult = this.TELEMETRY[err.message] || this.TELEMETRY.ERROR; + log.error("Failed to fetch region", err); + if (lazy.retryTimeout) { + this._retryCount++; + lazy.setTimeout(() => { + Services.tm.idleDispatchToMainThread(this._fetchRegion.bind(this)); + }, lazy.retryTimeout); + } + } + + let took = Date.now() - startTime; + if (result) { + await this._storeRegion(result); + } + Services.telemetry + .getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_TIME_MS") + .add(took); + + Services.telemetry + .getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_RESULT") + .add(telemetryResult); + + return result; + } + + /** + * Validate then store the region and report telemetry. + * + * @param region + * The region to store. + */ + async _storeRegion(region) { + let prefix = "SEARCH_SERVICE"; + let isTimezoneUS = isUSTimezone(); + // If it's a US region, but not a US timezone, we don't store + // the value. This works because no region defaults to + // ZZ (unknown) in nsURLFormatter + if (region != "US" || isTimezoneUS) { + this._setCurrentRegion(region, true); + } + + // and telemetry... + if (region == "US" && !isTimezoneUS) { + log.info("storeRegion mismatch - US Region, non-US timezone"); + Services.telemetry + .getHistogramById(`${prefix}_US_COUNTRY_MISMATCHED_TIMEZONE`) + .add(1); + } + if (region != "US" && isTimezoneUS) { + log.info("storeRegion mismatch - non-US Region, US timezone"); + Services.telemetry + .getHistogramById(`${prefix}_US_TIMEZONE_MISMATCHED_COUNTRY`) + .add(1); + } + // telemetry to compare our geoip response with + // platform-specific country data. + // On Mac and Windows, we can get a country code via sysinfo + let platformCC = await Services.sysinfo.countryCode; + if (platformCC) { + let probeUSMismatched, probeNonUSMismatched; + switch (AppConstants.platform) { + case "macosx": + probeUSMismatched = `${prefix}_US_COUNTRY_MISMATCHED_PLATFORM_OSX`; + probeNonUSMismatched = `${prefix}_NONUS_COUNTRY_MISMATCHED_PLATFORM_OSX`; + break; + case "win": + probeUSMismatched = `${prefix}_US_COUNTRY_MISMATCHED_PLATFORM_WIN`; + probeNonUSMismatched = `${prefix}_NONUS_COUNTRY_MISMATCHED_PLATFORM_WIN`; + break; + default: + log.error( + "Platform " + + Services.appinfo.OS + + " has system country code but no search service telemetry probes" + ); + break; + } + if (probeUSMismatched && probeNonUSMismatched) { + if (region == "US" || platformCC == "US") { + // one of the 2 said US, so record if they are the same. + Services.telemetry + .getHistogramById(probeUSMismatched) + .add(region != platformCC); + } else { + // non-US - record if they are the same + Services.telemetry + .getHistogramById(probeNonUSMismatched) + .add(region != platformCC); + } + } + } + } + + /** + * Save the update current region and check if the home region + * also needs an update. + * + * @param {string} region + * The region to store. + */ + _setCurrentRegion(region = "") { + log.info("Setting current region:", region); + this._current = region; + + let now = Math.round(Date.now() / 1000); + let prefs = Services.prefs; + prefs.setIntPref(`${UPDATE_PREFIX}.updated`, now); + + // Interval is in seconds. + let interval = prefs.getIntPref( + `${UPDATE_PREFIX}.interval`, + UPDATE_INTERVAL + ); + let seenRegion = prefs.getCharPref(`${UPDATE_PREFIX}.region`, null); + let firstSeen = prefs.getIntPref(`${UPDATE_PREFIX}.first-seen`, 0); + + // If we don't have a value for .home we can set it immediately. + if (!this._home) { + this._setHomeRegion(region); + } else if (region != this._home && region != seenRegion) { + // If we are in a different region than what is currently + // considered home, then keep track of when we first + // seen the new location. + prefs.setCharPref(`${UPDATE_PREFIX}.region`, region); + prefs.setIntPref(`${UPDATE_PREFIX}.first-seen`, now); + } else if (region != this._home && region == seenRegion) { + // If we have been in the new region for longer than + // a specified time period, then set that as the new home. + if (now >= firstSeen + interval) { + this._setHomeRegion(region); + } + } else { + // If we are at home again, stop tracking the seen region. + prefs.clearUserPref(`${UPDATE_PREFIX}.region`); + prefs.clearUserPref(`${UPDATE_PREFIX}.first-seen`); + } + } + + // Wrap a string as a nsISupports. + _createSupportsString(data) { + let string = Cc["@mozilla.org/supports-string;1"].createInstance( + Ci.nsISupportsString + ); + string.data = data; + return string; + } + + /** + * Save the updated home region and notify observers. + * + * @param {string} region + * The region to store. + * @param {boolean} [notify] + * Tests can disable the notification for convenience as it + * may trigger an engines reload. + */ + _setHomeRegion(region, notify = true) { + if (region == this._home) { + return; + } + log.info("Updating home region:", region); + this._home = region; + Services.prefs.setCharPref("browser.search.region", region); + if (notify) { + Services.obs.notifyObservers( + this._createSupportsString(region), + this.REGION_TOPIC + ); + } + } + + /** + * Make the request to fetch the region from the configured service. + */ + async _getRegion() { + log.info("_getRegion called"); + let fetchOpts = { + headers: { "Content-Type": "application/json" }, + credentials: "omit", + }; + if (lazy.wifiScanningEnabled) { + let wifiData = await this._fetchWifiData(); + if (wifiData) { + let postData = JSON.stringify({ wifiAccessPoints: wifiData }); + log.info("Sending wifi details: ", wifiData); + fetchOpts.method = "POST"; + fetchOpts.body = postData; + } + } + let url = Services.urlFormatter.formatURLPref("browser.region.network.url"); + log.info("_getRegion url is: ", url); + + if (!url) { + return null; + } + + try { + let req = await this._fetchTimeout(url, fetchOpts, lazy.networkTimeout); + let res = await req.json(); + log.info("_getRegion returning ", res.country_code); + return res.country_code; + } catch (err) { + log.error("Error fetching region", err); + let errCode = err.message in this.TELEMETRY ? err.message : "NO_RESULT"; + throw new Error(errCode); + } + } + + /** + * Setup the RemoteSetting client + sync listener and ensure + * the map files are downloaded. + */ + async _setupRemoteSettings() { + log.info("_setupRemoteSettings"); + this._rsClient = RemoteSettings(COLLECTION_ID); + this._rsClient.on("sync", this._onRegionFilesSync.bind(this)); + await this._ensureRegionFilesDownloaded(); + // Start listening to geolocation events only after + // we know the maps are downloded. + Services.obs.addObserver(this, GEOLOCATION_TOPIC); + } + + /** + * Called when RemoteSettings syncs new data, clean up any + * stale attachments and download any new ones. + * + * @param {Object} syncData + * Object describing the data that has just been synced. + */ + async _onRegionFilesSync({ data: { deleted } }) { + log.info("_onRegionFilesSync"); + const toDelete = deleted.filter(d => d.attachment); + // Remove local files of deleted records + await Promise.all( + toDelete.map(entry => this._rsClient.attachments.deleteDownloaded(entry)) + ); + await this._ensureRegionFilesDownloaded(); + } + + /** + * Download the RemoteSetting record attachments, when they are + * successfully downloaded set a flag so we can start using them + * for geocoding. + */ + async _ensureRegionFilesDownloaded() { + log.info("_ensureRegionFilesDownloaded"); + let records = (await this._rsClient.get()).filter(d => d.attachment); + log.info("_ensureRegionFilesDownloaded", records); + if (!records.length) { + log.info("_ensureRegionFilesDownloaded: Nothing to download"); + return; + } + await Promise.all(records.map(r => this._rsClient.attachments.download(r))); + log.info("_ensureRegionFilesDownloaded complete"); + this._regionFilesReady = true; + } + + /** + * Fetch an attachment from RemoteSettings. + * + * @param {String} id + * The id of the record to fetch the attachment from. + */ + async _fetchAttachment(id) { + let record = (await this._rsClient.get({ filters: { id } })).pop(); + let { buffer } = await this._rsClient.attachments.download(record); + let text = new TextDecoder("utf-8").decode(buffer); + return JSON.parse(text); + } + + /** + * Get a map of the world with region definitions. + */ + async _getPlainMap() { + return this._fetchAttachment("world"); + } + + /** + * Get a map with the regions expanded by a few km to help + * fallback lookups when a location is not within a region. + */ + async _getBufferedMap() { + return this._fetchAttachment("world-buffered"); + } + + /** + * Gets the users current location using the same reverse IP + * request that is used for GeoLocation requests. + * + * @returns {Object} location + * Object representing the user location, with a location key + * that contains the lat / lng coordinates. + */ + async _getLocation() { + log.info("_getLocation called"); + let fetchOpts = { headers: { "Content-Type": "application/json" } }; + let url = Services.urlFormatter.formatURLPref("geo.provider.network.url"); + let req = await this._fetchTimeout(url, fetchOpts, lazy.networkTimeout); + let result = await req.json(); + log.info("_getLocation returning", result); + return result; + } + + /** + * Return the users current region using + * request that is used for GeoLocation requests. + * + * @returns {String} + * A 2 character string representing a region. + */ + async _getRegionLocally() { + let { location } = await this._getLocation(); + return this._geoCode(location); + } + + /** + * Take a location and return the region code for that location + * by looking up the coordinates in geojson map files. + * Inspired by https://github.com/mozilla/ichnaea/blob/874e8284f0dfa1868e79aae64e14707eed660efe/ichnaea/geocode.py#L114 + * + * @param {Object} location + * A location object containing lat + lng coordinates. + * + * @returns {String} + * A 2 character string representing a region. + */ + async _geoCode(location) { + let plainMap = await this._getPlainMap(); + let polygons = this._getPolygonsContainingPoint(location, plainMap); + if (polygons.length == 1) { + log.info("Found in single exact region"); + return polygons[0].properties.alpha2; + } + if (polygons.length) { + log.info("Found in ", polygons.length, "overlapping exact regions"); + return this._findFurthest(location, polygons); + } + + // We haven't found a match in the exact map, use the buffered map + // to see if the point is close to a region. + let bufferedMap = await this._getBufferedMap(); + polygons = this._getPolygonsContainingPoint(location, bufferedMap); + + if (polygons.length === 1) { + log.info("Found in single buffered region"); + return polygons[0].properties.alpha2; + } + + // Matched more than one region, which one of those regions + // is it closest to without the buffer. + if (polygons.length) { + log.info("Found in ", polygons.length, "overlapping buffered regions"); + let regions = polygons.map(polygon => polygon.properties.alpha2); + let unBufferedRegions = plainMap.features.filter(feature => + regions.includes(feature.properties.alpha2) + ); + return this._findClosest(location, unBufferedRegions); + } + return null; + } + + /** + * Find all the polygons that contain a single point, return + * an array of those polygons along with the region that + * they define + * + * @param {Object} point + * A lat + lng coordinate. + * @param {Object} map + * Geojson object that defined seperate regions with a list + * of polygons. + * + * @returns {Array} + * An array of polygons that contain the point, along with the + * region they define. + */ + _getPolygonsContainingPoint(point, map) { + let polygons = []; + for (const feature of map.features) { + let coords = feature.geometry.coordinates; + if (feature.geometry.type === "Polygon") { + if (this._polygonInPoint(point, coords[0])) { + polygons.push(feature); + } + } else if (feature.geometry.type === "MultiPolygon") { + for (const innerCoords of coords) { + if (this._polygonInPoint(point, innerCoords[0])) { + polygons.push(feature); + } + } + } + } + return polygons; + } + + /** + * Find the largest distance between a point and any of the points that + * make up an array of regions. + * + * @param {Object} location + * A lat + lng coordinate. + * @param {Array} regions + * An array of GeoJSON region definitions. + * + * @returns {String} + * A 2 character string representing a region. + */ + _findFurthest(location, regions) { + let max = { distance: 0, region: null }; + this._traverse(regions, ({ lat, lng, region }) => { + let distance = this._distanceBetween(location, { lng, lat }); + if (distance > max.distance) { + max = { distance, region }; + } + }); + return max.region; + } + + /** + * Find the smallest distance between a point and any of the points that + * make up an array of regions. + * + * @param {Object} location + * A lat + lng coordinate. + * @param {Array} regions + * An array of GeoJSON region definitions. + * + * @returns {String} + * A 2 character string representing a region. + */ + _findClosest(location, regions) { + let min = { distance: Infinity, region: null }; + this._traverse(regions, ({ lat, lng, region }) => { + let distance = this._distanceBetween(location, { lng, lat }); + if (distance < min.distance) { + min = { distance, region }; + } + }); + return min.region; + } + + /** + * Utility function to loop over all the coordinate points in an + * array of polygons and call a function on them. + * + * @param {Array} regions + * An array of GeoJSON region definitions. + * @param {Function} fun + * Function to call on individual coordinates. + */ + _traverse(regions, fun) { + for (const region of regions) { + if (region.geometry.type === "Polygon") { + for (const [lng, lat] of region.geometry.coordinates[0]) { + fun({ lat, lng, region: region.properties.alpha2 }); + } + } else if (region.geometry.type === "MultiPolygon") { + for (const innerCoords of region.geometry.coordinates) { + for (const [lng, lat] of innerCoords[0]) { + fun({ lat, lng, region: region.properties.alpha2 }); + } + } + } + } + } + + /** + * Check whether a point is contained within a polygon using the + * point in polygon algorithm: + * https://en.wikipedia.org/wiki/Point_in_polygon + * This casts a ray from the point and counts how many times + * that ray intersects with the polygons borders, if it is + * an odd number of times the point is inside the polygon. + * + * @param {Object} location + * A lat + lng coordinate. + * @param {Object} polygon + * Array of coordinates that define the boundaries of a polygon. + * + * @returns {boolean} + * Whether the point is within the polygon. + */ + _polygonInPoint({ lng, lat }, poly) { + let inside = false; + // For each edge of the polygon. + for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) { + let xi = poly[i][0]; + let yi = poly[i][1]; + let xj = poly[j][0]; + let yj = poly[j][1]; + // Does a ray cast from the point intersect with this polygon edge. + let intersect = + yi > lat != yj > lat && lng < ((xj - xi) * (lat - yi)) / (yj - yi) + xi; + // If so toggle result, an odd number of intersections + // means the point is inside. + if (intersect) { + inside = !inside; + } + } + return inside; + } + + /** + * Find the distance between 2 points. + * + * @param {Object} p1 + * A lat + lng coordinate. + * @param {Object} p2 + * A lat + lng coordinate. + * + * @returns {int} + * The distance between the 2 points. + */ + _distanceBetween(p1, p2) { + return Math.hypot(p2.lng - p1.lng, p2.lat - p1.lat); + } + + /** + * A wrapper around fetch that implements a timeout, will throw + * a TIMEOUT error if the request is not completed in time. + * + * @param {String} url + * The time url to fetch. + * @param {Object} opts + * The options object passed to the call to fetch. + * @param {int} timeout + * The time in ms to wait for the request to complete. + */ + async _fetchTimeout(url, opts, timeout) { + let controller = new AbortController(); + opts.signal = controller.signal; + return Promise.race([fetch(url, opts), this._timeout(timeout, controller)]); + } + + /** + * Implement the timeout for network requests. This will be run for + * all network requests, but the error will only be returned if it + * completes first. + * + * @param {int} timeout + * The time in ms to wait for the request to complete. + * @param {Object} controller + * The AbortController passed to the fetch request that + * allows us to abort the request. + */ + async _timeout(timeout, controller) { + await new Promise(resolve => lazy.setTimeout(resolve, timeout)); + if (controller) { + // Yield so it is the TIMEOUT that is returned and not + // the result of the abort(). + lazy.setTimeout(() => controller.abort(), 0); + } + throw new Error("TIMEOUT"); + } + + async _fetchWifiData() { + log.info("fetchWifiData called"); + this.wifiService = Cc["@mozilla.org/wifi/monitor;1"].getService( + Ci.nsIWifiMonitor + ); + this.wifiService.startWatching(this, false); + + return new Promise(resolve => { + this._wifiDataPromise = resolve; + }); + } + + /** + * If the user is using geolocation then we will see frequent updates + * debounce those so we aren't processing them constantly. + * + * @returns {bool} + * Whether we should continue the update check. + */ + _needsUpdateCheck() { + let sinceUpdate = Math.round(Date.now() / 1000) - lazy.lastUpdated; + let needsUpdate = sinceUpdate >= lazy.updateDebounce; + if (!needsUpdate) { + log.info(`Ignoring update check, last seen ${sinceUpdate} seconds ago`); + } + return needsUpdate; + } + + /** + * Dispatch a promise returning function to the main thread and + * resolve when it is completed. + */ + _idleDispatch(fun) { + return new Promise(resolve => { + Services.tm.idleDispatchToMainThread(fun().then(resolve)); + }); + } + + /** + * timerManager will call this periodically to update the region + * in case the user never users geolocation. + */ + async _updateTimer() { + if (this._needsUpdateCheck()) { + await this._fetchRegion(); + } + } + + /** + * Called when we see geolocation updates. + * in case the user never users geolocation. + * + * @param {Object} location + * A location object containing lat + lng coordinates. + * + */ + async _seenLocation(location) { + log.info(`Got location update: ${location.lat}:${location.lng}`); + if (this._needsUpdateCheck()) { + let region = await this._geoCode(location); + if (region) { + this._setCurrentRegion(region); + } + } + } + + onChange(accessPoints) { + log.info("onChange called"); + if (!accessPoints || !this._wifiDataPromise) { + return; + } + + if (this.wifiService) { + this.wifiService.stopWatching(this); + this.wifiService = null; + } + + if (this._wifiDataPromise) { + let data = lazy.LocationHelper.formatWifiAccessPoints(accessPoints); + this._wifiDataPromise(data); + this._wifiDataPromise = null; + } + } + + observe(aSubject, aTopic, aData) { + log.info(`Observed ${aTopic}`); + switch (aTopic) { + case GEOLOCATION_TOPIC: + // aSubject from GeoLocation.cpp will be a GeoPosition + // DOM Object, but from tests we will receive a + // wrappedJSObject so handle both here. + let coords = aSubject.coords || aSubject.wrappedJSObject.coords; + this._seenLocation({ + lat: coords.latitude, + lng: coords.longitude, + }); + break; + } + } + + // For tests to create blank new instances. + newInstance() { + return new RegionDetector(); + } +} + +export let Region = new RegionDetector(); +Region.init(); + +// A method that tries to determine if this user is in a US geography. +function isUSTimezone() { + // Timezone assumptions! We assume that if the system clock's timezone is + // between Newfoundland and Hawaii, that the user is in North America. + + // This includes all of South America as well, but we have relatively few + // en-US users there, so that's OK. + + // 150 minutes = 2.5 hours (UTC-2.5), which is + // Newfoundland Daylight Time (http://www.timeanddate.com/time/zones/ndt) + + // 600 minutes = 10 hours (UTC-10), which is + // Hawaii-Aleutian Standard Time (http://www.timeanddate.com/time/zones/hast) + + let UTCOffset = new Date().getTimezoneOffset(); + return UTCOffset >= 150 && UTCOffset <= 600; +} diff --git a/toolkit/modules/RemotePageAccessManager.sys.mjs b/toolkit/modules/RemotePageAccessManager.sys.mjs new file mode 100644 index 0000000000..d2ab2fb805 --- /dev/null +++ b/toolkit/modules/RemotePageAccessManager.sys.mjs @@ -0,0 +1,390 @@ +/* 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/. */ + +/* + * RemotePageAccessManager determines which RPM functions a given + * about page is allowed to access. It does this based on a map from about + * page URLs to allowed functions for that page/URL. + * + * An RPM function will be exported into the page only if it appears + * in the access managers's accessMap for that page's uri. + * + * This module may be used from both the child and parent process. + * + * Please note that prefs that one wants to update need to be + * explicitly allowed within AsyncPrefs.sys.mjs. + */ +export let RemotePageAccessManager = { + /* The accessMap lists the permissions that are allowed per page. + * The structure should be of the following form: + * <URL> : { + * <function name>: [<keys>], + * ... + * } + * For the page with given URL, permission is allowed for each + * listed function with a matching key. The first argument to the + * function must match one of the keys. If keys is an array with a + * single asterisk element ["*"], then all values are permitted. + */ + accessMap: { + "about:certerror": { + RPMSendAsyncMessage: [ + "Browser:EnableOnlineMode", + "Browser:ResetSSLPreferences", + "GetChangedCertPrefs", + "Browser:OpenCaptivePortalPage", + "Browser:SSLErrorGoBack", + "Browser:PrimeMitm", + "Browser:ResetEnterpriseRootsPref", + "DisplayOfflineSupportPage", + ], + RPMRecordTelemetryEvent: ["*"], + RPMAddMessageListener: ["*"], + RPMRemoveMessageListener: ["*"], + RPMGetFormatURLPref: ["app.support.baseURL"], + RPMGetBoolPref: [ + "security.certerrors.mitm.priming.enabled", + "security.certerrors.permanentOverride", + "security.enterprise_roots.auto-enabled", + "security.certerror.hideAddException", + "network.trr.display_fallback_warning", + ], + RPMGetIntPref: [ + "security.dialog_enable_delay", + "services.settings.clock_skew_seconds", + "services.settings.last_update_seconds", + ], + RPMGetAppBuildID: ["*"], + RPMGetInnerMostURI: ["*"], + RPMIsWindowPrivate: ["*"], + RPMAddToHistogram: ["*"], + }, + "about:home": { + RPMSendAsyncMessage: ["ActivityStream:ContentToMain"], + RPMAddMessageListener: ["ActivityStream:MainToContent"], + }, + "about:httpsonlyerror": { + RPMGetFormatURLPref: ["app.support.baseURL"], + RPMGetIntPref: ["security.dialog_enable_delay"], + RPMSendAsyncMessage: ["goBack", "openInsecure"], + RPMAddMessageListener: ["WWWReachable"], + RPMTryPingSecureWWWLink: ["*"], + RPMOpenSecureWWWLink: ["*"], + }, + "about:certificate": { + RPMSendQuery: ["getCertificates"], + }, + "about:neterror": { + RPMSendAsyncMessage: [ + "Browser:EnableOnlineMode", + "Browser:ResetSSLPreferences", + "GetChangedCertPrefs", + "Browser:OpenCaptivePortalPage", + "Browser:SSLErrorGoBack", + "Browser:PrimeMitm", + "Browser:ResetEnterpriseRootsPref", + "ReportBlockingError", + "DisplayOfflineSupportPage", + "OpenTRRPreferences", + ], + RPMCheckAlternateHostAvailable: ["*"], + RPMRecordTelemetryEvent: ["security.doh.neterror"], + RPMAddMessageListener: ["*"], + RPMRemoveMessageListener: ["*"], + RPMGetFormatURLPref: [ + "app.support.baseURL", + "network.trr_ui.skip_reason_learn_more_url", + ], + RPMGetBoolPref: [ + "security.certerror.hideAddException", + "security.xfocsp.errorReporting.automatic", + "security.xfocsp.errorReporting.enabled", + "network.trr.display_fallback_warning", + ], + RPMSetPref: [ + "security.xfocsp.errorReporting.automatic", + "network.trr.display_fallback_warning", + ], + RPMAddToHistogram: ["*"], + RPMGetInnerMostURI: ["*"], + RPMGetHttpResponseHeader: ["*"], + RPMIsTRROnlyFailure: ["*"], + RPMIsFirefox: ["*"], + RPMIsNativeFallbackFailure: ["*"], + RPMGetTRRSkipReason: ["*"], + RPMGetTRRDomain: ["*"], + RPMIsSiteSpecificTRRError: ["*"], + RPMSetTRRDisabledLoadFlags: ["*"], + RPMSendQuery: ["Browser:AddTRRExcludedDomain"], + RPMGetIntPref: ["network.trr.mode"], + }, + "about:newtab": { + RPMSendAsyncMessage: ["ActivityStream:ContentToMain"], + RPMAddMessageListener: ["ActivityStream:MainToContent"], + }, + "about:pocket-saved": { + RPMSendAsyncMessage: ["*"], + RPMAddMessageListener: ["*"], + RPMRemoveMessageListener: ["*"], + RPMGetStringPref: ["extensions.pocket.site"], + }, + "about:pocket-signup": { + RPMSendAsyncMessage: ["*"], + RPMAddMessageListener: ["*"], + RPMRemoveMessageListener: ["*"], + RPMGetStringPref: ["extensions.pocket.site"], + }, + "about:pocket-home": { + RPMSendAsyncMessage: ["*"], + RPMAddMessageListener: ["*"], + RPMRemoveMessageListener: ["*"], + RPMGetStringPref: ["extensions.pocket.site"], + }, + "about:pocket-style-guide": { + RPMSendAsyncMessage: ["*"], + RPMAddMessageListener: ["*"], + RPMRemoveMessageListener: ["*"], + }, + "about:privatebrowsing": { + RPMSendAsyncMessage: [ + "OpenPrivateWindow", + "SearchBannerDismissed", + "OpenSearchPreferences", + "SearchHandoff", + ], + RPMSendQuery: [ + "IsPromoBlocked", + "ShouldShowSearchBanner", + "ShouldShowPromo", + "SpecialMessageActionDispatch", + ], + RPMAddMessageListener: ["*"], + RPMRemoveMessageListener: ["*"], + RPMGetFormatURLPref: [ + "app.support.baseURL", + "browser.privatebrowsing.vpnpromourl", + ], + RPMIsWindowPrivate: ["*"], + RPMGetBoolPref: ["browser.privatebrowsing.felt-privacy-v1"], + }, + "about:protections": { + RPMSendAsyncMessage: [ + "OpenContentBlockingPreferences", + "OpenAboutLogins", + "OpenSyncPreferences", + "ClearMonitorCache", + "RecordEntryPoint", + ], + RPMSendQuery: [ + "FetchUserLoginsData", + "FetchMonitorData", + "FetchContentBlockingEvents", + "FetchMobileDeviceConnected", + "GetShowProxyCard", + "FetchEntryPoint", + "FetchVPNSubStatus", + "FetchShowVPNCard", + ], + RPMAddMessageListener: ["*"], + RPMRemoveMessageListener: ["*"], + RPMSetPref: [ + "browser.contentblocking.report.show_mobile_app", + "browser.contentblocking.report.hide_vpn_banner", + ], + RPMGetBoolPref: [ + "browser.contentblocking.report.lockwise.enabled", + "browser.contentblocking.report.monitor.enabled", + "privacy.fingerprintingProtection", + "privacy.socialtracking.block_cookies.enabled", + "browser.contentblocking.report.proxy.enabled", + "privacy.trackingprotection.cryptomining.enabled", + "privacy.trackingprotection.fingerprinting.enabled", + "privacy.trackingprotection.enabled", + "privacy.trackingprotection.socialtracking.enabled", + "browser.contentblocking.report.show_mobile_app", + "browser.contentblocking.report.hide_vpn_banner", + "browser.vpn_promo.enabled", + ], + RPMGetStringPref: [ + "browser.contentblocking.category", + "browser.contentblocking.report.monitor.url", + "browser.contentblocking.report.monitor.sign_in_url", + "browser.contentblocking.report.manage_devices.url", + "browser.contentblocking.report.proxy_extension.url", + "browser.contentblocking.report.lockwise.mobile-android.url", + "browser.contentblocking.report.lockwise.mobile-ios.url", + "browser.contentblocking.report.mobile-ios.url", + "browser.contentblocking.report.mobile-android.url", + "browser.contentblocking.report.vpn.url", + "browser.contentblocking.report.vpn-promo.url", + "browser.contentblocking.report.vpn-android.url", + "browser.contentblocking.report.vpn-ios.url", + ], + RPMGetIntPref: ["network.cookie.cookieBehavior"], + RPMGetFormatURLPref: [ + "browser.contentblocking.report.monitor.how_it_works.url", + "browser.contentblocking.report.lockwise.how_it_works.url", + "browser.contentblocking.report.monitor.preferences_url", + "browser.contentblocking.report.monitor.home_page_url", + "browser.contentblocking.report.social.url", + "browser.contentblocking.report.cookie.url", + "browser.contentblocking.report.tracker.url", + "browser.contentblocking.report.fingerprinter.url", + "browser.contentblocking.report.cryptominer.url", + ], + RPMRecordTelemetryEvent: ["*"], + }, + "about:shoppingsidebar": { + RPMSetPref: [ + "browser.shopping.experience2023.optedIn", + "browser.shopping.experience2023.active", + "browser.shopping.experience2023.ads.userEnabled", + "browser.shopping.experience2023.sidebarClosedCount", + "browser.shopping.experience2023.showKeepSidebarClosedMessage", + "browser.shopping.experience2023.autoOpen.userEnabled", + ], + RPMGetFormatURLPref: ["app.support.baseURL"], + RPMGetIntPref: ["browser.shopping.experience2023.sidebarClosedCount"], + RPMGetBoolPref: [ + "browser.shopping.experience2023.showKeepSidebarClosedMessage", + ], + }, + "about:tabcrashed": { + RPMSendAsyncMessage: ["Load", "closeTab", "restoreTab", "restoreAll"], + RPMAddMessageListener: ["*"], + RPMRemoveMessageListener: ["*"], + }, + "about:welcome": { + RPMSendAsyncMessage: ["ActivityStream:ContentToMain"], + RPMAddMessageListener: ["ActivityStream:MainToContent"], + }, + }, + + /** + * Check if access is allowed to the given feature for a given document. + * This should be called from within the child process. + * + * The feature within the accessMap must list the given aValue, for access to + * be granted. + * + * @param aDocument child process document to call from + * @param aFeature to feature to check access to + * @param aValue value that must be included with that feature's allow list + * @returns true if access is allowed or false otherwise + */ + checkAllowAccess(aDocument, aFeature, aValue) { + let principal = aDocument.nodePrincipal; + // if there is no content principal; deny access + if (!principal) { + return false; + } + + return this.checkAllowAccessWithPrincipal( + principal, + aFeature, + aValue, + aDocument + ); + }, + + /** + * Check if access is allowed to the given feature for a given principal. + * This may be called from within the child or parent process. + * + * The feature within the accessMap must list the given aValue, for access to + * be granted. + * + * In the parent process, the passed-in document is expected to be null. + * + * @param aPrincipal principal being called from + * @param aFeature to feature to check access to + * @param aValue value that must be included with that feature's allow list + * @param aDocument optional child process document to call from + * @returns true if access is allowed or false otherwise + */ + checkAllowAccessWithPrincipal(aPrincipal, aFeature, aValue, aDocument) { + let accessMapForFeature = this.checkAllowAccessToFeature( + aPrincipal, + aFeature, + aDocument + ); + if (!accessMapForFeature) { + console.error( + "RemotePageAccessManager does not allow access to Feature: ", + aFeature, + " for: ", + aDocument.location + ); + + return false; + } + + // If the actual value is in the allow list for that feature; + // allow access + if (accessMapForFeature.includes(aValue) || accessMapForFeature[0] == "*") { + return true; + } + + return false; + }, + + /** + * Check if a particular feature can be accessed without checking for a + * specific feature value. + * + * @param aPrincipal principal being called from + * @param aFeature to feature to check access to + * @param aDocument optional child process document to call from + * @returns non-null allow list if access is allowed or null otherwise + */ + checkAllowAccessToFeature(aPrincipal, aFeature, aDocument) { + let spec; + if (!aPrincipal.isContentPrincipal) { + // For the sake of remote pages, when the principal has no uri, + // we want to access the "real" document URI directly, e.g. if the + // about: page is sandboxed. + if (!aDocument) { + return null; + } + if (!aDocument.documentURIObject.schemeIs("about")) { + return null; + } + spec = + aDocument.documentURIObject.prePath + + aDocument.documentURIObject.filePath; + } else { + if (!aPrincipal.schemeIs("about")) { + return null; + } + spec = aPrincipal.prePath + aPrincipal.filePath; + } + + // Check if there is an entry for that requestying URI in the accessMap; + // if not, deny access. + let accessMapForURI = this.accessMap[spec]; + if (!accessMapForURI) { + return null; + } + + // Check if the feature is allowed to be accessed for that URI; + // if not, deny access. + return accessMapForURI[aFeature]; + }, + + /** + * This function adds a new page to the access map, but can only + * be used in a test environment. + */ + addPage(aUrl, aFunctionMap) { + if (!Cu.isInAutomation) { + throw new Error("Cannot only modify privileges during testing"); + } + + if (aUrl in this.accessMap) { + throw new Error("Cannot modify privileges of existing page"); + } + + this.accessMap[aUrl] = aFunctionMap; + }, +}; diff --git a/toolkit/modules/ResetProfile.sys.mjs b/toolkit/modules/ResetProfile.sys.mjs new file mode 100644 index 0000000000..333ee7c0cf --- /dev/null +++ b/toolkit/modules/ResetProfile.sys.mjs @@ -0,0 +1,109 @@ +/* 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 { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineLazyGetter(lazy, "MigrationUtils", () => { + // MigrationUtils is currently only available in browser builds. + if (AppConstants.MOZ_BUILD_APP != "browser") { + return undefined; + } + + try { + let { MigrationUtils } = ChromeUtils.importESModule( + "resource:///modules/MigrationUtils.sys.mjs" + ); + return MigrationUtils; + } catch (e) { + console.error(`Unable to load MigrationUtils.sys.mjs: ${e}`); + } + return undefined; +}); + +const MOZ_APP_NAME = AppConstants.MOZ_APP_NAME; + +export var ResetProfile = { + /** + * Check if reset is supported for the currently running profile. + * + * @return boolean whether reset is supported. + */ + resetSupported() { + if (Services.policies && !Services.policies.isAllowed("profileRefresh")) { + return false; + } + + // Reset is only supported if the self-migrator used for reset exists. + if ( + !lazy.MigrationUtils || + !lazy.MigrationUtils.migratorExists(MOZ_APP_NAME) + ) { + return false; + } + + // We also need to be using a profile the profile manager knows about. + let profileService = Cc[ + "@mozilla.org/toolkit/profile-service;1" + ].getService(Ci.nsIToolkitProfileService); + let currentProfileDir = Services.dirsvc.get("ProfD", Ci.nsIFile); + for (let profile of profileService.profiles) { + if (profile.rootDir && profile.rootDir.equals(currentProfileDir)) { + return true; + } + } + return false; + }, + + /** + * Ask the user if they wish to restart the application to reset the profile. + */ + async openConfirmationDialog(window) { + let win = window; + // If we are, for instance, on an about page, get the chrome window to + // access its gDialogBox. + if (win.docShell.chromeEventHandler) { + win = win.browsingContext?.topChromeWindow; + } + + let params = { + learnMore: false, + reset: false, + }; + + if (win.gDialogBox) { + await win.gDialogBox.open( + "chrome://global/content/resetProfile.xhtml", + params + ); + } else { + win.openDialog( + "chrome://global/content/resetProfile.xhtml", + null, + "modal,centerscreen,titlebar", + params + ); + } + + if (params.learnMore) { + win.openTrustedLinkIn( + "https://support.mozilla.org/kb/refresh-firefox-reset-add-ons-and-settings", + "tab" + ); + return; + } + + if (!params.reset) { + return; + } + + // Set the reset profile environment variable. + Services.env.set("MOZ_RESET_PROFILE_RESTART", "1"); + + Services.startup.quit( + Ci.nsIAppStartup.eForceQuit | Ci.nsIAppStartup.eRestart + ); + }, +}; diff --git a/toolkit/modules/ResponsivenessMonitor.sys.mjs b/toolkit/modules/ResponsivenessMonitor.sys.mjs new file mode 100644 index 0000000000..2437fbbd6c --- /dev/null +++ b/toolkit/modules/ResponsivenessMonitor.sys.mjs @@ -0,0 +1,42 @@ +/* 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 ResponsivenessMonitor(intervalMS = 100) { + this._intervalMS = intervalMS; + this._prevTimestamp = Date.now(); + this._accumulatedDelay = 0; + this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + this._timer.initWithCallback( + this, + this._intervalMS, + Ci.nsITimer.TYPE_REPEATING_SLACK + ); +} + +ResponsivenessMonitor.prototype = { + QueryInterface: ChromeUtils.generateQI(["nsINamed", "nsITimerCallback"]), + + name: "ResponsivenessMonitor", + + notify() { + let now = Date.now(); + this._accumulatedDelay += Math.max( + 0, + now - this._prevTimestamp - this._intervalMS + ); + this._prevTimestamp = now; + }, + + abort() { + if (this._timer) { + this._timer.cancel(); + this._timer = null; + } + }, + + finish() { + this.abort(); + return this._accumulatedDelay; + }, +}; diff --git a/toolkit/modules/SelectionUtils.sys.mjs b/toolkit/modules/SelectionUtils.sys.mjs new file mode 100644 index 0000000000..8dcbc0c494 --- /dev/null +++ b/toolkit/modules/SelectionUtils.sys.mjs @@ -0,0 +1,154 @@ +/* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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 var SelectionUtils = { + /** + * Trim the selection text to a reasonable size and sanitize it to make it + * safe for search query input. + * + * @param aSelection + * The selection text to trim. + * @param aMaxLen + * The maximum string length, defaults to a reasonable size if undefined. + * @return The trimmed selection text. + */ + trimSelection(aSelection, aMaxLen) { + // Selections of more than 150 characters aren't useful. + const maxLen = Math.min(aMaxLen || 150, aSelection.length); + + if (aSelection.length > maxLen) { + // only use the first maxLen important chars. see bug 221361 + let pattern = new RegExp("^(?:\\s*.){0," + maxLen + "}"); + pattern.test(aSelection); + aSelection = RegExp.lastMatch; + } + + aSelection = aSelection.trim().replace(/\s+/g, " "); + + if (aSelection.length > maxLen) { + aSelection = aSelection.substr(0, maxLen); + } + + return aSelection; + }, + + /** + * Retrieve the text selection details for the given window. + * + * @param aTopWindow + * The top window of the element containing the selection. + * @param aCharLen + * The maximum string length for the selection text. + * @return The selection details containing the full and trimmed selection text + * and link details for link selections. + */ + getSelectionDetails(aTopWindow, aCharLen) { + let focusedWindow = {}; + let focusedElement = Services.focus.getFocusedElementForWindow( + aTopWindow, + true, + focusedWindow + ); + focusedWindow = focusedWindow.value; + + let selection = focusedWindow.getSelection(); + let selectionStr = selection.toString(); + let fullText; + + let url; + let linkText; + + let isDocumentLevelSelection = true; + // try getting a selected text in text input. + if (!selectionStr && focusedElement) { + // Don't get the selection for password fields. See bug 565717. + if ( + ChromeUtils.getClassName(focusedElement) === "HTMLTextAreaElement" || + (ChromeUtils.getClassName(focusedElement) === "HTMLInputElement" && + focusedElement.mozIsTextField(true)) + ) { + selection = focusedElement.editor.selection; + selectionStr = selection.toString(); + isDocumentLevelSelection = false; + } + } + + let collapsed = selection.isCollapsed; + + if (selectionStr) { + // Have some text, let's figure out if it looks like a URL that isn't + // actually a link. + linkText = selectionStr.trim(); + if (/^(?:https?|ftp):/i.test(linkText)) { + try { + url = Services.io.newURI(linkText); + } catch (ex) {} + } else if (/^(?:[a-z\d-]+\.)+[a-z]+$/i.test(linkText)) { + // Check if this could be a valid url, just missing the protocol. + // Now let's see if this is an intentional link selection. Our guess is + // based on whether the selection begins/ends with whitespace or is + // preceded/followed by a non-word character. + + // selection.toString() trims trailing whitespace, so we look for + // that explicitly in the first and last ranges. + let beginRange = selection.getRangeAt(0); + let delimitedAtStart = /^\s/.test(beginRange); + if (!delimitedAtStart) { + let container = beginRange.startContainer; + let offset = beginRange.startOffset; + if (container.nodeType == container.TEXT_NODE && offset > 0) { + delimitedAtStart = /\W/.test(container.textContent[offset - 1]); + } else { + delimitedAtStart = true; + } + } + + let delimitedAtEnd = false; + if (delimitedAtStart) { + let endRange = selection.getRangeAt(selection.rangeCount - 1); + delimitedAtEnd = /\s$/.test(endRange); + if (!delimitedAtEnd) { + let container = endRange.endContainer; + let offset = endRange.endOffset; + if ( + container.nodeType == container.TEXT_NODE && + offset < container.textContent.length + ) { + delimitedAtEnd = /\W/.test(container.textContent[offset]); + } else { + delimitedAtEnd = true; + } + } + } + + if (delimitedAtStart && delimitedAtEnd) { + try { + url = Services.uriFixup.getFixupURIInfo(linkText).preferredURI; + } catch (ex) {} + } + } + } + + if (selectionStr) { + // Pass up to 16K through unmolested. If an add-on needs more, they will + // have to use a content script. + fullText = selectionStr.substr(0, 16384); + selectionStr = this.trimSelection(selectionStr, aCharLen); + } + + if (url && !url.host) { + url = null; + } + + return { + text: selectionStr, + docSelectionIsCollapsed: collapsed, + isDocumentLevelSelection, + fullText, + linkURL: url ? url.spec : null, + linkText: url ? linkText : "", + }; + }, +}; diff --git a/toolkit/modules/ServiceRequest.sys.mjs b/toolkit/modules/ServiceRequest.sys.mjs new file mode 100644 index 0000000000..a88ba9c324 --- /dev/null +++ b/toolkit/modules/ServiceRequest.sys.mjs @@ -0,0 +1,180 @@ +/* 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/. */ + +/** + * This module consolidates various code and data update requests, so flags + * can be set, Telemetry collected, etc. in a central place. + */ + +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const lazy = {}; + +XPCOMUtils.defineLazyServiceGetter( + lazy, + "ProxyService", + "@mozilla.org/network/protocol-proxy-service;1", + "nsIProtocolProxyService" +); + +ChromeUtils.defineESModuleGetters(lazy, { + ExtensionPreferencesManager: + "resource://gre/modules/ExtensionPreferencesManager.sys.mjs", +}); + +XPCOMUtils.defineLazyServiceGetter( + lazy, + "CaptivePortalService", + "@mozilla.org/network/captive-portal-service;1", + "nsICaptivePortalService" +); + +XPCOMUtils.defineLazyServiceGetter( + lazy, + "gNetworkLinkService", + "@mozilla.org/network/network-link-service;1", + "nsINetworkLinkService" +); + +const PROXY_CONFIG_TYPES = [ + "direct", + "manual", + "pac", + "unused", // nsIProtocolProxyService.idl skips index 3. + "wpad", + "system", +]; + +function recordEvent(service, source = {}) { + try { + Services.telemetry.setEventRecordingEnabled("service_request", true); + Services.telemetry.recordEvent( + "service_request", + "bypass", + "proxy_info", + service, + source + ); + } catch (err) { + // If the telemetry throws just log the error so it doesn't break any + // functionality. + console.error(err); + } +} + +// If proxy.settings is used to change the proxy, an extension will +// be "in control". This returns the id of that extension. +async function getControllingExtension() { + if ( + !WebExtensionPolicy.getActiveExtensions().some(p => + p.permissions.includes("proxy") + ) + ) { + return undefined; + } + // Is this proxied by an extension that set proxy prefs? + let setting = await lazy.ExtensionPreferencesManager.getSetting( + "proxy.settings" + ); + return setting?.id; +} + +async function getProxySource(proxyInfo) { + // sourceId is set when using proxy.onRequest + if (proxyInfo.sourceId) { + return { + source: proxyInfo.sourceId, + type: "api", + }; + } + let type = PROXY_CONFIG_TYPES[lazy.ProxyService.proxyConfigType] || "unknown"; + + // If we have a policy it will have set the prefs. + if ( + Services.policies && + Services.policies.status === Services.policies.ACTIVE + ) { + let policies = Services.policies.getActivePolicies()?.filter(p => p.Proxy); + if (policies?.length) { + return { + source: "policy", + type, + }; + } + } + + let source = await getControllingExtension(); + return { + source: source || "prefs", + type, + }; +} + +/** + * ServiceRequest is intended to be a drop-in replacement for current users + * of XMLHttpRequest. + * + * @param {Object} options - Options for underlying XHR, e.g. { mozAnon: bool } + */ +export class ServiceRequest extends XMLHttpRequest { + constructor(options) { + super(options); + } + /** + * Opens an XMLHttpRequest, and sets the NSS "beConservative" flag. + * Requests are always async. + * + * @param {String} method - HTTP method to use, e.g. "GET". + * @param {String} url - URL to open. + * @param {Object} options - Additional options { bypassProxy: bool }. + */ + open(method, url, options) { + super.open(method, url, true); + + if (super.channel instanceof Ci.nsIHttpChannelInternal) { + let internal = super.channel.QueryInterface(Ci.nsIHttpChannelInternal); + // Disable cutting edge features, like TLS 1.3, where middleboxes might brick us + internal.beConservative = true; + // Disable use of proxy for this request if necessary. + if (options?.bypassProxy && this.bypassProxyEnabled) { + internal.bypassProxy = true; + } + } + } + + get bypassProxy() { + let { channel } = this; + return channel.QueryInterface(Ci.nsIHttpChannelInternal).bypassProxy; + } + + get isProxied() { + let { channel } = this; + return !!(channel instanceof Ci.nsIProxiedChannel && channel.proxyInfo); + } + + get bypassProxyEnabled() { + return Services.prefs.getBoolPref("network.proxy.allow_bypass", true); + } + + static async logProxySource(channel, service) { + if (channel.proxyInfo) { + let source = await getProxySource(channel.proxyInfo); + recordEvent(service, source); + } + } + + static get isOffline() { + try { + return ( + Services.io.offline || + lazy.CaptivePortalService.state == + lazy.CaptivePortalService.LOCKED_PORTAL || + !lazy.gNetworkLinkService.isLinkUp + ); + } catch (ex) { + // we cannot get state, assume the best. + } + return false; + } +} diff --git a/toolkit/modules/ShortcutUtils.sys.mjs b/toolkit/modules/ShortcutUtils.sys.mjs new file mode 100644 index 0000000000..e45855602b --- /dev/null +++ b/toolkit/modules/ShortcutUtils.sys.mjs @@ -0,0 +1,409 @@ +/* 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 { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineLazyGetter(lazy, "PlatformKeys", function () { + return Services.strings.createBundle( + "chrome://global-platform/locale/platformKeys.properties" + ); +}); + +ChromeUtils.defineLazyGetter(lazy, "Keys", function () { + return Services.strings.createBundle( + "chrome://global/locale/keys.properties" + ); +}); + +export var ShortcutUtils = { + IS_VALID: "valid", + INVALID_KEY: "invalid_key", + INVALID_MODIFIER: "invalid_modifier", + INVALID_COMBINATION: "invalid_combination", + DUPLICATE_MODIFIER: "duplicate_modifier", + MODIFIER_REQUIRED: "modifier_required", + + CLOSE_TAB: "CLOSE_TAB", + CYCLE_TABS: "CYCLE_TABS", + TOGGLE_CARET_BROWSING: "TOGGLE_CARET_BROWSING", + MOVE_TAB_BACKWARD: "MOVE_TAB_BACKWARD", + MOVE_TAB_FORWARD: "MOVE_TAB_FORWARD", + NEXT_TAB: "NEXT_TAB", + PREVIOUS_TAB: "PREVIOUS_TAB", + + /** + * Prettifies the modifier keys for an element. + * + * @param Node aElemKey + * The key element to get the modifiers from. + * @return string + * A prettified and properly separated modifier keys string. + */ + prettifyShortcut(aElemKey) { + let elemString = this.getModifierString(aElemKey.getAttribute("modifiers")); + let key = this.getKeyString( + aElemKey.getAttribute("keycode"), + aElemKey.getAttribute("key") + ); + return elemString + key; + }, + + metaKeyIsCommandKey() { + return AppConstants.platform == "macosx"; + }, + + getModifierString(elemMod) { + let elemString = ""; + let haveCloverLeaf = false; + + if (elemMod.match("accel")) { + if (Services.appinfo.OS == "Darwin") { + haveCloverLeaf = true; + } else { + elemString += + lazy.PlatformKeys.GetStringFromName("VK_CONTROL") + + lazy.PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR"); + } + } + if (elemMod.match("access")) { + if (Services.appinfo.OS == "Darwin") { + elemString += + lazy.PlatformKeys.GetStringFromName("VK_CONTROL") + + lazy.PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR"); + } else { + elemString += + lazy.PlatformKeys.GetStringFromName("VK_ALT") + + lazy.PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR"); + } + } + if (elemMod.match("meta") && !this.metaKeyIsCommandKey()) { + elemString += + lazy.PlatformKeys.GetStringFromName("VK_COMMAND_OR_WIN") + + lazy.PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR"); + } + if (elemMod.match("shift")) { + elemString += + lazy.PlatformKeys.GetStringFromName("VK_SHIFT") + + lazy.PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR"); + } + if (elemMod.match("alt")) { + elemString += + lazy.PlatformKeys.GetStringFromName("VK_ALT") + + lazy.PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR"); + } + if (elemMod.match("ctrl") || elemMod.match("control")) { + elemString += + lazy.PlatformKeys.GetStringFromName("VK_CONTROL") + + lazy.PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR"); + } + if (elemMod.match("meta") && this.metaKeyIsCommandKey()) { + elemString += + lazy.PlatformKeys.GetStringFromName("VK_COMMAND_OR_WIN") + + lazy.PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR"); + } + + if (haveCloverLeaf) { + elemString += + lazy.PlatformKeys.GetStringFromName("VK_COMMAND_OR_WIN") + + lazy.PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR"); + } + + return elemString; + }, + + getKeyString(keyCode, keyAttribute) { + let key; + if (keyCode) { + keyCode = keyCode.toUpperCase(); + if (AppConstants.platform == "macosx") { + // Return fancy Unicode symbols for some keys. + switch (keyCode) { + case "VK_LEFT": + return "\u2190"; // U+2190 LEFTWARDS ARROW + case "VK_RIGHT": + return "\u2192"; // U+2192 RIGHTWARDS ARROW + } + } + try { + let bundle = keyCode == "VK_RETURN" ? lazy.PlatformKeys : lazy.Keys; + // Some keys might not exist in the locale file, which will throw. + key = bundle.GetStringFromName(keyCode); + } catch (ex) { + console.error("Error finding ", keyCode, ": ", ex); + key = keyCode.replace(/^VK_/, ""); + } + } else { + key = keyAttribute.toUpperCase(); + } + + return key; + }, + + getKeyAttribute(chromeKey) { + if (/^[A-Z]$/.test(chromeKey)) { + // We use the key attribute for single characters. + return ["key", chromeKey]; + } + return ["keycode", this.getKeycodeAttribute(chromeKey)]; + }, + + /** + * Determines the corresponding XUL keycode from the given chrome key. + * + * For example: + * + * input | output + * --------------------------------------- + * "PageUp" | "VK_PAGE_UP" + * "Delete" | "VK_DELETE" + * + * @param {string} chromeKey The chrome key (e.g. "PageUp", "Space", ...) + * @returns {string} The constructed value for the Key's 'keycode' attribute. + */ + getKeycodeAttribute(chromeKey) { + if (/^[0-9]/.test(chromeKey)) { + return `VK_${chromeKey}`; + } + return `VK${chromeKey.replace(/([A-Z])/g, "_$&").toUpperCase()}`; + }, + + findShortcut(aElemCommand) { + let document = aElemCommand.ownerDocument; + return document.querySelector( + 'key[command="' + aElemCommand.getAttribute("id") + '"]' + ); + }, + + chromeModifierKeyMap: { + Alt: "alt", + Command: "accel", + Ctrl: "accel", + MacCtrl: "control", + Shift: "shift", + }, + + /** + * Determines the corresponding XUL modifiers from the chrome modifiers. + * + * For example: + * + * input | output + * --------------------------------------- + * ["Ctrl", "Shift"] | "accel,shift" + * ["MacCtrl"] | "control" + * + * @param {Array} chromeModifiers The array of chrome modifiers. + * @returns {string} The constructed value for the Key's 'modifiers' attribute. + */ + getModifiersAttribute(chromeModifiers) { + return Array.from(chromeModifiers, modifier => { + return ShortcutUtils.chromeModifierKeyMap[modifier]; + }) + .sort() + .join(","); + }, + + /** + * Validate if a shortcut string is valid and return an error code if it + * isn't valid. + * + * For example: + * + * input | output + * --------------------------------------- + * "Ctrl+Shift+A" | IS_VALID + * "Shift+F" | MODIFIER_REQUIRED + * "Command+>" | INVALID_KEY + * + * @param {string} string The shortcut string. + * @returns {string} The code for the validation result. + */ + validate(string) { + // A valid shortcut key for a webextension manifest + const MEDIA_KEYS = + /^(MediaNextTrack|MediaPlayPause|MediaPrevTrack|MediaStop)$/; + const BASIC_KEYS = + /^([A-Z0-9]|Comma|Period|Home|End|PageUp|PageDown|Space|Insert|Delete|Up|Down|Left|Right)$/; + const FUNCTION_KEYS = /^(F[1-9]|F1[0-2])$/; + + if (MEDIA_KEYS.test(string.trim())) { + return this.IS_VALID; + } + + let modifiers = string.split("+").map(s => s.trim()); + let key = modifiers.pop(); + + let chromeModifiers = modifiers.map( + m => ShortcutUtils.chromeModifierKeyMap[m] + ); + // If the modifier wasn't found it will be undefined. + if (chromeModifiers.some(modifier => !modifier)) { + return this.INVALID_MODIFIER; + } + + switch (modifiers.length) { + case 0: + // A lack of modifiers is only allowed with function keys. + if (!FUNCTION_KEYS.test(key)) { + return this.MODIFIER_REQUIRED; + } + break; + case 1: + // Shift is only allowed on its own with function keys. + if (chromeModifiers[0] == "shift" && !FUNCTION_KEYS.test(key)) { + return this.MODIFIER_REQUIRED; + } + break; + case 2: + if (chromeModifiers[0] == chromeModifiers[1]) { + return this.DUPLICATE_MODIFIER; + } + break; + default: + return this.INVALID_COMBINATION; + } + + if (!BASIC_KEYS.test(key) && !FUNCTION_KEYS.test(key)) { + return this.INVALID_KEY; + } + + return this.IS_VALID; + }, + + /** + * Attempt to find a key for a given shortcut string, such as + * "Ctrl+Shift+A" and determine if it is a system shortcut. + * + * @param {Object} win The window to look for key elements in. + * @param {string} value The shortcut string. + * @returns {boolean} Whether a system shortcut was found or not. + */ + isSystem(win, value) { + let modifiers = value.split("+"); + let chromeKey = modifiers.pop(); + let modifiersString = this.getModifiersAttribute(modifiers); + let keycode = this.getKeycodeAttribute(chromeKey); + + let baseSelector = "key"; + if (modifiers.length) { + baseSelector += `[modifiers="${modifiersString}"]`; + } + + let keyEl = win.document.querySelector( + [ + `${baseSelector}[key="${chromeKey}"]`, + `${baseSelector}[key="${chromeKey.toLowerCase()}"]`, + `${baseSelector}[keycode="${keycode}"]`, + ].join(",") + ); + return keyEl && !keyEl.closest("keyset").id.startsWith("ext-keyset-id"); + }, + + /** + * Determine what action a KeyboardEvent should perform, if any. + * + * @param {KeyboardEvent} event The event to check for a related system action. + * @returns {string} A string identifying the action, or null if no action is found. + */ + // eslint-disable-next-line complexity + getSystemActionForEvent(event, { rtl } = {}) { + // On Windows, Win key state is not strictly checked so that we can ignore + // Win key state to check the other modifier state. + const meaningfulMetaKey = event.metaKey && AppConstants.platform != "win"; + // This is set to true only when the Meta key is accel key on the platform. + const accelMetaKey = event.metaKey && this.metaKeyIsCommandKey(); + switch (event.keyCode) { + case event.DOM_VK_TAB: + if (event.ctrlKey && !event.altKey && !meaningfulMetaKey) { + return ShortcutUtils.CYCLE_TABS; + } + break; + case event.DOM_VK_F7: + // shift + F7 is the default DevTools shortcut for the Style Editor. + if (!event.shiftKey) { + return ShortcutUtils.TOGGLE_CARET_BROWSING; + } + break; + case event.DOM_VK_PAGE_UP: + if ( + event.ctrlKey && + !event.shiftKey && + !event.altKey && + !meaningfulMetaKey + ) { + return ShortcutUtils.PREVIOUS_TAB; + } + if ( + event.ctrlKey && + event.shiftKey && + !event.altKey && + !meaningfulMetaKey + ) { + return ShortcutUtils.MOVE_TAB_BACKWARD; + } + break; + case event.DOM_VK_PAGE_DOWN: + if ( + event.ctrlKey && + !event.shiftKey && + !event.altKey && + !meaningfulMetaKey + ) { + return ShortcutUtils.NEXT_TAB; + } + if ( + event.ctrlKey && + event.shiftKey && + !event.altKey && + !meaningfulMetaKey + ) { + return ShortcutUtils.MOVE_TAB_FORWARD; + } + break; + case event.DOM_VK_LEFT: + if (accelMetaKey && event.altKey && !event.shiftKey && !event.ctrlKey) { + return ShortcutUtils.PREVIOUS_TAB; + } + break; + case event.DOM_VK_RIGHT: + if (accelMetaKey && event.altKey && !event.shiftKey && !event.ctrlKey) { + return ShortcutUtils.NEXT_TAB; + } + break; + } + + if (AppConstants.platform == "macosx") { + if (!event.altKey && event.metaKey) { + switch (event.charCode) { + case "}".charCodeAt(0): + if (rtl) { + return ShortcutUtils.PREVIOUS_TAB; + } + return ShortcutUtils.NEXT_TAB; + case "{".charCodeAt(0): + if (rtl) { + return ShortcutUtils.NEXT_TAB; + } + return ShortcutUtils.PREVIOUS_TAB; + } + } + } + // Not on Mac from now on. + if (AppConstants.platform != "macosx") { + if ( + event.ctrlKey && + !event.shiftKey && + event.keyCode == KeyEvent.DOM_VK_F4 + ) { + return ShortcutUtils.CLOSE_TAB; + } + } + + return null; + }, +}; + +Object.freeze(ShortcutUtils); diff --git a/toolkit/modules/Sqlite.sys.mjs b/toolkit/modules/Sqlite.sys.mjs new file mode 100644 index 0000000000..2c9de0f438 --- /dev/null +++ b/toolkit/modules/Sqlite.sys.mjs @@ -0,0 +1,2020 @@ +/* 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/. */ + +/** + * PRIVACY WARNING + * =============== + * + * Database file names can be exposed through telemetry and in crash reports on + * the https://crash-stats.mozilla.org site, to allow recognizing the affected + * database. + * if your database name may contain privacy sensitive information, e.g. an + * URL origin, you should use openDatabaseWithFileURL and pass an explicit + * TelemetryFilename to it. That name will be used both for telemetry and for + * thread names in crash reports. + * If you have different needs (e.g. using the javascript module or an async + * connection from the main thread) please coordinate with the mozStorage peers. + */ + +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +import { setTimeout } from "resource://gre/modules/Timer.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs", + FileUtils: "resource://gre/modules/FileUtils.sys.mjs", + Log: "resource://gre/modules/Log.sys.mjs", +}); + +XPCOMUtils.defineLazyServiceGetter( + lazy, + "FinalizationWitnessService", + "@mozilla.org/toolkit/finalizationwitness;1", + "nsIFinalizationWitnessService" +); + +// Regular expression used by isInvalidBoundLikeQuery +var likeSqlRegex = /\bLIKE\b\s(?![@:?])/i; + +// Counts the number of created connections per database basename(). This is +// used for logging to distinguish connection instances. +var connectionCounters = new Map(); + +// Tracks identifiers of wrapped connections, that are Storage connections +// opened through mozStorage and then wrapped by Sqlite.sys.mjs to use its syntactic +// sugar API. Since these connections have an unknown origin, we use this set +// to differentiate their behavior. +var wrappedConnections = new Set(); + +/** + * Once `true`, reject any attempt to open or close a database. + */ +function isClosed() { + // If Barriers have not been initialized yet, just trust AppStartup. + if ( + typeof Object.getOwnPropertyDescriptor(lazy, "Barriers").get == "function" + ) { + // It's still possible to open new connections at profile-before-change, so + // use the next phase here, as a fallback. + return Services.startup.isInOrBeyondShutdownPhase( + Ci.nsIAppStartup.SHUTDOWN_PHASE_XPCOMWILLSHUTDOWN + ); + } + return lazy.Barriers.shutdown.client.isClosed; +} + +var Debugging = { + // Tests should fail if a connection auto closes. The exception is + // when finalization itself is tested, in which case this flag + // should be set to false. + failTestsOnAutoClose: true, +}; + +/** + * Helper function to check whether LIKE is implemented using proper bindings. + * + * @param sql + * (string) The SQL query to be verified. + * @return boolean value telling us whether query was correct or not + */ +function isInvalidBoundLikeQuery(sql) { + return likeSqlRegex.test(sql); +} + +// Displays a script error message +function logScriptError(message) { + let consoleMessage = Cc["@mozilla.org/scripterror;1"].createInstance( + Ci.nsIScriptError + ); + let stack = new Error(); + consoleMessage.init( + message, + stack.fileName, + null, + stack.lineNumber, + 0, + Ci.nsIScriptError.errorFlag, + "component javascript" + ); + Services.console.logMessage(consoleMessage); + + // This `Promise.reject` will cause tests to fail. The debugging + // flag can be used to suppress this for tests that explicitly + // test auto closes. + if (Debugging.failTestsOnAutoClose) { + Promise.reject(new Error(message)); + } +} + +/** + * Gets connection identifier from its database file name. + * + * @param fileName + * A database file string name. + * @return the connection identifier. + */ +function getIdentifierByFileName(fileName) { + let number = connectionCounters.get(fileName) || 0; + connectionCounters.set(fileName, number + 1); + return fileName + "#" + number; +} + +/** + * Convert mozIStorageError to common NS_ERROR_* + * The conversion is mostly based on the one in + * mozStoragePrivateHelpers::ConvertResultCode, plus a few additions. + * + * @param {integer} result a mozIStorageError result code. + * @returns {integer} an NS_ERROR_* result code. + */ +function convertStorageErrorResult(result) { + switch (result) { + case Ci.mozIStorageError.PERM: + case Ci.mozIStorageError.AUTH: + case Ci.mozIStorageError.CANTOPEN: + return Cr.NS_ERROR_FILE_ACCESS_DENIED; + case Ci.mozIStorageError.LOCKED: + return Cr.NS_ERROR_FILE_IS_LOCKED; + case Ci.mozIStorageError.READONLY: + return Cr.NS_ERROR_FILE_READ_ONLY; + case Ci.mozIStorageError.ABORT: + case Ci.mozIStorageError.INTERRUPT: + return Cr.NS_ERROR_ABORT; + case Ci.mozIStorageError.TOOBIG: + case Ci.mozIStorageError.FULL: + return Cr.NS_ERROR_FILE_NO_DEVICE_SPACE; + case Ci.mozIStorageError.NOMEM: + return Cr.NS_ERROR_OUT_OF_MEMORY; + case Ci.mozIStorageError.BUSY: + return Cr.NS_ERROR_STORAGE_BUSY; + case Ci.mozIStorageError.CONSTRAINT: + return Cr.NS_ERROR_STORAGE_CONSTRAINT; + case Ci.mozIStorageError.NOLFS: + case Ci.mozIStorageError.IOERR: + return Cr.NS_ERROR_STORAGE_IOERR; + case Ci.mozIStorageError.SCHEMA: + case Ci.mozIStorageError.MISMATCH: + case Ci.mozIStorageError.MISUSE: + case Ci.mozIStorageError.RANGE: + return Ci.NS_ERROR_UNEXPECTED; + case Ci.mozIStorageError.CORRUPT: + case Ci.mozIStorageError.EMPTY: + case Ci.mozIStorageError.FORMAT: + case Ci.mozIStorageError.NOTADB: + return Cr.NS_ERROR_FILE_CORRUPTED; + default: + return Cr.NS_ERROR_FAILURE; + } +} +/** + * Barriers used to ensure that Sqlite.sys.mjs is shutdown after all + * its clients. + */ +ChromeUtils.defineLazyGetter(lazy, "Barriers", () => { + let Barriers = { + /** + * Public barrier that clients may use to add blockers to the + * shutdown of Sqlite.sys.mjs. Triggered by profile-before-change. + * Once all blockers of this barrier are lifted, we close the + * ability to open new connections. + */ + shutdown: new lazy.AsyncShutdown.Barrier( + "Sqlite.sys.mjs: wait until all clients have completed their task" + ), + + /** + * Private barrier blocked by connections that are still open. + * Triggered after Barriers.shutdown is lifted and `isClosed()` returns + * `true`. + */ + connections: new lazy.AsyncShutdown.Barrier( + "Sqlite.sys.mjs: wait until all connections are closed" + ), + }; + + /** + * Observer for the event which is broadcasted when the finalization + * witness `_witness` of `OpenedConnection` is garbage collected. + * + * The observer is passed the connection identifier of the database + * connection that is being finalized. + */ + let finalizationObserver = function (subject, topic, identifier) { + let connectionData = ConnectionData.byId.get(identifier); + + if (connectionData === undefined) { + logScriptError( + "Error: Attempt to finalize unknown Sqlite connection: " + + identifier + + "\n" + ); + return; + } + + ConnectionData.byId.delete(identifier); + logScriptError( + "Warning: Sqlite connection '" + + identifier + + "' was not properly closed. Auto-close triggered by garbage collection.\n" + ); + connectionData.close(); + }; + Services.obs.addObserver(finalizationObserver, "sqlite-finalization-witness"); + + /** + * Ensure that Sqlite.sys.mjs: + * - informs its clients before shutting down; + * - lets clients open connections during shutdown, if necessary; + * - waits for all connections to be closed before shutdown. + */ + lazy.AsyncShutdown.profileBeforeChange.addBlocker( + "Sqlite.sys.mjs shutdown blocker", + async function () { + await Barriers.shutdown.wait(); + // At this stage, all clients have had a chance to open (and close) + // their databases. Some previous close operations may still be pending, + // so we need to wait until they are complete before proceeding. + await Barriers.connections.wait(); + + // Everything closed, no finalization events to catch + Services.obs.removeObserver( + finalizationObserver, + "sqlite-finalization-witness" + ); + }, + + function status() { + if (isClosed()) { + // We are waiting for the connections to close. The interesting + // status is therefore the list of connections still pending. + return { + description: "Waiting for connections to close", + state: Barriers.connections.state, + }; + } + + // We are still in the first stage: waiting for the barrier + // to be lifted. The interesting status is therefore that of + // the barrier. + return { + description: "Waiting for the barrier to be lifted", + state: Barriers.shutdown.state, + }; + } + ); + + return Barriers; +}); + +const VACUUM_CATEGORY = "vacuum-participant"; +const VACUUM_CONTRACTID = "@sqlite.module.js/vacuum-participant;"; +var registeredVacuumParticipants = new Map(); + +function registerVacuumParticipant(connectionData) { + let contractId = VACUUM_CONTRACTID + connectionData._identifier; + let factory = { + createInstance(iid) { + return connectionData.QueryInterface(iid); + }, + QueryInterface: ChromeUtils.generateQI(["nsIFactory"]), + }; + let cid = Services.uuid.generateUUID(); + Components.manager + .QueryInterface(Ci.nsIComponentRegistrar) + .registerFactory(cid, contractId, contractId, factory); + Services.catMan.addCategoryEntry( + VACUUM_CATEGORY, + contractId, + contractId, + false, + false + ); + registeredVacuumParticipants.set(contractId, { cid, factory }); +} + +function unregisterVacuumParticipant(connectionData) { + let contractId = VACUUM_CONTRACTID + connectionData._identifier; + let component = registeredVacuumParticipants.get(contractId); + if (component) { + Components.manager + .QueryInterface(Ci.nsIComponentRegistrar) + .unregisterFactory(component.cid, component.factory); + Services.catMan.deleteCategoryEntry(VACUUM_CATEGORY, contractId, false); + } +} + +/** + * Connection data with methods necessary for closing the connection. + * + * To support auto-closing in the event of garbage collection, this + * data structure contains all the connection data of an opened + * connection and all of the methods needed for sucessfully closing + * it. + * + * By putting this information in its own separate object, it is + * possible to store an additional reference to it without preventing + * a garbage collection of a finalization witness in + * OpenedConnection. When the witness detects a garbage collection, + * this object can be used to close the connection. + * + * This object contains more methods than just `close`. When + * OpenedConnection needs to use the methods in this object, it will + * dispatch its method calls here. + */ +function ConnectionData(connection, identifier, options = {}) { + this._log = lazy.Log.repository.getLoggerWithMessagePrefix( + "Sqlite.sys.mjs", + `Connection ${identifier}: ` + ); + this._log.manageLevelFromPref("toolkit.sqlitejsm.loglevel"); + this._log.debug("Opened"); + + this._dbConn = connection; + + // This is a unique identifier for the connection, generated through + // getIdentifierByFileName. It may be used for logging or as a key in Maps. + this._identifier = identifier; + + this._open = true; + + this._cachedStatements = new Map(); + this._anonymousStatements = new Map(); + this._anonymousCounter = 0; + + // A map from statement index to mozIStoragePendingStatement, to allow for + // canceling prior to finalizing the mozIStorageStatements. + this._pendingStatements = new Map(); + + // Increments for each executed statement for the life of the connection. + this._statementCounter = 0; + + // Increments whenever we request a unique operation id. + this._operationsCounter = 0; + + if ("defaultTransactionType" in options) { + this.defaultTransactionType = options.defaultTransactionType; + } else { + this.defaultTransactionType = convertStorageTransactionType( + this._dbConn.defaultTransactionType + ); + } + // Tracks whether this instance initiated a transaction. + this._initiatedTransaction = false; + // Manages a chain of transactions promises, so that new transactions + // always happen in queue to the previous ones. It never rejects. + this._transactionQueue = Promise.resolve(); + + this._idleShrinkMS = options.shrinkMemoryOnConnectionIdleMS; + if (this._idleShrinkMS) { + this._idleShrinkTimer = Cc["@mozilla.org/timer;1"].createInstance( + Ci.nsITimer + ); + // We wait for the first statement execute to start the timer because + // shrinking now would not do anything. + } + + // Deferred whose promise is resolved when the connection closing procedure + // is complete. + this._deferredClose = Promise.withResolvers(); + this._closeRequested = false; + + // An AsyncShutdown barrier used to make sure that we wait until clients + // are done before shutting down the connection. + this._barrier = new lazy.AsyncShutdown.Barrier( + `${this._identifier}: waiting for clients` + ); + + lazy.Barriers.connections.client.addBlocker( + this._identifier + ": waiting for shutdown", + this._deferredClose.promise, + () => ({ + identifier: this._identifier, + isCloseRequested: this._closeRequested, + hasDbConn: !!this._dbConn, + initiatedTransaction: this._initiatedTransaction, + pendingStatements: this._pendingStatements.size, + statementCounter: this._statementCounter, + }) + ); + + // We avoid creating a timer for every transaction, because in most cases they + // are not canceled and they are only used as a timeout. + // Instead the timer is reused when it's sufficiently close to the previous + // creation time (see `_getTimeoutPromise` for more info). + this._timeoutPromise = null; + // The last timestamp when we should consider using `this._timeoutPromise`. + this._timeoutPromiseExpires = 0; + + this._useIncrementalVacuum = !!options.incrementalVacuum; + if (this._useIncrementalVacuum) { + this._log.debug("Set auto_vacuum INCREMENTAL"); + this.execute("PRAGMA auto_vacuum = 2").catch(ex => { + this._log.error("Setting auto_vacuum to INCREMENTAL failed."); + console.error(ex); + }); + } + + this._expectedPageSize = options.pageSize ?? 0; + if (this._expectedPageSize) { + this._log.debug("Set page_size to " + this._expectedPageSize); + this.execute("PRAGMA page_size = " + this._expectedPageSize).catch(ex => { + this._log.error(`Setting page_size to ${this._expectedPageSize} failed.`); + console.error(ex); + }); + } + + this._vacuumOnIdle = options.vacuumOnIdle; + if (this._vacuumOnIdle) { + this._log.debug("Register as vacuum participant"); + this.QueryInterface = ChromeUtils.generateQI([ + Ci.mozIStorageVacuumParticipant, + ]); + registerVacuumParticipant(this); + } +} + +/** + * Map of connection identifiers to ConnectionData objects + * + * The connection identifier is a human-readable name of the + * database. Used by finalization witnesses to be able to close opened + * connections on garbage collection. + * + * Key: _identifier of ConnectionData + * Value: ConnectionData object + */ +ConnectionData.byId = new Map(); + +ConnectionData.prototype = Object.freeze({ + get expectedDatabasePageSize() { + return this._expectedPageSize; + }, + + get useIncrementalVacuum() { + return this._useIncrementalVacuum; + }, + + /** + * This should only be used by the VacuumManager component. + * @see unsafeRawConnection for an official (but still unsafe) API. + */ + get databaseConnection() { + if (this._vacuumOnIdle) { + return this._dbConn; + } + return null; + }, + + onBeginVacuum() { + let granted = !this.transactionInProgress; + this._log.debug("Begin Vacuum - " + granted ? "granted" : "denied"); + return granted; + }, + + onEndVacuum(succeeded) { + this._log.debug("End Vacuum - " + succeeded ? "success" : "failure"); + }, + + /** + * Run a task, ensuring that its execution will not be interrupted by shutdown. + * + * As the operations of this module are asynchronous, a sequence of operations, + * or even an individual operation, can still be pending when the process shuts + * down. If any of this operations is a write, this can cause data loss, simply + * because the write has not been completed (or even started) by shutdown. + * + * To avoid this risk, clients are encouraged to use `executeBeforeShutdown` for + * any write operation, as follows: + * + * myConnection.executeBeforeShutdown("Bookmarks: Removing a bookmark", + * async function(db) { + * // The connection will not be closed and shutdown will not proceed + * // until this task has completed. + * + * // `db` exposes the same API as `myConnection` but provides additional + * // logging support to help debug hard-to-catch shutdown timeouts. + * + * await db.execute(...); + * })); + * + * @param {string} name A human-readable name for the ongoing operation, used + * for logging and debugging purposes. + * @param {function(db)} task A function that takes as argument a Sqlite.sys.mjs + * db and returns a Promise. + */ + executeBeforeShutdown(parent, name, task) { + if (!name) { + throw new TypeError("Expected a human-readable name as first argument"); + } + if (typeof task != "function") { + throw new TypeError("Expected a function as second argument"); + } + if (this._closeRequested) { + throw new Error( + `${this._identifier}: cannot execute operation ${name}, the connection is already closing` + ); + } + + // Status, used for AsyncShutdown crash reports. + let status = { + // The latest command started by `task`, either as a + // sql string, or as one of "<not started>" or "<closing>". + command: "<not started>", + + // `true` if `command` was started but not completed yet. + isPending: false, + }; + + // An object with the same API as `this` but with + // additional logging. To keep logging simple, we + // assume that `task` is not running several queries + // concurrently. + let loggedDb = Object.create(parent, { + execute: { + value: async (sql, ...rest) => { + status.isPending = true; + status.command = sql; + try { + return await this.execute(sql, ...rest); + } finally { + status.isPending = false; + } + }, + }, + close: { + value: async () => { + status.isPending = true; + status.command = "<close>"; + try { + return await this.close(); + } finally { + status.isPending = false; + } + }, + }, + executeCached: { + value: async (sql, ...rest) => { + status.isPending = true; + status.command = "cached: " + sql; + try { + return await this.executeCached(sql, ...rest); + } finally { + status.isPending = false; + } + }, + }, + }); + + let promiseResult = task(loggedDb); + if ( + !promiseResult || + typeof promiseResult != "object" || + !("then" in promiseResult) + ) { + throw new TypeError("Expected a Promise"); + } + let key = `${this._identifier}: ${name} (${this._getOperationId()})`; + let promiseComplete = promiseResult.catch(() => {}); + this._barrier.client.addBlocker(key, promiseComplete, { + fetchState: () => status, + }); + + return (async () => { + try { + return await promiseResult; + } finally { + this._barrier.client.removeBlocker(key, promiseComplete); + } + })(); + }, + close() { + this._closeRequested = true; + + if (!this._dbConn) { + return this._deferredClose.promise; + } + + this._log.debug("Request to close connection."); + this._clearIdleShrinkTimer(); + + if (this._vacuumOnIdle) { + this._log.debug("Unregister as vacuum participant"); + unregisterVacuumParticipant(this); + } + + return this._barrier.wait().then(() => { + if (!this._dbConn) { + return undefined; + } + return this._finalize(); + }); + }, + + clone(readOnly = false) { + this.ensureOpen(); + + this._log.debug("Request to clone connection."); + + let options = { + connection: this._dbConn, + readOnly, + }; + if (this._idleShrinkMS) { + options.shrinkMemoryOnConnectionIdleMS = this._idleShrinkMS; + } + + return cloneStorageConnection(options); + }, + _getOperationId() { + return this._operationsCounter++; + }, + _finalize() { + this._log.debug("Finalizing connection."); + // Cancel any pending statements. + for (let [, /* k */ statement] of this._pendingStatements) { + statement.cancel(); + } + this._pendingStatements.clear(); + + // We no longer need to track these. + this._statementCounter = 0; + + // Next we finalize all active statements. + for (let [, /* k */ statement] of this._anonymousStatements) { + statement.finalize(); + } + this._anonymousStatements.clear(); + + for (let [, /* k */ statement] of this._cachedStatements) { + statement.finalize(); + } + this._cachedStatements.clear(); + + // This guards against operations performed between the call to this + // function and asyncClose() finishing. See also bug 726990. + this._open = false; + + // We must always close the connection at the Sqlite.sys.mjs-level, not + // necessarily at the mozStorage-level. + let markAsClosed = () => { + this._log.debug("Closed"); + // Now that the connection is closed, no need to keep + // a blocker for Barriers.connections. + lazy.Barriers.connections.client.removeBlocker( + this._deferredClose.promise + ); + this._deferredClose.resolve(); + }; + if (wrappedConnections.has(this._identifier)) { + wrappedConnections.delete(this._identifier); + this._dbConn = null; + markAsClosed(); + } else { + this._log.debug("Calling asyncClose()."); + try { + this._dbConn.asyncClose(markAsClosed); + } catch (ex) { + // If for any reason asyncClose fails, we must still remove the + // shutdown blockers and resolve _deferredClose. + markAsClosed(); + } finally { + this._dbConn = null; + } + } + return this._deferredClose.promise; + }, + + executeCached(sql, params = null, onRow = null) { + this.ensureOpen(); + + if (!sql) { + throw new Error("sql argument is empty."); + } + + let statement = this._cachedStatements.get(sql); + if (!statement) { + statement = this._dbConn.createAsyncStatement(sql); + this._cachedStatements.set(sql, statement); + } + + this._clearIdleShrinkTimer(); + + return new Promise((resolve, reject) => { + try { + this._executeStatement(sql, statement, params, onRow).then( + result => { + this._startIdleShrinkTimer(); + resolve(result); + }, + error => { + this._startIdleShrinkTimer(); + reject(error); + } + ); + } catch (ex) { + this._startIdleShrinkTimer(); + throw ex; + } + }); + }, + + execute(sql, params = null, onRow = null) { + if (typeof sql != "string") { + throw new Error("Must define SQL to execute as a string: " + sql); + } + + this.ensureOpen(); + + let statement = this._dbConn.createAsyncStatement(sql); + let index = this._anonymousCounter++; + + this._anonymousStatements.set(index, statement); + this._clearIdleShrinkTimer(); + + let onFinished = () => { + this._anonymousStatements.delete(index); + statement.finalize(); + this._startIdleShrinkTimer(); + }; + + return new Promise((resolve, reject) => { + try { + this._executeStatement(sql, statement, params, onRow).then( + rows => { + onFinished(); + resolve(rows); + }, + error => { + onFinished(); + reject(error); + } + ); + } catch (ex) { + onFinished(); + throw ex; + } + }); + }, + + get transactionInProgress() { + return this._open && this._dbConn.transactionInProgress; + }, + + executeTransaction(func, type) { + // Identify the caller for debugging purposes. + let caller = new Error().stack + .split("\n", 3) + .pop() + .match(/^([^@]*@).*\/([^\/:]+)[:0-9]*$/); + caller = caller[1] + caller[2]; + this._log.debug(`Transaction (type ${type}) requested by: ${caller}`); + + if (type == OpenedConnection.prototype.TRANSACTION_DEFAULT) { + type = this.defaultTransactionType; + } else if (!OpenedConnection.TRANSACTION_TYPES.includes(type)) { + throw new Error("Unknown transaction type: " + type); + } + this.ensureOpen(); + + // If a transaction yields on a never resolved promise, or is mistakenly + // nested, it could hang the transactions queue forever. Thus we timeout + // the execution after a meaningful amount of time, to ensure in any case + // we'll proceed after a while. + let timeoutPromise = this._getTimeoutPromise(); + + let promise = this._transactionQueue.then(() => { + if (this._closeRequested) { + throw new Error("Transaction canceled due to a closed connection."); + } + + let transactionPromise = (async () => { + // At this point we should never have an in progress transaction, since + // they are enqueued. + if (this._initiatedTransaction) { + this._log.error( + "Unexpected transaction in progress when trying to start a new one." + ); + } + try { + // We catch errors in statement execution to detect nested transactions. + try { + await this.execute("BEGIN " + type + " TRANSACTION"); + this._log.debug(`Begin transaction`); + this._initiatedTransaction = true; + } catch (ex) { + // Unfortunately, if we are wrapping an existing connection, a + // transaction could have been started by a client of the same + // connection that doesn't use Sqlite.sys.mjs (e.g. C++ consumer). + // The best we can do is proceed without a transaction and hope + // things won't break. + if (wrappedConnections.has(this._identifier)) { + this._log.warn( + "A new transaction could not be started cause the wrapped connection had one in progress", + ex + ); + } else { + this._log.warn( + "A transaction was already in progress, likely a nested transaction", + ex + ); + throw ex; + } + } + + let result; + try { + result = await Promise.race([func(), timeoutPromise]); + } catch (ex) { + // It's possible that the exception has been caused by trying to + // close the connection in the middle of a transaction. + if (this._closeRequested) { + this._log.warn( + "Connection closed while performing a transaction", + ex + ); + } else { + // Otherwise the function didn't resolve before the timeout, or + // generated an unexpected error. Then we rollback. + if (ex.becauseTimedOut) { + let caller_module = caller.split(":", 1)[0]; + Services.telemetry.keyedScalarAdd( + "mozstorage.sqlitejsm_transaction_timeout", + caller_module, + 1 + ); + this._log.error( + `The transaction requested by ${caller} timed out. Rolling back`, + ex + ); + } else { + this._log.error( + `Error during transaction requested by ${caller}. Rolling back`, + ex + ); + } + // If we began a transaction, we must rollback it. + if (this._initiatedTransaction) { + try { + await this.execute("ROLLBACK TRANSACTION"); + this._initiatedTransaction = false; + this._log.debug(`Roll back transaction`); + } catch (inner) { + this._log.error("Could not roll back transaction", inner); + } + } + } + // Rethrow the exception. + throw ex; + } + + // See comment above about connection being closed during transaction. + if (this._closeRequested) { + this._log.warn( + "Connection closed before committing the transaction." + ); + throw new Error( + "Connection closed before committing the transaction." + ); + } + + // If we began a transaction, we must commit it. + if (this._initiatedTransaction) { + try { + await this.execute("COMMIT TRANSACTION"); + this._log.debug(`Commit transaction`); + } catch (ex) { + this._log.warn("Error committing transaction", ex); + throw ex; + } + } + + return result; + } finally { + this._initiatedTransaction = false; + } + })(); + + return Promise.race([transactionPromise, timeoutPromise]); + }); + // Atomically update the queue before anyone else has a chance to enqueue + // further transactions. + this._transactionQueue = promise.catch(ex => { + this._log.error(ex); + }); + + // Make sure that we do not shutdown the connection during a transaction. + this._barrier.client.addBlocker( + `Transaction (${this._getOperationId()})`, + this._transactionQueue + ); + return promise; + }, + + shrinkMemory() { + this._log.debug("Shrinking memory usage."); + return this.execute("PRAGMA shrink_memory").finally(() => { + this._clearIdleShrinkTimer(); + }); + }, + + discardCachedStatements() { + let count = 0; + for (let [, /* k */ statement] of this._cachedStatements) { + ++count; + statement.finalize(); + } + this._cachedStatements.clear(); + this._log.debug("Discarded " + count + " cached statements."); + return count; + }, + + interrupt() { + this._log.debug("Trying to interrupt."); + this.ensureOpen(); + this._dbConn.interrupt(); + }, + + /** + * Helper method to bind parameters of various kinds through + * reflection. + */ + _bindParameters(statement, params) { + if (!params) { + return; + } + + function bindParam(obj, key, val) { + let isBlob = + val && typeof val == "object" && val.constructor.name == "Uint8Array"; + let args = [key, val]; + if (isBlob) { + args.push(val.length); + } + let methodName = `bind${isBlob ? "Blob" : ""}By${ + typeof key == "number" ? "Index" : "Name" + }`; + obj[methodName](...args); + } + + if (Array.isArray(params)) { + // It's an array of separate params. + if (params.length && typeof params[0] == "object" && params[0] !== null) { + let paramsArray = statement.newBindingParamsArray(); + for (let p of params) { + let bindings = paramsArray.newBindingParams(); + for (let [key, value] of Object.entries(p)) { + bindParam(bindings, key, value); + } + paramsArray.addParams(bindings); + } + + statement.bindParameters(paramsArray); + return; + } + + // Indexed params. + for (let i = 0; i < params.length; i++) { + bindParam(statement, i, params[i]); + } + return; + } + + // Named params. + if (params && typeof params == "object") { + for (let k in params) { + bindParam(statement, k, params[k]); + } + return; + } + + throw new Error( + "Invalid type for bound parameters. Expected Array or " + + "object. Got: " + + params + ); + }, + + _executeStatement(sql, statement, params, onRow) { + if (statement.state != statement.MOZ_STORAGE_STATEMENT_READY) { + throw new Error("Statement is not ready for execution."); + } + + if (onRow && typeof onRow != "function") { + throw new Error("onRow must be a function. Got: " + onRow); + } + + this._bindParameters(statement, params); + + let index = this._statementCounter++; + + let deferred = Promise.withResolvers(); + let userCancelled = false; + let errors = []; + let rows = []; + let handledRow = false; + + // Don't incur overhead for serializing params unless the messages go + // somewhere. + if (this._log.level <= lazy.Log.Level.Trace) { + let msg = "Stmt #" + index + " " + sql; + + if (params) { + msg += " - " + JSON.stringify(params); + } + this._log.trace(msg); + } else { + this._log.debug("Stmt #" + index + " starting"); + } + + let self = this; + let pending = statement.executeAsync({ + handleResult(resultSet) { + // .cancel() may not be immediate and handleResult() could be called + // after a .cancel(). + for ( + let row = resultSet.getNextRow(); + row && !userCancelled; + row = resultSet.getNextRow() + ) { + if (!onRow) { + rows.push(row); + continue; + } + + handledRow = true; + + try { + onRow(row, () => { + userCancelled = true; + pending.cancel(); + }); + } catch (e) { + self._log.warn("Exception when calling onRow callback", e); + } + } + }, + + handleError(error) { + self._log.warn( + "Error when executing SQL (" + error.result + "): " + error.message + ); + errors.push(error); + }, + + handleCompletion(reason) { + self._log.debug("Stmt #" + index + " finished."); + self._pendingStatements.delete(index); + + switch (reason) { + case Ci.mozIStorageStatementCallback.REASON_FINISHED: + case Ci.mozIStorageStatementCallback.REASON_CANCELED: + // If there is an onRow handler, we always instead resolve to a + // boolean indicating whether the onRow handler was called or not. + let result = onRow ? handledRow : rows; + deferred.resolve(result); + break; + + case Ci.mozIStorageStatementCallback.REASON_ERROR: + let error = new Error( + "Error(s) encountered during statement execution: " + + errors.map(e => e.message).join(", ") + ); + error.errors = errors; + + // Forward the error result. + // Corruption is the most critical one so it's handled apart. + if (errors.some(e => e.result == Ci.mozIStorageError.CORRUPT)) { + error.result = Cr.NS_ERROR_FILE_CORRUPTED; + } else { + // Just use the first error result in the other cases. + error.result = convertStorageErrorResult(errors[0]?.result); + } + + deferred.reject(error); + break; + + default: + deferred.reject( + new Error("Unknown completion reason code: " + reason) + ); + break; + } + }, + }); + + this._pendingStatements.set(index, pending); + return deferred.promise; + }, + + ensureOpen() { + if (!this._open) { + throw new Error("Connection is not open."); + } + }, + + _clearIdleShrinkTimer() { + if (!this._idleShrinkTimer) { + return; + } + + this._idleShrinkTimer.cancel(); + }, + + _startIdleShrinkTimer() { + if (!this._idleShrinkTimer) { + return; + } + + this._idleShrinkTimer.initWithCallback( + this.shrinkMemory.bind(this), + this._idleShrinkMS, + this._idleShrinkTimer.TYPE_ONE_SHOT + ); + }, + + /** + * Returns a promise that will resolve after a time comprised between 80% of + * `TRANSACTIONS_TIMEOUT_MS` and `TRANSACTIONS_TIMEOUT_MS`. Use + * this method instead of creating several individual timers that may survive + * longer than necessary. + */ + _getTimeoutPromise() { + if (this._timeoutPromise && Cu.now() <= this._timeoutPromiseExpires) { + return this._timeoutPromise; + } + let timeoutPromise = new Promise((resolve, reject) => { + setTimeout(() => { + // Clear out this._timeoutPromise if it hasn't changed since we set it. + if (this._timeoutPromise == timeoutPromise) { + this._timeoutPromise = null; + } + let e = new Error( + "Transaction timeout, most likely caused by unresolved pending work." + ); + e.becauseTimedOut = true; + reject(e); + }, Sqlite.TRANSACTIONS_TIMEOUT_MS); + }); + this._timeoutPromise = timeoutPromise; + this._timeoutPromiseExpires = + Cu.now() + Sqlite.TRANSACTIONS_TIMEOUT_MS * 0.2; + return this._timeoutPromise; + }, + + /** + * Asynchronously makes a copy of the SQLite database while there may still be + * open connections on it. + * + * @param {string} destFilePath + * The path on the local filesystem to write the database copy. Any existing + * file at this path will be overwritten. + * @return Promise<undefined, nsresult> + */ + async backupToFile(destFilePath) { + if (!this._dbConn) { + return Promise.reject( + new Error("No opened database connection to create a backup from.") + ); + } + let destFile = await IOUtils.getFile(destFilePath); + return new Promise((resolve, reject) => { + this._dbConn.backupToFileAsync(destFile, result => { + if (Components.isSuccessCode(result)) { + resolve(); + } else { + reject(result); + } + }); + }); + }, +}); + +/** + * Opens a connection to a SQLite database. + * + * The following parameters can control the connection: + * + * path -- (string) The filesystem path of the database file to open. If the + * file does not exist, a new database will be created. + * + * sharedMemoryCache -- (bool) Whether multiple connections to the database + * share the same memory cache. Sharing the memory cache likely results + * in less memory utilization. However, sharing also requires connections + * to obtain a lock, possibly making database access slower. Defaults to + * true. + * + * shrinkMemoryOnConnectionIdleMS -- (integer) If defined, the connection + * will attempt to minimize its memory usage after this many + * milliseconds of connection idle. The connection is idle when no + * statements are executing. There is no default value which means no + * automatic memory minimization will occur. Please note that this is + * *not* a timer on the idle service and this could fire while the + * application is active. + * + * readOnly -- (bool) Whether to open the database with SQLITE_OPEN_READONLY + * set. If used, writing to the database will fail. Defaults to false. + * + * ignoreLockingMode -- (bool) Whether to ignore locks on the database held + * by other connections. If used, implies readOnly. Defaults to false. + * USE WITH EXTREME CAUTION. This mode WILL produce incorrect results or + * return "false positive" corruption errors if other connections write + * to the DB at the same time. + * + * vacuumOnIdle -- (bool) Whether to register this connection to be vacuumed + * on idle by the VacuumManager component. + * If you're vacuum-ing an incremental vacuum database, ensure to also + * set incrementalVacuum to true, otherwise this will try to change it + * to full vacuum mode. + * + * incrementalVacuum -- (bool) if set to true auto_vacuum = INCREMENTAL will + * be enabled for the database. + * Changing auto vacuum of an already populated database requires a full + * VACUUM. You can evaluate to enable vacuumOnIdle for that. + * + * pageSize -- (integer) This allows to set a custom page size for the + * database. It is usually not necessary to set it, since the default + * value should be good for most consumers. + * Changing the page size of an already populated database requires a full + * VACUUM. You can evaluate to enable vacuumOnIdle for that. + * + * testDelayedOpenPromise -- (promise) Used by tests to delay the open + * callback handling and execute code between asyncOpen and its callback. + * + * FUTURE options to control: + * + * special named databases + * pragma TEMP STORE = MEMORY + * TRUNCATE JOURNAL + * SYNCHRONOUS = full + * + * @param options + * (Object) Parameters to control connection and open options. + * + * @return Promise<OpenedConnection> + */ +function openConnection(options) { + let log = lazy.Log.repository.getLoggerWithMessagePrefix( + "Sqlite.sys.mjs", + `ConnectionOpener: ` + ); + log.manageLevelFromPref("toolkit.sqlitejsm.loglevel"); + + if (!options.path) { + throw new Error("path not specified in connection options."); + } + + if (isClosed()) { + throw new Error( + "Sqlite.sys.mjs has been shutdown. Cannot open connection to: " + + options.path + ); + } + + // Retains absolute paths and normalizes relative as relative to profile. + let path = options.path; + let file; + try { + file = lazy.FileUtils.File(path); + } catch (ex) { + // For relative paths, we will get an exception from trying to initialize + // the file. We must then join this path to the profile directory. + if (ex.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH) { + path = PathUtils.joinRelative( + Services.dirsvc.get("ProfD", Ci.nsIFile).path, + options.path + ); + file = lazy.FileUtils.File(path); + } else { + throw ex; + } + } + + let sharedMemoryCache = + "sharedMemoryCache" in options ? options.sharedMemoryCache : true; + + let openedOptions = {}; + + if ("shrinkMemoryOnConnectionIdleMS" in options) { + if (!Number.isInteger(options.shrinkMemoryOnConnectionIdleMS)) { + throw new Error( + "shrinkMemoryOnConnectionIdleMS must be an integer. " + + "Got: " + + options.shrinkMemoryOnConnectionIdleMS + ); + } + + openedOptions.shrinkMemoryOnConnectionIdleMS = + options.shrinkMemoryOnConnectionIdleMS; + } + + if ("defaultTransactionType" in options) { + let defaultTransactionType = options.defaultTransactionType; + if (!OpenedConnection.TRANSACTION_TYPES.includes(defaultTransactionType)) { + throw new Error( + "Unknown default transaction type: " + defaultTransactionType + ); + } + + openedOptions.defaultTransactionType = defaultTransactionType; + } + + if ("vacuumOnIdle" in options) { + if (typeof options.vacuumOnIdle != "boolean") { + throw new Error("Invalid vacuumOnIdle: " + options.vacuumOnIdle); + } + openedOptions.vacuumOnIdle = options.vacuumOnIdle; + } + + if ("incrementalVacuum" in options) { + if (typeof options.incrementalVacuum != "boolean") { + throw new Error( + "Invalid incrementalVacuum: " + options.incrementalVacuum + ); + } + openedOptions.incrementalVacuum = options.incrementalVacuum; + } + + if ("pageSize" in options) { + if ( + ![512, 1024, 2048, 4096, 8192, 16384, 32768, 65536].includes( + options.pageSize + ) + ) { + throw new Error("Invalid pageSize: " + options.pageSize); + } + openedOptions.pageSize = options.pageSize; + } + + let identifier = getIdentifierByFileName(PathUtils.filename(path)); + + log.debug("Opening database: " + path + " (" + identifier + ")"); + + return new Promise((resolve, reject) => { + let dbOpenOptions = Ci.mozIStorageService.OPEN_DEFAULT; + if (sharedMemoryCache) { + dbOpenOptions |= Ci.mozIStorageService.OPEN_SHARED; + } + if (options.readOnly) { + dbOpenOptions |= Ci.mozIStorageService.OPEN_READONLY; + } + if (options.ignoreLockingMode) { + dbOpenOptions |= Ci.mozIStorageService.OPEN_IGNORE_LOCKING_MODE; + dbOpenOptions |= Ci.mozIStorageService.OPEN_READONLY; + } + + let dbConnectionOptions = Ci.mozIStorageService.CONNECTION_DEFAULT; + + Services.storage.openAsyncDatabase( + file, + dbOpenOptions, + dbConnectionOptions, + async (status, connection) => { + if (!connection) { + log.error(`Could not open connection to ${path}: ${status}`); + let error = new Components.Exception( + `Could not open connection to ${path}: ${status}`, + status + ); + reject(error); + return; + } + log.debug("Connection opened"); + + if (options.testDelayedOpenPromise) { + await options.testDelayedOpenPromise; + } + + if (isClosed()) { + connection.QueryInterface(Ci.mozIStorageAsyncConnection).asyncClose(); + reject( + new Error( + "Sqlite.sys.mjs has been shutdown. Cannot open connection to: " + + options.path + ) + ); + return; + } + + try { + resolve( + new OpenedConnection( + connection.QueryInterface(Ci.mozIStorageAsyncConnection), + identifier, + openedOptions + ) + ); + } catch (ex) { + log.error("Could not open database", ex); + connection.asyncClose(); + reject(ex); + } + } + ); + }); +} + +/** + * Creates a clone of an existing and open Storage connection. The clone has + * the same underlying characteristics of the original connection and is + * returned in form of an OpenedConnection handle. + * + * The following parameters can control the cloned connection: + * + * connection -- (mozIStorageAsyncConnection) The original Storage connection + * to clone. It's not possible to clone connections to memory databases. + * + * readOnly -- (boolean) - If true the clone will be read-only. If the + * original connection is already read-only, the clone will be, regardless + * of this option. If the original connection is using the shared cache, + * this parameter will be ignored and the clone will be as privileged as + * the original connection. + * shrinkMemoryOnConnectionIdleMS -- (integer) If defined, the connection + * will attempt to minimize its memory usage after this many + * milliseconds of connection idle. The connection is idle when no + * statements are executing. There is no default value which means no + * automatic memory minimization will occur. Please note that this is + * *not* a timer on the idle service and this could fire while the + * application is active. + * + * + * @param options + * (Object) Parameters to control connection and clone options. + * + * @return Promise<OpenedConnection> + */ +function cloneStorageConnection(options) { + let log = lazy.Log.repository.getLoggerWithMessagePrefix( + "Sqlite.sys.mjs", + `ConnectionCloner: ` + ); + log.manageLevelFromPref("toolkit.sqlitejsm.loglevel"); + + let source = options && options.connection; + if (!source) { + throw new TypeError("connection not specified in clone options."); + } + if (!(source instanceof Ci.mozIStorageAsyncConnection)) { + throw new TypeError("Connection must be a valid Storage connection."); + } + + if (isClosed()) { + throw new Error( + "Sqlite.sys.mjs has been shutdown. Cannot clone connection to: " + + source.databaseFile.path + ); + } + + let openedOptions = {}; + + if ("shrinkMemoryOnConnectionIdleMS" in options) { + if (!Number.isInteger(options.shrinkMemoryOnConnectionIdleMS)) { + throw new TypeError( + "shrinkMemoryOnConnectionIdleMS must be an integer. " + + "Got: " + + options.shrinkMemoryOnConnectionIdleMS + ); + } + openedOptions.shrinkMemoryOnConnectionIdleMS = + options.shrinkMemoryOnConnectionIdleMS; + } + + let path = source.databaseFile.path; + let identifier = getIdentifierByFileName(PathUtils.filename(path)); + + log.debug("Cloning database: " + path + " (" + identifier + ")"); + + return new Promise((resolve, reject) => { + source.asyncClone(!!options.readOnly, (status, connection) => { + if (!connection) { + log.error("Could not clone connection: " + status); + reject(new Error("Could not clone connection: " + status)); + return; + } + log.debug("Connection cloned"); + + if (isClosed()) { + connection.QueryInterface(Ci.mozIStorageAsyncConnection).asyncClose(); + reject( + new Error( + "Sqlite.sys.mjs has been shutdown. Cannot open connection to: " + + options.path + ) + ); + return; + } + + try { + let conn = connection.QueryInterface(Ci.mozIStorageAsyncConnection); + resolve(new OpenedConnection(conn, identifier, openedOptions)); + } catch (ex) { + log.error("Could not clone database", ex); + connection.asyncClose(); + reject(ex); + } + }); + }); +} + +/** + * Wraps an existing and open Storage connection with Sqlite.sys.mjs API. The + * wrapped connection clone has the same underlying characteristics of the + * original connection and is returned in form of an OpenedConnection handle. + * + * Clients are responsible for closing both the Sqlite.sys.mjs wrapper and the + * underlying mozStorage connection. + * + * The following parameters can control the wrapped connection: + * + * connection -- (mozIStorageAsyncConnection) The original Storage connection + * to wrap. + * + * @param options + * (Object) Parameters to control connection and wrap options. + * + * @return Promise<OpenedConnection> + */ +function wrapStorageConnection(options) { + let log = lazy.Log.repository.getLoggerWithMessagePrefix( + "Sqlite.sys.mjs", + `ConnectionCloner: ` + ); + log.manageLevelFromPref("toolkit.sqlitejsm.loglevel"); + + let connection = options && options.connection; + if (!connection || !(connection instanceof Ci.mozIStorageAsyncConnection)) { + throw new TypeError("connection not specified or invalid."); + } + + if (isClosed()) { + throw new Error( + "Sqlite.sys.mjs has been shutdown. Cannot wrap connection to: " + + connection.databaseFile.path + ); + } + + let identifier = getIdentifierByFileName(connection.databaseFile.leafName); + + log.debug("Wrapping database: " + identifier); + return new Promise(resolve => { + try { + let conn = connection.QueryInterface(Ci.mozIStorageAsyncConnection); + let wrapper = new OpenedConnection(conn, identifier); + // We must not handle shutdown of a wrapped connection, since that is + // already handled by the opener. + wrappedConnections.add(identifier); + resolve(wrapper); + } catch (ex) { + log.error("Could not wrap database", ex); + throw ex; + } + }); +} + +/** + * Handle on an opened SQLite database. + * + * This is essentially a glorified wrapper around mozIStorageConnection. + * However, it offers some compelling advantages. + * + * The main functions on this type are `execute` and `executeCached`. These are + * ultimately how all SQL statements are executed. It's worth explaining their + * differences. + * + * `execute` is used to execute one-shot SQL statements. These are SQL + * statements that are executed one time and then thrown away. They are useful + * for dynamically generated SQL statements and clients who don't care about + * performance (either their own or wasting resources in the overall + * application). Because of the performance considerations, it is recommended + * to avoid `execute` unless the statement you are executing will only be + * executed once or seldomly. + * + * `executeCached` is used to execute a statement that will presumably be + * executed multiple times. The statement is parsed once and stuffed away + * inside the connection instance. Subsequent calls to `executeCached` will not + * incur the overhead of creating a new statement object. This should be used + * in preference to `execute` when a specific SQL statement will be executed + * multiple times. + * + * Instances of this type are not meant to be created outside of this file. + * Instead, first open an instance of `UnopenedSqliteConnection` and obtain + * an instance of this type by calling `open`. + * + * FUTURE IMPROVEMENTS + * + * Ability to enqueue operations. Currently there can be race conditions, + * especially as far as transactions are concerned. It would be nice to have + * an enqueueOperation(func) API that serially executes passed functions. + * + * Support for SAVEPOINT (named/nested transactions) might be useful. + * + * @param connection + * (mozIStorageConnection) Underlying SQLite connection. + * @param identifier + * (string) The unique identifier of this database. It may be used for + * logging or as a key in Maps. + * @param options [optional] + * (object) Options to control behavior of connection. See + * `openConnection`. + */ +function OpenedConnection(connection, identifier, options = {}) { + // Store all connection data in a field distinct from the + // witness. This enables us to store an additional reference to this + // field without preventing garbage collection of + // OpenedConnection. On garbage collection, we will still be able to + // close the database using this extra reference. + this._connectionData = new ConnectionData(connection, identifier, options); + + // Store the extra reference in a map with connection identifier as + // key. + ConnectionData.byId.set( + this._connectionData._identifier, + this._connectionData + ); + + // Make a finalization witness. If this object is garbage collected + // before its `forget` method has been called, an event with topic + // "sqlite-finalization-witness" is broadcasted along with the + // connection identifier string of the database. + this._witness = lazy.FinalizationWitnessService.make( + "sqlite-finalization-witness", + this._connectionData._identifier + ); +} + +OpenedConnection.TRANSACTION_TYPES = ["DEFERRED", "IMMEDIATE", "EXCLUSIVE"]; + +// Converts a `mozIStorageAsyncConnection::TRANSACTION_*` constant into the +// corresponding `OpenedConnection.TRANSACTION_TYPES` constant. +function convertStorageTransactionType(type) { + if (!(type in OpenedConnection.TRANSACTION_TYPES)) { + throw new Error("Unknown storage transaction type: " + type); + } + return OpenedConnection.TRANSACTION_TYPES[type]; +} + +OpenedConnection.prototype = Object.freeze({ + TRANSACTION_DEFAULT: "DEFAULT", + TRANSACTION_DEFERRED: "DEFERRED", + TRANSACTION_IMMEDIATE: "IMMEDIATE", + TRANSACTION_EXCLUSIVE: "EXCLUSIVE", + + /** + * Returns a handle to the underlying `mozIStorageAsyncConnection`. This is + * ⚠️ **extremely unsafe** ⚠️ because `Sqlite.sys.mjs` continues to manage the + * connection's lifecycle, including transactions and shutdown blockers. + * Misusing the raw connection can easily lead to data loss, memory leaks, + * and errors. + * + * Consumers of the raw connection **must not** close or re-wrap it, + * and should not run statements concurrently with `Sqlite.sys.mjs`. + * + * It's _much_ safer to open a `mozIStorage{Async}Connection` yourself, + * and access it from JavaScript via `Sqlite.wrapStorageConnection`. + * `unsafeRawConnection` is an escape hatch for cases where you can't + * do that. + * + * Please do _not_ add new uses of `unsafeRawConnection` without review + * from a storage peer. + */ + get unsafeRawConnection() { + return this._connectionData._dbConn; + }, + + /** + * Returns the maximum number of bound parameters for statements executed + * on this connection. + * + * @type {number} + */ + get variableLimit() { + return this.unsafeRawConnection.variableLimit; + }, + + /** + * The integer schema version of the database. + * + * This is 0 if not schema version has been set. + * + * @return Promise<int> + */ + getSchemaVersion(schemaName = "main") { + return this.execute(`PRAGMA ${schemaName}.user_version`).then(result => + result[0].getInt32(0) + ); + }, + + setSchemaVersion(value, schemaName = "main") { + if (!Number.isInteger(value)) { + // Guarding against accidental SQLi + throw new TypeError("Schema version must be an integer. Got " + value); + } + this._connectionData.ensureOpen(); + return this.execute(`PRAGMA ${schemaName}.user_version = ${value}`); + }, + + /** + * Close the database connection. + * + * This must be performed when you are finished with the database. + * + * Closing the database connection has the side effect of forcefully + * cancelling all active statements. Therefore, callers should ensure that + * all active statements have completed before closing the connection, if + * possible. + * + * The returned promise will be resolved once the connection is closed. + * Successive calls to close() return the same promise. + * + * IMPROVEMENT: Resolve the promise to a closed connection which can be + * reopened. + * + * @return Promise<> + */ + close() { + // Unless cleanup has already been done by a previous call to + // `close`, delete the database entry from map and tell the + // finalization witness to forget. + if (ConnectionData.byId.has(this._connectionData._identifier)) { + ConnectionData.byId.delete(this._connectionData._identifier); + this._witness.forget(); + } + return this._connectionData.close(); + }, + + /** + * Clones this connection to a new Sqlite one. + * + * The following parameters can control the cloned connection: + * + * @param readOnly + * (boolean) - If true the clone will be read-only. If the original + * connection is already read-only, the clone will be, regardless of + * this option. If the original connection is using the shared cache, + * this parameter will be ignored and the clone will be as privileged as + * the original connection. + * + * @return Promise<OpenedConnection> + */ + clone(readOnly = false) { + return this._connectionData.clone(readOnly); + }, + + executeBeforeShutdown(name, task) { + return this._connectionData.executeBeforeShutdown(this, name, task); + }, + + /** + * Execute a SQL statement and cache the underlying statement object. + * + * This function executes a SQL statement and also caches the underlying + * derived statement object so subsequent executions are faster and use + * less resources. + * + * This function optionally binds parameters to the statement as well as + * optionally invokes a callback for every row retrieved. + * + * By default, no parameters are bound and no callback will be invoked for + * every row. + * + * Bound parameters can be defined as an Array of positional arguments or + * an object mapping named parameters to their values. If there are no bound + * parameters, the caller can pass nothing or null for this argument. + * + * Callers are encouraged to pass objects rather than Arrays for bound + * parameters because they prevent foot guns. With positional arguments, it + * is simple to modify the parameter count or positions without fixing all + * users of the statement. Objects/named parameters are a little safer + * because changes in order alone won't result in bad things happening. + * + * When `onRow` is not specified, all returned rows are buffered before the + * returned promise is resolved. For INSERT or UPDATE statements, this has + * no effect because no rows are returned from these. However, it has + * implications for SELECT statements. + * + * If your SELECT statement could return many rows or rows with large amounts + * of data, for performance reasons it is recommended to pass an `onRow` + * handler. Otherwise, the buffering may consume unacceptable amounts of + * resources. + * + * If the second parameter of an `onRow` handler is called during execution + * of the `onRow` handler, the execution of the statement is immediately + * cancelled. Subsequent rows will not be processed and no more `onRow` + * invocations will be made. The promise is resolved immediately. + * + * If an exception is thrown by the `onRow` handler, the exception is logged + * and processing of subsequent rows occurs as if nothing happened. The + * promise is still resolved (not rejected). + * + * The return value is a promise that will be resolved when the statement + * has completed fully. + * + * The promise will be rejected with an `Error` instance if the statement + * did not finish execution fully. The `Error` may have an `errors` property. + * If defined, it will be an Array of objects describing individual errors. + * Each object has the properties `result` and `message`. `result` is a + * numeric error code and `message` is a string description of the problem. + * + * @param name + * (string) The name of the registered statement to execute. + * @param params optional + * (Array or object) Parameters to bind. + * @param onRow optional + * (function) Callback to receive each row from result. + */ + executeCached(sql, params = null, onRow = null) { + if (isInvalidBoundLikeQuery(sql)) { + throw new Error("Please enter a LIKE clause with bindings"); + } + return this._connectionData.executeCached(sql, params, onRow); + }, + + /** + * Execute a one-shot SQL statement. + * + * If you find yourself feeding the same SQL string in this function, you + * should *not* use this function and instead use `executeCached`. + * + * See `executeCached` for the meaning of the arguments and extended usage info. + * + * @param sql + * (string) SQL to execute. + * @param params optional + * (Array or Object) Parameters to bind to the statement. + * @param onRow optional + * (function) Callback to receive result of a single row. + */ + execute(sql, params = null, onRow = null) { + if (isInvalidBoundLikeQuery(sql)) { + throw new Error("Please enter a LIKE clause with bindings"); + } + return this._connectionData.execute(sql, params, onRow); + }, + + /** + * The default behavior for transactions run on this connection. + */ + get defaultTransactionType() { + return this._connectionData.defaultTransactionType; + }, + + /** + * Whether a transaction is currently in progress. + * + * Note that this is true if a transaction is active on the connection, + * regardless of whether it was started by `Sqlite.sys.mjs` or another consumer. + * See the explanation above `mozIStorageConnection.transactionInProgress` for + * why this distinction matters. + */ + get transactionInProgress() { + return this._connectionData.transactionInProgress; + }, + + /** + * Perform a transaction. + * + * ***************************************************************************** + * YOU SHOULD _NEVER_ NEST executeTransaction CALLS FOR ANY REASON, NOR + * DIRECTLY, NOR THROUGH OTHER PROMISES. + * FOR EXAMPLE, NEVER DO SOMETHING LIKE: + * await executeTransaction(async function () { + * ...some_code... + * await executeTransaction(async function () { // WRONG! + * ...some_code... + * }) + * await someCodeThatExecuteTransaction(); // WRONG! + * await neverResolvedPromise; // WRONG! + * }); + * NESTING CALLS WILL BLOCK ANY FUTURE TRANSACTION UNTIL A TIMEOUT KICKS IN. + * ***************************************************************************** + * + * A transaction is specified by a user-supplied function that is an + * async function. The function receives this connection instance as its argument. + * + * The supplied function is expected to return promises. These are often + * promises created by calling `execute` and `executeCached`. If the + * generator is exhausted without any errors being thrown, the + * transaction is committed. If an error occurs, the transaction is + * rolled back. + * + * The returned value from this function is a promise that will be resolved + * once the transaction has been committed or rolled back. The promise will + * be resolved to whatever value the supplied function resolves to. If + * the transaction is rolled back, the promise is rejected. + * + * @param func + * (function) What to perform as part of the transaction. + * @param type optional + * One of the TRANSACTION_* constants attached to this type. + */ + executeTransaction(func, type = this.TRANSACTION_DEFAULT) { + return this._connectionData.executeTransaction(() => func(this), type); + }, + + /** + * Whether a table exists in the database (both persistent and temporary tables). + * + * @param name + * (string) Name of the table. + * + * @return Promise<bool> + */ + tableExists(name) { + return this.execute( + "SELECT name FROM (SELECT * FROM sqlite_master UNION ALL " + + "SELECT * FROM sqlite_temp_master) " + + "WHERE type = 'table' AND name=?", + [name] + ).then(function onResult(rows) { + return Promise.resolve(!!rows.length); + }); + }, + + /** + * Whether a named index exists (both persistent and temporary tables). + * + * @param name + * (string) Name of the index. + * + * @return Promise<bool> + */ + indexExists(name) { + return this.execute( + "SELECT name FROM (SELECT * FROM sqlite_master UNION ALL " + + "SELECT * FROM sqlite_temp_master) " + + "WHERE type = 'index' AND name=?", + [name] + ).then(function onResult(rows) { + return Promise.resolve(!!rows.length); + }); + }, + + /** + * Free up as much memory from the underlying database connection as possible. + * + * @return Promise<> + */ + shrinkMemory() { + return this._connectionData.shrinkMemory(); + }, + + /** + * Discard all cached statements. + * + * Note that this relies on us being non-interruptible between + * the insertion or retrieval of a statement in the cache and its + * execution: we finalize all statements, which is only safe if + * they will not be executed again. + * + * @return (integer) the number of statements discarded. + */ + discardCachedStatements() { + return this._connectionData.discardCachedStatements(); + }, + + /** + * Interrupts pending database operations returning at the first opportunity. + * Statement execution will throw an NS_ERROR_ABORT failure. + * Can only be used on read-only connections. + */ + interrupt() { + this._connectionData.interrupt(); + }, + + /** + * Asynchronously makes a copy of the SQLite database while there may still be + * open connections on it. + * + * @param {string} destFilePath + * The path on the local filesystem to write the database copy. Any existing + * file at this path will be overwritten. + * @return Promise<undefined, nsresult> + */ + backup(destFilePath) { + return this._connectionData.backupToFile(destFilePath); + }, +}); + +export var Sqlite = { + // The maximum time to wait before considering a transaction stuck and + // issuing a ROLLBACK, see `executeTransaction`. Could be modified by tests. + TRANSACTIONS_TIMEOUT_MS: 300000, // 5 minutes + + openConnection, + cloneStorageConnection, + wrapStorageConnection, + /** + * Shutdown barrier client. May be used by clients to perform last-minute + * cleanup prior to the shutdown of this module. + * + * See the documentation of AsyncShutdown.Barrier.prototype.client. + */ + get shutdown() { + return lazy.Barriers.shutdown.client; + }, + failTestsOnAutoClose(enabled) { + Debugging.failTestsOnAutoClose = enabled; + }, +}; diff --git a/toolkit/modules/SubDialog.sys.mjs b/toolkit/modules/SubDialog.sys.mjs new file mode 100644 index 0000000000..6cb8d3127e --- /dev/null +++ b/toolkit/modules/SubDialog.sys.mjs @@ -0,0 +1,1183 @@ +/* - 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 { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +let lazy = {}; + +XPCOMUtils.defineLazyServiceGetter( + lazy, + "dragService", + "@mozilla.org/widget/dragservice;1", + "nsIDragService" +); + +const HTML_NS = "http://www.w3.org/1999/xhtml"; + +/** + * The SubDialog resize callback. + * @callback SubDialog~resizeCallback + * @param {DOMNode} title - The title element of the dialog. + * @param {xul:browser} frame - The browser frame of the dialog. + */ + +/** + * SubDialog constructor creates a new subdialog from a template and appends + * it to the parentElement. + * @param {DOMNode} template - The template is copied to create a new dialog. + * @param {DOMNode} parentElement - New dialog is appended onto parentElement. + * @param {String} id - A unique identifier for the dialog. + * @param {Object} dialogOptions - Dialog options object. + * @param {String[]} [dialogOptions.styleSheets] - An array of URLs to additional + * stylesheets to inject into the frame. + * @param {Boolean} [consumeOutsideClicks] - Whether to close the dialog when + * its background overlay is clicked. + * @param {SubDialog~resizeCallback} [resizeCallback] - Function to be called on + * dialog resize. + */ +export function SubDialog({ + template, + parentElement, + id, + dialogOptions: { + styleSheets = [], + consumeOutsideClicks = true, + resizeCallback, + } = {}, +}) { + this._id = id; + + this._injectedStyleSheets = this._injectedStyleSheets.concat(styleSheets); + this._consumeOutsideClicks = consumeOutsideClicks; + this._resizeCallback = resizeCallback; + this._overlay = template.cloneNode(true); + this._box = this._overlay.querySelector(".dialogBox"); + this._titleBar = this._overlay.querySelector(".dialogTitleBar"); + this._titleElement = this._overlay.querySelector(".dialogTitle"); + this._closeButton = this._overlay.querySelector(".dialogClose"); + this._frame = this._overlay.querySelector(".dialogFrame"); + + this._overlay.classList.add(`dialogOverlay-${id}`); + this._frame.setAttribute("name", `dialogFrame-${id}`); + this._frameCreated = new Promise(resolve => { + this._frame.addEventListener( + "load", + () => { + // We intentionally avoid handling or passing the event to the + // resolve method to avoid shutdown window leaks. See bug 1686743. + resolve(); + }, + { + once: true, + capture: true, + } + ); + }); + + parentElement.appendChild(this._overlay); + this._overlay.hidden = false; +} + +SubDialog.prototype = { + _closingCallback: null, + _closingEvent: null, + _isClosing: false, + _frame: null, + _frameCreated: null, + _overlay: null, + _box: null, + _openedURL: null, + _injectedStyleSheets: ["chrome://global/skin/in-content/common.css"], + _resizeObserver: null, + _template: null, + _id: null, + _titleElement: null, + _closeButton: null, + + get frameContentWindow() { + return this._frame?.contentWindow; + }, + + get _window() { + return this._overlay?.ownerGlobal; + }, + + updateTitle(aEvent) { + if (aEvent.target != this._frame.contentDocument) { + return; + } + this._titleElement.textContent = this._frame.contentDocument.title; + }, + + injectStylesheet(aStylesheetURL) { + const doc = this._frame.contentDocument; + if ([...doc.styleSheets].find(s => s.href === aStylesheetURL)) { + return; + } + + // Attempt to insert the stylesheet as a link element into the same place in + // the document as other link elements. It is almost certain that any + // document will already have a localization or other stylesheet link + // present. + let links = doc.getElementsByTagNameNS(HTML_NS, "link"); + if (links.length) { + let stylesheetLink = doc.createElementNS(HTML_NS, "link"); + stylesheetLink.setAttribute("rel", "stylesheet"); + stylesheetLink.setAttribute("href", aStylesheetURL); + + // Insert after the last found link element. + links[links.length - 1].after(stylesheetLink); + + return; + } + + // In the odd case just insert at the top as a processing instruction. + let contentStylesheet = doc.createProcessingInstruction( + "xml-stylesheet", + 'href="' + aStylesheetURL + '" type="text/css"' + ); + doc.insertBefore(contentStylesheet, doc.documentElement); + }, + + async open( + aURL, + { features, closingCallback, closedCallback, sizeTo } = {}, + ...aParams + ) { + if (["available", "limitheight"].includes(sizeTo)) { + this._box.setAttribute("sizeto", sizeTo); + } + + // Create a promise so consumers can tell when we're done setting up. + this._dialogReady = new Promise(resolve => { + this._resolveDialogReady = resolve; + }); + this._frame._dialogReady = this._dialogReady; + + // Assign close callbacks sync to ensure we can always callback even if the + // SubDialog is closed directly after opening. + let dialog = null; + + if (closingCallback) { + this._closingCallback = (...args) => { + closingCallback.apply(dialog, args); + }; + } + if (closedCallback) { + this._closedCallback = (...args) => { + closedCallback.apply(dialog, args); + }; + } + + // Wait until frame is ready to prevent browser crash in tests + await this._frameCreated; + + // If we're closing now that we've waited for the dialog to load, abort. + if (this._isClosing) { + return; + } + this._addDialogEventListeners(); + + // Ensure we end any pending drag sessions: + try { + // The drag service getService call fails in puppeteer tests on Linux, + // so this is in a try...catch as it shouldn't stop us from opening the + // dialog. Bug 1806870 tracks fixing this. + if (lazy.dragService.getCurrentSession()) { + lazy.dragService.endDragSession(true); + } + } catch (ex) { + console.error(ex); + } + + // If the parent is chrome we also need open the dialog as chrome, otherwise + // the openDialog call will fail. + let dialogFeatures = `resizable,dialog=no,centerscreen,chrome=${ + this._window?.isChromeWindow ? "yes" : "no" + }`; + if (features) { + dialogFeatures = `${features},${dialogFeatures}`; + } + + dialog = this._window.openDialog( + aURL, + `dialogFrame-${this._id}`, + dialogFeatures, + ...aParams + ); + + this._closingEvent = null; + this._isClosing = false; + this._openedURL = aURL; + + dialogFeatures = dialogFeatures.replace(/,/g, "&"); + let featureParams = new URLSearchParams(dialogFeatures.toLowerCase()); + this._box.setAttribute( + "resizable", + featureParams.has("resizable") && + featureParams.get("resizable") != "no" && + featureParams.get("resizable") != "0" + ); + }, + + /** + * Close the dialog and mark it as aborted. + */ + abort() { + this._closingEvent = new CustomEvent("dialogclosing", { + bubbles: true, + detail: { dialog: this, abort: true }, + }); + this._frame.contentWindow.close(); + // It's possible that we're aborting this dialog before we've had a + // chance to set up the contentWindow.close function override in + // _onContentLoaded. If so, call this.close() directly to clean things + // up. That'll be a no-op if the contentWindow.close override had been + // set up, since this.close is idempotent. + this.close(this._closingEvent); + }, + + close(aEvent = null) { + if (this._isClosing) { + return; + } + this._isClosing = true; + this._closingPromise = new Promise(resolve => { + this._resolveClosePromise = resolve; + }); + + if (this._closingCallback) { + try { + this._closingCallback.call(null, aEvent); + } catch (ex) { + console.error(ex); + } + this._closingCallback = null; + } + + this._removeDialogEventListeners(); + + this._overlay.style.visibility = ""; + // Clear the sizing inline styles. + this._frame.removeAttribute("style"); + // Clear the sizing attributes + this._box.removeAttribute("width"); + this._box.removeAttribute("height"); + this._box.style.removeProperty("--box-max-height-requested"); + this._box.style.removeProperty("--box-max-width-requested"); + this._box.style.removeProperty("min-height"); + this._box.style.removeProperty("min-width"); + this._overlay.style.removeProperty("--subdialog-inner-height"); + + let onClosed = () => { + this._openedURL = null; + + this._resolveClosePromise(); + + if (this._closedCallback) { + try { + this._closedCallback.call(null, aEvent); + } catch (ex) { + console.error(ex); + } + this._closedCallback = null; + } + }; + + // Wait for the frame to unload before running the closed callback. + if (this._frame.contentWindow) { + this._frame.contentWindow.addEventListener("unload", onClosed, { + once: true, + }); + } else { + onClosed(); + } + + this._overlay.dispatchEvent( + new CustomEvent("dialogclose", { + bubbles: true, + detail: { dialog: this }, + }) + ); + + // Defer removing the overlay so the frame content window can unload. + Services.tm.dispatchToMainThread(() => { + this._overlay.remove(); + }); + }, + + handleEvent(aEvent) { + switch (aEvent.type) { + case "click": + // Close the dialog if the user clicked the overlay background, just + // like when the user presses the ESC key (case "command" below). + if (aEvent.target !== this._overlay) { + break; + } + if (this._consumeOutsideClicks) { + this._frame.contentWindow.close(); + break; + } + this._frame.focus(); + break; + case "command": + this._frame.contentWindow.close(); + break; + case "dialogclosing": + this._onDialogClosing(aEvent); + break; + case "DOMTitleChanged": + this.updateTitle(aEvent); + break; + case "DOMFrameContentLoaded": + this._onContentLoaded(aEvent); + break; + case "load": + this._onLoad(aEvent); + break; + case "unload": + this._onUnload(aEvent); + break; + case "keydown": + this._onKeyDown(aEvent); + break; + case "focus": + this._onParentWinFocus(aEvent); + break; + } + }, + + /* Private methods */ + + _onUnload(aEvent) { + if ( + aEvent.target !== this._frame?.contentDocument || + aEvent.target.location.href !== this._openedURL + ) { + return; + } + this.abort(); + }, + + _onContentLoaded(aEvent) { + if ( + aEvent.target != this._frame || + aEvent.target.contentWindow.location == "about:blank" + ) { + return; + } + + for (let styleSheetURL of this._injectedStyleSheets) { + this.injectStylesheet(styleSheetURL); + } + + let { contentDocument } = this._frame; + // Provide the ability for the dialog to know that it is loaded in a frame + // rather than as a top-level window. + for (let dialog of contentDocument.querySelectorAll("dialog")) { + dialog.setAttribute("subdialog", "true"); + } + // Sub-dialogs loaded in a chrome window should use the system font size so + // that the user has a way to increase or decrease it via system settings. + // Sub-dialogs loaded in the content area, on the other hand, can be zoomed + // like web content. + if (this._window.isChromeWindow) { + contentDocument.documentElement.classList.add("system-font-size"); + } + // Used by CSS to give the appropriate background colour in dark mode. + contentDocument.documentElement.setAttribute("dialogroot", "true"); + + this._frame.contentWindow.addEventListener("dialogclosing", this); + + let oldResizeBy = this._frame.contentWindow.resizeBy; + this._frame.contentWindow.resizeBy = (resizeByWidth, resizeByHeight) => { + // Only handle resizeByHeight currently. + let frameHeight = this._overlay.style.getPropertyValue( + "--subdialog-inner-height" + ); + if (frameHeight) { + frameHeight = parseFloat(frameHeight); + } else { + frameHeight = this._frame.clientHeight; + } + let boxMinHeight = parseFloat( + this._window.getComputedStyle(this._box).minHeight + ); + + this._box.style.minHeight = boxMinHeight + resizeByHeight + "px"; + + this._overlay.style.setProperty( + "--subdialog-inner-height", + frameHeight + resizeByHeight + "px" + ); + + oldResizeBy.call( + this._frame.contentWindow, + resizeByWidth, + resizeByHeight + ); + }; + + // Make window.close calls work like dialog closing. + let oldClose = this._frame.contentWindow.close; + this._frame.contentWindow.close = () => { + var closingEvent = this._closingEvent; + // If this._closingEvent is set, the dialog is closed externally + // (dialog.js) and "dialogclosing" has already been dispatched. + if (!closingEvent) { + // If called without closing event, we need to create and dispatch it. + // This is the case for any external close calls not going through + // dialog.js. + closingEvent = new CustomEvent("dialogclosing", { + bubbles: true, + detail: { button: null }, + }); + + this._frame.contentWindow.dispatchEvent(closingEvent); + } else if (this._closingEvent.detail?.abort) { + // If the dialog is aborted (SubDialog#abort) we need to dispatch the + // "dialogclosing" event ourselves. + this._frame.contentWindow.dispatchEvent(closingEvent); + } + + this.close(closingEvent); + oldClose.call(this._frame.contentWindow); + }; + + // XXX: Hack to make focus during the dialog's load functions work. Make the element visible + // sooner in DOMContentLoaded but mostly invisible instead of changing visibility just before + // the dialog's load event. + // Note that this needs to inherit so that hideDialog() works as expected. + this._overlay.style.visibility = "inherit"; + this._overlay.style.opacity = "0.01"; + + // Ensure the document gets an a11y role of dialog. + const a11yDoc = contentDocument.body || contentDocument.documentElement; + a11yDoc.setAttribute("role", "dialog"); + + Services.obs.notifyObservers(this._frame.contentWindow, "subdialog-loaded"); + }, + + async _onLoad(aEvent) { + let target = aEvent.currentTarget; + if (target.contentWindow.location == "about:blank") { + return; + } + + // In order to properly calculate the sizing of the subdialog, we need to + // ensure that all of the l10n is done. + if (target.contentDocument.l10n) { + await target.contentDocument.l10n.ready; + } + + // Some subdialogs may want to perform additional, asynchronous steps during initializations. + // + // In that case, we expect them to define a Promise which will delay measuring + // until the promise is fulfilled. + if (target.contentDocument.mozSubdialogReady) { + await target.contentDocument.mozSubdialogReady; + } + + await this.resizeDialog(); + this._resolveDialogReady(); + }, + + async resizeDialog() { + // Do this on load to wait for the CSS to load and apply before calculating the size. + let docEl = this._frame.contentDocument.documentElement; + + // These are deduced from styles which we don't change, so it's safe to get them now: + let boxHorizontalBorder = + 2 * parseFloat(this._window.getComputedStyle(this._box).borderLeftWidth); + let frameHorizontalMargin = + 2 * parseFloat(this._window.getComputedStyle(this._frame).marginLeft); + + // Then determine and set a bunch of width stuff: + let { scrollWidth } = docEl.ownerDocument.body || docEl; + // We need to convert em to px because an em value from the dialog window could + // translate to something else in the host window, as font sizes may vary. + let frameMinWidth = + this._emToPx(docEl.style.minWidth) || + this._emToPx(docEl.style.width) || + scrollWidth + "px"; + let frameWidth = docEl.getAttribute("width") + ? docEl.getAttribute("width") + "px" + : scrollWidth + "px"; + if ( + this._box.getAttribute("sizeto") == "available" && + docEl.style.maxWidth + ) { + this._box.style.setProperty( + "--box-max-width-requested", + this._emToPx(docEl.style.maxWidth) + ); + } + + if (this._box.getAttribute("sizeto") != "available") { + this._frame.style.width = frameWidth; + this._frame.style.minWidth = frameMinWidth; + } + + let boxMinWidth = `calc(${ + boxHorizontalBorder + frameHorizontalMargin + }px + ${frameMinWidth})`; + + // Temporary fix to allow parent chrome to collapse properly to min width. + // See Bug 1658722. + if (this._window.isChromeWindow) { + boxMinWidth = `min(80vw, ${boxMinWidth})`; + } + this._box.style.minWidth = boxMinWidth; + + this.resizeVertically(); + + this._overlay.dispatchEvent( + new CustomEvent("dialogopen", { + bubbles: true, + detail: { dialog: this }, + }) + ); + this._overlay.style.visibility = "inherit"; + this._overlay.style.opacity = ""; // XXX: focus hack continued from _onContentLoaded + + if (this._box.getAttribute("resizable") == "true") { + this._onResize = this._onResize.bind(this); + this._resizeObserver = new this._window.MutationObserver(this._onResize); + this._resizeObserver.observe(this._box, { attributes: true }); + } + + this._trapFocus(); + + this._resizeCallback?.({ + title: this._titleElement, + frame: this._frame, + }); + }, + + resizeVertically() { + let docEl = this._frame.contentDocument.documentElement; + let getDocHeight = () => { + let { scrollHeight } = docEl.ownerDocument.body || docEl; + // We need to convert em to px because an em value from the dialog window could + // translate to something else in the host window, as font sizes may vary. + return this._emToPx(docEl.style.height) || scrollHeight + "px"; + }; + + // If the title bar is disabled (not in the template), + // set its height to 0 for the calculation. + let titleBarHeight = 0; + if (this._titleBar) { + titleBarHeight = + this._titleBar.clientHeight + + parseFloat( + this._window.getComputedStyle(this._titleBar).borderBottomWidth + ); + } + + let boxVerticalBorder = + 2 * parseFloat(this._window.getComputedStyle(this._box).borderTopWidth); + let frameVerticalMargin = + 2 * parseFloat(this._window.getComputedStyle(this._frame).marginTop); + + // The difference between the frame and box shouldn't change, either: + let boxRect = this._box.getBoundingClientRect(); + let frameRect = this._frame.getBoundingClientRect(); + let frameSizeDifference = + frameRect.top - boxRect.top + (boxRect.bottom - frameRect.bottom); + + let contentPane = + this._frame.contentDocument.querySelector(".contentPane") || + this._frame.contentDocument.querySelector("dialog"); + + let sizeTo = this._box.getAttribute("sizeto"); + if (["available", "limitheight"].includes(sizeTo)) { + if (sizeTo == "limitheight") { + this._overlay.style.setProperty("--doc-height-px", getDocHeight()); + contentPane?.classList.add("sizeDetermined"); + } else { + if (docEl.style.maxHeight) { + this._box.style.setProperty( + "--box-max-height-requested", + this._emToPx(docEl.style.maxHeight) + ); + } + // Inform the CSS of the toolbar height so the bottom padding can be + // correctly calculated. + this._box.style.setProperty("--box-top-px", `${boxRect.top}px`); + } + return; + } + + // Now do the same but for the height. We need to do this afterwards because otherwise + // XUL assumes we'll optimize for height and gives us "wrong" values which then are no + // longer correct after we set the width: + let frameMinHeight = getDocHeight(); + let frameHeight = docEl.getAttribute("height") + ? docEl.getAttribute("height") + "px" + : frameMinHeight; + + // Now check if the frame height we calculated is possible at this window size, + // accounting for titlebar, padding/border and some spacing. + let frameOverhead = frameSizeDifference + titleBarHeight; + let maxHeight = this._window.innerHeight - frameOverhead; + // Do this with a frame height in pixels... + if (!frameHeight.endsWith("px")) { + console.error( + "This dialog (", + this._frame.contentWindow.location.href, + ") set a height in non-px-non-em units ('", + frameHeight, + "'), " + + "which is likely to lead to bad sizing in in-content preferences. " + + "Please consider changing this." + ); + } + + if ( + parseFloat(frameMinHeight) > maxHeight || + parseFloat(frameHeight) > maxHeight + ) { + // If the height is bigger than that of the window, we should let the + // contents scroll. The class is set on the "dialog" element, unless a + // content pane exists, which is usually the case when the "window" + // element is used to implement the subdialog instead. + frameMinHeight = maxHeight + "px"; + // There also instances where the subdialog is neither implemented using + // a content pane, nor a <dialog> (such as manageAddresses.xhtml) + // so make sure to check that we actually got a contentPane before we + // use it. + contentPane?.classList.add("doScroll"); + } + + this._overlay.style.setProperty("--subdialog-inner-height", frameHeight); + this._frame.style.height = `min( + calc(100vh - ${frameOverhead}px), + var(--subdialog-inner-height, ${frameHeight}) + )`; + this._box.style.minHeight = `calc( + ${boxVerticalBorder + titleBarHeight + frameVerticalMargin}px + + ${frameMinHeight} + )`; + }, + + /** + * Helper for converting em to px because an em value from the dialog window could + * translate to something else in the host window, as font sizes may vary. + * + * @param {String} val + * A CSS length value. + * @return {String} The converted CSS length value, or the original value if + * no conversion took place. + */ + _emToPx(val) { + if (val && val.endsWith("em")) { + let { fontSize } = this.frameContentWindow.getComputedStyle( + this._frame.contentDocument.documentElement + ); + return parseFloat(val) * parseFloat(fontSize) + "px"; + } + return val; + }, + + _onResize(mutations) { + let frame = this._frame; + // The width and height styles are needed for the initial + // layout of the frame, but afterward they need to be removed + // or their presence will restrict the contents of the <browser> + // from resizing to a smaller size. + frame.style.removeProperty("width"); + frame.style.removeProperty("height"); + + let docEl = frame.contentDocument.documentElement; + let persistedAttributes = docEl.getAttribute("persist"); + if ( + !persistedAttributes || + (!persistedAttributes.includes("width") && + !persistedAttributes.includes("height")) + ) { + return; + } + + for (let mutation of mutations) { + if (mutation.attributeName == "width") { + docEl.setAttribute("width", docEl.scrollWidth); + } else if (mutation.attributeName == "height") { + docEl.setAttribute("height", docEl.scrollHeight); + } + } + }, + + _onDialogClosing(aEvent) { + this._frame.contentWindow.removeEventListener("dialogclosing", this); + this._closingEvent = aEvent; + }, + + _onKeyDown(aEvent) { + // Close on ESC key if target is SubDialog + // If we're in the parent window, we need to check if the SubDialogs + // frame is targeted, so we don't close the wrong dialog. + if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE && !aEvent.defaultPrevented) { + if ( + (this._window.isChromeWindow && aEvent.currentTarget == this._box) || + (!this._window.isChromeWindow && aEvent.currentTarget == this._window) + ) { + // Prevent ESC on SubDialog from cancelling page load (Bug 1665339). + aEvent.preventDefault(); + this._frame.contentWindow.close(); + return; + } + } + + if ( + this._window.isChromeWindow || + aEvent.keyCode != aEvent.DOM_VK_TAB || + aEvent.ctrlKey || + aEvent.altKey || + aEvent.metaKey + ) { + return; + } + + let fm = Services.focus; + + let isLastFocusableElement = el => { + // XXXgijs unfortunately there is no way to get the last focusable element without asking + // the focus manager to move focus to it. + let rv = + el == + fm.moveFocus(this._frame.contentWindow, null, fm.MOVEFOCUS_LAST, 0); + fm.setFocus(el, 0); + return rv; + }; + + let forward = !aEvent.shiftKey; + // check if focus is leaving the frame (incl. the close button): + if ( + (aEvent.target == this._closeButton && !forward) || + (isLastFocusableElement(aEvent.originalTarget) && forward) + ) { + aEvent.preventDefault(); + aEvent.stopImmediatePropagation(); + + let parentWin = this._window.docShell.chromeEventHandler.ownerGlobal; + if (forward) { + fm.moveFocus(parentWin, null, fm.MOVEFOCUS_FIRST, fm.FLAG_BYKEY); + } else { + // Somehow, moving back 'past' the opening doc is not trivial. Cheat by doing it in 2 steps: + fm.moveFocus(this._window, null, fm.MOVEFOCUS_ROOT, fm.FLAG_BYKEY); + fm.moveFocus(parentWin, null, fm.MOVEFOCUS_BACKWARD, fm.FLAG_BYKEY); + } + } + }, + + _onParentWinFocus(aEvent) { + // Explicitly check for the focus target of |window| to avoid triggering this when the window + // is refocused + if ( + this._closeButton && + aEvent.target != this._closeButton && + aEvent.target != this._window + ) { + this._closeButton.focus(); + } + }, + + /** + * Setup dialog event listeners. + * @param {Boolean} [includeLoad] - Whether to register load/unload listeners. + */ + _addDialogEventListeners(includeLoad = true) { + if (this._window.isChromeWindow) { + // Only register an event listener if we have a title to show. + if (this._titleBar) { + this._frame.addEventListener("DOMTitleChanged", this, true); + } + + if (includeLoad) { + this._window.addEventListener("unload", this, true); + } + } else { + let chromeBrowser = this._window.docShell.chromeEventHandler; + + if (includeLoad) { + // For content windows we listen for unload of the browser + chromeBrowser.addEventListener("unload", this, true); + } + + if (this._titleBar) { + chromeBrowser.addEventListener("DOMTitleChanged", this, true); + } + } + + // Make the close button work. + this._closeButton?.addEventListener("command", this); + + if (includeLoad) { + // DOMFrameContentLoaded only fires on the top window + this._window.addEventListener("DOMFrameContentLoaded", this, true); + + // Wait for the stylesheets injected during DOMContentLoaded to load before showing the dialog + // otherwise there is a flicker of the stylesheet applying. + this._frame.addEventListener("load", this, true); + } + + // Ensure we get <esc> keypresses even if nothing in the subdialog is focusable + // (happens on OS X when only text inputs and lists are focusable, and + // the subdialog only has checkboxes/radiobuttons/buttons) + if (!this._window.isChromeWindow) { + this._window.addEventListener("keydown", this, true); + } + + this._overlay.addEventListener("click", this, true); + }, + + /** + * Remove dialog event listeners. + * @param {Boolean} [includeLoad] - Whether to remove load/unload listeners. + */ + _removeDialogEventListeners(includeLoad = true) { + if (this._window.isChromeWindow) { + this._frame.removeEventListener("DOMTitleChanged", this, true); + + if (includeLoad) { + this._window.removeEventListener("unload", this, true); + } + } else { + let chromeBrowser = this._window.docShell.chromeEventHandler; + if (includeLoad) { + chromeBrowser.removeEventListener("unload", this, true); + } + + chromeBrowser.removeEventListener("DOMTitleChanged", this, true); + } + + this._closeButton?.removeEventListener("command", this); + + if (includeLoad) { + this._window.removeEventListener("DOMFrameContentLoaded", this, true); + this._frame.removeEventListener("load", this, true); + this._frame.contentWindow.removeEventListener("dialogclosing", this); + } + + this._window.removeEventListener("keydown", this, true); + + this._overlay.removeEventListener("click", this, true); + + if (this._resizeObserver) { + this._resizeObserver.disconnect(); + this._resizeObserver = null; + } + + this._untrapFocus(); + }, + + /** + * Focus the dialog content. + * If the embedded document defines a custom focus handler it will be called. + * Otherwise we will focus the first focusable element in the content window. + * @param {boolean} [isInitialFocus] - Whether the dialog is focused for the + * first time after opening. + */ + focus(isInitialFocus = false) { + // If the content window has its own focus logic, hand off the focus call. + let focusHandler = this._frame?.contentDocument?.subDialogSetDefaultFocus; + if (focusHandler) { + focusHandler(isInitialFocus); + return; + } + // Handle focus ourselves. Try to move the focus to the first element in + // the content window. + let fm = Services.focus; + + // We're intentionally hiding the focus ring here for now per bug 1704882, + // but we aim to have a better fix that retains the focus ring for users + // that had brought up the dialog by keyboard in bug 1708261. + let focusedElement = fm.moveFocus( + this._frame.contentWindow, + null, + fm.MOVEFOCUS_FIRST, + fm.FLAG_NOSHOWRING + ); + if (!focusedElement) { + // Ensure the focus is pulled out of the content document even if there's + // nothing focusable in the dialog. + this._frame.focus(); + } + }, + + _trapFocus() { + // Attach a system event listener so the dialog can cancel keydown events. + // See Bug 1669990. + this._box.addEventListener("keydown", this, { mozSystemGroup: true }); + this._closeButton?.addEventListener("keydown", this); + + if (!this._window.isChromeWindow) { + this._window.addEventListener("focus", this, true); + } + }, + + _untrapFocus() { + this._box.removeEventListener("keydown", this, { mozSystemGroup: true }); + this._closeButton?.removeEventListener("keydown", this); + this._window.removeEventListener("focus", this, true); + }, +}; + +/** + * Manages multiple SubDialogs in a dialog stack element. + */ +export class SubDialogManager { + /** + * @param {Object} options - Dialog manager options. + * @param {DOMNode} options.dialogStack - Container element for all dialogs + * this instance manages. + * @param {DOMNode} options.dialogTemplate - Element to use as template for + * constructing new dialogs. + * @param {Number} [options.orderType] - Whether dialogs should be ordered as + * a stack or a queue. + * @param {Boolean} [options.allowDuplicateDialogs] - Whether to allow opening + * duplicate dialogs (same URI) at the same time. If disabled, opening a + * dialog with the same URI as an existing dialog will be a no-op. + * @param {Object} options.dialogOptions - Options passed to every + * SubDialog instance. + * @see {@link SubDialog} for a list of dialog options. + */ + constructor({ + dialogStack, + dialogTemplate, + orderType = SubDialogManager.ORDER_STACK, + allowDuplicateDialogs = false, + dialogOptions, + }) { + /** + * New dialogs are pushed to the end of the _dialogs array. + * Depending on the orderType either the last element (stack) or the first + * element (queue) in the array will be the top and visible. + * @type {SubDialog[]} + */ + this._dialogs = []; + this._dialogStack = dialogStack; + this._dialogTemplate = dialogTemplate; + this._topLevelPrevActiveElement = null; + this._orderType = orderType; + this._allowDuplicateDialogs = allowDuplicateDialogs; + this._dialogOptions = dialogOptions; + + this._preloadDialog = new SubDialog({ + template: this._dialogTemplate, + parentElement: this._dialogStack, + id: SubDialogManager._nextDialogID++, + dialogOptions: this._dialogOptions, + }); + } + + /** + * Get the dialog which is currently on top. This depends on whether the + * dialogs are in a stack or a queue. + */ + get _topDialog() { + if (!this._dialogs.length) { + return undefined; + } + if (this._orderType === SubDialogManager.ORDER_STACK) { + return this._dialogs[this._dialogs.length - 1]; + } + return this._dialogs[0]; + } + + open( + aURL, + { + features, + closingCallback, + closedCallback, + allowDuplicateDialogs, + sizeTo, + hideContent, + } = {}, + ...aParams + ) { + let allowDuplicates = + allowDuplicateDialogs != null + ? allowDuplicateDialogs + : this._allowDuplicateDialogs; + // If we're already open/opening on this URL, do nothing. + if ( + !allowDuplicates && + this._dialogs.some(dialog => dialog._openedURL == aURL) + ) { + return undefined; + } + + let doc = this._dialogStack.ownerDocument; + + // For dialog stacks, remember the last active element before opening the + // next dialog. This allows us to restore focus on dialog close. + if ( + this._orderType === SubDialogManager.ORDER_STACK && + this._dialogs.length + ) { + this._topDialog._prevActiveElement = doc.activeElement; + } + + if (!this._dialogs.length) { + // When opening the first dialog, show the dialog stack. + this._dialogStack.hidden = false; + this._dialogStack.classList.remove("temporarilyHidden"); + this._topLevelPrevActiveElement = doc.activeElement; + } + + // Consumers may pass this flag to make the dialog overlay background opaque, + // effectively hiding the content behind it. For example, + // this is used by the prompt code to prevent certain http authentication spoofing scenarios. + if (hideContent) { + this._preloadDialog._overlay.setAttribute("hideContent", true); + } + this._dialogs.push(this._preloadDialog); + this._preloadDialog.open( + aURL, + { + features, + closingCallback, + closedCallback, + sizeTo, + }, + ...aParams + ); + + let openedDialog = this._preloadDialog; + + this._preloadDialog = new SubDialog({ + template: this._dialogTemplate, + parentElement: this._dialogStack, + id: SubDialogManager._nextDialogID++, + dialogOptions: this._dialogOptions, + }); + + if (this._dialogs.length == 1) { + this._ensureStackEventListeners(); + } + + return openedDialog; + } + + close() { + this._topDialog.close(); + } + + /** + * Hides the dialog stack for a specific browser, without actually destroying + * frames for stuff within it. + * + * @param aBrowser - The browser associated with the tab dialog. + */ + hideDialog(aBrowser) { + aBrowser.removeAttribute("tabDialogShowing"); + this._dialogStack.classList.add("temporarilyHidden"); + } + + /** + * Abort open dialogs. + * @param {function} [filterFn] - Function which should return true for + * dialogs that should be aborted and false for dialogs that should remain + * open. Defaults to aborting all dialogs. + */ + abortDialogs(filterFn = () => true) { + this._dialogs.filter(filterFn).forEach(dialog => dialog.abort()); + } + + get hasDialogs() { + if (!this._dialogs.length) { + return false; + } + return this._dialogs.some(dialog => !dialog._isClosing); + } + + get dialogs() { + return [...this._dialogs]; + } + + focusTopDialog() { + this._topDialog?.focus(); + } + + handleEvent(aEvent) { + switch (aEvent.type) { + case "dialogopen": { + this._onDialogOpen(aEvent.detail.dialog); + break; + } + case "dialogclose": { + this._onDialogClose(aEvent.detail.dialog); + break; + } + } + } + + _onDialogOpen(dialog) { + let lowerDialogs = []; + if (dialog == this._topDialog) { + dialog.focus(true); + } else { + // Opening dialog is not on top, hide it + lowerDialogs.push(dialog); + } + + // For stack order, hide the previous top + if ( + this._dialogs.length && + this._orderType === SubDialogManager.ORDER_STACK + ) { + let index = this._dialogs.indexOf(dialog); + if (index > 0) { + lowerDialogs.push(this._dialogs[index - 1]); + } + } + + lowerDialogs.forEach(d => { + if (d._overlay.hasAttribute("topmost")) { + d._overlay.removeAttribute("topmost"); + d._removeDialogEventListeners(false); + } + }); + } + + _onDialogClose(dialog) { + this._dialogs.splice(this._dialogs.indexOf(dialog), 1); + + if (this._topDialog) { + // The prevActiveElement is only set for stacked dialogs + if (this._topDialog._prevActiveElement) { + this._topDialog._prevActiveElement.focus(); + } else { + this._topDialog.focus(true); + } + this._topDialog._overlay.setAttribute("topmost", true); + this._topDialog._addDialogEventListeners(false); + this._dialogStack.hidden = false; + this._dialogStack.classList.remove("temporarilyHidden"); + } else { + // We have closed the last dialog, do cleanup. + this._topLevelPrevActiveElement.focus(); + this._dialogStack.hidden = true; + this._removeStackEventListeners(); + } + } + + _ensureStackEventListeners() { + this._dialogStack.addEventListener("dialogopen", this); + this._dialogStack.addEventListener("dialogclose", this); + } + + _removeStackEventListeners() { + this._dialogStack.removeEventListener("dialogopen", this); + this._dialogStack.removeEventListener("dialogclose", this); + } +} + +// Used for the SubDialogManager orderType option. +SubDialogManager.ORDER_STACK = 0; +SubDialogManager.ORDER_QUEUE = 1; + +SubDialogManager._nextDialogID = 0; diff --git a/toolkit/modules/Timer.sys.mjs b/toolkit/modules/Timer.sys.mjs new file mode 100644 index 0000000000..c77fe90e11 --- /dev/null +++ b/toolkit/modules/Timer.sys.mjs @@ -0,0 +1,139 @@ +/* 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/. */ + +/** + * JS module implementation of setTimeout and clearTimeout. + */ + +// This gives us >=2^30 unique timer IDs, enough for 1 per ms for 12.4 days. +var gNextId = 1; // setTimeout and setInterval must return a positive integer + +var gTimerTable = new Map(); // int -> nsITimer or idleCallback + +// Don't generate this for every timer. +var setTimeout_timerCallbackQI = ChromeUtils.generateQI([ + "nsITimerCallback", + "nsINamed", +]); + +function _setTimeoutOrIsInterval( + aCallback, + aMilliseconds, + aIsInterval, + aTarget, + aArgs +) { + if (typeof aCallback !== "function") { + throw new Error( + `callback is not a function in ${ + aIsInterval ? "setInterval" : "setTimeout" + }` + ); + } + let id = gNextId++; + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + + if (aTarget) { + timer.target = aTarget; + } + + let callback = { + QueryInterface: setTimeout_timerCallbackQI, + + // nsITimerCallback + notify() { + if (!aIsInterval) { + gTimerTable.delete(id); + } + aCallback.apply(null, aArgs); + }, + + // nsINamed + get name() { + return `${ + aIsInterval ? "setInterval" : "setTimeout" + }() for ${Cu.getDebugName(aCallback)}`; + }, + }; + + timer.initWithCallback( + callback, + aMilliseconds, + aIsInterval ? timer.TYPE_REPEATING_SLACK : timer.TYPE_ONE_SHOT + ); + + gTimerTable.set(id, timer); + return id; +} + +export function setTimeout(aCallback, aMilliseconds, ...aArgs) { + return _setTimeoutOrIsInterval(aCallback, aMilliseconds, false, null, aArgs); +} + +export function setTimeoutWithTarget( + aCallback, + aMilliseconds, + aTarget, + ...aArgs +) { + return _setTimeoutOrIsInterval( + aCallback, + aMilliseconds, + false, + aTarget, + aArgs + ); +} + +export function setInterval(aCallback, aMilliseconds, ...aArgs) { + return _setTimeoutOrIsInterval(aCallback, aMilliseconds, true, null, aArgs); +} + +export function setIntervalWithTarget( + aCallback, + aMilliseconds, + aTarget, + ...aArgs +) { + return _setTimeoutOrIsInterval( + aCallback, + aMilliseconds, + true, + aTarget, + aArgs + ); +} + +function clear(aId) { + if (gTimerTable.has(aId)) { + gTimerTable.get(aId).cancel(); + gTimerTable.delete(aId); + } +} +export var clearInterval = clear; +export var clearTimeout = clear; + +export function requestIdleCallback(aCallback, aOptions) { + if (typeof aCallback !== "function") { + throw new Error("callback is not a function in requestIdleCallback"); + } + let id = gNextId++; + + let callback = (...aArgs) => { + if (gTimerTable.has(id)) { + gTimerTable.delete(id); + aCallback(...aArgs); + } + }; + + ChromeUtils.idleDispatch(callback, aOptions); + gTimerTable.set(id, callback); + return id; +} + +export function cancelIdleCallback(aId) { + if (gTimerTable.has(aId)) { + gTimerTable.delete(aId); + } +} diff --git a/toolkit/modules/Troubleshoot.sys.mjs b/toolkit/modules/Troubleshoot.sys.mjs new file mode 100644 index 0000000000..53259bcb67 --- /dev/null +++ b/toolkit/modules/Troubleshoot.sys.mjs @@ -0,0 +1,1130 @@ +/* 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 { AddonManager } from "resource://gre/modules/AddonManager.sys.mjs"; +import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; + +import { FeatureGate } from "resource://featuregates/FeatureGate.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + PlacesDBUtils: "resource://gre/modules/PlacesDBUtils.sys.mjs", +}); + +// We use a list of prefs for display to make sure we only show prefs that +// are useful for support and won't compromise the user's privacy. Note that +// entries are *prefixes*: for example, "accessibility." applies to all prefs +// under the "accessibility.*" branch. +const PREFS_FOR_DISPLAY = [ + "accessibility.", + "apz.", + "browser.cache.", + "browser.contentblocking.category", + "browser.display.", + "browser.download.always_ask_before_handling_new_types", + "browser.download.enable_spam_prevention", + "browser.download.folderList", + "browser.download.improvements_to_download_panel", + "browser.download.lastDir.savePerSite", + "browser.download.manager.addToRecentDocs", + "browser.download.manager.resumeOnWakeDelay", + "browser.download.open_pdf_attachments_inline", + "browser.download.preferred.", + "browser.download.skipConfirmLaunchExecutable", + "browser.download.start_downloads_in_tmp_dir", + "browser.download.useDownloadDir", + "browser.fixup.", + "browser.history_expire_", + "browser.link.open_newwindow", + "browser.places.", + "browser.privatebrowsing.", + "browser.search.context.loadInBackground", + "browser.search.log", + "browser.search.openintab", + "browser.search.param", + "browser.search.region", + "browser.search.searchEnginesURL", + "browser.search.suggest.enabled", + "browser.search.update", + "browser.sessionstore.", + "browser.startup.homepage", + "browser.startup.page", + "browser.tabs.", + "browser.urlbar.", + "browser.zoom.", + "doh-rollout.", + "dom.", + "extensions.checkCompatibility", + "extensions.eventPages.enabled", + "extensions.formautofill.", + "extensions.lastAppVersion", + "extensions.manifestV3.enabled", + "extensions.quarantinedDomains.enabled", + "extensions.InstallTrigger.enabled", + "extensions.InstallTriggerImpl.enabled", + "fission.autostart", + "font.", + "general.autoScroll", + "general.useragent.", + "gfx.", + "html5.", + "identity.fxaccounts.enabled", + "idle.", + "image.", + "javascript.", + "keyword.", + "layers.", + "layout.css.dpi", + "layout.display-list.", + "layout.frame_rate", + "media.", + "mousewheel.", + "network.", + "permissions.default.image", + "places.", + "plugin.", + "plugins.", + "privacy.", + "security.", + "services.sync.declinedEngines", + "services.sync.lastPing", + "services.sync.lastSync", + "services.sync.numClients", + "services.sync.engine.", + "signon.", + "storage.vacuum.last.", + "svg.", + "toolkit.startup.recent_crashes", + "ui.osk.enabled", + "ui.osk.detect_physical_keyboard", + "ui.osk.require_tablet_mode", + "ui.osk.debug.keyboardDisplayReason", + "webgl.", + "widget.dmabuf", + "widget.use-xdg-desktop-portal", + "widget.use-xdg-desktop-portal.file-picker", + "widget.use-xdg-desktop-portal.mime-handler", + "widget.gtk.overlay-scrollbars.enabled", + "widget.wayland", +]; + +// The list of prefs we don't display, unlike the list of prefs for display, +// is a list of regular expressions. +const PREF_REGEXES_NOT_TO_DISPLAY = [ + /^browser[.]fixup[.]domainwhitelist[.]/, + /^dom[.]push[.]userAgentID/, + /^media[.]webrtc[.]debug[.]aec_log_dir/, + /^media[.]webrtc[.]debug[.]log_file/, + /^print[.].*print_to_filename$/, + /^network[.]proxy[.]/, +]; + +// Table of getters for various preference types. +const PREFS_GETTERS = {}; + +PREFS_GETTERS[Ci.nsIPrefBranch.PREF_STRING] = (prefs, name) => + prefs.getStringPref(name); +PREFS_GETTERS[Ci.nsIPrefBranch.PREF_INT] = (prefs, name) => + prefs.getIntPref(name); +PREFS_GETTERS[Ci.nsIPrefBranch.PREF_BOOL] = (prefs, name) => + prefs.getBoolPref(name); + +// List of unimportant locked prefs (won't be shown on the troubleshooting +// session) +const PREFS_UNIMPORTANT_LOCKED = [ + "dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled", + "extensions.backgroundServiceWorkerEnabled.enabled", + "privacy.restrict3rdpartystorage.url_decorations", +]; + +function getPref(name) { + let type = Services.prefs.getPrefType(name); + if (!(type in PREFS_GETTERS)) { + throw new Error("Unknown preference type " + type + " for " + name); + } + return PREFS_GETTERS[type](Services.prefs, name); +} + +// Return the preferences filtered by PREF_REGEXES_NOT_TO_DISPLAY and PREFS_FOR_DISPLAY +// and also by the custom 'filter'-ing function. +function getPrefList(filter, allowlist = PREFS_FOR_DISPLAY) { + return allowlist.reduce(function (prefs, branch) { + Services.prefs.getChildList(branch).forEach(function (name) { + if ( + filter(name) && + !PREF_REGEXES_NOT_TO_DISPLAY.some(re => re.test(name)) + ) { + prefs[name] = getPref(name); + } + }); + return prefs; + }, {}); +} + +export var Troubleshoot = { + /** + * Captures a snapshot of data that may help troubleshooters troubleshoot + * trouble. + * + * @returns {Promise} + * A promise that is resolved with the snapshot data. + */ + snapshot() { + return new Promise(resolve => { + let snapshot = {}; + let numPending = Object.keys(dataProviders).length; + function providerDone(providerName, providerData) { + snapshot[providerName] = providerData; + if (--numPending == 0) { + // Ensure that done is always and truly called asynchronously. + Services.tm.dispatchToMainThread(() => resolve(snapshot)); + } + } + for (let name in dataProviders) { + try { + dataProviders[name](providerDone.bind(null, name)); + } catch (err) { + let msg = "Troubleshoot data provider failed: " + name + "\n" + err; + console.error(msg); + providerDone(name, msg); + } + } + }); + }, + + kMaxCrashAge: 3 * 24 * 60 * 60 * 1000, // 3 days +}; + +// Each data provider is a name => function mapping. When a snapshot is +// captured, each provider's function is called, and it's the function's job to +// generate the provider's data. The function is passed a "done" callback, and +// when done, it must pass its data to the callback. The resulting snapshot +// object will contain a name => data entry for each provider. +var dataProviders = { + application: async function application(done) { + let data = { + name: Services.appinfo.name, + osVersion: + Services.sysinfo.getProperty("name") + + " " + + Services.sysinfo.getProperty("version") + + " " + + Services.sysinfo.getProperty("build"), + version: AppConstants.MOZ_APP_VERSION_DISPLAY, + buildID: Services.appinfo.appBuildID, + distributionID: Services.prefs + .getDefaultBranch("") + .getCharPref("distribution.id", ""), + userAgent: Cc["@mozilla.org/network/protocol;1?name=http"].getService( + Ci.nsIHttpProtocolHandler + ).userAgent, + safeMode: Services.appinfo.inSafeMode, + memorySizeBytes: Services.sysinfo.getProperty("memsize"), + diskAvailableBytes: Services.dirsvc.get("ProfD", Ci.nsIFile) + .diskSpaceAvailable, + }; + + if (Services.sysinfo.getProperty("name") == "Windows_NT") { + if ((await Services.sysinfo.processInfo).isWindowsSMode) { + data.osVersion += " S"; + } + } + + if (AppConstants.MOZ_UPDATER) { + data.updateChannel = ChromeUtils.importESModule( + "resource://gre/modules/UpdateUtils.sys.mjs" + ).UpdateUtils.UpdateChannel; + } + + // eslint-disable-next-line mozilla/use-default-preference-values + try { + data.vendor = Services.prefs.getCharPref("app.support.vendor"); + } catch (e) {} + try { + data.supportURL = Services.urlFormatter.formatURLPref( + "app.support.baseURL" + ); + } catch (e) {} + + data.osTheme = Services.sysinfo.getProperty("osThemeInfo"); + + try { + // MacOSX: Check for rosetta status, if it exists + data.rosetta = Services.sysinfo.getProperty("rosettaStatus"); + } catch (e) {} + + try { + // Windows - Get info about attached pointing devices + data.pointingDevices = Services.sysinfo + .getProperty("pointingDevices") + .split(","); + } catch (e) {} + + data.numTotalWindows = 0; + data.numFissionWindows = 0; + data.numRemoteWindows = 0; + for (let { docShell } of Services.wm.getEnumerator("navigator:browser")) { + docShell.QueryInterface(Ci.nsILoadContext); + data.numTotalWindows++; + if (docShell.useRemoteSubframes) { + data.numFissionWindows++; + } + if (docShell.useRemoteTabs) { + data.numRemoteWindows++; + } + } + + try { + data.launcherProcessState = Services.appinfo.launcherProcessState; + } catch (e) {} + + data.fissionAutoStart = Services.appinfo.fissionAutostart; + data.fissionDecisionStatus = Services.appinfo.fissionDecisionStatusString; + + data.remoteAutoStart = Services.appinfo.browserTabsRemoteAutostart; + + if (Services.policies) { + data.policiesStatus = Services.policies.status; + } + + const keyLocationServiceGoogle = Services.urlFormatter + .formatURL("%GOOGLE_LOCATION_SERVICE_API_KEY%") + .trim(); + data.keyLocationServiceGoogleFound = + keyLocationServiceGoogle != "no-google-location-service-api-key" && + !!keyLocationServiceGoogle.length; + + const keySafebrowsingGoogle = Services.urlFormatter + .formatURL("%GOOGLE_SAFEBROWSING_API_KEY%") + .trim(); + data.keySafebrowsingGoogleFound = + keySafebrowsingGoogle != "no-google-safebrowsing-api-key" && + !!keySafebrowsingGoogle.length; + + const keyMozilla = Services.urlFormatter + .formatURL("%MOZILLA_API_KEY%") + .trim(); + data.keyMozillaFound = + keyMozilla != "no-mozilla-api-key" && !!keyMozilla.length; + + done(data); + }, + + addons: async function addons(done) { + let addons = await AddonManager.getAddonsByTypes([ + "extension", + "locale", + "dictionary", + "sitepermission", + "theme", + ]); + addons = addons.filter(e => !e.isSystem); + addons.sort(function (a, b) { + if (a.isActive != b.isActive) { + return b.isActive ? 1 : -1; + } + + if (a.type != b.type) { + return a.type.localeCompare(b.type); + } + + // In some unfortunate cases add-on names can be null. + let aname = a.name || ""; + let bname = b.name || ""; + let lc = aname.localeCompare(bname); + if (lc != 0) { + return lc; + } + if (a.version != b.version) { + return a.version > b.version ? 1 : -1; + } + return 0; + }); + let props = ["name", "type", "version", "isActive", "id"]; + done( + addons.map(function (ext) { + return props.reduce(function (extData, prop) { + extData[prop] = ext[prop]; + return extData; + }, {}); + }) + ); + }, + + securitySoftware: function securitySoftware(done) { + let data = {}; + + const keys = [ + "registeredAntiVirus", + "registeredAntiSpyware", + "registeredFirewall", + ]; + for (let key of keys) { + let prop = ""; + try { + prop = Services.sysinfo.getProperty(key); + } catch (e) {} + + data[key] = prop; + } + + done(data); + }, + + features: async function features(done) { + let features = await AddonManager.getAddonsByTypes(["extension"]); + features = features.filter(f => f.isSystem); + features.sort(function (a, b) { + // In some unfortunate cases addon names can be null. + let aname = a.name || null; + let bname = b.name || null; + let lc = aname.localeCompare(bname); + if (lc != 0) { + return lc; + } + if (a.version != b.version) { + return a.version > b.version ? 1 : -1; + } + return 0; + }); + let props = ["name", "version", "id"]; + done( + features.map(function (f) { + return props.reduce(function (fData, prop) { + fData[prop] = f[prop]; + return fData; + }, {}); + }) + ); + }, + + processes: async function processes(done) { + let remoteTypes = {}; + const processInfo = await ChromeUtils.requestProcInfo(); + for (let i = 0; i < processInfo.children.length; i++) { + let remoteType; + try { + remoteType = processInfo.children[i].type; + // Workaround for bug 1790070, since requestProcInfo refers to the preallocated content + // process as "preallocated", and the localization string mapping expects "prealloc". + remoteType = remoteType === "preallocated" ? "prealloc" : remoteType; + } catch (e) {} + + // The parent process is also managed by the ppmm (because + // of non-remote tabs), but it doesn't have a remoteType. + if (!remoteType) { + continue; + } + + if (remoteTypes[remoteType]) { + remoteTypes[remoteType]++; + } else { + remoteTypes[remoteType] = 1; + } + } + + try { + let winUtils = Services.wm.getMostRecentWindow("").windowUtils; + if (winUtils.gpuProcessPid != -1) { + remoteTypes.gpu = 1; + } + } catch (e) {} + + if (Services.io.socketProcessLaunched) { + remoteTypes.socket = 1; + } + + let data = { + remoteTypes, + maxWebContentProcesses: Services.appinfo.maxWebProcessCount, + }; + + done(data); + }, + + async experimentalFeatures(done) { + if (AppConstants.platform == "android") { + done(); + return; + } + let gates = await FeatureGate.all(); + done( + gates.map(gate => { + return [ + gate.title, + gate.preference, + Services.prefs.getBoolPref(gate.preference), + ]; + }) + ); + }, + + async legacyUserStylesheets(done) { + if (AppConstants.platform == "android") { + done({ active: false, types: [] }); + return; + } + + let active = Services.prefs.getBoolPref( + "toolkit.legacyUserProfileCustomizations.stylesheets" + ); + let types = []; + for (let name of ["userChrome.css", "userContent.css"]) { + let path = PathUtils.join(PathUtils.profileDir, "chrome", name); + if (await IOUtils.exists(path)) { + types.push(name); + } + } + done({ active, types }); + }, + + async environmentVariables(done) { + let Subprocess; + try { + // Subprocess is not available in all builds + Subprocess = ChromeUtils.importESModule( + "resource://gre/modules/Subprocess.sys.mjs" + ).Subprocess; + } catch (ex) { + done({}); + return; + } + + let environment = Subprocess.getEnvironment(); + let filteredEnvironment = {}; + // Limit the environment variables to those that we + // know may affect Firefox to reduce leaking PII. + let filteredEnvironmentKeys = ["xre_", "moz_", "gdk", "display"]; + for (let key of Object.keys(environment)) { + if (filteredEnvironmentKeys.some(k => key.toLowerCase().startsWith(k))) { + filteredEnvironment[key] = environment[key]; + } + } + done(filteredEnvironment); + }, + + modifiedPreferences: function modifiedPreferences(done) { + done(getPrefList(name => Services.prefs.prefHasUserValue(name))); + }, + + lockedPreferences: function lockedPreferences(done) { + done( + getPrefList( + name => + !PREFS_UNIMPORTANT_LOCKED.includes(name) && + Services.prefs.prefIsLocked(name) + ) + ); + }, + + places: async function places(done) { + const data = AppConstants.MOZ_PLACES + ? await lazy.PlacesDBUtils.getEntitiesStatsAndCounts() + : []; + done(data); + }, + + printingPreferences: function printingPreferences(done) { + let filter = name => Services.prefs.prefHasUserValue(name); + let prefs = getPrefList(filter, ["print."]); + + // print_printer is special and is the only pref that is outside of the + // "print." branch... Maybe we should change it to print.printer or + // something... + if (filter("print_printer")) { + prefs.print_printer = getPref("print_printer"); + } + + done(prefs); + }, + + graphics: function graphics(done) { + function statusMsgForFeature(feature) { + // We return an object because in the try-newer-driver case we need to + // include the suggested version, which the consumer likely needs to plug + // into a format string from a localization file. Rather than returning + // a string in some cases and an object in others, return an object always. + let msg = { key: "" }; + try { + var status = gfxInfo.getFeatureStatus(feature); + } catch (e) {} + switch (status) { + case Ci.nsIGfxInfo.FEATURE_BLOCKED_DEVICE: + case Ci.nsIGfxInfo.FEATURE_DISCOURAGED: + msg = { key: "blocked-gfx-card" }; + break; + case Ci.nsIGfxInfo.FEATURE_BLOCKED_OS_VERSION: + msg = { key: "blocked-os-version" }; + break; + case Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION: + try { + var driverVersion = + gfxInfo.getFeatureSuggestedDriverVersion(feature); + } catch (e) {} + msg = driverVersion + ? { key: "try-newer-driver", args: { driverVersion } } + : { key: "blocked-driver" }; + break; + case Ci.nsIGfxInfo.FEATURE_BLOCKED_MISMATCHED_VERSION: + msg = { key: "blocked-mismatched-version" }; + break; + } + return msg; + } + + let data = {}; + + try { + // nsIGfxInfo may not be implemented on some platforms. + var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo); + } catch (e) {} + + data.desktopEnvironment = Services.appinfo.desktopEnvironment; + data.numTotalWindows = 0; + data.numAcceleratedWindows = 0; + + let devicePixelRatios = []; + + for (let win of Services.ww.getWindowEnumerator()) { + let winUtils = win.windowUtils; + try { + // NOTE: windowless browser's windows should not be reported in the graphics troubleshoot report + if ( + winUtils.layerManagerType == "None" || + !winUtils.layerManagerRemote + ) { + continue; + } + devicePixelRatios.push(win.devicePixelRatio); + + data.numTotalWindows++; + data.windowLayerManagerType = winUtils.layerManagerType; + data.windowLayerManagerRemote = winUtils.layerManagerRemote; + } catch (e) { + continue; + } + if (data.windowLayerManagerType != "Basic") { + data.numAcceleratedWindows++; + } + } + data.graphicsDevicePixelRatios = devicePixelRatios; + + // If we had no OMTC windows, report back Basic Layers. + if (!data.windowLayerManagerType) { + data.windowLayerManagerType = "Basic"; + data.windowLayerManagerRemote = false; + } + + if (!data.numAcceleratedWindows && gfxInfo) { + let win = AppConstants.platform == "win"; + let feature = win + ? gfxInfo.FEATURE_DIRECT3D_9_LAYERS + : gfxInfo.FEATURE_OPENGL_LAYERS; + data.numAcceleratedWindowsMessage = statusMsgForFeature(feature); + } + + if (gfxInfo) { + // keys are the names of attributes on nsIGfxInfo, values become the names + // of the corresponding properties in our data object. A null value means + // no change. This is needed so that the names of properties in the data + // object are the same as the names of keys in aboutSupport.properties. + let gfxInfoProps = { + adapterDescription: null, + adapterVendorID: null, + adapterDeviceID: null, + adapterSubsysID: null, + adapterRAM: null, + adapterDriver: "adapterDrivers", + adapterDriverVendor: "driverVendor", + adapterDriverVersion: "driverVersion", + adapterDriverDate: "driverDate", + + adapterDescription2: null, + adapterVendorID2: null, + adapterDeviceID2: null, + adapterSubsysID2: null, + adapterRAM2: null, + adapterDriver2: "adapterDrivers2", + adapterDriverVendor2: "driverVendor2", + adapterDriverVersion2: "driverVersion2", + adapterDriverDate2: "driverDate2", + isGPU2Active: null, + + D2DEnabled: "direct2DEnabled", + DWriteEnabled: "directWriteEnabled", + DWriteVersion: "directWriteVersion", + cleartypeParameters: "clearTypeParameters", + TargetFrameRate: "targetFrameRate", + windowProtocol: null, + fontVisibilityDeterminationStr: "supportFontDetermination", + }; + + for (let prop in gfxInfoProps) { + try { + data[gfxInfoProps[prop] || prop] = gfxInfo[prop]; + } catch (e) {} + } + + if ("direct2DEnabled" in data && !data.direct2DEnabled) { + data.direct2DEnabledMessage = statusMsgForFeature( + Ci.nsIGfxInfo.FEATURE_DIRECT2D + ); + } + } + + let doc = new DOMParser().parseFromString("<html/>", "text/html"); + + function GetWebGLInfo(data, keyPrefix, contextType) { + data[keyPrefix + "Renderer"] = "-"; + data[keyPrefix + "Version"] = "-"; + data[keyPrefix + "DriverExtensions"] = "-"; + data[keyPrefix + "Extensions"] = "-"; + data[keyPrefix + "WSIInfo"] = "-"; + + // // + + let canvas = doc.createElement("canvas"); + canvas.width = 1; + canvas.height = 1; + + // // + + let creationError = null; + + canvas.addEventListener( + "webglcontextcreationerror", + + function (e) { + creationError = e.statusMessage; + } + ); + + let gl = null; + try { + gl = canvas.getContext(contextType); + } catch (e) { + if (!creationError) { + creationError = e.toString(); + } + } + if (!gl) { + data[keyPrefix + "Renderer"] = + creationError || "(no creation error info)"; + return; + } + + // // + + data[keyPrefix + "Extensions"] = gl.getSupportedExtensions().join(" "); + + // // + + let ext = gl.getExtension("MOZ_debug"); + // This extension is unconditionally available to chrome. No need to check. + let vendor = ext.getParameter(gl.VENDOR); + let renderer = ext.getParameter(gl.RENDERER); + + data[keyPrefix + "Renderer"] = vendor + " -- " + renderer; + data[keyPrefix + "Version"] = ext.getParameter(gl.VERSION); + data[keyPrefix + "DriverExtensions"] = ext.getParameter(ext.EXTENSIONS); + data[keyPrefix + "WSIInfo"] = ext.getParameter(ext.WSI_INFO); + + // // + + // Eagerly free resources. + let loseExt = gl.getExtension("WEBGL_lose_context"); + if (loseExt) { + loseExt.loseContext(); + } + } + + GetWebGLInfo(data, "webgl1", "webgl"); + GetWebGLInfo(data, "webgl2", "webgl2"); + + if (gfxInfo) { + let infoInfo = gfxInfo.getInfo(); + if (infoInfo) { + data.info = infoInfo; + } + + let failureIndices = {}; + + let failures = gfxInfo.getFailures(failureIndices); + if (failures.length) { + data.failures = failures; + if (failureIndices.value.length == failures.length) { + data.indices = failureIndices.value; + } + } + + data.featureLog = gfxInfo.getFeatureLog(); + data.crashGuards = gfxInfo.getActiveCrashGuards(); + } + + function getNavigator() { + for (let win of Services.ww.getWindowEnumerator()) { + let winUtils = win.windowUtils; + try { + // NOTE: windowless browser's windows should not be reported in the graphics troubleshoot report + if ( + winUtils.layerManagerType == "None" || + !winUtils.layerManagerRemote + ) { + continue; + } + const nav = win.navigator; + if (nav) { + return nav; + } + } catch (e) { + continue; + } + } + throw new Error("No window had window.navigator."); + } + + const navigator = getNavigator(); + + async function GetWebgpuInfo(adapterOpts) { + const ret = {}; + if (!navigator.gpu) { + ret["navigator.gpu"] = null; + return ret; + } + + const requestAdapterkey = `navigator.gpu.requestAdapter(${JSON.stringify( + adapterOpts + )})`; + + let adapter; + try { + adapter = await navigator.gpu.requestAdapter(adapterOpts); + } catch (e) { + // If WebGPU isn't supported or is blocked somehow, include + // that in the report. Anything else is an error which should + // have consequences (test failures, etc). + if (DOMException.isInstance(e) && e.name == "NotSupportedError") { + return { [requestAdapterkey]: { not_supported: e.message } }; + } + throw e; + } + + if (!adapter) { + ret[requestAdapterkey] = null; + return ret; + } + const desc = (ret[requestAdapterkey] = {}); + + desc.isFallbackAdapter = adapter.isFallbackAdapter; + + const adapterInfo = await adapter.requestAdapterInfo(); + // We can't directly enumerate properties of instances of `GPUAdapterInfo`s, so use the prototype instead. + const adapterInfoObj = {}; + for (const k of Object.keys(Object.getPrototypeOf(adapterInfo)).sort()) { + adapterInfoObj[k] = adapterInfo[k]; + } + desc[`requestAdapterInfo()`] = adapterInfoObj; + + desc.features = Array.from(adapter.features).sort(); + + desc.limits = {}; + const keys = Object.keys(Object.getPrototypeOf(adapter.limits)).sort(); // limits not directly enumerable? + for (const k of keys) { + desc.limits[k] = adapter.limits[k]; + } + + return ret; + } + + // Webgpu info is going to need awaits. + (async () => { + data.webgpuDefaultAdapter = await GetWebgpuInfo({}); + data.webgpuFallbackAdapter = await GetWebgpuInfo({ + forceFallbackAdapter: true, + }); + + done(data); + })(); + }, + + media: function media(done) { + function convertDevices(devices) { + if (!devices) { + return undefined; + } + let infos = []; + for (let i = 0; i < devices.length; ++i) { + let device = devices.queryElementAt(i, Ci.nsIAudioDeviceInfo); + infos.push({ + name: device.name, + groupId: device.groupId, + vendor: device.vendor, + type: device.type, + state: device.state, + preferred: device.preferred, + supportedFormat: device.supportedFormat, + defaultFormat: device.defaultFormat, + maxChannels: device.maxChannels, + defaultRate: device.defaultRate, + maxRate: device.maxRate, + minRate: device.minRate, + maxLatency: device.maxLatency, + minLatency: device.minLatency, + }); + } + return infos; + } + + let data = {}; + let winUtils = Services.wm.getMostRecentWindow("").windowUtils; + data.currentAudioBackend = winUtils.currentAudioBackend; + data.currentMaxAudioChannels = winUtils.currentMaxAudioChannels; + data.currentPreferredSampleRate = winUtils.currentPreferredSampleRate; + data.audioOutputDevices = convertDevices( + winUtils + .audioDevices(Ci.nsIDOMWindowUtils.AUDIO_OUTPUT) + .QueryInterface(Ci.nsIArray) + ); + data.audioInputDevices = convertDevices( + winUtils + .audioDevices(Ci.nsIDOMWindowUtils.AUDIO_INPUT) + .QueryInterface(Ci.nsIArray) + ); + + data.codecSupportInfo = "Unknown"; + + // We initialize gfxInfo here in the same way as in the media + // section -- should we break this out into a separate function? + try { + // nsIGfxInfo may not be implemented on some platforms. + var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo); + + // Note: CodecSupportInfo is not populated until we have + // actually instantiated a PDM. We may want to add a button + // or some other means of allowing the user to manually + // instantiate a PDM to ensure that the data is available. + data.codecSupportInfo = gfxInfo.CodecSupportInfo; + } catch (e) {} + + done(data); + }, + + accessibility: function accessibility(done) { + let data = {}; + data.isActive = Services.appinfo.accessibilityEnabled; + // eslint-disable-next-line mozilla/use-default-preference-values + try { + data.forceDisabled = Services.prefs.getIntPref( + "accessibility.force_disabled" + ); + } catch (e) {} + data.instantiator = Services.appinfo.accessibilityInstantiator; + done(data); + }, + + startupCache: function startupCache(done) { + const startupInfo = Cc["@mozilla.org/startupcacheinfo;1"].getService( + Ci.nsIStartupCacheInfo + ); + done({ + DiskCachePath: startupInfo.DiskCachePath, + IgnoreDiskCache: startupInfo.IgnoreDiskCache, + FoundDiskCacheOnInit: startupInfo.FoundDiskCacheOnInit, + WroteToDiskCache: startupInfo.WroteToDiskCache, + }); + }, + + libraryVersions: function libraryVersions(done) { + let data = {}; + let verInfo = Cc["@mozilla.org/security/nssversion;1"].getService( + Ci.nsINSSVersion + ); + for (let prop in verInfo) { + let match = /^([^_]+)_((Min)?Version)$/.exec(prop); + if (match) { + let verProp = match[2][0].toLowerCase() + match[2].substr(1); + data[match[1]] = data[match[1]] || {}; + data[match[1]][verProp] = verInfo[prop]; + } + } + done(data); + }, + + userJS: function userJS(done) { + let userJSFile = Services.dirsvc.get("PrefD", Ci.nsIFile); + userJSFile.append("user.js"); + done({ + exists: userJSFile.exists() && userJSFile.fileSize > 0, + }); + }, + + intl: function intl(done) { + const osPrefs = Cc["@mozilla.org/intl/ospreferences;1"].getService( + Ci.mozIOSPreferences + ); + done({ + localeService: { + requested: Services.locale.requestedLocales, + available: Services.locale.availableLocales, + supported: Services.locale.appLocalesAsBCP47, + regionalPrefs: Services.locale.regionalPrefsLocales, + defaultLocale: Services.locale.defaultLocale, + }, + osPrefs: { + systemLocales: osPrefs.systemLocales, + regionalPrefsLocales: osPrefs.regionalPrefsLocales, + }, + }); + }, + + async normandy(done) { + if (!AppConstants.MOZ_NORMANDY) { + done(); + return; + } + + const { PreferenceExperiments: NormandyPreferenceStudies } = + ChromeUtils.importESModule( + "resource://normandy/lib/PreferenceExperiments.sys.mjs" + ); + const { AddonStudies: NormandyAddonStudies } = ChromeUtils.importESModule( + "resource://normandy/lib/AddonStudies.sys.mjs" + ); + const { PreferenceRollouts: NormandyPreferenceRollouts } = + ChromeUtils.importESModule( + "resource://normandy/lib/PreferenceRollouts.sys.mjs" + ); + const { ExperimentManager } = ChromeUtils.importESModule( + "resource://nimbus/lib/ExperimentManager.sys.mjs" + ); + + // Get Normandy data in parallel, and sort each group by slug. + const [ + addonStudies, + prefRollouts, + prefStudies, + nimbusExperiments, + nimbusRollouts, + ] = await Promise.all( + [ + NormandyAddonStudies.getAllActive(), + NormandyPreferenceRollouts.getAllActive(), + NormandyPreferenceStudies.getAllActive(), + ExperimentManager.store + .ready() + .then(() => ExperimentManager.store.getAllActiveExperiments()), + ExperimentManager.store + .ready() + .then(() => ExperimentManager.store.getAllActiveRollouts()), + ].map(promise => + promise + .catch(error => { + console.error(error); + return []; + }) + .then(items => items.sort((a, b) => a.slug.localeCompare(b.slug))) + ) + ); + + done({ + addonStudies, + prefRollouts, + prefStudies, + nimbusExperiments, + nimbusRollouts, + }); + }, +}; + +if (AppConstants.MOZ_CRASHREPORTER) { + dataProviders.crashes = function crashes(done) { + const { CrashReports } = ChromeUtils.importESModule( + "resource://gre/modules/CrashReports.sys.mjs" + ); + let reports = CrashReports.getReports(); + let now = new Date(); + let reportsNew = reports.filter( + report => now - report.date < Troubleshoot.kMaxCrashAge + ); + let reportsSubmitted = reportsNew.filter(report => !report.pending); + let reportsPendingCount = reportsNew.length - reportsSubmitted.length; + let data = { submitted: reportsSubmitted, pending: reportsPendingCount }; + done(data); + }; +} + +if (AppConstants.MOZ_SANDBOX) { + dataProviders.sandbox = function sandbox(done) { + let data = {}; + if (AppConstants.unixstyle == "linux") { + const keys = [ + "hasSeccompBPF", + "hasSeccompTSync", + "hasPrivilegedUserNamespaces", + "hasUserNamespaces", + "canSandboxContent", + "canSandboxMedia", + ]; + + for (let key of keys) { + if (Services.sysinfo.hasKey(key)) { + data[key] = Services.sysinfo.getPropertyAsBool(key); + } + } + + let reporter = Cc["@mozilla.org/sandbox/syscall-reporter;1"].getService( + Ci.mozISandboxReporter + ); + const snapshot = reporter.snapshot(); + let syscalls = []; + for (let index = snapshot.begin; index < snapshot.end; ++index) { + let report = snapshot.getElement(index); + let { msecAgo, pid, tid, procType, syscall } = report; + let args = []; + for (let i = 0; i < report.numArgs; ++i) { + args.push(report.getArg(i)); + } + syscalls.push({ index, msecAgo, pid, tid, procType, syscall, args }); + } + data.syscallLog = syscalls; + } + + if (AppConstants.MOZ_SANDBOX) { + let sandboxSettings = Cc[ + "@mozilla.org/sandbox/sandbox-settings;1" + ].getService(Ci.mozISandboxSettings); + data.contentSandboxLevel = Services.prefs.getIntPref( + "security.sandbox.content.level" + ); + data.effectiveContentSandboxLevel = + sandboxSettings.effectiveContentSandboxLevel; + + if (AppConstants.platform == "win") { + data.contentWin32kLockdownState = + sandboxSettings.contentWin32kLockdownStateString; + + data.supportSandboxGpuLevel = Services.prefs.getIntPref( + "security.sandbox.gpu.level" + ); + } + } + + done(data); + }; +} + +if (AppConstants.ENABLE_WEBDRIVER) { + dataProviders.remoteAgent = function remoteAgent(done) { + const { RemoteAgent } = ChromeUtils.importESModule( + "chrome://remote/content/components/RemoteAgent.sys.mjs" + ); + const { running, scheme, host, port } = RemoteAgent; + let url = ""; + if (running) { + url = `${scheme}://${host}:${port}/`; + } + done({ running, url }); + }; +} diff --git a/toolkit/modules/UpdateUtils.sys.mjs b/toolkit/modules/UpdateUtils.sys.mjs new file mode 100644 index 0000000000..01149dbae3 --- /dev/null +++ b/toolkit/modules/UpdateUtils.sys.mjs @@ -0,0 +1,1130 @@ +/* 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 { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs", + WindowsVersionInfo: + "resource://gre/modules/components-utils/WindowsVersionInfo.sys.mjs", + ctypes: "resource://gre/modules/ctypes.sys.mjs", +}); + +const PER_INSTALLATION_PREFS_PLATFORMS = ["win"]; + +// The file that stores Application Update configuration settings. The file is +// located in the update directory which makes it a common setting across all +// application profiles and allows the Background Update Agent to read it. +const FILE_UPDATE_CONFIG_JSON = "update-config.json"; +const FILE_UPDATE_LOCALE = "update.locale"; +const PREF_APP_DISTRIBUTION = "distribution.id"; +const PREF_APP_DISTRIBUTION_VERSION = "distribution.version"; + +export var UpdateUtils = { + _locale: undefined, + _configFilePath: undefined, + + /** + * Read the update channel from defaults only. We do this to ensure that + * the channel is tightly coupled with the application and does not apply + * to other instances of the application that may use the same profile. + * + * @param [optional] aIncludePartners + * Whether or not to include the partner bits. Default: true. + */ + getUpdateChannel(aIncludePartners = true) { + let defaults = Services.prefs.getDefaultBranch(null); + let channel = defaults.getCharPref( + "app.update.channel", + AppConstants.MOZ_UPDATE_CHANNEL + ); + + if (aIncludePartners) { + try { + let partners = Services.prefs.getChildList("app.partner.").sort(); + if (partners.length) { + channel += "-cck"; + partners.forEach(function (prefName) { + channel += "-" + Services.prefs.getCharPref(prefName); + }); + } + } catch (e) { + console.error(e); + } + } + + return channel; + }, + + get UpdateChannel() { + return this.getUpdateChannel(); + }, + + /** + * Formats a URL by replacing %...% values with OS, build and locale specific + * values. + * + * @param url + * The URL to format. + * @return The formatted URL. + */ + async formatUpdateURL(url) { + const locale = await this.getLocale(); + + return url.replace(/%(\w+)%/g, (match, name) => { + let replacement = match; + switch (name) { + case "PRODUCT": + replacement = Services.appinfo.name; + break; + case "VERSION": + replacement = Services.appinfo.version; + break; + case "BUILD_ID": + replacement = Services.appinfo.appBuildID; + break; + case "BUILD_TARGET": + replacement = Services.appinfo.OS + "_" + this.ABI; + break; + case "OS_VERSION": + replacement = this.OSVersion; + break; + case "LOCALE": + replacement = locale; + break; + case "CHANNEL": + replacement = this.UpdateChannel; + break; + case "PLATFORM_VERSION": + replacement = Services.appinfo.platformVersion; + break; + case "SYSTEM_CAPABILITIES": + replacement = getSystemCapabilities(); + break; + case "DISTRIBUTION": + replacement = getDistributionPrefValue(PREF_APP_DISTRIBUTION); + break; + case "DISTRIBUTION_VERSION": + replacement = getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION); + break; + } + return encodeURIComponent(replacement); + }); + }, + + /** + * Gets the locale from the update.locale file for replacing %LOCALE% in the + * update url. The update.locale file can be located in the application + * directory or the GRE directory with preference given to it being located in + * the application directory. + */ + async getLocale() { + if (this._locale !== undefined) { + return this._locale; + } + + for (let res of ["app", "gre"]) { + const url = "resource://" + res + "/" + FILE_UPDATE_LOCALE; + let data; + try { + data = await fetch(url); + } catch (e) { + continue; + } + const locale = await data.text(); + if (locale) { + return (this._locale = locale.trim()); + } + } + + console.error( + FILE_UPDATE_LOCALE, + " file doesn't exist in either the application or GRE directories" + ); + + return (this._locale = null); + }, + + /* Get the path to the config file. */ + getConfigFilePath() { + let path = PathUtils.join( + Services.dirsvc.get("UpdRootD", Ci.nsIFile).path, + FILE_UPDATE_CONFIG_JSON + ); + return (this._configFilePath = path); + }, + + get configFilePath() { + if (this._configFilePath !== undefined) { + return this._configFilePath; + } + return this.getConfigFilePath(); + }, + + /** + * Determines whether or not the Application Update Service automatically + * downloads and installs updates. This corresponds to whether or not the user + * has selected "Automatically install updates" in about:preferences. + * + * On Windows, this setting is shared across all profiles for the installation + * and is read asynchronously from the file. On other operating systems, this + * setting is stored in a pref and is thus a per-profile setting. + * + * @return A Promise that resolves with a boolean. + */ + async getAppUpdateAutoEnabled() { + return this.readUpdateConfigSetting("app.update.auto"); + }, + + /** + * Toggles whether the Update Service automatically downloads and installs + * updates. This effectively selects between the "Automatically install + * updates" and "Check for updates but let you choose to install them" options + * in about:preferences. + * + * On Windows, this setting is shared across all profiles for the installation + * and is written asynchronously to the file. On other operating systems, this + * setting is stored in a pref and is thus a per-profile setting. + * + * If this method is called when the setting is locked, the returned promise + * will reject. The lock status can be determined with + * UpdateUtils.appUpdateAutoSettingIsLocked() + * + * @param enabled If set to true, automatic download and installation of + * updates will be enabled. If set to false, this will be + * disabled. + * @return A Promise that, once the setting has been saved, resolves with the + * boolean value that was saved. If the setting could not be + * successfully saved, the Promise will reject. + * On Windows, where this setting is stored in a file, this Promise + * may reject with an I/O error. + * On other operating systems, this promise should not reject as + * this operation simply sets a pref. + */ + async setAppUpdateAutoEnabled(enabledValue) { + return this.writeUpdateConfigSetting("app.update.auto", !!enabledValue); + }, + + /** + * This function should be used to determine if the automatic application + * update setting is locked by an enterprise policy + * + * @return true if the automatic update setting is currently locked. + * Otherwise, false. + */ + appUpdateAutoSettingIsLocked() { + return this.appUpdateSettingIsLocked("app.update.auto"); + }, + + /** + * Indicates whether or not per-installation prefs are supported on this + * platform. + */ + PER_INSTALLATION_PREFS_SUPPORTED: PER_INSTALLATION_PREFS_PLATFORMS.includes( + AppConstants.platform + ), + + /** + * Possible per-installation pref types. + */ + PER_INSTALLATION_PREF_TYPE_BOOL: "boolean", + PER_INSTALLATION_PREF_TYPE_ASCII_STRING: "ascii", + PER_INSTALLATION_PREF_TYPE_INT: "integer", + + /** + * We want the preference definitions to be part of UpdateUtils for a couple + * of reasons. It's a clean way for consumers to look up things like observer + * topic names. It also allows us to manipulate the supported prefs during + * testing. However, we want to use values out of UpdateUtils (like pref + * types) to construct this object. Therefore, this will initially be a + * placeholder, which we will properly define after the UpdateUtils object + * definition. + */ + PER_INSTALLATION_PREFS: null, + + /** + * This function initializes per-installation prefs. Note that it does not + * need to be called manually; it is already called within the file. + * + * This function is called on startup, so it does not read or write to disk. + */ + initPerInstallPrefs() { + // If we don't have per-installation prefs, we store the update config in + // preferences. In that case, the best way to notify observers of this + // setting is just to propagate it from a pref observer. This ensures that + // the expected observers still get notified, even if a user manually + // changes the pref value. + if (!UpdateUtils.PER_INSTALLATION_PREFS_SUPPORTED) { + let initialConfig = {}; + for (const [prefName, pref] of Object.entries( + UpdateUtils.PER_INSTALLATION_PREFS + )) { + const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type]; + + try { + let initialValue = prefTypeFns.getProfilePref(prefName); + initialConfig[prefName] = initialValue; + } catch (e) {} + + Services.prefs.addObserver(prefName, async (subject, topic, data) => { + let config = { ...gUpdateConfigCache }; + config[prefName] = await UpdateUtils.readUpdateConfigSetting( + prefName + ); + maybeUpdateConfigChanged(config); + }); + } + + // On the first call to maybeUpdateConfigChanged, it has nothing to + // compare its input to, so it just populates the cache and doesn't notify + // any observers. This makes sense during normal usage, because the first + // call will be on the first config file read, and we don't want to notify + // observers of changes on the first read. But that means that when + // propagating pref observers, we need to make one initial call to + // simulate that initial read so that the cache will be populated when the + // first pref observer fires. + maybeUpdateConfigChanged(initialConfig); + } + }, + + /** + * Reads an installation-specific configuration setting from the update config + * JSON file. This function is guaranteed not to throw. If there are problems + * reading the file, the default value will be returned so that update can + * proceed. This is particularly important since the configuration file is + * writable by anyone and we don't want an unprivileged user to be able to + * break update for other users. + * + * If relevant policies are active, this function will read the policy value + * rather than the stored value. + * + * @param prefName + * The preference to read. Must be a key of the + * PER_INSTALLATION_PREFS object. + * @return A Promise that resolves with the pref's value. + */ + readUpdateConfigSetting(prefName) { + if (!(prefName in this.PER_INSTALLATION_PREFS)) { + return Promise.reject( + new Error( + `UpdateUtils.readUpdateConfigSetting: Unknown per-installation ` + + `pref '${prefName}'` + ) + ); + } + + const pref = this.PER_INSTALLATION_PREFS[prefName]; + const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type]; + + if (Services.policies && "policyFn" in pref) { + let policyValue = pref.policyFn(); + if (policyValue !== null) { + return Promise.resolve(policyValue); + } + } + + if (!this.PER_INSTALLATION_PREFS_SUPPORTED) { + // If we don't have per-installation prefs, we use regular preferences. + let prefValue = prefTypeFns.getProfilePref(prefName, pref.defaultValue); + return Promise.resolve(prefValue); + } + + let readPromise = updateConfigIOPromise + // All promises returned by (read|write)UpdateConfigSetting are part of a + // single promise chain in order to serialize disk operations. But we + // don't want the entire promise chain to reject when one operation fails. + // So we are going to silently clear any rejections the promise chain + // might contain. + // + // We will also pass an empty function for the first then() argument as + // well, just to make sure we are starting fresh rather than potentially + // propagating some stale value. + .then( + () => {}, + () => {} + ) + .then(readUpdateConfig) + .then(maybeUpdateConfigChanged) + .then(config => { + return readEffectiveValue(config, prefName); + }); + updateConfigIOPromise = readPromise; + return readPromise; + }, + + /** + * Changes an installation-specific configuration setting by writing it to + * the update config JSON file. + * + * If this method is called on a prefName that is locked, the returned promise + * will reject. The lock status can be determined with + * appUpdateSettingIsLocked(). + * + * @param prefName + * The preference to change. This must be a key of the + * PER_INSTALLATION_PREFS object. + * @param value + * The value to be written. Its type must match + * PER_INSTALLATION_PREFS[prefName].type + * @param options + * Optional. An object containing any of the following keys: + * setDefaultOnly + * If set to true, the default branch value will be set rather + * than user value. If a user value is set for this pref, this + * will have no effect on the pref's effective value. + * NOTE - The behavior of the default pref branch currently + * differs depending on whether the current platform + * supports per-installation prefs. If they are + * supported, default branch values persist across + * Firefox sessions. If they aren't supported, default + * branch values reset when Firefox shuts down. + * @return A Promise that, once the setting has been saved, resolves with the + * value that was saved. + * @throw If there is an I/O error when attempting to write to the config + * file, the returned Promise will reject with a DOMException. + */ + writeUpdateConfigSetting(prefName, value, options) { + if (!(prefName in this.PER_INSTALLATION_PREFS)) { + return Promise.reject( + new Error( + `UpdateUtils.writeUpdateConfigSetting: Unknown per-installation ` + + `pref '${prefName}'` + ) + ); + } + + if (this.appUpdateSettingIsLocked(prefName)) { + return Promise.reject( + new Error( + `UpdateUtils.writeUpdateConfigSetting: Unable to change value of ` + + `setting '${prefName}' because it is locked by policy` + ) + ); + } + + if (!options) { + options = {}; + } + + const pref = this.PER_INSTALLATION_PREFS[prefName]; + const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type]; + + if (!prefTypeFns.isValid(value)) { + return Promise.reject( + new Error( + `UpdateUtils.writeUpdateConfigSetting: Attempted to change pref ` + + `'${prefName} to invalid value: ${JSON.stringify(value)}` + ) + ); + } + + if (!this.PER_INSTALLATION_PREFS_SUPPORTED) { + // If we don't have per-installation prefs, we use regular preferences. + if (options.setDefaultOnly) { + prefTypeFns.setProfileDefaultPref(prefName, value); + } else { + prefTypeFns.setProfilePref(prefName, value); + } + // Rather than call maybeUpdateConfigChanged, a pref observer has + // been connected to the relevant pref. This allows us to catch direct + // changes to prefs (which Firefox shouldn't be doing, but the user + // might do in about:config). + return Promise.resolve(value); + } + + let writePromise = updateConfigIOPromise + // All promises returned by (read|write)UpdateConfigSetting are part of a + // single promise chain in order to serialize disk operations. But we + // don't want the entire promise chain to reject when one operation fails. + // So we are going to silently clear any rejections the promise chain + // might contain. + // + // We will also pass an empty function for the first then() argument as + // well, just to make sure we are starting fresh rather than potentially + // propagating some stale value. + .then( + () => {}, + () => {} + ) + // We always re-read the update config before writing, rather than using a + // cached version. Otherwise, two simultaneous instances may overwrite + // each other's changes. + .then(readUpdateConfig) + .then(async config => { + setConfigValue(config, prefName, value, { + setDefaultOnly: !!options.setDefaultOnly, + }); + + try { + await writeUpdateConfig(config); + return config; + } catch (e) { + console.error( + "UpdateUtils.writeUpdateConfigSetting: App update configuration " + + "file write failed. Exception: ", + e + ); + // Re-throw the error so the caller knows that writing the value in + // the app update config file failed. + throw e; + } + }) + .then(maybeUpdateConfigChanged) + .then(() => { + // If this value wasn't written, a previous promise in the chain will + // have thrown, so we can unconditionally return the expected written + // value as the value that was written. + return value; + }); + updateConfigIOPromise = writePromise; + return writePromise; + }, + + /** + * Returns true if the specified pref is controlled by policy and thus should + * not be changeable by the user. + */ + appUpdateSettingIsLocked(prefName) { + if (!(prefName in UpdateUtils.PER_INSTALLATION_PREFS)) { + return Promise.reject( + new Error( + `UpdateUtils.appUpdateSettingIsLocked: Unknown per-installation pref '${prefName}'` + ) + ); + } + + // If we don't have policy support, nothing can be locked. + if (!Services.policies) { + return false; + } + + const pref = UpdateUtils.PER_INSTALLATION_PREFS[prefName]; + if (!pref.policyFn) { + return false; + } + const policyValue = pref.policyFn(); + return policyValue !== null; + }, +}; + +const PER_INSTALLATION_DEFAULTS_BRANCH = "__DEFAULTS__"; + +/** + * Some prefs are specific to the installation, not the profile. They are + * stored in JSON format in FILE_UPDATE_CONFIG_JSON. + * Not all platforms currently support per-installation prefs, in which case + * we fall back to using profile-specific prefs. + * + * Note: These prefs should always be accessed through UpdateUtils. Do NOT + * attempt to read or write their prefs directly. + * + * Keys in this object should be the name of the pref. The same name will be + * used whether we are writing it to the per-installation or per-profile pref. + * Values in this object should be objects with the following keys: + * type + * Must be one of the Update.PER_INSTALLATION_PREF_TYPE_* values, defined + * above. + * defaultValue + * The default value to use for this pref if no value is set. This must be + * of a type that is compatible with the type value specified. + * migrate + * Optional - defaults to false. A boolean indicating whether an existing + * value in the profile-specific prefs ought to be migrated to an + * installation specific pref. This is useful for prefs like + * app.update.auto that used to be profile-specific prefs. + * Note - Migration currently happens only on the creation of the JSON + * file. If we want to add more prefs that require migration, we + * will probably need to change this. + * observerTopic + * When a config value is changed, an observer will be fired, much like + * the existing preference observers. This specifies the topic of the + * observer that will be fired. + * policyFn + * Optional. If defined, should be a function that returns null or a value + * of the specified type of this pref. If null is returned, this has no + * effect. If another value is returned, it will be used rather than + * reading the pref. This function will only be called if + * Services.policies is defined. Asynchronous functions are not currently + * supported. + */ +UpdateUtils.PER_INSTALLATION_PREFS = { + "app.update.auto": { + type: UpdateUtils.PER_INSTALLATION_PREF_TYPE_BOOL, + defaultValue: true, + migrate: true, + observerTopic: "auto-update-config-change", + policyFn: () => { + if (!Services.policies.isAllowed("app-auto-updates-off")) { + // We aren't allowed to turn off auto-update - it is forced on. + return true; + } + if (!Services.policies.isAllowed("app-auto-updates-on")) { + // We aren't allowed to turn on auto-update - it is forced off. + return false; + } + return null; + }, + }, + "app.update.background.enabled": { + type: UpdateUtils.PER_INSTALLATION_PREF_TYPE_BOOL, + defaultValue: true, + observerTopic: "background-update-config-change", + policyFn: () => { + if (!Services.policies.isAllowed("app-background-update-off")) { + // We aren't allowed to turn off background update - it is forced on. + return true; + } + if (!Services.policies.isAllowed("app-background-update-on")) { + // We aren't allowed to turn on background update - it is forced off. + return false; + } + return null; + }, + }, +}; + +const TYPE_SPECIFIC_PREF_FNS = { + [UpdateUtils.PER_INSTALLATION_PREF_TYPE_BOOL]: { + getProfilePref: Services.prefs.getBoolPref, + setProfilePref: Services.prefs.setBoolPref, + setProfileDefaultPref: (pref, value) => { + let defaults = Services.prefs.getDefaultBranch(""); + defaults.setBoolPref(pref, value); + }, + isValid: value => typeof value == "boolean", + }, + [UpdateUtils.PER_INSTALLATION_PREF_TYPE_ASCII_STRING]: { + getProfilePref: Services.prefs.getCharPref, + setProfilePref: Services.prefs.setCharPref, + setProfileDefaultPref: (pref, value) => { + let defaults = Services.prefs.getDefaultBranch(""); + defaults.setCharPref(pref, value); + }, + isValid: value => typeof value == "string", + }, + [UpdateUtils.PER_INSTALLATION_PREF_TYPE_INT]: { + getProfilePref: Services.prefs.getIntPref, + setProfilePref: Services.prefs.setIntPref, + setProfileDefaultPref: (pref, value) => { + let defaults = Services.prefs.getDefaultBranch(""); + defaults.setIntPref(pref, value); + }, + isValid: value => Number.isInteger(value), + }, +}; + +/** + * Used for serializing reads and writes of the app update json config file so + * the writes don't happen out of order and the last write is the one that + * the sets the value. + */ +var updateConfigIOPromise = Promise.resolve(); + +/** + * Returns a pref name that we will use to keep track of if the passed pref has + * been migrated already, so we don't end up migrating it twice. + */ +function getPrefMigratedPref(prefName) { + return prefName + ".migrated"; +} + +/** + * @return true if prefs need to be migrated from profile-specific prefs to + * installation-specific prefs. + */ +function updateConfigNeedsMigration() { + for (const [prefName, pref] of Object.entries( + UpdateUtils.PER_INSTALLATION_PREFS + )) { + if (pref.migrate) { + let migratedPrefName = getPrefMigratedPref(prefName); + let migrated = Services.prefs.getBoolPref(migratedPrefName, false); + if (!migrated) { + return true; + } + } + } + return false; +} + +function setUpdateConfigMigrationDone() { + for (const [prefName, pref] of Object.entries( + UpdateUtils.PER_INSTALLATION_PREFS + )) { + if (pref.migrate) { + let migratedPrefName = getPrefMigratedPref(prefName); + Services.prefs.setBoolPref(migratedPrefName, true); + } + } +} + +/** + * Deletes the migrated data. + */ +function onMigrationSuccessful() { + for (const [prefName, pref] of Object.entries( + UpdateUtils.PER_INSTALLATION_PREFS + )) { + if (pref.migrate) { + Services.prefs.clearUserPref(prefName); + } + } +} + +function makeMigrationUpdateConfig() { + let config = makeDefaultUpdateConfig(); + + for (const [prefName, pref] of Object.entries( + UpdateUtils.PER_INSTALLATION_PREFS + )) { + if (!pref.migrate) { + continue; + } + let migratedPrefName = getPrefMigratedPref(prefName); + let alreadyMigrated = Services.prefs.getBoolPref(migratedPrefName, false); + if (alreadyMigrated) { + continue; + } + + const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type]; + + let prefHasValue = true; + let prefValue; + try { + // Without a second argument, this will throw if the pref has no user + // value or default value. + prefValue = prefTypeFns.getProfilePref(prefName); + } catch (e) { + prefHasValue = false; + } + if (prefHasValue) { + setConfigValue(config, prefName, prefValue); + } + } + + return config; +} + +function makeDefaultUpdateConfig() { + let config = {}; + + for (const [prefName, pref] of Object.entries( + UpdateUtils.PER_INSTALLATION_PREFS + )) { + setConfigValue(config, prefName, pref.defaultValue, { + setDefaultOnly: true, + }); + } + + return config; +} + +/** + * Sets the specified value in the config object. + * + * @param config + * The config object for which to set the value + * @param prefName + * The name of the preference to set. + * @param prefValue + * The value to set the preference to. + * @param options + * Optional. An object containing any of the following keys: + * setDefaultOnly + * If set to true, the default value will be set rather than + * user value. If a user value is set for this pref, this will + * have no effect on the pref's effective value. + */ +function setConfigValue(config, prefName, prefValue, options) { + if (!options) { + options = {}; + } + + if (options.setDefaultOnly) { + if (!(PER_INSTALLATION_DEFAULTS_BRANCH in config)) { + config[PER_INSTALLATION_DEFAULTS_BRANCH] = {}; + } + config[PER_INSTALLATION_DEFAULTS_BRANCH][prefName] = prefValue; + } else if (prefValue != readDefaultValue(config, prefName)) { + config[prefName] = prefValue; + } else { + delete config[prefName]; + } +} + +/** + * Reads the specified pref out of the given configuration object. + * If a user value of the pref is set, that will be returned. If only a default + * branch value is set, that will be returned. Otherwise, the default value from + * PER_INSTALLATION_PREFS will be returned. + * + * Values will be validated before being returned. Invalid values are ignored. + * + * @param config + * The configuration object to read. + * @param prefName + * The name of the preference to read. + * @return The value of the preference. + */ +function readEffectiveValue(config, prefName) { + if (!(prefName in UpdateUtils.PER_INSTALLATION_PREFS)) { + throw new Error( + `readEffectiveValue: Unknown per-installation pref '${prefName}'` + ); + } + const pref = UpdateUtils.PER_INSTALLATION_PREFS[prefName]; + const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type]; + + if (prefName in config) { + if (prefTypeFns.isValid(config[prefName])) { + return config[prefName]; + } + console.error( + `readEffectiveValue: Got invalid value for update config's` + + ` '${prefName}' value: "${config[prefName]}"` + ); + } + return readDefaultValue(config, prefName); +} + +/** + * Reads the default branch pref out of the given configuration object. If one + * is not set, the default value from PER_INSTALLATION_PREFS will be returned. + * + * Values will be validated before being returned. Invalid values are ignored. + * + * @param config + * The configuration object to read. + * @param prefName + * The name of the preference to read. + * @return The value of the preference. + */ +function readDefaultValue(config, prefName) { + if (!(prefName in UpdateUtils.PER_INSTALLATION_PREFS)) { + throw new Error( + `readDefaultValue: Unknown per-installation pref '${prefName}'` + ); + } + const pref = UpdateUtils.PER_INSTALLATION_PREFS[prefName]; + const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type]; + + if (PER_INSTALLATION_DEFAULTS_BRANCH in config) { + let defaults = config[PER_INSTALLATION_DEFAULTS_BRANCH]; + if (prefName in defaults) { + if (prefTypeFns.isValid(defaults[prefName])) { + return defaults[prefName]; + } + console.error( + `readEffectiveValue: Got invalid default value for update` + + ` config's '${prefName}' value: "${defaults[prefName]}"` + ); + } + } + return pref.defaultValue; +} + +/** + * Reads the update config and, if necessary, performs migration of un-migrated + * values. We don't want to completely give up on update if this file is + * unavailable, so default values will be returned on failure rather than + * throwing an error. + * + * @return An Update Config object. + */ +async function readUpdateConfig() { + try { + let config = await IOUtils.readJSON(UpdateUtils.getConfigFilePath()); + + // We only migrate once. If we read something, the migration has already + // happened so we should make sure it doesn't happen again. + setUpdateConfigMigrationDone(); + + return config; + } catch (e) { + if (DOMException.isInstance(e) && e.name == "NotFoundError") { + if (updateConfigNeedsMigration()) { + const migrationConfig = makeMigrationUpdateConfig(); + setUpdateConfigMigrationDone(); + try { + await writeUpdateConfig(migrationConfig); + onMigrationSuccessful(); + return migrationConfig; + } catch (e) { + console.error("readUpdateConfig: Migration failed: ", e); + } + } + } else { + // We only migrate once. If we got an error other than the file not + // existing, the migration has already happened so we should make sure + // it doesn't happen again. + setUpdateConfigMigrationDone(); + + console.error( + "readUpdateConfig: Unable to read app update configuration file. " + + "Exception: ", + e + ); + } + return makeDefaultUpdateConfig(); + } +} + +/** + * Writes the given configuration to the disk. + * + * @param config + * The configuration object to write. + * @return The configuration object written. + * @throw A DOMException will be thrown on I/O error. + */ +async function writeUpdateConfig(config) { + let path = UpdateUtils.getConfigFilePath(); + await IOUtils.writeJSON(path, config, { tmpPath: `${path}.tmp` }); + return config; +} + +var gUpdateConfigCache; +/** + * Notifies observers if any update config prefs have changed. + * + * @param config + * The most up-to-date config object. + * @return The same config object that was passed in. + */ +function maybeUpdateConfigChanged(config) { + if (!gUpdateConfigCache) { + // We don't want to generate a change notification for every pref on the + // first read of the session. + gUpdateConfigCache = config; + return config; + } + + for (const [prefName, pref] of Object.entries( + UpdateUtils.PER_INSTALLATION_PREFS + )) { + let newPrefValue = readEffectiveValue(config, prefName); + let oldPrefValue = readEffectiveValue(gUpdateConfigCache, prefName); + if (newPrefValue != oldPrefValue) { + Services.obs.notifyObservers( + null, + pref.observerTopic, + newPrefValue.toString() + ); + } + } + + gUpdateConfigCache = config; + return config; +} + +/** + * Note that this function sets up observers only, it does not do any I/O. + */ +UpdateUtils.initPerInstallPrefs(); + +/* Get the distribution pref values, from defaults only */ +function getDistributionPrefValue(aPrefName) { + let value = Services.prefs + .getDefaultBranch(null) + .getCharPref(aPrefName, "default"); + if (!value) { + value = "default"; + } + return value; +} + +function getSystemCapabilities() { + return "ISET:" + lazy.gInstructionSet + ",MEM:" + getMemoryMB(); +} + +/** + * Gets the RAM size in megabytes. This will round the value because sysinfo + * doesn't always provide RAM in multiples of 1024. + */ +function getMemoryMB() { + let memoryMB = "unknown"; + try { + memoryMB = Services.sysinfo.getProperty("memsize"); + if (memoryMB) { + memoryMB = Math.round(memoryMB / 1024 / 1024); + } + } catch (e) { + console.error("Error getting system info memsize property. Exception: ", e); + } + return memoryMB; +} + +/** + * Gets the supported CPU instruction set. + */ +ChromeUtils.defineLazyGetter(lazy, "gInstructionSet", function aus_gIS() { + const CPU_EXTENSIONS = [ + "hasSSE4_2", + "hasSSE4_1", + "hasSSE4A", + "hasSSSE3", + "hasSSE3", + "hasSSE2", + "hasSSE", + "hasMMX", + "hasNEON", + "hasARMv7", + "hasARMv6", + ]; + for (let ext of CPU_EXTENSIONS) { + if (Services.sysinfo.getProperty(ext)) { + return ext.substring(3); + } + } + + return "unknown"; +}); + +/* Windows only getter that returns the processor architecture. */ +ChromeUtils.defineLazyGetter(lazy, "gWinCPUArch", function aus_gWinCPUArch() { + // Get processor architecture + let arch = "unknown"; + + const WORD = lazy.ctypes.uint16_t; + const DWORD = lazy.ctypes.uint32_t; + + // This structure is described at: + // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx + const SYSTEM_INFO = new lazy.ctypes.StructType("SYSTEM_INFO", [ + { wProcessorArchitecture: WORD }, + { wReserved: WORD }, + { dwPageSize: DWORD }, + { lpMinimumApplicationAddress: lazy.ctypes.voidptr_t }, + { lpMaximumApplicationAddress: lazy.ctypes.voidptr_t }, + { dwActiveProcessorMask: DWORD.ptr }, + { dwNumberOfProcessors: DWORD }, + { dwProcessorType: DWORD }, + { dwAllocationGranularity: DWORD }, + { wProcessorLevel: WORD }, + { wProcessorRevision: WORD }, + ]); + + let kernel32 = false; + try { + kernel32 = lazy.ctypes.open("Kernel32"); + } catch (e) { + console.error("Unable to open kernel32! Exception: ", e); + } + + if (kernel32) { + try { + let GetNativeSystemInfo = kernel32.declare( + "GetNativeSystemInfo", + lazy.ctypes.winapi_abi, + lazy.ctypes.void_t, + SYSTEM_INFO.ptr + ); + let winSystemInfo = SYSTEM_INFO(); + // Default to unknown + winSystemInfo.wProcessorArchitecture = 0xffff; + + GetNativeSystemInfo(winSystemInfo.address()); + switch (winSystemInfo.wProcessorArchitecture) { + case 12: + arch = "aarch64"; + break; + case 9: + arch = "x64"; + break; + case 6: + arch = "IA64"; + break; + case 0: + arch = "x86"; + break; + } + } catch (e) { + console.error("Error getting processor architecture. Exception: ", e); + } finally { + kernel32.close(); + } + } + + return arch; +}); + +ChromeUtils.defineLazyGetter(UpdateUtils, "ABI", function () { + let abi = null; + try { + abi = Services.appinfo.XPCOMABI; + } catch (e) { + console.error("XPCOM ABI unknown"); + } + + if (AppConstants.platform == "win") { + // Windows build should report the CPU architecture that it's running on. + abi += "-" + lazy.gWinCPUArch; + } + + if (AppConstants.ASAN) { + // Allow ASan builds to receive their own updates + abi += "-asan"; + } + + return abi; +}); + +ChromeUtils.defineLazyGetter(UpdateUtils, "OSVersion", function () { + let osVersion; + try { + osVersion = + Services.sysinfo.getProperty("name") + + " " + + Services.sysinfo.getProperty("version"); + } catch (e) { + console.error("OS Version unknown."); + } + + if (osVersion) { + if (AppConstants.platform == "win") { + // Add service pack and build number + try { + const { servicePackMajor, servicePackMinor, buildNumber } = + lazy.WindowsVersionInfo.get(); + osVersion += `.${servicePackMajor}.${servicePackMinor}.${buildNumber}`; + } catch (err) { + console.error("Unable to retrieve windows version information: ", err); + osVersion += ".unknown"; + } + + // add UBR if on Windows 10 + if ( + Services.vc.compare(Services.sysinfo.getProperty("version"), "10") >= 0 + ) { + const WINDOWS_UBR_KEY_PATH = + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"; + let ubr = lazy.WindowsRegistry.readRegKey( + Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, + WINDOWS_UBR_KEY_PATH, + "UBR", + Ci.nsIWindowsRegKey.WOW64_64 + ); + if (ubr !== undefined) { + osVersion += `.${ubr}`; + } else { + osVersion += ".unknown"; + } + } + + // Add processor architecture + osVersion += " (" + lazy.gWinCPUArch + ")"; + } + + try { + osVersion += + " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")"; + } catch (e) { + // Not all platforms have a secondary widget library, so an error is nothing to worry about. + } + osVersion = encodeURIComponent(osVersion); + } + return osVersion; +}); diff --git a/toolkit/modules/WebChannel.sys.mjs b/toolkit/modules/WebChannel.sys.mjs new file mode 100644 index 0000000000..1b5d725cbd --- /dev/null +++ b/toolkit/modules/WebChannel.sys.mjs @@ -0,0 +1,274 @@ +/* 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/. */ + +/** + * WebChannel is an abstraction that uses the Message Manager and Custom Events + * to create a two-way communication channel between chrome and content code. + */ + +const ERRNO_UNKNOWN_ERROR = 999; +const ERROR_UNKNOWN = "UNKNOWN_ERROR"; + +/** + * WebChannelBroker is a global object that helps manage WebChannel objects. + * This object handles channel registration, origin validation and message multiplexing. + */ + +export var WebChannelBroker = Object.create({ + /** + * Register a new channel that callbacks messages + * based on proper origin and channel name + * + * @param channel {WebChannel} + */ + registerChannel(channel) { + if (!this._channelMap.has(channel)) { + this._channelMap.set(channel); + } else { + console.error("Failed to register the channel. Channel already exists."); + } + }, + + /** + * Unregister a channel + * + * @param channelToRemove {WebChannel} + * WebChannel to remove from the channel map + * + * Removes the specified channel from the channel map + */ + unregisterChannel(channelToRemove) { + if (!this._channelMap.delete(channelToRemove)) { + console.error("Failed to unregister the channel. Channel not found."); + } + }, + + /** + * Object to store pairs of message origins and callback functions + */ + _channelMap: new Map(), + + /** + * Deliver a message to a registered channel. + * + * @returns bool whether we managed to find a registered channel. + */ + tryToDeliver(data, sendingContext) { + let validChannelFound = false; + data.message = data.message || {}; + + for (var channel of this._channelMap.keys()) { + if ( + channel.id === data.id && + channel._originCheckCallback(sendingContext.principal) + ) { + validChannelFound = true; + channel.deliver(data, sendingContext); + } + } + return validChannelFound; + }, +}); + +/** + * Creates a new WebChannel that listens and sends messages over some channel id + * + * @param id {String} + * WebChannel id + * @param originOrPermission {nsIURI/string} + * If an nsIURI, incoming events will be accepted from any origin matching + * that URI's origin. + * If a string, it names a permission, and incoming events will be accepted + * from any https:// origin that has been granted that permission by the + * permission manager. + * @constructor + */ +export var WebChannel = function (id, originOrPermission) { + if (!id || !originOrPermission) { + throw new Error("WebChannel id and originOrPermission are required."); + } + + this.id = id; + // originOrPermission can be either an nsIURI or a string representing a + // permission name. + if (typeof originOrPermission == "string") { + this._originCheckCallback = requestPrincipal => { + // Accept events from any secure origin having the named permission. + // The permission manager operates on domain names rather than true + // origins (bug 1066517). To mitigate that, we explicitly check that + // the scheme is https://. + let uri = Services.io.newURI(requestPrincipal.originNoSuffix); + if (uri.scheme != "https") { + return false; + } + // OK - we have https - now we can check the permission. + let perm = Services.perms.testExactPermissionFromPrincipal( + requestPrincipal, + originOrPermission + ); + return perm == Ci.nsIPermissionManager.ALLOW_ACTION; + }; + } else { + // Accept events from any origin matching the given URI. + // We deliberately use `originNoSuffix` here because we only want to + // restrict based on the site's origin, not on other origin attributes + // such as containers or private browsing. + this._originCheckCallback = requestPrincipal => { + return originOrPermission.prePath === requestPrincipal.originNoSuffix; + }; + } + this._originOrPermission = originOrPermission; +}; + +WebChannel.prototype = { + /** + * WebChannel id + */ + id: null, + + /** + * The originOrPermission value passed to the constructor, mainly for + * debugging and tests. + */ + _originOrPermission: null, + + /** + * Callback that will be called with the principal of an incoming message + * to check if the request should be dispatched to the listeners. + */ + _originCheckCallback: null, + + /** + * WebChannelBroker that manages WebChannels + */ + _broker: WebChannelBroker, + + /** + * Callback that will be called with the contents of an incoming message + */ + _deliverCallback: null, + + /** + * Registers the callback for messages on this channel + * Registers the channel itself with the WebChannelBroker + * + * @param callback {Function} + * Callback that will be called when there is a message + * @param {String} id + * The WebChannel id that was used for this message + * @param {Object} message + * The message itself + * @param sendingContext {Object} + * The sending context of the source of the message. Can be passed to + * `send` to respond to a message. + * @param sendingContext.browser {browser} + * The <browser> object that captured the + * WebChannelMessageToChrome. + * @param sendingContext.eventTarget {EventTarget} + * The <EventTarget> where the message was sent. + * @param sendingContext.principal {Principal} + * The <Principal> of the EventTarget where the + * message was sent. + */ + listen(callback) { + if (this._deliverCallback) { + throw new Error("Failed to listen. Listener already attached."); + } else if (!callback) { + throw new Error("Failed to listen. Callback argument missing."); + } else { + this._deliverCallback = callback; + this._broker.registerChannel(this); + } + }, + + /** + * Resets the callback for messages on this channel + * Removes the channel from the WebChannelBroker + */ + stopListening() { + this._broker.unregisterChannel(this); + this._deliverCallback = null; + }, + + /** + * Sends messages over the WebChannel id using the "WebChannelMessageToContent" event + * + * @param message {Object} + * The message object that will be sent + * @param target {Object} + * A <target> with the information of where to send the message. + * @param target.browsingContext {BrowsingContext} + * The browsingContext we should send the message to. + * @param target.principal {Principal} + * Principal of the target. Prevents messages from + * being dispatched to unexpected origins. The system principal + * can be specified to send to any target. + * @param [target.eventTarget] {EventTarget} + * Optional eventTarget within the browser, use to send to a + * specific element. Can be null; if not null, should be + * a ContentDOMReference. + */ + send(message, target) { + let { browsingContext, principal, eventTarget } = target; + + if (message && browsingContext && principal) { + let { currentWindowGlobal } = browsingContext; + if (!currentWindowGlobal) { + console.error( + "Failed to send a WebChannel message. No currentWindowGlobal." + ); + return; + } + currentWindowGlobal + .getActor("WebChannel") + .sendAsyncMessage("WebChannelMessageToContent", { + id: this.id, + message, + eventTarget, + principal, + }); + } else if (!message) { + console.error("Failed to send a WebChannel message. Message not set."); + } else { + console.error("Failed to send a WebChannel message. Target invalid."); + } + }, + + /** + * Deliver WebChannel messages to the set "_channelCallback" + * + * @param data {Object} + * Message data + * @param sendingContext {Object} + * Message sending context. + * @param sendingContext.browsingContext {BrowsingContext} + * The browsingcontext from which the + * WebChannelMessageToChrome was sent. + * @param sendingContext.eventTarget {EventTarget} + * The <EventTarget> where the message was sent. + * Can be null; if not null, should be a ContentDOMReference. + * @param sendingContext.principal {Principal} + * The <Principal> of the EventTarget where the message was sent. + * + */ + deliver(data, sendingContext) { + if (this._deliverCallback) { + try { + this._deliverCallback(data.id, data.message, sendingContext); + } catch (ex) { + this.send( + { + errno: ERRNO_UNKNOWN_ERROR, + error: ex.message ? ex.message : ERROR_UNKNOWN, + }, + sendingContext + ); + console.error("Failed to execute WebChannel callback:"); + console.error(ex); + } + } else { + console.error("No callback set for this channel."); + } + }, +}; diff --git a/toolkit/modules/WindowsLaunchOnLogin.sys.mjs b/toolkit/modules/WindowsLaunchOnLogin.sys.mjs new file mode 100644 index 0000000000..962c80e400 --- /dev/null +++ b/toolkit/modules/WindowsLaunchOnLogin.sys.mjs @@ -0,0 +1,216 @@ +/* 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/. */ + +/** + * "Launch on Login" is a Firefox feature automatically launches Firefox when the + * user logs in to Windows. The technical mechanism is simply writing a registry + * key to `Software\Microsoft\Windows\CurrentVersion\Run`, but there is an issue: + * when disabled in the Windows UI, additional registry keys are written under + * `Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run`. Any + * keys stored here should be seen as a user override and should never be modified. + * When such keys are present, the launch on login feature should be considered + * disabled and not available from within Firefox. This module provides the + * functionality to access and modify these registry keys. + */ +export var WindowsLaunchOnLogin = { + /** + * Accepts another function as an argument and provides an open Windows + * launch on login registry key for the passed-in function to manipulate. + * + * @param func + * The function to use. + */ + async withLaunchOnLoginRegistryKey(func) { + let wrk = Cc["@mozilla.org/windows-registry-key;1"].createInstance( + Ci.nsIWindowsRegKey + ); + wrk.open( + wrk.ROOT_KEY_CURRENT_USER, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", + wrk.ACCESS_ALL + ); + try { + await func(wrk); + } finally { + wrk.close(); + } + }, + + /** + * Safely creates a Windows launch on login registry key + */ + async createLaunchOnLoginRegistryKey() { + try { + await this.withLaunchOnLoginRegistryKey(async wrk => { + // Added launch option -os-autostart for telemetry + // Add quote marks around path to account for spaces in path + let autostartPath = + this.quoteString(Services.dirsvc.get("XREExeF", Ci.nsIFile).path) + + " -os-autostart"; + try { + wrk.writeStringValue( + this.getLaunchOnLoginRegistryName(), + autostartPath + ); + } catch (e) { + console.error("Could not write value to registry", e); + } + }); + } catch (e) { + // We should only end up here if we fail to open the registry + console.error("Failed to open Windows registry", e); + } + }, + + /** + * Safely removes a Windows launch on login registry key + */ + async removeLaunchOnLoginRegistryKey() { + try { + await this.withLaunchOnLoginRegistryKey(async wrk => { + let registryName = this.getLaunchOnLoginRegistryName(); + if (wrk.hasValue(registryName)) { + try { + wrk.removeValue(registryName); + } catch (e) { + console.error("Failed to remove Windows registry value", e); + } + } + }); + } catch (e) { + // We should only end up here if we fail to open the registry + console.error("Failed to open Windows registry", e); + } + }, + + /** + * Gets a list of all launch on login shortcuts in the + * %USERNAME%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup folder + * that point to the current Firefox executable. + */ + getLaunchOnLoginShortcutList() { + let shellService = Cc["@mozilla.org/browser/shell-service;1"].getService( + Ci.nsIWindowsShellService + ); + return shellService.getLaunchOnLoginShortcuts(); + }, + + /** + * Safely removes all launch on login shortcuts in the + * %USERNAME%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup folder + * that point to the current Firefox executable. + */ + async removeLaunchOnLoginShortcuts() { + let shortcuts = this.getLaunchOnLoginShortcutList(); + for (let i = 0; i < shortcuts.length; i++) { + await IOUtils.remove(shortcuts[i]); + } + }, + + /** + * Checks if Windows launch on login was independently enabled or disabled + * by the user in the Windows Startup Apps menu. The registry key that + * stores this information should not be modified. + */ + getLaunchOnLoginApproved() { + try { + let wrkApproved = Cc[ + "@mozilla.org/windows-registry-key;1" + ].createInstance(Ci.nsIWindowsRegKey); + wrkApproved.open( + wrkApproved.ROOT_KEY_CURRENT_USER, + "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run", + wrkApproved.ACCESS_READ + ); + let registryName = this.getLaunchOnLoginRegistryName(); + if (!wrkApproved.hasValue(registryName)) { + wrkApproved.close(); + return true; + } + // There's very little consistency with these binary values aside from if the first byte + // is even it's enabled and odd is disabled. There's also no published specification. + let approvedByWindows = + wrkApproved.readBinaryValue(registryName).charCodeAt(0).toString(16) % + 2 == + 0; + wrkApproved.close(); + return approvedByWindows; + } catch (e) { + // We should only end up here if we fail to open the registry + console.error("Failed to open Windows registry", e); + } + return true; + }, + + /** + * Checks if Windows launch on login has an existing registry key or user-created shortcut in + * %USERNAME%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup. The registry key that + * stores this information should not be modified. + */ + getLaunchOnLoginEnabled() { + let registryName = this.getLaunchOnLoginRegistryName(); + let regExists = false; + let shortcutExists = false; + this.withLaunchOnLoginRegistryKey(wrk => { + try { + // Start by checking if registry key exists. + regExists = wrk.hasValue(registryName); + } catch (e) { + // We should only end up here if we fail to open the registry + console.error("Failed to open Windows registry", e); + } + }); + if (!regExists) { + shortcutExists = !!this.getLaunchOnLoginShortcutList().length; + } + // Even if a user disables it later on we want the launch on login + // infobar to remain disabled as the user is aware of the option. + if (regExists || shortcutExists) { + Services.prefs.setBoolPref( + "browser.startup.windowsLaunchOnLogin.disableLaunchOnLoginPrompt", + true + ); + } + return regExists || shortcutExists; + }, + + /** + * Quotes a string for use as a single command argument, using Windows quoting + * conventions. + * + * @see https://msdn.microsoft.com/en-us/library/17w5ykft(v=vs.85).aspx + * + * @param {string} str + * The argument string to quote. + * @returns {string} + */ + quoteString(str) { + if (!/[\s"]/.test(str)) { + return str; + } + + let escaped = str.replace(/(\\*)("|$)/g, (m0, m1, m2) => { + if (m2) { + m2 = `\\${m2}`; + } + return `${m1}${m1}${m2}`; + }); + + return `"${escaped}"`; + }, + + /** + * Generates a unique registry name for the current application + * like "Mozilla-Firefox-71AE18FE3142402B". + */ + getLaunchOnLoginRegistryName() { + let xreDirProvider = Cc[ + "@mozilla.org/xre/directory-provider;1" + ].createInstance(Ci.nsIXREDirProvider); + let registryName = `${Services.appinfo.vendor}-${ + Services.appinfo.name + }-${xreDirProvider.getInstallHash()}`; + return registryName; + }, +}; diff --git a/toolkit/modules/WindowsRegistry.sys.mjs b/toolkit/modules/WindowsRegistry.sys.mjs new file mode 100644 index 0000000000..7bc6c77ae1 --- /dev/null +++ b/toolkit/modules/WindowsRegistry.sys.mjs @@ -0,0 +1,88 @@ +/* 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 var WindowsRegistry = { + /** + * Safely reads a value from the registry. + * + * @param aRoot + * The root registry to use. + * @param aPath + * The registry path to the key. + * @param aKey + * The key name. + * @param [aRegistryNode=0] + * Optionally set to nsIWindowsRegKey.WOW64_64 (or nsIWindowsRegKey.WOW64_32) + * to access a 64-bit (32-bit) key from either a 32-bit or 64-bit application. + * @return The key value or undefined if it doesn't exist. If the key is + * a REG_MULTI_SZ, an array is returned. + */ + readRegKey(aRoot, aPath, aKey, aRegistryNode = 0) { + const kRegMultiSz = 7; + const kMode = Ci.nsIWindowsRegKey.ACCESS_READ | aRegistryNode; + let registry = Cc["@mozilla.org/windows-registry-key;1"].createInstance( + Ci.nsIWindowsRegKey + ); + try { + registry.open(aRoot, aPath, kMode); + if (registry.hasValue(aKey)) { + let type = registry.getValueType(aKey); + switch (type) { + case kRegMultiSz: + // nsIWindowsRegKey doesn't support REG_MULTI_SZ type out of the box. + let str = registry.readStringValue(aKey); + return str.split("\0").filter(v => v); + case Ci.nsIWindowsRegKey.TYPE_STRING: + return registry.readStringValue(aKey); + case Ci.nsIWindowsRegKey.TYPE_INT: + return registry.readIntValue(aKey); + default: + throw new Error("Unsupported registry value."); + } + } + } catch (ex) { + } finally { + registry.close(); + } + return undefined; + }, + + /** + * Safely removes a key from the registry. + * + * @param aRoot + * The root registry to use. + * @param aPath + * The registry path to the key. + * @param aKey + * The key name. + * @param [aRegistryNode=0] + * Optionally set to nsIWindowsRegKey.WOW64_64 (or nsIWindowsRegKey.WOW64_32) + * to access a 64-bit (32-bit) key from either a 32-bit or 64-bit application. + * @return True if the key was removed or never existed, false otherwise. + */ + removeRegKey(aRoot, aPath, aKey, aRegistryNode = 0) { + let registry = Cc["@mozilla.org/windows-registry-key;1"].createInstance( + Ci.nsIWindowsRegKey + ); + let result = false; + try { + let mode = + Ci.nsIWindowsRegKey.ACCESS_QUERY_VALUE | + Ci.nsIWindowsRegKey.ACCESS_SET_VALUE | + aRegistryNode; + registry.open(aRoot, aPath, mode); + if (registry.hasValue(aKey)) { + registry.removeValue(aKey); + result = !registry.hasValue(aKey); + } else { + result = true; + } + } catch (ex) { + } finally { + registry.close(); + } + return result; + }, +}; diff --git a/toolkit/modules/docs/AsyncShutdown.rst b/toolkit/modules/docs/AsyncShutdown.rst new file mode 100644 index 0000000000..8d9e0d4388 --- /dev/null +++ b/toolkit/modules/docs/AsyncShutdown.rst @@ -0,0 +1,275 @@ +.. _AsyncShutdown: + +============== +AsyncShutdown +============== + +During shutdown of the process, subsystems are closed one after another. ``AsyncShutdown`` is a module dedicated to express shutdown-time dependencies between: + +- services and their clients; +- shutdown phases (e.g. profile-before-change) and their clients. + +.. _AsyncShutdown_Barriers: + +Barriers: Expressing shutdown dependencies towards a service +============================================================ + +Consider a service FooService. At some point during the shutdown of the process, this service needs to: + +- inform its clients that it is about to shut down; +- wait until the clients have completed their final operations based on FooService (often asynchronously); +- only then shut itself down. + +This may be expressed as an instance of ``AsyncShutdown.Barrier``. An instance of ``AsyncShutdown.Barrier`` provides: + +- a capability ``client`` that may be published to clients, to let them register or unregister blockers; +- methods for the owner of the barrier to let it consult the state of blockers and wait until all client-registered blockers have been resolved. + +Shutdown timeouts +----------------- + +By design, an instance of ``AsyncShutdown.Barrier`` will cause a crash +if it takes more than 60 seconds `awake` for its clients to lift or +remove their blockers (`awake` meaning that seconds during which the +computer is asleep or too busy to do anything are not counted). This +mechanism helps ensure that we do not leave the process in a state in +which it can neither proceed with shutdown nor be relaunched. + +If the CrashReporter is enabled, this crash will report: + +- the name of the barrier that failed; +- for each blocker that has not been released yet: + + - the name of the blocker; + - the state of the blocker, if a state function has been provided (see :ref:`AsyncShutdown.Barrier.state`). + +Example 1: Simple Barrier client +-------------------------------- + +The following snippet presents an example of a client of FooService that has a shutdown dependency upon FooService. In this case, the client wishes to ensure that FooService is not shutdown before some state has been reached. An example is clients that need write data asynchronously and need to ensure that they have fully written their state to disk before shutdown, even if due to some user manipulation shutdown takes place immediately. + +.. warning:: + `addBlocker()` can throw if it's too late in the shutdown process to add a + new blocker. The consumer must handle this case, especially if the blocker + contains code unblocking other shutdown phases. + `nsIShutdownClient.isClosed` can also be used to check for this condition. + +.. code-block:: javascript + + // Some client of FooService called FooClient + + const { FooService } = ChromeUtils.import( + "resource://gre/modules/FooService.jsm" + ); + + // FooService.shutdown is the `client` capability of a `Barrier`. + // See example 2 for the definition of `FooService.shutdown` + FooService.shutdown.addBlocker( + "FooClient: Need to make sure that we have reached some state", + () => promiseReachedSomeState + ); + // promiseReachedSomeState should be an instance of Promise resolved once + // we have reached the expected state + +Example 2: Simple Barrier owner +------------------------------- + +The following snippet presents an example of a service FooService that +wishes to ensure that all clients have had a chance to complete any +outstanding operations before FooService shuts down. + +.. code-block:: javascript + + // Module FooService + + const { AsyncShutdown } = ChromeUtils.importESModule( + "resource://gre/modules/AsyncShutdown.sys.mjs" + ); + + this.exports = ["FooService"]; + + let shutdown = new AsyncShutdown.Barrier("FooService: Waiting for clients before shutting down"); + + // Export the `client` capability, to let clients register shutdown blockers + FooService.shutdown = shutdown.client; + + // This function should be triggered at some point during shutdown, generally + // as a client to another Barrier or Phase. Triggering this function is not covered + // in this snippet. + let onshutdown = async function() { + // Wait for all registered clients to have lifted the barrier + await shutdown.wait(); + + // Now deactivate FooService itself. + // ... + }); + +Frequently, a service that owns a ``AsyncShutdown.Barrier`` is itself a client of another Barrier. + +.. _AsyncShutdown.Barrier.state: + +Example 3: More sophisticated Barrier client +-------------------------------------------- + +The following snippet presents FooClient2, a more sophisticated client of FooService that needs to perform a number of operations during shutdown but before the shutdown of FooService. Also, given that this client is more sophisticated, we provide a function returning the state of FooClient2 during shutdown. If for some reason FooClient2's blocker is never lifted, this state can be reported as part of a crash report. + +.. code-block:: javascript + + // Some client of FooService called FooClient2 + + const { FooService } = ChromeUtils.import( + "resource://gre/modules/FooService.jsm" + ); + + FooService.shutdown.addBlocker( + "FooClient2: Collecting data, writing it to disk and shutting down", + () => Blocker.wait(), + () => Blocker.state + ); + + let Blocker = { + // This field contains information on the status of the blocker. + // It can be any JSON serializable object. + state: "Not started", + + async wait() { + // This method is called once FooService starts informing its clients that + // FooService wishes to shut down. + + // Update the state as we go. If the Barrier is used in conjunction with + // a Phase, this state will be reported as part of a crash report if FooClient fails + // to shutdown properly. + this.state = "Starting"; + + let data = await collectSomeData(); + this.state = "Data collection complete"; + + try { + await writeSomeDataToDisk(data); + this.state = "Data successfully written to disk"; + } catch (ex) { + this.state = "Writing data to disk failed, proceeding with shutdown: " + ex; + } + + await FooService.oneLastCall(); + this.state = "Ready"; + } + }; + + +Example 4: A service with both internal and external dependencies +----------------------------------------------------------------- + + .. code-block:: javascript + + // Module FooService2 + + let { AsyncShutdown } = ChromeUtils.importESModule( + "resource://gre/modules/AsyncShutdown.sys.mjs" + ); + + this.exports = ["FooService2"]; + + let shutdown = new AsyncShutdown.Barrier("FooService2: Waiting for clients before shutting down"); + + // Export the `client` capability, to let clients register shutdown blockers + FooService2.shutdown = shutdown.client; + + // A second barrier, used to avoid shutting down while any connections are open. + let connections = new AsyncShutdown.Barrier("FooService2: Waiting for all FooConnections to be closed before shutting down"); + + let isClosed = false; + + FooService2.openFooConnection = function(name) { + if (isClosed) { + throw new Error("FooService2 is closed"); + } + + let deferred = Promise.withResolvers(); + connections.client.addBlocker("FooService2: Waiting for connection " + name + " to close", deferred.promise); + + // ... + + + return { + // ... + // Some FooConnection object. Presumably, it will have additional methods. + // ... + close: function() { + // ... + // Perform any operation necessary for closing + // ... + + // Don't hoard blockers. + connections.client.removeBlocker(deferred.promise); + + // The barrier MUST be lifted, even if removeBlocker has been called. + deferred.resolve(); + } + }; + }; + + + // This function should be triggered at some point during shutdown, generally + // as a client to another Barrier. Triggering this function is not covered + // in this snippet. + let onshutdown = async function() { + // Wait for all registered clients to have lifted the barrier. + // These clients may open instances of FooConnection if they need to. + await shutdown.wait(); + + // Now stop accepting any other connection request. + isClosed = true; + + // Wait for all instances of FooConnection to be closed. + await connections.wait(); + + // Now finish shutting down FooService2 + // ... + }); + +.. _AsyncShutdown_phases: + +Phases: Expressing dependencies towards phases of shutdown +========================================================== + +The shutdown of a process takes place by phase, such as: + +- ``profileBeforeChange`` (once this phase is complete, there is no guarantee that the process has access to a profile directory); +- ``webWorkersShutdown`` (once this phase is complete, JavaScript does not have access to workers anymore); +- ... + +Much as services, phases have clients. For instance, all users of web workers MUST have finished using their web workers before the end of phase ``webWorkersShutdown``. + +Module ``AsyncShutdown`` provides pre-defined barriers for a set of +well-known phases. Each of the barriers provided blocks the corresponding shutdown +phase until all clients have lifted their blockers. + +List of phases +-------------- + +``AsyncShutdown.profileChangeTeardown`` + + The client capability for clients wishing to block asynchronously + during observer notification "profile-change-teardown". + + +``AsyncShutdown.profileBeforeChange`` + + The client capability for clients wishing to block asynchronously + during observer notification "profile-change-teardown". Once the + barrier is resolved, clients other than Telemetry MUST NOT access + files in the profile directory and clients MUST NOT use Telemetry + anymore. + +``AsyncShutdown.sendTelemetry`` + + The client capability for clients wishing to block asynchronously + during observer notification "profile-before-change-telemetry". + Once the barrier is resolved, Telemetry must stop its operations. + +``AsyncShutdown.webWorkersShutdown`` + + The client capability for clients wishing to block asynchronously + during observer notification "web-workers-shutdown". Once the phase + is complete, clients MUST NOT use web workers. diff --git a/toolkit/modules/docs/FirstStartup.rst b/toolkit/modules/docs/FirstStartup.rst new file mode 100644 index 0000000000..426c341daf --- /dev/null +++ b/toolkit/modules/docs/FirstStartup.rst @@ -0,0 +1,72 @@ +.. _FirstStartup: + +============== +FirstStartup +============== + +``FirstStartup`` is a module which is invoked on application startup by the Windows Installer, +to initialize services before the first application window appears. + +This is useful for: + +- one-time performance tuning +- downloading critical data (hotfixes, experiments, etc) + +Blocking until the first Application window appears is important because the Installer +will show a progress bar until this happens. This gives a user experience of: + +1. User downloads and starts the Windows Stub Installer. +2. Progress bar advances while the application is downloaded and installed. +3. Installer invokes the application with ``--first-startup``. +4. Application window appears, and the installer window closes. + +Overall, the user experiences a very fast first-startup, with critical tasks that normally +would be deferred until after UI startup already complete. + +.. _FirstStartup Architecture: + +FirstStartup: Example use case +============================== + +An example use of the ``FirstStartup`` module is to invoke the Normandy client to download an experiment +that will be used to customize the first-run page that Firefox shows. + +In this example, the first-run page would be loaded experimentally based on an attribution code provided +by the Installer. The flow for this looks like: + +1. User clicks on download link containing an attribution (UTM) code(s). +2. The download page serves a custom Windows Stub Installer with the appropriate attribution code embedded. +3. The installer invokes Firefox with the `--first-startup` flag, which blocks the first window. +4. Normandy is run by ``FirstStartup`` and downloads a list of available experiments, or "recipes". +5. Recipes are evaluated and filtered based on local information, such as the OS platform and the attribution codes. +6. A recipe is found which matches the current attribution code, and appropriate data is made available to the first-run page. +7. ``FirstStartup`` completes and unblocks, which causes Firefox to show the first window and load the appropriate first-run data. + +List of phases +============== + +``FirstStartup.NOT_STARTED`` + + The ``FirstStartup`` module has not been initialized (the ``init()`` + function has not been called). This is the default state. + +``FirstStartup.IN_PROGRESS`` + + ``FirstStartup.init()`` has been called, and the event loop is + spinning. This state will persist until either all startup tasks + have finished, or time-out has been reached. + + The time-out defaults to 30 seconds, but is configurable via the + ``first-startup.timeout`` pref, which is specified in milliseconds. + +``FirstStartup.TIMED_OUT`` + + The time-out has been reached before startup tasks are complete. + +``FirstStartup.SUCCESS`` + + All startup tasks have completed successfully, and application startup may resume. + +``FirstStartup.UNSUPPORTED`` + + No startup tasks are supported, and `FirstStartup` exited. diff --git a/toolkit/modules/docs/Region.rst b/toolkit/modules/docs/Region.rst new file mode 100644 index 0000000000..1e95739f4a --- /dev/null +++ b/toolkit/modules/docs/Region.rst @@ -0,0 +1,72 @@ +.. _Region: + +====== +Region +====== + +Firefox monitors the users region in order to show relevant local +search engines and content. The region is tracked in 2 properties: + + * ``Region.current`` - The most recent location we detected for the user. + * ``Region.home`` - Where we consider the users home location. + +These are tracked separately as to avoid updating the users +experience repeatedly as they travel for example. In general +callers should use ``Region.home``. + +If the user is detected in a current region that is not there `home` region +for a continuous period (current 2 weeks) then their `home` region +will be updated. + +Testing +======= + +To set the users region for testing you can use ``Region._setHomeRegion("US", false)``, the second parameter ``notify`` +will send a notification that the region has changed and trigger a +reload of search engines and other content. + +Updating test_Region_geocoding.js data +-------------------------------------- + +The test data used in this test is generated by running the MLS geocoding +service locally: + +Follow the Ichnaea location development guide @ https://ichnaea.readthedocs.io/en/latest/local_dev.html. + +Make a list of test locations in a CSV format, for example: + +.. code-block:: shell + + 23.7818724,38.0531587 + 23.7728138,38.0572369 + 1.6780180,48.5973431 + 1.7034801,48.5979913 + 1.6978640,48.5919751 + +You can use the MLS raw data files to get a large sample @ https://location.services.mozilla.com/downloads + +Save a script to run the geocoding in ``ichnaea/ichnaea`` + +.. code-block:: python + + import geocode + geocoder = geocode.Geocoder() + + f = open("mls.csv", "r") + r = open("mls-lookup-results.csv", "a") + + for x in f: + [lat, long] = x.strip().split(",") + region = geocoder.region(lat, long) + r.write("%s\n" % region) + +Run the script + +.. code-block:: shell + + $ make shell + $ cd ichnaea + $ python run.py + +If you want to commit the new test data ~500 seems to be a reasonable +number of data points to test before running into issues with the test length. diff --git a/toolkit/modules/docs/index.rst b/toolkit/modules/docs/index.rst new file mode 100644 index 0000000000..221e73ad23 --- /dev/null +++ b/toolkit/modules/docs/index.rst @@ -0,0 +1,12 @@ +=============== +Toolkit Modules +=============== + +The ``/toolkit/modules`` directory contains a number of self-contained toolkit modules considered small enough that they do not deserve individual directories. + +.. toctree:: + :maxdepth: 1 + + AsyncShutdown + FirstStartup + Region diff --git a/toolkit/modules/jar.mn b/toolkit/modules/jar.mn new file mode 100644 index 0000000000..2fd575c6ed --- /dev/null +++ b/toolkit/modules/jar.mn @@ -0,0 +1,6 @@ +# 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/. + +toolkit.jar: + content/global/win.xhtml (win.xhtml) diff --git a/toolkit/modules/metrics.yaml b/toolkit/modules/metrics.yaml new file mode 100644 index 0000000000..a60fa077f5 --- /dev/null +++ b/toolkit/modules/metrics.yaml @@ -0,0 +1,85 @@ +# 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/. + +# Adding a new metric? We have docs for that! +# https://firefox-source-docs.mozilla.org/toolkit/components/glean/user/new_definitions_file.html + +--- +$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0 +$tags: + - 'Toolkit :: General' + +first_startup: + status_code: + type: quantity + unit: status code + description: | + Status of the FirstStartup service, which runs + post-install/early-startup in Firefox. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1749345 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1749345 + data_sensitivity: + - technical + notification_emails: + - rhelmer@mozilla.com + - mconley@mozilla.com + expires: never + send_in_pings: + - first-startup + + elapsed: + type: quantity + unit: milliseconds + description: | + Number of milliseconds the FirstStartup service took to run. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1749345 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1749345 + data_sensitivity: + - technical + notification_emails: + - rhelmer@mozilla.com + - mconley@mozilla.com + expires: never + send_in_pings: + - first-startup + + normandy_init_time: + type: quantity + unit: milliseconds + description: > + Number of milliseconds until Normandy.init resolved in FirstStartup. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1841138 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1841138 + data_sensitivity: + - technical + notification_emails: + - rhelmer@mozilla.com + - mconley@mozilla.com + expires: never + send_in_pings: + - first-startup + + delete_tasks_time: + type: quantity + unit: milliseconds + description: > + Number of milliseconds until TaskScheduler.deleteAllTasks resolved in FirstStartup. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1841138 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1841138 + data_sensitivity: + - technical + notification_emails: + - rhelmer@mozilla.com + - mconley@mozilla.com + expires: never + send_in_pings: + - first-startup diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build new file mode 100644 index 0000000000..60e3ae20f9 --- /dev/null +++ b/toolkit/modules/moz.build @@ -0,0 +1,308 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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("../components/telemetry/telemetry-constants.mozbuild") + +with Files("**"): + BUG_COMPONENT = ("Toolkit", "General") + +with Files("docs/**"): + BUG_COMPONENT = ("Toolkit", "Async Tooling") + +with Files("subprocess/**"): + BUG_COMPONENT = ("Toolkit", "Async Tooling") + +with Files("tests/browser/*AsyncPrefs*"): + BUG_COMPONENT = ("Core", "Security: Process Sandboxing") + +with Files("tests/browser/*Finder*"): + BUG_COMPONENT = ("Toolkit", "Find Toolbar") + +with Files("tests/xpcshell/test_Color.js"): + BUG_COMPONENT = ("Toolkit", "Find Toolbar") + +with Files("tests/xpcshell/test_DeferredTask.js"): + BUG_COMPONENT = ("Toolkit", "Async Tooling") + +with Files("tests/xpcshell/test_FinderIterator.js"): + BUG_COMPONENT = ("Toolkit", "Find Toolbar") + +with Files("tests/xpcshell/test_Integration.js"): + BUG_COMPONENT = ("Toolkit", "Async Tooling") + +with Files("tests/xpcshell/test_JSONFile.js"): + BUG_COMPONENT = ("Toolkit", "Form Manager") + +with Files("tests/xpcshell/test_Match*.js"): + BUG_COMPONENT = ("WebExtensions", "General") + +with Files("tests/xpcshell/test_NewTabUtils.js"): + BUG_COMPONENT = ("Firefox", "New Tab Page") + +with Files("tests/xpcshell/test_UpdateUtils*.js"): + BUG_COMPONENT = ("Toolkit", "Application Update") + +with Files("AsyncPrefs.sys.mjs"): + BUG_COMPONENT = ("Core", "Security: Process Sandboxing") + +with Files("AsanReporter.sys.mjs"): + BUG_COMPONENT = ("Firefox Build System", "General") + +with Files("Color.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "Find Toolbar") + +with Files("Console.sys.mjs"): + BUG_COMPONENT = ("DevTools", "Console") + +with Files("DateTimePicker*.sys.mjs"): + BUG_COMPONENT = ("Core", "Layout: Form Controls") + +with Files("DeferredTask.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "Async Tooling") + +with Files("E10SUtils.sys.mjs"): + BUG_COMPONENT = ("Core", "Security: Process Sandboxing") + +with Files("Finder*.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "Find Toolbar") + +with Files("FormLikeFactory.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "Password Manager") + +with Files("IndexedDB.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "Async Tooling") + +with Files("InlineSpellChecker*.sys.mjs"): + BUG_COMPONENT = ("Core", "Spelling checker") + +with Files("Integration.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "Async Tooling") + +with Files("JSONFile.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "Form Manager") + +with Files("LightweightThemeConsumer.sys.mjs"): + BUG_COMPONENT = ("Firefox", "Toolbars and Customization") + +with Files("NLP.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "Find Toolbar") + +with Files("NewTabUtils.sys.mjs"): + BUG_COMPONENT = ("Firefox", "Tabbed Browser") + +with Files("ObjectUtils.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "Telemetry") + +with Files("PermissionsUtils.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "Add-ons Manager") + +with Files("PopupNotifications.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "PopupNotifications and Notification Bars") + +with Files("PrivateBrowsingUtils.sys.mjs"): + BUG_COMPONENT = ("Firefox", "Private Browsing") + +with Files("Promise*.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "Async Tooling") + +with Files("ResponsivenessMonitor.sys.mjs"): + BUG_COMPONENT = ("Firefox", "Migration") + +with Files("ShortcutUtils.sys.mjs"): + BUG_COMPONENT = ("Firefox", "Toolbars and Customization") + +with Files("Sqlite.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "Storage") + +with Files("SubDialog.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "Content Prompts") + +with Files("UpdateUtils.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "Application Update") + +with Files("WindowsLaunchOnLogin.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "General") + +with Files("WindowsRegistry.sys.mjs"): + BUG_COMPONENT = ("Toolkit", "General") + + +XPCSHELL_TESTS_MANIFESTS += ["tests/xpcshell/xpcshell.toml"] +BROWSER_CHROME_MANIFESTS += ["tests/browser/browser.toml"] +MOCHITEST_CHROME_MANIFESTS += ["tests/chrome/chrome.toml"] + +TESTING_JS_MODULES += [ + "tests/modules/MockDocument.sys.mjs", + "tests/modules/OSKeyStoreTestUtils.sys.mjs", + "tests/modules/PromiseTestUtils.sys.mjs", + "tests/xpcshell/RegionTestUtils.sys.mjs", + "tests/xpcshell/TestIntegration.sys.mjs", +] + +SPHINX_TREES["toolkit_modules"] = "docs" + +with Files("docs/**"): + SCHEDULES.exclusive = ["docs"] + +EXTRA_JS_MODULES += [ + "AboutPagesUtils.sys.mjs", + "ActorManagerParent.sys.mjs", + "AppMenuNotifications.sys.mjs", + "AsyncPrefs.sys.mjs", + "BinarySearch.sys.mjs", + "BrowserTelemetryUtils.sys.mjs", + "BrowserUtils.sys.mjs", + "CanonicalJSON.sys.mjs", + "CertUtils.sys.mjs", + "Color.sys.mjs", + "Console.sys.mjs", + "ContentDOMReference.sys.mjs", + "CreditCard.sys.mjs", + "DateTimePickerPanel.sys.mjs", + "DeferredTask.sys.mjs", + "Deprecated.sys.mjs", + "E10SUtils.sys.mjs", + "EventEmitter.sys.mjs", + "FileUtils.sys.mjs", + "FindBarContent.sys.mjs", + "Finder.sys.mjs", + "FinderHighlighter.sys.mjs", + "FinderIterator.sys.mjs", + "FinderParent.sys.mjs", + "FirstStartup.sys.mjs", + "FormLikeFactory.sys.mjs", + "Geometry.sys.mjs", + "HiddenFrame.sys.mjs", + "IgnoreLists.sys.mjs", + "IndexedDB.sys.mjs", + "InlineSpellChecker.sys.mjs", + "InlineSpellCheckerContent.sys.mjs", + "Integration.sys.mjs", + "JSONFile.sys.mjs", + "JsonSchema.sys.mjs", + "KeywordUtils.sys.mjs", + "LayoutUtils.sys.mjs", + "Log.sys.mjs", + "NewTabUtils.sys.mjs", + "NLP.sys.mjs", + "ObjectUtils.sys.mjs", + "OsEnvironment.sys.mjs", + "OSKeyStore.sys.mjs", + "PermissionsUtils.sys.mjs", + "PopupNotifications.sys.mjs", + "Preferences.sys.mjs", + "PrivateBrowsingUtils.sys.mjs", + "ProcessType.sys.mjs", + "ProfileAge.sys.mjs", + "Region.sys.mjs", + "RemotePageAccessManager.sys.mjs", + "ResetProfile.sys.mjs", + "ResponsivenessMonitor.sys.mjs", + "SelectionUtils.sys.mjs", + "ServiceRequest.sys.mjs", + "ShortcutUtils.sys.mjs", + "Sqlite.sys.mjs", + "SubDialog.sys.mjs", + "Timer.sys.mjs", + "Troubleshoot.sys.mjs", + "UpdateUtils.sys.mjs", + "WebChannel.sys.mjs", +] + +if CONFIG["MOZ_ASAN_REPORTER"]: + EXTRA_JS_MODULES += [ + "AsanReporter.sys.mjs", + ] + +if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa": + EXTRA_JS_MODULES += [ + "PropertyListUtils.sys.mjs", + ] + +EXTRA_JS_MODULES.third_party.jsesc += ["third_party/jsesc/jsesc.mjs"] +EXTRA_JS_MODULES.sessionstore += [ + "sessionstore/PrivacyFilter.sys.mjs", + "sessionstore/PrivacyLevel.sys.mjs", + "sessionstore/SessionHistory.sys.mjs", + "sessionstore/Utils.sys.mjs", +] + +EXTRA_JS_MODULES.third_party.fathom += ["third_party/fathom/fathom.mjs"] + +if CONFIG["MOZ_WIDGET_TOOLKIT"] in ("windows", "gtk"): + DEFINES["MENUBAR_CAN_AUTOHIDE"] = 1 + +if CONFIG["MOZ_WIDGET_TOOLKIT"] in ("windows", "gtk", "cocoa"): + DEFINES["HAVE_SHELL_SERVICE"] = 1 + +EXTRA_PP_JS_MODULES += [ + "AppConstants.sys.mjs", +] + +if "Android" != CONFIG["OS_TARGET"]: + EXTRA_JS_MODULES += [ + "ClipboardContextMenu.sys.mjs", + "GMPExtractor.worker.js", + "GMPInstallManager.sys.mjs", + "GMPUtils.sys.mjs", + "LightweightThemeConsumer.sys.mjs", + ] + + DIRS += [ + "subprocess", + ] + +if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows": + EXTRA_JS_MODULES += [ + "WindowsLaunchOnLogin.sys.mjs", + "WindowsRegistry.sys.mjs", + ] + +for var in ( + "ANDROID_PACKAGE_NAME", + "MOZ_APP_NAME", + "MOZ_APP_BASENAME", + "MOZ_APP_DISPLAYNAME", + "MOZ_APP_VERSION", + "MOZ_APP_VERSION_DISPLAY", + "MOZ_BING_API_CLIENTID", + "MOZ_BING_API_KEY", + "MOZ_GOOGLE_LOCATION_SERVICE_API_KEY", + "MOZ_GOOGLE_SAFEBROWSING_API_KEY", + "MOZ_MACBUNDLE_ID", + "MOZ_MACBUNDLE_NAME", + "MOZ_MOZILLA_API_KEY", + "MOZ_WIDGET_TOOLKIT", + "DLL_PREFIX", + "DLL_SUFFIX", + "DEBUG_JS_MODULES", + "OMNIJAR_NAME", +): + DEFINES[var] = CONFIG[var] or "" + +for var in ( + "MOZ_ALLOW_ADDON_SIDELOAD", + "MOZ_SYSTEM_NSS", + "MOZ_SYSTEM_POLICIES", + "MOZ_UNSIGNED_APP_SCOPE", + "MOZ_UNSIGNED_SYSTEM_SCOPE", + "MOZ_UPDATE_AGENT", + "MOZ_UPDATER", + "MOZ_WEBEXT_WEBIDL_ENABLED", +): + if CONFIG[var]: + DEFINES[var] = True + +JAR_MANIFESTS += ["jar.mn"] + +DEFINES["TOPOBJDIR"] = TOPOBJDIR + +XPIDL_SOURCES += [ + "nsIBrowserWindowTracker.idl", + "nsIRegion.idl", +] + +XPIDL_MODULE = "toolkit_modules" diff --git a/toolkit/modules/nsIBrowserWindowTracker.idl b/toolkit/modules/nsIBrowserWindowTracker.idl new file mode 100644 index 0000000000..60c6f5ec2e --- /dev/null +++ b/toolkit/modules/nsIBrowserWindowTracker.idl @@ -0,0 +1,25 @@ +/* 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 "nsISupports.idl" + +[scriptable, uuid(f6190951-69d0-4ce5-b503-d2535d9de98c)] +interface nsIVisibleTab : nsISupports +{ + attribute AString contentTitle; + attribute int64_t browserId; +}; + +[scriptable, uuid(846ff245-ccbf-4c7a-807e-060f02927651)] +interface nsIBrowserWindowTracker : nsISupports +{ + /** + * Return array of browser tabs that are currently visible. + */ + Array<nsIVisibleTab> getAllVisibleTabs(); + + /** + * Return browser having given browser id. + */ + nsISupports getBrowserById(in uint64_t aBrowserId); +}; diff --git a/toolkit/modules/nsIRegion.idl b/toolkit/modules/nsIRegion.idl new file mode 100644 index 0000000000..19c992d46e --- /dev/null +++ b/toolkit/modules/nsIRegion.idl @@ -0,0 +1,27 @@ +/* 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 "nsISupports.idl" + +[scriptable, uuid(21e6d094-e016-41a4-80cd-76d2e20871aa)] +interface nsIRegion : nsISupports +{ + /** + * The users current region. + */ + readonly attribute AString current; + + /** + * The users current home region. + */ + readonly attribute AString home; +}; + +%{ C++ +/** + * The observer topic to listen to for Region notifications. + */ +#define REGION_TOPIC "browser-region-updated" + +%} diff --git a/toolkit/modules/pings.yaml b/toolkit/modules/pings.yaml new file mode 100644 index 0000000000..e7a3ea6e86 --- /dev/null +++ b/toolkit/modules/pings.yaml @@ -0,0 +1,19 @@ +# 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/. + +--- +$schema: moz://mozilla.org/schemas/glean/pings/2-0-0 + +first-startup: + description: | + Sent during startup if Firefox was launched by the installer. + include_client_id: true + send_if_empty: false + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1749345 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1749345 + notification_emails: + - rhelmer@mozilla.com + - mconley@mozilla.com diff --git a/toolkit/modules/sessionstore/PrivacyFilter.sys.mjs b/toolkit/modules/sessionstore/PrivacyFilter.sys.mjs new file mode 100644 index 0000000000..d4c867b129 --- /dev/null +++ b/toolkit/modules/sessionstore/PrivacyFilter.sys.mjs @@ -0,0 +1,114 @@ +/* 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/. */ + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + PrivacyLevel: "resource://gre/modules/sessionstore/PrivacyLevel.sys.mjs", +}); + +/** + * A module that provides methods to filter various kinds of data collected + * from a tab by the current privacy level as set by the user. + */ +export var PrivacyFilter = Object.freeze({ + /** + * Filters the given (serialized) session storage |data| according to the + * current privacy level and returns a new object containing only data that + * we're allowed to store. + * + * @param data The session storage data as collected from a tab. + * @return object + */ + filterSessionStorageData(data) { + let retval = {}; + + if (lazy.PrivacyLevel.shouldSaveEverything()) { + return data; + } + + if (!lazy.PrivacyLevel.canSaveAnything()) { + return null; + } + + for (let host of Object.keys(data)) { + if (lazy.PrivacyLevel.check(host)) { + retval[host] = data[host]; + } + } + + return Object.keys(retval).length ? retval : null; + }, + + /** + * Filters the given (serialized) form |data| according to the current + * privacy level and returns a new object containing only data that we're + * allowed to store. + * + * @param data The form data as collected from a tab. + * @return object + */ + filterFormData(data) { + if (lazy.PrivacyLevel.shouldSaveEverything()) { + return Object.keys(data).length ? data : null; + } + + if (!lazy.PrivacyLevel.canSaveAnything()) { + return null; + } + + // If the given form data object has an associated URL that we are not + // allowed to store data for, bail out. We explicitly discard data for any + // children as well even if storing data for those frames would be allowed. + if (!data || (data.url && !lazy.PrivacyLevel.check(data.url))) { + return null; + } + + let retval = {}; + + for (let key of Object.keys(data)) { + if (key === "children") { + let recurse = child => this.filterFormData(child); + let children = data.children.map(recurse).filter(child => child); + + if (children.length) { + retval.children = children; + } + // Only copy keys other than "children" if we have a valid URL in + // data.url and we thus passed the privacy level check. + } else if (data.url) { + retval[key] = data[key]; + } + } + + return Object.keys(retval).length ? retval : null; + }, + + /** + * Removes any private windows and tabs from a given browser state object. + * + * @param browserState (object) + * The browser state for which we remove any private windows and tabs. + * The given object will be modified. + */ + filterPrivateWindowsAndTabs(browserState) { + // Remove private opened windows. + for (let i = browserState.windows.length - 1; i >= 0; i--) { + let win = browserState.windows[i]; + + if (win.isPrivate) { + browserState.windows.splice(i, 1); + + if (browserState.selectedWindow >= i) { + browserState.selectedWindow--; + } + } + } + + // Remove private closed windows. + browserState._closedWindows = browserState._closedWindows.filter( + win => !win.isPrivate + ); + }, +}); diff --git a/toolkit/modules/sessionstore/PrivacyLevel.sys.mjs b/toolkit/modules/sessionstore/PrivacyLevel.sys.mjs new file mode 100644 index 0000000000..c3267fa5cf --- /dev/null +++ b/toolkit/modules/sessionstore/PrivacyLevel.sys.mjs @@ -0,0 +1,63 @@ +/* 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 { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +// The following constants represent the different possible privacy levels that +// can be set by the user and that we need to consider when collecting text +// data, and cookies. +// +// Collect data from all sites (http and https). +const PRIVACY_NONE = 0; +// Collect data from unencrypted sites (http), only. +const PRIVACY_ENCRYPTED = 1; +// Collect no data. +const PRIVACY_FULL = 2; + +export var PrivacyLevel = { + /** + * Returns whether the current privacy level allows saving data for the given + * |url|. + * + * @param url The URL we want to save data for. + * @return bool + */ + check(url) { + return PrivacyLevel.canSave(url.startsWith("https:")); + }, + + /** + * Checks whether we're allowed to save data for a specific site. + * + * @param isHttps A boolean that tells whether the site uses TLS. + * @return {bool} Whether we can save data for the specified site. + */ + canSave(isHttps) { + // Never save any data when full privacy is requested. + if (this.privacyLevel == PRIVACY_FULL) { + return false; + } + + // Don't save data for encrypted sites when requested. + if (isHttps && this.privacyLevel == PRIVACY_ENCRYPTED) { + return false; + } + + return true; + }, + + canSaveAnything() { + return this.privacyLevel != PRIVACY_FULL; + }, + + shouldSaveEverything() { + return this.privacyLevel == PRIVACY_NONE; + }, +}; + +XPCOMUtils.defineLazyPreferenceGetter( + PrivacyLevel, + "privacyLevel", + "browser.sessionstore.privacy_level" +); diff --git a/toolkit/modules/sessionstore/SessionHistory.sys.mjs b/toolkit/modules/sessionstore/SessionHistory.sys.mjs new file mode 100644 index 0000000000..19f6210fba --- /dev/null +++ b/toolkit/modules/sessionstore/SessionHistory.sys.mjs @@ -0,0 +1,663 @@ +/* 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/. */ + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs", +}); + +/** + * The external API exported by this module. + */ +export var SessionHistory = Object.freeze({ + isEmpty(docShell) { + return SessionHistoryInternal.isEmpty(docShell); + }, + + collect(docShell, aFromIdx = -1) { + if (Services.appinfo.sessionHistoryInParent) { + throw new Error("Use SessionHistory.collectFromParent instead"); + } + return SessionHistoryInternal.collect(docShell, aFromIdx); + }, + + collectFromParent(uri, documentHasChildNodes, history, aFromIdx = -1) { + return SessionHistoryInternal.collectCommon( + uri, + documentHasChildNodes, + history, + aFromIdx + ); + }, + + collectNonWebControlledBlankLoadingSession(browsingContext) { + return SessionHistoryInternal.collectNonWebControlledBlankLoadingSession( + browsingContext + ); + }, + + restore(docShell, tabData) { + if (Services.appinfo.sessionHistoryInParent) { + throw new Error("Use SessionHistory.restoreFromParent instead"); + } + return SessionHistoryInternal.restore( + docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory + .legacySHistory, + tabData + ); + }, + + restoreFromParent(history, tabData) { + return SessionHistoryInternal.restore(history, tabData); + }, +}); + +/** + * The internal API for the SessionHistory module. + */ +var SessionHistoryInternal = { + /** + * Mapping from legacy docshellIDs to docshellUUIDs. + */ + _docshellUUIDMap: new Map(), + + /** + * Returns whether the given docShell's session history is empty. + * + * @param docShell + * The docShell that owns the session history. + */ + isEmpty(docShell) { + let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation); + let history = webNavigation.sessionHistory; + if (!webNavigation.currentURI) { + return true; + } + let uri = webNavigation.currentURI.spec; + return uri == "about:blank" && history.count == 0; + }, + + /** + * Collects session history data for a given docShell. + * + * @param docShell + * The docShell that owns the session history. + * @param aFromIdx + * The starting local index to collect the history from. + * @return An object reprereseting a partial global history update. + */ + collect(docShell, aFromIdx = -1) { + let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation); + let uri = webNavigation.currentURI.displaySpec; + let body = webNavigation.document.body; + let history = webNavigation.sessionHistory; + return this.collectCommon( + uri, + body && body.hasChildNodes(), + history.legacySHistory, + aFromIdx + ); + }, + + collectCommon(uri, documentHasChildNodes, shistory, aFromIdx) { + let data = { + entries: [], + requestedIndex: shistory.requestedIndex + 1, + }; + + // We want to keep track how many entries we *could* have collected and + // how many we skipped, so we can sanitiy-check the current history index + // and also determine whether we need to get any fallback data or not. + let skippedCount = 0, + entryCount = 0; + + if (shistory && shistory.count > 0) { + let count = shistory.count; + for (; entryCount < count; entryCount++) { + let shEntry = shistory.getEntryAtIndex(entryCount); + if (entryCount <= aFromIdx) { + skippedCount++; + continue; + } + let entry = this.serializeEntry(shEntry); + data.entries.push(entry); + } + + // Ensure the index isn't out of bounds if an exception was thrown above. + data.index = Math.min(shistory.index + 1, entryCount); + } + + // If either the session history isn't available yet or doesn't have any + // valid entries, make sure we at least include the current page, + // unless of course we just skipped all entries because aFromIdx was big enough. + if (!data.entries.length && (skippedCount != entryCount || aFromIdx < 0)) { + // We landed here because the history is inaccessible or there are no + // history entries. In that case we should at least record the docShell's + // current URL as a single history entry. If the URL is not about:blank + // or it's a blank tab that was modified (like a custom newtab page), + // record it. For about:blank we explicitly want an empty array without + // an 'index' property to denote that there are no history entries. + if (uri != "about:blank" || documentHasChildNodes) { + data.entries.push({ + url: uri, + triggeringPrincipal_base64: lazy.E10SUtils.SERIALIZED_SYSTEMPRINCIPAL, + }); + data.index = 1; + } + } + + data.fromIdx = aFromIdx; + + return data; + }, + + collectNonWebControlledBlankLoadingSession(browsingContext) { + if ( + browsingContext.sessionHistory?.count === 0 && + browsingContext.nonWebControlledBlankURI && + browsingContext.mostRecentLoadingSessionHistoryEntry + ) { + return { + entries: [ + this.serializeEntry( + browsingContext.mostRecentLoadingSessionHistoryEntry + ), + ], + // Set 1 to the index, as the array of session entries is 1-based. + index: 1, + fromIdx: -1, + requestedIndex: browsingContext.sessionHistory.requestedIndex + 1, + }; + } + + return null; + }, + + /** + * Get an object that is a serialized representation of a History entry. + * + * @param shEntry + * nsISHEntry instance + * @return object + */ + serializeEntry(shEntry) { + let entry = { url: shEntry.URI.displaySpec, title: shEntry.title }; + + if (shEntry.isSubFrame) { + entry.subframe = true; + } + + entry.cacheKey = shEntry.cacheKey; + entry.ID = shEntry.ID; + entry.docshellUUID = shEntry.docshellID.toString(); + + // We will include the property only if it's truthy to save a couple of + // bytes when the resulting object is stringified and saved to disk. + if (shEntry.referrerInfo) { + entry.referrerInfo = lazy.E10SUtils.serializeReferrerInfo( + shEntry.referrerInfo + ); + } + + if (shEntry.originalURI) { + entry.originalURI = shEntry.originalURI.spec; + } + + if (shEntry.resultPrincipalURI) { + entry.resultPrincipalURI = shEntry.resultPrincipalURI.spec; + + // For downgrade compatibility we store the loadReplace property as it + // would be stored before result principal URI introduction so that + // the old code can still create URL based principals for channels + // correctly. When resultPrincipalURI is non-null and not equal to + // channel's orignalURI in the new code, it's equal to setting + // LOAD_REPLACE in the old code. Note that we only do 'the best we can' + // here to derivate the 'old' loadReplace flag value. + entry.loadReplace = entry.resultPrincipalURI != entry.originalURI; + } else { + // We want to store the property to let the backward compatibility code, + // when reading the stored session, work. When this property is undefined + // that code will derive the result principal URI from the load replace + // flag. + entry.resultPrincipalURI = null; + } + + if (shEntry.loadReplace) { + // Storing under a new property name, since it has changed its meaning + // with the result principal URI introduction. + entry.loadReplace2 = shEntry.loadReplace; + } + + if (shEntry.isSrcdocEntry) { + entry.srcdocData = shEntry.srcdocData; + entry.isSrcdocEntry = shEntry.isSrcdocEntry; + } + + if (shEntry.baseURI) { + entry.baseURI = shEntry.baseURI.spec; + } + + if (shEntry.contentType) { + entry.contentType = shEntry.contentType; + } + + if (shEntry.scrollRestorationIsManual) { + entry.scrollRestorationIsManual = true; + } else { + let x = {}, + y = {}; + shEntry.getScrollPosition(x, y); + if (x.value !== 0 || y.value !== 0) { + entry.scroll = x.value + "," + y.value; + } + + let layoutHistoryState = shEntry.layoutHistoryState; + if (layoutHistoryState && layoutHistoryState.hasStates) { + let presStates = layoutHistoryState + .getKeys() + .map(key => this._getSerializablePresState(layoutHistoryState, key)) + .filter( + presState => + // Only keep presState entries that contain more than the key itself. + Object.getOwnPropertyNames(presState).length > 1 + ); + + if (presStates.length) { + entry.presState = presStates; + } + } + } + + // Collect triggeringPrincipal data for the current history entry. + if (shEntry.principalToInherit) { + entry.principalToInherit_base64 = lazy.E10SUtils.serializePrincipal( + shEntry.principalToInherit + ); + } + + if (shEntry.partitionedPrincipalToInherit) { + entry.partitionedPrincipalToInherit_base64 = + lazy.E10SUtils.serializePrincipal( + shEntry.partitionedPrincipalToInherit + ); + } + + entry.hasUserInteraction = shEntry.hasUserInteraction; + + if (shEntry.triggeringPrincipal) { + entry.triggeringPrincipal_base64 = lazy.E10SUtils.serializePrincipal( + shEntry.triggeringPrincipal + ); + } + + if (shEntry.csp) { + entry.csp = lazy.E10SUtils.serializeCSP(shEntry.csp); + } + + entry.docIdentifier = shEntry.bfcacheID; + + if (shEntry.stateData != null) { + let stateData = shEntry.stateData; + entry.structuredCloneState = stateData.getDataAsBase64(); + entry.structuredCloneVersion = stateData.formatVersion; + } + + if (shEntry.wireframe != null) { + entry.wireframe = shEntry.wireframe; + } + + if (shEntry.childCount > 0 && !shEntry.hasDynamicallyAddedChild()) { + let children = []; + for (let i = 0; i < shEntry.childCount; i++) { + let child = shEntry.GetChildAt(i); + + if (child) { + children.push(this.serializeEntry(child)); + } + } + + if (children.length) { + entry.children = children; + } + } + + entry.persist = shEntry.persist; + + return entry; + }, + + /** + * Get an object that is a serializable representation of a PresState. + * + * @param layoutHistoryState + * nsILayoutHistoryState instance + * @param stateKey + * The state key of the presState to be retrieved. + * @return object + */ + _getSerializablePresState(layoutHistoryState, stateKey) { + let presState = { stateKey }; + let x = {}, + y = {}, + scrollOriginDowngrade = {}, + res = {}; + + layoutHistoryState.getPresState(stateKey, x, y, scrollOriginDowngrade, res); + if (x.value !== 0 || y.value !== 0) { + presState.scroll = x.value + "," + y.value; + } + if (scrollOriginDowngrade.value === false) { + presState.scrollOriginDowngrade = scrollOriginDowngrade.value; + } + if (res.value != 1.0) { + presState.res = res.value; + } + + return presState; + }, + + /** + * Restores session history data for a given docShell. + * + * @param history + * The session history object. + * @param tabData + * The tabdata including all history entries. + * @return A reference to the docShell's nsISHistory interface. + */ + restore(history, tabData) { + if (history.count > 0) { + history.purgeHistory(history.count); + } + + let idMap = { used: {} }; + let docIdentMap = {}; + for (let i = 0; i < tabData.entries.length; i++) { + let entry = tabData.entries[i]; + // XXXzpao Wallpaper patch for bug 514751 + if (!entry.url) { + continue; + } + let persist = "persist" in entry ? entry.persist : true; + let shEntry = this.deserializeEntry(entry, idMap, docIdentMap, history); + + // To enable a smooth migration, we treat values of null/undefined as having + // user interaction (because we don't want to hide all session history that was + // added before we started recording user interaction). + // + // This attribute is only set on top-level SH history entries, so we set it + // outside of deserializeEntry since that is called recursively. + if (entry.hasUserInteraction == undefined) { + shEntry.hasUserInteraction = true; + } else { + shEntry.hasUserInteraction = entry.hasUserInteraction; + } + + history.addEntry(shEntry, persist); + } + + // Select the right history entry. + let index = tabData.index - 1; + if (index < history.count && history.index != index) { + history.index = index; + } + return history; + }, + + /** + * Expands serialized history data into a session-history-entry instance. + * + * @param entry + * Object containing serialized history data for a URL + * @param idMap + * Hash for ensuring unique frame IDs + * @param docIdentMap + * Hash to ensure reuse of BFCache entries + * @returns nsISHEntry + */ + deserializeEntry(entry, idMap, docIdentMap, shistory) { + var shEntry = shistory.createEntry(); + + shEntry.URI = Services.io.newURI(entry.url); + shEntry.title = entry.title || entry.url; + if (entry.subframe) { + shEntry.isSubFrame = entry.subframe || false; + } + shEntry.setLoadTypeAsHistory(); + if (entry.contentType) { + shEntry.contentType = entry.contentType; + } + // Referrer information is now stored as a referrerInfo property. We should + // also cope with the old format of passing `referrer` and `referrerPolicy` + // separately. + if (entry.referrerInfo) { + shEntry.referrerInfo = lazy.E10SUtils.deserializeReferrerInfo( + entry.referrerInfo + ); + } else if (entry.referrer) { + let ReferrerInfo = Components.Constructor( + "@mozilla.org/referrer-info;1", + "nsIReferrerInfo", + "init" + ); + shEntry.referrerInfo = new ReferrerInfo( + entry.referrerPolicy, + true, + Services.io.newURI(entry.referrer) + ); + } + + if (entry.originalURI) { + shEntry.originalURI = Services.io.newURI(entry.originalURI); + } + if (typeof entry.resultPrincipalURI === "undefined" && entry.loadReplace) { + // This is backward compatibility code for stored sessions saved prior to + // introduction of the resultPrincipalURI property. The equivalent of this + // property non-null value used to be the URL while the LOAD_REPLACE flag + // was set. + shEntry.resultPrincipalURI = shEntry.URI; + } else if (entry.resultPrincipalURI) { + shEntry.resultPrincipalURI = Services.io.newURI(entry.resultPrincipalURI); + } + if (entry.loadReplace2) { + shEntry.loadReplace = entry.loadReplace2; + } + if (entry.isSrcdocEntry) { + shEntry.srcdocData = entry.srcdocData; + } + if (entry.baseURI) { + shEntry.baseURI = Services.io.newURI(entry.baseURI); + } + + if (entry.cacheKey) { + shEntry.cacheKey = entry.cacheKey; + } + + if (entry.ID) { + // get a new unique ID for this frame (since the one from the last + // start might already be in use) + var id = idMap[entry.ID] || 0; + if (!id) { + // eslint-disable-next-line no-empty + for (id = Date.now(); id in idMap.used; id++) {} + idMap[entry.ID] = id; + idMap.used[id] = true; + } + shEntry.ID = id; + } + + // If we have the legacy docshellID on our entry, upgrade it to a + // docshellUUID by going through the mapping. + if (entry.docshellID) { + if (!this._docshellUUIDMap.has(entry.docshellID)) { + // Convert the nsID to a string so that the docshellUUID property + // is correctly stored as a string. + this._docshellUUIDMap.set( + entry.docshellID, + Services.uuid.generateUUID().toString() + ); + } + entry.docshellUUID = this._docshellUUIDMap.get(entry.docshellID); + delete entry.docshellID; + } + + if (entry.docshellUUID) { + shEntry.docshellID = Components.ID(entry.docshellUUID); + } + + if (entry.structuredCloneState && entry.structuredCloneVersion) { + var stateData = Cc[ + "@mozilla.org/docshell/structured-clone-container;1" + ].createInstance(Ci.nsIStructuredCloneContainer); + + stateData.initFromBase64( + entry.structuredCloneState, + entry.structuredCloneVersion + ); + shEntry.stateData = stateData; + } + + if (entry.scrollRestorationIsManual) { + shEntry.scrollRestorationIsManual = true; + } else { + if (entry.scroll) { + shEntry.setScrollPosition( + ...this._deserializeScrollPosition(entry.scroll) + ); + } + + if (entry.presState) { + let layoutHistoryState = shEntry.initLayoutHistoryState(); + + for (let presState of entry.presState) { + this._deserializePresState(layoutHistoryState, presState); + } + } + } + + let childDocIdents = {}; + if (entry.docIdentifier) { + // If we have a serialized document identifier, try to find an SHEntry + // which matches that doc identifier and adopt that SHEntry's + // BFCacheEntry. If we don't find a match, insert shEntry as the match + // for the document identifier. + let matchingEntry = docIdentMap[entry.docIdentifier]; + if (!matchingEntry) { + matchingEntry = { shEntry, childDocIdents }; + docIdentMap[entry.docIdentifier] = matchingEntry; + } else { + shEntry.adoptBFCacheEntry(matchingEntry.shEntry); + childDocIdents = matchingEntry.childDocIdents; + } + } + + // Every load must have a triggeringPrincipal to load otherwise we prevent it, + // this code *must* always return a valid principal: + shEntry.triggeringPrincipal = lazy.E10SUtils.deserializePrincipal( + entry.triggeringPrincipal_base64, + () => { + // This callback fires when we failed to deserialize the principal (or we don't have one) + // and this ensures we always have a principal returned from this function. + // We must always have a triggering principal for a load to work. + // A null principal won't always work however is safe to use. + console.warn( + "Couldn't deserialize the triggeringPrincipal, falling back to NullPrincipal" + ); + return Services.scriptSecurityManager.createNullPrincipal({}); + } + ); + // As both partitionedPrincipal and principalToInherit are both not required to load + // it's ok to keep these undefined when we don't have a previously defined principal. + if (entry.partitionedPrincipalToInherit_base64) { + shEntry.partitionedPrincipalToInherit = + lazy.E10SUtils.deserializePrincipal( + entry.partitionedPrincipalToInherit_base64 + ); + } + if (entry.principalToInherit_base64) { + shEntry.principalToInherit = lazy.E10SUtils.deserializePrincipal( + entry.principalToInherit_base64 + ); + } + if (entry.csp) { + shEntry.csp = lazy.E10SUtils.deserializeCSP(entry.csp); + } + if (entry.wireframe) { + shEntry.wireframe = entry.wireframe; + } + + if (entry.children) { + for (var i = 0; i < entry.children.length; i++) { + // XXXzpao Wallpaper patch for bug 514751 + if (!entry.children[i].url) { + continue; + } + + // We're getting sessionrestore.js files with a cycle in the + // doc-identifier graph, likely due to bug 698656. (That is, we have + // an entry where doc identifier A is an ancestor of doc identifier B, + // and another entry where doc identifier B is an ancestor of A.) + // + // If we were to respect these doc identifiers, we'd create a cycle in + // the SHEntries themselves, which causes the docshell to loop forever + // when it looks for the root SHEntry. + // + // So as a hack to fix this, we restrict the scope of a doc identifier + // to be a node's siblings and cousins, and pass childDocIdents, not + // aDocIdents, to _deserializeHistoryEntry. That is, we say that two + // SHEntries with the same doc identifier have the same document iff + // they have the same parent or their parents have the same document. + + shEntry.AddChild( + this.deserializeEntry( + entry.children[i], + idMap, + childDocIdents, + shistory + ), + i + ); + } + } + + return shEntry; + }, + + /** + * Expands serialized PresState data and adds it to the given nsILayoutHistoryState. + * + * @param layoutHistoryState + * nsILayoutHistoryState instance + * @param presState + * Object containing serialized PresState data. + */ + _deserializePresState(layoutHistoryState, presState) { + let stateKey = presState.stateKey; + let scrollOriginDowngrade = + typeof presState.scrollOriginDowngrade == "boolean" + ? presState.scrollOriginDowngrade + : true; + let res = presState.res || 1.0; + + layoutHistoryState.addNewPresState( + stateKey, + ...this._deserializeScrollPosition(presState.scroll), + scrollOriginDowngrade, + res + ); + }, + + /** + * Expands serialized scroll position data into an array containing the x and y coordinates, + * defaulting to 0,0 if no scroll position was found. + * + * @param scroll + * Object containing serialized scroll position data. + * @return An array containing the scroll position's x and y coordinates. + */ + _deserializeScrollPosition(scroll = "0,0") { + return scroll.split(",").map(pos => parseInt(pos, 10) || 0); + }, +}; diff --git a/toolkit/modules/sessionstore/Utils.sys.mjs b/toolkit/modules/sessionstore/Utils.sys.mjs new file mode 100644 index 0000000000..627cd22686 --- /dev/null +++ b/toolkit/modules/sessionstore/Utils.sys.mjs @@ -0,0 +1,29 @@ +/* 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 var Utils = Object.freeze({ + /** + * Restores frame tree |data|, starting at the given root |frame|. As the + * function recurses into descendant frames it will call cb(frame, data) for + * each frame it encounters, starting with the given root. + */ + restoreFrameTreeData(frame, data, cb) { + // Restore data for the root frame. + // The callback can abort by returning false. + if (cb(frame, data) === false) { + return; + } + + if (!data.hasOwnProperty("children")) { + return; + } + + // Recurse into child frames. + SessionStoreUtils.forEachNonDynamicChildFrame(frame, (subframe, index) => { + if (data.children[index]) { + this.restoreFrameTreeData(subframe, data.children[index], cb); + } + }); + }, +}); diff --git a/toolkit/modules/subprocess/.eslintrc.js b/toolkit/modules/subprocess/.eslintrc.js new file mode 100644 index 0000000000..7640781589 --- /dev/null +++ b/toolkit/modules/subprocess/.eslintrc.js @@ -0,0 +1,13 @@ +/* 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/. */ + +"use strict"; + +module.exports = { + extends: "../../components/extensions/.eslintrc.js", + + rules: { + "no-console": "off", + }, +}; diff --git a/toolkit/modules/subprocess/Subprocess.sys.mjs b/toolkit/modules/subprocess/Subprocess.sys.mjs new file mode 100644 index 0000000000..ffbeb0acbb --- /dev/null +++ b/toolkit/modules/subprocess/Subprocess.sys.mjs @@ -0,0 +1,198 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* 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/. */ + +/* + * These modules are loosely based on the subprocess.jsm module created + * by Jan Gerber and Patrick Brunschwig, though the implementation + * differs drastically. + */ + +import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; +import { SubprocessConstants } from "resource://gre/modules/subprocess/subprocess_common.sys.mjs"; + +const lazy = {}; + +if (AppConstants.platform == "win") { + ChromeUtils.defineESModuleGetters(lazy, { + SubprocessImpl: "resource://gre/modules/subprocess/subprocess_win.sys.mjs", + }); +} else { + ChromeUtils.defineESModuleGetters(lazy, { + SubprocessImpl: "resource://gre/modules/subprocess/subprocess_unix.sys.mjs", + }); +} + +function encodeEnvVar(name, value) { + if (typeof name === "string" && typeof value === "string") { + return `${name}=${value}`; + } + + let encoder = new TextEncoder(); + function encode(val) { + return typeof val === "string" ? encoder.encode(val) : val; + } + + return Uint8Array.of(...encode(name), ...encode("="), ...encode(value), 0); +} + +function platformSupportsDisclaimedSpawn() { + return AppConstants.isPlatformAndVersionAtLeast("macosx", 18); +} + +/** + * Allows for creation of and communication with OS-level sub-processes. + * + * @namespace + */ +export var Subprocess = { + /** + * Launches a process, and returns a handle to it. + * + * @param {object} options + * An object describing the process to launch. + * + * @param {string} options.command + * The full path of the executable to launch. Relative paths are not + * accepted, and `$PATH` is not searched. + * + * If a path search is necessary, the {@link Subprocess.pathSearch} method may + * be used to map a bare executable name to a full path. + * + * @param {string[]} [options.arguments] + * A list of strings to pass as arguments to the process. + * + * @param {object} [options.environment] An object containing a key + * and value for each environment variable to pass to the + * process. Values that are `=== null` are ignored. Only the + * object's own, enumerable properties are added to the environment. + * + * @param {boolean} [options.environmentAppend] If true, append the + * environment variables passed in `environment` to the existing set + * of environment variables. Values that are `=== null` are removed + * from the environment. Otherwise, the values in 'environment' + * constitute the entire set of environment variables passed to the + * new process. + * + * @param {string} [options.stderr] + * Defines how the process's stderr output is handled. One of: + * + * - `"ignore"`: (default) The process's standard error is not redirected. + * - `"stdout"`: The process's stderr is merged with its stdout. + * - `"pipe"`: The process's stderr is redirected to a pipe, which can be read + * from via its `stderr` property. + * + * @param {string} [options.workdir] + * The working directory in which to launch the new process. + * + * @param {boolean} [options.disclaim] + * macOS-specific option for 10.14+ OS versions. If true, enables a + * macOS-specific process launch option allowing the parent process to + * disclaim responsibility for the child process with respect to privacy/ + * security permission prompts and decisions. This option is ignored on + * platforms that do not support it. + * + * @returns {Promise<Process>} + * + * @throws {Error} + * May be rejected with an Error object if the process can not be + * launched. The object will include an `errorCode` property with + * one of the following values if it was rejected for the + * corresponding reason: + * + * - Subprocess.ERROR_BAD_EXECUTABLE: The given command could not + * be found, or the file that it references is not executable. + * + * Note that if the process is successfully launched, but exits with + * a non-zero exit code, the promise will still resolve successfully. + */ + call(options) { + options = Object.assign({}, options); + + options.stderr = options.stderr || "ignore"; + options.workdir = options.workdir || null; + options.disclaim = options.disclaim || false; + + let environment = {}; + if (!options.environment || options.environmentAppend) { + environment = this.getEnvironment(); + } + + if (options.environment) { + Object.assign(environment, options.environment); + } + + options.environment = Object.entries(environment) + .map(([key, val]) => (val !== null ? encodeEnvVar(key, val) : null)) + .filter(s => s); + + options.arguments = Array.from(options.arguments || []); + + if (options.disclaim && !platformSupportsDisclaimedSpawn()) { + options.disclaim = false; + } + + return Promise.resolve( + lazy.SubprocessImpl.isExecutableFile(options.command) + ).then(isExecutable => { + if (!isExecutable) { + let error = new Error( + `File at path "${options.command}" does not exist, or is not executable` + ); + error.errorCode = SubprocessConstants.ERROR_BAD_EXECUTABLE; + throw error; + } + + options.arguments.unshift(options.command); + + return lazy.SubprocessImpl.call(options); + }); + }, + + /** + * Returns an object with a key-value pair for every variable in the process's + * current environment. + * + * @returns {object} + */ + getEnvironment() { + let environment = Object.create(null); + for (let [k, v] of lazy.SubprocessImpl.getEnvironment()) { + environment[k] = v; + } + return environment; + }, + + /** + * Searches for the given executable file in the system executable + * file paths as specified by the PATH environment variable. + * + * On Windows, if the unadorned filename cannot be found, the + * extensions in the semicolon-separated list in the PATHSEP + * environment variable are successively appended to the original + * name and searched for in turn. + * + * @param {string} command + * The name of the executable to find. + * @param {object} [environment] + * An object containing a key for each environment variable to be used + * in the search. If not provided, full the current process environment + * is used. + * @returns {Promise<string>} + */ + pathSearch(command, environment = this.getEnvironment()) { + // Promise.resolve lets us get around returning one of the Promise.jsm + // pseudo-promises returned by Task.jsm. + let path = lazy.SubprocessImpl.pathSearch(command, environment); + return Promise.resolve(path); + }, +}; + +Object.assign(Subprocess, SubprocessConstants); +Object.freeze(Subprocess); + +export function getSubprocessImplForTest() { + return lazy.SubprocessImpl; +} diff --git a/toolkit/modules/subprocess/docs/index.rst b/toolkit/modules/subprocess/docs/index.rst new file mode 100644 index 0000000000..983fe30d4d --- /dev/null +++ b/toolkit/modules/subprocess/docs/index.rst @@ -0,0 +1,226 @@ +.. _Subprocess: + +================= +Subprocess Module +================= + +The Subprocess module allows a caller to spawn a native host executable, and +communicate with it asynchronously over its standard input and output pipes. + +Processes are launched asynchronously ``Subprocess.call`` method, based +on the properties of a single options object. The method returns a promise +which resolves, once the process has successfully launched, to a ``Process`` +object, which can be used to communicate with and control the process. + +A simple Hello World invocation, which writes a message to a process, reads it +back, logs it, and waits for the process to exit looks something like: + +.. code-block:: javascript + + let proc = await Subprocess.call({ + command: "/bin/cat", + }); + + proc.stdin.write("Hello World!"); + + let result = await proc.stdout.readString(); + console.log(result); + + proc.stdin.close(); + let {exitCode} = await proc.wait(); + +Input and Output Redirection +============================ + +Communication with the child process happens entirely via one-way pipes tied +to its standard input, standard output, and standard error file descriptors. +While standard input and output are always redirected to pipes, standard error +is inherited from the parent process by default. Standard error can, however, +optionally be either redirected to its own pipe or merged into the standard +output pipe. + +The module is designed primarily for use with processes following a strict +IO protocol, with predictable message sizes. Its read operations, therefore, +either complete after reading the exact amount of data specified, or do not +complete at all. For cases where this is not desirable, ``read()`` and +``readString`` may be called without any length argument, and will return a +chunk of data of an arbitrary size. + + +Process and Pipe Lifecycles +=========================== + +Once the process exits, any buffered data from its output pipes may still be +read until the pipe is explicitly closed. Unless the pipe is explicitly +closed, however, any pending buffered data *must* be read from the pipe, or +the resources associated with the pipe will not be freed. + +Beyond this, no explicit cleanup is required for either processes or their +pipes. So long as the caller ensures that the process exits, and there is no +pending input to be read on its ``stdout`` or ``stderr`` pipes, all resources +will be freed automatically. + +The preferred way to ensure that a process exits is to close its input pipe +and wait for it to exit gracefully. Processes which haven't exited gracefully +by shutdown time, however, must be forcibly terminated: + +.. code-block:: javascript + + let proc = await Subprocess.call({ + command: "/usr/bin/subprocess.py", + }); + + // Kill the process if it hasn't gracefully exited by shutdown time. + let blocker = () => proc.kill(); + + AsyncShutdown.profileBeforeChange.addBlocker( + "Subprocess: Killing hung process", + blocker); + + proc.wait().then(() => { + // Remove the shutdown blocker once we've exited. + AsyncShutdown.profileBeforeChange.removeBlocker(blocker); + + // Close standard output, in case there's any buffered data we haven't read. + proc.stdout.close(); + }); + + // Send a message to the process, and close stdin, so the process knows to + // exit. + proc.stdin.write(message); + proc.stdin.close(); + +In the simpler case of a short-running process which takes no input, and exits +immediately after producing output, it's generally enough to simply read its +output stream until EOF: + +.. code-block:: javascript + + let proc = await Subprocess.call({ + command: await Subprocess.pathSearch("ifconfig"), + }); + + // Read all of the process output. + let result = ""; + let string; + while ((string = await proc.stdout.readString())) { + result += string; + } + console.log(result); + + // The output pipe is closed and no buffered data remains to be read. + // This means the process has exited, and no further cleanup is necessary. + + +Bidirectional IO +================ + +When performing bidirectional IO, special care needs to be taken to avoid +deadlocks. While all IO operations in the Subprocess API are asynchronous, +careless ordering of operations can still lead to a state where both processes +are blocked on a read or write operation at the same time. For example, + +.. code-block:: javascript + + let proc = await Subprocess.call({ + command: "/bin/cat", + }); + + let size = 1024 * 1024; + await proc.stdin.write(new ArrayBuffer(size)); + + let result = await proc.stdout.read(size); + +The code attempts to write 1MB of data to an input pipe, and then read it back +from the output pipe. Because the data is big enough to fill both the input +and output pipe buffers, though, and because the code waits for the write +operation to complete before attempting any reads, the ``cat`` process will +block trying to write to its output indefinitely, and never finish reading the +data from its standard input. + +In order to avoid the deadlock, we need to avoid blocking on the write +operation: + +.. code-block:: javascript + + let size = 1024 * 1024; + proc.stdin.write(new ArrayBuffer(size)); + + let result = await proc.stdout.read(size); + +There is no silver bullet to avoiding deadlocks in this type of situation, +though. Any input operations that depend on output operations, or vice versa, +have the possibility of triggering deadlocks, and need to be thought out +carefully. + +Arguments +========= + +Arguments may be passed to the process in the form an array of strings. +Arguments are never split, or subjected to any sort of shell expansion, so the +target process will receive the exact arguments array as passed to +``Subprocess.call``. Argument 0 will always be the full path to the +executable, as passed via the ``command`` argument: + +.. code-block:: javascript + + let proc = await Subprocess.call({ + command: "/bin/sh", + arguments: ["-c", "echo -n $0"], + }); + + let output = await proc.stdout.readString(); + assert(output === "/bin/sh"); + + +Process Environment +=================== + +By default, the process is launched with the same environment variables and +working directory as the parent process, but either can be changed if +necessary. The working directory may be changed simply by passing a +``workdir`` option: + +.. code-block:: javascript + + let proc = await Subprocess.call({ + command: "/bin/pwd", + workdir: "/tmp", + }); + + let output = await proc.stdout.readString(); + assert(output === "/tmp\n"); + +The process's environment variables can be changed using the ``environment`` +and ``environmentAppend`` options. By default, passing an ``environment`` +object replaces the process's entire environment with the properties in that +object: + +.. code-block:: javascript + + let proc = await Subprocess.call({ + command: "/bin/pwd", + environment: {FOO: "BAR"}, + }); + + let output = await proc.stdout.readString(); + assert(output === "FOO=BAR\n"); + +In order to add variables to, or change variables from, the current set of +environment variables, the ``environmentAppend`` object must be passed in +addition: + +.. code-block:: javascript + + let proc = await Subprocess.call({ + command: "/bin/pwd", + environment: {FOO: "BAR"}, + environmentAppend: true, + }); + + let output = ""; + while ((string = await proc.stdout.readString())) { + output += string; + } + + assert(output.includes("FOO=BAR\n")); diff --git a/toolkit/modules/subprocess/moz.build b/toolkit/modules/subprocess/moz.build new file mode 100644 index 0000000000..8c79d7f20c --- /dev/null +++ b/toolkit/modules/subprocess/moz.build @@ -0,0 +1,35 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +EXTRA_JS_MODULES += [ + "Subprocess.sys.mjs", +] + +EXTRA_JS_MODULES.subprocess += [ + "subprocess_common.sys.mjs", + "subprocess_shared.js", + "subprocess_worker_common.js", +] + +if CONFIG["OS_TARGET"] == "WINNT": + EXTRA_JS_MODULES.subprocess += [ + "subprocess_shared_win.js", + "subprocess_win.sys.mjs", + "subprocess_win.worker.js", + ] +else: + EXTRA_JS_MODULES.subprocess += [ + "subprocess_shared_unix.js", + "subprocess_unix.sys.mjs", + "subprocess_unix.worker.js", + ] + +XPCSHELL_TESTS_MANIFESTS += ["test/xpcshell/xpcshell.toml"] + +SPHINX_TREES["toolkit_modules/subprocess"] = ["docs"] + +with Files("docs/**"): + SCHEDULES.exclusive = ["docs"] diff --git a/toolkit/modules/subprocess/subprocess_common.sys.mjs b/toolkit/modules/subprocess/subprocess_common.sys.mjs new file mode 100644 index 0000000000..aa3a28fb7b --- /dev/null +++ b/toolkit/modules/subprocess/subprocess_common.sys.mjs @@ -0,0 +1,711 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* 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/. */ + +/* eslint-disable mozilla/balanced-listeners */ + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs", + setTimeout: "resource://gre/modules/Timer.sys.mjs", +}); + +var obj = {}; +Services.scriptloader.loadSubScript( + "resource://gre/modules/subprocess/subprocess_shared.js", + obj +); + +const { ArrayBuffer_transfer } = obj; +export const SubprocessConstants = obj.SubprocessConstants; + +const BUFFER_SIZE = 32768; + +let nextResponseId = 0; + +/** + * Wraps a ChromeWorker so that messages sent to it return a promise which + * resolves when the message has been received and the operation it triggers is + * complete. + */ +export class PromiseWorker extends ChromeWorker { + constructor(url) { + super(url); + + this.listeners = new Map(); + this.pendingResponses = new Map(); + + this.addListener("close", this.onClose.bind(this)); + this.addListener("failure", this.onFailure.bind(this)); + this.addListener("success", this.onSuccess.bind(this)); + this.addListener("debug", this.onDebug.bind(this)); + + this.addEventListener("message", this.onmessage); + + this.shutdown = this.shutdown.bind(this); + lazy.AsyncShutdown.webWorkersShutdown.addBlocker( + "Subprocess.sys.mjs: Shut down IO worker", + this.shutdown + ); + } + + onClose() { + lazy.AsyncShutdown.webWorkersShutdown.removeBlocker(this.shutdown); + } + + shutdown() { + return this.call("shutdown", []); + } + + /** + * Adds a listener for the given message from the worker. Any message received + * from the worker with a `data.msg` property matching the given `msg` + * parameter are passed to the given listener. + * + * @param {string} msg + * The message to listen for. + * @param {function(Event)} listener + * The listener to call when matching messages are received. + */ + addListener(msg, listener) { + if (!this.listeners.has(msg)) { + this.listeners.set(msg, new Set()); + } + this.listeners.get(msg).add(listener); + } + + /** + * Removes the given message listener. + * + * @param {string} msg + * The message to stop listening for. + * @param {function(Event)} listener + * The listener to remove. + */ + removeListener(msg, listener) { + let listeners = this.listeners.get(msg); + if (listeners) { + listeners.delete(listener); + + if (!listeners.size) { + this.listeners.delete(msg); + } + } + } + + onmessage(event) { + let { msg } = event.data; + let listeners = this.listeners.get(msg) || new Set(); + + for (let listener of listeners) { + try { + listener(event.data); + } catch (e) { + console.error(e); + } + } + } + + /** + * Called when a message sent to the worker has failed, and rejects its + * corresponding promise. + * + * @private + */ + onFailure({ msgId, error }) { + this.pendingResponses.get(msgId).reject(error); + this.pendingResponses.delete(msgId); + } + + /** + * Called when a message sent to the worker has succeeded, and resolves its + * corresponding promise. + * + * @private + */ + onSuccess({ msgId, data }) { + this.pendingResponses.get(msgId).resolve(data); + this.pendingResponses.delete(msgId); + } + + onDebug({ message }) { + dump(`Worker debug: ${message}\n`); + } + + /** + * Calls the given method in the worker, and returns a promise which resolves + * or rejects when the method has completed. + * + * @param {string} method + * The name of the method to call. + * @param {Array} args + * The arguments to pass to the method. + * @param {Array} [transferList] + * A list of objects to transfer to the worker, rather than cloning. + * @returns {Promise} + */ + call(method, args, transferList = []) { + let msgId = nextResponseId++; + + return new Promise((resolve, reject) => { + this.pendingResponses.set(msgId, { resolve, reject }); + + let message = { + msg: method, + msgId, + args, + }; + + this.postMessage(message, transferList); + }); + } +} + +/** + * Represents an input or output pipe connected to a subprocess. + * + * @property {integer} fd + * The file descriptor number of the pipe on the child process's side. + * @readonly + */ +class Pipe { + /** + * @param {Process} process + * The child process that this pipe is connected to. + * @param {integer} fd + * The file descriptor number of the pipe on the child process's side. + * @param {integer} id + * The internal ID of the pipe, which ties it to the corresponding Pipe + * object on the Worker side. + */ + constructor(process, fd, id) { + this.id = id; + this.fd = fd; + this.processId = process.id; + this.worker = process.worker; + + /** + * @property {boolean} closed + * True if the file descriptor has been closed, and can no longer + * be read from or written to. Pending IO operations may still + * complete, but new operations may not be initiated. + * @readonly + */ + this.closed = false; + } + + /** + * Closes the end of the pipe which belongs to this process. + * + * @param {boolean} force + * If true, the pipe is closed immediately, regardless of any pending + * IO operations. If false, the pipe is closed after any existing + * pending IO operations have completed. + * @returns {Promise<object>} + * Resolves to an object with no properties once the pipe has been + * closed. + */ + close(force = false) { + this.closed = true; + return this.worker.call("close", [this.id, force]); + } +} + +/** + * Represents an output-only pipe, to which data may be written. + */ +class OutputPipe extends Pipe { + constructor(...args) { + super(...args); + + this.encoder = new TextEncoder(); + } + + /** + * Writes the given data to the stream. + * + * When given an array buffer or typed array, ownership of the buffer is + * transferred to the IO worker, and it may no longer be used from this + * thread. + * + * @param {ArrayBuffer|TypedArray|string} buffer + * Data to write to the stream. + * @returns {Promise<object>} + * Resolves to an object with a `bytesWritten` property, containing + * the number of bytes successfully written, once the operation has + * completed. + * + * @throws {object} + * May be rejected with an Error object, or an object with similar + * properties. The object will include an `errorCode` property with + * one of the following values if it was rejected for the + * corresponding reason: + * + * - Subprocess.ERROR_END_OF_FILE: The pipe was closed before + * all of the data in `buffer` could be written to it. + */ + write(buffer) { + if (typeof buffer === "string") { + buffer = this.encoder.encode(buffer); + } + + if (Cu.getClassName(buffer, true) !== "ArrayBuffer") { + if (buffer.byteLength === buffer.buffer.byteLength) { + buffer = buffer.buffer; + } else { + buffer = buffer.buffer.slice( + buffer.byteOffset, + buffer.byteOffset + buffer.byteLength + ); + } + } + + let args = [this.id, buffer]; + + return this.worker.call("write", args, [buffer]); + } +} + +/** + * Represents an input-only pipe, from which data may be read. + */ +class InputPipe extends Pipe { + constructor(...args) { + super(...args); + + this.buffers = []; + + /** + * @property {integer} dataAvailable + * The number of readable bytes currently stored in the input + * buffer. + * @readonly + */ + this.dataAvailable = 0; + + this.decoder = new TextDecoder(); + + this.pendingReads = []; + + this._pendingBufferRead = null; + + this.fillBuffer(); + } + + /** + * @property {integer} bufferSize + * The current size of the input buffer. This varies depending on + * the size of pending read operations. + * @readonly + */ + get bufferSize() { + if (this.pendingReads.length) { + return Math.max(this.pendingReads[0].length, BUFFER_SIZE); + } + return BUFFER_SIZE; + } + + /** + * Attempts to fill the input buffer. + * + * @private + */ + fillBuffer() { + let dataWanted = this.bufferSize - this.dataAvailable; + + if (!this._pendingBufferRead && dataWanted > 0) { + this._pendingBufferRead = this._read(dataWanted); + + this._pendingBufferRead.then(result => { + this._pendingBufferRead = null; + + if (result) { + this.onInput(result.buffer); + + this.fillBuffer(); + } + }); + } + } + + _read(size) { + let args = [this.id, size]; + + return this.worker.call("read", args).catch(e => { + this.closed = true; + + for (let { length, resolve, reject } of this.pendingReads.splice(0)) { + if ( + length === null && + e.errorCode === SubprocessConstants.ERROR_END_OF_FILE + ) { + resolve(new ArrayBuffer(0)); + } else { + reject(e); + } + } + }); + } + + /** + * Adds the given data to the end of the input buffer. + * + * @param {ArrayBuffer} buffer + * An input buffer to append to the current buffered input. + * @private + */ + onInput(buffer) { + this.buffers.push(buffer); + this.dataAvailable += buffer.byteLength; + this.checkPendingReads(); + } + + /** + * Checks the topmost pending read operations and fulfills as many as can be + * filled from the current input buffer. + * + * @private + */ + checkPendingReads() { + this.fillBuffer(); + + let reads = this.pendingReads; + while ( + reads.length && + this.dataAvailable && + reads[0].length <= this.dataAvailable + ) { + let pending = this.pendingReads.shift(); + + let length = pending.length || this.dataAvailable; + + let result; + let byteLength = this.buffers[0].byteLength; + if (byteLength == length) { + result = this.buffers.shift(); + } else if (byteLength > length) { + let buffer = this.buffers[0]; + + this.buffers[0] = buffer.slice(length); + result = ArrayBuffer_transfer(buffer, length); + } else { + result = ArrayBuffer_transfer(this.buffers.shift(), length); + let u8result = new Uint8Array(result); + + while (byteLength < length) { + let buffer = this.buffers[0]; + let u8buffer = new Uint8Array(buffer); + + let remaining = length - byteLength; + + if (buffer.byteLength <= remaining) { + this.buffers.shift(); + + u8result.set(u8buffer, byteLength); + } else { + this.buffers[0] = buffer.slice(remaining); + + u8result.set(u8buffer.subarray(0, remaining), byteLength); + } + + byteLength += Math.min(buffer.byteLength, remaining); + } + } + + this.dataAvailable -= result.byteLength; + pending.resolve(result); + } + } + + /** + * Reads exactly `length` bytes of binary data from the input stream, or, if + * length is not provided, reads the first chunk of data to become available. + * In the latter case, returns an empty array buffer on end of file. + * + * The read operation will not complete until enough data is available to + * fulfill the request. If the pipe closes without enough available data to + * fulfill the read, the operation fails, and any remaining buffered data is + * lost. + * + * @param {integer} [length] + * The number of bytes to read. + * @returns {Promise<ArrayBuffer>} + * + * @throws {object} + * May be rejected with an Error object, or an object with similar + * properties. The object will include an `errorCode` property with + * one of the following values if it was rejected for the + * corresponding reason: + * + * - Subprocess.ERROR_END_OF_FILE: The pipe was closed before + * enough input could be read to satisfy the request. + */ + read(length = null) { + if (length !== null && !(Number.isInteger(length) && length >= 0)) { + throw new RangeError("Length must be a non-negative integer"); + } + + if (length == 0) { + return Promise.resolve(new ArrayBuffer(0)); + } + + return new Promise((resolve, reject) => { + this.pendingReads.push({ length, resolve, reject }); + this.checkPendingReads(); + }); + } + + /** + * Reads exactly `length` bytes from the input stream, and parses them as + * UTF-8 JSON data. + * + * @param {integer} length + * The number of bytes to read. + * @returns {Promise<object>} + * + * @throws {object} + * May be rejected with an Error object, or an object with similar + * properties. The object will include an `errorCode` property with + * one of the following values if it was rejected for the + * corresponding reason: + * + * - Subprocess.ERROR_END_OF_FILE: The pipe was closed before + * enough input could be read to satisfy the request. + * - Subprocess.ERROR_INVALID_JSON: The data read from the pipe + * could not be parsed as a valid JSON string. + */ + readJSON(length) { + if (!Number.isInteger(length) || length <= 0) { + throw new RangeError("Length must be a positive integer"); + } + + return this.readString(length).then(string => { + try { + return JSON.parse(string); + } catch (e) { + e.errorCode = SubprocessConstants.ERROR_INVALID_JSON; + throw e; + } + }); + } + + /** + * Reads a chunk of UTF-8 data from the input stream, and converts it to a + * JavaScript string. + * + * If `length` is provided, reads exactly `length` bytes. Otherwise, reads the + * first chunk of data to become available, and returns an empty string on end + * of file. In the latter case, the chunk is decoded in streaming mode, and + * any incomplete UTF-8 sequences at the end of a chunk are returned at the + * start of a subsequent read operation. + * + * @param {integer} [length] + * The number of bytes to read. + * @param {object} [options] + * An options object as expected by TextDecoder.decode. + * @returns {Promise<string>} + * + * @throws {object} + * May be rejected with an Error object, or an object with similar + * properties. The object will include an `errorCode` property with + * one of the following values if it was rejected for the + * corresponding reason: + * + * - Subprocess.ERROR_END_OF_FILE: The pipe was closed before + * enough input could be read to satisfy the request. + */ + readString(length = null, options = { stream: length === null }) { + if (length !== null && !(Number.isInteger(length) && length >= 0)) { + throw new RangeError("Length must be a non-negative integer"); + } + + return this.read(length).then(buffer => { + return this.decoder.decode(buffer, options); + }); + } + + /** + * Reads 4 bytes from the input stream, and parses them as an unsigned + * integer, in native byte order. + * + * @returns {Promise<integer>} + * + * @throws {object} + * May be rejected with an Error object, or an object with similar + * properties. The object will include an `errorCode` property with + * one of the following values if it was rejected for the + * corresponding reason: + * + * - Subprocess.ERROR_END_OF_FILE: The pipe was closed before + * enough input could be read to satisfy the request. + */ + readUint32() { + return this.read(4).then(buffer => { + return new Uint32Array(buffer)[0]; + }); + } +} + +/** + * @class Process + * @augments BaseProcess + */ + +/** + * Represents a currently-running process, and allows interaction with it. + */ +export class BaseProcess { + /** + * @param {PromiseWorker} worker + * The worker instance which owns the process. + * @param {integer} processId + * The internal ID of the Process object, which ties it to the + * corresponding process on the Worker side. + * @param {integer[]} fds + * An array of internal Pipe IDs, one for each standard file descriptor + * in the child process. + * @param {integer} pid + * The operating system process ID of the process. + */ + constructor(worker, processId, fds, pid) { + this.id = processId; + this.worker = worker; + + /** + * @property {integer} pid + * The process ID of the process, assigned by the operating system. + * @readonly + */ + this.pid = pid; + + this.exitCode = null; + + this.exitPromise = new Promise(resolve => { + this.worker.call("wait", [this.id]).then(({ exitCode }) => { + resolve(Object.freeze({ exitCode })); + this.exitCode = exitCode; + }); + }); + + if (fds[0] !== undefined) { + /** + * @property {OutputPipe} stdin + * A Pipe object which allows writing to the process's standard + * input. + * @readonly + */ + this.stdin = new OutputPipe(this, 0, fds[0]); + } + if (fds[1] !== undefined) { + /** + * @property {InputPipe} stdout + * A Pipe object which allows reading from the process's standard + * output. + * @readonly + */ + this.stdout = new InputPipe(this, 1, fds[1]); + } + if (fds[2] !== undefined) { + /** + * @property {InputPipe} [stderr] + * An optional Pipe object which allows reading from the + * process's standard error output. + * @readonly + */ + this.stderr = new InputPipe(this, 2, fds[2]); + } + } + + /** + * Spawns a process, and resolves to a BaseProcess instance on success. + * + * @param {object} options + * An options object as passed to `Subprocess.call`. + * + * @returns {Promise<BaseProcess>} + */ + static create(options) { + let worker = this.getWorker(); + + return worker.call("spawn", [options]).then(({ processId, fds, pid }) => { + return new this(worker, processId, fds, pid); + }); + } + + static get WORKER_URL() { + throw new Error("Not implemented"); + } + + static get WorkerClass() { + return PromiseWorker; + } + + /** + * Gets the current subprocess worker, or spawns a new one if it does not + * currently exist. + * + * @returns {PromiseWorker} + */ + static getWorker() { + if (!this._worker) { + this._worker = new this.WorkerClass(this.WORKER_URL); + } + return this._worker; + } + + /** + * Kills the process. + * + * @param {integer} [timeout=300] + * A timeout, in milliseconds, after which the process will be forcibly + * killed. On platforms which support it, the process will be sent + * a `SIGTERM` signal immediately, so that it has a chance to terminate + * gracefully, and a `SIGKILL` signal if it hasn't exited within + * `timeout` milliseconds. On other platforms (namely Windows), the + * process will be forcibly terminated immediately. + * + * @returns {Promise<object>} + * Resolves to an object with an `exitCode` property when the process + * has exited. + */ + kill(timeout = 300) { + // If the process has already exited, don't bother sending a signal. + if (this.exitCode != null) { + return this.wait(); + } + + let force = timeout <= 0; + this.worker.call("kill", [this.id, force]); + + if (!force) { + lazy.setTimeout(() => { + if (this.exitCode == null) { + this.worker.call("kill", [this.id, true]); + } + }, timeout); + } + + return this.wait(); + } + + /** + * Returns a promise which resolves to the process's exit code, once it has + * exited. + * + * @returns {Promise<object>} + * Resolves to an object with an `exitCode` property, containing the + * process's exit code, once the process has exited. + * + * On Unix-like systems, a negative exit code indicates that the + * process was killed by a signal whose signal number is the absolute + * value of the error code. On Windows, an exit code of -9 indicates + * that the process was killed via the {@linkcode BaseProcess#kill kill()} + * method. + */ + wait() { + return this.exitPromise; + } +} diff --git a/toolkit/modules/subprocess/subprocess_shared.js b/toolkit/modules/subprocess/subprocess_shared.js new file mode 100644 index 0000000000..d59a14a351 --- /dev/null +++ b/toolkit/modules/subprocess/subprocess_shared.js @@ -0,0 +1,108 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* 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/. */ +"use strict"; + +/* exported ArrayBuffer_transfer, Library, SubprocessConstants */ + +// ctypes is either already available in the chrome worker scope, or defined +// in scope via loadSubScript. +/* global ctypes */ + +/** + * Returns a new ArrayBuffer whose contents have been taken from the `buffer`'s + * data and then is either truncated or zero-extended by `size`. If `size` is + * undefined, the `byteLength` of the `buffer` is used. This operation leaves + * `buffer` in a detached state. + * + * @param {ArrayBuffer} buffer + * @param {integer} [size = buffer.byteLength] + * @returns {ArrayBuffer} + */ +var ArrayBuffer_transfer = function (buffer, size = buffer.byteLength) { + let u8out = new Uint8Array(size); + let u8buffer = new Uint8Array(buffer, 0, Math.min(size, buffer.byteLength)); + u8out.set(u8buffer); + return u8out.buffer; +}; + +var libraries = {}; + +var Library = class Library { + constructor(name, names, definitions) { + if (name in libraries) { + return libraries[name]; + } + + for (let name of names) { + try { + if (!this.library) { + this.library = ctypes.open(name); + } + } catch (e) { + // Ignore errors until we've tried all the options. + } + } + if (!this.library) { + throw new Error("Could not load libc"); + } + + libraries[name] = this; + + for (let symbol of Object.keys(definitions)) { + this.declare(symbol, ...definitions[symbol]); + } + } + + declare(name, ...args) { + Object.defineProperty(this, name, { + configurable: true, + get() { + Object.defineProperty(this, name, { + configurable: true, + value: this.library.declare(name, ...args), + }); + + return this[name]; + }, + }); + } +}; + +/** + * Holds constants which apply to various Subprocess operations. + * + * @namespace + * @lends Subprocess + */ +var SubprocessConstants = { + /** + * @property {integer} ERROR_END_OF_FILE + * The operation failed because the end of the file was reached. + * @constant + */ + ERROR_END_OF_FILE: 0xff7a0001, + /** + * @property {integer} ERROR_INVALID_JSON + * The operation failed because an invalid JSON was encountered. + * @constant + */ + ERROR_INVALID_JSON: 0xff7a0002, + /** + * @property {integer} ERROR_BAD_EXECUTABLE + * The operation failed because the given file did not exist, or + * could not be executed. + * @constant + */ + ERROR_BAD_EXECUTABLE: 0xff7a0003, + /** + * @property {integer} ERROR_INVALID_OPTION + * The operation failed because an invalid option was provided. + * @constant + */ + ERROR_INVALID_OPTION: 0xff7a0004, +}; + +Object.freeze(SubprocessConstants); diff --git a/toolkit/modules/subprocess/subprocess_shared_unix.js b/toolkit/modules/subprocess/subprocess_shared_unix.js new file mode 100644 index 0000000000..345e5122e4 --- /dev/null +++ b/toolkit/modules/subprocess/subprocess_shared_unix.js @@ -0,0 +1,116 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* 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/. */ +"use strict"; + +/* exported LIBC, libc */ + +// ctypes is either already available in the chrome worker scope, or defined +// in scope via loadSubScript. +/* global ctypes */ + +// This file is loaded into the same scope as subprocess_shared.js. +/* import-globals-from subprocess_shared.js */ + +var LIBC = ChromeUtils.getLibcConstants(); + +const LIBC_CHOICES = ["a.out"]; + +const unix = { + pid_t: ctypes.int32_t, + + pollfd: new ctypes.StructType("pollfd", [ + { fd: ctypes.int }, + { events: ctypes.short }, + { revents: ctypes.short }, + ]), + + WEXITSTATUS(status) { + return (status >> 8) & 0xff; + }, + + WTERMSIG(status) { + return status & 0x7f; + }, +}; + +var libc = new Library("libc", LIBC_CHOICES, { + environ: [ctypes.char.ptr.ptr], + + // Darwin-only. + _NSGetEnviron: [ctypes.default_abi, ctypes.char.ptr.ptr.ptr], + + setenv: [ + ctypes.default_abi, + ctypes.int, + ctypes.char.ptr, + ctypes.char.ptr, + ctypes.int, + ], + + chdir: [ctypes.default_abi, ctypes.int, ctypes.char.ptr /* path */], + + close: [ctypes.default_abi, ctypes.int, ctypes.int /* fildes */], + + fcntl: [ + ctypes.default_abi, + ctypes.int, + ctypes.int /* fildes */, + ctypes.int /* cmd */, + ctypes.int /* ... */, + ], + + getcwd: [ + ctypes.default_abi, + ctypes.char.ptr, + ctypes.char.ptr /* buf */, + ctypes.size_t /* size */, + ], + + kill: [ + ctypes.default_abi, + ctypes.int, + unix.pid_t /* pid */, + ctypes.int /* signal */, + ], + + pipe: [ctypes.default_abi, ctypes.int, ctypes.int.array(2) /* pipefd */], + + poll: [ + ctypes.default_abi, + ctypes.int, + unix.pollfd.array() /* fds */, + ctypes.unsigned_int /* nfds */, + ctypes.int /* timeout */, + ], + + read: [ + ctypes.default_abi, + ctypes.ssize_t, + ctypes.int /* fildes */, + ctypes.char.ptr /* buf */, + ctypes.size_t /* nbyte */, + ], + + waitpid: [ + ctypes.default_abi, + unix.pid_t, + unix.pid_t /* pid */, + ctypes.int.ptr /* status */, + ctypes.int /* options */, + ], + + write: [ + ctypes.default_abi, + ctypes.ssize_t, + ctypes.int /* fildes */, + ctypes.char.ptr /* buf */, + ctypes.size_t /* nbyte */, + ], +}); + +unix.Fd = function (fd) { + return ctypes.CDataFinalizer(ctypes.int(fd), libc.close); +}; diff --git a/toolkit/modules/subprocess/subprocess_shared_win.js b/toolkit/modules/subprocess/subprocess_shared_win.js new file mode 100644 index 0000000000..964e647128 --- /dev/null +++ b/toolkit/modules/subprocess/subprocess_shared_win.js @@ -0,0 +1,532 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* 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/. */ +"use strict"; + +/* exported createPipe, libc, win32 */ + +// ctypes is either already available in the chrome worker scope, or defined +// in scope via loadSubScript. +/* global ctypes */ + +// This file is loaded into the same scope as subprocess_shared.js. +/* import-globals-from subprocess_shared.js */ + +const LIBC_CHOICES = ["kernel32.dll"]; + +var win32 = { + // On Windows 64, winapi_abi is an alias for default_abi. + WINAPI: ctypes.winapi_abi, + + VOID: ctypes.void_t, + + BYTE: ctypes.uint8_t, + WORD: ctypes.uint16_t, + DWORD: ctypes.uint32_t, + LONG: ctypes.long, + LARGE_INTEGER: ctypes.int64_t, + ULONGLONG: ctypes.uint64_t, + + UINT: ctypes.unsigned_int, + UCHAR: ctypes.unsigned_char, + + BOOL: ctypes.bool, + + HANDLE: ctypes.voidptr_t, + PVOID: ctypes.voidptr_t, + LPVOID: ctypes.voidptr_t, + + CHAR: ctypes.char, + WCHAR: ctypes.jschar, + + ULONG_PTR: ctypes.uintptr_t, + + SIZE_T: ctypes.size_t, + PSIZE_T: ctypes.size_t.ptr, +}; + +Object.assign(win32, { + DWORD_PTR: win32.ULONG_PTR, + + LPSTR: win32.CHAR.ptr, + LPWSTR: win32.WCHAR.ptr, + + LPBYTE: win32.BYTE.ptr, + LPDWORD: win32.DWORD.ptr, + LPHANDLE: win32.HANDLE.ptr, + + // This is an opaque type. + PROC_THREAD_ATTRIBUTE_LIST: ctypes.char.array(), + LPPROC_THREAD_ATTRIBUTE_LIST: ctypes.char.ptr, +}); + +Object.assign(win32, { + LPCSTR: win32.LPSTR, + LPCWSTR: win32.LPWSTR, + LPCVOID: win32.LPVOID, +}); + +Object.assign(win32, { + INVALID_HANDLE_VALUE: ctypes.cast(ctypes.int64_t(-1), win32.HANDLE), + NULL_HANDLE_VALUE: ctypes.cast(ctypes.uintptr_t(0), win32.HANDLE), + + CREATE_SUSPENDED: 0x00000004, + CREATE_NEW_CONSOLE: 0x00000010, + CREATE_UNICODE_ENVIRONMENT: 0x00000400, + CREATE_NO_WINDOW: 0x08000000, + CREATE_BREAKAWAY_FROM_JOB: 0x01000000, + EXTENDED_STARTUPINFO_PRESENT: 0x00080000, + + STARTF_USESTDHANDLES: 0x0100, + + DUPLICATE_CLOSE_SOURCE: 0x01, + DUPLICATE_SAME_ACCESS: 0x02, + + ERROR_HANDLE_EOF: 38, + ERROR_BROKEN_PIPE: 109, + ERROR_INSUFFICIENT_BUFFER: 122, + + FILE_ATTRIBUTE_NORMAL: 0x00000080, + FILE_FLAG_OVERLAPPED: 0x40000000, + + GENERIC_WRITE: 0x40000000, + + OPEN_EXISTING: 0x00000003, + + PIPE_TYPE_BYTE: 0x00, + + PIPE_ACCESS_INBOUND: 0x01, + PIPE_ACCESS_OUTBOUND: 0x02, + PIPE_ACCESS_DUPLEX: 0x03, + + PIPE_WAIT: 0x00, + PIPE_NOWAIT: 0x01, + + STILL_ACTIVE: 259, + + PROC_THREAD_ATTRIBUTE_HANDLE_LIST: 0x00020002, + + JobObjectBasicLimitInformation: 2, + JobObjectExtendedLimitInformation: 9, + + JOB_OBJECT_LIMIT_BREAKAWAY_OK: 0x00000800, + + // These constants are 32-bit unsigned integers, but Windows defines + // them as negative integers cast to an unsigned type. + STD_INPUT_HANDLE: -10 + 0x100000000, + STD_OUTPUT_HANDLE: -11 + 0x100000000, + STD_ERROR_HANDLE: -12 + 0x100000000, + + WAIT_TIMEOUT: 0x00000102, + WAIT_FAILED: 0xffffffff, +}); + +Object.assign(win32, { + JOBOBJECT_BASIC_LIMIT_INFORMATION: new ctypes.StructType( + "JOBOBJECT_BASIC_LIMIT_INFORMATION", + [ + { PerProcessUserTimeLimit: win32.LARGE_INTEGER }, + { PerJobUserTimeLimit: win32.LARGE_INTEGER }, + { LimitFlags: win32.DWORD }, + { MinimumWorkingSetSize: win32.SIZE_T }, + { MaximumWorkingSetSize: win32.SIZE_T }, + { ActiveProcessLimit: win32.DWORD }, + { Affinity: win32.ULONG_PTR }, + { PriorityClass: win32.DWORD }, + { SchedulingClass: win32.DWORD }, + ] + ), + + IO_COUNTERS: new ctypes.StructType("IO_COUNTERS", [ + { ReadOperationCount: win32.ULONGLONG }, + { WriteOperationCount: win32.ULONGLONG }, + { OtherOperationCount: win32.ULONGLONG }, + { ReadTransferCount: win32.ULONGLONG }, + { WriteTransferCount: win32.ULONGLONG }, + { OtherTransferCount: win32.ULONGLONG }, + ]), +}); + +Object.assign(win32, { + JOBOBJECT_EXTENDED_LIMIT_INFORMATION: new ctypes.StructType( + "JOBOBJECT_EXTENDED_LIMIT_INFORMATION", + [ + { BasicLimitInformation: win32.JOBOBJECT_BASIC_LIMIT_INFORMATION }, + { IoInfo: win32.IO_COUNTERS }, + { ProcessMemoryLimit: win32.SIZE_T }, + { JobMemoryLimit: win32.SIZE_T }, + { PeakProcessMemoryUsed: win32.SIZE_T }, + { PeakJobMemoryUsed: win32.SIZE_T }, + ] + ), + + OVERLAPPED: new ctypes.StructType("OVERLAPPED", [ + { Internal: win32.ULONG_PTR }, + { InternalHigh: win32.ULONG_PTR }, + { Offset: win32.DWORD }, + { OffsetHigh: win32.DWORD }, + { hEvent: win32.HANDLE }, + ]), + + PROCESS_INFORMATION: new ctypes.StructType("PROCESS_INFORMATION", [ + { hProcess: win32.HANDLE }, + { hThread: win32.HANDLE }, + { dwProcessId: win32.DWORD }, + { dwThreadId: win32.DWORD }, + ]), + + SECURITY_ATTRIBUTES: new ctypes.StructType("SECURITY_ATTRIBUTES", [ + { nLength: win32.DWORD }, + { lpSecurityDescriptor: win32.LPVOID }, + { bInheritHandle: win32.BOOL }, + ]), + + STARTUPINFOW: new ctypes.StructType("STARTUPINFOW", [ + { cb: win32.DWORD }, + { lpReserved: win32.LPWSTR }, + { lpDesktop: win32.LPWSTR }, + { lpTitle: win32.LPWSTR }, + { dwX: win32.DWORD }, + { dwY: win32.DWORD }, + { dwXSize: win32.DWORD }, + { dwYSize: win32.DWORD }, + { dwXCountChars: win32.DWORD }, + { dwYCountChars: win32.DWORD }, + { dwFillAttribute: win32.DWORD }, + { dwFlags: win32.DWORD }, + { wShowWindow: win32.WORD }, + { cbReserved2: win32.WORD }, + { lpReserved2: win32.LPBYTE }, + { hStdInput: win32.HANDLE }, + { hStdOutput: win32.HANDLE }, + { hStdError: win32.HANDLE }, + ]), +}); + +Object.assign(win32, { + STARTUPINFOEXW: new ctypes.StructType("STARTUPINFOEXW", [ + { StartupInfo: win32.STARTUPINFOW }, + { lpAttributeList: win32.LPPROC_THREAD_ATTRIBUTE_LIST }, + ]), +}); + +var libc = new Library("libc", LIBC_CHOICES, { + AssignProcessToJobObject: [ + win32.WINAPI, + win32.BOOL, + win32.HANDLE /* hJob */, + win32.HANDLE /* hProcess */, + ], + + CloseHandle: [win32.WINAPI, win32.BOOL, win32.HANDLE /* hObject */], + + CreateEventW: [ + win32.WINAPI, + win32.HANDLE, + win32.SECURITY_ATTRIBUTES.ptr /* opt lpEventAttributes */, + win32.BOOL /* bManualReset */, + win32.BOOL /* bInitialState */, + win32.LPWSTR /* lpName */, + ], + + CreateFileW: [ + win32.WINAPI, + win32.HANDLE, + win32.LPWSTR /* lpFileName */, + win32.DWORD /* dwDesiredAccess */, + win32.DWORD /* dwShareMode */, + win32.SECURITY_ATTRIBUTES.ptr /* opt lpSecurityAttributes */, + win32.DWORD /* dwCreationDisposition */, + win32.DWORD /* dwFlagsAndAttributes */, + win32.HANDLE /* opt hTemplateFile */, + ], + + CreateJobObjectW: [ + win32.WINAPI, + win32.HANDLE, + win32.SECURITY_ATTRIBUTES.ptr /* opt lpJobAttributes */, + win32.LPWSTR /* lpName */, + ], + + CreateNamedPipeW: [ + win32.WINAPI, + win32.HANDLE, + win32.LPWSTR /* lpName */, + win32.DWORD /* dwOpenMode */, + win32.DWORD /* dwPipeMode */, + win32.DWORD /* nMaxInstances */, + win32.DWORD /* nOutBufferSize */, + win32.DWORD /* nInBufferSize */, + win32.DWORD /* nDefaultTimeOut */, + win32.SECURITY_ATTRIBUTES.ptr /* opt lpSecurityAttributes */, + ], + + CreatePipe: [ + win32.WINAPI, + win32.BOOL, + win32.LPHANDLE /* out hReadPipe */, + win32.LPHANDLE /* out hWritePipe */, + win32.SECURITY_ATTRIBUTES.ptr /* opt lpPipeAttributes */, + win32.DWORD /* nSize */, + ], + + CreateProcessW: [ + win32.WINAPI, + win32.BOOL, + win32.LPCWSTR /* lpApplicationName */, + win32.LPWSTR /* lpCommandLine */, + win32.SECURITY_ATTRIBUTES.ptr /* lpProcessAttributes */, + win32.SECURITY_ATTRIBUTES.ptr /* lpThreadAttributes */, + win32.BOOL /* bInheritHandle */, + win32.DWORD /* dwCreationFlags */, + win32.LPVOID /* opt lpEnvironment */, + win32.LPCWSTR /* opt lpCurrentDirectory */, + win32.STARTUPINFOW.ptr /* lpStartupInfo */, + win32.PROCESS_INFORMATION.ptr /* out lpProcessInformation */, + ], + + CreateSemaphoreW: [ + win32.WINAPI, + win32.HANDLE, + win32.SECURITY_ATTRIBUTES.ptr /* opt lpSemaphoreAttributes */, + win32.LONG /* lInitialCount */, + win32.LONG /* lMaximumCount */, + win32.LPCWSTR /* opt lpName */, + ], + + DeleteProcThreadAttributeList: [ + win32.WINAPI, + win32.VOID, + win32.LPPROC_THREAD_ATTRIBUTE_LIST /* in/out lpAttributeList */, + ], + + DuplicateHandle: [ + win32.WINAPI, + win32.BOOL, + win32.HANDLE /* hSourceProcessHandle */, + win32.HANDLE /* hSourceHandle */, + win32.HANDLE /* hTargetProcessHandle */, + win32.LPHANDLE /* out lpTargetHandle */, + win32.DWORD /* dwDesiredAccess */, + win32.BOOL /* bInheritHandle */, + win32.DWORD /* dwOptions */, + ], + + FreeEnvironmentStringsW: [ + win32.WINAPI, + win32.BOOL, + win32.LPCWSTR /* lpszEnvironmentBlock */, + ], + + GetCurrentProcess: [win32.WINAPI, win32.HANDLE], + + GetCurrentProcessId: [win32.WINAPI, win32.DWORD], + + GetEnvironmentStringsW: [win32.WINAPI, win32.LPCWSTR], + + GetExitCodeProcess: [ + win32.WINAPI, + win32.BOOL, + win32.HANDLE /* hProcess */, + win32.LPDWORD /* lpExitCode */, + ], + + GetOverlappedResult: [ + win32.WINAPI, + win32.BOOL, + win32.HANDLE /* hFile */, + win32.OVERLAPPED.ptr /* lpOverlapped */, + win32.LPDWORD /* lpNumberOfBytesTransferred */, + win32.BOOL /* bWait */, + ], + + GetStdHandle: [win32.WINAPI, win32.HANDLE, win32.DWORD /* nStdHandle */], + + InitializeProcThreadAttributeList: [ + win32.WINAPI, + win32.BOOL, + win32.LPPROC_THREAD_ATTRIBUTE_LIST /* out opt lpAttributeList */, + win32.DWORD /* dwAttributeCount */, + win32.DWORD /* dwFlags */, + win32.PSIZE_T /* in/out lpSize */, + ], + + ReadFile: [ + win32.WINAPI, + win32.BOOL, + win32.HANDLE /* hFile */, + win32.LPVOID /* out lpBuffer */, + win32.DWORD /* nNumberOfBytesToRead */, + win32.LPDWORD /* opt out lpNumberOfBytesRead */, + win32.OVERLAPPED.ptr /* opt in/out lpOverlapped */, + ], + + ReleaseSemaphore: [ + win32.WINAPI, + win32.BOOL, + win32.HANDLE /* hSemaphore */, + win32.LONG /* lReleaseCount */, + win32.LONG.ptr /* opt out lpPreviousCount */, + ], + + ResumeThread: [win32.WINAPI, win32.DWORD, win32.HANDLE /* hThread */], + + SetInformationJobObject: [ + win32.WINAPI, + win32.BOOL, + win32.HANDLE /* hJob */, + ctypes.int /* JobObjectInfoClass */, + win32.LPVOID /* lpJobObjectInfo */, + win32.DWORD /* cbJobObjectInfoLengt */, + ], + + TerminateJobObject: [ + win32.WINAPI, + win32.BOOL, + win32.HANDLE /* hJob */, + win32.UINT /* uExitCode */, + ], + + TerminateProcess: [ + win32.WINAPI, + win32.BOOL, + win32.HANDLE /* hProcess */, + win32.UINT /* uExitCode */, + ], + + UpdateProcThreadAttribute: [ + win32.WINAPI, + win32.BOOL, + win32.LPPROC_THREAD_ATTRIBUTE_LIST /* in/out lpAttributeList */, + win32.DWORD /* dwFlags */, + win32.DWORD_PTR /* Attribute */, + win32.PVOID /* lpValue */, + win32.SIZE_T /* cbSize */, + win32.PVOID /* out opt lpPreviousValue */, + win32.PSIZE_T /* opt lpReturnSize */, + ], + + WaitForMultipleObjects: [ + win32.WINAPI, + win32.DWORD, + win32.DWORD /* nCount */, + win32.HANDLE.ptr /* hHandles */, + win32.BOOL /* bWaitAll */, + win32.DWORD /* dwMilliseconds */, + ], + + WaitForSingleObject: [ + win32.WINAPI, + win32.DWORD, + win32.HANDLE /* hHandle */, + win32.BOOL /* bWaitAll */, + win32.DWORD /* dwMilliseconds */, + ], + + WriteFile: [ + win32.WINAPI, + win32.BOOL, + win32.HANDLE /* hFile */, + win32.LPCVOID /* lpBuffer */, + win32.DWORD /* nNumberOfBytesToRead */, + win32.LPDWORD /* opt out lpNumberOfBytesWritten */, + win32.OVERLAPPED.ptr /* opt in/out lpOverlapped */, + ], +}); + +let nextNamedPipeId = 0; + +win32.Handle = function (handle) { + return ctypes.CDataFinalizer(win32.HANDLE(handle), libc.CloseHandle); +}; + +win32.createPipe = function (secAttr, readFlags = 0, writeFlags = 0, size = 0) { + readFlags |= win32.PIPE_ACCESS_INBOUND; + writeFlags |= win32.FILE_ATTRIBUTE_NORMAL; + + if (size == 0) { + size = 4096; + } + + let pid = libc.GetCurrentProcessId(); + let pipeName = String.raw`\\.\Pipe\SubProcessPipe.${pid}.${nextNamedPipeId++}`; + + let readHandle = libc.CreateNamedPipeW( + pipeName, + readFlags, + win32.PIPE_TYPE_BYTE | win32.PIPE_WAIT, + 1 /* number of connections */, + size /* output buffer size */, + size /* input buffer size */, + 0 /* timeout */, + secAttr.address() + ); + + let isInvalid = handle => + String(handle) == String(win32.INVALID_HANDLE_VALUE); + + if (isInvalid(readHandle)) { + return []; + } + + let writeHandle = libc.CreateFileW( + pipeName, + win32.GENERIC_WRITE, + 0, + secAttr.address(), + win32.OPEN_EXISTING, + writeFlags, + null + ); + + if (isInvalid(writeHandle)) { + libc.CloseHandle(readHandle); + return []; + } + + return [win32.Handle(readHandle), win32.Handle(writeHandle)]; +}; + +win32.createThreadAttributeList = function (handles) { + try { + void libc.InitializeProcThreadAttributeList; + void libc.DeleteProcThreadAttributeList; + void libc.UpdateProcThreadAttribute; + } catch (e) { + // This is only supported in Windows Vista and later. + return null; + } + + let size = win32.SIZE_T(); + if ( + !libc.InitializeProcThreadAttributeList(null, 1, 0, size.address()) && + ctypes.winLastError != win32.ERROR_INSUFFICIENT_BUFFER + ) { + return null; + } + + let attrList = win32.PROC_THREAD_ATTRIBUTE_LIST(size.value); + + if (!libc.InitializeProcThreadAttributeList(attrList, 1, 0, size.address())) { + return null; + } + + let ok = libc.UpdateProcThreadAttribute( + attrList, + 0, + win32.PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + handles, + handles.constructor.size, + null, + null + ); + + if (!ok) { + libc.DeleteProcThreadAttributeList(attrList); + return null; + } + + return attrList; +}; diff --git a/toolkit/modules/subprocess/subprocess_unix.sys.mjs b/toolkit/modules/subprocess/subprocess_unix.sys.mjs new file mode 100644 index 0000000000..59b5873af2 --- /dev/null +++ b/toolkit/modules/subprocess/subprocess_unix.sys.mjs @@ -0,0 +1,203 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* 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 { + BaseProcess, + PromiseWorker, +} from "resource://gre/modules/subprocess/subprocess_common.sys.mjs"; + +import { ctypes } from "resource://gre/modules/ctypes.sys.mjs"; + +var obj = { ctypes }; +Services.scriptloader.loadSubScript( + "resource://gre/modules/subprocess/subprocess_shared.js", + obj +); +Services.scriptloader.loadSubScript( + "resource://gre/modules/subprocess/subprocess_shared_unix.js", + obj +); + +const { SubprocessConstants, LIBC } = obj; + +// libc is exported for tests. +export var libc = obj.libc; + +class UnixPromiseWorker extends PromiseWorker { + constructor(...args) { + super(...args); + + let fds = ctypes.int.array(2)(); + let res = libc.pipe(fds); + if (res == -1) { + throw new Error("Unable to create pipe"); + } + + this.signalFd = fds[1]; + + libc.fcntl(fds[0], LIBC.F_SETFL, LIBC.O_NONBLOCK); + libc.fcntl(fds[0], LIBC.F_SETFD, LIBC.FD_CLOEXEC); + libc.fcntl(fds[1], LIBC.F_SETFD, LIBC.FD_CLOEXEC); + + this.call("init", [{ signalFd: fds[0] }]); + } + + closePipe() { + if (this.signalFd) { + libc.close(this.signalFd); + this.signalFd = null; + } + } + + onClose() { + this.closePipe(); + super.onClose(); + } + + signalWorker() { + libc.write(this.signalFd, new ArrayBuffer(1), 1); + } + + postMessage(...args) { + this.signalWorker(); + return super.postMessage(...args); + } +} + +class Process extends BaseProcess { + static get WORKER_URL() { + return "resource://gre/modules/subprocess/subprocess_unix.worker.js"; + } + + static get WorkerClass() { + return UnixPromiseWorker; + } +} + +// Convert a null-terminated char pointer into a sized char array, and then +// convert that into a JS typed array. +// The resulting array will not be null-terminated. +function ptrToUint8Array(input) { + let { cast, uint8_t } = ctypes; + + let len = 0; + for ( + let ptr = cast(input, uint8_t.ptr); + ptr.contents; + ptr = ptr.increment() + ) { + len++; + } + + let aryPtr = cast(input, uint8_t.array(len).ptr); + return new Uint8Array(aryPtr.contents); +} + +var SubprocessUnix = { + Process, + + call(options) { + return Process.create(options); + }, + + *getEnvironment() { + let environ; + if (Services.appinfo.OS === "Darwin") { + environ = libc._NSGetEnviron().contents; + } else { + environ = libc.environ; + } + + const EQUAL = "=".charCodeAt(0); + let decoder = new TextDecoder("utf-8", { fatal: true }); + + function decode(array) { + try { + return decoder.decode(array); + } catch (e) { + return array; + } + } + + for ( + let envp = environ; + !envp.isNull() && !envp.contents.isNull(); + envp = envp.increment() + ) { + let buf = ptrToUint8Array(envp.contents); + + for (let i = 0; i < buf.length; i++) { + if (buf[i] == EQUAL) { + yield [decode(buf.subarray(0, i)), decode(buf.subarray(i + 1))]; + break; + } + } + } + }, + + isExecutableFile: async function isExecutable(path) { + if (!PathUtils.isAbsolute(path)) { + return false; + } + + try { + let info = await IOUtils.stat(path); + + // FIXME: We really want access(path, X_OK) here, but IOUtils does not + // support it. + return info.type !== "directory" && info.permissions & 0o111; + } catch (e) { + return false; + } + }, + + /** + * Searches for the given executable file in the system executable + * file paths as specified by the PATH environment variable. + * + * On Windows, if the unadorned filename cannot be found, the + * extensions in the semicolon-separated list in the PATHEXT + * environment variable are successively appended to the original + * name and searched for in turn. + * + * @param {string} bin + * The name of the executable to find. + * @param {object} environment + * An object containing a key for each environment variable to be used + * in the search. + * @returns {Promise<string>} + */ + async pathSearch(bin, environment) { + if (PathUtils.isAbsolute(bin)) { + if (await this.isExecutableFile(bin)) { + return bin; + } + let error = new Error( + `File at path "${bin}" does not exist, or is not executable` + ); + error.errorCode = SubprocessConstants.ERROR_BAD_EXECUTABLE; + throw error; + } + + let dirs = []; + if (typeof environment.PATH === "string") { + dirs = environment.PATH.split(":"); + } + + for (let dir of dirs) { + let path = PathUtils.join(dir, bin); + + if (await this.isExecutableFile(path)) { + return path; + } + } + let error = new Error(`Executable not found: ${bin}`); + error.errorCode = SubprocessConstants.ERROR_BAD_EXECUTABLE; + throw error; + }, +}; + +export var SubprocessImpl = SubprocessUnix; diff --git a/toolkit/modules/subprocess/subprocess_unix.worker.js b/toolkit/modules/subprocess/subprocess_unix.worker.js new file mode 100644 index 0000000000..85632d2398 --- /dev/null +++ b/toolkit/modules/subprocess/subprocess_unix.worker.js @@ -0,0 +1,611 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* 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/. */ +"use strict"; + +/* exported Process */ + +/* import-globals-from subprocess_shared.js */ +/* import-globals-from subprocess_shared_unix.js */ +/* import-globals-from subprocess_worker_common.js */ +importScripts( + "resource://gre/modules/subprocess/subprocess_shared.js", + "resource://gre/modules/subprocess/subprocess_shared_unix.js", + "resource://gre/modules/subprocess/subprocess_worker_common.js" +); + +const POLL_TIMEOUT = 5000; + +let io; + +let nextPipeId = 0; + +class Pipe extends BasePipe { + constructor(process, fd) { + super(); + + this.process = process; + this.fd = fd; + this.id = nextPipeId++; + } + + get pollEvents() { + throw new Error("Not implemented"); + } + + /** + * Closes the file descriptor. + * + * @param {boolean} [force=false] + * If true, the file descriptor is closed immediately. If false, the + * file descriptor is closed after all current pending IO operations + * have completed. + * + * @returns {Promise<void>} + * Resolves when the file descriptor has been closed. + */ + close(force = false) { + if (!force && this.pending.length) { + this.closing = true; + return this.closedPromise; + } + + for (let { reject } of this.pending) { + let error = new Error("File closed"); + error.errorCode = SubprocessConstants.ERROR_END_OF_FILE; + reject(error); + } + this.pending.length = 0; + + if (!this.closed) { + this.fd.dispose(); + + this.closed = true; + this.resolveClosed(); + + io.pipes.delete(this.id); + io.updatePollFds(); + } + return this.closedPromise; + } + + /** + * Called when an error occurred while polling our file descriptor. + */ + onError() { + this.close(true); + this.process.wait(); + } +} + +class InputPipe extends Pipe { + /** + * A bit mask of poll() events which we currently wish to be notified of on + * this file descriptor. + */ + get pollEvents() { + if (this.pending.length) { + return LIBC.POLLIN; + } + return 0; + } + + /** + * Asynchronously reads at most `length` bytes of binary data from the file + * descriptor into an ArrayBuffer of the same size. Returns a promise which + * resolves when the operation is complete. + * + * @param {integer} length + * The number of bytes to read. + * + * @returns {Promise<ArrayBuffer>} + */ + read(length) { + if (this.closing || this.closed) { + throw new Error("Attempt to read from closed pipe"); + } + + return new Promise((resolve, reject) => { + this.pending.push({ resolve, reject, length }); + io.updatePollFds(); + }); + } + + /** + * Synchronously reads at most `count` bytes of binary data into an + * ArrayBuffer, and returns that buffer. If no data can be read without + * blocking, returns null instead. + * + * @param {integer} count + * The number of bytes to read. + * + * @returns {ArrayBuffer|null} + */ + readBuffer(count) { + let buffer = new ArrayBuffer(count); + + let read = +libc.read(this.fd, buffer, buffer.byteLength); + if (read < 0 && ctypes.errno != LIBC.EAGAIN) { + this.onError(); + } + + if (read <= 0) { + return null; + } + + if (read < buffer.byteLength) { + return ArrayBuffer_transfer(buffer, read); + } + + return buffer; + } + + /** + * Called when one of the IO operations matching the `pollEvents` mask may be + * performed without blocking. + * + * @returns {boolean} + * True if any data was successfully read. + */ + onReady() { + let result = false; + let reads = this.pending; + while (reads.length) { + let { resolve, length } = reads[0]; + + let buffer = this.readBuffer(length); + if (buffer) { + result = true; + this.shiftPending(); + resolve(buffer); + } else { + break; + } + } + + if (!reads.length) { + io.updatePollFds(); + } + return result; + } +} + +class OutputPipe extends Pipe { + /** + * A bit mask of poll() events which we currently wish to be notified of on + * this file discriptor. + */ + get pollEvents() { + if (this.pending.length) { + return LIBC.POLLOUT; + } + return 0; + } + + /** + * Asynchronously writes the given buffer to our file descriptor, and returns + * a promise which resolves when the operation is complete. + * + * @param {ArrayBuffer} buffer + * The buffer to write. + * + * @returns {Promise<integer>} + * Resolves to the number of bytes written when the operation is + * complete. + */ + write(buffer) { + if (this.closing || this.closed) { + throw new Error("Attempt to write to closed pipe"); + } + + return new Promise((resolve, reject) => { + this.pending.push({ resolve, reject, buffer, length: buffer.byteLength }); + io.updatePollFds(); + }); + } + + /** + * Attempts to synchronously write the given buffer to our file descriptor. + * Writes only as many bytes as can be written without blocking, and returns + * the number of byes successfully written. + * + * Closes the file descriptor if an IO error occurs. + * + * @param {ArrayBuffer} buffer + * The buffer to write. + * + * @returns {integer} + * The number of bytes successfully written. + */ + writeBuffer(buffer) { + let bytesWritten = libc.write(this.fd, buffer, buffer.byteLength); + + if (bytesWritten < 0 && ctypes.errno != LIBC.EAGAIN) { + this.onError(); + } + + return bytesWritten; + } + + /** + * Called when one of the IO operations matching the `pollEvents` mask may be + * performed without blocking. + */ + onReady() { + let writes = this.pending; + while (writes.length) { + let { buffer, resolve, length } = writes[0]; + + let written = this.writeBuffer(buffer); + + if (written == buffer.byteLength) { + resolve(length); + this.shiftPending(); + } else if (written > 0) { + writes[0].buffer = buffer.slice(written); + } else { + break; + } + } + + if (!writes.length) { + io.updatePollFds(); + } + } +} + +class Signal { + constructor(fd) { + this.fd = fd; + } + + cleanup() { + libc.close(this.fd); + this.fd = null; + } + + get pollEvents() { + return LIBC.POLLIN; + } + + /** + * Called when an error occurred while polling our file descriptor. + */ + onError() { + io.shutdown(); + } + + /** + * Called when one of the IO operations matching the `pollEvents` mask may be + * performed without blocking. + */ + onReady() { + let buffer = new ArrayBuffer(16); + let count = +libc.read(this.fd, buffer, buffer.byteLength); + if (count > 0) { + io.messageCount += count; + } + } +} + +class Process extends BaseProcess { + /** + * Each Process object opens an additional pipe from the target object, which + * will be automatically closed when the process exits, but otherwise + * carries no data. + * + * This property contains a bit mask of poll() events which we wish to be + * notified of on this descriptor. We're not expecting any input from this + * pipe, but we need to poll for input until the process exits in order to be + * notified when the pipe closes. + */ + get pollEvents() { + if (this.exitCode === null) { + return LIBC.POLLIN; + } + return 0; + } + + /** + * Kills the process with the given signal. + * + * @param {integer} signal + */ + kill(signal) { + libc.kill(this.pid, signal); + this.wait(); + } + + /** + * Initializes the IO pipes for use as standard input, output, and error + * descriptors in the spawned process. + * + * @param {object} options + * The Subprocess options object for this process. + * @returns {unix.Fd[]} + * The array of file descriptors belonging to the spawned process. + */ + initPipes(options) { + let stderr = options.stderr; + + let our_pipes = []; + let their_pipes = new Map(); + + let pipe = input => { + let fds = ctypes.int.array(2)(); + + let res = libc.pipe(fds); + if (res == -1) { + throw new Error("Unable to create pipe"); + } + + fds = Array.from(fds, unix.Fd); + + if (input) { + fds.reverse(); + } + + if (input) { + our_pipes.push(new InputPipe(this, fds[1])); + } else { + our_pipes.push(new OutputPipe(this, fds[1])); + } + + libc.fcntl(fds[0], LIBC.F_SETFD, LIBC.FD_CLOEXEC); + libc.fcntl(fds[1], LIBC.F_SETFD, LIBC.FD_CLOEXEC); + libc.fcntl(fds[1], LIBC.F_SETFL, LIBC.O_NONBLOCK); + + return fds[0]; + }; + + their_pipes.set(0, pipe(false)); + their_pipes.set(1, pipe(true)); + + if (stderr == "pipe") { + their_pipes.set(2, pipe(true)); + } else if (stderr == "stdout") { + their_pipes.set(2, their_pipes.get(1)); + } + + // Create an additional pipe that we can use to monitor for process exit. + their_pipes.set(3, pipe(true)); + this.fd = our_pipes.pop().fd; + + this.pipes = our_pipes; + + return their_pipes; + } + + spawn(options) { + let fds = this.initPipes(options); + + let launchOptions = { + environment: options.environment, + disclaim: options.disclaim, + fdMap: [], + }; + + // Check for truthiness to avoid chdir("null") + if (options.workdir) { + launchOptions.workdir = options.workdir; + } + + for (let [dst, src] of fds.entries()) { + launchOptions.fdMap.push({ src, dst }); + } + + try { + this.pid = IOUtils.launchProcess(options.arguments, launchOptions); + } finally { + for (let fd of new Set(fds.values())) { + fd.dispose(); + } + } + } + + /** + * Called when input is available on our sentinel file descriptor. + * + * @see pollEvents + */ + onReady() { + // We're not actually expecting any input on this pipe. If we get any, we + // can't poll the pipe any further without reading it. + if (this.wait() == undefined) { + this.kill(9); + } + } + + /** + * Called when an error occurred while polling our sentinel file descriptor. + * + * @see pollEvents + */ + onError() { + this.wait(); + } + + /** + * Attempts to wait for the process's exit status, without blocking. If + * successful, resolves the `exitPromise` to the process's exit value. + * + * @returns {integer|null} + * The process's exit status, if it has already exited. + */ + wait() { + if (this.exitCode !== null) { + return this.exitCode; + } + + let status = ctypes.int(); + + let res = libc.waitpid(this.pid, status.address(), LIBC.WNOHANG); + // If there's a failure here and we get any errno other than EINTR, it + // means that the process has been reaped by another thread (most likely + // the nspr process wait thread), and its actual exit status is not + // available to us. In that case, we have to assume success. + if (res == 0 || (res == -1 && ctypes.errno == LIBC.EINTR)) { + return null; + } + + let sig = unix.WTERMSIG(status.value); + if (sig) { + this.exitCode = -sig; + } else { + this.exitCode = unix.WEXITSTATUS(status.value); + } + + this.fd.dispose(); + io.updatePollFds(); + this.resolveExit(this.exitCode); + return this.exitCode; + } +} + +io = { + pollFds: null, + pollHandlers: null, + + pipes: new Map(), + + processes: new Map(), + + messageCount: 0, + + running: true, + + polling: false, + + init(details) { + this.signal = new Signal(details.signalFd); + this.updatePollFds(); + + setTimeout(this.loop.bind(this), 0); + }, + + shutdown() { + if (this.running) { + this.running = false; + + this.signal.cleanup(); + this.signal = null; + + self.postMessage({ msg: "close" }); + self.close(); + } + }, + + getPipe(pipeId) { + let pipe = this.pipes.get(pipeId); + + if (!pipe) { + let error = new Error("File closed"); + error.errorCode = SubprocessConstants.ERROR_END_OF_FILE; + throw error; + } + return pipe; + }, + + getProcess(processId) { + let process = this.processes.get(processId); + + if (!process) { + throw new Error(`Invalid process ID: ${processId}`); + } + return process; + }, + + updatePollFds() { + let handlers = [ + this.signal, + ...this.pipes.values(), + ...this.processes.values(), + ]; + + handlers = handlers.filter(handler => handler.pollEvents); + + // Our poll loop is only useful if we've got at least 1 thing to poll other than our own + // signal. + if (handlers.length == 1) { + this.polling = false; + } else if (!this.polling && this.running) { + // Restart the poll loop if necessary: + setTimeout(this.loop.bind(this), 0); + this.polling = true; + } + + let pollfds = unix.pollfd.array(handlers.length)(); + + for (let [i, handler] of handlers.entries()) { + let pollfd = pollfds[i]; + + pollfd.fd = handler.fd; + pollfd.events = handler.pollEvents; + pollfd.revents = 0; + } + + this.pollFds = pollfds; + this.pollHandlers = handlers; + }, + + loop() { + this.poll(); + if (this.running && this.polling) { + setTimeout(this.loop.bind(this), 0); + } + }, + + poll() { + let handlers = this.pollHandlers; + let pollfds = this.pollFds; + + let timeout = this.messageCount > 0 ? 0 : POLL_TIMEOUT; + let count = libc.poll(pollfds, pollfds.length, timeout); + + for (let i = 0; count && i < pollfds.length; i++) { + let pollfd = pollfds[i]; + if (pollfd.revents) { + count--; + + let handler = handlers[i]; + try { + let success = false; + if (pollfd.revents & handler.pollEvents) { + success = handler.onReady(); + } + // Only call the error handler in this iteration if we didn't also + // have a success. This is necessary because Linux systems set POLLHUP + // on a pipe when it's closed but there's still buffered data to be + // read, and Darwin sets POLLIN and POLLHUP on a closed pipe, even + // when there's no data to be read. + if ( + !success && + pollfd.revents & (LIBC.POLLERR | LIBC.POLLHUP | LIBC.POLLNVAL) + ) { + handler.onError(); + } + } catch (e) { + console.error(e); + debug(`Worker error: ${e} :: ${e.stack}`); + handler.onError(); + } + + pollfd.revents = 0; + } + } + }, + + addProcess(process) { + this.processes.set(process.id, process); + + for (let pipe of process.pipes) { + this.pipes.set(pipe.id, pipe); + } + }, + + cleanupProcess(process) { + this.processes.delete(process.id); + }, +}; diff --git a/toolkit/modules/subprocess/subprocess_win.sys.mjs b/toolkit/modules/subprocess/subprocess_win.sys.mjs new file mode 100644 index 0000000000..baf8640235 --- /dev/null +++ b/toolkit/modules/subprocess/subprocess_win.sys.mjs @@ -0,0 +1,173 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* 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 { + BaseProcess, + PromiseWorker, +} from "resource://gre/modules/subprocess/subprocess_common.sys.mjs"; + +import { ctypes } from "resource://gre/modules/ctypes.sys.mjs"; + +var obj = { ctypes }; +Services.scriptloader.loadSubScript( + "resource://gre/modules/subprocess/subprocess_shared.js", + obj +); +Services.scriptloader.loadSubScript( + "resource://gre/modules/subprocess/subprocess_shared_win.js", + obj +); + +const { SubprocessConstants } = obj; + +// libc and win32 are exported for tests. +export const libc = obj.libc; +export const win32 = obj.win32; + +class WinPromiseWorker extends PromiseWorker { + constructor(...args) { + super(...args); + + this.signalEvent = libc.CreateSemaphoreW(null, 0, 32, null); + + this.call("init", [ + { + comspec: Services.env.get("COMSPEC"), + signalEvent: String( + ctypes.cast(this.signalEvent, ctypes.uintptr_t).value + ), + }, + ]); + } + + signalWorker() { + libc.ReleaseSemaphore(this.signalEvent, 1, null); + } + + postMessage(...args) { + this.signalWorker(); + return super.postMessage(...args); + } +} + +class Process extends BaseProcess { + static get WORKER_URL() { + return "resource://gre/modules/subprocess/subprocess_win.worker.js"; + } + + static get WorkerClass() { + return WinPromiseWorker; + } +} + +var SubprocessWin = { + Process, + + call(options) { + return Process.create(options); + }, + + *getEnvironment() { + let env = libc.GetEnvironmentStringsW(); + try { + for (let p = env, q = env; ; p = p.increment()) { + if (p.contents == "\0") { + if (String(p) == String(q)) { + break; + } + + let str = q.readString(); + q = p.increment(); + + let idx = str.indexOf("="); + if (idx == 0) { + idx = str.indexOf("=", 1); + } + + if (idx >= 0) { + yield [str.slice(0, idx), str.slice(idx + 1)]; + } + } + } + } finally { + libc.FreeEnvironmentStringsW(env); + } + }, + + async isExecutableFile(path) { + if (!PathUtils.isAbsolute(path)) { + return false; + } + + try { + let info = await IOUtils.stat(path); + // On Windows, a FileType of "other" indicates it is a reparse point + // (i.e., a link). + return info.type !== "directory" && info.type !== "other"; + } catch (e) { + return false; + } + }, + + /** + * Searches for the given executable file in the system executable + * file paths as specified by the PATH environment variable. + * + * On Windows, if the unadorned filename cannot be found, the + * extensions in the semicolon-separated list in the PATHEXT + * environment variable are successively appended to the original + * name and searched for in turn. + * + * @param {string} bin + * The name of the executable to find. + * @param {object} environment + * An object containing a key for each environment variable to be used + * in the search. + * @returns {Promise<string>} + */ + async pathSearch(bin, environment) { + if (PathUtils.isAbsolute(bin)) { + if (await this.isExecutableFile(bin)) { + return bin; + } + let error = new Error( + `File at path "${bin}" does not exist, or is not a normal file` + ); + error.errorCode = SubprocessConstants.ERROR_BAD_EXECUTABLE; + throw error; + } + + let dirs = []; + let exts = []; + if (environment.PATH) { + dirs = environment.PATH.split(";"); + } + if (environment.PATHEXT) { + exts = environment.PATHEXT.split(";"); + } + + for (let dir of dirs) { + let path = PathUtils.join(dir, bin); + + if (await this.isExecutableFile(path)) { + return path; + } + + for (let ext of exts) { + let file = path + ext; + + if (await this.isExecutableFile(file)) { + return file; + } + } + } + let error = new Error(`Executable not found: ${bin}`); + error.errorCode = SubprocessConstants.ERROR_BAD_EXECUTABLE; + throw error; + }, +}; + +export var SubprocessImpl = SubprocessWin; diff --git a/toolkit/modules/subprocess/subprocess_win.worker.js b/toolkit/modules/subprocess/subprocess_win.worker.js new file mode 100644 index 0000000000..22d3857f8c --- /dev/null +++ b/toolkit/modules/subprocess/subprocess_win.worker.js @@ -0,0 +1,785 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* 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/. */ +"use strict"; + +/* exported Process */ + +/* import-globals-from subprocess_shared.js */ +/* import-globals-from subprocess_shared_win.js */ +/* import-globals-from subprocess_worker_common.js */ +importScripts( + "resource://gre/modules/subprocess/subprocess_shared.js", + "resource://gre/modules/subprocess/subprocess_shared_win.js", + "resource://gre/modules/subprocess/subprocess_worker_common.js" +); + +const POLL_TIMEOUT = 5000; + +// The exit code that we send when we forcibly terminate a process. +const TERMINATE_EXIT_CODE = 0x7f; + +let io; + +let nextPipeId = 0; + +class Pipe extends BasePipe { + constructor(process, origHandle) { + super(); + + let handle = win32.HANDLE(); + + let curProc = libc.GetCurrentProcess(); + libc.DuplicateHandle( + curProc, + origHandle, + curProc, + handle.address(), + 0, + false /* inheritable */, + win32.DUPLICATE_SAME_ACCESS + ); + + origHandle.dispose(); + + this.id = nextPipeId++; + this.process = process; + + this.handle = win32.Handle(handle); + + let event = libc.CreateEventW(null, false, false, null); + + this.overlapped = win32.OVERLAPPED(); + this.overlapped.hEvent = event; + + this._event = win32.Handle(event); + + this.buffer = null; + } + + get event() { + if (this.pending.length) { + return this._event; + } + return null; + } + + maybeClose() {} + + /** + * Closes the file handle. + * + * @param {boolean} [force=false] + * If true, the file handle is closed immediately. If false, the + * file handle is closed after all current pending IO operations + * have completed. + * + * @returns {Promise<void>} + * Resolves when the file handle has been closed. + */ + close(force = false) { + if (!force && this.pending.length) { + this.closing = true; + return this.closedPromise; + } + + for (let { reject } of this.pending) { + let error = new Error("File closed"); + error.errorCode = SubprocessConstants.ERROR_END_OF_FILE; + reject(error); + } + this.pending.length = 0; + + this.buffer = null; + + if (!this.closed) { + this.handle.dispose(); + this._event.dispose(); + + io.pipes.delete(this.id); + + this.handle = null; + this.closed = true; + this.resolveClosed(); + + io.updatePollEvents(); + } + return this.closedPromise; + } + + /** + * Called when an error occurred while attempting an IO operation on our file + * handle. + */ + onError() { + this.close(true); + } +} + +class InputPipe extends Pipe { + /** + * Queues the next chunk of data to be read from the pipe if, and only if, + * there is no IO operation currently pending. + */ + readNext() { + if (this.buffer === null) { + this.readBuffer(this.pending[0].length); + } + } + + /** + * Closes the pipe if there is a pending read operation with no more + * buffered data to be read. + */ + maybeClose() { + if (this.buffer) { + let read = win32.DWORD(); + + let ok = libc.GetOverlappedResult( + this.handle, + this.overlapped.address(), + read.address(), + false + ); + + if (!ok) { + this.onError(); + } + } + } + + /** + * Asynchronously reads at most `length` bytes of binary data from the file + * descriptor into an ArrayBuffer of the same size. Returns a promise which + * resolves when the operation is complete. + * + * @param {integer} length + * The number of bytes to read. + * + * @returns {Promise<ArrayBuffer>} + */ + read(length) { + if (this.closing || this.closed) { + throw new Error("Attempt to read from closed pipe"); + } + + return new Promise((resolve, reject) => { + this.pending.push({ resolve, reject, length }); + this.readNext(); + }); + } + + /** + * Initializes an overlapped IO read operation to read exactly `count` bytes + * into a new ArrayBuffer, which is stored in the `buffer` property until the + * operation completes. + * + * @param {integer} count + * The number of bytes to read. + */ + readBuffer(count) { + this.buffer = new ArrayBuffer(count); + + let ok = libc.ReadFile( + this.handle, + this.buffer, + count, + null, + this.overlapped.address() + ); + + if (!ok && (!this.process.handle || libc.winLastError)) { + this.onError(); + } else { + io.updatePollEvents(); + } + } + + /** + * Called when our pending overlapped IO operation has completed, whether + * successfully or in failure. + */ + onReady() { + let read = win32.DWORD(); + + let ok = libc.GetOverlappedResult( + this.handle, + this.overlapped.address(), + read.address(), + false + ); + + read = read.value; + + if (!ok) { + this.onError(); + } else if (read > 0) { + let buffer = this.buffer; + this.buffer = null; + + let { resolve } = this.shiftPending(); + + if (read == buffer.byteLength) { + resolve(buffer); + } else { + resolve(ArrayBuffer_transfer(buffer, read)); + } + + if (this.pending.length) { + this.readNext(); + } else { + io.updatePollEvents(); + } + } + } +} + +class OutputPipe extends Pipe { + /** + * Queues the next chunk of data to be written to the pipe if, and only if, + * there is no IO operation currently pending. + */ + writeNext() { + if (this.buffer === null) { + this.writeBuffer(this.pending[0].buffer); + } + } + + /** + * Asynchronously writes the given buffer to our file descriptor, and returns + * a promise which resolves when the operation is complete. + * + * @param {ArrayBuffer} buffer + * The buffer to write. + * + * @returns {Promise<integer>} + * Resolves to the number of bytes written when the operation is + * complete. + */ + write(buffer) { + if (this.closing || this.closed) { + throw new Error("Attempt to write to closed pipe"); + } + + return new Promise((resolve, reject) => { + this.pending.push({ resolve, reject, buffer }); + this.writeNext(); + }); + } + + /** + * Initializes an overapped IO read operation to write the data in `buffer` to + * our file descriptor. + * + * @param {ArrayBuffer} buffer + * The buffer to write. + */ + writeBuffer(buffer) { + this.buffer = buffer; + + let ok = libc.WriteFile( + this.handle, + buffer, + buffer.byteLength, + null, + this.overlapped.address() + ); + + if (!ok && libc.winLastError) { + this.onError(); + } else { + io.updatePollEvents(); + } + } + + /** + * Called when our pending overlapped IO operation has completed, whether + * successfully or in failure. + */ + onReady() { + let written = win32.DWORD(); + + let ok = libc.GetOverlappedResult( + this.handle, + this.overlapped.address(), + written.address(), + false + ); + + written = written.value; + + if (!ok || written != this.buffer.byteLength) { + this.onError(); + } else if (written > 0) { + let { resolve } = this.shiftPending(); + + this.buffer = null; + resolve(written); + + if (this.pending.length) { + this.writeNext(); + } else { + io.updatePollEvents(); + } + } + } +} + +class Signal { + constructor(event) { + this.event = event; + } + + cleanup() { + libc.CloseHandle(this.event); + this.event = null; + } + + onError() { + io.shutdown(); + } + + onReady() { + io.messageCount += 1; + } +} + +class Process extends BaseProcess { + constructor(...args) { + super(...args); + + this.killed = false; + } + + /** + * Returns our process handle for use as an event in a WaitForMultipleObjects + * call. + */ + get event() { + return this.handle; + } + + /** + * Forcibly terminates the process. + */ + kill() { + this.killed = true; + libc.TerminateJobObject(this.jobHandle, TERMINATE_EXIT_CODE); + } + + /** + * Initializes the IO pipes for use as standard input, output, and error + * descriptors in the spawned process. + * + * @returns {win32.Handle[]} + * The array of file handles belonging to the spawned process. + */ + initPipes({ stderr }) { + let our_pipes = []; + let their_pipes = []; + + let secAttr = new win32.SECURITY_ATTRIBUTES(); + secAttr.nLength = win32.SECURITY_ATTRIBUTES.size; + secAttr.bInheritHandle = true; + + let pipe = input => { + if (input) { + let handles = win32.createPipe(secAttr, win32.FILE_FLAG_OVERLAPPED); + our_pipes.push(new InputPipe(this, handles[0])); + return handles[1]; + } + let handles = win32.createPipe(secAttr, 0, win32.FILE_FLAG_OVERLAPPED); + our_pipes.push(new OutputPipe(this, handles[1])); + return handles[0]; + }; + + their_pipes[0] = pipe(false); + their_pipes[1] = pipe(true); + + if (stderr == "pipe") { + their_pipes[2] = pipe(true); + } else { + let srcHandle; + if (stderr == "stdout") { + srcHandle = their_pipes[1]; + } else { + srcHandle = libc.GetStdHandle(win32.STD_ERROR_HANDLE); + } + + // If we don't have a valid stderr handle, just pass it along without duplicating. + if ( + String(srcHandle) == win32.INVALID_HANDLE_VALUE || + String(srcHandle) == win32.NULL_HANDLE_VALUE + ) { + their_pipes[2] = srcHandle; + } else { + let handle = win32.HANDLE(); + + let curProc = libc.GetCurrentProcess(); + let ok = libc.DuplicateHandle( + curProc, + srcHandle, + curProc, + handle.address(), + 0, + true /* inheritable */, + win32.DUPLICATE_SAME_ACCESS + ); + + their_pipes[2] = ok && win32.Handle(handle); + } + } + + if (!their_pipes.every(handle => handle)) { + throw new Error("Failed to create pipe"); + } + + this.pipes = our_pipes; + + return their_pipes; + } + + /** + * Creates a null-separated, null-terminated string list. + * + * @param {Array<string>} strings + * @returns {win32.WCHAR.array} + */ + stringList(strings) { + // Remove empty strings, which would terminate the list early. + strings = strings.filter(string => string); + + let string = strings.join("\0") + "\0\0"; + + return win32.WCHAR.array()(string); + } + + /** + * Quotes a string for use as a single command argument, using Windows quoting + * conventions. + * + * @see https://msdn.microsoft.com/en-us/library/17w5ykft(v=vs.85).aspx + * + * @param {string} str + * The argument string to quote. + * @returns {string} + */ + quoteString(str) { + if (!/[\s"]/.test(str)) { + return str; + } + + let escaped = str.replace(/(\\*)("|$)/g, (m0, m1, m2) => { + if (m2) { + m2 = `\\${m2}`; + } + return `${m1}${m1}${m2}`; + }); + + return `"${escaped}"`; + } + + spawn(options) { + let { command, arguments: args } = options; + + if ( + /\\cmd\.exe$/i.test(command) && + args.length == 3 && + /^(\/S)?\/C$/i.test(args[1]) + ) { + // cmd.exe is insane and requires special treatment. + args = [this.quoteString(args[0]), "/S/C", `"${args[2]}"`]; + } else { + args = args.map(arg => this.quoteString(arg)); + } + + if (/\.(bat|cmd)$/i.test(command)) { + command = io.comspec; + args = ["cmd.exe", "/s/c", `"${args.join(" ")}"`]; + } + + let envp = this.stringList(options.environment); + + let handles = this.initPipes(options); + + let processFlags = + win32.CREATE_NO_WINDOW | + win32.CREATE_SUSPENDED | + win32.CREATE_UNICODE_ENVIRONMENT; + + let startupInfoEx = new win32.STARTUPINFOEXW(); + let startupInfo = startupInfoEx.StartupInfo; + + startupInfo.cb = win32.STARTUPINFOW.size; + startupInfo.dwFlags = win32.STARTF_USESTDHANDLES; + + startupInfo.hStdInput = handles[0]; + startupInfo.hStdOutput = handles[1]; + startupInfo.hStdError = handles[2]; + + // Note: This needs to be kept alive until we destroy the attribute list. + let handleArray = win32.HANDLE.array()(handles); + + let threadAttrs = win32.createThreadAttributeList(handleArray); + if (threadAttrs) { + // If have thread attributes to pass, pass the size of the full extended + // startup info struct. + processFlags |= win32.EXTENDED_STARTUPINFO_PRESENT; + startupInfo.cb = win32.STARTUPINFOEXW.size; + + startupInfoEx.lpAttributeList = threadAttrs; + } + + let procInfo = new win32.PROCESS_INFORMATION(); + + let errorMessage = "Failed to create process"; + let ok = libc.CreateProcessW( + command, + args.join(" "), + null /* Security attributes */, + null /* Thread security attributes */, + true /* Inherits handles */, + processFlags, + envp, + options.workdir, + startupInfo.address(), + procInfo.address() + ); + + for (let handle of new Set(handles)) { + // If any of our handles are invalid, they don't have finalizers. + if (handle && handle.dispose) { + handle.dispose(); + } + } + + if (threadAttrs) { + libc.DeleteProcThreadAttributeList(threadAttrs); + } + + if (ok) { + this.jobHandle = win32.Handle(libc.CreateJobObjectW(null, null)); + + let info = win32.JOBOBJECT_EXTENDED_LIMIT_INFORMATION(); + info.BasicLimitInformation.LimitFlags = + win32.JOB_OBJECT_LIMIT_BREAKAWAY_OK; + + ok = libc.SetInformationJobObject( + this.jobHandle, + win32.JobObjectExtendedLimitInformation, + ctypes.cast(info.address(), ctypes.voidptr_t), + info.constructor.size + ); + errorMessage = `Failed to set job limits: 0x${( + ctypes.winLastError || 0 + ).toString(16)}`; + } + + if (ok) { + ok = libc.AssignProcessToJobObject(this.jobHandle, procInfo.hProcess); + if (!ok) { + errorMessage = `Failed to attach process to job object: 0x${( + ctypes.winLastError || 0 + ).toString(16)}`; + libc.TerminateProcess(procInfo.hProcess, TERMINATE_EXIT_CODE); + } + } + + if (!ok) { + for (let pipe of this.pipes) { + pipe.close(); + } + throw new Error(errorMessage); + } + + this.handle = win32.Handle(procInfo.hProcess); + this.pid = procInfo.dwProcessId; + + libc.ResumeThread(procInfo.hThread); + libc.CloseHandle(procInfo.hThread); + } + + /** + * Called when our process handle is signaled as active, meaning the process + * has exited. + */ + onReady() { + this.wait(); + } + + /** + * Attempts to wait for the process's exit status, without blocking. If + * successful, resolves the `exitPromise` to the process's exit value. + * + * @returns {integer|null} + * The process's exit status, if it has already exited. + */ + wait() { + if (this.exitCode !== null) { + return this.exitCode; + } + + let status = win32.DWORD(); + + let ok = libc.GetExitCodeProcess(this.handle, status.address()); + if (ok && status.value != win32.STILL_ACTIVE) { + let exitCode = status.value; + if (this.killed && exitCode == TERMINATE_EXIT_CODE) { + // If we forcibly terminated the process, return the force kill exit + // code that we return on other platforms. + exitCode = -9; + } + + this.resolveExit(exitCode); + this.exitCode = exitCode; + + this.handle.dispose(); + this.handle = null; + + libc.TerminateJobObject(this.jobHandle, TERMINATE_EXIT_CODE); + this.jobHandle.dispose(); + this.jobHandle = null; + + for (let pipe of this.pipes) { + pipe.maybeClose(); + } + + io.updatePollEvents(); + + return exitCode; + } + } +} + +io = { + events: null, + eventHandlers: null, + + pipes: new Map(), + + processes: new Map(), + + messageCount: 0, + + running: true, + + polling: false, + + init(details) { + this.comspec = details.comspec; + + let signalEvent = ctypes.cast( + ctypes.uintptr_t(details.signalEvent), + win32.HANDLE + ); + this.signal = new Signal(signalEvent); + this.updatePollEvents(); + + setTimeout(this.loop.bind(this), 0); + }, + + shutdown() { + if (this.running) { + this.running = false; + + this.signal.cleanup(); + this.signal = null; + + self.postMessage({ msg: "close" }); + self.close(); + } + }, + + getPipe(pipeId) { + let pipe = this.pipes.get(pipeId); + + if (!pipe) { + let error = new Error("File closed"); + error.errorCode = SubprocessConstants.ERROR_END_OF_FILE; + throw error; + } + return pipe; + }, + + getProcess(processId) { + let process = this.processes.get(processId); + + if (!process) { + throw new Error(`Invalid process ID: ${processId}`); + } + return process; + }, + + updatePollEvents() { + let handlers = [ + this.signal, + ...this.pipes.values(), + ...this.processes.values(), + ]; + + handlers = handlers.filter(handler => handler.event); + + // Our poll loop is only useful if we've got at least 1 thing to poll other than our own + // signal. + if (handlers.length == 1) { + this.polling = false; + } else if (!this.polling && this.running) { + // Restart the poll loop if necessary: + setTimeout(this.loop.bind(this), 0); + this.polling = true; + } + + this.eventHandlers = handlers; + + let handles = handlers.map(handler => handler.event); + this.events = win32.HANDLE.array()(handles); + }, + + loop() { + this.poll(); + if (this.running && this.polling) { + setTimeout(this.loop.bind(this), 0); + } + }, + + poll() { + let timeout = this.messageCount > 0 ? 0 : POLL_TIMEOUT; + for (; ; timeout = 0) { + let events = this.events; + let handlers = this.eventHandlers; + + let result = libc.WaitForMultipleObjects( + events.length, + events, + false, + timeout + ); + + if (result < handlers.length) { + try { + handlers[result].onReady(); + } catch (e) { + console.error(e); + debug(`Worker error: ${e} :: ${e.stack}`); + handlers[result].onError(); + } + } else { + break; + } + } + }, + + addProcess(process) { + this.processes.set(process.id, process); + + for (let pipe of process.pipes) { + this.pipes.set(pipe.id, pipe); + } + }, + + cleanupProcess(process) { + this.processes.delete(process.id); + }, +}; diff --git a/toolkit/modules/subprocess/subprocess_worker_common.js b/toolkit/modules/subprocess/subprocess_worker_common.js new file mode 100644 index 0000000000..b22480c0dd --- /dev/null +++ b/toolkit/modules/subprocess/subprocess_worker_common.js @@ -0,0 +1,211 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* 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/. */ +"use strict"; + +// This file is loaded into the same context as subprocess_unix.worker.js +// and subprocess_win.worker.js +/* import-globals-from subprocess_unix.worker.js */ + +/* exported BasePipe, BaseProcess, debug */ + +function debug(message) { + self.postMessage({ msg: "debug", message }); +} + +class BasePipe { + constructor() { + this.closing = false; + this.closed = false; + + this.closedPromise = new Promise(resolve => { + this.resolveClosed = resolve; + }); + + this.pending = []; + } + + shiftPending() { + let result = this.pending.shift(); + + if (this.closing && !this.pending.length) { + this.close(); + } + + return result; + } +} + +let nextProcessId = 0; + +class BaseProcess { + constructor(options) { + this.id = nextProcessId++; + + this.exitCode = null; + + this.exitPromise = new Promise(resolve => { + this.resolveExit = resolve; + }); + this.exitPromise.then(() => { + // The input file descriptors will be closed after poll + // reports that their input buffers are empty. If we close + // them now, we may lose output. + this.pipes[0].close(true); + }); + + this.pid = null; + this.pipes = []; + + this.spawn(options); + } + + /** + * Waits for the process to exit and all of its pending IO operations to + * complete. + * + * @returns {Promise<void>} + */ + awaitFinished() { + return Promise.all([ + this.exitPromise, + ...this.pipes.map(pipe => pipe.closedPromise), + ]); + } +} + +let requests = { + init(details) { + io.init(details); + + return { data: {} }; + }, + + shutdown() { + io.shutdown(); + + return { data: {} }; + }, + + close(pipeId, force = false) { + let pipe = io.getPipe(pipeId); + + return pipe.close(force).then(() => ({ data: {} })); + }, + + spawn(options) { + let process = new Process(options); + let processId = process.id; + + io.addProcess(process); + + let fds = process.pipes.map(pipe => pipe.id); + + return { data: { processId, fds, pid: process.pid } }; + }, + + kill(processId, force = false) { + let process = io.getProcess(processId); + + process.kill(force ? 9 : 15); + + return { data: {} }; + }, + + wait(processId) { + let process = io.getProcess(processId); + + process.wait(); + + process.awaitFinished().then(() => { + io.cleanupProcess(process); + }); + + return process.exitPromise.then(exitCode => { + return { data: { exitCode } }; + }); + }, + + read(pipeId, count) { + let pipe = io.getPipe(pipeId); + + return pipe.read(count).then(buffer => { + return { data: { buffer } }; + }); + }, + + write(pipeId, buffer) { + let pipe = io.getPipe(pipeId); + + return pipe.write(buffer).then(bytesWritten => { + return { data: { bytesWritten } }; + }); + }, + + getOpenFiles() { + return { data: new Set(io.pipes.keys()) }; + }, + + getProcesses() { + let data = new Map( + Array.from(io.processes.values()) + .filter(proc => proc.exitCode == null) + .map(proc => [proc.id, proc.pid]) + ); + return { data }; + }, + + waitForNoProcesses() { + return Promise.all( + Array.from(io.processes.values(), proc => proc.awaitFinished()) + ); + }, +}; + +onmessage = event => { + io.messageCount--; + + let { msg, msgId, args } = event.data; + + new Promise(resolve => { + resolve(requests[msg](...args)); + }) + .then(result => { + let response = { + msg: "success", + msgId, + data: result.data, + }; + + self.postMessage(response, result.transfer || []); + }) + .catch(error => { + if (error instanceof Error) { + error = { + message: error.message, + fileName: error.fileName, + lineNumber: error.lineNumber, + column: error.column, + stack: error.stack, + errorCode: error.errorCode, + }; + } + + self.postMessage({ + msg: "failure", + msgId, + error, + }); + }) + .catch(error => { + console.error(error); + + self.postMessage({ + msg: "failure", + msgId, + error: {}, + }); + }); +}; diff --git a/toolkit/modules/subprocess/test/xpcshell/data_test_script.py b/toolkit/modules/subprocess/test/xpcshell/data_test_script.py new file mode 100644 index 0000000000..e1f5f5de93 --- /dev/null +++ b/toolkit/modules/subprocess/test/xpcshell/data_test_script.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 + +import os +import signal +import struct +import sys + + +def output(line, stream=sys.stdout, print_only=False): + if isinstance(line, str): + line = line.encode("utf-8", "surrogateescape") + if not print_only: + stream.buffer.write(struct.pack("@I", len(line))) + stream.buffer.write(line) + stream.flush() + + +def echo_loop(): + while True: + line = sys.stdin.buffer.readline() + if not line: + break + + output(line) + + +if sys.platform == "win32": + import msvcrt + + msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY) + + +cmd = sys.argv[1] +if cmd == "echo": + echo_loop() +elif cmd == "exit": + sys.exit(int(sys.argv[2])) +elif cmd == "env": + for var in sys.argv[2:]: + output(os.environ.get(var, "!")) +elif cmd == "pwd": + output(os.path.abspath(os.curdir)) +elif cmd == "print_args": + for arg in sys.argv[2:]: + output(arg) +elif cmd == "ignore_sigterm": + signal.signal(signal.SIGTERM, signal.SIG_IGN) + + output("Ready") + while True: + try: + signal.pause() + except AttributeError: + import time + + time.sleep(3600) +elif cmd == "print": + output(sys.argv[2], stream=sys.stdout, print_only=True) + output(sys.argv[3], stream=sys.stderr, print_only=True) diff --git a/toolkit/modules/subprocess/test/xpcshell/data_text_file.txt b/toolkit/modules/subprocess/test/xpcshell/data_text_file.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/toolkit/modules/subprocess/test/xpcshell/data_text_file.txt diff --git a/toolkit/modules/subprocess/test/xpcshell/head.js b/toolkit/modules/subprocess/test/xpcshell/head.js new file mode 100644 index 0000000000..a2b85047d3 --- /dev/null +++ b/toolkit/modules/subprocess/test/xpcshell/head.js @@ -0,0 +1,13 @@ +"use strict"; + +var { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); + +// eslint-disable-next-line no-unused-vars +ChromeUtils.defineESModuleGetters(this, { + Subprocess: "resource://gre/modules/Subprocess.sys.mjs", +}); diff --git a/toolkit/modules/subprocess/test/xpcshell/test_subprocess.js b/toolkit/modules/subprocess/test/xpcshell/test_subprocess.js new file mode 100644 index 0000000000..51c9956d0d --- /dev/null +++ b/toolkit/modules/subprocess/test/xpcshell/test_subprocess.js @@ -0,0 +1,870 @@ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ +"use strict"; + +const { setTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); + +const MAX_ROUND_TRIP_TIME_MS = AppConstants.DEBUG || AppConstants.ASAN ? 18 : 9; +const MAX_RETRIES = 5; + +let PYTHON; +let PYTHON_BIN; +let PYTHON_DIR; + +const TEST_SCRIPT = do_get_file("data_test_script.py").path; + +let read = pipe => { + return pipe.readUint32().then(count => { + return pipe.readString(count); + }); +}; + +let readAll = async function (pipe) { + let result = []; + let string; + while ((string = await pipe.readString())) { + result.push(string); + } + + return result.join(""); +}; + +add_task(async function setup() { + PYTHON = await Subprocess.pathSearch(Services.env.get("PYTHON")); + + PYTHON_BIN = PathUtils.filename(PYTHON); + PYTHON_DIR = PathUtils.parent(PYTHON); +}); + +add_task(async function test_subprocess_io() { + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "echo"], + }); + + Assert.throws(() => { + proc.stdout.read(-1); + }, /non-negative integer/); + Assert.throws(() => { + proc.stdout.read(1.1); + }, /non-negative integer/); + + Assert.throws(() => { + proc.stdout.read(Infinity); + }, /non-negative integer/); + Assert.throws(() => { + proc.stdout.read(NaN); + }, /non-negative integer/); + + Assert.throws(() => { + proc.stdout.readString(-1); + }, /non-negative integer/); + Assert.throws(() => { + proc.stdout.readString(1.1); + }, /non-negative integer/); + + Assert.throws(() => { + proc.stdout.readJSON(-1); + }, /positive integer/); + Assert.throws(() => { + proc.stdout.readJSON(0); + }, /positive integer/); + Assert.throws(() => { + proc.stdout.readJSON(1.1); + }, /positive integer/); + + const LINE1 = "I'm a leaf on the wind.\n"; + const LINE2 = "Watch how I soar.\n"; + + let outputPromise = read(proc.stdout); + + await new Promise(resolve => setTimeout(resolve, 100)); + + let [output] = await Promise.all([outputPromise, proc.stdin.write(LINE1)]); + + equal(output, LINE1, "Got expected output"); + + // Make sure it succeeds whether the write comes before or after the + // read. + let inputPromise = proc.stdin.write(LINE2); + + await new Promise(resolve => setTimeout(resolve, 100)); + + [output] = await Promise.all([read(proc.stdout), inputPromise]); + + equal(output, LINE2, "Got expected output"); + + let JSON_BLOB = { foo: { bar: "baz" } }; + + inputPromise = proc.stdin.write(JSON.stringify(JSON_BLOB) + "\n"); + + output = await proc.stdout.readUint32().then(count => { + return proc.stdout.readJSON(count); + }); + + Assert.deepEqual(output, JSON_BLOB, "Got expected JSON output"); + + await proc.stdin.close(); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); +}); + +add_task(async function test_subprocess_large_io() { + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "echo"], + }); + + const LINE = "I'm a leaf on the wind.\n"; + const BUFFER_SIZE = 4096; + + // Create a message that's ~3/4 the input buffer size. + let msg = + Array(((BUFFER_SIZE * 0.75) / 16) | 0) + .fill("0123456789abcdef") + .join("") + "\n"; + + // This sequence of writes and reads crosses several buffer size + // boundaries, and causes some branches of the read buffer code to be + // exercised which are not exercised by other tests. + proc.stdin.write(msg); + proc.stdin.write(msg); + proc.stdin.write(LINE); + + let output = await read(proc.stdout); + equal(output, msg, "Got the expected output"); + + output = await read(proc.stdout); + equal(output, msg, "Got the expected output"); + + output = await read(proc.stdout); + equal(output, LINE, "Got the expected output"); + + proc.stdin.close(); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); +}); + +add_task(async function test_subprocess_huge() { + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "echo"], + }); + + // This should be large enough to fill most pipe input/output buffers. + const MESSAGE_SIZE = 1024 * 16; + + let msg = Array(MESSAGE_SIZE).fill("0123456789abcdef").join("") + "\n"; + + proc.stdin.write(msg); + + let output = await read(proc.stdout); + equal(output, msg, "Got the expected output"); + + proc.stdin.close(); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); +}); + +add_task( + { skip_if: () => mozinfo.ccov }, + async function test_subprocess_round_trip_perf() { + let roundTripTime = Infinity; + for ( + let i = 0; + i < MAX_RETRIES && roundTripTime > MAX_ROUND_TRIP_TIME_MS; + i++ + ) { + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "echo"], + }); + + const LINE = "I'm a leaf on the wind.\n"; + + let now = Date.now(); + const COUNT = 1000; + for (let j = 0; j < COUNT; j++) { + let [output] = await Promise.all([ + read(proc.stdout), + proc.stdin.write(LINE), + ]); + + // We don't want to log this for every iteration, but we still need + // to fail if it goes wrong. + if (output !== LINE) { + equal(output, LINE, "Got expected output"); + } + } + + roundTripTime = (Date.now() - now) / COUNT; + + await proc.stdin.close(); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); + } + + Assert.lessOrEqual( + roundTripTime, + MAX_ROUND_TRIP_TIME_MS, + `Expected round trip time (${roundTripTime}ms) to be less than ${MAX_ROUND_TRIP_TIME_MS}ms` + ); + } +); + +add_task(async function test_subprocess_stderr_default() { + const LINE1 = "I'm a leaf on the wind.\n"; + const LINE2 = "Watch how I soar.\n"; + + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "print", LINE1, LINE2], + }); + + equal(proc.stderr, undefined, "There should be no stderr pipe by default"); + + let stdout = await readAll(proc.stdout); + + equal(stdout, LINE1, "Got the expected stdout output"); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); +}); + +add_task(async function test_subprocess_stderr_pipe() { + const LINE1 = "I'm a leaf on the wind.\n"; + const LINE2 = "Watch how I soar.\n"; + + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "print", LINE1, LINE2], + stderr: "pipe", + }); + + let [stdout, stderr] = await Promise.all([ + readAll(proc.stdout), + readAll(proc.stderr), + ]); + + equal(stdout, LINE1, "Got the expected stdout output"); + equal(stderr, LINE2, "Got the expected stderr output"); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); +}); + +add_task(async function test_subprocess_stderr_merged() { + const LINE1 = "I'm a leaf on the wind.\n"; + const LINE2 = "Watch how I soar.\n"; + + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "print", LINE1, LINE2], + stderr: "stdout", + }); + + equal(proc.stderr, undefined, "There should be no stderr pipe by default"); + + let stdout = await readAll(proc.stdout); + + equal(stdout, LINE1 + LINE2, "Got the expected merged stdout output"); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); +}); + +add_task(async function test_subprocess_read_after_exit() { + const LINE1 = "I'm a leaf on the wind.\n"; + const LINE2 = "Watch how I soar.\n"; + + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "print", LINE1, LINE2], + stderr: "pipe", + }); + + let { exitCode } = await proc.wait(); + equal(exitCode, 0, "Process exited with expected code"); + + let [stdout, stderr] = await Promise.all([ + readAll(proc.stdout), + readAll(proc.stderr), + ]); + + equal(stdout, LINE1, "Got the expected stdout output"); + equal(stderr, LINE2, "Got the expected stderr output"); +}); + +add_task(async function test_subprocess_lazy_close_output() { + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "echo"], + }); + + const LINE1 = "I'm a leaf on the wind.\n"; + const LINE2 = "Watch how I soar.\n"; + + let writePromises = [proc.stdin.write(LINE1), proc.stdin.write(LINE2)]; + let closedPromise = proc.stdin.close(); + + let output1 = await read(proc.stdout); + let output2 = await read(proc.stdout); + + await Promise.all([...writePromises, closedPromise]); + + equal(output1, LINE1, "Got expected output"); + equal(output2, LINE2, "Got expected output"); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); +}); + +add_task(async function test_subprocess_lazy_close_input() { + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "echo"], + }); + + let readPromise = proc.stdout.readUint32(); + let closedPromise = proc.stdout.close(); + + const LINE = "I'm a leaf on the wind.\n"; + + proc.stdin.write(LINE); + proc.stdin.close(); + + let len = await readPromise; + equal(len, LINE.length); + + await closedPromise; + + // Don't test for a successful exit here. The process may exit with a + // write error if we close the pipe after it's written the message + // size but before it's written the message. + await proc.wait(); +}); + +add_task(async function test_subprocess_force_close() { + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "echo"], + }); + + let readPromise = proc.stdout.readUint32(); + let closedPromise = proc.stdout.close(true); + + await Assert.rejects( + readPromise, + function (e) { + equal( + e.errorCode, + Subprocess.ERROR_END_OF_FILE, + "Got the expected error code" + ); + return /File closed/.test(e.message); + }, + "Promise should be rejected when file is closed" + ); + + await closedPromise; + await proc.stdin.close(); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); +}); + +add_task(async function test_subprocess_eof() { + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "echo"], + }); + + let readPromise = proc.stdout.readUint32(); + + await proc.stdin.close(); + + await Assert.rejects( + readPromise, + function (e) { + equal( + e.errorCode, + Subprocess.ERROR_END_OF_FILE, + "Got the expected error code" + ); + return /File closed/.test(e.message); + }, + "Promise should be rejected on EOF" + ); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); +}); + +add_task(async function test_subprocess_invalid_json() { + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "echo"], + }); + + const LINE = "I'm a leaf on the wind.\n"; + + proc.stdin.write(LINE); + proc.stdin.close(); + + let count = await proc.stdout.readUint32(); + let readPromise = proc.stdout.readJSON(count); + + await Assert.rejects( + readPromise, + function (e) { + equal( + e.errorCode, + Subprocess.ERROR_INVALID_JSON, + "Got the expected error code" + ); + return /SyntaxError/.test(e); + }, + "Promise should be rejected on EOF" + ); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); +}); + +if (AppConstants.platform == "win") { + add_task(async function test_subprocess_inherited_descriptors() { + let { libc, win32 } = ChromeUtils.importESModule( + "resource://gre/modules/subprocess/subprocess_win.sys.mjs" + ); + const { ctypes } = ChromeUtils.importESModule( + "resource://gre/modules/ctypes.sys.mjs" + ); + + let secAttr = new win32.SECURITY_ATTRIBUTES(); + secAttr.nLength = win32.SECURITY_ATTRIBUTES.size; + secAttr.bInheritHandle = true; + + let handles = win32.createPipe(secAttr, 0); + + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "echo"], + }); + + // Close the output end of the pipe. + // Ours should be the only copy, so reads should fail after this. + handles[1].dispose(); + + let buffer = new ArrayBuffer(1); + let succeeded = libc.ReadFile( + handles[0], + buffer, + buffer.byteLength, + null, + null + ); + + ok(!succeeded, "ReadFile should fail on broken pipe"); + equal( + ctypes.winLastError, + win32.ERROR_BROKEN_PIPE, + "Read should fail with ERROR_BROKEN_PIPE" + ); + + proc.stdin.close(); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); + }); +} + +add_task(async function test_subprocess_wait() { + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "exit", "42"], + }); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 42, "Got expected exit code"); +}); + +add_task(async function test_subprocess_pathSearch() { + let promise = Subprocess.call({ + command: PYTHON_BIN, + arguments: ["-u", TEST_SCRIPT, "exit", "13"], + environment: { + PATH: PYTHON_DIR, + }, + }); + + await Assert.rejects( + promise, + function (error) { + return error.errorCode == Subprocess.ERROR_BAD_EXECUTABLE; + }, + "Subprocess.call should fail for a bad executable" + ); +}); + +add_task(async function test_subprocess_workdir() { + let procDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile).path; + let tmpDir = PathUtils.normalize(PathUtils.tempDir); + + notEqual( + procDir, + tmpDir, + "Current process directory must not be the current temp directory" + ); + + async function pwd(options) { + let proc = await Subprocess.call( + Object.assign( + { + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "pwd"], + }, + options + ) + ); + + let pwdOutput = read(proc.stdout); + + let { exitCode } = await proc.wait(); + equal(exitCode, 0, "Got expected exit code"); + + return pwdOutput; + } + + let dir = await pwd({}); + equal( + dir, + procDir, + "Process should normally launch in current process directory" + ); + + dir = await pwd({ workdir: tmpDir }); + equal( + dir, + tmpDir, + "Process should launch in the directory specified in `workdir`" + ); +}); + +add_task(async function test_subprocess_term() { + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "echo"], + }); + + // Windows does not support killing processes gracefully, so they will + // always exit with -9 there. + let retVal = AppConstants.platform == "win" ? -9 : -15; + + // Kill gracefully with the default timeout of 300ms. + let { exitCode } = await proc.kill(); + + equal(exitCode, retVal, "Got expected exit code"); + + ({ exitCode } = await proc.wait()); + + equal(exitCode, retVal, "Got expected exit code"); +}); + +add_task(async function test_subprocess_kill() { + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "echo"], + }); + + // Force kill with no gracefull termination timeout. + let { exitCode } = await proc.kill(0); + + equal(exitCode, -9, "Got expected exit code"); + + ({ exitCode } = await proc.wait()); + + equal(exitCode, -9, "Got expected exit code"); +}); + +add_task(async function test_subprocess_kill_timeout() { + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "ignore_sigterm"], + }); + + // Wait for the process to set up its signal handler and tell us it's + // ready. + let msg = await read(proc.stdout); + equal(msg, "Ready", "Process is ready"); + + // Kill gracefully with the default timeout of 300ms. + // Expect a force kill after 300ms, since the process traps SIGTERM. + const TIMEOUT = 300; + let startTime = Date.now(); + + let { exitCode } = await proc.kill(TIMEOUT); + + // Graceful termination is not supported on Windows, so don't bother + // testing the timeout there. + if (AppConstants.platform != "win") { + let diff = Date.now() - startTime; + Assert.greaterOrEqual( + diff, + TIMEOUT, + `Process was killed after ${diff}ms (expected ~${TIMEOUT}ms)` + ); + } + + equal(exitCode, -9, "Got expected exit code"); + + ({ exitCode } = await proc.wait()); + + equal(exitCode, -9, "Got expected exit code"); +}); + +add_task(async function test_subprocess_arguments() { + let args = [ + String.raw`C:\Program Files\Company\Program.exe`, + String.raw`\\NETWORK SHARE\Foo Directory${"\\"}`, + String.raw`foo bar baz`, + String.raw`"foo bar baz"`, + String.raw`foo " bar`, + String.raw`Thing \" with "" "\" \\\" \\\\" quotes\\" \\`, + ]; + + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "print_args", ...args], + }); + + for (let [i, arg] of args.entries()) { + let val = await read(proc.stdout); + equal(val, arg, `Got correct value for args[${i}]`); + } + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); +}); + +add_task(async function test_subprocess_environment() { + let environment = { + FOO: "BAR", + EMPTY: "", + IGNORED: null, + }; + + // Our Windows environment can't handle launching python without + // PATH variables. + if (AppConstants.platform == "win") { + Object.assign(environment, { + PATH: Services.env.get("PATH"), + PATHEXT: Services.env.get("PATHEXT"), + SYSTEMROOT: Services.env.get("SYSTEMROOT"), + }); + } + + Services.env.set("BAR", "BAZ"); + + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "env", "FOO", "BAR", "EMPTY", "IGNORED"], + environment, + }); + + let foo = await read(proc.stdout); + let bar = await read(proc.stdout); + let empty = await read(proc.stdout); + let ignored = await read(proc.stdout); + + equal(foo, "BAR", "Got expected $FOO value"); + equal(bar, "!", "Got expected $BAR value"); + equal(empty, "", "Got expected $EMPTY value"); + equal(ignored, "!", "Got expected $IGNORED value"); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); +}); + +add_task(async function test_subprocess_environmentAppend() { + Services.env.set("VALUE_FROM_BASE_ENV", "untouched"); + Services.env.set("VALUE_FROM_BASE_ENV_EMPTY", "untouched"); + Services.env.set("VALUE_FROM_BASE_ENV_REMOVED", "untouched"); + + let proc = await Subprocess.call({ + command: PYTHON, + arguments: [ + "-u", + TEST_SCRIPT, + "env", + "VALUE_FROM_BASE_ENV", + "VALUE_FROM_BASE_ENV_EMPTY", + "VALUE_FROM_BASE_ENV_REMOVED", + "VALUE_APPENDED_ONCE", + ], + environmentAppend: true, + environment: { + VALUE_FROM_BASE_ENV_EMPTY: "", + VALUE_FROM_BASE_ENV_REMOVED: null, + VALUE_APPENDED_ONCE: "soon empty", + }, + }); + + let valueFromBaseEnv = await read(proc.stdout); + let valueFromBaseEnvEmpty = await read(proc.stdout); + let valueFromBaseEnvRemoved = await read(proc.stdout); + let valueAppendedOnce = await read(proc.stdout); + + equal( + valueFromBaseEnv, + "untouched", + "Got expected $VALUE_FROM_BASE_ENV value" + ); + equal( + valueFromBaseEnvEmpty, + "", + "Got expected $VALUE_FROM_BASE_ENV_EMPTY value" + ); + equal( + valueFromBaseEnvRemoved, + "!", + "Got expected $VALUE_FROM_BASE_ENV_REMOVED value" + ); + equal( + valueAppendedOnce, + "soon empty", + "Got expected $VALUE_APPENDED_ONCE value" + ); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); + + proc = await Subprocess.call({ + command: PYTHON, + arguments: [ + "-u", + TEST_SCRIPT, + "env", + "VALUE_FROM_BASE_ENV", + "VALUE_APPENDED_ONCE", + ], + environmentAppend: true, + }); + + valueFromBaseEnv = await read(proc.stdout); + valueAppendedOnce = await read(proc.stdout); + + equal( + valueFromBaseEnv, + "untouched", + "Got expected $VALUE_FROM_BASE_ENV value" + ); + equal(valueAppendedOnce, "!", "Got expected $VALUE_APPENDED_ONCE value"); + + ({ exitCode } = await proc.wait()); + + equal(exitCode, 0, "Got expected exit code"); +}); + +if (AppConstants.platform !== "win") { + add_task(async function test_subprocess_nonASCII() { + const { libc } = ChromeUtils.importESModule( + "resource://gre/modules/subprocess/subprocess_unix.sys.mjs" + ); + + // Use TextDecoder rather than a string with a \xff escape, since + // the latter will automatically be normalized to valid UTF-8. + let val = new TextDecoder().decode(Uint8Array.of(1, 255)); + + libc.setenv( + "FOO", + Uint8Array.from(val + "\0", c => c.charCodeAt(0)), + 1 + ); + + let proc = await Subprocess.call({ + command: PYTHON, + arguments: ["-u", TEST_SCRIPT, "env", "FOO"], + }); + + let foo = await read(proc.stdout); + + equal(foo, val, "Got expected $FOO value"); + + Services.env.set("FOO", ""); + + let { exitCode } = await proc.wait(); + + equal(exitCode, 0, "Got expected exit code"); + }); +} + +add_task(async function test_bad_executable() { + // Test with a non-executable file. + + let textFile = do_get_file("data_text_file.txt").path; + + let promise = Subprocess.call({ + command: textFile, + arguments: [], + }); + + await Assert.rejects( + promise, + function (error) { + if (AppConstants.platform == "win") { + return /Failed to create process/.test(error.message); + } + return error.errorCode == Subprocess.ERROR_BAD_EXECUTABLE; + }, + "Subprocess.call should fail for a bad executable" + ); + + // Test with a nonexistent file. + promise = Subprocess.call({ + command: textFile + ".doesNotExist", + arguments: [], + }); + + await Assert.rejects( + promise, + function (error) { + return error.errorCode == Subprocess.ERROR_BAD_EXECUTABLE; + }, + "Subprocess.call should fail for a bad executable" + ); +}); + +add_task(async function test_cleanup() { + let { getSubprocessImplForTest } = ChromeUtils.importESModule( + "resource://gre/modules/Subprocess.sys.mjs" + ); + + let worker = getSubprocessImplForTest().Process.getWorker(); + + let openFiles = await worker.call("getOpenFiles", []); + let processes = await worker.call("getProcesses", []); + + equal(openFiles.size, 0, "No remaining open files"); + equal(processes.size, 0, "No remaining processes"); +}); diff --git a/toolkit/modules/subprocess/test/xpcshell/test_subprocess_getEnvironment.js b/toolkit/modules/subprocess/test/xpcshell/test_subprocess_getEnvironment.js new file mode 100644 index 0000000000..cb4ea7247d --- /dev/null +++ b/toolkit/modules/subprocess/test/xpcshell/test_subprocess_getEnvironment.js @@ -0,0 +1,15 @@ +"use strict"; + +add_task(async function test_getEnvironment() { + Services.env.set("FOO", "BAR"); + + let environment = Subprocess.getEnvironment(); + + equal(environment.FOO, "BAR"); + equal(environment.PATH, Services.env.get("PATH")); + + Services.env.set("FOO", null); + + environment = Subprocess.getEnvironment(); + equal(environment.FOO || "", ""); +}); diff --git a/toolkit/modules/subprocess/test/xpcshell/test_subprocess_pathSearch.js b/toolkit/modules/subprocess/test/xpcshell/test_subprocess_pathSearch.js new file mode 100644 index 0000000000..9d9374f536 --- /dev/null +++ b/toolkit/modules/subprocess/test/xpcshell/test_subprocess_pathSearch.js @@ -0,0 +1,87 @@ +"use strict"; + +const PYTHON = Services.env.get("PYTHON"); + +const PYTHON_BIN = PathUtils.filename(PYTHON); +const PYTHON_DIR = PathUtils.parent(PYTHON); + +const DOES_NOT_EXIST = PathUtils.join( + PathUtils.tempDir, + "ThisPathDoesNotExist" +); + +const PATH_SEP = AppConstants.platform == "win" ? ";" : ":"; + +add_task(async function test_pathSearchAbsolute() { + let env = {}; + + let path = await Subprocess.pathSearch(PYTHON, env); + equal(path, PYTHON, "Full path resolves even with no PATH."); + + env.PATH = ""; + path = await Subprocess.pathSearch(PYTHON, env); + equal(path, PYTHON, "Full path resolves even with empty PATH."); + + await Assert.rejects( + Subprocess.pathSearch(DOES_NOT_EXIST, env), + function (e) { + equal( + e.errorCode, + Subprocess.ERROR_BAD_EXECUTABLE, + "Got the expected error code" + ); + return /File at path .* does not exist, or is not (executable|a normal file)/.test( + e.message + ); + }, + "Absolute path should throw for a nonexistent execuable" + ); +}); + +add_task(async function test_pathSearchRelative() { + let env = {}; + + await Assert.rejects( + Subprocess.pathSearch(PYTHON_BIN, env), + function (e) { + equal( + e.errorCode, + Subprocess.ERROR_BAD_EXECUTABLE, + "Got the expected error code" + ); + return /Executable not found:/.test(e.message); + }, + "Relative path should not be found when PATH is missing" + ); + + env.PATH = [DOES_NOT_EXIST, PYTHON_DIR].join(PATH_SEP); + + let path = await Subprocess.pathSearch(PYTHON_BIN, env); + equal(path, PYTHON, "Correct executable should be found in the path"); +}); + +add_task( + { + skip_if: () => AppConstants.platform != "win", + }, + async function test_pathSearch_PATHEXT() { + ok(PYTHON_BIN.endsWith(".exe"), "Python executable must end with .exe"); + + const python_bin = PYTHON_BIN.slice(0, -4); + + let env = { + PATH: PYTHON_DIR, + PATHEXT: [".com", ".exe", ".foobar"].join(";"), + }; + + let path = await Subprocess.pathSearch(python_bin, env); + equal( + path, + PYTHON, + "Correct executable should be found in the path, with guessed extension" + ); + } +); +// IMPORTANT: Do not add any tests beyond this point without removing +// the `skip_if` condition from the previous task, or it will prevent +// all succeeding tasks from running when it does not match. diff --git a/toolkit/modules/subprocess/test/xpcshell/xpcshell.toml b/toolkit/modules/subprocess/test/xpcshell/xpcshell.toml new file mode 100644 index 0000000000..776900b676 --- /dev/null +++ b/toolkit/modules/subprocess/test/xpcshell/xpcshell.toml @@ -0,0 +1,20 @@ +[DEFAULT] +head = "head.js" +firefox-appdir = "browser" +skip-if = ["os == 'android'"] +subprocess = true +support-files = [ + "data_text_file.txt", + "data_test_script.py", +] + +["test_subprocess.js"] +skip-if = [ + "verify", + "apple_silicon", # bug 1729546 +] +run-sequentially = "very high failure rate in parallel" + +["test_subprocess_getEnvironment.js"] + +["test_subprocess_pathSearch.js"] diff --git a/toolkit/modules/tests/browser/browser.toml b/toolkit/modules/tests/browser/browser.toml new file mode 100644 index 0000000000..932b06d19e --- /dev/null +++ b/toolkit/modules/tests/browser/browser.toml @@ -0,0 +1,53 @@ +[DEFAULT] +support-files = [ + "file_FinderIframeTest.html", + "file_FinderSample.html", + "file_getSelectionDetails_inputs.html", + "head.js", +] + +["browser_AsyncPrefs.js"] + +["browser_BrowserUtils.js"] + +["browser_CreditCard.js"] +skip-if = ["apple_silicon"] # Disabled due to bleedover with other tests when run in regular suites; passes in "failures" jobs + +["browser_Deprecated.js"] + +["browser_Finder.js"] + +["browser_FinderHighlighter.js"] +skip-if = ["true"] # bug 1831518 covers re-enabling + +["browser_FinderHighlighter2.js"] +skip-if = ["true"] # bug 1831518 covers re-enabling + +["browser_Finder_hidden_textarea.js"] +skip-if = ["verify && debug"] + +["browser_Finder_offscreen_text.js"] + +["browser_Finder_overflowed_onscreen.js"] + +["browser_Finder_overflowed_textarea.js"] +skip-if = ["verify && debug && (os == 'mac' || os == 'linux')"] + +["browser_Finder_pointer_events_none.js"] + +["browser_Finder_skip_invisible_and_option.js"] + +["browser_Finder_vertical_text.js"] + +["browser_Geometry.js"] + +["browser_InlineSpellChecker.js"] + +["browser_Troubleshoot.js"] + +["browser_web_channel.js"] +https_first_disabled = true +support-files = [ + "file_web_channel.html", + "file_web_channel_iframe.html", +] diff --git a/toolkit/modules/tests/browser/browser_AsyncPrefs.js b/toolkit/modules/tests/browser/browser_AsyncPrefs.js new file mode 100644 index 0000000000..96eadc4b2e --- /dev/null +++ b/toolkit/modules/tests/browser/browser_AsyncPrefs.js @@ -0,0 +1,133 @@ +"use strict"; + +const kWhiteListedBool = "testing.allowed-prefs.some-bool-pref"; +const kWhiteListedChar = "testing.allowed-prefs.some-char-pref"; +const kWhiteListedInt = "testing.allowed-prefs.some-int-pref"; + +function resetPrefs() { + for (let pref of [kWhiteListedBool, kWhiteListedChar, kWhiteListedBool]) { + Services.prefs.clearUserPref(pref); + } +} + +registerCleanupFunction(resetPrefs); + +Services.prefs + .getDefaultBranch("testing.allowed-prefs.") + .setBoolPref("some-bool-pref", false); +Services.prefs + .getDefaultBranch("testing.allowed-prefs.") + .setCharPref("some-char-pref", ""); +Services.prefs + .getDefaultBranch("testing.allowed-prefs.") + .setIntPref("some-int-pref", 0); + +async function runTest() { + let { AsyncPrefs } = ChromeUtils.importESModule( + "resource://gre/modules/AsyncPrefs.sys.mjs" + ); + const kInChildProcess = + Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT; + + // Need to define these again because when run in a content task we have no scope access. + const kNotWhiteListed = "some.pref.thats.not.whitelisted"; + const kWhiteListedBool = "testing.allowed-prefs.some-bool-pref"; + const kWhiteListedChar = "testing.allowed-prefs.some-char-pref"; + const kWhiteListedInt = "testing.allowed-prefs.some-int-pref"; + + const procDesc = kInChildProcess ? "child process" : "parent process"; + + const valueResultMap = [ + [true, "Bool"], + [false, "Bool"], + [10, "Int"], + [-1, "Int"], + ["", "Char"], + ["stuff", "Char"], + [[], false], + [{}, false], + [Services.io.newURI("http://mozilla.org/"), false], + ]; + + const prefMap = [ + ["Bool", kWhiteListedBool], + ["Char", kWhiteListedChar], + ["Int", kWhiteListedInt], + ]; + + function doesFail(pref, value) { + let msg = `Should not succeed setting ${pref} to ${value} in ${procDesc}`; + return AsyncPrefs.set(pref, value).then( + () => ok(false, msg), + error => ok(true, msg + "; " + error) + ); + } + + function doesWork(pref, value) { + let msg = `Should be able to set ${pref} to ${value} in ${procDesc}`; + return AsyncPrefs.set(pref, value).then( + () => ok(true, msg), + error => ok(false, msg + "; " + error) + ); + } + + function doReset(pref) { + let msg = `Should be able to reset ${pref} in ${procDesc}`; + return AsyncPrefs.reset(pref).then( + () => ok(true, msg), + () => ok(false, msg) + ); + } + + for (let [val] of valueResultMap) { + await doesFail(kNotWhiteListed, val); + is( + Services.prefs.prefHasUserValue(kNotWhiteListed), + false, + "Pref shouldn't get changed" + ); + } + + let resetMsg = `Should not succeed resetting ${kNotWhiteListed} in ${procDesc}`; + AsyncPrefs.reset(kNotWhiteListed).then( + () => ok(false, resetMsg), + error => ok(true, resetMsg + "; " + error) + ); + + for (let [type, pref] of prefMap) { + for (let [val, result] of valueResultMap) { + if (result == type) { + await doesWork(pref, val); + is( + Services.prefs["get" + type + "Pref"](pref), + val, + "Pref should have been updated" + ); + await doReset(pref); + } else { + await doesFail(pref, val); + is( + Services.prefs.prefHasUserValue(pref), + false, + `Pref ${pref} shouldn't get changed` + ); + } + } + } +} + +add_task(async function runInParent() { + await runTest(); + resetPrefs(); +}); + +if (gMultiProcessBrowser) { + add_task(async function runInChild() { + ok( + gBrowser.selectedBrowser.isRemoteBrowser, + "Should actually run this in child process" + ); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], runTest); + resetPrefs(); + }); +} diff --git a/toolkit/modules/tests/browser/browser_BrowserUtils.js b/toolkit/modules/tests/browser/browser_BrowserUtils.js new file mode 100644 index 0000000000..da28c07b69 --- /dev/null +++ b/toolkit/modules/tests/browser/browser_BrowserUtils.js @@ -0,0 +1,50 @@ +/* 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/. */ + +add_task(async function test_getSelectionDetails_input() { + // Mostly a regression test for bug 1420560 + const url = kFixtureBaseURL + "file_getSelectionDetails_inputs.html"; + await BrowserTestUtils.withNewTab({ gBrowser, url }, async browser => { + await SpecialPowers.spawn(browser, [], () => { + function checkSelection({ id, text, linkURL }) { + const { SelectionUtils } = ChromeUtils.importESModule( + "resource://gre/modules/SelectionUtils.sys.mjs" + ); + content.document.getElementById(id).select(); + // It seems that when running as a test, the previous line will set + // the both the window's selection and the input's selection to contain + // the input's text. Outside of tests, only the input's selection seems + // to be updated, so we explicitly clear the window's selection to + // ensure we're doing the right thing in the case that only the input's + // selection is present. + content.getSelection().removeAllRanges(); + let info = SelectionUtils.getSelectionDetails(content); + Assert.equal(text, info.text); + Assert.ok(!info.collapsed); + Assert.equal(linkURL, info.linkURL); + } + + checkSelection({ + id: "url-no-scheme", + text: "test.example.com", + linkURL: "http://test.example.com/", + }); + checkSelection({ + id: "url-with-scheme", + text: "https://test.example.com", + linkURL: "https://test.example.com/", + }); + checkSelection({ + id: "not-url", + text: "foo. bar", + linkURL: null, + }); + checkSelection({ + id: "not-url-number", + text: "3.5", + linkURL: null, + }); + }); + }); +}); diff --git a/toolkit/modules/tests/browser/browser_CreditCard.js b/toolkit/modules/tests/browser/browser_CreditCard.js new file mode 100644 index 0000000000..304c988707 --- /dev/null +++ b/toolkit/modules/tests/browser/browser_CreditCard.js @@ -0,0 +1,46 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { CreditCard } = ChromeUtils.importESModule( + "resource://gre/modules/CreditCard.sys.mjs" +); +const { OSKeyStore } = ChromeUtils.importESModule( + "resource://gre/modules/OSKeyStore.sys.mjs" +); +const { OSKeyStoreTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/OSKeyStoreTestUtils.sys.mjs" +); + +let oldGetters = {}; +let gFakeLoggedIn = true; + +add_setup(function () { + OSKeyStoreTestUtils.setup(); + oldGetters.isLoggedIn = Object.getOwnPropertyDescriptor( + OSKeyStore, + "isLoggedIn" + ).get; + OSKeyStore.__defineGetter__("isLoggedIn", () => gFakeLoggedIn); + registerCleanupFunction(async () => { + OSKeyStore.__defineGetter__("isLoggedIn", oldGetters.isLoggedIn); + await OSKeyStoreTestUtils.cleanup(); + }); +}); + +add_task(async function test_getLabel_withOSKeyStore() { + ok( + OSKeyStore.isLoggedIn, + "Confirm that OSKeyStore is faked and thinks it is logged in" + ); + + const ccNumber = "4111111111111111"; + const encryptedNumber = await OSKeyStore.encrypt(ccNumber); + const decryptedNumber = await OSKeyStore.decrypt(encryptedNumber); + is(decryptedNumber, ccNumber, "Decrypted CC number should match original"); + + const name = "Foxkeh"; + const label = CreditCard.getLabel({ name: "Foxkeh", number: ccNumber }); + is(label, `**** 1111, ${name}`, "Label matches"); +}); diff --git a/toolkit/modules/tests/browser/browser_Deprecated.js b/toolkit/modules/tests/browser/browser_Deprecated.js new file mode 100644 index 0000000000..b718ba37e7 --- /dev/null +++ b/toolkit/modules/tests/browser/browser_Deprecated.js @@ -0,0 +1,140 @@ +/* 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/. */ + +const PREF_DEPRECATION_WARNINGS = "devtools.errorconsole.deprecation_warnings"; + +// Using this named functions to test deprecation and the properly logged +// callstacks. +function basicDeprecatedFunction() { + Deprecated.warning("this method is deprecated.", "https://example.com"); + return true; +} + +function deprecationFunctionBogusCallstack() { + Deprecated.warning("this method is deprecated.", "https://example.com", { + caller: {}, + }); + return true; +} + +function deprecationFunctionCustomCallstack() { + // Get the nsIStackFrame that will contain the name of this function. + function getStack() { + return Components.stack; + } + Deprecated.warning( + "this method is deprecated.", + "https://example.com", + getStack() + ); + return true; +} + +var tests = [ + // Test deprecation warning without passing the callstack. + { + deprecatedFunction: basicDeprecatedFunction, + expectedObservation(aMessage) { + testAMessage(aMessage); + Assert.greater( + aMessage.indexOf("basicDeprecatedFunction"), + 0, + "Callstack is correctly logged." + ); + }, + }, + // Test a reported error when URL to documentation is not passed. + { + deprecatedFunction() { + Deprecated.warning("this method is deprecated."); + return true; + }, + expectedObservation(aMessage) { + Assert.greater( + aMessage.indexOf("must provide a URL"), + 0, + "Deprecation warning logged an empty URL argument." + ); + }, + }, + // Test deprecation with a bogus callstack passed as an argument (it will be + // replaced with the current call stack). + { + deprecatedFunction: deprecationFunctionBogusCallstack, + expectedObservation(aMessage) { + testAMessage(aMessage); + Assert.greater( + aMessage.indexOf("deprecationFunctionBogusCallstack"), + 0, + "Callstack is correctly logged." + ); + }, + }, + // Test deprecation with a valid custom callstack passed as an argument. + { + deprecatedFunction: deprecationFunctionCustomCallstack, + expectedObservation(aMessage) { + testAMessage(aMessage); + Assert.greater( + aMessage.indexOf("deprecationFunctionCustomCallstack"), + 0, + "Callstack is correctly logged." + ); + }, + // Set pref to true. + logWarnings: true, + }, +]; + +// Test Console Message attributes. +function testAMessage(aMessage) { + Assert.strictEqual( + aMessage.indexOf("DEPRECATION WARNING: this method is deprecated."), + 0, + "Deprecation is correctly logged." + ); + Assert.greater( + aMessage.indexOf("https://example.com"), + 0, + "URL is correctly logged." + ); +} + +add_task(async function test_setup() { + Services.prefs.setBoolPref(PREF_DEPRECATION_WARNINGS, true); + + // Check if Deprecated is loaded. + ok(Deprecated, "Deprecated object exists"); +}); + +add_task(async function test_pref_enabled() { + for (let [idx, test] of tests.entries()) { + info("Running test #" + idx); + + let promiseObserved = TestUtils.consoleMessageObserved(subject => { + let msg = subject.wrappedJSObject.arguments?.[0]; + return ( + msg.includes("DEPRECATION WARNING: ") || + msg.includes("must provide a URL") + ); + }); + + test.deprecatedFunction(); + + let msg = await promiseObserved; + + test.expectedObservation(msg.wrappedJSObject.arguments?.[0]); + } +}); + +add_task(async function test_pref_disabled() { + // Deprecation warnings will be logged only when the preference is set. + Services.prefs.setBoolPref(PREF_DEPRECATION_WARNINGS, false); + + let endFn = TestUtils.listenForConsoleMessages(); + basicDeprecatedFunction(); + + let messages = await endFn(); + Assert.equal(messages.length, 0, "Should not have received any messages"); +}); diff --git a/toolkit/modules/tests/browser/browser_Finder.js b/toolkit/modules/tests/browser/browser_Finder.js new file mode 100644 index 0000000000..7bcf7e8a00 --- /dev/null +++ b/toolkit/modules/tests/browser/browser_Finder.js @@ -0,0 +1,73 @@ +/* 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/. */ + +add_task(async function () { + const url = + "data:text/html;base64," + + btoa( + '<body><iframe srcdoc="content"/></iframe>' + + '<a href="http://test.com">test link</a>' + ); + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url); + + let finder = tab.linkedBrowser.finder; + let listener = { + onFindResult() { + ok(false, "onFindResult callback wasn't replaced"); + }, + onHighlightFinished() { + ok(false, "onHighlightFinished callback wasn't replaced"); + }, + }; + finder.addResultListener(listener); + + function waitForFind(which = "onFindResult") { + return new Promise(resolve => { + listener[which] = resolve; + }); + } + + let promiseFind = waitForFind("onHighlightFinished"); + finder.highlight(true, "content"); + let findResult = await promiseFind; + Assert.ok(findResult.found, "should find string"); + + promiseFind = waitForFind("onHighlightFinished"); + finder.highlight(true, "Bla"); + findResult = await promiseFind; + Assert.ok(!findResult.found, "should not find string"); + + // Search only for links and draw outlines. + promiseFind = waitForFind(); + finder.fastFind("test link", true, true); + findResult = await promiseFind; + is(findResult.result, Ci.nsITypeAheadFind.FIND_FOUND, "should find link"); + + await SpecialPowers.spawn(tab.linkedBrowser, [], async function (arg) { + Assert.ok( + !!content.document.getElementsByTagName("a")[0].style.outline, + "outline set" + ); + }); + + // Just a simple search for "test link". + promiseFind = waitForFind(); + finder.fastFind("test link", false, false); + findResult = await promiseFind; + is( + findResult.result, + Ci.nsITypeAheadFind.FIND_FOUND, + "should find link again" + ); + + await SpecialPowers.spawn(tab.linkedBrowser, [], async function (arg) { + Assert.ok( + !content.document.getElementsByTagName("a")[0].style.outline, + "outline not set" + ); + }); + + finder.removeResultListener(listener); + gBrowser.removeTab(tab); +}); diff --git a/toolkit/modules/tests/browser/browser_FinderHighlighter.js b/toolkit/modules/tests/browser/browser_FinderHighlighter.js new file mode 100644 index 0000000000..7e377e47d1 --- /dev/null +++ b/toolkit/modules/tests/browser/browser_FinderHighlighter.js @@ -0,0 +1,415 @@ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ +"use strict"; + +const kIteratorTimeout = Services.prefs.getIntPref("findbar.iteratorTimeout"); +const kPrefHighlightAll = "findbar.highlightAll"; +const kPrefModalHighlight = "findbar.modalHighlight"; + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [ + [kPrefHighlightAll, true], + [kPrefModalHighlight, true], + ], + }); +}); + +// Test the results of modal highlighting, which is on by default. +add_task(async function testModalResults() { + let tests = new Map([ + [ + "Roland", + { + rectCount: 2, + insertCalls: [2, 4], + removeCalls: [0, 1], + animationCalls: [1, 2], + }, + ], + [ + "their law might propagate their kind", + { + rectCount: 2, + insertCalls: [5, 6], + removeCalls: [4, 5], + // eslint-disable-next-line object-shorthand + extraTest: function (maskNode, outlineNode, rects) { + Assert.equal( + outlineNode.getElementsByTagName("div").length, + 2, + "There should be multiple rects drawn" + ); + }, + }, + ], + [ + "ro", + { + rectCount: 41, + insertCalls: [1, 4], + removeCalls: [0, 2], + }, + ], + [ + "new", + { + rectCount: 2, + insertCalls: [1, 4], + removeCalls: [0, 2], + }, + ], + [ + "o", + { + rectCount: 492, + insertCalls: [1, 4], + removeCalls: [0, 2], + }, + ], + ]); + let url = kFixtureBaseURL + "file_FinderSample.html"; + await BrowserTestUtils.withNewTab(url, async function (browser) { + let findbar = await gBrowser.getFindBar(); + + for (let [word, expectedResult] of tests) { + await promiseOpenFindbar(findbar); + Assert.ok(!findbar.hidden, "Findbar should be open now."); + + let timeout = kIteratorTimeout; + if (word.length == 1) { + timeout *= 4; + } else if (word.length == 2) { + timeout *= 2; + } + await new Promise(resolve => setTimeout(resolve, timeout)); + let promise = promiseTestHighlighterOutput( + browser, + word, + expectedResult, + expectedResult.extraTest + ); + await promiseEnterStringIntoFindField(findbar, word); + await promise; + + findbar.close(true); + } + }); +}); + +// Test if runtime switching of highlight modes between modal and non-modal works +// as expected. +add_task(async function testModalSwitching() { + let url = kFixtureBaseURL + "file_FinderSample.html"; + await BrowserTestUtils.withNewTab(url, async function (browser) { + let findbar = await gBrowser.getFindBar(); + + await promiseOpenFindbar(findbar); + Assert.ok(!findbar.hidden, "Findbar should be open now."); + + let word = "Roland"; + let expectedResult = { + rectCount: 2, + insertCalls: [2, 4], + removeCalls: [0, 1], + }; + let promise = promiseTestHighlighterOutput(browser, word, expectedResult); + await promiseEnterStringIntoFindField(findbar, word); + await promise; + + await SpecialPowers.pushPrefEnv({ set: [[kPrefModalHighlight, false]] }); + + expectedResult = { + rectCount: 0, + insertCalls: [0, 0], + removeCalls: [0, 0], + }; + promise = promiseTestHighlighterOutput(browser, word, expectedResult); + findbar.clear(); + await promiseEnterStringIntoFindField(findbar, word); + await promise; + + findbar.close(true); + }); + + await SpecialPowers.pushPrefEnv({ set: [[kPrefModalHighlight, true]] }); +}); + +// Test if highlighting a dark page is detected properly. +add_task(async function testDarkPageDetection() { + let url = kFixtureBaseURL + "file_FinderSample.html"; + await BrowserTestUtils.withNewTab(url, async function (browser) { + let findbar = await gBrowser.getFindBar(); + + await promiseOpenFindbar(findbar); + + let word = "Roland"; + let expectedResult = { + rectCount: 2, + insertCalls: [1, 3], + removeCalls: [0, 1], + }; + let promise = promiseTestHighlighterOutput( + browser, + word, + expectedResult, + function (node) { + Assert.ok( + node.style.background.startsWith("rgba(0, 0, 0"), + "White HTML page should have a black background color set for the mask" + ); + } + ); + await promiseEnterStringIntoFindField(findbar, word); + await promise; + + findbar.close(true); + }); + + await BrowserTestUtils.withNewTab(url, async function (browser) { + let findbar = await gBrowser.getFindBar(); + + await promiseOpenFindbar(findbar); + + let word = "Roland"; + let expectedResult = { + rectCount: 2, + insertCalls: [2, 4], + removeCalls: [0, 1], + }; + + await SpecialPowers.spawn(browser, [], async function () { + let dwu = content.windowUtils; + let uri = + "data:text/css;charset=utf-8," + + encodeURIComponent(` + body { + background: maroon radial-gradient(circle, #a01010 0%, #800000 80%) center center / cover no-repeat; + color: white; + }`); + try { + dwu.loadSheetUsingURIString(uri, dwu.USER_SHEET); + } catch (e) {} + }); + + let promise = promiseTestHighlighterOutput( + browser, + word, + expectedResult, + node => { + Assert.ok( + node.style.background.startsWith("rgba(255, 255, 255"), + "Dark HTML page should have a white background color set for the mask" + ); + } + ); + await promiseEnterStringIntoFindField(findbar, word); + await promise; + + findbar.close(true); + }); +}); + +add_task(async function testHighlightAllToggle() { + let url = kFixtureBaseURL + "file_FinderSample.html"; + await BrowserTestUtils.withNewTab(url, async function (browser) { + let findbar = await gBrowser.getFindBar(); + + await promiseOpenFindbar(findbar); + + let word = "Roland"; + let expectedResult = { + rectCount: 2, + insertCalls: [2, 4], + removeCalls: [0, 1], + }; + let promise = promiseTestHighlighterOutput(browser, word, expectedResult); + await promiseEnterStringIntoFindField(findbar, word); + await promise; + + // We now know we have multiple rectangles highlighted, so it's a good time + // to flip the pref. + expectedResult = { + rectCount: 0, + insertCalls: [0, 1], + removeCalls: [1, 2], + }; + promise = promiseTestHighlighterOutput(browser, word, expectedResult); + await SpecialPowers.pushPrefEnv({ set: [[kPrefHighlightAll, false]] }); + await promise; + + // For posterity, let's switch back. + expectedResult = { + rectCount: 2, + insertCalls: [1, 3], + removeCalls: [0, 1], + }; + promise = promiseTestHighlighterOutput(browser, word, expectedResult); + await SpecialPowers.pushPrefEnv({ set: [[kPrefHighlightAll, true]] }); + await promise; + }); +}); + +add_task(async function testXMLDocument() { + let url = + "data:text/xml;charset=utf-8," + + encodeURIComponent(`<?xml version="1.0"?> +<result> + <Title>Example</Title> + <Error>Error</Error> +</result>`); + await BrowserTestUtils.withNewTab(url, async function (browser) { + let findbar = await gBrowser.getFindBar(); + + await promiseOpenFindbar(findbar); + + let word = "Example"; + let expectedResult = { + rectCount: 0, + insertCalls: [1, 4], + removeCalls: [0, 1], + }; + let promise = promiseTestHighlighterOutput(browser, word, expectedResult); + await promiseEnterStringIntoFindField(findbar, word); + await promise; + + findbar.close(true); + }); +}); + +add_task(async function testHideOnLocationChange() { + let url = kFixtureBaseURL + "file_FinderSample.html"; + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url); + let browser = tab.linkedBrowser; + let findbar = await gBrowser.getFindBar(); + + await promiseOpenFindbar(findbar); + + let word = "Roland"; + let expectedResult = { + rectCount: 2, + insertCalls: [2, 4], + removeCalls: [0, 1], + }; + let promise = promiseTestHighlighterOutput(browser, word, expectedResult); + await promiseEnterStringIntoFindField(findbar, word); + await promise; + + // Now we try to navigate away! (Using the same page) + promise = promiseTestHighlighterOutput(browser, word, { + rectCount: 0, + insertCalls: [0, 0], + removeCalls: [1, 2], + }); + BrowserTestUtils.startLoadingURIString(browser, url); + await promise; + + BrowserTestUtils.removeTab(tab); +}); + +add_task(async function testHideOnClear() { + let url = kFixtureBaseURL + "file_FinderSample.html"; + await BrowserTestUtils.withNewTab(url, async function (browser) { + let findbar = await gBrowser.getFindBar(); + await promiseOpenFindbar(findbar); + + let word = "Roland"; + let expectedResult = { + rectCount: 2, + insertCalls: [2, 4], + removeCalls: [0, 2], + }; + let promise = promiseTestHighlighterOutput(browser, word, expectedResult); + await promiseEnterStringIntoFindField(findbar, word); + await promise; + + await new Promise(resolve => setTimeout(resolve, kIteratorTimeout)); + promise = promiseTestHighlighterOutput(browser, "", { + rectCount: 0, + insertCalls: [0, 0], + removeCalls: [1, 2], + }); + findbar.clear(); + await promise; + + findbar.close(true); + }); +}); + +add_task(async function testRectsAndTexts() { + let url = + "data:text/html;charset=utf-8," + + encodeURIComponent( + '<div style="width: 150px; border: 1px solid black">' + + "Here are a lot of words Please use find to highlight some words that wrap" + + " across a line boundary and see what happens.</div>" + ); + await BrowserTestUtils.withNewTab(url, async function (browser) { + let findbar = await gBrowser.getFindBar(); + await promiseOpenFindbar(findbar); + + let word = "words please use find to"; + let expectedResult = { + rectCount: 2, + insertCalls: [2, 4], + removeCalls: [0, 2], + }; + let promise = promiseTestHighlighterOutput( + browser, + word, + expectedResult, + (maskNode, outlineNode) => { + let boxes = outlineNode.getElementsByTagName("span"); + Assert.equal( + boxes.length, + 2, + "There should be two outline boxes containing text" + ); + Assert.equal( + boxes[0].textContent.trim(), + "words", + "First text should match" + ); + Assert.equal( + boxes[1].textContent.trim(), + "Please use find to", + "Second word should match" + ); + } + ); + await promiseEnterStringIntoFindField(findbar, word); + await promise; + }); +}); + +add_task(async function testTooLargeToggle() { + let url = kFixtureBaseURL + "file_FinderSample.html"; + await BrowserTestUtils.withNewTab(url, async function (browser) { + let findbar = await gBrowser.getFindBar(); + await promiseOpenFindbar(findbar); + + await SpecialPowers.spawn(browser, [], async function () { + let dwu = content.windowUtils; + let uri = + "data:text/css;charset=utf-8," + + encodeURIComponent(` + body { + min-height: 1234567px; + }`); + try { + dwu.loadSheetUsingURIString(uri, dwu.USER_SHEET); + } catch (e) {} + }); + + let word = "Roland"; + let expectedResult = { + rectCount: 2, + insertCalls: [2, 4], + removeCalls: [0, 2], + // No animations should be triggered when the page is too large. + animationCalls: [0, 0], + }; + let promise = promiseTestHighlighterOutput(browser, word, expectedResult); + await promiseEnterStringIntoFindField(findbar, word); + await promise; + }); +}); diff --git a/toolkit/modules/tests/browser/browser_FinderHighlighter2.js b/toolkit/modules/tests/browser/browser_FinderHighlighter2.js new file mode 100644 index 0000000000..1fa026b333 --- /dev/null +++ b/toolkit/modules/tests/browser/browser_FinderHighlighter2.js @@ -0,0 +1,70 @@ +"use strict"; + +const kPrefHighlightAll = "findbar.highlightAll"; +const kPrefModalHighlight = "findbar.modalHighlight"; + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [ + [kPrefHighlightAll, true], + [kPrefModalHighlight, true], + ], + }); +}); + +add_task(async function testIframeOffset() { + let url = kFixtureBaseURL + "file_FinderIframeTest.html"; + + await BrowserTestUtils.withNewTab(url, async function (browser) { + let findbar = gBrowser.getFindBar(); + await promiseOpenFindbar(findbar); + + let word = "frame"; + let expectedResult = { + rectCount: 12, + insertCalls: [2, 4], + removeCalls: [0, 2], + }; + let promise = promiseTestHighlighterOutput( + browser, + word, + expectedResult, + (maskNode, outlineNode, rects) => { + Assert.equal( + rects.length, + expectedResult.rectCount, + "Rect counts should match" + ); + // Checks to guard against regressing this functionality: + let expectedOffsets = [ + { x: 16, y: 60 }, + { x: 68, y: 104 }, + { x: 21, y: 215 }, + { x: 78, y: 264 }, + { x: 21, y: 375 }, + { x: 78, y: 424 }, + { x: 20, y: 534 }, + { x: 93, y: 534 }, + { x: 71, y: 577 }, + { x: 145, y: 577 }, + ]; + for (let i = 1, l = rects.length - 1; i < l; ++i) { + let rect = rects[i]; + let expected = expectedOffsets[i - 1]; + Assert.equal( + Math.floor(rect.x), + expected.x, + "Horizontal offset should match for rect " + i + ); + Assert.equal( + Math.floor(rect.y), + expected.y, + "Vertical offset should match for rect " + i + ); + } + } + ); + await promiseEnterStringIntoFindField(findbar, word); + await promise; + }); +}); diff --git a/toolkit/modules/tests/browser/browser_Finder_hidden_textarea.js b/toolkit/modules/tests/browser/browser_Finder_hidden_textarea.js new file mode 100644 index 0000000000..149bae7666 --- /dev/null +++ b/toolkit/modules/tests/browser/browser_Finder_hidden_textarea.js @@ -0,0 +1,70 @@ +/* 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/. */ +add_task(async function test_bug1174036() { + const URI = + "<body><textarea>e1</textarea><textarea>e2</textarea><textarea>e3</textarea></body>"; + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: "data:text/html;charset=utf-8," + encodeURIComponent(URI), + }, + async function (browser) { + // Hide the first textarea. + await SpecialPowers.spawn(browser, [], function () { + content.document.getElementsByTagName("textarea")[0].style.display = + "none"; + }); + + let finder = browser.finder; + let listener = { + onFindResult() { + ok(false, "callback wasn't replaced"); + }, + }; + finder.addResultListener(listener); + + function waitForFind() { + return new Promise(resolve => { + listener.onFindResult = resolve; + }); + } + + // Find the first 'e' (which should be in the second textarea). + let promiseFind = waitForFind(); + finder.fastFind("e", false, false); + let findResult = await promiseFind; + is( + findResult.result, + Ci.nsITypeAheadFind.FIND_FOUND, + "find first string" + ); + + let firstRect = findResult.rect; + + // Find the second 'e' (in the third textarea). + promiseFind = waitForFind(); + finder.findAgain("e", false, false, false); + findResult = await promiseFind; + is( + findResult.result, + Ci.nsITypeAheadFind.FIND_FOUND, + "find second string" + ); + ok(!findResult.rect.equals(firstRect), "found new string"); + + // Ensure that we properly wrap to the second textarea. + promiseFind = waitForFind(); + finder.findAgain("e", false, false, false); + findResult = await promiseFind; + is( + findResult.result, + Ci.nsITypeAheadFind.FIND_WRAPPED, + "wrapped to first string" + ); + ok(findResult.rect.equals(firstRect), "wrapped to original string"); + + finder.removeResultListener(listener); + } + ); +}); diff --git a/toolkit/modules/tests/browser/browser_Finder_offscreen_text.js b/toolkit/modules/tests/browser/browser_Finder_offscreen_text.js new file mode 100644 index 0000000000..ea6bb4508f --- /dev/null +++ b/toolkit/modules/tests/browser/browser_Finder_offscreen_text.js @@ -0,0 +1,72 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(async function test_offscreen_text() { + // Generate URI of a big DOM that contains the target text at several + // line positions (to force some targets to be offscreen). + const linesToGenerate = 155; + const linesToInsertTargetText = [5, 50, 150]; + let targetCount = linesToInsertTargetText.length; + let t = 0; + const TARGET_TEXT = "findthis"; + + let URI = "<body>"; + for (let i = 0; i < linesToGenerate; i++) { + URI += i + "<br>"; + if (t < targetCount && linesToInsertTargetText[t] == i) { + URI += TARGET_TEXT; + t++; + } + } + URI += "</body>"; + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: "data:text/html;charset=utf-8," + encodeURIComponent(URI), + }, + async function (browser) { + let finder = browser.finder; + let listener = { + onFindResult() { + ok(false, "callback wasn't replaced"); + }, + }; + finder.addResultListener(listener); + + function waitForFind() { + return new Promise(resolve => { + listener.onFindResult = resolve; + }); + } + + // Find each of the targets. + for (let t = 0; t < targetCount; ++t) { + let promiseFind = waitForFind(); + if (t == 0) { + finder.fastFind(TARGET_TEXT, false, false); + } else { + finder.findAgain(TARGET_TEXT, false, false, false); + } + let findResult = await promiseFind; + is( + findResult.result, + Ci.nsITypeAheadFind.FIND_FOUND, + "Found target " + t + ); + } + + // Find one more time and make sure we wrap. + let promiseFind = waitForFind(); + finder.findAgain(TARGET_TEXT, false, false, false); + let findResult = await promiseFind; + is( + findResult.result, + Ci.nsITypeAheadFind.FIND_WRAPPED, + "Wrapped to first target" + ); + + finder.removeResultListener(listener); + } + ); +}); diff --git a/toolkit/modules/tests/browser/browser_Finder_overflowed_onscreen.js b/toolkit/modules/tests/browser/browser_Finder_overflowed_onscreen.js new file mode 100644 index 0000000000..c31014c943 --- /dev/null +++ b/toolkit/modules/tests/browser/browser_Finder_overflowed_onscreen.js @@ -0,0 +1,45 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(async function test_vertical_text() { + const URI = + '<body><div style="max-height: 100px; max-width: 100px; overflow: scroll;"><div style="padding-left: 100px; max-height: 100px; max-width: 200px; overflow: auto;">d<br/><br/><br/><br/>c----------------b<br/><br/><br/><br/>a</div></div></body>'; + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: "data:text/html;charset=utf-8," + encodeURIComponent(URI), + }, + async function (browser) { + let finder = browser.finder; + let listener = { + onFindResult() { + ok(false, "callback wasn't replaced"); + }, + }; + finder.addResultListener(listener); + + function waitForFind() { + return new Promise(resolve => { + listener.onFindResult = resolve; + }); + } + + let targets = ["a", "b", "c", "d"]; + + for (let i = 0; i < targets.length; ++i) { + // Find the target text. + let target = targets[i]; + let promiseFind = waitForFind(); + finder.fastFind(target, false, false); + let findResult = await promiseFind; + isnot( + findResult.result, + Ci.nsITypeAheadFind.FIND_NOTFOUND, + "Found target text '" + target + "'." + ); + } + + finder.removeResultListener(listener); + } + ); +}); diff --git a/toolkit/modules/tests/browser/browser_Finder_overflowed_textarea.js b/toolkit/modules/tests/browser/browser_Finder_overflowed_textarea.js new file mode 100644 index 0000000000..479ee6c4bb --- /dev/null +++ b/toolkit/modules/tests/browser/browser_Finder_overflowed_textarea.js @@ -0,0 +1,75 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +add_task(async function test_offscreen_text() { + // Generate URI of a big DOM that contains the target text + // within a textarea at several line positions (to force + // some targets to be overflowed). + const linesToGenerate = 155; + const linesToInsertTargetText = [5, 50, 150]; + const targetCount = linesToInsertTargetText.length; + let t = 0; + const TARGET_TEXT = "findthis"; + + let URI = "<body><textarea>"; + for (let i = 0; i < linesToGenerate; i++) { + URI += i + " "; + if (t < targetCount && linesToInsertTargetText[t] == i) { + URI += TARGET_TEXT; + t++; + } + URI += "\n"; + } + URI += "</textarea></body>"; + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: "data:text/html;charset=utf-8," + encodeURIComponent(URI), + }, + async function (browser) { + let finder = browser.finder; + let listener = { + onFindResult() { + ok(false, "callback wasn't replaced"); + }, + }; + finder.addResultListener(listener); + + function waitForFind() { + return new Promise(resolve => { + listener.onFindResult = resolve; + }); + } + + // Find each of the targets. + for (let t = 0; t < targetCount; ++t) { + let promiseFind = waitForFind(); + if (t == 0) { + finder.fastFind(TARGET_TEXT, false, false); + } else { + finder.findAgain(TARGET_TEXT, false, false, false); + } + let findResult = await promiseFind; + is( + findResult.result, + Ci.nsITypeAheadFind.FIND_FOUND, + "Found target " + t + ); + } + + // Find one more time and make sure we wrap. + let promiseFind = waitForFind(); + finder.findAgain(TARGET_TEXT, false, false, false); + let findResult = await promiseFind; + is( + findResult.result, + Ci.nsITypeAheadFind.FIND_WRAPPED, + "Wrapped to first target" + ); + + finder.removeResultListener(listener); + } + ); +}); diff --git a/toolkit/modules/tests/browser/browser_Finder_pointer_events_none.js b/toolkit/modules/tests/browser/browser_Finder_pointer_events_none.js new file mode 100644 index 0000000000..2e6e31d4cf --- /dev/null +++ b/toolkit/modules/tests/browser/browser_Finder_pointer_events_none.js @@ -0,0 +1,39 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(async function test_offscreen_text() { + const URI = '<body><div style="pointer-events:none">find this</div></body>'; + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: "data:text/html;charset=utf-8," + encodeURIComponent(URI), + }, + async function (browser) { + let finder = browser.finder; + let listener = { + onFindResult() { + ok(false, "callback wasn't replaced"); + }, + }; + finder.addResultListener(listener); + + function waitForFind() { + return new Promise(resolve => { + listener.onFindResult = resolve; + }); + } + + // Find the target text. + let promiseFind = waitForFind(); + finder.fastFind("find this", false, false); + let findResult = await promiseFind; + is( + findResult.result, + Ci.nsITypeAheadFind.FIND_FOUND, + "Found target text." + ); + + finder.removeResultListener(listener); + } + ); +}); diff --git a/toolkit/modules/tests/browser/browser_Finder_skip_invisible_and_option.js b/toolkit/modules/tests/browser/browser_Finder_skip_invisible_and_option.js new file mode 100644 index 0000000000..c133bbb613 --- /dev/null +++ b/toolkit/modules/tests/browser/browser_Finder_skip_invisible_and_option.js @@ -0,0 +1,132 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(async function test_skip_invisible() { + const URI = ` + <body> + <div> + a + <div style="visibility:hidden;">a</div> + <div style="visibility: hidden"><span style="visibility: visible">a</div> + <select> + <option>a</option> + </select> + <select size=2> + <option>a</option> + <option>a</option> + </select> + <input placeholder="a"> + </body>`; + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: "data:text/html;charset=utf-8," + encodeURIComponent(URI), + }, + async function (browser) { + let finder = browser.finder; + let listener = { + onFindResult() { + ok(false, "callback wasn't replaced"); + }, + }; + finder.addResultListener(listener); + + function waitForFind() { + return new Promise(resolve => { + listener.onFindResult = resolve; + }); + } + + // Find the target text. There should be three results. + let target = "a"; + let promiseFind = waitForFind(); + finder.fastFind(target, false, false); + let findResult = await promiseFind; + + // Check the results and repeat four times. After the final repeat, make + // sure we've wrapped to the beginning. + let i = 0; + for (; i < 4; i++) { + isnot( + findResult.result, + Ci.nsITypeAheadFind.FIND_NOTFOUND, + "Should find target text '" + target + "' instance " + (i + 1) + "." + ); + + promiseFind = waitForFind(); + finder.findAgain("a", false, false, false); + findResult = await promiseFind; + } + is( + findResult.result, + Ci.nsITypeAheadFind.FIND_WRAPPED, + "After " + (i + 1) + " searches, we should wrap to first target text." + ); + + finder.removeResultListener(listener); + } + ); +}); + +add_task(async function test_find_anon_content() { + const URI = ` + <!doctype html> + <style> + div::before { content: "before content"; } + div::after { content: "after content"; } + span::after { content: ","; } + </style> + <div> </div> + <img alt="Some fallback text"> + <input type="submit" value="Some button text"> + <input type="password" value="password"> + <p>1<span></span>234</p> + + `; + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: "data:text/html;charset=utf-8," + encodeURIComponent(URI), + }, + async function (browser) { + let finder = browser.finder; + let listener = { + onFindResult() { + ok(false, "callback wasn't replaced"); + }, + }; + finder.addResultListener(listener); + + function waitForFind() { + return new Promise(resolve => { + listener.onFindResult = resolve; + }); + } + + async function assertFindable(text, findable = true) { + let promiseFind = waitForFind(); + finder.fastFind(text, false, false); + let findResult = await promiseFind; + is( + findResult.result, + findable + ? Ci.nsITypeAheadFind.FIND_FOUND + : Ci.nsITypeAheadFind.FIND_NOTFOUND, + `${text} should ${findable ? "" : "not "}be findable` + ); + } + + await assertFindable("before content"); + await assertFindable("after content"); + await assertFindable("fallback text"); + await assertFindable("button text"); + await assertFindable("password", false); + + // TODO(emilio): In an ideal world we could select the comma as well and + // then you'd find it with "1,234" instead... + await assertFindable("1234"); + + finder.removeResultListener(listener); + } + ); +}); diff --git a/toolkit/modules/tests/browser/browser_Finder_vertical_text.js b/toolkit/modules/tests/browser/browser_Finder_vertical_text.js new file mode 100644 index 0000000000..c20c4ea8f6 --- /dev/null +++ b/toolkit/modules/tests/browser/browser_Finder_vertical_text.js @@ -0,0 +1,59 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(async function test_vertical_text() { + const URI = + '<body><div style="writing-mode: vertical-rl">vertical-rl</div><div style="writing-mode: vertical-lr">vertical-lr</div><div style="writing-mode: sideways-rl">sideways-rl</div><div style="writing-mode: sideways-lr">sideways-lr</div></body>'; + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: "data:text/html;charset=utf-8," + encodeURIComponent(URI), + }, + async function (browser) { + let finder = browser.finder; + let listener = { + onFindResult() { + ok(false, "callback wasn't replaced"); + }, + }; + finder.addResultListener(listener); + + function waitForFind() { + return new Promise(resolve => { + listener.onFindResult = resolve; + }); + } + + let targets = [ + // Full matches use one path in our find code. + "vertical-rl", + "vertical-lr", + "sideways-rl", + "sideways-lr", + // Partial matches use a second path in our find code. + "l-r", + "l-l", + "s-r", + "s-l", + ]; + + for (let i = 0; i < targets.length; ++i) { + // Find the target text. + let target = targets[i]; + let promiseFind = waitForFind(); + finder.fastFind(target, false, false); + let findResult = await promiseFind; + + // We check the logical inversion of not not found, because found and wrapped are + // two different correct results, but not found is the only incorrect result. + isnot( + findResult.result, + Ci.nsITypeAheadFind.FIND_NOTFOUND, + "Found target text '" + target + "'." + ); + } + + finder.removeResultListener(listener); + } + ); +}); diff --git a/toolkit/modules/tests/browser/browser_Geometry.js b/toolkit/modules/tests/browser/browser_Geometry.js new file mode 100644 index 0000000000..83aeb305d1 --- /dev/null +++ b/toolkit/modules/tests/browser/browser_Geometry.js @@ -0,0 +1,134 @@ +/* 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/. */ + +const { Point, Rect } = ChromeUtils.importESModule( + "resource://gre/modules/Geometry.sys.mjs" +); + +function test() { + ok(Rect, "Rect class exists"); + for (var fname in tests) { + tests[fname](); + } +} + +var tests = { + testGetDimensions() { + let r = new Rect(5, 10, 100, 50); + Assert.equal(r.left, 5, "rect has correct left value"); + Assert.equal(r.top, 10, "rect has correct top value"); + Assert.equal(r.right, 105, "rect has correct right value"); + Assert.equal(r.bottom, 60, "rect has correct bottom value"); + Assert.equal(r.width, 100, "rect has correct width value"); + Assert.equal(r.height, 50, "rect has correct height value"); + Assert.equal(r.x, 5, "rect has correct x value"); + Assert.equal(r.y, 10, "rect has correct y value"); + }, + + testIsEmpty() { + let r = new Rect(0, 0, 0, 10); + ok(r.isEmpty(), "rect with nonpositive width is empty"); + r = new Rect(0, 0, 10, 0); + ok(r.isEmpty(), "rect with nonpositive height is empty"); + r = new Rect(0, 0, 10, 10); + ok(!r.isEmpty(), "rect with positive dimensions is not empty"); + }, + + testRestrictTo() { + let r1 = new Rect(10, 10, 100, 100); + let r2 = new Rect(50, 50, 100, 100); + r1.restrictTo(r2); + ok(r1.equals(new Rect(50, 50, 60, 60)), "intersection is non-empty"); + + r1 = new Rect(10, 10, 100, 100); + r2 = new Rect(120, 120, 100, 100); + r1.restrictTo(r2); + ok(r1.isEmpty(), "intersection is empty"); + + r1 = new Rect(10, 10, 100, 100); + r2 = new Rect(0, 0, 0, 0); + r1.restrictTo(r2); + ok(r1.isEmpty(), "intersection of rect and empty is empty"); + + r1 = new Rect(0, 0, 0, 0); + r2 = new Rect(0, 0, 0, 0); + r1.restrictTo(r2); + ok(r1.isEmpty(), "intersection of empty and empty is empty"); + }, + + testExpandToContain() { + let r1 = new Rect(10, 10, 100, 100); + let r2 = new Rect(50, 50, 100, 100); + r1.expandToContain(r2); + ok( + r1.equals(new Rect(10, 10, 140, 140)), + "correct expandToContain on intersecting rectangles" + ); + + r1 = new Rect(10, 10, 100, 100); + r2 = new Rect(120, 120, 100, 100); + r1.expandToContain(r2); + ok( + r1.equals(new Rect(10, 10, 210, 210)), + "correct expandToContain on non-intersecting rectangles" + ); + + r1 = new Rect(10, 10, 100, 100); + r2 = new Rect(0, 0, 0, 0); + r1.expandToContain(r2); + ok( + r1.equals(new Rect(10, 10, 100, 100)), + "expandToContain of rect and empty is rect" + ); + + r1 = new Rect(10, 10, 0, 0); + r2 = new Rect(0, 0, 0, 0); + r1.expandToContain(r2); + ok(r1.isEmpty(), "expandToContain of empty and empty is empty"); + }, + + testSubtract: function testSubtract() { + function equals(rects1, rects2) { + return ( + rects1.length == rects2.length && + rects1.every(function (r, i) { + return r.equals(rects2[i]); + }) + ); + } + + let r1 = new Rect(0, 0, 100, 100); + let r2 = new Rect(500, 500, 100, 100); + ok( + equals(r1.subtract(r2), [r1]), + "subtract area outside of region yields same region" + ); + + r1 = new Rect(0, 0, 100, 100); + r2 = new Rect(-10, -10, 50, 120); + ok( + equals(r1.subtract(r2), [new Rect(40, 0, 60, 100)]), + "subtracting vertical bar from edge leaves one rect" + ); + + r1 = new Rect(0, 0, 100, 100); + r2 = new Rect(-10, -10, 120, 50); + ok( + equals(r1.subtract(r2), [new Rect(0, 40, 100, 60)]), + "subtracting horizontal bar from edge leaves one rect" + ); + + r1 = new Rect(0, 0, 100, 100); + r2 = new Rect(40, 40, 20, 20); + ok( + equals(r1.subtract(r2), [ + new Rect(0, 0, 40, 100), + new Rect(40, 0, 20, 40), + new Rect(40, 60, 20, 40), + new Rect(60, 0, 40, 100), + ]), + "subtracting rect in middle leaves union of rects" + ); + }, +}; diff --git a/toolkit/modules/tests/browser/browser_InlineSpellChecker.js b/toolkit/modules/tests/browser/browser_InlineSpellChecker.js new file mode 100644 index 0000000000..c931615091 --- /dev/null +++ b/toolkit/modules/tests/browser/browser_InlineSpellChecker.js @@ -0,0 +1,47 @@ +var InlineSpellChecker; +var SpellCheckHelper; + +function test() { + let tempScope = ChromeUtils.importESModule( + "resource://gre/modules/InlineSpellChecker.sys.mjs" + ); + InlineSpellChecker = tempScope.InlineSpellChecker; + SpellCheckHelper = tempScope.SpellCheckHelper; + + ok(InlineSpellChecker, "InlineSpellChecker class exists"); + for (var fname in tests) { + tests[fname](); + } +} + +var tests = { + testFlagsForInputs() { + const HTML_NS = "http://www.w3.org/1999/xhtml"; + const { INPUT, EDITABLE, TEXTINPUT, NUMERIC, PASSWORD, SPELLCHECKABLE } = + SpellCheckHelper; + const kExpectedResults = { + text: INPUT | EDITABLE | TEXTINPUT | SPELLCHECKABLE, + password: INPUT | EDITABLE | TEXTINPUT | PASSWORD, + search: INPUT | EDITABLE | TEXTINPUT | SPELLCHECKABLE, + url: INPUT | EDITABLE | TEXTINPUT, + tel: INPUT | EDITABLE | TEXTINPUT, + email: INPUT | EDITABLE | TEXTINPUT, + number: INPUT | EDITABLE | TEXTINPUT | NUMERIC, + checkbox: INPUT, + radio: INPUT, + }; + + for (let [type, expectedFlags] of Object.entries(kExpectedResults)) { + let input = document.createElementNS(HTML_NS, "input"); + input.type = type; + let actualFlags = SpellCheckHelper.isEditable(input, window); + is( + actualFlags, + expectedFlags, + `For input type "${type}" expected flags ${ + "0x" + expectedFlags.toString(16) + }; got ${"0x" + actualFlags.toString(16)}` + ); + } + }, +}; diff --git a/toolkit/modules/tests/browser/browser_Troubleshoot.js b/toolkit/modules/tests/browser/browser_Troubleshoot.js new file mode 100644 index 0000000000..d627f175e4 --- /dev/null +++ b/toolkit/modules/tests/browser/browser_Troubleshoot.js @@ -0,0 +1,1380 @@ +/* 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/. */ + +// Ideally this would be an xpcshell test, but Troubleshoot relies on things +// that aren't initialized outside of a XUL app environment like AddonManager +// and the "@mozilla.org/xre/app-info;1" component. + +const { Troubleshoot } = ChromeUtils.importESModule( + "resource://gre/modules/Troubleshoot.sys.mjs" +); +const { sinon } = ChromeUtils.importESModule( + "resource://testing-common/Sinon.sys.mjs" +); + +const { FeatureGate } = ChromeUtils.importESModule( + "resource://featuregates/FeatureGate.sys.mjs" +); +const { PreferenceExperiments } = ChromeUtils.importESModule( + "resource://normandy/lib/PreferenceExperiments.sys.mjs" +); +const { PreferenceRollouts } = ChromeUtils.importESModule( + "resource://normandy/lib/PreferenceRollouts.sys.mjs" +); +const { AddonStudies } = ChromeUtils.importESModule( + "resource://normandy/lib/AddonStudies.sys.mjs" +); +const { NormandyTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/NormandyTestUtils.sys.mjs" +); + +NormandyTestUtils.init({ Assert }); + +add_task(async function snapshotSchema() { + let snapshot = await Troubleshoot.snapshot(); + try { + validateObject(snapshot, SNAPSHOT_SCHEMA); + ok(true, "The snapshot should conform to the schema."); + } catch (err) { + ok(false, "Schema mismatch, " + err); + } +}); + +add_task(async function experimentalFeatures() { + let featureGates = await FeatureGate.all(); + ok(featureGates.length, "Should be at least one FeatureGate"); + + let snapshot = await Troubleshoot.snapshot(); + for (let i = 0; i < snapshot.experimentalFeatures.length; i++) { + let experimentalFeature = snapshot.experimentalFeatures[i]; + is( + experimentalFeature[0], + featureGates[i].title, + "The first item in the array should be the title's l10n-id of the FeatureGate" + ); + is( + experimentalFeature[1], + featureGates[i].preference, + "The second item in the array should be the preference name for the FeatureGate" + ); + is( + experimentalFeature[2], + Services.prefs.getBoolPref(featureGates[i].preference), + "The third item in the array should be the preference value of the FeatureGate" + ); + } +}); + +add_task(async function modifiedPreferences() { + let prefs = [ + "javascript.troubleshoot", + "troubleshoot.foo", + "network.proxy.troubleshoot", + "print.print_to_filename", + ]; + prefs.forEach(function (p) { + Services.prefs.setBoolPref(p, true); + is(Services.prefs.getBoolPref(p), true, "The pref should be set: " + p); + }); + Services.prefs.setCharPref("dom.push.userAgentID", "testvalue"); + let snapshot = await Troubleshoot.snapshot(); + let p = snapshot.modifiedPreferences; + is( + p["javascript.troubleshoot"], + true, + "The pref should be present because it's in the allowed prefs " + + "and not in the pref regexes that are disallowed." + ); + ok( + !("troubleshoot.foo" in p), + "The pref should be absent because it's not in the allowed prefs." + ); + ok( + !("network.proxy.troubleshoot" in p), + "The pref should be absent because it's in the pref regexes " + + "that are disallowed." + ); + ok( + !("dom.push.userAgentID" in p), + "The pref should be absent because it's in the pref regexes " + + "that are disallowed." + ); + ok( + !("print.print_to_filename" in p), + "The pref should be absent because it's not in the allowed prefs." + ); + prefs.forEach(p => Services.prefs.deleteBranch(p)); + Services.prefs.clearUserPref("dom.push.userAgentID"); +}); + +add_task(async function unicodePreferences() { + let name = "font.name.sans-serif.x-western"; + let utf8Value = "\xc4\x8capk\xc5\xafv Krasopis"; + let unicodeValue = "\u010Capk\u016Fv Krasopis"; + + // set/getCharPref work with 8bit strings (utf8) + Services.prefs.setCharPref(name, utf8Value); + + let snapshot = await Troubleshoot.snapshot(); + let p = snapshot.modifiedPreferences; + is(p[name], unicodeValue, "The pref should have correct Unicode value."); + Services.prefs.deleteBranch(name); +}); + +add_task(async function printingPreferences() { + let prefs = [ + "javascript.print_to_filename", + "print.print_bgimages", + "print.print_to_filename", + ]; + prefs.forEach(function (p) { + Services.prefs.setBoolPref(p, true); + is(Services.prefs.getBoolPref(p), true, "The pref should be set: " + p); + }); + let snapshot = await Troubleshoot.snapshot(); + let p = snapshot.printingPreferences; + is(p["print.print_bgimages"], true, "The pref should be present"); + ok( + !("print.print_to_filename" in p), + "The pref should not be present (sensitive)" + ); + ok( + !("javascript.print_to_filename" in p), + "The pref should be absent because it's not a print pref." + ); + prefs.forEach(p => Services.prefs.deleteBranch(p)); +}); + +add_task(function normandy() { + const { + preferenceStudyFactory, + branchedAddonStudyFactory, + preferenceRolloutFactory, + } = NormandyTestUtils.factories; + + return NormandyTestUtils.decorate( + PreferenceExperiments.withMockExperiments([ + preferenceStudyFactory({ + userFacingName: "Test Pref Study B", + branch: "test-branch-pref", + }), + preferenceStudyFactory({ + userFacingName: "Test Pref Study A", + branch: "test-branch-pref", + }), + ]), + AddonStudies.withStudies([ + branchedAddonStudyFactory({ + userFacingName: "Test Addon Study B", + branch: "test-branch-addon", + }), + branchedAddonStudyFactory({ + userFacingName: "Test Addon Study A", + branch: "test-branch-addon", + }), + ]), + PreferenceRollouts.withTestMock({ + rollouts: [ + preferenceRolloutFactory({ + statue: "ACTIVE", + slug: "test-pref-rollout-b", + }), + preferenceRolloutFactory({ + statue: "ACTIVE", + slug: "test-pref-rollout-a", + }), + ], + }), + async function testNormandyInfoInTroubleshooting({ + prefExperiments, + addonStudies, + prefRollouts, + }) { + let snapshot = await Troubleshoot.snapshot(); + let info = snapshot.normandy; + // The order should be flipped, since each category is sorted by slug. + Assert.deepEqual( + info.prefStudies, + [prefExperiments[1], prefExperiments[0]], + "prefs studies should exist in the right order" + ); + Assert.deepEqual( + info.addonStudies, + [addonStudies[1], addonStudies[0]], + "addon studies should exist in the right order" + ); + Assert.deepEqual( + info.prefRollouts, + [prefRollouts[1], prefRollouts[0]], + "pref rollouts should exist in the right order" + ); + } + )(); +}); + +add_task(function normandyErrorHandling() { + return NormandyTestUtils.decorate( + NormandyTestUtils.withStub(PreferenceExperiments, "getAllActive", { + returnValue: Promise.reject("Expected error - PreferenceExperiments"), + }), + NormandyTestUtils.withStub(AddonStudies, "getAllActive", { + returnValue: Promise.reject("Expected error - AddonStudies"), + }), + NormandyTestUtils.withStub(PreferenceRollouts, "getAllActive", { + returnValue: Promise.reject("Expected error - PreferenceRollouts"), + }), + async function testNormandyErrorHandling() { + let consoleEndFn = TestUtils.listenForConsoleMessages(); + let snapshot = await Troubleshoot.snapshot(); + let info = snapshot.normandy; + Assert.deepEqual( + info.prefStudies, + [], + "prefs studies should be an empty list if there is an error" + ); + Assert.deepEqual( + info.addonStudies, + [], + "addon studies should be an empty list if there is an error" + ); + Assert.deepEqual( + info.prefRollouts, + [], + "pref rollouts should be an empty list if there is an error" + ); + let msgs = await consoleEndFn(); + let expectedSet = new Set([ + /Expected error - PreferenceExperiments/, + /Expected error - AddonStudies/, + /Expected error - PreferenceRollouts/, + ]); + + for (let msg of msgs) { + msg = msg.wrappedJSObject; + if (msg.level != "error") { + continue; + } + + let msgContents = msg.arguments[0]; + for (let expected of expectedSet) { + if (expected.test(msgContents)) { + expectedSet.delete(expected); + break; + } + } + } + + Assert.equal( + expectedSet.size, + 0, + "Should have no messages left in the expected set" + ); + } + )(); +}); + +add_task(async function themes() { + let snapshot = await Troubleshoot.snapshot(); + let foundTheme = false; + for (let addon of snapshot.addons) { + if (addon.type == "theme") { + foundTheme = true; + break; + } + } + ok(foundTheme, "found a theme in the addons list"); +}); + +// This is inspired by JSON Schema, or by the example on its Wikipedia page +// anyway. +const SNAPSHOT_SCHEMA = { + type: "object", + required: true, + properties: { + application: { + required: true, + type: "object", + properties: { + name: { + required: true, + type: "string", + }, + version: { + required: true, + type: "string", + }, + buildID: { + required: true, + type: "string", + }, + distributionID: { + required: true, + type: "string", + }, + userAgent: { + required: true, + type: "string", + }, + osVersion: { + required: true, + type: "string", + }, + osTheme: { + type: "string", + }, + rosetta: { + required: false, + type: "boolean", + }, + vendor: { + type: "string", + }, + updateChannel: { + type: "string", + }, + supportURL: { + type: "string", + }, + launcherProcessState: { + type: "number", + }, + remoteAutoStart: { + type: "boolean", + required: true, + }, + fissionAutoStart: { + type: "boolean", + }, + fissionDecisionStatus: { + type: "string", + }, + numTotalWindows: { + type: "number", + }, + numFissionWindows: { + type: "number", + }, + numRemoteWindows: { + type: "number", + }, + policiesStatus: { + type: "number", + }, + keyLocationServiceGoogleFound: { + type: "boolean", + }, + keySafebrowsingGoogleFound: { + type: "boolean", + }, + keyMozillaFound: { + type: "boolean", + }, + safeMode: { + type: "boolean", + }, + memorySizeBytes: { + type: "number", + }, + diskAvailableBytes: { + type: "number", + }, + pointingDevices: { + required: false, + type: "array", + }, + }, + }, + crashes: { + required: false, + type: "object", + properties: { + pending: { + required: true, + type: "number", + }, + submitted: { + required: true, + type: "array", + items: { + type: "object", + properties: { + id: { + required: true, + type: "string", + }, + date: { + required: true, + type: "number", + }, + pending: { + required: true, + type: "boolean", + }, + }, + }, + }, + }, + }, + addons: { + required: true, + type: "array", + items: { + type: "object", + properties: { + name: { + required: true, + type: "string", + }, + type: { + required: true, + type: "string", + }, + version: { + required: true, + type: "string", + }, + id: { + required: true, + type: "string", + }, + isActive: { + required: true, + type: "boolean", + }, + }, + }, + }, + securitySoftware: { + required: false, + type: "object", + properties: { + registeredAntiVirus: { + required: true, + type: "string", + }, + registeredAntiSpyware: { + required: true, + type: "string", + }, + registeredFirewall: { + required: true, + type: "string", + }, + }, + }, + features: { + required: true, + type: "array", + items: { + type: "object", + properties: { + name: { + required: true, + type: "string", + }, + version: { + required: true, + type: "string", + }, + id: { + required: true, + type: "string", + }, + }, + }, + }, + processes: { + required: true, + type: "object", + properties: { + maxWebContentProcesses: { + required: true, + type: "number", + }, + remoteTypes: { + required: true, + type: "object", + }, + }, + }, + experimentalFeatures: { + required: true, + type: "array", + }, + environmentVariables: { + required: true, + type: "object", + }, + modifiedPreferences: { + required: true, + type: "object", + }, + printingPreferences: { + required: true, + type: "object", + }, + lockedPreferences: { + required: true, + type: "object", + properties: { + "fission.autostart": { + required: false, + type: "boolean", + }, + "fission.autostart.session": { + required: false, + type: "boolean", + }, + "media.utility-process.enabled": { + required: false, + type: "boolean", + }, + "media.utility-ffmpeg.enabled": { + required: false, + type: "boolean", + }, + "media.utility-ffvpx.enabled": { + required: false, + type: "boolean", + }, + "media.utility-wmf.enabled": { + required: false, + type: "boolean", + }, + "media.utility-applemedia.enabled": { + required: false, + type: "boolean", + }, + "media.utility-vorbis.enabled": { + required: false, + type: "boolean", + }, + "media.utility-wav.enabled": { + required: false, + type: "boolean", + }, + "media.utility-opus.enabled": { + required: false, + type: "boolean", + }, + }, + }, + places: { + required: true, + type: "array", + items: { + type: "object", + items: { + entity: { + required: true, + type: "string", + }, + count: { + required: true, + type: "number", + }, + sizeBytes: { + required: true, + type: "number", + }, + sizePerc: { + required: true, + type: "number", + }, + efficiencyPerc: { + required: true, + type: "number", + }, + sequentialityPerc: { + required: true, + type: "number", + }, + }, + }, + }, + graphics: { + required: true, + type: "object", + properties: { + numTotalWindows: { + required: true, + type: "number", + }, + numAcceleratedWindows: { + required: true, + type: "number", + }, + graphicsDevicePixelRatios: { + type: "array", + items: { + type: "number", + }, + }, + windowLayerManagerType: { + type: "string", + }, + windowLayerManagerRemote: { + type: "boolean", + }, + numAcceleratedWindowsMessage: { + type: "object", + properties: { + key: { + required: true, + type: "string", + }, + args: { + required: false, + type: "object", + }, + }, + }, + adapterDescription: { + type: "string", + }, + adapterVendorID: { + type: "string", + }, + adapterDeviceID: { + type: "string", + }, + adapterSubsysID: { + type: "string", + }, + adapterRAM: { + type: "number", + }, + adapterDrivers: { + type: "string", + }, + driverVendor: { + type: "string", + }, + driverVersion: { + type: "string", + }, + driverDate: { + type: "string", + }, + adapterDescription2: { + type: "string", + }, + adapterVendorID2: { + type: "string", + }, + adapterDeviceID2: { + type: "string", + }, + adapterSubsysID2: { + type: "string", + }, + adapterRAM2: { + type: "number", + }, + adapterDrivers2: { + type: "string", + }, + driverVendor2: { + type: "string", + }, + driverVersion2: { + type: "string", + }, + driverDate2: { + type: "string", + }, + isGPU2Active: { + type: "boolean", + }, + direct2DEnabled: { + type: "boolean", + }, + directWriteEnabled: { + type: "boolean", + }, + directWriteVersion: { + type: "string", + }, + clearTypeParameters: { + type: "string", + }, + webgl1Renderer: { + type: "string", + }, + webgl1Version: { + type: "string", + }, + webgl1DriverExtensions: { + type: "string", + }, + webgl1Extensions: { + type: "string", + }, + webgl1WSIInfo: { + type: "string", + }, + webgl2Renderer: { + type: "string", + }, + webgl2Version: { + type: "string", + }, + webgl2DriverExtensions: { + type: "string", + }, + webgl2Extensions: { + type: "string", + }, + webgl2WSIInfo: { + type: "string", + }, + webgpuDefaultAdapter: { + type: "object", + }, + webgpuFallbackAdapter: { + type: "object", + }, + info: { + type: "object", + }, + failures: { + type: "object", + properties: { + key: { + required: true, + type: "string", + }, + args: { + required: false, + type: "object", + }, + }, + }, + indices: { + type: "array", + items: { + type: "number", + }, + }, + featureLog: { + type: "object", + }, + crashGuards: { + type: "array", + }, + direct2DEnabledMessage: { + type: "object", + properties: { + key: { + required: true, + type: "string", + }, + args: { + required: false, + type: "object", + }, + }, + }, + targetFrameRate: { + type: "number", + }, + windowProtocol: { + type: "string", + }, + desktopEnvironment: { + type: "string", + }, + supportFontDetermination: { + type: "string", + }, + }, + }, + media: { + required: true, + type: "object", + properties: { + currentAudioBackend: { + required: true, + type: "string", + }, + currentMaxAudioChannels: { + required: true, + type: "number", + }, + currentPreferredSampleRate: { + required: true, + type: "number", + }, + audioOutputDevices: { + required: true, + type: "array", + items: { + type: "object", + properties: { + name: { + required: true, + type: "string", + }, + groupId: { + required: true, + type: "string", + }, + vendor: { + required: true, + type: "string", + }, + type: { + required: true, + type: "number", + }, + state: { + required: true, + type: "number", + }, + preferred: { + required: true, + type: "number", + }, + supportedFormat: { + required: true, + type: "number", + }, + defaultFormat: { + required: true, + type: "number", + }, + maxChannels: { + required: true, + type: "number", + }, + defaultRate: { + required: true, + type: "number", + }, + maxRate: { + required: true, + type: "number", + }, + minRate: { + required: true, + type: "number", + }, + maxLatency: { + required: true, + type: "number", + }, + minLatency: { + required: true, + type: "number", + }, + }, + }, + }, + audioInputDevices: { + required: true, + type: "array", + items: { + type: "object", + properties: { + name: { + required: true, + type: "string", + }, + groupId: { + required: true, + type: "string", + }, + vendor: { + required: true, + type: "string", + }, + type: { + required: true, + type: "number", + }, + state: { + required: true, + type: "number", + }, + preferred: { + required: true, + type: "number", + }, + supportedFormat: { + required: true, + type: "number", + }, + defaultFormat: { + required: true, + type: "number", + }, + maxChannels: { + required: true, + type: "number", + }, + defaultRate: { + required: true, + type: "number", + }, + maxRate: { + required: true, + type: "number", + }, + minRate: { + required: true, + type: "number", + }, + maxLatency: { + required: true, + type: "number", + }, + minLatency: { + required: true, + type: "number", + }, + }, + }, + }, + codecSupportInfo: { + required: false, + type: "string", + }, + }, + }, + accessibility: { + required: true, + type: "object", + properties: { + isActive: { + required: true, + type: "boolean", + }, + forceDisabled: { + type: "number", + }, + handlerUsed: { + type: "boolean", + }, + instantiator: { + type: "string", + }, + }, + }, + libraryVersions: { + required: true, + type: "object", + properties: { + NSPR: { + required: true, + type: "object", + properties: { + minVersion: { + required: true, + type: "string", + }, + version: { + required: true, + type: "string", + }, + }, + }, + NSS: { + required: true, + type: "object", + properties: { + minVersion: { + required: true, + type: "string", + }, + version: { + required: true, + type: "string", + }, + }, + }, + NSSUTIL: { + required: true, + type: "object", + properties: { + minVersion: { + required: true, + type: "string", + }, + version: { + required: true, + type: "string", + }, + }, + }, + NSSSSL: { + required: true, + type: "object", + properties: { + minVersion: { + required: true, + type: "string", + }, + version: { + required: true, + type: "string", + }, + }, + }, + NSSSMIME: { + required: true, + type: "object", + properties: { + minVersion: { + required: true, + type: "string", + }, + version: { + required: true, + type: "string", + }, + }, + }, + }, + }, + userJS: { + required: true, + type: "object", + properties: { + exists: { + required: true, + type: "boolean", + }, + }, + }, + sandbox: { + required: false, + type: "object", + properties: { + hasSeccompBPF: { + required: AppConstants.platform == "linux", + type: "boolean", + }, + hasSeccompTSync: { + required: AppConstants.platform == "linux", + type: "boolean", + }, + hasUserNamespaces: { + required: AppConstants.platform == "linux", + type: "boolean", + }, + hasPrivilegedUserNamespaces: { + required: AppConstants.platform == "linux", + type: "boolean", + }, + canSandboxContent: { + required: false, + type: "boolean", + }, + canSandboxMedia: { + required: false, + type: "boolean", + }, + contentSandboxLevel: { + required: AppConstants.MOZ_SANDBOX, + type: "number", + }, + effectiveContentSandboxLevel: { + required: AppConstants.MOZ_SANDBOX, + type: "number", + }, + contentWin32kLockdownState: { + required: AppConstants.MOZ_SANDBOX && AppConstants.platform == "win", + type: "string", + }, + supportSandboxGpuLevel: { + required: AppConstants.MOZ_SANDBOX && AppConstants.platform == "win", + type: "number", + }, + syscallLog: { + required: AppConstants.platform == "linux", + type: "array", + items: { + type: "object", + properties: { + index: { + required: true, + type: "number", + }, + pid: { + required: true, + type: "number", + }, + tid: { + required: true, + type: "number", + }, + procType: { + required: true, + type: "string", + }, + syscall: { + required: true, + type: "number", + }, + args: { + required: true, + type: "array", + items: { + type: "string", + }, + }, + }, + }, + }, + }, + }, + startupCache: { + required: false, + type: "object", + properties: { + DiskCachePath: { + required: true, + type: "string", + }, + IgnoreDiskCache: { + required: true, + type: "boolean", + }, + FoundDiskCacheOnInit: { + required: true, + type: "boolean", + }, + WroteToDiskCache: { + required: true, + type: "boolean", + }, + }, + }, + intl: { + required: true, + type: "object", + properties: { + localeService: { + required: true, + type: "object", + properties: { + requested: { + required: true, + type: "array", + }, + available: { + required: true, + type: "array", + }, + supported: { + required: true, + type: "array", + }, + regionalPrefs: { + required: true, + type: "array", + }, + defaultLocale: { + required: true, + type: "string", + }, + }, + }, + osPrefs: { + required: true, + type: "object", + properties: { + systemLocales: { + required: true, + type: "array", + }, + regionalPrefsLocales: { + required: true, + type: "array", + }, + }, + }, + }, + }, + remoteAgent: { + type: "object", + properties: { + running: { + required: true, + type: "boolean", + }, + url: { + required: true, + type: "string", + }, + }, + }, + normandy: { + type: "object", + required: AppConstants.MOZ_NORMANDY, + properties: { + addonStudies: { + type: "array", + items: { + type: "object", + properties: { + userFacingName: { type: "string", required: true }, + branch: { type: "string", required: true }, + }, + }, + required: true, + }, + prefRollouts: { + type: "array", + items: { + type: "object", + properties: { + slug: { type: "string", required: true }, + state: { type: "string", required: true }, + }, + }, + required: true, + }, + prefStudies: { + type: "array", + items: { + type: "object", + properties: { + userFacingName: { type: "string", required: true }, + branch: { type: "string", required: true }, + }, + }, + required: true, + }, + nimbusExperiments: { + type: "array", + items: { + type: "object", + properties: { + userFacingName: { type: "string", required: true }, + branch: { + type: "object", + properties: { + slug: { type: "string", required: true }, + }, + }, + }, + }, + required: true, + }, + nimbusRollouts: { + type: "array", + items: { + type: "object", + properties: { + featureId: { type: "string", required: true }, + slug: { type: "string", required: true }, + }, + }, + }, + }, + }, + legacyUserStylesheets: { + type: "object", + properties: { + active: { + required: true, + type: "boolean", + }, + types: { + required: true, + type: "array", + }, + }, + }, + }, +}; + +/** + * Throws an Error if obj doesn't conform to schema. That way you get a nice + * error message and a stack to help you figure out what went wrong, which you + * wouldn't get if this just returned true or false instead. There's still + * room for improvement in communicating validation failures, however. + * + * @param obj The object to validate. + * @param schema The schema that obj should conform to. + */ +function validateObject(obj, schema) { + if (obj === undefined && !schema.required) { + return; + } + if (typeof schema.type != "string") { + throw schemaErr("'type' must be a string", schema); + } + if (objType(obj) != schema.type) { + throw validationErr("Object is not of the expected type", obj, schema); + } + let validatorFnName = "validateObject_" + schema.type; + if (!(validatorFnName in this)) { + throw schemaErr("Validator function not defined for type", schema); + } + this[validatorFnName](obj, schema); +} + +function validateObject_object(obj, schema) { + if (typeof schema.properties != "object") { + // Don't care what obj's properties are. + return; + } + // First check that all the schema's properties match the object. + for (let prop in schema.properties) { + validateObject(obj[prop], schema.properties[prop]); + } + // Now check that the object doesn't have any properties not in the schema. + for (let prop in obj) { + if (!(prop in schema.properties)) { + throw validationErr( + "Object has property " + prop + " not in schema", + obj, + schema + ); + } + } +} + +function validateObject_array(array, schema) { + if (typeof schema.items != "object") { + // Don't care what the array's elements are. + return; + } + array.forEach(elt => validateObject(elt, schema.items)); +} + +function validateObject_string(str, schema) {} +function validateObject_boolean(bool, schema) {} +function validateObject_number(num, schema) {} + +function validationErr(msg, obj, schema) { + return new Error( + "Validation error: " + + msg + + ": object=" + + JSON.stringify(obj) + + ", schema=" + + JSON.stringify(schema) + ); +} + +function schemaErr(msg, schema) { + return new Error("Schema error: " + msg + ": " + JSON.stringify(schema)); +} + +function objType(obj) { + let type = typeof obj; + if (type != "object") { + return type; + } + if (Array.isArray(obj)) { + return "array"; + } + if (obj === null) { + return "null"; + } + return type; +} diff --git a/toolkit/modules/tests/browser/browser_web_channel.js b/toolkit/modules/tests/browser/browser_web_channel.js new file mode 100644 index 0000000000..9dfa59485b --- /dev/null +++ b/toolkit/modules/tests/browser/browser_web_channel.js @@ -0,0 +1,587 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +ChromeUtils.defineESModuleGetters(this, { + WebChannel: "resource://gre/modules/WebChannel.sys.mjs", +}); + +const HTTP_PATH = "http://example.com"; +const HTTP_ENDPOINT = + getRootDirectory(gTestPath).replace("chrome://mochitests/content", "") + + "file_web_channel.html"; +const HTTP_MISMATCH_PATH = "http://example.org"; +const HTTP_IFRAME_PATH = "http://mochi.test:8888"; +const HTTP_REDIRECTED_IFRAME_PATH = "http://example.org"; + +requestLongerTimeout(2); // timeouts in debug builds. + +// Keep this synced with /mobile/android/tests/browser/robocop/testWebChannel.js +// as much as possible. (We only have that since we can't run browser chrome +// tests on Android. Yet?) +var gTests = [ + { + desc: "WebChannel generic message", + run() { + return new Promise(function (resolve, reject) { + let tab; + let channel = new WebChannel("generic", Services.io.newURI(HTTP_PATH)); + channel.listen(function (id, message, target) { + is(id, "generic"); + is(message.something.nested, "hello"); + channel.stopListening(); + gBrowser.removeTab(tab); + resolve(); + }); + + tab = BrowserTestUtils.addTab( + gBrowser, + HTTP_PATH + HTTP_ENDPOINT + "?generic" + ); + }); + }, + }, + { + desc: "WebChannel generic message in a private window.", + async run() { + let promiseTestDone = new Promise(function (resolve, reject) { + let channel = new WebChannel("generic", Services.io.newURI(HTTP_PATH)); + channel.listen(function (id, message, target) { + is(id, "generic"); + is(message.something.nested, "hello"); + channel.stopListening(); + resolve(); + }); + }); + + const url = HTTP_PATH + HTTP_ENDPOINT + "?generic"; + let privateWindow = await BrowserTestUtils.openNewBrowserWindow({ + private: true, + }); + await BrowserTestUtils.openNewForegroundTab(privateWindow.gBrowser, url); + await promiseTestDone; + await BrowserTestUtils.closeWindow(privateWindow); + }, + }, + { + desc: "WebChannel two way communication", + run() { + return new Promise(function (resolve, reject) { + let tab; + let channel = new WebChannel("twoway", Services.io.newURI(HTTP_PATH)); + + channel.listen(function (id, message, sender) { + is(id, "twoway", "bad id"); + ok(message.command, "command not ok"); + + if (message.command === "one") { + channel.send({ data: { nested: true } }, sender); + } + + if (message.command === "two") { + is(message.detail.data.nested, true); + channel.stopListening(); + gBrowser.removeTab(tab); + resolve(); + } + }); + + tab = BrowserTestUtils.addTab( + gBrowser, + HTTP_PATH + HTTP_ENDPOINT + "?twoway" + ); + }); + }, + }, + { + desc: "WebChannel two way communication in an iframe", + async run() { + let parentChannel = new WebChannel("echo", Services.io.newURI(HTTP_PATH)); + let iframeChannel = new WebChannel( + "twoway", + Services.io.newURI(HTTP_IFRAME_PATH) + ); + let promiseTestDone = new Promise(function (resolve, reject) { + parentChannel.listen(function (id, message, sender) { + reject(new Error("WebChannel message incorrectly sent to parent")); + }); + + iframeChannel.listen(function (id, message, sender) { + is(id, "twoway", "bad id (2)"); + ok(message.command, "command not ok (2)"); + + if (message.command === "one") { + iframeChannel.send({ data: { nested: true } }, sender); + } + + if (message.command === "two") { + is(message.detail.data.nested, true); + resolve(); + } + }); + }); + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: HTTP_PATH + HTTP_ENDPOINT + "?iframe", + }, + async function () { + await promiseTestDone; + parentChannel.stopListening(); + iframeChannel.stopListening(); + } + ); + }, + }, + { + desc: "WebChannel response to a redirected iframe", + async run() { + /** + * This test checks that WebChannel responses are only sent + * to an iframe if the iframe has not redirected to another origin. + * Test flow: + * 1. create a page, embed an iframe on origin A. + * 2. the iframe sends a message `redirecting`, then redirects to + * origin B. + * 3. the iframe at origin B is set up to echo any messages back to the + * test parent. + * 4. the test parent receives the `redirecting` message from origin A. + * the test parent creates a new channel with origin B. + * 5. when origin B is ready, it sends a `loaded` message to the test + * parent, letting the test parent know origin B is ready to echo + * messages. + * 5. the test parent tries to send a response to origin A. If the + * WebChannel does not perform a valid origin check, the response + * will be received by origin B. If the WebChannel does perform + * a valid origin check, the response will not be sent. + * 6. the test parent sends a `done` message to origin B, which origin + * B echoes back. If the response to origin A is not echoed but + * the message to origin B is, then hooray, the test passes. + */ + + let preRedirectChannel = new WebChannel( + "pre_redirect", + Services.io.newURI(HTTP_IFRAME_PATH) + ); + let postRedirectChannel = new WebChannel( + "post_redirect", + Services.io.newURI(HTTP_REDIRECTED_IFRAME_PATH) + ); + + let promiseTestDone = new Promise(function (resolve, reject) { + preRedirectChannel.listen(function (id, message, preRedirectSender) { + if (message.command === "redirecting") { + postRedirectChannel.listen(function ( + aId, + aMessage, + aPostRedirectSender + ) { + is(aId, "post_redirect"); + isnot(aMessage.command, "no_response_expected"); + + if (aMessage.command === "loaded") { + // The message should not be received on the preRedirectChannel + // because the target window has redirected. + preRedirectChannel.send( + { command: "no_response_expected" }, + preRedirectSender + ); + postRedirectChannel.send( + { command: "done" }, + aPostRedirectSender + ); + } else if (aMessage.command === "done") { + resolve(); + } else { + reject(new Error(`Unexpected command ${aMessage.command}`)); + } + }); + } else { + reject(new Error(`Unexpected command ${message.command}`)); + } + }); + }); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: HTTP_PATH + HTTP_ENDPOINT + "?iframe_pre_redirect", + }, + async function () { + await promiseTestDone; + preRedirectChannel.stopListening(); + postRedirectChannel.stopListening(); + } + ); + }, + }, + { + desc: "WebChannel multichannel", + run() { + return new Promise(function (resolve, reject) { + let tab; + let channel = new WebChannel( + "multichannel", + Services.io.newURI(HTTP_PATH) + ); + + channel.listen(function (id, message, sender) { + is(id, "multichannel"); + gBrowser.removeTab(tab); + resolve(); + }); + + tab = BrowserTestUtils.addTab( + gBrowser, + HTTP_PATH + HTTP_ENDPOINT + "?multichannel" + ); + }); + }, + }, + { + desc: "WebChannel unsolicited send, using system principal", + async run() { + let channel = new WebChannel("echo", Services.io.newURI(HTTP_PATH)); + + // an unsolicted message is sent from Chrome->Content which is then + // echoed back. If the echo is received here, then the content + // received the message. + let messagePromise = new Promise(function (resolve, reject) { + channel.listen(function (id, message, sender) { + is(id, "echo"); + is(message.command, "unsolicited"); + + resolve(); + }); + }); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: HTTP_PATH + HTTP_ENDPOINT + "?unsolicited", + }, + async function (targetBrowser) { + channel.send( + { command: "unsolicited" }, + { + browsingContext: targetBrowser.browsingContext, + principal: Services.scriptSecurityManager.getSystemPrincipal(), + } + ); + await messagePromise; + channel.stopListening(); + } + ); + }, + }, + { + desc: "WebChannel unsolicited send, using target origin's principal", + async run() { + let targetURI = Services.io.newURI(HTTP_PATH); + let channel = new WebChannel("echo", targetURI); + + // an unsolicted message is sent from Chrome->Content which is then + // echoed back. If the echo is received here, then the content + // received the message. + let messagePromise = new Promise(function (resolve, reject) { + channel.listen(function (id, message, sender) { + is(id, "echo"); + is(message.command, "unsolicited"); + + resolve(); + }); + }); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: HTTP_PATH + HTTP_ENDPOINT + "?unsolicited", + }, + async function (targetBrowser) { + channel.send( + { command: "unsolicited" }, + { + browsingContext: targetBrowser.browsingContext, + principal: Services.scriptSecurityManager.createContentPrincipal( + targetURI, + {} + ), + } + ); + + await messagePromise; + channel.stopListening(); + } + ); + }, + }, + { + desc: "WebChannel unsolicited send with principal mismatch", + async run() { + let targetURI = Services.io.newURI(HTTP_PATH); + let channel = new WebChannel("echo", targetURI); + + // two unsolicited messages are sent from Chrome->Content. The first, + // `unsolicited_no_response_expected` is sent to the wrong principal + // and should not be echoed back. The second, `done`, is sent to the + // correct principal and should be echoed back. + let messagePromise = new Promise(function (resolve, reject) { + channel.listen(function (id, message, sender) { + is(id, "echo"); + + if (message.command === "done") { + resolve(); + } else { + reject(new Error(`Unexpected command ${message.command}`)); + } + }); + }); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: HTTP_PATH + HTTP_ENDPOINT + "?unsolicited", + }, + async function (targetBrowser) { + let mismatchURI = Services.io.newURI(HTTP_MISMATCH_PATH); + let mismatchPrincipal = + Services.scriptSecurityManager.createContentPrincipal( + mismatchURI, + {} + ); + + // send a message to the wrong principal. It should not be delivered + // to content, and should not be echoed back. + channel.send( + { command: "unsolicited_no_response_expected" }, + { + browsingContext: targetBrowser.browsingContext, + principal: mismatchPrincipal, + } + ); + + let targetPrincipal = + Services.scriptSecurityManager.createContentPrincipal( + targetURI, + {} + ); + + // send the `done` message to the correct principal. It + // should be echoed back. + channel.send( + { command: "done" }, + { + browsingContext: targetBrowser.browsingContext, + principal: targetPrincipal, + } + ); + + await messagePromise; + channel.stopListening(); + } + ); + }, + }, + { + desc: "WebChannel non-window target", + async run() { + /** + * This test ensures messages can be received from and responses + * sent to non-window elements. + * + * First wait for the non-window element to send a "start" message. + * Then send the non-window element a "done" message. + * The non-window element will echo the "done" message back, if it + * receives the message. + * Listen for the response. If received, good to go! + */ + let channel = new WebChannel( + "not_a_window", + Services.io.newURI(HTTP_PATH) + ); + + let testDonePromise = new Promise(function (resolve, reject) { + channel.listen(function (id, message, sender) { + if (message.command === "start") { + channel.send({ command: "done" }, sender); + } else if (message.command === "done") { + resolve(); + } else { + reject(new Error(`Unexpected command ${message.command}`)); + } + }); + }); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: HTTP_PATH + HTTP_ENDPOINT + "?bubbles", + }, + async function () { + await testDonePromise; + channel.stopListening(); + } + ); + }, + }, + { + desc: "WebChannel disallows non-string message from non-whitelisted origin", + async run() { + /** + * This test ensures that non-string messages can't be sent via WebChannels. + * We create a page (on a non-whitelisted origin) which should send us two + * messages immediately. The first message has an object for it's detail, + * and the second has a string. We check that we only get the second + * message. + */ + let channel = new WebChannel("objects", Services.io.newURI(HTTP_PATH)); + let testDonePromise = new Promise((resolve, reject) => { + channel.listen((id, message, sender) => { + is(id, "objects"); + is(message.type, "string"); + resolve(); + }); + }); + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: HTTP_PATH + HTTP_ENDPOINT + "?object", + }, + async function () { + await testDonePromise; + channel.stopListening(); + } + ); + }, + }, + { + desc: "WebChannel allows both string and non-string message from whitelisted origin", + async run() { + /** + * Same process as above, but we whitelist the origin before loading the page, + * and expect to get *both* messages back (each exactly once). + */ + let channel = new WebChannel("objects", Services.io.newURI(HTTP_PATH)); + + let testDonePromise = new Promise((resolve, reject) => { + let sawObject = false; + let sawString = false; + channel.listen((id, message, sender) => { + is(id, "objects"); + if (message.type === "object") { + ok(!sawObject); + sawObject = true; + } else if (message.type === "string") { + ok(!sawString); + sawString = true; + } else { + reject(new Error(`Unknown message type: ${message.type}`)); + } + if (sawObject && sawString) { + resolve(); + } + }); + }); + const webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist"; + let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref); + let newWhitelist = origWhitelist + " " + HTTP_PATH; + Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist); + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: HTTP_PATH + HTTP_ENDPOINT + "?object", + }, + async function () { + await testDonePromise; + Services.prefs.setCharPref(webchannelWhitelistPref, origWhitelist); + channel.stopListening(); + } + ); + }, + }, + { + desc: "WebChannel errors handling the message are delivered back to content", + async run() { + const ERRNO_UNKNOWN_ERROR = 999; // WebChannel.sys.mjs doesn't export this. + + // The channel where we purposely fail responding to a command. + let channel = new WebChannel("error", Services.io.newURI(HTTP_PATH)); + // The channel where we see the response when the content sees the error + let echoChannel = new WebChannel("echo", Services.io.newURI(HTTP_PATH)); + + let testDonePromise = new Promise((resolve, reject) => { + // listen for the confirmation that content saw the error. + echoChannel.listen((id, message, sender) => { + is(id, "echo"); + is(message.error, "oh no"); + is(message.errno, ERRNO_UNKNOWN_ERROR); + resolve(); + }); + + // listen for a message telling us to simulate an error. + channel.listen((id, message, sender) => { + is(id, "error"); + is(message.command, "oops"); + throw new Error("oh no"); + }); + }); + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: HTTP_PATH + HTTP_ENDPOINT + "?error_thrown", + }, + async function () { + await testDonePromise; + channel.stopListening(); + echoChannel.stopListening(); + } + ); + }, + }, + { + desc: "WebChannel errors due to an invalid channel are delivered back to content", + async run() { + const ERRNO_NO_SUCH_CHANNEL = 2; // WebChannel.sys.mjs doesn't export this. + // The channel where we see the response when the content sees the error + let echoChannel = new WebChannel("echo", Services.io.newURI(HTTP_PATH)); + + let testDonePromise = new Promise((resolve, reject) => { + // listen for the confirmation that content saw the error. + echoChannel.listen((id, message, sender) => { + is(id, "echo"); + is(message.error, "No Such Channel"); + is(message.errno, ERRNO_NO_SUCH_CHANNEL); + resolve(); + }); + }); + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: HTTP_PATH + HTTP_ENDPOINT + "?error_invalid_channel", + }, + async function () { + await testDonePromise; + echoChannel.stopListening(); + } + ); + }, + }, +]; // gTests + +function test() { + waitForExplicitFinish(); + + (async function () { + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.https_first_pbm", false]], + }); + + for (let testCase of gTests) { + info("Running: " + testCase.desc); + await testCase.run(); + } + })().then(finish, ex => { + ok(false, "Unexpected Exception: " + ex); + finish(); + }); +} diff --git a/toolkit/modules/tests/browser/file_FinderIframeTest.html b/toolkit/modules/tests/browser/file_FinderIframeTest.html new file mode 100644 index 0000000000..826e5dc4ca --- /dev/null +++ b/toolkit/modules/tests/browser/file_FinderIframeTest.html @@ -0,0 +1,21 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> + <title>Test (i)frame offsets, bug 1366646</title> +</head> +<body> +<p>top level frame</p> +<iframe src="data:text/html,<p>frame without border</p> + <iframe src='data:text/html,<p>nested frame without border</p>' height='50' width='100%' style='background-color: yellow; border: 0'></iframe> + " style="background-color: pink; border: 0" width="100%" height="150"></iframe> +<iframe src="data:text/html,<p>frame with 5px border</p> + <iframe src='data:text/html,<p>nested frame with 5px border</p>' height='50' width='100%' style='background-color: yellow; border: solid 5px black'></iframe> + " style="background-color: pink; border: solid 5px black" width="100%" height="150"></iframe> +<iframe src="data:text/html,<p>frame with 5px padding</p> + <iframe src='data:text/html,<p>nested frame with 5px padding</p>' height='50' width='100%' style='background-color: yellow; border: 0; padding: 5px'></iframe> + " style="background-color: pink; border: 0; padding: 5px" width="100%" height="150"></iframe> +<!-- Testing deprecated HTML4 iframe properties too: --> +<iframe src="data:text/html,<p>frame with frameborder, marginwidth/ height and 5px padding</p> + <iframe src='data:text/html,<p>nested frame with frameborder, marginwidth/ height</p>' height='50' width='100%' frameborder='1' marginheight='5' marginwidth='5' style='background-color: yellow;'></iframe> + " frameborder="1" marginheight="5" marginwidth="5" style="background-color: pink; padding: 5px" width="100%" height="150"></iframe> +</body></html> diff --git a/toolkit/modules/tests/browser/file_FinderSample.html b/toolkit/modules/tests/browser/file_FinderSample.html new file mode 100644 index 0000000000..e952d1fe97 --- /dev/null +++ b/toolkit/modules/tests/browser/file_FinderSample.html @@ -0,0 +1,824 @@ +<!DOCTYPE html> +<html> +<head> + <title>Childe Roland</title> +</head> +<body> +<h1>"Childe Roland to the Dark Tower Came"</h1><h5>Robert Browning</h5> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>I.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>My first thought was, he lied in every word, +<dl> +<dd>That hoary cripple, with malicious eye</dd> +<dd>Askance to watch the working of his lie</dd> +</dl> +</dd> +<dd>On mine, and mouth scarce able to afford</dd> +<dd>Suppression of the glee that pursed and scored +<dl> +<dd>Its edge, at one more victim gained thereby.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>II.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>What else should he be set for, with his staff? +<dl> +<dd>What, save to waylay with his lies, ensnare</dd> +<dd>All travellers who might find him posted there,</dd> +</dl> +</dd> +<dd>And ask the road? I guessed what skull-like laugh</dd> +<dd>Would break, what crutch 'gin write my epitaph +<dl> +<dd>For pastime in the dusty thoroughfare,</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>III.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>If at his counsel I should turn aside +<dl> +<dd>Into that ominous tract which, all agree,</dd> +<dd>Hides the Dark Tower. Yet acquiescingly</dd> +</dl> +</dd> +<dd>I did turn as he pointed: neither pride</dd> +<dd>Nor hope rekindling at the end descried, +<dl> +<dd>So much as gladness that some end might be.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>IV.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>For, what with my whole world-wide wandering, +<dl> +<dd>What with my search drawn out thro' years, my hope</dd> +<dd>Dwindled into a ghost not fit to cope</dd> +</dl> +</dd> +<dd>With that obstreperous joy success would bring,</dd> +<dd>I hardly tried now to rebuke the spring +<dl> +<dd>My heart made, finding failure in its scope.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>V.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>As when a sick man very near to death +<dl> +<dd>Seems dead indeed, and feels begin and end</dd> +<dd>The tears and takes the farewell of each friend,</dd> +</dl> +</dd> +<dd>And hears one bid the other go, draw breath</dd> +<dd>Freelier outside ("since all is o'er," he saith, +<dl> +<dd>"And the blow fallen no grieving can amend;")</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>VI.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>While some discuss if near the other graves +<dl> +<dd>Be room enough for this, and when a day</dd> +<dd>Suits best for carrying the corpse away,</dd> +</dl> +</dd> +<dd>With care about the banners, scarves and staves:</dd> +<dd>And still the man hears all, and only craves +<dl> +<dd>He may not shame such tender love and stay.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>VII.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>Thus, I had so long suffered in this quest, +<dl> +<dd>Heard failure prophesied so oft, been writ</dd> +<dd>So many times among "The Band" - to wit,</dd> +</dl> +</dd> +<dd>The knights who to the Dark Tower's search addressed</dd> +<dd>Their steps - that just to fail as they, seemed best, +<dl> +<dd>And all the doubt was now—should I be fit?</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>VIII.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>So, quiet as despair, I turned from him, +<dl> +<dd>That hateful cripple, out of his highway</dd> +<dd>Into the path he pointed. All the day</dd> +</dl> +</dd> +<dd>Had been a dreary one at best, and dim</dd> +<dd>Was settling to its close, yet shot one grim +<dl> +<dd>Red leer to see the plain catch its estray.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>IX.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>For mark! no sooner was I fairly found +<dl> +<dd>Pledged to the plain, after a pace or two,</dd> +<dd>Than, pausing to throw backward a last view</dd> +</dl> +</dd> +<dd>O'er the safe road, 'twas gone; grey plain all round:</dd> +<dd>Nothing but plain to the horizon's bound. +<dl> +<dd>I might go on; nought else remained to do.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>X.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>So, on I went. I think I never saw +<dl> +<dd>Such starved ignoble nature; nothing throve:</dd> +<dd>For flowers - as well expect a cedar grove!</dd> +</dl> +</dd> +<dd>But cockle, spurge, according to their law</dd> +<dd>Might propagate their kind, with none to awe, +<dl> +<dd>You'd think; a burr had been a treasure trove.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XI.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>No! penury, inertness and grimace, +<dl> +<dd>In some strange sort, were the land's portion. "See</dd> +<dd>Or shut your eyes," said Nature peevishly,</dd> +</dl> +</dd> +<dd>"It nothing skills: I cannot help my case:</dd> +<dd>'Tis the Last Judgment's fire must cure this place, +<dl> +<dd>Calcine its clods and set my prisoners free."</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XII.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>If there pushed any ragged thistle-stalk +<dl> +<dd>Above its mates, the head was chopped; the bents</dd> +<dd>Were jealous else. What made those holes and rents</dd> +</dl> +</dd> +<dd>In the dock's harsh swarth leaves, bruised as to baulk</dd> +<dd>All hope of greenness? 'tis a brute must walk +<dl> +<dd>Pashing their life out, with a brute's intents.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XIII.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>As for the grass, it grew as scant as hair +<dl> +<dd>In leprosy; thin dry blades pricked the mud</dd> +<dd>Which underneath looked kneaded up with blood.</dd> +</dl> +</dd> +<dd>One stiff blind horse, his every bone a-stare,</dd> +<dd>Stood stupefied, however he came there: +<dl> +<dd>Thrust out past service from the devil's stud!</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XIV.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>Alive? he might be dead for aught I know, +<dl> +<dd>With that red gaunt and colloped neck a-strain,</dd> +<dd>And shut eyes underneath the rusty mane;</dd> +</dl> +</dd> +<dd>Seldom went such grotesqueness with such woe;</dd> +<dd>I never saw a brute I hated so; +<dl> +<dd>He must be wicked to deserve such pain.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XV.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>I shut my eyes and turned them on my heart. +<dl> +<dd>As a man calls for wine before he fights,</dd> +<dd>I asked one draught of earlier, happier sights,</dd> +</dl> +</dd> +<dd>Ere fitly I could hope to play my part.</dd> +<dd>Think first, fight afterwards - the soldier's art: +<dl> +<dd>One taste of the old time sets all to rights.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XVI.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>Not it! I fancied Cuthbert's reddening face +<dl> +<dd>Beneath its garniture of curly gold,</dd> +<dd>Dear fellow, till I almost felt him fold</dd> +</dl> +</dd> +<dd>An arm in mine to fix me to the place</dd> +<dd>That way he used. Alas, one night's disgrace! +<dl> +<dd>Out went my heart's new fire and left it cold.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XVII.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>Giles then, the soul of honour - there he stands +<dl> +<dd>Frank as ten years ago when knighted first.</dd> +<dd>What honest men should dare (he said) he durst.</dd> +</dl> +</dd> +<dd>Good - but the scene shifts - faugh! what hangman hands</dd> +<dd>Pin to his breast a parchment? His own bands +<dl> +<dd>Read it. Poor traitor, spit upon and curst!</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XVIII.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>Better this present than a past like that; +<dl> +<dd>Back therefore to my darkening path again!</dd> +<dd>No sound, no sight as far as eye could strain.</dd> +</dl> +</dd> +<dd>Will the night send a howlet or a bat?</dd> +<dd>I asked: when something on the dismal flat +<dl> +<dd>Came to arrest my thoughts and change their train.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XIX.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>A sudden little river crossed my path +<dl> +<dd>As unexpected as a serpent comes.</dd> +<dd>No sluggish tide congenial to the glooms;</dd> +</dl> +</dd> +<dd>This, as it frothed by, might have been a bath</dd> +<dd>For the fiend's glowing hoof - to see the wrath +<dl> +<dd>Of its black eddy bespate with flakes and spumes.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XX.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>So petty yet so spiteful! All along +<dl> +<dd>Low scrubby alders kneeled down over it;</dd> +<dd>Drenched willows flung them headlong in a fit</dd> +</dl> +</dd> +<dd>Of mute despair, a suicidal throng:</dd> +<dd>The river which had done them all the wrong, +<dl> +<dd>Whate'er that was, rolled by, deterred no whit.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XXI.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>Which, while I forded, - good saints, how I feared +<dl> +<dd>To set my foot upon a dead man's cheek,</dd> +<dd>Each step, or feel the spear I thrust to seek</dd> +</dl> +</dd> +<dd>For hollows, tangled in his hair or beard!</dd> +<dd>—It may have been a water-rat I speared, +<dl> +<dd>But, ugh! it sounded like a baby's shriek.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XXII.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>Glad was I when I reached the other bank. +<dl> +<dd>Now for a better country. Vain presage!</dd> +<dd>Who were the strugglers, what war did they wage,</dd> +</dl> +</dd> +<dd>Whose savage trample thus could pad the dank</dd> +<dd>Soil to a plash? Toads in a poisoned tank, +<dl> +<dd>Or wild cats in a red-hot iron cage—</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XXIII.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>The fight must so have seemed in that fell cirque. +<dl> +<dd>What penned them there, with all the plain to choose?</dd> +<dd>No foot-print leading to that horrid mews,</dd> +</dl> +</dd> +<dd>None out of it. Mad brewage set to work</dd> +<dd>Their brains, no doubt, like galley-slaves the Turk +<dl> +<dd>Pits for his pastime, Christians against Jews.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XXIV.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>And more than that - a furlong on - why, there! +<dl> +<dd>What bad use was that engine for, that wheel,</dd> +<dd>Or brake, not wheel - that harrow fit to reel</dd> +</dl> +</dd> +<dd>Men's bodies out like silk? with all the air</dd> +<dd>Of Tophet's tool, on earth left unaware, +<dl> +<dd>Or brought to sharpen its rusty teeth of steel.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XXV.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>Then came a bit of stubbed ground, once a wood, +<dl> +<dd>Next a marsh, it would seem, and now mere earth</dd> +<dd>Desperate and done with; (so a fool finds mirth,</dd> +</dl> +</dd> +<dd>Makes a thing and then mars it, till his mood</dd> +<dd>Changes and off he goes!) within a rood— +<dl> +<dd>Bog, clay and rubble, sand and stark black dearth.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XXVI.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>Now blotches rankling, coloured gay and grim, +<dl> +<dd>Now patches where some leanness of the soil's</dd> +<dd>Broke into moss or substances like boils;</dd> +</dl> +</dd> +<dd>Then came some palsied oak, a cleft in him</dd> +<dd>Like a distorted mouth that splits its rim +<dl> +<dd>Gaping at death, and dies while it recoils.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XXVII.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>And just as far as ever from the end! +<dl> +<dd>Nought in the distance but the evening, nought</dd> +<dd>To point my footstep further! At the thought,</dd> +</dl> +</dd> +<dd>A great black bird, Apollyon's bosom-friend,</dd> +<dd>Sailed past, nor beat his wide wing dragon-penned +<dl> +<dd>That brushed my cap—perchance the guide I sought.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XXVIII.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>For, looking up, aware I somehow grew, +<dl> +<dd>'Spite of the dusk, the plain had given place</dd> +<dd>All round to mountains - with such name to grace</dd> +</dl> +</dd> +<dd>Mere ugly heights and heaps now stolen in view.</dd> +<dd>How thus they had surprised me, - solve it, you! +<dl> +<dd>How to get from them was no clearer case.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XXIX.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>Yet half I seemed to recognise some trick +<dl> +<dd>Of mischief happened to me, God knows when—</dd> +<dd>In a bad dream perhaps. Here ended, then,</dd> +</dl> +</dd> +<dd>Progress this way. When, in the very nick</dd> +<dd>Of giving up, one time more, came a click +<dl> +<dd>As when a trap shuts - you're inside the den!</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XXX.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>Burningly it came on me all at once, +<dl> +<dd>This was the place! those two hills on the right,</dd> +<dd>Crouched like two bulls locked horn in horn in fight;</dd> +</dl> +</dd> +<dd>While to the left, a tall scalped mountain... Dunce,</dd> +<dd>Dotard, a-dozing at the very nonce, +<dl> +<dd>After a life spent training for the sight!</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XXXI.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>What in the midst lay but the Tower itself? +<dl> +<dd>The round squat turret, blind as the fool's heart</dd> +<dd>Built of brown stone, without a counterpart</dd> +</dl> +</dd> +<dd>In the whole world. The tempest's mocking elf</dd> +<dd>Points to the shipman thus the unseen shelf +<dl> +<dd>He strikes on, only when the timbers start.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XXXII.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>Not see? because of night perhaps? - why, day +<dl> +<dd>Came back again for that! before it left,</dd> +<dd>The dying sunset kindled through a cleft:</dd> +</dl> +</dd> +<dd>The hills, like giants at a hunting, lay</dd> +<dd>Chin upon hand, to see the game at bay,— +<dl> +<dd>"Now stab and end the creature - to the heft!"</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XXXIII.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>Not hear? when noise was everywhere! it tolled +<dl> +<dd>Increasing like a bell. Names in my ears</dd> +<dd>Of all the lost adventurers my peers,—</dd> +</dl> +</dd> +<dd>How such a one was strong, and such was bold,</dd> +<dd>And such was fortunate, yet each of old +<dl> +<dd>Lost, lost! one moment knelled the woe of years.</dd> +</dl> +</dd> +</dl> +<p><br /></p> +<dl> +<dd> +<dl> +<dd> +<dl> +<dd>XXXIV.</dd> +</dl> +</dd> +</dl> +</dd> +<dd>There they stood, ranged along the hillsides, met +<dl> +<dd>To view the last of me, a living frame</dd> +<dd>For one more picture! in a sheet of flame</dd> +</dl> +</dd> +<dd>I saw them and I knew them all. And yet</dd> +<dd>Dauntless the slug-horn to my lips I set, +<dl> +<dd>And blew "<i>Childe Roland to the Dark Tower came.</i>"</dd> +</dl> +</dd> +</dl> +</body> +</html> diff --git a/toolkit/modules/tests/browser/file_getSelectionDetails_inputs.html b/toolkit/modules/tests/browser/file_getSelectionDetails_inputs.html new file mode 100644 index 0000000000..2e49146785 --- /dev/null +++ b/toolkit/modules/tests/browser/file_getSelectionDetails_inputs.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html> + <body> + <input id="url-no-scheme" value="test.example.com" > + <input id="url-with-scheme" value="https://test.example.com"> + <input id="not-url" value="foo. bar"> + <input id="not-url-number" value="3.5"> + </body> +</html> diff --git a/toolkit/modules/tests/browser/file_web_channel.html b/toolkit/modules/tests/browser/file_web_channel.html new file mode 100644 index 0000000000..bcc9a13da0 --- /dev/null +++ b/toolkit/modules/tests/browser/file_web_channel.html @@ -0,0 +1,235 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>web_channel_test</title> +</head> +<body> +<script> + var IFRAME_SRC_ROOT = + "http://mochi.test:8888" + + location.pathname.replace("web_channel.html", "web_channel_iframe.html"); + + window.onload = function() { + var testName = window.location.search.replace(/^\?/, ""); + + switch (testName) { + case "generic": + test_generic(); + break; + case "twoway": + test_twoWay(); + break; + case "multichannel": + test_multichannel(); + break; + case "iframe": + test_iframe(); + break; + case "iframe_pre_redirect": + test_iframe_pre_redirect(); + break; + case "unsolicited": + test_unsolicited(); + break; + case "bubbles": + test_bubbles(); + break; + case "object": + test_object(); + break; + case "error_thrown": + test_error_thrown(); + break; + case "error_invalid_channel": + test_error_invalid_channel(); + break; + default: + throw new Error(`INVALID TEST NAME ${testName}`); + } + }; + + function test_generic() { + var event = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "generic", + message: { + something: { + nested: "hello", + }, + }, + }), + }); + + window.dispatchEvent(event); + } + + function test_twoWay() { + var firstMessage = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "twoway", + message: { + command: "one", + }, + }), + }); + + window.addEventListener("WebChannelMessageToContent", function(e) { + var secondMessage = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "twoway", + message: { + command: "two", + detail: e.detail.message, + }, + }), + }); + + if (!e.detail.message.error) { + window.dispatchEvent(secondMessage); + } + }, true); + + window.dispatchEvent(firstMessage); + } + + function test_multichannel() { + var event1 = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "wrongchannel", + message: {}, + }), + }); + + var event2 = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "multichannel", + message: {}, + }), + }); + + window.dispatchEvent(event1); + window.dispatchEvent(event2); + } + + function test_iframe() { + // Note that this message is the response to the message sent + // by the iframe! This is bad, as this page is *not* trusted. + window.addEventListener("WebChannelMessageToContent", function(e) { + // the test parent will fail if the echo message is received. + echoEventToChannel(e, "echo"); + }); + + // only attach the iframe for the iframe test to avoid + // interfering with other tests. + var iframe = document.createElement("iframe"); + iframe.setAttribute("src", IFRAME_SRC_ROOT + "?iframe"); + document.body.appendChild(iframe); + } + + function test_iframe_pre_redirect() { + var iframe = document.createElement("iframe"); + iframe.setAttribute("src", IFRAME_SRC_ROOT + "?iframe_pre_redirect"); + document.body.appendChild(iframe); + } + + function test_unsolicited() { + // echo any unsolicted events back to chrome. + window.addEventListener("WebChannelMessageToContent", function(e) { + echoEventToChannel(e, "echo"); + }, true); + } + + function test_bubbles() { + var event = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "not_a_window", + message: { + command: "start", + }, + }), + }); + + var nonWindowTarget = document.getElementById("not_a_window"); + + nonWindowTarget.addEventListener("WebChannelMessageToContent", function(e) { + echoEventToChannel(e, "not_a_window"); + }, true); + + + nonWindowTarget.dispatchEvent(event); + } + + function test_object() { + let objectMessage = new window.CustomEvent("WebChannelMessageToChrome", { + detail: { + id: "objects", + message: { type: "object" }, + }, + }); + + let stringMessage = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "objects", + message: { type: "string" }, + }), + }); + // Test fails if objectMessage is received, we send stringMessage to know + // when we should stop listening for objectMessage + window.dispatchEvent(objectMessage); + window.dispatchEvent(stringMessage); + } + + function test_error_thrown() { + var event = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "error", + message: { + command: "oops", + }, + }), + }); + + // echo the response back to chrome - chrome will check it is the + // expected error. + window.addEventListener("WebChannelMessageToContent", function(e) { + echoEventToChannel(e, "echo"); + }, true); + + window.dispatchEvent(event); + } + + function test_error_invalid_channel() { + var event = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "invalid-channel", + message: { + command: "oops", + }, + }), + }); + + // echo the response back to chrome - chrome will check it is the + // expected error. + window.addEventListener("WebChannelMessageToContent", function(e) { + echoEventToChannel(e, "echo"); + }, true); + + window.dispatchEvent(event); + } + + function echoEventToChannel(e, channelId) { + var echoedEvent = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: channelId, + message: e.detail.message, + }), + }); + + e.target.dispatchEvent(echoedEvent); + } +</script> + +<div id="not_a_window"></div> +</body> +</html> diff --git a/toolkit/modules/tests/browser/file_web_channel_iframe.html b/toolkit/modules/tests/browser/file_web_channel_iframe.html new file mode 100644 index 0000000000..101512184a --- /dev/null +++ b/toolkit/modules/tests/browser/file_web_channel_iframe.html @@ -0,0 +1,96 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>web_channel_test (iframe)</title> +</head> +<body> +<script> + var REDIRECTED_IFRAME_SRC_ROOT = "http://example.org/" + location.pathname; + + window.onload = function() { + var testName = window.location.search.replace(/^\?/, ""); + switch (testName) { + case "iframe": + test_iframe(); + break; + case "iframe_pre_redirect": + test_iframe_pre_redirect(); + break; + case "iframe_post_redirect": + test_iframe_post_redirect(); + break; + default: + throw new Error(`INVALID TEST NAME ${testName}`); + } + }; + + function test_iframe() { + var firstMessage = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "twoway", + message: { + command: "one", + }, + }), + }); + + window.addEventListener("WebChannelMessageToContent", function(e) { + var secondMessage = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "twoway", + message: { + command: "two", + detail: e.detail.message, + }, + }), + }); + + if (!e.detail.message.error) { + window.dispatchEvent(secondMessage); + } + }, true); + + window.dispatchEvent(firstMessage); + } + + + function test_iframe_pre_redirect() { + var firstMessage = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "pre_redirect", + message: { + command: "redirecting", + }, + }), + }); + window.dispatchEvent(firstMessage); + document.location = REDIRECTED_IFRAME_SRC_ROOT + "?iframe_post_redirect"; + } + + function test_iframe_post_redirect() { + window.addEventListener("WebChannelMessageToContent", function(e) { + var echoMessage = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "post_redirect", + message: e.detail.message, + }), + }); + + window.dispatchEvent(echoMessage); + }, true); + + // Let the test parent know the page has loaded and is ready to echo events + var loadedMessage = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "post_redirect", + message: { + command: "loaded", + }, + }), + }); + window.dispatchEvent(loadedMessage); + } +</script> +</body> +</html> diff --git a/toolkit/modules/tests/browser/head.js b/toolkit/modules/tests/browser/head.js new file mode 100644 index 0000000000..7c3f75b106 --- /dev/null +++ b/toolkit/modules/tests/browser/head.js @@ -0,0 +1,251 @@ +"use strict"; + +ChromeUtils.defineESModuleGetters(this, { + setTimeout: "resource://gre/modules/Timer.sys.mjs", +}); + +const kFixtureBaseURL = + "https://example.com/browser/toolkit/modules/tests/browser/"; + +function removeDupes(list) { + let j = 0; + for (let i = 1; i < list.length; i++) { + if (list[i] != list[j]) { + j++; + if (i != j) { + list[j] = list[i]; + } + } + } + list.length = j + 1; +} + +function compareLists(list1, list2, kind) { + list1.sort(); + removeDupes(list1); + list2.sort(); + removeDupes(list2); + is(String(list1), String(list2), `${kind} URLs correct`); +} + +async function promiseOpenFindbar(findbar) { + await gBrowser.getFindBar(); + findbar.onFindCommand(); + return gFindBar._startFindDeferred && gFindBar._startFindDeferred.promise; +} + +function promiseFindResult(findbar, str = null) { + let highlightFinished = false; + let findFinished = false; + return new Promise(resolve => { + let listener = { + onFindResult({ searchString }) { + if (str !== null && str != searchString) { + return; + } + findFinished = true; + if (highlightFinished) { + findbar.browser.finder.removeResultListener(listener); + resolve(); + } + }, + onHighlightFinished() { + highlightFinished = true; + if (findFinished) { + findbar.browser.finder.removeResultListener(listener); + resolve(); + } + }, + onMatchesCountResult: () => {}, + }; + findbar.browser.finder.addResultListener(listener); + }); +} + +function promiseEnterStringIntoFindField(findbar, str) { + let promise = promiseFindResult(findbar, str); + for (let i = 0; i < str.length; i++) { + let event = new KeyboardEvent("keypress", { + bubbles: true, + cancelable: true, + view: null, + keyCode: 0, + charCode: str.charCodeAt(i), + }); + findbar._findField.dispatchEvent(event); + } + return promise; +} + +function promiseTestHighlighterOutput( + browser, + word, + expectedResult, + extraTest = () => {} +) { + return SpecialPowers.spawn( + browser, + [{ word, expectedResult, extraTest: extraTest.toSource() }], + async function ({ word, expectedResult, extraTest }) { + return new Promise((resolve, reject) => { + let stubbed = {}; + let callCounts = { + insertCalls: [], + removeCalls: [], + animationCalls: [], + }; + let lastMaskNode, lastOutlineNode; + let rects = []; + + // Amount of milliseconds to wait after the last time one of our stubs + // was called. + const kTimeoutMs = 1000; + // The initial timeout may wait for a while for results to come in. + let timeout = content.setTimeout( + () => finish(false, "Timeout"), + kTimeoutMs * 5 + ); + + function finish(ok = true, message = "finished with error") { + // Restore the functions we stubbed out. + try { + content.document.insertAnonymousContent = stubbed.insert; + content.document.removeAnonymousContent = stubbed.remove; + } catch (ex) {} + stubbed = {}; + content.clearTimeout(timeout); + + if (expectedResult.rectCount !== 0) { + Assert.ok(ok, message); + } + + Assert.greaterOrEqual( + callCounts.insertCalls.length, + expectedResult.insertCalls[0], + `Min. insert calls should match for '${word}'.` + ); + Assert.lessOrEqual( + callCounts.insertCalls.length, + expectedResult.insertCalls[1], + `Max. insert calls should match for '${word}'.` + ); + Assert.greaterOrEqual( + callCounts.removeCalls.length, + expectedResult.removeCalls[0], + `Min. remove calls should match for '${word}'.` + ); + Assert.lessOrEqual( + callCounts.removeCalls.length, + expectedResult.removeCalls[1], + `Max. remove calls should match for '${word}'.` + ); + + // We reached the amount of calls we expected, so now we can check + // the amount of rects. + if (!lastMaskNode && expectedResult.rectCount !== 0) { + Assert.ok( + false, + `No mask node found, but expected ${expectedResult.rectCount} rects.` + ); + } + + Assert.equal( + rects.length, + expectedResult.rectCount, + `Amount of inserted rects should match for '${word}'.` + ); + + if ("animationCalls" in expectedResult) { + Assert.greaterOrEqual( + callCounts.animationCalls.length, + expectedResult.animationCalls[0], + `Min. animation calls should match for '${word}'.` + ); + Assert.lessOrEqual( + callCounts.animationCalls.length, + expectedResult.animationCalls[1], + `Max. animation calls should match for '${word}'.` + ); + } + + // Allow more specific assertions to be tested in `extraTest`. + // eslint-disable-next-line no-eval + extraTest = eval(extraTest); + extraTest(lastMaskNode, lastOutlineNode, rects); + + resolve(); + } + + function stubAnonymousContentNode(domNode, anonNode) { + let originals = [ + anonNode.setTextContentForElement, + anonNode.setAttributeForElement, + anonNode.removeAttributeForElement, + anonNode.setCutoutRectsForElement, + anonNode.setAnimationForElement, + ]; + anonNode.setTextContentForElement = (id, text) => { + try { + (domNode.querySelector("#" + id) || domNode).textContent = text; + } catch (ex) {} + return originals[0].call(anonNode, id, text); + }; + anonNode.setAttributeForElement = (id, attrName, attrValue) => { + try { + (domNode.querySelector("#" + id) || domNode).setAttribute( + attrName, + attrValue + ); + } catch (ex) {} + return originals[1].call(anonNode, id, attrName, attrValue); + }; + anonNode.removeAttributeForElement = (id, attrName) => { + try { + let node = domNode.querySelector("#" + id) || domNode; + if (node.hasAttribute(attrName)) { + node.removeAttribute(attrName); + } + } catch (ex) {} + return originals[2].call(anonNode, id, attrName); + }; + anonNode.setCutoutRectsForElement = (id, cutoutRects) => { + rects = cutoutRects; + return originals[3].call(anonNode, id, cutoutRects); + }; + anonNode.setAnimationForElement = (id, keyframes, options) => { + callCounts.animationCalls.push([keyframes, options]); + return originals[4].call(anonNode, id, keyframes, options); + }; + } + + // Create a function that will stub the original version and collects + // the arguments so we can check the results later. + function stub(which) { + stubbed[which] = content.document[which + "AnonymousContent"]; + let prop = which + "Calls"; + return function (node) { + callCounts[prop].push(node); + if (which == "insert") { + if (node.outerHTML.indexOf("outlineMask") > -1) { + lastMaskNode = node; + } else { + lastOutlineNode = node; + } + } + content.clearTimeout(timeout); + timeout = content.setTimeout(() => { + finish(); + }, kTimeoutMs); + let res = stubbed[which].call(content.document, node); + if (which == "insert") { + stubAnonymousContentNode(node, res); + } + return res; + }; + } + content.document.insertAnonymousContent = stub("insert"); + content.document.removeAnonymousContent = stub("remove"); + }); + } + ); +} diff --git a/toolkit/modules/tests/chrome/chrome.toml b/toolkit/modules/tests/chrome/chrome.toml new file mode 100644 index 0000000000..30d484a62b --- /dev/null +++ b/toolkit/modules/tests/chrome/chrome.toml @@ -0,0 +1,4 @@ +[DEFAULT] + +["test_bug544442_checkCert.xhtml"] +skip-if = ["verify"] diff --git a/toolkit/modules/tests/chrome/test_bug544442_checkCert.xhtml b/toolkit/modules/tests/chrome/test_bug544442_checkCert.xhtml new file mode 100644 index 0000000000..c8bf11b704 --- /dev/null +++ b/toolkit/modules/tests/chrome/test_bug544442_checkCert.xhtml @@ -0,0 +1,148 @@ +<?xml version="1.0"?> +<!-- +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +--> + +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<window title="Test CertUtils.sys.mjs checkCert - bug 340198 and bug 544442" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="testStart();"> +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + +<script type="application/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +const {CertUtils} = ChromeUtils.importESModule( + "resource://gre/modules/CertUtils.sys.mjs" +); + +function testStart() { + ok(true, "Entering testStart"); + + var request = new XMLHttpRequest(); + request.open("GET", "https://example.com/", true); + request.channel.notificationCallbacks = new CertUtils.BadCertHandler(true); + request.onerror = function(event) { testXHRError(event); }; + request.onload = function(event) { testXHRLoad(event); }; + request.send(null); +} + +function testXHRError(aEvent) { + ok(true, "Entering testXHRError - something went wrong"); + + var request = aEvent.target; + var status = 0; + try { + status = request.status; + } + catch (e) { + } + + if (status == 0) + status = request.channel.QueryInterface(Ci.nsIRequest).status; + + ok(false, "XHR onerror called: " + status); + + SimpleTest.finish(); +} + +function getCheckCertResult(aChannel, aAllowNonBuiltIn, aCerts) { + try { + CertUtils.checkCert(aChannel, aAllowNonBuiltIn, aCerts); + } + catch (e) { + return e.result; + } + return Cr.NS_OK; +} + +function testXHRLoad(aEvent) { + ok(true, "Entering testXHRLoad"); + + var channel = aEvent.target.channel; + + var certs = null; + is(getCheckCertResult(channel, false, certs), Cr.NS_ERROR_ABORT, + "checkCert should throw NS_ERROR_ABORT when the certificate attributes " + + "array passed to checkCert is null and the certificate is not builtin"); + + is(getCheckCertResult(channel, true, certs), Cr.NS_OK, + "checkCert should not throw when the certificate attributes array " + + "passed to checkCert is null and builtin certificates aren't enforced"); + + certs = [ { invalidAttribute: "Invalid attribute" } ]; + is(getCheckCertResult(channel, false, certs), Cr.NS_ERROR_ILLEGAL_VALUE, + "checkCert should throw NS_ERROR_ILLEGAL_VALUE when the certificate " + + "attributes array passed to checkCert has an element that has an " + + "attribute that does not exist on the certificate"); + + certs = [ { issuerName: "Incorrect issuerName" } ]; + is(getCheckCertResult(channel, false, certs), Cr.NS_ERROR_ILLEGAL_VALUE, + "checkCert should throw NS_ERROR_ILLEGAL_VALUE when the certificate " + + "attributes array passed to checkCert has an element that has an " + + "issuerName that is not the same as the certificate's"); + + var cert = channel.securityInfo.serverCert; + + certs = [ { issuerName: cert.issuerName, + commonName: cert.commonName } ]; + is(getCheckCertResult(channel, false, certs), Cr.NS_ERROR_ABORT, + "checkCert should throw NS_ERROR_ABORT when the certificate attributes " + + "array passed to checkCert has a single element that has the same " + + "issuerName and commonName as the certificate's and the certificate is " + + "not builtin"); + + is(getCheckCertResult(channel, true, certs), Cr.NS_OK, + "checkCert should not throw when the certificate attributes array " + + "passed to checkCert has a single element that has the same issuerName " + + "and commonName as the certificate's and and builtin certificates " + + "aren't enforced"); + + certs = [ { issuerName: "Incorrect issuerName", + invalidAttribute: "Invalid attribute" }, + { issuerName: cert.issuerName, + commonName: "Invalid Common Name" }, + { issuerName: cert.issuerName, + commonName: cert.commonName } ]; + is(getCheckCertResult(channel, false, certs), Cr.NS_ERROR_ABORT, + "checkCert should throw NS_ERROR_ABORT when the certificate attributes " + + "array passed to checkCert has an element that has the same issuerName " + + "and commonName as the certificate's and the certificate is not builtin"); + + is(getCheckCertResult(channel, true, certs), Cr.NS_OK, + "checkCert should not throw when the certificate attributes array " + + "passed to checkCert has an element that has the same issuerName and " + + "commonName as the certificate's and builtin certificates aren't enforced"); + + var mockChannel = { originalURI: SpecialPowers.Services.io.newURI("http://example.com/") }; + + certs = [ ]; + is(getCheckCertResult(mockChannel, false, certs), Cr.NS_ERROR_UNEXPECTED, + "checkCert should throw NS_ERROR_UNEXPECTED when the certificate " + + "attributes array passed to checkCert is not null and the channel's " + + "originalURI is not https"); + + certs = null; + is(getCheckCertResult(mockChannel, false, certs), Cr.NS_OK, + "checkCert should not throw when the certificate attributes object " + + "passed to checkCert is null and the the channel's originalURI is not " + + "https"); + + SimpleTest.finish(); +} + +]]> +</script> + +<body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"></pre> +</body> +</window> diff --git a/toolkit/modules/tests/modules/MockDocument.sys.mjs b/toolkit/modules/tests/modules/MockDocument.sys.mjs new file mode 100644 index 0000000000..163beb8450 --- /dev/null +++ b/toolkit/modules/tests/modules/MockDocument.sys.mjs @@ -0,0 +1,101 @@ +/** + * Provides infrastructure for tests that would require mock document. + */ + +import { NetUtil } from "resource://gre/modules/NetUtil.sys.mjs"; + +export const MockDocument = { + /** + * Create a document for the given URL containing the given HTML with the ownerDocument of all <form>s having a mocked location. + */ + createTestDocument( + aDocumentURL, + aContent = "<form>", + aType = "text/html", + useSystemPrincipal = false + ) { + let parser = new DOMParser(); + let parsedDoc; + if (useSystemPrincipal) { + parsedDoc = parser.parseFromSafeString(aContent, aType); + } else { + parsedDoc = parser.parseFromString(aContent, aType); + } + + // Assign ownerGlobal to documentElement as well for the form-less + // inputs treating it as rootElement. + this.mockOwnerGlobalProperty(parsedDoc.documentElement); + + for (let form of parsedDoc.forms) { + this.mockOwnerDocumentProperty(form, parsedDoc, aDocumentURL); + this.mockOwnerGlobalProperty(form); + for (let field of form.elements) { + this.mockOwnerGlobalProperty(field); + } + } + return parsedDoc; + }, + + mockOwnerDocumentProperty(aElement, aDoc, aURL) { + // Mock the document.location object so we can unit test without a frame. We use a proxy + // instead of just assigning to the property since it's not configurable or writable. + let document = new Proxy(aDoc, { + get(target, property, receiver) { + // document.location is normally null when a document is outside of a "browsing context". + // See https://html.spec.whatwg.org/#the-location-interface + if (property == "location") { + return new URL(aURL); + } + return target[property]; + }, + }); + + // Assign element.ownerDocument to the proxy so document.location works. + Object.defineProperty(aElement, "ownerDocument", { + value: document, + }); + }, + + mockOwnerGlobalProperty(aElement) { + Object.defineProperty(aElement, "ownerGlobal", { + value: { + windowUtils: { + addManuallyManagedState() {}, + removeManuallyManagedState() {}, + }, + UIEvent: Event, + Event, + }, + configurable: true, + }); + }, + + mockNodePrincipalProperty(aElement, aURL) { + Object.defineProperty(aElement, "nodePrincipal", { + value: Services.scriptSecurityManager.createContentPrincipal( + Services.io.newURI(aURL), + {} + ), + }); + }, + + mockBrowsingContextProperty(aElement, aBC) { + Object.defineProperty(aElement, "browsingContext", { + value: aBC, + }); + }, + + createTestDocumentFromFile(aDocumentURL, aFile) { + let fileStream = Cc[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Ci.nsIFileInputStream); + fileStream.init(aFile, -1, -1, 0); + + let data = NetUtil.readInputStreamToString( + fileStream, + fileStream.available() + ); + + return this.createTestDocument(aDocumentURL, data); + }, +}; diff --git a/toolkit/modules/tests/modules/OSKeyStoreTestUtils.sys.mjs b/toolkit/modules/tests/modules/OSKeyStoreTestUtils.sys.mjs new file mode 100644 index 0000000000..697905da87 --- /dev/null +++ b/toolkit/modules/tests/modules/OSKeyStoreTestUtils.sys.mjs @@ -0,0 +1,57 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +import { OSKeyStore } from "resource://gre/modules/OSKeyStore.sys.mjs"; +import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; + +const lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs", +}); +import { TestUtils } from "resource://testing-common/TestUtils.sys.mjs"; + +// Debug builds will be treated as "official" builds for the purposes of the automated testing for behavior of OSKeyStore.ensureLoggedIn +// We want to ensure that we catch test failures that would only otherwise show up in official builds +const isCanaryBuildForOSKeyStore = AppConstants.DEBUG; + +export var OSKeyStoreTestUtils = { + TEST_ONLY_REAUTH: "toolkit.osKeyStore.unofficialBuildOnlyLogin", + + setup() { + this.ORIGINAL_STORE_LABEL = OSKeyStore.STORE_LABEL; + OSKeyStore.STORE_LABEL = "test-" + Math.random().toString(36).substr(2); + }, + + async cleanup() { + await OSKeyStore.cleanup(); + OSKeyStore.STORE_LABEL = this.ORIGINAL_STORE_LABEL; + }, + + /** + * Checks whether or not the test can be run by bypassing + * the OS login dialog. We do not want the user to be able to + * do so with in official builds. + * @returns {boolean} True if the test can be performed. + */ + canTestOSKeyStoreLogin() { + return ( + lazy.UpdateUtils.getUpdateChannel(false) == "default" && + !isCanaryBuildForOSKeyStore + ); + }, + + // Wait for the observer message that simulates login success of failure. + async waitForOSKeyStoreLogin(login = false) { + const str = login ? "pass" : "cancel"; + + let prevValue = Services.prefs.getStringPref(this.TEST_ONLY_REAUTH, ""); + Services.prefs.setStringPref(this.TEST_ONLY_REAUTH, str); + + await TestUtils.topicObserved( + "oskeystore-testonly-reauth", + (subject, data) => data == str + ); + + Services.prefs.setStringPref(this.TEST_ONLY_REAUTH, prevValue); + }, +}; diff --git a/toolkit/modules/tests/modules/PromiseTestUtils.sys.mjs b/toolkit/modules/tests/modules/PromiseTestUtils.sys.mjs new file mode 100644 index 0000000000..2286ac03da --- /dev/null +++ b/toolkit/modules/tests/modules/PromiseTestUtils.sys.mjs @@ -0,0 +1,293 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Detects and reports unhandled rejections during test runs. Test harnesses + * will fail tests in this case, unless the test explicitly allows rejections. + */ + +import { Assert } from "resource://testing-common/Assert.sys.mjs"; + +export var PromiseTestUtils = { + /** + * Array of objects containing the details of the Promise rejections that are + * currently left uncaught. This includes DOM Promise and Promise.jsm. When + * rejections in DOM Promises are consumed, they are removed from this list. + * + * The objects contain at least the following properties: + * { + * message: The error message associated with the rejection, if any. + * date: Date object indicating when the rejection was observed. + * id: For DOM Promise only, the Promise ID from PromiseDebugging. This is + * only used for tracking and should not be checked by the callers. + * stack: nsIStackFrame, SavedFrame, or string indicating the stack at the + * time the rejection was triggered. May also be null if the + * rejection was triggered while a script was on the stack. + * } + */ + _rejections: [], + + /** + * When an uncaught rejection is detected, it is ignored if one of the + * functions in this array returns true when called with the rejection details + * as its only argument. When a function matches an expected rejection, it is + * then removed from the array. + */ + _rejectionIgnoreFns: [], + + /** + * If any of the functions in this array returns true when called with the + * rejection details as its only argument, the rejection is ignored. This + * happens after the "_rejectionIgnoreFns" array is processed. + */ + _globalRejectionIgnoreFns: [], + + /** + * Called only by the test infrastructure, registers the rejection observers. + * + * This should be called only once, and a matching "uninit" call must be made + * or the tests will crash on shutdown. + */ + init() { + if (this._initialized) { + console.error("This object was already initialized."); + return; + } + + PromiseDebugging.addUncaughtRejectionObserver(this); + + this._initialized = true; + }, + _initialized: false, + + /** + * Called only by the test infrastructure, unregisters the observers. + */ + uninit() { + if (!this._initialized) { + return; + } + + PromiseDebugging.removeUncaughtRejectionObserver(this); + + this._initialized = false; + }, + + /** + * Called only by the test infrastructure, collect all the + * JavaScript Developer Errors that have been thrown and + * treat them as uncaught promise rejections. + */ + collectJSDevErrors() { + let recentJSDevError = ChromeUtils.recentJSDevError; + if (!recentJSDevError) { + // Either `recentJSDevError` is not implemented in this version or there is no recent JS dev error. + return; + } + ChromeUtils.clearRecentJSDevError(); + Promise.reject( + `${recentJSDevError.message}\n${recentJSDevError.stack}\ndetected at\n` + ); + }, + + /** + * Called only by the test infrastructure, spins the event loop until the + * messages for pending DOM Promise rejections have been processed. + */ + ensureDOMPromiseRejectionsProcessed() { + let observed = false; + let observer = { + onLeftUncaught: promise => { + if ( + PromiseDebugging.getState(promise).reason === + this._ensureDOMPromiseRejectionsProcessedReason + ) { + observed = true; + return true; + } + return false; + }, + onConsumed() {}, + }; + + PromiseDebugging.addUncaughtRejectionObserver(observer); + Promise.reject(this._ensureDOMPromiseRejectionsProcessedReason); + Services.tm.spinEventLoopUntil( + "Test(PromiseTestUtils.sys.mjs:ensureDOMPromiseRejectionsProcessed)", + () => observed + ); + PromiseDebugging.removeUncaughtRejectionObserver(observer); + }, + _ensureDOMPromiseRejectionsProcessedReason: {}, + + /** + * Called only by the tests for PromiseDebugging.addUncaughtRejectionObserver + * and for JSMPromise.Debugging, disables the observers in this module. + */ + disableUncaughtRejectionObserverForSelfTest() { + this.uninit(); + }, + + /** + * Called by tests with uncaught rejections to disable the observers in this + * module. For new tests where uncaught rejections are expected, you should + * use the more granular expectUncaughtRejection function instead. + */ + thisTestLeaksUncaughtRejectionsAndShouldBeFixed() { + this.uninit(); + }, + + // UncaughtRejectionObserver + onLeftUncaught(promise) { + let message = "(Unable to convert rejection reason to string.)"; + let reason = null; + try { + reason = PromiseDebugging.getState(promise).reason; + if (reason === this._ensureDOMPromiseRejectionsProcessedReason) { + // Ignore the special promise for ensureDOMPromiseRejectionsProcessed. + return; + } + message = reason?.message || "" + reason; + } catch (ex) {} + + // We should convert the rejection stack to a string immediately. This is + // because the object might not be available when we report the rejection + // later, if the error occurred in a context that has been unloaded. + let stack = "(Unable to convert rejection stack to string.)"; + try { + // In some cases, the rejection stack from `PromiseDebugging` may be null. + // If the rejection reason was an Error object, use its `stack` to recover + // a meaningful value. + stack = + "" + + ((reason && reason.stack) || + PromiseDebugging.getRejectionStack(promise) || + "(No stack available.)"); + } catch (ex) {} + + // Always add a newline at the end of the stack for consistent reporting. + // This is already present when the stack is provided by PromiseDebugging. + if (!stack.endsWith("\n")) { + stack += "\n"; + } + + // It's important that we don't store any reference to the provided Promise + // object or its value after this function returns in order to avoid leaks. + this._rejections.push({ + id: PromiseDebugging.getPromiseID(promise), + message, + date: new Date(), + stack, + }); + }, + + // UncaughtRejectionObserver + onConsumed(promise) { + // We don't expect that many unhandled rejections will appear at the same + // time, so the algorithm doesn't need to be optimized for that case. + let id = PromiseDebugging.getPromiseID(promise); + let index = this._rejections.findIndex(rejection => rejection.id == id); + // If we get a consumption notification for a rejection that was left + // uncaught before this module was initialized, we can safely ignore it. + if (index != -1) { + this._rejections.splice(index, 1); + } + }, + + /** + * Informs the test suite that the test code will generate a Promise rejection + * that will still be unhandled when the test file terminates. + * + * This method must be called once for each instance of Promise that is + * expected to be uncaught, even if the rejection reason is the same for each + * instance. + * + * If the expected rejection does not occur, the test will fail. + * + * @param regExpOrCheckFn + * This can either be a regular expression that should match the error + * message of the rejection, or a check function that is invoked with + * the rejection details object as its first argument. + */ + expectUncaughtRejection(regExpOrCheckFn) { + let checkFn = !("test" in regExpOrCheckFn) + ? regExpOrCheckFn + : rejection => regExpOrCheckFn.test(rejection.message); + this._rejectionIgnoreFns.push(checkFn); + }, + + /** + * Allows an entire class of Promise rejections. Usage of this function + * should be kept to a minimum because it has a broad scope and doesn't + * prevent new unhandled rejections of this class from being added. + * + * @param regExp + * This should match the error message of the rejection. + */ + allowMatchingRejectionsGlobally(regExp) { + this._globalRejectionIgnoreFns.push(rejection => + regExp.test(rejection.message) + ); + }, + + /** + * Fails the test if there are any uncaught rejections at this time that have + * not been explicitly allowed using expectUncaughtRejection. + * + * Depending on the configuration of the test suite, this function might only + * report the details of the first uncaught rejection that was generated. + * + * This is called by the test suite at the end of each test function. + */ + assertNoUncaughtRejections() { + // If there is any uncaught rejection left at this point, the test fails. + while (this._rejections.length) { + let rejection = this._rejections.shift(); + + // If one of the ignore functions matches, ignore the rejection, then + // remove the function so that each function only matches one rejection. + let index = this._rejectionIgnoreFns.findIndex(f => f(rejection)); + if (index != -1) { + this._rejectionIgnoreFns.splice(index, 1); + continue; + } + + // Check the global ignore functions. + if (this._globalRejectionIgnoreFns.some(fn => fn(rejection))) { + continue; + } + + // Report the error. This operation can throw an exception, depending on + // the configuration of the test suite that handles the assertion. The + // first line of the message, including the latest call on the stack, is + // used to identify related test failures. To keep the first line similar + // between executions, we place the time-dependent rejection date on its + // own line, after all the other stack lines. + Assert.ok( + false, + `A promise chain failed to handle a rejection:` + + ` ${rejection.message} - stack: ${rejection.stack}` + + `Rejection date: ${rejection.date}` + ); + } + }, + + /** + * Fails the test if any rejection indicated by expectUncaughtRejection has + * not yet been reported at this time. + * + * This is called by the test suite at the end of each test file. + */ + assertNoMoreExpectedRejections() { + // Only log this condition is there is a failure. + if (this._rejectionIgnoreFns.length) { + Assert.equal( + this._rejectionIgnoreFns.length, + 0, + "Unable to find a rejection expected by expectUncaughtRejection." + ); + } + // Reset the list of expected rejections in case the test suite continues. + this._rejectionIgnoreFns = []; + }, +}; diff --git a/toolkit/modules/tests/xpcshell/RegionTestUtils.sys.mjs b/toolkit/modules/tests/xpcshell/RegionTestUtils.sys.mjs new file mode 100644 index 0000000000..6cb238781a --- /dev/null +++ b/toolkit/modules/tests/xpcshell/RegionTestUtils.sys.mjs @@ -0,0 +1,10 @@ +export const RegionTestUtils = Object.freeze({ + REGION_URL_PREF: "browser.region.network.url", + + setNetworkRegion(region) { + Services.prefs.setCharPref( + this.REGION_URL_PREF, + `data:application/json,{"country_code": "${region}"}` + ); + }, +}); diff --git a/toolkit/modules/tests/xpcshell/TestIntegration.sys.mjs b/toolkit/modules/tests/xpcshell/TestIntegration.sys.mjs new file mode 100644 index 0000000000..2379e4ead7 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/TestIntegration.sys.mjs @@ -0,0 +1,32 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Internal module used to test the generation of Integration.sys.mjs getters. + */ + +export var TestIntegration = { + value: "value", + + get valueFromThis() { + return this.value; + }, + + get property() { + return this._property; + }, + + set property(value) { + this._property = value; + }, + + method(argument) { + this.methodArgument = argument; + return "method" + argument; + }, + + async asyncMethod(argument) { + this.asyncMethodArgument = argument; + return "asyncMethod" + argument; + }, +}; diff --git a/toolkit/modules/tests/xpcshell/chromeappsstore.sqlite b/toolkit/modules/tests/xpcshell/chromeappsstore.sqlite Binary files differnew file mode 100644 index 0000000000..15d309df50 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/chromeappsstore.sqlite diff --git a/toolkit/modules/tests/xpcshell/corrupt.sqlite b/toolkit/modules/tests/xpcshell/corrupt.sqlite new file mode 100644 index 0000000000..bb5f5dc3a6 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/corrupt.sqlite @@ -0,0 +1 @@ +CORRUPTED_SQLITE_FILE_CONTENTS diff --git a/toolkit/modules/tests/xpcshell/head.js b/toolkit/modules/tests/xpcshell/head.js new file mode 100644 index 0000000000..6d133495f6 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/head.js @@ -0,0 +1,78 @@ +var { NewTabUtils } = ChromeUtils.importESModule( + "resource://gre/modules/NewTabUtils.sys.mjs" +); +Cu.importGlobalProperties(["btoa"]); + +ChromeUtils.defineESModuleGetters(this, { + PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs", + PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs", + Sqlite: "resource://gre/modules/Sqlite.sys.mjs", +}); + +const PREF_NEWTAB_ENHANCED = "browser.newtabpage.enhanced"; +const SEARCH_SHORTCUTS_EXPERIMENT_PREF = + "browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts"; + +// use time at the start of the tests, chnaging it inside timeDaysAgo() +// may cause tiny time differences, which break expected sql ordering +const TIME_NOW = new Date().getTime(); + +Services.prefs.setBoolPref(PREF_NEWTAB_ENHANCED, true); +Services.prefs.setBoolPref(SEARCH_SHORTCUTS_EXPERIMENT_PREF, false); + +do_get_profile(); + +// utility function to compute past timestamp in microseconds +function timeDaysAgo(numDays) { + return (TIME_NOW - numDays * 24 * 60 * 60 * 1000) * 1000; +} + +// tests that timestamp falls within 10 days of now +function isVisitDateOK(timestampMS) { + let range = 10 * 24 * 60 * 60 * 1000; + return Math.abs(Date.now() - timestampMS) < range; +} + +// a set up function to prep the activity stream provider +function setUpActivityStreamTest() { + return (async function () { + await PlacesUtils.history.clear(); + await PlacesUtils.bookmarks.eraseEverything(); + let faviconExpiredPromise = new Promise(resolve => { + Services.obs.addObserver(resolve, "places-favicons-expired"); + }); + PlacesUtils.favicons.expireAllFavicons(); + await faviconExpiredPromise; + })(); +} + +function do_check_links(actualLinks, expectedLinks) { + Assert.ok(Array.isArray(actualLinks)); + Assert.equal(actualLinks.length, expectedLinks.length); + for (let i = 0; i < expectedLinks.length; i++) { + let expected = expectedLinks[i]; + let actual = actualLinks[i]; + Assert.equal(actual.url, expected.url); + Assert.equal(actual.title, expected.title); + Assert.equal(actual.frecency, expected.frecency); + Assert.equal(actual.lastVisitDate, expected.lastVisitDate); + } +} + +function makeLinks(frecRangeStart, frecRangeEnd, step) { + let links = []; + // Remember, links are ordered by frecency descending. + for (let i = frecRangeEnd; i > frecRangeStart; i -= step) { + links.push(makeLink(i)); + } + return links; +} + +function makeLink(frecency) { + return { + url: "http://example" + frecency + ".com/", + title: "My frecency is " + frecency, + frecency, + lastVisitDate: 0, + }; +} diff --git a/toolkit/modules/tests/xpcshell/propertyLists/bug710259_propertyListBinary.plist b/toolkit/modules/tests/xpcshell/propertyLists/bug710259_propertyListBinary.plist Binary files differnew file mode 100644 index 0000000000..5888c9c9c5 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/propertyLists/bug710259_propertyListBinary.plist diff --git a/toolkit/modules/tests/xpcshell/propertyLists/bug710259_propertyListXML.plist b/toolkit/modules/tests/xpcshell/propertyLists/bug710259_propertyListXML.plist new file mode 100644 index 0000000000..9b6decc1e6 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/propertyLists/bug710259_propertyListXML.plist @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Boolean</key> + <false/> + <key>Array</key> + <array> + <string>abc</string> + <string>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</string> + <string>אאא</string> + <string>אאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאא</string> + <string>𐀀𐀀𐀀</string> + <date>2011-12-31T11:15:23Z</date> + <data>MjAxMS0xMi0zMVQxMToxNTozM1o=</data> + <dict> + <key>Negative Number</key> + <integer>-400</integer> + <key>Real Number</key> + <real>2.71828183</real> + <key>Big Int</key> + <integer>9007199254740993</integer> + <key>Negative Big Int</key> + <integer>-9007199254740993</integer> + </dict> + </array> +</dict> +</plist> diff --git a/toolkit/modules/tests/xpcshell/regions/mls-lookup-results.csv b/toolkit/modules/tests/xpcshell/regions/mls-lookup-results.csv new file mode 100644 index 0000000000..fc322591c0 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/regions/mls-lookup-results.csv @@ -0,0 +1,500 @@ +37.9654785,23.7081866,GR +37.9876014,23.6589032,GR +37.9883014,23.6609299,GR +37.9598175,23.6557615,GR +37.9704477,23.6560787,GR +37.9813344,23.6525741,GR +37.9826496,23.6426411,GR +37.9824003,23.6456122,GR +37.9616703,23.6501534,GR +37.9740817,23.6460451,GR +37.9781795,23.6465273,GR +37.9847711,23.6515311,GR +37.9853856,23.6682653,GR +37.9845841,23.6515267,GR +37.9617926,23.6485106,GR +37.9850971,23.6486791,GR +37.9811330,23.6544934,GR +37.9357839,23.7626945,GR +37.9786312,23.6514977,GR +37.9903302,23.6543755,GR +38.0568153,23.7752222,GR +38.0560672,23.7788794,GR +38.0582519,23.7715342,GR +38.0593758,23.7742029,GR +38.0531587,23.7818724,GR +38.0572369,23.7728138,GR +48.5973431,1.6780180,FR +48.5979913,1.7034801,FR +48.5919751,1.6978640,FR +48.6000849,1.6768291,FR +48.6034887,1.6999379,FR +48.5978051,1.6638092,FR +48.6376257,1.6741111,FR +49.4265468,1.5037056,FR +46.8486738,0.5214987,FR +46.8490996,0.5216334,FR +47.5857071,3.5711815,FR +47.6538292,3.5582079,FR +47.2327582,4.5300463,FR +47.2826665,4.5084869,FR +47.2540711,4.5699483,FR +47.2929411,4.5066057,FR +47.2565054,4.5703755,FR +45.1381125,3.5230710,FR +48.5991978,1.7043685,FR +48.6041437,1.7070556,FR +48.6034470,1.6975703,FR +49.1154164,6.3027416,FR +48.1019339,-1.6840807,FR +41.9269887,8.7486943,FR +41.9270681,8.7486366,FR +47.3164101,4.5255806,FR +47.3115723,4.5108329,FR +43.2100894,2.3624087,FR +47.6395718,6.8587501,FR +48.4183452,5.7300516,FR +49.3983185,1.5394318,FR +47.2266215,-1.5500636,FR +47.9237570,1.9526967,FR +47.9237570,1.9526967,FR +47.9237570,1.9526967,FR +47.9237570,1.9526967,FR +47.9237570,1.9526967,FR +47.9237570,1.9526967,FR +47.9237570,1.9526967,FR +42.7032269,9.4515596,FR +48.2170735,0.5135551,FR +49.6455599,-1.6211701,FR +49.6448199,-1.6216882,FR +49.6453807,-1.6213322,FR +49.6449471,-1.6216881,FR +50.6447423,3.1424029,FR +48.8430639,2.6417044,FR +49.0986507,6.2151161,FR +48.4023465,-4.5346028,FR +43.2833326,5.3986220,FR +43.1292588,5.9229219,FR +43.5765008,7.0563599,FR +43.5130386,5.2033540,FR +45.1913943,5.7349647,FR +43.6029865,3.8681286,FR +43.6031425,3.8673954,FR +48.7049330,-2.3680898,FR +48.0964136,-1.6801331,FR +48.0964172,-1.6801749,FR +48.0973063,-1.6803880,FR +47.2273876,-1.5535405,FR +43.5797555,1.4567384,FR +43.6089839,2.2390168,FR +43.6087890,1.4453330,FR +43.5798093,1.4567630,FR +43.6087890,1.4453330,FR +43.6087890,1.4453330,FR +43.6087890,1.4453330,FR +43.5796695,1.4566833,FR +43.6087890,1.4453330,FR +44.8371933,-0.5896829,FR +44.8381201,-0.5912532,FR +40.9770821,-4.1736877,ES +45.8348018,9.0233159,IT +45.8326549,9.0236659,IT +45.8362360,9.0186441,IT +45.8373194,9.0187067,IT +45.8331208,9.0225575,IT +45.8397212,9.0135366,IT +44.5479481,11.3678908,IT +44.5873568,11.3489182,IT +45.3735420,9.5964906,IT +45.4833792,10.0573170,IT +45.4420047,10.0874896,IT +45.4723775,10.0533189,IT +45.5055836,9.9784062,IT +45.4481467,9.7000695,IT +45.4245657,9.7728751,IT +45.3155705,9.7489700,IT +45.3926028,9.7460408,IT +45.3157654,9.7478592,IT +45.4725430,10.0714240,IT +45.3132326,9.7674537,IT +45.3548044,9.6222794,IT +45.4377996,9.7453548,IT +45.4595720,10.0546804,IT +45.3500208,9.7631035,IT +45.3173795,9.7603146,IT +45.3453084,9.7636403,IT +45.3578523,9.6287392,IT +45.3539046,9.6360777,IT +45.4776074,9.9254178,IT +45.4721612,9.9139425,IT +45.3108082,9.7663305,IT +45.5083586,9.9082672,IT +45.5029862,9.9087591,IT +45.4922637,10.1315414,IT +45.5411149,10.0441694,IT +45.5727084,10.0070523,IT +45.5749543,10.0174579,IT +45.5670928,10.0009175,IT +45.5144718,9.9228841,IT +45.5358410,9.9810805,IT +45.5677312,10.0299900,IT +45.5243032,10.0565833,IT +45.4856676,10.0434765,IT +45.5250278,10.0112389,IT +45.5614403,10.0159397,IT +45.5193783,9.9411184,IT +45.5187910,9.9094003,IT +45.5293263,10.0646735,IT +45.5140062,9.8868511,IT +45.5760749,10.0052500,IT +45.5429253,10.0039221,IT +45.5878450,9.9789695,IT +45.5246242,10.0136946,IT +45.5338352,10.0317553,IT +45.4499772,8.0183022,IT +37.5970300,15.1306400,IT +45.6417337,11.3802632,IT +45.6410320,11.3786680,IT +42.0605166,14.3474621,IT +42.0605111,14.3474605,IT +42.0605119,14.3474605,IT +42.0605127,14.3474606,IT +42.0605150,14.3474617,IT +46.7742916,23.5617349,RO +46.7725026,23.5963148,RO +46.7689745,23.5895185,RO +46.7704146,23.5958079,RO +46.7669028,23.5999428,RO +46.7642347,23.6039998,RO +46.7690645,23.5960537,RO +46.7620826,23.5996690,RO +46.7698253,23.5926115,RO +46.7706908,23.5957115,RO +46.7668246,23.6015324,RO +46.7687034,23.6010042,RO +46.7697323,23.5928183,RO +46.7622597,23.6052138,RO +46.7687054,23.5934761,RO +46.7724589,23.5887592,RO +46.7703869,23.5909540,RO +46.7595352,23.5891342,RO +46.7685949,23.5960166,RO +46.7684256,23.5958439,RO +46.7640843,23.6026524,RO +46.7704458,23.5936909,RO +46.7691064,23.5898320,RO +46.7694298,23.5896474,RO +46.7639577,23.6041193,RO +46.6240787,7.6877831,CH +45.8358954,9.0300171,IT +45.8320353,9.0288033,IT +49.7190898,18.0985198,CZ +49.6599258,18.1238834,CZ +49.6865179,18.0989673,CZ +49.6752888,18.1157805,CZ +49.6916446,18.0817774,CZ +48.2240460,16.3195867,AT +53.9960556,-0.9419033,GB +53.5081940,-1.6548512,GB +51.2056089,0.7516555,GB +51.4577140,-2.6276104,GB +51.3231576,-0.1307278,GB +51.2814035,-0.1602836,GB +51.3137381,-0.1409043,GB +55.8950849,-4.3004664,GB +55.8927568,-4.3009616,GB +53.2482592,-2.7927989,GB +53.2480516,-2.7930939,GB +53.2480505,-2.7930866,GB +52.8339910,-2.0582507,GB +52.8340441,-2.0581895,GB +52.2933008,-1.7794157,GB +52.2937728,-1.7794232,GB +52.8341528,-2.0582435,GB +51.2380415,-0.3224204,GB +51.2160149,-0.2881249,GB +51.2551639,-0.3230201,GB +51.2698820,-0.2825397,GB +51.2430078,-0.3229106,GB +51.9022231,-2.1206788,GB +51.9022949,-2.1207004,GB +51.2743289,-0.2100585,GB +51.2771481,-0.2577880,GB +51.2936440,-0.1532257,GB +51.2624205,-0.2015628,GB +51.2943867,-0.2165195,GB +51.2680425,-0.2428891,GB +51.2998277,-0.1693038,GB +51.2842670,-0.1546993,GB +51.2592701,-0.2080126,GB +51.2904407,-0.1559613,GB +51.3765628,-0.0923936,GB +51.3730049,-0.0974361,GB +51.3937050,-0.0764555,GB +51.3819882,-0.0831766,GB +51.3767607,-0.0882797,GB +51.3928943,-0.0711211,GB +51.3787071,-0.0860610,GB +51.3772349,-0.0871617,GB +51.3773889,-0.0858795,GB +51.5360635,-0.5233994,GB +50.9417129,-1.3788053,GB +50.9454652,-1.3732799,GB +51.3627875,-0.3073436,GB +52.6939289,-1.0657263,GB +50.7073760,-1.9458826,GB +50.7084994,-1.9467443,GB +53.3596385,-3.0035438,GB +53.3688629,-3.0212454,GB +53.3701021,-3.0218961,GB +51.4939779,-2.5478536,GB +51.5109748,-2.5623952,GB +51.5121917,-2.5622841,GB +51.8278176,-4.0091990,GB +51.8275716,-4.0101415,GB +51.5034849,-0.5876767,GB +51.4479497,-0.0492869,GB +51.4420413,-0.0500542,GB +53.2502865,-0.5354620,GB +51.2348539,-1.1165770,GB +51.2470841,-1.1297041,GB +51.2348691,-1.1165816,GB +51.2578538,-1.1384798,GB +51.2474629,-1.1274306,GB +51.2375313,-1.1191097,GB +51.2349519,-1.1166879,GB +51.2348950,-1.1165464,GB +51.2364890,-1.1182415,GB +50.9467441,-1.3658337,GB +50.9117812,-1.4280019,GB +50.9476210,-1.3658164,GB +51.6357538,-3.0371659,GB +51.6418362,-3.0629987,GB +51.6417450,-3.0639734,GB +51.1651151,-2.8722392,GB +51.1544596,-2.8926268,GB +51.2529658,-2.7512775,GB +51.1545397,-2.8927831,GB +51.1546218,-2.8929752,GB +51.2535910,-2.7485990,GB +51.1544521,-2.8926166,GB +51.4359710,0.2107303,GB +50.9007803,-1.4431984,GB +50.9023297,-1.4360130,GB +50.9157854,-1.3477610,GB +51.8656965,-0.4161008,GB +51.8657201,-0.4160218,GB +51.8656830,-0.4161138,GB +53.5414138,-0.0966635,GB +53.5412154,-0.0972191,GB +54.6694355,-5.5770995,GB +54.6518132,-5.5452213,GB +54.6886298,-5.5922598,GB +50.8457418,-0.1938097,GB +53.7673672,-0.3887516,GB +53.7230059,-0.5550714,GB +53.7231636,-0.5546532,GB +51.5345068,0.7860384,GB +51.5308567,0.7900960,GB +52.5813777,-0.2446999,GB +52.5657573,-0.2232862,GB +52.5448875,-0.2438984,GB +52.5840142,-0.2194286,GB +52.5669174,-0.1816896,GB +52.5665933,-0.2333894,GB +52.5730291,-0.2432356,GB +53.9598055,-1.0497069,GB +56.3408188,10.4627373,DK +56.3406040,10.4514480,DK +56.3441869,10.4682885,DK +56.3422631,10.4549651,DK +56.3426671,10.4520960,DK +62.2462240,24.7863090,FI +62.1876323,25.1076588,FI +62.2498016,25.1234319,FI +62.2386364,25.0392926,FI +55.6357467,37.3606058,RU +59.8555504,30.2025745,RU +56.2473540,43.9634419,RU +56.2399865,43.9656652,RU +56.2463058,43.9685674,RU +56.2414284,43.9632855,RU +56.2472682,43.9591466,RU +56.2944327,43.9693197,RU +56.2597394,43.9567827,RU +56.2697137,43.9319985,RU +56.2513515,43.9574786,RU +56.2552280,43.9740923,RU +56.3100700,43.9509601,RU +56.3094469,43.9515370,RU +56.2840841,43.9583041,RU +56.3095475,43.9524719,RU +56.2837737,43.9417001,RU +56.2596294,43.9731528,RU +56.2511321,43.9657140,RU +56.2598029,43.9665324,RU +56.2562407,43.9738197,RU +56.2616926,43.9724285,RU +56.2495757,43.9680569,RU +56.2563151,43.9856537,RU +56.2567896,43.9827472,RU +56.2869366,43.9328865,RU +56.3097153,43.9504258,RU +56.2387911,43.9627503,RU +56.2393599,43.9653458,RU +56.2394147,43.9618760,RU +60.1461054,30.4828024,RU +56.2401727,43.9674283,RU +56.2394904,43.9664688,RU +56.2395519,43.9648461,RU +56.2394565,43.9668042,RU +56.2461357,43.9552378,RU +56.2823740,43.9810684,RU +56.2841117,43.9896343,RU +56.3029668,43.9727618,RU +56.2845826,43.9864077,RU +56.2472484,43.9690707,RU +56.2850699,43.9863095,RU +56.2843203,43.9862612,RU +56.2830940,43.9862872,RU +44.9261068,37.9726588,RU +44.9277746,37.9676301,RU +43.1182653,131.8814462,RU +43.1162707,131.8794657,RU +43.1216429,131.8848745,RU +43.1240364,131.8813312,RU +43.1161959,131.8778761,RU +43.1179985,131.8871049,RU +43.1156961,131.8772347,RU +43.1194499,131.8841567,RU +43.1180042,131.8863594,RU +43.1236890,131.8858743,RU +43.1193562,131.8818384,RU +43.1305677,131.8878333,RU +43.1254235,131.8819272,RU +43.1249675,131.8809931,RU +43.1173521,131.8786183,RU +43.1168584,131.8775001,RU +43.1179972,131.8816909,RU +43.1172978,131.8783606,RU +43.1208989,131.8887896,RU +43.1201409,131.8841264,RU +43.1193750,131.8860153,RU +43.1245762,131.8820282,RU +43.1172415,131.8777375,RU +43.1458520,131.9022587,RU +43.1509085,131.9066930,RU +43.1351764,131.8934578,RU +43.1418441,131.9009774,RU +43.1372077,131.8944287,RU +43.1351408,131.8937244,RU +43.1373440,131.8948971,RU +43.1380187,131.8947159,RU +43.3142515,131.8515477,RU +43.1368820,131.8955131,RU +43.1352365,131.8934558,RU +43.1337687,131.8926530,RU +43.1226710,131.9065826,RU +43.1352671,131.8938160,RU +43.1347551,131.8931358,RU +43.1351881,131.8934227,RU +43.1376943,131.8966217,RU +43.1352331,131.8934442,RU +43.1457753,131.9073072,RU +43.1272213,131.8849857,RU +43.1258718,131.9077004,RU +43.1239023,131.9078360,RU +43.1255740,131.9090525,RU +43.1260276,131.9032653,RU +43.1351087,131.8936349,RU +43.1287728,131.8901788,RU +43.1258721,131.9052026,RU +43.1267225,131.9032113,RU +43.1306323,131.9077130,RU +43.1368942,131.8895030,RU +54.3612449,32.4212639,RU +55.8623685,37.4022353,RU +53.3263482,91.9328684,RU +53.3282315,91.9352287,RU +60.5903448,30.1122311,RU +60.0554887,30.3190921,RU +60.0554895,30.3192833,RU +60.0555011,30.3191016,RU +50.4083072,30.3342152,UA +50.4074586,30.3339951,UA +50.4033807,30.3405146,UA +50.2463317,30.2897407,UA +48.4047289,35.0327035,UA +48.4046525,35.0327664,UA +48.4046728,35.0328537,UA +47.8456386,35.1129540,UA +47.8417068,35.1167022,UA +47.8424569,35.1171101,UA +47.8402500,35.1283949,UA +50.0304112,20.0798884,PL +53.2819937,15.4354048,PL +53.2737215,15.3575210,PL +53.2718262,15.3626223,PL +53.3313585,15.0290376,PL +53.3224422,15.0273299,PL +53.2841077,15.2974127,PL +53.3521092,15.0982644,PL +53.2931896,15.0145583,PL +53.3293004,15.0371236,PL +53.3286116,15.0288377,PL +53.3391446,15.0342404,PL +53.3108883,15.0245747,PL +53.3422307,15.3425935,PL +53.3406551,15.0472115,PL +53.2858152,15.3330485,PL +53.2875728,15.3882854,PL +53.2832728,15.3111119,PL +53.3295582,15.0251596,PL +53.3198162,15.0727223,PL +53.3232866,15.0220230,PL +49.5325662,9.0132021,DE +51.4425490,7.5887978,DE +50.1756421,8.6990125,DE +52.4857113,13.4644430,DE +52.4857139,13.4643543,DE +47.5744534,9.5659116,DE +50.4073636,10.8102181,DE +50.4614576,10.8561482,DE +50.4123343,10.8003013,DE +50.2856636,10.8063183,DE +50.4189968,10.7560177,DE +47.5984417,9.6073298,DE +50.2005855,10.8001075,DE +50.1366933,11.0770859,DE +50.2917280,10.9803770,DE +50.2676658,10.9474116,DE +50.3497592,10.7940034,DE +50.2919688,10.9812515,DE +50.2991303,10.9953080,DE +50.3468939,10.9158285,DE +50.2735223,10.9594756,DE +50.3013297,10.9340768,DE +50.3607890,10.9219217,DE +50.3221726,10.8736274,DE +50.3416614,10.8119286,DE +50.2815708,10.9626708,DE +50.2995664,10.9648773,DE +50.2994548,10.9886843,DE +50.2959963,10.9764799,DE +50.8760697,12.0882661,DE +50.8867408,12.0804875,DE +50.7184645,12.5363186,DE +53.9554188,10.7764795,DE +54.0901796,10.5937256,DE +52.2404983,7.5183095,DE +50.0691240,8.2242020,DE +50.0665424,8.2296809,DE +50.0650111,8.2198465,DE +50.1019378,8.2112418,DE +50.1091701,8.2030878,DE +50.0688537,8.2367395,DE +50.1681021,8.2194497,DE +50.1450654,8.1957746,DE +50.1572125,8.2149774,DE +50.1501056,8.2135376,DE +50.1655267,8.1828888,DE diff --git a/toolkit/modules/tests/xpcshell/regions/world-buffered.geojson b/toolkit/modules/tests/xpcshell/regions/world-buffered.geojson new file mode 100644 index 0000000000..c8667764ab --- /dev/null +++ b/toolkit/modules/tests/xpcshell/regions/world-buffered.geojson @@ -0,0 +1,249 @@ +{"type": "FeatureCollection", "features": [ +{"geometry":{"coordinates":[[[0.92996265478190632,42.429551527666284],[0.92320151620754598,42.638175808805116],[1.0089261997395005,42.867663436182227],[1.1849715923719015,43.039540829989768],[1.4157431035820069,43.135098763590335],[1.6790965363768353,43.122580364217676],[1.9453341395240398,43.045276943801412],[2.1521164440872171,42.857835561799021],[2.230182734818615,42.670533041490835],[2.2225252603679158,42.425707226186347],[2.0759845538283987,42.167333127770014],[1.9280202373035285,42.055581054191478],[1.6315517218490532,41.951600494683753],[1.3622088675356401,41.945254290426803],[1.1816171072420374,42.015136847691075],[0.9917345315742816,42.217771068688357],[0.92996265478190632,42.429551527666284]]],"type":"Polygon"},"properties":{"alpha2":"AD"},"type":"Feature"}, +{"geometry":{"coordinates":[[[51.071993890966112,24.228113476499487],[51.091370871825717,24.43520584835618],[51.197696593873864,24.6273773663656],[51.334835334332766,24.758458893650314],[51.510934451713894,24.829017392695661],[51.653437133760491,24.835730449149558],[51.792035151673076,24.801927062512593],[52.040651703662647,24.672910048530259],[52.1417180977082,24.584824502868191],[52.307558552116596,24.768935658740411],[52.582759971899385,24.874274302744762],[52.778696752095435,24.853609143276373],[52.97133313786771,24.740333405770066],[53.264502142368507,24.888328374417238],[53.458706950639233,24.908552241891162],[53.645849444493955,24.852854850884878],[53.774575921985786,24.754661167739865],[53.921144696797349,24.750425841844883],[54.074830619708024,24.897908020249158],[54.250933455984537,24.972934505375708],[54.397802152080168,25.168009311892305],[54.772478139654233,25.426604625691422],[55.193043171616132,25.873452402379375],[55.552491813031779,26.134304762594095],[55.663883831857532,26.338719914152655],[55.760105693125709,26.446193961875036],[55.974775320900946,26.551100434715437],[56.300148998563508,26.529331575592565],[56.508188565520967,26.413063798596362],[56.638773300527049,26.213699096678305],[56.672013195042361,25.966047122045492],[56.797460897127905,25.817231688941444],[56.849820575501219,25.684442017111468],[56.887260408123943,25.000020070520176],[56.869948151968543,24.84732623571194],[56.742632089195666,24.627174861811383],[56.400925142104001,24.345487349270609],[56.48266530347226,24.111368223396994],[56.444610849091148,23.86681688438177],[56.365897146592076,23.739724515780768],[56.253129712428297,23.641582664820831],[55.949556340451544,23.550685649118098],[55.701864120383505,22.938222613994064],[55.678408845088185,22.619891501808151],[55.628436982251841,22.472112359921525],[55.433460810698236,22.235192370235804],[55.257361020159756,22.143605921967044],[55.059502641115337,22.12775424773054],[52.495395234777845,22.436604628231148],[52.317204596858588,22.493285271916378],[52.172298040521994,22.611466017984903],[51.18055454566197,23.796057101469906],[51.104130815453232,23.973157769860929],[51.071993890966112,24.228113476499487]]],"type":"Polygon"},"properties":{"alpha2":"AE"},"type":"Feature"}, +{"geometry":{"coordinates":[[[59.986640690502455,34.068897632122713],[60.037355646261204,34.315501131822799],[60.240180059305217,34.59316007659605],[60.324725243300207,34.823592872065483],[60.596408699848453,35.052489493535369],[60.60430885114183,35.33573528116392],[60.641389598284171,35.470775607224475],[60.751632496053439,35.630677515143702],[60.831885580143883,35.873838825999663],[60.968107531139083,36.023681839762709],[61.152588019779763,36.107206075870735],[61.44210325470371,36.119667012165273],[61.589440068696298,36.065231341966829],[61.774034836824484,35.937727397498783],[62.116441407326526,35.92559441963369],[62.442090404531776,35.744887953994777],[62.547692772948274,35.752277174367741],[62.630438618949817,35.801946442065351],[62.650239121046631,35.986442118848124],[62.711137645566694,36.118874876100939],[62.887817862173449,36.283368104898834],[63.808005580211464,36.504429815734817],[64.127439902693681,36.690839422379334],[64.363942423018756,37.344478014923411],[64.51175995084516,37.528292053483227],[64.957278687883317,37.719889954580971],[65.260252730954647,37.749842212070547],[65.458218086284703,37.963674608955479],[65.680344858288308,38.061653141986902],[65.875204070797608,38.056588556533164],[66.260516673962556,37.895777927080765],[66.481180717293554,37.852374050356701],[66.857764237347268,37.869849401283638],[67.190555899074326,37.819498355250715],[67.355906903803572,37.745779469457865],[67.622641080736017,37.755138383902143],[67.898344794573632,37.68591613785302],[68.089343751735811,37.543730972336611],[68.775201180726526,37.815191619451632],[69.042857489069561,37.813694582669953],[69.199757874256392,37.956906777784759],[69.322235335281022,38.02308505164801],[69.748928466720827,38.104053460245893],[69.836650142722959,38.251328469096187],[70.071097975193467,38.443535501517744],[70.328232617491878,38.743540096991772],[70.717514826607754,38.929425606644472],[70.916274993254802,38.954791288068485],[71.529744209040473,38.725034557732783],[71.672447006611606,38.583090389618206],[71.767327478156858,38.374817682229086],[71.906276935453675,38.290580892112189],[72.00050959530202,38.183581079836024],[72.06071087671566,38.05433495909972],[72.081985511530988,37.913352180283646],[71.99065660190449,37.373192535204957],[72.38596146783317,37.485057313029792],[72.710161111882655,37.731689646524622],[73.245571523712385,37.942743808881445],[73.396185683670623,37.961816016870671],[73.834518803938721,37.905348825319741],[73.992587459647822,37.837236364451549],[74.239618809166828,37.914767513886098],[74.822414828406011,37.866907069500492],[75.217493847544674,37.610214788410694],[75.314801492828408,37.496636697718202],[75.374164052399777,37.35936016280727],[75.385661193959223,37.160859947581486],[75.290947392484938,36.932084798581464],[75.184405619622851,36.827119989471832],[74.950145572076437,36.735510306331676],[74.854535419526044,36.632733417933814],[74.723210142461994,36.556655103176439],[74.080414214954544,36.327660482296785],[73.703878871128651,36.385149194445859],[72.693017083082694,36.329927375970968],[72.410642597655283,36.255391193912494],[71.919560238482049,35.954275822398266],[72.059278011346493,35.657546148315227],[72.113552981542213,35.101628745265941],[72.016149525318383,34.877660484097241],[71.572416388679073,34.403154910142455],[71.584273003241137,34.049481452999586],[71.52340980189085,33.868595088798173],[71.425425192182189,33.718135018773467],[71.258526556457312,33.594860163161684],[70.959262113709087,33.494509584076361],[70.771325758513228,33.479939543433872],[70.782416823829379,33.330557961736289],[70.741418722584953,33.150884875033313],[70.677761061785205,33.013051583984122],[70.575670619419185,32.900677852723398],[70.10923717022122,32.649896326454027],[69.907193009998466,32.607619456864334],[69.760785944569108,32.311932419426512],[69.778409866254961,31.912034209340625],[69.748720595121924,31.765476211709021],[69.554519182706315,31.499147868623783],[69.139324670951694,31.213853080966548],[69.000509272595309,31.152055197103696],[68.712422425763847,31.15158791784258],[68.571410111560809,31.193373920519498],[68.434113694270721,31.282038972226008],[68.312725745801146,31.281975692350059],[68.224348487060269,31.232133928048395],[68.162522522236543,31.080545013351898],[68.069840998999169,30.970427155706332],[67.770447651189855,30.80924176447963],[67.337354305010038,30.720502496080687],[67.033919886096129,30.768688438205032],[66.793906055378358,30.543453343951281],[66.771692886973909,29.809204203507992],[66.716539893591957,29.666849773639182],[66.621002150746875,29.547773428069096],[66.344324277959373,29.36479584878262],[65.303830038983875,29.09317444341303],[64.534020924417675,29.051046201221766],[64.279784265112298,28.926158773035105],[64.143463924467937,28.894209782661072],[63.531474721537585,28.992710930802311],[62.429666246202423,28.910722535554338],[60.710850980597954,29.376796298536455],[60.498664478656821,29.497005879793232],[60.386183877271094,29.657287756450991],[60.347324947608293,29.799344848089358],[60.351538667655788,29.946560602214561],[60.398459451123294,30.086162442381823],[61.189319869710488,30.936783573429182],[60.611880886720002,31.041118453938719],[60.457162267947872,31.152266760028375],[60.355246538285272,31.31321700385762],[60.289472473554774,31.841570770732766],[60.317794219559822,32.195776469614692],[60.073196712711095,32.957955225629242],[60.062539577401289,33.179314352634506],[60.107202052523363,33.346730730724325],[60.015035338931753,33.567981541207153],[59.986640690502455,34.068897632122713]]],"type":"Polygon"},"properties":{"alpha2":"AF"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-62.381746229492649,17.026885125707555],[-62.369327774804695,17.229215322849402],[-62.295966336290853,17.384409468099829],[-62.361276156623113,17.600861126056675],[-62.361292182334203,17.772058852175093],[-62.317871420528569,17.9180323264852],[-62.232521551652376,18.04416328081744],[-62.060516306090733,18.168414826876162],[-61.877131793119055,18.213164569079463],[-61.62965661274729,18.168101603449287],[-61.448239280408949,18.067095771759792],[-61.321901688948756,17.919767937403584],[-61.263617752654049,17.77767738847167],[-61.248271029931217,17.544246457385377],[-61.282488212527063,17.392197870611643],[-61.188208584773676,17.142140734456554],[-61.208092851354927,16.925134837048638],[-61.329091029240701,16.708489705212671],[-61.4789337178822,16.576120417063557],[-61.608359912670323,16.517363732810004],[-61.749090023237464,16.497400671382334],[-61.981454061615665,16.528655137619211],[-62.11955607008305,16.586505679663631],[-62.234337355961586,16.682651955564602],[-62.337830142790111,16.858112919256367],[-62.381746229492649,17.026885125707555]]],"type":"Polygon"},"properties":{"alpha2":"AG"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-63.032525535299627,17.688171725469584],[-63.231389117669075,17.676896842891715],[-63.4603715146215,17.772239576939533],[-63.615359132418824,17.965893942703122],[-63.658210649699889,18.210203014406364],[-63.600742633630219,18.42296133741095],[-63.487980611135079,18.571467348745653],[-63.196510602075442,18.739487457565136],[-62.998299275926222,18.768702585837946],[-62.765277069868667,18.716192786411352],[-62.6109954419559,18.602087479775033],[-62.511054125986774,18.438274857928178],[-62.480173614995763,18.248883105850865],[-62.524735033051904,18.057677965774694],[-62.658407421189622,17.858163435386672],[-62.780835424540591,17.773252884138362],[-63.032525535299627,17.688171725469584]]],"type":"Polygon"},"properties":{"alpha2":"AI"},"type":"Feature"}, +{"geometry":{"coordinates":[[[18.844555660646868,41.928941778579308],[18.7940810001122,42.058928648902054],[18.781812577432017,42.201331419290689],[18.850993319579519,42.427764906785733],[19.363769095614831,43.014637602976933],[19.574625262901371,43.130823288398581],[19.815336679043735,43.135000718039585],[20.030727899249939,43.025657174618978],[20.189269170882874,43.01877558690515],[20.330110932417949,42.970588835661061],[20.450525021443202,42.883074197159949],[20.579303009095597,42.731966484634405],[20.840943033132667,42.553751741343589],[20.961454393121098,42.40962303893749],[21.08077381378866,41.93803299149824],[21.007708486349671,41.617096222499477],[21.001953237420715,41.423180622581107],[21.248731175182712,41.289883858006839],[21.371035805935332,41.146673311413238],[21.517605335885261,40.736013829798857],[21.523739054963709,40.539607792046247],[21.373117232588982,40.228060423114108],[21.172516261906591,40.045201873521762],[21.033878229302484,39.788681961011086],[20.861182938093762,39.660648094201363],[20.739884871590089,39.453394680247122],[20.450982069714517,39.217419221691777],[20.315507087247649,39.165695465676954],[20.170887178512036,39.15502308037734],[19.732818726787958,39.287961704131767],[19.589295588046546,39.426628961499162],[19.504956991486857,39.642382652288163],[19.173994618032349,39.818410886810881],[18.882603425318305,40.169497479184457],[18.832981187852617,40.305503300068729],[18.824404841623412,40.450024565115989],[18.848139589881434,40.765072541494135],[18.953153929808924,41.038557557948138],[18.957540298094095,41.551286654238098],[18.858638410215999,41.743721454058623],[18.844555660646868,41.928941778579308]]],"type":"Polygon"},"properties":{"alpha2":"AL"},"type":"Feature"}, +{"geometry":{"coordinates":[[[43.093692264424128,40.746067779457817],[42.964954383938519,40.950244944879238],[42.946869494075095,41.190941213047395],[43.043652224650259,41.412063034254317],[43.232747648037964,41.562078625461623],[44.155085091009198,41.708173311926821],[44.637596444673633,41.711077715914989],[44.973038219262861,41.789859696627197],[45.220848526867755,41.739849764036222],[45.529083093900347,41.478292994718913],[45.692776841137828,41.403844169700811],[45.971576751285269,41.166672790684736],[46.074620065728588,40.958639553811601],[46.07640898349927,40.737045416417303],[46.308145585063613,40.596714692641264],[46.420082669535894,40.439420573041389],[46.464084479569685,40.251444000951665],[46.438064282550464,40.075052129398181],[46.721935642984462,39.993274683591686],[46.838899947044496,39.904424260132807],[46.924998842483738,39.785419947701648],[47.084370919411597,39.220140242317633],[46.922093944876124,38.654665688363202],[46.795336837600239,38.510678538475936],[46.623692157273105,38.425017769783366],[46.152987595512563,38.37946548726481],[45.960650321243207,38.402283086452215],[45.830071396481713,38.466846206768061],[45.723643012406299,38.566306003469464],[45.470306145770799,38.995297621024001],[44.996505685567662,39.102872755614406],[44.829099981951146,39.207500924676374],[44.601263519292843,39.232501139882409],[44.154516994208883,39.527320958783882],[43.863255891590207,39.529467630038901],[43.44785710340367,39.676834176667185],[43.294494832111127,39.792413870452322],[43.196009333003005,39.957275201081728],[43.071217981694502,40.442654484959142],[43.127233111761768,40.713903220530199],[43.093692264424128,40.746067779457817]]],"type":"Polygon"},"properties":{"alpha2":"AM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[11.801320082229232,-5.9940234960340453],[11.681754134800068,-5.7919731304462196],[11.655726064714152,-5.6371227530658237],[11.671874766594794,-5.4411715858975045],[11.52491000301284,-5.0831335719077178],[11.530533057636912,-4.8960247006396749],[11.580062968281437,-4.7642676323897213],[11.850810818906853,-4.4253522281796274],[12.089870022224003,-4.2155269093044936],[12.637519769022898,-3.9574072788338142],[12.834971888764201,-3.9321930349378094],[13.064375883470683,-3.9802105706501614],[13.194899772201618,-4.0562274507585236],[13.485836447554517,-4.3536194580253103],[13.569172013131281,-4.5781059204083698],[13.564628935905478,-4.722609144839037],[13.49511990244488,-4.9018426909495005],[13.363122117843776,-5.0416023226188713],[13.124506858723688,-5.1604357566389965],[13.025213826899131,-5.2684850051568342],[13.020365741701792,-5.3677343419177488],[14.411899299459511,-5.3924328529912353],[16.303383212429722,-5.3656622734418553],[16.624069637503627,-5.4395756983916899],[16.7887934963403,-5.5490060370726972],[17.138331579606557,-5.9293034178866701],[17.187701046858443,-6.0681729348251645],[17.226628858337374,-6.4509481584650619],[17.381977232358327,-6.7448313061137455],[17.460588468273638,-7.0655776223258693],[17.81103767952251,-7.584889657573898],[17.982247676373635,-7.5875880231464361],[18.510110984453977,-7.4389596261299076],[18.925300687974115,-7.491458627686419],[19.056112483587697,-6.9787974398179937],[19.147476692404126,-6.8200236480825529],[19.460455065531139,-6.5790288336118321],[19.829028875285562,-6.4886080908824741],[20.689702583176928,-6.4302305571360048],[20.827167917110195,-6.4800538553690572],[20.944342160397778,-6.5675139936618052],[21.071904455563018,-6.789128014101224],[21.880757688145458,-6.8344162361141851],[22.06280820603352,-6.899846128563536],[22.175413111675233,-6.9919750315608757],[22.275601166632377,-7.1574616338377615],[22.34032578772149,-7.3937830978451684],[22.285717193126249,-7.8996307073421441],[22.392242296819752,-8.2825272348746104],[22.405057955255874,-8.6749966425305143],[22.328987836918607,-9.3902708440260447],[22.665943839152746,-9.8681362145960332],[22.76825068114751,-10.189633704504182],[22.778305083065348,-10.585018319476744],[22.995597827483145,-10.586443923098722],[23.340934611162467,-10.480044129076074],[23.641314693929044,-10.491345578246495],[23.883845228033191,-10.378980045043591],[24.030484169439678,-10.37626273779007],[24.171603040930869,-10.416210541291344],[24.330388819529528,-10.529405453940434],[24.433799509556948,-10.694730107041144],[24.541311931199484,-11.333747661757998],[24.538184121043869,-11.495951637524685],[24.47813428541226,-11.706822188752144],[24.494859171071411,-12.391179722878787],[24.409240856671751,-12.743901737095364],[24.461107622078412,-13.029252179670399],[24.42744012887783,-13.172936881069768],[24.320608307317777,-13.337511409905124],[24.203073261151829,-13.426754372862471],[24.015850411902033,-13.485452059000904],[22.479952909658294,-13.499525537707369],[22.479788399418879,-15.888988600550736],[22.52643468000587,-16.134351860206444],[22.596543401907326,-16.310735419379299],[23.801466759099721,-17.371225349173152],[23.8723462426717,-17.552409232505688],[23.876770881566525,-17.69866759162122],[23.816973419575266,-17.883804741621677],[23.727830041610162,-17.999841559940208],[23.608911247108477,-18.085102367787556],[23.470401757758175,-18.132285059533956],[21.51200835608255,-18.491617513977328],[21.128542195169508,-18.462930932955413],[20.727618942460222,-18.519198644702463],[20.254442042344291,-18.377800332647119],[18.837371594813249,-18.289221452383416],[18.474035200610622,-18.1393985594882],[18.2217800871585,-17.900457943699152],[14.018464530061291,-17.908615495286352],[13.778643090642014,-17.847877368780232],[13.411541615613787,-17.645772774112352],[13.195876683293772,-17.487180977030913],[12.597938485610563,-17.709970608139798],[12.099698533176989,-17.672776210630403],[11.821504929967533,-17.742792116702287],[11.675159719571528,-17.744271532381813],[11.534655307052605,-17.703312780700205],[11.377005683808811,-17.589241739517234],[11.294163103536444,-17.46859259459519],[11.244328877630664,-17.280491143048827],[11.318019580566277,-16.65618903752971],[11.300357053948053,-16.089018826045425],[11.251912080965544,-15.859467805291148],[11.278161072267409,-15.66996183091238],[11.343898826589315,-15.541982594134453],[11.551811579361795,-15.31908740127537],[11.791829899970642,-14.523719016754958],[11.899134200856739,-13.898910952897456],[12.01934953722284,-13.608601937691429],[12.095156621516983,-13.231753352518844],[12.446159380346737,-12.786136288550324],[12.533559412636613,-12.557493044858578],[12.614290463074607,-12.438508842144994],[13.077077222809935,-12.135847633543891],[13.166686810890068,-12.021416740968311],[13.288934049512738,-11.708297364397232],[13.339110259044638,-11.116926707871841],[13.263404842347359,-10.869850326760179],[13.098816587848477,-10.656385804505922],[13.028908720066362,-10.456207778883952],[12.870753800126092,-10.190695520890097],[12.729612031859332,-9.8431902191941401],[12.673160799803021,-9.53932501063613],[12.502989039228313,-9.1126986181848366],[12.51651560003131,-8.916025255439191],[12.599693562677523,-8.6989308811328936],[12.717738826537246,-8.5461331698807808],[12.861200970021889,-8.4576455876051497],[12.407263602181587,-7.4393449799630984],[12.345292832399057,-7.1698457403577818],[12.101641371637861,-6.8615801932306697],[11.817461652461411,-6.3051480185286861],[11.784621981271924,-6.1561229273750095],[11.801320082229232,-5.9940234960340453]]],"type":"Polygon"},"properties":{"alpha2":"AO"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-180.0,-83.870711457475963],[-177.5239895666725,-83.619583275562974],[-175.64014841421218,-82.991113406875783],[-174.33060833061955,-82.315206011291011],[-171.07417880357352,-82.305387217976033],[-169.31130799309889,-81.951861082243568],[-166.83214303355518,-80.794620284033499],[-165.41728217597517,-79.66088464118711],[-164.47184898528306,-78.660856273833588],[-164.30742420805842,-78.545619804142035],[-163.36796755983787,-78.245656032122056],[-161.12070645927187,-77.90396227693752],[-158.14972284762104,-76.630278305623179],[-157.99953701260324,-76.607332961113556],[-152.69910215689856,-76.612165880521147],[-146.05268755862144,-75.034292990900767],[-144.71361324964985,-75.116696512051959],[-140.71828717345673,-74.911719791068549],[-135.78667837589066,-74.235034805658302],[-130.66443649266927,-73.777212760667382],[-128.78329964312655,-73.377687385927203],[-127.356998542241,-72.838076809215949],[-127.18069292933605,-72.805728018643151],[-126.15535719431013,-72.806176115770597],[-123.88740183062458,-73.181344169227913],[-114.54608775637253,-73.415853790085322],[-112.76726538746765,-73.664888355376675],[-108.8331368773903,-73.688008070845726],[-106.87553127597265,-73.232131055427246],[-105.2331565499607,-72.500151872427921],[-102.36936907014389,-71.544350014857329],[-97.11536160530693,-71.26798307940615],[-95.184355919690432,-70.918443301374012],[-94.294523664480224,-70.611871109240852],[-92.576032395613737,-69.707335550543959],[-90.933229522490166,-68.341747399550343],[-90.810009666983774,-68.266439006671035],[-90.622223079655356,-68.226324961983281],[-90.433166012054684,-68.259945850607764],[-90.270722823037403,-68.362342868648142],[-88.161466956366979,-70.349755253648155],[-85.657537299481135,-71.640885099038528],[-83.85951175690569,-72.060716976606258],[-81.046921363025135,-72.012026736198194],[-78.38241674492528,-71.148674556744609],[-75.934694497154538,-69.459013499208311],[-73.398818411548703,-68.933586171145237],[-70.546484703397326,-67.779794954553338],[-68.681447631276896,-66.442722803858842],[-66.815598750373681,-65.533857104721491],[-64.598838551243119,-64.181972425604187],[-62.85991646627604,-62.570020074945333],[-62.681312966306237,-62.463442468147278],[-57.630162040401935,-61.311258323828504],[-55.453649515006731,-60.591014909046187],[-53.961685527447898,-60.651488301104187],[-51.526379670379029,-61.181895384048751],[-49.714261000069222,-61.175252344858308],[-47.922797859889414,-60.820534982529857],[-45.905816852546039,-60.065055826686674],[-45.709201224012411,-60.033739329430794],[-45.01846866483686,-60.256697734876795],[-44.850521048085909,-60.357426377891997],[-44.757261631152964,-60.471439037638781],[-44.701269898825537,-60.607678280664366],[-44.692462061288005,-60.803318469516043],[-44.735984948691467,-60.944037919846387],[-44.853707448962155,-61.100543748574985],[-45.908198370710252,-62.041914260354282],[-47.074823925428426,-63.48827921555332],[-48.250095796508973,-66.029067535076777],[-48.635284351092515,-68.789259219037675],[-48.202450180991235,-71.542381616604061],[-46.988886500644597,-74.05122428774402],[-45.090677386001417,-76.108813025238788],[-43.543501701420489,-77.122850746016937],[-41.82819286362804,-77.815483902247777],[-39.04309033404023,-78.203074737913624],[-37.211284749533455,-78.005915362878923],[-33.959877511303134,-76.886158885367067],[-25.377722250116687,-75.264919493196686],[-23.636251468042506,-74.548219037276283],[-22.256801037456736,-73.672516183147323],[-20.973376332500724,-73.198125171744252],[-19.198465362382574,-72.937880309068746],[-16.570936438699967,-72.000135698738674],[-14.908527608272593,-71.766249153913662],[-12.25472459925877,-70.841244751867237],[-10.324237682381817,-70.452950210655828],[-8.6333916719304629,-70.405187445952635],[-6.1185288897998289,-69.919880281468664],[-0.78300592191359764,-69.856766618351656],[1.3071952323191249,-69.535242753887971],[6.4033802160504623,-69.945228446358826],[9.0957537775864008,-69.696827257219141],[11.264090111038465,-69.764278400006205],[16.108549982398859,-69.22557316864426],[20.131334650609883,-69.832434800537186],[23.81736312609879,-69.916592744906595],[26.336037423475883,-69.575941754970202],[28.12134977397529,-69.642557303770573],[29.915609841177393,-69.398702243385358],[33.129184145615021,-68.214796659707048],[33.271102646833349,-68.184927746664215],[34.252852587691876,-68.219129722842581],[36.341692383977971,-68.676067644906325],[37.27222411964118,-68.731337274961419],[39.202318856876765,-68.552964504897417],[42.81119818428644,-67.623700797353024],[44.830738049542326,-67.300493204078037],[48.264619538196676,-66.229438185749373],[49.597507656273649,-66.076312618135191],[51.819341703153881,-65.532078566859468],[53.627655727660596,-65.369250923825177],[55.439487693986663,-65.489572207514314],[58.349473825078782,-66.471416737051172],[60.906735318241452,-66.961917801858021],[67.477236617237992,-67.306527272308543],[69.572394622508384,-67.268282523850004],[71.936720679391016,-68.257339114178365],[74.600343642291065,-68.649830256318864],[76.362205619618948,-68.492430216072222],[79.128435763597807,-67.646759129639278],[82.598814181351443,-67.03628236520666],[85.02908296729376,-66.03946809153868],[85.181555384508087,-66.034006881480096],[87.157230353083293,-66.266954541239173],[88.967580514084958,-66.214344626307906],[90.730200509154457,-65.822268926499277],[92.116224360816645,-65.257685748477087],[92.272659172893256,-65.221779881649454],[94.88907272582739,-65.606953438849857],[97.386209141474637,-65.548850902997543],[102.76316222325826,-64.64869181435391],[102.97296269756525,-64.681375605181131],[105.32768988017119,-65.590633687811234],[107.05079210014958,-65.940528699792125],[108.8088498445337,-65.96759799311512],[113.12765191326669,-65.311590036600478],[116.22435829574796,-66.314551052003736],[117.90317596119111,-66.569712128343326],[121.1517961830492,-66.470647034492117],[125.86460568556274,-65.874724273807402],[128.06569932726353,-66.053031180986864],[130.67825289810168,-65.707895812921151],[135.40880181342962,-65.641314484285218],[140.52308269480059,-66.229126063173638],[143.82225974854194,-66.400432302706676],[144.71206412914628,-66.575515289145599],[147.98397221088584,-67.723806443783232],[149.84077124141996,-67.917889248961444],[153.89808758806575,-67.837355840604332],[156.44229436668115,-68.024554403769685],[157.40246211721896,-67.927661470138062],[158.35230748017904,-67.731064017582995],[160.20005283901276,-67.036868565693197],[162.04004540705978,-65.845179624969788],[162.22076315709404,-65.773216247667847],[162.36696472516337,-65.76789671626787],[162.50844766166063,-65.805122709879186],[165.10623704569684,-66.928214358086421],[167.20526040204334,-69.197406402587958],[168.74487818592777,-70.213691878211321],[170.65972823062719,-70.985822910496907],[171.17741970190667,-71.466869904914333],[171.24269457863036,-71.596345410137573],[171.26719733624432,-71.78758313285438],[171.06715904173458,-74.787483873386691],[171.35885251203339,-76.674395278453872],[172.02452473283299,-78.468800829533308],[173.03644433201356,-80.093309835997616],[174.3535180240637,-81.481952222286736],[175.922260447784,-82.578336276945407],[177.68169358638573,-83.339118018083965],[180.0,-83.854998825030037],[180.0,-90.0],[180.48160986924509,-90.0],[180.47352038681137,-90.1089206314157],[180.3589803228916,-90.323210018887579],[180.24555613231072,-90.416294846464211],[180.10999485016575,-90.472446168061353],[179.75451377891071,-90.49892578124998],[-179.75451377891073,-90.49892578124998],[-180.06186999744179,-90.482018796446468],[-180.24555613231072,-90.416294846464211],[-180.39010852595445,-90.285280193710292],[-180.48309301519652,-90.060795778691755],[-180.48315201470595,-90.0],[-180.0,-90.0],[-180.0,-83.870711457475963]]],"type":"Polygon"},"properties":{"alpha2":"AQ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-74.022034365852591,-49.808911948934849],[-74.074391653545902,-49.623527532917223],[-74.065129311519982,-49.478949660772152],[-73.898671926243637,-49.071632906237205],[-73.764898572219437,-48.916745757994065],[-73.508228760079547,-48.815819302232526],[-73.324612763831041,-48.607735623238938],[-73.085463749508008,-48.485886300687284],[-73.057814097259708,-48.316336721867842],[-72.971357057488589,-48.162754314428319],[-73.017674677059006,-47.870720326219498],[-72.973943336924037,-47.671838098513561],[-72.754230647317328,-47.204516084660625],[-72.654939190968079,-47.099890842274981],[-72.460414018212973,-46.972358579312584],[-72.415723941175827,-46.677427042069382],[-72.275364575741676,-46.46127693781154],[-72.351771179834216,-46.312300083486988],[-72.373575125936085,-46.118475391254918],[-72.339915287163336,-45.97569134528684],[-72.259539301147626,-45.840387673804436],[-72.270473026769764,-45.680098352422533],[-72.201778364181749,-45.37715212332381],[-72.368019072056896,-45.2826550557456],[-72.466499676184355,-45.167400795080674],[-72.571934715528641,-44.837329304735341],[-72.506635810477093,-44.540493955625678],[-72.332837700808327,-44.351226829567295],[-72.269888963595363,-43.9059862041535],[-72.405655246379041,-43.435417107270951],[-72.585936540763484,-43.227955300870775],[-72.646182307520334,-42.986658618429885],[-72.643429344656582,-42.573869591918431],[-72.597030512986748,-42.367523109257391],[-72.600906152781775,-42.168254490311519],[-72.52936794819378,-41.982883028997989],[-72.386774181124679,-41.802261055999722],[-72.410456699870082,-41.626830575192088],[-72.382353294775513,-41.030390581879786],[-72.438309104519433,-40.84205471250543],[-72.432970594907914,-40.699190053744907],[-72.289745117799626,-40.307106866857993],[-72.300722953516839,-40.077282648580798],[-72.265295545714878,-39.937895721823423],[-72.193189617709834,-39.816443963974351],[-72.219671080379939,-39.636129714308609],[-72.181455878621733,-39.4435910283675],[-72.072067649861367,-39.280600959228245],[-71.915323514115926,-39.173197109411063],[-71.89578328808345,-38.860500820934448],[-71.824653597916367,-38.669005938110537],[-71.682873551483837,-38.521940845703803],[-71.476210496345416,-38.407347240478856],[-71.513132024099221,-38.195790654931308],[-71.66369660480062,-37.825052637199342],[-71.653325765691363,-37.525998420569188],[-71.699869806601825,-37.315492715583751],[-71.647837119442357,-37.065508366429832],[-71.689947977026634,-36.888143509123864],[-71.668820361405281,-36.693392243687683],[-71.441543471802902,-36.206387625715188],[-71.336993397480811,-36.110811348316233],[-71.086464711472132,-35.970179036426977],[-70.896530323638203,-35.791802511983484],[-70.905395008719353,-35.684604414488895],[-71.042600383037609,-35.357236347481063],[-71.034563019629772,-35.105714083502171],[-70.969946478620116,-34.968089029156801],[-70.820852338273994,-34.819425003560163],[-70.669281797297927,-34.381567362301318],[-70.487119103317937,-34.054908016219485],[-70.381231237213782,-33.925333595266586],[-70.381204072432681,-33.603845988673072],[-70.510060196496539,-33.464352750070731],[-70.56824588249215,-33.328631739280645],[-70.57557996844767,-32.963090912864224],[-70.65125711700658,-32.694231459701655],[-70.793966246425768,-32.425035509355936],[-70.851026979733405,-32.10308895692409],[-71.07090520058486,-31.687107147879637],[-71.084162097566491,-31.541110372388101],[-71.003268901099688,-31.022758060425453],[-70.916451369345396,-30.844733012257716],[-70.801742029265839,-30.731315134128952],[-70.605705859767966,-30.141710243573645],[-70.437779632737843,-29.961605671387826],[-70.43113297474315,-29.812543635934702],[-70.522958121947752,-29.383903267968972],[-70.519431842801936,-29.239959759221993],[-70.451691943902503,-29.060881127005032],[-70.276278417562793,-28.826601565839301],[-70.106918893378648,-28.196164424015514],[-69.605798843235348,-27.630212938755434],[-69.279589531194247,-26.904659941387521],[-69.183715897631302,-26.785055073101145],[-69.03879288143726,-26.693155695663773],[-69.084875632046277,-26.550549243729776],[-69.086942958966063,-26.404183636670751],[-69.048540417624054,-26.189973805860223],[-68.96635311981909,-26.02960427771891],[-69.09998492569099,-25.476059484519521],[-68.996759318423017,-25.087244097057496],[-69.060473976079564,-24.873296285244294],[-69.055779492265927,-24.679833301959409],[-69.012530641199035,-24.530396706793749],[-68.880499278489225,-24.297500107855896],[-68.517574444682467,-23.969687021707646],[-67.761009670512635,-23.656981810932265],[-67.583093748329119,-23.135600090137796],[-67.678244489617896,-22.948323495803852],[-67.680096057050946,-22.702265962050319],[-67.409776548386901,-22.22327043615989],[-67.195518762204927,-22.060212205339539],[-67.034945048801163,-21.83516533335613],[-66.72282488712078,-21.691548123723024],[-66.549588433856314,-21.426708211075308],[-66.426343854474396,-21.347298961935493],[-66.285364500386208,-21.307051267382171],[-66.138771807582259,-21.309425633079083],[-65.925795852615266,-21.366094639531362],[-65.566665931907934,-21.603718381861089],[-64.996008372816675,-21.606505583051607],[-64.586324311720134,-21.698894225494804],[-64.382755826152362,-21.782104610026728],[-64.226559181486508,-21.64007674759948],[-64.070461968262109,-21.552720658239384],[-63.921802428143735,-21.512032362884167],[-62.839085284344819,-21.499356832082441],[-62.64081411055276,-21.538356647741246],[-62.474171003904345,-21.652648025302575],[-62.254558186930957,-21.928851914004522],[-62.004128590575682,-22.10156411711376],[-61.823024245882578,-22.301589806603044],[-61.550294829668239,-22.715648812014578],[-60.921098763842963,-23.148252184593069],[-60.713375256928373,-23.347488237827275],[-60.392837780061186,-23.471657864629389],[-60.009402704069295,-23.519725638216183],[-59.662896721419571,-23.649603813684042],[-58.937248756731201,-24.126916768070515],[-58.188320546795588,-24.46723129501995],[-57.702253649428151,-24.620885212320239],[-57.281224655294281,-24.98416977336273],[-57.196767589366722,-25.108229635892418],[-57.087448393688881,-25.4103000896996],[-57.085283911659261,-25.649076401629138],[-57.164315332354185,-25.823725668325149],[-57.328520464391467,-25.992610052398422],[-57.497813065149948,-26.315533494947481],[-57.69568719613838,-26.493505642426893],[-57.69737329308505,-26.705972708964506],[-57.73124388910302,-26.823878808508326],[-57.280548879907705,-26.941058870066303],[-57.122433164519194,-26.962895208005452],[-56.820869990693325,-26.943539871984513],[-56.55638386780997,-26.989020927829891],[-56.294548378287118,-26.840139440162453],[-56.098932091849569,-26.809123249590666],[-55.928433665194611,-26.825135234461381],[-55.698656918371206,-26.590013700613802],[-55.432297819476283,-26.488691662997404],[-55.207862138778978,-26.28879300321238],[-55.167584784324369,-26.180687327387606],[-55.079567292302244,-25.389784869341554],[-54.970604081362957,-25.224142931612128],[-54.805828540297895,-25.113873924453841],[-54.095970421726697,-25.026746547453897],[-53.912466059962988,-25.085856439260972],[-53.564834210534642,-25.290398943338065],[-53.44053181941247,-25.452835207959499],[-53.346368660110826,-25.791383131652456],[-53.172595217106071,-26.221301243385934],[-53.239938629391716,-26.701680945282998],[-53.223688311185477,-26.960733852055135],[-53.392445345902935,-27.347088822585434],[-53.575254139415442,-27.546082219795522],[-54.106400202984901,-27.872001002895999],[-54.522129784113062,-27.989669864021653],[-54.825469699320813,-28.283428055754655],[-55.054748766729958,-28.372384224189013],[-55.185905192790408,-28.462623595639197],[-55.255983673392201,-28.634148093389808],[-55.345118122336125,-28.745891178467456],[-55.462227071055374,-28.827848247601729],[-55.636278618854632,-28.880347183061911],[-55.846262217300477,-29.070878059103556],[-56.00611100753148,-29.313435915068673],[-56.248394516526844,-29.526826159339276],[-56.574065191173361,-29.936951874800567],[-56.88778561179825,-30.164239079001245],[-57.021944502814819,-30.355379987880667],[-57.228072718360359,-30.524006324734799],[-57.327555630170217,-30.67542145455899],[-57.325340993780806,-30.977887693557715],[-57.42010995841499,-31.355742790754416],[-57.510595981192459,-31.537312529770237],[-57.532038811942101,-31.840314489797802],[-57.645614061781266,-32.059365235136525],[-57.624469718782542,-32.357273724115792],[-57.706713706192637,-32.634249898077258],[-57.67134972018718,-32.949996927429808],[-57.691416361883206,-33.099746638388091],[-57.834560902278099,-33.355504817979551],[-57.99212367677972,-33.506132570969363],[-58.024447762824146,-33.636800466637105],[-57.899578341662497,-34.107861451511951],[-57.906166231448296,-34.299879027910215],[-57.302438997119154,-34.58328771623853],[-57.018814788279634,-34.77787369408248],[-56.723379668920842,-35.13953411197398],[-56.67866639011104,-35.274639698664892],[-56.662445548868135,-35.563159439627505],[-56.700493972480238,-35.704945998712603],[-56.792929714984425,-35.847051505859419],[-56.600568693318415,-35.869429802270439],[-56.461733400527329,-35.937873077254956],[-56.318447623575551,-36.087945412001815],[-56.231859041154635,-36.270336002891518],[-56.169417819524355,-36.772456531108872],[-56.296878721626413,-37.21194389443157],[-56.709735347635849,-37.773455660150788],[-56.986870592648103,-38.04924422084175],[-57.108735870152046,-38.325912803192637],[-57.192850291359747,-38.438325372411349],[-57.987936648232768,-38.897584601472211],[-58.864590370815975,-39.152588888056904],[-59.736965678160587,-39.329298335396373],[-60.843090247634322,-39.470151078355819],[-61.434577818416493,-39.485813760278127],[-61.593148993321378,-39.652897676667216],[-61.689085805979701,-40.057731106901663],[-61.780134624204294,-40.18072180148485],[-61.884331123521577,-40.258984680345947],[-61.77100923093473,-40.52032085072657],[-61.748250820395548,-40.715241626146657],[-61.837289447591459,-40.998831830571916],[-61.948011008100799,-41.167541943382545],[-62.166781691244573,-41.3353766853567],[-62.83021262347642,-41.592268627314873],[-63.590004755511131,-41.658336532866862],[-63.470292001217651,-41.734381135682241],[-63.238471740834882,-41.974338320498482],[-63.139962883157629,-42.181530878348489],[-63.125910732130677,-42.786302322931441],[-63.17794813612575,-42.933767849824612],[-63.311864718941479,-43.129158289011187],[-63.523819703618869,-43.275716981449712],[-64.003792425370563,-43.352248602943504],[-64.234032525386425,-43.517974041662519],[-64.579061452476893,-43.631419821718026],[-64.777884823359315,-43.818186730735206],[-64.739624200499833,-44.076536473294588],[-64.798763389932361,-44.459991046132643],[-64.961340171206217,-44.777085209716866],[-65.130043496035228,-44.929004700023583],[-65.174117841447057,-45.191867957643709],[-65.312708529644297,-45.386505316969107],[-65.434469886815421,-45.463864690062806],[-65.573249385033577,-45.503241733881779],[-66.086645642556661,-45.481029329525171],[-66.394173438176054,-45.644761993613031],[-66.672599496690623,-45.707018263222061],[-66.879047776433964,-45.907687597912158],[-67.059213686442732,-46.151831604563121],[-66.55597034709362,-46.545010960392993],[-65.875543393636818,-46.609438406187039],[-65.601680763790128,-46.726663515155451],[-65.484068135291764,-46.820268680015872],[-65.286985232984236,-47.129826694907315],[-65.238699855511356,-47.32607545825784],[-65.331770873060478,-47.79830906552894],[-65.31791058134722,-48.027297656060988],[-65.364969903191138,-48.168118054920072],[-65.487587891885155,-48.322812611772385],[-66.110170729617593,-48.754028460744912],[-66.78835104375726,-49.068061463546726],[-67.107925911403697,-49.312674176747322],[-67.187173543417387,-49.424707733038083],[-67.322503239873882,-50.050447802761134],[-67.516566742728344,-50.312133877036104],[-67.952793380399413,-50.552416068553548],[-68.224918091180797,-50.603486689626472],[-68.608777713418135,-50.774244845685217],[-68.66029773767201,-50.954363469015775],[-68.55256980653283,-51.368128385777347],[-67.938765784187808,-52.100345717064918],[-67.895067599769078,-52.338903520255066],[-67.974710667617813,-52.578595321329182],[-67.834585319255964,-52.753753749543343],[-67.760855767740324,-53.026949656575773],[-67.6428184808764,-53.20419846343809],[-67.058698968906356,-53.605841752268709],[-66.059135975671609,-54.062174344955963],[-65.702315937564862,-54.149043575442988],[-65.176107970629261,-54.144355863125909],[-65.042729321800266,-54.185840530269836],[-64.848623018472281,-54.309303398085902],[-64.69087582752212,-54.236975886958135],[-64.543069326392413,-54.216491244465075],[-63.809711698259449,-54.225339576849954],[-63.620331301400306,-54.265070775788949],[-63.460218650297037,-54.373732829148608],[-63.337049672418814,-54.580901912214429],[-63.325109395617311,-54.821623859447755],[-63.427172244751766,-55.039964879222914],[-63.575745720628937,-55.163938364893966],[-63.882974447491996,-55.302529712502505],[-64.242869243620461,-55.300415962260047],[-64.530833355160581,-55.390819109454114],[-64.675978844445595,-55.400805205753898],[-64.817854572769832,-55.36858389461419],[-65.017279550202261,-55.253615015060142],[-65.158523180246846,-55.341207730716654],[-65.370226880077922,-55.403952806522362],[-65.877605394618939,-55.416803506450599],[-66.464055961156276,-55.529703111168097],[-67.192617844819154,-55.400367352572438],[-68.231590180067428,-55.31909506477426],[-68.708931877643465,-55.350137700565107],[-68.895670089409904,-55.290438136561328],[-69.073753941295934,-55.123396745373164],[-69.151501709488784,-54.891940552512224],[-69.106757163410691,-52.67294953315907],[-69.25738990620961,-52.638328582657685],[-69.575871323680047,-52.628228958856326],[-70.03912599024774,-52.507434537807832],[-71.977483148526574,-52.466112628368627],[-72.20519844363271,-52.405747361985654],[-72.319292044592359,-52.320864170010218],[-72.418714984056578,-52.179042583963124],[-72.597728615151397,-52.067591509492331],[-72.835705233379073,-51.79873736678951],[-72.907079733586855,-51.558102082504625],[-72.84300830507118,-51.281558202300161],[-72.862810338443012,-51.209355303617293],[-73.04772662824054,-51.258904005991965],[-73.231651878814318,-51.237309272450069],[-73.431182393449987,-51.153402620249807],[-73.566290940580416,-51.01914178144478],[-73.753077639645937,-50.606961833270837],[-73.915880698094156,-50.404332661239962],[-73.976623799006205,-50.279576648675018],[-74.022034365852591,-49.808911948934849]],[[-63.915087304872181,-41.628562998612537],[-64.527961339082708,-41.402028216664419],[-64.547151823677751,-41.397245049118993],[-64.51852726527828,-41.557913842897065],[-64.529992761425078,-41.746941547478308],[-64.449156736387806,-41.770384566909158],[-63.915087304872181,-41.628562998612537]]],"type":"Polygon"},"properties":{"alpha2":"AR"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-171.17792755288335,-14.661414837327689],[-171.28373404600492,-14.499379237571018],[-171.31743675659493,-14.260706645242676],[-171.23555622062517,-14.033999660658719],[-171.0988002520283,-13.897075029462144],[-170.77645996262248,-13.765370114461566],[-170.50993772108981,-13.770412463717115],[-170.32085205135911,-13.833018789867475],[-170.14534941576292,-14.00192419104286],[-170.07615089434663,-14.18484682906365],[-170.0824140053991,-14.380320357467447],[-170.1631805148034,-14.558438009106267],[-170.30609342870042,-14.691948405361194],[-170.55902931892354,-14.822125811548775],[-170.78875417417177,-14.859145506168321],[-171.01427085519188,-14.795334418073667],[-171.17792755288335,-14.661414837327689]]],"type":"Polygon"},"properties":{"alpha2":"AS"},"type":"Feature"}, +{"geometry":{"coordinates":[[[9.0438935439127999,47.394591498123347],[9.0291380949900297,47.562955942495599],[9.0816067245308325,47.746708932670202],[9.1991783385966972,47.897357643417898],[9.3646789606563789,47.992896444864513],[9.7745386014714892,48.073876230519197],[10.140409648573645,47.951226135468268],[10.350015788416982,48.04322389302861],[10.668562428151107,48.046865196119363],[10.986772831312662,48.006843592998216],[11.176815180727708,47.916831726263908],[11.581230756820826,48.064923120349462],[11.886133870836526,48.100035748733049],[12.080313243092007,48.201067508453043],[12.272498380711719,48.21370400951475],[12.34223529204462,48.380721856144007],[12.474925683338379,48.517252150879422],[12.703766982692233,48.664469746267919],[13.013922597374936,48.766055893455125],[13.184620057668404,48.980034608953822],[13.423010724523056,49.076940610687053],[13.619044976626055,49.226742830179255],[13.867123325790027,49.263939690359507],[14.013700855203973,49.225438634230542],[14.247123274093578,49.097686603047507],[14.450760204704062,49.11254223568627],[14.698417586130635,49.404465240370797],[14.82985977342028,49.473304630539516],[14.975716641069781,49.50053851380585],[15.906223865500943,49.357554432348742],[16.193681934487458,49.248005209271817],[16.618342301614632,49.290162948106094],[17.109994954244758,49.149287542699341],[17.269834404682534,49.02096317748952],[17.39566080780099,48.83100268788008],[17.451404836812177,48.637028245982201],[17.433475216315191,48.457943983296644],[17.597496170220335,48.222775087113192],[17.64596887399971,48.037467720388442],[17.634251265044607,47.893898044288591],[17.487823292351976,47.438876433654848],[17.31510449978806,47.274074405532232],[17.0599119174672,47.202291880616698],[16.967560178673647,47.086278503957445],[16.908428183582153,46.800069845041357],[16.754318837487052,46.607785523187445],[16.623474429320279,46.536849930651201],[16.452908138538739,46.501388217704729],[16.271642113205143,46.288901855381376],[16.090611702111524,46.195944329397683],[15.769094677311516,46.19956556859691],[15.485466331951834,46.132016439025151],[15.121203308218817,46.126529787585092],[14.727931963450253,45.932789572385346],[14.583338417220972,45.901105869179609],[12.367378180992183,46.18535316271025],[12.103350254574023,46.292023978158397],[11.866978820473845,46.521172918054646],[11.404051879266712,46.483359121329094],[11.16362821348446,46.327057795643363],[10.966456133818939,46.271185170637288],[10.350479670260972,46.397363602242507],[10.172891990385015,46.354589163966885],[10.019043454107756,46.364869624273126],[9.3628923906333661,46.607455895241976],[9.2080094414835703,46.72409528853391],[9.1091438494637682,46.890885788021805],[9.04244855205833,47.150732535771375],[9.0438935439127999,47.394591498123347]]],"type":"Polygon"},"properties":{"alpha2":"AT"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[158.3468604641042,-54.803048306213995],[158.3468411966343,-54.605154154334997],[158.45707295136108,-54.270433655855733],[158.5787519815147,-54.121653163503211],[158.71871873200095,-54.03485487453522],[158.90528986785091,-53.976775045324388],[159.09987398546281,-53.994637756952187],[159.27275259868509,-54.08571486924636],[159.39752226444125,-54.236096336856548],[159.45512713362393,-54.422814665737107],[159.43676931703186,-54.617352697657537],[159.26242324712166,-55.023625270098783],[159.0829135428269,-55.187470442422885],[158.84735651917313,-55.247319110221696],[158.61140862338831,-55.189030475783717],[158.43081865636412,-55.026376738537643],[158.3468604641042,-54.803048306213995]]],[[[112.42566058001172,-25.697364241934245],[112.41476790896057,-25.495632995306853],[112.48459324452307,-25.306058290606948],[112.583530731529,-25.190695389456877],[112.90562295334544,-24.963573703927679],[112.98197298459964,-24.817153523710175],[112.91992017203788,-24.472512513785212],[112.93017106791201,-24.043287739319894],[113.02881793852993,-23.678718379508258],[113.25672257503734,-23.203643814161577],[113.27192075544286,-23.007842989059103],[113.19314933902055,-22.733533845391925],[113.19251575905659,-22.545248950771338],[113.51478733130415,-21.710232885361311],[113.64098676869601,-21.553844151822936],[113.93315986818523,-21.367451168054128],[114.12496039591311,-21.329609816709041],[114.44636011702222,-21.366730053810382],[114.70652897953913,-21.23334611040692],[114.81219097191504,-21.08791467185133],[114.81295761911962,-20.752911171418095],[114.85288363658312,-20.607672628377319],[115.12962661537445,-20.273060874659325],[115.30133888410937,-20.187400145224011],[115.49266968039342,-20.172723967424773],[115.67543761281621,-20.231194016846516],[115.91246280555043,-20.390274811379705],[116.01819754851644,-20.410822805029216],[116.15271562791061,-20.383905921661952],[116.61749745474772,-20.162790831398549],[117.43622276109151,-20.174243068973198],[118.01728320429122,-19.909865612956288],[118.53581918334416,-19.780885086863215],[118.94581747758245,-19.522306389062809],[119.08250370518481,-19.496853892557759],[119.49701317184343,-19.514759404328643],[120.06213858218415,-19.422086262755204],[120.7739955654795,-19.141530650564089],[120.97744978341684,-18.955260845815815],[121.37685547536402,-18.249099308294735],[121.65305006401964,-17.926293946431723],[121.68442833831197,-17.805376168709252],[121.67030557441129,-17.21960965345427],[121.89470966430214,-16.796431075336013],[122.32410147861614,-16.435432755456389],[122.52382597476692,-16.12525013534745],[122.67389981934286,-15.996959985692911],[122.81291020808155,-15.944714596716111],[123.17093341555845,-15.902299320737695],[123.45259802962524,-15.72057334056348],[123.80837282486863,-15.617189064567734],[123.89587910503835,-15.51373025271735],[124.11702314499583,-14.972334511215456],[124.21400825433624,-14.872887622951312],[124.51778206642636,-14.690485401277034],[124.66636849043331,-14.278646689214586],[124.75156046083006,-14.152706816521704],[124.91607236099081,-14.035287117747385],[125.52310416137968,-13.766840461022527],[125.86361859206136,-13.51596086868399],[126.37641924521077,-13.444203706824005],[126.78257775394232,-13.259789320142346],[127.03752306562835,-13.263383833295096],[127.76071286045993,-13.634797034122998],[128.01457395995681,-13.832075617491606],[128.31602901268278,-14.176629263875421],[128.4283446690323,-14.243395671075463],[128.69685735773874,-14.270470168141621],[128.86373176832237,-14.213822524615232],[129.24856566232378,-13.698163581485529],[129.38776350575225,-13.35573360608198],[129.61437819335887,-13.066680441030771],[129.70373915104608,-12.774880258593932],[129.83015794510231,-12.551075193269845],[129.84393767147589,-12.352134716520947],[129.61259238400706,-12.039313561126864],[129.54936965090283,-11.858090727428356],[129.55907744222444,-11.666401947833567],[129.68241188467286,-11.311093096257904],[129.74377962462282,-11.191738240303136],[130.05354270487518,-10.823970681563793],[130.26104446864721,-10.702044253527252],[130.50147147228006,-10.69118645695599],[130.86238603686471,-10.798448046892268],[131.25734968718908,-10.690975657837992],[131.67700069081178,-10.752919656813631],[132.0869157306345,-10.672487253305253],[132.42585484884304,-10.494141265955134],[132.67309734603475,-10.479304090636722],[132.89712462119442,-10.584948655918383],[133.15220680233762,-10.933831105633338],[133.57430214912489,-11.269648834205064],[134.04396470947654,-11.352757763243726],[134.43227920343438,-11.503758253656995],[134.84326027461063,-11.498340827675907],[135.19257145629604,-11.62205927812774],[135.34618865380696,-11.581889075799007],[135.84456945955336,-11.29061351479938],[136.13896845776739,-11.055629621146077],[136.32577995503064,-10.734556474341261],[136.47215400325308,-10.598506805282817],[136.75349578814965,-10.514331955534814],[136.89825541869661,-10.528069904332732],[137.032940622551,-10.582878459650026],[137.20488536468889,-10.75149069289712],[137.27806533665503,-10.98092428078799],[137.26638969141561,-11.125864824396428],[137.12876485696029,-11.543652145020578],[137.12515051821407,-11.694098311430729],[137.4048308119751,-12.150320868259692],[137.4462655024592,-12.342998519897554],[137.38922643130945,-12.581721616112986],[137.14561368437865,-12.898004064319636],[137.09337976461964,-13.135444026048663],[137.13067908039028,-13.293042471713978],[137.36915714274221,-13.638846155428681],[137.44938119148824,-14.164559694616962],[137.40283714614424,-14.395815528535564],[137.16559040655832,-14.738497707174211],[137.12277239974782,-14.864893855909802],[137.12938859359795,-14.976156893384182],[137.17704610426728,-15.075981227096738],[137.41404000242869,-15.299883424447051],[137.64204733212841,-15.639674866693836],[137.98153753370951,-15.818554818785183],[138.32984370550273,-16.171972010877003],[138.46573514489231,-16.253185799570009],[138.72126593807687,-16.263842197768199],[139.13127279299161,-15.995165448479559],[139.51601770586231,-15.901386027053661],[139.65885009746691,-15.901272166761357],[139.8383133378679,-15.963470024155038],[139.95044197940399,-16.051947551261772],[140.25706516720578,-16.515169285488039],[140.35368607648545,-16.584374719658793],[140.45267458341326,-16.606924709381506],[140.61460708325359,-16.570154220488625],[140.7040412061892,-16.499218826176296],[140.78492800678796,-16.36771199939858],[141.09821455881698,-15.066222708644995],[141.10504344823974,-14.928860590525089],[141.02661010963021,-14.522492485198988],[141.04335739314857,-14.196994784376612],[140.97536477528777,-13.756033194157171],[141.12453225675782,-13.184496544696795],[141.11545944661026,-12.911950763020931],[141.18274018641256,-12.634215039636684],[141.2005589752865,-12.248028420633505],[141.51535711886939,-11.60859976644379],[141.63781816809978,-11.193686647269136],[141.65830458461016,-10.451496664614558],[141.60222390075566,-10.181157878204667],[141.61503496205353,-9.9951791263007177],[141.69508020369963,-9.8268194555326183],[141.84874247456955,-9.6533172046214766],[142.02508152278349,-9.5687138907870892],[142.17140830038909,-9.5536115906168924],[142.31579705230385,-9.5817450872955412],[142.44574971611439,-9.6506791897660218],[142.67314574517488,-9.8213895077262645],[142.7903586205862,-9.9793105010223933],[142.87267711828954,-10.271307411771865],[143.03276874027154,-10.49119076634442],[143.24841880828404,-10.943466311793195],[143.37346977188068,-11.408242996597858],[143.63935761606305,-11.762539518271142],[143.69792274264503,-12.139104261091791],[143.87772566471102,-12.490877719949184],[144.13194758059819,-13.585846543351447],[144.23598340668019,-13.68662313581571],[144.60256529374158,-13.749904799768068],[144.77760274839093,-13.836465220046152],[145.10402180604711,-14.195163385169252],[145.61950832545205,-14.570523863285526],[145.74132559170224,-14.7356488090483],[145.78308872351491,-14.884271169539407],[145.79403454557169,-15.414680923781217],[145.9493347767658,-15.968793268755011],[145.97436000848003,-16.265830155243084],[146.02706741894519,-16.367836777552331],[146.29009333509768,-16.586940784339539],[146.38485615675367,-16.753120304527098],[146.42680267078592,-16.996489885720464],[146.60876107265827,-17.510299722853585],[146.64077060121346,-17.848045143503406],[146.81708089423162,-18.247740463571304],[146.8540230103535,-18.533539433153301],[146.89939346737773,-18.62952348018365],[147.04553517905921,-18.751502871034717],[147.68188930998025,-18.954235199647862],[148.03317089434725,-19.314626360557661],[148.51237824149899,-19.567408551596898],[148.64282475795764,-19.596669894644076],[149.01976612585642,-19.548501899397589],[149.15788293269929,-19.583530138282125],[149.28051085550226,-19.656094746741541],[149.4243367437368,-19.843260864497822],[149.53298176415882,-20.171252796679173],[149.54159571580985,-20.330951653910784],[149.49831027029325,-20.54379739889022],[149.51522593615604,-20.675793939526031],[149.94042592513694,-21.443785206637312],[150.01265915296028,-21.527357069041102],[150.23499470662455,-21.664774141579962],[150.66884415289559,-21.751364132584136],[150.90225428839463,-21.905637717620127],[151.22482543832919,-22.384763832590064],[151.2815597185492,-22.821393719911388],[151.33085802506565,-22.960991550715313],[151.6058330120012,-23.190268085164082],[151.80937829321778,-23.51240952058221],[152.18117422226285,-23.766736460390081],[152.5027949705842,-24.221972324588307],[152.63891883790092,-24.313656849523966],[152.81081747926225,-24.349773708626866],[153.19608933279835,-24.246340079058967],[153.34110625067882,-24.242633660784215],[153.56573588286608,-24.327866431083574],[153.70020635995829,-24.466153168858568],[153.83687233022602,-24.856097573217177],[153.8556517377759,-25.03658486046977],[153.64273550471495,-25.6692743280637],[153.66374252870722,-25.978041394214259],[153.61037574627255,-26.400013667446274],[153.63918362567298,-26.492518067841296],[153.88897486215203,-26.772261939045386],[153.95784769884048,-26.949658871485344],[154.03669423479039,-27.403920438873705],[153.97461729357553,-27.862727428129659],[154.06959427142334,-28.16920625959553],[154.10512377657665,-28.862870753640866],[154.07143854185827,-29.018121550812808],[153.84797723671275,-29.453936107535668],[153.75653227316644,-30.011276728075057],[153.54279803867061,-30.657365549335058],[153.54299221243676,-30.972218213613449],[153.39910881534567,-31.640064928574112],[153.22136121717708,-32.02994821791313],[153.0499615200788,-32.285502418870848],[152.89671721613433,-32.698655978766915],[152.48629207895149,-33.138572957467552],[152.12147800925854,-33.347538591010554],[151.96586603329789,-33.55794248972478],[151.67222830286801,-34.263857005158563],[151.36822903674235,-34.639416031326959],[151.25807719789577,-35.221121877769441],[150.88274145648671,-35.690740949544953],[150.66334318628657,-36.052031617417661],[150.46630954565717,-36.900296248256851],[150.48489360097713,-37.272646809686528],[150.42255191290573,-37.624443771851752],[150.37263126188375,-37.764035260444366],[150.28397357150078,-37.882853058025006],[150.16437286524274,-37.970451723246235],[149.59295517580495,-38.256948268360837],[148.39230134420262,-38.324391029501157],[148.01696691261421,-38.427044983380611],[147.71993562220391,-38.605100312531988],[147.54582860220191,-38.75936171763351],[147.43721406656127,-39.109830640019013],[147.29343826705042,-39.301131509222806],[147.12404468192605,-39.391771334831141],[146.86148786376791,-39.447742147762696],[146.62046523754066,-39.593209854489871],[146.48685215965054,-39.636867737508567],[146.29993235146515,-39.63438871101954],[146.16752409173316,-39.587202716405763],[145.66358609203877,-39.320274075908458],[145.36305338840194,-39.093252005014044],[144.83864256708989,-38.991310736153309],[144.74281468467333,-38.998481761540788],[144.5924905410628,-39.085284722084204],[144.54306296315374,-39.157818928732134],[144.51726620568334,-39.338329847042665],[144.62128985996836,-39.721530556788089],[144.70598549020397,-39.856465475296083],[145.07016672903742,-40.026434297144775],[145.36724785170045,-40.276521485271829],[146.24603064168252,-40.626904304554714],[146.50567973016027,-40.628587030428676],[146.88012444994337,-40.505415952673268],[147.10936697132087,-40.475450007254246],[147.21919478257982,-40.417767871764305],[147.31633722431675,-40.273558269775371],[147.28206788695476,-39.726713640539316],[147.30833999333012,-39.581798687940392],[147.44066358565377,-39.376945511210934],[147.607205442153,-39.273957348698197],[147.75127977955987,-39.243409806065202],[148.00300000407304,-39.250407667356313],[148.19917650782276,-39.29858840497198],[148.63718401829982,-39.620270242292996],[148.75440676303512,-39.785432134355254],[148.81460736407891,-40.018919557974598],[148.94749641268828,-40.2748752977447],[148.97262758047111,-40.414165113893709],[148.94396621803847,-40.600194869543657],[148.79528014815176,-40.925154879317233],[148.7931966601416,-41.735825621919687],[148.83886146378242,-42.269318460855203],[148.79604166216129,-42.424220978653068],[148.50574949824275,-43.055342507145539],[148.42627780635118,-43.38184884412771],[148.34270675180687,-43.500616472425342],[148.22853069007277,-43.590358868292519],[147.79394245060141,-43.747253104063226],[147.51043243288979,-43.957231417773492],[146.78772935866931,-44.116074828651172],[146.39436129533354,-44.027342398384761],[145.97553428049264,-44.041336910058732],[145.79471409402754,-43.979577012328285],[145.68168995972741,-43.890842774353594],[145.33200901962849,-43.448545009869804],[145.07669808251026,-43.210114826547525],[144.805748927407,-42.731295924670562],[144.65838554443604,-42.147286218264895],[144.3041320018379,-41.580512229742695],[144.09732316248957,-40.778939643563739],[144.01251305963473,-40.686141327163114],[143.60877025825317,-40.526028347576712],[143.45254665803259,-40.343525902710972],[143.34013810810839,-39.928381484821131],[143.36669048593666,-39.455151930109523],[143.29009283439402,-39.318406964921188],[142.41092946256441,-38.918465785801139],[141.92126033894078,-38.840968836825994],[141.42429460124742,-38.867807719252504],[141.2808843270422,-38.840923075518802],[140.90333041978926,-38.594992418141892],[140.47749455757162,-38.504500863915908],[140.10383193814829,-38.305382704684646],[139.33950420378432,-37.469312291000946],[139.24371461584684,-37.123175672214984],[139.28656937444063,-36.693532628785185],[139.22859520429427,-36.53025967914683],[139.08499998983891,-36.349801749835166],[138.80306336971478,-36.15382067708164],[138.63349437733939,-36.137537636762978],[138.23563828175597,-36.370109119999888],[137.9145556366839,-36.425982588369358],[137.47351744266763,-36.573126385250276],[136.67894262431273,-36.526464058846578],[136.35597600921389,-36.37823760377281],[136.18057842115996,-36.235708142945064],[136.09412929803099,-36.112867621759193],[136.04806652224349,-35.969893969565049],[136.03283501987701,-35.666739875512192],[135.93544448229488,-35.530658131377749],[135.80816735870621,-35.462781260759151],[135.41220694139832,-35.379599808776518],[135.13313322941096,-35.137446667179923],[134.87496286882836,-35.018500859183391],[134.72777139382453,-34.889821174608038],[134.63078823755609,-34.666470622589117],[134.66893064379659,-34.213117425555154],[134.63547338009724,-34.104390781451713],[134.36035182335723,-33.748037740929583],[134.03852443168375,-33.589211111305318],[133.92116083172519,-33.488346458810994],[133.73169010206581,-33.211061486719466],[133.53876270552325,-32.774959862137536],[133.42947607671124,-32.712445224406856],[133.05313065101632,-32.65655721248082],[132.71477095935904,-32.505780272639946],[132.06491310893409,-32.483341040324433],[131.27735834789291,-32.056101434403693],[131.14387722216728,-32.039151694508803],[130.76878796078643,-32.103022082403633],[130.09562448946463,-32.083969702883522],[129.23052939110781,-32.161202304269111],[129.06682825614408,-32.198432795856569],[128.22356588075689,-32.539526295695815],[127.4069291081576,-32.755711017129222],[126.78966767472778,-32.809493785559262],[126.11781866650135,-32.78848663736234],[124.97355716782313,-33.333497776276566],[124.57688730700147,-33.466926960178562],[124.4815706199369,-33.550752029267244],[124.26831273042716,-33.895444260429826],[124.03574741931841,-34.155725263136205],[123.67226584743986,-34.387029425308874],[123.22692965863273,-34.486942173734747],[122.79716177058722,-34.397394558900665],[122.13261775497165,-34.490460128675679],[121.78385919505726,-34.375018128045866],[121.39636492866826,-34.332268422519874],[120.85409660644804,-34.373238123316376],[120.47596757753192,-34.458728215750739],[120.11204630285049,-34.462236803316017],[119.96929435494089,-34.529959199635258],[119.68976592119988,-34.806355966647118],[119.3908185204326,-34.934499824896662],[119.04369230738348,-34.998432976142169],[118.35775862247472,-35.433557579402688],[117.9378841980226,-35.54855914775257],[117.5727217991349,-35.596734718906362],[116.38551028518036,-35.46936106665521],[115.7353165681874,-35.226023536275171],[115.32731443700935,-34.887864116350904],[114.88145862986349,-34.775687625542979],[114.64450757664825,-34.596751301074221],[114.52749838061433,-34.387219576763769],[114.47580804590939,-34.084086394273456],[114.4993472209536,-33.448310366914086],[114.54057892677316,-33.30692915587241],[114.62121070477831,-33.183692729536155],[114.73424736106539,-33.089292439622199],[114.99805972508189,-32.95934242321907],[115.09813375790581,-32.825662616445456],[115.2218817491281,-32.298442345432562],[115.21079993085954,-31.901618601692782],[114.62098204765812,-30.764176519467576],[114.49751079527691,-30.273082368897644],[114.45347622565266,-29.540245843347058],[114.42325637894952,-29.438192866274683],[114.19940947236904,-29.126077918475993],[114.04639869949081,-28.731502177782964],[113.70277125960867,-28.267953625090161],[113.55075828118774,-27.537251224925587],[113.29822378332726,-27.137351620188177],[112.76853992966269,-26.457575162021207],[112.50907141751112,-25.987626870288938],[112.42566058001172,-25.697364241934245]]],[[[123.07425426337178,-12.452472223882674],[123.10297654921148,-12.255831357361636],[123.20602724115955,-12.085910331649625],[123.36712738015162,-11.96955163920795],[123.56082791273305,-11.925136509266984],[123.77121142802217,-11.959054075056418],[123.93350759959439,-12.05968997010282],[124.04623813152558,-12.213831002437836],[124.09342151787823,-12.40739105736732],[124.06469923203855,-12.604031923888341],[123.96164854009049,-12.773952949600346],[123.80054840109845,-12.890311642042031],[123.60684786851705,-12.934726771983019],[123.3964643532279,-12.900809206193616],[123.23416818165565,-12.800173311147217],[123.12143764972444,-12.646032278812189],[123.07425426337178,-12.452472223882674]]]],"type":"MultiPolygon"},"properties":{"alpha2":"AU"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-70.385845828075318,12.162899958034407],[-70.511882876336031,12.32116971576588],[-70.565411335690527,12.567335334398143],[-70.505043610215466,12.805704211946672],[-70.384989816773029,12.970962984736397],[-70.22228457316578,13.07743523079561],[-69.982441410307487,13.111025100198177],[-69.796746884161735,13.053346001404131],[-69.614445107752445,12.915562969297557],[-69.453196976907321,12.679696435006766],[-69.39710228236919,12.456833190811608],[-69.442665929154629,12.212369917698439],[-69.561382672219594,12.051792825506309],[-69.733472445188241,11.95048252168467],[-69.98135591282643,11.930680393484495],[-70.205072337964211,12.013468296672599],[-70.385845828075318,12.162899958034407]]],"type":"Polygon"},"properties":{"alpha2":"AW"},"type":"Feature"}, +{"geometry":{"coordinates":[[[44.383978301013236,39.383977748758682],[44.291832089138197,39.552985813415383],[44.268742061806421,39.695900756345161],[44.302977785968132,39.885327696015963],[44.374631974751757,40.011119219993397],[44.479320278808444,40.111110165926213],[44.608266539305482,40.176918160718905],[45.043304122624619,40.26420760765739],[44.934865445629583,40.403603631345689],[44.887655636534149,40.534589506916319],[44.650909079608049,40.641827681929016],[44.504712858831546,40.842379478594793],[44.472958618272294,41.088521767366366],[44.514371824905275,41.402952493414652],[44.571983245099396,41.546581338225096],[44.711286057955448,41.697919635566215],[45.167965039517256,41.936396077970507],[45.358681379861096,41.943262280850853],[45.708816721739971,41.855474615510992],[45.776710491892835,41.991331329611597],[45.879855555974444,42.098513731875094],[46.166207573577275,42.315474854318495],[46.351573365453092,42.384528562081968],[46.549212087009067,42.376292841387702],[46.963841244131054,42.263916659915907],[47.447603954731136,41.911493725739049],[47.636359269662108,41.734505322175387],[47.776639706449572,41.873071187471325],[48.057090920126569,42.000536522791087],[48.215769064160526,42.194133844025536],[48.332863474358852,42.28280768180165],[48.568222665340464,42.344170244155798],[48.7601005512117,42.30782454742296],[48.923420149703333,42.200750547283135],[49.435427628477044,41.693343966038789],[49.632131406765147,41.332257584421782],[49.78499055111071,41.179616791030767],[49.92249596931746,41.083556741293499],[50.123939148624224,41.058420479787721],[50.40963452515512,40.949305709149563],[50.645970388772945,40.779336455139642],[50.763483627381895,40.615359640494681],[50.861754359648032,40.341176741917259],[50.858112351011364,40.193955153870412],[50.811739439509502,40.054180287414432],[50.726658467835264,39.93397758997002],[50.610250196741241,39.843774626410912],[50.424046414377194,39.783231728737569],[49.992938978486265,39.807991111016769],[49.924798045442401,39.781618730647466],[49.840291969920592,39.551110001054965],[49.861574174458461,39.318407528122798],[49.831597789173237,39.176519403965386],[49.550104694729022,38.711278009489888],[49.372240361104204,38.57570300472927],[49.356027638186667,38.324734960264891],[49.275260186015778,38.144967612702246],[49.172130895432268,38.038489110462955],[49.04231506765688,37.966926739943446],[48.713563339573234,37.905074073758875],[48.56868196153043,37.903444154056672],[48.386296592362513,37.965512988636405],[47.66835107974876,38.476839036903058],[47.529781005457444,38.675159351829883],[47.501018400696481,38.918055424454309],[47.220773744696693,38.73753454171392],[47.080822916125129,38.686161958409578],[46.731406954938087,38.438759487128245],[46.539340405517322,38.4046836457429],[46.357109314911057,38.441468349913869],[46.209322006782266,38.387163613870761],[46.063411223025405,38.380641300870664],[45.380639982813577,38.51634943682609],[45.229522318739662,38.573563403440431],[44.784056804857045,38.9046745062643],[44.383978301013236,39.383977748758682]]],"type":"Polygon"},"properties":{"alpha2":"AZ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[15.439536204494244,44.363945709333983],[15.280496200501087,44.561636241873558],[15.238967198634207,44.81193728368094],[15.343861312882128,45.345623143896788],[15.403820621321374,45.475176229009875],[15.49794607373167,45.582505254624529],[15.618564894354259,45.658861136142896],[15.755844661254125,45.698019610714191],[16.097445289413301,45.685707913818277],[16.255198064412845,45.633626128916987],[16.422163935585388,45.704523126436015],[16.666310914885635,45.709603554720651],[16.836360826251511,45.769448006473553],[16.978669062604407,45.772655053110213],[17.312723193761428,45.651122232203761],[18.29795614168874,45.633840547750701],[18.73691642039282,45.571632686740365],[18.88283163861837,45.52604554616741],[19.085717327546913,45.381501326494352],[19.374402721442532,45.393356133717241],[19.562025857333108,45.33053592217199],[19.723491004191121,45.198172738341484],[19.828515955696108,45.023603751859973],[19.84878554088786,44.770763093621127],[19.763855798394342,44.526245517404718],[20.002958119163413,44.315554116748025],[20.06386369711894,44.182269065671896],[20.080494253170805,43.98813969739615],[19.98321936441295,43.742057341847115],[19.968197077515288,43.480734044792705],[19.831743418227305,43.238001199571109],[19.679992469715817,43.117727457446783],[19.470254965260473,43.063255412350372],[19.296107565487837,42.871578367247643],[19.064701473384666,42.79431305439104],[19.023012979033709,42.728208020476416],[19.033473158632322,42.583895441391633],[19.003218274258607,42.445379854564514],[18.935298707932422,42.320925612641673],[18.835175715990516,42.22053979538083],[18.613896673777976,42.092530355737658],[18.384758472225201,42.062605402433469],[18.116310424795401,42.136452989426672],[17.59420709094379,42.417350393502907],[17.27773557896057,42.544377117489582],[17.176443726933787,42.650844841576912],[17.104120658760888,42.805236583476685],[16.927578439890446,42.964713358208179],[16.834259723326429,43.105552275336066],[16.396603961925315,43.392534089742064],[15.984041728167199,43.737017435767712],[15.792224958431737,43.947390938908583],[15.693505026483598,44.176116329187124],[15.439536204494244,44.363945709333983]]],"type":"Polygon"},"properties":{"alpha2":"BA"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-60.1462927144105,13.29021643169742],[-60.096118447200986,13.521595113114737],[-59.982040109346279,13.67360821092454],[-59.773900254306753,13.78644150409882],[-59.517656114010414,13.811881168089842],[-59.286496392617281,13.713444337604484],[-59.026326126114512,13.450522945668759],[-58.942535202032033,13.272487135782457],[-58.933926440225434,13.075907488360288],[-59.001833085440083,12.891228425940753],[-59.208442376072881,12.671426507688743],[-59.424370399407557,12.572103739661877],[-59.628093178576734,12.573905654829954],[-59.85731982297402,12.667062083676434],[-60.001863500806579,12.790247627794704],[-60.119946608304076,13.001672407660354],[-60.1462927144105,13.29021643169742]]],"type":"Polygon"},"properties":{"alpha2":"BB"},"type":"Feature"}, +{"geometry":{"coordinates":[[[87.644439829452381,24.302066308498375],[87.533520485190536,24.529282762020706],[87.547950822310057,24.781715293706618],[87.741061312940531,25.202224446207506],[87.953987201164509,25.373805547503078],[87.765938769319945,25.503173852922568],[87.641137567868611,25.657786678977001],[87.59283968526799,25.799226806151282],[87.59701035093039,25.997879949625439],[87.712847699559077,26.32802278561136],[87.846383588911081,26.490601039205941],[87.86256973461289,26.631721587673692],[87.943683459707302,26.801402885185119],[88.155694651999411,26.996908480891538],[88.336141512298326,27.064548071268753],[88.575451461223452,27.045919378609089],[88.84661619119035,26.881649333504946],[89.033951887260983,26.909843509542242],[89.177364915066676,26.884194962896977],[89.345652542328082,26.788257986677955],[89.446202668053132,26.668378729544891],[89.719090852864909,26.711242124408738],[89.944490667240387,26.632014930300475],[90.081887718805433,26.498181398610793],[90.282176907244747,26.138566288047713],[90.320473586494046,25.988537823745219],[90.320508070561701,25.700083233925124],[90.495359626909178,25.659295897077442],[92.145911936524968,25.659892494392544],[92.786986210821738,25.329100587226076],[92.885600435019541,25.219133855574089],[92.947782191960428,25.085152974892921],[92.964021864894775,24.765280928678383],[92.856290005641199,24.545406458286052],[92.631696332189421,24.39310881562194],[92.503545627122293,24.11142514353352],[92.621879477051635,24.012988021907475],[92.717914310562975,23.848311218050664],[92.828246012829339,23.399348972060025],[92.849225961441917,23.098392387047305],[92.980730371024521,22.785919276274459],[93.131399031084086,21.307969625820157],[93.110554158931365,21.163398168336581],[93.0488176384996,21.031019857956952],[92.821626927153559,20.833025804778043],[92.804987700729384,20.655974797090675],[92.71640049021606,20.482351731296898],[92.568158611769618,20.355793411472277],[92.382790480217665,20.295532987916683],[92.188466611255137,20.310728276449609],[91.977720419050414,20.431232902929956],[91.646459731533312,20.888472216132524],[91.554025710255402,21.137149951956701],[91.424451751254011,21.287027303389937],[91.373568346508279,21.416292267250075],[91.331268839719556,21.695485185834851],[91.139573300921896,21.614534716077426],[90.907624452218684,21.625060525093637],[90.655558682038873,21.557761560339664],[90.457626133306618,21.384608968894995],[90.247582738249662,21.325044109970964],[90.048818294263086,21.329376666607207],[89.90061446851675,21.389465446729258],[89.677454326680447,21.279701899706982],[89.244773284971203,21.226046257123311],[89.104211273024063,21.242054654979164],[88.934367347849388,21.322972313491338],[88.693835484799351,21.573323425754609],[88.620573141557003,21.712574653977178],[88.556582764579758,22.167634396519087],[88.436572579568292,22.507936892205304],[88.370355375450288,22.913255381936629],[88.254114980309311,23.075168267400166],[88.20655323149127,23.310007799135395],[88.076593971496933,23.580346816956894],[88.076249276865781,23.766671903632894],[88.141286200051269,23.956903765699778],[87.841769027731658,24.088893436178214],[87.644439829452381,24.302066308498375]]],"type":"Polygon"},"properties":{"alpha2":"BD"},"type":"Feature"}, +{"geometry":{"coordinates":[[[2.0496497760417878,50.94241584606128],[2.0252828935638547,51.085842489717884],[2.0432353954447473,51.230212343733562],[2.1019874266759691,51.363303086832005],[2.196565052731926,51.473847284879263],[2.3642029039608015,51.570368523319118],[3.1890293137992591,51.850818214057981],[3.4353417557580856,51.870093747312175],[3.6760654721432631,51.756973611925034],[3.8348146121349562,51.729046833893221],[4.3993389371185199,51.963608881275945],[4.7527117215396641,51.990847095155281],[5.0836144362381201,51.966047655371447],[5.2743889908082009,51.905569691092147],[5.4202006245241936,51.78507456899888],[5.6234665359905067,51.762858497997897],[5.9925790588911232,51.611369657733739],[6.1626552921845867,51.496049221556994],[6.2758230186537034,51.345703063775012],[6.3267807391917321,51.118255935684743],[6.5677941797518464,50.970337323607005],[6.7705967976922983,50.706933818830741],[6.8333254983737879,50.537121054530367],[6.8600431763996745,50.252174576192417],[6.8188572285121261,50.108305753382524],[6.7369414767072158,49.983068657894265],[6.3771321908286618,49.695031244640418],[6.3366289464819321,49.440896895351656],[6.1552638370056751,49.18737222129063],[5.9341229054811082,49.068321205672291],[5.4215100997849941,49.018521408180774],[5.2748821793424332,49.068436180265117],[5.0476213909076533,49.216490238675632],[4.6373654324814613,49.344614044812069],[4.4781191610249511,49.47559072922391],[4.1058881314222289,49.473701353169915],[3.9621553261877227,49.508209784259073],[3.7979622797600809,49.616207536896759],[3.678130386123514,49.806714333353597],[3.4441992445507257,49.870585523097937],[3.277891927094235,50.021150880633691],[3.0356954587738958,50.091891326880543],[2.887041981206707,50.21490332518637],[2.7176550947544644,50.227200785186852],[2.504908345725112,50.32098713409426],[2.1892062669149071,50.586709683921953],[2.0496497760417878,50.94241584606128]]],"type":"Polygon"},"properties":{"alpha2":"BE"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-5.8339194753862618,10.034276599436714],[-5.9608017106476376,10.184033869684839],[-6.0232624086881081,10.420371897926064],[-5.9637790384575666,10.78878801969428],[-5.9901760909639821,11.03496093801901],[-5.9687895282692347,11.187128993375065],[-5.9022029856999678,11.325616301397901],[-5.7573478290489319,11.467875286475715],[-5.7879192627531308,11.832978768402727],[-5.7637218656165885,11.981575871636773],[-5.665308159660035,12.155853043218722],[-5.3705035539682315,12.391552640374302],[-4.9582272803679874,12.526986815929432],[-4.9801353978211775,12.68496482889393],[-4.956267761498899,12.825259893841654],[-4.8212426410173173,13.037144888424701],[-4.8083621266011676,13.259247957793901],[-4.6910870196460648,13.463199807945513],[-4.4437947884407514,13.711262040640356],[-4.1172366550510233,13.872203888027981],[-3.9334547464496747,13.901765886058206],[-3.7123825694789261,13.843159974666884],[-3.5949181779594341,14.01861422346898],[-3.474013625319873,14.104357761636873],[-3.3309978438179733,14.151281896755098],[-3.2174389227435158,14.314964895593704],[-2.8040067594542526,14.677627421986008],[-2.5775926002607696,14.759141973878366],[-2.3657222022913222,14.765498480791317],[-2.1682144171865052,14.916597945409755],[-1.8426187618320014,14.99268731461863],[-1.309525331389515,15.25168677043628],[-0.97725656438376562,15.498005071144819],[-0.78092444379482795,15.556315470991134],[-0.12756923529478381,15.547318893070518],[0.41730136346372554,15.369523838860962],[0.57480915490777285,15.260748679666618],[0.69597306519779556,15.055389574770647],[0.7171760191943829,14.913006736199968],[0.69586671163906166,14.659393751360351],[0.848849953899383,14.425546076578788],[0.88014322588918814,14.203345207145791],[0.93548901279558594,14.129282223332558],[1.2296222140170221,13.9929712687804],[1.5965362338194702,13.663119719508984],[1.693426428091463,13.443132966279043],[1.6727243392136955,13.193486490071056],[1.7263840148577068,13.150952459751721],[1.923861730250731,13.206171200586651],[2.1727565390355488,13.196397172137544],[2.3101634860904414,13.15680790337184],[2.465439475236646,13.047047087724875],[2.690001407956609,12.652271433684344],[2.725972498101279,12.465782985174677],[2.7013768388723687,12.312595278969297],[2.8159282029551864,12.15708640937482],[2.8741130757560769,12.017654066916322],[2.8718336166885745,11.767570486177076],[2.7080040428706131,11.422610541263742],[2.3119040844456191,11.044450022459824],[2.087438184281059,10.930283714355463],[1.603019564011674,10.913998223620952],[1.3744189448819326,10.667676496542652],[1.0570075504251624,10.510963472912554],[0.43385784316786491,10.458708125587391],[0.20933606568017976,10.541763496704158],[-0.13849022839801617,10.616138692836085],[-0.4524449480762005,10.460648220451171],[-0.59728180840507716,10.428460717743073],[-0.74518748806274715,10.440193635773687],[-0.89027193768705304,10.495352039046509],[-1.5102012755304464,10.519254781859031],[-2.2945109353236033,10.488592159125467],[-2.2685910528898368,9.7780169840311704],[-2.1962276070687139,9.4916879689264544],[-2.2162815021219551,9.3408762665053899],[-2.2807615977130671,9.2030769719988257],[-2.3836979839981436,9.0910482762440523],[-2.5376643183609793,8.9804862101315255],[-2.6786322557879578,8.932769905079553],[-2.921289896599252,8.9371416712221396],[-3.0941051618633741,9.0101236641302229],[-3.4510895662648124,9.3990360749429218],[-3.7716068542950993,9.3985340205015557],[-3.940238428531226,9.3342287194929625],[-4.1145708402548697,9.1958809415775704],[-4.2526564279454071,9.1523506500477509],[-4.3974249691703431,9.1502093233291664],[-4.7919403347770952,9.2422623659291006],[-5.2738654361038959,9.5333463997009993],[-5.3708400359140001,9.631699700613936],[-5.4737026499050456,9.8225767303635898],[-5.6550117320267068,9.89521076635417],[-5.8339194753862618,10.034276599436714]]],"type":"Polygon"},"properties":{"alpha2":"BF"},"type":"Feature"}, +{"geometry":{"coordinates":[[[21.966263173294745,42.640177297666362],[21.990159101015855,42.99274600135017],[22.121451842074531,43.203635369788479],[21.879472350065377,43.683531602378444],[21.873280631181959,43.840130243530346],[21.947315632162386,44.167342294876292],[22.088175569993552,44.380345291821946],[22.247734794432812,44.477359238647068],[22.449499803826896,44.667278471667636],[22.636105836430232,44.732748066225113],[22.784845236358734,44.731119934458441],[22.926523627345382,44.685806653496073],[23.294767572334955,44.500915969532706],[23.445656039324536,44.35261496341267],[23.984564067851526,44.286561077316499],[24.486376268524573,44.291013107890969],[25.463329311513419,44.181280993062401],[25.588419140721058,44.216931383267628],[26.087316675213426,44.490522194654062],[27.141151052087992,44.664209103652965],[27.546422180982933,44.516135280279016],[28.020849040389081,44.468034693355307],[28.154262420891932,44.407858733173846],[28.31665939867797,44.268234513533997],[28.773276918620891,44.205238632323535],[28.970179887692026,44.060923103182958],[29.07528237242683,43.840579691929705],[29.051635732743978,43.402258247165499],[28.971869818539208,43.215675085390792],[28.810202993141669,43.02754799021529],[28.688895402574779,42.942344948436833],[28.547937407156432,42.896454498612414],[28.393347433112783,42.891658108935466],[28.37651329670237,42.640471522044038],[28.27784741937862,42.436489084290734],[28.455287307098281,42.204585369047962],[28.514096260758748,41.969230421050739],[28.476125039821198,41.778191870900805],[28.368040876581464,41.616156828642517],[28.250494775451262,41.528732883291546],[28.112656196122252,41.479128869742233],[27.584999076486536,41.42355881818996],[27.40015284339696,41.43953431895045],[27.150047862849785,41.553590033394933],[26.891426979995092,41.49698171437992],[26.634434390189071,41.323637148755985],[26.56522450694872,41.130908447257234],[26.432477146337263,40.983938047915494],[26.037241425732574,40.825263022158651],[25.196352215802037,40.746837374129989],[24.884209097136647,40.861707992318685],[24.617904429914187,40.881251085608049],[24.336008433111495,41.02651070787298],[24.27122303234767,41.028606081636411],[23.75459456553293,40.901487831976887],[23.401282890159209,40.885964427597244],[23.178532865783392,40.822811511164815],[22.838725292405694,40.842539684160428],[22.700603475205249,40.885419337390395],[22.54631650130165,41.000211145943858],[22.446767354911653,41.164745631934878],[22.416681994681294,41.354683781676087],[22.448113198468338,41.606909936460418],[22.288810207985563,41.700310844741061],[22.014118108332664,41.938549285614904],[21.87518025020297,42.141314529242777],[21.849652101541984,42.385784948866657],[21.892573503953084,42.527900832631005],[21.966263173294745,42.640177297666362]]],"type":"Polygon"},"properties":{"alpha2":"BG"},"type":"Feature"}, +{"geometry":{"coordinates":[[[49.970778183586681,26.057580368566576],[49.95292684962179,26.205721324363186],[49.979584507732596,26.3525333884884],[50.12631949571238,26.591841388731929],[50.243437795726621,26.674440770504315],[50.379176181294753,26.720423862513929],[50.581562378014809,26.745823774598112],[50.710775539308464,26.724639779623594],[50.924263132917673,26.608457117150387],[51.05774323866779,26.405335417045862],[51.116328991865444,26.034155014260442],[51.085255009103236,25.737460577574485],[50.987878885234714,25.525428729644439],[50.846836560592237,25.387640492885676],[50.61430202741716,25.308730071952102],[50.418529884211303,25.332220569482523],[50.247062091605386,25.429572157186549],[50.114043900268697,25.57891186181536],[49.98088343579677,25.845156844202894],[49.970778183586681,26.057580368566576]]],"type":"Polygon"},"properties":{"alpha2":"BH"},"type":"Feature"}, +{"geometry":{"coordinates":[[[28.514735602122755,-2.7348424354930478],[28.535121652509567,-2.5781972205224535],[28.649149206595212,-2.3226665183235129],[28.75088694523431,-2.2124011081896966],[28.880756418504472,-2.1372815498316498],[29.153020751312042,-2.0985963089926361],[29.45188467148823,-2.1967968211686024],[29.558920131779814,-2.0050646933583138],[29.761543362102145,-1.8691253423786307],[29.955338026593996,-1.8403814617935301],[30.117800124509106,-1.8713758352576988],[30.382437723135141,-1.813909577328964],[30.529575498108056,-1.8281290377648745],[30.851754491503474,-1.99921180824075],[31.00587178955513,-2.1879581596270872],[31.052695119564,-2.3779247490588524],[31.029016290714345,-2.5519729423412771],[31.216696274524242,-2.7413301945087798],[31.298588156671141,-3.0094653988296178],[31.276324673045753,-3.3907432689393655],[31.194724203011724,-3.5680788124234812],[30.854824228462249,-3.8843636474051348],[30.545438661537652,-4.3871554390887075],[30.258885586480559,-4.6973385779235173],[29.855199209686177,-4.9363685276530811],[29.297589415626515,-4.9377538161107211],[29.162877947361171,-4.8874176843337356],[29.048065437723906,-4.8008209553235259],[28.925645820001332,-4.5964830725181001],[28.854099002574678,-4.2604260144829631],[28.714853735823628,-3.8869305076427429],[28.718974510311124,-3.2380459129251999],[28.563051242917844,-3.0094517720161673],[28.514735602122755,-2.7348424354930478]]],"type":"Polygon"},"properties":{"alpha2":"BI"},"type":"Feature"}, +{"geometry":{"coordinates":[[[0.47086504564569237,9.9814254244919969],[0.33412476647692341,10.13079172258338],[0.26384117050480399,10.372940706117658],[0.30693922201405899,10.847359608961435],[0.48414130250609899,11.269673505564052],[0.83074582962872778,11.639814466671551],[1.012888809727909,11.733809499578967],[1.2056005755916159,11.895283566722151],[1.3905236304999624,11.945573925575653],[1.6712422422142801,11.921021666212313],[1.8259451309781554,11.94817026046827],[1.8942598014434553,12.026431005018233],[1.8663014282215884,12.228886423761939],[1.9087724562732189,12.423526485311113],[1.9891106005240247,12.550026325714907],[2.1033256027618905,12.647038491486281],[2.7242951630731014,12.876978650400936],[3.0376435295697477,12.841292155868029],[3.2196550416569085,12.732522138865852],[4.0298385513956401,11.943318103872629],[4.0950788526911541,11.703229420009171],[4.0471508903041213,11.484050802456602],[4.1914781267719494,11.234608844568854],[4.2411614372685422,10.92563360920923],[4.3233813280804583,10.711049808638688],[4.3249231306038203,10.511405243121533],[4.2433833575298623,10.241298343410794],[4.138882315517618,10.082721315326912],[3.9890065510549579,9.6557752265467727],[3.7831124586905256,9.458670979560555],[3.6319405121489834,9.2579023885385325],[3.5787580240795736,9.0142354719314906],[3.446374989647508,8.7864415253446726],[3.2373427805836346,8.6259798725145309],[3.1936103396892679,7.9464006067255326],[3.2819581777178373,7.5308433519500424],[3.2320041268723356,6.9609591781053064],[3.2721694045889986,6.6647420481462012],[3.1693498191546574,6.1809796157942642],[3.0664595803337278,6.0226950088558873],[2.9544860251891656,5.9354276208140382],[2.7758647262368101,5.8743142049478161],[1.5928350809843266,5.7179619535630879],[1.3575355888506166,5.793232536199155],[1.1869952288024548,5.9719711643783997],[1.1259876122512198,6.1607366948256335],[1.1531336235252705,6.386807168254939],[1.0326996481696022,6.9542039696560787],[1.055197612850221,7.1452475589852851],[1.1228135496404619,7.287067627899277],[1.0992540194093774,8.893023325774573],[0.90988272195392628,9.2090043225309159],[0.83921517555041258,9.7153387571075136],[0.47086504564569237,9.9814254244919969]]],"type":"Polygon"},"properties":{"alpha2":"BJ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-63.34793414991919,17.750862982789489],[-63.37326195511443,17.957151062594122],[-63.315380854868309,18.156935446030101],[-63.188635973510607,18.310582344754241],[-63.011967824030414,18.40256059977775],[-62.81341126339975,18.418274500262505],[-62.561831971868806,18.34808602486881],[-62.408705788350339,18.219647247116885],[-62.318116417271504,18.04149643023165],[-62.304537919509372,17.842097904389906],[-62.372521846005725,17.649297615528976],[-62.527627902193878,17.478742184274704],[-62.716771100697414,17.390162032860687],[-62.928775241145821,17.382202309594668],[-63.113757437948969,17.452641409157774],[-63.255708880145725,17.57969818615085],[-63.34793414991919,17.750862982789489]]],"type":"Polygon"},"properties":{"alpha2":"BL"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-65.153743299057751,31.86834570328401],[-65.284443515550592,32.007704155821344],[-65.359491074343225,32.233556335492125],[-65.338187947950914,32.423423398698958],[-65.21495518936625,32.627028483002832],[-64.957823991402506,32.810842424271307],[-64.773644795315349,32.879562223755279],[-64.552545644332483,32.867327117030058],[-64.334381951032469,32.752014096401219],[-64.197937617143992,32.546404190145267],[-64.170861832993921,32.350111233341003],[-64.222161254629825,32.158715328687521],[-64.368783842942761,31.949498792211457],[-64.531522704980446,31.835744690874652],[-64.757152887223938,31.764495895010811],[-64.967595365278825,31.778473202087344],[-65.153743299057751,31.86834570328401]]],"type":"Polygon"},"properties":{"alpha2":"BM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[113.73592412227461,4.2155205941673968],[113.59778809652435,4.4128974471488629],[113.56455440946043,4.6034394989696477],[113.60612417542313,4.7923381649939039],[113.71627676968043,4.9513260810325974],[113.92450981803978,5.072479625239577],[114.20393291682895,5.1105720878629102],[114.62646386880752,5.3982512589691556],[114.95786364823472,5.5207192276713473],[115.15350964228537,5.5047723250379033],[115.29196285225051,5.4522942474607712],[115.40897633594064,5.3615689223825971],[115.55403239669182,5.184476914337055],[115.70229293895429,4.9081764791700886],[115.82556677997971,4.4108299420885002],[115.77625327906692,4.1623489680996011],[115.61145230231854,3.959880525884075],[115.41332121758333,3.8687576275921325],[115.16607765226924,3.8545757617205845],[114.93295203071237,3.6229332016493454],[114.6541117151084,3.5263369012574244],[114.45788827983672,3.5474110121696745],[114.28816453852821,3.6376669138435918],[113.96784485529788,3.9111768673550253],[113.81420830123737,4.152487070327564],[113.73592412227461,4.2155205941673968]]],"type":"Polygon"},"properties":{"alpha2":"BN"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-70.037742228557974,-17.558516156181238],[-70.138616507945713,-17.330775487929579],[-70.117097896238519,-17.082625570404097],[-70.014322821581544,-16.911021523483935],[-69.605291329613905,-16.463957214315975],[-69.681567769317198,-16.334677684343639],[-69.913217155610056,-15.726776061729126],[-69.90619710092632,-15.521054358762628],[-69.776115221424661,-15.226228128854617],[-69.86184347232647,-15.003313990894078],[-69.873075131457611,-14.867220337464412],[-69.806787948815426,-14.572487078663958],[-69.46483040656689,-14.119463416902414],[-69.564802248027817,-13.777520841536164],[-69.570690997452019,-13.626668938770035],[-69.48063036240697,-13.366156184970054],[-69.468807165769178,-12.78300740556881],[-69.397299554399069,-12.607278549199229],[-69.29461350542968,-12.487932204092413],[-70.037035233549815,-11.150719107333682],[-70.078208338792109,-10.957046812123508],[-70.019837830590049,-10.717477875090486],[-69.854068608336462,-10.534937587010461],[-69.621216006689465,-10.453820032035438],[-69.166756981114702,-10.459849400307162],[-68.638071539340572,-10.568451144693904],[-68.338656409967754,-10.280683877513619],[-68.209203672697114,-10.222677331801849],[-67.933283124223564,-10.169027712437416],[-67.69779646536881,-9.9762731498738422],[-67.361621639153626,-9.8320535255536772],[-66.782937575563267,-9.445216163601339],[-66.33341351981646,-9.3310921801825479],[-65.703852786285395,-9.28889922806027],[-65.371978622118917,-9.2130120498190227],[-65.180810953831582,-9.2632751015578787],[-65.058549373204968,-9.3478760557616365],[-64.836015352780194,-9.7123127259051305],[-64.799899933196045,-10.18029234705044],[-64.916002483206242,-10.532552368307176],[-64.834564688678668,-10.867516850331182],[-64.871014654388873,-11.182046899332045],[-64.611999894725045,-11.597279268963055],[-64.210547005043878,-11.853492393353319],[-64.079817585017224,-11.994808165621331],[-63.957382007076561,-12.015212211594465],[-63.720534050083579,-11.979278215533387],[-63.576894835529593,-11.990903670456367],[-63.208122054040999,-12.17555395374367],[-62.992906766510224,-12.174961764027543],[-62.851154180642446,-12.218655956220601],[-62.693729391221218,-12.337438240263225],[-62.5521128430141,-12.531549583122631],[-61.866053498881726,-12.728171252485968],[-61.749670251367618,-12.822028694760188],[-61.59493408939565,-13.026614660393355],[-61.083822861049271,-12.990032954872465],[-60.936352191372791,-13.010185641887125],[-60.194101960169057,-13.399899692543261],[-60.071096113026307,-13.544762821860383],[-59.917295287651768,-13.910027349769884],[-59.928335704822175,-14.194419611427559],[-59.805582266850735,-14.538669473584775],[-59.774325976659348,-15.061489231685389],[-59.800197270548246,-15.248109282431978],[-59.755443282367224,-15.367064030047795],[-59.703140857315802,-15.786008310825862],[-58.590116994335965,-15.82602435057068],[-58.30221643140186,-15.786499355067283],[-58.156342683365892,-15.821884241934306],[-58.027544846886556,-15.898970932762838],[-57.902084750618187,-16.054147415718806],[-57.847301482999427,-16.246029593649073],[-57.86193764123125,-16.596603500381978],[-57.952934692081406,-16.806015647681015],[-57.930614638639,-16.934630107557684],[-57.82684947937296,-17.01160032727222],[-57.55961844970021,-17.093474940579402],[-57.416144849052436,-17.235651933916305],[-57.161373564651257,-17.840137941488486],[-57.028737233474459,-18.036701445909678],[-56.996046432897288,-18.22391702321281],[-57.035580207713309,-18.409807962949305],[-57.143785257755852,-18.573743675256218],[-57.234995398958638,-18.849098435901745],[-57.219351076426932,-19.091723492401904],[-57.256314925457488,-19.238094282824814],[-57.421759824561768,-19.447759804384898],[-57.507917764151422,-19.626493128082888],[-57.376147033526479,-19.857621943102433],[-57.361640505341661,-20.004277089438673],[-57.39051751384514,-20.148791066917571],[-57.526618777017241,-20.351338588927824],[-57.890706838842441,-20.607707065204416],[-58.15863594727147,-20.664355309929714],[-58.39981799559699,-20.602814382644564],[-58.519305933056941,-20.511540967585805],[-58.606255315426296,-20.388870643544045],[-58.65835424778323,-20.124487642966297],[-59.240804392995777,-19.791011657838361],[-59.955094869028024,-19.797975772119145],[-61.390809296432678,-20.08665125861484],[-61.476386141450199,-20.291231311461829],[-61.7697338534701,-20.725077765248351],[-61.788288020770935,-21.17200970684047],[-62.192244500756544,-22.432041261294387],[-62.303916514346035,-22.59314868234242],[-62.423964863602379,-22.678743443084592],[-62.563758035389093,-22.7256042833192],[-62.759737414923364,-22.721323800490939],[-62.897351227739343,-22.668403976720143],[-63.097907033763491,-22.507999517728123],[-63.654972062411844,-22.526094355135612],[-63.75436283468833,-22.703300846382643],[-63.870815062878165,-23.035621185575376],[-63.952620563662705,-23.160543964508271],[-64.111699218544715,-23.279303790493003],[-64.354392348945794,-23.326389468605985],[-64.546339933189529,-23.275733377067176],[-64.703446115382746,-23.154376209072602],[-64.951443609847132,-22.644641587124578],[-65.112777634332886,-22.603361506699226],[-65.820264730333747,-22.59693042560594],[-66.058281511371447,-22.507902157171962],[-66.359465856806636,-22.64571494690437],[-66.461573361302783,-22.777070507566151],[-66.650337723687386,-22.903277866535781],[-66.8053959711212,-23.134847361320162],[-66.95033051444257,-23.257517035099966],[-67.130551351245117,-23.317302657913537],[-67.747984404626891,-23.387351312196088],[-68.14963784279638,-23.243366704394795],[-68.260174926071102,-23.146651880606488],[-68.337841116660599,-23.021991675414128],[-68.375934690960761,-22.880142870285837],[-68.383733476992447,-22.557274062844037],[-68.438028695184855,-22.330816362084594],[-68.539129840734574,-22.172487084824404],[-68.664936803115012,-21.762530700814473],[-68.701144756056976,-21.453242015166765],[-68.987539091978888,-21.157870830861743],[-69.042863085268692,-21.024189136477933],[-69.06290675942526,-20.824990670231106],[-69.184386397730492,-20.681064473254914],[-69.241949798522597,-20.550455898552922],[-69.252167287006387,-20.032552421222107],[-69.184474369580897,-19.834456463285875],[-69.194053351917844,-19.657972973867093],[-69.146510119418778,-19.501284566097162],[-69.357225228937224,-19.281870929754774],[-69.428905582373602,-19.161943297169231],[-69.613034742994301,-18.346447996445228],[-69.720950826585891,-18.23233232052868],[-69.81458811974565,-18.014012193912375],[-69.927744500439701,-17.869526635351672],[-70.037742228557974,-17.558516156181238]]],"type":"Polygon"},"properties":{"alpha2":"BO"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-68.781804621969997,11.972794370743326],[-68.867946462789291,12.203827454043203],[-68.848433008658844,12.443622758355151],[-68.75717205930421,12.616790709677785],[-68.561993690464647,12.76286641922928],[-68.370120680968384,12.801602041971591],[-68.173836768875603,12.761953094185259],[-67.903584295502895,12.618489868527073],[-67.759115873844905,12.425695322589988],[-67.706095866493342,12.152367246043584],[-67.738143502912834,11.968321974584732],[-67.85614472146618,11.730211391961411],[-68.04266251501511,11.579595444725051],[-68.277851015015173,11.533115122652312],[-68.507641278212077,11.601456075324668],[-68.651722852580761,11.72906621932974],[-68.781804621969997,11.972794370743326]]],"type":"Polygon"},"properties":{"alpha2":"BQ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-74.363496842837264,-7.9008184027299704],[-74.447323959350712,-7.782380991726745],[-74.499929897376816,-7.5967548416389405],[-74.440471009853056,-7.227229051830995],[-74.374904721368665,-7.0940657426831706],[-74.27291266169162,-6.9858438954088022],[-74.230617722103673,-6.7429175179290439],[-74.161344243253311,-6.6104925881204251],[-73.711621904075429,-6.2486762511166027],[-73.735211503259677,-6.091154867649295],[-73.71242160707483,-5.9490207126457388],[-73.456234508092663,-5.4562034489201263],[-73.367821263363027,-4.9860882671189932],[-73.250091014596549,-4.7792615420412305],[-73.137371453164462,-4.6901702725716259],[-72.916273351517049,-4.6040924016185283],[-72.595134308858817,-4.3489756043908177],[-72.037371615632495,-4.0432224928502798],[-71.234445009412781,-3.8893433904482348],[-71.041027086772488,-3.7358514867914963],[-70.892328969058909,-3.6823010344634861],[-70.361513687245719,-3.6525855085266046],[-69.925752185044317,-1.237077875718914],[-70.068893339110531,-0.96505048816455663],[-70.110000892033597,-0.80447067887108237],[-70.274877443128247,-0.6723984449789786],[-70.486690328967725,-0.41548893505487727],[-70.568681460035293,-0.1780293267180692],[-70.549868563008431,0.64013303873209115],[-70.48595603446789,0.82970760279831879],[-70.345586258006605,0.98392003129963435],[-70.345744770384655,1.7596153574263504],[-70.289495610619525,1.9438809562178778],[-70.203484399767504,2.0604820107272745],[-69.998586988707117,2.1854019100759423],[-69.591869120476389,2.2706351752097222],[-69.268450501696748,2.2218563394593747],[-68.632875698952219,2.2245236659104917],[-68.521867863664895,2.364010479926189],[-68.348009432650812,2.4623463780722106],[-68.149531838524183,2.4847817859929471],[-67.927232186842986,2.4091856929494826],[-67.746495135293941,2.5344980832355732],[-67.491472063064037,2.6081251418145288],[-67.347541293786776,2.613653291061909],[-67.164502996135838,2.557259524746565],[-67.016095727046121,2.4361869937268814],[-66.679420509935071,1.9407374440007765],[-66.598543874602868,1.6380848710101175],[-66.186759194017625,1.2840941005335116],[-65.774689584890098,1.4744381064201175],[-65.629456554635325,1.4804923818469842],[-65.46985098950465,1.4355760261740194],[-65.275168203195463,1.5920417735036696],[-64.93885048038085,1.7236575775127421],[-64.736201554211348,1.8854456144499461],[-64.541432372596333,1.9501194051859212],[-64.440544642064765,2.195436241976167],[-64.539944376489501,2.422816382713719],[-64.531430022904019,2.6477586864087082],[-64.69947263265756,3.0676964788785615],[-64.729723724720529,3.3802755864563658],[-65.046370044585672,3.6840120444393389],[-65.295302241129704,4.0846814666638673],[-65.317583515633103,4.2305362416532066],[-65.296324916260872,4.3765435789120497],[-65.233377647848116,4.5099891182907026],[-65.106909144558045,4.6613171700379414],[-64.936936838450805,4.7532469209914261],[-64.69665277906681,4.7672592946004775],[-64.399795655926411,4.6377905200381635],[-64.029954627648337,4.5994367967117604],[-63.767963825052483,4.4217728662105413],[-63.557798143145845,4.4077062118524024],[-63.305827138444698,4.4425817048004541],[-63.070694948031417,4.3651596983065941],[-62.936819476329049,4.4643080555106298],[-62.597717047926167,4.6223973406820686],[-62.173707587844049,4.6194663337251551],[-61.833578499947059,4.724317920811111],[-61.542960322477988,4.9419074091439521],[-61.280048467827321,5.0181785694154168],[-61.223959748162038,5.0717740701427925],[-61.241029663148403,5.222568472552469],[-61.213641856539809,5.3664069404952759],[-61.115048263576263,5.5342786868859033],[-61.002763393153138,5.6282558366885356],[-60.820150610593572,5.6957405864884176],[-60.531061363522234,5.6943572002918685],[-60.241084185832641,5.7577090248926366],[-59.904568181758137,5.6785250320746172],[-59.600800590145255,5.395508218415447],[-59.501103492742061,5.1832079880567061],[-59.508849017061351,4.8434861362037696],[-59.348379670592479,4.7330001368811079],[-59.2375432692765,4.5624035396766462],[-59.205070599780385,4.3298411503319159],[-59.067269201468406,4.0586148089641725],[-59.055214253972395,3.8717764206458387],[-59.112729624825583,3.6936026649348914],[-59.331165243564541,3.3413806871844618],[-59.349476734557115,3.2161194848743699],[-59.475933236603275,2.8904311382618202],[-59.487276273955764,2.7546071308173734],[-59.281478478139014,2.4332014581961783],[-59.229428143976286,2.0715617299804148],[-58.968296102474156,1.8222007237919915],[-58.846128407437462,1.773972827115728],[-58.676555286540513,1.9575910271580699],[-58.555197439713375,2.0389076231174252],[-58.415516165008086,2.0816774402977765],[-58.23778919306902,2.0777032196156968],[-57.799852643564023,2.1917388215876685],[-57.626524944490974,2.3562955116049782],[-57.440475695962732,2.4478965404602664],[-57.095082894735683,2.5131124677111747],[-56.950329499196911,2.484413899084331],[-56.797844726407149,2.3980205547641864],[-56.612296699356115,2.4275876444658482],[-56.55215472249558,2.5660323213791831],[-56.320199140889507,2.864303791690324],[-56.204880606106123,2.9546722380466783],[-56.020070332796557,3.0162443201004252],[-55.825757052049617,3.0025218081383134],[-55.615764490324075,2.9249493630274759],[-55.095566520947742,3.0835831680740418],[-54.897201862515011,3.0906990538334589],[-54.755189835667707,3.0446087989001356],[-54.603653546430309,2.9274819093600568],[-54.46367496150603,2.8693277746268357],[-54.226083780995488,2.6784620244838115],[-53.95643761877011,2.8176374713939891],[-53.814821379957635,2.8523571296319088],[-53.533223215333045,2.7985322959555403],[-53.285455910588226,2.8370508466571929],[-53.055322274094038,2.7538357834642562],[-52.762366791836293,3.4287235321446223],[-52.584240321093766,3.6421227126968572],[-52.386750575954579,4.0066768058042017],[-52.048119905126001,4.4066198286128584],[-51.904721386553462,4.6598488680371357],[-51.748698607839309,4.7681069346190377],[-51.611150684522144,4.8065054553345661],[-51.381375827461191,4.8070275926712061],[-51.242783122705859,4.7630827182649194],[-50.879577864931555,4.4569966866545041],[-50.763454674286457,4.2966196395541179],[-50.589810501084209,3.7863924804256874],[-50.508712536954882,3.2082993617312989],[-50.299291263263704,2.6392510485105714],[-50.145903835599448,2.6014285611552888],[-49.987228888333902,2.4936950653298435],[-49.901485113625228,2.3777120851475111],[-49.825661712725484,2.1631400744342821],[-49.610891269627601,2.0170393230374666],[-49.496050923912186,1.8519642857749938],[-49.3853118010841,1.4789520198375106],[-49.400278374142097,1.1294194221689582],[-49.451747109410697,0.93984325786327272],[-49.574460281407767,0.78283490334949624],[-49.557496310511297,0.73396308875379945],[-49.428193034156727,0.66014696297810449],[-49.325150893266908,0.55341124741837866],[-49.142859785588904,0.48546418484805087],[-48.976975013327873,0.32161088489374406],[-48.467831222645025,0.25377472188749522],[-48.184449595585683,0.15695965124956285],[-48.022889759096195,0.038788735964669974],[-47.908748445396114,-0.17392424846692314],[-47.662538467892482,-0.18492924986624462],[-47.347660572961438,-0.1294577611995007],[-46.970432918709378,-0.25512481364043738],[-46.658713429729055,-0.30377062075889277],[-46.348398873575178,-0.50952291864014354],[-46.069143330061763,-0.55456385782562545],[-45.791789541616552,-0.67199259444009252],[-45.504251967128567,-0.84063041603320077],[-45.297977375657162,-0.88365108952348759],[-45.157700490421718,-0.80075904383379581],[-45.0201318678102,-0.76927320500567942],[-44.69039233218966,-0.81796850075740857],[-44.562830751626592,-0.89764444658284681],[-44.4647340492971,-1.011649929657146],[-44.404978098476548,-1.1496696366574781],[-44.390946476473268,-1.3200370392083549],[-44.225593170840305,-1.4839694222534709],[-43.957037738656602,-2.0373163382198087],[-43.913746009128751,-2.05794101076338],[-43.746923763330329,-2.0281227749619419],[-43.501701951426597,-1.8913379593819146],[-43.202336215968565,-1.8867518059255866],[-42.764290428546111,-1.9959613041093731],[-42.132946783505204,-2.2936249481213467],[-41.824016861774901,-2.249597773983445],[-41.397912992365647,-2.4053790832197843],[-40.493811012699005,-2.2961588131382347],[-39.808429842552101,-2.386830376144379],[-38.776557851522774,-2.9502898427812769],[-38.453635878273374,-3.198839127341258],[-38.198378135378256,-3.3017643519273645],[-37.692275773088085,-3.8619906655256795],[-37.352888630488813,-4.1575709394645219],[-37.027042400292963,-4.2953886883971455],[-36.871674686736242,-4.4507828037667325],[-36.505995581636292,-4.5847227848665959],[-35.916406349773254,-4.5586742156529905],[-35.246215878970986,-4.7251552489096058],[-34.99008705524907,-4.9545513879574585],[-34.763124713646654,-5.4038190209366661],[-34.621811061805033,-6.0065306980327682],[-34.498582630283828,-6.2937773291206662],[-34.305886467005827,-7.2766354316044426],[-34.368090250806084,-7.7273502129133957],[-34.33591367786191,-7.9392277559337012],[-34.347739837267234,-8.0841330503094877],[-34.702600389857885,-9.1365619678494454],[-34.909603121767148,-9.4838053736634649],[-35.456038749993489,-10.084745643607665],[-36.032199736303411,-10.824255734283486],[-36.569474553395764,-11.166455027772511],[-36.961743463918438,-11.710867773711028],[-37.258067067275405,-12.353710363832917],[-37.864283402616167,-13.174357523217026],[-38.143401709377301,-13.393968820889899],[-38.404295409977379,-13.478407954197596],[-38.500448056372008,-13.797687114565912],[-38.452061672615173,-13.933942338797801],[-38.444479593807252,-14.074020688638539],[-38.54991081005101,-14.664005957286802],[-38.381389682507113,-15.888109341332262],[-38.696485829828077,-17.216064774836337],[-38.654261705395086,-17.706066717905728],[-38.675043993879676,-17.846520352994801],[-38.76211895288634,-18.01405530945318],[-39.094994184424067,-18.312959255207375],[-39.182180145134801,-18.454291925472987],[-39.236472914444448,-18.699994273737637],[-39.206672544633335,-19.358848729179872],[-39.338720859387024,-19.800174835197105],[-39.425104025879222,-19.9203690402876],[-39.611013050628287,-20.080171089849681],[-39.997796213229954,-20.871575968214081],[-40.264912797598072,-21.157657804972704],[-40.389699480425143,-21.24248314892932],[-40.490503184164339,-21.432530536018909],[-40.532255745666731,-21.564488987582425],[-40.504542966610501,-22.06217107766544],[-40.597949808453905,-22.29542132560843],[-40.747949229266162,-22.43039830716684],[-41.431989242090069,-22.745256903097737],[-41.461768187733824,-22.930137245285604],[-41.519854965235005,-23.05743872933779],[-41.680881998697181,-23.29212354138561],[-41.838237688685027,-23.403203480651435],[-42.02590722616177,-23.446559112143937],[-42.817135690614201,-23.472848823710564],[-43.026803103888369,-23.449686281128184],[-43.811903061495144,-23.58056538585733],[-44.007795257151912,-23.660860940445282],[-44.300325329287908,-23.690070366204985],[-44.466787540071088,-23.793441433421584],[-44.760889570972999,-23.864990769547273],[-44.77146723507078,-24.042264195357088],[-44.823690740443155,-24.183209032333238],[-44.915443638641634,-24.302264260088226],[-45.038434131592034,-24.388670742642663],[-45.231518722134602,-24.440113531282176],[-45.482375458769226,-24.429840069445984],[-45.623272917256898,-24.388235119484097],[-45.77309805907398,-24.280740182729531],[-45.834564292894896,-24.286229837950813],[-46.571592518243477,-24.648680548911983],[-46.82682111934767,-24.884851839965137],[-47.440819849062954,-25.296901895709166],[-47.555762978529522,-25.500191041883074],[-48.000012052071433,-25.873773189164115],[-48.059289116206259,-25.979841636912994],[-48.005097705309495,-26.134049804320554],[-48.001607581829496,-26.279876642101911],[-48.040354051788249,-26.420505083908509],[-48.168597415695153,-26.664626214914652],[-48.084636508180076,-27.034847119126713],[-47.911097443509227,-27.273114657828739],[-47.882494039674775,-27.51697985070782],[-48.033213224809664,-27.978692344276418],[-48.12921360360226,-28.117118601626768],[-48.177361012326813,-28.37404482440094],[-48.299348812273323,-28.600127126448061],[-48.391206151136991,-28.863236758747565],[-48.493734033548009,-28.970478626123644],[-48.959734191938693,-29.265329463126772],[-49.136514509367778,-29.42356502628029],[-49.587034553466452,-30.036412814243434],[-49.863073916154214,-30.669270444580945],[-50.394839377137984,-31.42215469286349],[-50.841223966505765,-31.871754774748783],[-51.510391655711402,-32.310260972195067],[-51.818267504287363,-32.56339184766977],[-51.903756234587902,-32.686764265246268],[-52.219689743284185,-33.386566380350118],[-52.30101294605452,-33.494217838741683],[-53.088823016747455,-34.154900496872187],[-53.222635380718359,-34.219492517044777],[-53.369521057786933,-34.241908486654914],[-53.563261033196504,-34.203330830510318],[-53.810337255403972,-34.070097731450538],[-53.918357604561692,-33.97159354384803],[-53.993269640343058,-33.846056068556884],[-54.028669441204784,-33.704216987530756],[-54.029138892036983,-33.126827604201324],[-53.904092377634761,-32.800433205689934],[-54.019127022749579,-32.677862260477248],[-54.172339969730096,-32.396030090979572],[-54.464128085318485,-32.291174312164614],[-54.871273841734954,-31.919029332558864],[-55.390293828789581,-31.729401845762332],[-55.642841408646397,-31.512824876539444],[-55.776233301048961,-31.558579277652115],[-56.01163952462349,-31.578938240337219],[-56.251633087439956,-31.513677151736189],[-56.430494637485147,-31.340863767433444],[-56.517076544908996,-31.032995314592306],[-56.922839716950321,-30.688836752001777],[-57.057899682754844,-30.757977364873547],[-57.199262978063416,-30.782910270553586],[-57.677851137536003,-30.744944253284832],[-57.883963023805926,-30.635044052909379],[-58.075955894486967,-30.365424337818894],[-58.104612013159795,-30.125047171651453],[-58.065260866348872,-29.984264116008418],[-57.986874455773275,-29.860878735397112],[-57.75641105940413,-29.668991050622445],[-57.579073255599617,-29.429380724750544],[-57.262653880032602,-29.206424080944547],[-56.102150840654261,-27.875634786759999],[-55.976039611089426,-27.771664214502017],[-55.405817860738424,-27.441164383197968],[-55.0939733651458,-27.126774438666374],[-54.582991469415198,-26.969691968127883],[-54.422011697941294,-26.830456129541734],[-54.252156649127009,-26.76387632554707],[-54.202741616036015,-26.310588943964696],[-54.301249618984663,-26.104287849130809],[-54.529395808166015,-26.117370530336693],[-54.797599262111049,-26.041544755080839],[-54.921260853468546,-25.971544235884817],[-55.046289611765253,-25.82985406658544],[-55.109804653570635,-25.651881782389939],[-55.105658614729911,-25.365085333135468],[-55.064511417817002,-25.223852050949482],[-54.925359342638707,-24.981328880149565],[-54.802663283137484,-24.417022611216641],[-55.082954539706144,-24.505158669293699],[-55.279565525113604,-24.508322895766053],[-55.601934036619362,-24.415247943941132],[-55.802960874726473,-24.267504077038716],[-55.879613300757278,-24.137744001195617],[-55.934995760745956,-23.89082661113266],[-56.025329420095183,-23.693188308729219],[-56.131993753356809,-22.836691214083931],[-56.163582254394562,-22.773396492843364],[-56.473535133347958,-22.70826354945644],[-56.968957766848995,-22.770089871435214],[-57.292604385161738,-22.697281944425697],[-57.645278319898786,-22.675852585825492],[-57.822770000539968,-22.614236600514886],[-58.023578713176093,-22.604360938177258],[-58.16584779355113,-22.562694514145058],[-58.289646518952111,-22.481144175632689],[-58.384092197957358,-22.366878713485864],[-58.457157638756001,-22.212101010097292],[-58.485293615906919,-22.05804381920661],[-58.426919078150227,-21.689183186266238],[-58.439424861722095,-21.414719184796144],[-58.352772722040584,-21.067045148665649],[-58.653100540847177,-20.244426801725684],[-58.654406838554308,-20.093741306764539],[-58.597123676335045,-19.924286001209811],[-58.629781066497969,-19.707002834418375],[-58.578759972530804,-19.521583790521007],[-58.282975692965508,-18.927625708991179],[-58.100279342880462,-18.202108584829162],[-58.221123897297538,-17.929995473607661],[-58.698238550520401,-17.63233215940275],[-58.825288731808755,-17.490174696965205],[-58.880280937361825,-17.357761523108426],[-58.967069911921335,-16.813073601765893],[-60.241381809250818,-16.764743426479725],[-60.463405557742753,-16.677825185780179],[-60.595869022054465,-16.539652289047385],[-60.666708325137883,-16.361831840539214],[-60.728560051330184,-15.703731564207041],[-60.995352568194413,-15.380817256928127],[-61.079102144797837,-15.158087044551893],[-61.075545939083035,-15.014473732368254],[-61.03130229640864,-14.877799191511729],[-60.950023517095474,-14.759345867080528],[-60.833460548595077,-14.66625423167682],[-60.950453318233066,-14.360680531984125],[-60.947390299341009,-14.13584305965545],[-61.179728515137576,-14.006271216479567],[-61.774037983873185,-14.025101366453733],[-61.965525026793877,-13.993482516483402],[-62.093238823619465,-13.922804809667529],[-62.4299606927272,-13.612619444137168],[-62.998213953330065,-13.439516890113916],[-63.313043642410669,-13.193024938397585],[-63.521832729295774,-13.14815627785204],[-63.778140030891628,-13.002830412698785],[-63.979012397929139,-13.027813298528415],[-64.512082103904547,-12.931032705487951],[-64.742345891566288,-12.822023287274549],[-64.919765328557972,-12.584718054070777],[-65.328719653027392,-12.344963365649004],[-65.639050940687724,-11.959907810038791],[-65.684466004964634,-11.792030924013702],[-65.784046355943303,-11.629739895864141],[-65.885107624077961,-11.279009024045477],[-65.848709753419328,-10.921684816949687],[-65.946924267028535,-10.506060125514004],[-65.896259136895978,-10.288878951425293],[-66.389493916529204,-10.378369314801288],[-66.876427825981168,-10.710227829842836],[-67.156781328735164,-10.829220219642968],[-67.453829008963737,-11.104957188945059],[-67.597252026716276,-11.167106506563195],[-67.825751686863399,-11.187783370421867],[-68.067389979467137,-11.411488855545977],[-68.514098858479556,-11.597057637555416],[-68.805222942268969,-11.616097923569994],[-68.939351803804414,-11.575058642966264],[-69.076458969493217,-11.486900872496618],[-69.289215358129255,-11.453909840376564],[-69.857692450011299,-11.434487956347466],[-70.323814995375685,-11.566285403408136],[-70.87445947629071,-11.452618476087411],[-71.02757922363287,-11.328331471996876],[-71.120729188249882,-11.154503969846679],[-71.139694552790559,-10.456112823350841],[-71.330479982294165,-10.488347781871532],[-72.321207875453311,-10.48353688183758],[-72.457396068117461,-10.42039038374091],[-72.568702616031203,-10.319665230802203],[-72.645094478733398,-10.190440669066096],[-72.717476212090972,-9.9503415601947811],[-72.875401575564538,-9.9111838195039415],[-73.305927561324083,-9.9016988475814003],[-73.443396077449776,-9.8528203469638527],[-73.560902000945106,-9.7663387957987116],[-73.669584587246831,-9.6056498416477396],[-73.708926756993165,-9.4156892188044328],[-73.673006279150613,-9.2250519616301645],[-73.586158045108775,-9.082303018162829],[-73.800377516799259,-8.7821975094971201],[-73.978769952442988,-8.6009825741809962],[-74.081367696422546,-8.3402969062844523],[-74.223981814974692,-8.1570218395226437],[-74.271955579513985,-7.9968351345878483],[-74.363496842837264,-7.9008184027299704]]],"type":"Polygon"},"properties":{"alpha2":"BR"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-79.305432614572808,26.306192562191772],[-79.425462760265262,26.455131396764056],[-79.480781675438934,26.638242920002511],[-79.463292880943172,26.828726920343183],[-79.403536737909263,26.959581016973324],[-79.308997876900975,27.068006083237695],[-79.187501692100469,27.145027303132608],[-78.733735275819512,27.27784302596023],[-78.310980005408396,27.284907365885065],[-77.913916219507755,27.436494100181786],[-77.432429346756052,27.391996063475432],[-77.260200663167424,27.320677410066239],[-76.755305509590798,26.920302121915235],[-76.594408982580603,26.691969449655268],[-76.539757997282138,26.169079392833162],[-76.473078505534701,26.023132680507938],[-76.296006598466633,25.861730791379188],[-76.047379610624333,25.733626441469102],[-75.796564120652889,25.514908932026646],[-75.597291409415135,25.226165906206781],[-75.275949844229274,25.007142526737571],[-75.023575922980541,24.635306032836851],[-74.856181503954417,24.557699414301666],[-74.485541555140699,24.623423075685306],[-74.337851095572091,24.611604625445537],[-74.158541459760045,24.530053876590351],[-74.052569982965636,24.426505850439828],[-73.960958193025235,24.240056231456386],[-73.931051355076505,24.09166076674542],[-73.962259114229852,23.892861163774331],[-74.117194326066084,23.610155754315027],[-74.118351279028019,23.450634074006363],[-74.035149930981021,23.314523675021391],[-73.946969824986738,23.255944387623831],[-73.616653982572274,23.17215369861054],[-73.333474180576076,22.965372857769637],[-73.021260270336299,22.942989499645702],[-72.645851814619618,22.849156782738902],[-72.424647557737117,22.708275910287693],[-72.328451064327041,22.598469131836225],[-72.256891512460669,22.41803823420047],[-72.251677438638467,22.272147496412007],[-72.288824668988099,22.13096896682752],[-72.455969371477735,21.827075679812062],[-72.417564335988459,21.526547608743421],[-72.433224205284475,21.381101275931922],[-72.612866289822151,20.896793715270046],[-72.790758571874207,20.648495418122089],[-72.997471377135994,20.508928175410997],[-73.415052356427168,20.443389170150233],[-73.745657853140628,20.445721821955004],[-73.882086449768593,20.490215963709794],[-74.000258509807622,20.571627602033221],[-74.090440205102155,20.683250863328421],[-74.171706900713033,20.891471323075759],[-74.184137834174763,21.049277940306023],[-74.13385964610832,21.453262431940146],[-74.210472046818936,21.6012990296468],[-74.538984786783701,21.759301835464427],[-74.651816917247359,21.854732659824091],[-74.731825614194321,21.978977339049333],[-74.815874688031627,22.252741871710267],[-74.890396053057373,22.337550042626354],[-75.124029161371155,22.453714829617571],[-75.328288304824525,22.648114755804592],[-75.696524220636448,22.915919269536779],[-75.968647995354161,23.008056798425795],[-76.27592099079537,23.164722821977371],[-76.454506887382763,23.329599172577684],[-76.516023103584729,23.462864100336898],[-76.536191710840811,23.608249839348076],[-76.412111278313091,24.076790015690307],[-76.467488509957235,24.231882627693587],[-76.57480745250048,24.333115502903013],[-76.752412278188132,24.380117475810959],[-76.837389123183698,24.363421960578847],[-76.975142551637887,24.261565639000587],[-77.03322043373376,24.098652230578089],[-77.032434518219247,23.726435229670358],[-77.150252251466313,23.475153072006002],[-77.283950102199157,23.33301873239601],[-77.461870408419571,23.252883531507884],[-77.608170432433639,23.241228650210452],[-77.902422472914665,23.271123210505944],[-78.079216105128026,23.360101210516749],[-78.207634007224087,23.510706374101428],[-78.292847130453538,23.746293483405267],[-78.439234085352126,23.976601710877944],[-78.751796316315975,24.22683589138024],[-78.874549203385314,24.390850887311821],[-78.924268977126388,24.528759201608139],[-78.913028899446374,24.771261312675261],[-78.739064769820899,25.061371448126707],[-78.674386089964003,25.377268534812774],[-78.538042337981878,25.625087837837725],[-78.547463137331036,25.806820868950574],[-78.649625854799709,25.942330328962871],[-79.016781979768183,26.078340593377394],[-79.305432614572808,26.306192562191772]]],"type":"Polygon"},"properties":{"alpha2":"BS"},"type":"Feature"}, +{"geometry":{"coordinates":[[[88.301903728405136,26.932851336495233],[88.239028569643821,27.173265035626038],[88.299656129466854,27.414255349350306],[89.101115838315664,28.384326827335823],[89.260663417098698,28.510131257290123],[89.757711314315017,28.758308262633097],[89.933681894819856,28.808719304161546],[90.485673962477875,28.724418143306711],[90.618552543363904,28.664247959231073],[90.746166988542043,28.545296848840721],[90.97793718104397,28.50295410020891],[91.200901168949102,28.572925366642071],[91.35294677710182,28.571724083177493],[91.829322413017792,28.398561296531287],[92.048721387062528,28.213442360858057],[92.114205596257563,28.086461439379587],[92.141070011929045,27.926270116462785],[92.296890807205699,27.845228282997862],[92.396947231978174,27.741411074731779],[92.539044950121479,27.495916917265454],[92.579833933357335,27.347924250160869],[92.542259269727595,27.092616645027807],[92.562207412997751,26.810797950674282],[92.455243193980479,26.592389078809894],[92.177132723904407,26.388280662553246],[91.704244830396533,26.303287189273],[91.468573605891706,26.323823861435201],[91.302797169693946,26.290452691638976],[90.701410028894841,26.273150890785164],[90.406654123833022,26.341225149874436],[90.24189764938771,26.269183497692808],[89.836966586324976,26.207129894829158],[89.50054479237231,26.231797403210717],[89.270955373595399,26.325250476580678],[88.98972754180474,26.342222965089348],[88.624956926115757,26.519193773591411],[88.475433882504902,26.639519393565248],[88.301903728405136,26.932851336495233]]],"type":"Polygon"},"properties":{"alpha2":"BT"},"type":"Feature"}, +{"geometry":{"coordinates":[[[19.47757458654478,-22.000991537848027],[19.498931364620148,-21.85585386295989],[19.561491746679923,-21.723161529955195],[19.741279190405514,-21.559784384268063],[19.927852043995546,-21.502903992301793],[20.471116978313706,-21.499707178901023],[20.476653185915804,-18.271002927955355],[20.530199993265978,-18.089401800849465],[20.647429607969727,-17.940729519034949],[20.767313864236318,-17.86392817913741],[20.903984266093556,-17.824029783595332],[23.196348907159742,-17.500520247769675],[23.507333980733609,-17.573260120982827],[23.713536895396846,-17.746836186348247],[24.228257922442623,-17.496189151121808],[24.470075692636815,-17.491866227400241],[24.798245206241162,-17.334681426835939],[25.24368499526414,-17.288535394523819],[25.468980720580756,-17.340230155928669],[25.657965266330301,-17.493096353232037],[25.742627643280098,-17.668940044544811],[25.755949377404132,-17.837353651950984],[26.368478924948818,-18.682659041110437],[26.452339982085601,-18.97097225751817],[26.561806636182101,-19.188833063354178],[26.925737669042,-19.452994017858451],[27.459354818678243,-19.68781564544032],[27.568978189320813,-19.789439978451014],[27.687008746812108,-19.998117013772976],[27.91800612940925,-20.066104467157505],[28.068582423729136,-20.189636288304758],[28.172498858883237,-20.408828752341542],[28.18291547117769,-20.914613708167497],[28.322126349510178,-21.117700806209921],[29.135272505908254,-21.308133078481877],[29.314078062099544,-21.38967303094244],[29.447649065525976,-21.53382218222611],[29.511316659634581,-21.692925014643432],[29.77602674844724,-21.910138461617322],[29.840402203921695,-22.041153787780544],[29.864193429864649,-22.185178759661575],[29.809796687911334,-22.420727430974853],[29.65124914735409,-22.603223621279842],[29.520761328781582,-22.668661715394727],[29.334797081002957,-22.701232942892862],[29.058157046574575,-22.930366099552366],[28.545124108482536,-23.079052902119418],[28.047355157579769,-23.604475039077659],[27.438139833905733,-23.949546413132726],[27.300542832415502,-24.422784446009093],[27.231205145132066,-24.545532071077091],[26.741535398291852,-24.989450613154876],[26.29209999923145,-25.170422226204771],[26.003078295420398,-25.877054701636752],[25.666286722732568,-26.161935577188917],[24.827113024974057,-26.311745081012038],[24.666503449839414,-26.309612843268919],[24.19360025611546,-26.223501360311197],[23.996276851091238,-26.120752328781844],[23.722615187189113,-26.070472170990243],[23.277931986659045,-25.807011923895544],[22.983883601867426,-26.449794022690806],[22.570148247481072,-26.755203761670089],[22.286398441466304,-27.052970530803922],[22.086473936174691,-27.133862307827673],[21.896184370133412,-27.287073225709449],[21.715599022542612,-27.349180716578537],[20.928920074979583,-27.316628092481952],[20.716861592685174,-27.348070667132408],[20.573901109191087,-27.32025368739345],[20.445021659377577,-27.252418802426892],[20.188588387054526,-26.953721684280733],[20.1242837474319,-26.640477440505123],[20.132976976132447,-26.366600515060764],[20.180445732247325,-26.218922789163187],[20.294636950323397,-26.032252203973858],[19.996035586748732,-25.402283739124513],[19.621411612593953,-25.124327150641935],[19.537461112920912,-25.00797404487059],[19.490009588007169,-24.872570631319263],[19.47757458654478,-22.000991537848027]]],"type":"Polygon"},"properties":{"alpha2":"BW"},"type":"Feature"}, +{"geometry":{"coordinates":[[[22.979485004848517,51.826644732901315],[22.854160082504666,51.903505577908668],[22.756606783101596,52.013493210992166],[22.695259145682385,52.147098574053743],[22.675421023344359,52.292770721555719],[22.698807534892982,52.437915465633218],[22.793212417726256,52.609075516305133],[23.102979302415235,52.909770789188094],[23.339684299779364,53.021166208636075],[23.139715707775153,53.402060043421201],[22.988968065528912,53.876667314695439],[23.003082772781212,54.072699403979207],[23.124064982600686,54.285482171182807],[23.241014300730694,54.375901281765977],[23.428068030777663,54.436222978238249],[24.18364303483667,54.450162548567519],[24.361416555204048,54.421530341889586],[24.466057761666239,54.449070611098691],[24.68463200308976,54.609440948593537],[24.972341994512995,54.642793502419337],[25.189374576347486,54.725628168007191],[25.285588042109133,54.960585952970497],[25.473766050727995,55.237343564067501],[25.61641262372909,55.356073908260889],[25.85621450308188,55.431569333504761],[26.017379078617829,55.566471873906657],[26.149117226730116,55.89600773691761],[26.317165361063999,56.083886814945032],[26.963379451998605,56.322549517051492],[27.377000897576679,56.319671290869287],[27.69573475112729,56.533943013775421],[28.05409207185437,56.641579137333274],[28.198814545665176,56.637328743992228],[28.374440791131075,56.579562040849105],[28.672957654394349,56.579693657658623],[28.876484941292173,56.489758692312094],[29.185467952501881,56.511117636027492],[29.557730665585069,56.403871490302748],[29.714530796233497,56.305279797864891],[29.937082268522989,56.344990813283601],[30.318658917223647,56.337666969528144],[30.628765622527737,56.25584491146202],[30.905819174271038,56.098768606311026],[31.149415129093551,56.018681573245409],[31.263458556332314,55.91948683126602],[31.342649696386363,55.790745759848775],[31.379751904453233,55.644222975650266],[31.381067706954745,55.389928835967986],[31.469142508138304,55.17556326752478],[31.471613022199005,55.008937469831679],[31.580971405603592,54.882023617043991],[31.678026450847906,54.628662319944254],[32.051958640684418,54.482429253787458],[32.160191209660667,54.395577428194329],[32.261554534437167,54.253260920815926],[32.615365885888437,54.164590241400425],[32.745279568791965,54.096206864423884],[32.91415959329327,53.877172826711387],[33.09542894009553,53.729497995325097],[33.199618905150366,53.499366389317963],[33.176692162880428,53.173283233153015],[33.041771449667728,52.96779096109001],[32.918599835569594,52.884888578902839],[32.703648392208144,52.821075141599088],[32.325293465920595,52.626256022809969],[32.112891890139757,52.592260209098328],[32.110540372089929,52.477535660985289],[32.220951039850391,52.302230575825241],[32.262921656108595,52.114366461984211],[32.230808588477132,51.924568651707943],[32.129371566206153,51.760968564876777],[32.01651297624803,51.670300712076241],[31.882409872866738,51.615765599329485],[31.197380224775799,51.574007819346846],[31.108080675493305,51.522445059848899],[31.13035657937402,51.312208598773161],[31.051481429855819,51.083051073754277],[30.827851219974455,50.853414326114894],[30.649056131151454,50.776381455076233],[30.406719149688296,50.784634568073542],[30.15103356207986,50.860203666406541],[29.996270527867971,50.956933693199012],[29.30442121556278,50.88458941082115],[29.156768720163072,50.920221615445861],[29.006551359418591,51.017062705000249],[28.849771200173432,50.947947700574417],[28.656616908638522,50.93925586538321],[28.35990680317774,51.073057904958688],[28.192586813887978,51.096189510935119],[28.012045500320738,51.079221110807509],[27.801765400435226,50.988676620328761],[27.653367874370719,50.980403632500597],[27.509094749913743,51.016118246163529],[27.376911329016668,51.096613825055236],[27.110110751914085,51.133629823272656],[26.896319522415688,51.275014196421253],[25.743987554615597,51.424196392702022],[25.102869629063449,51.430902655949112],[24.581225754233447,51.385377359658193],[24.163028246026343,51.127211505798535],[23.883419207010828,51.103388927878967],[23.737165236840763,51.035980092910435],[23.544799122753286,51.021961477022302],[23.361439350319475,51.081798232395201],[23.1860085996755,51.245933982091728],[23.057400346105979,51.487943048705951],[23.040449577627669,51.641828212227495],[23.067488818151077,51.789168911044847],[22.979485004848517,51.826644732901315]]],"type":"Polygon"},"properties":{"alpha2":"BY"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-89.24260814659506,15.394550880925379],[-89.436677930969054,15.436021862099661],[-89.563139644494044,15.51533083869532],[-89.660553474455526,15.62843709178755],[-89.730719710352901,15.814070124479789],[-89.636700210174155,18.058728085522038],[-89.491907576699688,18.319143710041434],[-89.340767847850515,18.425594569724545],[-89.056233738082341,18.498895177981169],[-88.931439002169654,18.733822433806694],[-88.789397777579808,18.868701534331862],[-88.497121113786491,18.97522132970003],[-88.135903574561979,18.945928027163095],[-87.821025705387385,18.743270721589241],[-87.710372440633463,18.620676523498101],[-87.541680888233358,18.534901024272898],[-87.393627180957992,18.347244918795852],[-87.349601392607227,18.112305683841608],[-87.42504053841779,17.866985917849039],[-87.341815887982335,17.748019470670791],[-87.296543903442398,17.611334630964372],[-87.321443662051834,17.329462695906006],[-87.543711557797195,16.965954329325914],[-87.651019527963783,16.86847110885337],[-87.780481238992152,16.806345700764641],[-87.829188990568284,16.509270827868153],[-87.883241283388827,16.378441661684498],[-88.191524126151322,15.955309013832796],[-88.397062042620206,15.816541349560181],[-88.464920596663205,15.634663062394116],[-88.554129394166765,15.524397879746175],[-88.670711324505987,15.443618714271429],[-88.805285450088562,15.398825581091195],[-89.24260814659506,15.394550880925379]]],"type":"Polygon"},"properties":{"alpha2":"BZ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-141.50122235337778,69.649746596115506],[-141.46502439434445,69.836530895300356],[-141.393136679479,69.960237074681828],[-141.24877442727998,70.08416530713987],[-141.11560786538308,70.136488620856625],[-139.57409516134723,70.070075147676306],[-139.06416531901189,70.146731054698989],[-138.64783005009198,70.032018284561261],[-138.32269567071981,69.749535799056773],[-137.99260422156354,69.636880280179668],[-137.2263366953859,69.468195351596378],[-136.68626259717439,69.394059294258923],[-136.47626090962058,69.396916953307979],[-136.32284913245044,69.439476657460588],[-135.8775200416886,69.774149908743354],[-135.51241397452466,69.850927057868418],[-135.24194080379002,69.956010359650932],[-134.90452357603579,69.993639577219938],[-134.47724429775437,70.176292125565112],[-134.09964157083286,70.147301491282079],[-133.54822071794092,69.920292818841574],[-133.46975026511032,69.912378936630915],[-133.33966700057897,69.941721930446718],[-132.97546858933541,70.131674060189781],[-132.52453410811225,70.236439538039662],[-132.02435848662012,70.2511873599463],[-130.70082616154721,70.625927681352493],[-130.13090759464353,70.595962982190741],[-129.64498806556682,70.691158211703225],[-129.45284279139548,70.639636052601446],[-129.14227605127303,70.468484763675377],[-128.81870160500384,70.496924273510885],[-128.69455012573215,70.576250953212991],[-128.51258431740891,70.841197455227132],[-128.40811285363543,70.936488393162136],[-128.01522600276894,71.116233111934889],[-126.34964121533397,72.189318316482016],[-126.09912316613979,72.505949253664184],[-125.4954607069602,72.96695751461661],[-125.2133095357041,73.411090099212785],[-125.00618618741225,73.665164627866858],[-125.00083048294407,73.826859905901429],[-125.16561544936475,74.176447075121814],[-125.19528069267626,74.368572714102029],[-125.14942946008451,74.557490559431969],[-124.99749858808136,74.746021140482839],[-124.7748011224453,74.841061177616126],[-122.92231431949644,74.956565560831862],[-122.74239209851238,75.045806569516103],[-122.64716911582428,75.256899056310075],[-122.69697551527098,75.417986258401868],[-122.74869871284115,75.475196980542449],[-123.22545031711992,75.754961973683649],[-123.34435715212172,75.904200153491388],[-123.39177506598024,76.039637461739787],[-123.39192458721088,76.230453727728488],[-123.32057954675378,76.407430435022533],[-123.22604617860399,76.515389526008548],[-123.00399712355704,76.670936859682925],[-122.48626514020999,76.887114896926249],[-121.70573343001607,76.950242688110833],[-121.26934695792724,77.131337933211256],[-120.72107246951039,77.258980061094007],[-119.16961044010593,77.798024103565169],[-118.00192492495532,77.880486211855398],[-117.48780475663624,77.839130241938719],[-117.01899278451097,77.99971231085199],[-116.53369506349367,78.046548198946567],[-115.75417221725809,77.941000522389075],[-115.60714618958693,78.010723145126022],[-115.37351080369508,78.328687216448657],[-115.20368315480415,78.43500619749102],[-114.36106774808782,78.575428894247182],[-114.01205396813363,78.497918008471757],[-113.89113156137297,78.50100540890756],[-113.7913134419948,78.544239239802707],[-113.51043136419314,78.796115910102159],[-112.93593246142453,78.960234904775504],[-111.74199036020721,79.075932780475256],[-110.91975759522492,79.233209631785812],[-110.21097489959556,79.247152470153395],[-105.82269071233407,79.707867440010602],[-105.44042645264447,79.819558311747116],[-104.9067297359755,79.824318240211511],[-99.907700286535885,80.629028571753437],[-96.576390553554347,81.061756660911684],[-96.262689924042903,81.17321107811108],[-95.84712057248791,81.25073887370462],[-95.426209683415934,81.474848213051018],[-94.741814419595002,81.573025227098555],[-94.442470195948772,81.772946620173016],[-94.311232799818242,81.82240740946952],[-93.326554448453649,81.86279468738212],[-92.2840234200726,81.79638876674899],[-92.199666410827604,81.83022282829657],[-91.970516859699842,82.06434326648224],[-91.843100457528507,82.143033538650897],[-91.048889411281905,82.316311442250424],[-89.471297531189194,82.41139277538592],[-88.666318583463081,82.551754941106168],[-88.118554176530438,82.593825218695855],[-87.107344362105238,82.523194542810344],[-86.7087694341607,82.708814300582276],[-85.860503010429696,82.790374466280795],[-84.937927262121235,82.946767545332392],[-83.566168524578401,82.826583539962627],[-83.020187816426755,82.688559975188497],[-82.879264205145404,82.726741496903614],[-82.389794559297414,83.046352939792243],[-82.242611718224509,83.11151622714371],[-81.51149759982458,83.15204647297513],[-81.055151951764742,83.276263426546038],[-80.59637571761705,83.275136717178967],[-80.293674149497093,83.390483742118477],[-79.960682572505718,83.43353227735652],[-77.601112669219148,83.393275421942931],[-77.138761824129574,83.507394219727274],[-75.720886948688161,83.546716480344074],[-74.383847791244094,83.511063976694416],[-73.723012994085039,83.405251334688785],[-72.802591895312631,83.58178407233828],[-72.018296183841784,83.603990777351683],[-71.518157804113258,83.522913556017542],[-70.890482256600578,83.597302081054693],[-69.962795786018503,83.614965500029598],[-69.379242344658394,83.514348611722212],[-66.561083827307485,83.442074623523055],[-65.980396982763523,83.319380649479541],[-65.433845480667102,83.321148392554164],[-64.952127350445068,83.401788601055813],[-64.412950268792017,83.293678354500827],[-63.632313525658056,83.312749560078842],[-63.336938264283887,83.264757577427147],[-63.058072181086793,83.090429605327785],[-62.773427131940792,83.000169029459684],[-61.339306362824821,82.947395813446931],[-60.871818942662792,82.710410011342191],[-60.757845433117467,82.557124150250772],[-60.714981823720237,82.420020360647115],[-60.721347764916757,82.229112029552581],[-60.798838770293514,82.054522052250562],[-60.980274108748901,81.883634794003711],[-61.116052725845286,81.8066391769962],[-62.110262402235399,81.54881834901353],[-64.24301658000374,81.261957679702803],[-64.325336325815073,81.226256993961201],[-64.566453224838725,81.023266309120814],[-64.753207566528928,80.943964064187],[-68.48030817706659,80.199115976722794],[-69.452357974175001,79.89373788674385],[-69.889985654812079,79.851670872918859],[-70.379059934286872,79.605606634835553],[-71.223888553475064,79.286943627912351],[-72.187402666469552,79.18794774288051],[-72.759633352723981,79.221929105176031],[-73.204427880162967,79.028450834381772],[-73.728631390718959,78.974488881986275],[-73.861954576966298,78.883562543894229],[-73.90407293115689,78.812195341270041],[-73.958801615183049,78.569710525354566],[-74.028401239805362,78.432347321953117],[-74.245959820979266,78.221965596415885],[-74.370115177832588,78.153498032823094],[-74.717831147219968,78.05610785243077],[-74.941897492892011,77.89678835031421],[-75.361996340148366,77.74202409975841],[-75.687548589356851,77.543988167955007],[-75.840529675912933,77.511576636335434],[-77.221461951138451,77.440038923673811],[-77.381020877887835,77.397624950690869],[-77.472216289663407,77.326655702840682],[-77.528424615515661,77.225691048757085],[-77.543131335795323,77.088098135073963],[-77.486304440949837,76.709987694734238],[-77.567829526215476,76.478572826730471],[-77.849001609603235,76.224000075167808],[-78.328081041451256,75.986015291883319],[-78.907245592793274,75.301154679196898],[-78.934756416282752,75.203487449974304],[-78.908930363579373,74.83790275637152],[-79.038690317654982,74.420303600433911],[-78.966031530295481,74.271841876041435],[-78.831578535824292,74.175703562826243],[-78.75063285346134,74.157192877763023],[-78.033319756810556,74.146869999662542],[-77.039078090861409,73.96956931578157],[-76.039779912022965,73.513039083961047],[-75.552079802999543,73.063759509973664],[-75.062854716460166,72.925271134122241],[-74.646751241985157,72.635095290373414],[-74.105486782687976,72.513267118581496],[-73.780552939299952,72.287727396950274],[-73.359815242492601,72.125211152300423],[-73.215837816840832,72.110906492115106],[-72.88711562433474,72.176660117492602],[-71.543770527214861,72.006313850774518],[-71.13289764695898,71.861941960826627],[-70.795617446410816,71.632168188554132],[-70.48068118943894,71.513640229724373],[-70.186772968011184,71.349983820641413],[-69.194278046310757,71.274612305359568],[-68.237620437117684,71.047536910774724],[-67.90291471390438,70.811542564075353],[-67.526093049608889,70.682575559005301],[-66.959384142855882,70.345368996573072],[-66.643389485868838,69.871766591515723],[-66.352423565580764,69.657702597505974],[-66.25458582081599,69.537995015427114],[-66.189357418880846,69.343129109265305],[-66.181791959563242,68.953781292350484],[-66.143203121877804,68.866497961700986],[-65.720218830233023,68.52479515521712],[-65.418629314382073,68.465438509899286],[-64.947094053073869,68.529742445390326],[-64.76728746884649,68.505548406442074],[-64.179117332669733,68.194725393422615],[-63.797476365541534,68.101944436331507],[-63.340410594264696,67.840461007849257],[-62.806246735114414,67.684451963617605],[-62.319292077118448,67.67767799355866],[-62.045091401710991,67.553298832035082],[-61.805036016919153,67.490649890496471],[-60.943248817920242,66.997825123045402],[-60.857992844287082,66.880530906491757],[-60.809979165622686,66.743705466831656],[-60.822116870826967,66.50385263122449],[-60.912366966679521,66.333469728032512],[-61.247226243870969,65.993082628709331],[-61.515508005626593,65.82675963044305],[-61.724997257754843,65.613588586114631],[-61.988594381809193,65.503879834069579],[-62.413877941156812,65.205238311977183],[-62.813816260067775,65.081175362851582],[-63.04154203132569,64.770761930695571],[-63.353459109007638,64.498063530170285],[-63.547231269183541,64.432662396874022],[-63.806987554201712,64.425710319632088],[-63.947096608096729,64.349471295369199],[-63.998416349623312,64.281268816098759],[-64.031825739592577,64.141822757014296],[-63.92416345650711,63.815992430216092],[-63.914317433975675,63.657899047810183],[-64.063540157277615,62.918693187711483],[-63.938592350385008,62.625024914256372],[-63.923615473757565,62.421780935036765],[-63.990984361955697,62.229443068703887],[-64.197502865639976,61.918107021426238],[-64.172375617695337,61.550627770165448],[-64.296210029374279,61.052648869688994],[-64.275307499741544,60.942451464324968],[-64.010960777218614,60.670659871301098],[-63.832772011594422,60.328196864474897],[-63.491280578277916,59.944033390691132],[-62.898029730212492,59.436298728260155],[-62.462622582219929,58.995577397760833],[-62.255122719308517,58.849336918581074],[-62.012856149210975,58.493455199597705],[-61.62064290443179,58.275549850206474],[-61.520272257465038,58.18622434425933],[-61.22236411337078,57.765737045642055],[-61.107359591262885,57.49320362027283],[-60.874274903679144,57.205033253035296],[-60.83840014027831,57.070561597897935],[-60.824969788627882,56.668317149358373],[-60.735782287576626,56.547895457402291],[-60.434540055693382,56.338187215997941],[-60.061284440739414,56.197801477458718],[-59.955642834800834,56.101605311419952],[-59.807780186592211,55.886379012906879],[-59.689100314667286,55.806449372351395],[-59.420871009220363,55.708514353447718],[-58.892322264899789,55.644433363084303],[-58.4570542318381,55.367018550958463],[-57.817401659800517,55.353162433303488],[-57.499597057621941,55.162010133680333],[-57.259829492013182,55.086620357750512],[-57.09822913919016,54.984782538787044],[-56.99564408375722,54.876742800085509],[-56.929446179052107,54.743273267421579],[-56.89157238662348,54.494024707756559],[-56.777312181619834,54.329974048621963],[-56.669847734218493,54.279391144512459],[-56.385003597413913,54.257680521590828],[-56.252517387426451,54.216630826773105],[-55.604716106494521,53.815300582635821],[-55.320583221397939,53.357740168875722],[-55.298877538498402,53.22001839722099],[-55.324592966290311,52.857571840071067],[-55.281369734969111,52.696468461117306],[-55.185609802908353,52.54267001206405],[-54.878732983048252,52.299369553174692],[-54.780733775013694,52.069120448052836],[-54.791353091336738,51.86843914951497],[-54.949327386775998,51.52452101508338],[-55.04903002482471,51.176435067753168],[-54.97078929392319,50.819167133876398],[-55.050869678502544,50.475442538268219],[-55.018892638296535,50.271591984523297],[-54.946107811121912,50.183209526101869],[-54.844785102834493,50.129891622835267],[-54.71523601435554,50.118601865553622],[-54.2145237564959,50.244315812425171],[-53.97763327696866,50.223979291001612],[-53.651804329458137,50.037279283248523],[-53.480310263063039,49.828094101092098],[-53.225950160862567,49.62611239999562],[-53.137163714155015,49.513265018765715],[-53.006085759928943,49.221838764042829],[-52.708813083124276,49.018797230724246],[-52.582809290017607,48.860948740390761],[-52.493958176499454,48.468505901647831],[-52.227555985268168,47.867285809238687],[-52.169914787533955,47.499312741685614],[-52.182638875667962,47.354205652273762],[-52.366751874518314,46.994826060241849],[-52.502042441203308,46.622052530587524],[-52.74114702012622,46.305911790572281],[-52.96713501953424,46.192927821478584],[-53.566541045973686,46.129229448974399],[-53.768418813464265,46.171319520925479],[-53.993101868378567,46.296005029009493],[-54.307551187870942,46.369599004924154],[-54.617929319571111,46.583938643791704],[-54.724985738564627,46.613726385435356],[-54.832537046055911,46.601721122560491],[-55.11619098382328,46.448184396458728],[-55.248002005276398,46.41117621224145],[-55.84366420235191,46.382363605998528],[-56.025575662454585,46.40798691424127],[-56.310150621848997,46.582329462785296],[-56.530121757135944,46.97885664472129],[-56.68701195532568,47.058628555715501],[-58.055190234049668,47.172364786720941],[-58.396432683392398,47.16654569008471],[-59.114925567505011,47.071943718073456],[-59.607112218188007,47.188808812181918],[-59.725009407265041,47.27854187014136],[-59.811434028269147,47.398885352587712],[-59.866464145949514,47.637951740692444],[-59.85018599407875,47.991796907246481],[-59.688546635424913,48.338555289500036],[-59.646878648665833,48.697261953273411],[-59.587474433894997,48.828019530305838],[-59.493351643348149,48.936496739818267],[-58.935162788311061,49.266592055877901],[-58.767872707235526,49.430787094321907],[-58.583418620498229,49.73275559939141],[-58.38033341438144,49.930296784726501],[-58.265820426484446,50.095996103334656],[-58.210953732291159,50.231240298575131],[-58.219918877167423,50.383016073971206],[-58.292895997059141,50.50130666046779],[-58.413220283052581,50.570879565262445],[-58.578089197624593,50.575904445060488],[-59.754208436626662,49.833735180604187],[-59.989391685443337,49.763695884605397],[-60.964890111783049,49.700200198068757],[-61.103512252604069,49.649706118999632],[-61.200083942182424,49.544471498267562],[-61.231294629601301,49.428787152994467],[-61.198260265341979,49.106969603154681],[-61.248925353987651,48.917904065144008],[-61.368290527324518,48.762776104205301],[-61.615112045262492,48.59729870263056],[-61.871899427379525,48.559332429867254],[-62.837440699513856,48.67164314435982],[-63.446117139040275,48.80936581455191],[-63.54305164756861,48.793473651683328],[-63.713273177520044,48.656238840813813],[-63.783440396553779,48.383618490332452],[-63.943302724810728,48.095570481923424],[-63.974042148448667,47.758043920398833],[-63.913588849158181,47.610798130035938],[-63.627287509816163,47.400348967968945],[-63.408385757673194,47.070794340745671],[-63.006562640810444,46.933910226568386],[-62.801109905587992,46.94762753191182],[-62.61517760182182,47.025630192847672],[-62.573892348717294,47.073445682481285],[-62.354870815735758,47.677810809109204],[-62.181251210584108,47.853008356117314],[-61.655523808924414,48.146184891875755],[-61.471518070422341,48.160319734663574],[-61.161101338200076,48.077998519556644],[-61.012917465401614,47.957717206838957],[-60.854885994412676,47.644810592547948],[-60.720378675589096,47.528948898324316],[-60.076270744697325,47.444055036415897],[-59.940408114737394,47.380156164984555],[-59.80010485045468,47.238193561509171],[-59.726510410485673,47.052661232359931],[-59.698186571535445,46.714851839318918],[-59.440003625219411,46.426068251776677],[-59.362867507202289,46.250106861096221],[-59.336498730329147,45.879261165277754],[-59.382939119518419,45.739397138123806],[-59.468136362892146,45.619147042234914],[-60.120558421751056,45.228753755239531],[-60.50439688605011,45.095787214416447],[-60.902379102828554,44.775643223143398],[-61.510128447227132,44.64250899900977],[-62.386874764847484,44.361635702008535],[-63.030905762466404,44.209004608849646],[-63.475454840255146,43.999538502055628],[-63.913565405021295,43.959452528611457],[-65.180550884015048,43.078280540111848],[-65.320544891220308,43.051125370769839],[-65.776361984467073,43.04847513893116],[-66.297519331153978,43.318516713851508],[-66.443877203418623,43.429171473695156],[-66.572601523903231,43.591467375037965],[-66.699659867302202,43.923772735622656],[-66.778677941912349,44.02768192412524],[-66.888852435728055,44.106435317349082],[-67.127925749195043,44.186604133474781],[-67.246425629658901,44.272757789316309],[-67.476719435484313,44.648578897095973],[-67.829313689897347,44.92246393874332],[-68.008033287838671,45.227303365202459],[-68.241066222699416,45.489725355983332],[-68.298307503771639,45.672491018436382],[-68.312824521115743,46.51641942517287],[-68.394499524095409,46.65420361585187],[-68.535709207616733,46.724775925539028],[-68.876749622493719,46.703025385302027],[-69.148182953779397,46.727514565925524],[-69.317622169542091,46.669381319791164],[-69.490770482211644,46.508223412571411],[-69.75365432931153,46.023282012081886],[-69.825544099343432,45.74052163259168],[-69.893802875221027,45.611136687869497],[-70.493056476220886,44.938123132414489],[-70.678037812983192,44.808122431372993],[-71.063009419650925,44.720783432564119],[-71.268207488320812,44.5753774762494],[-71.465152628487004,44.511337976258318],[-74.646070032241909,44.50393520304376],[-74.852160502710205,44.48143309188842],[-75.122478260744415,44.354188813198476],[-76.067065096405784,43.725491284203244],[-76.482553894309618,43.261213300783517],[-76.610696484862643,43.175887982979347],[-76.810088394883422,43.129875899524421],[-78.230226098642746,43.100468731431967],[-78.342778941653805,43.02274690610615],[-78.388719220211414,42.950648679706546],[-78.465287180245568,42.696287669258716],[-78.53828762751624,42.570687769693301],[-78.844797338296601,42.341440416060408],[-80.121759207664084,41.883043153543191],[-81.163701117579635,41.707652708774589],[-82.281321798259597,41.201629237941297],[-82.667467205580536,41.175170437864146],[-82.870639169004619,41.217495414158797],[-83.355492374503569,41.454525764093638],[-83.564412537405389,41.71046085825013],[-83.640368422034129,41.952894066582076],[-83.632943169574006,42.266161965843907],[-83.497079765021752,42.563474874435897],[-83.393082544956499,42.683133163805159],[-83.108651749224762,42.845274231431183],[-82.986165955747552,42.959415300717858],[-82.701873343375297,43.632805620228801],[-82.705040597717954,43.802742063028937],[-82.930188849956437,44.787382600360878],[-83.041156316101862,44.998921897563648],[-83.83694227116078,45.381961691966559],[-84.035132110341038,45.551871981536671],[-84.286091625648282,45.692571392673138],[-84.525499323612124,45.91629518916335],[-84.844725332023117,46.046260004691334],[-85.129203246807819,46.281608652081573],[-85.370064428801768,46.55800434413608],[-88.382778879687322,47.760660515209956],[-88.495214719325105,47.761544733597908],[-89.138423602759588,47.539341784559475],[-89.454988038612868,47.496945170987487],[-89.947169122192307,47.498450059211102],[-90.317909968740068,47.596005608882123],[-90.964752320014426,47.639264766441954],[-91.548267900162472,47.559951802270703],[-92.126101899501577,47.764644414263429],[-92.579814183840355,47.805822199104696],[-92.709610963594443,47.874220333997357],[-92.909255244278285,48.049796792694892],[-93.04420079535393,48.101346061318047],[-93.226613664816952,48.113976511677976],[-93.654047451744376,48.029459240696269],[-93.792718216569241,48.033874996778565],[-94.175193714115409,48.167198870493991],[-94.788359020026789,48.272296322046344],[-95.140380092017153,48.467736134597551],[-95.418309574824889,48.493149531975028],[-122.49218157356678,48.49301775605948],[-122.60093921076655,48.477068867002544],[-122.72225264538504,48.413082824863586],[-122.95213894208737,48.063809534846371],[-123.15215953747794,47.937755581906835],[-123.56359954242306,47.823965662751441],[-124.07595385964196,47.91147620938623],[-125.21600436886166,48.253493969750181],[-126.22818285455315,48.765070994792566],[-126.78141643224792,48.972300117004217],[-127.57827847113174,49.587267703358378],[-127.99496900631185,49.646210048423335],[-128.17152612479507,49.734897073585962],[-128.27361218596567,49.842899404664905],[-128.41188410014209,50.085818497998282],[-128.74683334507799,50.39399016493757],[-128.83279032774035,50.570513285438672],[-128.84860677066234,50.717338919540701],[-128.8208438504808,50.862380799558721],[-128.59911090148748,51.306422615928639],[-128.69106763583744,51.678738260970526],[-128.77736564456168,51.779795562904809],[-129.45424579740111,52.20901299785217],[-129.80069255263174,52.61049980249161],[-130.32296684943987,52.758276602110264],[-130.4945489765089,52.689093731277971],[-130.58706851631106,52.527831198123913],[-130.58868077852,52.401386608464243],[-130.51249144517521,52.064827145868236],[-130.59841414272722,51.739900657733507],[-130.68327028530757,51.617610967820795],[-130.80036568422068,51.525719723700668],[-130.93932318915361,51.472370449896978],[-131.08782820082078,51.462291030156528],[-131.32091804788271,51.542295848827017],[-131.96119915930828,52.056077822263497],[-132.46943355871352,52.387645870674689],[-133.11458350775905,52.972464819155107],[-133.50137654980443,53.57082887213901],[-133.56330561902763,53.715627322065423],[-133.54912955717464,54.235121606185494],[-133.50269718986294,54.375988546876371],[-133.41698262800787,54.497035949160647],[-133.25504635380543,54.609562637995857],[-133.11170613152916,54.647681404916767],[-132.04825339963838,54.564299885806982],[-131.49060757566266,54.699020051039959],[-131.21529380484117,55.024131037343906],[-130.87475122734509,55.178871781201799],[-130.70504477412084,55.320697890402762],[-130.63860512478243,55.464224290689195],[-130.63842316805801,55.550015385710836],[-130.70569850108029,55.696979152420411],[-130.81212545414309,55.789129252345944],[-130.99242244198001,55.877824421310521],[-131.59474766857099,56.068549619899123],[-131.96245827621163,56.110623498693379],[-132.1323142873178,56.197564327930429],[-132.71530240991578,56.752893361940785],[-132.93092891837168,57.172278148380734],[-134.01165519367581,58.215624847899939],[-134.47755761803626,58.422951898132162],[-134.82261619669362,58.693670393451086],[-135.1832793394629,58.850762920090943],[-135.50852734410179,59.14981224679341],[-135.62006811147722,59.196398439590709],[-135.84994985262119,59.159638737267812],[-135.97879792760756,59.074991811319002],[-136.23936140782433,58.786298164181133],[-136.40003042218271,58.686284673692782],[-136.74479152305668,58.638053931103499],[-137.33502218595456,58.421972058057705],[-137.48918234198834,58.407119730312985],[-137.64875367211778,58.433046183963832],[-137.78203344565065,58.490299386413227],[-137.8932701893286,58.583400482430477],[-138.03666119705903,58.840506245640995],[-138.14696560824734,58.950913750262707],[-139.52099739999204,59.704208625830177],[-139.66675042593775,59.735034052588517],[-139.97034454321476,59.684172588844056],[-140.3183456263578,59.739483059201774],[-140.61006393340051,59.726602794052418],[-141.1345797834814,59.819176657815198],[-141.27109817244713,59.8801651524862],[-141.3834790829543,59.978792724501396],[-141.46167257472112,60.106239359817316],[-141.50118693611194,60.301052702762533],[-141.50122235337778,69.649746596115506]]],[[[-60.235446590409332,43.46676829627156],[-60.453031242071717,43.582738281705112],[-60.588739297205066,43.788591243628005],[-60.609570835317371,44.034270231700603],[-60.563571474111924,44.175909745749401],[-60.4778964399759,44.297718947770761],[-60.268894011403987,44.428524540202979],[-59.755141830800355,44.500178515701414],[-59.563519301685723,44.472220239263258],[-59.39689213365952,44.373547822847904],[-59.280254974380412,44.218962467910963],[-59.231103798452224,44.031652508777569],[-59.256811451125138,43.839715082829088],[-59.353521696178483,43.671941463367027],[-59.464270445479819,43.577356378888297],[-59.792790020051662,43.421875166807347],[-59.992150037655918,43.409685406967569],[-60.235446590409332,43.46676829627156]]]],"type":"MultiPolygon"},"properties":{"alpha2":"CA"},"type":"Feature"}, +{"geometry":{"coordinates":[[[96.326303481587345,-12.139958791955715],[96.358214270779072,-11.950143035268527],[96.4940540498866,-11.752586733384252],[96.659868934010063,-11.65484347706091],[96.857078932016179,-11.627011804506862],[97.139928354978011,-11.712532253338306],[97.293727276732326,-11.829189735576147],[97.396382429406415,-12.006355553870755],[97.423073807819222,-12.220535010484914],[97.357905700509832,-12.433197016782108],[97.232576286543306,-12.582714262351381],[97.052586622817543,-12.67775304390045],[96.822152707971824,-12.696369556399315],[96.63545133122193,-12.648937715799624],[96.477345094635695,-12.530950896465924],[96.370035144489165,-12.363341968772199],[96.326303481587345,-12.139958791955715]]],"type":"Polygon"},"properties":{"alpha2":"CC"},"type":"Feature"}, +{"geometry":{"coordinates":[[[11.836910505772844,-6.0871499046415769],[11.726585402323837,-5.8702424850959343],[11.721105700396967,-5.6749278234321912],[11.819095827077614,-5.4521759610873559],[12.007811049543152,-5.3034933177464323],[11.951984721922811,-5.0882885420483452],[11.983320109778887,-4.8966286454157464],[12.085316999387702,-4.7313650468241732],[12.538853303343918,-4.3302098794748813],[12.975825246944048,-4.12907682184363],[13.165969943814734,-4.1052330074825099],[13.327829315199228,-4.1446676526048343],[13.545730593598982,-3.9840253464434019],[13.873031520482202,-3.9573327906158746],[14.315963942976008,-3.8014643682624625],[14.466099273595704,-3.8114664489300023],[14.606460558863056,-3.8656823941496703],[14.802786531741534,-4.0684209270461942],[15.306348798260647,-3.6266076201523729],[15.560111305812317,-3.5046032694646381],[15.666770187864165,-3.2952286590643336],[15.714389360237339,-3.0075421692246831],[15.717870050640334,-2.1296403190061617],[15.77161253096655,-1.9478562348311217],[16.134050300150818,-1.5403830389204722],[16.504188894835544,-0.89621439304711747],[16.997177441581336,-0.57810068099717471],[17.22669671790138,-0.34871999759268468],[17.234674883342059,-0.17573367339359619],[17.391873547382882,0.33903506424288682],[17.405978624322135,1.1752076931302213],[17.560386281191203,1.6403252720249157],[17.586007944664068,2.1287490446409243],[17.754605381150604,2.6173311128876198],[18.030397069272311,3.1258328485086406],[18.119790029024355,3.3914390222348705],[18.130726224984631,3.9267101594834259],[18.068852094710493,4.2558182167026004],[18.133765293791704,4.5407254303307392],[18.253641454846459,4.7120466256599958],[18.459628706655597,4.8702550512497558],[18.708370170072083,5.2378629036220392],[19.078717280303398,5.5064751453098033],[19.454826746787006,5.6250273128046544],[19.921284668111987,5.5757565663211164],[20.070043203332627,5.5139917957231068],[20.541139314607786,5.2178663124142233],[20.837197772873868,4.9273545238689183],[21.049982297811141,4.9042463968990289],[21.306335596277414,4.8038113403803013],[21.52863828763202,4.7622873949720121],[21.727942590367029,4.779507607129541],[22.196327623042059,4.6962413446778992],[22.3394036924532,4.9242697621212219],[22.572690992241046,5.1295938706269792],[22.698973717371523,5.1954347063298298],[23.140443584136193,5.23612214774323],[23.375114867598359,5.186399568030204],[24.151151402602789,5.4646021823823459],[24.370265361362552,5.5052554005089682],[24.782800244257594,5.4431513719545173],[24.934388195296822,5.4615033114579479],[25.102246481453413,5.6571148218539253],[25.416545847368528,5.7999447667831801],[25.619198766440764,5.8029488360291692],[26.74097477167842,5.5880623055750176],[26.977763143981438,5.6784969698550745],[27.168009368179739,5.6948467371602636],[27.678608735393553,5.526256888717195],[28.150204480360127,5.0002801388672768],[28.351938730927802,4.8639070592314289],[28.556391261478787,4.9746749234167646],[28.691377266522661,5.0034437604188433],[29.092849083243529,4.9392614418265381],[29.235169052687599,5.0531902194282399],[29.503722800645441,5.1334633636869258],[29.690682739325556,5.1161973493985986],[29.904287617234033,5.0318764559317382],[30.056027160789377,4.912420605249066],[30.494762139766053,4.4015124945821391],[30.773594133039236,4.259171208507829],[30.937995142481565,4.0908022439620675],[31.09801837765664,3.989767640459239],[31.351273565333326,3.6681706750987986],[31.394222087464133,3.4341248014882599],[31.301968302165172,3.1184180562621728],[31.350221097682144,2.9127137243784924],[31.330467610882931,2.7524039745768438],[31.533075764871324,2.6201157238730621],[31.718715432046402,2.3742874767637359],[31.773716878729783,2.1402643139586228],[31.729172965552053,1.8936912131303134],[31.662777397517885,1.7588659414012866],[31.285289676071542,1.3187209492284426],[30.790451957879259,0.84883532915547355],[30.642566541650556,0.76609384005527525],[30.436121602215426,0.53177857701117059],[30.403679304228888,0.32703597763763176],[30.194237714145149,-0.10834745553020404],[30.064400367033201,-1.0153665696948337],[30.057909965553488,-1.5236700044552465],[29.928437601940484,-1.7431485009208374],[29.632963751899613,-1.9764869564394172],[29.609272536735794,-2.3416676297797974],[29.477881162830002,-2.5547698320261363],[29.652497376255894,-2.795666234628027],[29.716735527561944,-2.9677374258978242],[29.715852956102054,-3.7411398980717769],[29.81156930864033,-3.957071265925606],[29.900566091914616,-4.40341171459528],[29.840578058306392,-4.865552324062052],[30.103142229953228,-5.6622033314311349],[30.097404757390226,-5.8191774495286941],[30.004225330464074,-6.0647737946862454],[30.016017934567579,-6.1297112338265221],[30.115332921367852,-6.3038999249408434],[30.538337567229114,-6.6609689710056159],[30.643658548347222,-6.784291769324966],[31.2042399541122,-7.9826150513028535],[31.250348554172234,-8.1705220060526162],[31.235804708819767,-8.3153068608497804],[31.180188958070694,-8.449772714823526],[31.088211773063502,-8.5625307524723997],[30.967663318806142,-8.6440307389290432],[29.367496401584198,-8.9177818644698377],[29.191092235170821,-9.1941005342435336],[29.00831166131757,-9.3729193671578468],[29.124450364483966,-9.7571735816873169],[29.112455719034582,-10.351232077099839],[29.143097291622155,-10.601312384308358],[28.890293379296743,-11.461836162575134],[28.937269488721824,-11.557918316545251],[29.232172500813551,-11.81258165490169],[29.347080880107146,-11.754194471002975],[29.721374618211708,-11.661174533786575],[29.865359721771103,-11.660730636245576],[30.003501151607018,-11.701336700508516],[30.190259771965451,-11.849660422392482],[30.287066488740866,-12.067621814246319],[30.27185496099667,-13.494174822937438],[30.187901644900986,-13.719926100029859],[30.008143057657371,-13.880234080818406],[29.819672191861791,-13.944048369535169],[29.677544411081406,-13.951503242719506],[29.418166224876099,-13.85243033320212],[29.172649229417999,-13.897216397441451],[28.80298586871832,-13.821796874926394],[28.621259453993765,-13.677665158323594],[28.381050612692185,-13.30904707274871],[28.180097980792979,-13.171173140071797],[28.025508488036063,-12.895446469969011],[27.737394071062706,-12.775753947248354],[27.333106761659796,-12.665124820147609],[27.188692394253962,-12.545691041426196],[27.094310085379696,-12.392437116197714],[26.824101355511825,-12.46748477793715],[26.677286521416615,-12.471617153177535],[25.911427049207511,-12.376686901336353],[25.554878224927929,-12.252604514962336],[25.373088014155801,-12.233485439397796],[25.239034610963653,-12.172082343435878],[25.036720775460658,-12.012873092938399],[24.890625592504797,-11.820613613509364],[24.569748983616208,-11.936143159121986],[24.163281626751587,-11.868405766614792],[23.970102156699685,-11.712631022196099],[23.856427127914404,-11.512426507143637],[23.482594903079853,-11.481270257739549],[23.141481992770313,-11.583391626852302],[22.66123698098157,-11.566408046956644],[22.44635285638422,-11.665043931518495],[22.300601621913025,-11.693431221043699],[22.152940808514771,-11.677761353685273],[21.975478586408155,-11.591272706809212],[21.822770239670081,-11.39859441782696],[21.682783032123684,-10.959911776694586],[21.692272171042205,-10.774464768899165],[21.790750516725332,-10.562324436163186],[21.781498426138185,-10.372037795112384],[21.743174426164927,-10.266179903897042],[21.433455919602739,-9.8600607744866391],[21.319628967892562,-9.5472782745545164],[21.403806648680295,-8.6700612525217711],[21.398996851020275,-8.4711452088360861],[21.308508654887987,-8.1973888577039347],[21.285022735084475,-7.8035636869549174],[20.490118531080658,-7.7634374618450668],[20.259037417624775,-7.6413418899638401],[20.113953468767676,-7.4707219495196231],[19.983776125302569,-7.4802782325525987],[19.952769867318505,-7.6337078890158434],[19.861336137657972,-7.8132873848963369],[19.799699929417208,-8.1644714751242731],[19.689383438701142,-8.3246651260038558],[19.526287460245229,-8.4306435941827544],[18.940173730309951,-8.5012060351773986],[18.611881714226559,-8.4501158236270104],[18.066856917885215,-8.6039244067438219],[17.830523960111755,-8.5755798498612439],[17.487144100685004,-8.5901240025863022],[17.351124768326606,-8.5434798879454608],[17.200901045293438,-8.4251855519871715],[16.548315071349336,-7.5007578964968085],[16.450922101869995,-7.1262113755150356],[16.264165677115628,-6.7612677766441314],[16.205858590940707,-6.3659873180028033],[13.305296555402885,-6.3815616109727644],[13.023332279486899,-6.3519330801439278],[12.626619493148603,-6.4899246477880279],[12.471188551040136,-6.5024283896607313],[12.275162384744393,-6.4670335273474873],[12.143866071785357,-6.408193402193481],[11.836910505772844,-6.0871499046415769]]],"type":"Polygon"},"properties":{"alpha2":"CD"},"type":"Feature"}, +{"geometry":{"coordinates":[[[14.019629522664161,5.7550782853556477],[13.934363134417133,5.9841324418602175],[13.940073493315989,6.1316272401311283],[13.988600108101867,6.2710277113396344],[14.149918946619085,6.5062451154374257],[14.383757507511479,6.668178438066831],[14.596571263896189,7.0368411796136998],[14.795436523348661,7.4899844250985881],[15.218152848008987,7.9493488655478739],[15.461480890653265,8.0231730726839228],[15.826972878823508,8.0018913717958498],[16.047824933721106,8.0968678850300861],[16.257685127113991,8.2743166062144109],[16.436183970340711,8.3531879006524772],[16.679153519537671,8.346904690103802],[16.813250423279609,8.2872345095250459],[16.922148394618617,8.1934050365684747],[17.505364146064917,8.4620989869059429],[18.29678456429264,8.5336448152605904],[18.418647067059354,8.6607803469533149],[18.386286289960335,8.8398935949760418],[18.433610861161945,9.0915513506957026],[18.586141190030194,9.2879528654159031],[19.044722341441624,9.5056220543177741],[19.597588536434657,9.5211712828679111],[19.99124877825534,9.6262978452000603],[20.214826661604864,9.6423654520019468],[20.447195035525439,9.7937476329800042],[20.917652470082082,10.33509694918256],[21.07450615120478,10.434606590016122],[21.233221249127801,10.610863370127239],[21.286156307991952,10.833899024206421],[21.411494565755209,10.993567867686012],[21.811022790327975,11.265241213698012],[22.314175458802474,11.462589469787359],[22.50130477305612,11.49594122828738],[22.962235360977548,11.408884272169544],[23.135133167031444,11.336901945947641],[23.690328508113499,10.715393930755644],[23.99585143300072,10.246326085475213],[24.142483907656441,9.8304180019139213],[24.153797050087089,9.671867890863135],[24.094082902652456,9.1756237171798407],[24.322841175426909,9.1364525155346996],[24.460617310632774,9.0765632325569214],[24.631700092288519,8.896061838872555],[24.691922542265448,8.7053066660261607],[25.038257189950553,8.6018145119656406],[25.161484020822957,8.5309578520560922],[25.609417109020004,8.0950965003714295],[25.723238905323559,7.8770762491004325],[25.746979398287248,7.729316392989535],[26.189494436062695,7.4645501578203604],[26.73279542108267,6.9701533744802733],[26.846855718618116,6.7555595908387929],[26.857835177762777,6.482985316488036],[27.422917362280124,6.1361820388703014],[27.562223739237254,5.9975253457125728],[27.703249236894788,5.7220817029868822],[27.743164710806525,5.4853508605087269],[27.869036524411584,5.2898890237985796],[27.902468644672531,5.0951272096745619],[27.876134707917782,4.9488416739704082],[27.776881407449526,4.7779655397322438],[27.619177684541999,4.6588882258259972],[27.477119300033262,4.6151578065274963],[27.328482383988142,4.615120795035824],[27.135736573277612,4.6614593574386953],[26.963497773186724,4.5830226358171569],[26.779261210429503,4.5644363144005409],[25.675075767994834,4.7618252365897504],[25.485824000653757,4.5842727338419325],[25.170620654587999,4.4788993187613455],[24.782480471920476,4.4306023678691053],[24.381356124204888,4.4815013558656096],[23.4942599302201,4.1693034683393213],[23.349658303176799,4.1678993465563572],[23.085271595541748,4.2280017071781479],[22.86619744095028,3.8615972436299266],[22.667512915468212,3.6995622469811695],[22.474788148388139,3.6379834044259853],[21.715709110018768,3.7682663487234471],[21.443756308324939,3.7539530498973654],[20.834038399962534,3.9261293877798042],[20.406262813336319,3.9864228059444686],[20.231382123560198,4.0871676466296361],[19.907410747324484,4.4358052698522297],[19.613144105956863,4.6099972783087155],[19.550583974019087,4.6154672459510309],[19.448876191739036,4.5464673765807868],[19.121225075766493,4.1029739289324025],[19.098498154382437,3.3717000358715454],[19.019271689159449,3.1914362495369253],[18.917138748342119,3.0841814372077017],[18.74096418230252,2.9962360693013568],[18.593858316821734,2.9790722495364381],[18.366476220529123,3.0426085160377458],[18.086080428485058,3.0056954111725589],[17.459524017915736,3.1628911121822045],[16.980481939667968,3.0679859539841905],[16.935431966937855,2.6535220033520517],[16.605416310057315,2.00239633760942],[16.433221478606342,1.8374241478154594],[16.299452769260469,1.784193756407102],[16.156061906754395,1.7712862888277894],[15.970446345566282,1.8181539617248235],[15.785132362071328,1.9682393071289528],[15.623248447865848,2.3467909773508224],[15.570427884390755,2.6733437250175691],[15.40415364383149,2.7916452636197682],[14.681013739498754,3.6049531808680433],[14.552869911978558,3.8842958479799985],[14.542376752387758,4.0976749892765065],[14.32953698492762,4.3050817252710161],[14.247322803606817,4.4775354713470623],[14.167295081704161,4.969857785835635],[14.065165642632124,5.2365489389817315],[14.097302317878253,5.6423376974237982],[14.019629522664161,5.7550782853556477]]],"type":"Polygon"},"properties":{"alpha2":"CF"},"type":"Feature"}, +{"geometry":{"coordinates":[[[10.778797206736765,-4.2716743961134078],[10.650758469978337,-4.0572566149891776],[10.642393891323799,-3.8076590653280027],[10.884329087078486,-3.3470190257265475],[10.998725160925776,-3.233916646897705],[11.147236823579997,-3.1474408740177275],[11.060911750473243,-2.9858972139787716],[11.040611742233994,-2.7863148599251373],[11.098463931855733,-2.5668944532280875],[11.126391111599949,-2.1895949213577892],[11.260692892940044,-1.9793700970499224],[11.385163351632144,-1.8944160125959519],[11.529292860783693,-1.8504071936596536],[11.763129229171279,-1.8706817776203359],[11.939847135679836,-1.8456123342306514],[11.998988285979987,-1.6796780886442273],[12.122766621202461,-1.5364171574781766],[12.360897873588728,-1.3829481647473845],[12.542883145707302,-1.3293892972163794],[12.776781574505124,-1.36311717876729],[13.067333287944924,-1.5137606828367614],[13.181283078029866,-1.6165628128631684],[13.31113531674543,-1.8447694967335913],[13.567141936663702,-1.6674186558774458],[13.706988430746266,-1.6395200361488371],[13.883970813117495,-1.6622848985131682],[13.921456527478284,-1.6185443037542442],[13.950820059457376,-1.417878115777093],[13.924064397779851,-0.83884482595234466],[13.79029380795615,-0.71265709453232451],[13.509745390530615,-0.55974231372704142],[13.419208194897692,-0.43865054704897777],[13.362145882111507,-0.2459286421183168],[13.4422969214065,0.44346168025976179],[13.710569792710434,0.8643760411008472],[13.291357412942883,0.75808571501135935],[13.098639649020823,0.76528683426641619],[12.884910531164127,0.87493220714974307],[12.790517737855726,0.98505007320673887],[12.731700962832397,1.117626582548944],[12.675241713652703,1.8417922132704194],[12.835077915210965,2.3603061624211734],[12.9446614071986,2.5192613380124502],[13.062225430719806,2.604478830348008],[13.247384930175306,2.6591711453917717],[14.37287472703014,2.6567792154119481],[14.600532931220762,2.6983819912389357],[14.737935811747059,2.6728774005359446],[15.069433487106391,2.5283344155235157],[15.259017142073214,2.5254784102386605],[15.472787648327747,2.4451347611304266],[15.678354264969878,2.4152944146365076],[15.957671432387638,2.9374893419425865],[15.995905471508006,3.3013544057840201],[16.165094300323993,3.7316713382841566],[16.246786646054503,3.8479070244609033],[16.399940095031383,3.9585345118322923],[17.088565604171418,4.0832899445097732],[17.310500221323132,4.1669980532153978],[17.464691323216243,4.184397948111938],[18.037551386921205,4.0485118877429986],[18.584399469738397,4.0966836566215514],[18.722212479705419,4.051638714189993],[18.841307507194514,3.9689474390625348],[19.01472700640424,3.7720317867166946],[19.080183955622871,3.648687782564477],[19.118299616483558,3.2440553118553788],[18.573086171179483,1.9122977193932968],[18.552686455438693,1.4653054943258315],[18.404424864830894,1.0150116733180203],[18.421279259732191,0.47620228515075819],[18.376695801055074,0.13164839782502807],[18.23448703686962,-0.32283130367703716],[18.252570507591052,-0.55457055759672957],[18.228654147108148,-0.70183398288813037],[18.131753706331352,-0.87489787776654082],[17.627937656317979,-1.3580247361188507],[17.254890956509644,-1.586686277580033],[16.955103452944702,-2.119485135936539],[16.696958821274702,-2.4010842201483764],[16.716131534788982,-3.0623083474762058],[16.631849353128885,-3.5835485796725188],[16.218198337499384,-4.2951798784456185],[16.10415799596429,-4.3771150498877587],[15.837653258279239,-4.4884851133979469],[15.054455438156086,-5.2418214150654405],[14.855135134819374,-5.3592763838505242],[14.670613285551205,-5.3800773475738115],[14.278873396797694,-5.3133806821599157],[14.102382886129357,-5.2245631584817982],[13.988577802873706,-5.0975654044457679],[13.58544177782432,-5.3071825460491944],[13.352808635274911,-5.3332914365086692],[13.174064805859352,-5.2753342426621668],[12.776887738345014,-5.0080001119125193],[12.701438281288873,-5.0444444394428887],[12.286075033530141,-5.4262669795419773],[12.152736545800051,-5.4855674057702055],[12.007954933684234,-5.503842461286979],[11.818437958404168,-5.4622255364629488],[11.65903425035903,-5.3515950243400532],[11.398103783704693,-5.0221637606893195],[11.301464332466189,-4.7887998418078688],[10.778797206736765,-4.2716743961134078]]],"type":"Polygon"},"properties":{"alpha2":"CG"},"type":"Feature"}, +{"geometry":{"coordinates":[[[5.4703847048900878,46.202917594303734],[5.4912466752077451,46.357946360998866],[5.6160782404232652,46.672096846328415],[5.7600187964886231,46.909583107342094],[5.9720857730747019,47.082245839937904],[6.1249163227863956,47.322473821809879],[6.4067425426293791,47.471611996547352],[6.5047570387026621,47.699576851467647],[6.6802657358898303,47.861547182256622],[6.8135417770960407,47.928371043268712],[7.0291401951692993,47.978076179350495],[7.2677521330922374,47.97135596282012],[7.5602844020124182,48.089366181329034],[7.9251621959079488,48.067502420223306],[8.1009221745786348,48.091116684605225],[8.2746748767926555,48.206080775067861],[8.4519768411024447,48.263621248191718],[8.5921258725525131,48.261614426618351],[8.9828616682381952,48.164651313994298],[9.2765691082243436,48.161569595437527],[9.7921920116584822,47.950435928938951],[9.9668625622346383,47.832396077122624],[10.057966244357761,47.718105591678665],[10.112127382956476,47.582352996767561],[10.124048503333809,47.430639066480929],[10.356778747461336,47.484426888586242],[10.585302540106206,47.425364331986323],[10.837554081361954,47.220516977395874],[10.912824463559264,47.098832247826095],[10.950549067900594,46.96081174908084],[10.92933416278971,46.51620214024765],[10.877642881756849,46.326535045984443],[10.756830658688084,46.17145441554257],[10.602177901659282,46.08127472060788],[10.451104320897851,45.856738817137725],[10.323911085122706,45.7783796774198],[10.179245438329231,45.741095302111766],[9.9379112187343246,45.749053525112771],[9.7755490903722766,45.815401666134548],[9.540848793981187,45.804023192369385],[9.4964856616938356,45.657811270498883],[9.3835003414673732,45.506301426241976],[9.1733423562019691,45.38108523308054],[9.0353586786837283,45.336990815381618],[8.8905257416255381,45.334301340197989],[8.707727338453422,45.395006187195847],[8.3767224478141529,45.695820415623864],[8.2424922750994156,45.582796571038187],[7.9024329561119488,45.435942410416416],[7.7451708972920077,45.426041667841361],[7.5551240559936055,45.461897378493909],[7.1342867796040057,45.380670168335598],[6.9831182480065142,45.402403479683059],[6.7859441412898738,45.484830615739021],[6.4526095141554176,45.778994371696491],[6.1592396477441174,45.652627855293019],[5.8542252614172501,45.66541089250223],[5.6729738364717042,45.750473148384039],[5.5671340707293107,45.857625211234257],[5.4843116606621471,46.03991101652683],[5.4703847048900878,46.202917594303734]]],"type":"Polygon"},"properties":{"alpha2":"CH"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-8.8584885416098711,6.0778521125827227],[-9.0324677198593779,6.2513272965780917],[-9.1028146263297778,6.4867284577405719],[-9.0525440303015401,6.7272180855866912],[-8.8100882359624144,7.018574015795048],[-8.9667174003505536,7.4206993255222526],[-8.9829465169230396,7.6143388078471377],[-8.8985030267198759,7.8411554312866505],[-8.6937314306324218,8.0126682336753916],[-8.7558252450257505,8.2509406661555502],[-8.7004871508265023,8.6423860576876912],[-8.5964936123524147,8.8027514589400813],[-8.4250427102501462,8.9181604665619769],[-8.4228486122140396,9.0669218947864003],[-8.5664058876518805,9.2400942959858643],[-8.631984001876873,9.4270592673004021],[-8.6400770485537564,10.094242077735196],[-8.561435640933702,10.264328134154086],[-8.3347722524372756,10.524854637831483],[-8.132230965482913,10.643946842613811],[-7.8888540680444494,10.872251468879364],[-7.7481139770479404,10.919528548946602],[-7.433184982678898,10.935303910389845],[-7.2855978215114066,10.89214225463909],[-7.1510295089781533,10.79961130133008],[-7.0167412514273666,11.000397722166703],[-6.8571422278586613,11.113079496944502],[-6.7155501545311918,11.152365803796256],[-6.558640935219751,11.147059051501587],[-6.3411567649550165,11.217363889915747],[-6.1501076635582708,11.211315141245468],[-5.9372223985588866,11.104675168218749],[-5.8018208344047517,10.92036493560469],[-5.4417122418187187,10.926356580989598],[-5.2048467074172944,10.803206912159105],[-5.0440906160059207,10.774861289800983],[-4.8953832296106849,10.706647651524305],[-4.7077769280352619,10.551529246549853],[-4.5472843312165772,10.254175546059866],[-4.4794763790802943,10.210087988531596],[-4.4232245487373669,10.203205221719346],[-3.8753176212961833,10.409664970984636],[-3.1075113772176577,10.381513623628434],[-2.9305009814840552,10.300261757768729],[-2.5729101075742511,9.9651743602618001],[-2.3848933440086642,9.8724756314065178],[-2.258108487923459,9.7223817245171222],[-2.1989420119764254,9.5350266905792047],[-2.1744823211622699,9.2848880425879941],[-2.1938098926879022,9.1376116957411853],[-2.1068516298351345,8.8792242867297908],[-2.0063099054619871,8.1949669359499566],[-2.0895064947149495,7.9323436679876265],[-2.2259056881440933,7.7311089975157188],[-2.3870482785488019,7.5907888198713875],[-2.524450848116115,7.0126962759667251],[-2.7323147108380392,6.6734514554069282],[-2.7142923536375729,6.4709953206828237],[-2.5807183631978741,6.0517062463971998],[-2.4475115736854529,5.9606075151382631],[-2.3579832686385984,5.8449835659971079],[-2.2584742378789135,5.4820085269492376],[-2.267162167513475,5.3197175110457469],[-2.3566406695179936,4.9553087138513829],[-2.4630065214801191,4.7989234535756742],[-2.5770376870540135,4.713927803518037],[-3.1055042919647784,4.588986802540151],[-3.9944132214860506,4.739413176424037],[-5.4983693286427222,4.5918358383032816],[-5.7721450471686362,4.5297584023436128],[-6.6651557782302362,4.2043022366452538],[-7.3815263138389025,3.8790323123810757],[-7.5230528229620468,3.8520762582004706],[-7.6663903018841832,3.8665934472471482],[-7.7996380809411203,3.9213785822562452],[-7.9117331931128652,4.0118830985456269],[-7.9933688826858535,4.1305928109937406],[-8.0377673035466852,4.2676517832160483],[-8.0829735038730259,4.9606098224326605],[-8.0482806169745391,5.2217689977299884],[-7.9240712958134081,5.4831308263941825],[-8.1315144483991482,5.6042339436900122],[-8.2680645921333991,5.809583053374598],[-8.4955880017986818,5.8651124481208079],[-8.8584885416098711,6.0778521125827227]]],"type":"Polygon"},"properties":{"alpha2":"CI"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-160.32504032472471,-21.359153638462995],[-160.33140775937318,-21.125551794048846],[-160.25044027578912,-20.927142804338516],[-160.10247379875096,-20.779915176722099],[-159.91651948928285,-20.698065148043952],[-159.67610213807134,-20.697278068236322],[-159.50640976084767,-20.762831957901081],[-159.33465123175955,-20.915136644210108],[-159.25286116346905,-21.094412982543169],[-159.24356396300635,-21.320721864615219],[-159.31381127849144,-21.509349999612784],[-159.44334894773951,-21.650983678711537],[-159.6167010677888,-21.733379972825308],[-159.83911958961622,-21.744833288360908],[-160.08055285666492,-21.675871754341646],[-160.23327807572994,-21.545871827260552],[-160.32504032472471,-21.359153638462995]]],"type":"Polygon"},"properties":{"alpha2":"CK"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-76.088793982922283,-47.098758793666768],[-76.167872028814585,-46.970806491471969],[-76.207845435859838,-46.774880377724131],[-76.096096582789443,-46.372049311329775],[-75.972352791299784,-46.222804424636834],[-75.561822577280893,-45.947521225409915],[-75.555746004974338,-45.772004763133978],[-75.47551958667313,-45.587527110490569],[-75.370754586671467,-45.478327297994412],[-75.1948599665796,-45.392102106729318],[-75.381252368510133,-45.314012717008175],[-75.520264069305014,-45.170285648690815],[-75.623009263366313,-44.951749772838717],[-75.631642975297694,-44.715130910522255],[-75.583167428121754,-44.580665291370664],[-75.463551767625077,-44.432949036063654],[-75.18644088619034,-44.306885428652109],[-74.988939392180626,-44.30376919794805],[-74.857160333682401,-44.122333676368626],[-75.068578243673372,-44.053319691872893],[-75.246815847949804,-43.889509882397952],[-75.337388680814271,-43.666409047607026],[-75.327327386449312,-43.473356151776123],[-75.271714804229774,-43.339025213153114],[-75.179808162006637,-43.226372075213114],[-74.851806909632387,-43.048262578457617],[-74.686750819256247,-42.70478524154845],[-74.693814574325856,-42.421745784590158],[-74.643636835920006,-42.08967403954631],[-74.563355342223417,-41.919451590079326],[-74.524102478298786,-41.630042240803604],[-74.376041243295191,-41.433409572899421],[-74.475688288031876,-41.061603482071384],[-74.46325626712246,-40.834022692312061],[-74.267196345472598,-40.332357654511839],[-74.129482009307239,-39.764362200076377],[-74.025692931513419,-39.611136703601197],[-73.817213846514221,-39.457056678748728],[-73.743648204994102,-39.284349363592199],[-74.011013028470472,-38.602871607875002],[-73.983299435198234,-38.151997686513447],[-74.120692299722549,-37.896520697179511],[-74.157616458379451,-37.761389883834369],[-74.142005488925463,-37.200617453382947],[-74.04515210015748,-36.958097607400539],[-73.956442658457561,-36.836553066397045],[-73.78998609676286,-36.725613715688205],[-73.6278236064569,-36.689975349774556],[-73.556240572564036,-36.448123539616454],[-73.349754689278242,-36.208785839841639],[-73.251126876100557,-35.816433622039625],[-73.123154689293926,-35.610782766675698],[-73.070507673926301,-35.361412425065666],[-72.666148476471903,-34.828482820213608],[-72.545297273130146,-34.491199924280494],[-72.47275313367328,-33.995178867530413],[-72.185992443635158,-33.462504939530589],[-72.241187358813221,-33.133952195187653],[-72.219518259356235,-32.944779260714078],[-72.128697620325497,-32.777424665513749],[-72.006873806704249,-32.664926295392853],[-71.951893448886821,-32.542152025863047],[-71.945602406513743,-32.464981169004304],[-72.008461314227787,-32.273925103379923],[-72.026118849638564,-31.854798766624505],[-72.159807344858137,-31.213166190536842],[-72.208721276197295,-30.621144548094563],[-72.140659313073144,-30.163907060070667],[-72.032374351304711,-29.986819660620778],[-71.851513033757996,-29.828289563410333],[-71.82021013121215,-29.640488704127009],[-71.973790143628136,-29.306208009294082],[-72.017313735887925,-28.885255617237672],[-71.959612079979962,-28.690108891873017],[-71.752871024924218,-28.427042328880844],[-71.670879983708218,-28.225852537988221],[-71.564685759449887,-27.669788387150636],[-71.411756958343815,-27.396522093032026],[-71.390405681920726,-27.102864048697672],[-71.154521279754448,-26.264744635532125],[-71.145218464869657,-26.077641561564636],[-71.203487591955607,-25.883553602498026],[-71.210109975762862,-25.726430095619719],[-71.085224526974059,-25.3329834705075],[-70.967532192824493,-25.162543171699454],[-71.073740253431495,-24.655405693072684],[-71.017599765550173,-23.922894009202796],[-70.970143091141779,-23.705681246006399],[-71.073863570139864,-23.486086838271753],[-71.08738089580676,-23.180455860166283],[-71.01309114042202,-22.839596940980879],[-70.928778427658358,-22.716407541655364],[-70.796902181021267,-22.615981026702691],[-70.592832937530787,-21.463648296432993],[-70.696040607049298,-20.751397863160253],[-70.657115708145113,-19.7908099444317],[-70.768709431773317,-19.350505193525777],[-70.842656194495447,-18.609555121732075],[-70.913141226225875,-18.413845160278896],[-70.900839733397575,-18.216571171815119],[-70.813052759971015,-18.039478539142795],[-70.705535029204029,-17.936797529254644],[-70.572577993899387,-17.870270249256667],[-70.33687164457173,-17.832934344294745],[-70.327041359399672,-17.548392998724822],[-70.261710946403127,-17.417539904675937],[-70.161311130069478,-17.311189201976649],[-69.915475423007848,-17.191776852424073],[-69.700755643254709,-17.043859627868848],[-69.555652442527247,-17.008482673591026],[-69.406573902455804,-17.017541185913412],[-69.266821685628841,-17.07022691118388],[-69.148865305319887,-17.161838919674786],[-68.91956230841231,-17.573662511236432],[-68.760842574654305,-17.677947877097708],[-68.664891595912295,-17.794257508552771],[-68.598687506822785,-17.983450789732775],[-68.609895833533429,-18.185607003052144],[-68.488149958164243,-18.738238466353817],[-68.097176007296284,-19.088855578111207],[-67.995196080173116,-19.255330942829424],[-67.969103397454006,-19.497077201814683],[-68.010452101016952,-19.63796963752073],[-68.09375329363472,-19.768728798642456],[-68.066556048907515,-20.042084102654332],[-68.152494719381707,-20.255087171536683],[-68.005623951527625,-20.485012657656441],[-67.994335834134006,-20.727072520611721],[-67.724277404649214,-21.13828798334707],[-67.689436977174708,-21.51357655454229],[-67.630389584968057,-21.708959369614384],[-67.506625496911525,-21.924034515950925],[-67.428886781319278,-22.349501889417422],[-67.113084974333404,-22.3286727644854],[-66.885464753572577,-22.429215312789708],[-66.569172042031113,-22.763780008828153],[-66.517876569030236,-22.908058383445557],[-66.512650952808912,-23.061094875393497],[-66.882484169309805,-24.193070645045459],[-66.976793333559655,-24.359065996226882],[-67.126443915458665,-24.477612942255174],[-67.959241340155245,-24.82835637685594],[-67.896336718077364,-24.983727256540394],[-67.892867234028799,-25.183144323166907],[-67.967621945248524,-25.36805223790271],[-68.074580139881277,-25.489130418011005],[-67.91593052065457,-26.119358668306113],[-67.939473001772413,-26.308896528753746],[-68.006757138722818,-26.442234963215277],[-67.839872764836713,-26.73421406937916],[-67.818868455083347,-26.877595406165504],[-67.838112804725426,-27.110586118935004],[-67.922803372581413,-27.293762672486206],[-68.054986598984371,-27.434209435708038],[-68.518501068711359,-27.634191371761602],[-68.751256047458938,-28.190601892258059],[-69.198296410318505,-28.670447807612792],[-69.359495992120074,-29.27748501301523],[-69.480317736827644,-29.461462228599768],[-69.430510732197973,-29.885134492806504],[-69.350989088804951,-30.095027803167394],[-69.374220472119504,-30.344564868711881],[-69.586086532741319,-30.693837947433568],[-69.778153613657963,-30.827568887445885],[-69.816452952646813,-30.924871137224358],[-69.845560575439407,-31.209368371017359],[-69.917562101252585,-31.333201653613791],[-70.053932181690527,-31.462177075598344],[-69.857271572351451,-31.654266641075566],[-69.787696051144067,-31.778973587601286],[-69.754717985583412,-31.965984479773944],[-69.786402179832464,-32.135622712403162],[-69.697028634225617,-32.309317000202164],[-69.654441484189363,-32.5459350696486],[-69.554103250724452,-32.70895433771981],[-69.522728931366572,-32.882506846229617],[-69.40144962935976,-33.010186694046617],[-69.340131503263009,-33.142958915271898],[-69.298028465413026,-33.398191300533426],[-69.387840303369558,-33.781329156981201],[-69.355634349792368,-34.278612082633465],[-69.391571894011506,-34.417604905824305],[-69.465523270757615,-34.540656544823626],[-69.693659695027989,-34.707272926209107],[-69.760735450770525,-34.809207444851381],[-69.944346888872843,-35.303836311720559],[-69.880642835991708,-35.756270669151753],[-69.913230826637331,-36.15172781357159],[-69.959300211165058,-36.288149189439252],[-70.077118526375273,-36.439074581465505],[-70.292052847211764,-36.595035904833338],[-70.417612042811257,-36.766370017757744],[-70.644546926141786,-36.894484423614877],[-70.619165429653123,-37.137370062086426],[-70.655497246326604,-37.305801583346806],[-70.635135173645082,-37.45410379695587],[-70.65764703847212,-37.652460843208608],[-70.542342468514491,-37.924168767249519],[-70.504501244639215,-38.179936776572497],[-70.372822529120356,-38.392147442010355],[-70.357553867275172,-38.641137069398802],[-70.408113675030719,-38.783097909972156],[-70.598929375451533,-39.092578098922132],[-70.761133482766681,-39.200515741522487],[-70.914345040653899,-39.240539857451651],[-70.954790745816339,-39.470298157710275],[-71.158562614178791,-39.925396221660115],[-71.210719691160932,-40.457585977959987],[-71.268499157500742,-40.598024393864456],[-71.391860243489887,-40.758367388500503],[-71.385487569838773,-41.582083777452823],[-71.263978963750034,-41.93746612130132],[-71.261005093620511,-42.151290015657096],[-71.347235445813013,-42.381839426632915],[-71.562084995982488,-42.561499773434129],[-71.60779993037265,-42.698685743798855],[-71.406487674324239,-42.83647749342461],[-71.274519340121927,-43.08530949091891],[-71.250852791608423,-43.238845516131967],[-71.28518849971087,-43.422112115066085],[-71.181052237579266,-43.94192231075327],[-70.96942635769939,-44.004586641775497],[-70.757453856932443,-44.185896600339248],[-70.658226238604257,-44.410234389184815],[-70.663210597865898,-44.619622463326827],[-70.710562960042907,-44.776408025790339],[-70.877053872063243,-45.081966526133897],[-70.850035931021026,-45.309822966978693],[-70.877923448531462,-45.497854677659809],[-70.9441785070061,-45.624520991881454],[-71.142586880839616,-45.852691682806892],[-71.150166786058278,-46.087858231281402],[-71.257673645440335,-46.296514998237143],[-71.201525299729269,-46.600087093964227],[-71.209300719982693,-46.745719253217473],[-71.283893072443973,-46.92471095604764],[-71.421771801430111,-47.076083746474488],[-71.407499674579412,-47.249538805789271],[-71.442845899333548,-47.391850679468682],[-71.587058117390058,-47.58721429463057],[-71.771504426001485,-47.683456137944361],[-71.918895986058729,-47.824456268303635],[-71.795107515838524,-48.186470096157898],[-71.837211113050785,-48.434006481646684],[-71.946815651021282,-48.653278005529408],[-72.095940883615981,-48.790315024115237],[-72.13014697431565,-48.921369075737296],[-72.262718757966269,-49.155874568143091],[-72.407789885114497,-49.27802320502942],[-72.643002153024113,-49.383751444287391],[-72.763647807783542,-49.634717541979654],[-72.971378641402765,-49.772540234349613],[-72.99955856650908,-49.960386539728972],[-72.893078945741379,-50.146931450437677],[-72.407417912947523,-50.118402252879157],[-72.095870201207731,-50.245859100945481],[-71.947386410365581,-50.372909503465678],[-71.874504180931851,-50.500547303681635],[-71.7780329524645,-50.871384739663533],[-71.82889280067036,-51.383838380317904],[-71.710058319306697,-51.467964607082884],[-69.889071032561361,-51.513477052097521],[-69.428066291782869,-51.633773241466123],[-69.152444477897276,-51.639206446099841],[-68.359751697056538,-51.801034982823488],[-68.136341193591619,-51.910529120526974],[-67.99336038558738,-52.114140779801026],[-67.94412625742909,-52.377786285362092],[-67.992756780194284,-52.572440040934367],[-68.151910788651918,-52.762311577496135],[-68.134651911068303,-54.430883285317165],[-67.167343741858417,-54.484010168959976],[-67.024693887780472,-54.529208379055675],[-66.748877617470967,-54.71625638428295],[-66.48228135491334,-54.671475522339868],[-66.197388882158776,-54.750577479173742],[-66.021120484105353,-54.911094013966213],[-65.946795182154688,-55.087461493471196],[-65.944176529971855,-55.278832549270504],[-66.041304271007149,-55.496553260309859],[-66.145031303634696,-55.596337450527614],[-66.386772575408358,-55.744635572672159],[-66.573828785299824,-55.772152572819699],[-66.763342587673719,-55.725707071048404],[-66.795555135389293,-55.922064329393883],[-66.89677864360408,-56.084545311698925],[-67.009031799895425,-56.174683044192534],[-67.364202478604312,-56.342620320996438],[-67.570224935228779,-56.38932499740428],[-67.92898552983938,-56.354986551009226],[-68.112146258397317,-56.278377689167392],[-68.262859648112254,-56.116149910616855],[-68.497935812294656,-55.994188370433129],[-68.986752016415323,-55.93484596993305],[-69.200787898908359,-55.97417745985549],[-69.523665164654503,-55.931187723616276],[-70.163925855067532,-55.598246968201273],[-70.405654573016236,-55.671902002804671],[-70.548946637362306,-55.671403125608506],[-71.110997746420736,-55.531442923965109],[-71.304962519971454,-55.422500037857787],[-71.451256203221433,-55.42861112076659],[-71.595248162614681,-55.393631729834325],[-71.759457516404098,-55.284789131979771],[-71.872799568552409,-55.109903936864413],[-72.109658371240045,-55.055928891363578],[-72.267557316461634,-54.941833756398317],[-72.36987038491047,-54.776057362573951],[-72.407126484386339,-54.525646955831164],[-72.799151787698605,-54.621169755966996],[-73.048024385914459,-54.593955490186367],[-73.241199433649754,-54.483740687587563],[-73.57486662905994,-54.363807653078496],[-73.685606693145033,-54.264642962501647],[-73.78023571281561,-54.109092233156829],[-74.158150994455696,-53.935350823689205],[-74.282042761802359,-53.788337321912813],[-74.34346593916176,-53.575236061680421],[-74.466766110640933,-53.541020190518445],[-74.848143636154859,-53.329001721595091],[-75.11625705735139,-53.042827213208732],[-75.19802149838722,-52.865383360477637],[-75.197382176343879,-52.631346717153406],[-75.448538375721824,-52.29021879546513],[-75.543238300085989,-52.055292643822746],[-75.685734543895563,-51.929359634616375],[-75.772551734396473,-51.752028056898268],[-75.788819965481068,-51.452310566222273],[-75.67190676310895,-51.190194624039627],[-75.840194887664921,-51.021064766876293],[-75.972896728032936,-50.719066867150097],[-75.947004129524487,-50.300344792466007],[-75.908078734326111,-50.135409971353994],[-76.008694973918765,-49.987425278866425],[-76.066539157433695,-49.754658325519856],[-76.031571936545092,-49.50658215109771],[-76.137196727535212,-49.255473515205438],[-76.104969251704532,-49.009124433083628],[-76.149382044820371,-48.550860282389394],[-76.038754143794804,-48.238347126335782],[-76.060427485300906,-48.061182874619227],[-76.019423535761319,-47.872560285784431],[-75.910025792578764,-47.713525891129862],[-75.733492338711855,-47.601676055585422],[-75.605846966838101,-47.40275066437875],[-75.89082501564387,-47.292367137459337],[-76.088793982922283,-47.098758793666768]]],[[[-78.947716171736062,-34.159958324412045],[-79.186775179261105,-34.120962428218846],[-79.347053967645778,-34.010821615833841],[-79.453187943339429,-33.847861903750307],[-79.486246057955228,-33.607909720895442],[-79.40264482411466,-33.38057577556588],[-79.197437285684813,-33.187059445042109],[-79.020167375831491,-33.094983307348315],[-78.821000328518835,-33.079649575357422],[-78.562653811584397,-33.172070679739427],[-78.401951110509131,-33.288135049049508],[-78.284398596551739,-33.505214678156136],[-78.275023778127633,-33.703225811014235],[-78.343332476785378,-33.889317771194996],[-78.478587577169634,-34.03423965972906],[-78.653278779176787,-34.12351559854929],[-78.947716171736062,-34.159958324412045]]],[[[-109.33563850137766,-27.661263276323162],[-109.5316682178173,-27.661353872059514],[-109.71266683825712,-27.586073347792787],[-109.85081295040526,-27.446993120465727],[-109.93179051552725,-27.216672233814968],[-109.91107854604654,-27.02173975254204],[-109.77485062317484,-26.748983410090748],[-109.62485999910191,-26.627034508283366],[-109.43982074293518,-26.571095294496676],[-109.12766135991262,-26.610488885259926],[-108.94784623873976,-26.684041916331257],[-108.80963961583224,-26.820579294241828],[-108.72631415227592,-27.04775443906399],[-108.74345959405257,-27.24127332705293],[-108.86542822640848,-27.45025976685962],[-109.08529044145142,-27.600685918833555],[-109.33563850137766,-27.661263276323162]]]],"type":"MultiPolygon"},"properties":{"alpha2":"CL"},"type":"Feature"}, +{"geometry":{"coordinates":[[[8.0899570653521717,4.3741243312495541],[8.0331622306288128,4.6179303089787664],[8.0980994247142242,4.9450551716543982],[8.3371381779788152,5.4023155353385128],[8.5152225598933864,6.0501563835468675],[8.6007906807359102,6.2221758643830718],[9.0333449653981592,6.6859235821226566],[9.2689690462197962,6.868498748033689],[9.4310836012263266,7.1180588783244367],[9.9554632999911981,7.4593896086987579],[10.141321201242203,7.4960857810742194],[10.311828659219033,7.4663537961631947],[10.468508397328975,7.5434883235050556],[10.657959197187598,7.5601372972950776],[10.881078267203517,7.480489504086961],[11.207130729916399,7.2209802366951603],[11.268157914220064,7.2824292361329972],[11.318971148108503,7.4929392200336871],[11.5280347595957,7.7869834527929056],[11.573951040798365,7.9424510364745533],[11.68761559141117,8.1394747739977849],[11.774442602585093,8.4801415937776046],[11.961633862873045,8.8294236509660724],[12.118165621573738,9.0032169950936645],[12.339112045887465,9.0896558224378374],[12.472278287341624,9.6281234618262932],[12.57952890379301,9.7830724230999646],[12.742927265706104,9.9023302290846669],[12.802289510695401,10.212531674454425],[12.968161734119048,10.442915552225083],[13.06960938892173,10.786278785346635],[13.271650146040848,11.131246212888522],[13.664683007011059,11.598754503053032],[13.79602376486373,11.674823406210553],[13.988593060331446,11.731004521537532],[14.0814191053061,11.79825748799777],[14.090181633778499,11.895410211743265],[13.867814054584171,12.008234577196959],[13.717744495900446,12.243775661468044],[13.566247426160366,13.033618592938037],[13.588425956494378,13.231882721401627],[13.655435716309816,13.366158094349691],[13.844448269324577,13.52737967062073],[14.0375327344815,13.577568724788629],[14.587921680070998,13.503516282027624],[14.766236227139304,13.418302256660375],[14.870446605213751,13.312295288340373],[14.967735796662819,13.123159004323085],[15.168211829725831,12.945684473845962],[15.30050921870536,12.711914774011996],[15.365954407878677,12.419115205255126],[15.57662074188331,11.911751054561872],[15.619446698747344,11.493601027173842],[15.542409855837784,11.115911828596627],[15.609964246782985,10.811290157104883],[15.692278608984745,10.651410690748685],[16.058889519383708,10.301790649927133],[16.150367311218346,10.071319791717944],[16.146911404642296,9.9215800168319959],[16.074186302258955,9.7362152403021316],[15.974897179872789,9.6240741967500867],[15.846849006062298,9.5463721462175855],[15.603781682873407,9.4644846041708828],[15.161750261235445,9.479966600289135],[14.829165593163074,9.4424927524145783],[15.524464588872654,8.8456790581067963],[15.852126645305475,8.1642510023995314],[15.989333615634427,7.994708513853527],[16.044247692834574,7.8554825468909888],[16.049252499532006,7.6565771712576112],[15.979917489607725,7.3817479846121428],[15.775074016165352,7.0532826783190661],[15.630155559273263,6.9140909029311519],[15.478300526714568,6.5546038185122981],[15.300100758832492,6.3055624096486564],[15.204027927167516,6.0798489408168201],[15.107692200284701,5.9524861541865359],[15.082986749570194,5.4091234066833103],[15.136469362276365,5.2450935027922032],[15.189639585872655,4.8731416611127241],[15.497945339155473,4.5325705378810426],[15.621987269453841,4.1892846672307034],[15.635738050711737,4.0191567890682736],[16.035174097179226,3.5818724991505619],[16.259253117732886,3.4491377573490309],[16.5237940834174,3.1031063229447104],[16.598429956573074,2.5878866604686825],[16.682598422688212,2.2939416909816321],[16.666331058786227,2.1413559675039275],[16.596298463639478,1.97859078673519],[16.632836701371321,1.6689616595891177],[16.545330714326031,1.4373259019403748],[16.442123676646759,1.3290742621883767],[16.282099649494786,1.2288095957077887],[16.046568754270648,1.1766503011092118],[15.814029193877875,1.2408433495680673],[15.53529849210007,1.4329409698969997],[15.262577272215923,1.4508566131907386],[15.081779859011233,1.5134302682498559],[14.81637534515156,1.5200383415883509],[14.463884736826644,1.666534090538984],[13.300711139258196,1.6615340886239156],[13.143295723216584,1.6862480527037831],[12.990897772601942,1.762226228956618],[11.665299085122285,1.7988463716301428],[11.524820847447298,1.7078102148195946],[11.378338583465554,1.6701604773425709],[9.9746139093386326,1.6696551298972846],[9.7913128312097708,1.704754602300554],[9.4574556929080202,1.9412728742262089],[9.3661553746348751,2.0577893326547585],[9.3129711910540411,2.1959315630491965],[9.308870857287701,2.3926923580255686],[9.4277877171117819,3.0565107470314632],[9.267234566425099,3.2445644733854815],[9.1446051375285293,3.4804405859052667],[8.6704783030780224,3.715987796869054],[8.5736213438614701,3.8311226586117533],[8.4894055347014632,4.0342983602601654],[8.2496649946368734,4.1463817387600734],[8.0899570653521717,4.3741243312495541]]],"type":"Polygon"},"properties":{"alpha2":"CM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[73.185410339963639,38.961369969859582],[73.109307157238945,39.187920158169071],[73.155681825946047,39.601045331845178],[73.221703564632634,39.734575971636772],[73.338438687122846,39.852798048512511],[73.391841664685515,40.030521837724351],[73.571182747716477,40.313376469720936],[73.743257370016508,40.476833925405145],[74.2257560059859,40.61220486629535],[74.385863828463485,40.711961926624781],[74.525949152596809,40.87515869733577],[74.653392022619386,40.948088372182326],[74.796524518850404,40.980852851660728],[75.102159122261355,40.976973757983579],[75.451921644543873,41.121802693843804],[75.604740062409604,41.120893062428841],[75.804320483019566,41.053668875936189],[76.000168122288429,40.880328998666684],[76.178940665567481,40.92378468573672],[76.241024031005125,41.050256304872484],[76.469799927181313,41.33485511770072],[76.684029416785933,41.471109045496867],[76.879150522790994,41.523148283072139],[77.544308106514464,41.496197688568259],[77.864647252735196,41.545692176839012],[78.209176140738549,41.858895851629548],[79.135257693441204,42.256885309291818],[79.479264465067743,42.342484638455936],[79.692303680249736,42.47251627449652],[79.665359652855102,42.672995761601854],[79.706190982452156,42.863481067030158],[79.889756207994637,43.114104749754517],[79.974295762594636,43.320131612427936],[80.133324480313973,43.471040437941276],[79.878454536226371,43.94773999248774],[79.848807631666119,44.320239120593371],[79.549382330355513,44.50179932917812],[79.407723280192826,44.698389641163956],[79.373965311174274,44.840763822185785],[79.382869256751746,44.986814250624214],[79.433672582744848,45.124033163176136],[79.52202449071136,45.240669127837855],[79.929854546071326,45.489222099924177],[80.77957089849707,45.625472282068785],[81.613819505662946,45.842930131553054],[81.86706219354302,45.81717695059826],[82.506080286597438,47.145440132243458],[82.585935197900184,47.409345722091558],[82.709442841020049,47.567955184352044],[82.837967321221441,47.647637092480423],[82.984435663775102,47.685250562455899],[83.232743174654587,47.685557083881704],[84.05961308948352,47.47366111097049],[84.718072576448165,47.469431341697337],[84.876990431829398,47.42506062181829],[85.093746405795855,47.515774066120542],[85.02831417101217,47.869683877511228],[85.036187578526096,48.015058347942031],[85.297665208823233,48.61848282319648],[85.651097437927774,48.875457084578805],[86.247110084106495,48.98763330949896],[86.279105293642601,49.167156986590761],[86.379441844745841,49.340664956126396],[86.632361818098616,49.519497920809229],[86.806792798614808,49.584138990589352],[87.36835300534743,49.582009327023869],[87.811452781778442,49.662079486956038],[88.001247229109836,49.625730383595027],[88.126675786768615,49.552316366513132],[88.225693229425659,49.44593268811218],[88.333297800550483,49.127850285054564],[88.505753272364842,48.931952760863808],[88.792900403593848,48.80124103793429],[88.987782765199071,48.58488115097164],[89.198322765436572,48.493903745124761],[89.635430077166745,48.499084362281813],[89.772300324409827,48.456307904190744],[89.911023682679541,48.364735464580711],[90.194320928930097,48.348924976930888],[90.328431864618054,48.277001287323579],[90.43999904498348,48.160640602226678],[90.642225299164139,48.052417139547622],[90.738641014165381,47.938577012652694],[90.93975098751541,47.531254972032571],[91.01463615278513,47.438142362860589],[91.160201286637374,47.361332624918333],[91.289106876408127,47.22665302252431],[91.508475032782854,46.706756048205968],[91.524687081828063,46.502761624427151],[91.454624762134841,46.267739815940331],[91.499709544207064,46.078474166176086],[91.490123796549838,45.929682610667541],[91.437090559975616,45.790333088360271],[91.32615728215373,45.654664954780976],[91.663915413155692,45.573104637524516],[92.437710126341472,45.510552003607131],[92.806818557479659,45.534852279141397],[93.614473106396517,45.434524227450716],[93.886267098338109,45.344728237748441],[94.13265531447091,45.164374444651706],[94.3940511353169,45.105377494126316],[94.922179817603805,44.811463350375277],[95.093483888978795,44.765922161460075],[95.373536995242219,44.777307998812034],[95.557902806150082,44.732674402685745],[95.677881989395061,44.655516222722966],[95.771190634705718,44.54761865158445],[95.84985816847346,44.304821767031896],[96.012477029118756,44.123002212928654],[96.286990403164225,43.563247420228372],[96.669509237063238,43.263274594824431],[97.258742682716871,43.28695992493828],[99.432065409049045,43.075026774248698],[100.03408466590908,43.174953829210089],[101.5410526010799,43.036458691228397],[101.87644107592153,42.95011774334823],[102.28251440013557,42.644567509349848],[103.20560864720331,42.487734893347096],[103.75990442246622,42.274122563820008],[104.572176330817,42.371216379503281],[104.75549426172289,42.305358267758223],[104.92408333683261,42.1379875714238],[105.70952006534264,42.46817078113056],[106.66378133237937,42.777120921060209],[108.114964756308,42.943971556667641],[109.27967955558439,42.941773919963154],[110.11959483827728,43.214155292613299],[110.71526626687145,43.747000602175291],[111.11723619309988,43.897105859814218],[110.92635814817852,44.214769561895636],[110.90364064367246,44.400900206379497],[110.93284715693582,44.538842068539502],[111.2245432367919,45.131154903863738],[111.57312891792859,45.443718815309929],[111.70365054275456,45.524390164686707],[111.9037750547371,45.563740730671697],[112.46818064781864,45.554753423090638],[112.61630094602523,45.514032753182939],[112.88204380779911,45.363043276129943],[113.53572241715642,45.270987091357412],[113.83176646713702,45.408308452293589],[114.06958539246912,45.570649277387467],[114.23203107281667,45.76701730263202],[114.40913128267199,45.866433698274122],[115.12028098859471,45.889936509002709],[115.52352618464694,45.94890720648862],[115.77997595362433,46.093225415062427],[115.9762867865139,46.419107185344004],[116.26014226628668,46.687647792980584],[116.40412211457203,46.763788035644559],[116.7863984461186,46.882562931558653],[117.03162057739506,46.879053139269558],[117.16173561262897,47.002603969999164],[117.34058360851756,47.07634494727894],[117.69315647621568,47.045583996484858],[117.9262235877778,47.143845846157397],[118.204215545959,47.205758889569935],[118.60758443604041,47.205871986026096],[118.72414748265258,47.245099988783771],[118.51777135965457,47.320462930211733],[118.29306321688325,47.499450199623276],[118.12023281063205,47.522404976946348],[117.98807617122439,47.508232298275253],[117.61343494221668,47.227059793987202],[117.43595486936229,47.159798391073878],[117.20005635280941,47.175721222733934],[116.89567564399616,47.330500882076365],[116.71622039339222,47.367649224350714],[116.35679187362213,47.357626783021608],[115.98603900582997,47.194929449854058],[115.74884695062276,47.210019324918562],[115.29621861761052,47.491962155496772],[115.14235166978973,47.667031202028831],[115.06536994671615,47.858869548885089],[115.0267364215523,48.093699265921778],[115.0378277164003,48.241667178805727],[115.14952481263452,48.460488265135773],[115.31376255681066,48.587672472938941],[115.40613128766118,48.856667139261653],[115.61457905332296,49.075047336047682],[116.2615316779777,50.091891437518449],[116.43338532109125,50.256503897278172],[116.61403236581924,50.318658669932709],[116.75769842870527,50.317939027915209],[117.35360404219485,50.115980322020356],[117.76318856417167,50.040232547992261],[118.27419470213881,50.311983690774994],[118.66707306847923,50.454763696688033],[118.76549746499889,50.707871014708161],[119.03035266465741,50.996322162850426],[119.12455861888613,51.17919542744707],[119.32372357078265,51.383482274693193],[119.67807751266304,51.914560867665067],[119.7811711601184,52.010644078262224],[119.99431640399092,52.133696266529512],[119.74865081038874,52.2482092116975],[119.60130193237426,52.453136363236375],[119.54468809446411,52.727569622361045],[119.56749336606889,52.86772924764589],[119.62878164912533,52.995825099776042],[119.76141708352814,53.130241736002908],[120.42109740684613,53.58357897366249],[120.91208226515289,53.778898414152764],[122.30668679089004,53.98364245086826],[122.5253405215319,53.96224213187238],[123.08979324194775,54.0401710641245],[123.66060547420102,54.043513921525914],[124.81401362971772,53.669072057781612],[125.15820269807521,53.696424546225082],[125.83130274750079,53.507650307924962],[126.38343845334566,53.110049110430339],[126.53233593097853,52.87265212200802],[126.75772742808932,52.638924601059585],[126.93630031650686,52.212451780247207],[127.06412407174112,52.066141462735565],[127.26492031588832,51.702191892468235],[127.37697758944208,51.356198821401705],[127.7259653456353,50.980437287326311],[127.82872174240646,50.64853045967439],[127.95583114859458,50.549626561889994],[128.03748617713546,50.431776224436227],[128.088381099044,50.248042218393216],[128.07072238566872,50.073512246871253],[128.78433933402542,50.093424658858297],[128.9291612740025,50.046343856387473],[129.18576464192634,49.883903970529296],[129.6260187789477,49.871928910135026],[129.79030377483025,49.794257672827371],[130.35488792456971,49.387455946193036],[130.66178406437899,49.348953692364574],[130.80519252476418,49.292668401642096],[130.92465825084167,49.195395791136676],[131.07030699176966,48.984007830736999],[131.11603209209005,48.742796935206968],[131.27865197619138,48.498697338450981],[131.30398129394825,48.348285121404771],[131.28790002605368,48.210019522211184],[131.77097452837259,48.181649229955454],[132.26175168595771,48.217100122019403],[132.48801346432876,48.396363701046219],[132.84986676645406,48.53434447838481],[133.35277011830848,48.594608934138734],[133.72702442604054,48.759280771825281],[134.2851309849724,48.873176556825662],[134.69950609886942,48.803010378174861],[134.97889328208402,48.642922353011386],[135.10447115548203,48.492197993202865],[135.16239285398547,48.304760772327889],[135.13555700540496,48.055164750162675],[135.24915319413992,47.769079688250272],[135.24297973790621,47.620702409927823],[135.16802935671907,47.43804489152501],[134.94674138547245,47.168329643397136],[134.68151682291708,46.988875051500507],[134.53930094619056,46.742606467299453],[134.48432965577712,46.52179316662459],[134.37958229167833,46.346100722024971],[134.2946014994366,45.998677833999977],[133.94170332042569,45.550142742536785],[133.82303917162801,45.288063367588229],[133.61285949652824,45.132556259654194],[133.5596207705116,44.905590552908173],[133.4399723939994,44.75236294158637],[133.13606407340112,44.571943200062741],[132.93203368695015,44.530192975574643],[131.91495830090869,44.743997187869503],[131.76040058433699,44.594798642124886],[131.6341573168113,44.520939516738778],[131.75237590154254,44.123029743278046],[131.68953784125773,43.719497595842775],[131.75263440059996,43.444212285570515],[131.73815481383411,43.242115425841682],[131.46191595558423,42.59404602579216],[131.27571600366628,42.447469226563754],[131.04905526643569,42.383511942218448],[130.97682549853971,42.257867016831149],[130.87018681379885,42.157324278442779],[130.6868413241516,42.062622444006266],[130.49499909239026,42.039133713485931],[130.35309914073156,42.070648070594885],[130.13046082445791,42.198774877950001],[129.95770199818679,42.021308184953497],[129.56985904409453,41.887628107052485],[129.16067657908516,41.598378080344503],[128.98946958521324,41.542856904414712],[128.78918852654857,41.532365558738768],[128.73150109185113,41.32693478456347],[128.53822215231878,41.073704748976475],[128.43458870120284,40.977328793914971],[128.26257577432105,40.900965971951145],[128.07451861208725,40.893601779850101],[127.88430073250544,40.951310768271441],[127.59331083768022,40.948660727290068],[127.04410437725271,41.050304565890841],[126.92055809983799,41.104370517036948],[126.82565153060794,40.987385084359374],[126.24925343963838,40.477993349633863],[125.92927773772335,40.386985591917274],[125.64027633510551,40.212917308613491],[125.0922680346381,39.992502981530158],[124.78577147916273,39.779240715946152],[124.58088757370953,39.536099311577054],[124.33363607366243,39.396269756056178],[124.16496183010496,39.344812223766105],[123.73886577514359,39.329981389839837],[122.99502291546352,39.120544087258715],[122.62715832625312,38.949524091790295],[122.32342018006688,38.677347032713278],[122.04077298979269,38.550334910922167],[121.83291447786439,38.400030679023082],[121.25436456032745,38.24037906106242],[121.06727063080815,38.243319404700401],[121.74998823911115,37.959749141965261],[121.96964876283509,38.021049783768255],[122.11644799108913,38.025102317861574],[122.43763457801813,37.907320059393008],[122.72517190517868,37.899303681762355],[122.8664503663703,37.861059877943816],[123.05851095016862,37.713206061995919],[123.13161866480259,37.586409160660843],[123.1648770328695,37.443874697878307],[123.14283459481062,37.250522283278379],[123.05945006960393,37.091693097780549],[122.97976866896472,36.751591757713804],[122.82997984051914,36.555027363753666],[122.46775902301422,36.348810048644175],[122.16608027178702,36.354295693787378],[121.96911827566406,36.431799273669249],[121.31744122456098,36.175910453388447],[121.12359600964695,35.999301742528182],[121.05916565547882,35.860965840528237],[120.96411823446907,35.751294495722505],[120.84157904156619,35.673538709347362],[120.55165755532791,35.574111255351184],[120.39509347287076,35.472522809890322],[120.19340992159799,35.265897254878411],[119.99564656753697,35.152099280282883],[119.86469500535557,35.015703341362517],[120.2181780267053,34.87853954524283],[120.55652300873261,34.681139313069345],[120.66082196500678,34.58126894123123],[120.97250987637265,33.840562650017546],[121.32794042684328,33.219068594112578],[121.37850517060912,32.96410082860374],[121.6106052532478,32.845168494801726],[121.73575061650669,32.742959309910603],[121.83071067851485,32.626969054235673],[121.88199753508503,32.505204736370011],[122.21468987674194,32.221583619908657],[122.31232220683182,32.03903464652528],[122.36221880273199,31.480060361405272],[122.289651027014,31.233643477894063],[122.37765074505211,30.915065520214263],[122.34046211661335,30.727886250039965],[122.25460563670828,30.589475515226749],[122.52203145237984,30.507392527140031],[122.75264315153017,30.30590188766719],[122.86033803014334,30.148415088665743],[122.9000474129728,29.914095350881347],[122.87607149459662,29.714201605237296],[122.81731424142568,29.58050323464866],[122.45369076780709,29.249977642028259],[122.35662612911032,28.895964664963909],[122.27090019983397,28.781424525132209],[122.12528844005705,28.680904670599997],[122.0657395138039,28.525246444367919],[122.10939888298093,28.275249085373726],[122.04444529208057,28.045498559529719],[121.91958209831029,27.90007477450186],[121.79476219343491,27.827975900831852],[121.65632601033897,27.794727411589655],[121.4890122886393,27.64682295259205],[121.20065804971587,27.580166932694503],[121.1464749056334,27.51009713821837],[121.01363154069173,27.120788956387223],[120.59879401036507,26.669489302278162],[120.52262998754473,26.427159465751878],[120.37512810827886,26.262194340571231],[120.31702732457705,26.090007634778097],[120.19354321637682,25.944352054061081],[120.29824524973498,25.785454574923307],[120.33568043267333,25.591510102186952],[120.29750842876416,25.306471779128401],[120.16827967103241,25.097653027564437],[120.04753832478143,25.011517993128706],[119.62809476879127,24.869946297403096],[119.5010693134471,24.780877135405344],[119.37208660703122,24.737199839595398],[119.27254312072526,24.585945093957523],[119.14408048429048,24.48753987209405],[119.00490481973745,24.262664181152598],[118.84384025206606,24.157912575727998],[118.53011440323586,24.088101438197697],[118.45647803142269,23.947091684022794],[118.20609928351286,23.672726650280257],[117.85996728660528,23.394633389990766],[117.72749328837766,23.229713394025417],[117.50031520712822,23.10680987896194],[117.20103967066112,23.101348422834207],[116.99093660946919,22.880800096048272],[116.8733433939623,22.649813547942962],[116.72356823907349,22.514831322121523],[116.58261270833042,22.458823322610371],[116.3553494877771,22.443338622252],[115.96748601110259,22.315291789088199],[115.78033195224907,22.30694768182823],[115.58690616261552,22.227023343895439],[115.23273260116584,22.263904277244194],[115.03116437825518,22.149564382523266],[114.83661944043686,22.11730406709454],[114.63986722092986,22.036546600231539],[114.34810125820339,22.061615587114176],[114.00085792642247,22.012686964510596],[113.81811462103008,21.804007324340059],[113.34795434755401,21.61096196538217],[113.14393442835615,21.249032694986536],[113.02945483590922,21.154097859453575],[112.89211566304434,21.097035159891853],[112.74406769098093,21.08289309312643],[112.40898692662304,21.136929141686483],[112.11203341692583,21.283907641002394],[111.77601011581243,21.09028197255255],[111.03101486325113,20.933323046205565],[110.95646229120742,20.744626997612222],[111.00968177324097,20.487311453572694],[111.27306784742258,20.318641544797327],[111.38893163859845,20.163660046440551],[111.50745000089623,19.732511999748269],[111.50853670602075,19.58599698242287],[111.4671056266276,19.44545767514477],[111.35236645913528,19.288021421377973],[111.15734797552143,19.157527443631874],[111.09838566444151,19.073270846463462],[110.8994357592055,18.530451963952629],[110.78853579448159,18.378693057443449],[110.53156510724017,18.226755054866658],[110.27463736806877,17.986157721544448],[110.07014023697198,17.920451372596411],[109.88270943330622,17.792894060430957],[109.66921127469735,17.732988878561514],[109.51155755011105,17.730988823869613],[108.90565154099984,17.88359921852993],[108.4306615237149,18.115277303044341],[108.31750635246115,18.215488752301756],[108.23946750046342,18.344935130862986],[108.14668859588966,18.776907504776087],[108.15397599399599,19.324094475752148],[108.19463991233287,19.466415803741231],[108.29279219733456,19.635204658280124],[108.74741857716064,19.999997589613614],[108.96584077488554,20.284051233349423],[109.13304386103827,20.365053094583455],[109.35707193374645,20.398477246773087],[109.32109210908185,20.536480079043983],[109.18844007913827,20.758988751228102],[109.16354051983227,20.927127457945044],[108.87908110071714,20.98340397652456],[108.64697234809992,21.167878258700149],[107.99751975588883,21.008820399452084],[107.79505960799463,21.040832175772834],[107.64298115752408,21.125636170954564],[107.33241201211591,21.109506776624741],[107.14440194313538,21.153953402085843],[106.68391101700396,21.471797256182295],[106.45427103401914,21.525170352404601],[106.29544315386475,21.641020727118384],[106.19355307189896,21.809145728330769],[106.15919238941821,22.057573025139817],[106.05737747559745,22.252544182140536],[106.03765753682477,22.420455064587909],[105.99921403626368,22.442976617437729],[105.83875232772156,22.423063358033883],[105.7022014725957,22.443292085728427],[105.23856653146255,22.680492560120079],[105.14750276342153,22.556131722247045],[104.92503324901918,22.382595368540127],[104.70305943729392,22.321008770143997],[104.5022114863247,22.22162121547354],[104.31483564571967,22.207633491091617],[104.19667424585523,22.110316786902736],[104.06213183729987,22.055058604120386],[103.86897033728829,22.045562917010606],[103.66863153190475,22.120754952426608],[103.50335952570688,22.088343032909123],[103.36617948727677,22.104991944509656],[103.1329490880068,21.971814467148903],[102.94224590986688,21.950048640274311],[102.75739413417078,22.001733013098185],[102.57785240901428,22.129006443009391],[102.41886991430201,21.973199201046153],[102.23087810475323,21.890728144142372],[102.23303596729893,21.484567476835803],[102.30173038569917,21.233810204816436],[102.24414044368001,20.998015616870251],[102.15757048156465,20.879737222830599],[101.9852403813497,20.736423593979882],[101.75649126243974,20.653058147706549],[101.6096056307064,20.659528276857017],[101.44208993172957,20.717637830152789],[101.21377036163587,20.698671399114346],[101.07413580097281,20.728694922136203],[100.91184163842115,20.827350493815615],[100.78601835830567,21.007588650092686],[100.58738327996802,20.970782995456606],[99.987938360661872,21.007007287828284],[99.778677212176035,21.143494387933643],[99.474667699652997,21.579688115696428],[99.073305833542051,21.640731188344862],[98.861171099220044,21.752248865726155],[98.744549531733455,21.905282932172192],[98.674774703840512,22.168956521584683],[98.7060434824281,22.373062538552126],[98.852025377340439,22.679284454886258],[98.611407150368791,22.759923192618299],[98.441179007377571,22.924579313586573],[98.382950531726266,23.055154981733828],[98.368811603547158,23.262817940435436],[98.298171109034598,23.519753659816192],[98.24271720412932,23.602521117181585],[97.776555210453864,23.406547713185667],[97.463271290219225,23.421606800919957],[97.325410602870718,23.472218439577084],[97.208200643603035,23.560699694836206],[97.121752931387405,23.679417427869002],[97.073525174489845,23.818130046431232],[97.075341769689672,24.013379765229399],[97.137589443683311,24.184229759391119],[97.041780458662799,24.391721909878953],[97.031163081564827,24.670520128622016],[97.11547134015342,24.950492884573926],[97.247461181633582,25.145379572928611],[97.31500210548387,25.370084174991192],[97.436453446660281,25.57274852973973],[97.663051403195126,25.726228970728922],[97.741871929676805,25.869278125863492],[97.843187884957544,25.970994065891581],[98.071916030554732,26.07754208060684],[98.099804549491381,26.276270542404514],[98.192220448962814,26.438370352766444],[98.2348296654351,26.705063628910992],[98.198727783837768,27.060961125739212],[98.035660455159544,27.125258794592281],[97.895904919011329,27.254452757263277],[97.659727308861832,27.849334798015281],[97.489676273783971,27.747052329212838],[97.290563262110354,27.719263213517742],[97.142829371917827,27.751612922938897],[96.966236015319851,27.852631879784266],[96.584672880985849,27.905395923861462],[96.394107681601753,27.868159641374969],[96.250123689786662,27.887798726756259],[96.012448991327673,27.990499952853469],[95.871150211356607,28.129721100964801],[95.795106140587848,28.312929104620931],[95.806110839160766,28.548895183201626],[95.713996034305424,28.657674417094011],[95.556352608057097,28.566374865147914],[95.372721321489564,28.537879247202472],[94.570037868306599,28.717669482013029],[93.97914319219359,28.280156078338862],[93.562782325134037,28.172259634968032],[93.438074521528236,28.016972952357499],[93.148140223488227,27.819130274349813],[93.074510832675372,27.666737865026839],[92.970062585244477,27.554093828818718],[92.59606988433282,27.358361218368948],[92.265128278978821,27.316124969717308],[92.027944490163051,27.233120304498758],[91.539856855351459,27.268795541961886],[91.355587837455957,27.343659261206334],[91.204517418596637,27.502299075699739],[90.959499709536516,27.474071741935234],[90.628895931806269,27.573219575361655],[90.239059349333431,27.593618696676195],[90.097809986470111,27.650428868056668],[89.969877275143077,27.759912909944312],[89.800656104553283,27.665213370210644],[89.380446286541869,27.185090635912506],[89.298130440295424,27.025764796005163],[89.195243322724593,26.919424816402337],[89.018566398865786,26.832906261027368],[88.822189031572719,26.821238979277545],[88.636510591143136,26.886229075507821],[88.396773824684374,27.091861814834392],[88.31632320940237,27.209533964010593],[88.263643116879663,27.39564731558816],[87.925791244366067,27.381380227011718],[87.604032483168041,27.316255049695421],[87.015845020219018,27.354647839378291],[86.731871002431248,27.504708371770633],[86.486576265191459,27.438884284803972],[86.248361920928104,27.475547659606978],[85.999763194455099,27.410660326242848],[85.806407998522573,27.447376992463742],[85.642201554927126,27.555869338987446],[85.456752576898239,27.776815936360901],[85.018485179346911,27.827127693007544],[84.876918014542767,27.880693865179076],[84.757866609042267,27.974167710719662],[84.674188023423199,28.090913394581964],[84.490388383009687,28.148683521385244],[83.915677474332966,28.522200700782065],[83.769512394062644,28.715061351832581],[83.563729118028391,28.684247188245966],[83.336395809602962,28.7492341034087],[82.934775707242608,29.136381888866644],[82.612569507062801,29.246050988592732],[82.2357284889613,29.505716763694846],[81.968617579642384,29.632287192322398],[81.742991522350778,29.862419687124],[81.523009930757382,29.671426926370522],[81.292333385165975,29.571315448157556],[81.150870685697058,29.538698877867013],[80.959076437152177,29.560529341744367],[80.789844784628528,29.653385417001257],[80.594821704590601,29.879382505905536],[80.428505643532745,29.977352035692526],[79.917867586201041,30.150067372492508],[79.808968407622487,30.246518931320114],[79.708144891043972,30.426448236516261],[79.664177997180587,30.45430312699909],[79.469740343246684,30.460171620415288],[79.321877376563648,30.51268880121728],[79.025644942909054,30.715805709285746],[78.923970355369718,30.821913653613244],[78.68759960665588,30.807697172025129],[78.506738726220988,30.870423403652186],[78.393980781338598,30.959925593806926],[78.291843527998282,31.121830564965663],[78.218509373016985,31.767084293866592],[78.033024000541388,32.026002984943872],[77.89460868696078,32.493063717941737],[77.914067600893887,32.691646556380839],[78.043869366492956,32.903519201804563],[78.211948752337918,33.011051118498933],[78.521280738816856,33.073514590082432],[78.390157768915955,33.216380353283903],[78.316023769395187,33.382506915299132],[78.227597354358281,33.987030319609254],[78.242419579317044,34.135428324129556],[78.034979008408158,34.217622608803964],[77.895048761319757,34.337872594526154],[77.607651875756403,34.973610742026082],[76.968369135649823,35.067226619396273],[76.523391526947535,35.222317579578146],[76.343663845528283,35.323140078047224],[76.125932524025373,35.313467202715884],[75.945495821346569,35.36805014638918],[75.830589732148098,35.451064020543406],[75.730078619962597,35.578297946055521],[75.556344223139931,35.698224699890318],[75.472463007361213,35.811779042887451],[75.415957905761942,35.990818862295967],[75.437414621015691,36.238818405403073],[75.202487047492042,36.290374918984057],[75.007099243312766,36.464016676464489],[74.341067850568407,36.564307158582956],[74.016086909240656,36.79082710446167],[73.895421799445813,37.000861777120917],[73.887639132343409,37.242965190012285],[73.964850544185651,37.421463836392881],[74.064638076912885,37.528410224097911],[74.413858806368779,37.690775022512845],[74.30706844133563,37.973809959714785],[74.287941698732652,38.132537672399863],[74.119760314429755,38.055677876024937],[73.925945691928064,38.036632185983507],[73.783849775681503,38.072166653462105],[73.491867154594019,38.215053571467131],[73.369255336941947,38.360616203102964],[73.238380811251943,38.653740157148185],[73.185410339963639,38.961369969859582]],[[120.90431051460445,38.308856727420547],[120.7308981970873,38.490611930520068],[120.61046284024417,38.861927660121914],[120.64206919876338,39.104575978840415],[120.78317485764734,39.301441732069613],[120.78141979123481,39.660501280514062],[120.85872852121086,39.832089606666472],[121.03450765268397,39.989459179520985],[121.16666733100641,40.200852117033165],[121.45028694190952,40.360062391932544],[121.29797928136514,40.366098238509878],[121.12331670754924,40.229130243935067],[120.83039185370539,39.875459007955534],[120.70181706581783,39.783544942105571],[119.83953624953192,39.459820276852959],[119.77042115642953,39.410113217887613],[119.62475835860816,39.108607124469309],[119.23313653797116,38.753509051052248],[119.09467546189944,38.696909443850771],[118.70433533939578,38.672190320289864],[118.50661355580267,38.609110584295067],[118.90979612486538,38.614241821884072],[119.23972675497538,38.442730765149179],[119.37516883689977,38.288634363981629],[119.58584343376786,37.752237489669056],[119.79998813255548,37.879275867741825],[119.89963715839521,38.028088288107107],[120.0613003194764,38.138773835915245],[120.64830591865102,38.323234648258996],[120.90431051460445,38.308856727420547]]],"type":"Polygon"},"properties":{"alpha2":"CN"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-79.376876956957545,1.2683673597974572],[-79.464868855855954,1.3857230057451022],[-79.515019470043924,1.5235629049184038],[-79.504428142705564,1.7662290055963124],[-79.340401114123438,2.0735916466312627],[-79.12935843400723,2.2334665551223698],[-79.091694174538574,2.4630572980618828],[-79.025581712034551,2.5945852297446748],[-78.799792634465064,2.8473242825573331],[-78.585106511992777,2.9387160316820364],[-78.405752323647803,3.0913724153670303],[-78.182329162121349,3.1454159674569873],[-78.091309616589257,3.3425910027482408],[-77.89833947830229,3.4979233294729197],[-77.784321970108891,3.6838269412974429],[-77.972148040054066,3.998514167651237],[-78.020115179230444,4.1947099363087919],[-77.986549137077574,4.3938755630973647],[-77.811801556950215,4.6489407614609286],[-77.799842229054846,4.7152622807197337],[-77.855891473606263,5.1380272515728178],[-77.97832675188927,5.3076506233368184],[-78.031998561251896,5.4915812692200143],[-77.996364721212373,5.7275722714625328],[-77.882394464707332,5.8952056882297077],[-77.965291060189656,6.1143222423851542],[-77.951181971538688,6.4290757386392148],[-78.337687485314135,6.9860316920205383],[-78.400916515852202,7.2305615906052783],[-78.195733621422974,7.946839092734316],[-78.06829981820789,8.0936150115037861],[-77.873526047956531,8.1854007732560934],[-77.9627580761301,8.3750961918558566],[-77.97349594191266,8.5670868627608865],[-77.933149684598206,8.7059577355644979],[-77.801439226730679,8.9330916805416187],[-77.65296149309529,9.0692007094522573],[-77.512647665775049,9.1263932697546757],[-77.258141364414584,9.1290386725968595],[-77.099667723581263,9.0719746010424114],[-76.928162710760176,9.1395182046895815],[-76.681936319224789,9.3153683041232451],[-76.510745872088407,9.5957684566134809],[-76.329807092328338,9.7636069529802292],[-76.165057331105089,9.8479990162214133],[-76.148414266971415,9.9081324254082208],[-76.207729027213475,10.126976200647986],[-76.189684651300468,10.277586496324737],[-76.127307998618406,10.415855125688408],[-75.994441944028083,10.585079867490821],[-75.864296212089116,10.88443107033228],[-75.765034871958505,10.995614969301728],[-75.086731809523826,11.546879745521235],[-74.863950228601908,11.609074682452322],[-74.600786370984935,11.564387235650065],[-74.398341143817603,11.74877990996443],[-74.223072653586101,11.814738149898032],[-73.680269918581601,11.774180117309845],[-73.490057973426019,11.790158066514875],[-72.956196658669327,12.153544982319842],[-72.627440216858062,12.285911582303157],[-72.556289262945597,12.458538161685805],[-72.458322168980047,12.570267965075363],[-71.858420094359104,12.897812211206546],[-71.388689944600102,12.920832697999666],[-70.979384459170916,12.747365952934793],[-70.838414573727292,12.600299993909513],[-70.671051794840778,12.288666396755294],[-70.63838141530978,12.017909009436456],[-70.691811083538283,11.819935747859059],[-71.036316959371575,11.450292503954085],[-71.640298181049673,11.228551582867652],[-71.882699463013878,10.855880810435002],[-72.15973591944109,10.686908597386749],[-72.265856254802799,10.56185973400556],[-72.393894954734364,10.315406275730401],[-72.565955766939183,9.5534722626028632],[-72.372716770281258,9.3740436227807322],[-72.217008415853016,8.8961691565983347],[-72.024663721991615,8.6920575499161981],[-71.937935429918582,8.5256985376463987],[-71.857908906763427,8.0818200307543417],[-71.883901018834422,7.9281829608130057],[-71.948604780800466,7.797075520433042],[-71.832163600397479,7.6999457514751626],[-71.727953649878714,7.5245947809457361],[-71.105556962490979,7.4977575794934808],[-70.837631394504669,7.5796559594773889],[-70.682503172121869,7.5868609744529785],[-70.271556967453023,7.4631881744118447],[-69.949401104619398,7.4198423997229908],[-69.786287870646902,7.3194611004496481],[-69.184665776536079,6.6507390346110249],[-68.951235556977991,6.6977566552709451],[-68.492853524746138,6.6626201375258249],[-67.825580353536509,6.788534276377268],[-67.405479672316773,6.7142910736966659],[-67.121203963388467,6.5260942296766347],[-67.000158876654325,6.3129557519467419],[-66.946527119721139,6.1084866615676985],[-66.944485151899741,5.9558080104375524],[-67.020893940813068,5.7193213396714082],[-67.12620225556158,5.572091043766302],[-67.185561582258572,5.3556000888769102],[-67.322237440478347,5.1355620037283023],[-67.350467600107933,4.6117140123223139],[-67.20227652069552,4.0881048583714064],[-66.888931756449566,3.6832399045747146],[-66.818433533406122,3.4993550518265786],[-66.815590499242617,3.3512559335446772],[-66.85626299790863,3.2088228631874212],[-66.95076952359284,3.0267442308761487],[-67.054104804789915,2.9198889988008152],[-66.844528682786006,2.7300968043943712],[-66.744637688460273,2.5701613201992597],[-66.704962968042707,2.3386928079833194],[-66.38569718771204,1.3893686081740368],[-66.389249742142184,1.1101379680122412],[-66.497833371536345,0.89645681684885736],[-66.693388163394417,0.75786258061855349],[-67.041068714267524,0.68725395391312638],[-67.281999394588823,0.72725759632237197],[-67.475239674670703,0.87660979175305731],[-67.563824572671379,1.0515617250633236],[-67.595053785413384,1.3417099783762703],[-67.868956372003893,1.2533371082710318],[-68.804174882459833,1.231549240712791],[-68.694977984497783,1.0387049275206919],[-68.664033483165227,0.88800812298054266],[-68.659009218557912,0.58530283975079644],[-68.751431000137501,0.3616569199295056],[-68.89504267860255,0.23079764561145055],[-69.154379423225947,0.14250200550680697],[-69.304931018278424,0.12765916781458092],[-69.556660023994809,0.17315371733774565],[-69.556128738071223,0.039916652792333265],[-69.31606394629803,-0.12373960225665448],[-69.191042160566525,-0.27794474946047332],[-69.104818557737033,-0.6239613211169095],[-68.977940694386632,-0.8311872508251128],[-68.900708153487841,-1.1802606593008886],[-69.474377255394032,-4.3269157692373916],[-69.572906112616423,-4.5445570842131575],[-69.677484039071032,-4.6438952543730752],[-69.806067583818091,-4.7092472763199655],[-69.947956222409829,-4.7351746554942826],[-70.091342200991747,-4.7195197553549946],[-70.264335613603393,-4.6367035536324988],[-70.522468782472558,-4.3668435225329763],[-70.679071811725251,-4.3433709598411809],[-70.969651202446798,-4.2227786180239955],[-71.148240776162922,-4.0624976329732743],[-71.223826836787339,-3.8852911882270429],[-71.21590425709546,-3.6454548633303232],[-70.757780694254848,-2.8854911090727757],[-70.987636033502937,-2.7676304202544006],[-71.43493807060743,-2.8324103646165915],[-71.705343224955072,-2.7416222819506939],[-72.060184820139753,-2.8753114292736091],[-72.370583597989892,-2.9280089478645093],[-72.660758272550538,-2.8724852839512773],[-72.996403276808991,-2.8907579349556456],[-73.179492220773454,-2.8332873688754043],[-73.433745856137307,-2.6926874968830847],[-73.568477332876697,-2.558176997550726],[-73.643161718746015,-2.3830545039574145],[-73.648886253652307,-2.1876652772944527],[-73.907125269462469,-1.9775806363523858],[-73.973161901196718,-1.8425527008204567],[-74.001936305126947,-1.6618930774758547],[-74.589845578826839,-1.3336812588296256],[-74.675997922777853,-1.2260231975249654],[-74.860034464204816,-0.84441332509824463],[-75.091200544365734,-0.65271964641653446],[-75.201315383941562,-0.59947632802910611],[-75.42005758181233,-0.58755045420388707],[-75.96139093221808,-0.37512611933095102],[-76.222800010641038,-0.18323921801137802],[-76.418886360346193,-0.25850299141339272],[-76.855190448697712,-0.25045301974505163],[-77.612215758503197,-0.056860448163554456],[-77.797381788530117,0.09563601190577814],[-77.913618035218732,0.31714964239179927],[-78.404715608334456,0.52188796661404901],[-78.947819976843775,0.86045526296988162],[-79.376876956957545,1.2683673597974572]]],[[[-79.767378095204279,15.236974904795719],[-79.958719811386828,15.275035138540076],[-80.084574737286104,15.350469678114353],[-80.183112901355557,15.459189788285919],[-80.257770735405899,15.639429743787655],[-80.264970458540375,15.908414665369062],[-80.229317861459919,16.050747811386827],[-80.120931485797556,16.212959485797555],[-80.003076463617276,16.30036672737846],[-79.816386665369066,16.356998458540382],[-79.51236574378764,16.349798735405898],[-79.332125788285907,16.275140901355556],[-79.190973815287762,16.131848524705767],[-79.097830485797559,16.261831485797554],[-78.935618811386831,16.370217861459924],[-78.793285665369069,16.405870458540381],[-78.481494334630924,16.405870458540381],[-78.252717788285906,16.324012901355555],[-78.089542272621529,16.143976463617278],[-78.032910541459614,15.957286665369061],[-78.052032736929604,15.631482566168488],[-78.143997678114346,15.459428262713896],[-78.294804536382713,15.335664272621543],[-78.481494334630924,15.279032541459621],[-78.841822256212353,15.286232264594105],[-79.022062211714086,15.360890098644449],[-79.165652111968058,15.50824991119114],[-79.256357514202435,15.383421514202446],[-79.418569188613162,15.275035138540076],[-79.560902334630924,15.239382541459621],[-79.767378095204279,15.236974904795719]]],[[[-81.85752709520429,11.681878904795719],[-82.002669433831528,11.703408736929616],[-82.135312211714094,11.766144098644448],[-82.298487727378472,11.946180536382721],[-82.355119458540386,12.132870334630939],[-82.355119458540386,12.652933665369062],[-82.298487727378472,12.839623463617281],[-82.174723737286115,12.99043032188565],[-82.002669433831528,13.082395263070387],[-81.851079306652125,13.103925095204282],[-81.900222095204285,13.318698904795719],[-81.862161861459924,13.591919811386827],[-81.71741873728611,13.78708332188565],[-81.509381489044983,13.8880615215287],[-81.632441727378463,14.038230536382722],[-81.689073458540378,14.224920334630939],[-81.681873735405901,14.440210256212346],[-81.607215901355559,14.620450211714083],[-81.469266211714086,14.758399901355554],[-81.289026256212352,14.833057735405898],[-81.002385743787642,14.833057735405898],[-80.791726605330652,14.735839497699637],[-80.653144144553877,14.879440333932283],[-80.427982701660824,14.963688055941054],[-80.071995425171664,14.97003893595582],[-79.839481063310203,14.890542588452288],[-79.698699388292113,14.75229187573567],[-79.614998426249372,14.521257794502278],[-79.622142264594103,14.172391743787655],[-79.715201099001717,13.967340911452121],[-79.57819215363098,13.806712996989795],[-79.521559069888781,13.620025093556736],[-79.528756277695933,13.388478738343089],[-79.603413081904236,13.208236818647299],[-79.701951294061416,13.099515301829713],[-79.874006513729142,13.007549061201363],[-80.101088075538684,12.986018904795719],[-80.292429797760349,13.0240791328958],[-80.454641478844721,13.132465507249604],[-80.57955826200002,13.340876562639961],[-80.598680458660866,13.620019664145874],[-80.544357944587858,13.801827034608586],[-80.736899631318138,13.930832746495184],[-80.864232536382715,13.832968272621542],[-81.018559615030597,13.781137097430962],[-80.880377138540084,13.591919811386827],[-80.842316904795723,13.400578095204281],[-80.880377138540084,13.127357188613173],[-81.050822040836778,12.913131940329357],[-80.981313138540074,12.795266811386828],[-80.945660541459617,12.652933665369062],[-80.945660541459617,12.132870334630939],[-80.981313138540074,11.990537188613173],[-81.089699514202437,11.828325514202445],[-81.298110566168475,11.703408736929614],[-81.85752709520429,11.681878904795719]]]],"type":"MultiPolygon"},"properties":{"alpha2":"CO"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-86.294860936485378,10.581937419880715],[-86.375397316833542,10.720859776847114],[-86.40672791614108,10.866782979113578],[-86.393592876519136,11.015452606633119],[-86.337162539844712,11.153622084143457],[-85.919009619477336,11.560568936144449],[-85.705605243061029,11.674238117561702],[-85.463815971619496,11.674480797340339],[-84.950285249575927,11.494841008862075],[-84.707107126485823,11.551951919779151],[-84.247896962964319,11.469424550832183],[-84.080098700474522,11.401559175480056],[-83.960316256730366,11.301609764970646],[-83.803713039282542,11.389747945080137],[-83.662550578371693,11.416231636501607],[-83.473734509316756,11.387533961943694],[-83.346815166324177,11.320305198337286],[-83.216998507334765,11.180224152127554],[-82.924385408750737,10.59354432754817],[-82.465859340686706,10.091932993474037],[-82.210754083663574,9.9306050662051195],[-82.124538333700968,9.8153627559309218],[-82.074721090469779,9.6803361367190259],[-82.065430000638713,9.5367129447592962],[-82.097434884725772,9.3963931920032309],[-82.16808395301689,9.2710031813622393],[-82.28830977118038,9.1531062865247037],[-82.236495425841397,9.0074078784819882],[-82.230771338517741,8.864354960464917],[-82.265820969589228,8.7255442047914435],[-82.351428983814031,8.5866488427024432],[-82.348968366412265,8.42682131764502],[-82.408007849961365,8.243774282883475],[-82.384192046676205,8.0038628631676172],[-82.475284592986299,7.7773042828143444],[-82.57867858257481,7.6721542513694807],[-82.755355719092123,7.5871433401113855],[-82.951137722788417,7.5766042400757749],[-83.092381642233391,7.6189952935623761],[-83.21511637817143,7.7007450996178308],[-83.414529235726036,7.9170467228709827],[-83.759544717778581,7.9951418057681343],[-84.099258151634174,8.2732479632123432],[-84.180469138379735,8.3898019108234703],[-84.225623780955601,8.524490831398893],[-84.231077188110959,8.6664426099094989],[-84.17759499163364,8.8444612360745936],[-84.207499369120939,8.8762633205747665],[-84.422196633063351,8.9960110689985786],[-84.859182793607587,9.1528037849304216],[-85.077993193217836,9.0833991883662577],[-85.274455239364784,9.1083559489117576],[-85.445817600800154,9.207628888610687],[-85.598943047304317,9.3694395272267776],[-85.921735238282565,9.5010695477849598],[-86.198259648005589,9.8355613043858767],[-86.270604015052456,9.9746851978907838],[-86.3492786120745,10.280868688029457],[-86.294860936485378,10.581937419880715]]],"type":"Polygon"},"properties":{"alpha2":"CR"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-85.141978333659296,21.42703874004636],[-85.286117752578178,21.556018820442201],[-85.355885951056749,21.683664805024041],[-85.386620454011819,21.874629261280415],[-85.342576908415111,22.062969908291247],[-85.193408763094894,22.252152418706117],[-84.866488171488413,22.426793714824683],[-84.748612956398077,22.694661187188384],[-84.350133237669979,23.063744131149701],[-83.405033843257087,23.445208486947021],[-82.747682968471366,23.53901155861978],[-82.179234225971342,23.684156182272595],[-81.571886767741375,23.622153067853265],[-81.219823613133627,23.654752798617075],[-80.997291798575318,23.581408836622703],[-80.584575330784475,23.598478582014405],[-80.443610964093708,23.558126369981871],[-80.246821403257599,23.44716468893985],[-79.95611750770307,23.427620200167201],[-79.396048181967274,23.27173197064517],[-79.045020767637979,23.060128625315446],[-78.937676730132367,22.946240966765128],[-78.801206855166569,23.021774737235361],[-78.655516445474305,23.051314153175422],[-78.282851532137954,23.033676362661609],[-78.11486432976794,22.978926365825071],[-77.839843172258711,22.823211693987862],[-77.538928568566931,22.547540373103473],[-77.353324667126742,22.467657606616996],[-77.243412363612933,22.366074059081132],[-77.16850990453878,22.242039715134517],[-76.587494222504162,21.836210215956811],[-75.973413430174787,21.630616769490992],[-75.641113475073666,21.604045677457666],[-75.511227371011813,21.563730227389492],[-75.264282733027542,21.396727809353159],[-75.149487064195,21.189124319961905],[-74.724083293789334,21.124596340148042],[-74.294504119255649,20.847696980514424],[-74.082527074455044,20.802425889174586],[-73.950105320176519,20.739327689612054],[-73.697319069640315,20.469792905543869],[-73.64799007199727,20.335681913172355],[-73.638609955658595,20.193094670752803],[-73.712322693253171,19.968155674940242],[-73.886023839330207,19.740252755943192],[-73.997122165254297,19.650318423863553],[-74.175942255390169,19.585885348939129],[-74.541520807145645,19.562008619801002],[-75.009072462433494,19.413321406880243],[-75.595552239995996,19.393245499636794],[-75.861235620918691,19.465328643735784],[-76.16553517860784,19.48786699746621],[-77.770343569951095,19.358798995888808],[-77.913247326468806,19.396813580929436],[-78.038782341149314,19.474964411966923],[-78.180685915447071,19.674498877162314],[-78.213303213310738,19.818730302943102],[-78.202306970354826,19.966194450936911],[-78.068256436670879,20.221160084427879],[-78.384197358336039,20.340005135816384],[-78.834220616769713,20.690694842257169],[-78.945147916609713,20.845715899180163],[-79.010415260065841,21.076366041756511],[-79.238570841949908,21.055513709774011],[-79.506229208961628,21.107838263083476],[-80.337334899019069,21.370656598851305],[-80.627824687462876,21.557802884706291],[-81.0010592922591,21.55466179592753],[-81.224825059489575,21.617468712580045],[-81.438656976576283,21.61138611388046],[-81.634372265642639,21.689441990142655],[-81.937366725910465,21.715432614553873],[-82.117613178081456,21.801423531787599],[-82.067389205476033,21.644909918515157],[-82.067923615303329,21.495170854733662],[-82.112772323671294,21.352304951965547],[-82.197912926561017,21.229125608909989],[-82.315709308782104,21.136680570364678],[-82.646387116852594,20.988291833952147],[-82.940139854798986,20.941903501132224],[-83.134216939832157,20.973009306208066],[-83.473982945605513,21.158638346731166],[-83.586210668699692,21.30724722304895],[-83.674167112245996,21.593668929335919],[-83.860756775970188,21.473261603203582],[-84.46329238214237,21.277992007996314],[-84.610264561564904,21.288210099308209],[-84.742052078267065,21.337904167764307],[-84.964016848843627,21.344284997056391],[-85.141978333659296,21.42703874004636]]],"type":"Polygon"},"properties":{"alpha2":"CU"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-25.825427696049111,16.945155964394406],[-25.827208101547892,17.185448357522343],[-25.729539223449621,17.399242205516938],[-25.587095184624868,17.522968517838329],[-25.189901027250034,17.686885170372491],[-25.006482035810269,17.681213887905315],[-24.792759339043016,17.612950786594656],[-24.677565112530836,17.525080207302612],[-24.495424427588866,17.269306442117461],[-24.402549849758202,17.200155635619662],[-23.922353478179236,17.093309315271508],[-23.684823687679597,16.986311360855375],[-23.481676543304459,17.024518587225103],[-23.272982225991033,17.22054400659016],[-23.08188155208305,17.317195494099952],[-22.908534266208246,17.342743961479659],[-22.766598901519576,17.323032431410148],[-22.560777608562358,17.204792865704402],[-22.450374634309519,17.049483894483046],[-22.378716023749252,16.616237663886668],[-22.203021405220905,16.264872263393467],[-22.18282224178429,16.113829094275445],[-22.20349006827529,15.971101726195293],[-22.326730010635846,15.7240610635527],[-22.590087976248775,15.480960286585548],[-22.648531349551519,14.992040633726546],[-22.720674731698089,14.862186978154362],[-22.827785275945033,14.759259961467153],[-23.066066462645953,14.639843402604692],[-23.259061950617816,14.481729891900422],[-23.391240851856487,14.430149260957856],[-23.664559265061403,14.425046265181374],[-23.934729369244494,14.465914956505014],[-24.297869034242801,14.327006386694649],[-24.531464409934951,14.340705887042299],[-24.707924046530231,14.413261819671053],[-24.900168649814475,14.586986509406888],[-24.99188532607009,14.777801115963582],[-25.014038879903254,14.975700276715665],[-24.978527453979698,15.121220697974302],[-24.882299548967001,15.297261116354527],[-24.771752865488914,15.396814993177177],[-24.503489790778605,15.544079289298324],[-24.425535991616798,15.633718741722328],[-24.386590727108004,15.776436772520857],[-24.420737831807752,15.920378236954416],[-24.806730651665966,16.259604018697591],[-25.185019653266167,16.326260175164144],[-25.605083330894658,16.534734789578508],[-25.735934147544928,16.678778173595799],[-25.825427696049111,16.945155964394406]]],"type":"Polygon"},"properties":{"alpha2":"CV"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-69.657660604634287,12.348836791112172],[-69.611217429716305,12.592512947521785],[-69.453374810682874,12.783878423289201],[-69.222984153584932,12.875830491351868],[-68.942273549494317,12.840932981430681],[-68.506306825843794,12.541437407973634],[-68.355946741754394,12.365789104965977],[-68.258466965497036,12.143328783153441],[-68.277314746068157,11.901180764933875],[-68.37408937724436,11.731877993319427],[-68.528267199592221,11.612471306690519],[-68.774674789392236,11.546565306907951],[-68.979942408249215,11.577990901959959],[-69.307163696536691,11.751520502936163],[-69.539862553547977,11.98112641888328],[-69.635321947603941,12.164786140284345],[-69.657660604634287,12.348836791112172]]],"type":"Polygon"},"properties":{"alpha2":"CW"},"type":"Feature"}, +{"geometry":{"coordinates":[[[105.43398060835366,-10.988529865413984],[105.24404383231055,-10.878631715148346],[105.11094505457967,-10.673185440792608],[105.08552052322113,-10.4782801820713],[105.14963302665964,-10.234406091891701],[105.27347514101673,-10.077735039806043],[105.44875484376344,-9.9820286468372998],[105.68453656137191,-9.9314172876819526],[105.91869877533716,-9.9787930681867749],[106.10414943567801,-10.129406678483754],[106.20013502059108,-10.335990752934729],[106.2206282946869,-10.559951749978023],[106.151438481618,-10.771862372606744],[106.00332929089954,-10.958816626320022],[105.83238198659615,-11.04507546499535],[105.6415477467901,-11.060755420858026],[105.43398060835366,-10.988529865413984]]],"type":"Polygon"},"properties":{"alpha2":"CX"},"type":"Feature"}, +{"geometry":{"coordinates":[[[31.805105315721498,35.020577245282254],[31.835112274450893,35.26352668987375],[31.976849822170735,35.463114559660141],[32.148574199141422,35.558749859143248],[32.525311244452205,35.665765612065208],[32.642835193840995,35.790917580067273],[32.815648647408324,35.873970760358048],[32.958964048399828,35.88985390138383],[33.501488186739842,35.845033431101477],[33.872459156882201,35.941610685710437],[34.294687865294208,36.114744131266896],[34.588499721812184,36.160559875044235],[34.776039067357921,36.110257167111065],[34.896232693300334,36.027316389726622],[34.98733538276911,35.913184973184663],[35.041575828454746,35.77759863787707],[35.049064420598512,35.58357469723871],[34.98207569645691,35.401327943108456],[34.85071192234102,35.258342183666613],[34.544263984802292,35.062787364992722],[34.52308576905947,34.827119491209857],[34.394939950856688,34.626723751505615],[34.188697025787469,34.508217806197536],[33.89167759273834,34.472407548658374],[33.658342654536703,34.314519738298827],[33.370602803940649,34.226059451469489],[33.254072012443785,34.134698601604569],[33.112076476114019,34.08082828310183],[32.797074927466227,34.097525751730487],[32.293198339291777,34.254561252779538],[32.156039827190071,34.324545756098729],[32.046681039595242,34.432947526022367],[31.862962986150279,34.744862257812493],[31.805105315721498,35.020577245282254]]],"type":"Polygon"},"properties":{"alpha2":"CY"},"type":"Feature"}, +{"geometry":{"coordinates":[[[11.678563103122485,49.984519419230509],[11.601643960911614,50.161350957486889],[11.607965000256488,50.401461299817953],[11.725525833867701,50.610918652119985],[11.920419208988376,50.765022799968122],[12.05832760095057,50.805819302344943],[12.244910058029431,50.797296415487324],[12.448823406157292,50.883029640610239],[12.801406065568148,50.922417448455469],[13.402523401216474,51.180014103948437],[13.856731263838114,51.283413588315184],[14.017461818468069,51.451146659195835],[14.194508166374213,51.521756973854103],[14.561479305563052,51.505601258293247],[14.750253590043215,51.446892256371619],[14.901296653415987,51.50392627162929],[15.052725350850929,51.510617049340681],[15.457163654414911,51.417149132885072],[15.664248139279669,51.24935949081371],[16.075774707360388,51.140089124042497],[16.290887125959479,51.155251808874084],[16.437054810846305,51.13073782603098],[16.636761910471566,51.037510086690176],[16.782367686299178,50.916762321827569],[16.919694154902512,50.925248878530518],[17.240221268585987,50.870159140261798],[17.492377445414974,50.776206280053366],[17.707443724891775,50.806882224449481],[17.89831700227198,50.766861827914994],[18.059360720650034,50.656865641946524],[18.15371731740764,50.52044540015897],[18.753387730590116,50.341212183257845],[18.885153457275464,50.260932102714378],[19.024719559949517,50.071670319601758],[19.233569985456853,49.873983127301486],[19.292263300370148,49.733002239562957],[19.331161553949631,49.482723526798267],[19.257452639778595,49.248416414396161],[19.081059664660888,49.077482669103986],[18.942889150370515,49.023475386962694],[18.726690489798798,48.993949561895271],[18.563992748665349,48.901560210787039],[18.398418201922858,48.674780480380981],[17.953537898113488,48.427953844079312],[17.595174193537037,48.340048086702375],[17.378430557548459,48.330594576240202],[17.238921050319604,48.189019629090737],[17.064721519179241,48.111858666916866],[16.827412892784711,48.115262593904426],[16.550584582582882,48.27066366987183],[16.393745777589295,48.239925460174426],[15.939897744531802,48.268941104263746],[15.666940063746898,48.382397786726784],[15.312240447790764,48.45905990415136],[14.978329285356555,48.19018138113573],[14.851552053326825,48.125902251409897],[14.157942440974598,48.079734123224242],[13.822713860159022,48.156993756678389],[13.632072462781849,48.321980187286542],[13.417967312800334,48.453172809822881],[13.181250896820531,48.527987222471779],[12.723976660022229,48.856154662814426],[12.381262142501566,49.014616098110409],[11.987471313626493,49.444146172421405],[11.918235322735487,49.576315095235174],[11.892081541110125,49.711028267040049],[11.678563103122485,49.984519419230509]]],"type":"Polygon"},"properties":{"alpha2":"CZ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[5.4988985990375348,50.682175959215336],[5.3935992973729805,50.844286130129596],[5.3578474791483108,51.037487004846255],[5.4212270396812983,51.273835840909157],[5.5873403517641727,51.4505096343009],[5.4965117859743957,51.589735552257842],[5.454250570842917,51.730022070473169],[5.4640836497210508,51.924577718762784],[5.5202738812551564,52.059888381230721],[5.6132736227656927,52.173102185938191],[5.735097426338907,52.254497998977456],[6.0490247839111877,52.366468425452538],[6.2186763148224884,52.377274964011065],[6.2068891494404861,52.619224948119367],[6.2520513635761228,52.810576952929587],[6.4054533310008592,53.001425671884142],[6.6365829148356772,53.100486828447821],[6.562655673921534,53.28123641727754],[6.574308468500341,53.51837340687522],[6.6921502869409881,53.835437446081599],[6.8911638905232113,54.04204464387508],[7.0265265177967171,54.11912622379031],[7.1790411186934273,54.150810720597597],[7.9849657167656583,54.189868731557731],[8.1620724135298524,54.166230793089539],[8.1458438256063683,54.281673177288738],[7.9520293839677958,54.39422537797634],[7.8311306881770202,54.557352665310752],[7.7850648259608031,54.755102362795398],[7.8030919782988946,54.99268923785877],[7.866380510530548,55.164170374766407],[8.0664345519813612,55.426138660735624],[8.188083302668975,55.508834049908508],[8.3774511918563608,55.557704377887084],[8.5710792324760661,55.530196409990616],[8.785592293753071,55.399570743801085],[8.9693837375105989,55.392241152390561],[9.3202112109895516,55.315641862972733],[9.6955503410173467,55.348794324959925],[10.022866072814839,55.262971586742765],[10.228552016011975,55.150288368507383],[10.457702204315815,54.901738342818582],[10.689232586945558,54.845370650065313],[10.812012847976348,54.952032169125609],[10.994326811297878,55.024835155534227],[11.141895916460342,55.029874175919154],[11.388691872140809,54.976323936523329],[11.565808069829876,54.874579018288813],[11.730249973990892,54.639880680360172],[11.932378031083907,54.661915546131112],[12.458943390205038,54.953399815099687],[12.877081831477559,54.94593737685431],[13.059028774487967,55.112536251370152],[13.230875536948981,55.185485749222288],[13.545061621973453,55.183847511503842],[13.884648398426833,55.010490589293767],[14.061499900122369,54.839187761835525],[14.19006877695824,54.519316665954868],[14.514806588493201,54.347433464251864],[14.663117994691692,54.164032935888244],[14.714580175954197,53.909172518216117],[14.908191439213269,53.361082322620241],[14.908970728281073,53.210931506726247],[14.855204763757056,52.986906604496966],[15.015986047960531,52.832637361826812],[15.086675894649723,52.705707087013451],[15.114197212358912,52.457754594257629],[15.210475898021597,52.28186065292369],[15.249482579764848,52.134332871203746],[15.200728323636305,51.858968133351425],[15.370392526867017,51.681589571324849],[15.491317516411289,51.408908153009861],[15.51626576937794,51.261329345649671],[15.496416833146629,51.11297861448049],[15.363146458592698,50.781911437622249],[15.126772513503738,50.471913382647166],[15.011492803324398,50.382712892255078],[14.875385488389233,50.33055028221338],[14.730017588461868,50.319858568789883],[14.408095637214704,50.398397088737021],[13.800411407351152,50.240596817272007],[13.658898948957953,50.152844201602313],[13.43551586867885,50.098616823322963],[13.011426201235469,49.911908571759348],[13.006083688022485,49.82140053288628],[13.182366947174117,49.753764002724481],[14.122103284540481,49.161055431439358],[14.253623386762811,49.005936985585869],[14.31249004165687,48.811273207562941],[14.286163619328484,48.513474837704315],[14.188137656161713,48.305587321116235],[13.97667767782454,48.111675737729122],[13.760598153833609,48.045383626831899],[13.661388432526037,47.952422850534205],[13.501919576688284,47.875991062882505],[13.553722562023577,47.644749310613818],[13.502106655672161,47.368960156996181],[13.449464520029505,47.232233802882867],[13.322396572435219,47.084583808285153],[13.148550006138894,46.996696632898143],[12.954312493688484,46.98191241943897],[12.718775118181359,47.033944293893384],[12.492989694662089,47.163032049855062],[12.408127297388543,47.172493569092282],[12.275983395570643,47.128021974710109],[11.813904790008701,47.087472918784115],[11.419133809576365,46.940076305732951],[11.10376752227312,46.897118590623265],[10.898974875823921,46.914175028533549],[10.711283608285914,47.003852070011739],[10.537001521764958,46.866750280649889],[10.218857108315037,46.780323697978616],[10.077748485334777,46.790282445287744],[9.9450764489314682,46.839363509701137],[9.7086033623711838,47.045209357405483],[9.4375079549142367,47.032011604099772],[9.0766432648471458,47.165174721799772],[8.739822302366548,47.175802298637279],[8.5225555013590046,47.100797610759805],[7.918213305590994,47.064425991333508],[7.4589597212639172,47.118261490407434],[7.2389390840528849,47.228217194089368],[7.0820814537587982,47.45620332627233],[7.0304612856613575,47.7036364317999],[7.1099391550607169,48.222393597846356],[7.2868875730217146,48.626943695646659],[6.9729434459280579,48.617028429286741],[6.813682430145982,48.666056232384598],[6.5803483868010222,48.685484443075708],[6.4024327767376237,48.787914660913451],[6.2387843778063026,48.963796360676298],[6.0775558635424671,49.030280043707776],[5.9629582589745791,49.129954734197106],[5.8539870363605431,49.356500209993456],[5.8555233625604188,49.579096297443279],[5.6907972026805407,49.761967301372295],[5.629886893083186,49.894943414589868],[5.6129845399602374,50.088679097675204],[5.6762544117888361,50.365397218646365],[5.5644861556003189,50.494895059078033],[5.4988985990375348,50.682175959215336]]],"type":"Polygon"},"properties":{"alpha2":"DE"},"type":"Feature"}, +{"geometry":{"coordinates":[[[41.26736153944546,11.563007313051759],[41.297150559042649,11.760962520915893],[41.421089598553067,12.030349348343254],[41.600969585158481,12.222015115763419],[41.994351863674538,12.785633755919921],[42.263821812225977,12.985077363229408],[42.403463694228563,13.018876373150114],[42.551543057737561,13.0104318316752],[42.656385475985566,13.076476993782359],[43.002483392140142,13.195095630106783],[43.248758240434832,13.190484933436203],[43.384787270358999,13.130238872735681],[43.497068193403486,13.03263518872571],[43.812378416543488,12.5649652162216],[43.898735213647726,12.293177679129245],[43.907434739457607,12.144437174879096],[43.840880442712837,11.897400616424461],[43.702747934293903,11.701271451329744],[43.745377730926698,11.517295520255717],[43.711739310431497,11.318661873407979],[43.314350443760034,10.688821871812687],[43.123994090780016,10.541890976723346],[42.887061649771567,10.500814858973627],[42.562822518629893,10.561047557849548],[41.972949418091396,10.441465278264706],[41.678118192177799,10.495337511665632],[41.542496709991269,10.5511248311252],[41.397679804579319,10.681682757657406],[41.313809284134678,10.857702768093237],[41.26736153944546,11.563007313051759]]],"type":"Polygon"},"properties":{"alpha2":"DJ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[14.556194201375392,54.619121267435702],[14.418613370628764,54.678914252328681],[14.304961056780067,54.776826649849014],[14.225467643779643,54.904044900587216],[14.187288710858379,55.049117486255],[14.222978621360838,55.294881142692539],[14.324985192909494,55.53319300432635],[14.412516341730568,55.650692470750585],[14.574671367895462,55.75866001193797],[14.81471474557476,55.794025519439167],[15.323821396980209,55.60626525568992],[15.488599137784906,55.495238273960261],[15.613218731698845,55.281479482697037],[15.633279341175481,55.027686827271772],[15.572307510362055,54.841541501128368],[15.395708917730957,54.643244268074753],[15.270545667901681,54.556088101313428],[15.074158098129617,54.505734274488944],[14.556194201375392,54.619121267435702]]],[[[7.6572989652825125,55.954801053301111],[7.6238081599775729,56.185217236159907],[7.6997740537848864,56.791985429942272],[7.9035036608308067,57.174756902095602],[8.3060546377580664,57.502124439115484],[8.5263270880913833,57.600731115248934],[9.2396691170072494,57.6600619155316],[9.7497049010221097,58.033239923994422],[10.13802486758202,58.10672137973922],[10.498591666817589,58.224289042610494],[10.645188513138184,58.235399817410325],[10.788679699290801,58.203394127848753],[10.953902141914034,58.098943810513454],[11.044361394665652,57.983050720730603],[11.101782910160622,57.819330017449303],[11.291395021780621,57.808722663708004],[11.428780310612586,57.752828241521321],[11.543723786921657,57.659089768773946],[11.626110928696093,57.53575582012531],[11.673283313885612,57.34428936834486],[11.623280774896834,57.10385580129315],[11.503683438579506,56.947072150271552],[11.27307782825015,56.803048032394742],[11.38327422795671,56.645233750758223],[11.425338298004808,56.440167169327282],[11.689714089814009,56.4325212573751],[12.062334081049464,56.593189714177655],[12.200376669929211,56.61802126254937],[12.788142152588961,56.517855327907895],[12.969722963212011,56.378274980111769],[13.072578511850068,56.218042935832393],[13.108075010394082,56.030977250869341],[13.08450202031492,55.881025413971862],[13.151626193624795,55.713350140410043],[13.164348243140008,55.563725883298844],[13.111699444272917,55.371164847617429],[12.983910997782628,55.211501986834961],[13.046382040601411,55.014962911364428],[13.026256080191295,54.817399443634102],[12.961098640868745,54.682984376224759],[12.859142924354773,54.573815085140446],[12.68206311625365,54.48393116206779],[12.442052306626882,54.45715134645522],[12.131229728676219,54.336811770888531],[11.894583677494031,54.196517137148817],[11.433643071558901,54.129668384134405],[10.924185054917022,54.296656202006957],[10.750444360153731,54.248994009026404],[10.598398787743317,54.253742671426053],[10.130413716948212,54.412972903621544],[9.8024428793416103,54.329746821558324],[9.5866567033050156,54.343124508568565],[9.3171052887043633,54.306776229832337],[8.4516687959452401,54.454143333332681],[8.3257091759435351,54.541622397743481],[8.2321879855185198,54.663163335384468],[8.0769824627998403,55.075704706825633],[7.8390305934340496,55.195032508368527],[7.6811781257233047,55.38448015369422],[7.6329219367567784,55.576522711139006],[7.6572989652825125,55.954801053301111]]]],"type":"MultiPolygon"},"properties":{"alpha2":"DK"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-61.924612727903366,15.294762452668131],[-61.979852992308111,15.493211440040504],[-61.934794818648449,15.786617404598816],[-61.821126456357703,15.976524360108682],[-61.62217120944468,16.105103892009712],[-61.386361093403849,16.12767545533449],[-61.109480494600696,16.03828887508125],[-60.949650373168033,15.920574436737118],[-60.800966863577735,15.678169522684067],[-60.751688489489005,15.354505972477298],[-60.810811821995948,15.081639548350044],[-60.913394545691659,14.911275249637688],[-61.120763349426355,14.775899918960853],[-61.408161513069174,14.72865651912943],[-61.639045171438404,14.802839727501283],[-61.807866007806119,14.976937221206414],[-61.924612727903366,15.294762452668131]]],"type":"Polygon"},"properties":{"alpha2":"DM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-72.393585596986512,18.289376626802863],[-72.480754448699898,18.460139459165038],[-72.497250749568238,18.651153138473724],[-72.440648992148255,18.834332271243671],[-72.306187018555434,18.992569423932089],[-72.24332256539833,19.229930474308905],[-72.230424100931387,19.472773780338411],[-72.278745644085319,19.731644628179446],[-72.253660793936717,19.875041196576358],[-72.188334216314871,20.005134485377923],[-71.98096684679679,20.237692550625329],[-71.620029183720078,20.391198011293056],[-71.274383519255537,20.357253336852398],[-70.902280714119129,20.41101347774589],[-70.543069056651504,20.278876281788531],[-70.27487313164626,20.244159737503317],[-70.103263144700207,20.157984330269912],[-69.914560694096082,20.169844278399108],[-69.768660563591126,20.134836792954548],[-69.602574048241607,20.024373686870952],[-69.46980434379789,19.819073632986857],[-69.256423468237799,19.822745718161997],[-69.111151912615597,19.779427208400271],[-68.893156826090944,19.63872337355474],[-68.767779268331054,19.453883504920714],[-68.489360562704306,19.364744180277121],[-68.361434313322121,19.285842635477547],[-67.948144692421351,18.92277532809641],[-67.874944381884518,18.796586578422225],[-67.841283489749898,18.654640002740656],[-67.862341224927434,18.461816373034964],[-68.030339651835121,18.138457937493921],[-68.366656679947482,17.831575223213854],[-68.489819490587323,17.755830083752667],[-68.629506392052392,17.718494127413134],[-68.774035981117692,17.722689596554549],[-68.911321881823071,17.768065642484267],[-69.114816391816191,17.913489528039324],[-69.305674925683249,17.936696876285382],[-69.724511644598365,17.938065099912269],[-70.024962875283336,17.777737913894114],[-70.417168342160025,17.721213886828636],[-70.574305432323584,17.727711123071298],[-70.72251027977849,17.783184636045021],[-71.035568389150399,17.310502535496909],[-71.236694446326567,17.178561648829913],[-71.42083423822811,17.136183770706598],[-71.651461309007132,17.183298114549821],[-72.001682563621216,17.438984033323372],[-72.227456462299955,17.841861500684125],[-72.271679362082182,18.133883050474449],[-72.393585596986512,18.289376626802863]]],"type":"Polygon"},"properties":{"alpha2":"DO"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-8.9390014821814212,26.856491262344449],[-9.1061817216484719,27.019549034879667],[-9.1831290729264019,27.287825504868408],[-9.1757428450461607,28.738658686676516],[-9.1216765066474608,28.920222803445288],[-9.0039444711084933,29.068640677212969],[-7.9533304998305194,29.771234741770201],[-7.6736814171590311,29.867270256908593],[-7.3404776120800586,30.078828391932149],[-7.1559384361891283,30.117948099762931],[-6.8931458277920425,30.100253271053205],[-6.7762930431478692,30.222659265246794],[-6.5931663876531594,30.307087081227181],[-6.0628949255060345,30.331101476040132],[-5.6480951393771024,30.433634320793878],[-5.547219635584864,30.513982466983421],[-5.3442679179898196,30.794634237514167],[-5.2295392852654823,30.891435640004012],[-4.5405941901845646,31.153048241938862],[-4.3040265860338467,31.296405921879103],[-4.3463613919469992,31.61048464028406],[-4.314688537898153,31.795174694172054],[-4.2172493293317874,31.95523464020075],[-3.988446924153318,32.13846778675029],[-3.3873836415764718,32.244668391455065],[-3.2210239311632924,32.449444829148],[-2.9739178042381655,32.561967457286336],[-2.4778911060839888,32.629093955560009],[-1.9331373494904134,32.612073274987935],[-2.1731586557339821,33.242683051873286],[-2.1532885769918395,33.510656648066309],[-2.2122468569404306,33.740642163829236],[-2.2068072242200829,34.055203137824911],[-2.2747355421345552,34.239350969141846],[-2.3169563518186753,34.49591205842345],[-2.5246868218590195,34.660638997005918],[-2.6638175828418658,34.875033177180207],[-2.7113496398058303,35.015149860752821],[-2.7157937634068987,35.163042494849435],[-2.6767607909592446,35.305760428258367],[-2.5976687588986382,35.430806147573904],[-2.4854435924213942,35.527229658523098],[-2.3015683477239026,35.597134095621314],[-2.000818834121517,35.597066716964925],[-1.8970570481602032,35.634012589686343],[-1.6201182557996878,35.783529033712711],[-1.2779043010986131,36.041885618168244],[-0.57992385014081882,36.337076657893995],[-0.25808091750616308,36.353929958494817],[-0.15147076127642994,36.460511208446206],[0.12054785477268254,36.624467977925768],[0.77572474065421027,36.903698712768822],[1.1765906818792906,37.012801136555005],[2.4572074791736438,37.098747445779914],[2.8616529808081403,37.271644754831847],[3.4104654312090421,37.293686677731941],[3.7836954677085699,37.396192102036196],[4.808232669219044,37.393604882108399],[5.3576092962157746,37.184676515948368],[5.9100677727387083,37.34218567167845],[6.1075991791263604,37.494645514776835],[6.4615833816001524,37.584859047488266],[6.6536606155280591,37.55669196110123],[6.8578118895163831,37.451851824571477],[7.0334897547951503,37.561972970673779],[7.2282331253190168,37.591553936116611],[7.5956875789366274,37.531873955661503],[7.9623327331910057,37.373834712087785],[8.6666808314110018,37.428757089258362],[8.8446987549423461,37.358900431705642],[8.9536976394504304,37.265083354211114],[9.0492783993381156,37.099449263389985],[9.0963194500826159,36.90249072147887],[9.0950098650169071,36.756773695303629],[9.0517406137168077,36.617622918725381],[8.9701870152449441,36.496857951509348],[8.8471472102668312,36.398691059400058],[8.7573970006493571,35.850512423570272],[8.8901071043563924,35.266110425806431],[8.885545345209886,35.112363423288564],[8.7651086261485567,34.838164965596043],[8.7314366919035731,34.616751816117649],[8.6515300843834471,34.442479247287025],[8.4558178693078947,34.190700021980064],[8.2052848637598164,34.037645121497981],[8.0410107916232771,33.849218073303888],[8.1405671987963171,33.616293574835332],[8.4422906545314795,33.430879589689539],[8.6078211833878928,33.23049163844609],[8.7703568686183875,32.862169741286451],[9.3657444336191098,32.45482884350082],[9.4921773295764016,32.293554590024122],[10.002682976268199,30.354235342983888],[10.017732531634604,30.202023992710853],[9.9781640336343127,30.034373272109036],[10.090841828614971,29.852754222488599],[10.289334263394551,29.302052913493615],[10.341492084670618,28.995291124866167],[10.318803965957425,28.562765799959216],[10.415076681280476,27.7594855012489],[10.267723298663794,27.264879932914496],[10.387921173617055,26.926938029195604],[10.382157854280633,26.604713281765264],[10.271582229699948,26.269421336473759],[10.067939301578624,26.093010776752028],[10.457769882168927,25.534118756289239],[10.573227539566901,25.038236057293989],[10.77534633710111,25.043127136343248],[11.64612493066492,24.794548859733414],[11.776537746583974,24.735601037874794],[11.884909114197065,24.642123794978559],[12.422820764169066,23.724725627804052],[12.463130971236165,23.584498142981712],[12.461242630153755,23.438604022259359],[12.366385688490151,23.216368347097106],[12.221574352639752,23.087271614472662],[7.7725873044137233,20.463470794209954],[6.0701470261441992,19.037180197586167],[5.9267527281486743,18.985633373104871],[3.4189575098854945,18.497824447300552],[3.2638142671201393,18.492199231736471],[3.0252040099526094,18.58734444584212],[2.7705580218301042,18.779557062632119],[2.6783944653051135,18.891415320660876],[2.6221851234532054,19.025007614335099],[2.6066530179346761,19.169108775811072],[2.6955075568039311,19.45483632009422],[2.2224194595594469,19.598748493856732],[1.9851253263913053,19.777096220042271],[1.6001996151865265,19.857050614660892],[1.459683583080243,19.926717987525805],[1.232923922728296,20.191224390999672],[0.79544156793080156,20.48128043268526],[0.69529523777260616,20.65348714049551],[0.66869598092975557,20.816602767785469],[-4.7835789185856736,24.381356667003164],[-8.9390014821814212,26.856491262344449]]],"type":"Polygon"},"properties":{"alpha2":"DZ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-81.359643516658892,-2.4931956529297805],[-81.430297594499436,-2.3660109964764136],[-81.462336653795703,-2.1752295736340832],[-81.419557572022228,-1.9865658442504195],[-81.303293452415105,-1.8238583540276692],[-81.334720314194684,-1.6388257383388709],[-81.315737164927782,-1.4120644139295209],[-81.397068673395296,-1.1497955249911138],[-81.387589584431382,-0.95927813325203592],[-81.217110091405601,-0.64518584202178397],[-80.97028750072046,-0.47970541863753569],[-80.976917415610401,-0.29702231292295683],[-80.906358036988223,-0.1038532455967604],[-80.542398397968356,0.29369163641285678],[-80.587555448107281,0.8068417259733861],[-80.538224146474661,1.0022620956106039],[-80.375803589192429,1.1935800147042697],[-79.985974473734274,1.4154867436618042],[-79.39473413122488,1.5784431253414468],[-79.190008381555003,1.8301888153448345],[-79.022399744129046,1.9277166939885986],[-78.830289838177976,1.954138777328376],[-78.642575330469455,1.9054807250832928],[-78.365007349633984,1.6801062206011108],[-77.943173600802254,1.4112097343501087],[-77.75872526307063,1.3364043061706525],[-77.575459980365721,1.3210503863869438],[-77.446016986458943,1.2664911761617581],[-77.277354814566365,1.0999565072047872],[-77.12952973505358,1.0045713700616743],[-77.013408776698839,0.8440779005941389],[-76.752477125673011,0.75224198188446378],[-76.398932824590347,0.92516945506691106],[-76.205263253593401,0.93557024748929973],[-76.065400803731407,0.89414973185689561],[-75.807619547601604,0.77167030370062983],[-75.566496025441339,0.56463648435720537],[-75.390645086749814,0.50823856220202557],[-75.016381702264269,0.31516211163240659],[-74.87766066717856,0.18344758849265141],[-74.811937692793066,0.055481384612691462],[-74.785693307011897,-0.13400073432245002],[-74.82840870044167,-0.30950861999046375],[-74.767830853593281,-0.48953683456145758],[-74.756373386856637,-1.0320230550017542],[-74.851878211363797,-1.2543707255450922],[-75.011527642186834,-1.3915696031300566],[-75.158957342117404,-1.8146909301178844],[-75.751257096591928,-2.5006045925509914],[-76.384644430109461,-2.9664306401913052],[-77.592426864525606,-3.4174047818469404],[-77.660088173130873,-3.5036224225030037],[-77.739781137346583,-3.737858212883796],[-77.940004211354591,-3.9140738998162417],[-78.174823114244234,-4.4265629603885781],[-78.19408614684211,-4.650337983411184],[-78.270671228831745,-4.8402264799648318],[-78.439148547046059,-4.9976893269565954],[-78.562545567590561,-5.2084390892905947],[-78.873516725888692,-5.447431456286119],[-79.102929705452397,-5.4896841394717653],[-79.49644223076848,-5.3994317856712257],[-79.695524962875879,-5.2696969615874183],[-79.915047523055492,-4.9619597751849991],[-80.105275867270834,-4.8831928190496638],[-80.30233254730679,-4.9567442766020928],[-80.501832547792901,-4.9491956091795961],[-80.690454173374818,-4.8827118368901372],[-80.84835622242295,-4.7662346563928164],[-80.963552143348082,-4.5507205386121594],[-80.96404912058378,-4.2980187468115201],[-81.009688842522777,-4.0594989236516899],[-80.94545849703502,-3.8040985748069209],[-80.7769583611543,-3.6014120438553525],[-80.824218908209971,-3.3949998391902811],[-80.753249212103995,-3.1317998881669356],[-80.77195353730508,-2.982521012973073],[-80.935689790523625,-2.8448777742919895],[-81.176837959788173,-2.7178689970341399],[-81.359643516658892,-2.4931956529297805]]],[[[-92.129248978957619,-0.46612584417280761],[-92.147285705210152,-0.229680377036454],[-92.090873901400968,-0.069036927328540248],[-92.073294091514697,0.15273852901570398],[-92.008746565251343,0.28502840430338272],[-91.715090376912059,0.55184668387507407],[-91.341709847405781,0.62517502987236895],[-91.107375263086993,0.55621011789428798],[-90.870955655449649,0.33037697747164024],[-90.70220649551284,0.33313044094393623],[-90.402762478372068,0.23399694535639767],[-90.136673289938543,-0.0034866165101045139],[-89.854874842029815,-0.16991891669410392],[-89.733579549253704,-0.33116924309491674],[-89.505296137288653,-0.21657775461273343],[-89.353990954796899,-0.18160190105422669],[-89.199252328935557,-0.19473682577998419],[-89.05084171639983,-0.25420597465528455],[-88.930077964879302,-0.33591311444475463],[-88.838021522785468,-0.44898632896928664],[-88.782500847998094,-0.58380989903417946],[-88.768237405459203,-0.72891846039713604],[-88.83780315230112,-0.9601529433650573],[-89.047663413369222,-1.245580201253901],[-89.16304959857807,-1.3403058057170909],[-89.417177668467914,-1.4376281096380472],[-89.560811324807517,-1.4515156668953939],[-89.747124301093763,-1.4056254501866066],[-89.880197500762122,-1.3157200088093901],[-89.924683552033088,-1.5001261511515152],[-90.038499050460644,-1.6580036351724867],[-90.283736586852243,-1.8098888332291763],[-90.475498408378257,-1.8414733582085123],[-90.619329013119952,-1.8154429844478408],[-90.749485945538467,-1.7489298567069063],[-90.882122350617621,-1.643006816919238],[-90.978593410641253,-1.4951884819372978],[-91.136673822902154,-1.519409084805186],[-91.473258405597633,-1.5062856036455858],[-91.609304041180735,-1.4565728454712585],[-91.801430539053086,-1.3102974449149738],[-91.947179761160157,-1.1112401947692583],[-91.993482186531992,-0.90155934726606657],[-91.986658353636159,-0.77293683182932338],[-92.067416661975358,-0.64698544699695304],[-92.129248978957619,-0.46612584417280761]]]],"type":"MultiPolygon"},"properties":{"alpha2":"EC"},"type":"Feature"}, +{"geometry":{"coordinates":[[[21.476486528571339,57.974724611563595],[21.360260990794085,58.227876738272364],[21.377028346757104,58.616187561599908],[21.434149731724766,58.754730076598435],[21.55985512319192,58.894377050825042],[21.568325941628476,59.048857339861051],[21.61975151736166,59.185813651566477],[21.78501611152852,59.362957516376923],[21.965679945718112,59.435036942378034],[22.284168907386682,59.464889849572508],[22.489717430412608,59.557793526865183],[22.6349414466418,59.585428601087429],[22.78189890982333,59.569390638864775],[23.072869828071955,59.463805097664597],[23.171204204477167,59.576719521903854],[23.297724662047987,59.655047506838173],[23.730859194618176,59.755578301222542],[23.8555630479885,59.831119383897537],[24.273349609794479,59.960780884333126],[25.174749834589296,60.014084140089167],[25.360256657519269,60.115988383405444],[25.516967056925271,60.138715086566592],[26.673334953063726,60.051374365966865],[27.055758564239405,59.948627317641481],[27.764687815705994,59.921513798306165],[27.906914757897798,59.972711292055287],[28.053214593466301,59.982335265826649],[28.240687688574102,59.928902451570892],[28.461265699507951,59.766370957437999],[28.56059368172394,59.660887070180017],[28.638461275181754,59.484668605618545],[28.643921483010892,59.292090061918124],[28.548738868244506,59.071804028306744],[28.445247470777201,58.970401714859669],[28.23645229502435,58.862807080370516],[28.001519554504377,58.604919958387605],[28.025794645053196,58.346765455292164],[28.198666482416993,58.136364934640099],[28.259225586536861,58.001835963958143],[28.274408696761451,57.806275656757776],[28.2353395280226,57.664011644799011],[28.085007826966905,57.471456829354196],[27.816534414877875,57.345569328460812],[27.662425026031094,57.136569230022573],[27.447683439624051,57.03762287632177],[27.305129096682602,57.030510437756824],[26.919944002965845,57.101811234314511],[26.492439426968545,57.043679372535557],[26.353069656696565,57.056797988672784],[26.053880415408891,57.165046582735243],[25.728982090314293,57.385620615055338],[25.340475221060785,57.506190783568876],[25.064879831888877,57.52504240657565],[24.383894102576715,57.374550713521344],[24.188546479782921,57.389185853167653],[24.054002238405268,57.449241616372561],[23.942803590998921,57.545904475822361],[23.864606515515366,57.670780674386499],[23.824685981874364,57.835767748140483],[23.60928613707441,57.88678509605689],[23.457070409893646,57.970029512090434],[23.278467419982817,57.9420218481239],[22.857675624528738,57.747425598855997],[22.599947528212855,57.721863540194462],[22.425055423793196,57.547914886483902],[22.218816750441043,57.457092282159046],[21.900559548254449,57.444514778597672],[21.719203240366728,57.518149483499052],[21.60954418369538,57.616374044941807],[21.533244195632211,57.742276725621096],[21.476486528571339,57.974724611563595]]],"type":"Polygon"},"properties":{"alpha2":"EE"},"type":"Feature"}, +{"geometry":{"coordinates":[[[24.220284778314394,30.072360011740088],[24.20358834731935,30.213098430292536],[24.227052376356159,30.352867928417041],[24.448254247542859,30.742640072909992],[24.35416197696286,31.299998183734015],[24.401022355969598,31.548653629764569],[24.82040500477202,32.030273173780884],[24.947329840880325,32.11155230084043],[25.092708909290369,32.151315814076902],[25.292329754575498,32.134098576520103],[25.459203593520307,32.047320829216602],[25.93628307148175,32.11879286185431],[27.330904557024787,31.871070260313299],[27.700745017752023,31.700748441599131],[27.946742733402335,31.68089966715063],[28.15543177972663,31.583970992169782],[28.65018311772166,31.531566857362037],[29.106950413079794,31.358195343300512],[29.236812999430313,31.394797561790369],[29.712422606279702,31.677560609981995],[29.970177995032049,31.742795169994395],[30.203607748340715,31.919177590707736],[31.02925891074015,32.100172888115836],[31.180353449309393,32.093481394574411],[31.571506789446239,31.976316150445314],[31.815931616034408,32.035798849346321],[32.019407401696725,32.023836689299571],[32.159550800695101,31.961599089404594],[32.440522269261123,31.74029340118468],[32.699495954579341,31.597308981813828],[33.089032617122776,31.666249494647325],[33.352937861071879,31.615926081632889],[33.600663578006944,31.628808863835751],[34.170662250535742,31.821500419097028],[34.314960510770895,31.808396708342826],[34.449442888139814,31.75446911503823],[34.594203515797169,31.627338982121803],[34.665047237718078,31.500947280626747],[35.370998200256608,29.65611096655153],[35.403620189206976,29.457408811632909],[35.355213643467359,29.261951535324407],[35.195440720497132,29.034062478454658],[35.092385626048987,28.603001465248958],[34.937384012764419,28.231400843560767],[34.849209790613763,27.798716844660603],[34.578690829349142,27.416191913247864],[34.340132752816203,27.279867241484485],[34.436621201606769,26.844357996372569],[35.008836214336476,25.92119637191503],[35.605460485480101,24.770457483409285],[35.818661412967309,24.53055298781161],[36.115440832746998,24.311735384878826],[36.232893787066253,24.15669647248162],[36.282299844187321,23.968570710409068],[36.256182432750741,23.775826990263688],[36.158493875643792,23.607633034825355],[36.029769839785004,23.503721103625818],[36.126215794636927,23.233565549719909],[36.174585160923932,23.182628428276676],[36.54289888429075,23.018256857626824],[36.76936786387369,22.756105138225777],[37.157348966608858,22.475011506540469],[37.271760227162552,22.330752616548978],[37.349620944804265,22.142113020310831],[37.371158481504985,21.996973382611028],[37.349637258436175,21.8518313250385],[37.286910629948885,21.719186121591925],[37.106886669462234,21.556000590533024],[36.871194890437934,21.496945284280027],[24.980539715242255,21.496079450921243],[24.835392154613675,21.517599822626671],[24.702742112493155,21.580328660334633],[24.539553237020961,21.760361321955362],[24.480503307356177,21.996063409631113],[24.480275536856421,29.096157007505919],[24.220284778314394,30.072360011740088]]],"type":"Polygon"},"properties":{"alpha2":"EG"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-17.452182652286393,20.503496420381051],[-17.574935120920152,20.705145355902999],[-17.591488763332144,20.940637263780346],[-17.468810088416536,21.601939900840875],[-17.392942290987524,21.733352776662656],[-17.281148673137888,21.835956156853417],[-17.094363556292421,21.912107882843149],[-16.075719186360697,21.998886756985957],[-15.121998250051153,21.951057765770315],[-15.034135521645666,22.156053975927076],[-14.69405201899006,22.542827384637143],[-14.58450542105772,23.228008607334584],[-14.319026812245799,23.94937306186922],[-14.053489258844962,24.202922601457175],[-13.651364061363466,24.383746621069246],[-13.347524988936986,24.821083145430098],[-12.849347734748257,25.160681093884719],[-12.49624668952322,26.236441589036016],[-12.361478864181739,26.39020514403423],[-12.04465429351551,26.550762905117757],[-11.883306896936977,26.773786153304989],[-11.872405956891876,27.022746229077057],[-11.811526597983303,27.155659333389917],[-11.714805266874587,27.265282203940536],[-11.544736825242998,27.359408995731599],[-10.775587656018034,27.519512462036726],[-10.439113206976147,27.4778178767212],[-10.201679390817015,27.386047480023315],[-10.037764896539572,27.410422668144484],[-9.8727842864369038,27.383544681801155],[-9.5988205422549697,27.552386222526017],[-9.3150927167004944,27.597066285946852],[-9.304065175152294,27.77158505949124],[-9.2259102955965666,27.944745084632551],[-9.088801241814366,28.07625054792689],[-8.9125324915038213,28.147115963816926],[-8.5384866335759444,28.134700575709211],[-8.4058923734053028,28.07201566245832],[-8.2971910214615505,27.973554237194048],[-8.2217355186802035,27.847788171502678],[-8.1860182485335375,27.705538697715454],[-8.1824561490891661,25.996075781332564],[-8.2413781259076249,25.760258653381989],[-8.3651222942928154,25.609344809023661],[-8.5372123946366774,25.517298054643113],[-8.6824038108754529,25.495737484088096],[-11.517255758920738,25.495441390792219],[-11.545139802939147,23.322814213894937],[-11.635492998975559,23.15259383695366],[-11.783512872283072,23.029188837597673],[-12.223163440026351,22.841343754229413],[-12.432194467526868,22.795429194760683],[-12.624724114158452,22.668233629300225],[-12.516661513448073,21.31950261153148],[-12.580210962882539,21.089823461932536],[-12.703904774167038,20.943877133213437],[-12.873355342849713,20.855068380323949],[-13.015700840562321,20.834156454410962],[-16.53785756467866,20.828902039114865],[-16.58104860986877,20.628927634986265],[-16.654140595445782,20.499194059876533],[-16.762196886737186,20.396738817069302],[-16.89563365191173,20.330648964852067],[-17.042615989898341,20.306786207544246],[-17.190107602739776,20.327267003617017],[-17.325027023386774,20.390274850736404],[-17.452182652286393,20.503496420381051]]],"type":"Polygon"},"properties":{"alpha2":"EH"},"type":"Feature"}, +{"geometry":{"coordinates":[[[35.930081901414546,15.076628908922988],[35.95730218005297,15.303410065727977],[36.088206499510086,15.516702993972423],[36.401331995671882,16.355157878997751],[36.402575804967086,16.744402554235354],[36.487315552760407,16.947992596644614],[36.538140908052441,17.226435259329186],[36.637427489645987,17.393056098696206],[36.837599455393679,17.528308233202591],[37.107293264974565,17.565127230813953],[37.262016027141229,17.734322639289228],[37.591668254576469,17.919849742016929],[37.968423485421809,18.03647452652671],[38.269647360703651,18.371577768743865],[38.388762441574237,18.4534276762224],[38.574298299504164,18.503507394248768],[38.765019351754233,18.479906272156391],[38.894423813497617,18.415545607725722],[39.028321331285646,18.277694283954066],[39.374544176370961,17.615462663204184],[39.615162215944842,16.893162774705551],[39.703930843053136,16.447736227154699],[39.871983943456769,16.543313977553154],[40.061349158243708,16.585236042516996],[40.252420598551154,16.551944169620221],[40.416448398906589,16.448447672999837],[40.528751857754713,16.290319316172596],[40.581306254296024,16.105122132184871],[40.766026949307765,15.978003261055868],[40.870377540683791,15.819157776844714],[40.905989591989638,15.5850981206149],[40.836857791144837,15.339988859586251],[41.034314278542666,15.195475242249355],[41.381885109267635,15.075908227512462],[41.506284678591555,14.995787674798294],[42.009124398471918,14.353280906816542],[42.566063312954824,13.970731883180049],[42.66432980995188,13.859716930981969],[42.774624430239854,13.651421578626612],[43.046748440846528,13.396956084483413],[43.24721737166918,13.333257122379292],[43.448904802935118,13.164946323431682],[43.528600732140923,13.050747254713908],[43.596493906798429,12.848487212736245],[43.616416146284443,12.705821577825333],[43.594838327510899,12.563396942499669],[43.473020379590956,12.358240564588909],[43.185897702660199,12.19395642820246],[43.0043910964189,11.981110399891618],[42.826174382738813,11.895788935585704],[42.628820926620683,11.886167986336083],[42.407581459840685,11.968125581273819],[42.230725039790286,11.989069682410785],[42.058089333764656,12.082930194663085],[41.805820954854291,12.381103764502351],[41.561163840543038,12.571108946129915],[41.380864005794635,12.849850960113354],[41.017108219920125,13.138739149006],[40.529875221992469,13.684648954531767],[40.301399073246849,13.777904436182112],[40.020101021644635,13.946208999919898],[39.824148919902051,13.946001657200481],[39.52593105956241,14.015737344785425],[39.218028785929548,13.973147292004295],[39.024970169386343,14.03004590686994],[38.399109764241892,13.929904888869327],[38.24940185555139,13.963204711224872],[38.084041935737083,14.069782933224111],[37.91454819826383,13.78594135185271],[37.796054232905796,13.702754528487104],[37.658694165351434,13.657004345434016],[37.350305315686256,13.681880750117845],[37.164884038446608,13.79295525362228],[36.994071496917215,13.773170063887111],[36.792751249237917,13.804112793766375],[36.534139041681875,13.757164269145155],[36.38798895637337,13.776071025748367],[36.17659505364437,13.897969949404377],[36.048070354909363,14.10540218295203],[35.930081901414546,15.076628908922988]]],"type":"Polygon"},"properties":{"alpha2":"ER"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[0.93948028065603184,38.492838082284862],[0.78008056562658967,38.673143819842736],[0.72371576130883897,38.913108371547672],[0.76644838303590257,39.106374630207398],[0.97094433573072236,39.408146640413719],[1.1698736668989882,39.54753465806904],[1.5310782023696199,39.616841235390659],[1.7179500148913096,39.595389583753573],[1.8743278148669567,39.513290148978193],[1.899275104379921,39.777172123755264],[2.002039426421975,39.949799789973405],[2.5892565773904272,40.315014440555004],[3.1107079980217085,40.467967234043599],[3.3100035394503,40.446830051423078],[3.4592119711197626,40.369157378932783],[3.6792719856645886,40.531421076885664],[4.0814502532050456,40.574333928284979],[4.4873186751967404,40.458245211336994],[4.6015362184668822,40.361896019103234],[4.77601946640113,40.106531845336882],[4.8210792964543279,39.924321360618485],[4.7957782679574033,39.738335052029022],[4.6858725986170846,39.545249963080565],[4.5842123068335354,39.437428152133677],[4.4083549198505176,39.348596413884678],[4.2612426402720578,39.330754467841395],[3.8857522997595875,39.43431607990717],[3.5895237423891282,39.024816450805915],[3.2518085446717286,38.834620131764936],[3.1135312873249146,38.803154916416304],[2.9260493981748841,38.823533698435718],[2.5097646208950573,38.978643472602911],[2.3316257696544347,39.007233527115034],[2.1124960012012095,39.141383633802917],[2.0725474990985613,38.528304280985786],[1.9812822777563834,38.356321294159038],[1.7870721399254084,38.211109070886508],[1.5962990769501855,38.172210139955979],[1.2661500037474556,38.191176701812097],[1.059899965101708,38.310349604849108],[0.93948028065603184,38.492838082284862]]],[[[-9.7353804467206366,43.032080443988797],[-9.7139126697016565,43.180723742835738],[-9.6182349015259554,43.41064550657979],[-9.4534822883829559,43.591011450798199],[-9.0236709101372252,43.811162534888894],[-8.6828283629269691,43.84104450382339],[-8.5856366897673659,43.956132488348565],[-8.4135230370324603,44.054403110511906],[-7.8550855235763324,44.239018564960141],[-7.679667507512991,44.263946604334883],[-7.3441359837595312,44.213519720894041],[-7.0347812739788029,44.061022371779707],[-6.1481932674448077,44.094465129113132],[-5.8536170855611136,44.144809291707276],[-4.494775412556641,43.92053794672281],[-3.6146349299324845,44.019195437914604],[-3.1149332170959698,43.905528945163738],[-2.8143759977022196,43.950550922020717],[-2.2682508593839428,43.829839205760322],[-2.0980006785562093,43.838829677493543],[-1.8409308845361727,43.904884809172046],[-1.6970544123052149,43.897553214472559],[-1.5612404092999541,43.849503780239161],[-1.4375648926378639,43.757692453685884],[-1.1629998994989652,43.674147337529355],[-0.96501659433399056,43.494732899500598],[-0.60966489591274353,43.415526038349249],[-0.43029156649486966,43.317383871511396],[-0.18538094706088937,43.311964639138921],[0.09422329726431096,43.195004764787122],[0.32119220195928305,43.192976491130104],[0.50796781448921946,43.308583458974695],[0.78666634850356987,43.337895368867535],[1.5115779489398997,43.16362693749852],[1.7406726049014605,43.001561811878702],[2.0614644989968083,42.90308611336291],[2.2424246877935263,42.918920927661702],[2.5441077973091102,42.867458658898855],[2.9392618098420766,42.966063620839513],[3.3361370068767999,42.91508882924532],[3.4786480362528032,42.853439108300151],[3.6265130470549858,42.709465342447679],[3.7667154952559745,42.484398500314555],[3.8051571160937034,42.252967445454331],[3.7429861638849102,42.046337303465528],[3.7185181778532335,41.775695276013742],[3.6479604476389933,41.644528912265507],[3.5418971426549049,41.539965134561321],[3.253161411775745,41.333646896311947],[2.5764111652038282,41.035562991955693],[2.2945325206619236,40.834827887980644],[1.3556698799241413,40.609271900729318],[1.3080033167062186,40.463195772999946],[1.2191844955542406,40.339565545658481],[1.0972809398740984,40.248391321143728],[0.85438891501821257,40.146467695946683],[0.41481728409892277,39.678190237356809],[0.21699652994120666,39.413010196237181],[0.25422252471698387,39.322797575521342],[0.51650955234460683,39.147314815233131],[0.64322969732050506,38.99303796068223],[0.69948592771281526,38.801479667896778],[0.67630889799077443,38.603181487657793],[0.60854959656697127,38.469157885899172],[0.50404828469917651,38.361300922397952],[-0.14863238499093157,37.958224401052505],[-0.24406296023371227,37.776468087118104],[-0.22578824866048613,37.615241786649229],[-0.25219096409445563,37.467430797554975],[-0.32128775945531057,37.3341235406251],[-0.42684743270442804,37.22734176660515],[-0.70736725502464171,37.094989206162424],[-1.1827485524353665,37.063379172029201],[-1.3214268235571065,36.990682373513486],[-1.3926273180835891,36.921871596150801],[-1.5857181219370708,36.592771255928355],[-1.9138019525123642,36.327383682652716],[-2.049046390477069,36.265308607646979],[-2.2462241162299641,36.249146838408521],[-2.4428120811539116,36.303136934353375],[-2.7907956230036848,36.21502986572699],[-3.1414106884824249,36.252959041170321],[-3.441755013201095,36.208259240657199],[-3.8277387342125153,36.253074317820904],[-4.1938278248167524,36.227956865712578],[-4.5199098009795255,36.031094759972802],[-4.849195720815854,36.000685671728988],[-4.9751851449079698,35.81719217106123],[-5.1262425684617572,35.693691701406472],[-5.480199096113517,35.547751079038896],[-5.6700343029669611,35.528172855297584],[-6.2280285395542734,35.727129686613786],[-6.3596409094107695,35.804324903552853],[-6.6605775136972341,36.220422400055718],[-6.8072294571562493,36.371021266579234],[-6.8858248189293807,36.606515092872513],[-7.0268705836478143,36.689299249988899],[-7.4409971019259915,36.680920663882631],[-7.6250535561667254,36.73024096437495],[-7.7772925298686939,36.844838369557152],[-7.8898740473350131,37.053806668282476],[-7.9907935832181058,37.452141612763199],[-7.9905495837056444,37.605200809903849],[-7.8881623986115175,37.957336626856311],[-7.7395453731532546,38.154288848032458],[-7.8231007177870895,38.318613796058031],[-7.8425748228484666,38.471613539913584],[-7.754852940549732,38.874991779255936],[-7.6771293976428723,39.01911363069059],[-7.9884057937873649,39.449951406436703],[-8.0299339596825181,39.588161376427003],[-8.030258612778832,39.73247519755202],[-7.9670490823452695,39.913661375060009],[-7.8770280159812147,40.02645672947682],[-7.7143729761841087,40.128276244729001],[-7.531924515077546,40.163162213042781],[-7.4797499730285546,40.39113797719012],[-7.3426112919529851,40.567351824182147],[-7.3335303115982908,40.634221357511514],[-7.4175284050484391,40.906464205534917],[-7.4258048616839485,41.057865641462207],[-7.3883183759992797,41.204786225201516],[-7.2966954324745075,41.346221711444784],[-7.4343841057668536,41.33500958107166],[-7.7141700283425907,41.378805826917656],[-8.2186970959207013,41.316623468300676],[-8.406024481500463,41.381286661522672],[-8.5486010386759705,41.507686034913398],[-8.8641299149530166,41.427272862670833],[-9.0067167551640122,41.451578100855762],[-9.1363800126371935,41.51568107478073],[-9.2422685575121832,41.614217198455442],[-9.3155208944544015,41.738940278665503],[-9.3845746281562139,42.056341386628525],[-9.3713904677023603,42.226900472030046],[-9.4764852218561497,42.363351784292135],[-9.5317694961736965,42.560192074564689],[-9.6905239846085287,42.770923926085906],[-9.7353804467206366,43.032080443988797]]],[[[-18.433716919326464,27.34276813791972],[-18.545306092488421,27.442467100966525],[-18.622395969126735,27.570721448727003],[-18.657127946270865,27.816007745831094],[-18.618675721452384,27.96062259018009],[-18.53915262131812,28.087382612839495],[-18.425681379425644,28.184934158511282],[-18.273669894096837,28.252763294312672],[-18.467191229922875,28.578767044953366],[-18.497169099384124,28.815927680768553],[-18.459998978875838,28.955384338113113],[-18.384544565356684,29.078414450755499],[-18.245004484682855,29.231596551746595],[-18.12273966021726,29.305172804151677],[-17.98467079239893,29.341216388309512],[-17.757928943608349,29.344968258814458],[-17.531709586451814,29.269952998709325],[-17.369285629527319,29.116558509032437],[-17.290304079335868,28.994830058185137],[-17.249601865865269,28.85554893658238],[-17.248699559410625,28.702333977288383],[-17.024023691689575,28.841954966765638],[-16.726838869196101,28.888210281937038],[-16.460445723603012,29.037559790117307],[-16.120150646865561,29.075714176640979],[-15.975471230272133,29.053207496749138],[-15.843515089957982,28.989754669712351],[-15.735598090018389,28.89079707601886],[-15.630693521833656,28.654938695557],[-15.375447851715176,28.657466457495783],[-15.184671490430141,28.602590118468189],[-15.030284618787041,28.477803394516936],[-14.941874267561248,28.317552201965849],[-14.826520199379596,28.471790177267447],[-14.611819149768895,28.597159245277407],[-14.430055057084813,28.966865201352654],[-14.30296049035349,29.109511047600211],[-14.209513062299695,29.324965873969536],[-14.026374218418017,29.495490259902105],[-13.654182625252618,29.694551330869739],[-13.414252173268874,29.731670426433606],[-13.271804679888692,29.69697930218685],[-13.145543213667583,29.62246215906195],[-12.963187464191774,29.393281804981285],[-12.925064695689562,29.154959619824204],[-13.013442050946011,28.822173571655377],[-13.154804579992726,28.62534097905796],[-13.343745039824718,28.519996651787018],[-13.385007203623397,28.26427693713546],[-13.510682049739344,27.978553400678447],[-13.684343943222196,27.817150683684307],[-13.953622492611776,27.718898883558531],[-14.143381028982585,27.593480737886448],[-14.374950739745664,27.558050073058656],[-14.626498169560886,27.619435137595232],[-14.758866603695623,27.678426198004832],[-14.89467523340601,27.805829688339852],[-14.955385406879426,27.626523064254901],[-15.050203726603502,27.507435160057415],[-15.340520472741295,27.29768808370417],[-15.523400835725953,27.248523195548547],[-15.762990224905952,27.270327855819392],[-15.899740957525854,27.322288249180644],[-16.131335856430798,27.507074644265284],[-16.238482515286151,27.635984039577657],[-16.393026940646308,27.555240943526218],[-16.647993356396068,27.507552106132728],[-16.790760811940149,27.525422023532286],[-16.944412848134384,27.598366697823966],[-17.182556664237548,27.515960608573135],[-17.449715718254794,27.570473534743169],[-17.650468359841724,27.274969462419115],[-17.77646732592514,27.192196762303055],[-17.921410683853704,27.150742180759082],[-18.169018313249811,27.181851079862241],[-18.433716919326464,27.34276813791972]]]],"type":"MultiPolygon"},"properties":{"alpha2":"ES"},"type":"Feature"}, +{"geometry":{"coordinates":[[[32.738407306784445,7.4729468089109483],[32.592464845455325,7.6087489863145246],[32.524531691974992,7.7424337632660016],[32.500877823046487,7.9403780177698025],[32.556441286744345,8.1318307140377026],[32.83840198780964,8.6692763266599115],[32.961076736422179,8.8210987245988264],[33.180065947287304,8.9266590921488351],[33.577874247315762,8.9391238197009937],[33.582825700658233,9.5713237496025165],[33.683930807879761,10.008874563378852],[33.799117583084211,10.273122576857082],[33.777660538063955,10.569305076821131],[33.825410318552301,10.744888332263011],[34.100701288568324,11.162559541611147],[34.321254611547388,11.312541886100066],[34.488865103774465,11.372500160950898],[34.663265396411141,12.035970012902091],[34.856796103573828,12.269759395737852],[35.273770996448029,12.92798825199657],[35.45192070788665,13.073275876715762],[35.670031887211039,13.14174558518534],[35.738524192714095,13.430138177023489],[35.918315901024066,13.805707346613692],[36.047381359249094,14.406123050204735],[36.10918633182618,14.535024807136189],[36.283868247275123,14.694905450719832],[36.530997740443794,14.783876576842681],[36.829489545293633,14.800744660944369],[37.084318626778817,14.935511364060091],[37.375282668664987,14.938724356555138],[37.542032905334153,15.216463301795869],[37.709039148465557,15.320236179929804],[37.853689734744805,15.351013223208495],[38.094330625180525,15.305685928363566],[38.28946681421715,15.160264291666456],[38.489306497750725,15.071631916943968],[38.618718184945145,14.962196533193183],[38.883331299945098,15.090168552216729],[39.079132413304201,15.127986778778986],[39.227209488963574,15.104007580263325],[39.388646016281243,15.018127628752623],[39.596849911119705,15.032252099579001],[39.956653868594856,14.948215892035067],[40.269581752974048,14.938950105239897],[41.161594417459398,14.476660547028825],[41.696653248630113,13.875454136541379],[42.117897419058224,13.537861638113693],[42.309038163016751,13.256291197064733],[42.524474315727971,13.083745219745085],[42.844013989799798,12.64811549903381],[42.877948619230636,12.450314027302761],[42.831370935630964,12.2551026017046],[42.313038944312787,11.539451682127932],[42.506144476299291,11.577873474832801],[42.666763481205734,11.568515256700689],[43.079843369547817,11.473645925568279],[43.28900594609911,11.339083994483399],[43.392836489266315,11.168469523434492],[43.421604587803863,10.970827230561161],[43.370719994769402,10.777692982905778],[43.241145647464521,10.565905124734448],[43.274265758617041,10.474448752938374],[43.386779276789149,10.338018191393418],[43.566090406570069,10.199283051197689],[43.775072941681074,9.8166559139905001],[43.916334268217952,9.7402262679515079],[44.241949282199428,9.4505860570441076],[47.054318486061071,8.503996270611994],[48.127127970727784,8.4739956096573046],[48.259731557775147,8.4097056730947468],[48.396722423619856,8.2696251637234095],[48.458039396470859,8.1356209246806266],[48.477628861951047,7.9895621690949161],[48.436442604507398,7.7980086814513818],[48.358550988337512,7.6729095073074722],[46.77720125181294,6.144144514670864],[45.194039707826121,4.4813874561634472],[44.967506198515338,4.4129908781732947],[44.053457033406502,4.4484207427225186],[43.738996529498003,4.3767923732723091],[43.425516423281792,4.2293428214737894],[43.204332529724603,3.9671374027617325],[43.09075597511147,3.8826055685401011],[42.311029725993833,3.692924267720243],[42.14581697233114,3.5520733080057827],[41.958473686060309,3.4835564089532682],[41.139521431034822,3.4506860831653587],[40.87200063542241,3.5408658806072357],[40.677369991870307,3.6866889890828634],[40.200530333921563,3.468226920653279],[39.956484642722039,3.1773176066708708],[39.710531676454025,3.0054395565072944],[39.523550019647992,2.9571686458464734],[37.96510932651794,3.1639464687606842],[37.816146186034281,3.228276055795968],[36.73476514350704,3.9223812065076755],[35.943311047452795,3.974573620981332],[35.725779062205241,4.0655867528388798],[35.336222624318516,4.5481606441328832],[35.277008315991701,4.6919970200541226],[35.2665568572246,4.8687544555707447],[35.068284569600877,4.9362422864639672],[34.900109268187613,5.0694072499086928],[34.560926731841938,5.5916290179639176],[34.47079311934813,5.907581361875982],[34.272292186083682,6.3781161720705954],[33.703900775368673,6.8791681236637734],[33.564382309660417,7.1339610490672714],[33.460649684921734,7.2025380171023681],[33.04280755206252,7.2956494944961703],[32.738407306784445,7.4729468089109483]]],"type":"Polygon"},"properties":{"alpha2":"ET"},"type":"Feature"}, +{"geometry":{"coordinates":[[[19.061947147006752,59.982552522536999],[19.021449065363882,60.230608336355097],[19.058232209856858,60.377934862006327],[19.13773916685377,60.524350716221861],[19.275346395145888,60.660453459803811],[19.49881035689782,60.742911903598028],[19.726929373983129,60.878842252598531],[19.866439319569835,60.905097854029414],[20.053202259319619,60.877570617523666],[20.447732190556142,60.737359660974121],[20.654300602419806,60.561493535937011],[20.718416203055618,60.546479040040026],[20.73702391260548,60.751153060874046],[20.875851375757495,60.971612053635063],[20.891242910588318,61.173439820961654],[21.01810171464393,61.43057346550043],[21.023080764091134,61.543870391474123],[20.865682459772433,61.677577821930662],[20.79045119030361,61.808017446466465],[20.75746674698976,61.954940099771555],[20.770642165605757,62.10890974668397],[20.703403186379362,62.224112766488759],[20.612399745092699,62.530817226068123],[20.616816148215253,62.736154076017314],[20.696808807288267,62.962121876731707],[20.609446025874565,63.120925566297565],[20.585647308257421,63.31321115692522],[20.636735159649426,63.500107274496969],[20.719980353488278,63.619707433489801],[20.834174222651374,63.710226605900623],[21.017532963401017,63.772834109555944],[21.550763371650124,63.709144325654854],[21.839861970085622,63.732616671474979],[22.30184349301944,64.091270455006622],[22.615418761709577,64.170795344423212],[23.333664012666187,64.497700149521123],[23.971736557896943,64.916341083748861],[24.077936201709321,65.018939710979524],[24.087253969701909,65.144357499444581],[24.151728595009118,65.304894480392093],[23.979262780484255,65.337623645772908],[23.847747052990304,65.411503663040236],[23.610139953495569,65.71531122258699],[23.38846468495376,65.842355240666507],[23.272640874512781,65.994648941138735],[23.182469790728089,66.454433850067872],[23.206023812352996,66.595353403141146],[23.290411216999438,66.753112468324659],[23.212760374885878,66.881533934774239],[23.157721697611859,67.059183829123072],[23.041203293102662,67.179923936273497],[22.976450423332448,67.315939880152598],[22.958156783681847,67.515363955414145],[22.998173471045114,67.672549927684273],[22.694825034135931,67.889019791490895],[21.858544342823283,68.040811603643249],[20.359609060409888,68.611817538211156],[20.217801219176398,68.743694226031835],[20.127622242401888,68.967403446393746],[20.152667783451552,69.20730089114997],[20.287109438052429,69.407559485560327],[20.453098547995697,69.507301853719994],[20.739352216777515,69.560523657132237],[20.87624001276021,69.667682285599014],[21.018182477281702,69.731006988329426],[21.620465204485594,69.772677696938217],[21.816167810105821,69.722801388292325],[22.64929524002099,69.209778458253624],[23.288093144689768,69.152125195222126],[23.516998919696992,69.190382971870491],[23.714079219812771,69.284244489447403],[23.857135236910629,69.305653680117288],[24.846026119776177,69.124824156672318],[25.014743865021668,69.262771857970179],[25.272423524129934,69.349549755025663],[25.349745326755041,69.557339557101727],[25.693232775574252,70.036631246428584],[26.29640613818453,70.359293859552167],[26.481959371733719,70.412951613557297],[27.071306414937862,70.40937754074487],[27.503988812212061,70.534274107323853],[27.857420963095326,70.560372641275592],[28.052638834434852,70.534192829014955],[28.559985157046924,70.303782789921556],[29.338485444430848,70.130733017492474],[29.464698226448764,70.052659232451845],[29.692530541726057,69.82056409555004],[29.777574065545814,69.701966520630151],[29.824750021517545,69.563864330572514],[29.830039436035459,69.418022608782692],[29.792991697831599,69.276865778219872],[29.683681978420875,69.116541990989461],[29.460906888765454,68.958630302744908],[29.39205924170631,68.761336378465501],[29.227660195104793,68.596855734410795],[29.548999485386009,68.517507593889746],[30.232276689102285,68.119523329351424],[30.380507347271216,67.986421265983552],[30.450786524710232,67.854068580906471],[30.472346376812421,67.606923126680982],[30.360710961783465,67.275925086090353],[30.257596380499471,67.160658629946141],[29.811739496691658,66.863948453739354],[30.007737721774316,66.648275674808446],[30.279494499669088,66.420162876100477],[30.555836341592823,65.960807331188917],[30.593704574005443,65.719486099838292],[30.570161109831652,65.525879482455323],[30.474339643744681,65.356008756205256],[30.322904741254192,65.236906700265649],[30.325784271049951,65.182923532799009],[30.455534348538901,65.09384299824508],[30.543322176198508,64.98193723822483],[30.60945317782005,64.714743686555082],[30.826419384944309,64.58993113407206],[30.970134973759276,64.403720858016058],[31.012804579036793,64.172403452352867],[30.964817996066884,63.828994843739352],[31.446964188903689,63.631362326020657],[31.850738707485668,63.320302600198346],[31.980569425662601,63.121789803207975],[32.033060568036845,62.912203408458296],[31.989574345820387,62.679948156375545],[31.53113244665947,62.11914571707046],[29.92008295886939,61.10227337193917],[27.899306663894556,60.055263507123406],[27.750588148952637,60.005866796387551],[27.434826327811653,59.965877764707557],[27.171644432563657,60.027404471687873],[26.587873899570809,59.915977283754337],[26.240918082400746,59.906701604228815],[25.871348231899127,59.780795837623224],[25.574395489001734,59.794687375448042],[24.813613109709497,59.645567438533064],[24.520058514594609,59.527157513596215],[23.548011458096109,59.479144811681103],[23.039110403171218,59.316534141861695],[22.779044996645236,59.362061092388103],[22.563211003085438,59.528811665460836],[22.269156896425724,59.568453298656081],[22.081090504765911,59.707057032591678],[21.79700173441271,59.610463145399997],[21.403673374378567,59.63209643775798],[21.219571336545297,59.704188507763313],[21.072782892260356,59.850187736527403],[20.913084187062154,59.642116041862941],[20.686070645437589,59.546239848214483],[20.33647633886487,59.536012061271066],[20.146092813694196,59.606701014862807],[19.729840321919138,59.586864317143338],[19.278677917149828,59.716920144487034],[19.161540296056181,59.814668227171772],[19.061947147006752,59.982552522536999]]],"type":"Polygon"},"properties":{"alpha2":"FI"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[176.7286837214923,-17.370789746972736],[176.68439340276339,-17.139627137744093],[176.75741723544297,-16.87478787941301],[176.91440168465985,-16.682483821762077],[177.12256985901541,-16.577495542944035],[177.36715543462054,-16.556080522406109],[177.73853586433185,-16.711307616009567],[177.86626237447595,-16.697933420421045],[177.9683867241022,-16.643177333789087],[178.19677548574086,-16.306675631874935],[178.33902685958924,-16.187005656634852],[179.12289066824297,-15.912948367963761],[179.42616512937263,-15.76517696412753],[179.89688590686302,-15.664878308134757],[180.0,-15.663882256175476],[180.0,-17.503994472100622],[179.98265998891006,-17.511927534234044],[179.86263072797871,-17.615254687730097],[179.81404024023828,-17.766477475727029],[179.85724320025838,-18.130322987895639],[179.77991130741205,-18.354538256488294],[179.5776352966011,-18.546200239363777],[179.22487622251376,-18.616630280508971],[179.07002327679922,-18.702211567119921],[178.99264678579391,-18.855313370530546],[178.98254383324067,-19.081507578084778],[178.92181866631523,-19.262872787662371],[178.83364571397016,-19.376512267905433],[178.6730514673543,-19.480388988575349],[178.02987794902762,-19.647624670610035],[177.75489845374466,-19.57719187411568],[177.59476558528868,-19.462820835765214],[177.47621567901675,-19.248330557304168],[177.46014862067423,-19.10119480492467],[177.48698670266575,-18.891410746160211],[177.4331949066914,-18.74251817320863],[177.3428841701224,-18.650224057224492],[176.97031034510718,-18.432277447316789],[176.80192108424009,-18.157735708989449],[176.76463197545485,-17.967483198089742],[176.80354970716473,-17.552102557053129],[176.7286837214923,-17.370789746972736]]],[[[176.8605745931213,-12.969531421177676],[176.65522888317017,-12.846159466952418],[176.54753424760898,-12.686818551761149],[176.50962533441731,-12.450280153218094],[176.58609634856461,-12.223256622102987],[176.7593933688498,-12.057861209696968],[176.95370959411167,-11.990904725835032],[177.14710965328263,-11.984108982877828],[177.38615046104968,-12.066167964882085],[177.52816778674847,-12.196218559193614],[177.61050302885883,-12.370296087283963],[177.62094353994016,-12.562579923338323],[177.55794069661232,-12.744548930622607],[177.43083960424698,-12.88921195353779],[177.21669652181575,-12.996306704510918],[177.02810588663993,-13.01173719610515],[176.8605745931213,-12.969531421177676]]],[[[174.10432097453258,-21.806080088451978],[174.09398709586799,-21.605148554622087],[174.163690139957,-21.416411287554318],[174.30214371452792,-21.2704288296218],[174.51167774688599,-21.182788289802257],[174.70345791211687,-21.179736566057628],[174.88227623027092,-21.249112021993838],[175.02181855240937,-21.380705658336968],[175.11605596887208,-21.583458486594136],[175.12081416416527,-21.782586378683739],[175.04745787591685,-21.967771185849198],[174.9076286173281,-22.109624406725651],[174.7235170444242,-22.185634186052678],[174.53856783929493,-22.198414573422891],[174.34849126002456,-22.137190863669261],[174.19753365832378,-22.006465001224729],[174.10432097453258,-21.806080088451978]]],[[[-180.0,-15.635394935089865],[-179.92473327361753,-15.627400226541177],[-179.69142080862224,-15.696360836168818],[-179.54582510382872,-15.826591902004978],[-179.47577679260576,-15.955738423211363],[-179.43997044837772,-16.215235097336336],[-179.34934811647398,-16.522589668325089],[-179.25155431809048,-16.620053072987805],[-178.84701445426222,-16.685093407713559],[-178.66552007172461,-16.774752829480075],[-178.48570985713312,-16.964462471215491],[-178.34013861927602,-17.302377285986296],[-178.01647320758315,-17.4629769945624],[-177.85091927387759,-17.63221991409301],[-177.77073049292446,-17.807662423712024],[-177.75816305379544,-18.029865952735779],[-177.85985414155996,-18.2623052612843],[-178.09362383328738,-18.448564943079507],[-178.17300784365312,-18.590142794454582],[-178.18347297415272,-18.726746162137161],[-178.06843076278753,-19.028641035185029],[-178.04889879177978,-19.219592083311607],[-178.12697357178311,-19.445579283606659],[-178.26021185028856,-19.583750777819446],[-178.48321249987359,-19.669986479558432],[-178.67474722018818,-19.657406670891099],[-178.85934052535447,-19.575100770485808],[-179.07783090663966,-19.353716011547853],[-179.22625775031227,-19.283383990028923],[-179.39108562408825,-19.285470258064858],[-179.67594170873264,-19.465876720868298],[-179.94817563388673,-19.490930291892806],[-180.0,-19.468410750971618],[-180.0,-18.46097106052688],[-179.93724062453956,-18.436711118888244],[-179.74220494753644,-18.414031385581033],[-179.62868846743385,-18.314221680807172],[-179.56153058103169,-18.149387031284618],[-179.55922043404394,-17.741864423254338],[-179.61554524054219,-17.576413548308018],[-179.71034879328425,-17.480919619013434],[-180.0,-17.452130976675775],[-180.0,-15.635394935089865]]],[[[-178.74379592263378,-21.168489934770001],[-178.93214375509663,-21.119774875717059],[-179.10273972296079,-20.990687278720777],[-179.20285934341942,-20.8163090012848],[-179.22798717671373,-20.667151093687167],[-179.19098647116004,-20.460007324354521],[-179.08427285494321,-20.296340857263722],[-178.92298404348381,-20.186066528274289],[-178.73174852109213,-20.146023010551257],[-178.5397675759429,-20.182324870499123],[-178.37635632080003,-20.289428885497102],[-178.2510554406401,-20.473098098260486],[-178.21091201778194,-20.661170809780071],[-178.26438267750569,-20.89466804620908],[-178.38237056822504,-21.04652882738942],[-178.5489496335378,-21.142623854402355],[-178.74379592263378,-21.168489934770001]]]],"type":"MultiPolygon"},"properties":{"alpha2":"FJ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-61.486392178576217,-52.204534252601789],[-61.600992075037318,-52.043842858526162],[-61.640851255628071,-51.900838971489783],[-61.625894859546797,-51.704036617298783],[-61.535946155073702,-51.528354828366425],[-61.385020899112014,-51.401168242942646],[-61.062575863685588,-51.288449440792483],[-60.981794192352666,-51.077249536655636],[-60.839825987908313,-50.938309028218008],[-60.355677172928701,-50.787318559481491],[-60.068964694434825,-50.785437607668747],[-59.736185658715556,-50.924846677764869],[-59.425011099382203,-50.861646911564726],[-59.229608397182439,-50.886098236138253],[-59.073795339731504,-50.798634145656351],[-58.864223308663178,-50.770364704470701],[-58.628025389627645,-50.816949928015092],[-58.409658176990106,-50.817784291707881],[-58.168503264594463,-50.898227233615849],[-57.860280478593602,-50.898370108576557],[-57.636916925704391,-50.993351963907514],[-57.454416238699523,-51.165291082657404],[-57.351371724576964,-51.316065316952887],[-57.313673703821479,-51.44809367095506],[-57.293014678613154,-51.666792711605645],[-57.324852236403451,-51.814166599952017],[-57.445664249010001,-52.018474120931906],[-57.556596718220078,-52.122022288610985],[-57.693483054848606,-52.187523178674851],[-57.954163601020078,-52.240932781671717],[-58.066612086190503,-52.438968090798063],[-58.270098205847489,-52.571426329967757],[-58.414226251601384,-52.598344346941197],[-58.787064918056892,-52.585695257956623],[-59.328369550718463,-52.803119086831117],[-59.516981825512794,-52.792764880218584],[-59.66443154844977,-52.734187184705625],[-59.899824882633531,-52.723260233610716],[-60.127301163580888,-52.585062284113178],[-60.432767852241447,-52.688696733933718],[-60.840409168932609,-52.663839456513386],[-61.01994096673581,-52.60218571892861],[-61.260959792240143,-52.457359473956842],[-61.486392178576217,-52.204534252601789]]],"type":"Polygon"},"properties":{"alpha2":"FK"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[162.46657413921454,5.1120129532450367],[162.4225157406531,5.3073773724056892],[162.4586578162677,5.5043599836383663],[162.56920196293569,5.6713581584644022],[162.77053815899421,5.7974126298233379],[162.9632512166375,5.8339577599460002],[163.17967750364409,5.7844100480938963],[163.34397946944335,5.6702833950594203],[163.45039163572034,5.5008830415574428],[163.49220185864516,5.2694532668480507],[163.45093065411058,5.0794928549278469],[163.34033383262593,4.9196284952408798],[163.17712844458293,4.8140241784050373],[162.98598348733148,4.7786423223490342],[162.75214243229811,4.8339922830396969],[162.5816473552847,4.9432545177792795],[162.46657413921454,5.1120129532450367]]],[[[157.69646679021042,6.6543014491426566],[157.62969482477916,6.8719822493518636],[157.68196536921164,7.1542259947790896],[157.80430693650186,7.3185865390227685],[158.03071967048484,7.4518288635454963],[158.23072105599914,7.4746780805710777],[158.53310888849202,7.3892744457763815],[158.6890303400337,7.2566664543721133],[158.80194114828066,7.0688825120069563],[158.83352497619862,6.8736348517016079],[158.76085757482824,6.5898203488628528],[158.64100870362438,6.4360078605223157],[158.45666920330967,6.3323592033095242],[158.26326975751692,6.2921173373947745],[157.99540641621567,6.3390407329484235],[157.8306166097716,6.4483869135809577],[157.69646679021042,6.6543014491426566]]],[[[151.26739772161673,6.9478690325775485],[151.13404081419401,7.1026758842628936],[151.07104503788017,7.3426383224318723],[151.13114791123169,7.583341633959316],[151.27008955828703,7.7595577339492827],[151.42141126905031,7.8525600587163549],[151.85440263820203,7.9653863557205247],[152.09977218531608,7.9217947069277681],[152.30122531882907,7.7703614411837041],[152.39232782094214,7.5908325241198726],[152.40753829314849,7.3923577676586607],[152.34596196896439,7.2068788411446825],[152.21798988732169,7.0591723003252547],[152.10043577842757,6.9837601352904919],[151.72148524016862,6.8408116692679659],[151.45979023568688,6.8534968324255665],[151.26739772161673,6.9478690325775485]]],[[[137.56785458441169,9.3765411856419032],[137.58310732425073,9.5857640194310605],[137.69773456896789,9.8206943911942925],[137.82690872164258,9.9664331855326047],[138.02107482794401,10.066879013021829],[138.23629955809278,10.089646236340327],[138.42579969031391,10.030658524541899],[138.57742240076561,9.9025944627200388],[138.69188335845595,9.6890305784486443],[138.70875502759452,9.4870648704431009],[138.64407515465714,9.294993752191278],[138.49507058655036,9.1182148504416585],[138.20253339312865,8.9393304379955119],[138.00409906230416,8.9248642945092609],[137.77381633431958,9.0162810180240491],[137.63933206074739,9.1629078679489275],[137.56785458441169,9.3765411856419032]]]],"type":"MultiPolygon"},"properties":{"alpha2":"FM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-7.8386519933204317,61.863365484484142],[-7.9200327259736447,62.093709863468668],[-7.9118838987314035,62.241021316449569],[-7.861097827394242,62.379541504591607],[-7.6952397804184338,62.558908348880927],[-7.2914909398956684,62.77090335782497],[-6.9776897453211184,62.815399724400109],[-6.7997191109015889,62.790364960730848],[-6.5812356621097416,62.854610999301876],[-6.4314719297348741,62.839944835738329],[-6.0938876831945832,62.648853922672203],[-5.9935828270811582,62.54065516588431],[-5.9292227183125288,62.407892929048408],[-5.9064113293233786,62.262127104306238],[-5.9271348965812605,62.116049834032971],[-5.9895889747257902,61.98238037883285],[-6.1237704468113607,61.811791102521568],[-6.1970303339481045,61.542278283208809],[-6.1838853896971209,61.353116770683648],[-6.2239879094761399,61.209729377368426],[-6.3773041375038897,61.016699685293908],[-6.5554369251560001,60.930381901312408],[-6.82860393705585,60.934016136019679],[-7.1683339684457197,61.120580085222485],[-7.357919334538261,61.345960017163847],[-7.4304042837484436,61.57411827412524],[-7.6623108753392239,61.663033448516416],[-7.8386519933204317,61.863365484484142]]],"type":"Polygon"},"properties":{"alpha2":"FO"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[8.1042936422981384,42.034432623571959],[8.0703166265761901,42.169922706036409],[8.0676229507549788,42.399202275859253],[8.1421742219159992,42.623112199286432],[8.4296022236311252,42.961300260764141],[8.878807709181725,43.160247822535744],[8.9591751478957491,43.311523108635058],[9.0632912730680317,43.417122618313307],[9.2412343669539201,43.50202106772786],[9.4381634456914014,43.511530501059291],[9.7246285336405336,43.407035130160104],[9.8710014515042435,43.270018563943744],[9.9599329959806173,43.036733950834076],[10.047312835975177,42.065704116699123],[9.8900212305441499,41.753554241981448],[9.8213688550660603,41.455727354911993],[9.5783864985867169,41.075224728661034],[9.4696862865944009,40.973429431358639],[9.2876230204056487,40.895632607505455],[9.0896373801688668,40.894600639970676],[8.4907741089004443,41.201807934445995],[8.3933156238822129,41.308779000392938],[8.1596229880976043,41.740811763356248],[8.1042936422981384,42.034432623571959]]],[[[-5.1831819179533705,48.180378816399909],[-5.2611361058822084,48.416799947561195],[-5.234357205647763,48.614911791841998],[-5.1197283207200552,48.840777083565065],[-4.9150840707259915,49.000288569165576],[-4.6223720461386586,49.111291903351308],[-4.1077167715248333,49.204959905905739],[-3.8176734066458251,49.210314675828918],[-3.575120770517517,49.301872829255686],[-3.2407947058478648,49.340167042036938],[-2.8467043745791183,49.265260063715573],[-2.6104001223010576,49.120492709542233],[-2.1745355043048962,49.143996870428836],[-2.3336164760729954,49.40907421925705],[-2.3640225725826385,49.65418522418895],[-2.2919327958111406,49.928955497655004],[-2.2035273002523263,50.043324921854719],[-2.0860985701587662,50.127624329832393],[-1.9015931857719284,50.181508608918314],[-1.156688862575727,50.169434621121326],[-0.93465637316826133,50.060607658455183],[-0.79520682314404445,49.866139948757919],[-0.30372081156434561,49.815780180749996],[-0.21609906865431566,49.998830192458406],[-0.032039301586863217,50.152367447613301],[0.44184114049572243,50.331272594607732],[1.0565030188510629,50.471436409479999],[1.1140093507326601,50.921148861781383],[1.2790799494662248,51.193441240105614],[1.4273122249576184,51.320573772799278],[1.8269008213896174,51.482933557637267],[2.4895132942385412,51.595554768577045],[2.6897419098614863,51.568783648070784],[2.8630354931422368,51.464968786893984],[2.9966296430276729,51.266373763239798],[3.1767052760544923,51.27426264032497],[3.3172443223003389,51.232723005735501],[3.4397029444913243,51.152220517496907],[3.5971410883896504,50.984761265795228],[3.7770543234605847,50.942932632588615],[3.9501640346489073,50.834342614345189],[4.2469838634234316,50.778111755839078],[4.5506847415658713,50.574724606061423],[4.6857241727582011,50.634929644928576],[4.8255583151226933,50.652872222319431],[5.0947755538103587,50.577351543267774],[5.2738382181393106,50.416739857343266],[5.3576152023869614,50.184147527899931],[5.6364962876410516,50.035440640902145],[5.8752148119547805,50.030683121094398],[6.0573362079677597,49.971397587094536],[6.3124522175524893,49.98915671353172],[6.5806093747987084,49.92614858706316],[6.717066586604326,49.870456131649462],[6.9149564112881565,49.705453435408906],[7.231311558104129,49.626692392081793],[7.526382785168261,49.646158710700519],[7.7700811690401874,49.543652268863347],[8.2662244999507823,49.455767761385388],[8.4365800184763096,49.371930032940952],[8.5369099635070782,49.270341137781131],[8.6283854972686598,49.052224879717954],[8.6257266553606211,48.767493352619162],[8.5074139453175697,48.547289366917305],[8.2367203128631363,48.304520890929766],[8.0913799015630889,47.985110774963808],[8.0626451244035557,47.816387348316518],[8.1141979554589163,47.561355286505808],[8.0388286459089571,47.327219137139508],[7.942099011399379,47.214466485839601],[7.669653790520826,47.02220750293862],[7.2939843523890717,46.928098917668187],[7.1214760676831643,46.767151936449011],[7.2390072468766604,46.594885354281026],[7.2917694958249131,46.371033543886433],[7.4893113825431268,46.100517136406502],[7.5206641187203083,45.913654592309719],[7.4942929734296078,45.766115191190579],[7.6055562941805626,45.613791026329842],[7.6477174762182738,45.474449526889501],[7.6246598298704384,45.234454617580539],[7.4808851782202712,44.944313709055649],[7.5264681190106542,44.658724533721362],[7.7994610768062147,44.637524863167897],[8.0032556491686382,44.505082867248831],[8.1595095298591787,44.213745963729728],[8.1767492034477716,44.072645989193283],[8.1535850156441434,43.93239681963059],[7.982912148827646,43.667832998583691],[7.9010389012823419,43.478480367428261],[7.7938177611102066,43.368004568712365],[7.4006344761736562,43.205292008521511],[7.0613304768959324,42.991148998401648],[6.7887910639171851,42.749573424452372],[6.2069459343673543,42.58095114543211],[5.7170583313004029,42.606817115285139],[5.4892427328140716,42.702807052164317],[5.2076275729100665,42.770031896868687],[5.0426731251421968,42.883553280918981],[4.9560158019111258,42.905062146856601],[4.6433614630158004,42.878232152664118],[4.0035228166448391,43.031836022444914],[3.5799444395679041,42.780410994604736],[3.6741294421469703,42.619994905358531],[3.7110656910393418,42.429642144599264],[3.672805475253516,42.239551071955297],[3.565102895562422,42.078310213799561],[3.3583083776828309,41.953553237122009],[3.2141695163778561,41.931391778479188],[2.9975609153813476,41.948850103754424],[2.7147129771714433,41.844733253654184],[2.2440121502802022,41.904601044899877],[2.0001600356370934,41.858893092043722],[1.8537709705970216,41.876682869438469],[1.464702779101442,42.097552508452551],[1.2671733989016485,42.122895437556991],[1.074682274429958,42.241763723632637],[0.91972362168735289,42.281910013438434],[0.67874131765586576,42.192068134182605],[-0.087426571300116968,42.191486496282799],[-0.39674489011684111,42.308597874450506],[-0.71693885774987742,42.316584509198599],[-1.0005856566771689,42.473173618701608],[-1.3072851307998872,42.545181683515565],[-1.5769131019855633,42.559517045909836],[-1.743911108354693,42.648927657046769],[-1.8904776551620199,42.838666504258597],[-2.1170132192926001,42.981966344187377],[-2.2420132357255449,43.185549322599826],[-2.2925790782206175,43.37237643399687],[-2.2684034791586969,43.56440980190515],[-2.2029863361293986,43.694447122246665],[-2.0632077312720263,43.828324736523008],[-1.9072247612505746,43.894682054631595],[-1.8339861048113024,44.137920704783852],[-1.6832837177694324,45.234633153336055],[-1.623425276135636,45.469836599129273],[-1.8281096173737497,45.771705387906927],[-1.8857981220493707,46.005399656806723],[-2.1287281638359548,46.1506482431992],[-2.4657139367940468,46.531011384291411],[-2.5437211567592408,46.650868121094263],[-2.5891193071937786,46.818473233637434],[-2.7967812213965888,46.907716235091897],[-2.943434974184918,47.073699052844979],[-3.302138300054684,47.199948567467281],[-3.9318340269548702,47.33298945761053],[-4.4510222702658311,47.342814849107661],[-4.5901582384023119,47.407607850032328],[-4.7240266472792181,47.529149482242183],[-4.9634810843115966,47.628848466468263],[-5.0709983534353205,47.729999515485154],[-5.1442931561329885,47.858137604180712],[-5.1831819179533705,48.180378816399909]]]],"type":"MultiPolygon"},"properties":{"alpha2":"FR"},"type":"Feature"}, +{"geometry":{"coordinates":[[[8.2572732035672711,-0.81690669294974072],[8.2111669990605272,-0.67835464133924317],[8.2070565987701425,-0.53239040699073059],[8.2452925779657669,-0.39146325206859867],[8.3226137971549505,-0.26759282717793714],[8.4324255440586633,-0.17134401761932855],[8.5653619953843965,-0.11092586630355517],[8.8202095372573588,-0.10574074299750963],[8.8021510492064117,0.28237379620672787],[8.8440173011715792,0.48767108357053301],[8.8417967515588423,0.71775056289444317],[8.9181528998791393,0.89390151962431896],[9.0911903259991913,1.0495350321756769],[9.1233720272738665,1.2087613235011605],[9.1976429652583445,1.3404408572439133],[9.3509486286235362,1.4703918500298814],[9.6536986165291445,1.5771654080710602],[9.8018018896175043,1.570223838117266],[10.019155640481696,1.4904684677542486],[10.837630914739657,1.4977634760137137],[10.872511836545112,2.452005913474931],[11.001692390021836,2.6594766589545804],[11.213777210675026,2.780933459813725],[11.360224471786418,2.7993422399280155],[13.2317957001067,2.7561117224582508],[13.425292773702955,2.7122683706830055],[13.638952771319595,2.5586700930065369],[13.742946408961471,2.3866329616951165],[13.788639637565034,2.1149059010756708],[13.752334833693332,1.9081320783810367],[13.870516011136486,1.9181578366277223],[14.253607717422712,1.8646866952231107],[14.469024175001271,1.7785637055407331],[14.598521602044094,1.6447573599323704],[14.911445369599527,1.0348210930259683],[14.929202116237855,0.74048312738469069],[14.865080264839307,0.55776327550071003],[14.66810897459481,0.26161027738621734],[14.406757059471991,0.10381493534786561],[14.759476059413611,-0.16101942802233582],[14.923161907590647,-0.38170277993978063],[14.977321667601419,-0.56339703422922294],[14.917929855281347,-0.98622304898032853],[14.955358510524349,-1.4171228448835045],[14.911317993492297,-1.8188253958905423],[14.838824972257949,-2.0971199814207111],[14.720213903949375,-2.2627271967094895],[14.682562051533859,-2.4833027031004109],[14.619143614559393,-2.626002376296205],[14.357714028895625,-2.8872112266291072],[14.069850150527413,-2.9845800858030311],[13.879020895354529,-2.9770272247407004],[13.601476368151863,-2.8755146797605544],[13.470861521384506,-2.895162906949639],[13.048709360086757,-2.8561101951883501],[12.732162903395009,-2.7396363761300067],[12.598334454079714,-2.8060751853572663],[12.249434948168593,-2.883874901000218],[12.257330946014127,-2.937678691797351],[12.396692154839329,-3.1293469295463816],[12.433915079607489,-3.3206775336183947],[12.372423874667701,-3.5660092599205324],[12.36945073584717,-3.7663928443813557],[12.292536407632189,-3.9478473289255533],[12.191699707830775,-4.0565043226087196],[12.016484585389854,-4.1467329473016399],[11.767308232445828,-4.1915534489853563],[11.563204612524366,-4.1647927697986526],[11.425177336318075,-4.3195641467788999],[11.248996230892866,-4.4015833990237052],[11.006949494639148,-4.4005286209788856],[10.793760130856541,-4.2859073740053502],[10.558514681297767,-3.9842618765343216],[10.277444708632986,-3.7412699666060298],[9.9900342807537879,-3.3665768761472545],[9.2544617106176954,-2.7028739135545026],[9.1088790556222889,-2.4409476996684334],[8.8628465380481067,-2.1471399915150813],[8.7673886563747612,-1.8712755810690966],[8.6049031400047866,-1.6007514180663271],[8.5444275551967284,-1.3853151739158218],[8.2572732035672711,-0.81690669294974072]]],"type":"Polygon"},"properties":{"alpha2":"GA"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-8.4618641677579287,54.067082761868029],[-8.5624705748737444,54.179014777871608],[-8.6366175468823236,54.364841524950961],[-8.6319738910057282,54.564860998623487],[-8.5492831374123472,54.747046598104603],[-8.3619389605922585,54.906135221270929],[-8.2007019401366286,55.104664370231667],[-8.06444920876827,55.174935451802781],[-7.8436975694055144,55.226910608391016],[-7.6456384913710211,55.448231420188925],[-7.406195161339963,55.555219969304744],[-6.9920659635713571,55.680731874092601],[-6.9622283806738068,55.873980273417565],[-6.8525592474036321,56.121023190682315],[-7.0702599533960804,56.29019837846765],[-7.1628959497422615,56.535298560823712],[-7.3602965148327337,56.46891130801599],[-7.5824377658096989,56.466216817192539],[-7.7233418978398998,56.499410270396879],[-7.9186522261913783,56.638475043535315],[-8.0267682223336543,56.852475199766495],[-8.0228296355197433,57.092203427188124],[-7.9114581717658101,57.31061966632388],[-8.0086071943725745,57.533946356514313],[-7.9903864085317338,57.771796791867132],[-7.9262109205368878,57.900727216599975],[-7.8279478580921964,58.006016903845023],[-7.57999485626401,58.112629231697781],[-7.5550138448801691,58.352689504118686],[-7.4167465033025266,58.556144099127089],[-7.292164307301479,58.63709630319125],[-7.0745309810355383,58.697769861000346],[-6.9111384187795704,58.79190314919439],[-6.3602432586735258,58.987248995255968],[-6.2163725967235148,59.002099794431338],[-6.0742705630289242,58.975149365382016],[-5.9079576423375322,58.878573286249726],[-5.7904010751814781,58.726364941642892],[-5.7309442789875513,58.546946302466864],[-5.6487129220282135,58.630337112786719],[-5.4994395253141981,58.712145115920045],[-5.3512576544490429,58.937795114601002],[-5.1743770799372779,59.040759200558824],[-4.9399768530096448,59.087328263690665],[-4.6628402137013243,59.041630595398338],[-4.5046250058647468,59.068028063926015],[-4.3096998242779749,59.035876825681846],[-3.8624604570901697,59.084251878293635],[-3.7572894536990802,59.354483997871945],[-3.6690241428974568,59.478825219230686],[-3.229105999409934,59.797331916732197],[-2.9704290800649988,59.846854081295241],[-2.6751909128648195,59.759823424314611],[-2.3447162991499311,59.793418409060479],[-2.1623231114191959,59.733167991697208],[-1.9878994368171214,59.569385152489254],[-1.9163024287659969,59.391140219430397],[-1.9171907636114731,59.19905534604306],[-1.9657578613076891,59.063005251973351],[-2.0511929322522691,58.946518172313397],[-2.2865628174449606,58.805043211080694],[-2.3586928226784774,58.661280299319522],[-2.594930298279027,58.391704722720846],[-2.676618715041255,58.184426656861753],[-1.9989980166027435,58.196529256334905],[-1.6594930671030157,58.064235269022298],[-1.5351466623511123,57.984558437574805],[-1.4395002084098525,57.872032077584365],[-1.3123849628169215,57.648845375132161],[-1.2817055852779997,57.503132946679258],[-1.3095717981639623,57.307136454661588],[-1.3796484023416129,57.175749219517733],[-1.6252315148977277,56.941750181740218],[-1.8585286027542449,56.566004195598047],[-2.0324418241270874,56.389857164881413],[-1.8669491460459531,56.316895936118271],[-1.2702010067062686,55.888718968266602],[-0.88895773952517754,55.111115216939027],[-0.44913410642613116,54.951474301223364],[0.26565171773657464,54.474686673856461],[0.3919363789101184,54.268912582211364],[0.40868987804856216,54.040078260928944],[0.53281829875049969,53.883989295570707],[0.60433767192729249,53.70943237817945],[0.76827608114165269,53.455599518392098],[1.3841301167582487,53.411216925997422],[1.9920251097423427,53.123310456642798],[2.1886023028220136,52.839809973444879],[2.2415892574960319,52.400063387011834],[2.0270354115958424,51.874782920667535],[1.9413594383319945,51.762945516892927],[1.8117225755737223,51.666023612827914],[1.8912223137796473,51.514535291061499],[1.9146995984240194,51.36572952326442],[1.887861348550544,51.085133266781298],[1.8375012301939089,50.94491201187315],[1.6705193395397111,50.763350820206618],[1.3642468448710579,50.630870569064228],[1.2399513453152329,50.511814882086469],[1.105903430714914,50.447859992391095],[0.8508684942433683,50.426326077428733],[0.35790672089165232,50.279687188505086],[-0.22206923791433586,50.313069834915474],[-0.7845399140098962,50.280890838809192],[-1.048357148647012,50.132140780710621],[-1.1983624297605657,50.091850770984806],[-1.3534939378053827,50.099537412475293],[-1.7156762242637884,50.212492505359123],[-1.950193382149682,50.110821390232836],[-2.2768101485492358,50.125970866671111],[-2.446268676871945,50.100048145427365],[-2.9616952107332861,50.211381426947327],[-3.0896824225292621,50.17963339496351],[-3.259076692736234,49.942377191300118],[-3.4384715456678308,49.802337217699751],[-3.5822431121972356,49.749819478371712],[-3.8490390436482973,49.732600600998943],[-4.2697033251262795,49.859725856612251],[-4.5897482941419758,49.804166836692467],[-4.7072390396476882,49.749090891437042],[-4.8564869503241281,49.612782238411199],[-4.9920930928674352,49.554826596774632],[-5.2506366641728874,49.52230677417861],[-5.4610808027531954,49.576411493093318],[-5.6830188700633535,49.554698681803686],[-5.9043455932770694,49.638263278481503],[-6.0378891185056771,49.77329224440463],[-6.1465344059416953,50.034788566458595],[-6.1450694970520106,50.235904750038927],[-6.064478551400029,50.420173479710812],[-5.9595199883050221,50.529139157023074],[-5.7966610286660485,50.641845588372995],[-5.5279687561215258,50.724756716002659],[-5.0225987355821209,51.079854635385843],[-4.9985452497525751,51.136440158871785],[-5.3362697672481936,51.253137783443378],[-5.486640076669981,51.361261467551905],[-5.7069382428112005,51.65220779586533],[-5.7619038440519823,51.886580368187701],[-5.7221332729552659,52.075715942265091],[-5.6136032104506493,52.23563838984419],[-5.3209836270746971,52.4379549508566],[-5.0891084325324121,52.515990202879337],[-5.1622017134504645,52.664349303293186],[-5.1803738557708714,52.855691967283583],[-5.1250212438685736,53.03975263710484],[-5.0291677925776845,53.173388631073259],[-5.067016627677547,53.410745744458715],[-5.0180243568232274,53.603403280781308],[-4.8586805322191768,53.792854112729358],[-4.6279881226051218,53.882648791367444],[-4.2361298884833518,53.910727548848861],[-3.9256879898261072,53.77898629583261],[-3.5707969936915425,53.817586998799683],[-3.9273820369978245,54.118819324985232],[-4.0323434444098822,54.280497259926172],[-4.2898496765463685,54.304791574262488],[-4.4707479137621036,54.260785769028125],[-4.6364727044873941,54.272584508199316],[-4.8460842679939464,54.194046905102979],[-5.0598807213769872,54.212930556771177],[-5.2050504397791686,53.975280004927988],[-5.3076667922849259,53.872200416916812],[-5.575671070368359,53.76444503021677],[-5.7960329189715392,53.604043909834992],[-5.9932227846972221,53.552201236454316],[-6.3006877886552406,53.589165700052511],[-6.7451167832411638,53.568058357392921],[-6.9202537711359788,53.638405496116796],[-7.0313904316596938,53.736324798013243],[-7.163205924401816,53.659801739828531],[-7.3026767906793184,53.624241269988012],[-7.693032028618016,53.651455126263365],[-8.0870967074126572,53.772762352532609],[-8.4618641677579287,54.067082761868029]]],[[[-2.1247119729756099,60.099558399359942],[-2.1588349435208847,60.246925039531753],[-2.1473263404942449,60.397752277823564],[-2.0066810136607782,60.726371125469548],[-1.8873530816119399,60.888568487628831],[-1.6673131518032307,61.029670770769471],[-1.371273583753748,61.106502752860109],[-1.1095552764478422,61.271086274718407],[-0.92148220239570222,61.310171960204698],[-0.58851820083961504,61.275970842116685],[-0.42505245121649637,61.169462191960051],[-0.33628394876524598,61.052617087077003],[-0.28525877963234081,60.915034175065642],[-0.27637176726260299,60.768563541238557],[-0.36156626260068619,60.498138564408798],[-0.46977941999297629,60.332965192933798],[-0.59911788739179483,60.238931136500241],[-0.79095318052724028,59.718574593603073],[-0.93973227339538234,59.524429185321544],[-1.0604039336706699,59.439831226024467],[-1.2987088601539889,59.387289743741299],[-1.6079134023665973,59.479577448094069],[-1.7582844411613776,59.614883065843358],[-1.8363313829667736,59.776230369958945],[-2.0356010754690077,59.92961613832329],[-2.1247119729756099,60.099558399359942]]]],"type":"MultiPolygon"},"properties":{"alpha2":"GB"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-62.237444443037383,12.214359398214542],[-62.122119215000303,12.475007697047852],[-61.94616551356242,12.647010710898494],[-61.711604308965882,12.734105010388374],[-61.480743344054879,12.706850729311583],[-61.272673654404727,12.594667051142752],[-61.139391184353443,12.399439090605263],[-61.107388142785126,12.212399400753837],[-61.145568293474291,11.920495445255565],[-61.237550370647391,11.741022679062278],[-61.397319273511108,11.610168332240795],[-61.637945425376941,11.518924882927397],[-61.896314672213187,11.521999095916318],[-62.111962389432897,11.633260996563447],[-62.230380013786053,11.787973782018749],[-62.280677486342391,11.976199561324425],[-62.237444443037383,12.214359398214542]]],"type":"Polygon"},"properties":{"alpha2":"GD"},"type":"Feature"}, +{"geometry":{"coordinates":[[[39.732444118312081,42.984746532031025],[39.58728488637226,43.108764559473428],[39.499192977321727,43.278150182558427],[39.481012781274693,43.46820584340481],[39.51525518109117,43.607641791131527],[39.587711054563584,43.731597718230589],[39.794369648645137,43.959980441651048],[39.917329024436597,44.024063105117115],[40.132782688438937,44.069233837667731],[40.776061417026838,44.016955413917053],[41.195114854744013,43.86515917373405],[41.527818235936792,43.803533236320028],[41.72687306009837,43.712056068046323],[42.033653808884651,43.693075403405992],[42.417113610185787,43.724350852640931],[42.661403491671415,43.66388290766367],[42.881188792615163,43.654637340123735],[43.194317999208714,43.548303325432457],[43.368565170293557,43.42167183429779],[44.076783344968838,43.150821955400772],[44.480537278809038,43.247813715405485],[45.006450822318897,43.237448128924186],[45.344593159802017,43.139514747203982],[45.533118408437602,43.027521233478986],[45.7371969034213,43.010757072091515],[45.910249769073801,42.947715447450392],[46.126729696343681,42.775702635482212],[46.196592492665523,42.647422349841875],[46.226498061281603,42.494663851585059],[46.419828826284125,42.444745325981316],[46.762337024888971,42.264113534265157],[46.8579977330416,42.14871948944311],[46.915158238952479,42.010157470012579],[46.908849020341506,41.750082857102356],[47.027859059821601,41.63105243123109],[47.136200160022256,41.404860613509655],[47.148470045550482,41.128949564156997],[47.056904557195402,40.909314004672517],[46.93386353277036,40.765464721969323],[46.644895318307519,40.606732587131944],[46.451473612292851,40.57049498591789],[46.305384211789033,40.594286157087417],[46.114990506330606,40.674148552932941],[45.959981023030224,40.672610164266274],[45.633357313584227,40.750778752188388],[45.360659533112731,40.913833533265702],[44.934754857077948,40.720419235223368],[44.240009925408302,40.71076148520013],[43.4556602596207,40.607635810205359],[43.304692108685124,40.625906356386608],[42.981082689284825,40.7526720652126],[42.689345292575837,40.993062441669615],[42.509497760560059,40.941945430294901],[42.0500809238751,40.984269589823064],[41.850378452848105,40.933316094516911],[41.69301045310543,40.949918903418563],[41.249317862848685,41.092339121385095],[41.137377107482742,41.188074521054986],[41.058188873452735,41.312272842513288],[41.014943058290442,41.503275719356481],[41.032910173576212,41.649471417754754],[41.09269586836519,41.784088170651451],[41.223541155136722,41.934969497306859],[41.075131583184081,42.309030498841295],[40.842452713188131,42.418488389635165],[40.660598969677736,42.58495776855225],[40.262198700696032,42.687578592406496],[39.732444118312081,42.984746532031025]]],"type":"Polygon"},"properties":{"alpha2":"GE"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-54.890118082158679,1.9087365595128105],[-54.9991593923517,2.0057533251118311],[-55.07553854708928,2.1301256888325466],[-55.112747351878696,2.2712560008921741],[-55.107615280164069,2.4171186828084914],[-55.007783384066862,2.6372480676413392],[-54.770483838470646,2.8283283938361459],[-54.682430741977981,2.9781631299902998],[-54.6918080229688,3.2428436762221957],[-54.563656096406127,3.5208455132242493],[-54.767002891639699,3.7775066274517961],[-54.837698098762061,3.9416924636122563],[-54.943865107501949,4.409738584059351],[-54.966999496272372,4.9927245302056296],[-54.753382434890227,5.4563670606526395],[-54.494540053129839,5.7278998769882499],[-54.347809049856103,6.0272719623130167],[-54.258741236800567,6.1363837307019216],[-54.055003084779287,6.2501713606445444],[-53.743917835996918,6.2711920854970948],[-53.268493935079306,6.0360803826659746],[-52.689642868887745,5.878639895295664],[-51.791798686623963,5.1390591206485245],[-51.629962059033765,5.0947303657849048],[-51.469141793204109,4.9839961813475711],[-51.383349748208921,4.8648050423789195],[-51.185289997842553,4.3661675651414527],[-51.152929437306106,4.0502839080158441],[-51.179060957811885,3.9015015386425485],[-51.248434123735713,3.7673135598591223],[-51.41806469456801,3.6129275511853578],[-51.877329588331875,2.9460910297649541],[-52.116859430646173,2.3411657483910715],[-52.381249595162913,1.97931201927447],[-52.724532393643734,1.7453418764868189],[-52.860438056223281,1.6948092649001461],[-53.269909173639135,1.7067699436022505],[-53.706792367158329,1.782999765662483],[-53.933853169176331,1.6614174408670204],[-54.123375364852762,1.621337509257851],[-54.621514349095307,1.7445660756891379],[-54.890118082158679,1.9087365595128105]]],"type":"Polygon"},"properties":{"alpha2":"GF"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-3.1170497401667503,49.300898768765173],[-3.1393043883122997,49.548562197415926],[-3.0684520723087592,49.735256562594927],[-2.9301910595073872,49.879335461160245],[-2.6936027513226706,49.982892714505851],[-2.4884441747629942,50.004979142676596],[-2.2915196880430924,49.950254444029852],[-2.1329456728151008,49.821303570413782],[-2.0312993271082682,49.62996834310281],[-2.0170162450624245,49.428208390430477],[-2.0733956213483098,49.254980068015996],[-2.2154716821787597,49.055148727005232],[-2.3900651595036506,48.954427007819028],[-2.590242184792837,48.930828160673585],[-2.8667553588416976,49.006071274996579],[-3.0144925833547966,49.121117418014201],[-3.1170497401667503,49.300898768765173]]],"type":"Polygon"},"properties":{"alpha2":"GG"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-3.7387328409381757,6.5785081123655713],[-3.7015126382523094,6.9877806490811363],[-3.4688110827748986,7.3790939410175937],[-3.3745760492718295,7.8304567852245572],[-3.2488039114049672,8.1294391005683497],[-3.0464362235772207,8.3746963850823768],[-3.0880512536408102,8.6651100036412672],[-3.2010030617858676,8.8364230159865969],[-3.2412129646447125,8.9713634322248268],[-3.2017561541508148,9.4117382143604846],[-3.2770807819083037,9.6898195126534858],[-3.2559237442347264,9.9119856746808086],[-3.2806756129024737,10.133870067280897],[-3.4134461488089021,10.557308054252712],[-3.307085506128026,11.147046663852604],[-3.2163847412556725,11.315210429027408],[-3.0692297156217312,11.437075432604606],[-2.8871084067230766,11.494846614964302],[-1.5461499097281084,11.522371571123445],[-0.74080866180066618,11.478891736015767],[-0.52435824682889254,11.613170547897056],[-0.33683636529723371,11.665243550609812],[0.085380733949716639,11.591018758500187],[0.25011187316430611,11.500505014350336],[0.42370678947625556,11.300580128440341],[0.48639249175094462,11.170248278540562],[0.50915423204323251,11.02742744131141],[0.4829577741887448,10.8590221194123],[0.76952576971962083,10.605920691100605],[0.87060923870403206,10.391196136501208],[0.83931273403423468,9.797285503082751],[0.97014766964789012,9.5980164785581046],[1.0272783210762293,9.3994821710778105],[0.95915457620774824,8.8220518871133713],[1.1362166782886456,8.5725338008674079],[1.1854073170143931,8.3812193329276266],[1.1711194157197107,8.2333263695521612],[1.0946825937207918,8.0498332981081901],[1.0811615246428439,7.5776224919455766],[1.1335411941601687,7.3225879200225501],[1.0688418435215596,6.9174653193672544],[1.1291100592665391,6.8090188671103524],[1.2934952800543478,6.7134381706059996],[1.5134577471728028,6.5216446231571723],[1.6358581617647627,6.360607207642091],[1.6860175867816267,6.0587106273591473],[1.6542983394735298,5.9117747999842436],[1.5803395801983997,5.780907118937229],[1.436108586230211,5.6539942007827673],[1.2897788534042607,5.4440572793832764],[1.1186116334654543,5.3399040284196859],[0.81740174345995542,5.2651947010635425],[0.3440924716764564,5.2475476621391248],[-0.11973702405521519,5.0510644944411158],[-0.62408719557848957,4.7580470207169965],[-1.3069634763277336,4.5729616209689627],[-1.8484141365964724,4.2874952223864629],[-2.1844727227286223,4.2733353074865761],[-2.5856041164037351,4.4591648598011613],[-3.3161584708210134,4.6316857320487763],[-3.4411803199360271,4.7110618919268976],[-3.5374678383140856,4.8235774522629216],[-3.596574285244805,4.9593620811586856],[-3.6091148417406895,5.1558515381520698],[-3.5448243260244165,5.3419485608464621],[-3.4360632978349845,5.4701968707833188],[-3.6783508152484901,6.2017320936196985],[-3.7387328409381757,6.5785081123655713]]],"type":"Polygon"},"properties":{"alpha2":"GH"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-5.3923039323680806,35.60943217669314],[-5.6248214673009258,35.679965599795601],[-5.7693738609446026,35.810980252549498],[-5.8527857218015438,35.987339814844042],[-5.8623583501866641,36.228135332432018],[-5.7966344002044101,36.411821467300911],[-5.665619747450517,36.556373860944596],[-5.489260185155973,36.639785721801545],[-5.262864667567964,36.649358350186667],[-5.0791785326990775,36.583634400204403],[-4.9346261390553989,36.452619747450505],[-4.8512142781984577,36.276260185155962],[-4.8416416498133366,36.035464667567986],[-4.9073655997955905,35.851778532699093],[-5.0383802525494836,35.707226139055408],[-5.2147398148440276,35.623814278198459],[-5.3923039323680806,35.60943217669314]]],"type":"Polygon"},"properties":{"alpha2":"GI"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-73.224479484601716,77.935906099173351],[-73.285814875110518,78.06849566016659],[-73.306208068972026,78.213154360068714],[-73.28391813823329,78.357532962364573],[-73.220847931646489,78.489306141935927],[-72.988188082524687,78.791545659943495],[-72.569414658994305,78.972546959667739],[-71.608954165481421,79.131123915067064],[-70.890423457253007,79.143717171877498],[-69.484431072279861,79.317136512978252],[-68.934964773924378,79.468772015246017],[-67.831308568167344,79.590200616788209],[-67.732414633226668,79.643901097781651],[-67.669427173738541,79.721220711781299],[-67.628892963786072,79.883236738555439],[-67.692209760363042,80.2866937923731],[-67.63203530601254,80.517440827342881],[-67.510397752898484,80.665268097449754],[-67.180004617866942,80.876926950743353],[-66.456317352486948,81.076320567158803],[-65.705342779010664,81.187625579330273],[-64.616556519994688,81.489596204950359],[-63.5796299905385,81.549933608469729],[-62.969845639599129,81.713301345831951],[-61.932115038357622,81.689587732967027],[-61.81356847593473,81.746005575737726],[-61.581969132672775,82.071431426901214],[-61.47427365564554,82.165641199603598],[-60.92118805355792,82.348747420441427],[-60.129941000730625,82.436345628128706],[-59.613932505154395,82.413835719180085],[-58.732382807524196,82.593313227878596],[-56.634376752990576,82.725450398965677],[-55.748575077602659,82.739089364258945],[-54.720499273040986,82.851146014655328],[-54.195780200087256,82.819269644016359],[-53.478028358995608,82.66848495823227],[-53.118112626788133,82.811536684433804],[-52.720651207822655,82.817322387866255],[-51.697123115020283,82.592521229434567],[-51.521155092384788,82.628999473660059],[-51.221568895791684,82.902042215120076],[-51.082156517448333,82.950537531884081],[-50.880067380707942,82.970782991648846],[-48.863445907866264,82.906226327815517],[-47.979567313189776,82.790435445841396],[-47.862509371067347,82.821439420791791],[-47.461749541730924,83.118474855048277],[-46.99123130646035,83.186264287905928],[-46.693024172883419,83.402334193730297],[-46.245040391548912,83.557243713725967],[-45.391298372649132,83.530680893960124],[-43.234576048605312,83.753066963640563],[-42.22508371554224,83.730282058456197],[-41.670775193610147,83.633520897573661],[-41.345464539235437,83.617623125195976],[-40.360772165310607,83.830718591830419],[-39.214528236945725,83.718262207375673],[-38.74263554493529,83.89137232235656],[-38.184223180208022,83.906590886971017],[-37.722274872339383,83.997440315701951],[-36.994759427459563,83.969003639161045],[-36.594760544860357,84.036912871119853],[-34.689989354428711,84.070932560020736],[-33.884914628829115,84.030784705519494],[-32.967293286511833,84.099545016918654],[-29.913773950291745,84.063365766988966],[-25.93427770962208,83.790168914066285],[-25.423133507567126,83.653609985127503],[-24.984101525237421,83.638627851118684],[-24.357841911376102,83.389307197265694],[-22.512521146910426,83.289101406935075],[-21.544738896625155,83.159560873318014],[-21.176446167886468,82.95671537405272],[-21.065323353846523,82.799520197995179],[-20.948034625288038,82.45851406377335],[-20.618431196696527,82.068140959969568],[-20.414699727763701,81.983222755729457],[-20.245357690953401,82.026237294061346],[-20.149442204093489,82.111514971732291],[-20.009566105814113,82.377775214829285],[-19.868914032726099,82.50506419760265],[-19.690968674806143,82.570798057934837],[-19.320915671885917,82.622172257740544],[-18.610348981147752,82.405190372323773],[-18.100515662035907,81.996329752174475],[-17.854444387749506,81.936897858925221],[-17.371913400973977,81.922282962900937],[-16.392519432659903,82.237179158763709],[-15.524222401373944,82.333440941137752],[-13.671882970183521,82.287739284702099],[-12.405719109657319,82.181716542197663],[-11.281375176848888,81.958238537041069],[-11.148979261983131,81.895600907417105],[-11.040414333980845,81.797286069646262],[-10.96499619772168,81.671730282677899],[-10.929196378169594,81.529707313274741],[-10.947874389165321,81.335865058995921],[-11.039188011290621,81.163860669362947],[-11.147371448382215,81.065126202953806],[-11.379551976031431,80.947496183577954],[-13.010850008847818,80.601208218627946],[-13.792244461896587,80.508803518682086],[-14.364056004349933,80.280202149714],[-15.07282357688546,80.228155268625713],[-15.400131697064735,80.164554695379991],[-15.781070100065627,79.919600962894393],[-16.852355496257793,79.695989205152074],[-17.439192110824731,79.358308520499619],[-17.952611967319381,79.219082926571431],[-18.323105593652777,79.190941139634447],[-18.437618431122498,79.118495621210542],[-18.538124892967044,78.93513241524073],[-18.539783749673951,78.761149462982559],[-18.40701634964622,78.464554285824534],[-18.314005347549806,78.370745687659806],[-18.165147786673554,78.325738701063528],[-17.821925706174966,78.37194799086069],[-17.524767619345603,78.332372989937397],[-17.323831568687194,78.206596078708515],[-17.179117981838584,77.970572985863683],[-17.145685394470256,77.726084651893103],[-17.206681677195384,77.537555231571545],[-17.336121228639787,77.387524263585163],[-17.696623491725379,77.158706696573617],[-17.778345518478645,77.047725200095286],[-17.874831148364372,76.733696331601848],[-18.106602848439117,76.358981334409535],[-18.11503761768989,76.300637103515967],[-18.084007937234261,76.112815977766985],[-17.98114385007413,75.950979006327742],[-17.578013903485513,75.699453035396289],[-17.253409095573559,75.586601845058851],[-16.994502718420147,75.338471836712273],[-16.899208830850498,75.111214793710403],[-16.924039535202997,74.866041131333191],[-17.062963285673707,74.662505188175118],[-17.282234564925545,74.550047219594148],[-17.533650289108355,74.496207393229341],[-18.435085297323724,74.505876154171389],[-18.57309061934264,74.474086368966596],[-18.714147772905051,74.361519189777468],[-18.904825660758668,74.022306471762406],[-19.05137819864715,73.883023674914284],[-19.191248027581082,73.823277204187406],[-19.693768572028397,73.745114460384528],[-19.813830794341712,73.683722334344779],[-20.098139068080368,73.210713446266496],[-20.28605752111903,73.04681340528353],[-20.479386021859117,72.994634974160235],[-21.269147848484888,72.945602906256241],[-21.401628766286908,72.875386913724327],[-21.501796520095979,72.693212289429695],[-21.584654324783372,72.149974878129271],[-21.548457142659196,72.032411593177272],[-21.240078316096064,71.539630366821129],[-21.173146889437959,71.047492039685494],[-21.024036918348294,70.540588167326007],[-21.078677972036896,70.298910687697401],[-21.242639625804912,70.113141408747694],[-21.710791763836948,69.906034367631491],[-21.966709067090306,69.65055820012806],[-22.13058985495358,69.559383647911119],[-22.621676370978925,69.444848856531891],[-22.915283564156987,69.312320519072145],[-23.274130481701256,69.26976914425066],[-23.60401435619573,69.108370121016719],[-23.97609451193907,69.041602548872561],[-24.667383953327398,68.82491385115739],[-25.52148815548027,68.423296104954233],[-26.224976630286942,68.217699052393868],[-29.181159900543275,67.804030409037694],[-29.840156975637047,67.797834491482988],[-30.10175094071576,67.708986401776414],[-30.827389737884733,67.572863717841102],[-31.590001542703789,67.659644194840936],[-31.753608350888221,67.643454983129374],[-32.152422180014739,67.431771465944934],[-32.861317571733416,67.180321792819583],[-33.83641525245654,66.300499546689849],[-34.396921257924554,65.99540953998671],[-34.969438892792809,65.790011186556683],[-35.32314259980469,65.731540239879294],[-36.186416673953467,65.370457229977546],[-36.47099019534452,65.311747217971444],[-36.879006355545783,65.056759768452494],[-37.07150912873626,65.034490526359463],[-37.531463326737757,65.114521194557568],[-37.798793447343371,65.096057101475495],[-38.2672111556478,65.181632156812313],[-39.111064873632174,65.069933884079404],[-39.531674735645055,64.796214978120005],[-39.639299726083777,64.680889132177597],[-39.720158696080802,64.291879755751253],[-39.790882472221448,64.170527396448151],[-40.007339252390537,63.951401850463455],[-40.069251748905202,63.59370863607348],[-40.196450267994557,63.373590255813468],[-40.988261021168441,62.722532880182662],[-41.519567940206379,62.404390159753476],[-41.58700815125195,62.333070800214685],[-41.642188819220344,62.183706613331111],[-41.611283175723941,61.857407779193245],[-41.653139043848284,61.657353874137726],[-42.015505006040151,61.209072683550247],[-42.169261022187889,60.888911557771934],[-42.271278198356626,60.543265920180858],[-42.562972577209855,60.214017304364276],[-42.657918690579208,59.880527999364404],[-42.73317540617176,59.749811050243601],[-43.129504159734864,59.466904146017512],[-43.857406965602614,59.319147261720232],[-44.511632001851062,59.416880295293794],[-44.804029684266723,59.543661748706114],[-45.550227926865965,59.734131156128932],[-45.686044234511392,59.809441976787589],[-45.939795831264647,60.049876652520283],[-46.293251525990463,60.164161680169592],[-47.035940214694563,60.3004699362575],[-47.420059444573909,60.296626902583284],[-47.845821172055317,60.224782153282341],[-48.384249072026449,60.313600551748578],[-48.505977926852026,60.390698868170041],[-48.812193779683533,60.706179272472205],[-49.186061897991898,60.854101214212534],[-49.396920779547948,61.065989794364192],[-49.597208375206776,61.197417299600787],[-49.694848159770416,61.299497392177109],[-49.820370352330038,61.510800929933389],[-50.156427370525769,61.835696447426372],[-50.663088065977327,62.111684462248775],[-50.754896711629577,62.229992419099581],[-50.892443965570969,62.540605536270824],[-51.84854518962171,63.318518982563411],[-52.078362407525397,63.704505755271164],[-52.484607243930284,64.079211262941968],[-52.545092808036195,64.2171579294357],[-52.613393994387934,64.621310206199553],[-52.67942613532464,64.742605373530978],[-52.972458937440116,65.015772708144283],[-53.376506161151788,65.127597162841113],[-53.545452209207866,65.235004106688478],[-53.636802846978149,65.354727783362321],[-53.744608198522634,65.603316417245182],[-53.979591064144088,65.814210768740253],[-54.080952246582555,65.976749393147756],[-54.151167242483403,66.62778912562294],[-54.347850732588292,66.951763735437069],[-54.380485336036074,67.187346338859626],[-54.259896795954695,67.608435988177803],[-53.89012675830476,68.310455405199534],[-53.85745767277453,68.532314189096255],[-53.902399277976969,68.68338841399121],[-54.014668185446034,68.794806302220834],[-54.341933040631339,68.91688506299694],[-54.571248988149065,69.059844833838767],[-55.019542384659736,69.184301198030241],[-55.232847822054467,69.325518635675309],[-55.332387523617129,69.434188483124018],[-55.408048110915686,69.614923623724579],[-55.415611375861538,69.762097279502598],[-55.309099020337833,70.3025083497526],[-55.185502135330417,70.618310352998776],[-55.204136436512854,70.743240069740082],[-55.315758905204738,70.875784865452502],[-55.763698581627608,71.079500892571957],[-55.997486942641814,71.251996964465647],[-56.112397571738512,71.400072931580638],[-56.164709613683755,71.580057361418213],[-56.140834990425013,71.926810937203001],[-56.170741244262565,72.047687071625731],[-56.238945042742245,72.141385789670053],[-56.558948692445924,72.358002670656532],[-56.647246066217363,72.470850655715282],[-56.699912215344916,72.604107440468724],[-56.698242192904488,72.841450683232623],[-56.564270458340452,73.108220942820751],[-56.539099408625368,73.244432739019217],[-56.63376139641224,73.507587138345613],[-56.773392685986067,73.606087370774347],[-57.298814164179319,73.630883515320335],[-57.437178367948711,73.671240318150268],[-57.558282252232182,73.749388725880991],[-57.652062895923059,73.858835129605893],[-57.71072775607874,73.99048526672054],[-57.750405712288135,74.392278564432246],[-57.876042413407312,74.529135121688967],[-58.381435498790864,74.783015872030447],[-58.792601848197734,74.908313353168779],[-58.913849394591523,74.995262150184317],[-59.08747740173407,75.197919515185973],[-59.225404348733115,75.280370554878672],[-60.239151651845809,75.49623558994395],[-61.389884826063998,75.677658581661149],[-62.78526159264424,75.754635464779724],[-63.27135560895136,75.83649675199058],[-63.795564176160497,75.720303127052901],[-64.382932910287266,75.777140881913141],[-65.362148035510984,75.631061541848055],[-66.017561172414702,75.719396162780868],[-66.235974035914765,75.663839054759592],[-66.497944749929061,75.510834577160736],[-66.767769344301982,75.471324869530733],[-68.449953764249912,75.608506892085373],[-69.598961626520605,75.885380582339494],[-69.720702604716749,75.962798455810315],[-70.020096427187937,76.272492983605417],[-70.115777383301122,76.31866621938974],[-70.824434078794695,76.352947436946977],[-71.388462763042767,76.594995031407308],[-71.676526348832084,76.804197121717166],[-72.542442709904094,76.888575676571875],[-72.73137156463568,76.946183228719605],[-72.850188392889066,77.03537048270158],[-72.937553948297889,77.155533141947899],[-72.985754968869145,77.296062379350403],[-73.014427293051057,77.615223312575793],[-73.224479484601716,77.935906099173351]]],"type":"Polygon"},"properties":{"alpha2":"GL"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-17.305914019689439,13.205832271359048],[-17.320632572114036,13.403324842179273],[-17.257522929075026,13.591040383012061],[-17.093115497558919,13.789109469159056],[-16.809382773238628,14.021665937490427],[-16.581201316844336,14.086711151750778],[-15.767455305286452,14.092399359833019],[-15.452715336622646,14.253979600257681],[-15.129981534461095,14.311353428358792],[-14.753027011091392,14.250374145093039],[-14.276005868917894,14.018915844732971],[-13.946623687341853,14.042232272125229],[-13.746633540217406,13.986717449818508],[-13.544716329319257,13.872067950910633],[-13.448875166159668,13.77283696007653],[-13.357796929397336,13.580577064297056],[-13.32733994328532,13.426317377359911],[-13.346296399804967,13.270226596808362],[-13.4252828510038,13.067966119121394],[-13.555093993670786,12.930047398565957],[-13.726889937738372,12.850306193144398],[-14.281096260937952,12.737305183420521],[-14.569898218585243,12.786800403738907],[-15.032656502118607,12.965509535529263],[-15.180735401813216,12.907486755134506],[-15.419304085710008,12.878802163569187],[-15.55104235941301,12.744773492269262],[-15.73351924068402,12.666974492111402],[-16.478130578677135,12.654338759793854],[-16.717051426766719,12.566764619641315],[-16.91175329587843,12.587230407219497],[-17.119864628253911,12.714276875228185],[-17.227048116968618,12.878104119738166],[-17.305914019689439,13.205832271359048]]],"type":"Polygon"},"properties":{"alpha2":"GM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-15.124526797860939,10.34011877008785],[-15.352757755317365,10.436023127796789],[-15.507007046261476,10.629657246722866],[-15.550850489277943,10.823559629036918],[-15.493521557238813,11.156393099693807],[-15.056235862118818,11.851414460490657],[-14.940251801455094,11.931803650363198],[-14.440059484352537,12.12794043869185],[-14.427781457481423,12.349580675224356],[-14.33843398394437,12.522240487535765],[-14.226784549419804,12.62527952693876],[-14.184281478526604,12.880439004457605],[-14.066987394081751,13.042178556554161],[-13.942218808953973,13.125961621518794],[-13.748120072442257,13.173324744755185],[-13.003151101710509,13.126919098520512],[-12.863551349046434,13.082605758808892],[-12.594246822734366,12.919436119853074],[-12.334321935302372,12.848404853978842],[-12.043978382054631,12.898152040605581],[-11.304545926349642,12.897231429618719],[-11.127117427808383,12.827976495547004],[-10.996012972359589,12.70689600216742],[-10.814558752083379,12.690613848849281],[-10.621863174796115,12.595534464230075],[-10.40879115009851,12.694150841057407],[-10.254184869978774,12.711997211972317],[-9.876338358265766,12.59822955572203],[-9.7732378715956347,12.791641408665159],[-9.5802927074635011,12.928946734176472],[-9.2578432086187998,12.979384342938982],[-9.0710190510781228,12.961335056549121],[-8.7300001629503701,12.767182672343473],[-8.6176070960343569,12.668970789056102],[-8.3586396964570184,12.118626995467311],[-8.3112211966387992,11.9035009373418],[-8.0839402442061221,11.767189174451552],[-7.9397718106912931,11.562473429579338],[-7.9084407757024655,11.414489358657034],[-7.9230151247729301,11.263045608958045],[-7.8316955260814929,11.085063004964399],[-7.7946078389283278,10.769236850148687],[-7.5952011375987487,10.590168964700172],[-7.5034268111431901,10.40908636029492],[-7.5050861675706333,10.044271090259929],[-7.6155857517442662,9.828874730960683],[-7.4601723023394362,9.6598772775044672],[-7.3958603916635122,9.4048146140812356],[-7.2934177531129389,9.2029492089372784],[-7.2788057078200108,9.0585758914979202],[-7.304826587909572,8.920954836651708],[-7.2264063429862793,8.7242383545820896],[-7.1825990940096096,8.4446837759042559],[-7.1929878858832952,8.3034666091409406],[-7.3150244425414259,8.0522798487023515],[-7.5437546739266699,7.9002604116620967],[-7.7935584655648098,7.3166111014979531],[-7.9208587862369297,7.1655930121670677],[-8.1451487994633194,7.0646062172635213],[-8.4284194300445865,7.0616340980034282],[-8.5441976969105387,6.9016206957060415],[-8.7438844999541736,6.784721772213671],[-9.067175001012437,6.7186986159023983],[-9.2193249562003423,6.7266486164903698],[-9.3620170517783077,6.78005065046474],[-9.5218473480885653,6.9163903246107674],[-9.7099166406704445,6.9808888934323221],[-9.8839266439268965,7.1452551258103041],[-9.9615969112243725,7.3716686173636612],[-9.8927754848126117,7.7215271315445229],[-9.9526056776054102,7.9433473365791514],[-10.207466173707418,7.951395728725319],[-10.391846217245522,7.8442907884047237],[-10.574133779086059,7.8162156103709615],[-10.916663922236003,7.8792597404838043],[-11.108586697949068,8.0311151212518741],[-11.180202804556108,8.1604091491806958],[-11.211450213120486,8.3544173885786428],[-11.184051437949993,8.4996589787061492],[-11.082271824847815,8.7252507309251346],[-11.212070528429907,8.9122871134188841],[-11.239658161667691,9.1875814982556925],[-11.45209497513496,9.4796740577732521],[-11.718459568764185,9.489750397880389],[-12.028483847834497,9.3887721335518997],[-12.181496477886265,9.3776675233600439],[-12.371055258532905,9.0548838863185583],[-12.570052510212976,8.8946498645711554],[-12.74304799413267,8.6929889040550368],[-13.033154480156332,8.557242236020338],[-13.393434318059853,8.559806010033542],[-13.528767445520732,8.6088460300820095],[-13.677033889416947,8.7298975771605036],[-13.752157176430037,8.8526838577583185],[-13.797680007452589,9.0476787782448103],[-13.97478313229824,9.1243399660107283],[-14.107875503381713,9.259898384073793],[-14.168949377328175,9.3890458476292569],[-14.190889564921385,9.528191601907217],[-14.370997652024545,9.6813579417160796],[-14.756968466471955,9.8730944861348977],[-15.053965156450376,10.32962461118772],[-15.124526797860939,10.34011877008785]]],"type":"Polygon"},"properties":{"alpha2":"GN"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-62.288686351751863,16.229113104942982],[-62.251639283598998,16.502040894800121],[-62.073384927278738,16.73537530674589],[-61.638682621323589,16.977473205048863],[-61.496495725669483,17.005724432122708],[-61.35218058437821,16.991999086188656],[-61.090775316132024,16.855765464049522],[-60.743230805900907,16.511571840758467],[-60.673307226724901,16.272545977486111],[-60.719518918732682,16.046995181870788],[-60.718370775341505,15.800877510140248],[-60.771053654816924,15.670583783299874],[-60.886471884958517,15.527422995212049],[-61.098878030959447,15.407830220795681],[-61.350278178874589,15.390420740934273],[-61.576250993882375,15.471767432113372],[-61.790777788905061,15.476982054510778],[-61.967453950085478,15.54746946488061],[-62.11663553614919,15.685087474298795],[-62.231972163425937,15.899343927860897],[-62.288686351751863,16.229113104942982]]],"type":"Polygon"},"properties":{"alpha2":"GP"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[9.2327960641722289,0.66352913653450984],[9.0982719029236083,0.73064780469307011],[8.9611830569540807,0.87607938656605344],[8.8920047303329284,1.063584202027503],[8.8918057159083279,1.2139225736040287],[9.0430142601750489,1.6501202157491202],[9.21424620569063,1.8728579954061548],[9.2869811081735616,2.0211585427158489],[9.330928849332027,2.4746163629406217],[9.402429902706773,2.6060904519354993],[9.5096373954936482,2.7105145401579156],[9.6429464640222271,2.7785331329011802],[9.7904137692966557,2.8040523528310635],[9.9861901585512385,2.7685951936622564],[10.142614350816253,2.6685703910432004],[11.377853456988822,2.6647601579431872],[11.563748534769116,2.6083957429215254],[11.743350203192003,2.446284244566626],[11.825811963987466,2.21882665466102],[11.832955237245935,0.95348652975559145],[11.751513662881694,0.72314689350690986],[11.612979554804186,0.5842565713708423],[11.382849820318775,0.50222375252512097],[10.130312435548745,0.50369092145582717],[9.8953641643602666,0.46048586418863779],[9.4229178792471373,0.56132153291646025],[9.2327960641722289,0.66352913653450984]]],[[[8.0057619004019624,3.0751520711433389],[7.9350081121497897,3.3096812325408203],[7.9961749548607095,3.6247328482252725],[8.0729483680738205,3.7609677629124869],[8.1913759439073122,3.8690333540012709],[8.2849617306250458,4.0472736623271937],[8.3950097488503612,4.1492844477003397],[8.5790530947884083,4.2262428492372965],[9.0134638620386571,4.2471521160781922],[9.197605787660363,4.1669537042022489],[9.3760265198334221,3.9676570330178276],[9.4499679232658984,3.7265674706783463],[9.4159049770276546,3.4572952168534998],[9.1047500598861113,2.9250531939328082],[8.9555531010433178,2.7918356621194587],[8.7158961200403571,2.7240140127389498],[8.3406587325803763,2.7832685363161822],[8.13568612511793,2.8976611127771061],[8.0057619004019624,3.0751520711433389]]]],"type":"MultiPolygon"},"properties":{"alpha2":"GQ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[19.253196098532356,39.459005703503202],[19.16472928374084,39.636143320548747],[19.147731359620416,39.784102042434327],[19.193723416705488,39.97668668194904],[19.275743551128425,40.100998428601002],[19.434705070255465,40.219046504098877],[19.973359705902269,40.361128036836043],[20.216395823679527,40.516030029737237],[20.408707438418524,40.758734540942051],[20.55929686926298,41.141022674413826],[20.665559435286376,41.249285584074478],[20.799397856351369,41.320698941318746],[21.468451728314726,41.421811975189655],[21.829463082748472,41.602566041954724],[22.325675331755551,41.664298067293963],[22.699644039178452,41.823737749343628],[23.582833155423234,41.893387264434239],[23.983612610483892,42.020995467638812],[24.588796201018063,42.046640683249983],[24.72995469661392,42.004507959990804],[24.908212753946575,41.901330186287055],[25.256459573799265,41.802018078976708],[25.407987342689331,41.797336085640268],[25.57030922598155,41.867921750550515],[25.79899478330184,42.117995595453664],[25.9677955216032,42.204791230597976],[26.156783513758803,42.222418208563084],[26.558868370036343,42.172455833381662],[26.863232912765859,42.012945220106232],[26.995323223055077,41.879750190706304],[27.069033200884714,41.707252059999028],[27.122207909201435,41.360135748225971],[27.066996765354414,41.170140095642296],[26.748068866874892,40.680492266166951],[26.358778726278807,40.255097067328613],[26.330787987124086,40.162068646014575],[26.342669868231265,40.048457993607592],[26.390946973633298,39.960229605451168],[26.758582086232163,39.685815009632663],[27.023123914653286,39.335801166996681],[27.086544289893723,39.138447377613112],[27.091360787406778,38.992046162970951],[26.934341067826075,38.574869878354086],[26.939398561606005,38.466293639097387],[27.003688699063556,38.353354149766268],[27.314216217395828,38.186490847042784],[27.476842818504604,38.010295606985082],[27.535480152948089,37.826945457717279],[27.544902836716684,37.453825100433264],[27.593409016031217,37.343066284358883],[27.745863234787848,37.203102540226183],[28.107078005302466,37.076149374682153],[28.592473531789526,36.778354086869186],[28.677541525359246,36.657787619979821],[28.723713990631936,36.517641482256359],[28.701518715835899,36.20634721000485],[28.517208451592378,35.81114385673078],[28.387155151345933,35.666104535648991],[27.777404914573388,35.362665218789189],[27.382053904711601,34.973902731340019],[27.186847339708919,34.912542945525175],[26.833948020922943,34.914891809128129],[26.537471879022217,34.64076620342481],[26.281236666999142,34.533067466607648],[24.739362133527784,34.438912171113849],[24.607112672970846,34.473892970618095],[24.210312124283234,34.681799782294135],[23.454233039476197,34.771420593046727],[23.254768776334288,34.901431459458593],[23.126682592637376,35.050415979755215],[22.996650308525986,35.541817493157161],[22.924373521775401,35.63172090214222],[21.969145976257376,36.196864140744019],[21.612348612744888,36.324288736673246],[21.343908107051607,36.557320170290495],[21.000112248149176,37.092847109897065],[20.619545795080665,37.207075115529818],[20.331296465857989,37.410821138537926],[20.128819605823178,37.704891181410062],[19.945359560281929,37.891565753943532],[19.878636782658408,38.023838794657884],[19.861317516857671,38.268524579114143],[19.96528469188004,38.644190593264767],[19.934370678277926,38.802680226637229],[19.871520244351167,38.886779566963739],[19.553207047077294,39.088062154733862],[19.253196098532356,39.459005703503202]]],"type":"Polygon"},"properties":{"alpha2":"GR"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-26.733804734665682,-58.827412748348131],[-26.894786203703045,-58.644935915417676],[-26.948864035427835,-58.45719013691172],[-26.909615157138202,-58.21703944779599],[-26.798579411695172,-58.056278903927868],[-26.589070251700292,-57.92007530735949],[-26.406034152105086,-57.883443602497948],[-26.16243103720333,-57.902795473093946],[-25.93050631806787,-58.043897604354044],[-25.793022743839533,-58.268189779021846],[-25.760267791461729,-58.504361554849403],[-25.803313999517954,-58.695268998244543],[-25.916339463499128,-58.855030922567977],[-26.08202930261988,-58.959172677804901],[-26.275000742851514,-58.991740318594758],[-26.575002560129938,-58.913440904000744],[-26.733804734665682,-58.827412748348131]]],[[[-38.095050552907828,-54.501792604058352],[-38.233704791157557,-54.458317547881052],[-38.354002997294216,-54.376810370929988],[-38.487181485961429,-54.176360795424422],[-38.511289244710419,-53.936912767384641],[-38.420741321436928,-53.713938294058877],[-38.279050223241391,-53.58258093117324],[-38.09813476710729,-53.514752310194204],[-37.314559979531445,-53.48892111045204],[-37.111298995734877,-53.564837504249397],[-36.588190728063033,-53.621918544235776],[-36.35206982121273,-53.752205887392982],[-36.208495227810822,-53.765574292950113],[-36.036782209629202,-53.843943806135066],[-35.784453432506659,-54.06816810332279],[-35.61719312804459,-54.139754526149517],[-35.506614597462615,-54.240925083694421],[-35.334750747773484,-54.578030102843556],[-35.302373273143864,-54.820639996160708],[-35.389025573989692,-55.049548798867981],[-35.530958555459861,-55.185487627020464],[-35.795366699329136,-55.312950782465755],[-36.135927333303862,-55.363062271097988],[-36.278996430181309,-55.328061376434263],[-36.532102337139811,-55.193621591995807],[-36.738935277081772,-54.97932646975277],[-37.558807882192774,-54.663229954384725],[-37.779236146884244,-54.644724972403424],[-38.016963098012923,-54.514235303229768],[-38.095050552907828,-54.501792604058352]]]],"type":"MultiPolygon"},"properties":{"alpha2":"GS"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-92.537832209157017,14.147733472207264],[-92.666172897092139,14.292632667990278],[-92.729862758089112,14.475418637704607],[-92.729046787814909,14.620992538385224],[-92.661293916259282,14.81281439319838],[-92.639608995016161,15.013393100812623],[-92.697654610278889,15.208262255624073],[-92.687956695213387,15.399687297682567],[-92.633404141404185,15.533109697924477],[-92.07923637994746,16.433889055823123],[-91.831016422927348,16.560880200082618],[-91.393801576594058,16.570578175071919],[-91.81017617799202,16.95705347439684],[-91.880565747041487,17.089102064564347],[-91.908182010113336,17.286109301879392],[-91.856796455806816,17.478291596457147],[-91.770860559001378,17.600792793334733],[-91.652524251662086,17.692379036633042],[-91.495842974202347,17.747482793835268],[-91.453847940232038,18.000173306035549],[-91.346102603833899,18.16607783577356],[-91.18244831179274,18.277211283482359],[-90.988502440288059,18.316177543624033],[-89.111484406178505,18.312072681337213],[-88.96700690866335,18.275129910278451],[-88.839850381213139,18.197221418131896],[-88.716451700670135,18.04204911509645],[-88.663060104294814,17.851117149659604],[-88.698295608866076,16.438390534651138],[-88.423940126072111,16.420223391315769],[-87.929264255021025,16.129402079283857],[-87.799154234793519,15.984889847504967],[-87.729277773682298,15.752995420534418],[-87.757885035176628,15.5606571063921],[-87.857702982637221,15.393777569413864],[-88.688416059961369,14.76293272983879],[-88.690826207596217,14.47119669186173],[-88.818413056428682,14.25353841567023],[-89.232134322282292,13.853292955024095],[-89.622305189371573,13.575257162602941],[-89.694436778944308,13.43830862938429],[-89.800235942195613,13.333380840798362],[-89.932257928361324,13.264290328200259],[-90.07877744039294,13.237173242143188],[-90.226781609359548,13.254437938222633],[-90.678172099086183,13.424472047114548],[-91.192262695254257,13.42794399827018],[-91.564011460231058,13.526531502362513],[-92.086244868690585,13.805585319593812],[-92.537832209157017,14.147733472207264]]],"type":"Polygon"},"properties":{"alpha2":"GT"},"type":"Feature"}, +{"geometry":{"coordinates":[[[144.15068675161052,13.449479569295388],[144.195749056555,13.636662741515222],[144.2745286752631,13.758150520797505],[144.54405523865515,14.026308437639386],[144.76570459196512,14.116119498976653],[144.98044816461666,14.102586871732811],[145.18563404715007,14.014621659256683],[145.35860931401717,13.84282314899505],[145.43194123573102,13.656411169914367],[145.4352332868265,13.505761999881367],[145.39349305749121,13.360973314939878],[145.15710591354633,12.98324173841125],[145.01645227333552,12.84301513173661],[144.83230349809338,12.768609646045913],[144.54858029409505,12.781906166455364],[144.32218365847069,12.925910576181678],[144.18394912540978,13.135348878809427],[144.15068675161052,13.449479569295388]]],"type":"Polygon"},"properties":{"alpha2":"GU"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-16.95790922594993,11.919844423519629],[-17.135840675486747,12.090793429887562],[-17.210352821876473,12.326019073797511],[-17.163299524349874,12.568236229887512],[-17.0061397973354,12.758457111278569],[-16.826165591439217,12.841325973895877],[-16.525070400589374,12.861062193138226],[-16.212460664041739,12.952674831175152],[-15.757920551976284,12.964576906495736],[-15.283880791722058,13.171938667321594],[-13.72744330017146,13.173682276342658],[-13.581029564700405,13.151134902808195],[-13.408161737969532,13.056756584532206],[-13.310021942328342,12.945788361606018],[-13.181444289993296,12.565725979222618],[-13.187540611324808,12.361941624329337],[-13.261988014009265,12.142112499692523],[-13.233625313326797,12.011163624556572],[-13.235621594517632,11.685120190226781],[-13.270788287097268,11.545543046757034],[-13.344260580903601,11.421767858253842],[-13.449949540334611,11.324052357433576],[-13.579096309031238,11.260494609035881],[-13.887083580334396,11.169352431615989],[-14.169487556528951,11.155241749218014],[-14.364169030328531,11.079013017283943],[-14.67907359291457,10.597625034791379],[-14.791665116845229,10.508250835823519],[-14.972231281027218,10.445522148566477],[-15.209189452468143,10.468913942843614],[-15.575498307381785,10.67842584589493],[-15.732565917160999,10.585793742232632],[-15.887802456978298,10.555304808402134],[-16.272775347353232,10.551033801427289],[-16.4923801424756,10.643333187378651],[-16.663050954491634,10.85299352238061],[-16.735406779501197,11.086050027859505],[-16.688838106721459,11.325595553541747],[-16.527966259391281,11.518901985261142],[-16.729908260846663,11.790487840394579],[-16.95790922594993,11.919844423519629]]],"type":"Polygon"},"properties":{"alpha2":"GW"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-61.765797796507151,5.6083007756056409],[-61.86555541463602,5.7826367684481239],[-61.888656809079507,5.9821635745416524],[-61.831373911844814,6.1746818605809741],[-61.678498215071542,6.3744678146122347],[-61.70196501461394,6.6257414192600042],[-61.665124554296817,6.7801385296689487],[-61.560133556198991,6.9737442102844707],[-61.467166359788642,7.0771298982043236],[-61.133181418897586,7.2264962302108779],[-61.218028011941072,7.5154783311956441],[-61.170147378728522,7.7502303641253514],[-60.845411480791078,8.1869424277856169],[-60.485786994637543,8.3770107566358973],[-60.506952818379879,8.6479619474490708],[-60.456049564308323,8.7878294691695764],[-60.366229089717649,8.9065153876755936],[-60.245450949104217,8.9935022007984546],[-60.104418049534161,9.0410814625221469],[-59.858643786338327,9.0231028818567243],[-59.619997350156872,8.8657236024079271],[-59.402577667845001,8.7872404454840591],[-58.931270817941694,8.494798407866849],[-58.127104222630379,7.7178023204510469],[-58.033105511853691,7.543743645427587],[-58.001596929124403,7.3217247323768166],[-57.674422839843267,7.179314990774273],[-57.254887456707735,6.7733580051876769],[-57.003866580161286,6.6386150935283172],[-56.734739245148425,6.3033760526398774],[-56.692491185201831,6.0999148449536849],[-56.735013384712175,5.0394088749193742],[-56.919871752709021,4.7359974778464302],[-57.021226174576412,4.627993711088596],[-57.196767746958798,4.5387639929892281],[-57.370232839664695,4.516113963401061],[-57.501875306473323,4.1241082060544869],[-57.326447627917062,3.8766015613008222],[-57.076231806282458,3.8220897414713879],[-56.957682876857881,3.7377199001327677],[-56.86844521199081,3.6227905393745714],[-56.719108061721933,3.0577305474138576],[-56.489559835694735,2.6339826064314651],[-56.370813656328039,2.4471306939972903],[-56.132495062035865,2.2985549783312158],[-56.021624967665495,2.1343390853482833],[-55.985954784824301,1.8901824660655955],[-56.071393079131504,1.6586978779191042],[-56.213854684502245,1.5209890244307827],[-56.39861952282024,1.4494279186874981],[-56.849246686949471,1.3816763420955054],[-57.121429958144759,1.4488228872654549],[-57.360182026583203,1.2615819576816938],[-57.650445383418045,1.197366639388961],[-57.857251066887393,1.0530802840276334],[-58.087043872416714,1.022595945216529],[-58.199924240463282,0.89424710372764371],[-58.327483926423149,0.82018863324743696],[-58.737714824048282,0.70853070130693729],[-58.985422905583668,0.7311983850518109],[-59.200072222690999,0.84918523810459168],[-59.509044285226295,0.96059671940465485],[-59.819796772401141,1.2664838062229422],[-60.002985844925512,1.3767063709518537],[-60.098469976608349,1.494825569757521],[-60.228699020962843,1.7402677388305066],[-60.253976707397598,2.0202901211211874],[-60.347718401156193,2.1630225247655477],[-60.482738098174814,2.5840868858175794],[-60.464421828445701,3.0766904466265914],[-60.341427262618737,3.4417916189117364],[-60.345892364585659,3.67784639555645],[-60.299852565036467,3.8139789541827716],[-60.153598742815845,3.9872715583121523],[-60.164946234526994,4.0154537074759666],[-60.427604603506545,4.118592259268218],[-60.565217229937723,4.2572322477196094],[-60.646194769303776,4.4866510223527092],[-60.616785944843961,4.7065707821838902],[-60.869814525647776,4.7188887048390393],[-61.045451176383615,4.8048580316328771],[-61.765797796507151,5.6083007756056409]]],"type":"Polygon"},"properties":{"alpha2":"GY"},"type":"Feature"}, +{"geometry":{"coordinates":[[[113.3999516461745,22.479322016539133],[113.49052090651304,22.719552253602512],[113.76095250505057,22.950412988964647],[114.01905598747148,23.053835517380239],[114.25073145135418,23.060476731168979],[114.50167111305981,22.98162734441874],[114.66363032952079,22.843673154641632],[114.79693459073221,22.6024081698576],[114.83439045988212,22.371541944723141],[114.78835475800145,22.185426789374095],[114.65630811300579,21.946395140501497],[114.49500071341133,21.785530803918633],[114.28491838153208,21.70151786234257],[113.83136995713703,21.712784105997258],[113.5862358314772,21.796936284994235],[113.40985865514705,21.985340509468475],[113.33964905080499,22.21963356263354],[113.3999516461745,22.479322016539133]]],"type":"Polygon"},"properties":{"alpha2":"HK"},"type":"Feature"}, +{"geometry":{"coordinates":[[[72.761236300744827,-53.07440247332098],[72.77383504735694,-52.828047423465904],[72.86875596851155,-52.654190774617341],[73.06917634694409,-52.510381140328647],[73.311007362046723,-52.46656323759273],[73.743688871775518,-52.55297764371857],[74.117738268376911,-52.699066239413817],[74.279957379757249,-52.88080471713954],[74.33704535571583,-53.117627176767094],[74.275450900422271,-53.353317731223953],[74.10979503820225,-53.531929180171751],[73.680524957528235,-53.675699002531466],[73.4634744353977,-53.683912606941234],[73.262655601805392,-53.641044381418929],[73.085917952091805,-53.524435366864076],[72.837036533748417,-53.265004619345838],[72.761236300744827,-53.07440247332098]]],"type":"Polygon"},"properties":{"alpha2":"HM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-89.502605465276559,13.936285859791838],[-89.633931889404778,13.996431565009251],[-89.772126964661524,14.129818318885624],[-89.836883752555963,14.258933360426095],[-89.862029671786786,14.401171956722738],[-89.830815501228102,14.590686020821494],[-89.714641620430825,14.779288170211268],[-89.688475777950686,15.046286636164414],[-89.589294828722032,15.296480721284961],[-89.506628173102754,15.414737589600229],[-89.213645330016519,15.589531253603282],[-88.495169604856031,16.15156792014427],[-88.263075226373715,16.227053389229908],[-88.053839838239654,16.345976233278648],[-87.613441936783573,16.409593122835123],[-87.348557028554026,16.330641187573512],[-87.129249789067202,16.31410699032244],[-87.080832100508843,16.517872665696327],[-86.962074125891831,16.675624057405948],[-86.690117079670856,16.845623454951816],[-86.522329498659047,16.906583630238313],[-86.167405887876271,16.919594438541353],[-85.932261047821783,17.000966288145168],[-85.734105381600358,17.000587671011928],[-85.551658276522744,16.923264548626758],[-85.413575467067574,16.781141541553442],[-85.34154468745308,16.596540950561099],[-85.344926403864946,16.409404301985028],[-85.045957958686998,16.484369888121744],[-84.908562205630119,16.485345086245587],[-84.50210379152702,16.361971947830476],[-84.039115793876931,16.270206152959567],[-83.466082555219216,15.827412247841599],[-83.050572406948078,15.625002618628089],[-82.759657165732762,15.295218286717249],[-82.665038018289351,15.075580146580563],[-82.662095371406664,14.931221139323796],[-82.700526281148697,14.792040510918708],[-82.777125889089959,14.669644913734501],[-82.885506344915527,14.574241250587441],[-83.063538935666799,14.50236462302564],[-83.311094540667099,14.493724178323978],[-83.499015235366684,14.395663809169527],[-84.366313905581706,14.151645374047717],[-84.727892940006015,14.167748769364618],[-84.839556099742126,13.974220353400908],[-85.283127286418278,13.640148516668585],[-85.469263572567584,13.434799637666961],[-85.751922156176775,13.34588940096755],[-86.003197379622279,13.393722959697101],[-86.212025355929171,13.28545323288264],[-86.301950258948878,13.025791635931913],[-86.496655549197399,12.861258706796123],[-86.588094006639508,12.718585012394575],[-86.795539460674092,12.566837336773478],[-86.986410971627095,12.49698893404504],[-87.364488323349875,12.480239127890458],[-87.597746876446806,12.552832823191334],[-87.741811577661665,12.685954174982289],[-87.865370628776432,12.892635649300153],[-88.048245199299998,12.957594999426172],[-88.199087023041244,13.080499803043992],[-88.274878344708597,13.205683425946704],[-88.31318612901822,13.383997342647495],[-88.506275592196602,13.355033601795999],[-88.693877023998553,13.401351907846379],[-88.81524952654469,13.481281013575202],[-88.907951222065847,13.591918947228619],[-89.149208909715682,13.727502551463939],[-89.325708870365915,13.884550856656404],[-89.502605465276559,13.936285859791838]]],"type":"Polygon"},"properties":{"alpha2":"HN"},"type":"Feature"}, +{"geometry":{"coordinates":[[[13.038568732977302,45.337968793569758],[13.019529372010133,45.527011989946828],[13.072392332989995,45.70950948231026],[13.189524240855915,45.859108758786405],[13.327640729726035,45.949482070097389],[13.570401786181726,46.016564021504003],[13.80226240621829,45.971410063385626],[13.978552916046848,46.008971425719395],[14.191356971832686,45.997688106305837],[14.345816355724486,46.104478528736323],[14.528661536102934,46.155364015427203],[14.717298352962221,46.134439291567823],[14.8863166509444,46.043278294984823],[15.096655426485174,46.200521828795203],[15.185968688265977,46.43055176225009],[15.368181382356402,46.622650557768587],[15.766519656330814,46.757260738638081],[16.128575740649758,46.993518261844301],[16.316920952622162,47.034072878914223],[16.966836721784226,46.86574309811698],[17.551029511467018,46.446599184074643],[17.775936079609018,46.384325025546445],[17.960571238588905,46.283212875719492],[18.311012505664088,46.261796022423681],[18.517722706341786,46.384694369589759],[18.854532090488433,46.428951252407643],[19.003469859278539,46.421744345595606],[19.143659299822801,46.37093766062344],[19.262632300587349,46.281049821807215],[19.349807689143713,46.160075237253189],[19.404580093332974,45.903741900127073],[19.528063894307277,45.699805987967544],[19.707996129472214,45.583582334973485],[19.810818206175359,45.475181700141079],[19.877021177516799,45.34124050530103],[19.900693627365268,45.193718668924895],[19.863020377676357,44.99869307702005],[19.752387393772594,44.833724600902613],[19.533599837817022,44.707097846865423],[19.377432899917267,44.521317393732119],[19.056252098230868,44.377600240878046],[18.908310994130019,44.367042332277308],[18.698466387973227,44.404279477361897],[18.531292884315167,44.48670971706909],[18.418831293820737,44.603792105925486],[18.256132646786796,44.629095212348709],[17.774067656754688,44.579850244461639],[17.559287106493027,44.646588321212619],[17.14215192643681,44.661006384613721],[16.937975469015392,44.719794397225762],[16.697228070616749,44.696901726957385],[16.609829570878631,44.622608077453897],[16.690994206394237,44.455929851045674],[17.298675031064658,43.982071211456329],[17.578463822981377,43.845170878844343],[17.671593026843485,43.735543725351825],[17.743784362125247,43.57367605810439],[17.98356707778963,43.362843036549719],[18.713515731315081,42.975555076893947],[18.857419474342098,42.828853735823245],[19.008611721696553,42.523120856267283],[19.013609422060107,42.377982719579414],[18.976688087623369,42.237530340484007],[18.900962410389948,42.113612312991272],[18.79282062537207,42.016682389524377],[18.661385597660871,41.954917600034676],[18.517745215493647,41.933528435932161],[18.328203482994859,41.970421017313228],[17.85939434012252,42.214043199275928],[17.63438868527431,42.212791153480268],[17.190159006363707,42.314938273357697],[17.027006562158377,42.404155364082307],[16.778748303994249,42.400962934020185],[16.440622431993607,42.504275428910894],[16.326767248321332,42.597238991391833],[16.23760190028915,42.726671632153121],[16.041767329432645,42.842678701316181],[15.914093566780219,43.025813359811494],[15.663078658078163,43.138015138345708],[15.517756461214637,43.324215350621628],[15.387494128214396,43.394486172754924],[15.003097852356056,43.427415295662001],[14.823335643619266,43.517317681437113],[14.454179589079963,43.883400821182249],[14.367065755706529,44.134824262737062],[14.186774041324973,44.216812983881915],[14.063739772318245,44.345938919907077],[13.759108411996255,44.348101007184709],[13.548286332418787,44.44740049285214],[13.190572964250663,44.868879906257732],[13.038568732977302,45.337968793569758]]],"type":"Polygon"},"properties":{"alpha2":"HR"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-74.912338473610319,18.202519066552455],[-74.974318762549018,18.390502618066698],[-74.958496646609163,18.587807033891629],[-74.807367644507323,18.895747512928462],[-74.677026321763961,19.032034202596762],[-74.550961768331248,19.096975306468693],[-74.261930161971677,19.161334961940714],[-73.761463847779822,19.073331125772693],[-73.680255821162021,19.247644984283337],[-73.812865693606611,19.382575306139085],[-73.917964615190002,19.581677364647987],[-73.934720580903743,19.78018162386854],[-73.895220512787162,19.924729148328154],[-73.812569694148394,20.09028026149597],[-73.719559231528919,20.192135432943836],[-73.517081935524246,20.311519480754924],[-73.354604974620727,20.364940232950513],[-73.185386290787179,20.508435752011035],[-72.826196274464181,20.590441170440553],[-72.639272308656842,20.56807957969961],[-72.382226046318138,20.449078866443642],[-72.223557245508886,20.278897742425531],[-72.133581261238973,20.245198843343513],[-71.694859718023849,20.210673190944323],[-71.557684055544186,20.166049163287916],[-71.405249561974315,20.049620035757918],[-71.302510406393921,19.895055865165414],[-71.146230734554948,19.189345768140367],[-71.161071710547688,19.040006836230702],[-71.24721668200317,18.856789895830072],[-71.250257127309808,18.650867377996658],[-71.29746552449518,18.507363089944231],[-71.24274304135254,18.342829372445017],[-71.24196387598397,18.204355944604139],[-71.31255375498246,17.834503391965576],[-71.423356182909828,17.67792284660943],[-71.540998987975527,17.594435532992893],[-71.677580753095597,17.54800943521035],[-71.86939024296403,17.549949748341334],[-72.005004876715063,17.599129339954125],[-72.173817529418884,17.725235944646617],[-72.45343998289637,17.719726002088429],[-72.871584976702053,17.651960730742093],[-73.409313380686243,17.742637839897721],[-73.490509492734645,17.729058388413861],[-73.610060327536743,17.62449119186882],[-73.744727798419717,17.562229679946533],[-73.990218116372503,17.553397666222406],[-74.171058359722565,17.632164067078666],[-74.347780135595613,17.780391321529827],[-74.688884266408692,17.925588649453402],[-74.797810266517928,18.02020635798802],[-74.912338473610319,18.202519066552455]]],"type":"Polygon"},"properties":{"alpha2":"HT"},"type":"Feature"}, +{"geometry":{"coordinates":[[[15.948158090539541,47.360065142129976],[15.966829160049379,47.46564530111295],[15.924297341552693,47.622807391389628],[15.93002410130622,47.765565616609287],[15.975891177681477,47.90087606687063],[16.058153485979592,48.017690532064833],[16.170094240731594,48.10647101455222],[16.425871761804615,48.223744585545582],[16.674700253190483,48.241840365781165],[16.799510903905048,48.370975652686781],[17.014549137470073,48.485500110258968],[17.200919073659076,48.511125915844531],[17.441857944426456,48.474882008347976],[17.867980452631329,48.275139676253112],[18.378441312519218,48.280248448813992],[18.519411932015071,48.419240045720151],[18.769175521773253,48.529033273028411],[19.249764836180645,48.586775731934488],[19.431047995260563,48.683485312183308],[19.581934551561684,48.720902691836081],[19.886998474631699,48.661781897202587],[20.006236526439576,48.711202584017506],[20.230369981009833,48.953878630892746],[20.464568140891707,49.02603331248833],[20.891625280909455,49.044837616509767],[21.091436238967859,49.013297660566323],[21.406384965192178,49.053039707349534],[21.720297529669171,48.970181841194666],[21.901386385949245,48.866971464432332],[22.238332370322556,48.906955780322669],[22.423323364222711,48.877480339879455],[22.547754630075218,48.811465816433042],[22.680660149008549,48.672279940759637],[23.16413976314384,48.43743101234525],[23.287225491062923,48.275636640881658],[23.376003497711398,47.96655981799023],[23.342917923913308,47.767497227374847],[23.233840574404432,47.597724726656466],[22.810317090542178,47.309164665218304],[22.465149834782391,47.221377516065289],[22.371787567540927,47.067042830259339],[22.235887957233484,46.949644475871089],[21.987516554430453,46.599738843062568],[21.912008982004018,46.425802142861571],[21.74300417019375,46.267850718009463],[21.641983792289974,46.084769298607831],[21.263700544747817,45.795131592911467],[21.123590232487679,45.750620162384209],[20.954235807373163,45.747182126945653],[20.667113411160045,45.637053180306033],[20.48635332293594,45.651055868753708],[20.250321924201963,45.608928954140239],[19.691984271869469,45.653333067260199],[19.416102246845703,45.522550209566823],[18.832165451792704,45.412088606515375],[18.424092929741352,45.257599506227216],[17.64421847127894,45.318040528330386],[17.369698124677338,45.462324729663237],[17.094841321202242,45.54517967955379],[16.586204896414223,45.923776565333291],[16.190860384609451,46.120550147799257],[15.977615506373228,46.377122110846663],[15.839600656042599,46.432843275737383],[15.725488999661676,46.525200573596805],[15.643118863515175,46.646718067239078],[15.599590995037214,46.78692029709174],[15.598657729902675,46.93372107912672],[15.663276506316882,47.117901570777164],[15.755723940300909,47.231940215716769],[15.948158090539541,47.360065142129976]]],"type":"Polygon"},"properties":{"alpha2":"HU"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[94.766768684608707,5.0949527363617904],[94.713797466696974,5.2934736261951247],[94.757342281137767,6.0282802284157997],[94.814025960657602,6.1643203756289733],[94.907907807569444,6.2779256571880282],[95.123717818024289,6.3918551752714228],[95.319490086481409,6.4000790925038462],[95.584128680746616,6.3221268247709039],[95.877523513170971,6.0627580442441005],[96.360035838564173,5.7665461396615738],[97.679854206903485,5.6929342998456072],[97.846507667780728,5.5966021778364343],[98.267628161442744,5.2264334372651886],[98.829695945932386,4.4101320021770647],[99.1368635890923,4.1457747554333491],[100.07078424297279,3.5497660996561442],[100.49607950332364,2.9935349186056901],[100.70206014022195,2.8185577627519041],[101.03051907507827,2.7845933183251992],[101.46589666617196,2.6248915209874935],[101.68678851156666,2.6234758626966728],[101.87162905393582,2.5689902780768259],[101.98907275638439,2.4840765698977969],[102.30567603242446,2.1136478661480305],[102.73625703419589,1.9317413261117466],[103.00083317547913,1.6657942912198409],[103.09822692426962,1.6207752263322546],[103.62458138757911,1.5902874516503005],[103.94431291148065,1.6730492330594258],[104.60313081010692,1.7146952559627697],[104.74972172225145,1.6870585850351245],[104.92025708877073,1.5858249125923056],[105.03802230052028,1.4262569879592326],[105.15579564871243,1.1280100486623663],[105.15837565934309,0.98929875790965105],[105.04791489275837,0.61137117023862531],[105.0486078245347,0.48742468798733196],[105.13963330848917,0.29169762171099983],[105.42176579031613,-0.0081982172778914952],[105.4951125657475,-0.18864282930267967],[105.4939454652815,-0.38342128127173031],[105.38240946849746,-0.72663486447523873],[105.44516716346074,-0.89870373269263126],[105.58542145196076,-0.99073453364355635],[106.08498103417395,-1.0510911622313501],[106.29733474893118,-1.1533702409451205],[106.39340198338817,-1.2583206023409279],[106.59854284635107,-1.6262126499931102],[106.71653059719746,-1.9488910508095332],[106.85213764178951,-2.0629955668229574],[107.20156562362001,-2.1822081410519902],[107.77491119970229,-2.036211581555877],[108.24603114479305,-2.0365375679211093],[108.43181660827946,-2.0925013565658648],[108.54936042611926,-2.1789818594430557],[108.63692803070727,-2.2957180625548945],[108.77882610132718,-2.7269199194793909],[108.77233620457919,-2.9601468350402036],[108.62892637192913,-3.3427522103987375],[108.50174141252684,-3.5040193407900975],[108.24073750163753,-3.6900131356636345],[108.08760196745594,-3.7247576792830124],[107.54887444853995,-3.7039859971902467],[107.41557729364686,-3.6669499748039698],[107.13979670585216,-3.5178954229647257],[107.00384613474071,-3.5096603528219159],[106.59759227034573,-3.6066330520346583],[106.48176818837823,-3.7084945907507061],[106.35210543511054,-4.2373585085224947],[106.37313728912444,-5.1807133346427117],[106.43542170842362,-5.3562205879603253],[106.54905833537693,-5.4585076910565418],[106.73632633629046,-5.4859155406303133],[106.92342472606856,-5.4205899775194126],[107.07257734150576,-5.4059457909818027],[107.45671459982626,-5.4949673812434003],[107.86390424228914,-5.7198137367733306],[108.10747434696016,-5.7704150704237227],[108.38367801045261,-5.7739267623985242],[108.52731945408895,-5.8233243981589959],[109.07394345231585,-6.3064570660464128],[109.22490112693978,-6.3393909876274757],[109.54883538230095,-6.3133888569956893],[109.96649756746901,-6.3961598400534223],[110.14721524015054,-6.3554374928703075],[110.54907217989108,-6.0151635165668047],[110.77736955179589,-5.928717509840129],[111.11518845788771,-5.9576337143591465],[111.48504568283487,-6.1470406135202715],[111.77312755483118,-6.1832308109454255],[111.91758798625199,-6.1536471321153119],[112.03960506959176,-6.0477114895034418],[112.0715792107741,-5.9754939591578538],[112.10503717996042,-5.6709693385896651],[112.2181289420619,-5.4664304077705532],[112.51783955118164,-5.2577651908273699],[112.65933100574371,-5.2282084249985559],[112.80336484295889,-5.2403611143065616],[113.05496604888037,-5.3763513827436382],[113.17678160701439,-5.5361724498015841],[113.2258363800939,-5.731045313272003],[113.1800788398849,-6.0829321583117153],[113.19072600893688,-6.151709639660897],[113.29317519709892,-6.3060206085100559],[113.48663868773133,-6.3736488218045881],[114.07075538253859,-6.3835066123375412],[114.20689959474231,-6.4315091360539896],[114.40950391070413,-6.5615840220657988],[114.62951739828209,-6.6161791737533857],[114.7307333958004,-6.6015673004658533],[115.00801824012395,-6.4197036935998009],[115.15475922835221,-6.3695853323216722],[115.49199232771873,-6.3521110543300674],[115.74727570705306,-6.4594516599063825],[115.92697047868174,-6.6160346493805609],[116.0196685850748,-6.7816675450653641],[116.03869481138332,-7.0173488849641874],[115.88551833551668,-7.3862347456533666],[115.88348740651885,-7.5043882700806606],[115.94339314926573,-7.6245334249269723],[116.10603296237279,-7.7244868899986763],[116.46293670939193,-7.7088584158523537],[116.93907988616753,-7.8482552644514705],[117.06180844444121,-7.8406495049598073],[117.41297967377692,-7.6709876903451475],[117.89367313826475,-7.5907606043775413],[118.24280315963306,-7.6393855803937578],[118.60119766470316,-7.7703415948883725],[119.1090692077735,-7.6402026424500269],[119.3043505435467,-7.6849264978009701],[119.62138997105882,-7.9028470386849436],[119.78697789147674,-7.9250701321290604],[120.06658164691875,-7.8176664191404166],[120.33581534476006,-7.5683719541250696],[120.16300724212375,-7.2594899936184802],[120.14105420088413,-6.8358077123545344],[120.05678420860825,-6.7076320788161139],[119.89315260364124,-6.2972867270383315],[119.77168903933332,-6.2116042517228358],[119.27105501115518,-6.0198172198603777],[118.93333229498313,-5.6548067253855265],[118.88572308451683,-5.516988821843138],[118.86761938742411,-5.2491153173271075],[119.09552359264285,-4.3998195125885493],[119.09478744790141,-4.2452199808941433],[119.03483527305038,-4.129015416050664],[118.62690050147805,-3.8852772016626598],[118.52117807202642,-3.7800984496832464],[118.45117798082747,-3.648417816372032],[118.31767421303125,-3.2156011920620933],[118.28444456359719,-2.7083068844181613],[118.30841511996657,-2.5676277803856262],[118.37115009153112,-2.4394500864336432],[118.60978390646603,-2.189864701896719],[118.66819946955574,-2.0871581904940495],[118.80565893457273,-1.7186072455403325],[118.87053350190403,-1.1321891972285145],[119.22924718590097,-0.36637479069797796],[119.20993066654603,0.1617748534339464],[119.07231755351808,0.33386189694040391],[118.92855302074535,0.35720098095392067],[118.23262778884887,0.32085811331186015],[118.12881422760323,0.27122878460787997],[118.04420967388511,0.17633250864362227],[117.97639846327293,-0.16849554498261429],[118.06101092237927,-0.74319798104596835],[118.034956036211,-0.93182641691954637],[117.90728573915274,-1.1317414538206445],[117.56468001125825,-1.3312551600700961],[117.31367253121577,-1.5785485356686126],[117.10356038315467,-1.7127260654442018],[117.00628481172663,-1.8614656652553505],[116.99256657235709,-1.9748140964086935],[117.05778479031304,-2.253208447704349],[117.05455232753505,-2.4087613516342499],[117.00787094527838,-2.6522330354979666],[116.84328780893691,-3.0175695825699322],[116.91881591378555,-3.4367175990103762],[116.91073622229746,-3.5800027916730226],[116.72312473862291,-4.1380428944910692],[116.34735611968026,-4.4835531584959334],[116.16767402322299,-4.5473143462942822],[116.02429624210079,-4.5480719708865731],[115.88659850225952,-4.5081083054246385],[115.64568705431758,-4.372099484212546],[115.52995029838274,-4.3570857618760339],[114.85771524506494,-4.6410542640380852],[114.57842451330959,-4.65534261016467],[114.44694624052043,-4.6037351659499164],[114.26710634610774,-4.459453639295738],[114.18292654143578,-4.34294737401751],[114.07890407132486,-4.0926078811800641],[113.98664190294453,-4.0019344854667844],[113.57715092488868,-3.9377855431015258],[113.16229538684256,-3.744186977118618],[113.01259427496034,-3.7604877071984517],[112.70433984414589,-3.8886378201480287],[112.35288682810112,-3.897050041364726],[111.94337106181437,-4.0436685492243276],[111.70312211699382,-4.0262494488250011],[111.53436609024524,-3.9317950442189074],[111.29746784642541,-3.6487460165772934],[111.19984549128472,-3.5889027991360924],[110.14651314778909,-3.4531359679331195],[109.9335289852651,-3.3469738775222235],[109.83837048998861,-3.239335844914633],[109.77774820290441,-3.1090821555404027],[109.60565173971894,-2.2511005616289133],[109.47476910829201,-2.0865677514558421],[109.34005830744846,-2.042523974260618],[108.90311932119619,-2.1578332298523599],[108.70818256572696,-2.1517708649363785],[108.49191671207068,-2.0411688149472895],[108.37289435865222,-1.886667131176623],[108.30600983731873,-1.6033313695914913],[108.32887998594107,-1.414897694805394],[108.42033718224961,-1.2485670760523115],[108.66259056176072,-1.0384544136569813],[108.72829067409276,-0.89402341913097583],[108.72680476450992,-0.7765617362956011],[108.62492039178888,-0.44632505453763549],[108.62207966877669,-0.1494164806654826],[108.45517606789234,0.25781000663411302],[108.41034158946998,0.85602296905534203],[108.59272169268023,1.6294962090723812],[108.81211848465402,1.9947857578010899],[108.90238102360885,2.3345412464083113],[108.68707284098507,2.3737082727244965],[108.46914415829917,2.5005715717335422],[108.31211173911807,2.7310448267094407],[108.26606821164249,3.0777717183462534],[107.84084436521594,3.2782372200761354],[107.69787564754002,3.4095870720593391],[107.50938618208407,3.9041156053285628],[107.50837041314948,4.1046886763994568],[107.57508545075109,4.2983522650537846],[107.71385735651759,4.4490136070848108],[107.99754697687332,4.6487856365134288],[108.13070844094145,4.7020213299203704],[108.36825542289199,4.7012073473551306],[108.57861756683656,4.5908579042370414],[108.86134947880345,4.1584181672462908],[108.9088893606913,3.5389239057924327],[109.19843695001542,3.3878323356401219],[109.34698853111874,3.1892753080627405],[109.38279358716443,3.0438287482421558],[109.3833229032082,2.8289665505493709],[109.3249593554886,2.5997143188481102],[109.75321722641723,2.5107408647367224],[109.93557580322469,2.4208167922397341],[110.08918026989151,2.2195871268791842],[110.16879074641874,1.8634216296156583],[110.37658315421881,1.6177025285514595],[110.5036388934777,1.5234002995930442],[110.61299255660693,1.4868516093098996],[111.06963902104025,1.5487439266445446],[111.56881306050212,1.5449231714046887],[111.65288478967815,1.58485383735903],[111.93685625679072,1.8731254143237974],[112.42307497603937,2.0552371613959211],[113.03376445041563,2.0575328229174623],[113.45050044269556,1.8642940877514318],[113.65225867534598,1.8640593743517673],[114.06513079922465,1.98028041104704],[114.21232870909581,2.0996397642290914],[114.24684532205386,2.1640377090151355],[114.32885494870644,2.4526835778213019],[114.54858695101453,2.7486776380283615],[114.67843793330468,3.1329775890601219],[115.00465887465187,3.5381503294859602],[115.08316023109791,4.0547411584294917],[115.24035612657163,4.4312002056296844],[115.35052135990763,4.5687769021402271],[115.64044930164381,4.7947542960787208],[115.82642512689672,4.8397000513930326],[116.54359094180198,4.8690968862630033],[117.17107505735349,4.8310745028757607],[117.66204222922815,4.6814195107855472],[118.0244739280495,4.6650125060654029],[118.16062426065535,4.6017105097872912],[118.27184214342066,4.5008410974033435],[118.34809836772466,4.3715002915593448],[118.41312547466357,4.1478026403752786],[118.40217147904224,3.9154693549217763],[118.18314868973827,3.3542679120991736],[118.16652501930045,3.1706128506485713],[118.27299885646106,2.8852780717564608],[118.53495935910206,2.4899246524221721],[118.59075024774265,2.0234602649940983],[118.68435375738008,1.8873517221113205],[118.94823665226458,1.7107572546951171],[119.50365485507839,1.1933330692600039],[119.68026558427829,1.1756257808785053],[120.05110612228034,1.4194584978318525],[120.40227506021832,1.5235652762080982],[120.68466175508247,1.7332497853669993],[121.00572622387284,1.8249202506083415],[121.51708810532367,1.7298249100046994],[121.81456975144783,1.6005560249823416],[122.53885728248059,1.5067920319736234],[122.80015763958693,1.4316762879799136],[123.79482021788419,1.3765766619026594],[123.97032365132571,1.4531538187475526],[124.30012128412343,1.7909970682902037],[124.85205312321479,2.2053934868861584],[125.04461903629343,2.2740085812849289],[124.93323045693668,2.4401265683440174],[124.86114356992628,2.7538085694578212],[124.90422709780856,2.9498922901935596],[125.04217835468901,3.1856277626189557],[124.95616172145354,3.6820121391042808],[125.00340765178044,3.9129861544975464],[125.07714592768612,4.0417975248481408],[125.27224280973415,4.1912266640223388],[125.46537180814363,4.2317229310794549],[125.65912832793447,4.1943442110538358],[126.05407477489126,4.0186560310425099],[126.20146353466151,4.3088951393468182],[126.29573499685104,4.7373607276806577],[126.40382179424286,4.8997340285581155],[126.52150055004741,4.9873382193323259],[126.65953983989677,5.0370165674006557],[126.95518833285708,5.0152254867668224],[127.08556649905513,4.9546649411601926],[127.2226088262785,4.8213946392102507],[127.40837033457356,4.3977486350058328],[127.41820674771346,4.249263820280178],[127.35049025329945,3.980387825953517],[127.34897552276171,3.7225488017447876],[127.29525951792486,3.5392994625289624],[127.2116567670295,3.4225368909928306],[127.09831408341216,3.3343532522641959],[126.86950372846813,3.2693206435305546],[126.68084474568566,3.2988759766220763],[126.24844620452602,3.4742884453721836],[125.93120609728469,2.9603263535182713],[125.94033004085841,2.6909274069153017],[125.82054087277243,2.3657493334077717],[125.68448505750865,2.2214023103892813],[125.46429540960712,2.134833275984561],[125.55922178614036,2.0058605096381306],[125.71845236747365,1.5814931054553303],[125.71769385685235,1.393916828608601],[125.64786751922526,1.2198199660647],[125.34445828936477,0.75602259536094585],[124.80918075919065,0.14915637452178532],[124.53040351898578,-0.035781603644432114],[124.00824320830495,-0.18286427065414268],[123.92134277006724,-0.26991016768970105],[123.87560214333493,-0.45672975275138583],[123.95233172876945,-0.97022466011526798],[124.03876389311522,-1.0965044988632344],[124.16024474087611,-1.1583665346619978],[124.68227780406586,-1.1370862878092631],[125.20421611603778,-1.1970132091160068],[125.5432387504087,-1.2977672770294706],[126.44397341411623,-1.3105227278063354],[126.60297301217599,-1.2376912106989673],[126.67694096195598,-1.1273669818306571],[126.69489169053126,-0.97398411892455306],[126.60824142052647,-0.41935731260792425],[126.63448107159446,-0.19473837722377194],[126.70142983371038,-0.017158855085701918],[126.86495805884572,0.1745118899626639],[126.91276180012073,0.29084088630966659],[126.91194863978734,0.40930437235831052],[126.7961449410565,0.71994134034293644],[126.79957504477562,0.92087335017451555],[127.16829369461264,2.0312993106620878],[127.56464412330514,2.5072260028517213],[127.86083343443761,2.6905122626795395],[128.15684322523771,2.959616144770254],[128.3344896907725,3.0446791763216541],[128.6287404305545,3.0958606572960212],[128.8225352900142,3.045158541036042],[128.94614471292255,2.9589796045216956],[129.12462139556462,2.7162465783781213],[129.1774715229399,2.5731321017812592],[129.18470525335533,2.4207425925640744],[129.09563777430347,1.9857553855249483],[129.20164635511674,1.5771427397520119],[129.15745034566098,0.86876822920511498],[129.18601524465322,0.71330494925979337],[129.28685830515388,0.58858578190276045],[129.77821142200457,0.32845105217960419],[129.89934525768766,0.30828207006788055],[130.28749215116824,0.42057274771102426],[130.79073409270524,0.49430002420641267],[131.10843852374327,0.45222286827089725],[131.52482666080215,0.28309191965244462],[131.85212441043578,0.036898184594595068],[131.9220988028419,0.030464905991070965],[132.35280376086948,0.14194417448747992],[132.69189995819298,0.13606020836319771],[133.04737595538751,0.044255694668714429],[133.5472256292291,-0.20439339570368029],[134.114787297823,-0.26528397829833694],[134.45022443982049,-0.45822303317673829],[134.56058392440386,-0.48535800110891325],[134.8310061482124,-0.43203723885657758],[135.13336136470957,-0.21966511767411701],[135.30903156204801,-0.15804314236996858],[135.95453366783016,-0.22560608125975584],[136.13226000304488,-0.30611169981659647],[136.70325436641681,-0.71803563596014275],[136.79659265004216,-0.82674331222386122],[136.93116916336064,-1.0867051221198676],[137.06280725088749,-1.183095627963459],[137.18978697046197,-1.1951531587720585],[137.71136400483462,-0.99340624401598721],[138.00476025474853,-0.9935077078522907],[138.32855955718401,-1.1564367115376843],[138.86797492159101,-1.3419839629501069],[139.87854731778842,-1.8328584714370171],[140.74231306143761,-1.9614076906733013],[141.18425780407091,-2.1575521669460347],[141.39425625070291,-2.3419151725135068],[141.47252403307624,-2.6101747849267154],[141.47499986899879,-6.3268403229468957],[141.43266900496135,-6.6492181012645997],[141.47519531188334,-6.9255620375286808],[141.45409036499066,-9.2603644657785029],[141.36433639251953,-9.4308157797090857],[141.25802529026527,-9.5292379984962583],[141.08117274948631,-9.6056094906581961],[140.93663587104373,-9.6155127418204316],[140.79532714847139,-9.5835623343350687],[140.66911028761936,-9.5124406945187285],[139.8679790462642,-8.7611336821859176],[139.70612901370953,-8.6832997238837741],[139.40095039446174,-8.6959197240931179],[139.01310588733293,-8.8719110492387028],[138.88341100045426,-8.899415524013051],[137.59680224716726,-8.8827701566149244],[137.45678008205761,-8.8463238853915271],[137.3330561929248,-8.7713109753964567],[137.23599098931007,-8.6640128689112323],[137.17371253175784,-8.5334145108319799],[137.1533231301419,-8.3421085594362516],[137.18667690718777,-8.201317711136884],[137.37403810241224,-7.733555834264509],[137.60457734633485,-7.3479297793491307],[137.97232676541935,-7.0358972909784896],[138.07269174058629,-6.8688868863747441],[138.07677629464382,-6.8112864652747778],[137.84468382958482,-6.1826836119009121],[137.59642074391252,-5.8318351428615296],[137.42537212574257,-5.6570543078382078],[137.13760969151281,-5.4961293901805615],[136.49248964881446,-5.3030732144917758],[135.88871144281367,-5.0389067285600841],[135.41826105615323,-4.9547370568538698],[135.26469354522763,-5.0042354386978065],[135.16846713782326,-5.1042799975540554],[135.13242079790416,-5.3312937897173827],[135.23631761003463,-5.6098104454843147],[135.27554575914309,-5.9773752866250511],[135.3803204784096,-6.2572573409758929],[135.37779402264636,-6.4066301740879892],[135.0886879115269,-7.0245188944814831],[134.99198824127782,-7.1361528354198356],[134.86689389947318,-7.2146663288089847],[134.30623540082976,-7.3963513112337056],[134.06022173097986,-7.3877933735413039],[133.88511974518389,-7.2960837464969499],[133.64749165757246,-7.0513548449317103],[133.57222441558761,-6.8779180862570737],[133.53640218282885,-6.5550270309228784],[133.41143508250548,-6.4132363488484581],[133.27195023317111,-6.3722560691538517],[132.95744480030973,-6.47414230832723],[132.81738958739845,-6.4862186751895381],[132.58587656060081,-6.4511281069006365],[132.40596542262995,-6.3640150714363335],[132.30154251726503,-6.2558257763575913],[132.23400688010437,-6.1214829148137397],[132.13465750550245,-5.6652967390412714],[132.15087224602527,-5.4693712478866008],[132.2123624376564,-5.3348938055703838],[132.31049651768711,-5.2242821945376043],[132.64912939998777,-5.0574280447522915],[132.76736459117734,-4.9307764585686318],[132.81956685988172,-4.7720901060485712],[132.80702633541901,-4.6722624673124891],[132.73770697355272,-4.5507856453256963],[132.41423358896196,-4.2167132363668101],[132.19598308926103,-3.549904977833386],[132.09556263229723,-3.4596961104395358],[131.68130916101867,-3.2865892301584609],[131.5164631672543,-3.1136240563003477],[131.46358069957091,-2.9794086619110534],[131.45121744339508,-2.8356815685149552],[131.58892173470184,-2.3957093777659213],[131.57076888177713,-2.2283823049073344],[131.43595320302157,-2.0096165375799728],[131.33313855303183,-1.9603058453590931],[131.09382302166142,-1.9723762185710798],[130.99037186613521,-2.0231725671766516],[130.89023666267548,-2.1393935273863836],[130.75193412979377,-2.4231280382325111],[130.74844289728119,-2.52609759448932],[130.78564553673652,-2.6271501047705086],[130.9867428326136,-2.8410475446676586],[131.26866359067202,-3.0351535703024108],[131.35167408568626,-3.1625566902843465],[131.39603832638846,-3.359762149140137],[131.24885506512302,-4.0855289825141545],[131.16472281355425,-4.2031116617949769],[131.05046454896348,-4.2917060814476713],[130.81984245976065,-4.3561896490603651],[130.63012963917009,-4.3250623398103318],[129.86076015011577,-3.953345779025021],[129.67205050159825,-3.915629508728538],[129.16023737562185,-3.9351880725790509],[128.85207203560546,-4.0818446425820039],[128.54978315238344,-4.1191527011481437],[128.0324790320285,-4.2669191022058675],[127.84879778719534,-4.252856694826491],[127.53482179910363,-4.1118526814361882],[127.42388781465888,-4.1069175154375017],[126.79657756549601,-4.3101668286654045],[126.56228995291653,-4.3067937818411082],[125.90281808254714,-3.9970161466093357],[125.64024873097549,-3.6961928566655837],[125.56864107745278,-3.5260319601620349],[125.52734184159452,-3.1769998153307224],[125.5808109121288,-2.7555288721307605],[125.45787384262307,-2.5262808373414072],[125.38043586542867,-2.4633813851796984],[125.21230527299512,-2.4196406202660095],[124.54181084592749,-2.5057614750879189],[124.11127636918995,-2.455753150737884],[123.8062198821694,-2.4944710007743915],[123.35229283302795,-2.3580715247277353],[122.95609542924583,-2.4328865647324052],[122.84129095079038,-2.5203367283804941],[122.77833989005796,-2.6572730609505903],[122.77956927341648,-2.7918197846141863],[122.90958401976215,-3.2691536194498005],[122.95769830563063,-3.363494944517285],[123.08179692953586,-3.4626811293499551],[123.3573059763158,-3.5202626115113187],[123.49051940847731,-3.5836565007423409],[123.6880876534409,-3.8074198397104198],[123.74610148535788,-4.0428187854144095],[123.68031220717651,-4.4171249926066736],[123.72478269508578,-4.6806601408012867],[123.79135616824951,-4.7768125521913367],[124.01745998623015,-4.9613693134781185],[124.08886728138243,-5.0835404920895906],[124.16696747466443,-5.3353195438098355],[124.46860262010219,-5.6774971862312515],[124.54048077341477,-5.9864225018591721],[124.5160062455312,-6.1774275698380876],[124.42116121081546,-6.3450174200633418],[124.3119679691695,-6.4401498866266822],[124.13296581420697,-6.5111440203809892],[123.9882511824963,-6.5167140818186384],[123.84801052772501,-6.4805767193678898],[123.72400911536104,-6.4057636192629284],[123.46868423134374,-6.1103864689409511],[123.36238532697361,-6.0500949920375859],[123.26633303733355,-6.0385797776030081],[122.88568221063348,-6.1648684167790373],[122.57740287027792,-6.1577102465905442],[122.4497663939615,-6.1224859228749766],[122.16352559124064,-5.9718074984244884],[121.91145062647192,-5.959051005858182],[121.77386955506907,-5.9193453907731417],[121.45270652739677,-5.70309269586176],[121.31771404187393,-5.684967053993395],[121.18968483925107,-5.7315744072549935],[121.08933723769972,-5.841678024388238],[121.04832185775402,-5.9720790465555513],[121.0080310074356,-6.5604488542215371],[121.21792787046837,-6.8206946610013723],[121.27005810033445,-6.9603466650464494],[121.27870246928958,-7.1091603673223611],[121.24309265741527,-7.2539090612645625],[121.13719236015085,-7.4574443845635807],[120.99591913354837,-7.6213155525255329],[121.20557737564724,-7.8939785072239204],[121.54979618270865,-8.0110513880903529],[122.24740150149893,-7.9424613030821405],[122.58158952398995,-7.6699655671410927],[122.80599582917758,-7.5959772066573512],[122.94834991565035,-7.6049843175675615],[123.26671748662103,-7.7286770388368931],[123.43138285540552,-7.7566986211586899],[123.7738295564214,-7.6915105839691691],[124.08726637939611,-7.7208146204079835],[124.49896037419876,-7.6366534866576439],[125.10810357080054,-7.6852885719943318],[125.31174538780455,-7.6606674811756603],[125.63452316124,-7.298801652034153],[125.79711621628594,-7.1973297736935429],[125.93776312202525,-7.165811891361308],[126.19178957127305,-7.1687260931273906],[126.58429163111555,-7.0734507151816164],[126.96327675580919,-7.125239425795983],[127.34892731975727,-7.0143242471130467],[127.74446358276361,-7.0913645968292975],[127.88095376911399,-7.0813490619524373],[127.99310152024445,-7.023891333775345],[128.32997226925067,-6.6680337604606263],[128.45821578798763,-6.5993903179095961],[128.60078969387547,-6.5705631837618457],[128.79227654472751,-6.597764979312756],[128.99390818989747,-6.7295918322719821],[129.32112214487316,-7.3551462332347715],[129.48057319730373,-7.3152464140405087],[129.9229121798046,-7.3329899163013517],[130.10352140831631,-7.4140775060791473],[130.35812984500603,-7.6369576448494625],[130.63277303229171,-7.5243004042055066],[130.86296029236908,-7.167994869812981],[131.18943385210548,-6.8023727441550124],[131.29759638820767,-6.7118218456454368],[131.4727151820116,-6.644552897840347],[131.93656129942556,-6.6058714677931523],[132.173190380645,-6.6695589893701621],[132.29019495969871,-6.7601848103342652],[132.3755447102692,-6.8810919276703046],[132.46861251585923,-7.260220849485675],[132.44547762754198,-7.4013622375422008],[132.38333085640849,-7.5301795241059875],[132.1048298179864,-7.7869243350938557],[131.71282652902767,-8.3171535342167342],[131.46321022497207,-8.5391331203309857],[130.87686673199212,-8.8384132029822879],[130.72958095378218,-8.8466542387139651],[130.54161752240799,-8.7906514140576206],[130.17385168912134,-8.454816870038993],[129.71136031861923,-8.5397522478581713],[129.48326949100309,-8.4833755693328143],[129.33534853385112,-8.3663720437035973],[129.10959072673461,-8.100849354032178],[129.0493666750684,-7.983763319576151],[128.70078642688981,-8.1102935619232994],[128.48512232778111,-8.5100662840973644],[128.27885935639665,-8.6841722084231439],[128.10276157755362,-8.7480490569648008],[127.91551474606221,-8.7426357779688235],[127.57402374785394,-8.6238365221766209],[127.45342348420247,-8.5277364605679367],[127.26626255514448,-8.2695698796961938],[127.11645679209121,-8.2116261027510031],[126.93817515531278,-8.237119467518216],[126.71025537727164,-8.3888370527528551],[126.5712820610317,-8.4393873650658922],[126.13194574276677,-8.4188684661999211],[125.76084675913727,-8.5037875965134386],[125.6163259489245,-8.7094986853107574],[125.64488163082189,-9.1810774817685488],[125.54425941525376,-9.661270326208971],[125.33893944098521,-9.9939590813516563],[124.67810438950858,-10.580362874883571],[124.31356677099092,-10.684062449380381],[123.97727602633489,-10.828448162825042],[123.44633783576718,-11.248093264221771],[122.9481839241292,-11.400352109642764],[122.80436358477739,-11.406254174625701],[122.62128608808001,-11.350419213594295],[122.26183537181058,-11.015800089190966],[121.84396094852363,-11.101021863224863],[121.50896761678092,-11.022274533701111],[121.39415419759841,-10.94556202695208],[121.27058151678668,-10.799756906577631],[121.17220083405634,-10.569649953000916],[120.48671451371696,-10.790732907770407],[120.33552817772613,-10.782192374807995],[119.94225923213209,-10.655907610099081],[119.48472706315519,-10.308477824167154],[118.9259270136416,-10.17977473590309],[118.78822001299615,-10.107786082714158],[118.67926371324975,-9.9969975049622288],[118.36284710239231,-9.4459994382426],[118.24844233222223,-9.3849460889432113],[118.08545311761831,-9.381538449243493],[117.17028593124569,-9.5859540563316674],[116.98879913968102,-9.5926960322223103],[116.48795269426201,-9.4309788651193927],[115.7145348095942,-9.3037035489005326],[115.4217620742573,-9.2979111733830742],[115.10479607697906,-9.3463010944947484],[114.76447260717021,-9.2581213194314671],[114.44588586059388,-9.2508461069462413],[113.22132109754384,-8.8295336839982159],[112.65007082483631,-8.9068791964916603],[112.10548714157382,-8.8271693937510918],[111.42645876186965,-8.7968649638577023],[110.50234304633335,-8.6382999720020024],[109.83783206053658,-8.3526911145972491],[109.26092571250039,-8.2130719539353478],[108.8419457604242,-8.1976071146857166],[108.51336664141544,-8.2922129187711935],[108.17154749117995,-8.2796682807342137],[107.68074712357684,-8.1736770698744543],[107.20950105819996,-7.9740865801136698],[106.31455934551808,-7.8485249714090104],[106.14508938165589,-7.7509666189594215],[105.91023611701267,-7.4609622397216357],[105.79401953766342,-7.3815230660343527],[105.13476260551097,-7.3194965087036943],[104.92192539241356,-7.2063781322821168],[104.67052493106371,-6.8288808957901512],[104.5542970759784,-6.4747803753568141],[104.13417942562367,-6.1623859290698704],[103.49568818087965,-5.4715233640907881],[103.23132507502982,-5.3289764833264233],[103.13257326291986,-5.3248925722263447],[103.02569139785696,-5.3644028488225484],[102.93746019571186,-5.4465161592590059],[102.73571476345086,-5.8152065869304188],[102.62416539014384,-5.9064640558976693],[102.49122022211256,-5.9621126913223543],[102.265625479633,-5.9822031627597045],[102.11868231956352,-5.9537234944269564],[101.98675270290379,-5.883027242421873],[101.73905137329248,-5.6558515579227722],[101.62538642194815,-5.4379985905046357],[101.62922787020169,-5.1923059372723719],[101.74964793705516,-4.9781129306731859],[102.08017458641281,-4.7043483774206249],[102.11807848594536,-4.5523542421000656],[102.0842876712407,-4.4209044899278283],[101.81777965052923,-4.0275359243749849],[101.2715311102027,-3.5820047953694605],[101.17882196303964,-3.5383868212524057],[101.00453462868033,-3.5403381639900049],[100.7330126046469,-3.7493634174455694],[100.55329712435589,-3.8193910978831807],[100.36044680357936,-3.8161842832507058],[100.18315895020311,-3.7402201671896704],[99.964870250016361,-3.5511190583877963],[99.818318160378752,-3.3144248594603862],[99.61529625169581,-3.1184087011620161],[99.437655628602855,-2.7702781939397165],[99.241337608834328,-2.5972499466241459],[99.057719495504642,-2.3544831259294403],[98.447141434323214,-1.9329080812316295],[98.151133693272996,-1.4170087975915797],[98.047585097851481,-0.99879231960426673],[97.846316706264417,-0.71674317474452642],[97.810997142893115,-0.51562198526998393],[97.849413630619807,-0.21231862756248782],[97.808041244686251,-0.071320151752654271],[97.705914051820983,0.045459377526319969],[97.448943435106344,0.15601119196201374],[97.337391721544265,0.23724201351107965],[97.177313554599763,0.47499372313812283],[97.004185104791489,0.65016968742282732],[96.862218900698537,0.91706526617516659],[96.628586235805273,1.2111345793729578],[96.586915291068138,1.3439017224140901],[96.56235789626794,1.6567004521427009],[96.513248312916886,1.745575295385714],[96.418736165226321,1.8212776778969562],[96.138339849235678,1.9159709240394844],[95.87216639964042,2.0931582937829516],[95.537671697246608,2.2365202101673423],[95.428052513847291,2.3327353502520398],[95.311217309032088,2.5004514627996861],[95.223503861858418,2.7530335740312739],[95.246798421419541,2.9923086764507336],[95.379072814093902,3.1930540280422908],[95.656331413049159,3.4060313964088169],[95.750340687453559,3.5321477058819211],[95.771524363664298,3.6655939876673798],[95.693616379444848,3.8479483507350998],[95.074574344351916,4.491446380976484],[94.766768684608707,5.0949527363617904]],[[122.50173976757766,-10.248497940182672],[122.73098811170831,-10.183607596622803],[122.84819295716596,-10.092300597775028],[123.12652558912669,-9.7183075541245287],[123.21545276992893,-9.4317967477423164],[123.20573261282141,-9.3223628031115897],[123.15508196757133,-9.2234637014590675],[123.06161764653544,-9.1426798300315006],[122.86990186666063,-9.1150362652999632],[122.55825387743016,-9.2171021709467453],[122.15661076402253,-9.2659748158695248],[121.83299340794537,-9.3890533692661862],[121.45829836184953,-9.415730638657509],[121.31572205564395,-9.5261542390934526],[121.27008612600665,-9.6314903012368021],[121.26893720260028,-9.7440039200327533],[121.39144532940179,-10.07608164768096],[121.75765506485017,-9.946265093552725],[121.95377513373234,-9.9294625007436839],[122.23124355708524,-10.005687727512912],[122.50173976757766,-10.248497940182672]],[[128.63637993905047,-1.7304839575744229],[128.56452680411843,-2.077782588459701],[128.66302905440435,-2.2602793280154305],[128.84120758631846,-2.3323393171805145],[129.00876624287574,-2.3201729040611792],[129.18945144262477,-2.2059378400290637],[129.24808474537031,-2.0609843064146802],[129.25259455901343,-1.7510241690438055],[129.33090747931251,-1.5783579013709057],[129.46787628152609,-1.4472621321865269],[129.78941252792427,-1.2783830748489999],[129.87550816980371,-1.1645021891064444],[129.90148079050832,-1.0139547686514194],[129.86234718446877,-0.85194832526956576],[129.796773736213,-0.76649425540560112],[129.69631163395729,-0.70873524780274022],[129.22913595515692,-0.64496787112110254],[129.04279462917484,-0.70685770097104006],[128.96621281391498,-0.80414767175386126],[128.85370845755614,-1.1487518858204386],[128.66291523873795,-1.412202098637527],[128.63637993905047,-1.7304839575744229]]],[[[105.19478440813459,3.0204913673551892],[105.23279279001243,3.2567312388917924],[105.30782452723317,3.3801842728428939],[105.41501971805879,3.4770326298034249],[105.59221559603471,3.551211900100228],[105.79913108366047,3.5756820072398598],[106.04470624669469,3.6953009044510363],[106.18306522971652,3.7268505927577551],[106.370681071456,3.706499123498701],[106.5373901626065,3.6180572482336846],[106.72450996444672,3.3945712314565712],[106.78299261290927,3.1961982394134165],[106.75583516029646,2.9272029255445213],[106.61785147455565,2.7179991379929254],[106.44437884889848,2.615946649397686],[106.20633007987907,2.5581306190248521],[105.94220202228384,2.3984002405726534],[105.79302988979565,2.3650969104398314],[105.59180205282816,2.3931236186702498],[105.34010927297709,2.5497500585398463],[105.22563978977715,2.7548984264409104],[105.19478440813459,3.0204913673551892]]]],"type":"MultiPolygon"},"properties":{"alpha2":"ID"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-10.8652743784004,51.981044836589412],[-10.881474827842036,52.226862017576309],[-10.77389973643567,52.477890945107688],[-10.638778083318236,52.618723412932397],[-10.391895533412622,52.721835831436003],[-10.251703312621618,52.93957965502851],[-10.452317685122699,53.067400329864213],[-10.571084292851769,53.273227653053681],[-10.611333219746069,53.61770135544166],[-10.718875703943482,53.768149287233683],[-10.76001003433046,53.907632237151027],[-10.759268083188729,54.053052235646867],[-10.693760165457084,54.234977726825214],[-10.435709221210963,54.57641145215959],[-10.170192756702679,54.744389845168733],[-10.012977615761722,54.775451402477913],[-9.2501178957903978,54.793749903950179],[-9.1218467140159252,55.029995729769311],[-8.8278584360772712,55.218114603672191],[-8.627854822455145,55.499786104775843],[-8.4656865593959427,55.608049638576077],[-7.8550915955915226,55.739170219251271],[-7.6597501087354267,55.747344242536187],[-7.4636957036897025,55.842326963466036],[-7.3262287487318227,55.865174939138399],[-7.1877950638057619,55.849196560762955],[-6.7455331306919533,55.688433603092363],[-6.5556284939179195,55.52884552073369],[-6.4876557324693467,55.39531246074305],[-6.4622999779569996,55.247635583329838],[-6.5191021111817804,55.006169965155507],[-6.7089520495471282,54.807418698608032],[-6.3987447994082487,54.583662235100064],[-6.1333923868312583,54.581231121358506],[-5.9499529391149313,54.510464301464602],[-5.8382396454079872,54.413561953549646],[-5.700067690887165,54.21979533571885],[-5.6574461873334574,54.03183880935542],[-5.7006915199488857,53.81481592895738],[-5.6465893380002949,53.644670902747826],[-5.6345596726801848,53.388868303841548],[-5.5345239938591018,53.009687422058114],[-5.5318320064509754,52.862779415148175],[-5.5719588675029028,52.721432167871917],[-5.8280395750476641,52.271806252032214],[-5.843791919214306,52.126435475001806],[-5.8998905585529453,51.989192363078622],[-5.9937698979077103,51.874434362275551],[-6.1171750409358543,51.79225224877019],[-6.4811089426543296,51.695041306511122],[-6.687141884508117,51.701332012287956],[-6.8098724707642786,51.666023273880072],[-7.2696532450384357,51.632493387233325],[-7.4673467956939286,51.518588756051798],[-8.7018820582275254,51.097767604581378],[-9.7316811353758368,50.974070797777053],[-10.016645271708589,51.017662638576603],[-10.158386365405486,51.1027100478778],[-10.348356002404516,51.155845831549101],[-10.464079759978404,51.237641060112196],[-10.780968576867213,51.561780513987514],[-10.866804454378933,51.761469538818375],[-10.8652743784004,51.981044836589412]]],"type":"Polygon"},"properties":{"alpha2":"IE"},"type":"Feature"}, +{"geometry":{"coordinates":[[[33.779525089915438,31.026681011940177],[33.745430668560068,31.215412140889921],[33.784892506708466,31.403094404122623],[34.233846739599869,32.128691293520134],[34.457323151637048,32.798976452552218],[34.659440820995663,33.303238143380028],[34.739213960380518,33.420577691032292],[34.93400155842086,33.552150960700438],[35.165217328521805,33.589024476824584],[35.285399293593464,33.68508454082567],[35.712859616309963,33.906603747874726],[35.956004950947651,33.924007056471261],[36.178482429394442,33.824376235486319],[36.279446216157353,33.717242077179911],[36.357645692131733,33.537815405836803],[36.403520914776095,33.19086860257066],[36.404214801099769,32.854159612580588],[36.204722315449388,32.45978181744762],[36.043283568503021,32.306122290550313],[35.942687539847434,32.084279111179811],[35.834540430199119,31.983457461564431],[35.69242881739406,31.916265544519543],[35.832155027296686,31.802284184874999],[35.926506755666296,31.632332594816582],[35.950440425374936,31.488107477524725],[35.920314832181049,31.266670956597508],[35.93303787679428,31.054219012457505],[35.647400375062894,30.359901342455384],[35.628730572340899,30.029254161006154],[35.549121939561225,29.828504036077074],[35.448456403105709,29.399174725627748],[35.378965147757079,29.262658069867101],[35.206814922826048,29.079333226107245],[35.033838502673404,28.994513587546908],[34.841632890412043,28.981390492273135],[34.658733095008486,29.041912206191903],[34.51229257592945,29.167093624086853],[34.405006161071164,29.379849183499569],[34.199077921027126,29.992142168944426],[34.064776967820364,30.262184038813288],[33.779525089915438,31.026681011940177]]],"type":"Polygon"},"properties":{"alpha2":"IL"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-5.1743941387105705,54.386254627321762],[-5.0422122658711492,54.58789865670915],[-4.76075803053849,54.808251369920157],[-4.5481420436436357,54.891449184327868],[-4.3240198250201551,54.897572791891825],[-4.0920171836744448,54.803004051196019],[-3.9614792052055163,54.669972316991],[-3.8492256750588876,54.373274286329632],[-3.8463773428277657,54.179497563518346],[-3.9174259990034539,53.999193321317534],[-4.0354507963648931,53.856797833446329],[-4.3489645137647628,53.635187404090068],[-4.538012491580508,53.564830763153083],[-4.857452359630722,53.57810766256214],[-5.0989494544265428,53.684124346639365],[-5.2460148519222312,53.879851347682092],[-5.2825311673814861,54.121933665877833],[-5.2465209423648265,54.265338429380634],[-5.1743941387105705,54.386254627321762]]],"type":"Polygon"},"properties":{"alpha2":"IM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[67.682416602953907,23.727569677125295],[67.678998368817787,23.973361309077934],[67.793124230241219,24.19107786429803],[68.071869055927053,24.381183581065418],[68.292665467856708,24.442615409245018],[68.401383256409162,24.638661741970928],[68.56093755561497,24.762328144220387],[68.807183174940874,24.812759853837118],[69.585142158930509,24.772212672286468],[69.823862801431488,24.704509373185459],[70.370492762791784,24.897089332041379],[70.200031882161895,25.210837423844982],[70.057357393131852,25.251779050343117],[69.940408867814327,25.326219742815844],[69.684750900566655,25.632328244205254],[69.613416983204715,25.797004475637625],[69.584670727622111,26.151042901103644],[69.192566165523886,26.363054484389671],[69.030672860523794,26.554866529161544],[68.982385877004319,26.801178668419869],[69.095958235210631,27.357899704614294],[69.508472679094751,27.795359703171819],[69.789493172771671,28.200793657220242],[70.205689747730773,28.483899149609126],[70.436551017554251,28.523730836934462],[70.699310535561494,28.464512030266786],[70.874833197801848,28.379490752212494],[70.982191435257477,28.267807743155405],[71.499300879675459,28.398633550127595],[71.754001033289384,28.695530547995805],[71.98409462084085,29.100869642783831],[72.578330082866302,29.445119629252069],[72.778476051020405,29.768484041277468],[72.992557560320662,30.248002812093908],[73.146401637599965,30.375307746488225],[73.401964638306254,30.483004623049627],[73.436217294066012,30.623119847120819],[73.507580298735959,30.745607454668363],[74.055101755492302,31.303245352116512],[74.079914940821467,31.460913361966956],[74.031095154452885,31.718799418677907],[74.036433080497687,31.85841134425343],[74.102807408091195,32.031962478774723],[74.265880182749385,32.22381426614642],[74.231207859183343,32.286990573973917],[74.000074715753641,32.4155440930599],[73.852935924184393,32.669296385304044],[73.655103145225425,32.831401216650711],[73.547525820070391,32.985488342916341],[73.504661008723531,33.21564863634255],[73.550749698214958,33.408327678169641],[73.484657378793102,33.585398116113048],[73.490168637004857,33.796104503514059],[73.301848517146865,34.294343562699723],[73.323545897330533,34.545403171990174],[73.562087660004025,34.954149958562745],[73.757602387499077,35.109818666605292],[74.279107831380102,35.264671929254369],[75.257957398413424,35.133779176898514],[75.619722649280206,35.019660753774964],[75.916603262396876,35.153904070329709],[76.405820722866366,35.229986626746005],[76.820212480070381,35.554382374059827],[77.570762958856236,35.940299684562028],[77.742336742427383,35.992369665256724],[78.172797164191707,35.962307773230933],[78.38629991773378,35.842598482409343],[78.517398691229545,35.635893737260702],[78.532333244968044,35.33019228207845],[78.663704579530972,35.049147470740976],[78.921938299527042,34.950277251380591],[79.262060199896652,34.708344120476305],[79.433787082344878,34.489203713503414],[79.4674100967312,34.352680949100737],[79.462015514841696,34.137148955037013],[79.393254752118594,33.961510893610573],[79.272289581247577,33.830234291785843],[79.282104228266249,33.736020139603454],[79.498168013198438,33.544081647358446],[79.586915603869528,33.383419891204028],[79.729460289936654,32.766923371884545],[79.717829353543067,32.465557220284708],[79.668401295483477,32.281865739915467],[79.587889385486619,32.163622270939676],[79.231040979233441,31.916655409130282],[79.228012028626367,31.886933364344728],[79.428781201235168,31.785158515568401],[79.684465446882626,31.47684683813327],[79.973870688502174,31.434715255824269],[80.414989713013938,31.21317651762287],[80.522586997188228,31.122284946532915],[80.624380890922353,30.969544642519093],[80.893716561411921,30.867836435831752],[81.243224905795316,30.661794093835276],[81.359643826923985,30.565837096752269],[81.442140852329857,30.43952260318509],[81.50976945439983,30.177049016172639],[81.45381151154281,29.934678463578248],[81.328300319105125,29.77927804508764],[81.071910336531687,29.667649392837792],[80.811862767745154,29.426845657907727],[80.749741196683303,29.298431666944072],[80.728214282023487,29.128478468294908],[81.447261211409227,28.74947622538124],[81.637887822903295,28.563663846180567],[81.903640842087711,28.409087409482439],[82.124940082152733,28.394625390508317],[82.566843524287023,28.178834440428936],[82.836931277988214,28.147033685390014],[82.998393639396923,28.056411050431521],[83.100546135470793,27.94548604671164],[83.220578100232984,27.917328181334522],[83.447329023034229,27.965081354290032],[83.75324742316451,27.91136645471839],[84.037056283082279,27.988197032455393],[84.220839252264284,27.973968580347503],[84.874846344585961,27.722615521717803],[85.020724638772208,27.583674324970566],[85.111122125138621,27.398334972248559],[85.32844603123749,27.290089053885495],[85.735333066765662,27.321134248781501],[85.881032865053342,27.271308694063382],[86.036078853972569,27.147592371447551],[86.503278452602331,27.054808288469857],[86.711225685254007,26.97642537854281],[87.036526756440608,27.054742271045196],[87.223893859159745,27.010055061192713],[87.358125940682115,26.919391646125476],[87.531473131508946,26.924187361645718],[87.491315015570748,27.052697887255608],[87.4899277391429,27.206257881337898],[87.623640246077784,27.745238143341918],[87.610339838635824,28.007766697755955],[87.687935896216189,28.191427993950576],[87.790277175917552,28.301059936341851],[87.920527008445831,28.375409660280486],[88.447869879277462,28.575880265650035],[88.603595169120069,28.592458431469908],[89.027483264783939,28.453790733831809],[89.148313588480434,28.368839574030268],[89.239051576164826,28.25229222628175],[89.344320391423693,27.933851089940312],[89.299058997344517,27.605098100562596],[89.389630773378585,27.344901988213021],[89.821400405946505,27.216831759869041],[89.929408358933983,27.23369781961658],[90.153590788749185,27.351605358586152],[90.29721435314795,27.387689627554817],[90.445108043418628,27.380122653540358],[90.792036483111303,27.275811017918059],[91.152420673471013,27.291316548678942],[91.095016284338243,27.549478750299276],[91.151686896437823,27.898215731552938],[91.241795978537212,28.07219866412829],[91.391843031640022,28.198197034591864],[91.578787005824253,28.25686268059119],[91.850157412002915,28.242012324526542],[92.235917688923521,28.333411367117016],[92.377002157601908,28.526378143697379],[92.70314765543101,28.718678772767845],[92.872621448159123,28.954897184762761],[92.978917666181971,29.048079775653317],[93.107045432766199,29.107788708328197],[93.582719756723677,29.206343531020281],[93.727397321512314,29.36621603894622],[94.464297840195371,29.786231150432872],[94.615000429283626,29.812049749131539],[94.766459210871673,29.791118722483724],[95.036775115046453,29.635329880685578],[95.204127495606713,29.595402175058613],[95.825505218290843,29.900775208582377],[96.012292877976535,29.946404173610169],[96.247224110921621,29.899819593571948],[96.459593585536538,29.738600014115143],[96.693890179044232,29.617136648063113],[96.808264781919661,29.461299425346336],[97.069370297565754,28.867172019763586],[97.302923401460035,28.814614971872803],[97.62458915017001,28.616061992817265],[97.75309573349837,28.471508715276808],[97.817125741822167,28.288999126376744],[97.834342957688733,27.913250023275712],[97.807443054031182,27.774299296241963],[97.680929533799187,27.576883248986128],[97.488968693812112,27.441874680739797],[97.571799233430511,27.286304897902721],[97.601438311824779,27.09559179125306],[97.55663718173507,26.907861072512866],[97.44407120509625,26.751084591753234],[97.28051269622523,26.648622022041586],[97.090331849401835,26.61574027151692],[96.763150826975206,26.671169697943945],[96.55726041878826,26.808284617671362],[96.369325210258808,26.780133092237861],[95.592218481945324,26.258013095847708],[95.632070760036456,26.02972660190385],[95.593320597683473,25.847998186903364],[95.461342703578822,25.652752851187902],[95.345783959704775,25.400787675328527],[95.171852813685831,25.224067586339107],[95.205575343005876,25.03334224743482],[95.180859084273976,24.888007513043934],[95.023662313959306,24.529315140966247],[94.742795881208053,24.094949655484406],[94.576419611668271,23.656558767699522],[94.457821615337636,23.50134915725739],[94.288806456137848,23.403423757753924],[94.095169571042092,23.37772814260941],[93.883571919692329,23.424334958529116],[93.838304873707642,22.969247434623814],[93.718167409110421,22.744789962293154],[93.603626303051001,22.628060577948428],[93.650757631200449,22.244881500043384],[93.61633730431592,22.047922005125809],[93.507497563477699,21.880196895630263],[93.234141785978991,21.60575798104334],[93.105620158152462,21.528232402233773],[92.959880684971267,21.492343265356386],[92.761231565457848,21.514370229263736],[92.57155826476442,21.478430945975589],[92.429254530043934,21.500148646999204],[92.299035900157705,21.561509219712477],[92.162607743038606,21.695775177893495],[92.099174861206421,21.824997006373096],[92.006529974136441,22.520373133241769],[91.96639571216059,22.601262612123477],[91.72440804179179,22.491068436432716],[91.525696990517957,22.488821926862308],[91.081752486353238,22.662479828952712],[90.964182842320071,22.748679439722494],[90.876475161084798,22.865128384532152],[90.668602541049992,23.52863429459892],[90.768095171979226,24.106293366386332],[91.072839689749927,24.497461247499526],[91.195287125229214,24.562823472370106],[91.418443002388429,24.598362541252467],[91.578494063028799,24.665087663404467],[90.330645728051806,24.669979881231598],[89.62655414268734,24.837792270230327],[89.49721849533185,24.922876823713381],[89.401066390460855,25.041966937640076],[89.260768898505276,24.866506671516021],[88.979729316731209,24.703343708989305],[89.135471354760568,24.55783906108103],[89.212342488093213,24.378983411596966],[89.199106188028736,23.965734424253057],[89.139074148837608,23.739136691459024],[89.214370212162692,23.596082189292353],[89.350036646148368,23.454376620542618],[89.42119758929438,23.2676336111799],[89.422998714687466,23.117318896274483],[89.382106453824022,22.971525184027065],[89.549565322099156,22.290617120331106],[89.520132205943185,21.89119859746258],[89.550957599340364,21.63335315250584],[89.48048812414099,21.397635417884466],[89.386105801717918,21.282864744981406],[89.262161546169622,21.200900413341529],[88.807888994373656,21.088667414915221],[88.663302627093486,21.091429325913673],[88.51088231844632,21.142622446442136],[88.070212686410343,21.13875851391332],[87.857299129143925,21.212587888889018],[87.385658427754819,21.060995082729558],[87.472142031490833,20.755496827945326],[87.455970368137898,20.562741149006953],[87.396072606337569,20.430167511174414],[87.251125268749021,20.242515718785654],[87.169911198490198,20.041595404235888],[87.077903959338641,19.935717035055802],[86.828395214691795,19.780374110070003],[86.526222074016061,19.484801617817922],[85.723048462912075,19.220257732662965],[85.502365885137976,19.087645706237414],[85.188930179814037,18.822604460101843],[85.06729385682786,18.635991295281976],[84.446762139684182,17.928887115707656],[83.95172671903272,17.658144511555275],[83.508037071589087,17.216664935219399],[82.858050540838605,16.815875391007626],[82.812729801438962,16.571640550772859],[82.684082004847994,16.301498706202111],[82.550116702880217,16.153907935461753],[81.930431688067756,15.858999442922427],[81.616190749804574,15.831231272016675],[81.559461257770522,15.702615917871002],[81.377644949137732,15.457421794362318],[81.190288509600791,15.305653652521457],[81.002372207201205,15.259166243784225],[80.651544163542709,15.298017360563586],[80.566259422419876,15.077144896929303],[80.675546013172251,14.532239573775541],[80.63452197765055,14.18400970891877],[80.824254771015092,13.493737409335687],[80.83508909128733,13.277571557022394],[80.697759145754873,12.516024249999527],[80.593065326178603,12.234447863771353],[80.326975708642451,11.79881655963209],[80.281382899425168,11.64491041432194],[80.348394705001539,11.190855234366881],[80.314860445468341,10.172605022625005],[80.221741151726263,10.002296941991423],[80.112820206202699,9.9050986768727896],[79.933052467544144,9.831889351416887],[79.709938514522747,9.8232613187315483],[79.609348643091963,9.6633022867305343],[79.768754913798759,9.5418654437627488],[79.874086251280431,9.3807780152276816],[79.910764900146447,9.19183747694888],[79.873355986887404,9.003040168979469],[79.731848461670481,8.8095209883344534],[79.563279379064042,8.7166322663228737],[79.372111540813066,8.6943004973341811],[79.020846560223362,8.7537475133750267],[78.669853459081679,8.6490812170710321],[78.590298339914582,8.3256040513576899],[78.41925316620555,8.0370119087811496],[77.763316870298112,7.6431356545209903],[77.570671805082014,7.5813892337714517],[77.418650208345284,7.5884342662913919],[77.102279286854667,7.687024077751877],[76.767719799756122,7.915390210924822],[76.115311768671532,8.6623359763060925],[75.845710246231477,9.3093141324968478],[75.699547466979226,10.076104457200806],[75.468973944340604,10.574028104708077],[75.276050331269204,11.129729974235584],[75.112682222491884,11.410923043394353],[74.771868818337865,11.794329779136952],[74.497696346495985,12.342653807531647],[74.284966093559646,12.958946189688005],[74.182872284572483,13.536941177058415],[74.028829799449085,13.875472531208757],[73.914925552036976,14.286744805706265],[73.524311987063371,14.811592667431418],[73.432942142495108,15.057175250760753],[73.338286678880962,15.207657830814847],[73.286651131954642,15.42264710691779],[73.003090243993924,15.892889839371277],[72.851180176985821,16.345530331746783],[72.749091334027938,17.090937664667326],[72.390672029435848,18.521361302588559],[72.388184275491696,18.78259307275977],[72.315734190445355,18.968369509431266],[72.29814138305133,19.257848009801357],[72.173572154070555,19.756778142165402],[72.184045926069757,19.95647505009164],[72.37939118118345,20.70977920713004],[72.362632495334537,20.796487185185622],[72.209239094680953,20.695097870719238],[71.128357367442334,20.250334037527001],[70.627123171049377,20.249199395456699],[70.236548036605654,20.406652206274362],[69.686009792555112,20.820480213647649],[68.62238324351506,21.882015770944211],[68.532012789396248,22.044700519942822],[68.483048348137146,22.365107752521034],[68.501484610135591,22.515961629185],[68.565558327300494,22.655911511879324],[68.316443902154774,22.809766441071893],[68.100347171795136,23.115719370389218],[67.95792282871318,23.181091180756543],[67.844513524524501,23.285080583274191],[67.768094517509326,23.418630579452984],[67.682416602953907,23.727569677125295]]],[[[92.250784011762633,9.0153721627443115],[92.215458753100378,9.160721521665284],[92.224707893908374,9.310015739345026],[92.277703661610474,9.4498933953303741],[92.369703081257583,9.5678358254515476],[92.657543562018887,9.7236437346091513],[92.80533942671191,9.7399196509060513],[92.951386502806088,9.7120131169757702],[93.15638769070496,9.5757062497532495],[93.294459227167664,9.2932197219623252],[93.295320101465592,9.0571514432710192],[93.182216254570733,8.8411361392833694],[93.331863488378218,8.7903008108157472],[93.446594617499613,8.7061568080269254],[93.600724326775875,8.7087861185834452],[93.743734574163398,8.6662727910432586],[93.934505279932168,8.5093671602823306],[94.003817208180465,8.377249143044402],[94.031061854370208,8.2305622549787838],[94.02913036053377,7.9702356636521685],[93.977475350289922,7.8207211795706746],[94.300058105458646,7.441790471551716],[94.427356433040288,7.0182245490773001],[94.404135516601784,6.816670780766751],[94.285158221070276,6.5447010698578119],[94.206524135513433,6.4214487223745866],[94.095599806090377,6.3262108427597434],[93.914162168137096,6.2564433478561554],[93.719829866797383,6.261141907975305],[93.541975933886846,6.3395963429005437],[93.407482720892617,6.479948384102916],[93.178952903339564,6.8737707272143433],[93.098823236497125,7.2825918895512807],[93.114257396477697,7.4449317058429161],[92.960279705848905,7.5834534181821986],[92.867307047250165,7.7852025877738855],[92.693556953388324,7.9397697356989125],[92.595220688853459,8.1024979393774021],[92.56847730656979,8.3378327553768301],[92.686560091691163,8.6345751387843634],[92.548059526983423,8.6710273910230811],[92.423634457053936,8.7470087982138782],[92.326416613042184,8.8556525420344965],[92.250784011762633,9.0153721627443115]]],[[[91.854789504366565,10.710303354875506],[91.865119015841088,10.859969614592123],[91.919381887020606,10.999834652811895],[92.124747647598312,11.22147379444281],[92.096192780379511,11.398564432442468],[92.119663918429936,11.545575440687186],[92.034590378276363,11.893974688199407],[92.060599459671366,12.033814625921149],[92.224570248261912,12.404785682731209],[92.235002357913487,12.63854528386857],[92.180124592973399,12.924292800237511],[92.210702435900586,13.111959090587863],[92.343577628540615,13.30916607376855],[92.415163763453663,13.591000678339016],[92.602935482939174,13.868313641676176],[92.955528565566055,14.033729961723155],[93.100500254571998,14.043737376345003],[93.242226842556221,14.011635768612605],[93.368737002688434,13.940136689290055],[93.469344705651011,13.835279505152544],[93.557681161326272,13.611408445711692],[93.571896691271036,13.219712182609554],[93.45450702499997,12.876547926234892],[93.48994677374894,12.545925808379025],[93.434729705013865,12.310535496409273],[93.537149273058915,12.054747580560786],[93.560372392650251,11.862976840006869],[93.485399380966399,11.63434774385939],[93.353125038015463,11.493568383109858],[93.220358069880106,11.425892214956411],[93.145942574004067,11.169753024504944],[93.005707314968163,10.991474300448619],[93.074064332035462,10.709306100436425],[93.034092651228661,10.508322914628673],[92.883179284449341,10.235713066810714],[92.778900792216831,10.125849228829274],[92.59817672068904,10.037057571952332],[92.397082155596237,10.02675590943281],[92.155404777888378,10.095882049842558],[91.972415735319103,10.24411315805531],[91.888146639852749,10.413328285330124],[91.854789504366565,10.710303354875506]]],[[[72.691491086126419,7.9103311150464402],[72.578351637644019,8.0644828583979358],[72.53135025613183,8.2498316240187304],[72.557361043059302,8.4392695656899299],[72.652579838146721,8.6050907821952549],[72.854807681127198,8.7628049603610663],[73.045199763341486,8.8149498618928597],[73.24093544623716,8.7893416958707764],[73.41150483289023,8.6899720824950162],[73.53283900267688,8.5299966835834145],[73.582186559757787,8.3451911553441569],[73.558529049157173,8.1553791777193876],[73.432462800073836,7.9311046446833391],[73.274938562335294,7.8087773194224717],[73.08213173230142,7.7577539299538154],[72.884720082325742,7.7861528812454486],[72.691491086126419,7.9103311150464402]]],[[[72.272937987282702,11.19714528271961],[72.30650522050702,11.397647586525309],[72.411343860365037,11.585712613307486],[72.562674011376942,11.706324117939207],[72.748467980856901,11.760440202051923],[72.940895355943908,11.739954720072157],[73.111132094020078,11.64793622919124],[73.233678127897903,11.498168344664455],[73.290177066749195,11.313085067290471],[73.272165832903298,11.120410354081489],[73.163026953352372,10.883393282339153],[73.015180852419249,10.758988730282129],[72.831204859549857,10.699931163608936],[72.592468841396084,10.730387151752961],[72.429212074489172,10.833741833276051],[72.317333904188047,10.991279883104372],[72.272937987282702,11.19714528271961]]]],"type":"MultiPolygon"},"properties":{"alpha2":"IN"},"type":"Feature"}, +{"geometry":{"coordinates":[[[71.943749627708129,-7.55436222335954],[71.855321699077081,-7.3355099853179269],[71.865359711015699,-7.140991244150598],[71.948960548502725,-6.9650673903942977],[72.09343751614135,-6.8344354334658588],[72.326104790747763,-6.7352117014883426],[72.574192491186679,-6.7375408065767397],[72.756438415816817,-6.8290548454725029],[72.931028787377883,-7.0201681414738246],[72.996083726504295,-7.2487839561455782],[72.968856069480523,-7.5270909495206642],[72.856676527295562,-7.7322231070106158],[72.69972424942506,-7.8603475989355687],[72.469938879873538,-7.9333616179162014],[72.22724133910333,-7.8923856816453597],[72.033500046989317,-7.7405784506608803],[71.943749627708129,-7.55436222335954]]],"type":"Polygon"},"properties":{"alpha2":"IO"},"type":"Feature"}, +{"geometry":{"coordinates":[[[38.297844484162212,33.219011520269987],[38.273848399808323,33.368379140014383],[38.312828776071974,33.565672227776567],[38.391827246538035,33.694690596842555],[38.50579696081099,33.794176284863319],[40.46178255752929,34.777208731404677],[40.649849938312336,34.84684946940942],[40.698437308062147,34.927900606354243],[40.731993579399813,35.411403370995195],[40.845115294865643,35.729345691590154],[40.745994025205221,36.063653177185415],[40.812830630816762,36.510881633237631],[40.897096029442856,36.684333072407178],[41.120854829339336,36.917399757131697],[41.248809600642744,36.985358607891165],[41.564105884400092,37.066913362717898],[42.065743019286145,37.513240295383099],[42.241806051419218,37.596035640770211],[42.499637530863197,37.799412032377681],[42.633395466034244,37.849897137237399],[43.161766147084478,37.862299072208792],[43.738055299135596,37.727587641107483],[44.097788698582519,37.810654794952725],[44.243556089918719,37.780108092015588],[44.479698852765196,37.659054350185208],[44.601597195811735,37.675797337778867],[44.86922260340414,37.645566985956457],[45.001654798461054,37.585421947953783],[45.140913921730373,37.451222622443744],[45.297593516565307,37.118703489510594],[45.441461832465315,36.965715417191532],[45.512748891702898,36.777720314216133],[45.648555451680259,36.645439714996229],[45.763589106902351,36.412102096101421],[45.907029021044998,36.330291588393401],[46.23358803370116,36.315968642629251],[46.371488883509102,36.276830401714932],[46.599012067591488,36.152325296163632],[46.717808765957024,36.001721216540616],[46.764567462960962,35.865253794066213],[46.770419474713982,35.721116751288321],[46.714247125703778,35.537707614252234],[46.609171435982368,35.403654264128519],[46.654422043289458,35.200195496091439],[46.594657236586798,34.934386517245116],[46.438921448445569,34.731866470979242],[46.163239237710158,34.566362530883438],[46.120758077189741,34.415519698106223],[46.038228741208265,34.284966351013402],[46.025444628102647,34.087778914404439],[46.156366192119698,34.025765455594197],[46.260221594267428,33.933395098381958],[46.555064703667142,33.516607680937156],[46.629080394866371,33.354699721910912],[46.840053372041481,33.253775071322302],[47.287373489857352,32.957811753362257],[47.532903108540808,32.896652642575738],[47.695613566831597,32.803935150221839],[47.788804028618536,32.698362154187869],[47.925145167589839,32.446056143822034],[48.236940177345588,32.084521221091187],[48.303351119552815,31.954631596214274],[48.329443543032006,31.811101413560941],[48.312996280356678,31.666148954299274],[48.230574768439347,31.438353602110517],[48.350706282378255,31.355934356625024],[48.462607934595511,31.202973075354855],[48.504441802805971,31.066730437296872],[48.525032742781463,30.759618957649053],[48.765764359648657,30.550914696462929],[48.86714306750347,30.345505990561616],[48.986755650649101,30.198408431817004],[49.043122745757685,30.015462817421383],[49.026610520723622,29.824743930936602],[48.967454445983435,29.693495366490719],[48.835497957124495,29.55480927387292],[48.70735163672218,29.489203272507812],[48.512231729503462,29.443488987011889],[48.355011722169436,29.448341854626001],[48.14103748125455,29.510259297683319],[47.905940057229301,29.48839448558326],[47.601806446132478,29.585548862810779],[47.46724488641469,29.568215306849549],[47.16055297510821,29.036468583270274],[46.849064465508732,28.713290288466588],[46.665771862457653,28.615109260064283],[46.310933481109529,28.565713119704579],[44.548553736183088,28.722837238928065],[44.404276775201566,28.794257011406948],[41.799513272483168,30.659113375710884],[40.235832294383535,31.448179165086817],[39.06840171015714,31.630705476269824],[38.845857468245676,31.724539370251712],[38.716156763962367,31.86868439056725],[38.509543010832303,32.308568508400938],[38.481965218808448,32.464326521832575],[38.497235689611792,32.599019089001658],[38.297844484162212,33.219011520269987]]],"type":"Polygon"},"properties":{"alpha2":"IQ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[43.571496310822681,39.163679655712336],[43.529281528090834,39.301788045008095],[43.528299597833225,39.446200824525462],[43.568632436658866,39.584870493837933],[43.646915315389514,39.706228666091008],[43.798647076726915,39.823927853804776],[44.004903091927069,39.881937780048759],[44.114135321618036,40.031336974679881],[44.280437582879507,40.163214474788084],[44.493891895199063,40.259534415322179],[44.727792400353238,40.248096404965096],[45.088244089594561,40.070302566596311],[45.49497256758891,39.622232735120676],[45.694466925293995,39.476008896774403],[46.19605658579782,39.380480043814302],[46.362739258180959,39.400447949008154],[46.62182885034224,39.591748877847415],[46.783027905280093,39.649896140371574],[47.203365851820365,39.917780829197667],[47.600718526537079,40.1176867448876],[48.064216111334794,40.179016832751493],[48.28671667310892,40.090385710590851],[48.745377658739969,39.664848786377902],[48.820528300165805,39.435244321937141],[48.768345945834888,39.175439792887097],[48.783822936896392,38.932530949351602],[49.039434917676346,38.905167942605175],[49.166227219422545,38.837000637444426],[49.268342828849882,38.735534034139391],[49.35186605567376,38.56329663884248],[49.457599874692683,38.041385350098786],[49.586945633882024,37.986944248416933],[50.299400142216271,37.877495218296339],[50.474586841719749,37.769592414235547],[50.69330298612028,37.518171927938184],[51.100894108051349,37.286157125479477],[51.797894638462985,37.119200484211653],[52.139605036947337,37.122194782005565],[53.415377752898003,37.370801822706895],[53.482960314170853,37.595957587903044],[53.649596233928186,37.767418421968948],[53.828893578210717,37.835949892967612],[54.153421405855084,37.838366453702783],[54.373427716550985,37.897809244696482],[54.594146861910161,38.17306863847859],[55.014551162224691,38.434975782029028],[55.462488325813446,38.585936963982427],[56.040885437555531,38.586424832454703],[56.284710619328528,38.724313775009264],[56.645571571444016,38.755970032551986],[57.324313846779603,38.698772808578617],[57.493239732294874,38.616385674510809],[57.69465581691145,38.408535177910771],[58.134720939765344,38.306116449548682],[58.418366423518449,38.159765900659771],[58.883811801691699,38.178537697779909],[59.443697300304819,37.993525796718835],[59.641608842737071,37.865636802598956],[59.784282640084349,37.659275132671837],[60.230416416623342,37.454591358489807],[60.557072241705619,37.150774565009243],[61.173603948594483,37.139424117285174],[61.391856278444436,37.061679882398188],[61.576416381479923,36.862995337784149],[61.655705522730926,36.689573365354669],[61.69081423635398,36.115162875526963],[61.743915549668799,35.956663419337339],[61.778028969828725,35.498395122350381],[61.748483268868505,35.343876316028485],[61.624973939504073,35.083296077373632],[61.548909545924111,34.682528935359365],[61.378609964690078,34.418214714390828],[61.360492349270316,34.153205098892208],[61.207603366978283,33.935028159717049],[61.317804980727708,33.817721663855835],[61.394055742253521,33.654177073548659],[61.416717219858704,33.509403807720446],[61.396434460973786,33.364278169882631],[61.306116607893948,33.191643462381442],[61.11108779689615,32.989543964777269],[61.31804618620837,32.353956952107112],[61.295876476591872,31.932408082766116],[61.818408849295963,31.856466028160927],[62.023053955027422,31.726048775551842],[62.152397686320874,31.585755746592199],[62.225707378268282,31.453012039611124],[62.310188746173402,30.945699431485068],[62.28518374510486,30.759778280718841],[62.196632090012841,30.550684621569506],[61.54884392416546,29.868133675631647],[61.731119046450061,29.654358559109316],[62.014989805514517,29.115728386468017],[62.123105948062367,29.013417951724055],[62.55674508905399,28.871121462943773],[62.745236105077858,28.7460535809258],[62.982998867599349,28.689818962046193],[63.101356491020368,28.60665474016729],[63.190953854156611,28.493089220128738],[63.253145649763866,28.311076088595296],[63.244941250817192,28.007940019599111],[63.278305078892821,27.738939769352449],[63.436399738072282,27.675286593709302],[63.643973932159291,27.515555388825749],[63.735731699630513,27.39902994555549],[63.789269644832302,27.26071380317072],[63.782476941503461,27.015236580848928],[63.582533513622892,26.385778584054712],[63.487873220088318,26.276087154585674],[63.365736569762838,26.198142177080481],[63.226379514392868,26.158488778870179],[62.835713383529516,26.142506551718824],[62.668013986555948,26.101078786705273],[62.528224347113415,25.949234563937384],[62.251727251233454,25.831383621134407],[62.161481796489653,25.590485370619277],[62.069906269385001,25.070239237955441],[61.982699508012793,24.895942816476591],[61.87681662661786,24.794575037420227],[61.615731252460158,24.645690360057312],[61.374106531016075,24.603822120532481],[60.535679154123599,24.829652385496384],[60.356724727664634,24.813693985945665],[60.025826381489978,24.874052457402652],[59.824567885357176,24.867392879707324],[59.418663343878912,24.962286345350666],[58.9593956268995,24.925088430226484],[58.642638892627218,25.063835131834132],[58.113428709572524,25.09986076286231],[57.896787298995399,25.162895325856358],[57.730553636902442,25.157633976990034],[57.488370903531788,25.25824983094893],[57.152038822060973,25.326331815297589],[57.019892811483722,25.403325862526035],[56.891277300482827,25.560766295113275],[56.622573430254754,26.238584416221592],[56.570012229738197,26.545144648252229],[56.221037966030579,26.278511800359801],[56.091692313588325,26.220658599527571],[55.869637585719317,26.207411666871991],[55.484996503817698,26.087147527008629],[55.330829582071139,26.092075613445829],[55.154043602334191,26.142064208923692],[54.94186054898892,26.039567673555634],[54.802058874932989,26.008283465417801],[54.483793204018802,26.035832852000919],[54.10882887073555,26.205423454454117],[53.577258847969439,26.242660157758724],[53.433264331043958,26.30668194987285],[53.147247624148079,26.535389851545489],[52.755307675632615,26.696809903098409],[52.427176721195536,26.899256871566983],[52.316442250656685,26.993143211126235],[52.180899001045816,27.198314738252432],[51.89559773338194,27.329329832221173],[51.549036564753656,27.359114371949293],[51.273298925172767,27.474003241635568],[50.877956285459028,27.833045799986358],[50.644377415997845,28.293547549921737],[50.595947610638675,28.449162741312989],[50.46093691113898,28.578805288145961],[50.38353346455262,28.741313355886522],[50.223783039436626,28.932173255863862],[50.159368855437492,29.210435567542159],[49.797141473947583,29.588071387023476],[49.653938372409968,29.539128621909484],[49.466481163560189,29.53711008577887],[49.332637436418942,29.581353728384844],[49.185369790443353,29.682009898542912],[49.006125602235912,29.56689328172126],[48.531391053008036,29.462846516242958],[48.307664937602254,29.523360613113464],[48.082691314733275,29.682669266040477],[47.940338142201192,29.920723718379346],[47.706154720651853,30.072766455633502],[47.604453464253737,30.180699430513009],[47.526157294611451,30.361650792649336],[47.514402194361402,30.531216277790513],[47.333533728594446,30.641825895670273],[47.21977799451755,30.806489268057938],[47.182250182901868,30.952284819456246],[47.179687627379472,31.400520606035098],[47.243216775189538,31.688431341870231],[47.040899200109536,31.973896849615763],[46.856696282156641,32.042735137240768],[46.221856461929598,32.43902749330362],[45.951097095005629,32.48481395184433],[45.781607540618253,32.583413707208855],[45.643749184847586,32.784988566453777],[45.58155174321459,33.103629510140955],[45.372335510114603,33.26247383773044],[44.974852381156737,33.703689583519754],[44.901143910782636,33.933880166925967],[44.912909807377304,34.079354641371332],[44.973879944838792,34.228672003137845],[44.938594575382197,34.388051658607331],[44.954489706227733,34.543050004387297],[45.079511723482511,34.850394827780697],[45.501138502923894,35.319484078958766],[45.482336298528963,35.399056062300858],[45.330580844439268,35.51330335011091],[45.098424414269829,35.590497740663182],[44.959479367434575,35.718625239805505],[44.837125127797627,35.994456463420434],[44.662444285463238,36.160252484651998],[44.565360384688233,36.411157013189161],[44.434384036817576,36.574732174494791],[44.300495031922871,37.014798423631369],[44.133564227915038,37.199368444975306],[44.071625565984547,37.404160368398735],[43.908304256134116,37.49195785455511],[43.785616170136457,37.638346793531774],[43.723271490083306,37.867958996204067],[43.740358166647127,38.010578299145926],[43.823952192295451,38.239581215951873],[43.766114900452592,38.652704621347105],[43.67773099339535,38.81594473172359],[43.646700155874846,39.004584568509728],[43.571496310822681,39.163679655712336]]],"type":"Polygon"},"properties":{"alpha2":"IR"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-24.812538472182712,65.156715150774929],[-24.944556097031853,65.355071489574271],[-24.97393110570621,65.544085945666666],[-24.930181045092105,65.730298963317907],[-24.819709061316509,65.886456885138116],[-24.255279350486742,66.259331047653461],[-24.002237884016356,66.495417148281874],[-22.98599009047717,66.930477356661456],[-22.283720524693411,66.908471852347773],[-21.222871214835695,66.489315953647377],[-20.994764315942774,66.361743271897325],[-20.876322838216634,66.342042853405232],[-20.738395896714142,66.37414779429929],[-20.405812496112286,66.55823856531174],[-20.253866766836762,66.597086861723525],[-19.738550128399183,66.505909644350524],[-18.86646712725268,66.683187213106663],[-18.562973413447285,66.632593618872463],[-18.153194981947788,66.659338620174893],[-17.507884796169673,66.593293310672223],[-17.034075807677365,66.720654665622163],[-16.639686183205434,66.958367754667009],[-16.057795929219044,67.025717698809444],[-15.898763810391273,66.999134483118652],[-15.368894843019996,66.7790166537864],[-14.856603076471407,66.880249459814294],[-14.501542858261621,66.871446277096425],[-14.324230991065283,66.799885112525587],[-14.161128023205766,66.626314226066341],[-14.062786570759416,66.289689069248865],[-13.994865780905567,66.19271741519664],[-13.870138029813036,66.11440117201397],[-13.389237099348655,65.962951196147898],[-13.211725794404915,65.809096439148803],[-13.133145750476491,65.637686976328581],[-13.057703207517376,65.075516994382582],[-13.123192907885311,64.849582592986337],[-13.246213529113112,64.706328690933219],[-13.680138986815347,64.364612870393103],[-14.026078656438036,64.215582370740364],[-14.408522314009723,63.964665032053908],[-14.917346074606122,63.807603445695307],[-15.259150801836391,63.788182433275473],[-15.705560007232275,63.691471948477222],[-16.518366910198928,63.381161936266743],[-17.387756404818269,63.26874551991466],[-17.830115495598154,63.048914112845601],[-18.676851834727533,62.90784993303906],[-20.296149066705702,63.066375358080229],[-20.755617826705308,63.248848488809827],[-21.225468426220655,63.373190690092315],[-22.636241136204362,63.329024582910449],[-22.827123964549649,63.360493634802054],[-22.991772674114515,63.462068030309375],[-23.082957655265538,63.575446416007331],[-23.19918005227952,63.790117518538629],[-23.282324633428694,64.102812426697767],[-23.34346005641763,64.181374969718746],[-23.482712901578626,64.250456470770771],[-23.937742394568456,64.254647865124568],[-24.229624391581819,64.377154662939702],[-24.455139250925352,64.608499286499836],[-24.584859284621349,64.947578304596902],[-24.812538472182712,65.156715150774929]]],"type":"Polygon"},"properties":{"alpha2":"IS"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[6.1921875296159996,44.872739907496111],[6.1405913072778242,45.006347022475047],[6.1342707297264329,45.196695461784273],[6.1995865600799904,45.375598565036469],[6.3520590381775728,45.534125984405371],[6.2973403359558286,45.686516009565366],[6.2926664226736531,45.832788874855368],[6.3521678679641331,46.018052268648781],[6.4411396295063019,46.134248749807199],[6.8786744425540318,46.40478154903505],[7.0195538534782651,46.425493783800157],[7.1693035131869411,46.403114389977524],[7.4638215168132671,46.47231721596674],[7.6378428161445404,46.469997260769837],[7.7885222492255011,46.665147961230296],[8.2439368595161806,46.927516369561573],[8.3876164399956501,46.945067227294288],[8.5923649535000735,46.906889270123798],[8.7250911055733251,46.840151256859599],[8.8337652570249627,46.735325989021767],[8.9686219657198016,46.881111818055381],[9.1436190983332395,46.961233372963648],[9.4554669159969222,46.98129368349948],[9.6924275826741866,46.905779904071466],[9.8249518033976582,47.025452839957516],[10.010722242504496,47.096748741324646],[10.164211239803935,47.272830475979674],[10.347178974805434,47.353322105434984],[10.497249601182224,47.362677851433631],[10.826669665022942,47.302139532186899],[11.143535720079891,47.465133758691806],[11.703460972544994,47.48537712301107],[12.149524496354909,47.581516695533487],[12.33885412035977,47.552333208705207],[12.503313912479857,47.454096966894483],[12.595573075178647,47.343449345726029],[12.665723381669364,47.161472850655528],[13.859017211366032,46.994027993900829],[14.060337238808131,46.866415392371948],[14.179699741442255,46.66009689469621],[14.197140580163062,46.469540655159427],[14.124641184087194,46.254075614661126],[14.130295144606043,46.077081341762799],[14.27562839028578,45.913266549934164],[14.35868161465557,45.739323714412137],[14.369764607450406,45.546889010217775],[14.329436744986769,45.407648322294811],[14.217227409525611,45.250922405028305],[14.008043440465109,45.133084761723708],[13.661363109455328,45.091271075591202],[13.521316773922953,45.128988721089293],[13.333519027307474,45.235602485354001],[13.003200371521938,45.105701997098627],[13.003951704508609,44.830581290068579],[12.942666060361981,44.695798693251376],[12.816115002788493,44.533584551404495],[12.951808661089405,44.426242637395134],[13.899554293341362,43.941710815655782],[14.245105097795072,43.417044902932268],[14.429958107446478,42.989739709545752],[14.822662098396986,42.659606230017701],[15.084143771592128,42.505194723524859],[15.269027461059988,42.434690950199389],[16.015487027100736,42.436635809858416],[16.362361296352027,42.355172013950749],[16.521530620218016,42.246004724829952],[16.607016609164955,42.128691338435516],[16.679393377090854,41.91108773061908],[16.682586867329267,41.737961816839494],[17.314127845753937,41.515394931359879],[17.694443505298281,41.292793522250598],[18.257810793606314,41.052754720169332],[18.66858453673775,40.736861718980805],[18.894429352287268,40.469168253124415],[18.982294152635017,40.162065933004449],[18.949911596887166,39.919418494661791],[18.694448559325078,39.470925912686518],[18.574159370888133,39.381326430993056],[18.383021957181157,39.324517205114674],[18.184883423346438,39.346920786822096],[17.877086836668774,39.479310954941525],[17.710970341246647,39.597685557549248],[17.547963614918626,39.810621574961296],[17.320217298218182,39.845470428340029],[17.507778459599187,39.688998819539513],[17.597807937728248,39.507850890964221],[17.673611086671485,39.024694406914819],[17.622362326078559,38.776223721295722],[17.53406590739727,38.650910756642041],[17.345386702842138,38.484795109971827],[17.212428971922424,38.432728523905801],[17.047923983315911,38.421626392106944],[17.007999827774896,38.21950058223586],[16.927849334766027,38.087167926985686],[16.62247983050425,37.867374584625352],[16.350120881867277,37.537206015338896],[16.112273113447841,37.44516538955601],[15.689446535479217,37.441123721065352],[15.791136443549263,37.116720388400921],[15.779766885017191,36.925605031716337],[15.724157032362752,36.792797953500681],[15.612060741180946,36.654647405011644],[15.517051154144545,36.394393920676066],[15.324484301514437,36.235290216739543],[15.129447192697107,36.188368580501177],[14.623741211602105,36.234351008249959],[14.266737501739836,36.358047763785756],[14.018091373712767,36.597659497117462],[13.722447276193392,36.63510382702794],[12.979089571318768,37.013976837003931],[12.800261518839509,37.077585311320512],[12.497648035928552,37.115407138969147],[12.3861196400031,37.164776983914322],[12.484367891337588,37.015958911330898],[12.548303225069404,36.809012512018434],[12.529734360534842,36.612969651625789],[12.436907772583053,36.439300914052609],[12.326838245827144,36.340191388089657],[12.144419708102275,36.266022102429396],[11.996419232818811,36.260203324831053],[11.743991839722336,36.320900313038727],[11.619138139120375,36.397760595747542],[11.497224173662612,36.549871284443142],[11.442742313658316,36.737040735795546],[11.447033257013102,36.929878653100701],[11.550820643084544,37.14655336843019],[11.743689474096978,37.289806179203843],[11.932644656081042,37.328526973083626],[12.207025173098502,37.285845450206949],[11.982357791247132,37.617388160842005],[11.938422735891217,37.854439227806822],[11.968244523281811,37.996924909086047],[12.097544024566789,38.270178126435567],[12.221605134191154,38.431716278076408],[12.492147215324799,38.620088911034998],[12.686402655842038,38.680383779441087],[12.939051880250387,38.638733603486109],[13.135172895146766,38.68945312391029],[13.419397260529866,38.676091181801141],[13.829281817710594,38.506348840460248],[14.394633865749423,38.544595934713016],[14.697295735507875,38.658090252990128],[15.042397724459356,38.661869258975798],[15.4081964791453,38.781724753797228],[15.474287263902983,38.907238058687327],[15.592898169860023,39.024554641337893],[15.549606070589824,39.176647435700097],[15.346998878129655,39.56454418196661],[15.117328996414473,39.602696662781291],[14.687300931067213,39.81437541150666],[14.544413019720023,39.948177361448934],[14.468522239761407,40.110103499004794],[14.249648325951988,40.107325346153701],[14.005006477056291,40.228693379226137],[13.822925237310226,40.210964891105291],[13.632665490847545,40.267695946159527],[13.512841455493323,40.356826066498783],[13.424684631679353,40.477367977116181],[13.376059305516197,40.61856838595336],[13.371154548931605,40.765650395083902],[13.098182962096008,40.744191021052174],[12.945248609078376,40.76511613672119],[12.639250258637373,40.945985513198643],[12.393752074754488,41.02976986996417],[11.798375695618844,41.518090061178114],[11.495103009786048,41.691578921431393],[11.322578228622506,41.876478320849941],[11.230721520251441,41.913276243146726],[11.005591958613785,41.924171578105408],[10.821820613661988,42.003245216833328],[10.712616967696009,42.106793530851071],[10.631685706534139,42.261641098342302],[10.369489683248581,42.216064406099214],[10.03182778295664,42.25226356613036],[9.8469023188329245,42.331073124602653],[9.6832622311493211,42.520480661052765],[9.6284225115923423,42.782696380777182],[9.642927815139041,42.932621266335751],[9.7014244992468761,43.071423330762471],[9.8949472724594028,43.251972202032768],[9.7919049141767101,43.566554572997156],[9.5543342754881859,43.633658615828701],[9.1462152122732512,43.831265618696563],[8.8041444286569259,43.900040617910534],[8.2967875341659791,43.469919179590875],[8.1641078316581588,43.403898955938487],[7.550804403832184,43.270710669863455],[7.403362531274233,43.275572341026226],[7.2637568502458132,43.323248283119497],[7.0807431691768787,43.485037171100359],[6.9948950081737946,43.737307984417569],[6.6410813177913282,43.908349013922717],[6.4934227444048425,44.045351751964958],[6.3547095539028264,44.404236958193515],[6.3521070616011253,44.604099250963515],[6.1921875296159996,44.872739907496111]]],[[[12.218364732843659,35.06120794226851],[11.995547299265201,35.167008780441876],[11.89633808512256,35.278154891145007],[11.834281105604184,35.413598079930154],[11.8266701879358,35.660141179604274],[11.937852327107253,35.880322827327092],[12.051375559848809,35.97680288600835],[12.713281502399749,36.36297212856649],[13.013417848661854,36.374042640325555],[13.22849369880732,36.268520191097672],[13.32469291243263,36.160504779155396],[13.385948078409038,36.029472635921806],[13.397975018018721,35.790207173870819],[13.325702070355494,35.611972498835542],[13.036057148341836,35.173152554334088],[12.885275001571205,35.047305841415252],[12.697655342607536,34.989241097313581],[12.218364732843659,35.06120794226851]]],[[[7.6825400019787127,40.7332852839886],[7.7174344802547505,41.104083632712054],[7.7751071461515098,41.251193315318297],[7.9880666339874669,41.495357526700126],[8.1607773619575408,41.595496600191019],[8.3086914162806487,41.621451380185647],[8.4576409509671358,41.602313611570985],[8.6777572021092997,41.470558425388518],[9.015306528178165,41.709142439743886],[9.2569961320013103,41.755979760865607],[9.448221619472319,41.705862562924665],[9.9176295822625402,41.415211985633036],[10.066113408522584,41.233030805183169],[10.304198785805367,40.528753992581144],[10.277631178469926,40.336278294937507],[10.188385526310489,40.16444276825132],[10.199459905147235,39.933514815589774],[10.055265396439385,39.082658329330378],[9.9592314720420667,38.862161056565526],[9.8144309072035991,38.734429935098554],[9.6503428341526138,38.667488157176344],[9.514133598298594,38.640593425071593],[9.3330314445431473,38.663992745365839],[9.1367434577613569,38.483323012983163],[8.9613248587795002,38.41958201839639],[8.3096460806300811,38.481632278762866],[8.1208361863178258,38.569483609424402],[7.9567931511996424,38.745009442063548],[7.8773625836624976,38.920577507030181],[7.9000246687812332,39.507062151510702],[7.9425076989170211,39.687019317546458],[7.8995730126268766,39.982245711841017],[7.9408630355575616,40.193040258117982],[7.7498411080180025,40.414777524333914],[7.6982313661394777,40.562145548349939],[7.6825400019787127,40.7332852839886]]]],"type":"MultiPolygon"},"properties":{"alpha2":"IT"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-2.1611696670962961,48.682167041840977],[-2.379944925243846,48.697949618608789],[-2.554599816151315,48.791665913472677],[-2.679117483195339,48.945881429666187],[-2.7339301867592178,49.136361461084007],[-2.7084652299797911,49.374310627664599],[-2.6292327593561025,49.553831596639412],[-2.4440196623678658,49.713271623086008],[-2.2547112239683034,49.764923126970281],[-1.9726925908125348,49.742996334685969],[-1.7372567754574186,49.644260142286598],[-1.5803890760099137,49.471418012844396],[-1.5138816239088644,49.24167890058208],[-1.5453485738305446,48.996793582193341],[-1.6901592462673665,48.796821441013321],[-1.9274813215354192,48.686008345695889],[-2.1611696670962961,48.682167041840977]]],"type":"Polygon"},"properties":{"alpha2":"JE"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-78.757212582960832,18.012841686672743],[-78.819295594188901,18.147430394330833],[-78.836039827267598,18.343773710258379],[-78.782125835823564,18.553987667978053],[-78.697101399830387,18.684502208515042],[-78.514947576834288,18.84912987258625],[-78.392281744164819,18.916001327836277],[-77.862502538470679,19.021831089028343],[-77.179971253913436,18.950506569929839],[-76.792254193603185,18.876513827645383],[-76.513115756662287,18.723605534444982],[-76.112434984173902,18.591592733224225],[-75.959455709249738,18.463835264257582],[-75.815660690897971,18.246096972726782],[-75.730785752244969,18.052599553177213],[-75.71117202118802,17.904848050437291],[-75.735981796538255,17.757879754349496],[-75.803010461313235,17.624754388222822],[-75.990885606227252,17.464765353001955],[-76.271062755301756,17.380937075234904],[-76.546754904673108,17.366894191893628],[-76.71618588263739,17.404280729890193],[-76.857203528655944,17.356717983350197],[-77.013259149915243,17.253575461121759],[-77.206262615326153,17.21531812975703],[-77.399082849917363,17.254488503714462],[-77.556179988462461,17.357088600697068],[-77.760283007719167,17.367963783955545],[-78.00404120512917,17.436794333835508],[-78.339579731455729,17.720018581939897],[-78.579162731662791,17.807598530684501],[-78.757212582960832,18.012841686672743]]],"type":"Polygon"},"properties":{"alpha2":"JM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[34.883270662004456,28.858317533507506],[34.742975124504042,28.899056512088674],[34.585417056232032,29.012662829505523],[34.483042799499479,29.177739938133833],[34.451303062155738,29.369373779894563],[34.634311249665167,30.204614770196937],[34.653643942405331,30.532093247904278],[34.906422405588316,31.161101017172975],[34.906529707005305,31.305289679255104],[35.047424186750035,31.849229799687013],[35.034752956988804,32.109521574487758],[35.104815576167525,32.767888707028725],[35.207211180587805,32.983835986824594],[35.31358160866376,33.08133266605261],[35.542045048972646,33.1891174772054],[35.788989701577719,33.22683237770071],[36.087296621292325,33.174968338352159],[36.354593735321956,32.96009527601354],[36.532027494856656,32.87170292367621],[36.724286904983259,32.845035897597043],[38.58181544407627,33.833734901535536],[38.819240375354923,33.869792623801708],[38.960174301643718,33.835705295946326],[39.085401450496349,33.76261303445623],[39.231582178199851,33.572082322559773],[39.488995055354458,32.789461524667452],[39.628528639686493,32.674270320957604],[39.707846669256568,32.545450729291268],[39.782519679096161,32.343247139986907],[39.782505515846168,32.144522586496052],[39.731225326756622,32.004113939891774],[39.640731842548469,31.885138416567976],[39.21376862478111,31.557508899682833],[37.955101039885925,31.226703990925987],[38.364360842097433,30.819479920310318],[38.441097764149973,30.692676707921741],[38.47729017596216,30.548948773248707],[38.469757834401037,30.400925553081176],[38.363558063155104,30.179689227702518],[38.252772107314591,30.081230699038603],[38.022289849919041,29.958876894692683],[37.879648040841865,29.7098268617627],[37.743811191918077,29.577482747782291],[37.614686825248164,29.516935065005143],[37.021700660688424,29.394434259890705],[36.809412412100741,29.122701595421596],[36.235075540465218,28.729437185816735],[36.00055015945221,28.705432591548409],[34.883270662004456,28.858317533507506]]],"type":"Polygon"},"properties":{"alpha2":"JO"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[128.34985318829601,32.261638110214037],[128.22120545238118,32.403756374510543],[128.16531365473105,32.536645241565054],[128.1496604993919,32.679957218592534],[128.19183788592937,32.943671088337148],[128.25750013175184,33.072588587837338],[128.35732376345513,33.177309620860306],[128.48295104659491,33.249066417993049],[128.62052954884459,33.281409111280077],[128.76565548697428,33.494876955821582],[128.88928993927209,33.580938744451608],[129.01919390673552,33.62341400026299],[128.86966216157202,33.721155123308719],[128.78063219523423,33.834747561532438],[128.6985760122551,34.036576721745796],[128.68861753331177,34.189168433642124],[128.88653781342782,34.845450565521226],[128.97862309236942,34.966674158487457],[129.18354325627089,35.108699771379086],[129.36638183274621,35.179064790309305],[129.51369658620911,35.182350430360749],[129.65557193416339,35.142553223659419],[129.81518396750687,35.028952256460499],[129.91889496803191,34.862743925421789],[129.96844740274426,34.621892957966679],[129.97082922471569,34.475183643187286],[129.92138292864266,34.313937296931911],[130.12753282486639,34.184066271059535],[130.39738330392032,34.343059535736657],[130.48626354481726,34.559816226586065],[130.73267648403743,34.811772317988229],[130.90464077097661,34.880283378787205],[131.19647070785044,34.911696794079631],[131.95450770738529,35.421070636934921],[132.45979234724987,35.856737080543084],[132.76435240351012,35.971554137128095],[132.69049994566572,36.21572329877278],[132.73922431220527,36.47146661731238],[132.85052801199308,36.644471880028199],[133.06426426737886,36.78307101796247],[133.25700193412325,36.838335933499174],[133.40741557522676,36.827179986226767],[133.54765904740941,36.771677850234369],[133.66497091354034,36.676879935413218],[133.80829197200032,36.505958585732621],[133.86477013580415,36.372635101885187],[133.87663345074171,36.180472663021035],[133.81663227442837,36.001261833616113],[135.1221455685257,36.244076918013221],[135.3079619885855,36.228621660604759],[135.53154200531179,36.144755131441762],[135.67785624919827,36.429970262483572],[136.01946868005723,36.732048269648857],[136.23590723573886,36.97820360540927],[136.22168876219123,37.245009059024312],[136.25783133720626,37.390308435066757],[136.45906260690981,37.701464434575172],[136.5692599823405,37.799910196688785],[137.1823275902758,38.001741754538941],[137.32881225794853,38.021759624189386],[137.56539444904567,37.958850738444511],[137.72619878834092,37.81585136185987],[137.76033050279733,38.17850367340268],[137.84113801270618,38.365909048340413],[138.19833273455174,38.71167454423427],[138.3696120655822,38.797306550727839],[138.56051884737593,38.812274865246309],[138.74305129334661,38.754383968359903],[138.89043594605545,38.632125169411694],[138.97481511079803,38.480608143659715],[139.09566097632714,38.76544743727414],[139.34362185429507,39.099781894866119],[139.4873141980348,39.491200634987443],[139.30380623872622,39.67974470969952],[139.25224246288113,39.819203430683018],[139.2439687585302,39.967659160293124],[139.30097755687987,40.156935583917701],[139.46533556843534,40.337451779246123],[139.42306563962785,40.595973895481038],[139.45877196362838,40.783953962289374],[139.70326103878813,41.11173978128496],[139.55041220667147,41.324404624086526],[139.51276290167482,41.590735727469202],[139.3704077907858,41.588403448181019],[139.22540063270898,41.630181327059042],[139.09946244262565,41.713320002388819],[139.00406491367536,41.830246383189888],[138.93889138351125,42.019972886738394],[138.94932194601171,42.331570263851674],[139.00872102839193,42.466266779916829],[139.10477142691178,42.577827182264095],[139.22914695421258,42.656580789396401],[139.37323932455391,42.698669421480069],[139.459156997283,42.900556088662618],[139.54961709785525,43.014083234132642],[139.90387392992116,43.194689367093382],[139.91875565745454,43.462524295310452],[139.98619433195029,43.594195398202231],[140.08921048586291,43.700369435459599],[140.31310893288554,43.806722958279039],[140.49479205230554,43.837846157359145],[140.63173008123999,43.816480837528495],[140.90297959991676,43.708404561263194],[140.95683476118617,43.877962091120224],[141.14717088002701,44.164745962429841],[141.18503521516365,44.416411240200809],[141.2686972135505,44.614612753908133],[141.04027084208013,44.648226598116118],[140.82379638280327,44.771329806045692],[140.61263511997305,45.000792771818169],[140.4945529826598,45.316574600318916],[140.47195661379237,45.461284639111064],[140.49226208651658,45.60633386865284],[140.5537270620313,45.739276141832555],[140.69013450904438,45.878257119603774],[140.86928941048461,45.954586832512987],[141.01549139172343,45.963337369357227],[141.24071238924992,45.914236485759844],[141.39324097827301,45.818495645037899],[141.80846216540067,45.988930530205344],[142.05465883520174,45.994139082205898],[142.27348304396364,45.881197084283059],[143.18786996965386,45.070635764741191],[143.94372145445845,44.615910146432348],[144.23193178369525,44.583553893851906],[144.60757839686258,44.442114681737529],[145.14733688680897,44.775246022866042],[145.380889189915,44.827087136039829],[145.61179144037163,44.764490570356983],[145.7871816349641,44.601786978073243],[145.84823079431104,44.470881944846525],[145.86705450390295,44.279743773489464],[145.83271413734349,44.139444568910044],[145.70401133802491,43.888171908634455],[145.98823276096576,43.860868987147441],[146.16077304023025,43.762723847813611],[146.3011656982863,43.55926454882426],[146.33169039868002,43.363124652297834],[146.30271348758347,43.216652725144591],[146.19980534122777,43.046910185523089],[145.75850242284579,42.743466779425475],[145.6134608837223,42.686352533028021],[145.44050876685731,42.673787779809544],[145.01730348696287,42.510710501931889],[144.67335646722415,42.449100285973067],[144.29802441510964,42.469748970387485],[144.20799474653882,42.435973526675298],[143.87635635076617,42.171410188117385],[143.76476332956193,41.869117010630511],[143.60484036275074,41.662393957796681],[143.49190761323436,41.570643623352204],[143.30961225427447,41.505840252379812],[143.11636252689482,41.51509323327258],[142.3478006074528,41.784358171309442],[141.74529319470156,42.066221888087206],[141.58383143399979,42.05468327605373],[141.64691613356686,41.865516456637302],[141.8292401237907,41.736188877586017],[141.92891513888318,41.564304755778267],[141.95500484293734,41.41714281765536],[141.90563202792777,41.079820231617909],[141.92741258661155,40.881314279163746],[142.19095496579587,40.598847613131142],[142.26064061282435,40.478025232652726],[142.46038072714526,39.974813020968135],[142.48452775898079,39.519596735680402],[142.36509152582042,38.926492238480101],[142.25909509884613,38.762972388830086],[142.02763122367998,38.582519675051948],[141.89867190748598,38.151545256214483],[141.73608210189042,37.982754299778769],[141.45372703865254,37.892699146857254],[141.53595467876352,37.471073247706897],[141.49219437214339,37.018985070263341],[141.40644602900267,36.761534175475262],[141.17457864734945,36.477913686040381],[141.10502876853991,36.247147781425511],[141.34170156413364,35.900791809029407],[141.37241209608291,35.759526583955804],[141.3614240650881,35.615379909479564],[141.30965602661411,35.480401871339069],[141.22143558172689,35.365876093928009],[141.10413760694468,35.281376474404084],[140.91303900930447,35.215510118594096],[140.84515827244445,35.010066764016578],[140.64784785017494,34.776730838605033],[140.36394611229699,34.631494813282636],[140.14404091038384,34.452709881619683],[140.00436830741057,34.406980774769963],[139.86221054162385,34.40346790589615],[139.7251423476487,34.265286811260033],[139.54521670515342,34.189835649923758],[139.39868426582157,34.181998428486374],[139.19772481379758,34.230053178247225],[139.02355175947284,34.145074772980209],[138.81857672898053,34.119855287813586],[138.67713936533843,34.145929519613951],[138.52660448681129,34.22866867777828],[138.3831488323853,34.135849769853571],[138.23781680960894,34.098985247203885],[137.56058257049608,34.158733095842116],[137.30740466929717,34.115030962484724],[137.20225345104001,33.965981927009025],[137.08871214424676,33.883056690386859],[136.6359888524257,33.732957794003077],[136.30219902612529,33.244312963587973],[136.20019550906568,33.150528026463171],[135.80806554905089,33.000095973571895],[135.61157918787183,32.994274991040264],[135.27144102261926,33.087661658098071],[135.1368007701621,33.166191845618876],[134.95926230227013,33.369519253652996],[134.70178251620626,33.212113200979644],[134.56368310410815,32.925184386198936],[134.4539609537895,32.828333308817754],[134.32089553234832,32.767401584099666],[134.17588739619023,32.747609618345713],[134.03136030963847,32.770653114462895],[133.89969682245339,32.834557790369324],[133.71354666415536,32.987170489898936],[133.64332251420822,32.960960921689157],[133.36859526186532,32.531234244086157],[133.05336859160624,32.28969473072668],[132.90373146614414,32.2559452176801],[132.5770742968692,32.266967425446033],[132.39727477036942,32.326703927705495],[132.2738611903514,32.425101440024235],[132.10518278955809,32.218681602807976],[131.96817633846302,31.816949024139163],[131.93164738488309,31.505644876719117],[131.79348594126111,31.200143158743902],[131.68275245032606,31.045842427089823],[131.55099240453023,30.956806772580052],[131.57151446837878,30.685137428612432],[131.39902503039136,30.158657008540374],[131.31284106607578,30.043313579246021],[131.1973611788091,29.957312544232487],[130.96219855012339,29.897134855787691],[130.7154025497469,29.771913414135952],[130.51102283431678,29.741622964440417],[130.37673801860512,29.759212536097344],[130.18391034086582,29.838940049697758],[130.07243134540718,29.932295271225886],[129.91644217719897,30.222965607299976],[129.88929972093891,30.418351081122392],[129.96394572428392,30.652400115966323],[130.10359132989183,30.79910593560033],[129.85469450213475,30.931287167469836],[129.71977098117074,31.141704009964538],[129.4657225392871,31.191775936365303],[129.3461000525364,31.274440465597969],[129.25532028939028,31.388027429342962],[129.18758337944715,31.619119971121886],[129.22259983309803,31.841784018990509],[129.31158876838234,32.024057189487891],[129.4660862910909,32.169697638754919],[129.30966198490449,32.375079855783163],[129.14680635454758,32.220786143605061],[128.81886768886352,32.091124842506019],[128.5790223502344,32.116711235367717],[128.34985318829601,32.261638110214037]]],[[[141.67745956365135,26.466261699185189],[141.6130241057898,26.647057762884234],[141.62144806631557,26.838808073882561],[141.70149011921961,27.013256962230415],[141.88340879161416,27.168383717770528],[142.06831219424612,27.219860669642411],[142.31397212909758,27.185998139252209],[142.48497735831575,27.091115676193429],[142.60650754306056,26.937897255590858],[142.68021964695848,26.704699316591483],[142.67558105555574,26.505929660339497],[142.59384695188521,26.324682787059125],[142.40396564283535,26.165785713262721],[142.21114891191505,26.117282094284889],[142.0000033725924,26.145719487300983],[141.83278894701567,26.246789059276285],[141.67745956365135,26.466261699185189]]],[[[139.39562254322567,32.774808201342971],[139.28217699083757,32.993903976442169],[139.27642430265536,33.191939755626741],[139.34803721318002,33.376663479887455],[139.48577213359673,33.51907259110007],[139.70905552590614,33.618907254741274],[139.90904442307317,33.618818620321242],[140.15651376417964,33.505477629753742],[140.29290449589834,33.365301696078618],[140.3650884356347,33.183529302892538],[140.34929151517082,32.940455927607772],[140.25418796002899,32.769555260903864],[140.09208477667073,32.623773231676182],[139.90518442193786,32.552385370830265],[139.70525896504765,32.559986749827807],[139.52431884618318,32.645360297608967],[139.39562254322567,32.774808201342971]]],[[[128.38858086592649,27.768179004428436],[128.38788901506285,27.911856578223802],[128.45008854168847,28.092504127522805],[128.66897803160177,28.308689079419395],[128.73270805803941,28.500849365062471],[128.89640318961716,28.671360565753297],[129.46223587880661,28.962559295867905],[129.65385731472668,29.015895685514323],[129.89759276885349,28.971776894639287],[130.02277839472367,28.889838869085349],[130.11811670966696,28.774531078108474],[130.20990510288499,28.450570995125872],[130.19223768444024,28.299348350681388],[130.1298785177907,28.160453569634527],[130.02860965109497,28.046765212686918],[129.89019194668083,27.961856108725932],[129.67286605468408,27.717500308347134],[129.48427956915734,27.606703100743026],[129.38496013798058,27.404407076684159],[129.23882022751425,27.28282953146503],[129.10498722983729,27.232577352363514],[128.96240422545023,27.222250705685823],[128.76970101639125,27.245286638958163],[128.6373763206924,27.302612310704326],[128.47005978731937,27.473075429413754],[128.38858086592649,27.768179004428436]]],[[[127.42674409086668,26.831727615770497],[127.49504882880603,26.976245432648653],[127.63781946556693,27.114507453926986],[127.86199734122678,27.190874944436963],[128.07184324168898,27.346761520350626],[128.26978522560242,27.381281211225652],[128.51006206131635,27.311529070226609],[128.70223806063112,27.147402798180693],[128.80214068995272,26.9805444787211],[128.83082440945626,26.788191994064167],[128.77853029690741,26.548282744598225],[128.70823269674034,26.417199959500227],[128.35048985446159,26.128612066693481],[128.17215686468163,25.815005435031292],[127.98287584965983,25.685991521999345],[127.78624711452015,25.612945606334357],[127.6407277526036,25.595115403989759],[127.49629232212597,25.620259135435006],[127.32671305294168,25.716382862714024],[127.20677630566101,25.870045077115673],[127.16073793738639,26.00923656196607],[127.15705072246377,26.155797829886222],[127.25435852222562,26.590590373241355],[127.32102124825471,26.722636137052639],[127.42674409086668,26.831727615770497]]],[[[123.51363362359045,23.84613262622662],[123.34386564550074,23.947507494612108],[123.24986587025792,24.062765394128423],[123.19392780156481,24.200574224097693],[123.18100091607171,24.348740471192176],[123.2122290022469,24.494154183683293],[123.28484895641218,24.62394895786025],[123.4435303323999,24.791498988519752],[123.56456922783214,24.86929374922207],[123.7027503331507,24.909398875242694],[123.89999275043907,24.900391373486055],[124.1649792644526,25.040446670982089],[124.35677815694883,25.065224834838446],[124.58622039252502,24.991875840209921],[124.77211850806623,24.786611468807187],[124.80599845413452,25.018977027343361],[124.89777695280233,25.189375510289118],[125.09135144529263,25.333049832552209],[125.28102642590173,25.371550969403838],[125.42515052358016,25.351154357462995],[125.557292654012,25.290111465368817],[125.79342485487224,25.100566119540737],[125.90379038051552,24.938933322393179],[125.94365522150304,24.747317809406105],[125.90691117362448,24.555079453946643],[125.7991882643024,24.391673563075326],[125.63699210379318,24.282137651702268],[125.36827738200728,24.217391206411584],[125.13600072254573,24.250790069782202],[124.958213111522,24.341207015346857],[124.82037462625092,24.513795021723585],[124.77577912208922,24.352653543830662],[124.5871843310177,24.037407099415187],[124.39651475202895,23.881962949118076],[124.25398126654761,23.840005120709542],[124.09784098418565,23.843015497696477],[123.8552235365504,23.767142896519996],[123.7069176897304,23.780550117006943],[123.51363362359045,23.84613262622662]]]],"type":"MultiPolygon"},"properties":{"alpha2":"JP"},"type":"Feature"}, +{"geometry":{"coordinates":[[[33.776995729094523,-1.4855729438780105],[33.644337660235749,-1.4294524607623313],[33.533182935414821,-1.3378429688498699],[33.452756361656149,-1.2183472028499116],[33.40973259560392,-1.0808821985282335],[33.458997708212834,0.2977571112165931],[33.738734501399577,0.87258516984221635],[33.979115672096427,1.139713417857352],[34.095676119685166,1.359576699021475],[34.363038117726681,1.594810854379819],[34.470890396822725,1.7889790682142011],[34.389398294817283,2.390038205888827],[34.340314645159893,2.4867495989676858],[34.18865131279513,2.6258579564362279],[33.988827388585079,2.9658145707932486],[33.924629224700723,3.3661315980957669],[33.787959458537642,3.4850071501725495],[33.711704143527463,3.6026368208904933],[33.504968744785451,4.0533959152153827],[33.480696505166527,4.2860603755969082],[33.518879645759242,4.4219950212832],[33.625583448265189,4.5765010591765209],[34.993123246075889,5.9093784824794069],[35.172134816095429,5.9825945291482965],[35.317426041674544,5.9895208813488727],[35.991979598280409,5.7763378733643194],[36.140032038984955,5.6501497968444472],[36.213026830620009,5.5233508045885413],[36.298128762848322,5.2004303995081305],[36.286049998435807,4.9641538853635216],[37.086800159323005,4.8772173253568054],[38.298593954418038,4.1223749187037697],[39.332384586670138,3.9904553959029259],[39.456035078187803,4.1687274383786814],[39.594195780343327,4.2853741550691753],[40.557474230270621,4.7276059939492656],[40.750270254936758,4.7725755123376148],[40.990909629488627,4.7189545977861522],[41.332999905817999,4.461844434032038],[41.96483262313194,4.4708656943974123],[42.143827605442461,4.4044349548562751],[42.284873134699829,4.2757554867005592],[42.377264365381677,4.056605980971244],[42.379385046924746,3.9130415213783851],[42.340619441711979,3.774793630975366],[41.754336607911249,2.9191131083926263],[41.46616506480035,2.6207508706417038],[41.478794622213194,-0.65858677551906397],[41.980169858828191,-1.3729412281197027],[42.030574476172021,-1.6512746019021971],[42.009599627431015,-1.8448492860324424],[41.883083497652791,-2.0517391160403848],[41.449344037304755,-2.4753361085538437],[41.271069838789707,-2.5829321706606545],[40.987178006752146,-2.9029321500673668],[40.829733553114131,-3.0034922054676407],[40.671763729458924,-3.0389461021382105],[40.557506743260667,-3.4835760587320168],[40.375614471626164,-3.7023561652586525],[40.184660092584664,-4.204357570250389],[39.812892961780058,-4.869891489328281],[39.645240357371655,-5.0470960261500943],[39.226186018098936,-5.1757832342387875],[39.077947713761105,-5.1643053151972715],[38.939638604719462,-5.1097440848721893],[37.499836183340911,-4.0751547806400792],[37.266160064520363,-3.8614600027637267],[37.173724164489222,-3.7440112446927678],[37.120131725997787,-3.6044889994287317],[37.134858540732033,-3.3349240176413457],[33.865760312271682,-1.5087752723020551],[33.776995729094523,-1.4855729438780105]]],"type":"Polygon"},"properties":{"alpha2":"KE"},"type":"Feature"}, +{"geometry":{"coordinates":[[[68.749168253724733,39.62185290684539],[68.737149563975706,39.84891583504043],[68.884657989611057,40.235351337631243],[69.024845791026877,40.380830171568178],[69.279046375248896,40.527894522077979],[69.418974275383562,40.584896304082179],[69.950871869549403,40.701747356654558],[70.498657956907863,40.572248880841137],[70.818540787887542,40.723837738819363],[70.578748614178096,40.806119672098198],[70.423259832681623,40.915437823735509],[70.273052495327974,40.95393899357353],[69.854704022687159,41.157973994078574],[69.713120594969013,41.35397438104868],[69.679147435440271,41.495968629618439],[69.687641201609523,41.641723211472723],[69.737877662576963,41.778810191834538],[69.825573347782793,41.895540689098674],[70.396116396804203,42.284504422799536],[70.430823411267284,42.530498515196534],[70.665445038502909,42.884859724208603],[71.018309007930782,43.172873241384352],[71.324881189774743,43.273395061209612],[71.881118329488274,43.317322081386614],[72.397504197226624,43.242423780901532],[72.874581195671169,43.122646691950663],[73.041780955605617,43.047656642720106],[73.147875863487741,43.290905683018025],[73.366340989241948,43.482338078838929],[73.798694099009722,43.671525476560944],[74.223708050177123,43.739896127576522],[74.40637377856855,43.699558532134617],[74.98509435966568,43.451108760274543],[75.453608041997626,43.332724162035063],[75.795493715871174,43.435461781133377],[76.496157255919286,43.42026289130645],[76.992934700303621,43.473393748270148],[77.327267712402588,43.408981571053616],[78.58912303290947,43.360048100260542],[78.948241005001819,43.27467911395388],[79.26161169625729,43.256921589897274],[79.426787857128801,43.175368313173543],[79.679586705292664,42.94903838817806],[80.03816775528351,42.898952253372336],[80.183597220523993,42.838357579329994],[80.591246016866251,42.51244564189583],[80.672716157105441,42.377364230424618],[80.736924442539845,42.154176630174831],[80.741503540069189,42.006130356521176],[80.70250910054007,41.863238435956269],[80.623363068090413,41.738040301687448],[80.511010876608992,41.641522674560377],[80.375311955482815,41.58215542700664],[80.1098418993857,41.535564467392412],[79.934992509956132,41.428551474230261],[79.425249013868012,41.295083572265099],[78.939504994468621,41.099497642088551],[78.722023977576939,40.991060470494865],[78.458825344014244,40.705135091809908],[78.337954922454941,40.624273798829677],[77.610055098712252,40.493823626068284],[77.105768254951997,40.510928265922274],[76.819866587018041,40.08291039632735],[76.534675486487032,39.901624946124969],[76.399592625795009,39.859117884375472],[76.134046607145578,39.871313588868574],[75.916644043622583,39.80546468503568],[75.622572293216223,39.809067967926104],[75.439995659286836,39.865950205235862],[75.28037307872539,40.002886538336597],[75.192316474615026,39.981975956133013],[75.04488301698872,39.877122852939152],[74.852201975094403,39.826297510752923],[74.635588775832858,39.690367606372156],[74.404615309423448,39.619779551341445],[74.375412146691673,39.403944755182167],[74.271230298409662,39.236165441738578],[74.076168682617919,39.058102580752283],[73.841819323941735,38.978388456487579],[73.522256782038056,38.957460445671025],[73.149378586230441,38.863607294285401],[72.61506785338922,38.875119503937967],[72.402369635906737,38.738423021863788],[72.209218097466575,38.708205832297068],[72.06479988074436,38.735854427352422],[71.936508850830805,38.800421889148438],[71.780314798599946,38.778233571982099],[71.630362909621638,38.800738131699518],[71.494052288790883,38.867156795228439],[71.316827010095736,39.022155598110608],[71.097868322054737,38.920820385060374],[70.794308092755486,38.894951165822768],[70.612282492766482,38.931232880532313],[70.393842976495151,39.060962908520658],[70.219493264145839,39.043530373205613],[69.651715126167318,39.07235861079716],[69.228540520641175,39.029866510891608],[69.048446138432155,39.091682037416419],[68.904520105838685,39.216343350439722],[68.833155967849635,39.340481724837417],[68.749168253724733,39.62185290684539]],[[71.687677885598632,40.72045046959488],[71.695920200471363,40.718854305594981],[71.704887232586657,40.726947550122802],[71.69949263860326,40.729145333504547],[71.687677885598632,40.72045046959488]]],"type":"Polygon"},"properties":{"alpha2":"KG"},"type":"Feature"}, +{"geometry":{"coordinates":[[[101.82047256561577,13.518096291131714],[101.83573887458168,13.664375916586051],[101.92051390576164,13.840588512173015],[102.02528120806996,13.943809640724972],[102.25195614683467,14.059780122292246],[102.70413934050471,14.630492297759483],[102.87876661004903,14.728502058866519],[103.44713739301763,14.907289071323865],[104.00500920583988,14.861598776600662],[104.84519731850902,14.923153613016382],[105.10530806136991,14.840178681347034],[105.37156546202547,14.808973165483678],[105.58723492816513,14.652431798923358],[105.71791051656162,14.769216423446835],[105.98400075793194,14.913775464518782],[106.19809083843252,14.974593779359113],[106.3499123645619,15.054378154102867],[106.4939547007729,15.077847446489756],[106.63863807643027,15.05872355790366],[106.89176041796544,14.906803522512551],[106.99282151424805,14.99337410053522],[107.29104847945965,15.149572791828616],[107.53270833988577,15.204537995774073],[107.6800680018362,15.17816993250192],[107.81301731418127,15.109361320556532],[107.91963862359484,15.004280203747618],[108.00477706845228,14.824395034545361],[108.01844606256498,14.675320209941003],[107.97803430871447,14.422134894697507],[107.85201211654578,14.173968600178211],[108.10255046660446,13.489933347811425],[108.09574814405265,13.348347898181688],[107.99322785062004,12.990502523826354],[108.04161960266899,12.748937905758204],[108.03432799575337,12.372391236440743],[107.94930935800534,12.147734461548289],[107.65961607942059,11.837552720053674],[107.51885605172934,11.776772183601901],[107.33866164631269,11.764427886936597],[107.17111008646138,11.639434911615096],[106.87332594342423,11.530609029110616],[106.76280204472718,11.344117461450873],[106.61825481463215,11.238441124380234],[106.65750471360327,11.093617632015331],[106.65487461662899,10.70176851979922],[106.57969351289758,10.517799144283863],[106.438786342016,10.377648872504125],[106.30306608362116,10.314980084902926],[106.15488558886804,10.295234035857829],[105.74548312219953,10.375558705795484],[105.59815835640026,10.43442399916872],[105.37699838466136,10.349658410609933],[105.07997036346664,10.090437457100382],[104.75852266222951,10.025333172154939],[104.55393295515113,9.9278459034835471],[104.31835271962746,9.9252996302416179],[104.05926016607829,10.061879929615994],[103.95841091120245,10.077384086154586],[103.71490184777593,10.012023519465757],[103.52393983321825,10.028629403279945],[103.27080778650972,10.180568648861172],[103.04938231268906,10.237025608595905],[102.89801958725317,10.358917336173208],[102.69560502162946,10.711979745498496],[102.62928727681152,10.947830039925327],[102.5227886399181,11.122213488684249],[102.49434867153767,11.263253506110843],[102.51138606203131,11.453194041990605],[102.26160654064948,11.934458844610495],[102.22887660565047,12.235161120397789],[102.08658862113968,12.38859898341722],[102.02296025377512,12.51975675483828],[101.9756627773228,12.860626906223798],[101.84463462556685,13.172222512249663],[101.82047256561577,13.518096291131714]]],"type":"Polygon"},"properties":{"alpha2":"KH"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[174.22976024950873,-1.2807955472193058],[174.08932084553254,-1.1439215364651654],[174.00629549999709,-1.0025699790879261],[173.8899346643698,-0.6779576017026836],[173.89542829841963,-0.48023064339059107],[173.97716858543114,-0.30010664294173406],[174.12236280100186,-0.16577582062893625],[174.30828739408315,-0.098261557350860562],[174.52152454872382,-0.10927853698083101],[174.76574339076384,-0.2368603746053341],[174.89390542362429,-0.37155021363165341],[175.02601122251636,-0.72700587745260825],[175.13187079617464,-0.83322034482403518],[175.23085775686303,-1.0033859550034583],[175.27583867251894,-1.2136382932027443],[175.2647853596558,-1.3756834974075547],[175.18623511261279,-1.5506010730083069],[175.04762626351135,-1.6830921538980252],[174.86934335022485,-1.7536719129982148],[174.72525405109502,-1.7593389281958247],[174.56472897186748,-1.7172833363793039],[174.40604993880007,-1.6120198418192979],[174.3194118070918,-1.4980047054218628],[174.22976024950873,-1.2807955472193058]]],[[[172.44178396712272,2.0172546971906811],[172.5086275103877,2.2033051240779349],[172.60364400947674,2.3176884722476006],[172.77427900547409,2.4175209256990016],[172.92054699248146,2.4443045291617911],[173.06822340194464,2.4268871951033204],[173.20424673661316,2.3668094299710418],[173.35679840619395,2.2274526176975922],[173.47134151135771,2.0556160563029522],[173.56027043397964,1.710823606808821],[173.65597947969979,1.4957919209169506],[173.66594670037748,1.2967659346018077],[173.52179106461733,0.71486446712701424],[173.36232366223453,0.50189494031650517],[173.20702717660623,0.38570227039260924],[173.01919855592482,0.3373479092091507],[172.80735163226933,0.37095186904957972],[172.63508006623712,0.47310716197492464],[172.51672000728115,0.63467477252787829],[172.47126265653429,0.82973072757394029],[172.52529797952803,1.49970697126526],[172.44077494686803,1.8425789959081742],[172.44178396712272,2.0172546971906811]]],[[[172.45978291363522,2.6204932219977244],[172.31779082564199,2.7848045621588788],[172.25533276196563,2.9749108359546126],[172.27241959845173,3.1742834646517264],[172.40027067905643,3.3881591984129846],[172.75152475689694,3.6009148547569136],[172.89094378475457,3.6423054962101467],[173.13058547727371,3.6181132067976525],[173.29659769952661,3.5189468933041428],[173.43149787487158,3.319409051398496],[173.46432403757186,3.0644134577057351],[173.40349267909403,2.8828181735443259],[173.20625657337172,2.6424614975977128],[173.00872362250945,2.5005870935560455],[172.81663109586134,2.4655015487246343],[172.62574919053864,2.5066674626649732],[172.45978291363522,2.6204932219977244]]],[[[169.03066111447109,-0.78345602588840502],[169.10013601230142,-0.59187476357440372],[169.23668122915157,-0.44573630674519044],[169.4260869777772,-0.36067460751368663],[169.6144166953944,-0.3536260081676243],[169.80755354583002,-0.42594702431091869],[169.95361765703004,-0.55623633418022145],[170.03847391230764,-0.73261503127014094],[170.0491189293127,-0.92805482368671866],[169.98022609433423,-1.1281625594280282],[169.84976431092596,-1.273313741858636],[169.67367367321833,-1.3574637864384669],[169.47728932887728,-1.3709016964434586],[169.28067287964271,-1.3019545777127903],[169.12833243060999,-1.1708615784382919],[169.03991790548523,-0.99037374334342287],[169.03066111447109,-0.78345602588840502]]],[[[-151.79083523304823,-10.89318521266385],[-151.59438514815196,-10.939366544414492],[-151.43488229769363,-11.055645533840972],[-151.32635311691075,-11.233386255542868],[-151.28425222713545,-11.416345072672794],[-151.3176292117052,-11.622108473957574],[-151.43020972597597,-11.801623101611735],[-151.58611855190588,-11.911727514305152],[-151.77192577558145,-11.955387786033418],[-151.97523489550085,-11.920960365873782],[-152.14330771564056,-11.819541046097592],[-152.2684914280207,-11.641643467863458],[-152.31756899675929,-11.435531834933439],[-152.26870675010525,-11.187705197548082],[-152.15478339665697,-11.028086456813416],[-151.98823417413197,-10.924558541104892],[-151.79083523304823,-10.89318521266385]]],[[[-155.51333708556163,-4.071267001587656],[-155.48582275126915,-3.8836225610462396],[-155.35735827939916,-3.6853486597905687],[-155.19734128639863,-3.5835545705283613],[-154.98176141492959,-3.5329353188477346],[-154.78583186978091,-3.5602982542564465],[-154.61585106212772,-3.6615091327524523],[-154.49107506715438,-3.8308320661990853],[-154.44472223174824,-4.0239824243160749],[-154.49150940529537,-4.2703279420812406],[-154.57394269999787,-4.4106221296113493],[-154.73041949524003,-4.5367530391475857],[-154.92392052658795,-4.5910793187641037],[-155.12318067620743,-4.5648231468551179],[-155.34108988543008,-4.4323010758988168],[-155.46568820997769,-4.2679212103837258],[-155.51333708556163,-4.071267001587656]]],[[[-155.8722658641484,-5.1105756328254923],[-155.64470927054188,-5.1673407105245932],[-155.48313046771324,-5.2949016783943437],[-155.38553952394261,-5.4724356596197214],[-155.36640055139941,-5.6741185952532636],[-155.42615138225642,-5.8659486473494864],[-155.54864668301946,-6.0133246594902081],[-155.71758327696139,-6.1037968369306759],[-155.95861733379132,-6.1287617388068334],[-156.14280109192092,-6.0753328852923598],[-156.30538136710322,-5.9451439910556587],[-156.39852288684608,-5.7848108848339743],[-156.42731444817903,-5.5891306031496075],[-156.38139137294098,-5.3983620473553851],[-156.26559535094219,-5.2399548126456343],[-156.09775969710262,-5.1383046084187542],[-155.8722658641484,-5.1105756328254923]]],[[[-157.8893404210481,1.5112523348479403],[-157.98834132142184,1.6170229239945018],[-158.05286286107477,1.74673632624746],[-158.07748817394136,1.8895025329262227],[-158.0601498570685,2.0333356952054311],[-157.8661944161137,2.3595490798202481],[-157.75414364389306,2.4538706066872384],[-157.61956868203751,2.51167636442245],[-157.33419499544155,2.5124958840966625],[-157.04749347228878,2.3853022140051658],[-156.91522097874204,2.2576682124485212],[-156.69973718370841,1.8876271101514588],[-156.67950291031823,1.6968984378885548],[-156.7325621505631,1.512584658513717],[-156.85110737471896,1.3618070670996407],[-157.01769496020543,1.2667521955538246],[-157.21527401684764,1.2338234567130115],[-157.39361573220836,1.2546906659375496],[-157.64495357172385,1.3421868238172185],[-157.8893404210481,1.5112523348479403]]],[[[-159.78528452290968,3.5454042221086639],[-159.89494981992019,3.7605014961255998],[-159.89103620749015,4.001909860304778],[-159.78793573168934,4.201940482312021],[-159.6037950285037,4.3574966383728055],[-159.39485718704512,4.4193225791504833],[-159.15677654764028,4.3878575270396425],[-158.99424210495994,4.2841502529459712],[-158.82319337082251,4.0814710374457581],[-158.763187140253,3.8911970543894503],[-158.78235537203858,3.6926082449728841],[-158.8887695808989,3.4805448968528037],[-159.04316920571449,3.35478747667347],[-159.23440561087151,3.2992644164553404],[-159.46910739519444,3.3204059492428102],[-159.66420302932775,3.4207329158134399],[-159.78528452290968,3.5454042221086639]]],[[[-171.55354430246706,-3.3581667378922582],[-171.75341313869922,-3.3429973395415598],[-171.93867613211259,-3.2650814152628227],[-172.08056041655115,-3.1439596098230891],[-172.20912016300142,-2.9049397394759131],[-172.22257740628416,-2.7104885907878611],[-172.15981850265194,-2.5229493504433695],[-172.03475707161434,-2.3752399027195503],[-171.85622421704247,-2.2818893056972445],[-171.65534147181057,-2.2665779767510084],[-171.44917904098489,-2.333185457727025],[-171.31172059764049,-2.432330546114124],[-171.16396498031486,-2.6141649756475007],[-170.96710477595107,-2.6308118139342556],[-170.7769347339071,-2.7245549458196074],[-170.67198690480052,-2.8346466002129769],[-170.60730107172728,-2.9638425170239362],[-170.58318138610647,-3.1544434869387001],[-170.61770439606599,-3.3106570594719256],[-170.72332906965008,-3.481842772171333],[-170.87762967413772,-3.5947182443510011],[-171.06301390420211,-3.6414326533536392],[-171.25237892001525,-3.6151564368997597],[-171.41803989805766,-3.51973113040793],[-171.55354430246706,-3.3581667378922582]]],[[[-172.55449531284319,-4.1291801096205294],[-172.34058273118632,-4.0118666623275985],[-172.13177792285015,-3.9970734843061089],[-171.87678905211524,-4.1046196382262803],[-171.73436458310547,-4.268042964532432],[-171.62933208275669,-4.1073588274431225],[-171.4892888721001,-4.0021319770694932],[-171.3076065706675,-3.9428859784619688],[-171.11696750264562,-3.956125795250605],[-170.94521911510051,-4.0399174319484761],[-170.8040499980716,-4.196935700209063],[-170.73892492864931,-4.3766818598102768],[-170.74755679890478,-4.5780567135045391],[-170.80277742789673,-4.7157315926643655],[-170.89596047282242,-4.8311465412469445],[-171.12814009423994,-4.9537321986108749],[-171.28664698613545,-4.9652464484711638],[-171.44488237294922,-4.9277060365066037],[-171.58896442981884,-4.8423890515293158],[-171.70982929421893,-4.6960557578675939],[-171.79774929826542,-4.8389499220342209],[-171.96337536566381,-4.9667655213165789],[-172.13781198646942,-5.0180389969561308],[-172.31571344414976,-5.0130330484657133],[-172.45869954773198,-4.9597869386701685],[-172.57897000075889,-4.8658976839351746],[-172.71101265179394,-4.6330215358370488],[-172.72661807558077,-4.4850525821410772],[-172.6979901955167,-4.339043044807096],[-172.55449531284319,-4.1291801096205294]]],[[[-174.57917387674848,-5.1877075039849139],[-174.78602149141727,-5.1143280949296352],[-174.92177677627933,-4.990967990318806],[-175.01722533437027,-4.8096873636824569],[-175.0387825045031,-4.6264780179172202],[-174.99009931045404,-4.4411078760228744],[-174.87525508116209,-4.28767076875386],[-174.71112371518842,-4.1887109911047409],[-174.52182079968293,-4.1587685618469026],[-174.31805515700844,-4.2083025445868705],[-174.11732381597992,-4.3695196371080245],[-174.02437769153434,-4.5412929133882232],[-174.00442022689265,-4.7416003062079328],[-174.06307329034283,-4.9328607422660413],[-174.1920087696206,-5.0858194897656048],[-174.37058629348658,-5.1759904619414083],[-174.57917387674848,-5.1877075039849139]]]],"type":"MultiPolygon"},"properties":{"alpha2":"KI"},"type":"Feature"}, +{"geometry":{"coordinates":[[[42.842540477918732,-12.071567507766975],[42.74941239904593,-11.900107938131953],[42.726915465834097,-11.755090793896505],[42.76448014970827,-11.341847206723344],[42.806474917781969,-11.213784319285105],[42.910309369497931,-11.055650134281358],[43.042509924175825,-10.945620990073181],[43.228624784706049,-10.87973856153835],[43.421026317553434,-10.875092311755161],[43.6077316157102,-10.945531364506875],[43.800609626359147,-11.11950574073267],[43.879844636345815,-11.29592279979245],[43.88700861979418,-11.513499880513978],[43.974762465278801,-11.736656338661493],[44.276270741405391,-11.603504291476339],[44.460550250453444,-11.571730074662145],[44.667905969563066,-11.619968662032823],[44.837230336963444,-11.735867327128155],[44.925528522605795,-11.862495913453557],[45.009537397994038,-12.090201245091158],[45.016645078016694,-12.419983927762235],[44.89720622198098,-12.666251103312872],[44.793159470039377,-12.764799893949334],[44.66543207318032,-12.82978683738504],[44.428947394496369,-12.850455794397121],[44.181309122579393,-12.748864802238248],[43.940659193356566,-12.861142369524888],[43.599335258583267,-12.838500059923792],[43.46486876964255,-12.801394028491638],[43.311315779978635,-12.697287718680073],[43.175466182907279,-12.488996876349573],[43.134077472190675,-12.313636695127093],[42.98950942950087,-12.232944869375956],[42.842540477918732,-12.071567507766975]]],"type":"Polygon"},"properties":{"alpha2":"KM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-63.33010539669575,17.246649867679967],[-63.328303471523029,17.469026918309616],[-63.214852969237839,17.70165969787702],[-62.9948393836275,17.860475380620418],[-62.748222008486302,17.900145082990974],[-62.601810161827387,17.863624619834571],[-62.433597238277486,17.767159901444025],[-62.144098930436925,17.482501974340217],[-62.054517125249198,17.310370809396566],[-62.035545335880549,17.066746953738924],[-62.097130946600217,16.876124590061561],[-62.228598084199383,16.724975442815726],[-62.45815941370563,16.616542487354099],[-62.65660688791538,16.606400436369746],[-62.799441368679254,16.650428453635481],[-62.963547684337271,16.762090049368759],[-63.25081050169721,17.056234661583801],[-63.33010539669575,17.246649867679967]]],"type":"Polygon"},"properties":{"alpha2":"KN"},"type":"Feature"}, +{"geometry":{"coordinates":[[[124.08925075224494,39.479695346212139],[123.91467394289387,39.659127952261116],[123.85215305877976,39.85021014117126],[123.90703923140342,40.2449233489818],[124.02555294643037,40.449811602499175],[124.6474243497742,40.896956093858051],[125.20353581167328,41.119477745006513],[125.53123110415352,41.325921347579907],[125.77366584819588,41.390297635385991],[126.06744487446608,41.672371612856203],[126.22514379194411,41.969980074428889],[126.51178743594376,42.170008238253793],[126.84596463153726,42.25733154078673],[127.03725521545893,42.262442980780193],[127.216456447854,42.195322696037174],[127.44377568722668,41.998860484161966],[127.53931223880539,41.977527895653452],[127.61232421963932,42.237267895023109],[127.7781794154306,42.409939740898267],[127.95736173760589,42.479508547183976],[128.78540292623438,42.548417543242387],[129.01708463566288,42.815835474257732],[129.15306433585189,42.886851366860988],[129.32164916215814,42.913634416765674],[129.45832254594228,43.235384222577466],[129.58622799569994,43.388537539618689],[129.71622720064551,43.463564449594735],[129.86263628638599,43.496628177752029],[130.23292874705794,43.450779791871575],[130.55828214770548,43.277357320124615],[130.66143293206747,43.160930615421613],[130.72399684474371,43.017737961076691],[130.87431657002782,42.899672398963929],[131.14330714204391,42.506813123021487],[131.18265939742807,42.367721570648179],[131.18057773647169,42.223185323868691],[131.1372361448966,42.085284756846001],[131.0562571233192,41.965545632037923],[130.85722657585893,41.83247604427045],[130.53082689251747,41.765786747372559],[130.2324884267519,41.488976653905489],[130.26545653006951,41.291555797522953],[130.19571964898722,40.745175374550961],[130.14121402718095,40.606998629320145],[130.0485198437828,40.490931992877037],[129.92581779641134,40.407218891740349],[129.6123501735062,40.290086085316297],[129.39581448387122,40.081324201089309],[128.99138934839786,39.889749497100979],[128.76350016268503,39.698750745481206],[128.46223260385284,39.561765793339816],[128.16606337734626,39.518207350826167],[128.09718660943616,39.475371210878855],[128.71902608872398,38.985651073181756],[128.80816485086982,38.87198940253105],[128.86982871335528,38.690087078454567],[128.85836795010783,38.49835930700025],[128.77547375203528,38.325098118712276],[128.44176697235773,37.95694975779319],[128.31889107124951,37.875092494434099],[128.12830665954874,37.828070352747829],[127.29164246316718,37.790971493757027],[127.12307994986847,37.639748964236588],[126.95676974635052,37.400369632412122],[126.73913493403272,37.293323109777845],[126.59298673079154,37.283788079274608],[126.40077158292461,37.328653900456182],[126.22247859131053,37.254502521471373],[126.0792956699344,37.244614250374099],[125.93920261373185,37.275808199834309],[125.79750781767682,37.359165249418616],[125.52919361846733,37.236916394797532],[125.33618475313473,37.225489032972639],[125.18468500710189,37.25600801119031],[124.96671059937529,37.413426999526109],[124.74688781775842,37.494145753992413],[124.58328998744308,37.640333245122022],[124.4207465985972,37.708871536226681],[124.25236100815894,37.89007817431451],[124.19144466972409,38.129825442215584],[124.23112358047881,38.324460055402128],[124.30935452083236,38.451757293862663],[124.4503134390759,38.568584893831655],[124.65758156697068,38.867686693672141],[124.68580535862192,39.035803575274883],[124.33978996450469,39.214337712192773],[124.17360878952823,39.428429324737444],[124.08925075224494,39.479695346212139]]],"type":"Polygon"},"properties":{"alpha2":"KP"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[130.45477983204518,37.158479286654185],[130.34557100767768,37.325927383972505],[130.3145194921178,37.572924633449496],[130.37888326189821,37.762193589580477],[130.51224963477577,37.911119219344933],[130.73798907244631,38.025195212833246],[130.93519707038971,38.052441330649216],[131.17101489405184,37.975950778871002],[131.34340268599595,37.816714946473567],[131.43041575056569,37.58953382680113],[131.41547666408283,37.394777470794146],[131.32706011913663,37.220605920046523],[131.15504020277666,37.037799374091641],[130.97290726747576,36.959585843325115],[130.77469505523538,36.958289141714829],[130.59155440904254,37.034113057654068],[130.45477983204518,37.158479286654185]]],[[[125.59004940235458,34.594552492029379],[125.51247291229062,34.821633906426591],[125.54963038485072,35.058706475080633],[125.65708182656951,35.218611031863695],[125.82515215289484,35.33307312850085],[125.98665511851679,35.634880949079097],[126.00771307251665,35.783385306419433],[126.09093950735989,35.948780933587692],[126.05270514842783,36.059164144761525],[125.75092337101732,36.436865032255248],[125.66157079906299,36.74392563944339],[125.68739785047013,36.932830688955733],[125.78333359430592,37.1190345198522],[125.87080412550979,37.231209463618931],[126.01530663670134,37.335711006198942],[125.87063048568405,37.739966787337522],[125.8817395520975,37.88145220854117],[125.95707769141323,38.054493333970626],[126.13088440620807,38.236098210011619],[126.39015602170596,38.323893231827412],[126.79374063597778,38.685994691829457],[126.92678263600513,38.756006546924496],[127.88743858080316,38.809067268133525],[128.08367988967234,39.029852711404267],[128.31278092123068,39.119309627258453],[128.46127928667818,39.115547233792277],[128.64517352665112,39.043538138848305],[128.78684111673709,38.905942008201009],[129.0374876821765,38.454203207710442],[129.75210854081166,37.550123280149357],[129.89952222766246,37.193653255451693],[129.97207699448049,36.774850108860825],[129.92825282676665,36.401268713642203],[130.0400668659814,36.227722208979003],[130.07225785563776,36.031997552147558],[129.95820947597335,35.524681079052229],[129.72659902514712,35.029817236502041],[129.54304668393434,34.805585538629799],[129.4161275015619,34.724727968577696],[129.22055583347122,34.658234543758596],[129.09315261392828,34.44393470739913],[128.83556291953687,34.274183578007495],[128.60082083571632,34.239241359365103],[128.3695568604287,34.320877415529964],[127.93891586216273,34.107867002560077],[127.66662560901494,34.099673344728664],[127.52234146203931,34.004324495711259],[127.38006681909913,33.966646364096064],[127.16835329926745,33.989186981885055],[127.13576864352821,33.955882411121365],[127.31347556962933,33.797515405605495],[127.4287860134968,33.49069504094971],[127.40661750640685,33.28965860253566],[127.27987918515555,33.051190320231164],[127.17353462752476,32.941996217311342],[127.03923311485003,32.869915562878745],[126.69612727324196,32.751842861875062],[126.34298116568233,32.705486851473935],[126.1488477183877,32.719815016740597],[126.01495891366331,32.779047889857054],[125.78772951031694,32.98488237594831],[125.70899790306314,33.108870233288407],[125.66603814062019,33.299364865644669],[125.71921222458143,33.53669391031773],[125.80377119137657,33.656783193282926],[126.05129823679083,33.869824019825849],[125.81836671359807,33.991991136342953],[125.68286840450317,34.13718384552682],[125.61457932307832,34.323670616976912],[125.62778866218125,34.535871209990894],[125.59004940235458,34.594552492029379]]]],"type":"MultiPolygon"},"properties":{"alpha2":"KR"},"type":"Feature"}, +{"geometry":{"coordinates":[[[46.4678333381434,28.60052833176195],[46.280066939385392,28.664463630385299],[46.131935050696285,28.796376050065955],[46.03711564547622,29.024465436671104],[46.04807836969119,29.222515333390074],[46.135184984144765,29.400718533785138],[46.481224391388977,29.806361752402786],[46.71538935609604,30.262349911071645],[46.96319451121375,30.468774355266635],[47.263891397413261,30.574914915223864],[47.79544378432157,30.579469753205093],[48.093620474220067,30.470621402567371],[48.266307537235434,30.471848645131953],[48.410783170406084,30.42452957368479],[48.53436012904212,30.335979935645142],[48.762493562259486,30.06223545127969],[48.839109822854205,29.876504998415907],[48.834684335163367,29.608936474938851],[48.77603421655482,29.462938187737642],[48.616791005290665,29.259731800037351],[48.894387559110726,28.756259367782242],[48.941201573845518,28.513509370586508],[48.865992246663112,28.27800423217359],[48.729401388463494,28.133941001451824],[48.498231941360743,28.046306534258111],[47.57409713403429,28.042980830388707],[47.427935295064351,28.09670216328087],[47.305389146663636,28.192783932055885],[47.156827651559006,28.367087127684684],[47.090542792033261,28.520058383817361],[46.4678333381434,28.60052833176195]]],"type":"Polygon"},"properties":{"alpha2":"KW"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-80.16378151048319,19.170027310145322],[-80.308701573585424,19.203342089394031],[-80.43741511324113,19.277803649377976],[-80.586081868716818,19.474047974251299],[-80.623070416447362,19.717451931830965],[-80.529930795885292,19.962996791271934],[-80.294296467945458,20.159305352075151],[-79.885306177243152,20.255245728312147],[-79.682737362655985,20.258487107834007],[-79.448522912309173,20.161422874085009],[-79.319643407003042,20.023793576773095],[-79.250897241258727,19.848221041728515],[-79.253383574058134,19.647461324188473],[-79.333762598424201,19.463109391754298],[-79.480275608729357,19.325340163108798],[-79.677073648407713,19.233718695384127],[-80.16378151048319,19.170027310145322]]],[[[-81.913414924316982,19.301176362106691],[-81.903973949489469,19.495602900996669],[-81.790021911330442,19.709596871556332],[-81.633970622490153,19.825953247753411],[-81.418254507510071,19.883912384217638],[-81.031790395767374,19.836704365735606],[-80.855996061916883,19.764404553255901],[-80.671444625244789,19.550031528060583],[-80.607596505319876,19.314629201816818],[-80.66270977798608,19.077030033478746],[-80.784544140827322,18.923700831954864],[-80.95592612614459,18.828929153075599],[-81.291103074716887,18.77223796825081],[-81.532626201648952,18.795304277650477],[-81.743260509790957,18.910799990002644],[-81.857282815497442,19.066342239889323],[-81.913414924316982,19.301176362106691]]]],"type":"MultiPolygon"},"properties":{"alpha2":"KY"},"type":"Feature"}, +{"geometry":{"coordinates":[[[46.133276487977362,48.421315394093369],[46.109477719687369,48.567405681703285],[46.129492799011658,48.71406225368596],[46.261087787105907,49.039730588474661],[46.347584612272733,49.160096768777237],[46.303476372950598,49.400995684113511],[46.422973355035737,49.87621462148666],[46.605092902179564,50.168993664439732],[46.808847529338053,50.329701051440203],[46.879563494284348,50.496716498516768],[47.098833040045314,50.733755052157392],[47.431654438437263,50.885806446584404],[47.640772307863635,50.91078816465938],[47.900468867708661,50.838300351587712],[48.130206558675305,50.676758714470999],[48.205673370670752,50.88420223254456],[48.385995476382135,51.051491630558807],[48.525534824924641,51.102473199663478],[48.718254049719391,51.112777426718914],[48.964343555828954,51.233327193398779],[49.157338934813431,51.449271024596364],[49.280958330006541,51.533772274304106],[49.679084669037827,51.616081985377974],[50.025159582021011,51.744876794424329],[50.51732972806294,52.145393962579597],[50.653565470041656,52.208810311810161],[50.852638165320386,52.225509979759231],[51.271499923421366,52.135312487655192],[51.537035958720217,52.003142688960843],[51.877974868322639,52.155474598798435],[52.237758926204869,52.208752962588768],[52.555602913610095,52.127740974252077],[52.748874874767971,51.985027666873776],[53.438202620810117,51.971974177262332],[53.778793484211917,51.835654255358051],[53.971526568733175,51.678515911636467],[54.259277362665472,51.565450049578544],[54.410952055130437,51.463002967959888],[54.706911164899438,51.497379261353579],[54.862353460338348,51.479134047595196],[55.535449459853446,51.13803368689755],[55.648503214612191,51.107183908320401],[55.731817423077473,51.132560480734476],[55.919288050370362,51.291056895296151],[56.36302614904772,51.502529091023739],[56.642698273477606,51.501843336640121],[57.052117414554971,51.563310799884185],[57.311216764227787,51.517953374352849],[57.482012663043555,51.443805734566865],[57.632190485906158,51.54671445925473],[57.817632313529892,51.590984553532465],[58.485539292301759,51.547385014293475],[58.816776587376168,51.392087964286468],[59.090933259905739,51.174217942031852],[59.562671256854586,51.107548353380139],[59.685711487065817,51.220077879261559],[59.826247613497991,51.294559227711794],[60.066292894716,51.348974338012354],[59.743966174701676,51.523565027464137],[59.608139674056773,51.66579311287299],[59.531796592875878,51.898519760715466],[59.57514352173272,52.139581998361407],[59.655443563332987,52.263811760849308],[59.76857798136912,52.35910989317324],[60.280553790974857,52.603145790959445],[60.28335970934959,52.76853937062161],[60.359363016694992,52.976066238504892],[60.443856203575272,53.092916281268423],[60.63628692680458,53.259225063777549],[60.538269231846932,53.387130739503938],[60.489452645277652,53.523791243209814],[60.481935987325116,53.66871416151691],[60.516352435770692,53.809691679135192],[60.63668676220265,53.9932615196876],[60.736704520647052,54.253809272709574],[60.997373774875818,54.461055728963345],[61.324703468217059,54.548947009181731],[61.859245441102871,54.468442078644138],[62.027305318644203,54.502205396293611],[62.390101014287147,54.511644794318237],[62.544584760911334,54.561165491336268],[62.915333677102943,54.59467058643105],[63.086735193469799,54.659764899534061],[63.787513742983201,54.735333376612338],[64.380071134613161,54.877638621151569],[64.890736143880048,54.87639508201493],[65.033363858938856,54.972265597066773],[65.373639505843698,55.112207166912107],[65.62488337067667,55.122409026479573],[65.901073676713949,55.192892687848143],[66.212614417528499,55.172033384266115],[67.782942958271974,55.425289748724246],[67.902531001448565,55.557750234493447],[68.034650072320005,55.630294209167992],[68.392143042402893,55.694248314630087],[68.833922956954524,55.868374975659655],[68.965141748475048,55.889215596109494],[69.584597173552964,55.848329341833555],[70.150489686853902,55.690945096038178],[70.441242557285179,55.780071250103838],[70.742272973299492,55.804910000265942],[70.88834145023614,55.781792791489409],[71.060578395304759,55.686960030419378],[71.300610120903229,55.43989668975464],[71.672610931787148,54.705394017766309],[71.841981949094489,54.713597435317624],[72.057404100312795,54.808568343786547],[72.202694166792725,54.825106234570526],[72.435555642348163,54.758648253953169],[72.62172670261161,54.608604797256206],[73.005518125546516,54.598678412829528],[73.312062935112493,54.469528120045453],[73.597459681986535,54.558520625901515],[73.79196609879574,54.547255716295943],[73.926876832385119,54.490056936702977],[74.039411462723919,54.396202989534714],[74.145495531100366,54.251060554384189],[74.206958789619591,54.084649198940681],[74.672983251748164,54.298756678778453],[74.95919918097141,54.329800224688746],[75.135270275269306,54.48785850279404],[75.298669664652451,54.5697797653321],[75.582604006899103,54.605898593667959],[76.155199604480373,54.799140539921623],[76.725512663306077,54.929560939546349],[76.874002344979175,54.940770598993396],[77.01920784321085,54.907752341747006],[77.185631357278638,54.800528226267986],[77.31450672614838,54.590348429622168],[77.334594347887219,54.393395936802676],[77.25571982066397,54.165394972509539],[78.192800856254081,53.641816639269742],[79.516385919125923,52.207002231530865],[80.073272388396759,51.51336071706541],[80.269357396159336,51.650104114488208],[80.605639765883893,51.776038868515592],[80.759444675445252,51.792547936094579],[81.344734784893461,51.640937964746911],[81.798710053790174,51.240493280527794],[82.362736512333598,51.235235637796933],[82.595814270028882,51.365054185993934],[82.850473142390868,51.39443836636169],[83.042910250390534,51.475041563725384],[83.491807588068497,51.476204931523775],[84.245019367568418,51.174441852525987],[84.545838196945851,50.856616673646108],[84.65283481927672,50.686051735951821],[85.180928420214727,50.523051947306733],[85.307032438961812,50.447273615397627],[85.430812923282019,50.295813011668209],[85.497835714125586,50.109557446113364],[86.059209069151677,50.024067801782749],[86.584797095671078,50.268741447425015],[86.776412673295425,50.266723856783521],[86.964946165032046,50.189107545666666],[87.139012859324808,50.033990861405648],[87.23687463072406,49.756942477073565],[87.47033363898872,49.656366434129062],[87.617373140216714,49.536441901908297],[87.782210306647471,49.282686959138097],[87.822443354994988,49.091949751875298],[87.786682841121007,48.900323641803581],[87.680364274507497,48.736935322738248],[87.473699161668819,48.609517908423975],[87.204232753620019,48.58452593535953],[87.105020811725836,48.381005720560992],[86.823233064419895,48.110579035269481],[86.683516218575036,48.047202893781311],[86.087843088310848,47.942809911913386],[86.052404974040257,47.86358984456966],[86.155620339963789,47.302343983960888],[86.145650188038374,47.151778183099836],[86.063935787619499,46.968580974000105],[85.743276824514723,46.635801775337043],[85.602979030594184,46.577930432524887],[85.365612859758599,46.542531436901207],[84.944471288632116,46.356667030088914],[84.798980383168796,46.331118999324254],[84.605487644314906,46.364754252457899],[84.434892255389983,46.471130310273828],[83.926206399162936,46.478486099265695],[83.365308466554268,46.622114024267091],[82.939537759055511,45.76742565075093],[83.040218065200591,45.657223965288338],[83.113163494401476,45.48452187575095],[83.122968953043411,45.343890906924216],[83.078411518890874,45.08167442475996],[82.98054019997042,44.895501792949354],[82.793008868446748,44.70596883218716],[82.558016054846789,44.627098348170115],[82.408732514425068,44.638610379369624],[82.219697618525345,44.700172922899064],[81.928545323104871,44.661343278037698],[81.782306408837925,44.688266660100773],[81.561107689791086,44.787210103325876],[80.978496770296687,44.662984230065199],[80.941574130842923,44.519501670347118],[80.854933097345139,44.375469119672609],[80.858363987975764,44.257285458441991],[81.123206196507795,43.726618057872464],[81.272764301674471,43.27364186979888],[81.278723727581863,43.079499353509597],[81.16794359464555,42.809172224029489],[80.994330264116613,42.668398615070949],[80.876468280142021,42.504985400483534],[80.729950610845975,42.412334057803669],[80.758284419282589,42.211724507509068],[80.711396725780205,42.022875155354292],[80.595970623755832,41.866224752920367],[80.425136236682889,41.739262083450875],[80.236602729583112,41.691057466119084],[80.043945776023563,41.718477653510888],[79.705514831813119,41.939220621438572],[79.225041916016892,42.02693307710458],[78.891691174702359,42.280637514761551],[78.457631172110837,42.366181542649166],[77.220946435900075,42.412740783883955],[76.950266186888868,42.466468996255557],[76.503372536833112,42.419320446374023],[75.971603377212134,42.430202835576686],[75.780932764163069,42.33820075020418],[75.599666683314695,42.315978605739211],[74.911503175252122,42.423497922517527],[74.200878219080565,42.702278391984485],[74.104315593887961,42.695328602102855],[73.96552948270319,42.644316172398369],[73.988396854476846,42.344431536436609],[73.902170518646301,42.122475884433548],[73.764603411240415,41.989749745548735],[73.587282673938944,41.918347412910705],[73.396126170292135,41.918705319068984],[73.098624666143493,42.029843102894098],[72.75310055595925,42.071892401209894],[72.52565877139466,42.178405212877628],[72.174467794493808,42.264925573909359],[71.782415038455937,42.315891639805507],[71.487917867257323,42.263759285189771],[71.445548170230794,42.225098698154007],[71.37225009563376,41.986646924213389],[71.241498181390611,41.845113707159598],[70.995513317143661,41.735760519364952],[70.802391156981912,41.586360933657659],[70.534373649123651,41.53791090246888],[70.297049101672911,41.36267981205858],[69.861616815227833,41.209715379021354],[69.442855143071881,40.98623589856517],[69.340574912672153,40.851747183862365],[69.099983337294759,40.656269145971891],[69.055782058232168,40.453474440664749],[68.941291884082503,40.294210992745356],[68.714957731086784,40.159556077020056],[68.526593101710816,40.109871777872925],[68.172928128345816,40.171463865836607],[68.021437475028705,40.235030351624452],[67.703310492217156,40.44706194305261],[67.57194857602903,40.656506442060873],[66.778984686772517,40.643836737131593],[66.544030562416012,40.707591073468237],[66.408188888090194,40.780537935486869],[66.250706967670766,40.981298660666852],[66.0842194960657,41.503170049867357],[65.902463437950857,41.516765798020451],[65.767993670906705,41.567469217795782],[65.653500338643511,41.654326755713278],[65.568441943729198,41.770162952475879],[65.50982243247779,42.000876099849286],[65.546837284415176,42.448335342993175],[65.427878651397961,42.546807440866502],[65.162890251375813,42.910482257371768],[64.988223162949964,43.005229747704988],[64.805031599402298,43.154796737591255],[64.465986515658315,43.051985449546244],[63.219395088278176,43.125442010747193],[62.045364972983478,42.995393409559206],[61.848795994783579,43.012799781045366],[61.674271519818895,43.104905983861279],[60.844938860551487,43.781399971203498],[60.66704480747142,44.000388435061829],[58.484306559706518,45.025474068829027],[56.475851318809447,44.593136582370853],[56.474934322024403,41.274937575103777],[56.397252137035174,41.051259756911165],[56.225366630439751,40.888405033803295],[56.092408386299688,40.835924656022428],[55.559781317731513,40.763178022913188],[55.239466059560428,40.836117435531278],[54.883455004316374,41.117894598180541],[54.529242599555332,41.567350404678905],[54.014161296495992,41.823547748254761],[53.758706100373843,41.800622673242742],[53.263177843926691,41.683485716551623],[52.633403703918702,41.300613396489098],[52.489037487286346,41.280807263783601],[52.299337414185977,41.320226565847996],[52.106290917001949,41.465041246563075],[52.015366177292861,41.63613388468162],[51.962385104785973,42.098746186494495],[52.028746781550844,42.363855130756043],[51.709687699666297,42.406996414879835],[51.579090358586242,42.486974885144058],[51.407572030885056,42.661826249167497],[51.152879583743591,42.695143320369247],[50.9430904082623,42.819772451274346],[50.817269020938149,43.028848745748647],[50.795707972621578,43.300302394556944],[50.658868904637323,43.45842545960317],[50.470587799468582,43.79796812369252],[50.073444927265314,43.89804179685818],[49.951790704207731,43.974433492104588],[49.856877027836113,44.082261295235256],[49.785060939485604,44.259264689967218],[49.775632134217645,44.47260249922401],[49.558831560952434,44.693280650779911],[49.507306288901461,44.828531047641995],[49.49667773096666,44.972872854709465],[49.527836455016939,45.114211658393508],[49.661736883812324,45.36965892733317],[49.764209365858576,45.470158506300386],[49.891010465906781,45.537406227271951],[50.417686151184633,45.578073823135568],[50.598696626183774,45.516189834743216],[50.747656142159038,45.384591261335771],[50.877667568141071,45.471862169195724],[51.080529449703192,45.728548160432268],[51.248083462520476,45.82864620657859],[52.265250168506881,45.898486646025603],[52.440740800050222,45.998223256065771],[52.598443254132611,46.211829618035004],[52.565083308787933,46.460156596691633],[52.272034917088568,46.347111713330598],[52.114162878937208,46.329471344211889],[51.572696217924275,46.46450798952781],[51.414647132109266,46.555354129156427],[51.212212875047321,46.588809363425717],[50.719309730344804,46.401456221615284],[50.568712313172398,46.378855762836679],[50.142030685011456,46.154735522410924],[49.7281605372768,46.069779330596781],[49.572393476250319,45.956884942167264],[49.357128850710367,45.877385204585444],[49.204572693716244,45.848192181128674],[49.050418853944748,45.867223926688816],[48.309086684574943,46.165157386496823],[48.162261193429778,46.280073348554971],[48.067744619494945,46.440790554968572],[48.019602960546031,46.705149978940398],[48.046731159170413,46.899021914416188],[48.12446717794618,47.040771002639715],[47.905121888404196,47.261137770050397],[47.550238478636444,47.293847115816192],[47.268124598604153,47.243802422482787],[47.127687659208661,47.269811101177176],[47.000333475528407,47.33446322576205],[46.703461369335919,47.63300706233786],[46.598924584590854,47.890406546416671],[46.405666697970837,47.982646940360091],[46.286574616487123,48.081248691896171],[46.203299996110367,48.211519820237086],[46.133276487977362,48.421315394093369]]],"type":"Polygon"},"properties":{"alpha2":"KZ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[99.90085909642697,19.806689603098711],[99.702601741299361,19.975340317297913],[99.638681049464338,20.106302556510446],[99.615237849403428,20.250133635610659],[99.768634838270742,20.866917537391529],[99.864160249881948,21.048646233141785],[100.12708279555551,21.245471345061336],[100.42694516129056,21.688180582730013],[100.67262765737814,21.811747467416847],[100.86602754223307,21.986080428510331],[101.06040636877165,22.060372716056467],[101.02486363469735,22.262309950121793],[101.06862682121907,22.458284141411319],[101.43622148913813,22.893080880922781],[101.61100862077599,22.978408991709557],[101.7563850827331,22.994698570662386],[102.20585884228383,22.898802894268474],[102.38932099417734,22.813627739118605],[102.84313698534709,22.320808621791343],[103.08274179522633,22.229872404636232],[103.35756007940378,21.969971554785612],[103.44593370184579,21.73970214552612],[103.40012483532816,21.367819767431797],[103.44248373241381,21.307863748199285],[103.60049875987235,21.260131081619754],[103.97847575982041,21.429913301804913],[104.22265197766747,21.430301962204595],[104.7909652825895,21.114169931853723],[104.99363212181586,20.931782798349868],[105.05904046793503,20.799346981109153],[105.08321077978212,20.631399853019442],[105.24540679183139,20.519147297757574],[105.3358653626847,20.39229429223365],[105.42743794122552,20.001460276937699],[105.40210557522262,19.860236098521131],[105.33771030281892,19.73202047455591],[104.94263377928847,19.267304736778467],[105.36750061791994,19.100899094849925],[105.48723105828788,19.016702721949304],[105.61725436799766,18.81213499582806],[105.64157472181176,18.618735111004678],[105.76183809007532,18.551223471070251],[105.86529565098326,18.444179171881558],[106.10174066570413,18.035562295744267],[106.88542116988663,17.274871436373562],[106.96649833701943,17.13901490182543],[107.0027434049838,16.991546626778881],[107.17557475985154,16.895663883366232],[107.31887909982242,16.699082572951443],[107.71742106984381,16.426008418918663],[107.81826675369759,16.310898195787665],[107.89546707183395,16.06967238640112],[107.85596020967418,15.767323044356877],[108.07689990658979,15.520115493688964],[108.15210046184272,15.28261175329621],[108.12288512169016,15.084694486776064],[108.01838834220131,14.861802168290129],[108.00120499666019,14.572293230992797],[107.92183535334563,14.408751825231306],[107.71067024870719,14.160201344595507],[107.45491112034537,14.064127501990001],[107.12492180132165,13.863829658591504],[106.82388355819263,13.81494549615423],[106.59095076038236,13.87072092448121],[106.44365516160292,13.592991527961237],[106.33333450219379,13.498481756185507],[106.2005027890275,13.439672906248859],[105.79564490173766,13.436765411500858],[105.65691121135711,13.490401603379617],[105.48705853815088,13.62545296087173],[105.26265307132081,13.619776466350816],[105.04976136058376,13.710098286651778],[104.80093806986476,13.97098104706518],[104.7055824833451,14.199316170165268],[104.68356019494576,14.346720113147336],[104.70596367658007,14.494066601411108],[104.77080234273728,14.628263661602299],[104.8723151732556,14.737387668278942],[105.01964653532502,14.82381582951594],[105.03720608809867,14.944893718672963],[104.99081933294053,15.262164932157509],[105.0418558207639,15.481065542363661],[104.92884261088084,15.657095844226138],[104.6817449803201,15.819387889689482],[104.37041645443205,16.246595903516308],[104.25587592909054,16.577175348876022],[104.24454759493027,16.907701463816501],[104.28293438837116,17.181724598424498],[104.05599321797412,17.36503763843557],[103.70248595186429,17.827327608497928],[103.53528973624577,17.877204642307007],[103.43625443291165,17.711499319888237],[103.33563139734228,17.617156322873278],[103.06086861338107,17.50039014493958],[102.94803652259765,17.403476107119438],[102.81145099486451,17.341509400847226],[102.66263270057782,17.322838719071797],[102.51497259503638,17.349144085517736],[102.11078257010332,17.614783879090677],[101.36097620974327,17.038211166934612],[101.05836088815482,16.981987887465205],[100.71387179941901,17.103862288777194],[100.47755398647688,17.330839757034507],[100.40886791844851,17.573512552766584],[100.42894020922567,17.724564364009805],[100.61050686456336,18.176864187124931],[100.55182289127816,18.373978299896581],[100.56518908666958,18.574954688783478],[100.71270505239936,18.845691097219035],[100.76689890449771,19.019670848289152],[100.61951988652991,19.00056247491829],[100.46960633098072,19.02694599755047],[100.16502464440428,19.195386693017316],[99.959582337448893,19.450443811439769],[99.90085909642697,19.806689603098711]]],"type":"Polygon"},"properties":{"alpha2":"LA"},"type":"Feature"}, +{"geometry":{"coordinates":[[[35.138992462448634,32.584848072796504],[34.948434423764091,32.610436419325637],[34.781621227702914,32.706042314568819],[34.663219294406495,32.857528656451727],[34.609062368382112,33.09081581551856],[34.648617428304107,33.278971407569294],[35.130211032927313,34.211222948225142],[35.212882237968451,34.492866420442212],[35.61409525564131,34.973447311490787],[35.777086294006452,35.087478776379719],[36.319002226729388,35.177937460655656],[36.638450228901192,35.087957233075471],[36.862806851512602,34.868431821215232],[37.083484613464094,34.256103669078485],[37.040904495604146,34.016620477823523],[36.855775236236099,33.747853111195738],[36.793651965234503,33.582401268572454],[36.647388305983164,33.426477633987169],[36.471680539398974,33.342874929900439],[36.374354884620189,33.207855189690449],[35.916876179880695,32.854230272838841],[35.771082228996832,32.704133175415841],[35.513347239637085,32.586514292609849],[35.138992462448634,32.584848072796504]]],"type":"Polygon"},"properties":{"alpha2":"LB"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-61.567251279662884,13.791138423003254],[-61.563983854823519,13.95916690910183],[-61.50113854100821,14.156940069268952],[-61.297161549176792,14.427088677671895],[-61.110067733417146,14.550488204829],[-60.923467121383432,14.592798474552872],[-60.734637464664473,14.561922787048506],[-60.571230938411709,14.462382571489828],[-60.457176759906659,14.308754476074295],[-60.387769231096094,14.038560661632934],[-60.407939565928523,13.710806220873284],[-60.537511865761644,13.437549317035403],[-60.724953028414298,13.272190548149746],[-60.868351164607397,13.224880996996143],[-61.019333723193171,13.222538755142313],[-61.317619230131854,13.354467853290997],[-61.489022722375296,13.525745774709112],[-61.567251279662884,13.791138423003254]]],"type":"Polygon"},"properties":{"alpha2":"LC"},"type":"Feature"}, +{"geometry":{"coordinates":[[[8.9878003784458276,47.008055407675563],[8.993991829976963,47.269740421832871],[9.1054374885287412,47.53821604541551],[9.2433985321145684,47.681645547800386],[9.4758536736643517,47.767565610452223],[9.6739339333466159,47.748343684797668],[9.8855318840298878,47.619332223036842],[10.059866645842412,47.330474378219066],[10.10732795505459,47.161387298875198],[10.089053920987139,46.963004768098308],[9.9820957995839805,46.758776589207834],[9.8391043328359107,46.629862829726385],[9.6632973609374346,46.564554862544057],[9.3814627888324438,46.573949749819178],[9.2028055994321658,46.651722325035919],[9.0674417800346205,46.791877957025356],[8.9878003784458276,47.008055407675563]]],"type":"Polygon"},"properties":{"alpha2":"LI"},"type":"Feature"}, +{"geometry":{"coordinates":[[[79.216093598161024,8.1285117907862556],[79.246683902438491,8.3622000810979689],[79.382294120710696,8.5963058031581099],[79.421229374787501,8.723527597603713],[79.285342997696659,8.9148506078455068],[79.248094903852817,9.1045047170380062],[79.285559001149167,9.2941162753417359],[79.409643529233023,9.471453325775899],[79.345963824833433,9.7134950182104252],[79.365860955200773,9.8542754647188797],[79.424579157945715,9.9837636980931119],[79.69111564262586,10.218824863289649],[79.867456801874184,10.299058997048709],[80.332075266182201,10.28976650623823],[80.516375344816836,10.220928087470076],[81.094083680580965,9.6874310415514504],[81.63233502334063,8.8692206614050306],[81.775638696672004,8.7271954292697043],[81.848005318339062,8.5861591517042815],[81.910995064937325,8.302397029398632],[82.292306897871583,7.6231344046151541],[82.372859543673172,7.3198268305286156],[82.360989194178103,6.8844451186135114],[82.205213526401778,6.3724250912847493],[81.993942910591755,6.0751661334465794],[81.592514697974764,5.7928084211755948],[80.904494052356142,5.5129666902673033],[80.463782175844898,5.4506294257129948],[80.145323262183567,5.5248566339036298],[79.999582793648997,5.589526537369343],[79.748886730217805,5.7931413141104988],[79.665441815191954,5.9010545958272047],[79.387996472137473,6.6630964455357029],[79.216093598161024,8.1285117907862556]]],"type":"Polygon"},"properties":{"alpha2":"LK"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-11.862708061398386,6.5549505711126566],[-11.951423176869245,6.6770576555527859],[-12.005866367199122,6.8701736965859777],[-11.993994519229142,7.020638214028641],[-11.937764024944791,7.1607047814029192],[-11.636756356376729,7.5694634451764466],[-11.099030200020415,8.0500498171958856],[-11.008495503487746,8.3123602977034832],[-10.784928480207981,8.5353030289610832],[-10.737217886088905,8.6938858741288758],[-10.656257886823662,8.8175831800949886],[-10.499198323232065,8.9357567618006311],[-10.222356785086422,9.0138022474925314],[-9.9863630716458136,8.9945771655838893],[-9.7728087066909684,9.0373437241310999],[-9.5436302291466681,8.9769925823383243],[-9.3340846995363798,8.8492950945238213],[-9.097787319788651,8.6161840121020763],[-9.0395039976190681,8.4893968416930825],[-8.9577261663742611,8.091933971591363],[-8.648636008831188,8.1720218477293525],[-8.4526982756823941,8.1606660125605437],[-8.2762266452681352,8.0747689001364193],[-8.0623820277259117,7.84698235304338],[-7.9791131210780915,7.7162277941829824],[-7.8066220155578803,7.1723183858826145],[-7.8335211189958374,6.7633282150829812],[-7.5650904567965824,6.615746867925953],[-7.3893847027632154,6.3546626127883554],[-7.1373994564771657,6.2276424467367812],[-7.0121754343397864,6.0740402875883746],[-6.9003576604313581,5.5376929434596471],[-6.9428176802486457,5.2122811626132961],[-7.0801197803630167,4.8799363459508607],[-7.0454073013270362,4.3483545843015285],[-7.107341661296716,4.1105403112243106],[-7.2750952365129082,3.9309568169207298],[-7.4104869413016905,3.870140266232287],[-7.5577666686231222,3.851748733306037],[-8.4938180108442136,4.1487888134928417],[-9.4180993800664492,4.6446677244329351],[-10.553331130963713,5.6536714588812416],[-11.0795776079327,5.9060125532468408],[-11.238771530415381,6.100719684158685],[-11.564312791089657,6.2693998311948826],[-11.862708061398386,6.5549505711126566]]],"type":"Polygon"},"properties":{"alpha2":"LR"},"type":"Feature"}, +{"geometry":{"coordinates":[[[26.621650589265467,-29.918632000972789],[26.56577844380466,-29.780708656221073],[26.552972642156512,-29.632450242935207],[26.628635994811187,-29.397977365000603],[26.769416807409872,-29.219299140746507],[27.011543458183521,-29.077972718504732],[27.404506890671783,-28.565613848057765],[28.054833876795342,-28.234160736778058],[28.530867560753745,-28.091104404215368],[28.684247893888507,-28.085454532711665],[28.877730544715586,-28.150159883418304],[29.26987394988274,-28.489522035617586],[29.596719741135054,-28.686706165856908],[29.725721087588674,-28.825858490731854],[29.87282184140355,-29.137903921895099],[29.878578533937851,-29.378378873644543],[29.768574120439474,-29.722230401896546],[29.598521474007651,-29.971837227148249],[29.554867141921605,-30.121727895092437],[29.476972783289533,-30.244895753509248],[29.366871269729312,-30.340370929159437],[28.968396803996793,-30.545755226291003],[28.657923285631092,-30.624050886017859],[28.350742065032861,-31.015314273876655],[28.134848929763301,-31.128168589907016],[27.988301297120312,-31.141087724618497],[27.577294119369718,-31.067751862386594],[27.081387741314963,-30.710230974432967],[26.953575822553962,-30.562210516566235],[26.8690648542775,-30.336809241527988],[26.621650589265467,-29.918632000972789]]],"type":"Polygon"},"properties":{"alpha2":"LS"},"type":"Feature"}, +{"geometry":{"coordinates":[[[20.59505926311547,55.698765318923769],[20.551346263499038,56.14029009638611],[20.591211800289717,56.276876726008311],[20.667936304108029,56.396703763562428],[20.816462873944364,56.513926686108498],[21.507399306564864,56.792436571826187],[22.091074724536856,56.906467783306752],[22.928270196649557,56.893452537343038],[23.137888579888653,56.839657520484565],[23.857402873837291,56.826970853333968],[24.169933060018305,56.769286307216881],[24.39330332261931,56.78324083332911],[24.719745572355844,56.895961545610227],[24.859321861201867,56.91055626259628],[25.08333993480505,56.848247110313103],[25.309323401828816,56.671753718985805],[25.86688562333784,56.560981036577815],[26.483224948802306,56.214059029339673],[26.768878336119379,56.135496973326056],[26.897679108446269,56.064025096173253],[27.026704478086597,55.916688617670445],[27.092645439841505,55.658868519596048],[27.223316936231072,55.495256708563595],[27.267967404552714,55.358784207509864],[27.271990300642653,55.215249458503742],[27.235053841651364,55.076490293536573],[27.160204311485163,54.953950684841665],[26.879642824103431,54.715196051703067],[26.70522792253167,54.641366421374748],[26.504183574728291,54.628427895063886],[26.197028457266867,54.479560954576009],[26.264343307678956,54.242038054216948],[26.234597114090182,54.037818604211651],[26.15584885266146,53.866483431874961],[25.97818418148815,53.712800772726872],[25.844211584876049,53.666350666840472],[25.568798744618285,53.640176772561198],[25.327105718451246,53.705334471974822],[25.149930375482747,53.652601135945645],[25.042934191472142,53.557226196641601],[24.906334102933322,53.494392674224159],[24.359855352461331,53.394947534459661],[24.072594432731595,53.44561634946897],[23.447393036954836,53.440313278241462],[23.3075203444096,53.472764843685979],[23.182638026369546,53.54363007395159],[23.083045114725401,53.647064720806],[23.007092777948376,53.808052800815261],[22.86531032239392,53.866122072829981],[22.600731373302924,53.885279618478243],[22.436661935548759,53.981094775301713],[22.214372504341931,54.311329284177752],[22.187133837477056,54.5727003898172],[21.787396800867363,54.608247759800562],[21.090259819412225,54.813004621091821],[20.793460005103164,54.798362182887864],[20.575433869164719,54.906704440055016],[20.434421015401909,55.105170597658756],[20.400373407235929,55.297662198393525],[20.442732353198384,55.488497141923816],[20.59505926311547,55.698765318923769]]],"type":"Polygon"},"properties":{"alpha2":"LT"},"type":"Feature"}, +{"geometry":{"coordinates":[[[5.2910322044333897,49.561720485720393],[5.2255040300023801,49.825319282552854],[5.3011187334756773,50.151027363897967],[5.520646915894992,50.443178307805702],[5.7103272939700807,50.590281089099093],[5.8883304704793069,50.659109426864532],[6.1921588797149809,50.643583702707275],[6.3693068677827398,50.568351515026386],[6.503456643405495,50.437265593260257],[6.5871136941934818,50.287729889403884],[6.7535082119672563,50.221394886501315],[6.8628742251415646,50.128117650504983],[6.972817096312939,49.916928863792982],[6.9919004510326204,49.715256090838153],[6.922817254703161,49.46731443543117],[6.7201044147361593,49.123435361010586],[6.6051910120777322,49.026612039093372],[6.4666989681833007,48.9683042389671],[5.9775759318233295,48.946892858077121],[5.5810700264408792,49.068026331261422],[5.3522745504471541,49.296709617562307],[5.2910322044333897,49.561720485720393]]],"type":"Polygon"},"properties":{"alpha2":"LU"},"type":"Feature"}, +{"geometry":{"coordinates":[[[21.233183000654911,55.606632745460921],[21.088220024866448,55.57214254977881],[20.939530536966881,55.581903396862998],[20.800320446131565,55.635048373645937],[20.682953745550829,55.726857390318756],[20.562920422086407,55.94239120034576],[20.517685235819979,56.314223624843819],[20.581179858808316,56.921354701495936],[20.629474213873184,57.05721249314756],[20.749904742646343,57.206395192129278],[20.925642312421164,57.330027707896235],[21.088546264741971,57.65766219077608],[21.425698292570914,57.968379949804152],[21.637562649303366,58.062346092053225],[22.463240608850487,58.215631478064701],[22.66740558365111,58.21111799563888],[22.809966342682067,58.153844683817248],[23.00255353124329,57.972476205972441],[23.424824040784145,57.732249990141071],[23.623559902140812,57.509135001629609],[23.769931679684454,57.493546115459573],[23.852108577350236,57.5300566242286],[23.822755134847927,57.875040524889137],[23.847226948162124,58.024955320152976],[23.915584804068295,58.16060383294171],[24.021519733065404,58.269466619532267],[24.203930377307881,58.356060561563851],[25.077284944651222,58.562063595871116],[25.440391892243948,58.528912021100489],[26.138600452855389,58.31565264346483],[26.259127666857516,58.25995999683893],[26.530443645508868,58.05412752857039],[26.998187772635607,58.107692622760766],[27.601321032951684,57.999745740353482],[27.772721704949756,57.933981439740769],[27.909207180467131,57.81029476614605],[28.133652269295322,57.689149684115968],[28.264482170906394,57.537727874676115],[28.325456911411234,57.347131157626812],[28.299192977368286,57.095989337515398],[28.559193553615991,56.750089757483579],[28.701269078236805,56.283809600384615],[28.655973446695015,56.051280265492018],[28.491681115485857,55.780181221518852],[28.332102835852883,55.678365943270983],[28.094649768172339,55.607148035134152],[27.794741934471404,55.349110079446035],[27.602501907505662,55.299715420509195],[27.146912825705346,55.319202492966106],[26.967063918547598,55.230827372776218],[26.628492969321321,55.169029793484704],[26.108385601411442,55.281609218300204],[25.816221519879655,55.472274010307622],[25.496476603698515,55.627515421865603],[24.944263030393934,55.716719547456933],[24.720549966597311,55.838121469511094],[24.550219896911251,55.790339411499296],[24.106433047253528,55.764648258602051],[23.735531373116824,55.83281812049659],[23.266129475264272,55.860352308145693],[23.040941796302238,55.824352648271791],[22.780923199051795,55.894333003189651],[22.134612465275236,55.904934911480126],[21.781712283541054,55.827726975155379],[21.233183000654911,55.606632745460921]]],"type":"Polygon"},"properties":{"alpha2":"LV"},"type":"Feature"}, +{"geometry":{"coordinates":[[[8.8979801777090817,29.832720357095837],[8.8347524984286281,29.96153368696314],[8.8107139930068534,30.103000162270117],[8.842556454908113,30.291135687634725],[8.9118096688319515,30.41681227948823],[9.0538479640537251,30.544225155504126],[9.5846209845259889,30.798722511860387],[9.7300660060685331,30.978613511252323],[9.616398890516507,31.428882306186196],[9.6281508673945311,31.576879800610175],[9.7098838856133405,31.756543290553399],[9.9804651782487053,32.08888850686391],[10.172782676111316,32.177274032157293],[10.28782914249337,32.312491074185914],[10.541056124369872,32.491034185787804],[10.958309497586217,32.718452685391398],[11.04703903182258,33.382750285973032],[11.191005577177661,33.570878831487022],[11.405409569854429,33.671624271795501],[11.595570972293176,33.673291280228824],[11.950724774716994,33.574221374627854],[12.448491051935228,33.34021801401007],[12.719892596888638,33.309101443602266],[13.236439068885911,33.412216065626367],[13.397945210495672,33.401152511101444],[14.318190564914662,33.182568062132049],[14.6843369016949,32.989471429141851],[15.365433385477019,32.853891360984036],[15.535225523945961,32.739247896207452],[15.781346363008208,32.426919392390154],[15.842746590185653,32.285319092810965],[15.86344047195802,32.074285964071208],[15.921810162771914,31.937356342927924],[16.013881041420532,31.841119291069777],[16.233156315740391,31.758442472567324],[16.872125389170769,31.706212513270721],[17.958401830237602,31.411150855779265],[18.437513102345669,31.211761491333675],[18.923079087664274,30.855558785595512],[19.151783658754976,30.778791960604732],[19.393246583397687,30.889886010564943],[19.60659716591443,31.14247991267872],[19.475634338135134,31.437600828363735],[19.427524150817852,31.847863904155055],[19.564459567321535,32.28329707189269],[19.658599310705082,32.441860879582762],[20.151478835397096,32.880560862429064],[20.885539961594887,33.242969992295713],[21.250923555048747,33.287696065232609],[21.50925463779495,33.420706434877914],[22.303751536206377,33.404465540760597],[23.334494461042578,33.054965183910383],[23.50646991343358,32.895905476007989],[23.587263656886869,32.686051781087393],[23.903674994525176,32.647077529876675],[24.240235770604421,32.510835268669609],[24.738286103050047,32.512814970897523],[25.195240960361954,32.38959028850131],[25.338287377699601,32.2692603546542],[25.565811467072791,31.932968482897483],[25.629595100528768,31.797001349015819],[25.650124275176591,31.648226464101999],[25.585033761717721,31.408275221960618],[25.395015229939339,31.175059300833514],[25.456032617341712,30.606001315256425],[25.24400667743069,30.121960113913424],[25.480273434520598,29.176776795074929],[25.479271913792093,20.002660452204477],[25.458646688029962,19.860588934911728],[25.398502350764815,19.730234131937159],[25.265827146117591,19.592975972288002],[25.091380771568918,19.515445496181631],[24.479685639340975,19.48540129673334],[24.420672245303745,19.260637199002868],[24.261611744806565,19.083788960397964],[24.038834699973616,19.000446710538903],[23.84841484999383,19.014620565056877],[15.975261960037704,22.890902606958228],[14.427387910212085,22.16697832785065],[14.186562631311414,22.120734070522388],[13.952713593562638,22.194558008627858],[13.272003942137989,22.71179165492833],[11.858139144612215,23.030289831847057],[11.717914175922973,23.085114880218299],[11.568146364680585,23.217897722428244],[11.158173681997587,23.892044658694225],[10.70297128282623,24.02515604747002],[10.491309565468223,23.983231815989303],[10.337805380284035,23.99081816784463],[10.086149788077567,24.09111966447994],[9.9703588995872199,24.179753355459489],[9.6701981836420448,24.571432300360094],[9.5233506037667794,25.138362009665645],[8.9663226442422292,25.942581516744642],[8.926832379642617,26.082069651518914],[8.9290180647204807,26.227023587380252],[9.0418899298857447,26.552064618410974],[9.1591853842962649,26.707025170950335],[9.3635586317670878,26.838294560246389],[9.2480000899887429,27.345470193704301],[9.3974168055638589,27.857989858242149],[9.3161492024707044,28.541687800637437],[9.3210172445031816,29.044716164047983],[9.1948750410765925,29.39902033371904],[8.8979801777090817,29.832720357095837]]],"type":"Polygon"},"properties":{"alpha2":"LY"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-16.959187562469666,20.922984531702912],[-17.107942541633157,20.93315796586786],[-17.247083559645183,20.986742662332585],[-17.396526559555888,21.116974054940297],[-17.468634114985758,21.247480924639049],[-17.499347341706759,21.44331280035707],[-17.39627103674615,22.081862379907257],[-17.185017312061529,22.470068381762825],[-17.010665539948999,22.652588838507974],[-16.838068667920478,22.77166709156527],[-16.679125025260358,23.270766688161661],[-16.478065649889967,23.617054954598668],[-16.4523389654128,23.835629688041362],[-16.301132725564642,24.14164286412154],[-15.915553683164317,24.453019233170995],[-15.506700525897706,24.86432743066511],[-15.377186185829093,24.94430553185309],[-15.260709951807552,25.585258175766917],[-14.987276545397481,26.123323756727888],[-14.815511978411706,26.551067062635205],[-14.66448623446446,26.686061286318587],[-13.970826239914443,27.088242266536689],[-13.610646799473059,27.902266715344474],[-13.2473106450865,28.315032097766995],[-13.05772407876481,28.401935214965917],[-12.152532864614429,28.604073559047844],[-11.806841862103207,28.747914055423017],[-11.372823389663225,29.119419018914815],[-10.806665495646552,29.4580055551165],[-10.588643269253078,29.696838677278343],[-10.15674527969724,30.256108615513163],[-10.322031367243367,30.493498723391447],[-10.37309173751056,30.671792854610732],[-10.297664573278706,31.528013572428467],[-10.10695561496501,31.962289120478935],[-9.8077579314989602,32.325074695599888],[-9.7220101585291445,32.724076410201711],[-9.6531501179742918,32.861983621574602],[-8.7548815817279113,33.690036548307731],[-8.5067332401197415,33.829752733885421],[-7.7535132386517907,34.103558667097815],[-7.2357404254463189,34.365853068333166],[-6.7947656056961137,35.016611743245868],[-6.3847740115651774,35.981169616850444],[-6.2701594684389086,36.146964924376029],[-6.099395006356354,36.254035349719366],[-5.3790574541655936,36.42927318327375],[-5.1198874126539069,36.376860100939815],[-4.9877810097368158,36.309582189894734],[-4.8290928890913394,36.122309066828038],[-4.7695780197851585,35.845584366973256],[-4.6091110118603442,35.738083929067123],[-4.3509600600079628,35.676974052970827],[-3.7224147655370667,35.778850179186492],[-3.3948642412174079,35.72977371754812],[-3.1048545196639985,35.888983015328087],[-2.8555384728476381,35.893041355752757],[-2.6741414440469411,35.808232327645143],[-2.5132727568980293,35.612316683424048],[-2.1584726573858064,35.600193252729788],[-1.9784545495393031,35.541862622781878],[-1.8648719743930751,35.456139215182851],[-1.7722383761057314,35.334331398833712],[-1.4145124309150809,35.075204181283262],[-1.3213997178865546,34.909661083033541],[-1.2401478616798789,34.547929070123089],[-1.2102075391208584,33.859429495437837],[-1.1333524766500906,33.60925376780709],[-1.1519641202064566,33.357374905501551],[-1.0509082683937856,33.121217420312703],[-0.78691836500780399,32.932754346693557],[-0.69529827519648213,32.829717853288031],[-0.56915414101578166,32.52603747871273],[-0.59792409457833107,32.292097166929182],[-0.72612762061475133,32.101844865017014],[-0.79137136108236272,31.860530901946909],[-0.88100236670918042,31.745661585210314],[-1.0444147561547683,31.641607607498671],[-1.1864159687899882,31.608983495137821],[-2.4240692973321933,31.62873529281006],[-2.5679418971663441,31.606465033183078],[-2.7716942441377088,31.399053638792356],[-3.1742622963836697,31.281982435837513],[-3.130143952098754,31.139043843325446],[-3.1383780274721955,30.949667639278598],[-3.2386832314429626,30.711711127977754],[-3.3657527603770143,30.565900174107217],[-3.5386532339869041,30.479223039419722],[-3.8103565011401788,30.429414477295015],[-4.1052499175422756,30.249062245676082],[-4.6295073490431351,30.058209084118236],[-4.831121779388023,29.808235521517776],[-5.183158989617187,29.533651209413446],[-5.6866926338408517,29.37700025201406],[-6.1674838201591315,29.32123375950453],[-6.3346290721046312,29.169592216737854],[-6.5202406051385751,29.082471700316308],[-7.028300766403766,29.098015683178414],[-7.2887436052385874,28.932943103266723],[-7.4906253766625372,28.879440899286038],[-8.0499028037685569,28.522758734395957],[-8.180092641517767,28.42876236807702],[-8.1863505430877854,27.604051838625821],[-8.2879968694561477,27.351810798463831],[-8.26651463302629,27.054305380458839],[-8.3647154385796583,26.838964512301562],[-8.46821360082334,26.740469216026391],[-8.5953425423625607,26.675266262111169],[-8.9360721625710617,26.594749429757186],[-9.2662400127719504,26.589798670615409],[-9.606706399421137,26.394873431924534],[-9.7582739833233152,26.353866912371291],[-10.041235575837739,26.388616820569169],[-10.312580374760014,26.364902907622774],[-10.680396574173759,26.501137919555465],[-10.86131445729939,26.484768261260474],[-10.932754490579635,26.340809176737157],[-11.113225129096806,26.15814482610233],[-11.343540954558989,25.773424108491898],[-11.462957029874723,25.674468449594485],[-11.636899892006577,25.603808379329188],[-11.95851947947844,24.668282833843385],[-12.05174120179006,24.505428035066647],[-12.644210153567997,24.094335294941342],[-12.989575483951167,23.598314061530392],[-13.477714001108753,23.369127207859481],[-13.611419784706834,22.988365538142148],[-13.769192981874186,22.096879237271544],[-14.136150349061406,21.655311196877367],[-14.222890146431794,21.379388516940818],[-14.421228123328101,21.125047153946415],[-14.578331720443586,21.031575043962444],[-14.950235295787248,20.941623491321515],[-16.02180410469046,20.999434159974687],[-16.959187562469666,20.922984531702912]]],"type":"Polygon"},"properties":{"alpha2":"MA"},"type":"Feature"}, +{"geometry":{"coordinates":[[[7.5008937609801247,43.24739579078966],[7.3047086682683755,43.237457640944697],[7.1198357423839793,43.30386064313285],[6.9748100140039071,43.436355531836099],[6.8827853390652765,43.6630323804348],[6.8964821269531047,43.879276613685491],[6.9809406575817547,44.053953737882772],[7.1422460357026329,44.196435780177168],[7.3476100250916012,44.266169251410631],[7.5392806481828893,44.254819163004811],[7.7397395306789623,44.158990338837263],[7.8722241681338136,44.006874790052784],[7.9316961069935985,43.833805321566793],[7.9201332146846095,43.616584578164606],[7.8355790302046007,43.446833800954181],[7.6938908337100935,43.320781464796717],[7.5008937609801247,43.24739579078966]]],"type":"Polygon"},"properties":{"alpha2":"MC"},"type":"Feature"}, +{"geometry":{"coordinates":[[[26.595501604564689,47.760670997172419],[26.403880113681009,47.808914678007305],[26.245912910874051,47.927626982956404],[26.146272102772091,48.098266831379249],[26.120520088394269,48.294182839567618],[26.152578049984882,48.439314365543616],[26.258458834713583,48.60615413278817],[26.687668781559704,48.860801104066006],[26.822488124569848,48.886291435686111],[27.134484411025209,48.875116863629238],[27.455313017016792,48.968585723244317],[27.608226247478722,48.973987075292769],[27.96739803006831,48.894049264383277],[28.561912330697929,48.645501939812085],[28.964751057474032,48.581416382647205],[29.136828957294306,48.463716422335985],[29.268920090000435,48.443296515122945],[29.40347959894547,48.379827723973342],[29.611983129688031,48.154562670094975],[29.68517385631538,47.979038256152016],[29.687737611672887,47.763894738247068],[29.933008849676618,47.578653425153796],[30.00263870814798,47.457992279483271],[30.042091764637345,47.304766668271355],[30.255728888291099,47.155890443368548],[30.336104898893812,47.028268762854324],[30.396554269095532,46.847717336013702],[30.57762721622699,46.6471525241328],[30.63058568702689,46.414330643010359],[30.590567428608558,46.226869824719593],[30.447154487627525,46.035968393823893],[30.313096608550076,45.938036649151407],[30.179469437433522,45.888941248095591],[29.789695176696096,45.853157869563539],[29.580943337036324,45.921807612937016],[29.43581191357033,45.927604370846119],[29.342094253958891,45.742904863216545],[28.994168289281607,45.431220747316431],[28.91986943623575,45.248262633878809],[28.787618088484624,45.109747690842497],[28.659295503545543,45.04435454565813],[28.279678063625866,44.955272391174383],[28.043856670114224,44.980063458536115],[27.88016707255774,45.07722152385071],[27.70845835844456,45.25896567799569],[27.59235648062452,45.468529376546989],[27.575207529778481,45.612157372649641],[27.630173234634618,45.829537853957341],[27.625070960185564,46.214361839975055],[27.726301878364595,46.511629867240067],[27.679748149062092,46.620444172762305],[27.49756647248045,46.763952183669204],[26.980684665499101,47.289173960097365],[26.749851047027335,47.657101030824364],[26.660064062212676,47.75758713291733],[26.595501604564689,47.760670997172419]]],"type":"Polygon"},"properties":{"alpha2":"MD"},"type":"Feature"}, +{"geometry":{"coordinates":[[[17.937141419460705,42.535559176650779],[17.972983087968803,42.747712016695644],[17.952632929687724,43.052413513271333],[18.02245356022252,43.238118608540191],[18.2032414713773,43.413104529143602],[18.319753042484702,43.583062774377964],[18.504652065719487,43.721977039005672],[18.645250991524957,43.918589773667215],[18.764229579807079,43.995839962845153],[18.900115001564696,44.036566469586944],[19.366107014756384,44.002600926426815],[19.501177541002509,43.927743346243751],[19.849174787591856,43.624892077879089],[20.111855868833953,43.552377747382408],[20.530232704559552,43.361499577901185],[20.671481074101138,43.266838979403467],[20.761465533544797,43.161351585693829],[20.844001994316987,42.909352492694687],[20.824697563845486,42.690155314682876],[20.703736988693056,42.480757672309714],[20.529223290086872,42.364230344713683],[20.444175014569957,42.222909697432122],[20.328747403330123,42.123423096052854],[19.855089238267219,41.981276435993358],[19.821341948789382,41.726620688582273],[19.731216078489549,41.555368913745355],[19.582812403260274,41.431166691299303],[19.398361700383401,41.372619436108955],[19.205494569227859,41.388497490206539],[18.959417786674379,41.503393381947575],[18.832026108523824,41.596266798469223],[18.620368998857732,41.825418046318731],[18.213912706949341,42.035902007051234],[18.00686189573813,42.270384510563758],[17.937141419460705,42.535559176650779]]],"type":"Polygon"},"properties":{"alpha2":"ME"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-63.146790346884558,17.569741144050472],[-63.332604035065927,17.615339333140831],[-63.518845910097923,17.764045004665146],[-63.604443207540974,17.935156150515809],[-63.611755550747709,18.173370218941162],[-63.52856545984698,18.371276278094154],[-63.392077438141442,18.506633812149698],[-63.158480601800427,18.605886300638748],[-62.927115663437846,18.603230922447796],[-62.740192327680255,18.525324103060179],[-62.597318703994155,18.38693671504425],[-62.519702735824104,18.203798234674629],[-62.521560969267597,17.968977104555425],[-62.600854871504247,17.783813561646973],[-62.746779537259549,17.644964533999936],[-62.935652525333282,17.574964141640848],[-63.146790346884558,17.569741144050472]]],"type":"Polygon"},"properties":{"alpha2":"MF"},"type":"Feature"}, +{"geometry":{"coordinates":[[[42.792960960599096,-21.885317456367687],[42.825002585841574,-21.750637398634947],[42.964624786889004,-21.467588327186643],[43.066672849140225,-21.110746467584971],[43.19300589981335,-20.963535356319998],[43.39375539543785,-20.832393851731414],[43.473416055955219,-20.624598074523149],[43.798297931585594,-20.134686589997607],[43.910935941931903,-19.821441708983233],[43.935947317197197,-19.558369069690418],[43.763180056679431,-19.228806936117625],[43.73491649015412,-18.855801579161209],[43.56022819989844,-18.42808776624473],[43.514492927753523,-17.87652239718588],[43.443883235641707,-17.588976911697561],[43.533967787424494,-17.164966336341941],[43.925554785912759,-16.543562433476289],[43.948401664045441,-16.168426323849062],[44.013606391634426,-15.98717641807087],[44.105206338834186,-15.874949488835162],[44.225133947096985,-15.793691856891822],[44.72821409456256,-15.691571137971291],[45.056217990684544,-15.479321877081762],[45.196370012917633,-15.451429655366784],[45.341786162192534,-15.465217133562899],[45.570725913665726,-15.331088553235283],[46.033316628648755,-15.253807984211594],[46.189070110267501,-15.103625459194461],[46.592162716924037,-14.822298000548894],[46.795010967931916,-14.733369996800597],[46.987026540813957,-14.425802132229189],[47.147675305909267,-14.297300312842131],[47.295337253741543,-14.225165426108864],[47.430063849804199,-13.961542265228724],[47.399344109323934,-13.705373237206594],[47.427724389495786,-13.558958825727265],[47.581076143438231,-13.316012618161109],[47.69502591424618,-13.205985545720402],[47.795991539597459,-12.95417964681708],[48.060043859991517,-12.750941136098184],[48.336650236810193,-12.699705095481805],[48.290872699951983,-12.536232554105213],[48.293942043353681,-12.38546096015315],[48.341825308750892,-12.242462053490673],[48.430166748093818,-12.120243869464796],[48.645822288669514,-11.988183482031957],[48.845133654066764,-11.734923788844656],[49.012771608246361,-11.619140344077067],[49.316958758080375,-11.58331555361181],[49.541081319165116,-11.664648504822731],[49.676958510830076,-11.799457742107581],[50.410434314445546,-12.910781554260064],[50.666206774953999,-13.954228375208213],[50.726116044475624,-14.594510753404769],[50.92334392324026,-15.017297017639663],[50.979018255463671,-15.444335551067429],[50.833218265421081,-15.887119215677739],[50.612052849574205,-16.255966696341392],[50.431120424859749,-16.40777477965722],[50.495334623104981,-16.532360443000563],[50.522461344971966,-16.679885822926995],[50.46835195369362,-16.922238177671325],[50.218210836891139,-17.393684588528387],[50.114659833683326,-17.492957877350026],[49.966587116904279,-17.564766686786061],[49.971924261307706,-17.9725436589815],[49.830631179937932,-18.513388163699581],[49.393108237326857,-19.687205188355264],[48.82667400145219,-21.501947456659128],[48.39126354027038,-22.603186583043318],[48.02438168926043,-24.053985482759348],[47.826989783600368,-24.432725694589422],[47.58279116837371,-25.077595192713463],[47.369959980014336,-25.348802758494188],[46.932855566022191,-25.606037528441959],[46.312334417626822,-25.714365828554868],[45.740573146284063,-26.006611636621976],[45.572017836531018,-26.057904038419547],[45.087931409027071,-26.054978727198833],[44.615925063391849,-25.802367212851607],[44.188551974664144,-25.701140313374548],[44.053005743800469,-25.630114875877016],[43.635673000092623,-25.295738526398797],[43.44992095036055,-24.852771863311325],[43.199453074718839,-24.470079198783218],[43.146932772168874,-23.764260845460537],[43.188877676238342,-23.503965371337479],[43.132504081907271,-23.345484604467345],[42.943914649107867,-23.095253252073853],[42.846997544088339,-22.822493053567648],[42.759341502798435,-22.324478865635601],[42.792960960599096,-21.885317456367687]]],"type":"Polygon"},"properties":{"alpha2":"MG"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[170.70888826623133,6.7780440858815565],[170.56794604012987,6.9804741943397328],[170.53697195218072,7.1262033548508112],[170.55031104441642,7.274589509750359],[170.60677899847121,7.4124581157214458],[170.70259574111338,7.5306510017248787],[170.82389261558876,7.6172050178495354],[170.96530996479743,7.6641695023841301],[171.11428736595002,7.6673731582060682],[171.30293873650879,7.6030232689347041],[171.49223200854652,7.6008711962788658],[171.89010107423633,7.4854883085412371],[172.15475600762227,7.275257588667646],[172.24967463937685,7.053452885144563],[172.24304352787999,6.8598665749817869],[172.16333508005883,6.6833270766589896],[172.02251182215167,6.5503291775983952],[171.84170830091884,6.4808330062510855],[171.63857794298181,6.4854277514649183],[171.35337255690948,6.5780200352405584],[171.06511242049828,6.5929442193343313],[170.85870925497804,6.6692772334072794],[170.70888826623133,6.7780440858815565]]],[[[169.16500203257038,6.0629208741104597],[169.2469886126907,6.239099463186335],[169.44417059452613,6.4207620614985217],[169.62516045283814,6.5014526359645437],[169.77384692843836,6.5119203712114082],[169.96435695886521,6.457383907022467],[170.11871946293323,6.3331240383708698],[170.21268822963833,6.1586586592740815],[170.23150328498889,5.9613916034547643],[170.17243830472211,5.7504107350273692],[169.95619333507727,5.4342654389300025],[169.79030837246177,5.3316745617129513],[169.56300511209869,5.3024816324876021],[169.31531287121322,5.3849549093296485],[169.15172648705516,5.5632579038426115],[169.0910226689804,5.7974960506192277],[169.16500203257038,6.0629208741104597]]],[[[168.45415790269209,6.8736456014504315],[168.26881189722999,7.0309953246942429],[168.18834935667417,7.208852667900719],[168.19253502138486,7.4519463892441271],[168.30512171715827,7.6674389266323972],[168.45944795587712,7.784963055101306],[168.64688882776403,7.8349073067801243],[168.99047818398714,7.782329153710756],[169.16187904495823,7.6826617652826599],[169.28106584509342,7.5242119305359783],[169.32929677307609,7.3318954698685594],[169.2989876421436,7.1359536416648019],[169.19490448353815,6.9671977838333623],[169.07871361224156,6.8688009293770707],[168.89244011739663,6.799789992777888],[168.64575840188132,6.8084416266345773],[168.45415790269209,6.8736456014504315]]],[[[166.66853465113374,10.685683519223097],[166.50095028488204,10.790783838401245],[166.3695483745461,10.999160118659919],[166.34693278614893,11.195677629494915],[166.40229882413021,11.385586025153424],[166.52742240548241,11.540355690017563],[166.71071703792487,11.643608886489787],[166.92326597324211,11.66717405361171],[167.12084650726598,11.613016514692191],[167.27861586805332,11.49035301050019],[167.37574532502325,11.31570085605418],[167.39671844491346,11.116960737542653],[167.33818477127926,10.925881394036375],[167.20805468249065,10.767304500131344],[167.07267568995556,10.687756330489957],[166.86754200348184,10.646511901846463],[166.66853465113374,10.685683519223097]]]],"type":"MultiPolygon"},"properties":{"alpha2":"MH"},"type":"Feature"}, +{"geometry":{"coordinates":[[[19.955347529370606,41.441283774809087],[19.959439902455454,41.623294309759245],[20.110573229355669,42.094573544046575],[20.191772397463556,42.207614722936221],[20.519692010074614,42.498815184649956],[20.939568164802637,42.656373141854438],[21.16600020020276,42.666033497101516],[21.313741959106025,42.714063770020687],[22.208932835558954,42.856978741680201],[22.450462052296039,42.811366412918879],[22.834275092384512,42.54156569603613],[23.159583302825315,42.375223362818026],[23.452454545422913,41.945092529242899],[23.503590149805152,41.762285935885117],[23.388934058259458,41.159074461498264],[23.275508600793199,40.995321605034768],[23.0275919505427,40.780933583312247],[22.897619125131406,40.709569239717176],[22.571908493285488,40.624854951920838],[22.198671482407274,40.649619247236089],[22.012947794232417,40.508536330117529],[21.612948883537523,40.370579319900585],[21.408856245535393,40.394834983657418],[20.9163956509591,40.352443637425182],[20.469925126625029,40.485340286393125],[20.358567148550215,40.583329796883518],[20.048231340765348,41.03694542120531],[19.955347529370606,41.441283774809087]]],"type":"Polygon"},"properties":{"alpha2":"MK"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-12.730622852312715,14.591694453881235],[-12.774578931310353,14.734282715244982],[-12.774502991914954,14.883492444796342],[-12.678328950533661,15.111033310299588],[-12.529404108327041,15.242071681795126],[-12.308850652271225,15.307243741304385],[-12.227063531741393,15.603540277562196],[-12.149987451461842,15.738124613412326],[-11.780264392519388,16.052370968290198],[-11.599026473062615,16.127153250087432],[-11.451718348483261,16.133933519383106],[-11.308839016830683,16.09744856076561],[-10.966177405219225,15.840601464371163],[-10.766669691815839,15.917557374777607],[-10.44511719815886,15.937340523983117],[-9.9517398793211047,15.88054220779194],[-9.8279591746234871,15.902138396419341],[-9.5971625483003731,16.112188525496492],[-9.3666480214348375,16.176854992645524],[-9.1324955663343399,16.126941799600115],[-8.9664935046336964,15.996104149519567],[-5.9333407551817432,15.99627467304032],[-5.9100806298545452,16.115536082176423],[-6.0579361056637691,16.315962627496909],[-6.1259342753992811,16.516296223029407],[-7.0905809919646723,24.937492882670693],[-7.0854646833625852,25.08547159592101],[-7.012203966020417,25.268189012994998],[-6.9136733141799462,25.378713158939778],[-6.740533090323642,25.472389463791469],[-6.5941103949393645,25.49439560987237],[-4.8229498722739859,25.495390722383465],[-4.5910813374683315,25.438524163990888],[1.4147339665183707,21.523606111306396],[1.5793365899509264,21.346385785913778],[1.6533058402131047,21.102957922091015],[1.9386977169368216,20.932513919257424],[2.0688569827156673,20.762893444385867],[2.4160260036034891,20.707255571454834],[2.6818242610720042,20.506857476440139],[3.0169950954819122,20.432534850966313],[3.4132654988510467,20.261729461349869],[3.5913566737208207,20.105085426913327],[3.6661788010503216,19.97904955786943],[3.7327353687405087,19.559221916009413],[4.1843380161479411,19.640645352145945],[4.3765305585389607,19.619750800311476],[4.5823145264146232,19.494705231853672],[4.7057850406259591,19.287972390374687],[4.7346492487782132,17.002820806109668],[4.6675483089591374,16.463222662238387],[4.3046742093486046,15.496860777164823],[4.2148780421548944,15.38710085928261],[3.9827668420596742,15.208577421412038],[3.8347218865321913,14.981545326222824],[3.6687331006088439,14.884507566901705],[3.5267481535248453,14.857136638421743],[3.2200161733787058,14.893481073046328],[3.0164478127689245,14.841204002267196],[1.5182698969869601,14.780161848035258],[1.1118217728821849,14.51013575367474],[0.47092925536538427,14.479753727941766],[0.25644681579422068,14.413282275977762],[0.11010217392935966,14.42340528957709],[-0.23713230310417877,14.533805286297079],[-0.41542886029116338,14.512870094935153],[-0.58713654330776099,14.547297270475468],[-0.83257918956381094,14.369284496953174],[-1.61337736685301,14.014120375030462],[-1.7511374905806145,13.820948981788053],[-1.9110651504524105,13.711296957710639],[-2.1494237493022172,13.672354988394614],[-2.3815113389683003,13.728505638594978],[-2.5045435609628792,13.397411600706352],[-2.6508181169680829,13.256524514477494],[-2.8096289818439124,13.186056090151416],[-2.8793383115400886,13.013769145932413],[-3.0160058416298892,12.870796501274594],[-3.4172404175398841,12.695275913548283],[-3.5566452798715789,12.683778553445572],[-3.732484463860255,12.727053880860483],[-3.7870773633406078,12.556856867462146],[-3.9232092965423564,12.396932107940087],[-3.9485259050167754,12.199158673819243],[-4.0448355477947118,12.01760912293599],[-4.4222778132636309,11.659692035843584],[-4.758124089734225,11.541548935552404],[-4.7686850804901102,11.244771599741885],[-4.8558233740955528,10.976558107832293],[-4.978456945378495,10.807486963342029],[-5.0344501280023373,10.32317301565328],[-5.1104736025546966,10.144810445818413],[-5.2906133745785162,9.9839714482036346],[-5.5951973902566676,9.9172787668771001],[-5.7778370285045471,9.7660455850756342],[-5.9090002907313659,9.7110976030195477],[-6.1445532751996854,9.7072800042661544],[-6.4381355886447862,9.8034892409597987],[-6.5493948007393641,9.8709635162882261],[-6.6071560052211726,9.8571556040864952],[-6.7968370649533973,9.6946472864465569],[-6.9840558174786294,9.6445964311924541],[-7.1293385203710882,9.656253898921582],[-7.565805936998574,9.8027469451314726],[-7.9432941278383336,9.6649514395962903],[-8.0908481886335366,9.672930739719531],[-8.2296259987625611,9.7236908569712046],[-8.3803051728074216,9.8497619121534257],[-8.4674575244390091,10.014458502011575],[-8.6695313585715841,10.190230505732822],[-8.7408798756585195,10.328224371848311],[-8.7870377505726296,10.524831700534826],[-8.9722816273454633,10.614033415937518],[-9.0988637601455533,10.758579715226016],[-9.1659745655957501,10.988306245636737],[-9.105157299565759,11.248222429230941],[-9.2696479056692453,11.451040051838863],[-9.324895443973249,11.739008897037126],[-9.5257400441837401,11.585334710311914],[-9.7137504299039872,11.531805608260559],[-9.8604480375846606,11.541639747471917],[-10.223016329533849,11.656389156688322],[-10.42557623128738,11.467672567217619],[-10.66881484781756,11.399691825032303],[-10.924301086297099,11.454003237112993],[-11.066835284750796,11.543704449849587],[-11.325286123712372,11.510914258371859],[-11.610995799633665,11.620046914892677],[-11.877712194797445,11.868782892471128],[-11.989477092773933,12.087746566916657],[-12.000534363628416,12.235813904421294],[-11.939226498843574,12.45325784291545],[-11.930069309331094,12.826299854483754],[-12.100758659254288,12.894717173011413],[-12.211030211966726,12.990447226752345],[-12.52951882801616,13.478702046719464],[-12.552247406006725,13.674511943003997],[-12.497408291370563,13.899640039010546],[-12.504626905481816,13.987263776453634],[-12.699220197956896,14.290945513304143],[-12.730622852312715,14.591694453881235]]],"type":"Polygon"},"properties":{"alpha2":"ML"},"type":"Feature"}, +{"geometry":{"coordinates":[[[91.699381351188009,21.154545007999985],[91.679977511778958,21.306225290319436],[91.726510428865808,21.50409247703681],[91.90872338813611,21.762410611145107],[92.069231777898722,21.880338549988629],[92.090591769608437,22.101274193476275],[92.147076991871359,22.236337074083828],[92.355142326925957,22.50282532937706],[92.58762953004755,22.619771475166342],[92.585865159599962,22.800985909896447],[92.696404595118452,23.208012846089584],[92.764256129050864,23.331716299684285],[92.897902030052137,23.454620636040879],[92.909148817043871,23.617871993372507],[92.826512122773892,24.05678716007025],[92.879852632990065,24.288814451135572],[92.996833766548889,24.439960227561244],[93.208071054036168,24.549781364890318],[93.398989605101605,24.558710989691992],[93.576598924431508,24.499156577422255],[93.821554067996729,24.486473117981717],[94.108454346520546,24.988694284627424],[94.054305608777085,25.248354404986951],[94.135047380852512,25.548447131730121],[94.30227723060186,25.800001093913451],[94.524768188524163,25.980819475214464],[94.581604231522732,26.11016839963278],[94.571175833219627,26.577111665069108],[94.748513969583371,26.921563455846758],[94.866332907421167,27.022571676580739],[95.22569738248356,27.198725415779887],[95.572922568835281,27.431205478591437],[95.81683551955625,27.652947618693531],[96.429363955866648,27.812392315279677],[96.552748598496564,28.003557048301317],[96.815088630209218,28.215999922795508],[96.89434183366032,28.475831298823234],[97.167882868293447,28.84613664245326],[97.286364199734336,28.942038329862338],[97.478483274408063,29.006430069471413],[97.750548801354455,28.991241499963316],[97.891667341839593,28.942175786567233],[98.207166184943233,28.740647987867423],[98.513153871151047,28.421882019636552],[98.636950179406597,28.10482819836021],[98.940107080287831,27.980187823020312],[99.096785367255762,27.798607644432117],[99.149249087120907,27.613348696986307],[99.232073917271535,26.582817751247234],[99.134098604297833,26.034036373766533],[99.151498961702075,25.79685132404289],[99.085172983504464,25.607158903788953],[98.949520265699988,25.45890004288033],[98.804699322066071,25.382141518223317],[98.664040972784349,25.211671649628688],[98.486867215388401,25.108341318288048],[98.368872041890853,24.944028126333581],[98.236960255632795,24.84662011826553],[98.163357089318538,24.607914280471455],[98.875203830384123,24.61930656998668],[99.016687830438329,24.586690437806034],[99.179582598349199,24.483068801106462],[99.269090255662761,24.368745078143991],[99.321977372679711,24.233524934661602],[99.328315734415014,24.04056904805654],[99.26906808512193,23.870626452918312],[99.347565767809655,23.600093593195368],[99.563701678654922,23.544100989394511],[99.800467544569997,23.398478225349329],[99.902378441159087,23.294550405218668],[99.970038388405513,23.165674673681217],[99.992643835067497,22.925675082261378],[99.848100807269716,22.553138370771723],[100.06829596101552,22.50454475199485],[100.24235165600138,22.40794032694447],[100.36492067042877,22.251081675290774],[100.44799104954905,21.987475220249173],[100.60328838880292,22.097952116130628],[100.98128459327444,22.245846996802655],[101.17217431117825,22.247038681938385],[101.36005227722498,22.179724038432671],[101.47979967476017,22.093348972969697],[101.56909931128249,21.975766380435068],[101.62016422592848,21.837229505768502],[101.64696315185688,21.589028113772947],[101.61648621032261,21.409645962078436],[101.48367601123414,21.17344933145149],[101.38117627488211,21.069506808088693],[101.11993766052035,20.907189271136488],[101.11055949049319,20.750298301015793],[101.03383599451733,20.575177384490985],[100.85557882594534,20.417242148210061],[100.65991623907395,20.357318380715864],[100.56067437764125,20.076541207991507],[100.39889678472844,19.900472538967382],[100.17386136149058,19.819656550024916],[99.885167043932753,19.868328688900515],[99.636313673696861,19.652975568715508],[99.389455857253878,19.595839201281205],[99.264738047706999,19.41422427827683],[99.096694159177588,19.306678858708924],[98.532770744511083,19.201084759782784],[98.306339895871417,19.198411052005554],[98.289087917120284,19.010624397492201],[98.224967738315655,18.853085295471775],[98.245438789224593,18.530460727795241],[98.155073504727198,18.317755803341065],[98.22275428127405,17.985324477508833],[98.848396795034972,17.262019354871221],[98.917381692499305,17.119698429352908],[98.961607974027288,16.900580302450972],[99.11353174853339,16.832836801174782],[99.225233173657443,16.730441916030426],[99.331902326111532,16.581978898720042],[99.38635450466559,16.39252063876156],[99.376371235158174,16.244585116820755],[99.302754953198985,15.996912919737955],[99.076199409434906,15.757006085936448],[99.026836718059954,15.197642767499106],[98.924029786387621,15.028616748689259],[98.784038159981279,14.923191380417371],[98.937717658150348,14.707722388114846],[99.330890187494305,14.353079208536228],[99.601829066133249,13.89938039158768],[99.674255561743507,13.280767900025682],[99.64799320624978,13.063902475581283],[99.874103531696861,12.720453696891866],[99.942379956481261,12.342953485210758],[100.11402757203156,11.803866933851133],[100.06258835742273,11.559244240188821],[99.613321194820159,10.839403231168589],[99.259241606554468,10.45591980988752],[99.183786606308772,10.05568570713263],[99.128839956975668,9.9296312128445408],[98.819448825337687,9.6063338253670398],[98.682065274777742,9.5497816405317746],[98.508844447891946,9.5387992591538531],[98.296927000497874,9.402864131178168],[98.059869519510215,9.3812335610138131],[97.905360012055255,9.425692214407146],[97.784650305792113,9.5057029241488618],[97.691933625595283,9.616951133450355],[97.634987991145124,9.7501042091811811],[97.622475037345339,9.9422602707187959],[97.667359543868315,10.136257089802045],[97.739651006508211,10.273410861532284],[97.885606668126641,10.416374660192371],[97.697415725701134,10.565948739599975],[97.592454825001369,10.779927059677165],[97.598475968056221,11.018185864289036],[97.738560017930723,11.252980328316932],[97.595239325549912,11.43432898958881],[97.538048907061764,11.567677767668771],[97.51089615433267,11.876144038416315],[97.544469954740848,12.038547913459746],[97.46826406693522,12.177346651484809],[97.439457970025188,12.368722484128812],[97.467151255151862,12.511615228700087],[97.534824708231483,12.640477784318461],[97.787308817732367,12.849882987523646],[97.759665141047705,13.000789485093243],[97.772664980728663,13.320160769355839],[97.640168817045904,13.544285127875986],[97.591168669041096,14.060306098128851],[97.321742749372987,14.76358195337342],[97.230756123066328,15.654277491148513],[97.127431873175141,15.817063880848135],[97.084019459624685,16.047180735189261],[96.991301006930897,16.226772379929962],[96.868986290195394,16.129195172517189],[96.599564542303455,16.032753444370471],[96.109104143811138,15.75604440725035],[95.881350401307273,15.459536129242071],[95.634687741871318,15.287234947007244],[95.496454171253532,15.234562752706298],[95.299810483274641,15.231098482111324],[95.04230433484453,15.322938078084118],[94.747378222325793,15.295498823271283],[94.531625136246205,15.363423870186773],[94.284358523530983,15.365494502229248],[94.106234090819171,15.453345485883428],[93.870579692545149,15.663015326513225],[93.744308594483002,15.875745554462018],[93.714810493301798,16.14133277963435],[93.7856993993543,16.635538458404046],[93.892866398683424,16.84768365660911],[94.073501743961259,17.438332530870145],[94.080815891193012,17.535364671631132],[93.950468753895777,18.045917458729146],[93.873682102057089,18.21763324789946],[93.679536670440243,18.175978524274804],[93.482688127884146,18.214017312538861],[93.353653909194037,18.292091842012976],[93.099511316349691,18.551947543250062],[92.997394587696206,18.780755480268866],[92.995191292751329,18.932111248355305],[93.060695018707293,19.118823947906481],[92.995509570849279,19.330401372475301],[92.748059375830579,19.411701675904059],[92.553986946124525,19.59897460013395],[92.441396602014962,19.869699368425259],[91.882558423448273,20.557742248823814],[91.699381351188009,21.154545007999985]]],"type":"Polygon"},"properties":{"alpha2":"MM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[87.414654082631188,48.505030579763655],[87.298298420272033,48.654015030206189],[87.245570279701127,48.835549461729897],[87.252694497629733,48.97752373221811],[87.356512019027704,49.362575968381115],[87.499702441653881,49.550493210199669],[87.732645621608,49.660078334380017],[87.831277894966192,49.79702397567727],[87.946128951676485,49.886488667507287],[88.129823492334154,49.947541231234986],[88.623585105830301,49.964070720546928],[88.796655010755856,50.028628872803495],[88.957370714093557,50.035843865631001],[89.196205138330896,50.12447468073146],[89.280409535492197,50.245983583887444],[89.390481460669534,50.333763375657121],[89.671976405832012,50.425924069397375],[89.852910141788556,50.55142588156599],[90.491910580707653,50.706105960386587],[90.837619648665935,50.880162042944825],[91.216013621663606,50.960669117466907],[91.563903960652794,51.144719976967245],[91.965799135920221,51.190123872806787],[92.115709618069914,51.30309141563459],[92.256925383585454,51.354262982606102],[92.4566015052296,51.35343355401195],[92.701873135030283,51.243093725794424],[93.047037036273679,51.266682808581606],[93.183665494597591,51.21528246468948],[93.323859210400911,51.099126453424923],[94.41280491326971,51.029237217011925],[94.53883808983521,50.964961443679279],[94.64182182168706,50.867955837621878],[94.793132014628554,50.551968586302934],[95.026998071065748,50.534086900788665],[95.251224647018418,50.440103427015167],[95.41975073476884,50.4264183510045],[95.759599892297487,50.511352115488314],[96.090484136511165,50.497790932983321],[96.440091529163894,50.397948950694357],[97.047306672327537,50.378765256239411],[97.267358366360952,50.295549778102455],[97.415887443605655,50.380147783371264],[97.577978814095786,50.426312223973234],[97.37340216453012,50.771852129869586],[97.325997700642475,50.964050051096216],[97.367720696233263,51.226776203582233],[97.545035440227451,51.64560260404938],[97.698013278762232,51.816579546675634],[97.87965529838138,51.929826922968857],[98.067086660590974,52.127635027281578],[98.367234085276507,52.252318304369304],[98.601618042260412,52.523189879598597],[98.779677327185524,52.603972800314438],[98.926250057751247,52.615948371022597],[99.526917814872704,52.412105717681449],[99.857533453069365,52.351892816462311],[100.07318273289509,52.247963488281613],[100.5591019675802,52.217844390697991],[101.42076496338896,51.963458236715752],[101.68120366560925,51.95510826511628],[102.31415932061608,51.8102650055854],[102.48950779190785,51.685696164117239],[102.60562993343055,51.531243893910045],[102.6491834357234,51.392596365763517],[102.65192598740839,51.206567127602632],[102.74457219632278,50.941076737548826],[102.97427308674675,50.822568120820364],[103.27535554253774,50.777427794130404],[103.49746846752208,50.678154517497802],[103.67099091455258,50.64394882634712],[104.027466608114,50.659425732291481],[104.37196365414802,50.796785894329503],[105.34633215811678,50.972063530143352],[105.94392913246529,50.900446498173892],[106.27779469482954,50.808198509545157],[106.80000622039843,50.804371814925815],[106.93372458475361,50.760031263018291],[107.37353849274375,50.489487834782381],[107.95371030727495,50.44622219921839],[108.13670635097191,50.396517529230586],[108.25447146047146,50.316108165174242],[108.34474910244703,50.205725946485174],[108.41923618775607,49.971220848352502],[108.71516984280959,49.827563744999843],[109.27543785563675,49.833219565084612],[110.15341071627763,49.6856047514013],[110.45414862900908,49.719083585069647],[110.70073417904838,49.666519427068124],[111.23153935887073,49.844418752951661],[111.99297865447265,49.919714451202857],[112.40882993827883,50.024955057121296],[112.71259296822785,50.028860582532992],[112.89740010055266,50.219756802621852],[113.33997777584437,50.44884831029718],[114.25403268090129,50.772261469003951],[114.8830803185911,50.713357031625449],[115.55637927805441,50.391827291503169],[115.65445286836341,50.387276984574029],[116.11999536246479,50.510340913990859],[116.48764911861562,50.458949073641328],[116.77131472521666,50.368926999391441],[117.01671065602248,50.196100310084319],[117.1545760795199,49.989956054060265],[117.18204329128397,49.792710387848011],[117.10574362566692,49.556742825851543],[116.44798537721182,48.515367397743404],[116.31635290380636,48.35995610014146],[116.80517302398539,48.367605631303256],[117.30328929740003,48.249012993497928],[117.52687289578009,48.425462150563426],[117.7146734547045,48.484794755879527],[118.15186895085274,48.528629226651859],[118.69939618073056,48.441577345442653],[119.00614159759138,48.209669721670942],[119.3690612491356,48.063142138105547],[119.52855584677118,47.885174323656955],[120.07117365446446,47.496488353576218],[120.36298641362984,47.040479869389188],[120.39562636081826,46.725327362451161],[120.34759443104288,46.518933220915308],[120.2075012418988,46.30618775114803],[120.04760814024185,46.206098930163634],[119.76025929443796,46.122798997220485],[119.63132199293794,46.10410493453908],[119.13108311929089,46.139178420098652],[118.87524363308555,46.216302318301224],[118.74745044596692,46.192766527468898],[118.33832051346589,46.208538423661501],[118.11514150732177,46.154430245193261],[117.89006618282903,46.041128980592909],[117.69776500950563,46.020791025116189],[117.56375104434309,45.918491546370731],[117.42124752834849,45.870007400035618],[116.88706089443528,45.873046151830557],[116.72771219710975,45.749533034558148],[116.64464870298933,45.51578469788732],[116.52005317759713,45.357477989855987],[115.91993807131006,45.019278846582587],[115.79125397152671,44.970789471907231],[115.22468278674782,44.894336696652395],[114.81856682245204,44.89746480717934],[114.32103291169342,44.532855015673036],[113.70263348822259,44.259483347727617],[113.5099709299575,44.251899687033472],[112.63418460605129,44.388461431573873],[112.26933273311208,44.556392424046422],[112.07847077163507,44.557930716393649],[112.00338783507101,44.426804545439943],[112.30483119135388,44.14744242314223],[112.41965794733353,43.923032554274926],[112.42519263188922,43.62371145987116],[112.35698413135805,43.446608981304571],[112.22768857783953,43.307683291457522],[111.993371989774,43.202123338184322],[111.79278427472113,43.060901121695331],[111.2630624473,42.89142538611393],[110.73998770217355,42.407022587484917],[110.60276059502414,42.316720796254657],[109.39334832244624,41.941545598649988],[108.20598379159166,41.945926538656707],[106.87667553218007,41.796832233481602],[106.03018512002004,41.520718847532059],[105.43352351587494,41.288683675145322],[105.15955885442908,41.128317371808031],[104.96637639564314,41.095989453100238],[104.38311772627854,41.172406550902146],[104.14556880713259,41.305574893149533],[103.62506540668129,41.259082501109106],[102.93760123454145,41.520565096281125],[102.00645892193286,41.681336717722701],[101.70264521388015,41.795506472072809],[101.40386204263724,42.044205948798613],[100.02028179186613,42.171312327313871],[99.463729969535706,42.06867279761623],[97.20210810295896,42.286925611390281],[96.376251808105494,42.220683172816862],[96.179219118414395,42.26514111848612],[96.015853256948262,42.383926576550117],[95.887894992915378,42.605324528324296],[95.512619258558871,42.906321706382556],[95.185663403981906,43.53322297296711],[95.02076147168151,43.635692878687664],[94.901587260089968,43.799980002539243],[94.560051156125809,43.874771157288421],[94.037359919794497,44.155856272291672],[93.734794906097832,44.227725388327933],[93.39394149129744,44.456476496798309],[92.796255812154058,44.530908788335978],[92.389806296605613,44.510608229495439],[91.550344263143614,44.577818814569142],[91.019342749070759,44.707859051961634],[90.778542440372149,44.706186248357938],[90.597099148730621,44.782278285043134],[90.488181708180974,44.882465451086517],[90.243721381971397,45.2514703764733],[90.163446539366475,45.488237549764008],[90.235755965149224,45.889241317519904],[90.422829883015098,46.166536835063205],[90.422139613030225,46.371987297563798],[90.491607176364738,46.557674828870496],[90.352584324933531,46.660328564816794],[90.104654830432935,46.974233060935227],[89.97995078626974,47.257650730961565],[89.859603116994947,47.330328981083902],[89.577860007208898,47.369090354942834],[89.361340004234975,47.507677733024032],[89.100307246352699,47.489173547581387],[88.956235053826731,47.513073512030331],[88.381558214984963,47.759336978676849],[88.256757769348354,47.836912327322452],[88.147916346024004,47.966019973924617],[87.688341184910612,48.149132387518442],[87.586278557625633,48.246984842113029],[87.493798046627589,48.435955188191159],[87.414654082631188,48.505030579763655]]],"type":"Polygon"},"properties":{"alpha2":"MN"},"type":"Feature"}, +{"geometry":{"coordinates":[[[113.60161212443737,21.711095961322666],[113.40662525101216,21.701160085630089],[113.2226985377788,21.766654868575699],[113.05055258482625,21.938395988432863],[112.98462493708374,22.122167987652155],[112.99656626446446,22.340029456544901],[113.09291481752135,22.539526944229298],[113.23046536183217,22.666091782892074],[113.40488510350502,22.733297172115847],[113.62937265373937,22.735103999872422],[113.80669493197784,22.660130857780771],[113.96470189977056,22.498648982622971],[114.03930846837878,22.314182727865539],[114.03606407057566,22.115226920946959],[113.95548254031895,21.933291338376605],[113.8103260384548,21.797190165750173],[113.60161212443737,21.711095961322666]]],"type":"Polygon"},"properties":{"alpha2":"MO"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[145.14816146975394,18.766388040501184],[145.17274397347396,18.96521786933755],[145.24180148295659,19.099169751628185],[145.3476354715753,19.206460073830176],[145.48063111349552,19.277341704845409],[145.62870599596397,19.30537517710799],[145.8138237009403,19.285115968328391],[145.95685672273649,19.223546035050504],[146.08047990976587,19.120848748554128],[146.16253011205205,18.993080931206332],[146.20244484395008,18.846576101857501],[146.17569007758848,18.538704440669008],[146.30165246669304,18.314613799638678],[146.33331009891558,18.168385645754316],[146.28719395399938,17.925022434022416],[146.12728235331338,17.722304470141346],[145.9313452727051,17.6002908798283],[145.72365733249217,17.558614832574804],[145.53333852625585,17.599130844709528],[145.3728978494691,17.709228912261683],[145.28707252449735,17.827768035799071],[145.23924571337753,17.966079663486781],[145.23351474097251,18.112314632529102],[145.29134499635671,18.380751715949849],[145.19080215242681,18.563656678354899],[145.14816146975394,18.766388040501184]]],[[[144.65853062989362,14.090118967819775],[144.6696616145353,14.290409500173922],[144.75861979196679,14.470205603685276],[144.86821557965791,14.573932242738438],[145.13790055028855,14.712529994623793],[145.087799589848,15.020143535972847],[145.106637421887,15.166859048473489],[145.16757771948809,15.301642525400535],[145.42446169257536,15.654291031782197],[145.5828773142735,15.76278128179343],[145.77049878864784,15.803571303846738],[145.9596589701153,15.770646096289195],[146.14901759047831,15.644026821117937],[146.24121945623492,15.535691913207069],[146.30185456642073,15.401222200609823],[146.32061414141509,15.25491157714297],[146.29586544384509,15.109494170475758],[146.17416820296035,14.845789007109362],[146.02209033580598,14.620322778246114],[145.89749005652121,14.503308623755903],[145.72341368800133,14.420572112129404],[145.7606230729977,14.266568655421944],[145.75634559659616,14.071912488555462],[145.70983740772988,13.932375086170673],[145.62474050464277,13.812407526468467],[145.49894855988038,13.700873163064191],[145.36160320753555,13.634416855996934],[145.21061055056495,13.61247593302862],[145.04417321543883,13.640209799023182],[144.90679483342583,13.702929025746021],[144.77991049768852,13.810480424583302],[144.70378556262415,13.928856724111835],[144.65853062989362,14.090118967819775]]],[[[146.11280349679419,16.041081399129272],[145.98355774533078,15.90037652409679],[145.76391898273991,15.808732548262704],[145.57299568364792,15.815847321139239],[145.39867923158772,15.894052823953279],[145.22988020009561,16.061606383607771],[145.14745926214752,16.248804701707076],[145.13776395358693,16.452134324439953],[145.22851378117198,16.672278523563389],[145.36876748817591,16.802161719513144],[145.54751156163528,16.869920874773626],[145.80927213337466,16.865400502980584],[145.99371543100935,16.779669724725874],[146.15163668114027,16.608956815601054],[146.21508414946703,16.416451121675404],[146.19689944161667,16.214576557078306],[146.11280349679419,16.041081399129272]]]],"type":"MultiPolygon"},"properties":{"alpha2":"MP"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-61.663936327151283,14.575453797044556],[-61.719049251611722,14.82453962523228],[-61.685788941979617,15.011432204894868],[-61.621481005093159,15.136931416337925],[-61.389895211391121,15.325863379667171],[-61.10674238582591,15.374594825019042],[-60.807396400504473,15.275043437874018],[-60.539566337917378,15.070590858564451],[-60.357073792728663,14.666463438856836],[-60.327876389149509,14.458151571409553],[-60.391705518799057,14.209827977675529],[-60.512609875058075,14.056933317587081],[-60.709991670717415,13.950247768865044],[-60.91189640049118,13.929048954273192],[-61.236420050911718,13.99814810375632],[-61.40190917702872,14.099169656090924],[-61.518812826032544,14.254745536022767],[-61.663936327151283,14.575453797044556]]],"type":"Polygon"},"properties":{"alpha2":"MQ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-17.556529116472294,20.814131068528521],[-17.550923366117008,21.011370874317507],[-17.421154705538282,21.532366008259629],[-17.275845811407965,21.72015416467541],[-17.106837250093143,21.808297269234167],[-13.559785391437481,21.833307863044414],[-13.651171511017685,22.827094658464773],[-13.589851370559654,23.064547525020345],[-13.414832798728041,23.320784056698908],[-13.306808385099705,23.417255173145744],[-12.858196898293706,23.710700959639681],[-12.519751716274113,23.822775212914081],[-12.516077974561846,25.996025468532675],[-12.478007580274904,26.186548219942459],[-12.402954255270648,26.311925095433448],[-12.25301169128443,26.435478732495071],[-12.06729387999156,26.492550339222966],[-9.1830998035877034,26.50890911760975],[-9.1606578305707131,27.433708323047917],[-9.0306257200373778,27.64503092358974],[-8.8626439862708253,27.752192247219458],[-8.6661525266711195,27.785245514314678],[-8.4280150585984028,27.715559521680376],[-4.5682483327435648,25.425842176840057],[-4.4251479427913436,25.298196691252194],[-4.3554093939692695,25.171970302684542],[-4.3235186648141326,24.982882828246407],[-4.3651455120674498,24.795697633363936],[-4.4741672772764955,24.637946733822467],[-4.634548563183678,24.532832820727208],[-4.8226997899718462,24.49581650738368],[-6.0292508698613148,24.494244182300768],[-5.1554697054240961,16.831875269505808],[-4.93924214896614,16.552678251670766],[-4.8623576864291884,16.329745474493233],[-5.0500396800344172,15.306911129345929],[-5.1585072794915385,15.143601909455922],[-5.3678634378843748,15.017957306562558],[-5.5137528358675203,14.996523542014923],[-9.267630108194874,15.004484889039466],[-9.8884835855986637,14.87683959692423],[-10.445800691978848,14.919978356523567],[-10.627808621088743,14.728542305071004],[-10.758042433293648,14.670034593279748],[-10.899502312947231,14.650718780357053],[-11.149873957865493,14.695681822908837],[-11.3868240699005,14.878714298387283],[-11.474893822719789,14.692969689751997],[-11.773142710930548,14.371408846150874],[-11.940934644322844,14.273225519039915],[-12.133490204616107,14.246458845091693],[-12.56857942259448,14.393835685563525],[-13.162477376732506,14.845863509005271],[-13.264916536191468,14.951729056874663],[-13.340423578482278,15.107643980407483],[-13.597940643248805,15.306296340389565],[-13.76509422259625,15.620934328221431],[-14.007852580702277,15.668270607611655],[-14.144010153980787,15.731275092237079],[-14.443850086058298,16.062281995058019],[-14.602549540689198,16.150462958818064],[-14.941404517702084,16.1537449736669],[-15.704427099252204,15.989400082472661],[-15.975923297120747,15.994208658677369],[-16.081294708744572,15.629345130810718],[-16.230904702635598,15.442486218104113],[-16.450226755627963,15.346583973077299],[-16.594703978166113,15.342904394300312],[-16.734198158592662,15.380700965036834],[-16.857054721546316,15.456815826079401],[-16.953009145115168,15.564889669954244],[-17.014044549617843,15.695893053610964],[-17.035061498509329,15.838880797811674],[-17.03232084036317,16.342781294450102],[-16.95066299149283,16.713193721765908],[-16.571482965250524,17.659993703835386],[-16.534377183937881,17.904861338398355],[-16.577131740123004,18.416827714388038],[-16.667584702093819,18.759377299474654],[-16.930396252056116,19.084882977179706],[-17.01172375161407,19.312683785262703],[-16.961368824821243,19.584462085198577],[-16.952363214735911,19.86446602146],[-16.883115656983772,20.001581474958449],[-16.760346255386345,20.144222046061227],[-17.001416215347962,20.30908267719915],[-17.20125242541916,20.330803923956648],[-17.372946606513164,20.426766773028007],[-17.494343042382585,20.581526798815148],[-17.556529116472294,20.814131068528521]]],"type":"Polygon"},"properties":{"alpha2":"MR"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-62.722723486819561,16.761549625810847],[-62.683663283974155,16.945482313614814],[-62.541777671571609,17.160677873482459],[-62.368053821965042,17.271884817142258],[-62.159524585975269,17.308997225351909],[-61.964342206161909,17.262293201937204],[-61.803247590792807,17.142602132546827],[-61.676950065865938,16.906155368968282],[-61.649302636399476,16.715039003897164],[-61.682959041650797,16.515038520658887],[-61.780400251405375,16.349680554815901],[-61.977209172944661,16.213948209519252],[-62.166400346473047,16.181625677361051],[-62.422001255377978,16.241695805167605],[-62.582211408574082,16.353545920849768],[-62.687322045686358,16.518256259113894],[-62.722723486819561,16.761549625810847]]],"type":"Polygon"},"properties":{"alpha2":"MS"},"type":"Feature"}, +{"geometry":{"coordinates":[[[13.783093017478647,35.757165673032844],[13.712803481712807,35.884137044279875],[13.681939073245099,36.025945845997072],[13.693100099717748,36.170644772885296],[13.745346251438367,36.306043028357493],[13.834275821899656,36.420733391736889],[13.952396548875608,36.505053269661374],[14.222204585811644,36.573832757475948],[14.505084693170444,36.519709812682223],[14.761094878954321,36.347257694052367],[14.974493535514489,36.140899373598664],[15.062901450428587,35.907198108019372],[15.044283573997847,35.707403069160243],[14.948613828304859,35.53101717790318],[14.762178794649392,35.376264996710681],[14.575224892151905,35.322271396423133],[14.35036919036952,35.32942709016217],[14.197053931431572,35.382986395179564],[14.012874648449705,35.505537584209534],[13.783093017478647,35.757165673032844]]],"type":"Polygon"},"properties":{"alpha2":"MT"},"type":"Feature"}, +{"geometry":{"coordinates":[[[56.866082984558297,-20.641700173509296],[56.824202415583677,-20.506421778989658],[56.821929827054177,-20.364827070674611],[56.926553557697403,-20.0313383701154],[57.13204819752378,-19.734898810150398],[57.304060930892192,-19.577734283576067],[57.531453900488039,-19.499369683755219],[57.813297337104878,-19.515453119050523],[57.988832696019429,-19.616703410414186],[58.164946144440719,-19.839963162658098],[58.284215664702209,-20.12597496810978],[58.270200017323887,-20.427365844580461],[58.216238121630056,-20.571881324032574],[58.106699248502771,-20.734398318226287],[57.903287984237075,-20.916392107057067],[57.586978805456852,-21.009071333917934],[57.250666251607058,-20.985524358598614],[57.034277304062009,-20.861386879061033],[56.866082984558297,-20.641700173509296]]],"type":"Polygon"},"properties":{"alpha2":"MU"},"type":"Feature"}, +{"geometry":{"coordinates":[[[72.973033167167031,2.9592834232662319],[72.88623265179146,3.183747944971405],[72.901378111600494,3.3976033190457962],[73.008047317533439,3.5995996847328509],[73.19912201067693,3.7450728462514249],[73.063875186294311,3.8801300523817766],[72.994998216058576,4.0250192276487828],[72.975518282640721,4.2177131830673371],[73.0119883676591,4.3636057448665566],[73.13996835846892,4.5768264631737017],[73.311435584441739,4.7028106104635388],[73.497159118920948,4.746966733928998],[73.714300756920764,4.7074602903087852],[73.927795927188811,4.543538105837313],[74.021880598918088,4.3216401439530916],[74.022766991373913,4.1651069141177066],[73.963679909910539,3.9502418749670367],[73.84842580940672,3.7948099719999751],[73.707833446186569,3.7031900450685695],[73.848900899311772,3.565533063075415],[73.916048970267923,3.434744848104033],[73.941394028023666,3.2409239440340056],[73.902995736647526,3.0748369741693522],[73.837764887610831,2.9542811569558385],[73.717904393000637,2.8325640552795592],[73.48023765531606,2.7355513412630073],[73.314611361045067,2.736220997593775],[73.17279465360663,2.7819457243775769],[72.973033167167031,2.9592834232662319]]],"type":"Polygon"},"properties":{"alpha2":"MV"},"type":"Feature"}, +{"geometry":{"coordinates":[[[32.269789639130408,-13.889399800370756],[32.18490219019683,-13.709006948913915],[32.187825640540972,-13.460757274103566],[32.276937662010489,-13.28241348398797],[32.468341134058939,-13.084812108031974],[32.497560377211578,-12.55434207216976],[32.63456342179488,-12.314265855658585],[32.761200692970881,-12.204379460515099],[32.789375215769283,-11.775268703761737],[32.727405701461485,-11.502177181111428],[32.749561483382372,-11.289923966352097],[32.816392956813374,-11.132461356726907],[32.767321233364598,-10.968888286876355],[32.77593810280478,-10.774448134975742],[32.858142876631248,-10.598029320020471],[33.023071206391997,-10.453211604077566],[32.877618520583766,-10.285911855356842],[32.814793734371783,-10.08804440776669],[32.659903435455469,-9.992658482383149],[32.543485617087114,-9.834903061873332],[32.425201278037584,-9.4782714971722726],[32.450964094323737,-9.2348239072237028],[32.589207394121715,-9.0327861834795264],[32.713279813446889,-8.9523799049597983],[32.855442576836055,-8.9117734575880867],[33.051556049723601,-8.9251344696424546],[33.501309840254514,-9.0493668781983878],[33.621455384582134,-9.10924143287634],[33.921397793845607,-9.0012280074196767],[34.160812831531139,-9.0237451750898394],[34.679565283321608,-9.3835466520687056],[34.962097755870879,-9.7893917215220867],[35.012168733904161,-9.9224162594195988],[35.063566329714519,-10.322056386102394],[35.152601257233783,-10.618328665500879],[35.137509035634601,-10.953064759972913],[35.322598950323595,-11.141292417928909],[35.44201805760926,-11.445920591746322],[35.44488446124565,-11.689169624730052],[35.363365956406476,-11.866666675620831],[35.252322978292973,-11.977336739934957],[35.198663366535776,-12.299397552533017],[35.080796333403057,-12.456253511584023],[34.947664923562108,-12.541156559452538],[35.029119363037616,-13.068199506249744],[35.234096441604024,-13.173663110957211],[35.640157496115386,-13.587866285917075],[36.268429057519761,-14.401962287129324],[36.38220079495543,-14.790855192053669],[36.388521984779153,-14.954780991058115],[36.311340564095261,-15.288454727346389],[36.318712924304236,-15.713712317929886],[36.237157069724375,-16.190926563422472],[36.176505302906932,-16.327285180320899],[36.078310965658858,-16.439669259944935],[35.951322116551736,-16.51806659040318],[35.741210282279035,-16.586460239652485],[35.779978914984419,-16.776521118295399],[35.768435146107905,-17.181280581005097],[35.705779674982139,-17.367638915931504],[35.613872281710862,-17.483489431452032],[35.399501041158338,-17.601812074540927],[35.102416682697566,-17.626470627329969],[34.903144567764848,-17.575207710630256],[34.719837756510607,-17.441277385994603],[34.604632521273821,-17.270376258478525],[34.561345175620737,-17.090337464972578],[34.007565909899348,-16.533438644048442],[33.910820602975569,-16.262266147872399],[33.812665142484207,-16.132550461853725],[33.76102996374248,-15.998859647825189],[33.764571290371691,-15.761489388985323],[34.04499280231466,-15.154294839390209],[34.049912888646894,-14.983185124579734],[33.658021827147465,-15.067431990531116],[33.51562283231339,-15.05306555258613],[33.38312756891338,-14.998946485285012],[33.271392099034429,-14.909509060009508],[32.969428681156145,-14.521304273335982],[32.781108040560497,-14.475198592983752],[32.622774950441944,-14.35887510417083],[32.269789639130408,-13.889399800370756]]],"type":"Polygon"},"properties":{"alpha2":"MW"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-117.60085557728816,32.37072515459348],[-117.62725643974379,32.559930613606568],[-117.55832993744554,32.787699685114703],[-117.43145262573431,32.930520758600686],[-117.26027263587287,33.015333035599873],[-114.7135511932004,33.214942415146744],[-114.568230859048,33.189828999171326],[-114.4365577551096,33.123414566407426],[-114.32998674786883,33.021476822682118],[-114.24331497176198,32.848348919637587],[-110.95602262635983,31.82540469425934],[-108.70847319552497,31.829502326407809],[-108.66531197860189,31.989197258800132],[-108.58770948920673,32.108557804366157],[-108.47961245280537,32.201208313913526],[-108.30346456489259,32.270601014568271],[-106.39554970934495,32.266278180027342],[-106.16170164284748,32.178495603760823],[-105.82623865625864,31.83840482574379],[-104.59646512133436,30.967075583249798],[-104.50396979893767,30.861980857133588],[-104.23982921523505,30.368586554148685],[-104.15652927112127,30.070878699855268],[-104.07765724087604,29.96902127732784],[-103.77523230602009,29.776708145698564],[-103.30921722874629,29.570035390922907],[-103.17011908070731,29.888368783864124],[-103.07023230862401,30.013886260122352],[-102.87749148288295,30.177544617012877],[-102.49288002792737,30.341768033513407],[-102.34701205064597,30.364760903435783],[-101.93926774006563,30.296470173978943],[-101.46177563321018,30.300607056464528],[-101.12388880993348,30.171622710795326],[-100.38401871169678,29.517874687957647],[-99.909375498089958,28.770304842298533],[-99.841657006872978,28.598095740536799],[-99.421587571391939,28.110264539795224],[-99.102495481745038,27.844178147467094],[-99.020005379937373,27.667774300663869],[-98.955523610643041,27.238057948521188],[-98.795411205841589,27.009115112266148],[-98.720435820184193,26.838162833888159],[-98.108343798745736,26.589053138659711],[-97.676877697880997,26.526203297942732],[-97.382757743685929,26.42283434228403],[-97.150491581747644,26.461181324539133],[-97.005038127335425,26.440767835363062],[-96.832127083491727,26.350001012003258],[-96.70717311948313,26.199923191575042],[-96.649237635292664,26.013428352564514],[-96.676473773384203,25.648549969377907],[-97.030236379600623,24.856135929720015],[-97.174901411108451,24.291802926303141],[-97.263409484952987,23.310888212673291],[-97.250324138079421,22.872699260506611],[-97.336317310613637,22.593938173071081],[-97.28185662558829,22.282623806708678],[-97.190939612810723,22.127839820666392],[-96.942994132131105,21.898474409842201],[-96.8613379609384,21.77484901037429],[-96.815687605843365,21.533820101453486],[-96.877042523878984,21.263932980057756],[-96.743744859349434,20.964676301875489],[-96.049501838383904,20.160428226465008],[-95.827356997968636,19.590888097631243],[-95.494360268753866,19.233500120623852],[-95.106885007093908,19.194815750017977],[-94.966869942726802,19.151865517541683],[-94.788533909241366,19.030857361792723],[-94.526803466448158,18.934251630724276],[-94.295506492727426,18.692330447139021],[-93.677836451452322,18.914224657449591],[-93.078418529720821,18.958054045417565],[-92.876726763676203,19.082838266731532],[-92.556033648488921,19.161651129365541],[-91.861439883821404,19.20300028354594],[-91.752208378335268,19.248531992733124],[-91.222047959705449,19.593803431025638],[-91.146743419957104,19.939775449882614],[-90.990240823473158,20.152248293475907],[-90.973931967688614,20.657724255197586],[-90.816086484977063,21.197657600366743],[-90.701755385372849,21.367496625561351],[-89.998849390456101,21.741658951095108],[-89.023256406261069,21.898102990240005],[-88.664067255272528,22.032759705190898],[-88.124494792866443,22.11557238652447],[-87.555246251736293,22.011028964021147],[-87.231006771337164,22.110576255081082],[-87.08413551117745,22.119239279830872],[-86.896447061431374,22.064122640166037],[-86.549881919752849,21.839486327847698],[-86.413281057678219,21.706242955399357],[-86.209851038240274,21.303946338798571],[-86.215717590043482,21.063561865123809],[-86.346611548710058,20.839313092427158],[-86.257676892931684,20.600722692347617],[-86.295084341618278,20.356286312006954],[-86.643796794154895,19.912979969400258],[-86.766760658790403,19.825753101766256],[-86.948321014118548,19.774828395034124],[-86.9694285526584,19.705675459436808],[-86.935484529530981,19.478254722560639],[-86.961465621948307,19.341078319599781],[-87.398110680267152,18.062427851074371],[-87.551315055595964,17.870758379565274],[-87.728707119335709,17.78501536155116],[-87.925451681876069,17.774461379988878],[-88.110998397531219,17.840735249089743],[-88.225351694768079,17.93516785901679],[-88.419074000613847,17.649695197178023],[-88.522812761633958,17.55401152035088],[-88.698986529830066,17.456009676740219],[-89.06382601612782,17.324740199476125],[-90.492543406882604,17.316034069495686],[-90.501298396000465,17.166064255311671],[-90.33384003318838,17.036424413462576],[-90.222190706390876,16.859615836762355],[-90.035878633002852,16.714323913299179],[-89.940177232823814,16.540733742171199],[-89.919449994373636,16.343597527601936],[-89.956724245863043,15.977013745323756],[-90.005751205653723,15.838598055219638],[-90.129164941443193,15.687314263694571],[-90.34871016330689,15.582778133706485],[-91.444248571353327,15.570679546612739],[-91.62066761110097,15.281773198579199],[-91.577822541323613,15.021725298797557],[-91.653673841606093,14.803602810053359],[-91.694029111190517,14.510081379072453],[-91.845161070124874,14.232822965739018],[-92.042316004537568,14.084387726917546],[-92.286453557088535,14.048332994611119],[-92.518098804108774,14.133442146999066],[-93.154940336708904,14.77616285992133],[-94.083337473138272,15.53629826241337],[-94.570476343456235,15.692374295296188],[-94.753976478958307,15.715048996601553],[-94.955506778519521,15.693634752263613],[-95.249338915361932,15.523555926195774],[-96.037675239906122,15.225250002901653],[-96.489993051298015,15.152548533371517],[-96.976929240963926,15.256114954555466],[-97.323322812384873,15.418142437270493],[-97.93798797450161,15.50184580574096],[-98.331686869021922,15.736280378297145],[-98.734808175436626,15.853469810917648],[-99.006075594001473,16.064397506452991],[-99.846232358046308,16.244686420507367],[-100.24777006367226,16.4683097959668],[-101.21664335563284,16.824519342795615],[-101.86571258253379,17.228086262890212],[-102.11585042906297,17.456371755342964],[-102.32325894195621,17.4692168470871],[-102.80615876425063,17.574679551919832],[-103.6518255260891,17.872053999624409],[-103.80407701799118,17.981408142507206],[-104.22528664870551,18.425144684015969],[-105.26287582588398,18.929179127589258],[-105.89247795075896,19.690631448875749],[-106.15626179048945,20.272752985862898],[-106.14987568477029,20.52316306591484],[-106.00707927576789,20.754822045894741],[-105.98291044611518,20.972485272242391],[-105.77998706103007,21.261029005285746],[-106.00295995153164,21.604073350859625],[-106.03104597590033,21.444365530010245],[-106.12722552724583,21.234568704954093],[-106.26792058251304,21.103916775489516],[-106.44747976070887,21.035920255173416],[-106.63942524728148,21.040605917759105],[-106.896110598162,21.153808208820681],[-107.00646884187741,21.261124701719552],[-107.09339010511309,21.446353957224698],[-107.13195468473543,21.781922868034219],[-107.08471066266777,21.92447072947402],[-106.99726939935587,22.046560515666844],[-106.78497793979214,22.175928930510047],[-106.58623908521352,22.194879436322768],[-106.3405390225461,22.136177250527762],[-106.14892848784943,21.991764497385642],[-106.15168019409806,22.215266950675591],[-106.20906292791992,22.331697186877729],[-106.77046062351549,22.857770554266796],[-107.29891439319613,23.529308250206931],[-108.31758811485673,24.275105966714907],[-108.46728239802617,24.534604902425869],[-108.67910695783375,24.795047689759084],[-108.96873500110168,24.981652978946322],[-109.1169252036984,24.988543936254072],[-109.25462149775423,25.034637629093368],[-109.65325532740435,25.275733170744129],[-109.79414763473419,25.440211587443667],[-109.87179103653153,25.614174740656999],[-109.92515782868379,26.018295886179679],[-109.88806733941831,26.22188166558357],[-110.12069454488329,26.362493721958945],[-110.30587844163044,26.644738308144472],[-110.55327086042666,26.745627004218594],[-110.82523113218275,26.963458705142365],[-111.0075990463924,27.22767904074183],[-111.06138991877765,27.409446378811221],[-111.43001085530747,27.573866447912206],[-111.77463095968454,27.96082490144649],[-111.98954042516698,28.078110029660991],[-112.17173923180205,28.281693652244353],[-112.31676074752087,28.271616187856957],[-112.27567978705746,28.10918116029071],[-111.96024084716599,27.860519699374827],[-111.80291336737706,27.549597567906613],[-111.64239315977483,27.424066833300468],[-111.54734866436578,27.280905831772607],[-111.19073846912541,27.033374864359878],[-110.96361866333855,26.558877634858614],[-110.78815930441701,26.473169122182863],[-110.65881362294839,26.326605086229858],[-110.59143909595205,26.092654929893115],[-110.62074087533244,25.806893156374581],[-110.50397252766749,25.542250590279711],[-110.29445698058421,25.441252042524532],[-110.169174852285,25.303384665917065],[-110.05744028435264,25.026918550621311],[-110.0465229431618,24.819109211915187],[-109.89936847319775,24.84433304016542],[-109.71242474908361,24.811685508358913],[-109.55111220983015,24.711725106207258],[-109.46116573629479,24.6009298286614],[-109.3068383938288,24.295578822247393],[-109.30873884052991,24.065964132144003],[-109.1104593793569,23.89865949368491],[-108.93701260892081,23.605319590521017],[-108.92148907929413,23.459448604051715],[-108.98489556459937,23.053833245322387],[-109.10958088163788,22.856479415208067],[-109.59134995971246,22.479612772383422],[-109.97535586527266,22.395199253444176],[-110.17414944546567,22.423327395997333],[-110.34585146109946,22.527387464019341],[-110.49232499380395,22.714364481972478],[-110.63879559106972,23.114723992161242],[-110.70709903330331,23.215108072723144],[-110.931141481689,23.338590042944087],[-111.33606963909298,23.700600479608379],[-111.60018476449274,23.8598406523511],[-111.75606038720602,23.848586181366976],[-111.89727807055431,23.88213470024025],[-112.392430389705,24.146661661976623],[-112.71277476507233,24.512900004346182],[-112.78781999213878,24.6973146135972],[-112.78495210298331,24.896393264944987],[-112.58396667689016,25.521162492051015],[-112.62747091578737,25.682650974057584],[-112.69405953916872,25.784318471060431],[-112.95914038099285,25.917266655274315],[-113.40281826528087,26.260853566155731],[-113.5738650446082,26.222176717227708],[-113.77362306482689,26.253237098119946],[-113.90631263980438,26.32754304687608],[-114.08475310387476,26.495240567645798],[-114.24548730273457,26.554386439826782],[-114.40173991474641,26.67837249053423],[-114.734978397877,26.810957397129133],[-114.84351232304347,26.916256121468589],[-114.94480017924647,27.104276322707339],[-115.37059273051376,27.428143007571069],[-115.50025332969298,27.624960123496553],[-115.65849754030705,27.708573687633749],[-115.78378425993051,27.850975640080236],[-115.83769468273324,27.983027154932159],[-115.8478662935178,28.172425192167573],[-115.66758251396615,28.616338931619087],[-115.57892238784933,28.729633273175544],[-115.46167374520992,28.81299382494301],[-115.32554300357529,28.859519591715468],[-115.15047456313651,28.860564271907204],[-115.20871778875556,28.915436187713144],[-115.43833359006081,29.007961334309353],[-115.94572577891385,29.337042934979031],[-116.09104302497327,29.481247310328534],[-116.2680357207263,29.764307767911074],[-116.31136712533358,30.027621905126729],[-116.4451909227154,30.195835905264829],[-116.51869626029412,30.505315614228415],[-116.71271990153762,30.694233956441458],[-116.80855601277268,30.982850135874742],[-117.09439078880412,31.314101150320187],[-117.19632250820388,31.676006855911318],[-117.49467017925208,32.091570778352015],[-117.60085557728816,32.37072515459348]],[[-114.73082864088825,28.465208838392488],[-114.66786652953031,28.306798323786655],[-114.60760462995341,28.299226178206567],[-114.59145818579211,28.333905604469777],[-114.73082864088825,28.465208838392488]],[[-113.25841383115386,29.992274203436768],[-113.2857464329222,30.089308244703776],[-113.52628157153291,30.477333088049896],[-113.61152260159098,30.82020912779662],[-113.85778988820435,30.904463235595411],[-114.00643466307483,31.02499235276159],[-114.268957493087,31.022697280179166],[-114.14041720506845,30.589480109714188],[-114.13880779398734,30.327232948393679],[-114.01248297799656,30.197315239865858],[-113.78682584529966,30.030710114785862],[-113.5544564070463,30.071722747303252],[-113.25841383115386,29.992274203436768]]],[[[-111.34731657791053,18.370181912176786],[-111.48553031469893,18.513826397915491],[-111.55664065179249,18.70005232328193],[-111.54934480723912,18.899259617255826],[-111.46480243438195,19.079784857906066],[-111.28685661988149,19.264754503147618],[-111.11192845060069,19.347585497437045],[-110.91864893825108,19.3577770688964],[-110.69527406009132,19.267112916645726],[-110.56377257798184,19.125098134929459],[-110.43086219236012,18.86743941056217],[-110.41983768095986,18.670541047290353],[-110.51337191154494,18.443453099332476],[-110.70375023657921,18.288296140880721],[-110.94763288131928,18.22133842920385],[-111.1329365494901,18.246276441411066],[-111.34731657791053,18.370181912176786]]],[[[-118.85750961011404,28.958361755880727],[-118.8998906049536,29.197959510690833],[-118.86808543948626,29.341412675107577],[-118.79595416720517,29.469426184123975],[-118.60439652846303,29.619455860765537],[-118.27982935732655,29.681573344930587],[-118.09565668102471,29.633173789753879],[-117.91241736777316,29.482531808501729],[-117.7787866373598,29.204278380317156],[-117.74368199826571,28.916239922064285],[-117.81551698985812,28.682729001415751],[-117.95223239400781,28.531336206956905],[-118.12164954945253,28.431718110911511],[-118.31637208004672,28.405089673148879],[-118.46093904165552,28.435945622759803],[-118.6278215246443,28.539753977385928],[-118.71940146813901,28.655791945158114],[-118.85750961011404,28.958361755880727]]]],"type":"MultiPolygon"},"properties":{"alpha2":"MX"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[109.0972021561125,1.6624470007954946],[109.03924411848969,1.8999372294995827],[109.07923409807034,2.0921079162017979],[109.24544617395834,2.3479929174629639],[109.39460974889093,2.468804169647175],[109.5782986163856,2.5245135108703041],[109.76944061508831,2.506910469288909],[109.9398651372699,2.4185893903770119],[110.09706408118683,2.2237489641171613],[110.44740038938617,2.2097090174166936],[110.72056046904558,2.0918142156606727],[110.70792786557496,2.3840232203643983],[110.734418599506,2.5357534129003128],[110.81821174376465,2.6983862016063442],[110.88121550587604,2.9811093057249001],[111.03608159039671,3.1699500807377339],[111.21351245149582,3.2534453031245856],[111.41545521196016,3.2613556079221717],[111.6061829822939,3.3386091648317349],[112.01952114035061,3.4060369983846357],[112.6955825354223,3.5995240770096015],[113.05481696734702,4.0515342128700853],[113.46180195687612,4.4834450012644398],[113.52193776376353,4.7355258350572456],[113.65983831332863,4.9259670266517244],[113.84707161132978,5.0483075209170352],[113.98877138300942,5.0871724703820487],[114.18355965046797,5.0722624481351319],[114.33496643699922,5.0012622907579463],[114.47490033401199,5.1375244395415809],[114.79632557454121,5.3431825693138819],[114.92036612069009,5.3875568170929471],[114.93760156512649,5.547254469823625],[115.02771610207245,5.7240464673205196],[115.31768229679349,6.0130938766704158],[115.44467881193408,6.0770943846717111],[115.59539259285221,6.1026566599067893],[115.70528821587189,6.3788595946069027],[116.09248342374106,6.8243026518719576],[116.36954636183648,7.3013405822338964],[116.48086634427669,7.3982944571847806],[116.60426211033831,7.4547257163865126],[116.72531847837544,7.6279670522365741],[116.98418516489043,7.8095116769845649],[117.34402273487412,7.8449595564067209],[117.47986076465141,7.8023772532713407],[117.59819300159283,7.7232400017741618],[117.71246285522068,7.5723127657419269],[117.77391226466622,7.3717725663542648],[117.77498246369858,7.2162600115442652],[117.7193068589969,7.024397784041378],[118.03596060869891,6.7859138285696705],[118.14231277211421,6.5331222628103163],[118.32487598801514,6.436289449642584],[118.46209805550006,6.2801103407983794],[118.73330089118313,6.1800784859943043],[118.93191647494815,6.0019107047737323],[119.07144035620327,5.9320335012365728],[119.28216247399541,5.9197596673058488],[119.46155837261061,5.84273079887177],[119.64743769796164,5.6760313690466138],[119.72191762692344,5.5454717175289483],[119.75422917201415,5.3986757827200478],[119.74540057394515,5.1349784134677687],[119.6637560526303,4.9188580807303293],[119.41231206914419,4.6865200931600528],[119.08340396378875,4.5645150841320969],[119.08876260962681,4.3825355247231776],[119.01943917784642,4.1964665514135397],[118.83420201204567,3.9888347728899096],[118.71756536695139,3.9122424821244191],[117.75962021549252,3.6673016743705009],[117.36385030078785,3.7001723757964489],[116.99868270365025,3.8377312378135096],[116.60165150138934,3.8444480673236874],[116.40611514260642,3.8085187033598147],[116.24361790976924,3.8376785969570038],[116.06998849502695,3.7937018389308297],[116.05867818707725,3.3626878880219331],[115.90592381130848,2.8202436632953938],[115.82536900237754,2.7017109463575419],[115.67435484126136,2.5892519079314322],[115.67090222769495,2.434494119554631],[115.59540784989292,2.2467462418943196],[115.30351179843605,1.9813723335369873],[115.25649383335362,1.690563132858655],[115.09671203432002,1.4913148836469752],[114.93168276571116,1.1797195931082336],[114.83706957773747,1.071933680465321],[114.67105372015708,0.9780696865877414],[114.52991901693936,0.95256568983312051],[114.3294701800521,0.98313832545712798],[114.11948267188299,0.96005075423561315],[113.80399999953197,0.77038854112414101],[113.61312325666903,0.73628575733300283],[112.85363295963197,0.95810428802776693],[112.67754385683658,1.0577018341988145],[112.5949930587812,1.0579373197881996],[112.40552970599877,0.76549642123793016],[111.97492922307073,0.54024960634703567],[111.51147005650563,0.49615686753396582],[111.13757805101889,0.5375378042950778],[110.60742390710809,0.37263290247539077],[110.45594746239401,0.36468630583732753],[110.2204128727588,0.45163793810296776],[109.37778485219002,1.1738728152301212],[109.0972021561125,1.6624470007954946]]],[[[99.223401587156914,6.1518188358219312],[99.163542386574008,6.2889322705854802],[99.151373340868773,6.4874564122216603],[99.19403788137484,6.6308543022705999],[99.277217139698266,6.755210359106794],[99.437830711033143,6.8725270296562586],[99.767795849488422,6.9584004005371582],[99.96755278234447,7.1257170953876088],[100.19770748327745,7.1784140792975171],[100.44712519324401,7.1466491962377239],[100.66593777063663,6.9773055655138378],[100.8415512205929,6.9643036084349665],[100.98071458759223,6.9043581318652851],[101.15729873705826,6.7332849240253312],[101.37310581629799,6.6267453720708058],[101.52516417882974,6.4059715435582474],[101.62591872894416,6.40205998255888],[101.78990162110235,6.6332505506495387],[101.91838848185895,6.7073570804326836],[102.06296245338594,6.7405085193231242],[102.43674234463647,6.675871830484124],[102.66983583455148,6.5476130277403337],[102.91980085883381,6.1949601899197306],[103.47437627243991,5.7360796830936049],[103.87047809205467,5.0576117803952627],[103.94936481084291,4.7356086505745081],[103.96759603710574,4.4148925400967887],[103.8803344511854,3.8030458942165875],[103.94536598403261,3.6092285434511222],[103.95108042691231,3.3132284357179991],[104.14407306555506,3.3696707581286831],[104.34065306342323,3.3463726939322269],[104.54868730007318,3.2141043069870765],[104.65318472911586,3.0459770257962577],[104.71519169220737,2.8101207206822343],[104.70655990487801,2.6112616798301609],[104.62103185231146,2.4315274924720507],[104.49613107015116,2.3149552686766781],[104.68819472374078,1.8939897980985838],[104.77479017957867,1.4539522190784391],[104.7509232132021,1.2541701296277454],[104.650239330022,1.0799711887093766],[104.48904227742344,0.95956344152157702],[104.24567711140199,0.86996779002285951],[104.04738452537937,0.88208162104254539],[103.87368494096479,0.9593225676552628],[103.54978159720987,0.8345522188263117],[103.31349460232771,0.85839532909598049],[103.14921131101008,0.95513059388763855],[102.99163463468088,1.1682334863228376],[102.45842357547986,1.4341629767255077],[102.23837343869775,1.6382070563729916],[101.86442232011012,1.8353238846492044],[101.51201568150417,2.138704642257037],[101.22540469044419,2.2797087148908721],[100.96142857975703,2.5135387060193413],[100.82080204976842,2.7290490586198612],[100.79468032163065,3.0759682378036022],[100.25575179166702,3.7701187008588524],[100.21967827625832,4.024984207594704],[100.12608599438256,4.267876243334908],[100.10979449982123,4.5809576972243757],[100.03303825307759,4.8088715953930796],[99.856154135810954,4.9119408063470615],[99.717905967046619,5.1219582526598426],[99.691381979611435,5.2715322793900734],[99.712187111973066,5.5359964461203512],[99.823681954857165,5.7704736850488105],[99.666180735470391,5.7696276873735384],[99.488904068195467,5.8334511704117764],[99.347834127372664,5.9583500316695543],[99.223401587156914,6.1518188358219312]]]],"type":"MultiPolygon"},"properties":{"alpha2":"MY"},"type":"Feature"}, +{"geometry":{"coordinates":[[[29.751095744426038,-15.178545953483228],[29.722963248061902,-14.980342713016283],[29.754608168946529,-14.833116504053176],[29.861697412279888,-14.663977934100718],[29.981245572127786,-14.57240722316963],[30.535210295150176,-14.338815825717921],[31.37470353401741,-14.103753635591946],[33.090356193811452,-13.526178319169571],[33.291995887943351,-13.521835040292622],[33.478947171426768,-13.59750965905554],[33.815227113723338,-13.977978605777365],[34.403948644229338,-13.914092086278263],[34.266278145029482,-13.792425236041666],[34.085812419256762,-13.506311783923428],[33.991019749925393,-12.763430808540141],[33.858604658356093,-12.14057953327368],[33.90485160160403,-11.953498641887883],[34.092641361003999,-11.630734964356193],[34.177859558018383,-11.384498726180121],[34.264061433914023,-11.267915493117373],[34.423763512508742,-11.159918040380857],[34.788805162669938,-11.077586134124326],[35.416255031866591,-11.096603111754995],[35.600554287626196,-10.988650509265742],[35.791534453156999,-10.953205793991312],[36.068981042915176,-10.981963689141985],[36.450405537391411,-11.200353357300516],[36.738080584553089,-11.09030357185731],[36.946252786055275,-11.067970584941143],[37.100832560007575,-11.083362669636525],[37.338138244648469,-11.189837236757811],[37.404959415017615,-11.163844350789736],[37.571679163758468,-10.936603257678868],[37.741101873156495,-10.828227385155504],[38.188123928294324,-10.779020089116491],[38.46283060224377,-10.841129711555563],[38.65608574397784,-10.748372630616089],[39.136333840012767,-10.652637430353817],[39.721508092917929,-10.393513173786916],[40.205677841892523,-10.03628643623621],[40.388660655829732,-9.9703214753016347],[40.582969042001729,-9.9791723131553827],[40.759197079428809,-10.061499496345858],[41.064688002838331,-10.450497248989597],[41.10764747329673,-10.600243796693466],[41.095082342211157,-10.873376237284186],[41.02503257759988,-11.203050161739728],[40.956147747604049,-11.359394528250327],[40.946826239863029,-11.616818947553618],[41.026082665268817,-11.932501089336556],[41.012728227914792,-12.265626313930953],[41.08041675536807,-12.620146514863935],[41.044758081468231,-12.9114626988534],[41.089772540133488,-13.917524561823473],[41.29575834375914,-14.320369319222246],[41.344235946601792,-14.724584583932417],[41.309744378167565,-14.947975816003787],[41.17254487080222,-15.166702800993654],[41.124539682313667,-15.420671311486215],[41.019962533589066,-15.66715795343317],[40.554369252101445,-16.231427616838634],[40.2480934085985,-16.730495587015142],[40.141882736133788,-16.837365654870371],[39.565589671764073,-17.182963839766927],[39.310647342158177,-17.4184407999422],[38.337526907628146,-17.708842965820203],[37.538358243731516,-18.147924462889929],[37.380012359568369,-18.277152107847151],[37.303261831854954,-18.423658941864428],[36.941727804622616,-18.822738021889819],[36.819896445753621,-19.046346402047806],[36.72294470604777,-19.154202710176616],[36.427866757421874,-19.322625842791751],[36.172689042929086,-19.386199650909848],[35.689255222814566,-19.874486532425003],[35.246852089751648,-20.190857399823134],[35.231569749345759,-20.317777574565131],[35.432713895296338,-20.589795334780309],[35.601510099904431,-21.070364173945801],[35.624779661337143,-21.257672340203037],[35.749083646780441,-21.516668976809527],[35.785539202264481,-21.718923526508057],[35.943686159717977,-21.90723744772685],[36.032916303844907,-22.217824722421323],[36.001499161650429,-22.630957163329445],[36.074556393146125,-22.98702224285055],[35.94231943248591,-23.525257822018879],[36.010103784551127,-23.649380139060501],[36.040441386010443,-23.788255709521749],[35.968309825965775,-24.208323267451828],[35.642633387218531,-24.745500398150831],[35.266758960632451,-25.068169903025499],[33.579132942537647,-25.708933362711228],[33.389943877070536,-25.838014735024615],[33.454584790580519,-26.07811935072915],[33.377013063880092,-26.943081193110054],[33.272457422220391,-27.166248498693388],[33.16195900871972,-27.265982488558034],[33.026996835272818,-27.328767676771296],[32.879532204208083,-27.349039595906607],[32.011639465016806,-27.3287850494286],[31.797408587927364,-27.226906159541269],[31.654245108360769,-27.037754381802934],[31.543648867829585,-26.250032734472608],[31.46462137995303,-26.083646852872651],[31.421410252749709,-25.806103871020987],[31.483054415993447,-25.51808555674808],[31.480670816905867,-24.518714070073663],[31.345935206728907,-24.113521061891724],[31.078787723442641,-23.660671027080099],[31.031584428289705,-23.344486038559694],[30.789663627897674,-22.44141105792816],[30.811583737662875,-22.250782612375978],[30.903687539608413,-22.082447871062911],[31.853816996879583,-21.13826039961441],[31.880699736659572,-20.975062264335751],[31.982311965206431,-20.783432819222778],[31.999871068094766,-20.574959277151855],[32.04571486457376,-20.435630551474144],[32.13001666392551,-20.315599913299387],[32.30447135281284,-20.16150954578503],[32.46262197796338,-19.892785955996498],[32.294209451962097,-19.515583879414173],[32.298762088130253,-19.236405971377682],[32.220926772472055,-19.080760941117369],[32.199686929788896,-18.933488782226991],[32.262555125979681,-18.631695929921907],[32.341046082814017,-18.504938337919096],[32.448521557290576,-18.407654218230313],[32.487298423802983,-18.276783632420823],[32.472678117437368,-17.372938025689468],[32.383598404623271,-17.031082368737458],[32.139418340553249,-16.944100213401882],[31.754138772773867,-16.892809692328481],[31.466798701107628,-16.686361425289405],[31.277330926286922,-16.632454431659113],[31.083843046614444,-16.522581525318053],[30.327079991980789,-16.471233311740445],[30.139148814314368,-16.398635036007175],[30.025573453964682,-16.298288314062717],[29.930374042925269,-16.120733507548557],[29.866463485998626,-15.502062217575043],[29.751095744426038,-15.178545953483228]],[[34.606167234285344,-13.991075942298425],[34.903316891037989,-14.295549421620438],[34.997954930132217,-14.528732506800289],[35.056993995852494,-14.993529272589047],[35.039668363677876,-15.326523305371303],[35.009619190339833,-15.47024970569807],[34.822882720716905,-15.860660678149999],[34.874461555145146,-15.97709966556384],[34.947605041908183,-15.863220357122096],[35.065224352349013,-15.763248166446779],[35.314657724376438,-15.677806293273456],[35.306971492756077,-15.229140226704066],[35.363806979486895,-14.903364410146601],[35.354113199680441,-14.85168039511759],[34.874385451480883,-14.231621906982147],[34.652130669177815,-14.004470820253287],[34.606167234285344,-13.991075942298425]]],"type":"Polygon"},"properties":{"alpha2":"MZ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[11.243369385577061,-17.239284821654632],[11.267433888257742,-17.095843141072937],[11.331762644576454,-16.965396113019946],[11.430912253978006,-16.858981964651932],[11.556492828801753,-16.785605297759059],[11.967553358899879,-16.671037342424423],[12.454668897724439,-16.700628762659122],[12.957253125244691,-16.490549714016648],[13.102505665435935,-16.467933053200849],[13.668227548503825,-16.578377887473948],[13.973222631672829,-16.814134533156029],[14.149035991543839,-16.907382756062908],[18.442390045966526,-16.901710376873062],[18.662894292814894,-16.976545679267591],[19.057034884535327,-17.305182954886128],[20.465224920660024,-17.392807281139422],[20.79921480176445,-17.502971007107703],[21.124768284196858,-17.456109155526562],[21.429571714020895,-17.490357714939027],[24.323041226440004,-16.983559158567701],[25.121802052663153,-17.080499255831246],[25.56406581596368,-17.397739663384247],[25.666755448364608,-17.505037806004225],[25.746479920507998,-17.685682716275359],[25.750053655099894,-17.883105621447161],[25.676919312862694,-18.066517570487946],[25.538482508965092,-18.207314681225437],[25.356333058289401,-18.283539038775707],[25.051846940192991,-18.316010772864477],[24.666574391418614,-18.533627149787655],[24.387412744816729,-18.531520709217027],[23.747939332389983,-18.937255493202002],[23.555276945067057,-18.957749430672166],[23.369288294052328,-18.903462980652659],[23.217900540176505,-18.782547535243758],[23.041295808862255,-18.530986858730806],[21.494151463569803,-18.764829285644833],[21.472031100477533,-21.998898953839173],[21.449982860529406,-22.142357640403819],[21.358753556601741,-22.312425170334865],[21.166151185496538,-22.456263179590675],[21.025629980509599,-22.492599257869458],[20.478613687118251,-22.500575965915697],[20.480233829209183,-28.450894204935821],[20.439328267485571,-28.649101830535795],[20.2844180777128,-28.84785770479084],[20.150890983955676,-28.921004962357827],[19.829398665000685,-29.004544335335957],[19.463444367274292,-29.337191097435255],[19.295346898288539,-29.420323943152354],[19.108445897402628,-29.435681188487514],[18.76517405396018,-29.368011608899089],[18.066460120703734,-29.370326900713724],[17.23275889690326,-29.149168364042293],[17.082670878942547,-29.039161017037461],[16.987440360720139,-28.895050259709137],[16.540381979212828,-29.108570986541757],[16.388295713539986,-29.113748835641221],[16.241695665224306,-29.072938687865843],[16.114161128362095,-28.989920712493348],[15.322075518106816,-28.269536635642346],[14.827680979139915,-27.470478080192475],[14.642785827842516,-26.885655817979327],[14.635095712076279,-26.697550524032206],[14.531365748093059,-26.561584237551365],[14.478471694237671,-26.419782555296177],[14.440314608494575,-26.078276209535119],[14.349472606839228,-25.789195145200726],[14.333226179280388,-25.117405807206087],[14.004400735249542,-24.255489677704844],[13.974716045927742,-23.350609272708514],[13.903591077790853,-22.974689548624685],[13.988351908647717,-22.628228659582156],[13.903563309112906,-22.469887344009894],[13.561783101370324,-22.051202370076034],[13.398512491138295,-21.716459895142449],[13.009713687051484,-21.15198060391171],[12.726217612413922,-20.435438586200668],[12.598180392534708,-20.258364469381732],[12.045848515068407,-19.217530814790749],[11.640345735191342,-18.769289548509647],[11.321299878486778,-18.209342209166199],[11.234550549541295,-17.780939442585577],[11.243369385577061,-17.239284821654632]]],"type":"Polygon"},"properties":{"alpha2":"NA"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[163.59778263229086,-20.469818497185496],[163.53849893107227,-20.220047663115448],[163.60504846737024,-19.935237858662294],[163.75411370930578,-19.747236418531301],[163.97359528799149,-19.650319833458084],[164.16617668590868,-19.65423004354065],[164.64903683537949,-19.831014680908783],[165.22786616890735,-20.240163621511044],[165.46435210303054,-20.350784388412237],[165.65319118316259,-20.495405442291158],[165.77339815017817,-20.529924550275041],[165.96348357949574,-20.482915348637317],[166.05402165035667,-20.38591845144596],[166.1680423420988,-20.135804881107575],[166.30866468032195,-19.99652927110645],[166.49231028309489,-19.922727982969295],[166.73067560248174,-19.930613770983484],[166.8680750522166,-19.982598778203169],[167.09219669665325,-20.135448207237307],[167.53169458181341,-20.276619999179978],[167.67215528528061,-20.406871352696516],[167.87444948769431,-20.758679980303285],[168.230102457002,-20.901326671851038],[168.46608980321437,-21.068480941773391],[168.55987161005507,-21.177404508505681],[168.62975089969433,-21.355299755716217],[168.60970921909015,-21.715594164595462],[168.56111073222939,-21.850365957811409],[168.47628907131943,-21.965823893927872],[168.36220817926164,-22.052488761314248],[168.15723001189386,-22.144096609485697],[168.05266623738078,-22.245703650781891],[168.0137786576451,-22.371202016088475],[168.03270954812081,-22.725305659346834],[167.98218911300134,-22.862527849132505],[167.89630643821684,-22.980388611201782],[167.78838570854953,-23.077015273427943],[167.65729524764447,-23.138650383589205],[167.51403805245224,-23.160120636744303],[167.32813970034093,-23.13102334598161],[166.95438601958759,-22.899782694189522],[166.63373341978888,-22.854915474202251],[166.22120623832254,-22.689733578639682],[165.46947879915655,-22.209060845587196],[164.96833836422323,-21.942280787998317],[164.59269515786971,-21.660073022899329],[164.08280747540309,-21.162588057837858],[163.75412427954157,-20.756406550632775],[163.59778263229086,-20.469818497185496]]],[[[159.43000317845389,-19.199435539681719],[159.46432932532656,-18.990934181849248],[159.60233569680958,-18.767297603710286],[159.76251749996703,-18.657718926603483],[160.00113719779037,-18.619086527944134],[160.22999401901541,-18.696909261089701],[160.36969541658701,-18.83162828742152],[160.44757556493775,-19.009393241875756],[160.47235205129093,-19.27786912077465],[160.40174334915213,-19.515437300397195],[160.29742375795408,-19.677329410607985],[160.13965213424609,-19.787781876441816],[159.90341357768287,-19.829483194665631],[159.71736093674917,-19.779723298586195],[159.53347957803334,-19.625660644235793],[159.45190921169186,-19.451196012315311],[159.43000317845389,-19.199435539681719]]]],"type":"MultiPolygon"},"properties":{"alpha2":"NC"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-0.21192457608983958,14.167724636087565],[-0.30820896747779358,14.333182980434524],[-0.33538856553343965,14.474559610459423],[-0.26116765607669423,15.038339463256751],[-0.16590479477934908,15.265912688018318],[0.026579011290038868,15.420232387901315],[0.16991271051607076,15.460974757482839],[0.75477581066667765,15.479642256526681],[1.0342731989253175,15.695353603819187],[1.2278739099680305,15.766724401163282],[2.7508749329501501,15.832582886028687],[2.9623819520677186,15.918222702615182],[3.2587424988347915,15.907430848082475],[3.4266204436979528,16.027782831539447],[3.6910998532200723,16.702515074410904],[3.7343324066484271,17.09744883889115],[3.7371352065260974,19.238374592249542],[3.8399176082482898,19.45800085529735],[3.9897978526051174,19.58226123041117],[5.5901481211386788,19.932484581220425],[7.1927278333935796,21.280927517977972],[11.798982529144071,23.988240469554686],[12.076788705382155,24.005641380562754],[13.639725541551266,23.654202270540321],[14.288637614125678,23.208481564276518],[14.79878519986792,23.462457742750267],[14.941419612086563,23.494587614830845],[15.180067246242933,23.453688553416622],[15.303863696137363,23.375898173404643],[15.399864604060649,23.265622473874892],[15.470873686704934,23.084655278897259],[15.663725286723528,22.014405424924561],[15.682871433789215,21.70135385143039],[16.037745499595907,21.208331926828933],[16.091720598289911,21.077175358033042],[16.1062253722076,20.930537372925006],[16.364029507084332,20.644679684216825],[16.458253651372129,20.41433724179435],[16.429763086209014,20.167104319543679],[16.224839766001917,19.754899804888005],[15.965685281256887,16.817189881639454],[15.8672772905706,16.599619775194032],[14.763606570599883,15.440469399813116],[14.230350790979216,14.697132183055253],[13.996909619391808,14.279879249515451],[14.103214668407253,13.758049357047803],[14.085338616182618,13.562379101171603],[13.993449118853349,13.388704916105514],[13.84173626801544,13.263846543665256],[13.702534508077894,13.21421338680925],[13.49361254162468,13.191791385581286],[13.115848666880158,13.01158392242402],[12.967780753454175,12.924238001258193],[12.745301791978875,12.681276101955909],[12.565473807892108,12.60458037747104],[12.019519606148474,12.600465214951919],[11.741535074410603,12.744378592086399],[11.359701341928941,12.850544126364515],[10.563621475303078,12.832764953542116],[10.286693066475912,12.752360031934549],[9.8297913822941165,12.361319407798623],[9.5967913218830425,12.310974587879207],[9.1412957427413843,12.325321278121285],[8.6078809635074816,12.429291171681479],[7.9063373150162235,12.813190778086755],[7.1825987153719799,12.516612579300558],[6.8651640241623983,12.51324513770097],[6.6869727178009812,12.577291386246724],[6.4725523204454865,12.733885195980591],[6.162060965023084,13.123669233285007],[6.0579197989073208,13.201794672785775],[5.5286228066326482,13.338821446940138],[5.3099666328801032,13.262118249716051],[4.7705630507865795,13.23853810184929],[4.6120130160271389,13.15506294457535],[4.4696326322453679,12.68087738503487],[4.3526989832764107,12.481942450098686],[4.1550923351157394,12.290140196448633],[4.1300025871760502,11.949639702770494],[4.1617764716972276,11.710513208695108],[4.1016185657524895,11.519727176437948],[3.8613131770557159,11.273140653802175],[3.7233773604040463,11.213251330636808],[3.573872785249848,11.197083382327529],[3.3356762967549702,11.269412623622417],[2.8414109155481122,11.684890126453867],[2.691249833852845,11.499193944918131],[2.5166559997135356,11.414085903902459],[2.2747588695346197,11.410759998432214],[2.097891131986044,11.491035704467341],[1.6797887332655599,11.999200514672101],[1.6060435172856369,12.134457775827277],[1.4552654204384192,12.147863023890368],[1.318687610303964,12.200565313415451],[0.62228409161856724,12.700538195393358],[0.50150931075754912,12.924561513550618],[0.48165622759092075,13.22179340288451],[0.32978402546514501,13.295275104401231],[0.22789156153358336,13.391262048871823],[-0.066806413091256067,13.840425546506522],[-0.13348130271861422,14.078222773756556],[-0.21192457608983958,14.167724636087565]]],"type":"Polygon"},"properties":{"alpha2":"NE"},"type":"Feature"}, +{"geometry":{"coordinates":[[[167.42347532436176,-29.157259966312751],[167.41347083012033,-28.944438790578776],[167.48128586453936,-28.764996423887972],[167.6262184369545,-28.610118495728155],[167.80090615400445,-28.528770871016061],[168.02195406618455,-28.524320102766673],[168.23171854241374,-28.603543495073747],[168.38751200202611,-28.738706279391081],[168.47473393889973,-28.918942193716813],[168.48230903230922,-29.136736540669556],[168.40150866387285,-29.342664278959209],[168.2302975770219,-29.517073394066479],[168.05461651380605,-29.58709740604797],[167.86549448337897,-29.586861114327004],[167.62858736890942,-29.488474029328998],[167.49945823061688,-29.352115360197178],[167.42347532436176,-29.157259966312751]]],"type":"Polygon"},"properties":{"alpha2":"NF"},"type":"Feature"}, +{"geometry":{"coordinates":[[[2.1956254706683938,7.777718499237638],[2.2764372553714081,9.085966053303137],[2.308012867059388,9.2269358681495408],[2.4444833588495669,9.4234740752614385],[2.642786530744655,9.53000155878056],[2.6996315069839545,9.6949876159349895],[2.8531164354447744,9.8939672000730372],[2.9177824317170549,10.058160228861398],[3.082489094132876,10.229948341253014],[3.1166458917185671,10.484856784449798],[3.2749674396305699,10.709815778075912],[3.2431268494405687,10.873206142613418],[3.050249260649613,11.154006460320671],[2.989656393839204,11.355662525757769],[3.0048904101503924,11.617026715352337],[3.1231337784102209,11.887705012673949],[3.1722669596935247,12.687023258955866],[3.2636713154765093,12.850975765481705],[3.5615635570233239,13.112650316182004],[3.6725022933925713,13.612814531429516],[3.7739048940915554,13.789540234165484],[3.8920468692411547,13.887205450026247],[4.3962174207142972,14.174195848588841],[4.7718365242755905,14.256896235383968],[5.1139945729919747,14.256243718098741],[5.3880707167786399,14.361702891886804],[5.5748974409915295,14.365695127143823],[6.5317765401606112,14.101208221837805],[6.6596596742738976,14.007577319803683],[7.0962747172036469,13.565908634511368],[7.6529013898908289,13.8080370109202],[7.8774356228394522,13.838413697480982],[8.2956130815444293,13.748952988607972],[8.9298630344434979,13.383878358385775],[9.4210170011302683,13.316431856851645],[9.8163726020338551,13.650225334640274],[10.384291468687248,13.821169741252142],[11.454815702391301,13.851422754603153],[12.236493168751261,13.624971664617849],[12.630580942989672,13.886722254176572],[13.160562955649185,14.143227753650507],[13.691983900567244,14.196957464516814],[13.870140166438777,14.129046676296145],[14.009923113677877,13.999384585123778],[14.495962265840776,13.329693091955956],[14.63898729532932,12.783840514985945],[14.868567032035457,12.655371577424452],[15.096532198604912,12.300442981454763],[15.117375691968286,12.105610243160362],[15.057527587894537,11.447168082374803],[14.998486576953152,11.252926127841659],[14.825250412641344,11.06884899709614],[14.425620055393631,10.820980154337937],[14.26039920473286,10.765705397998776],[14.1060815250007,10.578252306391647],[13.997023245358299,10.395476156755425],[13.863434095430268,9.9518384785640741],[13.734476207730683,9.7789707187931718],[13.651756015730204,9.3527808365873764],[13.536995948704064,9.1958891721629001],[13.358473545859118,9.0757512507135196],[13.267226922043475,8.6929518967403467],[13.114932195198678,8.4254842485200587],[12.736934127510933,8.1561821195248765],[12.47860628055048,7.4006817986208073],[12.352325234170694,7.2100221888397069],[12.358690607879399,7.0669458837319343],[12.324371023068641,6.9282681095476821],[12.220723487309852,6.7690596507072947],[12.031366386892429,6.6042854109767184],[11.914547191089241,6.3369792410030206],[11.551476594799432,6.0393682588032087],[11.125736810968794,5.9402618455171003],[10.979742425084062,5.9692747233099226],[10.810497871793917,6.0718279263526984],[10.679420091080653,6.2049068386860382],[10.583747024572277,6.4085975812736828],[10.410004252344342,6.3779574801966659],[10.173069414467179,6.4006702852348516],[10.021699369970166,6.1872554272640672],[9.4481685529766271,5.67862495434583],[9.2562106003720981,4.9918395241740292],[8.966675326562088,4.4706858104068914],[8.8650404001800283,4.3626150638955075],[8.4662372872203857,4.088877240519821],[7.832381202018432,4.0236223009703327],[7.6032089209774458,4.0438891493861959],[7.2876819491263456,3.8950002313501839],[7.0560734504374443,3.9010722521390888],[6.190892252117921,3.7779304927893884],[6.0285449884204585,3.7990353281420393],[5.7373456833708074,3.896737193108875],[5.2044311386961626,4.3265557845547455],[4.9249374687234875,4.928392314805774],[4.8733569068970812,5.1590622381706988],[4.7070807761202555,5.3492773115125818],[4.6268927961120276,5.5189759345090952],[4.497390567729453,5.675666409241245],[4.2464464362784158,5.8650459073640802],[4.0551137359774536,5.9095789653549602],[2.6728190501797622,5.870647348310345],[2.4853996282365394,5.921133606202245],[2.3313282765885419,6.0391883416380514],[2.2338234875069354,6.2070207522851328],[2.2070481048259558,6.350525817251806],[2.2553020448736705,6.715258445229277],[2.221837116039679,6.9653847903143618],[2.2604684688289063,7.4464900759655714],[2.1956254706683938,7.777718499237638]]],"type":"Polygon"},"properties":{"alpha2":"NG"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-88.169473630222001,12.944193455229513],[-88.154552715531182,13.088628234113415],[-88.098760672696756,13.222685263033243],[-87.84148302999354,13.472225714902482],[-87.603631682649365,13.542692365685427],[-87.343129531455915,13.485980520655675],[-87.255710149100182,13.592532215883599],[-87.257150124512009,13.785935462003941],[-87.222813057374168,13.932110524629133],[-87.075835738919309,14.13257521188928],[-86.899331853176477,14.225809357027627],[-86.55889608624345,14.264586648264078],[-86.326590659209828,14.459940719187857],[-86.195673873394895,14.525197510134406],[-86.002575528035365,14.548455179783877],[-85.827456312507294,14.501719549073826],[-85.658036590632506,14.6345456819859],[-85.581108161139653,14.796308059126371],[-85.35342745443424,15.090043701389806],[-85.245171395423952,15.177525742813199],[-85.037795269154273,15.276869246532181],[-84.760221585294303,15.296592496237388],[-84.516251401067052,15.176232125570777],[-84.342163384546069,15.236878714927842],[-84.031012925486621,15.277755526017733],[-83.641320191246479,15.466063852787595],[-83.127513942658695,15.491942634976503],[-82.981161985922228,15.460573810760316],[-82.850651983109927,15.387293270783946],[-82.699014177556421,15.191316112813288],[-82.660092148228742,14.946599652083011],[-82.758097988984815,14.680702419828462],[-82.693316915696784,14.412900497189394],[-82.693972074583712,14.262967564350465],[-82.739049742831995,14.119969951055181],[-82.952378538122332,13.772059420914694],[-83.058797512722862,13.30818042618578],[-83.015870910793481,12.343771003651563],[-83.076872261887345,12.1642719565756],[-83.193905367435647,12.025991285628178],[-83.210279604364672,11.924815379532061],[-83.15875982964522,11.724114652704483],[-83.15620238801651,11.577387679030943],[-83.19635302237927,11.436237858374497],[-83.303467102994006,11.284132601691768],[-83.163891879620792,11.062552803735098],[-83.153157394349904,10.813235553452987],[-83.340258975487686,10.455336552034993],[-83.550898056173281,10.312529450485583],[-83.853479161805282,10.239944502695291],[-84.00818962818272,10.243567863500479],[-84.392280361138788,10.333584593834033],[-84.622771992256361,10.517329099494898],[-84.929636138662559,10.446020041282626],[-85.503143570432513,10.620931640243708],[-85.746436883897701,10.562369035769171],[-85.983678282622492,10.623461428684351],[-86.101638442687005,10.712770269552941],[-86.30949942331597,10.968403649346504],[-86.817191981713307,11.380052893394192],[-87.146128469096496,11.832904341357699],[-88.018309265110631,12.547637217610525],[-88.141278217327056,12.744551462382463],[-88.169473630222001,12.944193455229513]]],"type":"Polygon"},"properties":{"alpha2":"NI"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[2.9262890191686877,51.112737126699436],[2.8668693313718019,51.250263973215503],[2.8555336198624204,51.449107618816228],[2.8989388273221923,51.59249632400568],[3.0484119265472889,51.839648688083663],[3.4302111332591103,52.15124435499034],[3.6337481293825853,52.229345593467706],[3.7323979860498215,52.348400826728252],[4.0568713262703229,52.606631722526444],[4.2293785658306957,53.017322696607842],[4.2641068473462909,53.244412649484225],[4.3331541505149085,53.381734243880338],[4.6034396102752684,53.642548637025627],[4.9612898839805952,53.836015601206512],[5.1131316921809624,53.885579525237461],[5.6788108122341718,53.972211076243802],[6.0020706434673343,53.947262679784821],[6.2268720544970888,54.010666785331729],[6.3726247954550912,54.007705158141327],[6.5944736499870737,54.099934413189878],[6.8713113200071598,54.120230111831859],[7.0938383801540441,54.030219949571219],[7.1976625703615529,53.929095481012759],[7.2767236379742837,53.775931839436396],[7.4766700373705479,53.696576037863977],[7.6419892115145958,53.510146892442755],[7.6957807541893786,53.317413221679509],[7.6841078439883468,52.929234700125939],[7.4982153207386606,52.474267846954781],[7.5179334364020347,52.228141574678403],[7.4860869405606856,52.086726908644302],[7.06377105082447,51.528741117720237],[6.9374938961089061,51.451106892022246],[6.6925391469463476,51.390399811555653],[6.6207609133228171,51.041366444673351],[6.5151037576198689,50.832168676336515],[6.4600462514942674,50.570104404242294],[6.3533429383001074,50.403219386338563],[6.2350003073766809,50.312698673818517],[5.9962482795347194,50.250673294458593],[5.4952645495838208,50.318958442380911],[5.3659922827036377,50.398574674722994],[5.2457033773176969,50.535846824041414],[5.1437039483183007,50.784342713695473],[4.8542635802170579,50.909052044525744],[4.5813916403354433,50.918958012860934],[3.9724215763672821,50.712834578819106],[3.8224398625498179,50.714230330771457],[3.6054498847743819,50.769404360413304],[3.3745573533374125,50.749328783121982],[3.2309567398889865,50.788406420787233],[3.1052188685056175,50.868019143691555],[2.9262890191686877,51.112737126699436]]],[[[-63.542221246888758,17.220041973078164],[-63.700100785167422,17.402497623517874],[-63.746402313452187,17.540634678042377],[-63.750917271072005,17.686255049254314],[-63.652932327891854,17.935866893212786],[-63.466806743417031,18.097971614638933],[-63.275663752186446,18.150380513897087],[-63.079175324949183,18.124406800649798],[-62.648688826725987,17.903460572613568],[-62.491813353031404,17.721420364671918],[-62.439525620688613,17.535710779377688],[-62.461442097157736,17.344029468126735],[-62.554299677091201,17.174915521710108],[-62.704272959947545,17.053548015955897],[-62.885188492316423,16.98677918329599],[-63.076292795228994,16.98590720279299],[-63.542221246888758,17.220041973078164]]]],"type":"MultiPolygon"},"properties":{"alpha2":"NL"},"type":"Feature"}, +{"geometry":{"coordinates":[[[4.4644841677024836,61.524111994198655],[4.4286885853995468,61.875301901356998],[4.4688779604010147,62.069876832917572],[4.5474643313251626,62.196994231270402],[4.657591285275295,62.293422171861955],[4.7553882159319869,62.475166596898944],[4.9036398524500493,62.598534862884378],[5.1788318318312783,62.66685870862775],[5.310530188943817,62.758218170808959],[5.545445985241912,62.847898349108682],[5.888757151587086,62.901854773076181],[6.1564231548777997,63.071231637773487],[6.4368570318610407,63.120210091130147],[6.7937093206707431,63.409520155766621],[7.3224437438725714,63.54692783815824],[7.4108272882551018,63.722249857157159],[7.5189435097341155,63.824322438662961],[7.6522956786547578,63.890088146188361],[7.8541325584163548,63.935517251787118],[7.9601473605979107,64.065049591665087],[8.1226387881235596,64.159072397268076],[8.7949495765958741,64.303494331866432],[8.9881202022532847,64.251667767145904],[9.2035440295686701,64.080547781840224],[9.3396536325648416,64.212000678846763],[9.5635000747478678,64.33099887349087],[9.7325936831365727,64.498899996569122],[9.9873681587227416,64.618082802587367],[10.245123320493642,64.803454695121445],[10.255907571025453,64.994246158070084],[10.313820421848282,65.131341689591096],[10.409520065360347,65.245318271769719],[10.559804949947132,65.353875321030173],[10.685468031851785,65.406368068917288],[11.043823240878691,65.469249640876683],[11.305162640494229,65.444366783788624],[11.278565250107427,65.594431112253133],[11.29695389755477,65.739481181175421],[11.355233977679507,65.911050421748996],[11.478354137840444,66.066029820023758],[11.651964362410705,66.161091261483094],[11.85601189517139,66.192017689444143],[11.978774816289896,66.42314206089199],[12.239850758288416,66.619291518260553],[12.411585716617278,66.696672830151854],[12.631711404521621,66.698796363859884],[12.724241090072436,66.863444563122442],[12.952285780011755,67.068176975099263],[13.229452896381105,67.175376301271882],[13.417841734456024,67.348820113782153],[13.682673256657917,67.439127073038094],[13.921009686965226,67.582388604418341],[14.067032684758296,67.620549743195568],[14.162961600236754,67.707782031253998],[13.337473445318246,67.515609653622903],[12.952390183524312,67.338262315790644],[12.807352473687029,67.321916718154043],[12.663773899491886,67.348152359351289],[12.53388914493001,67.414733635768485],[12.428766009888935,67.515986992829255],[12.331633276151305,67.737325878252562],[12.349679202040383,67.978365324656224],[12.499122980355326,68.244274496499685],[12.878586181101157,68.533614947874625],[13.046048045879889,68.569370429526415],[13.428743533791843,68.736682138471338],[13.877745622180905,68.769452877854349],[13.946579885335241,68.971143404696278],[14.077299534179422,69.113949112849284],[14.338839713053899,69.270018650018159],[14.611529102925864,69.324122632640794],[14.867551992102731,69.470399217993659],[15.019989129632448,69.499999896819006],[15.258476036382504,69.469495794373358],[15.671082798140077,69.725819280109206],[16.019597257116384,69.800938991849321],[16.336516787566708,69.728773033775312],[16.54169832980493,69.555471296066855],[16.633012947388359,69.699179320362404],[16.746884781055208,69.791781999853029],[17.337348514910079,70.067705354077475],[17.533065757957484,70.095056542629251],[17.856474403009035,70.054153845412444],[18.168988058834159,70.233975041715354],[18.42365398299183,70.292083182819937],[18.649419384370649,70.43659377137287],[18.841952170679381,70.650603399130887],[19.016672771876028,70.730236657368195],[19.238984731136085,70.746502569998569],[19.383752852896137,70.71667101318711],[19.686271991509322,70.773336039698307],[19.823064707380681,70.753534952134018],[20.196218830711427,70.609006953806031],[20.41842194263068,70.697546989532214],[20.758464099380426,70.7185096326386],[20.90384284946936,70.70517410363118],[21.06088026694594,70.636196966596074],[21.260554574954956,70.724004088406616],[21.501661425973605,70.733453263298998],[21.616379220892874,70.983286832612123],[21.728967186740491,71.080163309970118],[21.865046402797475,71.139691025164922],[22.877202608066376,71.214942885049638],[23.359904463900051,71.341047491614404],[23.56181598828158,71.313873580765787],[23.697671597475782,71.240736441826996],[23.955175798175684,71.218790798795752],[24.548832600813071,71.488702824595507],[24.695374901330396,71.499389353669642],[25.008891483794024,71.447060689106863],[25.159343003295653,71.527821084722859],[25.577167607979728,71.641740635834608],[26.284060229636992,71.520008433218223],[26.480313177746638,71.410939511525456],[26.747107656647398,71.432080505773285],[26.943020515767628,71.351939509910054],[27.463576170247258,71.571281876017139],[27.6467716459473,71.588926263181705],[28.229181210022094,71.535014190607299],[28.564776515528333,71.444100661385121],[28.718834210086058,71.350260326641802],[29.206758411476034,71.349544498657266],[29.531345201399194,71.219529352252422],[29.775774413216677,71.165548790722156],[30.038816102819354,71.202016938862741],[30.177925238637975,71.189808971497683],[30.480659074385301,71.043646875736741],[30.716960853648988,71.008489626942719],[31.144046349098058,70.850712668369056],[31.308973510656426,70.7192286284494],[31.441704916400969,70.479256825936517],[31.446840157182702,70.228154297129194],[31.335572120933488,69.964689840509095],[31.415109430752352,69.744714306845424],[31.411616748244374,69.541843211428883],[31.327625152745306,69.312141256137679],[31.203698587317305,69.167208864113164],[31.079795483248819,69.095010297129861],[30.940495361532669,69.060950227222193],[30.625824645019286,69.032485495500794],[30.399099977689019,69.061475255531732],[30.12844306747262,68.911176039370176],[29.635289966485395,68.824828400993013],[29.395337994391877,68.625346594703771],[29.092143871619751,68.537854392747093],[28.946232940824871,68.523413752940201],[28.802406836361715,68.551919602838325],[28.536351683580452,68.722076659201662],[28.405901183383175,68.865317534698221],[28.34949550957932,68.999666348722457],[28.334218854139753,69.144572620724631],[28.367947006404485,69.319341434485466],[27.781450561689795,69.556854096066004],[27.202347436613312,69.412295254905047],[26.667581331887817,69.418740794918051],[26.398891344121992,69.290739434399995],[26.257408424028004,69.087782382720775],[26.215068338131676,68.811579611407439],[26.080810965472001,68.617065031386161],[25.780933096043807,68.431614582001771],[25.514254065508609,68.365223601520398],[25.286438189961999,68.181382571964079],[25.043799730170882,68.104119639143633],[24.841610464675441,68.103572037619827],[23.967151551796626,68.281639699363069],[23.790035698337856,68.221009187557769],[23.341963465969204,68.149315614898171],[22.322280471651329,68.22807399162339],[22.137224180203376,68.301797702587379],[21.967377739588244,68.472193135250492],[21.484717488963941,68.763678700074465],[21.352403649676383,68.63042503591538],[21.224002932140042,68.567457743016178],[21.083029536007974,68.543413495847801],[20.760640416413821,68.563801979565],[20.70879259287997,68.304504253212372],[20.605033940594289,68.136251527254146],[20.488489590795393,68.043922999795825],[20.089064009479355,67.871129366038474],[19.910018282377713,67.860275485272112],[18.63942294011698,68.013230231201376],[18.512263304041447,67.830322598231064],[18.215763205333516,67.564499941884037],[18.090648860207679,67.496412483832557],[17.856302999215046,67.468810920988005],[17.370664024403787,67.57317947911929],[17.106476587383682,67.476306997089367],[16.922407669792076,67.259938586473879],[16.931237287871095,67.102220288340988],[16.881236108178477,66.908196227846034],[16.743422508104295,66.688652296735228],[15.974956828094744,66.217058656826666],[15.884452138339213,66.007312973711038],[15.737487904875671,65.875370064028459],[15.314099032527729,65.718042297874618],[15.121636890682932,65.682420292277683],[15.022987850375406,65.462851766341032],[14.918273922412517,65.061878617406762],[14.539077372922389,64.661150318686325],[14.61148632425108,64.479910884353885],[14.638481906312848,64.12383877349761],[14.582164185448107,63.938387783316038],[14.419661508016485,63.744786601204417],[14.233314560183738,63.595226714890579],[14.060229854868894,63.5242841142285],[13.240698963699268,63.571317252156135],[12.974943091521821,63.522228998893191],[12.681784895943242,63.32145833128272],[12.649795886601607,63.252305736780031],[12.717451481099651,62.981842130711257],[12.631770201899194,62.720673383841593],[12.759854624790428,62.489347926433446],[12.798579641365235,62.353938225621867],[12.752262835454216,62.016491633116019],[13.26327024934811,61.673882968031037],[13.355411761695274,61.508561559630543],[13.374262304886026,61.273540420937564],[13.309649394562989,61.095646075348014],[13.031369807461989,60.681217515978958],[13.086692411892349,60.409402352193787],[12.929787089566698,59.876743664368533],[12.813732169781121,59.729377235269304],[12.578947294935963,59.558793279000788],[12.274397000712845,59.439650388100517],[12.286435144512412,59.183528948461941],[12.154072869386964,58.785105683108263],[11.960395993735512,58.542313095993549],[11.782059772949095,58.445686107966068],[11.369127278117629,58.420179725503729],[11.190418761781986,58.495765736260338],[11.044322275596272,58.641754473792801],[10.609652708174247,58.737950180568163],[10.47373367714896,58.605396873419103],[10.347320548647195,58.538592676251362],[9.9229676205677713,58.464628758888409],[9.7552746437420907,58.467877729814283],[9.5537367729521563,58.304270647953004],[8.3028287923933579,57.664644448086548],[7.4625237459002518,57.521322422823566],[6.9554896703102997,57.526881820314024],[6.7421593391792358,57.590587481531237],[6.5210242572766273,57.602455539116697],[6.3824222628650737,57.643005239797489],[6.1616100422510707,57.823939023454301],[5.4067808914771964,58.123867626509671],[5.0927676570443507,58.462859121659072],[5.0191834726296616,58.687531364549649],[4.8174171432689414,58.811670479917822],[4.6916176439531814,58.989965786233292],[4.6365786714313515,59.175560979251678],[4.6730568853656544,59.464042094117552],[4.607669331597803,59.682226846743077],[4.6287747878307925,59.88499328703854],[4.5079224954790265,60.027482625493327],[4.4461213266524569,60.224356729077144],[4.4330994073155852,60.464693494563846],[4.5071894718697054,60.677208063606273],[4.3481510760530471,60.867377086215562],[4.2997631285401576,61.061709504019952],[4.3553694340630047,61.350662135382855],[4.4644841677024836,61.524111994198655]]],"type":"Polygon"},"properties":{"alpha2":"NO"},"type":"Feature"}, +{"geometry":{"coordinates":[[[79.752815970891788,29.42712416231889],[79.804206496003644,29.639302260022482],[79.999393852585939,30.026442788359894],[80.596184209496826,30.567515349630551],[80.871732258622544,30.670815680174009],[81.046455312822602,30.672749482798849],[81.211909965285045,30.793253369982654],[81.572506914026945,30.882183374093959],[82.206044577970815,30.799288027394095],[82.400639453314326,30.676186370057209],[82.568112155623282,30.440773863805912],[83.046575013250049,30.152651361605166],[83.419804909940822,30.03677183788227],[83.740054061661695,29.741075922349211],[83.878693875121499,29.775915489895965],[84.031076161524609,29.770070724495127],[84.389262293462679,29.628471139368038],[84.529710180773776,29.477423043119835],[84.638435754420883,29.251288900824299],[84.911610804923882,29.068689425106097],[85.156206426331053,29.092046491143158],[85.351335251733758,29.053515955071063],[85.516004582794167,28.941963850110366],[85.62168404112937,28.780510513330494],[85.840473675792879,28.75012970032715],[86.061014421483023,28.607843912197744],[86.24566498381941,28.602158703550941],[86.377020055452064,28.552376037895041],[86.519097289082225,28.583743964180101],[86.791687804637121,28.584332916351084],[87.326408141161025,28.327787514123877],[87.561072335485946,28.318890983057404],[87.898090322955369,28.385325351611485],[88.19145293491718,28.363687212844017],[88.37530442019542,28.294033102830205],[88.544587614345147,28.11712836847024],[88.643675352831025,27.818738660922303],[88.632101539796849,27.630468744607459],[88.503495228278481,27.211108938701759],[88.65228224121087,26.819244180500711],[88.639044865686031,26.577410497658637],[88.437264313182325,26.114426308471021],[88.259434816610124,25.95300597468129],[88.075260537606951,25.89593328600909],[87.772142130094622,25.931134276680488],[87.226646706771376,25.864293082162405],[86.925652251487321,25.973794969821842],[86.609142832871029,25.943876764142832],[86.221270734484307,26.092498273848026],[86.030372962005984,26.129743796806007],[85.807810689993758,26.104559591362282],[85.664295186195872,26.121664865869359],[85.531668134744592,26.179104882144255],[85.416311399887078,26.273250060740324],[85.231530766563921,26.250550749729065],[85.094123639249247,26.272309370412405],[84.409560410783641,26.624250076285577],[84.266845011956988,26.76786099762284],[84.202216112552065,26.912886687625928],[84.111014288342531,26.94389135402492],[83.877302184556868,26.880469136421755],[83.533215114420955,26.932998348121082],[83.258943497317816,26.87218441401291],[82.55942488011847,27.050480143310505],[82.366641948667592,27.179842807572058],[81.955344670960187,27.375068243469478],[81.764517082259232,27.375196995170075],[81.540495205550116,27.463220388695127],[81.023681782229588,27.767230398286141],[80.846210008422759,27.937650974523653],[80.513207311664502,28.112176000700718],[80.219734009028727,28.153527693472931],[79.725141263270487,28.491900592588756],[79.604414493475332,28.647320191460683],[79.552193425639658,28.8867858360497],[79.657658542532346,29.262255942592432],[79.752815970891788,29.42712416231889]]],"type":"Polygon"},"properties":{"alpha2":"NP"},"type":"Feature"}, +{"geometry":{"coordinates":[[[166.43660711411701,-0.6925060786011481],[166.40744411806688,-0.50998045628721511],[166.45350352930745,-0.30391890668794574],[166.55792316945096,-0.14802519886977739],[166.73848977341578,-0.030754023602240277],[166.92930355661647,0.010288815920614536],[167.12813605998508,-0.02676272425203069],[167.30690153753838,-0.14150793174637449],[167.4179847581116,-0.30726729375875628],[167.45817342327703,-0.51757395854567123],[167.4231445339617,-0.70041409685531475],[167.30269835740518,-0.89348524397014328],[167.10429850516365,-1.0223684895484377],[166.91640321509641,-1.0500305661720393],[166.70534767378223,-0.99946356839233919],[166.54664914959363,-0.88265432462663085],[166.43660711411701,-0.6925060786011481]]],"type":"Polygon"},"properties":{"alpha2":"NR"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-170.37362316281673,-19.335354079780025],[-170.44071492107733,-19.158368013533703],[-170.43721217296019,-18.969124513176425],[-170.32240134094926,-18.709820227471184],[-170.18172953754402,-18.571636183912354],[-169.99113665170503,-18.486053064484995],[-169.75942304910123,-18.471881232591862],[-169.57587564999164,-18.538144088303643],[-169.43167152529409,-18.669618740564797],[-169.32088075970441,-18.87998055830418],[-169.29554691766543,-19.085639638229036],[-169.36518673797832,-19.323155132772435],[-169.49886228139408,-19.479201277954978],[-169.73077207593661,-19.606719012038614],[-169.97202869341604,-19.632902730510828],[-170.19719824912701,-19.542409560759921],[-170.37362316281673,-19.335354079780025]]],"type":"Polygon"},"properties":{"alpha2":"NU"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[166.09980999067201,-46.229014852232844],[166.02065083637888,-46.103309392037225],[165.97869346935286,-45.910317952079502],[166.06224839354064,-45.543888620404473],[166.15076350034747,-45.372599878326341],[166.5558083287271,-44.872341056926679],[166.88958919822838,-44.568611420879151],[167.43177254026153,-44.216502985850816],[168.08645543361479,-43.668838775469091],[168.74052986817048,-43.452838120609492],[169.41209285839284,-43.132551074974572],[170.05074764581613,-42.676955380525328],[170.51484238356471,-42.457675664026162],[170.7995261652602,-42.169380172575003],[171.09897355288973,-41.479966729524484],[171.21072012149216,-41.378564470775395],[171.47643159339106,-41.235848358149234],[171.55154054697442,-41.156822476518428],[171.70414000043218,-40.703734185747471],[171.9282662129236,-40.397338278750148],[172.4767140973963,-40.047515614117607],[172.60836453952004,-40.01419626307321],[173.1557775253975,-40.000721725316417],[173.28462500786512,-39.917824407222163],[173.35549267967085,-39.759135569750079],[173.26718577564023,-39.267410576482121],[173.35163383675936,-38.956491152298227],[173.51582647753304,-38.778577151553648],[174.00982450069333,-38.51672094309788],[174.15038915693353,-38.352851011027944],[174.30430706798052,-37.824748800112872],[174.20615891501947,-37.462693881137398],[173.8929023858669,-36.919284133128443],[172.70115978459404,-35.481413381335109],[172.56249286760033,-35.066474570573412],[172.24536239208794,-34.646206014116153],[172.20771194583077,-34.452746107538381],[172.23059271887146,-34.306279674154297],[172.29541323926634,-34.172959585574468],[172.43675930057043,-34.035607546964677],[172.62020373503381,-33.963549021994666],[173.00504041964871,-33.931505477746242],[173.15061077188986,-33.941673909521398],[173.28704564647225,-33.993439240140106],[173.40272399859771,-34.082392280031499],[173.56895482229729,-34.32220734554582],[173.65319403820766,-34.391578762787383],[173.82821612190398,-34.500182272052356],[174.64364753901762,-34.86666957912189],[175.01442572536192,-35.415407031387801],[175.12285581133582,-35.523037490900244],[175.23080413943097,-35.567776807562112],[175.56302673955685,-35.596144644347724],[175.73158354254772,-35.689805306565447],[175.95367903162384,-35.943250443315904],[176.07556177022653,-36.310402672256672],[176.31628451223841,-36.71352496495571],[176.43725319452224,-37.126749282422487],[176.55196303792241,-37.245051507309512],[176.88297124516978,-37.395263460379567],[177.11374988317394,-37.445479494652844],[177.31310969186629,-37.40208227593827],[177.83420566776132,-37.08763051566077],[177.98161395109901,-37.056704179838825],[178.29448686934907,-37.068311241418527],[178.44016819192001,-37.096965255986831],[178.74925181205148,-37.240723928331029],[178.93626456795513,-37.394088034512649],[179.01971710778639,-37.569421867243918],[179.03007936568576,-37.763326232629936],[178.8705625886609,-38.157048288618967],[178.787132291386,-38.603178663724883],[178.49134584235122,-39.069643759240201],[178.36565971080998,-39.440664541230184],[178.29021560217919,-39.56131657031986],[178.09990843206424,-39.700429757090497],[177.73953941652681,-39.759754850026894],[177.61785997534236,-39.835485942738529],[177.22907862302617,-40.472723832895717],[176.27358438574475,-41.61905837388575],[175.47387111008888,-42.081875829993017],[175.22891397356696,-42.102997927522331],[174.86640682165685,-42.002554638585778],[174.66004157736654,-42.096462679886152],[173.92107642814977,-42.864240467044823],[173.62556319464161,-43.280050052063956],[173.57900399064911,-43.407866583124616],[173.60653788521978,-43.894463436695624],[173.5317137607914,-44.075524186965993],[173.38428767855081,-44.258711674865815],[173.26114169785248,-44.333794218756573],[173.12168729197327,-44.370601685935604],[172.62605034975203,-44.340346204352443],[172.36665937778889,-44.369506580471921],[172.04481443838242,-44.488527081280026],[171.77990372640264,-44.65406636911689],[171.70745860135651,-44.768390290567019],[171.55576897721016,-45.269870213077098],[171.38532749946316,-45.494093772339077],[171.32398059002588,-45.626155664288355],[171.22399305587331,-46.091598493436045],[171.06855896968312,-46.275383533426059],[170.60407731672049,-46.458911134172027],[169.87544706851259,-47.013418067350564],[169.4039398117508,-47.116080989317219],[168.87629144140439,-47.143248609982351],[168.77146158100967,-47.197502395353844],[168.54547317125642,-47.454852379754676],[168.42296340920743,-47.540076992649006],[167.68258000329479,-47.745950836865802],[167.53295231722981,-47.762054434920422],[167.26888986237887,-47.688433830990704],[167.09719607303086,-47.519735574108914],[167.02972836354616,-47.338646997526467],[167.03311565120356,-46.942057923821679],[166.99684931251406,-46.862997146844158],[166.86032031826406,-46.750810244500457],[166.55277190515989,-46.663704577598999],[166.42457355713847,-46.591188730281708],[166.09980999067201,-46.229014852232844]]],[[[169.00667391086651,-51.995106568466994],[169.2523355071535,-52.003782577297223],[169.39370662320195,-52.047237407730442],[169.5700396118437,-52.179678054422837],[169.68531541659945,-52.337638414084729],[169.73118920270744,-52.527732028219248],[169.70064411834255,-52.720882187959653],[169.59835233794593,-52.887544655389775],[169.43996040667693,-53.002226748566869],[169.12780573187652,-53.063836492509964],[168.9325443591419,-53.031477617842398],[168.67833762544089,-52.858249298491607],[168.56552848694025,-52.697531789398205],[168.52480318686204,-52.456400827471526],[168.60330324121236,-52.2247967853368],[168.78225059205806,-52.058121842734565],[169.00667391086651,-51.995106568466994]]],[[[165.83758524130053,-51.304756265711688],[165.61186050969678,-51.22282857646146],[165.45036384009597,-51.045113561813508],[165.3903452795916,-50.812602114528758],[165.44564842143416,-50.578924209655817],[165.7431585416484,-50.191584037335424],[165.91019498670713,-50.078073491200968],[166.15922171883378,-50.043151075103886],[166.5063493987227,-50.120121208768381],[166.6536901709195,-50.242466665458018],[166.75621390787816,-50.457870709160929],[166.74010932838138,-50.887639325732209],[166.6846555547466,-51.077232571786269],[166.5601021819289,-51.230553076870841],[166.38588985905128,-51.323670135014012],[166.23882300675839,-51.344828723542648],[165.83758524130053,-51.304756265711688]]],[[[-177.21704441723148,-44.157781196708719],[-177.32811106460153,-43.940691597823772],[-177.32328958405861,-43.696887480903193],[-177.20372678777923,-43.484358819664521],[-176.95906817542055,-43.299982849854672],[-176.58059944295604,-43.218785649243721],[-176.10044745880131,-43.247354636593819],[-175.87520068931761,-43.343902893311018],[-175.72288802154372,-43.535888633077199],[-175.67898245651037,-43.727706139763285],[-175.69236358503693,-43.94701622032062],[-175.62489480105734,-44.234525081628867],[-175.63639160707837,-44.379927864585504],[-175.68936154621952,-44.515826103674918],[-175.77929705763907,-44.630655323065838],[-176.04203012380384,-44.79660201826411],[-176.23787028657446,-44.829230425631678],[-176.47516755905551,-44.759808262382123],[-176.89603786059598,-44.507450060006576],[-177.21704441723148,-44.157781196708719]]]],"type":"MultiPolygon"},"properties":{"alpha2":"NZ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[51.520805514718312,18.793467003483226],[51.481727297210263,18.934635642185512],[51.495923772411942,19.128873567526611],[51.583252599106544,19.302952824122876],[51.730464278605282,19.43046216875349],[54.570799023396788,20.387443518461698],[55.087591016800665,21.935119811176776],[54.703648037773135,22.572936793061],[54.713345626032478,23.149055733747819],[54.991321430888171,23.794657521901449],[54.969568474505394,23.969142283464453],[55.019164842856917,24.15979347664738],[55.138555114469469,24.316489488913213],[55.267990379409788,24.398997628860283],[55.302085137874556,24.946052544581686],[55.346467432408019,25.087022266718261],[55.46555804220317,25.243312908058016],[55.718242544326486,25.430393969230337],[55.661317582120517,25.563336072992289],[55.649895056677074,25.810154445201274],[55.581956500146568,26.028296968638557],[55.625748261876709,26.269964382342113],[55.830042720252443,26.578467482259992],[56.205849240204856,26.806034862119571],[56.396568636898408,26.850669885365232],[56.589800557236359,26.818613707523845],[56.755890723634828,26.714785820128306],[56.869350159481037,26.555120138177699],[56.91276684713727,26.364119706742969],[56.872125323609296,25.768608904900013],[56.841277796013657,25.628281672188521],[56.737680311963707,25.44282450563545],[56.758498303459149,25.313380376805977],[57.050500490083294,24.765011438865379],[57.261142942447449,24.514300218586779],[57.365353315698719,24.428611564032003],[57.734083806080463,24.290946923721759],[58.205944505383385,24.208956583466435],[58.414084897837775,24.133605092419639],[58.681678142906961,24.132378955552991],[58.849716124450417,24.062957125356782],[59.132308089897116,23.864629965884049],[59.770960622686729,23.03829977743548],[60.084807007507976,22.934772646580086],[60.19903112815178,22.838349474164655],[60.279647680870582,22.71247036810157],[60.322921678692758,22.518513174548886],[60.268712282842579,22.046771584657776],[59.772197985246933,21.200311357448243],[59.354680826577706,20.850608788081079],[59.430700622392749,20.655540998214068],[59.450373429792855,20.505060554242487],[59.423994978317189,20.355609690522559],[59.353995396880194,20.220956647602868],[59.092550001507277,19.883254484276176],[58.815678466102916,19.729026651786107],[58.674934525671091,19.704128141285103],[58.532896663703823,19.72006672218841],[58.401171528291286,19.775539970641997],[58.288205414102691,19.868799438293419],[58.225464400218229,19.645231618473407],[58.310578656150334,18.988919194217786],[58.279727160804917,18.842135028549297],[58.206729239881014,18.711105605911367],[58.055668215772137,18.580947614694853],[57.828648374537828,18.48219286647473],[57.304627883827763,18.413177652863908],[57.119714823788883,18.335102381153405],[56.974954064575833,17.901750065318314],[56.639550495901915,17.558823307047067],[56.466314208381064,17.495131765880892],[55.780389006472149,17.379731423946236],[55.761933359863143,17.204405342413356],[55.7058619383197,17.069263796379285],[55.434712248445244,16.703559406485784],[55.278947614272099,16.587662419761799],[54.789342947732216,16.465169711439952],[54.486354583487227,16.527671027474796],[54.237569622983116,16.514422167212384],[53.754632427404587,16.281652025300868],[53.142386722348121,16.151895186644726],[52.998612864216888,16.156333575548327],[52.820123933059286,16.225083059580498],[52.654246063169275,16.396113432733877],[52.410766278981164,16.916901546734032],[52.269953869115831,17.102596952966671],[51.520805514718312,18.793467003483226]]],"type":"Polygon"},"properties":{"alpha2":"OM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-83.413352749306,8.020163892249089],[-83.505999896511625,8.1939483433643936],[-83.527092307554284,8.3405670624343653],[-83.487189226981457,8.5334200465844319],[-83.40963667494276,8.659640149444872],[-83.400490160304813,8.8663786185738616],[-83.440093682374027,9.0604499407470698],[-83.431007011976078,9.5379536181458366],[-83.334681903637303,9.7508218254025163],[-83.0510088943862,10.024555935044607],[-82.819332528881247,10.091192800425603],[-82.492140162924215,10.071235372024917],[-82.350120678878454,10.028506505996379],[-82.195865811956764,9.9259905751303954],[-81.972762837396331,9.8396067378171281],[-81.869238985760546,9.742517483889042],[-81.796124712109432,9.6173328929331401],[-81.583204117295381,9.5454757202608569],[-81.302696289015756,9.2972771175793945],[-81.065091490657906,9.3464627540161871],[-80.742773281906807,9.5416737641445106],[-80.369703234741436,9.6594996564546403],[-79.815856444839852,10.036976577845943],[-79.56387375668487,10.097405713330664],[-78.990697167910227,10.021611163105241],[-78.763188428257791,9.9257433584817054],[-78.366628561796915,9.8865943800115978],[-77.849604002704481,9.6782896807477687],[-77.558321416325427,9.4873846475980734],[-77.338803215193252,9.2488043958557817],[-77.042744698047628,9.0322834056781325],[-76.946090521985568,8.9159522350422442],[-76.879228556027584,8.7263387779199029],[-76.894807371303045,8.5171848368268996],[-76.722475963593595,8.1322227552356381],[-76.696369882482713,7.9822431485152254],[-76.755089785277249,7.7372112667254296],[-76.989863051592906,7.3602099333514097],[-77.416618068944175,7.093241927236301],[-77.485037201406769,6.9527306297959761],[-77.621116519991546,6.8156224462703117],[-77.75199945609134,6.7525648012346418],[-77.94403181238944,6.7315931758610956],[-78.129682057718028,6.7849806401728667],[-78.281239119394527,6.904758317526329],[-78.577433649694356,7.2541130074229097],[-78.850461955827853,7.7372567269980692],[-79.151678505037921,7.7118760915047941],[-79.340125249653994,7.7661345526339494],[-79.493210882321506,7.8886945184926729],[-79.60146669970726,8.105653600092305],[-79.730733267878847,8.0326283275488368],[-79.62584538222562,7.8857337875419802],[-79.512988860983498,7.5388454238376195],[-79.534892144225665,7.3489426682552104],[-79.626459966206724,7.1811387816130177],[-79.871035389255013,6.994828966754457],[-80.068371957142531,6.9347665691493416],[-80.278167018094436,6.8017914583592498],[-80.83011044333449,6.7205701653178433],[-81.079448406201394,6.7784701845743154],[-81.28040790618482,6.9522800455954199],[-81.427630245414505,6.8649910526818845],[-81.572935890579842,6.8339579449238936],[-81.983030186089323,6.920008005643723],[-82.171102050445995,7.0705073310509379],[-82.323542506297727,7.3286523958410292],[-82.356616766994449,7.5139505465054901],[-82.309406908426396,7.7184440713353411],[-82.469178752462838,7.7850646396021936],[-82.571132459197855,7.6773459679775229],[-82.699424385422702,7.6046176556509515],[-82.843352421646102,7.5724781762334263],[-82.990395850174835,7.5837234363401764],[-83.127762936295085,7.6373751781495995],[-83.243503718409514,7.7287660808910408],[-83.413352749306,8.020163892249089]]],"type":"Polygon"},"properties":{"alpha2":"PA"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-81.759317120866228,-4.9361115613665172],[-81.819142018452865,-4.7994906854488875],[-81.831710416036486,-4.6016072715696703],[-81.70461034612326,-4.0720276827508579],[-81.639886483136181,-3.945673288468432],[-81.136814098041654,-3.3630927550103991],[-80.617901846531012,-2.9831741428313601],[-80.444557241936806,-2.9027464940674639],[-80.3015375957619,-2.8886955193284956],[-80.160428685297305,-2.9159044235807481],[-79.912061228479814,-3.0795305437748395],[-79.795803266369774,-3.2779451575348926],[-79.689997463237489,-3.7758645135193909],[-79.686125724191683,-3.9549812434058715],[-79.442392381063243,-3.9952557355652667],[-79.159539711671002,-4.1895461879363962],[-78.909205088836117,-3.617662129539275],[-78.799011650128293,-3.187827935099778],[-78.677367918030214,-3.0238963618118375],[-78.467778095951957,-2.9116090229776197],[-78.198934636615462,-2.6137823368222355],[-78.076289625668181,-2.5307925109723053],[-76.893908319048592,-2.100363012395936],[-76.432270238145975,-1.7624868323895597],[-76.041610000482081,-1.3086504138805015],[-75.835739146555142,-0.66690394762162852],[-76.091574076818929,-0.30557797620866423],[-76.124298213413169,-0.16507912712077652],[-76.115547756228111,-0.021085289718226319],[-76.066051121171199,0.1144170306637575],[-75.979928565684588,0.23014818959390715],[-75.864349205401609,0.31647435704871757],[-75.72893423414321,0.36620946808657473],[-75.509387395464998,0.36897934738618993],[-75.272577678748974,0.45567443938172947],[-75.088199133757712,0.44676375440738819],[-74.938206953555934,0.40743776060311843],[-74.767866062530445,0.29909530951326468],[-74.523636834153294,0.21505100768549973],[-74.035613287615462,-0.25799124734332674],[-73.887521053384873,-0.5571922648154688],[-73.608477506452815,-0.74387557762278611],[-73.392855465816112,-0.82916784631216511],[-73.286106291651095,-0.92207534317304618],[-73.089341298806914,-1.1986316370563204],[-73.030152914725747,-1.3534654307517939],[-72.883586722155243,-1.4408980168355887],[-72.784070206788357,-1.5486393514906776],[-72.681105856628236,-1.8554560838603884],[-72.328424954671547,-1.9084864145462712],[-72.190096408257887,-1.8543108012351524],[-71.96692231051054,-1.7012898665266343],[-71.782248951369397,-1.6538856707216396],[-71.593257243768491,-1.6790559272655361],[-71.322076083833352,-1.7976911951138788],[-71.078122807053248,-1.7192740756929203],[-70.879980417719366,-1.715023531633928],[-69.866291893041904,-2.2142975682820243],[-69.708531390197194,-2.3423845099866352],[-69.590309106038859,-2.6093958666357113],[-69.580921241671589,-2.8415801197684494],[-69.627005897411863,-2.9740390302853399],[-69.947227152915218,-3.4784383177695615],[-69.827338784952047,-3.6013025703920292],[-69.758178170868746,-3.7376935434323326],[-69.557882460940874,-3.9469191391933727],[-69.494923749199344,-4.0722275805344408],[-69.477558638732347,-4.3737035491593801],[-69.566306225928628,-4.5929535242753303],[-69.704411041905558,-4.7232591632182697],[-69.957462014694741,-4.8236074398949658],[-70.092929410683936,-4.831296801929108],[-70.373363254196121,-4.7825817750548776],[-70.584036814018702,-4.6730077735568587],[-70.828821584214126,-4.8288245060831834],[-71.692671838585142,-4.9947146016845805],[-72.436558023786588,-5.459238327252506],[-72.521244434965666,-5.8333254250731112],[-72.687851860140924,-6.1326633214699147],[-72.637747290944574,-6.477018120841679],[-72.679212281814699,-6.6653705385398192],[-72.755806583944775,-6.7885432784799455],[-72.929833298879629,-6.9567214139136944],[-73.253305754463739,-7.126470087643014],[-73.223135999209134,-7.3588444082882836],[-73.280625078413394,-7.5462496260184784],[-73.234584858295179,-7.6653688739117483],[-73.217381706538802,-7.8431895147756459],[-73.127313008683785,-8.0260116871628728],[-72.980024419809851,-8.1552218325503638],[-72.867752598007542,-8.3769745400981179],[-72.576543696146615,-8.6902349743156684],[-72.502630235229219,-8.8272270672739026],[-72.474743715588644,-8.9762600801822217],[-72.215713283619991,-9.0390764703476894],[-72.087668612159945,-9.1040196236682629],[-71.983357621999957,-9.2026718835733217],[-71.791739775883713,-9.4953073459545045],[-71.455619563325413,-9.4879301193946866],[-70.837164515582515,-9.0346350266387194],[-70.66368111164212,-8.9530485699994635],[-70.424884131809591,-8.9517212418496737],[-70.212706717085993,-9.0612954361582148],[-70.095157055487704,-9.2127382253818162],[-70.043237209667865,-9.3972840759440608],[-70.135718323478201,-9.9361009133158422],[-70.120702509214254,-10.456960884204223],[-69.982063235842503,-10.430595258117782],[-69.500897327513101,-10.458089850437599],[-69.359664883729124,-10.502543987763623],[-69.23764382004417,-10.586410748281654],[-69.145534297025094,-10.702335764010758],[-68.211276151133276,-12.343533878069954],[-68.187061218409781,-12.541158182575067],[-68.221036837747107,-12.687019208052062],[-68.335767166130751,-12.952845358609348],[-68.476538541434195,-13.100165221045676],[-68.475189265744518,-13.436595728227442],[-68.522646567558738,-13.677262192336276],[-68.388720375211221,-14.038304454880638],[-68.382088879449242,-14.273779156756893],[-68.455707660353298,-14.447957038259567],[-68.809101709862716,-14.888507959416287],[-68.703526432883706,-15.063801009205951],[-68.673138254601554,-15.256862022174328],[-68.719079169225253,-15.446823672175642],[-68.855804943214835,-15.683689400439706],[-68.825561831155241,-15.759456691354632],[-68.614048277035778,-15.861717692344154],[-68.418114935956581,-16.074286891639829],[-68.349881081662318,-16.255292252275574],[-68.355458163334788,-16.448651233475687],[-68.628118458331429,-16.951418681575607],[-68.850029226911857,-17.133601708019228],[-69.032753827402317,-17.354066469750638],[-69.012673305518533,-17.544702749072869],[-69.066156995036067,-17.733910008318379],[-69.302820356570194,-17.984502171131101],[-69.350723040033699,-18.205536482864691],[-69.589114007096597,-18.574869630598613],[-69.715182037105762,-18.659016586585338],[-70.090490336329054,-18.815654085291769],[-70.474875728186234,-18.842142781350905],[-70.667879837550259,-18.778565153606351],[-71.667525788285189,-18.057379554572538],[-71.785336578911966,-17.903328040411225],[-71.850956010064095,-17.695723309150395],[-72.385455793402301,-17.42041254043329],[-72.700320821809967,-17.164100179302739],[-72.979329592665351,-17.078524558345475],[-73.598399702455765,-16.766676012226739],[-73.972659922375115,-16.637732999436444],[-74.370687390654069,-16.367252841722362],[-74.622730748059411,-16.26679829612862],[-75.354009296227858,-15.844705768551988],[-75.485281900849472,-15.735190263672179],[-75.868289832958567,-15.283876898524019],[-76.296359111720179,-14.977573420305601],[-76.564900593075606,-14.558512376393359],[-76.724202468068981,-14.37882362632771],[-76.849567340259199,-14.023873058044876],[-76.875737345703556,-13.882517232994372],[-76.860605790605291,-13.73955788737131],[-76.741679995549717,-13.521848166570285],[-76.870674335716643,-13.341833700491309],[-77.232841673607808,-12.669963065378194],[-77.588559004673158,-12.304884192727471],[-77.696795208302035,-11.897882814805293],[-78.062468185634202,-11.54368767509848],[-78.206311461344484,-11.026581013935418],[-78.634902359724833,-10.308097171842221],[-78.900987659801942,-9.5846111685877204],[-79.12665212787428,-9.1619996327215247],[-79.238349355195666,-8.8080289577178661],[-79.408107918711011,-8.526915577659631],[-79.772286693318122,-8.1421983327194383],[-80.067310266517339,-7.5255587625940334],[-80.425738833428937,-7.0544488948039223],[-81.394174660487934,-6.488211973618113],[-81.572966920837146,-6.3097509013531461],[-81.629256437053556,-6.1678786247649722],[-81.662963159008129,-5.8421600622238712],[-81.615493231945678,-5.6604508591152465],[-81.527740551529774,-5.5329676815740934],[-81.64784552775437,-5.3055723766979872],[-81.662327022722195,-5.0899772216723518],[-81.759317120866228,-4.9361115613665172]]],"type":"Polygon"},"properties":{"alpha2":"PE"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-137.41929795475832,-18.620417780597549],[-137.54501252014188,-18.412127186838546],[-137.56694185714179,-18.266854100513438],[-137.54570098438847,-18.121478756123974],[-137.48312385289105,-17.988552957103487],[-137.38461341359201,-17.879553604899964],[-137.25867512300738,-17.803891775710436],[-137.11618257648303,-17.768100161934584],[-136.83977218756792,-17.810565213461008],[-136.61528023341788,-17.989229315840273],[-136.34832383776617,-17.975774244681144],[-136.07475752896082,-18.088393831895083],[-135.86558113427239,-18.286973964140209],[-135.79479349664726,-18.520836828173152],[-135.82418786978474,-18.714813781529735],[-135.94576772199269,-18.90412653378791],[-136.1080709881725,-19.021546700104746],[-136.30343847762646,-19.065834280022418],[-136.50050992155127,-19.029880258552055],[-136.81948599347143,-18.83663704405954],[-136.9668952808334,-18.860380690840891],[-137.15996502524106,-18.823550649240833],[-137.41929795475832,-18.620417780597549]]],[[[-139.01781713101167,-21.004231489813925],[-139.0679463338013,-20.796634451481463],[-139.03490146952669,-20.608172329062821],[-138.89899932767926,-20.412375997696952],[-138.71075770193687,-20.299278872013623],[-138.51904706791294,-20.272256113737662],[-138.33144274056988,-20.320087202214292],[-138.17607275949402,-20.435600689059672],[-138.03963929194703,-20.659144053747173],[-138.00613709935979,-20.854758717034969],[-138.04494962524893,-21.050273409955871],[-138.15705762506022,-21.215089075753035],[-138.35188729016917,-21.340884172645154],[-138.54543485987992,-21.375376803832648],[-138.78148005068084,-21.310333299882014],[-138.93004176005462,-21.1815706428144],[-139.01781713101167,-21.004231489813925]]],[[[-140.71145429525421,-9.0463317539755348],[-140.75217322074209,-8.8649583830921426],[-140.7091857868046,-8.6254530323881831],[-140.59802044799943,-8.4488831703294611],[-140.41483933448711,-8.3195296830778638],[-140.26680530016662,-8.2836175186239398],[-139.9985659312224,-8.3052106232232124],[-139.76650267954554,-8.3955268680935617],[-139.63850693343394,-8.3633172294236662],[-139.48561770777536,-8.3701021498296413],[-139.25523041460545,-8.4609871824354226],[-139.0932992010396,-8.6187144015229364],[-139.01901824079013,-8.7955631891624613],[-139.01863330965639,-9.0072243549921129],[-139.10093256747911,-9.2019183284984649],[-138.91378424103257,-9.2078595200775961],[-138.60918363154354,-9.2923015921196814],[-138.45370137795661,-9.410314616586863],[-138.37352189847681,-9.5332972732686887],[-138.32884201093023,-9.7722681616009837],[-138.40441051532397,-10.007359527582825],[-138.2202942441001,-10.169072481988001],[-138.12906849882745,-10.397341755797822],[-138.14225754921699,-10.594290819167089],[-138.21313527267282,-10.764156438765793],[-138.3613709719244,-10.922560632665595],[-138.56974930776661,-11.017998085849898],[-138.76651516029054,-11.025787948835736],[-138.95102873143642,-10.956996781177466],[-139.12138895621422,-10.780585312788375],[-139.18832855688015,-10.505592847900042],[-139.40993012641346,-10.425837752088132],[-139.57566926648823,-10.242952053492356],[-139.62499482031473,-10.102272525909276],[-139.66193430423255,-9.7063510383154536],[-139.81595740220305,-9.8571274747317119],[-139.99549285772127,-9.9333251000440903],[-140.19052137073322,-9.935038868913928],[-140.37136813765113,-9.8620080207282115],[-140.48093947902774,-9.7644790193617208],[-140.59394279387979,-9.5892120673551933],[-140.64360414218157,-9.381662476810666],[-140.62375693280936,-9.2200189822765939],[-140.71145429525421,-9.0463317539755348]]],[[[-141.33724548112494,-17.717282295642853],[-141.28058905272721,-17.576206963746582],[-141.15046501800404,-17.391839925854111],[-141.0054808848987,-17.281585862637368],[-140.8291581145935,-17.203258192865285],[-140.68504545037538,-17.171237565229696],[-140.53786366581173,-17.182696311883159],[-140.40044335097639,-17.236635515184421],[-140.27505194737108,-17.334804372138088],[-140.17246976698777,-17.497334315896225],[-140.1387764555119,-17.686553361216738],[-140.17895041351164,-17.87450319348283],[-140.30847371728103,-18.05264494555594],[-140.20377218167391,-18.240972364030824],[-140.17250348641997,-18.429824377532061],[-140.21449060013504,-18.616585981156135],[-140.32357941877464,-18.773883291858418],[-140.52918447527136,-18.894642607009185],[-140.71969512474604,-18.913310942315167],[-140.90325901681703,-18.859023634092118],[-141.1438894323706,-18.698991392857167],[-141.39215270183828,-18.333803805032286],[-141.4595683343056,-18.175274179085175],[-141.45839557444631,-17.938487503095537],[-141.33724548112494,-17.717282295642853]]],[[[-142.2690464986276,-16.533621485786426],[-142.45565802144665,-16.601203383342906],[-142.65386042564165,-16.590860255310826],[-142.87056400191921,-16.47200010863979],[-142.98581291538136,-16.310417741804901],[-143.02763228173959,-16.066821118192141],[-142.97065382701578,-15.84170809484899],[-142.85853019106636,-15.672732639043936],[-142.73504307975108,-15.583151219379898],[-142.52300214323935,-15.519860913773273],[-142.33244055760326,-15.540898613334839],[-142.1638046797425,-15.632103017949476],[-142.04188823516347,-15.780064780794445],[-141.98461601484163,-15.963029792483525],[-142.00040847904802,-16.154097583587287],[-142.11429138798314,-16.399190072988123],[-142.2690464986276,-16.533621485786426]]],[[[-143.96865708167093,-17.006941338743861],[-144.14032324327181,-16.829968391306966],[-144.20654602957563,-16.592474732997026],[-144.18773407670147,-16.444750199251818],[-144.12624953482364,-16.309118055011414],[-143.94527699604956,-16.141673630282057],[-143.70628750943897,-16.081070656321351],[-143.32932316849846,-16.132158708109937],[-143.15775584885759,-16.209464534048994],[-142.96583197001308,-16.399001306662228],[-142.89483916252863,-16.581048991959548],[-142.89904054529458,-16.776404275531529],[-142.97779446064177,-16.955231452588386],[-143.11907318898986,-17.090219079254616],[-143.25335718824198,-17.149939586277206],[-143.44821853886171,-17.164444498248283],[-143.73309047634507,-17.124939940672618],[-143.96865708167093,-17.006941338743861]]],[[[-146.11283504804703,-16.107906544237455],[-146.0664339012215,-15.868119252520197],[-145.95006674749513,-15.710270653659036],[-145.78194203658032,-15.609317316871119],[-145.62749746101352,-15.580707917867301],[-145.48074003138683,-15.373578375389327],[-145.26372765679795,-15.268174340463634],[-145.07004979024705,-15.26554809777646],[-144.86598689590139,-15.339906103305479],[-144.64553047060176,-15.564394227588847],[-144.5661834376001,-15.736251436196937],[-144.56257781277358,-15.969461691818568],[-144.65143988148151,-16.191664467126103],[-144.79130852409483,-16.323378169128684],[-144.98543730569358,-16.394531069206813],[-145.02925123521362,-16.557584707340208],[-145.11045894476339,-16.68054995938477],[-145.22398460244503,-16.774502449232862],[-145.35996730365088,-16.831281409010828],[-145.62395695146662,-16.830617886153306],[-145.83886973313531,-16.715605222041592],[-146.05689987104952,-16.387104752621934],[-146.11283504804703,-16.107906544237455]]],[[[-150.39631923222902,-17.623868888095796],[-150.40824622751148,-17.443309352018698],[-150.36335539389924,-17.277121138385855],[-150.28808870202283,-17.152125287047884],[-150.17994137809831,-17.05418043478047],[-149.95258150480095,-16.972357182412054],[-149.40956307174972,-17.00647387246233],[-149.11083528290419,-17.100750278664911],[-148.80195715618643,-17.407156498039178],[-148.67999669887692,-17.644689840028722],[-148.65712859800993,-17.88920750109304],[-148.72537080521477,-18.07422039190276],[-148.88971473530384,-18.267783632734471],[-149.06825498758113,-18.348956212719038],[-149.26428172982352,-18.355211270705624],[-149.76138275742005,-18.200212887851769],[-149.96080250320443,-18.056725147431845],[-150.13592574368548,-17.9859010050689],[-150.30665478969999,-17.825291075067113],[-150.39631923222902,-17.623868888095796]]],[[[-151.98096972432825,-16.792146050593672],[-152.00685567725384,-16.546758287979131],[-151.9764717435983,-16.406266552465699],[-151.90669300155764,-16.275876950707381],[-151.80182469751327,-16.171602505638958],[-151.67104092928525,-16.102565363059437],[-151.42715945330878,-16.080586241600614],[-151.19489683032549,-16.17861239542005],[-151.02228817904327,-16.346204470616993],[-150.88371109622244,-16.728009758677302],[-150.86803849588003,-16.920682396549665],[-150.92662288135472,-17.104900326091666],[-151.08997020401165,-17.281794558979414],[-151.30647036702621,-17.366760381962912],[-151.52586616894953,-17.372534338280829],[-151.71093789555749,-17.309703631688187],[-151.86560116595925,-17.188094595053027],[-151.96137338867271,-17.016230793281899],[-151.98096972432825,-16.792146050593672]]]],"type":"MultiPolygon"},"properties":{"alpha2":"PF"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[154.05674405447269,-5.2378911406639812],[154.04600309089633,-5.0358371074545305],[154.09148680405849,-4.890575955957452],[154.17856954515568,-4.7657313865910904],[154.38825729450616,-4.5779292709753374],[154.52725525469523,-4.5253778365819999],[154.72479957803191,-4.5227228442670526],[154.86515965765395,-4.5715197114002946],[154.99719964352897,-4.6661653197469821],[155.14096490645184,-4.8562446474308603],[155.20643752423558,-5.1135188794710453],[155.43669003925913,-5.2568124285076676],[155.61203909511957,-5.5240726352946812],[155.75294453540636,-5.650208901395068],[155.83932943391113,-5.7823383043013958],[156.14471939854559,-5.9984693528877848],[156.25895802549846,-6.137019464687623],[156.43017120366008,-6.5220905415425268],[156.45590041541601,-6.6589826683062299],[156.39228584141688,-6.9537433542173845],[156.29885945980715,-7.1181231802594347],[156.19206847042693,-7.2119912298891631],[156.06317376741174,-7.2720052423255428],[155.61682652035921,-7.3546973499048525],[155.4313069960109,-7.3206449881189508],[155.12252133530583,-7.1788461447397438],[154.86175197687405,-6.9269180983917824],[154.74792254893302,-6.6355684353081674],[154.33233138774176,-6.1910888822848689],[154.05674405447269,-5.2378911406639812]]],[[[152.74491179659077,-11.522796948009269],[152.70570206931652,-11.281882360604234],[152.78521424203245,-11.051111791580697],[152.92243782489021,-10.911185861647152],[153.15161409839689,-10.827188975996275],[153.34675944331639,-10.845296209809021],[153.6845966385996,-10.980914385780968],[153.81225513414483,-10.895043152837111],[154.03557967782285,-10.816084604192085],[154.18684923669068,-10.81896046649957],[154.41489631598708,-10.8799420238827],[154.54829851410608,-10.939304803855642],[154.65886262025006,-11.034675997314022],[154.7371596893648,-11.157922316030398],[154.77651252042779,-11.298533271494124],[154.73339080093794,-11.592855348992412],[154.66205990546774,-11.720623446497868],[154.55679341926876,-11.822268664152507],[154.37955799614625,-11.902531301798264],[154.14121893520627,-11.908642271369747],[153.95683789077853,-12.045647965481129],[153.6081558431012,-12.127334700534433],[153.40604186553955,-12.107964642572805],[153.00783285380788,-11.931595195011962],[152.87379886908374,-11.798319914199727],[152.74491179659077,-11.522796948009269]]],[[[140.41026071202592,-6.9530923066093582],[140.36346957623152,-6.7100068952099097],[140.47497655810074,-6.2522187331091477],[140.47609301229335,-2.5608480510253702],[140.51216211833423,-2.4176235786625568],[140.62183649307786,-2.2547382687894175],[140.74097797999801,-2.1674483152514807],[140.8804235443306,-2.1187742514991825],[141.24488084946771,-2.1327118618973588],[142.72363897369388,-2.735566823996336],[143.03721877404146,-2.8369055823799769],[143.69656739864652,-2.9679068699768081],[144.23017954632553,-3.3118381793955671],[144.5502726249488,-3.3307851105077946],[144.7258955549508,-3.3914634352680162],[145.29462156771714,-3.865358262800819],[145.56738798768936,-3.9430288397039548],[145.76307540572537,-4.1024722355936998],[145.9836574305061,-4.0425819326271242],[146.22547925879724,-4.0962733311882262],[146.38151185305989,-4.2196508163711419],[146.52990936726619,-4.4894505240212057],[146.55310533850172,-4.6334300350089697],[146.53376880931887,-4.7779784743121869],[146.46428196212648,-4.9537311252832286],[146.37109135764152,-5.080488945962129],[146.53131593931283,-5.1322907180342927],[146.59282766179581,-4.9895957645685085],[146.72565542676861,-4.8497616965061745],[146.92711594381626,-4.7345832450709437],[147.1207733366181,-4.6912171838454322],[147.26880176081042,-4.7104672188437098],[147.40455045675708,-4.7725580375295449],[147.55611500834721,-4.8945950287587339],[147.64704634763538,-5.0165715201367558],[147.85330746936,-4.9951463068751982],[148.11786510164853,-5.083653579410143],[148.2533245273753,-5.0050745189749808],[148.44338852105767,-4.9721544766243753],[149.01049731717575,-4.984977319067017],[149.3919236665941,-5.0738048448557524],[149.52765379061501,-5.0550798005850206],[149.58850398700957,-4.8327517049665385],[149.67177171799716,-4.7053316291021483],[149.87873699886688,-4.5589086160242145],[150.06619809932818,-4.5126208165352111],[150.25721964970151,-4.5408164165583136],[150.46549839829581,-4.6672755667467545],[150.56985342757088,-4.770424782226403],[150.65368887156575,-4.9443596128324971],[150.78272084027856,-4.7611019779018546],[151.09170403146749,-4.519548665799431],[151.04479511349402,-4.3174714210758491],[151.09653017966968,-4.0770707074184287],[151.23175885623672,-3.8554993769466051],[151.46377379816983,-3.7186777327052756],[150.84265830491415,-3.284040489710307],[150.56601927552219,-3.2050641898910897],[150.44114485010596,-3.1345087013493811],[150.12321436967284,-3.1581832685269906],[149.94409681631038,-3.10814012057434],[149.6248565342199,-2.8430666560048561],[149.53168039609784,-2.7284089098176318],[149.47608009437258,-2.5915264860951668],[149.46825846751904,-2.3952525657990051],[149.53664626080334,-2.2111120233734938],[149.65644536981031,-2.0788434013684642],[149.42691637156904,-2.0123176657986805],[149.18993595291056,-1.8224404397377116],[149.06753863446409,-1.6162457900797567],[149.04835080503241,-1.4246973803817871],[149.13779797601299,-1.122176697314897],[149.25776569062074,-0.97208027139998809],[149.47207433010789,-0.86551146769719445],[149.66415615124782,-0.86043568123550052],[149.9314844723614,-0.9611489343430053],[150.09693989808082,-1.0965840758454413],[150.17870148404165,-1.2205334164055881],[150.26218080297417,-1.6170611100479093],[150.23297580540378,-1.759462964691346],[150.16291611201339,-1.8888166842672631],[150.3743838979529,-1.9065869002226068],[150.74168916130446,-2.0808293332495542],[151.01656881356337,-2.1112321370460463],[151.52985244393068,-2.432624390031437],[151.70947540887309,-2.268672699466006],[151.84686875785656,-2.218946446658058],[151.99277106988129,-2.2110630521451435],[152.1347225019399,-2.245695746232069],[152.31231129595142,-2.3460826955172829],[152.47543150633655,-2.5708760201854872],[152.67662429004969,-2.5445165321559835],[152.8621606592661,-2.5958249947606782],[153.01456212850982,-2.7134264182606329],[153.12480399026677,-2.926358253220338],[153.1453247301564,-3.2479727482082259],[153.06844545490787,-3.4874724155280412],[153.294229292571,-3.68440649309591],[153.51862427151818,-3.5598547940840009],[153.68661723461986,-3.5420535954682606],[153.83138115240962,-3.5707817837817322],[154.03432227223428,-3.7069302048263291],[154.14849728636636,-3.9229986125507472],[154.14392375741946,-4.2003160449188606],[154.05042367880981,-4.4221524988071668],[153.9473851280278,-4.5246798072290506],[153.81921108444297,-4.5932378590581475],[153.56995870136217,-4.615914642358069],[153.44713032329403,-4.9309386606808081],[153.21669961612812,-5.2120475310831988],[153.09407110030185,-5.2892807347635511],[152.95444421880427,-5.328101535988611],[152.80954914918166,-5.3252485649850811],[152.64682385353993,-5.2673048491126693],[152.60623981202298,-5.5443915294235806],[152.42950210590655,-5.8125632991574294],[152.04744364884755,-6.0303142046030747],[151.7859998315131,-6.0643558316575188],[151.55446683863124,-6.2995162122790109],[151.43979229886574,-6.3732647734812877],[151.04310879092046,-6.5173881966681009],[150.53637563169684,-6.763999728802073],[149.61117469050046,-6.7884773548173927],[149.41538276037232,-6.7303364654615407],[149.25379061141976,-6.6079997339874117],[149.07126281846951,-6.623287523922933],[148.93463853093925,-6.5894419135348929],[148.51362527738689,-6.3286955252055899],[148.30142756963045,-6.2584311334716212],[148.35267207527997,-6.5917630651345629],[148.23212393795487,-6.9719003574256382],[148.13942189276838,-7.079834587296709],[148.02007186926608,-7.1572880510644055],[147.72838471903964,-7.2083681442963989],[148.01812960087577,-7.4671503788426001],[148.4875478213271,-7.7577705464284321],[148.59138828507679,-7.9194771437926699],[148.68484855927096,-8.2351270344606231],[148.84547588230905,-8.3874727300990575],[148.93557273926393,-8.5607897021895827],[149.23375390264977,-8.5327282985409294],[149.42280938755042,-8.5847398798438164],[149.60760082629622,-8.7443871711073484],[149.72166663393557,-8.9797488174387503],[149.95572011746745,-8.7751347792296954],[150.14006496137009,-8.711315568440483],[150.33480020575243,-8.7228956260649326],[150.55826824915883,-8.8347241979313704],[150.50527008696119,-8.5213613674542241],[150.54715616178632,-8.3234513044064808],[150.66403055291804,-8.1583355479887167],[150.89967597206171,-7.9690040751159597],[151.09261169987533,-7.9197453649617797],[151.24163341536519,-7.9348240945302688],[151.37950246756523,-7.9933643731288297],[151.49384657609244,-8.0901128336749757],[151.5744045739317,-8.2163873313261817],[151.61394723798719,-8.3608560735936006],[151.61562665711764,-8.8939392847193357],[151.51473563530215,-9.1158640724491686],[151.31329030821891,-9.2665339153658834],[151.37305090257047,-9.4371606639974956],[151.53853027195822,-9.5106456332358551],[151.71195585703003,-9.6790123957846372],[151.78549005329447,-9.85372279466322],[151.78870638353879,-10.043250251028592],[151.69769428961217,-10.373073806581434],[151.62733092757134,-10.498834034170056],[151.52402261890251,-10.59930308871332],[151.38793310381431,-10.668642113817043],[151.35067224506355,-10.823922534070222],[151.24909777336961,-10.985308249324552],[151.13698854187331,-11.074727853020677],[151.00410925098399,-11.12864852961707],[150.81365479246776,-11.138125956448878],[150.63680945184782,-11.086470329007788],[150.4011076375958,-11.148075588766488],[150.24636661815333,-11.149267217799649],[149.79332802355546,-11.024144010516405],[149.5338122516624,-10.830335231376203],[148.87061627790186,-10.750233027856455],[148.62918573945936,-10.676386325779447],[148.35766835881864,-10.686144441285201],[148.1248220046887,-10.613321520339898],[147.562992331038,-10.525616639827682],[147.22199352487254,-10.28673256311945],[147.02231321032383,-10.00993736147384],[146.65638959363835,-9.7336521223816419],[146.4854875455257,-9.4564023873221288],[146.25048475422832,-9.2754685941404098],[145.71594527154778,-8.4929265915157224],[145.58295691951602,-8.4356249271165549],[144.82466535544233,-8.2790811280022361],[144.60750647359006,-8.1459000126571564],[144.33201047704682,-8.2528582023147674],[144.23842943263972,-8.3739587328683243],[144.09010108248629,-8.4740416062121025],[144.10535257763897,-8.7268874599943587],[144.0684721157605,-8.8714038999911526],[143.99060593654309,-8.9986125004543069],[143.84025503313381,-9.1193237934980917],[143.71072074001785,-9.3230524035597728],[142.82675179995206,-9.7942253401290529],[142.57046476892438,-9.8216901783246549],[142.16343115635058,-9.6789072544542449],[141.61106337001047,-9.7111513744759268],[141.38720944428547,-9.6707760809545373],[141.12380667659639,-9.720975487548559],[140.94224107972443,-9.6831381924862896],[140.62587274877635,-9.4751734510414405],[140.53675334163245,-9.3567572805651604],[140.48626095859041,-9.2174189247018834],[140.4752584687233,-7.091044745704882],[140.41026071202592,-6.9530923066093582]]],[[[152.25558910171748,-9.436872145986273],[152.14173063921982,-9.3417760029758004],[152.06078594145956,-9.2174579358966291],[152.0198804166763,-9.0748614284358666],[152.02261490064311,-8.9265389807240112],[152.0687486819221,-8.7855471365531432],[152.15422069123582,-8.6642971405874505],[152.43127156892893,-8.5011837640664112],[152.65291714466491,-8.4600639590631008],[152.88019991418957,-8.4724143012580821],[153.019357365742,-8.5134250681857697],[153.36135085948038,-8.7906344747068381],[153.46448116399637,-8.9592025603197634],[153.48941152058478,-9.2040355510952949],[153.41831831871335,-9.4232621513061794],[153.3030111505482,-9.5786993197388774],[153.13731393202735,-9.6787037603157646],[152.89418471007198,-9.723351405251865],[152.74535264315492,-9.7089187725824075],[152.25558910171748,-9.436872145986273]]],[[[146.11039466988291,-2.4234680911235795],[146.03944550313116,-2.2422241188219836],[146.04307579623648,-2.047622004932137],[146.09484322018457,-1.9106941767051604],[146.2454373356328,-1.6893202651536157],[146.45984383573989,-1.5144589478922401],[146.79987114272095,-1.4525828991398186],[147.49752927434506,-1.5145055620997834],[147.68584339815999,-1.5740606997758944],[147.86888471350832,-1.7488175141719622],[148.08416353597713,-1.8131155101671106],[148.24344295583418,-1.9433607256380925],[148.34676482094889,-2.1129002968277359],[148.37607559867214,-2.2593377645664532],[148.34596910119447,-2.4555842239474486],[148.24182642162424,-2.6389093276664424],[148.09215532104,-2.7698049497715167],[147.90330498917993,-2.8320195332123532],[147.59770005239406,-2.7960142259986132],[147.36868223017871,-2.6538113295404449],[147.23790848697308,-2.680663397158475],[146.81791435780028,-2.6654811839431636],[146.58618184624694,-2.7097393915040819],[146.44570244314423,-2.6950438370358434],[146.23836995286723,-2.5865369140314458],[146.11039466988291,-2.4234680911235795]]]],"type":"MultiPolygon"},"properties":{"alpha2":"PG"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[116.55490006139942,7.6158648478691955],[116.47373412329395,7.8319293535231918],[116.50963263266021,8.1755237939166037],[116.59661377608539,8.3541741201084481],[116.72972831121167,8.474270129371293],[116.75596468676689,8.6192939736774239],[116.91851054174715,8.9658473008768542],[117.56742025753699,9.6267594371531242],[117.80468811367648,9.7518375396810786],[118.76744922666853,10.782705563686763],[118.79068207859963,11.014609814435847],[119.03248010834403,11.543782755513968],[119.1566624021234,11.686973687047404],[119.3768043406111,11.830879685389879],[119.37779816692813,12.332703951045561],[119.52021045662453,12.623925887584647],[119.67173172454024,12.754912098543093],[119.86250334147766,12.815922526038026],[120.01302621721607,12.809391020910079],[120.28282389395477,12.716184073535134],[120.2465985693225,12.837008166029396],[119.94572834143261,13.103457911894472],[119.8426774936357,13.35510693492771],[119.70965674202039,13.475672450394089],[119.6228207530326,13.648147457441651],[119.60781728144002,13.840665022561602],[119.66241049359262,14.057565993529515],[119.77450470171031,14.218472295666199],[119.91991696526755,14.314997971076844],[119.87625542350479,14.387272148128542],[119.68966631550187,14.54162029786518],[119.59810424892667,14.72650459198897],[119.44881564849834,15.306266093130867],[119.40506064617576,15.657274221890249],[119.27861526590875,15.911651877358407],[119.28273738268791,16.354260895751192],[119.35168552156837,16.524635193771729],[119.49740304269761,16.700496282681652],[119.66061357854774,16.795832205601247],[119.83399267965558,16.823692089374877],[119.92010000424168,17.146629436411942],[119.92331222775954,17.343792571268196],[119.86761160463595,17.543958478127291],[119.86661331946628,17.727010993502851],[120.17739879995609,18.774987400269424],[120.35247451257244,18.942138198829639],[120.68564804250022,19.085979246146675],[120.69903626687653,19.21882099003879],[120.78110060859237,19.434700237095772],[121.10958718611109,19.811914829271196],[121.24116617106232,19.875901315046303],[121.38563417927563,19.899059342715457],[121.70591980814808,19.826204858632281],[121.90260041605811,19.684490809153147],[121.99896031408913,19.506431213550158],[122.1720267851982,19.454765118883873],[122.32845281239614,19.328970761232661],[122.44825798411041,19.150971239591971],[122.48508432524321,18.90879709892382],[122.6255393219689,18.805474989094048],[122.71131054682861,18.68185717933158],[122.79908415021563,18.444517582648661],[122.81383997419523,18.290093018014566],[122.78055447150902,18.138578148068834],[122.67376884649101,17.925485283779548],[122.66034668884787,17.738573794154071],[122.95100928307794,17.37621156128461],[123.01522919733551,17.184807911865679],[122.98172473280893,16.93566750440101],[122.57705207053733,15.951367951559462],[122.41414991958985,15.770196426630056],[122.10608539468777,15.653276857393264],[122.08697875006416,15.532353440000422],[122.27829848368127,15.441072398492727],[122.4622260028363,15.254739764867828],[122.54093429020604,15.07114163108599],[122.53133182009735,14.820844543994861],[122.71342062613499,14.809663769651413],[123.04169515162774,14.714712869504858],[123.25898865128846,14.557254077051859],[123.48762956176468,14.53257045042162],[123.8149249147916,14.364572583219068],[123.97300560222718,14.509265345289196],[124.15387380267454,14.572246433796471],[124.34515124837158,14.562609929749595],[124.55632224537645,14.451613386008511],[124.84448324663921,14.130153184405522],[124.91664202808533,13.887685052671662],[124.88398711384498,13.540418853233504],[124.73908682423516,13.284628389932481],[124.62453504668071,13.166531157955653],[124.64242396684394,13.034524752923335],[125.20160201951566,13.069662852750545],[125.46302491309123,12.9748940729936],[125.60770941829198,12.865844592359482],[125.74703749885731,12.659453315129953],[125.9121654151604,12.520044665667877],[126.00954807765504,12.349968879027452],[126.03407898370794,12.155527651906946],[125.96708320699854,11.876047328491856],[125.98536853846223,11.722088216381353],[126.21643653913529,11.250714934821531],[126.24772820249333,11.107226082545942],[126.20458752240948,10.867889203437729],[126.08877252530618,10.707091006873283],[126.15343381699333,10.552216277770086],[126.32942233790605,10.488471566940174],[126.47158046078151,10.361699074641667],[126.63956950756766,9.9777219732795217],[126.66903401664996,9.7435459716181931],[126.60771965067265,9.5550304126703836],[126.79120946236029,9.0658435353228537],[126.81910207287227,8.8592293310237036],[126.80344009211163,8.7228341120605872],[126.95519305836638,8.2596370038719886],[126.94347190644217,8.018601027057068],[127.03065594776484,7.8712066955410354],[127.0662689941239,7.7368790310088373],[127.06471219861304,7.1198778081089387],[126.8666767014457,6.7535801834248597],[126.73215303423356,6.5975933082388458],[126.6443045790443,6.1031136143514262],[126.56465274498153,5.9800421073502639],[126.45273025104534,5.885361772242053],[126.31815840720749,5.827211713332539],[126.13901194619628,5.8133619078527898],[125.87018182043091,5.3848024898660656],[125.75804744523654,5.2662194891423768],[125.46942376554468,5.1157563173323108],[125.221893940535,5.118282526805122],[124.98506315260708,5.234727675312131],[124.85590207167704,5.3809474347719011],[124.1926269663392,5.6640435141885064],[123.88119731151853,5.8592944554890067],[123.61367301202428,6.2228698516021295],[123.49032429507055,6.9182435369914099],[123.35395033991355,6.9091912197576022],[123.18743721511609,6.9503715420982992],[123.00998531445528,6.8447690570432602],[122.77420143443241,6.8175236054252153],[122.81634486754504,6.6844568168814478],[122.8191574638912,6.5394246876219553],[122.78022019246312,6.3996888006441148],[122.70281036122249,6.2770105742017392],[122.50810060609194,6.0896595388526906],[122.38965165556837,6.0194517617314744],[122.09096610569135,5.925639512544735],[121.91191409354266,5.920803936943261],[121.82329446240706,5.6766986320494564],[121.56192213780706,5.4478196105270769],[121.42333084552189,5.3871595676388253],[121.2729408099198,5.3707271517458199],[121.10618214880617,5.4066535645366471],[120.86632566064968,5.4006332507195127],[120.71182362464191,5.4475359521435234],[120.74830374464987,5.2991637892262862],[120.72792374470153,5.1092327955530177],[120.60773256409767,4.8914739720628244],[120.46165225987262,4.7478124646474962],[120.32065213923751,4.685530492873732],[119.85609903197886,4.5709118758903156],[119.66632876592647,4.594496788371063],[119.46407542815692,4.7203384318613164],[119.35907204951305,4.8801611225147052],[119.32174620414131,5.0677132039257211],[119.34822220395566,5.2751483357257829],[119.43895064344036,5.4475782393802321],[119.546845729808,5.5467426290141555],[120.01398474099662,5.800705622686043],[120.20866753094826,5.8398218935435535],[120.40087157682191,5.8009373828081463],[120.37701324368895,5.9723261167456192],[120.42469190978815,6.1665897345862719],[120.5087935474069,6.2913584748212177],[120.78369611750493,6.5263577200542473],[121.01247276989852,6.5950733013016345],[121.31699849054912,6.5278878549272932],[121.32643903771921,6.7448282531460455],[121.42696021990298,6.9354973171997711],[121.40586872104269,7.1097529195731672],[121.44361227686528,7.3349815875853546],[121.57899731064015,7.5786522019919689],[121.65189399133671,7.9496512645245785],[121.70974770985127,8.0779772170793063],[122.04095845381902,8.4306952549349745],[122.16871699318224,8.4972697018540568],[122.58047319236718,8.6001760244777312],[122.64424095781922,8.7031707000332972],[122.48112316750365,8.9535723409069128],[122.22772761314462,9.1116731965450537],[121.9783372680632,9.4437584838627568],[121.91552342046458,9.6284745685568485],[121.92968109559254,9.9625109571533059],[121.64180194290321,10.068620422110303],[121.5384202387968,10.171232044799142],[121.46899947886575,10.299285470935962],[121.43896813987357,10.490618405752352],[121.56360541302092,11.414287682308826],[121.45559085498215,11.546026764499002],[121.39402630208996,11.744988171366709],[121.14397003679888,11.727705639484343],[120.93008354957759,11.789909987723879],[120.79651247597126,11.870167834714927],[120.69545427918935,11.688905248446366],[120.51718485727464,11.556217075285197],[120.19617213342876,11.028359662999277],[120.38617724821466,10.890281204377334],[120.49289803983977,10.676743725441785],[120.48845126901564,10.438064424905519],[120.37385057861506,10.228650327875371],[120.12272365936299,10.030959042163206],[119.67803305154655,9.9152177186664918],[119.52171095428031,9.6859798534018413],[119.21873935514749,9.5400720016084044],[118.78775983979153,8.9020417389957007],[118.5031683667021,8.733854917917224],[118.28504128346658,8.4736122667320188],[117.85579434833011,8.1995061387166288],[117.77544672699774,7.9441405832175418],[117.68153206515923,7.8361197122166573],[117.56363047630028,7.7605483759878373],[117.44197780310776,7.5270461740206276],[117.30389434430614,7.3907123601486235],[117.12430522367285,7.3172161143284331],[116.88292939480655,7.3295223076711586],[116.71174624094411,7.420902164495347],[116.55490006139942,7.6158648478691955]]],[[[121.41474753002727,20.372351047316403],[121.31468197022905,20.548930859205154],[121.29273446565864,20.744340153453802],[121.38172608365291,21.023776689954143],[121.48999000914426,21.188517815582429],[121.65351621313926,21.298608217721142],[121.86853496409306,21.338994171013045],[122.06297463267519,21.298904360375015],[122.22648584632252,21.186307465893435],[122.33329009576377,21.018955053491915],[122.36624145482936,20.839922337396459],[122.48340724096725,20.681726508843571],[122.53041475366931,20.488841609051804],[122.47872365797375,20.247073575864292],[122.30304724921631,20.008834749240723],[122.18290163763778,19.91622694036127],[122.04051439169194,19.863910151096352],[121.88899150228684,19.856699864461259],[121.71669687397406,19.900314640148512],[121.58972372730092,19.979299779010653],[121.49178557919591,20.092299563448087],[121.43164234361738,20.229206916615457],[121.41474753002727,20.372351047316403]]]],"type":"MultiPolygon"},"properties":{"alpha2":"PH"},"type":"Feature"}, +{"geometry":{"coordinates":[[[60.4857789809788,29.509498351814148],[60.401007707407814,29.626490219244449],[60.353225664269253,29.762835891053935],[60.34642232072364,29.907151449412261],[60.381165708765444,30.047387546245609],[60.454554996491702,30.171835440220185],[60.560462687444222,30.270104595425579],[60.784300488744861,30.354711213372259],[60.975890098318224,30.340590984759739],[62.53187533556099,29.915184198724237],[63.58774172736014,29.997418678129876],[64.024469829046424,29.929819600067262],[64.461846179759362,30.060653722449331],[65.047533568853922,30.063125220243958],[65.759559053958426,30.251524867344575],[65.799831166524228,30.382299515018246],[65.802036888561318,30.728753204818084],[65.969312560157519,31.169880979867596],[66.069103208515983,31.286955638229045],[66.28238688018854,31.416805466742165],[66.54025251609778,31.671639132641687],[66.866033241889951,31.801393690919873],[67.151235627314819,31.765044916880594],[67.309692896713216,31.928011186155185],[67.74296237084441,32.106406240018096],[68.011798214641928,32.279893609003864],[68.153263865355356,32.302664755675949],[68.392586793763925,32.269433396728218],[68.588552274118811,32.302613331271246],[68.751756999731896,32.277762277391908],[68.766018039469088,32.587802226892329],[68.929081946747885,32.860857570996245],[69.084655481238755,33.295573246182109],[69.255862807053916,33.45523835398145],[69.520591670268487,33.537921147387962],[69.403977094435106,33.71234955418258],[69.368338267680201,33.899184636657651],[69.410992434859438,34.150960707283474],[69.501733028221281,34.322329205029035],[69.650848357872619,34.446287406718966],[69.936741992171122,34.548181125051947],[70.07849081380725,34.544492495144546],[70.471750635972413,34.456719889061127],[70.48240749745797,34.657577637477004],[70.565469632857173,34.82963775302661],[71.068431078500794,35.305048382301813],[71.068222532324498,35.413557508114096],[70.999287303160429,35.554589065753959],[70.809927247179246,35.711851158727491],[70.699087822378033,35.925630724003611],[70.691247122080838,36.118802786303085],[70.784396194307959,36.340862082355152],[71.325134603258832,36.839617302412357],[71.462803492848593,36.910693978223399],[71.614580907425633,36.938799273818574],[72.076426755436401,37.203699900726505],[72.541147729661205,37.322685381790613],[73.08206175094044,37.367210023043768],[73.754059102638578,37.388048963989561],[73.995473437035557,37.351150838548016],[74.580723989629362,37.536100818780582],[75.514160797544363,37.384289039437697],[75.746559677679031,37.213846029581362],[76.060358200187281,37.096130142527215],[76.211510118258659,36.984602044352705],[76.439018430619171,36.5684210020694],[76.475714081500982,36.380669398952946],[76.653352423861918,36.376227422232915],[76.786962115698699,36.327659315989024],[77.128151109223666,36.007028159250176],[77.528643361609028,35.248861685725082],[77.548338999579727,35.107922972693487],[77.52753167492763,34.967144088527931],[77.384459705351802,34.671692730218211],[77.267958042720721,34.569614894572119],[76.770763904187319,34.268574811248264],[76.179442258198279,34.175681158561204],[75.881772375820034,34.034016838138079],[75.736699515387414,34.004030895727141],[74.699621248587533,34.199617004674742],[74.748269251352738,33.905733282551573],[74.71360973367895,33.763217741062888],[74.634049874397405,33.63066261170713],[74.630279895770045,33.369214474132157],[74.728754551065038,33.253376004145885],[74.97033135776681,33.151926843012035],[75.12756330666339,32.940994147478314],[75.451546009037472,32.82188228782752],[75.574216833637621,32.737901816366119],[75.76365155658489,32.533584321666666],[75.83248480663832,32.306616506007863],[75.788983361265437,32.073464131136163],[75.595908108297508,31.775775710428775],[75.473731711423284,31.69145362141014],[75.08686240975895,31.545074343015994],[75.067114010131249,31.290435591756321],[75.121936114943693,31.137167594451448],[75.130580437379223,30.9906883251731],[75.050977946539845,30.761111098067552],[74.914388600099869,30.621807608035834],[74.614544216053687,30.454983058738968],[74.431652766158692,30.252439449044012],[74.378674640085222,29.995216415157664],[74.212865182721913,29.783134205369468],[73.777368012244906,29.553040362279489],[73.652552335571215,29.281032807779194],[73.264407744207617,28.683732401442889],[72.700430167876604,28.361008973347065],[72.578242975287608,28.120030930665354],[72.377353045579369,27.895617595660763],[72.297942294930849,27.703892120117789],[72.164367170393675,27.558424406624994],[71.984885930583758,27.476025049101754],[70.867246148022375,27.214658042536179],[70.63877183807449,27.235859711268137],[70.416764975884121,27.352304109677654],[70.187938067197607,27.061600046192869],[70.376334719118347,26.965206858426175],[70.592697773567878,26.733748495083464],[70.64650261833691,26.536491083930763],[70.608465458274424,26.204164182464041],[70.910952443947266,26.092189714921531],[71.075320414058609,25.926736169415868],[71.140987015300283,25.751432085789872],[71.159473601154929,25.537662794307838],[71.536583617679895,24.792036316943999],[71.541059779889935,24.347731331987404],[71.4611982279296,24.124910297597165],[71.328339805189756,23.989067334387713],[70.914247120232275,23.767705618703694],[70.598745170758718,23.750915820665728],[70.370145973843094,23.833660028677929],[70.181126621424625,23.71813254895466],[69.925407560577597,23.671187873641344],[69.66582006683042,23.675390314964414],[69.39867980785921,23.773850560573408],[69.185971104360192,23.775323697861559],[69.101798892128414,23.637534724440428],[68.950170998871073,23.519061923430176],[68.813005590673768,23.472935418617851],[68.515220994706453,23.446003458794475],[68.306066693363306,23.29147201926374],[68.06673983663454,23.256130751177359],[67.838536493217063,23.327107048064931],[67.574222580530105,23.320210758433362],[67.349673863268833,23.426121811160101],[66.888940417191108,23.90470782877053],[66.747177824686545,24.345587003909685],[66.473048277779966,24.417279590634902],[66.317049187784363,24.54354385725329],[66.223266861751242,24.720979074805935],[66.195336003925277,24.936403497288612],[65.707263149931251,24.856256506409377],[65.412730379188517,24.870358404716818],[65.011335831332801,24.822951898313381],[64.866723725899007,24.729560607512653],[64.666567938776552,24.684392414242527],[63.995893862990549,24.863059197390513],[63.858225036084079,24.871969847302807],[63.703012226719608,24.758176925492592],[63.559461827745338,24.715778302565365],[62.738269314528772,24.757876762183855],[62.338564156741981,24.635704273063247],[62.144761426781692,24.665486379077446],[61.877832164009895,24.632143685350496],[61.346504875007206,24.737831822893565],[61.160707775924159,24.895427264586093],[61.071444259287951,25.122118486003409],[61.179401304147142,25.881626868530436],[61.295542821265414,26.094925251695429],[61.390692935022514,26.439690649918326],[61.474667396397138,26.564289781969755],[61.591868406096296,26.658314125608118],[61.942560196450607,26.819690307327484],[62.138384158183563,26.97646560633012],[62.307238134472364,27.039398012433963],[62.2545272670964,27.226339538690823],[62.299881688818992,27.537930068743361],[62.241627675747111,27.875942079860508],[62.134872381266383,27.954362061987609],[61.619403476748346,28.126950908725188],[61.280292632491999,28.429264281299336],[61.180439385510468,28.557687690919614],[60.88779527350561,29.096581866059079],[60.4857789809788,29.509498351814148]]],"type":"Polygon"},"properties":{"alpha2":"PK"},"type":"Feature"}, +{"geometry":{"coordinates":[[[13.858668028149841,53.348173896740079],[13.71883003009826,53.864959462654305],[13.715261757692975,54.010961734441011],[13.801914341918913,54.236767283734736],[13.984413445700243,54.395483677229194],[14.171244056060079,54.448419708993576],[14.373093374582496,54.434735225949865],[15.960225429358196,54.756872539199506],[16.363504556637704,55.013353561835849],[16.759899134064792,55.0850196101782],[17.162365742479942,55.219088131107995],[18.38257185908931,55.334379558154673],[19.015462177852857,55.113710358666616],[19.130663610199161,55.019036334574828],[19.221471565290532,54.873197413419348],[19.623718072112251,54.958541335273686],[22.183331691401687,54.859690778228646],[22.663696649162482,54.852287811334449],[22.892821568940434,54.890257230923893],[23.046988918671182,54.866244433317114],[23.699794251214723,54.578407665662752],[23.849267383292833,54.448792765812946],[23.9216427074183,54.318764642422799],[24.062638306898215,53.796191384119048],[24.352824338368009,53.210421592023572],[24.384326351647466,53.058572852682559],[24.395873232506123,52.631809810482515],[24.305854635303888,52.410224054883336],[24.106235753750273,52.248377310269667],[24.152193002010872,52.04014227678968],[24.085714559054502,51.659709600780054],[24.144488575556966,51.533323478331106],[24.308608689558167,51.332916090349478],[24.48662589140989,51.184223213508574],[24.560477997254981,51.056720352626371],[24.595538008048319,50.863977886002026],[24.567336851903214,50.382702502368261],[24.479218322419989,50.217101524014879],[24.260876844218402,50.001777263417445],[24.094555918740621,49.925378079208002],[23.89608651281138,49.892326462159062],[23.237612328989439,49.390017875935946],[23.340583535410012,49.164144038317112],[23.324074477524611,48.923076902687484],[23.156202623597583,48.660544235063732],[22.941074444035884,48.538588473754004],[22.742927157367735,48.525505234229492],[21.909083349179429,48.721637909773619],[21.76522599048527,48.780789168149269],[21.649044666494845,48.875183604335618],[21.334071014472954,48.925564679145673],[20.970915065926992,48.817749173887371],[20.458951722532021,48.883298245878692],[20.309242886253422,48.749550281232437],[20.082036240088406,48.682159741599435],[19.625241687084547,48.724150810770873],[19.492527111401277,48.797129183370842],[19.361703848483771,48.947930839259705],[19.159730691355279,48.90036049814541],[18.826630470380771,48.917032141018574],[18.646724876809493,49.013787965907319],[18.22362955015009,49.447388248445812],[18.017028150824466,49.497928612456306],[17.781818742895354,49.485966943248215],[17.645983714795076,49.519133410401992],[17.232982873023261,49.80101649699953],[16.77042151457557,49.619937653854386],[16.519150618015008,49.617056694442319],[16.379021424694081,49.67547017628813],[16.171075962920096,49.855343215118914],[15.912551946031725,50.008773340706483],[15.751382181116735,50.195609528597785],[15.217519194486226,50.328771036621255],[15.070642582781225,50.409123808142034],[14.832701381409988,50.359748054808669],[14.684526015127499,50.375142944118195],[14.468344411703271,50.493881606417617],[14.353277886669273,50.655081157629837],[14.313393553477768,50.798615758878711],[14.328799969770847,50.99607016584757],[14.399733690015202,51.145943726982331],[14.277132262563704,51.299814074634554],[14.110636282548334,51.742418638823253],[14.107889670216135,51.895706267499051],[14.158110988714343,52.056064930938646],[14.093680933794024,52.166516325635932],[14.055838305133214,52.335838972560715],[13.75632445035539,52.544856254607545],[13.64449282665735,52.75435722347779],[13.62922649055978,52.896909134011985],[13.669156863760728,53.097587821499594],[13.738170484311846,53.230365412369139],[13.858668028149841,53.348173896740079]]],"type":"Polygon"},"properties":{"alpha2":"PL"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-56.86960385439442,46.940016024137428],[-56.864702629644562,47.21452813923095],[-56.737556027786255,47.43660518862324],[-56.525320006573857,47.572190422136899],[-56.383662187610263,47.598357740564566],[-56.240423240047789,47.583051855147133],[-56.029160425777064,47.498878058925364],[-55.858298128137818,47.327222244442481],[-55.796090355098116,47.166523564054081],[-55.66362125108752,46.960615397107254],[-55.637818102856343,46.815988820902611],[-55.654151750790795,46.65872544198988],[-55.747929825472745,46.46664242382549],[-55.919436268011339,46.319430816095434],[-56.151394087764167,46.253525196165171],[-56.361093909108675,46.281502754561345],[-56.600054328881626,46.360243224197859],[-56.765370157014154,46.495570292777501],[-56.871678139754863,46.706984330707428],[-56.86960385439442,46.940016024137428]]],"type":"Polygon"},"properties":{"alpha2":"PM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-128.83935188574722,-24.442530017960213],[-128.83961116851208,-24.23936882878704],[-128.75902157995418,-24.052875251873861],[-128.59398720670285,-23.898827329740911],[-128.41229755075955,-23.830314385144099],[-128.21820817245893,-23.836185320759508],[-128.01139139213578,-23.928207620992548],[-127.8755233223027,-24.075764285985198],[-127.79745822077734,-24.284773038817942],[-127.80319462542636,-24.509960208179461],[-127.87825556522773,-24.681214784158261],[-128.03553927502412,-24.836583188973705],[-128.21572005775391,-24.905124303419065],[-128.40843934512284,-24.900404651891662],[-128.60477718647229,-24.810870602682652],[-128.757191300844,-24.649171041111067],[-128.83935188574722,-24.442530017960213]]],"type":"Polygon"},"properties":{"alpha2":"PN"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-68.220805491921368,17.689123768202986],[-68.358530502111563,17.832032680736035],[-68.43566412506884,18.066848610269666],[-68.409509208074084,18.263589699090137],[-68.308873221442809,18.434655575021068],[-68.174175149940581,18.550523485706901],[-68.034095960013943,18.607157520325195],[-67.824835160270624,18.621048267144616],[-67.706316347879209,18.597163852506192],[-67.452830900701713,18.903210864077746],[-67.319698754805259,18.972340625398349],[-67.116233609552268,19.018768550021274],[-65.998232675306625,18.963808934482081],[-65.450047935323269,18.848072260763036],[-65.31984369196644,18.774149690199543],[-65.19606752439141,18.630524563577982],[-64.952888089591212,18.497893591505907],[-64.840598290773187,18.341548297489506],[-64.800053319110006,18.202577395538423],[-64.801020041014411,18.057815965022467],[-64.843417418871553,17.919398954987191],[-64.923691398137152,17.79892947931334],[-65.035112833868396,17.706506159875794],[-65.215702168747356,17.639870268588574],[-65.60785102699883,17.61005620732055],[-65.800109408663261,17.504665903131791],[-66.053569577394583,17.456167996950587],[-66.442264388265514,17.451984818527237],[-66.600681972774169,17.483566221887578],[-66.882423945178516,17.448485224509341],[-67.188876725262446,17.469095467336643],[-67.421819349064478,17.547895590162991],[-67.571733468297026,17.664248993686812],[-67.730608399454212,17.581356791657285],[-67.878657611678349,17.559423616544901],[-68.073522885332807,17.599626682478657],[-68.220805491921368,17.689123768202986]]],"type":"Polygon"},"properties":{"alpha2":"PR"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-9.9745195676334575,38.85036710904221],[-9.8321253970770268,39.539577848872973],[-9.7466231600106426,39.672125498648214],[-9.5530626862290795,39.85046316290326],[-9.3798976654853767,40.170302764022239],[-9.3537536907528089,40.395033900202513],[-9.1814188401265788,40.86139316927499],[-9.1687659966895758,41.035455088508606],[-9.3610675961332692,41.604603356957064],[-9.3862296020073952,41.798483780656071],[-9.3110963480767204,42.030096787360712],[-9.1733421856335369,42.245663774709222],[-9.0705884501284793,42.345588369279582],[-8.7533021349330724,42.524994301610832],[-8.2847584195355797,42.636851777067008],[-7.9883332366271302,42.580006116715005],[-7.8675806691574071,42.494711712847604],[-7.7677175133841985,42.368740663380848],[-7.4910069590141672,42.344667750960568],[-7.3487198554698034,42.438418147330239],[-7.1593848249686154,42.480709551554256],[-6.5330124468938076,42.434804820682317],[-6.3104642587025213,42.336118430153427],[-6.1335346887312703,42.100484559618458],[-5.9477647674882315,42.004893829071882],[-5.8375088675597624,41.893682389618306],[-5.7242382558411187,41.638507642360565],[-5.7329732671433922,41.391517278670918],[-5.795870761884264,41.256135389445191],[-5.9663610793681574,41.073737725710266],[-6.355383286636874,40.830798717858343],[-6.3193495465266656,40.249195729741274],[-6.4171228721907108,40.034473553210255],[-6.4150956859524353,39.876139330949634],[-6.4538004305513956,39.735567561259103],[-6.6424479009842763,39.407682010872684],[-6.5344981783673379,39.243470348032275],[-6.4982216269689355,39.049463848160471],[-6.5703769102200189,38.753787978063947],[-6.6607856505902614,38.589765710290237],[-6.5589941550662711,38.48927343922454],[-6.4888624968571857,38.361100205358866],[-6.4587767386252777,38.218125821239362],[-6.4713058223187616,38.072558484846397],[-6.6513030402609727,37.710514725556145],[-6.7687201218911506,37.614421171554135],[-6.9201131713560535,37.551624564790345],[-6.9696691327849774,37.493189545047706],[-6.9121075091047715,37.254705494137418],[-6.9110250814660494,37.11208932195045],[-6.9718225811350738,36.932443069274768],[-7.0951514037269181,36.788362836124875],[-7.6460169153018764,36.542661365326296],[-7.8002919364745278,36.507056366915826],[-8.5731975562617642,36.607962034498946],[-9.0471815475682611,36.534908223096515],[-9.2787528586849266,36.619065179661099],[-9.4420156260179358,36.80359955118378],[-9.495968815815365,36.993949199303813],[-9.4718663206299976,37.19032385386641],[-9.311697469329788,37.554212976694096],[-9.3037390405414779,37.673881240518732],[-9.377844711589292,37.976484432123847],[-9.5463532209137441,38.075589790922812],[-9.6821732225710289,38.276149919102842],[-9.884656897660367,38.445900929732559],[-9.9633836748727891,38.628936870382063],[-9.9745195676334575,38.85036710904221]]],[[[-17.627746947041881,32.490713178324853],[-17.731221789891514,32.710094989047256],[-17.719272378288448,32.952360615046764],[-17.544455546967569,33.22178466689919],[-17.33455928411232,33.347222139122813],[-17.18840157142839,33.368329353307473],[-16.877518139630403,33.338456811283663],[-16.481906700667551,33.210766979503177],[-16.325270404533331,33.09578836576167],[-16.209755387854905,32.883125143175917],[-16.2076455074615,32.641123255324814],[-16.288870484047123,32.4646083435943],[-16.534629186846825,32.250689440031081],[-16.721597434386236,32.162148914873157],[-16.876776996993247,32.150081102604744],[-17.190631016441479,32.194848448029575],[-17.441431687800389,32.301532611727794],[-17.627746947041881,32.490713178324853]]],[[[-26.194861219139302,37.51268466498049],[-26.322066522554355,37.714647045568761],[-26.341154244962034,37.952566467312927],[-26.274772825202749,38.132316710046808],[-26.105629837118961,38.300722891560078],[-25.956414795886836,38.380149079647282],[-25.770399229094949,38.410724269685637],[-25.509072187783339,38.337057230990979],[-25.133475937924068,38.335314603460048],[-24.989252486563739,38.29896989442345],[-24.792467130646475,38.15093191969288],[-24.701511344919489,37.975356199008431],[-24.702860092540035,37.656028320561745],[-24.80953720665871,37.441676839907132],[-24.686355335597167,37.328207213730877],[-24.564489202567295,37.119836138197321],[-24.531887803838931,36.928931188983384],[-24.57427662101027,36.739958336403348],[-24.722018756089302,36.549273964333644],[-24.894416093509772,36.461031633880772],[-25.166871867702156,36.443673540340697],[-25.367031168756426,36.488594776768672],[-25.532306230790567,36.610109455513104],[-25.670742506080263,36.833427041186589],[-25.697148539307758,37.026867465954261],[-25.633819452322744,37.240473449493066],[-25.958036602189861,37.315947506348607],[-26.194861219139302,37.51268466498049]]],[[[-29.061387464944666,38.149253957302783],[-29.18059600915106,38.230831416168257],[-29.271509428947734,38.343082513746978],[-29.326539887106332,38.476638522528354],[-29.341094418583364,38.620352559491479],[-29.295941616916959,38.807043834257023],[-29.217320606585144,38.928222609383525],[-29.107339354853949,39.021869195409899],[-28.975177142825927,39.080167638944999],[-28.78194423608284,39.131083582095357],[-28.62384711330391,39.132418848608935],[-28.476611044476847,39.21517313773024],[-28.334370254483208,39.243023934048992],[-28.190117653070491,39.228906704176659],[-27.771547088958133,39.083344995759198],[-27.659902546162058,39.183784558261777],[-27.52738782647203,39.245205465911901],[-27.071397173320708,39.286463020177059],[-26.924561968015308,39.246721074792312],[-26.716160688720844,39.120186477001013],[-26.622156082213817,39.012359250087734],[-26.56253111526722,38.882326791736112],[-26.544579777059408,38.630365168324538],[-26.599697395497142,38.44621994870031],[-26.773352260829824,38.251744952724735],[-26.954764721440178,38.154431745663004],[-27.108352348362011,38.134430352866183],[-27.446386054970979,38.182911399981222],[-27.892799055825755,37.943536432998492],[-28.238911743114429,37.884997650739912],[-28.593062828152693,37.92845953539716],[-29.061387464944666,38.149253957302783]]],[[[-31.446063912286949,38.921629001511505],[-31.646989842498904,39.05170407150284],[-31.749257682067341,39.214388264287486],[-31.782615966725047,39.403629069709254],[-31.719446217011637,39.695333945257239],[-31.608358969760371,39.855893977277304],[-31.444251091630615,39.961669705683683],[-31.242265521237272,40.018791717248597],[-31.004989784650313,39.981044451189447],[-30.781176561686951,39.828714830757747],[-30.672408888693123,39.659431102236702],[-30.643596622665243,39.41054419083364],[-30.728682762499208,39.146985334924494],[-30.843631989942182,38.990536218996127],[-31.009506199671765,38.889663044608241],[-31.201298427545769,38.859573310617563],[-31.446063912286949,38.921629001511505]]]],"type":"MultiPolygon"},"properties":{"alpha2":"PT"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[134.037815655522,7.2628746038076546],[134.0065011815291,7.4430965752797382],[134.05040903371466,7.7079775346057549],[134.15180576943069,7.8879360201377491],[134.35481577504336,8.1142181749544697],[134.52857011535895,8.1962019258824146],[134.72041258760768,8.2066121149395226],[134.90201810643038,8.143911702438011],[135.04657304832611,8.0173582553563829],[135.13273421947392,7.8456370895638212],[135.15192282863967,7.5759879246991453],[135.06073317363561,7.1998180403345371],[134.99029515741969,7.0757912261082279],[134.88770426405884,6.976698979312733],[134.60680231432795,6.8661416194277862],[134.3676047304111,6.8897137949244316],[134.16704383248717,7.0221768381977716],[134.037815655522,7.2628746038076546]]],[[[131.02435236782753,2.5378666015194358],[130.80794560275686,2.6474064103409449],[130.68835613540909,2.8011047531470372],[130.63655470257336,2.9888317699507736],[130.66137391700428,3.193763926460885],[130.7533516237261,3.3600751554985013],[130.91540616827817,3.4945554799464573],[131.12354374861252,3.5579371074645612],[131.3243360841208,3.5366704184964868],[131.51076948213944,3.4370098983385136],[131.63244984210357,3.2837997792726501],[131.6860216065017,3.0956256135525702],[131.66106645956521,2.88600966370714],[131.56195733020903,2.7124676383993096],[131.39925081310335,2.5809223155508763],[131.1916195944012,2.5239145526101279],[131.02435236782753,2.5378666015194358]]]],"type":"MultiPolygon"},"properties":{"alpha2":"PW"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-62.976639034227162,-22.612853736651459],[-63.116285705961083,-22.416013970848706],[-63.147460638196627,-22.17669157338505],[-62.780050679503496,-20.995473335406423],[-62.751949456655403,-20.409230840689826],[-62.361136644370582,-19.815730884852563],[-62.201276862654161,-19.416746885658426],[-62.077204173104505,-19.2617599193409],[-61.854158978131366,-19.155103126967131],[-60.058831807787932,-18.800441060206875],[-59.091674148763474,-18.788528585962709],[-58.866609673850036,-18.839516551599633],[-57.860162828440608,-19.455012990196014],[-57.710565172468918,-19.63635515533883],[-57.667265744417172,-19.771490019680723],[-57.640858330392028,-20.102753350866255],[-57.423643172670857,-20.571399951048519],[-57.33495724551031,-20.931634416967171],[-57.335187542558884,-21.222811991228784],[-57.440622488952599,-21.683641638648748],[-56.931666880655783,-21.762503197999543],[-56.844447880154121,-21.745808537734014],[-56.606816865199896,-21.602423845408467],[-56.40994694297229,-21.577897691543665],[-56.219036206618505,-21.631864248281694],[-56.006501340909729,-21.789226920299775],[-55.749025031841896,-21.817982664141283],[-55.608337869852406,-21.869702495809914],[-55.45635994634565,-21.9985871955638],[-55.14938598478571,-22.496955896558696],[-55.118357287570618,-22.650590587169905],[-55.140676066409455,-22.847845561962107],[-55.019658127126512,-23.425624296777897],[-54.702189376591257,-23.318706312031281],[-54.502884759806768,-23.328055789399464],[-53.981238771815782,-23.620795714494339],[-53.814680847852451,-23.787988859084873],[-53.743361303078643,-24.012952438729009],[-53.790558847876916,-24.404557472596224],[-53.95689330439226,-25.262740039570087],[-54.112116161096516,-25.579318039398331],[-54.137654470606819,-26.081010967064749],[-54.304051420046477,-26.74763173285081],[-54.455578564139287,-26.988248503045],[-54.8019369159612,-27.3029040052871],[-54.932217488305561,-27.387520230739256],[-55.111494005007813,-27.44898775471454],[-55.384280105711746,-27.78984303699233],[-55.59943639044095,-27.901130114784706],[-55.793790026727358,-27.908313200925274],[-55.998146978209036,-27.842847806752832],[-56.208607960938693,-27.99829516400947],[-56.449153875264024,-28.053409581859867],[-56.696338030077833,-27.981240742027325],[-56.979619431639932,-27.952460570668848],[-57.162905243947449,-27.967290350755626],[-58.172784522215046,-27.779903366745703],[-58.612493141076605,-27.814050304785468],[-58.804105443908817,-27.772615705505757],[-58.965059091840601,-27.660695985029044],[-59.070610937705851,-27.4954963831876],[-59.116965034689905,-27.169896795265178],[-59.096155790455384,-26.984889116702909],[-59.009106108878314,-26.820319317803047],[-58.705614426797474,-26.474101489496025],[-58.68988601851305,-26.268396575832735],[-58.565760131207746,-25.972547597475003],[-58.490685149947204,-25.854993222979008],[-58.32107032298979,-25.707847547412893],[-58.212522388133451,-25.494670894377546],[-58.536186209308013,-25.429103289713591],[-58.754588650690764,-25.288438606385007],[-59.420245808482612,-25.004536951610586],[-60.120961281860644,-24.542905648327658],[-60.654830665515611,-24.440676686737032],[-61.302569955341255,-24.176413758097148],[-61.523797974703037,-23.951310668408201],[-62.148890422133952,-23.538854594822375],[-62.315798376573383,-23.374518392895425],[-62.609327731664926,-22.928574523446709],[-62.976639034227162,-22.612853736651459]]],"type":"Polygon"},"properties":{"alpha2":"PY"},"type":"Feature"}, +{"geometry":{"coordinates":[[[50.25737081404786,25.348746951001953],[50.296541614076588,25.624435246602079],[50.41597934505706,25.833607641610861],[50.573850204442678,26.237417099095097],[50.805254011842045,26.478061569016994],[51.048902270807154,26.605183435748945],[51.238044177462463,26.652397834998013],[51.476157523334358,26.60491521606674],[51.874286108257216,26.276591363657371],[51.995295581346944,26.115030182701204],[52.071323165312833,25.807626149806978],[52.004656841905138,25.521147139413028],[52.090174565999213,25.253810277164003],[52.093794525219288,24.932105550339099],[52.041338453236818,24.7568327349776],[51.853865784031484,24.4068573716096],[51.753405864263087,24.289025212124457],[51.601697482497919,24.189840784328371],[51.221139886831601,24.08145006991462],[51.056973944576278,24.066164169066933],[50.793352703082036,24.104902205550992],[50.58287401678303,24.234249637921259],[50.42378941927786,24.428088750353293],[50.318466062484461,24.67262952114822],[50.322616994965045,24.921982935537333],[50.25737081404786,25.348746951001953]]],"type":"Polygon"},"properties":{"alpha2":"QA"},"type":"Feature"}, +{"geometry":{"coordinates":[[[54.783675098405936,-21.277621412420395],[54.734573551062809,-21.097446770033706],[54.743368444781851,-20.957301872008408],[54.887259265661385,-20.639611071915741],[55.090394971025241,-20.455808409510258],[55.407011406336125,-20.367249305211701],[55.739876754603813,-20.400834032835082],[55.975532277994411,-20.517159274755137],[56.250982986782923,-20.855641673935164],[56.336879205079882,-21.09497933082535],[56.300234991963087,-21.424566166849463],[56.185779312806062,-21.653799140521599],[55.992884960575509,-21.799267812495735],[55.707411379736129,-21.86612894568092],[55.412632684384832,-21.835822136249895],[55.07534910448679,-21.682490277885698],[54.912847653507129,-21.520280532527643],[54.783675098405936,-21.277621412420395]]],"type":"Polygon"},"properties":{"alpha2":"RE"},"type":"Feature"}, +{"geometry":{"coordinates":[[[19.866349364994861,45.778779504470855],[19.768626201660037,45.948098777705383],[19.743297959894353,46.141947463823946],[19.794236703060722,46.330690880624282],[19.87822127026109,46.451383282612817],[20.083297177115703,46.582636623467863],[20.455182977271477,46.639700552590618],[20.608731067115205,46.721970829937391],[20.820531182817085,46.756151552066747],[20.924805078765651,46.913531740267857],[21.088760944050797,47.048908007605995],[21.173629215785919,47.162379505943889],[21.252656058129055,47.331358423236139],[21.399550364647915,47.474630178177847],[21.630773596210251,47.841494280867188],[22.071373099244457,48.176955481701356],[22.467696653340443,48.261736963114352],[22.920748150051164,48.536734627958324],[23.059064505128308,48.580625724352622],[23.251914515577948,48.574351330379692],[23.526572497490573,48.484662967230207],[24.19166788466638,48.410991371075582],[24.638190183494899,48.427741437830122],[24.996843572896225,48.271094679068518],[25.346288889975742,48.396434939667685],[25.880889604709932,48.465996076553594],[26.00504364096378,48.602956988277569],[26.185814395500156,48.688937311608719],[26.757088968153816,48.761423992909201],[26.912741216372424,48.724444684492646],[27.294680780958064,48.543780741107078],[27.718769097664271,47.967303446965104],[28.165426598156561,47.512170960393327],[28.385400735014198,47.36751793261358],[28.492868659103308,47.247559897047637],[28.734663170986654,46.707883195141115],[28.742451450775086,46.410836446570286],[28.611235135127654,45.939269509868176],[28.632369585522948,45.775974417357368],[29.351221658163993,45.916687688375617],[29.498669310653455,45.910343757622996],[29.710481070408321,45.849707522660246],[29.841341611173302,45.788953271640523],[30.086581008276273,45.583683442628448],[30.179622325402338,45.418980905010521],[30.204817867172149,45.23150078002319],[30.176632921458964,45.092070905090615],[29.962951010419712,44.551233248777287],[29.743392998607874,44.379535086072778],[29.300727034066355,44.284060368841509],[29.16923298097387,44.11909187584213],[29.150031400438159,43.893023389906887],[29.026526805184499,43.507554486326562],[28.904351900537442,43.357615935246585],[28.780074137346148,43.282024933097212],[28.590744948546028,43.242493493789119],[28.125712698421538,43.28266836792519],[27.866620288808594,43.357503879788922],[27.671559810488922,43.483197488735776],[27.30412884204269,43.535679602739464],[26.999869125051923,43.645715365771743],[26.410925578595563,43.53655194556368],[26.013052248599209,43.306172120049609],[25.593192754220617,43.180317248465634],[25.452489347360974,43.172977694117812],[23.250757631212966,43.370003474878025],[22.926241183015939,43.334749121807647],[22.72757550758314,43.372829309034685],[22.597514752643189,43.451827067005134],[22.463330396502382,43.590341395426783],[22.387076800616484,43.727307613567916],[22.357196490925244,43.878732360306344],[22.157291703246543,44.046395555590628],[21.924428603811901,44.07150655171084],[21.735441428927334,44.183775469310611],[21.447240011029631,44.248093288699003],[21.045034929855468,44.438757679468274],[20.902516355688469,44.625734996162095],[20.851045064714185,44.865645411415514],[20.44935125415126,45.104446609505771],[20.327458290838891,45.264297053027605],[20.265973685681676,45.46667238024795],[20.057952037623497,45.576300099174979],[19.866349364994861,45.778779504470855]]],"type":"Polygon"},"properties":{"alpha2":"RO"},"type":"Feature"}, +{"geometry":{"coordinates":[[[18.384658358488021,45.638823496431897],[18.342892521197498,45.834439332143404],[18.381143346972753,46.030772759437887],[18.523186544976493,46.253672988243721],[18.687719719556984,46.381558751603436],[18.944311227378513,46.494211490556012],[19.138823106914344,46.505884822949639],[19.45164252073646,46.648644073861682],[20.22403934500911,46.636842803007745],[20.39760415143915,46.583699708792693],[21.083013629516195,46.143374042412944],[21.227157938881223,45.962660988698438],[21.274157625672899,45.775819524124266],[21.774614203708079,45.564068105976702],[21.925914563529386,45.37201825019735],[21.98898524744201,45.137536884536992],[22.112696640514478,45.101779657755081],[22.379293879200283,45.19181356156939],[22.567322860849771,45.201140743051994],[22.926371992646075,45.061098762178673],[23.053376328672851,44.978626598857666],[23.14986594534512,44.861915361322943],[23.206990136085082,44.721670625504608],[23.219509068171138,44.570756625385755],[23.17752307530899,44.400671421366731],[23.203210385759924,44.198630579381444],[23.147437322264626,44.00544595453421],[22.995648952950095,43.774134668417389],[23.359527642968516,43.509376672341006],[23.452428908614415,43.341452416063362],[23.475232743999147,43.150902721503762],[23.367626125826302,42.834680248240787],[23.031271615248709,42.504992902189457],[22.986788658707329,42.272992425370639],[22.794396082061958,41.995528299323951],[22.629593212863835,41.874259489740211],[22.481389587049154,41.832642368667585],[21.969420421627532,41.821708157193768],[21.615849561811778,41.750587228781491],[21.4206633643613,41.768331895205449],[21.247293051733482,41.859739004124876],[21.147853772158591,41.968560396704596],[21.041378474207754,42.181240508778949],[21.026393896483263,42.410062612759326],[20.884581513555041,42.581714994886134],[20.766328689013829,42.45638710077732],[20.633970490949579,42.386258107838287],[20.412292826430768,42.332816265312076],[20.212879333169578,42.345825246049095],[20.075452712788003,42.40674014397834],[19.955549157237236,42.513799939983706],[19.36390095903203,42.741013589888546],[18.894354521934016,43.069783845775198],[18.775062136490984,43.219912908553013],[18.703002675941562,43.441509242421034],[18.70659768793546,43.64239098065763],[18.773393978401252,43.801552121814197],[18.749254722485016,44.02398721463716],[18.626622675136531,44.271285288722794],[18.648155858561076,44.545399615733075],[18.547396484436888,44.682668423534501],[18.502159705485873,44.82424184347807],[18.509825623991901,45.021687768356557],[18.576686807688358,45.199091959717741],[18.384658358488021,45.638823496431897]]],"type":"Polygon"},"properties":{"alpha2":"RS"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[178.22459899264652,70.752709698945097],[178.15586384716607,70.884431575405657],[178.1288706660518,71.030536130898781],[178.18135020121932,71.27087078742376],[178.30283010285919,71.426631161325759],[178.68925038929396,71.688325880317791],[179.39108350377273,71.922302565304449],[179.99967664571676,72.037476328076053],[180.0,72.03742841640242],[180.0,70.478068740191958],[179.73483157639177,70.406805584168993],[178.73459393787471,70.325684576636064],[178.58627293887452,70.366891046992123],[178.45751680692962,70.451264919904361],[178.22459899264652,70.752709698945097]]],[[[27.051536600543507,58.02418676229523],[26.995240452599965,58.185572058707869],[27.010813078176955,58.431417698595709],[26.935516980526952,58.753717232290747],[26.958566547609582,58.940763501491247],[27.049217331139982,59.105990692987831],[27.385987049371462,59.395444064398085],[27.529117071051523,59.582137965487917],[27.532894706842253,59.860090091704301],[27.6450512023298,60.061004177324527],[27.449625373317431,60.178288385213186],[27.323217385928121,60.381283458915547],[27.305536880820426,60.619764860627257],[27.400628165985324,60.83918142041464],[27.544046870266499,60.966805552868856],[28.156964523092206,61.329233769996144],[29.02664566049766,61.736825005647731],[30.782674158674602,62.826748361788894],[30.232059230281592,63.040746940819908],[29.753571467211739,63.295609551243558],[29.637420070321411,63.382506993625036],[29.551100837605823,63.499088735572087],[29.494530123597478,63.683487608411184],[29.50062107255971,63.828419360738806],[29.572464194519188,64.007421471941058],[29.669198982621296,64.11707074380611],[29.506845665447326,64.384581764415287],[29.34680127655124,64.49393398764046],[29.183444770812617,64.698818175176001],[29.105395030899803,64.93828393817779],[29.123165614105911,65.369751816552395],[29.216252808044267,65.559087529738676],[29.239090906809498,65.774104667098854],[29.331828298034171,65.943756741681725],[28.687008648549483,66.566256244716314],[28.609836318813393,66.688191991194557],[28.570701546171065,66.827088946943547],[28.582831330956246,67.018588332183072],[28.667475711409651,67.242530266589966],[28.773558393859766,67.360203764832804],[29.095072369787683,67.597158416198397],[28.540945083590611,67.711327266125906],[28.308264047523728,67.861642393499366],[27.998253108084654,68.325405919619655],[27.971356194770017,68.467813211311466],[27.993399603940659,68.634633852022901],[27.916428357459164,68.861280287779849],[27.924944275269894,69.005188831164816],[27.974164620640671,69.140686238213533],[28.05999776735009,69.256508777649088],[28.175308512443035,69.343028265520061],[28.894806335597178,69.517413538342637],[29.080269916790055,69.691664814538569],[29.210562450291743,69.765194629380147],[29.729631045341833,69.860890672758643],[29.855892633269935,70.01929129050778],[30.026094483268892,70.111642856371446],[30.218621476383692,70.13239950308305],[30.473667786774033,70.087105669878113],[30.646524093703484,70.230464907858931],[30.833210421432085,70.281844080460587],[31.484033520722459,70.212141804130823],[31.814859424822863,70.421140497776022],[32.046653829591079,70.449355559986273],[33.101982653354604,70.224975989930797],[33.338001551147777,70.097249100207634],[33.46238021284617,69.928565342803481],[33.624393564502064,69.898096773788254],[33.773492657639501,69.812114044073255],[34.29283249608833,69.809272186098298],[34.983118329555353,69.727160285715911],[35.308416777072345,69.774810897078211],[35.991480629555276,69.673690914198986],[37.893694994451153,69.164273028130239],[38.591099079787995,68.843771205501469],[38.951037935367083,68.810216904939352],[39.570771714145152,68.602994486305008],[39.769808997526042,68.648987309285232],[39.961633061370406,68.626735574883355],[40.564538321743036,68.304861881200111],[41.162708483094946,68.173041304762094],[41.328898971328506,68.057459320323659],[41.540604464548771,67.676027882978033],[41.692807534528981,67.581326546707501],[41.812085847441004,67.419977255077143],[41.857317789406288,67.174252800001582],[41.781469919908602,66.865069069049085],[42.001951817262182,66.963074182407311],[42.085582954735088,67.106642739903421],[42.196750711109225,67.204931219343109],[42.331869549833797,67.266264690380936],[42.479038800685267,67.285241171921029],[42.822095920052739,67.244161615270357],[43.03773561206286,67.094924834010499],[43.155641121531573,66.917084495197003],[43.464302143267624,66.868414027580499],[43.309832128366395,67.092090346516741],[43.290297404973245,67.341466361596261],[43.411415850474235,67.667650290050318],[43.625177694719746,67.993931914519436],[43.092586949799731,68.217626708974649],[42.924569198512728,68.392778944270205],[42.841063290736223,68.619165508398652],[42.857564994036409,68.812190167247536],[42.946200035830877,68.984453496199563],[43.093662231101746,69.110094755750055],[43.277814068389766,69.170252265331015],[44.100597977950507,69.049155507688809],[45.144118705079514,69.074110626514255],[45.952286603787329,68.975744534872064],[46.123425097183194,68.922560474708817],[46.992238080282711,68.363506933027494],[47.115335049066779,68.222114846159002],[47.168448199729575,68.091536093886461],[47.185530554082625,67.780858556820306],[47.145442407314569,67.642256642158429],[47.067446063381766,67.520872456234741],[46.958036293523655,67.426813684613847],[46.826323671271716,67.367912620117892],[46.419003552402877,67.324838909640633],[46.493042248042038,67.316102159092296],[47.292608731523799,67.428178772658882],[47.374585902375522,67.583769252684746],[47.420993943791764,67.793588071782537],[47.574571088943891,67.983717678112839],[47.751542309099918,68.068531422096939],[48.312523812977112,68.156136190825165],[48.437847124331896,68.282174885129464],[48.123044316193443,68.417789808618224],[47.840463877299733,68.800281637869148],[47.7791188463937,69.032307872977654],[47.839512594719245,69.406879807595104],[47.932894996190441,69.58536763592231],[48.179807133543761,69.787223331540773],[48.86194047960231,69.999685636695858],[49.311365126690006,70.00355348583264],[50.414365327832741,69.69032813450886],[50.627406079247883,69.529977277585459],[50.730391654165189,69.368339098036444],[50.78272128244965,69.083773848360437],[50.71155695089908,68.832956730584925],[51.907978843161636,69.031215005281894],[52.12700236396141,69.032833198538967],[52.573600488039482,69.219041933485002],[53.745323054222126,69.492449705469113],[54.492409382934561,69.492195198225204],[54.680796614133143,69.454320636983908],[54.840987286561322,69.348192508125933],[54.928132582114728,69.23291304404313],[54.978669310827662,69.097525834797565],[54.986129321448686,68.935624805873516],[55.294092155604936,69.051973734570623],[56.032085847249462,69.148484321161973],[57.079614075475703,69.064624900307578],[58.058936203184651,69.376315558886475],[58.681434468938001,69.472518716297984],[58.349450317950044,69.675742595529229],[58.079751315025696,69.958483134818692],[57.981078487043888,70.179237422112323],[57.988071079059594,70.385399327153038],[57.826079901345587,70.271122287578493],[57.356652874943009,70.117574757060353],[56.737370430970479,70.135116571850745],[56.437293194821692,70.063205556200842],[56.134839302976189,70.132376234736782],[55.728601635769145,70.120490563120129],[55.51645224867498,70.182671998978932],[54.522791065916039,70.186514714601145],[53.310115671979062,70.37886270568228],[53.088727387999832,70.472740931487962],[52.87190779126054,70.492273946855093],[52.486887681555032,70.729495236434374],[52.06170797411361,70.821820670223559],[51.849882275818096,70.985910435738319],[51.653043757261926,71.018934456435531],[51.298151444253662,71.165697040046865],[51.19590270640407,71.264271242692246],[50.982234486383916,71.600920233274678],[50.929247702951891,71.843195667841641],[50.9570002439498,72.048557929744675],[51.008797544584823,72.180862436811864],[51.276829158576554,72.465265278841827],[51.40880190481063,72.540141201089156],[51.755846767855189,72.635948420992861],[52.037914061825362,72.645849604767292],[52.059490327405051,72.909748794415165],[52.187163477426893,73.11093375269968],[52.357062958914163,73.230955245917713],[52.772806531310948,73.362751951548177],[52.97383394398549,73.597571615085457],[53.267991518195835,73.700421636831777],[53.268921489168164,73.84087668375723],[53.312999467049814,73.983417580504778],[53.397173269879126,74.106606647783408],[53.513947801185537,74.199474748969436],[54.435081868262635,74.430774512405463],[55.152882545095217,74.882381860362102],[55.316383739530551,75.05037394049981],[55.322091729861249,75.232438234522064],[55.374147831030797,75.369135648157481],[55.463536709961033,75.48491841409087],[55.582608101484972,75.569877370558928],[55.881222493612086,75.666655589822867],[56.165861295622534,75.686829007779494],[56.435669246145373,75.637870889057908],[56.599535410803334,75.787017558403818],[56.788997236515698,75.848062079577545],[57.418734930862946,75.857566135993196],[57.900787968339486,76.137371935707563],[58.772358073892221,76.342432365698357],[59.802561614581663,76.458648741867037],[59.981366686012123,76.547206190117507],[60.233273510636465,76.59341506694912],[60.678814556984605,76.583760229722614],[60.844954144258843,76.695420687226772],[61.154302974646598,76.779067168547954],[62.953955313195571,76.7380171690411],[64.389120209896475,76.873630468863212],[65.601550731496133,77.063873367449901],[65.877805675529842,77.210349532799825],[66.265484517173945,77.314388775146298],[67.522234397932564,77.507606185232362],[68.570597676727374,77.426262899651448],[69.042068785314171,77.260756355870171],[69.211670373372684,77.155501871456138],[69.37763152142098,76.952087902659443],[69.438719112689853,76.759749404794334],[69.418797980901573,76.558928738337144],[69.312063207493566,76.290614641662486],[69.127417304993628,76.12805125965437],[68.347997924454177,75.819590697176054],[66.358996091423691,75.489682370510096],[63.727866012431093,75.172749585959735],[61.450178940734752,74.818296002858872],[60.884500082227056,74.60855507980348],[60.721360858760832,74.461799873867236],[60.601717121457874,74.403010601876133],[60.188253540879451,74.26797246582386],[60.042551834222174,74.250593025024202],[59.815998137103172,74.130958442738248],[59.674211709758183,74.111685984448968],[59.432928136956036,74.134619449788133],[59.292026511675267,74.045769779632494],[59.057978756686396,73.987470067999084],[58.940945472828865,73.846135276357444],[58.684936656484922,73.69253850480996],[58.295810291547326,73.588722308371189],[58.196724822026717,73.450092432777097],[58.072916065523557,73.360326400590822],[57.859843139338729,73.290734191408959],[57.660415336178716,73.152629782180597],[57.376663333768477,73.054915705658658],[57.149835084270634,72.902690350687152],[56.783119726195871,72.821967215730979],[56.621507897266831,72.690833478447843],[56.517238925771629,72.501093288747214],[56.364782680407217,72.369917126355119],[56.222981719534708,72.31716832945493],[56.012413509267482,72.292827997099067],[55.99199045479341,72.063643027543947],[56.138731150524229,71.898955637254858],[56.67337142461993,71.559921515992528],[57.065488035403341,71.398524715317464],[57.802502173530172,71.196220473594153],[57.930919597718002,71.124222082863838],[58.03280145402038,71.017944660712303],[58.099314665736905,70.886602375937201],[58.10983960230552,70.609290548878278],[58.243808885689369,70.711040851505388],[58.614179704941819,70.899125250113272],[59.067241799755685,70.961141691618892],[59.808980404904425,70.667774133630658],[60.591544982880229,70.420922832731861],[60.758228823716848,70.309985206607337],[61.022139113882645,70.351271416240294],[61.804134870681693,70.262514761678588],[63.441125393436195,70.168627891012434],[64.32115675867685,70.016893725229252],[65.951905576786785,69.561956163834523],[66.331603255177953,69.496446791572097],[66.304917821396259,69.684950374274948],[66.374620522506248,70.041220534743886],[66.172790868446171,70.146219752976521],[66.032229178514257,70.283761475116322],[65.917601804231907,70.578807562520126],[65.895068123155497,70.728802396651588],[65.958865193919081,70.971622369457748],[66.1467935778618,71.160588794046092],[66.24513888356293,71.388036888107749],[66.715472892233663,71.739443673280562],[67.408704806959335,71.895878492901019],[68.001376695509805,72.120555329940245],[68.218792227192125,72.333167959219693],[68.724356911152114,73.057978921227544],[69.155381762858696,73.396049186666517],[69.513024407716145,73.472213414630147],[69.577240082965659,73.628583078850809],[69.675429501761229,73.741974532724143],[70.01257944535152,73.925389231143996],[70.314234723396524,73.975871984965664],[70.961671494684239,74.013556997242375],[71.337209737770252,73.937122757226135],[71.888718440143407,73.683485249275478],[72.065027296946582,73.471601584108015],[72.122026876269558,73.311811747086466],[72.520752389524489,73.284483146372366],[73.07644285490079,73.115485212225749],[73.245555146216418,72.940025966020443],[73.307824189897204,72.754534123631132],[73.304769230031425,72.607400914590869],[73.205342484252228,72.131675786127332],[73.523384170447258,72.322345694249989],[74.224442825607397,72.468532992135707],[73.920941726279054,72.554958106378194],[73.794046089606951,72.626536315893645],[73.666888250772871,72.772676372564547],[73.613540068731496,72.908248914610255],[73.607006798727028,73.101855083220087],[73.674552373397034,73.28341396480738],[73.866189852162947,73.482384909662315],[73.990347694884164,73.56345094295439],[74.132820986937418,73.604539052115157],[74.580406713702942,73.621652230459205],[74.868829485190673,73.58407387271383],[74.941649783883534,73.728334986659448],[75.044934976435542,73.832671557612755],[75.353812768707527,73.991269263829764],[75.524168672913902,74.038235516375792],[75.921646780183082,74.07326762626262],[76.318033194684588,74.050405306618757],[76.997691032680976,73.882913911821376],[77.174901845042839,73.716968045770798],[77.253056776878381,73.487112636639282],[77.21372948923009,73.247539960978102],[77.06619205124916,73.05473330426922],[76.939348558668101,72.98121816222816],[76.796634928593321,72.947653783634692],[76.090306028688516,73.017680834737433],[75.920456986371889,72.968556797103119],[76.037214497686534,72.829322578632087],[76.098372089090944,72.647457783182233],[76.216516984293605,72.450497980680325],[76.418730728745629,72.486257845839859],[76.515374487927787,72.680851097239071],[76.665335439944485,72.805120829491443],[77.188460161134486,73.027645131128736],[77.71495847962548,73.129936653899492],[78.185666507260834,73.072076083167573],[78.273374896133063,73.199657952639413],[78.386739031657072,73.288694853749732],[79.053820198897824,73.58175259647146],[79.254362199953505,73.585920358260623],[79.541887461988964,73.502730749303424],[79.908927471530902,73.256232990724641],[79.90174224705541,73.415063895388897],[79.963976715902774,73.604422393037325],[80.317798596531901,73.991860111854294],[80.546727448196151,74.06687790578151],[81.830998411290551,74.158801819264397],[81.87625399804395,74.341282988379433],[81.957093523364122,74.464229187769689],[82.07021987332088,74.558320769454539],[82.205835945452364,74.615409102571974],[82.540407559000457,74.660861868151727],[82.798413439978646,74.599698684260701],[83.166133071209714,74.651159714617393],[83.730819726759037,74.576483915999617],[83.893094916437249,74.506108180300529],[83.925896797221185,74.640356989433585],[84.000758270712183,74.768172061092741],[84.196818188870552,74.91552282320184],[84.618396080020347,75.008281452832989],[84.959168222975407,75.00766505833974],[85.140295367798288,74.937243780789103],[85.306017544751342,74.763395084325026],[85.395390873379185,74.950425559493468],[85.624768741574897,75.169433093772625],[85.75178953284572,75.2230472473763],[85.918557604889216,75.244362298301979],[86.180758271758478,75.415687794668074],[86.595817628914361,75.469134556738354],[86.743422141813355,75.595102481071521],[86.888372697110725,75.65557922414763],[87.186954639277417,75.691304720595525],[87.654799256958171,75.639125655915436],[88.651095826179628,75.86243568991101],[89.274694721937621,75.968561816704295],[89.555936209033121,75.962636832483241],[90.118663905595611,76.08650727746091],[91.418705678873778,76.148383103138158],[91.807957133749326,76.222129488848665],[92.452921597405222,76.270600809074622],[92.685853875184208,76.485126618320393],[92.902658550107873,76.570062228063733],[93.106935922808262,76.55652602423848],[93.380679850159197,76.599989660193714],[93.648353431208804,76.563379990813289],[94.07290759428588,76.622595359068882],[94.381029224101482,76.611926902333309],[94.555186916178471,76.651122397663542],[94.954547830604071,76.632890287433511],[94.802803761733912,76.844642973803545],[94.772323455634705,76.989229822945049],[94.785434789540261,77.136411679597941],[94.899358245779041,77.352941111340854],[95.013229560888433,77.447109734564464],[95.149652349158245,77.503883221732892],[96.454985933452804,77.699867938135952],[96.689572756753037,77.678553819865868],[96.853793068145521,77.584826668166386],[96.947471917390075,77.477859376055221],[97.043065960339177,77.289086553852485],[97.060070125191885,77.122065066029563],[97.315260333440023,77.201997170241967],[97.565544067310512,77.171356653918281],[97.935330544749633,76.958885559692362],[98.045189555189452,76.801365705596936],[98.084331902202948,76.655704043098623],[98.358194327247546,76.702409477993655],[98.513183781334476,76.885877543385035],[98.800200404532205,77.004517702828807],[99.588748005335603,76.972551122552673],[100.4186262579537,77.009256696451772],[100.51006441160511,77.205843533748052],[100.70095088382207,77.398160482011676],[101.44966107812941,77.696432700301273],[101.26217629565447,77.692077250279951],[99.88063786904786,77.458183954900861],[99.416513242899725,77.483320315349772],[99.056983275899967,77.59451153592947],[98.874894604560808,77.75603165262342],[98.810722471550349,77.888272953181826],[98.787777139273743,78.033460197646363],[98.808041618995134,78.179045825680561],[98.867340114302095,78.308326023340825],[98.381658978127589,78.288686492087223],[97.525579243463611,78.328025378401634],[96.725151904384205,78.487099625112606],[95.730754829758965,78.505669632139842],[95.453351270880447,78.587206219346044],[95.133240947358672,78.549761345354639],[94.744001751297318,78.588514980140161],[94.570851977747623,78.640137108794519],[94.028976017312004,78.916363419929908],[92.988448282470216,79.002086731821947],[92.811522809927155,79.069889046749367],[92.67825435403887,79.191581880931295],[92.058343762069853,79.194075725675333],[90.946669704229848,79.438475164731756],[90.78538023316483,79.539293945428142],[90.63979339993503,79.727744594002345],[90.584996923021663,79.862631569180792],[90.576298824830843,80.056021735751472],[90.61876493963203,80.1952832848187],[90.699549499212011,80.316407208501744],[90.854631075558302,80.432274020472747],[91.046143948550281,80.491357195163346],[91.074838678303436,80.569280833798516],[89.864321911817228,80.626736178606507],[89.728414452582783,80.681428191176209],[89.569492851259355,80.796766554045007],[89.432054258239916,80.998617453508714],[89.402310737282235,81.143063205115681],[89.415999801774817,81.289902783259905],[89.530422905837483,81.505635269738264],[89.733372645163826,81.6414460048546],[90.038302122975438,81.712360245326053],[91.128490007084892,81.698704784056105],[91.646698827382892,81.654282604561601],[91.866657920661268,81.541193323911045],[91.997787791694321,81.394310684486939],[92.062039973486975,81.208189191971385],[92.044197312965281,80.995390199697582],[92.151007572601117,81.006551871092583],[92.231963755244252,81.13672575788074],[92.383611405733447,81.255499910766346],[92.975121272079193,81.480029614100332],[94.749644402474345,81.633444013794431],[95.10048091226426,81.767248131892316],[95.879773312618028,81.773233280349586],[96.637887837761994,81.570285870235224],[96.919940716354148,81.439140943345905],[98.046064405511544,81.249759036917354],[98.20301810627592,81.132893219320081],[98.303098422198431,80.964738487387976],[98.355704884036584,80.721824184374185],[98.306093143987624,80.480618628584452],[98.568816181563676,80.551113613969278],[99.314830642125813,80.516077694524427],[100.03931214183099,80.347351204153682],[100.32490964601934,80.174061657520241],[100.5316667100594,80.112905872430943],[100.65142377608561,80.025185770775551],[100.77805902879184,79.814534513061759],[100.78227767648913,79.542954154538847],[100.99961273712731,79.663990269219468],[101.29512845616546,79.731241805274095],[101.57572987123173,79.856481004556187],[101.84959543389766,79.869353679674362],[101.99498755657626,79.839921017767452],[102.15272124033176,79.912761293200006],[102.2980594439656,79.929641124300332],[103.10589464642372,79.82716929325278],[103.28455025302475,79.768292064463679],[103.44891236542492,79.620420616118835],[103.91006018335671,79.636925559058724],[104.2351324069614,79.501276879887683],[104.62373879491649,79.432211772862374],[104.78530788897859,79.335252024033309],[105.31619628512054,79.288716382332083],[105.52227838087522,79.147760332934453],[105.75054746837156,78.882730191495071],[105.82873318753003,78.707849827975693],[105.99674606449102,78.760587212810165],[106.6771938124827,78.834628049248423],[106.82689420387293,78.798482174867146],[107.01875120760027,78.680682071017159],[107.55808872126279,78.687070270320874],[107.84703774306972,78.607222181985179],[107.97643052748515,78.544183433897331],[108.08250459508771,78.446896905531347],[108.18478899400758,78.231549430999095],[108.19317129204866,78.087861875870587],[108.1602745402205,77.947739857121149],[108.08882474449847,77.822794656690959],[107.95106460131434,77.701798672498043],[108.12350102537081,77.49772120436802],[108.17756169041166,77.236775946464761],[109.96125474370854,77.212789978989008],[110.49903130392821,77.257499416842023],[111.20656694572764,77.214754834535029],[111.87429853054572,77.095391106307162],[112.39159603046647,77.14354921374948],[112.52765904114969,77.125397324088468],[112.89281554262483,76.979098945063186],[113.09495046570009,76.756864530463389],[113.3369352679708,76.747230094921463],[113.52391661864282,76.683617478205619],[113.79486680497826,76.422857311318197],[113.9648668640734,76.409277540871585],[114.13905978183958,76.333919032661072],[114.24422016781416,76.237420580976448],[114.31783254611956,76.115142718237223],[114.36801545140717,75.908192950397051],[114.3602277261536,75.754078703146931],[114.22422546195752,75.490446763427286],[114.18256387710434,75.24707046211725],[113.95851107792866,74.931319738585714],[113.70034607868494,74.789035132114876],[113.83863327001318,74.614526961882774],[113.88635904422857,74.42217557898465],[113.85560621074038,74.226392364025529],[113.73876854123144,74.045957505327124],[114.05069660453181,74.08459622543765],[114.76379676831198,74.106386306397283],[115.35423664978804,74.202340180349012],[116.50338492211695,74.176024917533795],[117.34269163886589,74.098894554313944],[118.4602743304546,74.08907415790172],[119.13388294868523,73.965745251184032],[119.32464272998911,73.799107070140707],[119.42833713345303,73.56576028494807],[119.67654413209603,73.647720222979189],[119.98461237288383,73.667038794929226],[120.39171175598456,73.582388821154893],[120.59422177190714,73.468453324794837],[121.00485346288757,73.438549379690244],[121.77863571842596,73.468714196682754],[122.21463263398887,73.403823672517319],[122.43679108987827,73.508251226896704],[122.80749217032556,73.491788944298733],[122.81916751114947,73.64961914400358],[122.87322611336555,73.784382747473444],[122.96372629496845,73.897931320443703],[123.19072072318087,74.082882087637628],[123.37167625336394,74.134677693488854],[123.73442503006839,74.135013246691017],[123.90366255113238,74.178026794621914],[123.99538338679712,74.293507959436212],[124.11700184474996,74.377294508367783],[124.38879457364696,74.440875103671971],[124.78328963861433,74.378230629338347],[124.96223170586975,74.279764885682482],[125.06445959213926,74.15877785326208],[125.75301719709759,73.976858827503108],[126.33238795873127,74.036258693790259],[126.47891585984543,74.00145490906948],[126.63558355761128,73.899949316658535],[126.83605334549748,74.007410682852154],[127.03514443630232,74.047124389585633],[128.0561856919627,73.935007040960969],[128.49537689292092,73.763114179975091],[128.83326766342003,73.722679329391823],[129.25556649194877,73.587292880844643],[129.39039876147274,73.519177939751017],[129.49873460251806,73.41390338608214],[129.57068538554134,73.281078422586887],[129.59948182950987,73.110470086007055],[129.70942697360809,72.913828889735285],[129.72814402039171,72.709527119519137],[129.83593182517458,72.579604586935204],[129.89267698544725,72.451116420561505],[129.91041778137054,72.162981691388111],[129.88334950814109,72.005689912578518],[129.95309099257071,71.824252352481309],[129.94439284364603,71.616191784768688],[130.00009032237588,71.574643585790028],[130.53352693418634,71.416050535009845],[130.69767360834578,71.458556044708729],[130.88004063621833,71.446777285213685],[131.04738223134922,71.386829569194646],[131.19207839137252,71.281401478779628],[131.28093416678425,71.322191926169396],[131.58054586940037,71.580896091610668],[131.91224689327296,72.030481380576163],[132.41892460854137,72.367097761857892],[132.60404465032408,72.423240689794056],[132.74937627292979,72.416519133076264],[132.88662059508133,72.368244337467218],[133.10471913002795,72.183766830366622],[133.55859943765515,71.978877422281457],[134.12811981132293,71.881356140855118],[134.57408054854616,71.886259347972555],[134.90474844325163,72.000820886746666],[135.86095885519592,72.129882974736063],[136.87529678815622,71.976454683943714],[137.22690473321103,72.076694055274558],[137.81657115930548,72.087063272368709],[138.01249257147137,72.052355786094353],[138.18798799522935,72.094111614465888],[138.44700272452607,72.082840092667382],[138.67153248400132,72.134227309626908],[138.64842273963473,72.415127756502073],[138.6927860643498,72.551081273891072],[138.77382016507107,72.668915640654376],[138.92708405871812,72.781496172523603],[139.45866294075299,72.974208231277984],[139.32417533179964,73.080628719890228],[139.2398395756274,73.200130275879175],[139.19369382374862,73.338924048205101],[139.18968688658981,73.48513309754037],[139.22816164804786,73.626245940271517],[139.3393608863642,73.785791194761799],[139.50301892293493,73.890843590728409],[139.67001441150927,73.925924450498428],[139.60642308515591,74.11680143596557],[139.61116610919214,74.277450534736971],[139.4436346105274,74.201202019715126],[139.07969833713037,74.157221370626289],[137.96396650097387,74.314497903724089],[137.4976661879335,74.538907560621496],[137.0393026087618,74.65698135816649],[136.7685709584342,74.796130282966786],[136.60844997742964,74.930243069142932],[136.46414603092992,75.199387780656551],[136.2429685867117,75.005578506664435],[136.06581435586568,74.923669124490246],[135.47234231321556,74.890164917958543],[135.27739434201521,74.921327766302227],[135.14752769039009,74.993337866672164],[135.04453755697898,75.100313724600866],[134.95586773010166,75.329631392125364],[134.9712861980608,75.526451374438679],[135.15272662945867,75.971653662836374],[135.43741454717795,76.271317499356286],[135.57268759161644,76.328868852491041],[135.76781452199202,76.340195993907699],[135.90883822289865,76.29868369956128],[136.13229671709118,76.147511077582337],[136.46368842598935,76.009080941831058],[136.56885610743024,75.904995462623461],[136.6528508941995,75.729046123137067],[136.75294041179077,75.786108592764066],[136.83606805984752,75.999326001265331],[136.96156450446895,76.143531109524062],[137.1577186970473,76.242666973386392],[137.36206724752918,76.413671918675576],[137.5330520457274,76.478193628130583],[138.83447725587675,76.699017256308764],[139.86603123110714,76.437912483114971],[140.16945493174987,76.320337729227219],[140.58062680999319,76.257184631337694],[140.77429650992707,76.417432068989584],[141.32225268576582,76.609523262047333],[141.50560151171697,76.636512196391621],[142.75507362630532,76.357251875318553],[143.17346215578087,76.316567441120583],[143.72823288279514,76.361616916370195],[145.38591053751364,76.06773809574301],[145.70543592847406,75.891499644890246],[145.79443093515775,75.777036969434221],[145.84665125226712,75.642539197966769],[146.06375434069869,75.894249731083733],[146.34991862459952,76.045049979422814],[146.5472614438024,76.08147105276656],[146.90930453182784,75.984469610348825],[147.07379761223234,75.89188286493507],[147.46926093663006,75.939774629143614],[148.44169754710035,75.913465878915474],[148.63251193566495,75.869674006921798],[148.82310947421979,75.745579855337937],[149.10933362944832,75.761460250127911],[150.12074403203172,75.719026546785585],[150.54565942452851,75.618386932276849],[150.83960624786624,75.656045433460818],[151.02795843439972,75.611932632706797],[151.18546865203496,75.499624945067836],[151.30380225100316,75.289733508377537],[151.31022866936141,75.048868490187303],[151.20325550625063,74.832965738433728],[150.88643251891133,74.525939849511502],[150.75540425148452,74.451299246460152],[149.91752103640115,74.30183577878347],[149.06649129144566,74.272669601860059],[148.07158135664403,74.325616886898601],[147.53732299016738,74.463968692595074],[147.04961426192952,74.507717343278173],[145.95632925585883,74.737031014364533],[145.7608731622461,74.8830913591502],[145.65459214207493,75.127079249214916],[145.52618686048382,75.059255296021192],[145.31548614093023,75.01876134120171],[145.2017245834931,74.883644821534418],[145.07846318237367,74.808780844892425],[144.36595805081961,74.582332604466899],[144.19886707236262,74.559607510215713],[143.44501372338215,74.585447582763834],[143.28554909002426,74.496012775504141],[142.56213476696072,74.32928295405209],[142.79258801989454,74.278570370421619],[143.50133732836463,74.043005698959391],[143.64040874907332,73.970927064890475],[143.83628587865223,73.79227351934928],[143.9184145796109,73.666813464633137],[143.95964955458649,73.522643337043206],[143.98384178938562,73.162395671284415],[145.52036937536343,73.041003329379592],[146.31232458543883,72.938927811334608],[146.46295277819343,72.894525891337992],[146.59758625109225,72.800646620067539],[147.00258497485723,72.764528632912572],[147.38627080228099,72.838714100495906],[148.41293229626075,72.811860945423717],[149.62876442493811,72.647303288223483],[149.98304792351894,72.54152633718364],[150.29218279021984,72.356248980445159],[150.42226333685468,72.2140097970087],[150.50064961552386,72.020937067050923],[150.74704131234375,71.997711349315864],[150.95724758259033,71.873539117212047],[151.19417750139417,71.871180061893369],[151.72505112474155,71.765986536698762],[152.31762199480701,71.470202807037992],[152.46125942349889,71.358294753338825],[152.55518582523487,71.33739500407215],[153.74533365786161,71.379034185118087],[155.84939724218242,71.592933394775415],[157.45589129179513,71.574362438072384],[158.80815048035782,71.423444908955844],[159.46632864341967,71.276232773950937],[159.97257423027332,71.084789320895325],[160.08118620894592,71.272889413519806],[160.24442823212976,71.390202083669649],[160.44067923504915,71.433742462216131],[160.65186324288902,71.415879754242482],[160.83175682500092,71.344572283493648],[161.10026146228529,71.145684240308611],[161.19519171841836,70.973650598502033],[161.21650492364986,70.778322477829448],[161.16090969440691,70.589864265145195],[161.03699156356808,70.437379432154728],[160.81594702978001,70.332398522692728],[160.50491545055951,70.341806986979179],[160.49337670826037,70.199356878972907],[160.88096449166042,70.134754147576203],[161.15712401390098,70.040547411792517],[161.47249002384493,70.138140638878824],[161.6234962824702,70.12501224536868],[161.85328363239046,70.027680647521876],[162.35429567103895,70.148676384472907],[163.9633881878874,70.234573348176298],[164.58155041637372,70.10872580433923],[165.76716797478525,70.084394490115713],[166.81746568244861,70.006410265539486],[167.43210897819364,70.197391518298559],[167.58402145835285,70.317149410939649],[167.90238658623292,70.449242548430746],[168.33673769553369,70.515034113422502],[169.54725091564552,70.336298872883404],[169.76249128800933,70.218983723085415],[169.88430514787044,70.04314221751558],[169.99229935507654,70.118695540823666],[170.04191638564021,70.325986065080414],[170.16072128161488,70.482267580194829],[170.33053503577034,70.580765317090865],[170.47580915960572,70.607173296314556],[172.58807117098596,70.467506463657941],[173.13042261912202,70.362879159816444],[173.30527679173557,70.428497701095367],[173.4583948751463,70.446233625323117],[173.97944302263792,70.374103177773648],[174.78870347431882,70.35591924600385],[175.94321418754652,70.394851259627671],[177.01803661153835,70.138554571469413],[179.00423457817908,69.861240825734512],[179.16945268742751,69.776458488097717],[179.45013197827109,69.726875623338032],[180.0,69.51610585655834],[180.0,64.55456952871377],[179.60563346063975,64.347737913223327],[179.07833068654617,64.222145588008786],[179.22255271823352,63.804761340155274],[179.2907807674072,63.724203317568964],[179.61959085418192,63.590402410396841],[179.77594843327665,63.462957970106629],[179.85285431388471,63.332145516231222],[179.89250323643762,63.167936021669966],[180.0,63.02464863323496],[180.0,62.438792945777351],[179.99246817569752,62.419765563991916],[179.86427509599216,62.283193926480742],[179.5749799868305,62.109401111333348],[179.43397174183511,61.931045206245471],[179.26192509351276,61.84100451136527],[179.0685500268618,61.823299722995955],[177.94231101989314,62.051318692196496],[177.28379316573989,62.095014764493691],[176.84338091633637,62.021394740436349],[176.45272240571668,61.862031058199371],[174.90143627547388,61.463509249625794],[174.65438928294111,61.343908733356315],[174.24572610628204,61.301076554253775],[173.89093162296089,61.184411372949832],[173.73074430775378,61.184972648867671],[173.34399204181955,60.954182923235216],[172.77159209674855,60.735422507252267],[172.64247781262839,60.62886429698343],[172.27191824572199,60.459742304802916],[171.84608838894354,60.34868771920425],[171.00469061251397,60.040413867073795],[170.7025850601276,59.610363892211588],[170.5841213701618,59.523496811174255],[170.44552583156005,59.474805823425427],[170.102056032989,59.489115222365662],[169.94103951776438,59.534098115105586],[169.66040706291585,59.684934366046406],[169.34328650364708,60.000495684377931],[169.08855587061598,60.089417781042762],[168.19771534993532,60.075253129939867],[167.36317086211736,59.921916318709336],[167.18544588315984,59.853517905019451],[166.49667297170154,59.411074546955362],[166.30600554305846,59.357301407834107],[166.12720891506629,59.353251874144121],[165.94323969962713,59.412958604270123],[165.79569518720163,59.538019320343409],[165.68347772137852,59.74043142743345],[165.49975172380269,59.684098076322158],[165.31197482652465,59.455887591835577],[165.06528911324398,59.357125383398291],[165.14884340243742,59.081906795902356],[165.15270647599098,58.87823789491236],[165.05583357507385,58.648766282504283],[164.93482395177219,58.500972358014245],[164.76722290388341,58.409341239968235],[164.13359087505313,58.270582281467917],[163.65505952603547,58.045141749719498],[163.72460953596675,57.820741294494951],[163.67310297324656,57.517411550569328],[163.58501596994222,57.333915107707952],[163.39068105199922,57.169236886155701],[163.5740519041164,57.073925053941238],[163.72158848160882,56.870904562798138],[163.83381977232895,56.270861093548682],[163.81229773607237,56.082755522688224],[163.75071988541774,55.954384061115888],[163.65545676924734,55.84857289660588],[163.12196726948824,55.55612862188881],[162.91522834417674,55.538221754404674],[162.67476109095438,55.594139646142438],[162.4820135235465,55.700348497348919],[162.41503112433972,55.677733471925166],[162.24527527413585,55.4448388021342],[162.49811836568435,55.160155488353816],[162.59540436925877,54.851365039128765],[162.58226616279748,54.602195773964716],[162.45035360370875,54.390401033883485],[162.32683038038374,54.304018705860692],[161.76290967816828,54.035200371265176],[161.28835237643796,54.020873545810865],[161.04558393586584,54.078963402266801],[160.93095238379198,54.059934049508897],[160.39766108851111,53.788471470892723],[160.44414724958622,53.658627640176675],[160.44323681783513,53.440841963837251],[160.52441989786396,53.109506318999486],[160.47862700734902,52.919874927292682],[160.36375372187319,52.762200636977589],[160.1972869965403,52.660485923689322],[159.97632041328524,52.626173474443135],[159.79479161875818,52.649240687854871],[159.62997978838098,52.728501514647256],[159.16769984136451,52.59170951810065],[159.00764201559969,52.503300716525473],[158.89616624075057,52.054866903065509],[158.43831140190926,51.438506979051979],[158.06891621656021,51.170092939429601],[157.81676733869833,51.060450626399657],[157.47256485387464,50.792325291701538],[156.8777267611064,50.487232622009188],[156.74319051382699,50.28969444949572],[156.58355258726758,50.190870942091443],[156.43700788387298,50.155692041710353],[156.26647272414715,49.941586750924486],[156.06279108607728,49.781814080892389],[155.7545210334452,49.685689101844197],[155.57303650477292,49.5734887930901],[155.39140055781854,49.542009442756935],[155.27666011395098,49.131868483153575],[155.17516268891777,48.970299394382771],[154.92505241849975,48.814268284793037],[154.71198709332799,48.768524631017414],[154.58234044167511,48.539147630094547],[154.32193780024099,48.324183357180807],[154.18953475378854,48.261057819688993],[154.04451074768389,48.239070599608169],[153.89934678004309,48.260113952295072],[153.71624724581832,48.355728166525047],[153.60657472715951,48.454633604634218],[153.51397439186385,48.627776454712382],[153.49259801021347,48.773904471684034],[153.51484213019612,48.919902917438534],[153.57876614390636,49.053034684901078],[153.85335616067758,49.322976666535006],[154.11411387971026,49.403425841306273],[154.17118265009043,49.614253650173417],[154.43029984837457,49.954035939880718],[154.57634783537463,50.078591063279575],[154.73467225523123,50.134719962051676],[154.71968209303458,50.332200214300379],[154.74976300425831,50.47167271990628],[154.84836455999832,50.633773195565077],[154.99723870991662,50.745817526918778],[154.9773686679562,51.011406228603484],[155.02779058886051,51.151119651855851],[155.11703153739356,51.269855761988765],[155.2823825429542,51.377813753431639],[155.47663565605473,51.413332917372188],[155.7942799240202,51.385761099995264],[156.00610992986691,51.249846635958704],[156.03704570129887,51.257723287472444],[155.99213557099773,51.842029499715359],[155.89596804530913,52.271733951122421],[155.62964826139623,52.729351459853412],[155.46287240571812,53.628183772448821],[155.216233952518,54.422210479761169],[155.06556580049556,55.165429470107135],[155.05977299989078,55.416234200701695],[155.22674649800067,56.17404037217316],[155.54540200431859,56.937202734658648],[155.67933626308192,57.092377287294184],[156.35374078712985,57.508112873191166],[156.29218983121191,57.733603381413644],[156.32508808865595,57.926880414185973],[156.42977300334817,58.092649456458687],[156.61964211633139,58.235351556218454],[156.95836176707269,58.329214150385226],[157.21206122580594,58.291585035056151],[157.38584128377803,58.433317924573757],[157.56747326502617,58.509612604770346],[157.9624111188935,58.492813674882797],[158.09480723582419,58.517238239622642],[158.93906354253912,58.942889511894698],[159.2399120117766,59.167042708731145],[159.48601764906186,59.472344637392048],[159.59024873807425,59.555662923306031],[160.29032653884897,59.97579838776749],[160.65866256324034,60.097154117906939],[160.93418823364971,60.282856263702953],[160.57394738936918,60.242761534871967],[160.18081608924871,60.138778021025985],[160.03301488429216,60.158984964550413],[159.89775320138338,60.221895964303194],[159.78707123302908,60.321911056336496],[159.70571497022723,60.464707922212114],[159.50570432786736,60.546087698832324],[159.36934001374382,60.687865696785266],[159.30908565723456,60.822999350271409],[159.29101763385256,60.969850425561738],[159.31671810199387,61.115559648743307],[159.38496277639248,61.248804907457902],[159.12304570267236,61.414610267006701],[158.0902870315775,61.254254255800269],[157.55375493260425,61.292014687904938],[157.11444003553953,61.130006749844881],[157.03354160437442,60.978138240268805],[156.93082290256689,60.873652341183252],[156.34898009268554,60.581366382143877],[155.98734593844881,60.262516125121721],[155.17765246018536,59.904664093252919],[155.41809733235249,59.792063914501867],[155.53270182476433,59.700381390671133],[155.63509044886004,59.534265600228103],[155.66549496423366,59.390684820519105],[155.65583779327895,59.124569943508085],[155.59587974424073,58.945082310899963],[155.47351188241956,58.800732443227545],[155.2601500771739,58.700478185764467],[154.92133318849304,58.687751883900262],[154.72555473493378,58.642029537786563],[154.50125181853605,58.676604007411832],[154.36699911104131,58.623559499139056],[153.98755803224657,58.57629225027447],[153.85065267487161,58.602138151604926],[153.599797153053,58.711654022578742],[153.44672015145994,58.622765284885006],[153.23815610053475,58.587907685721753],[153.02359971919074,58.469305795737199],[152.87511431140175,58.430007664591173],[152.3798089395184,58.508330645585353],[152.15145081746903,58.414605421383975],[151.25710316183867,58.380210558530528],[151.08224322840172,58.439241705745317],[150.88494110453621,58.610871588878545],[150.61423250296102,58.519541503804653],[150.35760800595207,58.548157495828029],[150.2236063727004,58.600960900032305],[150.11021632179143,58.68977156135476],[150.00700792859647,58.851102910608212],[149.97202318041127,59.039400133296823],[149.99642390330322,59.188312498206898],[149.64611208827304,59.254871403812004],[149.53997974444013,59.11744685402973],[149.32985824228552,59.004745956905822],[149.16964606645828,58.853296945045479],[149.02981106603531,58.796564974026886],[148.75930363451548,58.759240134351991],[148.38563795135912,58.773803509239876],[148.1126058661639,58.900784764924531],[148.0217642372366,58.895210740484046],[147.80198372972117,58.804067169511377],[147.52668182364823,58.769005933639313],[146.66801505106355,58.914046443232436],[146.55212564676251,58.806634410297377],[146.42853098069091,58.746384351677001],[146.02463514259037,58.671386848338095],[145.72721115882064,58.742355273191819],[145.50792961552926,58.910363677006622],[144.50633554040672,58.876765342695641],[143.92069538371982,58.908489548394975],[143.57302007072897,58.846287546098033],[143.21792821325698,58.86359138184087],[142.74353230364133,58.76358393669932],[142.31203517162695,58.580195276961732],[141.84654607625245,58.21282595612491],[141.06913220472975,57.877946878930295],[140.8492666812673,57.517216603414454],[140.71419363385121,57.391380549900596],[140.25327448004802,57.235844761213656],[140.03732264569715,57.072709867128061],[139.6306656208161,56.866127559781916],[139.41279698305047,56.802411887207626],[139.19754022462888,56.645391345290463],[138.90628392811593,56.522271042506645],[138.57832491809319,56.273915635779211],[138.38314098351097,56.041140388527616],[137.9963721001003,55.743412743951573],[137.82022447030289,55.645374491232126],[138.09901266546757,55.56010332127321],[138.31279336484155,55.539567159277802],[138.55056237353097,55.395636414370756],[138.64248793888234,55.276933610955517],[138.69503036867118,55.136292371712933],[138.70345229764305,54.986393284937201],[138.65596846654756,54.817704599783568],[139.30826312024084,54.70572036962534],[139.66077069768517,54.774766663142856],[139.95186615564029,54.731152291293114],[140.45779611861312,54.465813187904892],[140.70724668067521,54.179645945629446],[140.88616875413561,54.063370238927952],[141.21789662408182,53.947053036684316],[141.42877216096227,53.799049821097611],[141.52681496110569,53.768278474881569],[141.63811250210225,53.840913128602118],[141.95315734679994,53.958653723772166],[141.86076791521734,54.12351304807666],[141.83679186017051,54.31660079362468],[141.86860489592181,54.45943831923281],[141.9722595419023,54.624099920898409],[142.13091795129768,54.736728681987941],[142.35179199111101,54.781202422838952],[142.5179253592465,54.88422292259196],[142.76407097549659,54.910744669600405],[143.02644492202035,54.816035091958696],[143.14518166058519,54.714033627063365],[143.41095956723495,54.387286616363227],[143.47542412513937,54.162929008909529],[143.43489586748092,53.912255658032727],[143.6900640202482,53.476789785622017],[143.82138781059604,53.016097805183712],[143.82074094045089,52.562814396580933],[143.78313113701282,52.417212600961491],[143.67813324449645,52.221760721873295],[143.68510632339263,52.058468441139041],[143.7529171467483,51.882902178859624],[143.91794790021703,51.660887451791957],[144.27064091345522,50.503211751221762],[144.49779779085029,50.113638325629978],[144.73200657284417,49.515391550642391],[144.80653484631762,49.395128105325767],[145.10417430116729,49.122507477622534],[145.1769370340935,48.988743028816344],[145.20606281659076,48.839280552384167],[145.20752556964024,48.56352706873507],[145.14117980674354,48.381727973867292],[144.97027000476911,48.211746839183434],[144.73969059073553,48.141483901703708],[144.54812293593267,48.16895215214717],[144.41924096187435,48.236580923269052],[144.20815027753031,48.507930470373445],[143.8857786908406,48.759625188338802],[143.75782124755526,48.801660273100936],[143.43243066893521,48.776664474921716],[143.07180687157495,47.959410243037155],[143.06641456833006,47.875588727999151],[143.10011044684191,47.820960437791143],[143.33762996235831,47.625843142644719],[143.52456696279785,47.27316405327813],[143.79170106108168,47.147176637769377],[143.89151809312872,47.043678859940911],[143.95775328678658,46.916053905393561],[144.07915433968336,46.324686902490804],[143.88449723322037,45.800169781603088],[143.75724366817025,45.64962011503394],[143.63068333785731,45.570468502918786],[143.43656203416137,45.529239007175327],[143.28875619078681,45.550117885825244],[143.15370079016898,45.613698808068499],[143.01396641111478,45.754614581416277],[142.9515247509579,45.890200472961808],[142.91288911727349,46.086524225464757],[142.76090970293177,46.110105880293624],[142.62938289582891,45.819465139148228],[142.40704880723982,45.541661915069589],[142.2368224762383,45.443544830193389],[142.04193461975581,45.418591082849701],[141.89767513082009,45.450659663809418],[141.7690908412753,45.523496979211806],[141.55091154868705,45.76272030137708],[141.46572331351868,45.903568480991495],[141.33104542194312,46.472848985352371],[141.38356348242249,46.822961615749819],[141.5176455196092,47.173674129205772],[141.47046211066453,47.665663062359933],[141.65571540990291,48.094901489888599],[141.41226394419391,48.541238302080686],[141.367286910822,48.776772026534104],[141.39587297360299,48.918700745423592],[141.54098064149176,49.231700665021762],[141.64190721646861,49.635027488168674],[141.6509687944432,50.180280421792581],[141.56739979636723,50.66467673886612],[141.69865151363632,51.084192061873324],[141.42003295005267,51.337889705256757],[141.30346811635488,51.254823124528116],[141.17343316933281,51.051424068239129],[141.10944396063203,50.800322134111916],[141.0030402843071,50.618767997984754],[141.00321772443263,50.408451333445356],[141.10158362720676,50.231068572224714],[141.11373073265767,49.980415230903127],[141.0055347151692,49.755177594896338],[141.0085671542204,49.504746793154538],[140.86125525808211,49.092988188910418],[140.87708794190715,48.933427391707887],[140.84563378212349,48.787060528694816],[140.67993086931182,48.53821534930232],[140.51607993982259,48.126738570139395],[139.73036327722039,47.525478454704874],[139.37516044062039,47.051654833955624],[138.96247921123094,46.712682711839832],[138.72982122452925,46.234541098976955],[138.60213290247634,46.118751430681804],[138.4644115503601,45.902026745978546],[138.04347423553159,45.469745078764554],[137.13790064545486,44.794006322551326],[136.64978487267106,44.346644237136545],[136.46472162196554,44.107331574889592],[136.17698097650739,43.955444399495541],[135.97164336226095,43.714213388963493],[135.90635226047203,43.568841171367673],[135.80543469458999,43.452853919022509],[135.60691662908272,43.314568041498191],[135.40268280120671,43.106198285029315],[134.15158159520229,42.468057097401058],[133.30119352569596,42.217621222426374],[133.11866008652484,42.198962044258295],[132.59875024885363,42.374003331573178],[132.14181152515695,42.410638882317684],[132.00975337415406,42.479378679749757],[131.8954904271412,42.596274379001493],[131.84761192378613,42.596277263585151],[131.4306165849213,42.207014742517316],[131.17231934467503,42.126934665825345],[130.9999145678058,41.912587141019529],[130.87126575627249,41.837896032367624],[130.7263360405656,41.804360142254232],[130.48362396780939,41.846217434379056],[130.32251432542139,41.960923222579595],[129.9754121654797,42.508207665353851],[129.92561341134382,42.702863017030531],[129.95646229553896,42.90140512388389],[130.09964931351666,43.106570167638992],[130.37810834163758,43.269975776305415],[130.70955614008201,43.352475977158853],[130.72236775224616,43.37847071620272],[130.6747409049473,43.721067887376492],[130.72073066601345,44.085254084009733],[130.48258409913555,44.816649303613637],[130.49508971163576,44.958628258451995],[130.54714617811203,45.091310353170925],[130.634523512127,45.203914151781099],[130.80956816159301,45.328707627333408],[131.19881595390393,45.448656604478906],[131.35435160025776,45.605358122207065],[131.73100653722921,45.811760299005691],[131.93387551732704,45.819788352097056],[132.17238593161093,45.714711128728318],[132.69296969186399,45.602923123060442],[132.79109126665898,45.801004847711631],[133.00990793668245,45.968757751288749],[133.09263614535158,46.148907806419672],[133.22310084337954,46.285447567614753],[133.36732869978229,46.512131330686017],[133.40934009723588,46.700784569509779],[133.56682923118748,46.940317648523425],[133.66844988340819,47.220865428930232],[133.67596217051872,47.391640480962678],[133.7200675212529,47.524501411170476],[133.93449967692877,47.765188442163186],[133.59862099860089,47.61463894218997],[133.2256091471491,47.598758973344935],[132.95977811686333,47.488508138365653],[132.74454280632804,47.293352052585504],[132.55247687433115,47.221102889393805],[131.76596980566703,47.180745074714046],[131.32524451435177,47.225739735909144],[130.94393392179936,47.207724142418584],[130.75240912819959,47.255481753423794],[130.59429165802104,47.373640128483245],[130.47604602774706,47.577296556033978],[130.30154894134051,47.766456586806768],[130.23677570812183,47.956911956444159],[130.24533493720114,48.199858557564497],[130.08203186896341,48.433476327910284],[129.79295137749813,48.536173567677309],[129.49505236211786,48.790252619663555],[129.34572140981933,48.863584701341082],[128.94938399340674,48.888303761651997],[128.61111936113156,49.009520944604482],[128.48281295112798,49.088186522029957],[127.87137559740052,49.085500225314625],[127.49618945153115,49.22010520621695],[127.19745361337624,49.448426976184685],[127.03702032696546,49.694300296085459],[127.00308383206189,49.977375497699903],[126.87510043713655,50.159864219723026],[126.80691055057284,50.501009885300675],[126.49991796877718,50.837082798517393],[126.34366622722553,51.240313357978422],[126.24715262517556,51.375727903956346],[126.19592952523392,51.529372442028475],[126.06630374841683,51.696920507157095],[125.93377620141486,52.01653879132224],[125.85615339387671,52.112868242340006],[125.68556870962993,52.227291305585204],[125.56955427146481,52.417458601921574],[125.45555761848503,52.4718548288728],[125.30815808806504,52.602629912505165],[125.09575953625142,52.680319033932541],[124.85898658049462,52.636216193926522],[124.70059061798013,52.646687662808702],[123.55555877452653,53.038090804691748],[123.30680012742501,53.051473667071086],[122.77506235166661,52.969490387439777],[122.30829811884564,52.975932359031788],[121.10530748347374,52.789057544313046],[121.18058305037948,52.623776530005529],[121.18256130499418,52.37919997392278],[121.24510948367221,52.163380555717332],[121.20480763179773,51.889753685461557],[121.01814216651626,51.60425573944093],[120.41025936019889,51.21929953488479],[120.23954604571561,50.998231064693059],[120.1291888047575,50.786312688777961],[119.95711714210586,50.611703474638261],[119.83334010829209,50.38850964066873],[119.82581042723105,50.138147490479362],[119.6494125474586,49.753265181561645],[119.44456730743346,49.604528707568704],[118.65845170319581,49.384488371470731],[118.12021508310505,49.078927518245713],[117.9335084035863,49.017374635366536],[117.11250417589441,49.143081645671749],[116.52122327179885,49.350451658063228],[116.22909009947367,49.48858413884021],[115.78629841353721,49.385536467693633],[115.35158200071382,49.40272452652296],[114.6220680067815,49.737897557473822],[114.36343031642912,49.761502965984633],[113.76465373371359,49.54252331443989],[113.52198791063309,49.415333617306338],[113.32350219257329,49.194422717097815],[112.9368368149696,49.041066001650336],[112.53034259031385,49.027453016939702],[112.16984837098074,48.932782637543198],[111.43288564155813,48.855891113906203],[110.78314536076579,48.64871745779557],[110.40418351511902,48.705364389297685],[110.15858124002156,48.672608356407665],[109.19887699963154,48.833400035697686],[108.53190310962252,48.829803301856806],[107.78363045555849,49.161352605934191],[107.5835422676298,49.337408470624005],[107.48912170540032,49.469226324529934],[107.06197136365294,49.51997821529968],[106.54961720606369,49.812847158420979],[106.12813425939302,49.812945029055378],[105.77913439033158,49.911714420629117],[105.38490076395874,49.965214515418388],[104.58814779158205,49.818574484496068],[104.14390936449304,49.658617554697479],[103.59541682341128,49.640184441397516],[103.16535078304713,49.720302690671872],[102.97815054446066,49.815870964290916],[102.49997272117032,49.92216769646496],[101.98712000947394,50.185714090753393],[101.88590216350296,50.290470013648985],[101.81917319487786,50.419953991310095],[101.69650506643944,50.928393701558896],[101.54511063658296,50.961494166151226],[101.27933819022304,50.963393126305071],[100.39770775133069,51.227225999541986],[99.838891830711773,51.262727643863478],[99.522388477132267,51.402484630668688],[99.057793483760278,51.510610741014254],[98.879260556523434,51.362184123331389],[98.671674300241449,51.286116039973514],[98.545432045457773,51.126446738959679],[98.403280035710722,51.041225287156948],[98.670892573445698,50.84394424350689],[98.745064227135472,50.714866375564569],[98.777944613565836,50.569671987271498],[98.726665328019209,50.151269996798142],[98.521611789288613,49.804283840814939],[98.416100500715132,49.688037616582953],[98.214790127529909,49.561698905465299],[98.007871829627135,49.471353026107913],[97.813287280454261,49.441498193439884],[97.495420201627084,49.26050472292156],[97.143828380974213,49.235256736266663],[96.999786268786792,49.276745191371973],[96.80322387292135,49.389320218678613],[96.253782261134631,49.405135678647689],[96.039400602525077,49.470920818617699],[95.805676611244934,49.477430955194791],[95.487994271742267,49.412377489026035],[95.028340134427708,49.442684277472736],[94.799839621976545,49.535190684057262],[94.495536206039773,49.538418199854902],[94.354167727182073,49.597308159485856],[94.014466464307887,49.855787526561976],[93.878633247454147,50.071376758207705],[93.030381543986664,50.109503684854253],[92.793084532093019,50.208688136617333],[92.574215509311557,50.191121497775519],[92.396503481506869,50.244492893384418],[92.265851283265704,50.206265548988682],[91.897394781854899,50.183033775487701],[91.587018746842446,49.998808093722559],[91.18368304143516,49.932327534764681],[90.806544607152603,49.746117877176715],[90.336874366531177,49.637185061930808],[90.103262846962195,49.499401176187405],[90.002087928781762,49.358823437395102],[89.888738318979179,49.27625596807853],[89.469693392677485,49.136865490955259],[89.280420495325302,49.032087051271496],[88.864984431007386,48.949788353080208],[88.515294936621288,48.957079193109237],[88.394635039905097,48.841601694712793],[88.127178687416801,48.706102757061714],[87.415899432680277,48.576938166205572],[87.09086452418812,48.643168779076746],[86.92622574236303,48.782729937078798],[86.689591519184759,48.896379561156756],[86.480614432673931,49.09926903716169],[86.347718874609896,49.028277632582757],[86.165941358807501,48.99980548832373],[85.027358247799484,49.160160966975589],[84.865522861346861,49.276718196235961],[84.570513034285128,49.664287967389569],[84.065433244984291,49.811000881597614],[83.879336534558647,49.961930001692501],[83.726993659587791,50.265669343261948],[83.642276798003138,50.353575653280274],[83.302068596857822,50.483454847090954],[83.13957544163091,50.412206161798494],[82.922117742756271,50.391173094465906],[82.604982728209094,50.240275736839067],[82.067144140660972,50.212049887934157],[81.846493921490747,50.26192651734042],[81.350597457288316,50.253617490272852],[81.174276548404364,50.334052594554755],[81.050442959581886,50.463012566588333],[80.877815737038219,50.508042532906039],[80.760788007080492,50.577921301783206],[80.627094888513298,50.489857353500902],[80.453333127174872,50.441178850681197],[80.219056520652941,50.282541979472683],[80.016051230467582,50.260942230959493],[79.797069866149187,50.311974898463568],[79.609752573552825,50.445872408999158],[79.07202641268114,51.187610403246616],[78.110137400240546,52.296583644829617],[77.565067470559413,52.855593967642221],[76.302606507223274,53.524067257082116],[76.097099296873736,53.731674408116291],[75.757000516474662,53.639951679489634],[75.626550987057712,53.537127917467934],[75.214288127221792,53.353976496718218],[74.945608084980591,53.324000781195217],[74.820178462720079,53.264256694642945],[74.631579916972314,53.071774622694065],[74.500391032820545,53.010416128806042],[74.260818441459463,52.998448834886148],[74.02460752322699,53.098640698042971],[73.881554344079177,53.113802240892994],[73.539774985001273,52.967955999031041],[73.342981293965622,52.952028477297461],[73.155364639629624,53.013521841132672],[73.038459546278489,53.10510345702869],[72.836440657167685,53.379633032539019],[72.787470318154831,53.566422748934791],[72.578967331102263,53.459887847813334],[72.380102471201198,53.446567999089353],[72.150036656408474,53.539686585301176],[71.995272684893195,53.727814343143372],[71.288170181799799,53.661414542822435],[70.941652348495069,53.735948420654744],[70.814672012064491,53.797220222396334],[70.639021887736362,53.980150527583625],[70.57395916964829,54.117374252302263],[70.556415669666819,54.318489031910012],[70.644591027544493,54.548469088062639],[70.53472245601796,54.761745994629827],[70.31663026523637,54.681120239374103],[70.13576828981553,54.664950684978521],[69.421157720518664,54.858730347234889],[69.052685569806087,54.882703157067134],[68.622360034779106,54.738682731329575],[68.432696227199685,54.560546313374857],[68.290822937357859,54.495450795015756],[66.84019207924193,54.245247499621193],[65.622275903738327,54.128289817965928],[65.447467761622406,53.957186893715019],[65.203953937183385,53.854016547032316],[64.47942330514978,53.878591342196898],[63.904685178064547,53.740161329777749],[63.341738390788443,53.67974775913072],[63.161681583552735,53.613234450171277],[62.763019335723634,53.577315107713069],[62.565488353627345,53.517897980133377],[62.312599243023271,53.508371484243995],[62.498780615830029,53.282450466256776],[62.572417761393012,53.105289871891699],[62.573918542792136,52.913440834735802],[62.503061995147064,52.735149906592291],[62.370280556239742,52.596667435195542],[62.080279247690285,52.455369591054001],[61.470868475218097,52.487028249220565],[61.494091393219392,52.350903701791282],[61.47669905135028,52.205485770447936],[61.417917907057578,52.071345376130964],[61.327819913945326,51.964826983738398],[61.624125670461126,51.868097794543125],[61.857511256294103,51.722158275354687],[61.962940035946374,51.612825766733778],[62.081754746812763,51.284831844103955],[62.047627662688271,51.040550762672126],[61.804273029917475,50.582268705670124],[61.698536987814904,50.468289073939687],[61.064664062161761,50.210896739058619],[60.400556917100303,50.179530004912138],[60.153898135688578,50.242506747880789],[60.056418438687956,50.147271577736142],[59.918659132076172,50.074126985275797],[59.495367385403675,49.993873167917648],[59.313641171048204,50.039089224235326],[59.155057675009907,50.150472410696992],[58.682556323776268,50.236911036986925],[58.221056414332153,50.568696874687433],[58.019631174377309,50.575495495108669],[57.785086303404441,50.442974673317885],[57.432314471391201,50.389192639447373],[57.2477304201647,50.428474816888247],[57.010926351610287,50.553588944382419],[56.547020528174151,50.485301011322022],[56.261455719278885,50.260477709072376],[55.807410858191119,50.098023013568316],[55.564421017348117,50.098178071639879],[55.201341530191137,50.191467614344248],[55.027069895032255,50.281120374665669],[54.731512119726261,50.070424558895759],[54.482265991203874,50.041145741088123],[54.336934970967256,50.084346857271747],[54.13373283856739,50.215471903802495],[54.04006552334365,50.331595017250848],[53.963792251587741,50.524035863368354],[53.854156536100568,50.623766429301661],[53.431363557592235,50.822982779164484],[53.250294477512334,50.976483135073501],[52.472413325774824,50.991712220743075],[52.337882245610423,51.039642598327553],[52.144463640426146,51.183307878736358],[51.726616858469932,50.998236176524543],[51.316506041351339,50.976041467372497],[51.12215821879343,51.029584632151249],[50.93439772537014,51.181722380583942],[50.513832968148236,50.866167360130191],[49.996716074679632,50.663508425649439],[49.768140989035174,50.612082280050387],[49.625336911585727,50.453505239109468],[49.278441442107649,50.271247073978614],[49.341055040504365,50.057432571527698],[49.304499381272862,49.820726091873595],[49.113628617391299,49.576116676804794],[48.951878286672766,49.467232722740178],[48.533769356465754,49.338739872371434],[48.339404489046551,49.337825460206162],[48.062239794541206,49.44089226482442],[47.681642568200225,49.75064584593634],[47.583084793425535,49.630548644813082],[47.386747675529342,49.504261984969823],[47.507047952491085,49.303539550729425],[47.523639618418571,49.064195999143841],[47.454545349856204,48.88440158764746],[47.278666922820555,48.683652452429989],[47.45666982514296,48.542450113297981],[47.589605280015661,48.293839665846427],[48.281483611804077,48.19533269730254],[48.521674349710125,48.060945362119931],[48.957378903056664,47.614944337705104],[49.360377817338787,47.072861877487092],[49.428015273654644,46.948140909057003],[49.45850566457603,46.782238059982404],[49.576398812080761,46.699503760397683],[49.671032678197349,46.576344756259246],[49.745585117066945,46.295021845200182],[49.691756258419993,46.066108214493099],[49.539238418799364,45.887118536175635],[49.199758592157195,45.71828578954154],[49.087441727236687,45.547957651627136],[48.96905900536369,45.458176568797228],[48.780402064072874,45.399635033266144],[48.583204306558883,45.408884013364037],[48.449488189471985,45.338217253672177],[48.339382667474325,45.135909335341587],[48.183968283869952,45.019557344043932],[47.995951265318716,44.971173284218835],[47.778014292455438,45.008395314693807],[47.530669236335889,44.590858396574887],[47.705068644658333,44.380478410678087],[47.932579517593489,44.294012026045266],[48.094294537505597,44.105933942684068],[48.139875373374842,43.963206344240596],[48.141123012516587,43.813382369681946],[48.00657687253846,43.406318268760096],[48.007014490065359,43.222160187858165],[48.132371961545104,43.076526636376791],[48.195019567744254,42.937778080365888],[48.478461004761584,42.656319136812641],[48.72244206204973,42.339930521314237],[48.978007582826812,42.136933400174001],[49.0451073301763,42.007639734589212],[49.071675125550207,41.815784633306187],[49.000161100601737,41.585384339323227],[48.760417313633376,41.264826723886145],[48.646650458175287,41.172199186454037],[48.367177554203252,41.038675201713005],[48.133222187242716,40.793551727167092],[47.900368167912049,40.714507531302168],[47.490482012687764,40.728343238081322],[46.985812026577669,40.89799111080206],[46.87589608857882,40.99672476915012],[46.778145450541956,41.158117506451461],[46.58490463505801,41.299911901608432],[46.403470462560492,41.329393450088787],[46.238171338171817,41.426084957917176],[45.76176145671969,41.574114442109575],[45.40195629805676,41.76485834820987],[45.250465281025953,41.890232271817673],[45.158726600000485,42.065607101929977],[44.991855652601451,42.169224067295907],[44.727981499136916,42.11904828021234],[44.48040151389818,42.211081841641715],[44.069547303692744,42.079541699744858],[43.754845826264706,42.076851684829748],[43.478545213385416,42.18819136854092],[43.294688587592198,42.390592463026636],[42.875997817224444,42.536977783522694],[42.698530547650456,42.659952115196276],[42.486924459274974,42.661660866023503],[42.313595373587134,42.715714671944461],[42.061135386609912,42.690503671236115],[41.500219232095326,42.725969343790567],[41.202400287297124,42.848926052658264],[40.954667266394395,42.891411605441228],[40.55449873330214,43.032107942996142],[40.313967221975467,43.048548006695945],[40.071049098167457,42.928791756599203],[39.827478747193993,42.943378149009312],[38.508331222191558,43.823588008958545],[37.961729616341621,43.97068281880572],[37.674378280548531,44.182453207752808],[37.309583297845492,44.231510496491552],[37.183835424539083,44.302051643832804],[36.940811530082357,44.526347957217638],[36.468455363199553,44.661196262827005],[36.338307643757588,44.73649125894967],[36.178920614911775,44.948800657410075],[36.119426357047224,45.180652288985478],[36.154565279786354,45.369577420889136],[36.310025882852102,45.657022932163152],[36.453836387193739,45.794598811669459],[36.750921699717715,45.913388343193269],[36.983078232301033,45.912863384714726],[37.194928365242212,45.82684890760062],[37.464154666030147,46.180576099938321],[37.285333132561817,46.501206544698363],[37.266971866798016,46.646913199531816],[37.291735257777141,46.791669338643146],[37.357486961286952,46.922986812075898],[37.458554560679943,47.029536827074217],[37.703500644310139,47.131260257752253],[37.718350262356687,47.448904771002461],[37.861896960885147,47.821304843893671],[38.046326900179508,47.99697281231078],[38.38561331397662,48.112057600431477],[38.644715917732235,48.303739853951313],[38.778823873187122,48.335749878945322],[39.213691512064479,48.339517066217205],[39.156127195999403,48.486637502914498],[39.14640435792527,48.62842538769759],[39.210342867598989,48.85606752442321],[39.196475367356982,49.105551194227886],[39.253352292420537,49.255984057373027],[39.082264156032672,49.323120668065926],[38.812211490435693,49.336436349954361],[38.489316201780333,49.468179138851461],[38.339091040093059,49.49540126025277],[38.0814406876598,49.421424622604214],[37.892897653917117,49.444540885731158],[37.420522866521473,49.697621409079048],[37.25354719444244,49.873253248678239],[36.96035134293809,49.821072639903988],[36.748467618508485,49.726408471240013],[36.597183214744398,49.709965541254441],[36.356072558459971,49.781512073825795],[36.174528709996075,49.798360048814651],[35.946140208990002,49.914834827538598],[35.644508598580465,49.847127823641024],[35.366790433574309,49.922212638709922],[35.004183343979719,50.250306725976358],[34.938636411131711,50.378324344062719],[34.907620034135519,50.634488172669727],[34.878471744288731,50.669304278660327],[34.630095247154458,50.679202461787142],[34.084615222656097,50.772539218782789],[33.95172199350344,50.829874143580469],[33.840818296777904,50.92287024031998],[33.728916163736827,51.134864855696783],[33.708591325683528,51.354234955554176],[33.626797724202781,51.540142253323232],[33.641182904388138,51.801087044909757],[33.608946629973765,51.836795174762862],[33.309108690077849,51.849296778618786],[32.872100088330768,51.75652308207016],[32.647048889317659,51.772556626286971],[32.466443932477475,51.649186075371723],[32.155500315832526,51.55188042424566],[31.644131484138693,51.615771679191774],[31.462502434788188,51.702095300679034],[31.142560356623473,52.065945607534729],[31.088103782324545,52.210451848679369],[31.055353183062795,52.527195775644117],[30.831297362798768,52.757940640313656],[30.759894375125423,52.98888785112333],[30.773128646430266,53.134254341154538],[30.854494612349065,53.310456895099684],[31.08019505520846,53.564460635052903],[31.274128893687383,53.674013731398041],[31.265229864337808,53.710679276710358],[31.086269944375438,53.809856535610706],[30.893645845535676,54.025023710969172],[30.740832399386811,54.120097350753056],[30.605643542162809,54.320575829508321],[30.485593401478951,54.393865563891701],[30.383971662022681,54.504628690676157],[30.300984117959118,54.739223355382883],[30.310352191531717,54.889248977803611],[30.370102611617099,55.050115380327725],[30.315040895417319,55.283374739305167],[30.172277056455876,55.341655368500625],[30.020020856923388,55.341760878503834],[29.598062967319798,55.198418026264456],[29.401034096187225,55.191456341005583],[29.216630651508428,55.261200234731227],[28.963943439795809,55.472121713492619],[28.75582072515137,55.444354116570061],[28.612471601038877,55.477232715468652],[28.450997785020164,55.569240320627955],[28.153016694349393,55.574237814072561],[27.843276786636444,55.746789909460617],[27.740661245990083,55.853413507463777],[27.67374918345314,55.985403343134315],[27.663064697313104,56.261454147587948],[27.580717125153217,56.348679505026048],[27.33155079632656,56.4521344050507],[27.203459031271464,56.601656346264114],[27.13984264219545,56.83846067892312],[27.185807430482949,57.057076926800683],[26.986081391112339,57.187812101379052],[26.883415911711396,57.354434036209319],[26.85309117760621,57.498477086362797],[26.866025069093865,57.645108270791802],[26.946538937885617,57.876802417223978],[27.051536600543507,58.02418676229523]],[[162.96391452991671,58.392538393992737],[162.95704342606089,58.410748784274034],[162.93803224101657,58.40041622537548],[162.96391452991671,58.392538393992737]],[[169.72421073522318,69.384777362156299],[169.71245458801477,69.37611563408808],[169.7854884015623,69.297046755828745],[169.75504647520003,69.335341877371704],[169.72421073522318,69.384777362156299]],[[106.88127527636934,77.602623005024142],[106.49625858172308,77.632181263687244],[106.5367056885417,77.536993594263393],[106.83167542048881,77.528749913251033],[106.88127527636934,77.602623005024142]],[[105.56002192206674,78.035617373294386],[105.55024286147386,78.060791668495796],[105.51115198439753,78.041898518903636],[105.56002192206674,78.035617373294386]],[[85.357521216476883,74.39800069138839],[85.339125143659444,74.33701005634336],[85.287529118138153,74.238108622807857],[85.440260633668871,74.290340121081968],[85.357521216476883,74.39800069138839]],[[80.040274051867101,72.877643253834862],[80.034803409471095,72.72125578529463],[80.156950740770441,72.696723273282402],[80.158098545864121,72.751367090428019],[80.040274051867101,72.877643253834862]],[[73.204179002641695,70.846447237864069],[73.188448603744106,70.351777366253998],[73.134810994228346,70.211034718741359],[73.045135177647182,70.096218806461479],[73.076420798730183,69.83913537015961],[73.164251712838265,70.083067638071697],[73.449173519352641,70.498912693277205],[73.549380792547552,70.589847639798236],[73.204179002641695,70.846447237864069]],[[73.114763209114116,69.481107366431132],[73.070995056375367,69.257830012333315],[73.276620483609648,69.190101880746283],[73.28422447745379,69.285556600878877],[73.114763209114116,69.481107366431132]],[[73.969008412915841,68.154565815419119],[73.649912082884796,67.922621902887286],[73.637313276611494,67.745290245275868],[73.584187434275506,67.613918616231061],[73.410732158491669,67.404408249614946],[73.276929122437394,67.314138052805049],[73.446214087610826,67.385386089355748],[73.772614957997234,67.812280418960611],[74.119244759503204,67.987022200755263],[73.969008412915841,68.154565815419119]],[[59.512882861757348,69.242425135915354],[59.658905895519787,69.145825737632492],[59.82882582169276,69.201517320187108],[59.519058587202409,69.241333628218683],[59.512882861757348,69.242425135915354]],[[39.284006469486968,65.629102655245987],[38.632149906373719,65.569284222714231],[37.847407081250999,65.598597963308578],[36.920416076642681,65.77576337324615],[35.466070788301828,65.898738684982703],[35.27915019062862,65.944252230229523],[35.274436171307116,65.720030880469253],[35.224628658325585,65.546759248059502],[35.42773745675246,65.640519121658329],[35.905649768399527,65.673742319270374],[36.122487455338145,65.576883156247675],[36.279569160094802,65.367617747155677],[36.455222371710541,65.432164825929817],[36.569237586724888,65.561495550018165],[36.697925757431008,65.636665281444024],[37.205398762592644,65.689624152483148],[37.688857236820638,65.581374053557624],[38.152274025588916,65.368916618248051],[38.487532506505858,65.351083571697956],[38.695710704606462,65.273328308145466],[39.338167352734672,65.151760599573336],[39.250557280940058,65.413942288005259],[39.284006469486968,65.629102655245987]],[[142.0843296833809,74.393310631632986],[141.88378674434611,74.480816946804524],[141.51699177863298,74.449975119554892],[141.54722654391838,74.384223584382369],[142.0843296833809,74.393310631632986]],[[161.21421240776402,60.468276895619084],[161.46246680181085,60.574082104590254],[161.55928970573439,60.668408792685291],[161.30199776997091,60.536503932934707],[161.21421240776402,60.468276895619084]],[[162.47668656274229,61.138727084629345],[162.5310335808114,61.157732218641307],[162.51274780364972,61.157214461274918],[162.47668656274229,61.138727084629345]]],[[[165.54492210031458,54.839253148468536],[165.35757767788868,54.986914750615036],[165.25996686417426,55.204570465928391],[165.27430661493801,55.442680215646028],[165.36505704600435,55.611312333951552],[165.51254233175897,55.73346192481786],[165.83134798465622,55.841107977776126],[166.33234457667612,55.808544286637243],[166.47344207474691,55.770925216912559],[166.59765315035727,55.69414431391899],[166.71901137670227,55.542585528608889],[166.76962155449155,55.384087302954406],[167.03979863954319,55.151774981730178],[167.24108901079518,55.313512276205081],[167.47617426184487,55.354465232939994],[168.13441254845287,55.121745913332276],[168.50655462707098,54.775206209878647],[168.57452358341081,54.59128264699423],[168.57579263130864,54.443808431849774],[168.53398323295269,54.302379195748635],[168.45273284858109,54.179299426271648],[168.29568524334292,54.061895398661946],[168.15464364387981,54.018796421459115],[168.00716398899269,54.018718901061384],[167.86607715885884,54.061669581822308],[167.23744152797872,54.362611354089047],[167.09852061005569,54.48444159913285],[166.98551330321783,54.328436404721309],[166.86454780657527,54.245304147349621],[166.72465480187148,54.200879257607085],[166.57788949805337,54.198990027904919],[166.43689932096922,54.239799261657474],[165.98056381530438,54.502711513124297],[165.7104962143008,54.763991654899037],[165.54492210031458,54.839253148468536]]],[[[151.287587641193,46.594093969377113],[151.22056264881235,46.782537473904874],[151.22216762465641,46.932980222988732],[151.26845328505181,47.076134804966159],[151.35522881583182,47.199039662303925],[151.474637353324,47.290566695869664],[151.66336443694939,47.352101747779223],[151.99157166990688,47.578836800426089],[152.259641664486,47.641169756821718],[152.50374232291477,47.592926837283393],[152.48621266573093,47.767697911573386],[152.51815998322971,47.907975447591411],[152.58872817382419,48.033349083906529],[152.75119887187114,48.198320472654949],[152.94544711268134,48.290249605499291],[153.14296922219796,48.304339398637126],[153.33046999291957,48.240645073949864],[153.47853917444147,48.109157342575408],[153.57034052654595,47.934863948151865],[153.60060437218556,47.748416494147712],[153.55954135157765,47.564046263914946],[153.48509373081367,47.443079715888281],[153.32843140154276,47.288615266934848],[153.14742343473714,47.215238898097937],[152.9521146561126,47.216806234372385],[152.76919894766738,47.277395728254866],[152.78336667860026,47.071780245040436],[152.69165577116885,46.846491502590759],[152.11562856376889,46.387364666291646],[151.89982162347309,46.294469951412708],[151.64419374352019,46.300830660035857],[151.42246263874321,46.414496718310346],[151.287587641193,46.594093969377113]]],[[[152.43633378113782,75.658993398988244],[152.25824982841831,75.744419795857652],[152.1043147607175,75.936273698334134],[152.06048993814068,76.178313591512151],[152.13738165579477,76.411962021251611],[152.23484639914932,76.524085872285752],[152.36095416504568,76.602622268615832],[152.69562747811872,76.683695443893242],[152.8964391751999,76.685074647997496],[153.08159715877301,76.607333018173293],[153.27734689581808,76.432554378581457],[153.36727368093594,76.255972310285344],[153.38154218651263,76.05832498985329],[153.29074887776616,75.828857632569452],[153.14510865923398,75.694481368775371],[152.860046920168,75.591510304193974],[152.7119233045905,75.591609349452341],[152.43633378113782,75.658993398988244]]],[[[144.93417402610058,43.722675307264559],[144.93752061964332,43.915006181520702],[145.01323120725803,44.091840204840615],[145.3809184073958,44.417571626401624],[145.80788446827,44.896636541545568],[145.9420475336955,44.970074358706547],[146.09213359748776,44.999519469830041],[146.42275205180121,44.933364471300564],[146.65979006723035,44.930844333420062],[146.91716773784609,45.231770273604297],[147.38646846370236,45.519247995888243],[147.58863546720863,45.753785976231541],[147.70746343094933,45.833672359992583],[147.93949739545351,45.882822975537721],[148.20143668491363,45.798713337379176],[148.48430848763456,45.968972626230915],[148.67246090373223,46.018117606517173],[148.81816500168057,46.00540838012946],[149.07235343834733,45.923907024947127],[149.41242679439188,46.270500811501108],[149.55259467742994,46.328915938539232],[149.74492872621053,46.472333467554684],[150.25602659310059,46.703989900683297],[150.60960940276155,46.705070999454144],[150.75228719088577,46.666666352644491],[150.91341975057699,46.554294695253247],[151.01882606696995,46.388521960335936],[151.04847023703218,46.145668289163517],[150.9860351949344,45.959407835240121],[150.89434287697381,45.843544178524098],[150.60990306419686,45.65560551477563],[150.48172659694984,45.52397682698733],[149.88855878782121,45.18442864479222],[149.4975921915335,45.096080293231076],[149.276128248611,45.124538376566335],[149.15730615978546,44.978972246673507],[148.98531339631649,44.86364700337711],[148.46336574529849,44.74647296402425],[148.1434398665329,44.546511312645478],[147.89559043886524,44.474273720242579],[147.48635154124827,44.138890548008831],[147.32660898811312,44.061889789008696],[147.39044605424368,43.894605926440398],[147.39558671204392,43.748821158367178],[147.35843074656398,43.60775713320826],[147.24905004842583,43.447581390949715],[147.13118377422379,43.361632764605197],[146.87122842276673,43.253402942785485],[146.63624862658585,43.219138631799019],[146.25420448687791,42.959301076059987],[146.11947633427067,42.915039702607821],[145.97774040165277,42.910384631205716],[145.63024390940939,43.018552223823697],[145.45742124502007,43.175165607037677],[145.29181722054435,43.240266575022069],[145.06862974140356,43.40179281586213],[144.97195082160667,43.559866217942918],[144.93417402610058,43.722675307264559]]],[[[148.40717975417809,76.14851491432259],[148.26104974017267,76.167975648744246],[148.08698322939205,76.258073678535197],[147.98672008884324,76.366147744919033],[147.92233136281538,76.4987629519579],[147.89941441507636,76.644390952987152],[147.93626797803077,76.836897108850067],[148.01135156938304,76.963763792763118],[148.12016834083178,77.063220373384624],[148.63301273778285,77.238769948237874],[149.42799009735654,77.281297659648374],[149.66059733989306,77.211458741245821],[149.83285729365815,77.040256778252044],[149.89915426304074,76.856875399342087],[149.89026453789782,76.662080658481059],[149.8075402105147,76.485500070818532],[149.66356332623798,76.353990850327349],[149.21156352958857,76.163925274804086],[148.40717975417809,76.14851491432259]]],[[[134.91711643321489,74.083112049948895],[134.89243890682579,74.324333995528889],[134.98415552486637,74.548800182989908],[135.17069563668082,74.703718852046748],[135.4081872409763,74.75265498621404],[135.755289535043,74.703293183840088],[136.5215111656604,74.410320330394399],[136.69083748051764,74.236605743894415],[136.7452492178987,74.10059636792549],[136.75790765855075,73.954654765232704],[136.7084902660809,73.766256309955679],[136.59083837190315,73.611033604059685],[136.33406772577601,73.432631572410671],[136.10411963158481,73.3856036177351],[135.92045501957037,73.427585561197162],[135.16016439853587,73.764678217135767],[135.00874159501134,73.896919563233709],[134.91711643321489,74.083112049948895]]],[[[89.091584328268752,76.729418812060672],[88.949461567283493,76.765469947345792],[88.823907333791766,76.841197736153632],[88.67975256632289,77.036579772587757],[88.644438160146834,77.178887383985924],[88.651922541868984,77.325320124777235],[88.701562097665118,77.463285620629563],[88.789088110956655,77.580919631624297],[89.087228256350599,77.76185763622064],[89.26274736587537,77.800827832123133],[89.80367407603687,77.764488591806796],[89.940456557679497,77.70656711882026],[90.054186507432263,77.611016907692957],[90.153025158463024,77.4400474565862],[90.177957126393892,77.244144345012074],[90.12509306091259,77.053868170123792],[90.002679678449894,76.89890173523105],[89.614368871647457,76.699183637120171],[89.091584328268752,76.729418812060672]]],[[[81.273397465633593,74.92264960496172],[81.08993895637424,75.08318053555648],[81.004191556561153,75.311379106372726],[81.00928112608922,75.458509110631255],[81.056998438158772,75.597779351529837],[81.218491669984545,75.780391345582117],[81.819023885449141,75.988598314183932],[82.173816217698644,76.015374026690466],[82.410272888387624,75.948734552667034],[82.586449116254641,75.777519736293002],[82.705097293107627,75.476919223495756],[82.716711058634573,75.283146860405097],[82.627097625819786,75.058586625161453],[82.4852657990496,74.926048550773444],[82.160684788775725,74.781759403805154],[81.964326344138058,74.747556993447006],[81.548427449335051,74.800639316185681],[81.273397465633593,74.92264960496172]]],[[[78.839787723388298,80.36778396231],[78.667931265001187,80.456121692330527],[78.542404182153405,80.603026928133303],[78.481954294554725,80.786558963276619],[78.485167965983905,80.93184755022105],[78.553673934591018,81.11252718133575],[78.647594616699834,81.223423559331508],[79.060818761988102,81.433636010019541],[79.798125247073315,81.475019719508197],[80.514326749545504,81.419922049497927],[80.735169869761492,81.321586470791601],[80.835864312415936,81.215900057269067],[80.901742522979632,81.085634750461509],[80.926207452788134,80.893089968238783],[80.851743770637796,80.663097077420716],[80.719071944295862,80.521427700183338],[80.512852693071466,80.397285807720678],[80.059252653854301,80.34920561187252],[79.122672909127644,80.312621244333926],[78.839787723388298,80.36778396231]]],[[[75.781181928117249,79.224308135303559],[75.670056693712297,79.321957937541242],[75.592368715015724,79.447850270765528],[75.554918580653052,79.59096487181948],[75.560984570765498,79.738773888332574],[75.610035685524593,79.878338531919354],[75.697778127373795,79.997441703568782],[75.816531168071393,80.085657444361956],[75.955899498183868,80.135263594789819],[76.534646332947801,80.138781836409976],[77.698487655945002,79.989783752757646],[77.833787879132842,79.937640374996732],[77.948436907567142,79.848867166661847],[78.032792720242099,79.73092998377642],[78.079760965562372,79.593747382217288],[78.077897984591999,79.400953645346434],[78.003226032873187,79.223198208576605],[77.866848201090221,79.086911809134463],[77.689042724895941,79.01235908745339],[76.606778472799874,78.995488348907301],[76.414425814351034,79.052240185532568],[76.013044202199282,79.099232951554725],[75.781181928117249,79.224308135303559]]],[[[44.855965189386211,80.113923040203915],[44.668318740755076,80.171691394526391],[44.517381883055279,80.297255869728247],[44.442591924015431,80.424590166542174],[44.406431969627093,80.617568999950507],[44.430047616003904,80.763342325234959],[44.525285182002484,80.935034428919636],[44.67928718251212,81.056820127111308],[44.81931164676223,81.103731858370679],[46.720867099831764,81.251445469633282],[47.411911419124621,81.352986522183059],[48.526996015305393,81.298677859430782],[48.890384544912528,81.17350044853923],[48.998821531290709,81.256675502260634],[49.13922574182601,81.309972446136854],[49.956415433001375,81.404044116267585],[50.17014483670058,81.581178006662853],[50.632768995940523,81.668080477828525],[51.120142561338099,81.577980147154335],[51.285406564094714,81.474237056919279],[51.411865196270952,81.280566662248518],[51.966159039987055,81.112836585272674],[52.110507148063988,80.977339870399902],[52.196387183207506,80.7646362985829],[52.507438600827761,80.798009835836439],[52.808803696346786,80.900075915752382],[53.462593475668655,80.883207985543365],[53.560754476924849,80.991310617866745],[53.636273771314713,81.158764963511459],[53.780225213096458,81.295479640552145],[53.917186679915275,81.35498685023974],[54.148875700090478,81.394916520022036],[54.492422175254447,81.592464443793304],[54.633402677005741,81.612942855054129],[55.043284755012621,81.576580543766127],[55.188907142159174,81.726945723378719],[55.370804508689702,81.801767709084416],[56.096137244850411,81.807395680687108],[57.019631281656153,82.035805134827385],[57.543412205175358,82.061121547296835],[57.791534629329306,82.257823497751517],[58.140180949231016,82.328082442784208],[59.409111554029771,82.325312317580199],[59.642262583750153,82.2670492097389],[59.79207658160292,82.145470611966729],[59.897085986610655,81.929306282682447],[59.900049055903928,81.736389695502297],[59.822063595575088,81.545850871043555],[59.858357675065399,81.447922033797965],[60.531549452138975,81.584246428418808],[61.447593795301586,81.60363736947869],[61.619375182439128,81.576255316445412],[61.622949510577712,81.800567773730776],[61.705569531770813,81.976088329056878],[61.848777024597993,82.106951414921781],[62.031014373290894,82.173457670156282],[62.815149010601779,82.218544674045773],[63.774862503867702,82.182583943833393],[63.924985185580759,82.136580182259181],[64.123956701560317,82.014676056763363],[64.238502424726761,81.853850699191383],[64.280913538737948,81.67425942179004],[64.581593247789868,81.698458875623018],[65.222267227507288,81.640579780091244],[65.617624750052329,81.497482742460562],[65.732250854339199,81.413218557562772],[65.81850501106787,81.300082280942505],[65.912516456134156,81.085305230479108],[65.936911870084629,80.93878338939777],[65.900994732462834,80.74459633906369],[65.825804410311804,80.616494029988075],[65.673768247940245,80.49046365022302],[65.533945252712087,80.440330665660923],[64.61346647093994,80.259747478992381],[63.399220546853421,80.200739382547212],[62.944783928363222,80.2163826729834],[62.479020164270608,80.318879427836023],[62.340423343604868,80.193042991059826],[62.206278445506442,80.134544810164172],[61.800381641312995,80.097111154955911],[61.134279986414583,79.925800851538881],[60.409749042609448,79.978356500896453],[60.328689328423188,79.720330570213605],[60.153917703229482,79.557670315814491],[59.971710112747992,79.498182727660563],[59.351299678693586,79.42370095232009],[58.752130381415242,79.513688887762001],[58.585768428028693,79.612569087574485],[58.476548969108705,79.754129185014818],[57.825800974983188,79.605109548546835],[57.309197798443478,79.665632829881858],[57.032104191528795,79.573705659893918],[55.753475924837929,79.590917874202859],[55.529341420818902,79.64461967895231],[55.37307169820852,79.749629568121691],[55.177021976702711,79.727323367316473],[54.81713916181895,79.783791624775787],[54.687060564639168,79.851428646139496],[54.556046635926734,79.992038043565074],[54.348276474912829,79.926835459784854],[54.1861652461739,79.926231816822963],[53.963434598831967,79.764594893358208],[53.566025530063769,79.68738868404192],[52.593474060512932,79.680967839691874],[52.06900953726857,79.785178436695347],[51.922366768057039,79.858276809185327],[51.883357975756816,79.71782071919904],[51.806507243783543,79.595885285544199],[51.698066763389249,79.500938209774191],[51.567047628348618,79.440869322331295],[50.414702636211217,79.426141144083303],[49.968890643904132,79.496060688289148],[49.781700257307989,79.587751641856372],[49.391886274394537,79.676623944183518],[49.265657477284378,79.754835223658134],[49.046820806917907,79.667645852844444],[48.700161152476305,79.678907829185775],[48.462357146747294,79.60195894553452],[48.14848063978269,79.625899408494902],[47.691502244985379,79.58407022261207],[47.542152566255986,79.621636305971165],[47.383471573815974,79.706142773536655],[46.891160905880874,79.693032006280433],[46.55125242158207,79.794309851232455],[46.316049397057611,79.954204598683162],[46.060025152462515,79.953622682630709],[45.818556845538083,80.05081335622917],[45.58562970886269,80.039954249064294],[44.855965189386211,80.113923040203915]]],[[[19.587539911719176,53.959540286814402],[19.351555763594671,54.028194962141107],[19.176790657367938,54.200992937406497],[19.119841138899734,54.338070276259344],[19.105644997198844,54.485826541918527],[19.178630125589805,54.720507503319354],[19.274120996842896,54.834151412034537],[19.457930894624159,54.969431812425775],[19.551536561127485,55.187319677614902],[19.685837317402981,55.329110477010062],[19.86420972942431,55.408637872032052],[20.303263693748406,55.466040779802093],[20.642978643626719,55.715342175657433],[20.780164611886789,55.771850802903565],[20.977235720009936,55.78038090851031],[21.132687438739431,55.733428798210596],[21.43975685095651,55.772757124260515],[22.140161405667993,55.564818059435545],[22.717844593375879,55.535668861947755],[22.848873011421542,55.472000776977445],[23.187444055919752,55.189130927850364],[23.310306237948957,54.980774053145907],[23.328348600310093,54.787410553878885],[23.233691709984903,54.531701721436434],[23.265777440812926,54.354579661762052],[23.226774568561087,54.163320962732335],[23.117514526016055,54.001570052065681],[22.90833623044967,53.877688034302061],[22.714414340084765,53.850674088279703],[19.587539911719176,53.959540286814402]]],[[[-179.8116026002306,64.604328837528755],[-180.0,64.568060062198825],[-180.0,69.467808873888615],[-179.40440846676964,69.40775402711725],[-179.13048132840996,69.307329597568454],[-178.74049145326813,69.235636175796799],[-178.34888198560458,69.052295461222613],[-178.11422216595307,69.02245116850051],[-177.29607341236201,68.728460186599037],[-175.16962860971253,68.145984230439097],[-175.01183778223552,68.050384754743533],[-174.90600439161497,67.926684506940688],[-174.55571497108846,67.752919850108228],[-174.41229534521497,67.589018426043026],[-173.62365271964262,67.641351758854739],[-171.64468257851266,67.407738781853837],[-171.12139401519966,67.119591869606225],[-170.32599546390503,66.799357775732574],[-170.14462398290516,66.76846479046678],[-169.94991009794305,66.662073675739762],[-169.6823001321159,66.633661009529348],[-169.54778967564135,66.586746908317778],[-169.36986766156636,66.431712174503531],[-169.27343272519383,66.263218045703667],[-169.23359017713537,66.122302065432535],[-169.26174673314623,65.881436405038627],[-169.3999270419404,65.682149525729088],[-169.71668613767756,65.512555993077413],[-169.99768733548552,65.519133166126139],[-170.08866815378417,65.494121721889414],[-170.19079062934216,65.320569479904293],[-170.34422178348964,65.205969049439204],[-170.48162408895763,65.162816142007145],[-170.76449126341089,65.142523931699131],[-171.01372855175509,65.019858415025837],[-171.71530598125702,65.01040268260968],[-171.77348828851521,64.81065525715934],[-171.92525449559142,64.640331596975741],[-171.88188595165585,64.485052014778915],[-171.88733366528217,64.340836503927335],[-171.93374457669077,64.204184323351384],[-172.01725213880133,64.086480125362129],[-172.21881322462457,63.957982698201768],[-172.66604451398086,63.920799139897177],[-172.94888996503272,63.801395386484415],[-173.31408735701277,63.791381550242406],[-173.55120196932305,63.870384884080892],[-173.7749908506328,63.869721414358956],[-174.08863046136958,63.948266746844084],[-174.51397190546749,64.174422006138869],[-174.70478145477153,64.234999549918342],[-175.08905717136153,64.312993044975869],[-175.60560418611391,64.34439494520656],[-176.18935194439712,64.640475561828509],[-176.30585682418427,64.797715411348065],[-176.35304616639888,65.003262487066564],[-177.03136121582756,65.103585082400002],[-177.43166234494686,65.007302262460115],[-178.46229240231958,64.998326354158351],[-178.75170732382793,65.102793722754129],[-178.92222197546815,65.262910170485981],[-179.3980355373188,64.792525489480269],[-179.8116026002306,64.604328837528755]]],[[[-180.0,72.029633132903314],[-179.10254634902608,72.096051536833997],[-178.39503177235065,72.039072849349893],[-177.31443900425541,71.703994142310563],[-177.15997180093916,71.586827484919354],[-177.06794105571873,71.472854715876267],[-177.00367768144292,71.288988631419812],[-177.00467463445668,71.142501579999347],[-177.07307353243493,70.950574298438909],[-177.19376762717172,70.791455957167216],[-177.32024527312689,70.710387694398975],[-177.67048504540222,70.592181124801598],[-179.5765957124685,70.428510829071186],[-180.0,70.494933642364785],[-180.0,72.029633132903314]]]],"type":"MultiPolygon"},"properties":{"alpha2":"RU"},"type":"Feature"}, +{"geometry":{"coordinates":[[[28.375441436083193,-2.5780884843390783],[28.358036823807257,-2.4336610560686474],[28.382942244596734,-2.2903365202048107],[28.4778235751435,-2.1217793884131044],[28.63469997749846,-1.9675541857420493],[28.646510608994657,-1.7519845210260205],[28.705984512086175,-1.5756753943069577],[28.997395004048741,-1.1650927804250555],[29.374279003070118,-0.93106926125289147],[29.722247790590959,-0.84654189540411917],[29.89197274704917,-0.84085157033549995],[30.071048862391244,-0.66699997284734203],[30.242362304869221,-0.58892230995030603],[30.633211380801779,-0.58301320949539936],[30.770256095286822,-0.64079424886862446],[30.884241859862954,-0.73633035593465102],[30.965083258635538,-0.86116853328368437],[31.003679965325201,-0.99334901458766611],[31.112888206560104,-1.0992108387597537],[31.265430748596636,-1.3518254556619611],[31.309064527574439,-1.5064279350079925],[31.31409690655742,-1.8533982084780047],[31.3691996883956,-2.0592672414645619],[31.37086027243182,-2.2170456986350571],[31.24999571842211,-2.604776522922938],[31.15267817151414,-2.7190313526662275],[31.025837816549622,-2.7992585014896036],[30.610672187412771,-2.8966394254635857],[30.347146766752179,-2.8559397040452477],[30.227075227571166,-3.0625731178803002],[30.071225056688416,-3.1739149670782636],[29.81840775223759,-3.2798116808073901],[29.447075622220588,-3.307640261120067],[29.230222378834352,-3.2767865384245569],[29.084289200828788,-3.2144956958338846],[28.868040149643925,-3.198055832602174],[28.642101586080344,-3.0962325371332899],[28.475920545004154,-2.907880261541707],[28.375441436083193,-2.5780884843390783]]],"type":"Polygon"},"properties":{"alpha2":"RW"},"type":"Feature"}, +{"geometry":{"coordinates":[[[34.119185300174266,28.096430929911115],[34.141866865871705,28.305548214242467],[34.296904950902899,28.654051331826945],[34.473667853718709,29.502221867949178],[34.538970138768853,29.636560425805119],[34.641040406477039,29.745614985588581],[34.818209857390578,29.835305771688862],[34.966526651128014,29.853007897409594],[35.933879498886505,29.727980723130774],[36.12292512956558,29.86154243826951],[36.390778663883694,30.207923986373533],[36.509832368914608,30.301324771760573],[37.142857813882017,30.459345427145177],[37.201434634305606,30.563527476100646],[36.578958908563671,31.166520238528165],[36.48445665232407,31.333995113692406],[36.459262042761459,31.476402131157624],[36.490567236144635,31.666134825000551],[36.560174250417475,31.79289992484421],[36.663149397371534,31.894441276441189],[36.837151217188406,31.976306268629976],[38.741630415658392,32.455006693923025],[38.947556457306092,32.583402800289129],[39.081751057555017,32.620166991427794],[40.551598326330115,32.404393933894504],[42.333075810683148,31.50805426243252],[44.873409933362474,29.692132238786286],[46.315277365355477,29.568847733497368],[46.595243191696198,29.592210958437292],[47.538770677286337,29.478007212785492],[47.67187425071269,29.428585993205548],[47.785719631935272,29.343743507516383],[47.95287446456674,29.046870916015635],[48.493967174025428,29.040026696787855],[48.63159801356047,29.005501498222838],[48.753987890108327,28.933703479826274],[48.876707559562043,28.790364333887705],[49.055716979050942,28.398520133072601],[49.235638016891151,28.156903306776648],[49.294963799724208,28.016345360912414],[49.573898303723752,27.862243682087914],[49.670076507704906,27.742852978742828],[49.721076550194283,27.616274100239487],[49.868798616838134,27.526064599484254],[50.020143203580226,27.368341430994263],[50.300454593300735,27.217500705913253],[50.56799278557974,26.936248141605571],[50.629815837071476,26.801569867113237],[50.641738675999328,26.578331331784334],[50.713430543151581,26.322940111247128],[50.62612344002428,25.980291282038834],[50.886186960436099,25.677809969385681],[51.019204445189715,25.310666972462471],[51.172234653310326,25.098322732473612],[51.343057126835085,25.101307662808249],[51.58029684661782,25.041029104583135],[51.778496848033825,24.909627280612888],[51.859699807424825,24.791235787372372],[51.903417705203502,24.656902400242881],[52.017287433266731,24.505666678161596],[52.075940534319145,24.282529125108258],[52.789004889759966,23.436604811325381],[54.945187714007531,23.146175443195006],[55.095652721929106,23.195519930018826],[55.242167440362103,23.200531333486428],[55.427847339638802,23.141234275322404],[55.576745314423391,23.0154465207222],[56.085242442079135,22.230789425251164],[56.140636157965091,21.991157502649614],[56.115441425890729,21.844761048109422],[55.451829468684068,19.839092298160057],[55.348838288730455,19.661729468199564],[55.183305168601457,19.540636352209471],[52.092147463899238,18.509659626410009],[49.191612859244039,18.0970166292005],[48.489867860742727,17.751173166553745],[48.018151424037534,17.191300344249726],[47.881507966940262,16.87431208253998],[47.797011914213016,16.760300787699791],[47.258782336351615,16.460353242244071],[46.955516744090723,16.454107221791844],[46.812751402445393,16.481028326506014],[46.645745544191882,16.578112914903354],[46.504315663896548,16.746621557687952],[46.265038808332086,16.733584366344274],[45.492319438836169,16.803900226990823],[45.069652278233825,16.925397987247081],[44.267242886210745,16.903304709341388],[43.958718702173627,16.826691955195368],[43.68122831862209,16.862238281253592],[43.648360101641792,16.562336709473872],[43.56101221729044,16.38457323027302],[43.127421986300469,15.994746653811148],[42.953735280279716,15.896575945326893],[42.805648420303982,15.872229549056676],[42.609673695022451,15.909625630448589],[42.480957564750291,15.986791833650363],[42.369234283217097,16.118719231763837],[42.151439525538244,16.071127567124947],[41.958107949508744,16.112655040429139],[41.579134970800624,16.298786107035465],[41.323036969690193,16.6546002152593],[41.282586493653639,16.845484843938479],[41.302316488509973,16.99090810944854],[41.440928253646042,17.273993224249232],[41.555192918208149,17.397396703685292],[41.339413056176241,17.600844388387134],[41.019263522046749,18.159628103034013],[40.790986722845659,18.438775534796061],[40.644048677065499,18.903632333215739],[40.35783902064486,19.417639274612259],[40.157457595020894,19.608410549963224],[39.915893807308748,19.773200909270482],[39.668103192796842,19.842332515824999],[39.392997197252448,20.019736243596732],[38.854268411479303,20.706327937684275],[38.654185064947193,21.072147533269117],[38.597278952914699,21.251422353288195],[38.619088840523418,21.470779111187781],[38.490558131575888,21.83266191744406],[38.579302363878945,22.421762380333931],[38.054807256846956,23.410846084202294],[37.80387317003116,23.647837636519885],[37.547370960122628,23.784490430929509],[37.318869464199551,23.844989672577075],[37.161354489666657,23.969042714392216],[36.743278798932231,24.578650707734159],[36.68448483444341,24.762454566445818],[36.691458926232372,24.918838076771525],[36.458271987585782,25.09316131571407],[36.225746620667948,25.205322341856743],[36.101147561240637,25.345419920828093],[36.005490865906623,25.614476712685807],[36.067502472054151,25.915648465075332],[35.838137409447924,26.305375403683755],[35.467527539289371,26.750840426345004],[35.341943547751612,26.968996410591512],[35.166465833192227,27.153945091670966],[35.003176764132235,27.457475929804158],[34.887072810187426,27.601953842974897],[34.581837695647259,27.566779699506252],[34.351298127804782,27.646582921549086],[34.185961896613662,27.825973493332921],[34.119185300174266,28.096430929911115]]],"type":"Polygon"},"properties":{"alpha2":"SA"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[165.43974551827085,-10.410392960069293],[165.67505992847299,-10.233718114683798],[165.941579528886,-10.167326528224258],[166.30355205828846,-10.214417442392],[166.47621214680692,-10.30550880312108],[166.60083172150078,-10.455772251004415],[166.65097663232649,-10.593769152735131],[166.65972229127271,-10.925223030866119],[166.62240536044624,-11.072600118572856],[166.79654235152793,-11.059681355016131],[167.08870192228784,-11.137711463859773],[167.20304255642969,-11.220770754935977],[167.36415642232936,-11.421274054921643],[167.41624018453334,-11.559801921179227],[167.42549090184031,-11.707508089786053],[167.39109811194987,-11.851451912226453],[167.31607499304982,-11.979022361961922],[167.11997348716662,-12.1259661530217],[166.85559947546312,-12.174078830398912],[166.65968439153266,-12.154467032921517],[166.48653124419047,-12.060736940223043],[166.38801238779078,-11.950164820323213],[166.26425543256704,-11.697084150402468],[166.24423630631742,-11.552006004800738],[166.28357612427288,-11.362638244761527],[166.11067264398298,-11.375652534059622],[165.6714297329147,-11.320481147724944],[165.49811515571176,-11.225714966115071],[165.39987181590169,-11.114254799913644],[165.31006527533515,-10.906182488067255],[165.29108926027675,-10.761498499717062],[165.33178128427326,-10.57179073289389],[165.43974551827085,-10.410392960069293]]],[[[155.55325577289955,-7.5725643821228648],[155.33608249301048,-7.4527654881085521],[155.24355586749687,-7.3349341010470379],[155.19009086422909,-7.1949808744627379],[155.19904580006369,-6.9471183770392297],[155.32347598174204,-6.6963675138588048],[155.51028583534057,-6.529498638763517],[155.92989159719116,-6.4392585088451737],[156.25508851177406,-6.1770207229357883],[156.38942801756315,-6.1258267021399906],[156.53278123602479,-6.1149738067851569],[156.81752162804659,-6.1899237232164337],[157.31975634945871,-6.4854227621541121],[157.62712693972108,-6.829793128639742],[157.99717517698662,-7.0965105115900196],[158.11777393116682,-7.1152744503195162],[158.47230139204112,-7.0459506024935852],[158.89227147108159,-7.1310866249159544],[159.28012150518572,-7.3934406678966864],[159.66555157120214,-7.5875549799526798],[160.14614000038728,-7.8996042539394962],[160.29696724295204,-7.9070256170652424],[160.73485997791593,-7.8152317927250028],[160.97725100243116,-7.870198771664727],[161.13305977996899,-7.9949424453211728],[161.43255006867608,-8.3672712279978487],[161.67790065916975,-8.9140296347972861],[161.81881360084745,-9.0870628173326473],[161.99563598607818,-9.4061771851655536],[162.09780048402322,-9.8321861393479519],[162.19452687036886,-9.9411722264632658],[162.52032848539318,-10.164064040326583],[162.80872543937753,-10.58001379515229],[162.87147899169292,-10.81618894781896],[162.81480896220324,-11.053897211922179],[162.65225194201682,-11.236357964560774],[162.42263741355814,-11.319987381550419],[162.01953284833229,-11.312525658966132],[161.61693305173529,-11.183490936929013],[161.21579188169414,-10.951498391533425],[160.77552637792741,-10.489356346806634],[160.68989808753298,-10.443215114890544],[160.22935981114887,-10.330445607824272],[159.73020106747512,-10.271042253076921],[159.58576262695976,-10.214585843552014],[159.32313101783458,-9.9881406637591805],[159.11160521233384,-9.6760170418967544],[158.79682892313912,-9.5264608212190076],[158.52790611433164,-9.3134325612596296],[158.12770484597885,-9.3221067314897379],[157.86003009369534,-9.2684756881336554],[157.60597917983677,-9.2916501457383784],[157.11440630247938,-9.1492251590417517],[156.69151811471448,-8.744430354947788],[156.40206083444576,-8.6577527235832097],[156.24004758311517,-8.5501752548662697],[156.15244918274919,-8.4330412673448265],[156.06195758547136,-8.2164223296327794],[156.02007890464736,-7.8318900217256564],[155.98425853439369,-7.7511020242745508],[155.85002315467713,-7.6494337889346253],[155.55325577289955,-7.5725643821228648]]],[[[160.78011171260243,-11.309378954454008],[160.88975683355005,-11.390705451958894],[161.0094129965556,-11.550341187556088],[161.06170967253047,-11.683911631595707],[161.07393092193084,-11.826833452540075],[161.0450708867607,-11.967343609500123],[160.94725840397382,-12.131099364421656],[160.67923649740325,-12.300299457845451],[160.47545789697034,-12.330148433905586],[160.20780228805913,-12.254679023493862],[159.68716191514108,-11.965509196499207],[159.58885646174787,-11.857297893960384],[159.52605373773059,-11.725277190962689],[159.48723111263129,-11.501919989174716],[159.53707933229316,-11.27731078456357],[159.66770822337287,-11.099874459962315],[159.83883736480894,-10.999921174977855],[160.03535597185117,-10.974303225918236],[160.20988725344455,-11.01220066214],[160.78011171260243,-11.309378954454008]]]],"type":"MultiPolygon"},"properties":{"alpha2":"SB"},"type":"Feature"}, +{"geometry":{"coordinates":[[[54.981189350863936,-4.9059150307689601],[54.889425892338508,-4.6846272901541992],[54.911130803614462,-4.4460529971269844],[55.041321609749609,-4.2449581178664388],[55.281248030667896,-4.0905518467685198],[55.479731170994391,-4.0597123506598152],[55.718610219830801,-4.1338413681228916],[55.864764943208037,-4.2716289885705665],[56.000195410684363,-4.4975256307117268],[56.04028103064821,-4.6841512833741321],[56.017869135812987,-4.940845647872206],[55.887542979912901,-5.1472187115874384],[55.721885154889506,-5.2519347247246735],[55.528704039790973,-5.2849349545439575],[55.33767840354588,-5.2411495152290861],[55.16568194617831,-5.1307375621877949],[54.981189350863936,-4.9059150307689601]]],"type":"Polygon"},"properties":{"alpha2":"SC"},"type":"Feature"}, +{"geometry":{"coordinates":[[[21.456727812029094,12.452920143642913],[21.353740755824386,12.624952612629928],[21.332490946567372,12.873716535305547],[21.541542677798486,13.335569508830051],[21.679628836355203,13.503561297513262],[21.611355104089732,13.731868001071279],[21.612660939366428,13.876559752654408],[21.744817669007659,14.168134422117275],[21.917743798978787,14.34625522351979],[21.882503050648886,14.576796854425586],[21.932457583694969,14.769715254139459],[22.053792189182449,14.92780028768537],[22.239363849471758,15.062029131930956],[22.337675044431005,15.260746664912661],[22.449150333087797,15.393513906457656],[22.438741385674106,15.600916377823211],[22.497829781422677,15.77729181382359],[22.7536978237199,16.057813820334317],[22.976888727402716,16.185520771686082],[23.475416581446595,16.222063949420431],[23.490214641359159,20.093782867372354],[23.594107969822648,20.313047480536262],[23.788990933903417,20.457586141787274],[23.931097226526195,20.493270198046641],[24.460999437429113,20.496170166983109],[24.483544352986321,22.050682664462624],[24.541672219555,22.235259871086935],[24.704776316047038,22.412722489234618],[24.836572802480056,22.474455885456024],[24.98056383388483,22.495619744274965],[30.992935839252183,22.494890390524933],[31.164777268185166,22.643247212355206],[31.390606010136366,22.702104525191505],[31.684230933221158,22.640252336991409],[31.860192882877769,22.495880981200617],[36.918523248611884,22.494265501094031],[37.140825726860804,22.417571827100037],[37.303482356908489,22.247739167791412],[37.356661704705438,22.116128148717628],[37.410507448685543,21.766938440330883],[37.696278315443337,21.349708560574093],[37.748662784717169,21.206314205663542],[37.747169702938159,21.003388876437448],[37.682604514016894,20.831523937651131],[37.727276571031048,20.563935142344253],[37.697985748343363,20.153264717143788],[37.760487949753646,19.835411687068309],[37.757272190528141,19.616537588777973],[37.887245997889394,19.168180663944188],[38.208130739595795,18.965924541423202],[38.408803460446272,18.770736107800531],[38.568012652012868,18.697284707860206],[38.971132765797442,18.350082056059414],[39.090719762999449,18.139709911855864],[39.097512840058634,17.897818731651498],[39.019735821325973,17.71977931431033],[38.559004735080428,17.189727803085614],[38.431240486767734,17.116453587101461],[37.97127962750011,16.984481899924724],[37.690905923325516,16.6476838949316],[37.559245838191181,16.584448847014027],[37.400923759907201,16.558074240718362],[37.393527872948638,16.156503560789741],[36.947945905760903,15.036315004543372],[37.021275682343799,14.204144759402269],[36.866621770542395,13.473554676039935],[36.653020607783105,12.977647010753845],[36.585019990468943,12.56137620793605],[36.509896745949433,12.438065646699094],[36.402669090764476,12.341363907607976],[35.989278511363096,12.192061720264263],[35.562599047872219,11.559438754824377],[35.46161105125514,11.203657007630806],[35.419477484808887,10.768744967219414],[35.374223688325223,10.635347846679815],[35.259835690934473,10.486994411000026],[35.066420539429565,10.342842701033087],[34.807803443399891,10.248098763410804],[34.785735636911134,10.034293717861038],[34.635540699415799,9.6918649550890432],[34.538744894725646,9.2677684088827608],[34.462742713421626,9.142527947011855],[34.35370385485178,9.044691184113681],[34.173417919398737,8.97095441221588],[33.841995663951266,8.9648677418244134],[33.705072455687322,8.9983066193921566],[33.547214500600312,9.0993780537006188],[33.437667608972077,9.2514768615592367],[33.378861364597299,9.4227909369411034],[33.439400398279574,9.9207222742828343],[33.108855770493989,10.212007393603479],[32.81328078202354,10.359404536573642],[32.717212593590808,10.464354946344715],[32.649095535696311,10.612350770805602],[32.32830345453803,10.34066489323957],[32.025168848279669,9.8856500376196728],[31.495505340509631,9.4026544310705429],[31.363265202975732,9.3163625212637946],[31.210709722041781,9.275612393532823],[30.654030636574937,9.2418482365108119],[30.467663396022267,9.3226008705816064],[30.173859334900282,9.5745873256808576],[30.026370559591591,9.6547689009272606],[29.775234252481294,9.3704383958321493],[29.323988228542628,9.187620943912215],[29.25307819253856,9.0385243532130879],[29.154418775304304,8.9341810649078628],[29.030171334336842,8.8621826216164532],[28.890584396159259,8.8284678012319731],[27.953084052953749,8.8381226810774489],[27.739394518527842,8.9362328227863443],[27.602482591894333,9.0939862631552817],[27.134217741216304,9.1088023451024487],[26.760940144330281,8.9949589919648432],[26.523283351884661,9.0030919798412139],[26.244705565408776,9.1312179554790109],[25.825184552023924,9.5985557735771891],[25.521606796368403,9.8672650544316483],[25.461495475367762,9.8585385204885618],[25.289096979639684,9.6100866568984635],[25.251481690323526,9.3544270653869912],[25.133282891930303,9.161371136118083],[24.976498339008998,8.6585554594840648],[24.860451176013999,8.5102900615758301],[24.545038297474097,8.3616810910071528],[24.369021461939472,8.2178058216860315],[24.226278200391945,8.172207140075832],[23.561012050379713,8.2470361287468936],[23.24123800939466,8.4131461451490548],[23.079824119258873,8.614651116823353],[22.963078662882669,9.0459472792630162],[23.002726612899387,9.2436839099081425],[23.121884896524396,9.4658673138621605],[23.12717600687364,9.7231682313750412],[22.90888718169866,10.089159479997258],[22.401555593980941,10.720921255942272],[22.360409399661645,10.911439462816618],[22.384231738056314,11.096301990554888],[22.220452803042399,11.246917175643926],[22.12034993412561,11.411112231932707],[22.077141821537118,11.759827748161113],[21.994204457349632,11.922815692083361],[21.940776316560441,12.179179467249241],[21.675928094233331,12.246697643655018],[21.456727812029094,12.452920143642913]]],"type":"Polygon"},"properties":{"alpha2":"SD"},"type":"Feature"}, +{"geometry":{"coordinates":[[[10.683570055637126,58.801898514619424],[10.648894577393376,58.949938481529067],[10.660316957003758,59.10155559744306],[10.825928698419311,59.409839718688403],[10.976197300793716,59.525525576778989],[11.181521509228684,59.578841741356293],[11.197208356340795,59.718401467588912],[11.255548751029515,59.85481328030891],[11.612903413027615,60.248219221524337],[11.744426352267226,60.326880605657813],[12.029799346934144,60.412474529195116],[11.850332037932041,60.707285677232271],[11.802800712497371,60.911375168421451],[11.805923394032105,61.109142444113239],[11.891016164098021,61.29730346735294],[11.726661298177888,61.464059494708053],[11.660278320310743,61.653111008687382],[11.662591166541148,61.80381650812604],[11.765153728272253,62.187635523338429],[11.627528362492345,62.479743295925665],[11.629448733942885,62.937446458370374],[11.504811156455595,63.224081692147493],[11.518027663003199,63.423907349545097],[11.579301245591022,63.561526456702325],[11.683737456119264,63.683380704952882],[11.741234749286386,63.843716234548516],[11.828530202958468,63.955886496931122],[12.526895026243302,64.420885156500134],[12.662800115354845,64.4826929232906],[13.151455602281528,64.571365190208468],[13.176649773382133,64.740406403719547],[13.2716834967783,64.907658013875135],[13.784681815685971,65.3234167235927],[14.029558964331434,65.580008373581805],[14.105150510633623,65.833400539724181],[14.050281715974331,66.047438946354873],[14.047684172665187,66.193228682330997],[14.087258721623742,66.333568446553244],[14.165639722483913,66.45652296274271],[14.276161217771223,66.551635493077484],[14.409423843105078,66.610817137498344],[14.95499215933053,66.663233732138096],[15.015738829226772,66.779470901340147],[15.122927644406731,66.889499021286085],[15.671232956666017,67.222322068294275],[15.630826003203779,67.370563994425979],[15.648906164997564,67.569746337306199],[15.743603578436023,67.745907904777113],[15.914686680557383,67.919913337252268],[16.261153913435983,68.031114575338577],[16.413184835968259,68.230501109252657],[16.561047974961358,68.342482035917897],[17.144605567034411,68.570033836050328],[17.339169651653421,68.603358043784027],[17.66284585918617,68.538430007971215],[17.689404563506532,68.689237573282099],[17.757065988828554,68.820468658900026],[17.860104484418493,68.926217109175312],[18.036641255178314,69.012061538439283],[18.389449879248119,69.06211043677601],[19.63104641098257,68.926163319092069],[19.640370431614265,69.156906486568673],[19.727890653934502,69.329316713287724],[19.874280635433671,69.455630009931312],[20.106120707432819,69.520820648306668],[20.665770941847818,69.534618255472537],[20.997092131905934,69.469074984529129],[21.141913334599096,69.414386592361112],[21.265181845775334,69.319731215895743],[22.115748251478117,69.009657135183502],[22.988904165936226,68.848990952589688],[23.871224731762201,68.396834889967977],[24.051447882980231,68.236289128315434],[24.135465255074102,68.01002289794512],[24.099794553660416,67.762122918688775],[24.250091420957137,67.483381325640224],[24.26535394948634,67.23411878211293],[24.417886338825472,67.066436949775451],[24.483519796662591,66.879457614644139],[24.47136292747188,66.681666732434465],[24.340145116821546,66.421430994198658],[24.577029937551117,66.073729695805483],[24.647382716270041,65.8928879323296],[24.63087046329191,65.651765534700218],[24.503190187033329,65.446557318023849],[24.294174468381041,65.325210317676806],[23.933801966992409,65.284377781976872],[23.654419122745839,65.319012010727164],[23.35078962709845,65.294240235745875],[23.09347940927433,65.235641037485806],[22.675653601671542,65.329336495672223],[22.495827166889864,65.160223482152944],[22.046998905623052,64.965015299454308],[21.917936069794038,64.764268641472356],[22.000040615739525,64.600501157143114],[22.019287451726438,64.457553683389094],[21.996927919573459,64.315059921174537],[21.908445438818315,64.136281304069556],[21.765252043044693,63.996096029282093],[21.338320870780016,63.775219875560104],[21.070337695883637,63.474035507596007],[20.37740842245007,63.193426048287122],[20.115982832883958,63.135962551183049],[19.899447492088893,62.996146736804306],[19.519366103010437,62.948259273947862],[19.212489771996673,62.770806003141459],[18.927009914943145,62.710111154155051],[18.773413957196656,62.504160901602525],[18.480649435067939,62.369005729462103],[18.342391392084913,62.204777093427097],[18.108209661097941,62.077584986283227],[18.047336467512771,61.952722636824745],[17.940297054152445,61.838906076553066],[17.964931964775623,61.688263627985449],[17.944473981765853,61.543108790895744],[17.853902173148899,61.370527645795264],[17.691688563727908,61.23962977296592],[17.702221987011395,61.119859806118598],[17.805495338579984,61.075225381670691],[18.000141128772214,61.087568009578547],[18.15043264519695,61.050051824390955],[18.433805860742037,60.8375601925786],[18.831228123153942,60.671691130878258],[18.981428943750068,60.518276945745896],[19.156200760521209,60.422979657307003],[19.448508702830654,60.027658520393196],[19.486791429215014,59.886096142417252],[19.482356931270484,59.739515757557605],[19.390289031325008,59.486077305935531],[19.292085944847397,59.374705667366975],[19.132769443231986,59.276373224903885],[18.914855523927748,58.848774507116303],[18.750573034256007,58.657704773920479],[18.632923180232687,58.578808426390211],[18.497868160749658,58.536073621865519],[18.200365991386523,58.564626178300941],[17.374172668252633,58.275966849641364],[17.238980343583702,58.105152220120935],[17.184720718306639,57.819645894525522],[17.339356916421064,57.767717925421202],[17.492083102769886,57.650666756356436],[17.571149385711585,57.529463584107226],[17.613695652307488,57.374758621231059],[17.679816280360157,57.759451053764678],[17.838431742545204,57.957741971577782],[18.345058869758692,58.291759069341268],[18.848704096738125,58.391850869821617],[19.08830698445772,58.478948073037834],[19.425558992514567,58.453823212731969],[19.606211360173514,58.380126088050645],[19.744905191164268,58.242901580865571],[19.827978217801526,58.014542309932722],[19.821548206568234,57.867940546128651],[19.772835400073483,57.729519143598807],[19.61058445051135,57.548622173635671],[19.403987246362014,57.453023527270517],[19.390217290423244,57.268563906186955],[19.303535326361473,57.093393620242587],[18.996452914775634,56.840495806937433],[18.786553381174301,56.742486884377527],[18.618890656238918,56.563810422646817],[18.189387684241122,56.422591538563566],[17.993055192072006,56.445012210169047],[17.859989688665554,56.511239746896344],[17.752272527775332,56.613658144010969],[17.67942268129373,56.743216681689447],[17.647877894472217,56.888466264278051],[17.661953849231608,57.041817197504052],[17.607785817852314,57.225921476458183],[17.547297087875485,57.064685887819529],[16.907111040977473,55.985342247150733],[16.816910238654614,55.87366272648277],[16.698694698568602,55.792215896228385],[16.466590913180131,55.740522713815437],[16.2530060824404,55.794030062058916],[16.030855846540291,55.668806990694087],[15.885391561935418,55.628669047347209],[15.560173337031587,55.682424103836404],[15.099053842032507,55.67152260974725],[14.96725818784649,55.580828406919984],[14.840664629496205,55.541372102092652],[14.800572231594666,55.329885619284426],[14.686893551861417,55.166350351614867],[14.443527503875721,54.975983644447766],[14.273241129156725,54.906907344987602],[13.818688806419576,54.922679318060808],[13.299588233711273,54.847140809069749],[12.764489504468786,54.926598748231683],[12.588459668451746,55.009903152773823],[12.457607312505601,55.154138335470869],[12.386903662832877,55.386159589241814],[12.424433779323671,55.605189542798875],[12.022312181978709,56.071159298124343],[11.971998560040371,56.307412755028473],[12.055547005065693,56.566523194461382],[11.743060017941639,56.976585059667698],[11.58429618511858,57.098286492129084],[11.474337553888597,57.291289227064098],[11.304073091278152,57.464584307180274],[11.239232730430766,57.641265918769726],[11.061560526205863,57.803119226739149],[10.979978472176054,57.948250709026439],[10.803898152656981,58.140582481849876],[10.756697178243378,58.279136297797749],[10.71761770117266,58.717323163166981],[10.683570055637126,58.801898514619424]]],"type":"Polygon"},"properties":{"alpha2":"SE"},"type":"Feature"}, +{"geometry":{"coordinates":[[[103.4834774111592,0.85438588513960734],[103.35551572303771,0.92195802100357804],[103.25226363359481,1.0233443770767505],[103.15732847795417,1.2434130479117502],[103.17571423971702,1.4823792953694133],[103.35581180696239,1.7807023729282858],[103.55725370851387,1.9007869439724618],[103.82134194276642,1.9467654000737522],[104.09863248579829,1.8783618483387663],[104.33855817796558,1.7294596202673775],[104.45251741347731,1.5692536319993247],[104.49590253492732,1.3774975440330457],[104.46200573987437,1.1838388924821746],[104.33010421281472,0.98519619171422135],[104.17137609152627,0.87416800353594293],[103.8856798724761,0.7699608226248964],[103.69809133536307,0.78068424125212776],[103.4834774111592,0.85438588513960734]]],"type":"Polygon"},"properties":{"alpha2":"SG"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-5.7726742164919598,-16.503657993648897],[-5.9677521553731845,-16.468061842396033],[-6.1673445534043472,-16.322647455904526],[-6.2729988877515108,-16.09944434037029],[-6.2653495118401974,-15.859797229875801],[-6.1619773183150475,-15.640440189715727],[-5.9884401624803605,-15.492592915248029],[-5.8074153703316203,-15.416428119720912],[-5.5707013577697326,-15.421513802668672],[-5.3853109541649378,-15.497012040428217],[-5.2469894708849809,-15.635209203184983],[-5.1722441658428107,-15.815886893852483],[-5.170925100456544,-16.074819415768612],[-5.2534954945230208,-16.261933050093418],[-5.3936947474086043,-16.398580632714385],[-5.5808236923150041,-16.484923138073491],[-5.7726742164919598,-16.503657993648897]]],[[[-14.903713827465298,-8.0480408767701963],[-14.89734741946134,-7.8131579586506206],[-14.808336389425891,-7.6186213691877729],[-14.653456920537241,-7.4619713739398277],[-14.478179839455203,-7.3918931867312603],[-14.271303247658976,-7.3941388793035401],[-14.094157736291191,-7.4629795315264698],[-13.893775128052949,-7.6479720083992619],[-13.815289107286873,-7.8246126417940456],[-13.809666666314603,-8.0178232962086415],[-13.902152612715865,-8.2356078619022721],[-14.070450487043514,-8.3909579929183504],[-14.263247212838033,-8.4636907647956861],[-14.455886961243595,-8.4722127095731405],[-14.653440030910343,-8.4056821750283284],[-14.812423839929165,-8.2619695626730731],[-14.903713827465298,-8.0480408767701963]]]],"type":"MultiPolygon"},"properties":{"alpha2":"SH"},"type":"Feature"}, +{"geometry":{"coordinates":[[[12.985910154276615,45.951993461833375],[12.899726600267472,46.117463513566641],[12.87851811930083,46.256138228060728],[12.89666820508085,46.395246281354218],[12.978416057442157,46.586595980847314],[13.120089646814312,46.731839169543335],[13.464008164895723,46.960753895984205],[13.607321957210742,47.011327706050821],[14.482864273567099,46.930387813127091],[14.770475775110024,47.090301684871896],[15.350673702137707,47.128635871417487],[15.691987502935836,47.202827800969956],[15.872602278827635,47.311847038415053],[16.011605703689867,47.356404443228008],[16.347231961163615,47.352988288546442],[16.528738529623791,47.292790961294067],[16.674372287351449,47.168858878229827],[16.959686516727604,46.730388347797778],[17.007549147206753,46.59070725278356],[16.991651328258914,46.346735621607799],[16.861977716386946,46.139468518936056],[16.741510383677024,46.054091347218368],[16.563689970683171,46.002981581280544],[16.434831395149157,45.918123839236046],[16.152482128185202,45.845534645766953],[16.072052407141125,45.591209275305545],[15.977380436302115,45.482726443306873],[15.833656930809793,45.395872103371879],[15.795352869324143,45.26254232526346],[15.71533835718955,45.137824784830009],[15.558906367327248,45.018076483233656],[15.315474692167175,44.947125328068651],[14.734636122075809,44.981295640925239],[14.579426660531487,45.027352256556085],[14.420508548360111,44.984226746447618],[14.109919261527024,44.980945481580797],[13.841416981670744,44.929968752525234],[13.436971796625855,45.009845343735478],[13.281527105231563,45.104698804779439],[13.126459784457589,45.302719203053975],[13.078584091108326,45.499299339739167],[13.099412378292518,45.659356530551918],[12.985910154276615,45.951993461833375]]],"type":"Polygon"},"properties":{"alpha2":"SI"},"type":"Feature"}, +{"geometry":{"coordinates":[[[16.425866365382955,48.198456228458788],[16.362953518585865,48.434463834799566],[16.398764027054444,48.627283419596317],[16.563710596051099,48.945459222968488],[16.801215779681275,49.212346829191937],[16.919843270510743,49.291749941197367],[17.056073438865926,49.334395890671637],[17.451319724992544,49.331296717540141],[17.56965086645895,49.35852383750246],[17.676933349026275,49.422433090578316],[17.762780290045455,49.559429197684963],[17.878611894660565,49.669741657195608],[18.505751438943857,49.982948857298787],[18.841181398312397,50.010245307913522],[19.062979220978072,49.966136778239694],[19.262233479176054,50.064159274038715],[19.46204090889556,50.097024132379268],[19.703221125631487,50.023538124570869],[20.017975985895475,49.786240974196751],[20.30653312573623,49.881724638097531],[20.650990405954616,49.890197341426415],[20.830618962277839,49.851670656774935],[21.012382149025608,49.913484740021104],[21.333761128594574,49.928232683622383],[21.968577875440918,49.837004976177809],[22.115725867642411,49.789380547889962],[22.307348951223453,49.649574633932566],[22.800449503595924,49.498317571907513],[22.944179098322472,49.364593785329326],[23.034151025021526,49.137253813692624],[23.020859782006308,48.941387557394663],[22.961520797252454,48.806177265853009],[22.692369799072313,48.382402397261735],[22.602365065701267,48.289644110527099],[22.499858865442945,48.078938439634534],[22.311150009974615,47.935257759448724],[21.6926697460854,47.846786719044452],[21.508501338424367,47.895683479836954],[21.324341964933403,48.035662348680013],[21.088118892338024,48.006339923839882],[20.756729718696906,48.027163059985853],[20.557607901924165,47.848743384701848],[20.081307136058104,47.66397700622921],[19.942342850518141,47.645036373678458],[19.7190183404308,47.679710285905905],[19.574244394527813,47.622559479943419],[19.174823655498134,47.570898851570789],[19.012237058377742,47.378790540255629],[18.782682560000392,47.290858870688048],[17.723784748152003,47.271926222902202],[17.444316414837228,47.348207159345726],[17.134866630191283,47.516624400185343],[16.932225189961969,47.563987111078106],[16.768544664615355,47.653520135177665],[16.673760660310833,47.756942587557006],[16.425866365382955,48.198456228458788]]],"type":"Polygon"},"properties":{"alpha2":"SK"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-13.754021182655915,8.8569949261048251],[-13.789856712596002,9.099353645696084],[-13.706256697269657,9.3296425329777666],[-13.565998003680303,9.4675770716263248],[-13.372440922472288,9.5427568966037395],[-13.274507086467098,9.6507290016956464],[-13.139093386115427,9.7322341057017496],[-12.914722747499891,10.143120819265455],[-12.767553953909095,10.285159112323845],[-12.537132811141392,10.385082831515851],[-12.331327892288282,10.426659079482297],[-12.173202393617794,10.418485395136242],[-11.958117458805148,10.490544828040306],[-11.225048084898702,10.493355940196706],[-11.020618799722406,10.441981626820434],[-10.845634177397784,10.324338851525363],[-10.269401950421308,9.5834022362461564],[-10.212162301783842,9.4590273694481066],[-10.118942876781748,9.0152055557077357],[-10.086600917985381,8.9438997773294435],[-9.9397512487138648,8.8480703350598322],[-9.8484631695483227,8.7314656882996236],[-9.7953338072700831,8.5932360680740185],[-9.7850237029127385,8.4455070635016316],[-9.8635217943989488,8.0955718155729457],[-9.9860236331750727,7.8613207143379897],[-10.138165144245944,7.7267578605493057],[-10.254470851280802,7.4506611807796279],[-10.899003212004912,6.888226748119795],[-11.21298768014211,6.5028107619198394],[-11.395668389977555,6.4195325559920748],[-11.546281063685012,6.4083705024388875],[-11.739242243399994,6.4638095180464097],[-12.150333625632678,6.7318430955079398],[-13.173550058911539,7.1231617150174662],[-13.327938163053549,7.2423805066189413],[-13.407066812844864,7.3659199257931363],[-13.446932933335628,7.5071078280536003],[-13.444104317648737,7.6537888774123708],[-13.402768306439969,7.784308352544806],[-13.554671550609923,7.922770663675065],[-13.758923474882266,8.3139650844551287],[-13.766855121864284,8.5048650519779123],[-13.713747478938668,8.6809837989313579],[-13.754021182655915,8.8569949261048251]]],"type":"Polygon"},"properties":{"alpha2":"SL"},"type":"Feature"}, +{"geometry":{"coordinates":[[[11.979086216984314,43.660323659811958],[11.905931317053152,43.841389118554289],[11.907713387725638,44.036666265086154],[11.984160582391388,44.216366476998239],[12.170700365224969,44.402672103244527],[12.362997955382166,44.476049060642097],[12.619015580861211,44.475967165856773],[12.799129828724158,44.392753833882637],[12.932699597672434,44.246040788366642],[13.009640777329098,44.021680718394421],[12.996813060467581,43.82164230100404],[12.906115171077325,43.631954311506348],[12.762527577726953,43.485689301738788],[12.572318868791934,43.409325062125404],[12.319757797338859,43.405855374252191],[12.143197120672378,43.482321132913988],[11.979086216984314,43.660323659811958]]],"type":"Polygon"},"properties":{"alpha2":"SM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-17.911434178917599,14.425657161082103],[-17.991079619292446,14.549574090114701],[-18.031159693147199,14.691321714973709],[-18.028195642219409,14.838597021374577],[-17.982444731919632,14.978617218775216],[-17.823439187130482,15.163587151232395],[-17.459472436185919,15.335499459379889],[-17.258171417405233,15.577096308516417],[-17.034508872607599,15.937728012258834],[-16.891215323289877,16.420852529908831],[-16.63622767885817,16.834595974429416],[-16.527933910952363,16.939070713891141],[-16.295216902406001,17.027905334936932],[-15.788102399622224,16.993060074024566],[-14.986238751441121,17.178118924717339],[-14.380068037096988,17.131553001293728],[-14.054102591963295,17.014261641560708],[-13.65689801283612,16.666626431335761],[-13.329121510365754,16.602250018774384],[-13.101892067411738,16.452866462973304],[-12.978236672073816,16.311349829703175],[-12.82669209489678,15.990155214839927],[-12.571047144830553,15.799740208141825],[-12.372961967252721,15.510451456046818],[-12.209784505915193,15.411768855541075],[-11.878631256923669,15.105950348993575],[-11.713830108715159,14.810344712817301],[-11.689383016946081,14.592553000649671],[-11.538419678260279,14.342986187926124],[-11.463086776010673,13.838602160340725],[-11.338109191910995,13.77191552011984],[-11.22907490507577,13.661466485646638],[-10.916931786997356,13.101968763171927],[-10.890946083385124,12.959762279854653],[-10.932067921844357,12.64191216002348],[-10.889900137310425,12.421725608206886],[-10.90702595965689,12.273849718620582],[-11.02815135518053,12.059200107061294],[-11.237104935553299,11.928493495014651],[-11.972813399187945,11.8981590605181],[-12.270807174448315,11.829141935821117],[-12.942735571535334,11.991018502757434],[-13.205594186936443,12.011502728642288],[-13.42173804860805,12.144224632452181],[-15.047845155668295,12.177566648895102],[-15.428843905283019,12.012651389032097],[-15.742667508077878,11.947556925343362],[-16.094106157669369,11.950239396175489],[-16.401947460292106,11.867061789055967],[-16.739720931706575,11.855880599925532],[-16.880275013263912,11.884365290460122],[-17.00696198605797,11.951575694130646],[-17.109359653656728,12.051982591839977],[-17.233320334831088,12.251999402109318],[-17.278116640140873,12.392407568056653],[-17.249987325411308,13.177766308859772],[-17.171299271649463,13.352791193239833],[-17.069075741341212,13.459518349305201],[-17.197887592612414,13.628334215321971],[-17.275806495133569,13.908733480228888],[-17.424251111050104,14.152869380389657],[-17.670697418592002,14.205777200079288],[-17.911434178917599,14.425657161082103]]],"type":"Polygon"},"properties":{"alpha2":"SN"},"type":"Feature"}, +{"geometry":{"coordinates":[[[40.464682681793882,2.8126610287292388],[40.500998387344907,3.0016780640410148],[40.574020419654445,3.1266614507254258],[40.958051781483995,3.5253833913115793],[41.62041812645127,4.4325819952327157],[41.731249450016165,4.5429077022524016],[41.9213053545148,4.6270340734226041],[42.555034340705909,4.7603525296076885],[42.868728662494874,5.0729147427715837],[43.425281128256941,5.3288242471000888],[43.968142847853741,5.4498931838036331],[44.716186501697365,5.4122580241282803],[45.585777726309075,6.3551374592843928],[46.786638656428515,7.5341519468285973],[43.824141400282677,8.5277772234287514],[43.384549001985896,8.8780979457196807],[43.12420285536583,9.031093840556581],[42.779637087591205,9.5473273262209233],[42.414694233811581,9.9433106606005328],[42.184487759851351,10.435727339896985],[42.156943871949942,10.585096614013613],[42.175519086930045,10.735844108849504],[42.238499295409682,10.874058938555711],[42.92454540669808,11.882399690801028],[43.048541254912308,11.958807966206836],[43.189296415692034,11.996240300340984],[43.334867422388051,11.991520455601551],[43.472902170438559,11.945048923934428],[43.814326766605532,11.678977579027109],[44.035150848052929,11.334485484263173],[44.1930789629521,11.155673426088894],[44.495514259267573,10.93226556311345],[44.787773264434193,10.935129660619433],[45.717075376932556,11.325647728719234],[46.480804923153563,11.264599596730008],[47.307577449212125,11.664200116391445],[47.44412850320353,11.672257885240981],[47.765114153299358,11.619359897519862],[47.923940654670339,11.635174477482332],[48.468686762982671,11.809338706686486],[49.001743343103421,11.777629914667571],[49.496000638844095,11.928741869047998],[49.930549118132994,12.007205546990361],[50.130561070493336,12.117642360244691],[50.368649861084336,12.366061931468465],[50.709308789374241,12.476319883063194],[50.89671738120613,12.470401840906097],[51.409268893072749,12.306009747914333],[51.541515757120166,12.240020527839263],[51.648693335164097,12.138252276497655],[51.721436960507525,12.009597019362989],[51.754385759299652,11.815879556389609],[51.59636636123539,11.280230425586289],[51.624636679861453,10.904018810306521],[51.755805751861928,10.791841003895403],[51.8501011927249,10.61109589597846],[51.88423408663769,10.374635696543951],[51.841195639019901,10.18350327158417],[51.762103144491313,10.059171605752764],[51.650203285516028,9.9632945627211953],[51.515215229897855,9.9041996335005482],[51.380707568110829,9.8872358286270536],[51.313742043760548,9.3239506863616306],[51.26405341967893,9.1895007694361972],[50.862451898589555,8.5903964168245146],[50.52210967427829,7.9277947106393682],[50.282586227011826,7.6726128242566034],[50.121137072098563,7.2516036239557948],[49.68919728518074,6.5563159831473934],[49.490711680600036,5.9392022163686597],[48.396046509173367,4.227379054269365],[47.251209881403703,2.951950007984065],[46.360523729915847,2.0833536520974985],[45.183651438784359,1.3838943395412697],[44.656090263253624,1.0074720745868635],[43.821276956401199,0.26715427091506261],[42.37504404571564,-1.2855154241821374],[41.897347914155844,-2.0370641508283041],[41.780412952335048,-2.1291562764016643],[41.641549854705751,-2.1827453145106568],[41.395226762841311,-2.1753297096062401],[41.182364306223498,-2.0511539013971323],[41.092962256684785,-1.9321496223693235],[41.036985242530392,-1.7640721884113375],[40.556304454892647,-1.1374286385098977],[40.487971887949421,-0.96491459859000217],[40.464682681793882,2.8126610287292388]]],"type":"Polygon"},"properties":{"alpha2":"SO"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-58.550011773506164,4.106909696161436],[-58.532026161039028,4.3192493174897617],[-58.389064892541839,4.6585198451916385],[-58.41367553614689,4.8516632635218526],[-58.347369308234263,5.0749094630905542],[-58.152720098301259,5.3167311706092519],[-57.795140472684473,5.4836538380580544],[-57.619271192086529,5.8044535729217648],[-57.468664675820676,6.2214802452604987],[-57.363413237807855,6.333182100837095],[-57.141698631224877,6.4621519610189404],[-56.938009056985777,6.4916305596220569],[-56.140194213690314,6.3596882948818605],[-55.941629790084917,6.4483955514320455],[-55.678417673825031,6.4846493730154044],[-55.375740167042331,6.4566304034443149],[-54.760680498728107,6.481952585861479],[-54.231185717555526,6.3940085898765107],[-53.849484976841957,6.2638362827910123],[-53.695022007174927,6.1553822133026248],[-53.591765842172528,5.9973972185216411],[-53.554428872150488,5.8123916919649341],[-53.599378138428925,5.366715679865008],[-53.656508622522956,5.2375285344453406],[-53.972943764290093,4.7946950694036978],[-53.948452631362187,4.527905944717614],[-53.875764739953738,4.2597352299493974],[-53.58244543576734,3.8853224871140553],[-53.521286965925782,3.7474853187483457],[-53.514549804644993,3.3797361242985406],[-53.558069518654506,3.2342305341375792],[-53.689776062388937,3.0118699715239474],[-53.699887409116961,2.7537589811107859],[-53.763109068693019,2.5673098967494439],[-54.032994323391932,2.1244510063929085],[-54.244215940340716,1.9375312553980395],[-54.376882638014308,1.8695370449475712],[-54.737528562304831,1.8335709048937716],[-54.869607979852191,1.8730439577925631],[-55.120906471645199,2.0147788649862992],[-55.250960906865373,1.9592819585126877],[-55.422988443625364,1.9372005358833213],[-55.468807198185544,1.6940965041444918],[-55.582359153035661,1.5280836350032758],[-55.795891251913567,1.3954543826723163],[-55.979237493158784,1.3441114458193366],[-56.605309720125653,1.4561971187448044],[-56.944074476894656,1.5979754107076281],[-57.102703573087631,1.7346900849673186],[-57.625225092338233,2.5951099190598912],[-57.728897972557569,2.9007106717341782],[-57.892498978564134,2.9593660931389074],[-58.007654742652818,3.048859620018685],[-58.132131837412075,3.2743774196469038],[-58.258863470584103,3.4150412719177243],[-58.483395904517558,3.7870094400091348],[-58.550011773506164,4.106909696161436]]],"type":"Polygon"},"properties":{"alpha2":"SR"},"type":"Feature"}, +{"geometry":{"coordinates":[[[23.658095126750876,8.5647445477425581],[23.65640272827482,8.7571569712836013],[23.703302444617563,8.8940569858741512],[23.893951823612941,9.151925373144671],[24.144021535817256,9.2972607775890079],[24.199399633612522,9.5471088234852708],[24.285118955138579,9.7057148782627873],[24.29893021163231,9.885427954563335],[24.346571415950908,10.014916934699276],[24.535147549612521,10.263840873325725],[24.649118206140773,10.568831939742953],[24.751949621225084,10.680544511813549],[24.88357021881966,10.756241387414306],[25.11057565993384,10.819767798296345],[25.799170152551614,10.902798453040276],[25.99587659929438,10.886894508803179],[26.20822208465794,10.76314896278784],[26.297563757395224,10.644607429433975],[26.367993677155884,10.448940616531265],[26.779823183725433,10.039214135377222],[27.036897569781182,10.112240761806571],[27.935570007662804,10.098398985357379],[28.155801929102449,10.019009630839722],[28.322334226668904,9.8364754474164915],[28.528941719710616,9.84357765387435],[28.746366352011144,10.041020206609877],[29.121760876524203,10.188222194239199],[29.189949032842897,10.342707798226709],[29.328068426516246,10.480794985853311],[29.90891103324315,10.76816714818777],[30.058764822337711,10.773987546712318],[30.203604167887462,10.735108257550866],[30.872822539630217,10.276164375427651],[30.982743407520083,10.27872797054876],[31.291769482800028,10.568191539560956],[31.560629676678762,10.995883820181227],[31.859102892553494,11.270723888810275],[31.846086399316391,11.498689584468126],[31.634558955944659,11.765746097869174],[31.575880741109913,11.950841758937552],[31.607877627128421,12.190563046162143],[31.713048187202109,12.353788932394441],[31.828734251992959,12.442916077547629],[31.965238566341252,12.494814880561306],[32.314506272483257,12.511789411926834],[32.446748827626379,12.640279299216658],[32.629421210266365,12.714215386273521],[33.347472782951066,12.69453983367468],[33.55189097678867,12.571361489707883],[33.63827550537502,12.45603795818602],[33.688186007671661,12.320868636732305],[33.691314992316919,12.129295744955433],[33.585784028893578,11.537467311287749],[33.648764943540982,11.067757414064513],[34.333366487359946,10.440986231789987],[34.401547842862961,10.262658770245302],[34.456309753044721,9.7875722847332103],[34.536561836242448,9.6603387476619087],[34.573964585275633,9.5237761284302724],[34.601514275905153,8.6955997527065154],[34.553232200217018,8.4033371309498577],[34.435865606336733,8.2029126237957097],[34.239774744831642,8.0339585099139992],[34.113513708170224,7.9705995426518061],[34.300747337518438,7.8114317588695128],[34.48544252864604,7.5096667977822431],[34.576639982018477,7.416003209332632],[34.808056549108187,7.2791236460177755],[35.130906027607161,6.9307854983618871],[35.422440648002038,6.2321679393189386],[35.473037964934399,6.0075330295366198],[35.728587502074767,5.6869537751920207],[35.765506011317619,5.5425114528659503],[35.745929232645814,5.3452770182990372],[35.617365097505122,5.1345745427745992],[33.838622965518375,3.3975701790959145],[33.712201208615483,3.3077341892119745],[33.46082041314444,3.2561068886529285],[33.086228102093983,3.2794396730678219],[32.921760497704959,3.3262225531963514],[32.583457253486579,3.2486720923738548],[32.387562053929166,3.0878821641079992],[32.151171333101892,3.0201769054182117],[31.956510820110552,3.0532417353699928],[31.692038143836808,3.2095232492678485],[31.476921269433998,3.1803216450194327],[31.262893661822236,3.2261269360341349],[31.155031526442354,3.1040779231284286],[31.03000365151831,3.0292886712989207],[30.840071650940455,2.9911977769860867],[30.695874127164743,3.0119938303182234],[30.253500332472761,3.2519698102839962],[30.100423659734119,3.4753527393544257],[29.860938782932937,3.6102394919146392],[29.515415021754958,3.985983559616848],[29.356785404193065,3.9099065689226253],[29.211955684501685,3.8923398602845323],[28.807058740542157,3.9735440167520539],[28.532798161410113,3.8471455972560142],[28.329763466863628,3.8203392209042297],[27.949863696715454,3.913077266498354],[27.519762384681918,4.2155264788017774],[26.845462166087451,5.0056961542190441],[26.780948861748907,5.1366868762720381],[26.742266459212683,5.3871027863641832],[26.521863980209293,5.5309727201586343],[26.248606582354022,5.6458962864011033],[26.108104228644109,5.7779931031290559],[25.871509189596878,6.1857077911103096],[25.825576787798006,6.4389537594842743],[25.598545539007858,6.6540915088144743],[24.910879279206348,7.0889381570253454],[24.732933586803153,7.3335145434545321],[24.684897755446524,7.5955801531416203],[24.590786834429419,7.6883407769994658],[24.062080471605089,7.8485689783873216],[23.83060424097647,8.0420318534971553],[23.731654624396157,8.2193090686957166],[23.658095126750876,8.5647445477425581]]],"type":"Polygon"},"properties":{"alpha2":"SS"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[6.8513160878477324,1.4620601126945463],[6.8348164257122992,1.6654018431385849],[6.9009128028600735,1.8584079754725014],[7.0574490834397707,2.0553011000008556],[7.2518935046667625,2.1716907951449373],[7.4389657913116833,2.198252815184222],[7.6225374295527777,2.1534975364720115],[7.7999449989386331,2.0266310029892023],[7.9173499772304741,1.8399859487326233],[7.9518225266522036,1.6142841046973884],[7.8794317645162266,1.362437154024958],[7.7535794720247013,1.1922581881310101],[7.5441303823885137,1.0672862796414762],[7.3025919707139009,1.0489302467330743],[7.0748214153969995,1.1415055560964593],[6.9284273629600763,1.2836453778933437],[6.8513160878477324,1.4620601126945463]]],[[[5.9846460381844091,0.10116216595179994],[5.9760731811754368,0.31428073386937916],[6.0550775770618275,0.54712704284628599],[6.2308895701980838,0.74485458474305777],[6.500325917715454,0.88399650175216116],[6.7492682925082059,0.90024348182298397],[6.9313787941300165,0.84026350638240943],[7.0775262191025643,0.71615564027919687],[7.2284376575554159,0.46940871505661319],[7.2490856847113223,0.21828608067156072],[7.1946211913938276,0.015365720621821982],[6.9910347692618275,-0.25365057959153819],[6.71453702088969,-0.42683083986570053],[6.4699707546819676,-0.4447587578402708],[6.2471134278296345,-0.35255044576753281],[6.0997302196884267,-0.20454447695199765],[5.9846460381844091,0.10116216595179994]]]],"type":"MultiPolygon"},"properties":{"alpha2":"ST"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-90.592903417876045,13.670470701528222],[-90.580840573744538,13.9867261226411],[-90.396976893238516,14.261970210977548],[-90.059013365732042,14.508301081062774],[-89.983501182318662,14.676021534952229],[-89.84852637313611,14.807468402902431],[-89.720373664324327,14.867873121577581],[-89.437938823043581,14.925682161112384],[-88.92632169666085,14.830703231304016],[-88.454891063179673,14.476666555248659],[-88.366637512173909,14.44132139353168],[-88.112091134210402,14.485569378691252],[-87.58851752192291,14.34177162160575],[-87.317069751729036,14.114640199780778],[-87.246779922467894,13.986522496405914],[-87.216534584859389,13.843553878980416],[-87.243259993773606,13.374892591847427],[-87.442627896570585,12.958364990151674],[-87.624287908721769,12.785956735024765],[-87.753342012880864,12.713480266716676],[-88.14764341435928,12.665451938992819],[-88.646637428388914,12.702664844685662],[-89.039744942084383,12.814218836436288],[-89.435508819732178,12.99541456325646],[-89.973887771716292,13.090008186440228],[-90.426646031371547,13.362473094014957],[-90.524816145208192,13.481173851328807],[-90.592903417876045,13.670470701528222]]],"type":"Polygon"},"properties":{"alpha2":"SV"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-63.421569297076431,17.662250908312174],[-63.572233510263608,17.841936482147002],[-63.622751539002181,18.023257969564359],[-63.585391721878601,18.258638337022436],[-63.469836961623166,18.428711075656004],[-63.298025756439436,18.537008954766389],[-63.117932820348607,18.568945312499991],[-62.886336512644718,18.552799183982582],[-62.667461350274429,18.431580862264404],[-62.552382475852923,18.266866790862505],[-62.511438787085019,18.070150022370374],[-62.54175947451612,17.877194942608018],[-62.653589813356085,17.682736833575831],[-62.813956939748742,17.565367404581391],[-63.007376206749932,17.519736436559235],[-63.242711918257157,17.565139040911436],[-63.421569297076431,17.662250908312174]]],"type":"Polygon"},"properties":{"alpha2":"SX"},"type":"Feature"}, +{"geometry":{"coordinates":[[[35.363550578480591,35.273222017050948],[35.279229370838401,35.452095069713337],[35.270850787392298,35.649668940370447],[35.465650376949505,36.176221050061862],[35.556464238581412,36.286288943104317],[35.674670779698317,36.366222116320948],[35.939908417348256,36.415251072045855],[36.057805919890562,36.556388337454131],[36.118756655777666,36.846469980123516],[36.255334041694262,37.097806646080898],[36.401184752189373,37.230908848910765],[36.538444599663066,37.287634639824397],[36.686311983378509,37.301538521168446],[37.018697266936293,37.252201906613841],[37.252495671036961,37.15117826423068],[37.344125045502651,37.147881957574761],[38.041399001549401,37.376325791114624],[38.228463545936243,37.400190053213365],[38.575431981820358,37.344287385952775],[38.887127603006803,37.193471249228537],[39.296462611698026,37.182899996551491],[39.859099752504918,37.303058277448351],[40.6218930959297,37.590152396523777],[41.471676448421626,37.590595355921337],[41.84481744096432,37.668998567092459],[42.044879245184518,37.77146736082323],[42.18750763230792,37.796804834893067],[42.377561388255714,37.765472454066391],[42.649208619566778,37.599259789613058],[42.761168651952012,37.450507892854709],[42.841473288306531,37.238994842140421],[42.844703645179834,36.990721369774057],[42.728106701120197,36.771506167467429],[42.08309651448883,36.19349079399138],[41.784781362355531,36.083279475023545],[41.848609932622068,35.87118629489315],[41.852060276478873,35.597608530042855],[41.712989876194285,35.183957801046525],[41.679274075444852,34.64681087299688],[41.620091333206297,34.506724381529793],[41.312038447558741,34.049316189748751],[41.186499716652435,33.968934114728313],[40.858996833189103,33.85773086364604],[39.006016796777445,32.929448547338986],[36.925662910216907,31.829227223495838],[36.741316910669227,31.823528809793519],[36.163015912584612,31.932934500116357],[36.012390980221411,32.029190069809488],[35.791974281404642,32.111648683024455],[35.638627028278464,32.257579693063811],[35.458106209788959,32.359006443188008],[35.340220230537646,32.51180385680609],[35.287722817489545,32.746381366807277],[35.370963328382054,33.030598261352552],[35.337440542467405,33.318396888483107],[35.35639162013652,33.467351736956054],[35.457676633277281,33.684606616980695],[35.531581056352692,33.974958418967262],[35.61530803851501,34.08594526808622],[35.746571637277206,34.186002727081672],[35.615252336550718,34.28365133481929],[35.510572790183026,34.447809523315669],[35.395507770815335,34.859588225240181],[35.402426058281115,35.220961574210179],[35.363550578480591,35.273222017050948]]],"type":"Polygon"},"properties":{"alpha2":"SY"},"type":"Feature"}, +{"geometry":{"coordinates":[[[30.289411661344811,-26.463984559780542],[30.342534294538737,-26.231095128866968],[30.686748663671832,-25.682814893992933],[30.859058609684993,-25.484846382924687],[31.094125274951118,-25.317870814517615],[31.278852588723311,-25.259059841207623],[31.505042372834076,-25.254962920730804],[31.946199849407421,-25.457697799408599],[32.186089190210659,-25.518131060209377],[32.453393858638911,-25.70951619992443],[32.528349442925837,-25.84262907723539],[32.559613682909749,-25.992161431660868],[32.54906209946467,-26.257926307567473],[32.602640079396494,-26.464300112247884],[32.611201868876982,-26.877453968139818],[32.558757454711113,-27.065105232505903],[32.451536861344763,-27.206165426954499],[32.44699825936911,-27.410674093561219],[32.395141035645793,-27.548628722005951],[32.228079200196056,-27.726522245938718],[31.996715101832763,-27.804152244712331],[31.415299767002459,-27.792221513760484],[31.054744154556722,-27.687528646364012],[30.729370176860257,-27.48408358918423],[30.524926391387712,-27.190301807496844],[30.379095174742044,-27.042364193217889],[30.297726446781887,-26.820293270096371],[30.289411661344811,-26.463984559780542]]],"type":"Polygon"},"properties":{"alpha2":"SZ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-72.836696690310234,21.725481582416606],[-72.806740532662687,22.007526143075346],[-72.670548156420523,22.21851585880005],[-72.497632945764494,22.322325122603047],[-72.024863714757529,22.449639790031981],[-71.764617280799754,22.422216178741326],[-71.368198858776054,22.232190348387078],[-71.187992614606301,22.005486028322096],[-71.138487339065279,21.808928431262313],[-71.171078768304511,21.608869789838227],[-71.305953338372944,21.405577152591942],[-71.462084176776528,21.296155765770006],[-71.647822778863869,21.253131931802276],[-71.954272903936058,21.30200363080608],[-72.26176156791611,21.257879773418683],[-72.497416758997375,21.286161074659621],[-72.630478515897295,21.35584002512007],[-72.736842023369945,21.461893745303193],[-72.836696690310234,21.725481582416606]]],"type":"Polygon"},"properties":{"alpha2":"TC"},"type":"Feature"}, +{"geometry":{"coordinates":[[[12.961622365794543,14.266726402017852],[12.950004341078628,14.419598381848131],[13.006967337507621,14.615264245811463],[13.94868987689177,16.023029717922963],[14.990305392428425,17.125462700629278],[15.236529943581196,19.942788897006224],[15.350549969149233,20.266529196042484],[15.154876116759153,20.482482329413127],[15.061264312341317,20.855383751875397],[14.754702214380467,21.2639423069805],[14.686486388643299,21.457206329322648],[14.671092221911834,21.885824918910057],[14.483595190874045,23.013104473659034],[14.511271027080832,23.162826974589994],[14.582725243523027,23.297277984803372],[14.77951832887589,23.454697920993535],[15.825695331772106,23.919209482787348],[16.018142702770927,23.943795392297751],[16.16070946963875,23.912718901049566],[24.201409970682967,19.944795026093566],[24.360248457714437,19.821178101949972],[24.440007062407531,19.692480468079864],[24.480024399385744,19.49522575549144],[24.449468319038303,15.577871622481418],[24.328312036368999,15.372303527231104],[24.125682874517292,15.246293569758048],[23.98371961078648,15.221800972104491],[23.634050765896593,15.244024715440711],[23.444751230808205,15.225133097048149],[23.369558936446079,14.920138803528157],[23.197033699396918,14.720210942232429],[23.104445828349444,14.473879036625679],[23.001660871143574,14.348476764729854],[23.036708552688477,14.121575969565775],[22.982611200671933,13.932524046663922],[22.859798847235691,13.778952308080477],[22.671163042374257,13.647119373242873],[22.725926626477207,13.328946086958609],[22.650656261668271,13.060683123871128],[22.765614380370778,12.941460713687579],[22.885994227841735,12.710178026461763],[22.95009477249306,12.334738059407659],[23.070581237913181,12.090466075969763],[23.080633863752851,11.83216735956432],[23.30471237831421,11.664308166573734],[23.414437586232768,11.436780031329677],[23.428915383661582,11.099965357780441],[23.305265358858122,10.692649153954781],[23.186002627390636,10.540901228731174],[23.018150286686428,10.44562991276003],[22.826719028355711,10.421031025373127],[22.544000103409129,10.465294860998247],[22.190272853414022,10.2886005416063],[22.140029709235257,10.091530290917829],[22.026958341435009,9.9269974370836689],[21.868808782271895,9.8144299116648579],[21.719555957426731,9.6206593297770162],[21.510359164960573,9.5019467179070247],[21.084161896766386,9.0143967106334877],[20.61334693257723,8.7073746944310955],[20.431194089256305,8.6353680565533928],[20.160822092489632,8.6305030854083071],[19.587278643060248,8.5143658095835555],[19.482191134086488,8.3240885998848881],[18.94204140923004,7.7160428209581928],[18.791039009448184,7.6004957563333182],[18.607816521042118,7.5496787451501302],[17.776231488377725,7.4836396522172164],[17.012106378295744,7.1041067728481089],[16.826172354238437,7.053244793672695],[16.634815284017233,7.0765014895717266],[16.457484148453251,7.1780393183565678],[15.979135955092403,6.9938368837432936],[15.82938610656911,6.9757521134945355],[15.319394381731183,7.0506285675767737],[15.115727811500808,7.1819743209070532],[14.997749194714629,7.3936647191978642],[15.001736337728284,7.6732189919825284],[14.720321761686826,8.2314261646308697],[13.980921128120123,8.8481365389958828],[13.590833513312546,9.3089452051405317],[13.522376193827984,9.4587501125766007],[13.477885666571334,9.7111948756498343],[13.525732025863968,9.905757972140016],[13.909334473639568,10.351555299481326],[14.159344744163445,10.472381229347194],[14.67422534905894,10.471487556709643],[14.582819489967639,10.734450370658253],[14.530355404185658,11.101299266258021],[14.600930159059777,11.544163093484713],[14.582686185032276,11.735937076064973],[14.411477400808957,12.09715615207274],[14.366674358282189,12.318466171054517],[14.117101750601297,12.564055755097417],[13.891239703334179,12.609549081131602],[13.725422447577653,12.710874993798177],[13.17537667808695,13.451585714351669],[12.961622365794543,14.266726402017852]]],"type":"Polygon"},"properties":{"alpha2":"TD"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[68.29968546028681,-49.234511615716563],[68.279255965996057,-48.969009115623287],[68.414279970786225,-48.574475726007314],[68.613171530472968,-48.332809896934506],[68.741517299736032,-48.234963077521073],[68.868249872582012,-48.179800210824453],[69.060714182536074,-48.15668302035921],[69.28704559500116,-48.212657896711235],[69.501142937137089,-48.409108347947338],[69.817948185136103,-48.524940745228534],[69.971000799846891,-48.644893918748281],[70.341051848029139,-48.558992214758483],[70.505591475751814,-48.571284570518706],[70.711814902378819,-48.638916222093187],[70.82696351655099,-48.720099259356601],[70.95888046515374,-48.878848970966089],[71.037416486931335,-49.069140473920108],[71.055139757060715,-49.21180572186551],[70.966724588011189,-49.519152216241004],[70.781657756709734,-49.740189799251404],[70.677997047693694,-49.918420628356628],[70.421463239476722,-50.116641720376499],[70.139109915370696,-50.203979944514913],[69.538251864594898,-50.145156882523331],[69.323427148966999,-50.092748765053607],[69.151239298995037,-50.179033286127918],[69.012887272386237,-50.204364938780905],[68.785814375708952,-50.20196977796904],[68.584449790059949,-50.143107567094162],[68.427983573654302,-50.016078837275948],[68.305295479812941,-49.798747160388736],[68.284653013609471,-49.611044399882374],[68.327190086336358,-49.423920740612751],[68.29968546028681,-49.234511615716563]]],[[[51.247004405637234,-46.65613175526218],[51.162803628615343,-46.430199554541481],[51.194128624025815,-46.191130788626481],[51.333695061125155,-45.994518632824807],[51.583402343683751,-45.852906273902484],[51.773255428062186,-45.82811333327431],[52.000844227986434,-45.899417936071792],[52.215894026397883,-46.09606542164618],[52.307597056387515,-46.278690493542925],[52.333111764419691,-46.473463703240242],[52.281626845573008,-46.663034024422409],[52.161089018394811,-46.818141199501191],[51.990103346432846,-46.914844371762889],[51.768282178241712,-46.948401234511344],[51.546333620352875,-46.904784137148972],[51.380179971689536,-46.814978516301274],[51.247004405637234,-46.65613175526218]]]],"type":"MultiPolygon"},"properties":{"alpha2":"TF"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-0.55701645752321283,10.537156958595212],[-0.58944387791510111,10.736878199736298],[-0.53799879553585328,10.946381748967642],[-0.56780930412491071,11.125818654179525],[-0.54298134332520598,11.271211964291233],[-0.44646883405318499,11.441923593978037],[-0.24658067861029193,11.58226306770792],[-0.0040638992816740827,11.611177671582386],[0.59528265095933486,11.475391525078942],[1.016539732689028,11.479306433651868],[1.1943679653989567,11.397340266943603],[1.3270817415689,11.253366705314054],[1.3844101484917701,11.117708260662191],[1.3997293502529287,10.971232727199849],[1.3037879213163599,10.634162961057374],[1.6651151111368618,10.364871274682478],[1.7785062456613301,10.216928678675531],[1.8233705391408814,10.084144461098552],[1.8758756991034664,9.5357184772496524],[2.0398734597068464,9.2877266054154077],[2.0970439066749424,9.1042755834622326],[2.1246706293169573,7.0084113491804683],[2.0922973315961069,6.8189765375344802],[2.2456380601779897,6.4708197014058682],[2.2774060530311773,6.2792395155587863],[2.2515899098963343,6.1354850264476664],[2.1853335830446619,6.0053238742628379],[2.001758825088368,5.8478319558627412],[1.8055171680031514,5.7517031658087454],[1.3276765282472405,5.6098134294209601],[1.0894232290927883,5.5993770496013502],[0.87343640423413893,5.7004871087903304],[0.38557035068807233,6.0971804501471825],[0.29543593638748311,6.2183744168539281],[0.062381858675115087,6.6632237063022819],[0.027245091949753886,6.8129873846110662],[0.086895062009690285,7.1670125177613482],[0.018915974100542576,7.3400627874405355],[0.0002582821599222962,7.5496090171594048],[0.019449020659330407,7.6840273471888922],[0.095491410833617965,7.8561929038681697],[0.10086715553236347,8.2733336117589751],[-0.040501786694704944,8.4533946871904195],[-0.11254058026330077,8.6292870656196037],[-0.11372836833793309,8.8193562786126165],[-0.033315963869904,9.0410498692574315],[-0.17771431766275081,9.1793087104445661],[-0.26300439100166795,9.4058300037904701],[-0.25846617973453134,9.5519185136602331],[-0.15518568634745319,9.9260096445122006],[-0.15407143887642114,10.037860590169908],[-0.45888747238613858,10.332554466109078],[-0.55701645752321283,10.537156958595212]]],"type":"Polygon"},"properties":{"alpha2":"TG"},"type":"Feature"}, +{"geometry":{"coordinates":[[[96.924491290705632,18.299247569166642],[96.875269628300373,18.485470763236567],[96.917682679317494,18.721615903856428],[97.028631086947811,18.879071614784408],[97.210037370486432,18.989404565789595],[97.301957238877108,19.261735532860143],[97.354835913116105,19.650516815840913],[97.608155993485425,20.036594212074252],[97.742977631741752,20.168755691104952],[97.964067189840335,20.248979274257259],[98.368398619394313,20.19827230516157],[98.589500380888722,20.243244743521572],[98.657976841807027,20.378936757704732],[98.756264524687154,20.485489798752038],[99.01730429673843,20.595899219655639],[99.15826809386644,20.762269684100492],[99.334883282480007,20.847146658777994],[99.618050020193678,20.844971370135081],[99.881876047117075,20.924093421479153],[100.15138725237645,20.858989552626046],[100.29132704883702,20.88494799994784],[100.44083025586491,20.870309026438502],[100.65910011659601,20.75114976617791],[100.94535885207655,20.424704250750903],[101.02471147753282,20.254049596857634],[101.03863727229837,20.104477261477733],[101.46712445016689,19.97804964955295],[101.57996666313288,19.886424217286361],[101.66168663410889,19.766214270905945],[101.70537785372714,19.627579274626612],[101.70547717753938,19.361545726220658],[101.78503592978025,18.944931282758468],[101.64295661780484,18.517683520035259],[101.92923155377788,18.679774803441557],[102.1286797940231,18.709655896095462],[102.32370876898047,18.658312160721131],[102.69349476677529,18.438039886484088],[102.98213310604164,18.803418492766685],[103.11508589988276,18.877193322948134],[103.26406523276852,18.907596107876035],[103.5542468208475,18.913493379980309],[103.87976626648988,18.816206108368469],[104.07055681175484,18.803875852737626],[104.20306966212455,18.749700804132615],[104.34577654774702,18.623705595900493],[104.77453679565326,18.066639432721935],[105.08112252865801,17.826480955707641],[105.16890731738648,17.717448086059193],[105.3089078194533,17.375243590119016],[105.24530956935202,16.841901330200649],[105.26319394369357,16.719509559933268],[105.41011770818493,16.535799267698465],[105.6569353112843,16.419807261248518],[105.76807247343909,16.332153988596829],[105.8934338617789,16.096584545756507],[106.03188143540547,15.968068570501568],[106.10801610366404,15.83463059546316],[106.13521502414767,15.581843547115025],[106.09822416908814,15.358328727110447],[106.01320872834278,15.171603716280815],[106.0455758758487,14.902898532644224],[105.9698350894098,14.449001652253104],[105.90450353609518,14.275018989415583],[105.78014396661088,14.136914404755213],[105.33548964708396,13.80150307462384],[105.14792460066808,13.733178793744912],[104.90065746571806,13.758701887151334],[104.67847217766061,13.913430022042615],[103.97374363794624,13.858532819929614],[103.59192056401137,13.90903259703029],[103.40326871716658,13.865140212993351],[103.29295777474152,13.796290927143511],[102.90782406669707,13.241066622477856],[102.97930750956216,12.925999325307631],[103.15914873909557,12.721173698227537],[103.22755501204553,12.590769761445662],[103.25453786555721,12.396851742226298],[103.22397912458726,12.24179818627886],[103.42558128131631,11.795505500001648],[103.43047815218372,11.651985713129712],[103.39438045642636,11.512993393453165],[103.32026580756995,11.389993688604134],[103.21424774766356,11.2931325532518],[103.085071455659,11.230399833304297],[102.93087378217095,11.207323914713465],[102.71148386556899,11.08760439479537],[102.56460631095135,11.073200445317799],[102.41992191006211,11.102302231351235],[102.23333539322674,11.214842995570967],[102.10603748422999,11.354967020867258],[102.0369255695212,11.557862797758345],[101.85490464225478,11.757456626408066],[101.7813022473632,12.073063966968439],[101.73808748579897,12.118695207185926],[101.66753609116346,12.158108116209281],[101.46250147133449,12.119507925974276],[100.81700980830556,12.160604204238066],[100.63286803783485,12.230064521546105],[100.48098888962785,12.379062398953771],[100.48789819968229,12.139675974159708],[100.45525507574345,11.990614334477009],[100.09987924743567,11.286837360684174],[99.956666100361858,10.719152439533779],[99.744136701762159,10.358622444731154],[99.697701292770489,10.202686669814058],[99.904835896715767,10.287045093078577],[100.10108833306303,10.279338601451716],[100.36624929170524,10.156110004236439],[100.5226287750357,9.9661050151977939],[100.56980618963387,9.7742197032743938],[100.55271151958247,9.4329618486803763],[100.50248155292228,9.241487531457599],[100.40118694461546,9.1030913318905586],[100.427456106854,8.9324925124119385],[100.55758071905628,8.8157221602282192],[100.70077971700994,8.5889085947064867],[100.96031448594283,7.5350434558390829],[101.10673695795873,7.3918759043270974],[101.40919058970692,7.3964031886650972],[101.65250571764513,7.340505043008247],[101.7842097347034,7.2748817431219344],[102.16418274993521,6.8258680301183414],[102.48016649977606,6.5678681880156082],[102.55990813079032,6.4401954381247197],[102.59846436522226,6.1940106763014882],[102.53971798256478,6.0027167085983812],[102.25976660779493,5.5080441574190209],[101.98988416527054,5.3209755636311638],[101.7546961059015,5.2808613637890645],[101.52327530478927,5.3339986656840033],[101.33383568782121,5.18790656181383],[101.14959818343419,5.1382763414443708],[101.00648886519132,5.1487179755325467],[100.87223811560146,5.1993740800505526],[100.62575014527741,5.420187265126188],[100.53797550242753,5.541056875876528],[100.48343170511548,5.7320097986825003],[100.50264460562056,5.9116145561868301],[100.43983202725605,5.9891509640438878],[100.37115404040171,6.0106423275109107],[100.23191703395788,5.9552971823542826],[100.08356643279208,5.94366727246247],[99.938363072673482,5.9762117017955516],[99.814246786892667,6.0464314699492645],[99.616205219776802,6.017160894035869],[99.383904275404333,6.0894523639236171],[99.240187159837873,6.2217524561603925],[99.119561953913617,6.4851068405327483],[99.11601111851455,6.6917494941357054],[99.18771100499805,6.8895067924666318],[99.158131065312631,6.925696942216951],[98.74622679141973,7.1133617901499422],[98.649354333409434,7.2227051377169253],[98.586572838364816,7.3622217029086405],[98.326081358147761,7.2772446504300916],[98.081956931709215,7.324713171449698],[97.92229830479414,7.4446938671755873],[97.82233455207944,7.6175912142878763],[97.762765716263047,7.9119451768116242],[97.798088055608432,8.179624587484323],[97.727317094170957,8.529307155292706],[97.78177045184573,8.904977020563587],[97.771786687611609,9.2084330640331871],[97.90779198153308,9.4581219022160141],[98.224565496070156,10.340944587115491],[98.264425864161311,10.487328298362783],[98.263866221517006,10.74077867140517],[98.310651725891745,10.885330541937556],[98.78632439358168,11.408439455862087],[99.05834246976616,11.883083805148507],[98.930012196516003,12.3117672166423],[98.763021865000368,12.537108136443807],[98.634255259106126,12.92931500991938],[98.625134348987928,13.08358690513132],[98.669369149207554,13.315790330674835],[98.639546259044863,13.575526687194346],[98.549614286222294,13.719307841124294],[98.201216202738891,14.022661839386213],[97.838974862588046,14.524697753043645],[97.77675103562801,14.642695818727828],[97.707167991729349,14.905574828426936],[97.691953294432423,15.230573211231194],[97.721295479019886,15.374693357551809],[97.857700919015883,15.576456312927096],[98.062645126133262,15.708706441262716],[98.100056836278867,16.137467727626902],[98.142254042194708,16.26365463460278],[98.005790645896724,16.568998132205781],[97.972588206816269,16.736022621854524],[97.298790936492793,17.508053429676682],[97.216947814430327,17.696399193648812],[97.207117297367375,17.917247158203129],[97.050002278315532,18.061076352801113],[96.924491290705632,18.299247569166642]]],"type":"Polygon"},"properties":{"alpha2":"TH"},"type":"Feature"}, +{"geometry":{"coordinates":[[[67.155065626782331,38.781608706548923],[67.027658208467031,38.859777146068552],[66.929050119556223,38.972113248814118],[66.851133073302066,39.206991802921877],[66.863047256844965,39.355991827784997],[66.970603501252569,39.671051956592116],[67.113153534190843,39.855160144380598],[67.484223215598206,40.062558035413687],[67.631399196110351,40.113396315380186],[68.140910840339188,40.067915548158588],[68.152609138653403,40.312882887356075],[68.247849486765375,40.488383449207326],[68.450035731897103,40.633116049053847],[68.722023348728712,40.689728475718077],[68.780239813558595,40.827708631973181],[68.963074390217258,41.07214323867953],[69.183111507196784,41.240440062934375],[69.455815103577791,41.295122383321868],[69.733702491863355,41.209806076485904],[69.947003743410974,41.292873549336356],[70.071678729527292,41.426592970131537],[70.240075442527598,41.509504271271609],[70.675423750340769,41.513745760305341],[70.891287914990642,41.409370970664554],[71.085656035568661,41.204268886544185],[71.235890536145803,40.843286191131284],[71.242423021237187,40.633218817068531],[71.371061846973433,40.506352431177405],[71.435820495358229,40.376453824545202],[71.446095786830597,40.102712659173598],[71.682646022305548,40.056078931100728],[71.948653085166242,39.848861224447127],[72.12134888948971,39.84566586092852],[72.252962135456343,39.805200541270935],[72.52751346624656,39.876055375944389],[73.054835025384776,39.864481230128185],[73.440622511626273,39.958758475675531],[73.810547218285663,39.915508643844532],[73.979483277123009,39.807667906233263],[74.070666582829162,39.687603191596374],[74.121915528241431,39.545816462547172],[74.130960678559703,39.370925000536658],[74.266516653424731,39.159688365794182],[74.452167691348663,39.128028558492737],[75.119455329448101,38.85447782191666],[75.223704983630725,38.743952822940813],[75.312353581330115,38.555283954691909],[75.33455611861487,38.370601813268394],[75.302152266823597,38.211415217918528],[75.437536848389158,37.787750908940836],[75.598360789865879,37.526181749903017],[75.60851613719629,37.286403868744777],[75.559449599707762,37.150060930399995],[75.473347539935929,37.033513269588425],[75.036433001668613,36.753445825017124],[74.788682517808198,36.742565212637906],[74.433884517902584,36.904117119707472],[74.279260350419747,36.842462559826252],[73.715024168503092,36.733066958600318],[73.460527603616725,36.790483842382955],[73.295889040800205,36.888068108752826],[73.176165287074767,36.840304465485282],[72.971278116076647,36.640179711218536],[72.836526975454916,36.562520413235653],[72.517025751857986,36.500532437917492],[71.939107719549256,36.213748987804394],[71.605886208166737,36.200750578209231],[71.460573265265865,36.241176617260052],[71.333927611110255,36.323097275773918],[71.08071158017195,36.628547174916257],[70.939281624795086,37.049482995818046],[70.974618136962377,37.52245038998425],[70.854272977218386,37.654308695205486],[70.798437503320244,37.781185272258377],[70.749282960313124,37.72575686120048],[70.69978128570925,37.443394292668756],[70.520260638770822,37.208578170356191],[70.401637830858604,37.130362604285104],[70.172845524477111,37.062581423232473],[69.894501184918326,37.070856671514541],[69.798718715450875,36.888293293875762],[69.537232099285504,36.674966569014394],[69.394333020985783,36.625403130537237],[69.243151563962158,36.620837162425417],[69.097521848084,36.661686481462979],[68.898803608228135,36.774233879352089],[68.653206250297814,36.696315330462099],[68.41654433482509,36.538168128016075],[68.173823467018764,36.461417363261567],[67.969564719334272,36.45976852903393],[67.678118965837143,36.560311171593376],[67.404257172118179,36.795364319924651],[67.298551206930838,36.963487932462755],[67.266629573331983,37.159498925719333],[67.341887989702215,37.649969678800595],[67.408390224085878,37.77853234239668],[67.71816716510564,38.135666203827178],[67.626163850524478,38.280681737370713],[67.569926557937777,38.511034441103597],[67.425167427066498,38.573618371430506],[67.266756373106048,38.73435083750055],[67.155065626782331,38.781608706548923]]],"type":"Polygon"},"properties":{"alpha2":"TJ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-171.67917401811317,-9.4894475907496805],[-171.70244417734571,-9.2924797410248576],[-171.64730344543298,-9.1019611492285328],[-171.52242829023945,-8.9478701704162766],[-171.3461368090291,-8.8540548815906419],[-171.15660858337043,-8.8348951707907393],[-170.93465470875981,-8.9101531901836868],[-170.79006477062953,-9.0450678115194787],[-170.70535697303626,-9.2199707896550294],[-170.69055430177579,-9.4175201152942005],[-170.75125363352109,-9.6011897608790218],[-170.87790480001618,-9.7496722592625318],[-171.04729501816729,-9.8375491731402978],[-171.23727086960608,-9.8555651068678625],[-171.42016016937191,-9.8010958308572498],[-171.58074129509288,-9.6686019919935546],[-171.67917401811317,-9.4894475907496805]]],[[[-172.96912053853768,-8.7166503775799047],[-172.99806940024135,-8.5293328658421501],[-172.93378597826876,-8.3021342461781096],[-172.80864647659175,-8.1557940806694056],[-172.63134743388528,-8.0651916358637052],[-172.43274914737196,-8.0509622949310256],[-172.24434434315529,-8.1153624758117857],[-172.08759043839737,-8.256105305740558],[-172.00046216399312,-8.4304060893143227],[-171.98186135399524,-8.630124224765261],[-172.03857713669896,-8.8165715986282951],[-172.1622622874452,-8.9671746753332382],[-172.33678938259797,-9.0605810273360099],[-172.52647826154328,-9.0807681663927617],[-172.70992218307785,-9.0284449494538137],[-172.86041949657456,-8.9112274165967289],[-172.96912053853768,-8.7166503775799047]]]],"type":"MultiPolygon"},"properties":{"alpha2":"TK"},"type":"Feature"}, +{"geometry":{"coordinates":[[[123.63082647540519,-9.633818307063688],[123.54885678172394,-9.451655933511951],[123.53821238835947,-9.3017735833839179],[123.59361549517442,-9.1098552237216666],[123.68250161782372,-8.9887054024656088],[124.0158626808401,-8.7909024655637467],[124.36332336999398,-8.6972135599906526],[124.4898004702425,-8.6933273457330422],[124.76768937790293,-8.3493034136920343],[125.01795440654209,-8.176206907960907],[125.0866652611442,-8.0049864747311634],[125.23105603644699,-7.825040130571649],[125.46802919283304,-7.6729584291221595],[125.6614723077595,-7.6405666851852185],[125.85249171209048,-7.6850756837896279],[126.01169374959532,-7.799636475695225],[126.12158758875569,-7.9888430951563771],[126.51285523786096,-7.9664211944457159],[126.93837465156817,-7.8167326550732588],[127.32710958203855,-7.8860011228973477],[127.60326854973096,-8.0302859558722233],[127.70405659600847,-8.1359518793295784],[127.77001723455588,-8.2662312399933171],[127.79455650764588,-8.458832877825202],[127.74387835812865,-8.6462609759877225],[127.66070174026183,-8.7662825069426695],[127.39643739896492,-8.9955226656702987],[127.14692751915516,-9.1580835525385851],[126.85491028321566,-9.2511016713265093],[126.57919277338009,-9.4170791779204812],[126.34006742591519,-9.4739612862264533],[126.10202467094457,-9.5987155429020312],[125.63594816715414,-9.7276028737646261],[125.21904868723347,-9.9882637753175505],[125.07290817579593,-10.011510627120913],[124.8796650090532,-9.9745856821756966],[124.75239132674581,-9.8990947887255132],[124.64501830505546,-9.7767067542917943],[124.49719167487741,-9.8797835568947079],[124.35793968289831,-9.9211725861462163],[123.9664163042726,-9.9006299572787313],[123.78833124002979,-9.814758129265023],[123.63082647540519,-9.633818307063688]]],"type":"Polygon"},"properties":{"alpha2":"TL"},"type":"Feature"}, +{"geometry":{"coordinates":[[[52.039940224779599,41.571280505778596],[51.999502079206088,41.707372328533893],[51.998945295468289,41.849343867337097],[52.085301264420075,42.068115869177184],[52.221172685194531,42.199203119566199],[52.786891733455562,42.567858928806153],[52.944732890736248,42.635516819872088],[53.566755979785007,42.782714056983913],[54.076956560333258,42.832995964178586],[54.30403920700288,42.800208399338139],[55.117545305813891,42.389748236434784],[55.219643139959182,42.305739694533479],[55.634863841342188,41.795820370416351],[55.955142059670258,41.823504978729069],[56.46716427099382,41.802831779846549],[56.473342498051863,41.951045680523336],[56.544156242907235,42.127496156600387],[56.938355723612624,42.491211134883855],[57.222261615006509,42.629894663914385],[57.513216065599785,42.671156432309473],[57.836226886062903,43.015605580802351],[58.060097537166669,43.14629635446623],[58.300924342806042,43.186601290531051],[58.521324087828368,43.27351495675174],[58.712763147837158,43.26260846659774],[58.845887023381891,43.20717593716892],[59.088437683793714,43.031075710706574],[59.39077604382522,42.954440544195037],[59.561504473758397,42.815819698239665],[59.992932100747396,42.776450022776807],[60.298587151478998,42.600913314020808],[60.421396654217681,42.455506237398893],[60.485472159946362,42.239436946003259],[60.642322298035793,42.049960327444275],[60.686432579072466,41.912738260193613],[60.683751852848502,41.734993162378437],[60.884432025786055,41.748106873572141],[61.203577905810874,41.702885651770906],[61.423330877749365,41.770425063485533],[61.564779917913441,41.771252643601308],[62.189285389568511,41.503391514395354],[62.324910007754688,41.357939866377684],[62.532774980262062,40.933532904164544],[62.811694812575922,40.57545670358823],[62.908900395256609,40.305771467822005],[63.754180623428219,39.811159202660015],[64.036815528064238,39.58288377342307],[64.24254051210697,39.476006647951564],[64.405924792441581,39.467737168970316],[64.552995536814876,39.413971216392952],[64.890572974680282,39.180103703767607],[65.726454341647369,38.744078184160998],[66.100955937720883,38.726715646138125],[66.891976580706853,38.397310254557738],[67.07102701818188,38.165845497570054],[67.127289677611685,37.973748418923755],[67.103697245342545,37.774976761964865],[67.023030065385754,37.614155352282403],[67.000934579586968,37.205094024030629],[66.880803132104717,37.000478383971348],[66.767347233431195,36.913038344782628],[66.633755423423509,36.861354404375454],[66.443387677456869,36.854924773461477],[65.947390697159818,36.942471213531029],[65.806826875183219,36.819579517564414],[65.668610172610684,36.764545772999938],[65.196711755402148,36.740264502808273],[64.954428030346236,36.114002992096019],[64.794659079975233,35.928704511665451],[64.443270856000211,35.727869715731472],[64.249452776214483,35.57028430304365],[63.577182918967637,35.404439233493349],[63.473864539140372,35.170750556150985],[63.371412673390061,35.05766129625507],[62.917705122597795,34.811334448390568],[62.334802453914754,34.671825468830718],[62.101929913772743,34.715437254458834],[61.823852405210111,34.928106847677249],[61.431410036004074,34.969891129401617],[60.916469441053906,35.258548052148036],[60.807846941834214,35.410964870919635],[60.758688371051399,35.669672569113565],[60.670304746828869,35.84722503756074],[60.653465588564686,35.992974550120771],[60.67713614407073,36.128491810334083],[60.276603280915019,36.142088988143165],[60.084266104155077,36.209050543044171],[59.734926892052009,36.569444362292934],[59.152880708728489,36.8553325477329],[58.943829413790006,37.098525403527425],[58.771497813997989,37.166054937212813],[58.383832938672612,37.135809918706585],[58.108456028556795,37.189852234563041],[57.792340206200407,37.360883717376765],[57.148603606249154,37.51761396698646],[57.023566112176056,37.598158215429812],[56.922461380500955,37.721586078245522],[56.632813145029189,37.738334110453835],[56.480777275278598,37.62741924601837],[56.300364103706379,37.58076677500037],[55.579930969273974,37.584117643905884],[55.24995621030039,37.399549863533323],[55.045436870658563,37.111794773274589],[54.874078095593966,37.001445068222566],[54.279024258013266,36.840308749073529],[53.751298539984461,36.871123153972057],[53.546818879147501,37.004793339424928],[53.43007665855805,37.219388083318727],[53.324357674000623,37.905993520185135],[53.339864751083354,38.318758532511708],[53.127070650954835,38.257629502489493],[52.936402874070566,38.28447015707404],[52.735239532386039,38.415064042695526],[52.633118576093004,38.578299860353027],[52.519162411481865,39.072126916826498],[52.548452681356075,39.222348044047685],[52.62902074820672,39.36514833999523],[52.630134403596955,39.500130160490258],[52.351213308756499,39.845609803789891],[52.236791996101296,40.345296475300721],[52.243678545015591,40.496939035321489],[52.39173184376417,40.985380978122485],[52.179705882649067,41.274130074919633],[52.039940224779599,41.571280505778596]],[[53.361382956343029,41.324890566411192],[53.391643163509109,41.294563986368466],[53.637982589539966,41.317290338151516],[53.514630755004752,41.604847992852584],[53.360499272390562,41.596115808072263],[53.361382956343029,41.324890566411192]]],"type":"Polygon"},"properties":{"alpha2":"TM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[7.0377520039000343,33.642825483008203],[7.0006621009095333,33.815666509246611],[7.0148337237591534,34.107992827003095],[7.0838715842141182,34.33522432036154],[7.173866947831228,34.446831672521874],[7.3634694215673022,34.594583334996194],[7.5134066105063129,34.790078304984391],[7.7793742435091522,34.949593250328803],[7.8505374076020447,35.218966701218456],[7.8184963468759454,35.537481506829081],[7.7510988270636787,35.742582975230235],[7.8040707327418151,36.223441263753074],[7.7169845976544327,36.423722092338352],[7.7191961240692688,36.62478061782673],[7.8004314793196547,36.808710600640808],[8.1049971849080951,37.106395760911866],[8.2345907147934696,37.301558785111752],[8.4038647925688501,37.406155805892872],[8.6258735126487913,37.466144199416789],[8.9679913345041786,37.663170039152],[9.6615146530226887,37.839423204865604],[10.371627071703339,37.673884466225701],[10.540094672297542,37.568584085695427],[10.633155012579895,37.448354217141841],[10.799649817948559,37.535457414993992],[11.085818770999985,37.571218781884347],[11.270874273100169,37.522633636119707],[11.424133611881839,37.408101412170602],[11.504438501403925,37.288711969899424],[11.610089073378932,37.000683858666399],[11.614025794980231,36.763805417430582],[11.53753479574875,36.589683126912085],[11.347477906517881,36.410185383156666],[11.145075606561692,36.133515282195035],[11.342379396307987,36.001881821544139],[11.456793886783638,35.845940833512195],[11.523811509696603,35.536475435549434],[11.601415024890283,35.374724038849976],[11.61509778647217,35.17301542215435],[11.751703539174862,34.961626175192983],[11.776325331607124,34.715513277952326],[11.744834094718174,34.575397544911652],[11.643827711329942,34.41333105294116],[11.531908071109285,34.323341549224352],[11.3177892840163,34.221808747527923],[11.473922502047257,34.032133035728783],[11.559458711356836,33.701295972493732],[11.812036576802734,33.575904856710814],[11.964613932730291,33.377125312323642],[12.004346195852627,33.179842162840927],[11.96304397456661,32.764281973072784],[12.031049547004407,32.54119871133242],[12.028382831206514,32.388360554255314],[11.948818432473612,32.18390794636904],[11.81493543146034,32.0216255846248],[11.099480164149906,31.657294016415126],[10.985743639294833,31.576461689139091],[10.803361259684641,31.35861465585802],[10.674147929429195,31.277746054348668],[10.751648910004654,30.928982177771918],[10.724335213873454,30.690554050510961],[10.221952302227749,30.009369912401009],[10.088395057028789,29.926509099725138],[9.5665286144334907,29.731975358344783],[9.3204965614170572,29.770749863054899],[9.1564854566990608,29.88523564150978],[9.0352663975092753,30.102815909494126],[8.6006923162402593,31.75922056195876],[7.9795800947720075,32.190678050980878],[7.8731876492095854,32.348798045982839],[7.7706490327433935,32.667247952846274],[7.4407083674040972,32.861924503914743],[7.3395091913707988,32.958312837502625],[7.0377520039000343,33.642825483008203]]],"type":"Polygon"},"properties":{"alpha2":"TN"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-174.49253560789094,-18.90439489527726],[-174.56682800008852,-18.673871519338828],[-174.54189528040246,-18.481018007028126],[-174.43291163293483,-18.298040733121677],[-174.23317661285893,-18.128140124379112],[-174.01976250964103,-18.069089004694842],[-173.81528663981985,-18.090395837360646],[-173.59949236415727,-18.207843603989531],[-173.47436233204158,-18.36825615913833],[-173.42349560424128,-18.565239124872761],[-173.45690342843983,-18.784381421968796],[-173.61359540006973,-19.024788566196552],[-173.76261623498579,-19.142108825481913],[-173.9446105379451,-19.195487905356021],[-174.15942208351512,-19.173549913160787],[-174.3460840792473,-19.069057672290771],[-174.49253560789094,-18.90439489527726]]],[[[-175.8139316815091,-21.319348523788335],[-175.85553568077367,-21.182380259485143],[-175.85662517091004,-21.039236935532266],[-175.76967899311978,-20.818631287035608],[-175.54107898498239,-20.621901096228388],[-175.39506895172897,-20.575310106174467],[-175.24180571560953,-20.575135123140278],[-174.8248641994422,-20.699259543867562],[-174.52410956209715,-20.988082636812145],[-174.42431098029851,-21.200492073409301],[-174.4280629300726,-21.540734253405809],[-174.50215490846912,-21.723765742950395],[-174.64132852325213,-21.863840195923963],[-174.82387817538796,-21.939111518037393],[-175.02133331347807,-21.937840349505581],[-175.20289871774455,-21.860224942522375],[-175.35970479626511,-21.723094028999345],[-175.62317656717607,-21.565271670078367],[-175.73486037492012,-21.456646812676002],[-175.8139316815091,-21.319348523788335]]]],"type":"MultiPolygon"},"properties":{"alpha2":"TO"},"type":"Feature"}, +{"geometry":{"coordinates":[[[25.358041364707621,39.749150804195125],[25.262978505635708,39.860027148441979],[25.204010759317359,39.993643449029165],[25.198025245706756,40.235436766673182],[25.276315874472566,40.413145623740839],[25.492307184026568,40.669060995864477],[25.610690363629832,40.982862721533415],[25.814236870048841,41.2583713325049],[25.855742826920917,41.434913673214659],[25.831107218039332,41.82572616265098],[25.91366469069715,42.051678942869394],[26.050533215387315,42.187944014965659],[26.423088606476455,42.425507728199946],[27.196094045502843,42.589992567365137],[27.352071109082811,42.580586738921333],[27.67863504701684,42.474618331303063],[28.074165005120687,42.464522720659687],[28.221289426408816,42.423064012043056],[28.417380402976246,42.263228336105271],[28.594507226015885,41.941292655068771],[29.071197603791671,41.741857448061914],[29.920507503337035,41.658712211461307],[30.398094842608309,41.693337837804407],[30.852349409101979,41.600705991885441],[31.039976539860021,41.637509363086671],[31.241790450509107,41.769871605643615],[32.149070613481655,42.203780063348653],[33.354015182812198,42.515828201670018],[34.541851945600818,42.461531196890611],[34.962694122104665,42.554223996940088],[35.14749835272962,42.543593942505765],[35.41747950542144,42.451824682503762],[35.697794375535679,42.227759283383456],[36.262671012765139,42.134738682037643],[36.594911217090818,41.882910743688264],[36.932652815001227,41.837923435897139],[37.391701396995629,41.633932892849266],[38.38829651498569,41.438065787652867],[39.432172760169031,41.605524337285345],[39.935319002438668,41.479757153265297],[40.180502132056894,41.475192504601893],[40.844732802120959,41.706029872023983],[41.388733467571967,42.001359928059841],[42.230878520124044,41.989507611988714],[42.67916393115371,42.078138743469204],[42.818626916220985,42.073482705141593],[42.992309631851036,42.008101288901244],[43.77331794780757,41.520802429045013],[44.063715603899126,41.179121433226612],[44.228830269844494,40.726212595391488],[44.313910209652278,40.595073603530452],[44.847753399471344,40.295932334069924],[45.186699580280845,39.985588630475824],[45.266736644581755,39.865788239961773],[45.314437558487455,39.680240369427921],[45.27121916019312,39.445545366034956],[45.124143930744516,39.257613326586139],[44.844133226840988,39.07962896776057],[44.774249797102293,38.874918469673759],[44.797294196538353,38.734921379862108],[44.940085801890334,38.427115101295335],[44.973472330168271,38.046607813032949],[45.264873225408117,37.443180943171164],[45.290681037842234,37.198036588193759],[45.217708993509191,36.932383961208032],[45.069286530791686,36.747066016837799],[44.944328865040077,36.676848753143673],[44.665363809973194,36.617825959969878],[44.472906783491077,36.517076415411402],[44.325704261467479,36.480974117550524],[44.078244429851644,36.522237683432387],[43.742527783364828,36.711605018750504],[43.065489993771607,36.831677643207378],[42.900400138066757,36.806949149902522],[42.418901741779429,36.613229010753194],[42.019671082684553,36.665244171912363],[41.387120749485419,36.573500806373112],[40.779734926717055,36.586070088244036],[40.132991513205717,36.339976440386117],[39.43455872410668,36.188051617929865],[38.713942997886463,36.196817692254015],[38.29329017489956,36.345204567224066],[38.164525981159414,36.347805468418841],[37.543079971665648,36.155935722396116],[37.225922545886441,36.116355600390754],[36.431365946293987,35.426505577403582],[36.305082943777251,35.360899179163155],[36.165432204351625,35.33352643372902],[35.978031621542002,35.359864300165746],[35.639702440748337,35.486547194132861],[35.462707117242466,35.66355588813569],[35.343255557664428,35.980853524810101],[35.265334333518204,36.067586091026037],[34.772786093221498,36.231312409293132],[34.642005700313938,36.20612934465715],[34.259077117966498,35.898195123265928],[33.864619260777559,35.712722746306106],[32.826108288078437,35.537799768940708],[32.679918493879384,35.550178316666944],[32.186534206405589,35.719721738544045],[31.697617733714864,36.114614999285379],[31.220562343896081,36.281696144125185],[31.0794628420457,36.245851014659706],[30.73661755975786,35.877052502479508],[30.560991860263488,35.77623181322091],[30.149037436321112,35.748351129801705],[29.75255770078034,35.661755053357687],[29.613167226156097,35.663526581779237],[29.024385883263665,35.86480608622044],[28.662812292114754,36.170740425051562],[28.538252240061507,36.222504354508381],[28.412332200067766,36.22461102205223],[28.001879525684135,36.13573843783719],[27.33698266907043,36.226904198524792],[27.116265054067707,36.344708829242514],[26.80772738545015,36.772217099246014],[26.729786090534766,37.237802827199403],[26.56353329172342,37.548068591942155],[26.455482317174827,37.649769252353828],[25.997826289827991,37.873100675182286],[25.84376431728592,38.055727161203635],[25.792034640788824,38.288990028693441],[25.90050878922613,38.826565333573683],[25.885131501349036,38.926807052920594],[25.663871480306863,39.250097344084779],[25.524789279683155,39.604235355710991],[25.358041364707621,39.749150804195125]]],"type":"Polygon"},"properties":{"alpha2":"TR"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-62.112017508320918,10.525366053301264],[-62.14823370231413,10.667436890936772],[-62.140791735199478,10.817509303335678],[-62.089099004346885,10.958594289832345],[-61.997823800018985,11.077950684351711],[-61.766386890147039,11.216156350590644],[-61.295059237783875,11.29982606568398],[-61.170931412242957,11.547996556014258],[-60.909595879362818,11.734905857807084],[-60.629999950383429,11.818812096316639],[-60.40525498657081,11.81040359281217],[-60.192221493187034,11.697608634510605],[-60.075771878384472,11.543033076068642],[-60.026127568444245,11.307148751849956],[-60.073342676348823,11.102739391739368],[-60.199619145764295,10.903941921123264],[-60.42279933870072,10.774456903363451],[-60.517582790929097,10.537473013030262],[-60.468755256885451,10.32120570651273],[-60.540293073029325,9.9696063570285212],[-60.642916642373301,9.797550266509818],[-60.804843441569702,9.6795900741927223],[-61.114539735840786,9.5820803576949647],[-61.581008516117109,9.5650303279599225],[-61.999678829037194,9.5785045716257073],[-62.181555405652837,9.6528927102731323],[-62.346122930847251,9.8340209893050101],[-62.395683560506072,9.9732614242846065],[-62.402372165064989,10.120907681651804],[-62.318386499260384,10.350769230748259],[-62.112017508320918,10.525366053301264]]],"type":"Polygon"},"properties":{"alpha2":"TT"},"type":"Feature"}, +{"geometry":{"coordinates":[[[175.5825282796832,-5.879829659748502],[175.54105032788723,-5.6490309842124828],[175.60935866972235,-5.4247043929440721],[175.77147402604939,-5.2328661114973967],[175.99136191340995,-5.1370477212494592],[176.23057246928178,-5.1546589925570236],[177.52895030860236,-5.5787538350926669],[177.73997840565036,-5.7136838403598889],[179.09759525862231,-7.1776179269476845],[180.0,-8.5842287204379613],[180.0,-10.831876258370997],[179.95764640383652,-10.963681142008955],[179.89344999283801,-11.094257542452675],[179.79437748655366,-11.200822142749699],[179.62286993251209,-11.290350123225789],[179.4302142659852,-11.308067192298596],[179.24525497072162,-11.251320743272682],[179.05388608931202,-11.106744576880249],[178.94978267722402,-10.891174048323979],[178.83090036080276,-9.8499684143018591],[178.7804119016254,-9.7262102255346825],[176.7755888854617,-7.5954191393558821],[175.9427941504012,-6.6419770782222001],[175.5825282796832,-5.879829659748502]]],"type":"Polygon"},"properties":{"alpha2":"TV"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[119.60924695243249,22.962140390893094],[119.57835804050812,23.224672771535982],[119.70630881883309,23.921397322467062],[120.20004921145865,24.734340602982908],[120.70065561182895,25.3990887307964],[121.32196644668002,25.736974390778389],[121.52720551293855,25.776538936161867],[121.70113611588033,25.763391659396724],[121.83848591201146,25.711004160765761],[122.25972580118982,25.408667935946834],[122.36274787067975,25.253271784157747],[122.4286842439311,24.979494570626734],[122.40905900988821,24.83497786710889],[122.32399194661878,24.66427473518554],[122.3133741386455,24.415094056861935],[122.0933282236234,23.905598501385047],[121.8686345291665,23.005759610056266],[121.71916813254687,22.701044675370508],[121.41389253494583,22.288350841583505],[121.32022372422178,21.78717465607787],[121.19517512032185,21.573690980328852],[121.03018772837493,21.463006397178827],[120.83512120300861,21.425310679516777],[120.59080116314355,21.479892558715342],[120.42543087287429,21.569922038602282],[120.25313671439896,21.79066887214729],[120.16123869802468,22.039425520461677],[119.89437640123442,22.279625871681802],[119.60924695243249,22.962140390893094]]],[[[117.79371026727456,24.440547298115639],[117.81752037645637,24.633442427395305],[117.94670689449535,24.838165869007597],[118.11057211044512,24.94268210315715],[118.41581279198826,25.021721829891696],[118.60480961654491,24.981158787497574],[118.76429668329344,24.871938662791937],[118.91504352525622,24.641460205415751],[118.95079740685873,24.445737931633012],[118.90739541903336,24.251568376972298],[118.80294428670264,24.084655366906492],[118.64302490923212,23.963867618447818],[118.44851973348187,23.915580725043494],[118.16883317845665,23.952813430406007],[117.99753516146464,24.034902719888386],[117.84620238156312,24.216809328590717],[117.79371026727456,24.440547298115639]]]],"type":"MultiPolygon"},"properties":{"alpha2":"TW"},"type":"Feature"}, +{"geometry":{"coordinates":[[[28.85219650414567,-5.0654042768155989],[28.831340939051824,-4.8113185005847452],[28.922226480425511,-4.313896340420758],[28.982909324427389,-4.1791385380221255],[29.08033576647421,-4.0680072586559524],[29.205993824781917,-3.9902118523275432],[29.611635901396941,-3.9219134796364972],[29.920348646600637,-3.5126278076988213],[29.994574297208608,-3.3348624130272997],[30.080461284883313,-3.2231619080209231],[29.933049008719411,-2.9185947393082889],[29.933183397389325,-2.5488024671386404],[30.114007496074137,-2.1549260850228995],[30.318754786097259,-1.9756947535915383],[30.311941403093904,-1.7459342838705274],[30.080102019862913,-1.4654571673455026],[29.983596848289288,-1.162475970877423],[30.004405662603631,-0.92067818709086824],[30.136618989764738,-0.71716206704567664],[30.302389977429176,-0.61474543092952982],[30.766972180792987,-0.49692244142234304],[33.9851102046824,-0.50429128659602407],[34.18186612888605,-0.54524904057614909],[37.92710725147726,-2.6336963076301059],[38.030484596307048,-2.7287796889210494],[38.119766425122016,-2.8928523251092937],[38.1851114048319,-3.1944971672157285],[38.181223303753889,-3.3302564063662108],[39.513089755880351,-4.2863257768281828],[39.645429495145585,-4.427450653563378],[39.860971934895737,-4.4065091247649777],[40.010091101684942,-4.4280546805751264],[40.146021717135795,-4.4930439463213254],[40.28572861770121,-4.6365699565640135],[40.363755165930819,-4.8734623815022937],[40.348927853265202,-5.3169403244585673],[40.189352809082678,-5.7026275046078219],[40.040409397140905,-5.8500742265896983],[39.922213369620628,-5.9122918224342005],[40.053186327602013,-6.2487474364801194],[40.072779496705074,-6.3919119193855023],[40.034167398273141,-6.5801321052962551],[39.949785948888398,-6.7151082360441121],[40.038818025747226,-6.9407371011226422],[40.02509480582998,-7.1642437930874765],[40.195399328052154,-7.2413859793135638],[40.3295107904056,-7.3825854623755092],[40.399509219387539,-7.5643088738876259],[40.403125319284463,-7.7107287667016742],[40.282101033720146,-8.1013851719026526],[40.20696152930654,-8.2222331563810389],[39.983210348117517,-8.3951138394111187],[39.828415013151648,-8.4629576325267735],[40.11119226904561,-9.0223941613853906],[40.135684175831067,-9.3490158852894787],[40.241627619370121,-9.681711478827328],[40.710626519548448,-9.9712162524254886],[40.90384663295589,-10.227876066241178],[40.961385384808082,-10.420953467515178],[40.919735421995448,-10.668334424215333],[40.802146461412875,-10.83192668319032],[40.247295076130953,-11.248461657895232],[39.696514255358139,-11.469903425740911],[39.517900953872044,-11.582188227305013],[38.997441414390373,-11.69580398918356],[38.703195493173162,-11.866123549327932],[38.567607261526682,-11.907231735373381],[38.379323174812882,-11.900199010908455],[38.177414951376512,-11.820687410467839],[37.945228512865079,-12.029212929925857],[37.50571779011338,-12.192742994310294],[37.31572669209266,-12.20572635460473],[36.967345860840346,-12.101838501059509],[36.741068323157961,-12.180022567224924],[36.28946996517233,-12.205904566362246],[35.997461823067006,-12.131311079926794],[35.8390556543158,-12.025132829730785],[35.592703055494553,-12.101358633460727],[34.939605723523051,-12.077482824431293],[34.755829627625396,-12.034449321325152],[34.569680501170708,-11.890792841644361],[34.492211372919115,-11.753922554348147],[34.351848116109728,-11.609443817151101],[34.140103178125763,-11.25606686222315],[34.108932433577436,-11.05340975524873],[34.148519836519007,-10.7865515878658],[34.099231754224206,-10.648162656401755],[34.040916046317136,-10.235862980972968],[33.989997502317131,-10.169062706090045],[33.841180479503294,-10.162328480775603],[33.631439610875589,-10.101756177538682],[33.288553764752308,-10.089881259261631],[32.247638729774387,-9.6325918178532444],[31.748666297594301,-9.514589800072395],[31.554730203778796,-9.3695799791373826],[31.334281519958974,-9.278751805905328],[31.171263231316232,-9.1094525993880477],[30.980868472757429,-9.1019447394291522],[30.847403617831574,-9.057349231023931],[30.518821991946474,-8.8062347470355178],[29.813074473287347,-7.3596434379137277],[29.323578398369325,-6.9342452282991029],[29.082111741547418,-6.5121353351905347],[28.98029033885291,-6.0212433728371106],[29.069667201304505,-5.6808053993941972],[28.85219650414567,-5.0654042768155989]]],"type":"Polygon"},"properties":{"alpha2":"TZ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[21.874091578744064,47.977230282401251],[21.75938657749257,48.072209938488612],[21.659226612849793,48.242996301383812],[21.632327506852846,48.389470908326935],[21.650215001833399,48.65255478009977],[21.723643330212031,48.840555402585508],[21.924234836280867,49.036135863747688],[22.167926039500728,49.406846213926663],[22.154285928245354,49.606671656818868],[22.213516149461757,49.783410632499212],[22.388909097408437,49.991745552302355],[23.090120435514283,50.55981616517785],[23.464893400747098,50.811349543139784],[23.286702420137569,50.98282531002117],[23.185508035585475,51.167273874562227],[23.108861762325525,51.615223066860729],[23.131184918197782,51.758163671123398],[23.193497844124174,51.888729512359014],[23.290583617291524,51.995989440960386],[23.414314084875322,52.070963509988069],[23.55433036403663,52.107374788527771],[23.856265960094259,52.113472629379004],[24.100792120077749,52.293588347579828],[24.227009438969326,52.348683603590452],[25.026393300342455,52.428682036719778],[25.986189289266392,52.409546244483444],[27.202739532979319,52.2483569245029],[27.384364567132437,52.187249432863155],[27.510733198175682,52.090809906659537],[27.755424116966509,52.063969936098886],[28.229542389755515,52.10551874861023],[28.701778928746648,52.01677700406735],[28.981990154214731,52.112727935840574],[29.170774655930419,52.122602024067426],[29.349731872907576,52.061680761784515],[29.515216929665751,51.919177575160163],[30.047159436864359,51.9815134902111],[30.200505122634581,51.963361189683091],[30.427784590894149,52.272791379540799],[30.953459057313051,52.560611092632676],[31.588393644719837,52.607865429607415],[31.960750335769173,52.567432303673193],[32.144719801318175,52.721673476182843],[32.405499771585838,52.806146855765363],[32.794889462005422,52.762481121135238],[33.108720422894599,52.838902820625584],[33.825627017854906,52.836236362203394],[34.225775815408639,52.648200446793624],[34.479139154578611,52.336731702349077],[34.761262387467603,52.123504040977274],[34.879142020000799,51.915092550982401],[34.888787353086741,51.689102500442488],[35.111198241351254,51.700938478946895],[35.247022014987415,51.66846388526784],[35.637044020786611,51.423405606276717],[35.755175711285652,51.274771601535186],[35.894737804435501,50.936450580760464],[36.22500573177912,50.895974529208523],[36.467103153138837,50.778536078679331],[36.622183866622322,50.749365646367515],[37.316856627669083,50.899919859297952],[37.506102115509947,50.904269768716425],[37.722790770223597,50.811242323262107],[37.925649492193784,50.65503294635738],[38.042287899591201,50.502575769466667],[38.219808263556104,50.550641288102902],[38.364173219558623,50.540861508666588],[38.580709376425602,50.457830166196544],[38.811271787007144,50.425148726607119],[39.007749869113745,50.338801017946444],[39.31824890934584,50.334675070045463],[39.511019822247462,50.226082524519107],[39.894157070552502,50.076573041764476],[40.223793929433469,50.055687764734422],[40.394482997385133,49.965770434520088],[40.492979345632044,49.859232755881337],[40.556740170051249,49.728901006652322],[40.622754175220706,49.423702660070539],[40.591105674354395,49.1207597334596],[40.491266705332016,48.929924114721274],[40.484627281070608,48.686695100386238],[40.395911280429488,48.512880918442406],[40.456552727083597,48.302792478421736],[40.444172019267199,48.110310616107881],[40.169473724174658,47.596096324669062],[40.007692397378939,47.425386825736169],[39.785762602618441,47.347527356669978],[39.034892899319289,47.341496034238091],[38.774214659980473,47.183114054416365],[38.634429866412816,46.820817738143944],[38.538527430555909,46.711151603814663],[38.415083469958468,46.633785417311564],[38.274577852594888,46.595287239285909],[37.738248470603487,46.580168097806606],[37.515972395663461,46.449192207722945],[37.225056317594976,46.395676928046932],[37.021570008869119,46.269084865299291],[36.886818888051359,46.22323961608771],[36.598802542364659,46.243823281397354],[36.35705799883263,46.164685910114997],[35.985121044142254,46.135049415126929],[35.738131711436672,45.996731152065976],[35.605137997150223,45.846439015704277],[36.203334241402743,45.952410825212766],[36.695864731822894,45.878490697192703],[36.829147192350391,45.823781241258544],[36.941296517825307,45.73333862050707],[37.057084175012264,45.524674236401815],[37.062971368722977,45.286109889314737],[36.830675100473179,44.823472333887942],[36.70948707799127,44.678324223070142],[36.497082727076773,44.576481936284075],[35.927130010149881,44.508843985099183],[35.784309460952848,44.513014018357737],[35.622683523267455,44.570176130271101],[35.285240553691139,44.343596983691867],[35.131266401231592,44.304783828296358],[34.760276171984714,44.290719953880924],[34.513697003990913,44.095397145591299],[33.962307178765258,43.890581377960885],[33.530986598059414,43.948667450623695],[33.199155782009335,44.121818028318302],[33.085302669699352,44.21276896744223],[32.983117204309096,44.377387892442869],[32.951186916969498,44.568494450561104],[32.98663061361448,44.737639742098722],[32.796350964850873,44.838278236704944],[32.591290609532876,44.828692052197901],[32.446402413441319,44.856279574361444],[32.175121484483839,45.030966990366913],[32.082046773552364,45.142452193116746],[32.01482218581458,45.323480213796437],[32.021210810224638,45.516481444222229],[32.088717051546169,45.674559583650229],[31.599215521966745,45.724532594500204],[31.366238256624875,45.798788178219702],[31.209006789942411,45.90593071224221],[31.104939805007646,46.060387809885512],[30.874237442688433,45.761380875799013],[30.540269918702514,45.483527468936209],[30.201635915323546,45.329886867229675],[30.195102547735882,45.157802387969319],[30.145074517066867,45.021659281552758],[30.058075606214114,44.905603524315964],[29.897594786852444,44.798640570139511],[29.756997812772294,44.76300014145415],[29.612087000129755,44.769213486499332],[29.475056738300648,44.816757746094126],[29.345460056436675,44.908182568656926],[28.882853158982719,44.749564091456733],[28.73522145345305,44.735072955822858],[28.194313039899235,44.862806230705161],[28.049785327426246,44.925219748599595],[27.829521707146629,45.129400954156218],[27.753438601266179,45.253122276610284],[27.714484776719104,45.490496260770854],[27.766856949571597,45.676383436833603],[27.885769387265402,45.828556755373981],[28.072444417274955,45.935404058454253],[28.281215798870775,46.136754525875823],[28.436308697694916,46.334223289129874],[28.459076477955396,46.598419010518896],[28.587489177705041,46.793351648661101],[28.745496976060281,46.910571821007352],[28.976263318662799,46.995119195446676],[28.722230796434474,47.207656626822043],[28.646417842053673,47.38369117331046],[28.63810303701063,47.540726529009063],[28.563841510666599,47.601674671049075],[28.432050839721253,47.59178384095172],[28.285118679119236,47.623563601818603],[27.651316779978167,47.938320809440228],[27.569939454693273,47.952354189885007],[27.278661524114533,47.874209506434156],[26.947062314807599,47.870380611273362],[26.619777955874323,47.748854224066122],[26.451471284809866,47.584645249600527],[26.31858721991863,47.517706761171155],[25.565984838748722,47.418415228009586],[25.195578870141674,47.260965763756786],[24.874595993171354,47.218405503493692],[24.689768994339516,47.261413621864257],[24.389803355653434,47.428564622677108],[24.142842105910795,47.407485346539012],[23.202457738568125,47.533479711415957],[22.947670378344309,47.452609922350632],[22.745974760534903,47.464929806180123],[21.874091578744064,47.977230282401251]]],"type":"Polygon"},"properties":{"alpha2":"UA"},"type":"Feature"}, +{"geometry":{"coordinates":[[[29.06252667233845,-0.99567725914457694],[29.234281340341592,0.22530308936615531],[29.427805280757305,0.62158616386364518],[29.449786127740325,0.900904420322995],[29.491858542287076,1.0345479020786137],[29.602085853196556,1.1847818733905537],[29.808350659089481,1.3253026726554877],[29.983281831830574,1.5536311900392297],[30.226651357462725,1.6904469726723372],[30.541813631416549,1.9920812622609403],[30.413503529188649,2.0675000442998783],[30.316546572463775,2.1726663431077462],[30.253333923221255,2.3009814351197044],[30.229039056856337,2.4419437042934304],[30.308436045236849,2.8201048998272857],[30.259797733648139,2.9775175273585868],[30.259792628373688,3.1156414879465273],[30.346635650400781,3.401629144934073],[30.355370132244332,3.6182651629916229],[30.570456035916756,3.9817580027710595],[30.871354633070663,4.1988540600249662],[31.049315735824539,4.2745823450124716],[31.242702772326641,4.2771084791062286],[31.48442619390147,4.2103528183763128],[31.689464251666092,4.2904107980006625],[31.836751636470538,4.3008372085452979],[31.980661911903681,4.2677900243364286],[32.140657304755663,4.166090146961154],[32.660619912259236,4.2728622808108518],[32.967865274139264,4.3790466689661542],[33.159496478652414,4.3528549318470997],[33.300735309578101,4.2819362516126169],[33.678241291058299,4.6215096496904398],[33.90598051809129,4.7149175171180424],[34.150692170448664,4.6883394730311814],[34.318590070289616,4.5840422927304436],[34.59704294419727,4.1395875734507515],[34.810227010704722,3.9837577666430493],[34.907981382662022,3.819724468847566],[34.936679224410852,3.6790201374108493],[34.914879894528021,3.390342472869297],[34.983632396347275,3.266682210655889],[35.16640862605054,3.082836457470953],[35.386597977560669,2.6158731670891524],[35.478157123395363,1.7717082197755778],[35.443556892247578,1.4984054095843844],[35.298720628731907,1.2411269685803967],[35.230303494974919,0.9927102424408023],[35.102902783851171,0.84804550632153941],[34.888525667987736,0.71152884697948615],[34.81453883017182,0.57279715979154444],[34.566942211352185,0.29501262025982739],[34.44572109168741,0.046066543505733207],[34.398364773869524,-1.0696163255838627],[34.358922141223303,-1.2070460807270638],[34.282197078050096,-1.3276940129507517],[34.133123920816914,-1.4457020885268772],[33.950773576951086,-1.4995264714835204],[30.834524715201432,-1.5020638421490575],[30.617381852202602,-1.5493440225403325],[30.395183483047333,-1.7731559057782478],[30.141284250166994,-1.922875074256494],[29.905661473792751,-1.9690914457488162],[29.647697419595396,-1.8836099834475519],[29.418166294928465,-1.8616797270170329],[29.208893398829243,-1.7257935960782045],[29.091346897111404,-1.5056958778774523],[29.06252667233845,-0.99567725914457694]]],"type":"Polygon"},"properties":{"alpha2":"UG"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[176.94087802928863,51.51031807457948],[176.84091259783077,51.616207354663743],[176.76273310111148,51.793346517452399],[176.75771425514657,51.986905624409232],[176.80284024983689,52.125358763508814],[176.9590849158848,52.309068670725154],[177.16619869129249,52.413850358197536],[177.32377053617137,52.548818134066146],[177.503851991712,52.606645912827361],[177.7527134238689,52.595892506333797],[177.89155516805627,52.550800835671104],[178.01145542355817,52.467528887076625],[178.11993805976718,52.318706543862348],[178.37244051358726,52.475311471269727],[178.51626553653259,52.494394497814667],[178.65951433435112,52.47138186974054],[178.89087939825473,52.364556215339043],[179.04891199531693,52.185905665109246],[179.15539160449015,52.337759577215657],[179.27045036427154,52.421479850829805],[179.53038295424639,52.520711224736175],[179.72543106034018,52.520409360941265],[180.0,52.396423305351014],[180.0,51.517830673135833],[179.93979977595998,51.475483950904341],[179.94004756952299,51.269007277937256],[179.86426613970758,51.091458849273863],[179.72687273420991,50.955851615869946],[179.5483479118063,50.882399880618408],[179.13609231063455,50.89312637262438],[178.49106765199642,51.157169942371993],[178.25312362392626,51.337584008756323],[178.15244944286655,51.583682309647557],[178.0399678679606,51.722446526320809],[177.86185489514747,51.525577900398829],[177.4231910350243,51.350389071067404],[177.18724629896818,51.361723649996762],[176.94087802928863,51.51031807457948]]],[[[172.30105896815428,52.477265662746724],[172.17461991194003,52.554452739442858],[172.05142578570593,52.708116508020026],[171.99713155680152,52.897434978380389],[172.02016137586102,53.093033991532309],[172.08647447336426,53.225500035652175],[172.22926702308877,53.361145287154365],[172.54683127028042,53.489773893044394],[172.6920111981097,53.50708591082725],[173.20979032069289,53.483653155579574],[173.71835673594958,53.264365134105368],[173.85402972510437,53.125616566397589],[173.9199985218292,52.973216357318854],[174.08449338002288,52.888158467205436],[174.18556491201392,52.781269971416734],[174.25115589539865,52.64959450669528],[174.275588615896,52.504530219980431],[174.24102664803354,52.312019419528021],[174.11728079243088,52.052870064614531],[173.93213147954486,51.905831017597194],[173.747843970692,51.860434934032597],[173.18935457890021,51.952819075932979],[173.02986054757159,52.07190373667401],[172.92690801880147,52.252720449879689],[172.78735923766914,52.274683238169132],[172.30105896815428,52.477265662746724]]],[[[-125.20635496719783,48.322805777139962],[-125.19042820811337,48.517437626902101],[-125.10116337641799,48.691124150326225],[-124.99390677494522,48.791466135079688],[-124.81466538927548,48.868975774867003],[-124.6194051199425,48.87191615184534],[-123.89296821903464,48.665724480453392],[-123.65911015471204,48.653952725022421],[-123.56419714471264,48.90322537756866],[-123.45782107799555,49.009283041259259],[-123.27020226054955,49.106889368809782],[-123.14128781667397,49.342771529864329],[-122.977782612562,49.453928370863274],[-122.83364746883007,49.490544144420184],[-95.638948316723528,49.492935248470019],[-95.541069726195161,49.687226157237959],[-95.391186001040012,49.810162097290537],[-95.205808551719613,49.866850523855923],[-94.892771259882693,49.847052432940615],[-94.621242493923148,49.746682947049678],[-94.464747400565912,49.617552818191911],[-94.388460575939334,49.485350312423101],[-94.324715371545054,49.206202971102321],[-93.92043672074503,49.140649906525866],[-93.64871387333703,49.03925364720677],[-93.413665285373213,49.115392866938627],[-92.873827268569613,49.096417819707945],[-92.332418679879481,48.905957345111553],[-92.18017818535894,48.818963146083462],[-91.82657817866847,48.768597341962419],[-91.421821455596444,48.57143968312527],[-91.024536875348005,48.696881188692871],[-90.887035560638751,48.707946230082044],[-90.595950236366576,48.61244578473783],[-90.046816811745515,48.615866332269952],[-89.900162802167785,48.579691544659077],[-89.768834274430574,48.502984712696332],[-89.5206255112669,48.498269807338382],[-88.469010001331043,48.794496919465054],[-88.236601043618379,48.782344605245015],[-84.690224129437624,47.363837192016092],[-84.486569136032415,47.213075161983831],[-84.38619477586488,47.019078795002173],[-84.093103158831866,47.037285597411234],[-83.903260998457185,46.97760729694258],[-83.72284240734605,46.80772190079221],[-83.650707818360118,46.614827524810678],[-83.422233481872283,46.577557372002339],[-83.197602304229505,46.436007988836167],[-83.058495318899631,46.292072512355674],[-82.985860911441947,46.095165966236308],[-82.2986203110936,45.778670512548203],[-82.180815046634009,45.682998836092438],[-82.078268262767139,45.509268425575236],[-81.638242942601124,43.58191900410533],[-81.67588900091954,43.380304679536259],[-81.938932551300141,42.886768727279318],[-82.112117403377567,42.376241688525305],[-81.443114783302306,42.680770927486854],[-80.371062927383008,42.853842589284532],[-79.561577294055297,43.143607850760475],[-79.643287724993527,43.300821497037369],[-79.671019455427029,43.444271948365596],[-79.656098971780182,43.589614509327113],[-79.599800309136441,43.724438622317457],[-79.428506880394806,43.895328217046703],[-79.013247528513531,44.054363654728284],[-78.720580440485136,44.125048237584657],[-77.062376921649928,44.141971804125795],[-76.805464910439483,44.42306048975216],[-76.424281115668052,44.722521870790459],[-76.127428879094396,44.857170290342836],[-75.655608982490719,45.202644905661991],[-75.158049205678424,45.442493493123472],[-74.859586423331208,45.503665557691257],[-71.789131033857785,45.521888835443399],[-71.630106645285835,45.687634725829454],[-71.500551604653126,45.758811103669679],[-71.150891718227626,45.790913993582862],[-71.023729243279377,45.934412091831511],[-70.78694105865479,46.117439729457999],[-70.707572208604034,46.448024835423873],[-70.524526967078032,46.69781035628192],[-70.463629004366865,46.91364839518603],[-70.389960213992751,47.030893872941043],[-69.557885742122892,47.850951566951778],[-69.34182785668412,47.952825119697096],[-69.197826343651215,47.960705956844023],[-68.911367417030732,47.906701354040045],[-68.685985366105982,47.767965929219436],[-68.291075029992257,47.843837552018854],[-68.107197880511208,47.828501957632454],[-67.546878138713538,47.509693541580575],[-67.433215362776721,47.414772194222209],[-67.35234065088811,47.290721519919636],[-67.311348102707996,47.148422890932245],[-67.27375898495491,46.07630056685781],[-67.046348762657189,45.920104680503911],[-66.970116620833878,45.792317840591259],[-66.933304608434824,45.630424274316951],[-66.786003928104634,45.536706692109767],[-66.695148156504644,45.424502753063926],[-66.606139850256454,45.155606319776723],[-66.512361149814481,44.984238605852447],[-66.488340236087453,44.794780693562011],[-66.537102927023625,44.610136428017704],[-66.617538508361903,44.491156573988839],[-66.979190596828104,44.223048476133471],[-67.123888949524414,44.18038634984886],[-67.273090255485059,44.180163047860383],[-67.582661001271944,44.074209820339924],[-68.138899612741483,43.782089923616063],[-68.389889202264584,43.755725334012332],[-68.594822240836052,43.680986619035856],[-68.778260690419856,43.690778991092245],[-69.028868719745873,43.52724318788087],[-69.47606481765196,43.398915546413313],[-69.652285671170191,43.297655502019474],[-69.890913091771026,43.270022322049527],[-70.136802825164182,43.021051814549445],[-70.1995391546034,42.935780351595696],[-70.123447933852191,42.777351413024469],[-70.109011287333416,42.591168741102869],[-69.899141737669979,42.531934240242244],[-69.775921825095736,42.45099109857059],[-69.580319606781543,42.264066798954417],[-69.485647854021082,42.047636406603971],[-69.434328039687301,41.694963080763593],[-69.511386231394397,41.444164691614297],[-69.480089249437512,41.309020590029697],[-69.488497961206576,41.164871115336467],[-69.562624458946075,40.987755558410178],[-69.698033232312014,40.851633839834619],[-69.828057798731223,40.788837425801269],[-69.970605264426325,40.765815750974021],[-70.324491755261988,40.794992423433314],[-70.510712822540754,40.87100482604739],[-70.833723287858788,40.830055785907383],[-71.015057000640837,40.883643750682985],[-71.151548760574585,40.988146442048034],[-71.428464358471018,40.90595624898662],[-71.5216735602733,40.738032646740479],[-71.678738095199733,40.61420024907563],[-72.156532710441965,40.428591468515627],[-73.098877962052342,40.163643444361711],[-73.498862279864767,40.117571546495654],[-73.709741887369432,39.415303426241692],[-73.885016197548225,39.188544506461021],[-74.090238797659978,39.055679841400135],[-74.43102085164449,38.658921926267226],[-74.557351223264845,38.561906180754086],[-74.546744816447642,38.339525669425008],[-74.694202663175375,37.947595839083185],[-74.945845463345194,37.572362769696902],[-75.096463176991065,37.447924819334588],[-75.185632952912513,37.261169923059597],[-75.426222434262996,37.073552795128407],[-75.484792952693837,36.964746544601873],[-75.298160354102066,36.432837416568525],[-75.039105928412155,35.954566677336423],[-74.983848270195594,35.787614268740576],[-74.970682698283682,35.374353938354524],[-75.044706641523561,35.099807753260421],[-75.115118338018462,34.973163988377806],[-75.302389935856084,34.823081896254337],[-75.803674688050549,34.645626613660241],[-76.15046733732369,34.289413172540741],[-76.306293564859331,34.183842061682164],[-76.490109910071013,34.143362142352693],[-76.794575316892804,34.202543728508608],[-76.985695827952128,34.193113250321666],[-77.405365256862595,33.913409254882772],[-77.522811334206907,33.647026192882379],[-77.70963655932438,33.490174822806779],[-77.850193074159833,33.44605982254415],[-78.310512472327119,33.417039744930705],[-78.531564403570187,33.317177003076317],[-78.715776359148478,33.124373456160264],[-78.861458665564186,32.85651079557568],[-78.965945255308796,32.743634372960187],[-79.260555954527945,32.584520354775549],[-79.665198271139758,32.250261424600076],[-80.083023229019574,32.067088056929279],[-80.193595472178444,31.940737460208364],[-80.363579279549924,31.838861346209086],[-80.626641793889661,31.565436782994979],[-80.787412203366301,31.255483141378814],[-80.839939326403126,31.043563093535848],[-80.934423965961102,30.902464922292246],[-80.956834035391651,30.703799586111757],[-80.769681086164795,29.947125600380879],[-80.468980299083157,29.307443460014554],[-80.060407226298096,28.67243902628363],[-80.024544845110455,28.473544714869217],[-80.070620201758487,28.2761087687146],[-79.610479496764,27.139222840802461],[-79.541485908154741,26.572878873188944],[-79.629870848721197,25.776504600657169],[-79.668028049178133,25.634358982973133],[-79.774867017954037,25.463541107071453],[-79.764357124342965,25.274971734686169],[-79.832569730500893,25.083373141804692],[-80.00916832845013,24.809238168757094],[-80.337704471940029,24.503930602327294],[-80.923080976087718,24.222089390285458],[-81.71225976260213,24.052049721644689],[-81.858826040136236,24.045084968572546],[-82.045426127710698,24.102016990739145],[-82.196034789747458,24.226024566113697],[-82.287722852775389,24.398228360871428],[-82.299253106503016,24.640936759219858],[-82.249596997507538,24.77901084199787],[-82.125587488714828,24.929617912083465],[-81.665194428824279,25.185626076295708],[-81.658375423008238,25.321040230281799],[-81.744536146224036,25.438019609374404],[-81.983022137248554,25.56102828304676],[-82.093611472524842,25.656382968954546],[-82.259537639669148,25.975999059717303],[-82.488486183723637,26.084384466121584],[-82.619358298827677,26.234937256178458],[-82.672797469333005,26.375153203524661],[-82.68520378783397,26.561074709770935],[-83.159831505995925,27.27267380020778],[-83.21408710317813,27.500820603198164],[-83.33276029514829,27.74422743706938],[-83.335400066849587,27.934188210372977],[-83.163208675297327,28.558082809421602],[-83.167875462516449,28.726275377668859],[-83.6339145541519,29.088895256697516],[-84.045663866972959,29.539154239164827],[-84.18610181506746,29.447967027187932],[-84.354051701703256,29.407298344902237],[-84.478605917990791,29.304744292187465],[-84.797812592184485,29.153329298386918],[-85.030505805244871,29.107376630802655],[-85.589185559575981,29.24301778413772],[-85.751140348197751,29.364585055571148],[-85.882081279632786,29.633922129401473],[-85.971445607309875,29.709228998576865],[-86.585715198688973,29.916222274949142],[-87.596019461521607,29.765288219080936],[-88.132543448210939,29.731936521769107],[-88.331468833395206,29.749869138238928],[-88.383163839120158,29.578906954812144],[-88.542405036457623,29.361770819172808],[-88.515973266362849,29.204967428382261],[-88.531327616006578,29.044851564094472],[-88.6434045073471,28.815852513564511],[-88.935162976870444,28.568100599616855],[-89.346695806620843,28.482551136906039],[-89.495676283798986,28.496277129498591],[-89.633926421535648,28.553465282718243],[-89.818435575811407,28.750448505063765],[-89.948010170705572,28.673015436779448],[-90.17003472020555,28.607008911990892],[-90.36737077876063,28.629685455371771],[-90.502548313184846,28.69784050975575],[-90.659918463031389,28.639492345621242],[-90.799869241241581,28.633512550820395],[-91.475310631917012,28.824782156901172],[-91.598086121846222,28.895421580031091],[-91.700591993341618,29.004550930198363],[-91.887940244205083,28.989983888712491],[-92.127918232807957,29.071949400523245],[-92.26713003426093,29.057123708318102],[-92.770597446188972,29.107302264365817],[-93.347779790288484,29.284596142868931],[-93.621935136088936,29.271637520640247],[-93.950656268919204,29.190029353871026],[-94.429318880506656,28.989734849348828],[-95.024644016003009,28.530600266046985],[-95.455524719486434,28.287225662902205],[-95.998964709213297,28.056545542845775],[-96.13442478389527,27.923125789229911],[-96.550120751353063,27.675726398874314],[-96.809434811613244,27.299047362122888],[-96.873422091535531,27.147950910385202],[-96.854908129277348,26.885625434009558],[-96.648200888864125,26.117705873674375],[-96.648405812115072,25.91741674243805],[-96.683682181055957,25.772321738847314],[-96.793835333305182,25.607171233052235],[-96.91424046053757,25.518855959125222],[-97.228625212338216,25.391947475202073],[-97.417739354879316,25.373677701514875],[-97.915733838770961,25.551502075356698],[-98.46194256014185,25.647652033719595],[-99.294569224566587,25.983380192109223],[-99.46140496119817,26.093811844933743],[-99.550506447526047,26.215102106213646],[-99.680171064107213,26.531260111172799],[-99.906961154726673,26.840110697615913],[-99.98857749963544,27.281906471341514],[-100.11927009952879,27.388097294162453],[-100.45523574909774,27.80395788764438],[-100.68782419977323,28.017543242048617],[-100.82600725720819,28.351081837288728],[-101.12728564758913,28.840103204425379],[-101.61158008158134,29.264548390611466],[-102.21397293759023,29.34067139410708],[-102.32623946197661,29.307718381867712],[-102.49456559211862,28.913456510370491],[-102.80477289467707,28.631891891495954],[-102.93954278382775,28.563971548445675],[-103.20730774957163,28.505890454290444],[-103.46346029463469,28.545311403275253],[-104.19497442300944,28.867485322897309],[-104.73228116403288,29.199819536143689],[-105.07203174689324,29.636436011192927],[-105.16567970491219,29.979067673151345],[-105.317146784971,30.257875738686643],[-106.43498411122468,31.041785990956267],[-106.65960196268873,31.258337621476684],[-107.71875431987131,31.270727981538137],[-107.76605414896832,31.108910508299932],[-107.85242510336676,30.985038247322908],[-107.9718398773834,30.892602374687822],[-108.16332122617675,30.832315499410075],[-111.0916675139985,30.826952119660081],[-114.98016214790965,32.02952751436947],[-115.19895424705352,32.168366848434403],[-117.09293923294699,32.034847281565483],[-117.29384445759952,32.061879791918606],[-117.46750553997623,32.166452706081039],[-117.6509647322779,32.374783221793358],[-117.73364144855974,32.566612944327957],[-117.75828659927662,32.805529499351124],[-117.86833239550414,32.956683862784622],[-117.86132275611986,32.726580192805905],[-117.93635927410847,32.548283993945361],[-118.07345726981374,32.411813656019611],[-118.30019475898465,32.330305620417093],[-118.56789832550596,32.348171235276403],[-118.74815517448518,32.421578989728793],[-118.88672692063878,32.558250498045624],[-119.07133940783032,32.87817142361984],[-119.19023777612229,32.783301591868295],[-119.37544845758831,32.721377840596638],[-119.62678574673498,32.731812879743437],[-119.80694911137836,32.799861513663323],[-119.91769961309558,32.893199347812029],[-120.02846981406368,33.067860779761901],[-120.07433241866535,33.255773271213663],[-120.05710357274259,33.408776963579548],[-120.23992626984584,33.423523129999978],[-120.45059307869815,33.529025710809947],[-120.65677020779118,33.581990990189716],[-120.84622387080049,33.74009667103536],[-120.92820342780452,33.920490186190911],[-120.92316189464188,34.165193496849461],[-121.05506895733895,34.294875483946434],[-121.13139358851846,34.466750916142232],[-121.13568935468216,34.795149276496133],[-121.25853236257376,34.91160488262755],[-121.35372270588901,35.109539887692172],[-121.64535551857891,35.331237385250141],[-122.01469715977697,35.792520941445595],[-122.29846306293254,36.061850757230701],[-122.3591885373511,36.198179986104272],[-122.41767737944286,36.560456445636504],[-122.82504146111309,36.953141903258903],[-122.98322870005676,37.489710161216863],[-123.16806743809353,37.518585411327841],[-123.33893330589224,37.622703115805528],[-123.47344327673022,37.832433723748451],[-123.49765411690923,38.094665130230076],[-124.11544311590283,38.62783610751832],[-124.17826416785114,38.758660830117428],[-124.21390544534782,39.000547401670445],[-124.31265707535907,39.28255664042441],[-124.29343815819509,39.54271179419932],[-124.44197112051225,39.71664488255955],[-124.70386742242781,39.929976719512261],[-124.80327116537666,40.10984886023369],[-124.86894843052593,40.440994918564222],[-124.86330647730868,40.581182192256271],[-124.62819187046946,41.09685540196827],[-124.58222446352443,41.399530471292344],[-124.71085683845287,41.608122919749839],[-124.74340636737298,41.808553698483678],[-124.83941032125365,41.998979522287605],[-124.91357965194103,42.297220019070501],[-124.91750365289009,42.46636752557135],[-125.01505513760839,42.658897027974916],[-125.03714644367963,42.860052884934333],[-124.63889892921128,43.861377494293016],[-124.55714001736906,44.820207633977056],[-124.43488653483743,45.616521754757436],[-124.44553867056401,45.936698250274659],[-124.56218480824326,46.178641014328193],[-124.54786228172252,46.607719619443202],[-124.67084059935226,47.03566926376147],[-124.82355895769403,47.41334769498711],[-125.1167587268597,47.764636966255452],[-125.20635496719783,48.322805777139962]],[[-119.06119766563587,33.549749613754827],[-119.05709765727494,33.572432931058515],[-119.08561233646363,33.582060867181582],[-119.0717333904995,33.564535504272101],[-119.06119766563587,33.549749613754827]]],[[[-171.2589463505245,52.349158151968403],[-171.32665857795644,52.588674196185167],[-171.27050062772452,52.831159589161302],[-171.14475871860597,52.98653682142043],[-170.87285174783756,53.159445475905819],[-170.63165802586553,53.194747485119684],[-170.39178301049211,53.128626011028082],[-170.18577353077751,53.290595404173494],[-169.8281515297754,53.383108509364561],[-169.55775345232658,53.328893041700994],[-169.38058649477878,53.238281494876112],[-169.22503437515971,53.355457883681908],[-169.08206435472127,53.55673072753001],[-168.76976309459843,53.71640061991004],[-168.60591209071964,53.883376501801273],[-168.47417963003491,53.963778067465242],[-167.98003396345158,54.057883200721719],[-167.61064317057694,53.957301784099208],[-167.51019668759832,54.182550639933709],[-167.22116736735259,54.407232157391853],[-166.71489507159663,54.50430237362395],[-166.48159993335523,54.467298177050985],[-166.22824097257927,54.654654968808458],[-165.88179513507865,54.689929461848173],[-165.66562280793053,54.772899901071206],[-165.51575122682337,54.774743578700573],[-165.37175606196564,54.731939557054375],[-165.31435650477232,54.867924005773801],[-165.22024083391059,54.980791560220041],[-164.99585824928286,55.10838290669237],[-164.78361923187074,55.302799893057283],[-164.66291038815424,55.371420190997412],[-164.34172185645238,55.407498718031746],[-163.86913610194597,55.545064124294619],[-163.52030251441954,55.535215458096509],[-163.28416185153074,55.663953235339427],[-163.08061664047514,55.702522681130183],[-162.39778072586287,56.157113257954613],[-161.84512337768243,56.384495264348281],[-161.28180518442034,56.516757564512631],[-160.8209886282277,56.486897527003507],[-160.69239573845218,56.630487379355934],[-160.39871830967883,56.82982050651345],[-159.95102251431368,57.033014948346839],[-159.48337399980045,57.154736056052251],[-159.18680243708516,57.319606610285426],[-159.00863705786179,57.364587020052554],[-158.75140660466826,57.614795677033847],[-158.16174942509701,57.935635039557454],[-158.11007136031733,58.121171335094743],[-158.38476169361707,58.147578780563734],[-158.50141690153208,58.031999512252618],[-158.63207122991625,57.96635339815154],[-158.88714618052299,57.908835324285377],[-159.07304430698989,57.919996302296347],[-159.24196318631573,57.998412455360409],[-159.73812466550979,58.354086080813467],[-159.98589205052539,58.324501220634644],[-160.34507014786263,58.459913248402842],[-160.63370404166611,58.168861766689552],[-160.85361417574927,58.080989756636903],[-161.14973771201528,58.075758579419841],[-161.37091992556876,58.170623152111439],[-161.75477799094989,58.112335890810755],[-162.32828932438221,58.179594895712164],[-162.52612156570163,58.322021242456508],[-162.60342096836192,58.447306272677778],[-162.64088480742345,58.589671989098399],[-162.60767012728695,58.831166595613382],[-162.46686456175618,59.025815995517071],[-162.52300427726084,59.281235283650219],[-162.4733445284979,59.499905581316696],[-162.66260667713473,59.487486007230139],[-163.12860606674136,59.354210605134583],[-163.98956116627852,59.3148349855066],[-164.40570221459666,59.471744680489351],[-164.58851236671919,59.667974207812129],[-164.83204910266463,59.81375738676374],[-165.09350978579076,59.880368762692108],[-165.16036915785438,59.661090611877789],[-165.32573110303051,59.490181522434398],[-165.50374388623112,59.421200346673814],[-165.80444564986135,59.394309939409219],[-165.99102826357807,59.289897361518925],[-166.17589378433948,59.265113513902207],[-167.30002720016992,59.535433292474842],[-167.75198314399287,59.819094496415829],[-167.89613343088394,60.011093465045583],[-167.93585827558084,60.199707908678342],[-167.88139355156247,60.433537981601539],[-167.72421171298416,60.615022908763045],[-167.50055443362061,60.702317413477616],[-167.00598511670066,60.720836293342138],[-166.5499435320996,60.87700926928111],[-166.07714607872782,60.884286256411933],[-166.28201675202718,61.059486371573882],[-166.46064608572749,61.152276745475618],[-166.58492680164485,61.294974878442162],[-166.66727533458459,61.626294186888998],[-166.62154959207544,61.860889621320887],[-166.48318048708379,62.096733852484448],[-166.38111475534669,62.201050041908829],[-166.1694173106153,62.296155469144352],[-166.05462019241006,62.459723272353322],[-165.75932020021432,62.694908005815513],[-165.46632295234406,62.892275486522998],[-165.2955087916072,62.969444311544635],[-165.19293174836085,63.227258578400523],[-165.06323338632876,63.370966988127883],[-164.86905303248767,63.474531231712824],[-164.66595146219129,63.643764177532653],[-164.49016610515747,63.708220568354534],[-163.88806581589176,63.744023469508974],[-163.24938900041673,63.562637065076338],[-162.89045451582771,63.695190784776031],[-162.64324585811883,63.883016746805851],[-162.84214019513377,63.875690888640044],[-163.0442694338843,63.934733003237767],[-163.23295415926279,63.931998193488162],[-163.7817069454789,64.086940925328463],[-164.25457657148027,64.083411978576081],[-164.98402137918643,63.953850614642484],[-166.19131209882207,64.085150112260862],[-166.48038472382183,64.150658120334981],[-166.79361889245024,64.337833410941514],[-166.89085096711301,64.441545974364331],[-166.96394732957353,64.599947417806106],[-167.25904953118805,64.782361413873062],[-167.37064852191384,64.925513787111839],[-168.08654062554376,65.077536663415543],[-168.2700279604127,65.157447491858576],[-168.45135158816939,65.31422269062486],[-168.53693246868778,65.43753497242615],[-168.58205811744773,65.580691117167945],[-168.58266180990839,65.730789889677112],[-168.53868914129086,65.874304385000741],[-168.45410292953807,65.9983010734027],[-168.09158973291852,66.221267698844343],[-167.8197303574139,66.234988699652106],[-167.53746469835531,66.341314638716128],[-167.2058325968836,66.373230925645359],[-166.90345452766098,66.526638986799156],[-166.41346774028767,66.643167347910307],[-166.12397750538395,66.769547783143196],[-164.55218778838491,67.079270402928387],[-164.28127456452339,67.095736211786956],[-164.37841346655995,67.165453895849595],[-165.50335829144137,67.557019385975593],[-166.10500077515778,67.677858970151249],[-166.39529427506852,67.796658015420746],[-166.95371923685886,67.888826954597846],[-167.1560177070057,68.023954306968307],[-167.23759642188926,68.14613537074149],[-167.284912186794,68.335649042235602],[-167.27033695593579,68.481836804077119],[-167.21390332188341,68.617478036799497],[-167.04152630764725,68.78914701287097],[-166.70806907403971,68.893940840008582],[-166.68928930092756,69.023300200090333],[-166.62601475720155,69.160615042486427],[-166.44023556819755,69.328305326411112],[-166.24701739918368,69.383645469268657],[-165.51187325071689,69.368113243127709],[-164.38239499901297,69.432705808010965],[-164.02771785430582,69.512699674002363],[-163.78518434761605,69.608961743832197],[-163.57788340155449,69.747533805964295],[-163.48455734768575,69.921875528751158],[-163.23283164496897,70.171559661321567],[-162.02519895968413,70.810222762211438],[-161.82830102927741,70.828693394028619],[-161.57681184895313,70.745198359007219],[-161.11122713522289,70.796133712564256],[-159.8580674857707,71.254292339535965],[-159.38366695014221,71.373508701424726],[-159.22270593700131,71.369824101377517],[-158.9794969026249,71.299642871456385],[-158.0069955529404,71.354359575259792],[-157.51877028485282,71.502952696804982],[-156.97068675227135,71.782254314608224],[-156.5032536576046,71.90629136401688],[-156.22213909255001,71.865477617170086],[-156.02539822496561,71.734030844385444],[-155.49016058697597,71.657698808756905],[-155.33158014264691,71.571030312271319],[-155.14261206309669,71.598318138288178],[-154.69998777238416,71.534636423087719],[-154.33558826970545,71.327815880873189],[-154.22180655232356,71.314530089690209],[-153.9583447856287,71.375496132553494],[-153.23025956002806,71.432341077184859],[-152.21232250813807,71.338932658195802],[-151.95939706650938,71.228607093753681],[-151.8032240864778,71.064161668752149],[-151.58805347851433,71.02596075433118],[-151.43033628078456,70.929716488105015],[-151.27044100194064,70.922281752887855],[-150.68173062247814,71.009285770531235],[-150.27176171919484,70.943685024496745],[-149.91192672447355,71.007614172422322],[-149.26200034119671,71.000358256561441],[-148.57459816809978,70.902952846179474],[-148.38463331493955,70.830388216068087],[-148.04673586293313,70.84596461123671],[-147.60438178542378,70.713612359542239],[-145.80814611729653,70.659593853436249],[-145.09496249326156,70.505336525148081],[-144.67206029234504,70.485162796362502],[-143.81117726190263,70.597598017607709],[-143.18502811620485,70.614892327268223],[-142.57673165764334,70.516124927720739],[-142.15151105722299,70.353743967514873],[-141.57746452994328,70.254641328448727],[-141.30159762537804,70.154822156207544],[-140.94683188339491,70.147482576426995],[-140.76244141186058,70.089247322949589],[-140.58519066135469,69.926179152522337],[-140.52352882768793,69.794470906822013],[-140.50237601770709,69.650589697671833],[-140.50215655797581,60.796453588145198],[-140.04534441976566,60.717752993858532],[-139.734057357056,60.824684584410548],[-139.09240078097417,60.843291278502235],[-138.90075875664269,60.810423789374724],[-138.77329460115297,60.738739598820317],[-138.67202287082958,60.633241870183859],[-138.58193860881815,60.386585505187533],[-138.41738974485216,60.309688749701834],[-138.2676051170117,60.151544323898193],[-137.35847140103425,59.667449255019498],[-137.20170379649531,59.536428225438918],[-136.9448066406944,59.601702096854652],[-136.82120021713121,59.81091874910971],[-136.64169718818042,59.988775097449711],[-136.47619604495748,60.080145570454988],[-135.53917552043285,60.288979074080601],[-135.26871230378811,60.248023655382923],[-134.73090518403833,59.962426484178451],[-134.61169741790178,59.816886268191723],[-134.56137380196287,59.677413941116519],[-134.16488857150955,59.502059646271128],[-133.96716025143078,59.29942047521039],[-133.58433034701153,59.145347010505759],[-133.07631689940834,58.790687146997151],[-132.92738475173903,58.572191315910914],[-131.95174769343652,57.6330079298749],[-131.84151833292671,57.488767728005278],[-131.67370259019881,57.375290984831103],[-131.44287215502015,57.057864935017129],[-130.60606289681942,56.821814061683874],[-130.15171899155578,56.609146286740391],[-129.99910134790071,56.599177655047498],[-129.86064239775283,56.549151848639319],[-129.68073886676785,56.384644126590821],[-129.55157327665265,56.139886703141514],[-129.51671480842947,55.999264381447631],[-129.53522878375205,55.80751452747127],[-129.61262633543791,55.611407261164942],[-129.53685840180285,55.305786961372576],[-129.59313383685856,55.067404723465387],[-129.85399360043743,54.67934550546066],[-130.32579989163906,54.336678934287825],[-130.59690289940141,54.270405903397588],[-130.96362099264391,54.321037841292274],[-131.15236200661769,54.410691670006784],[-131.42671761123418,54.394921306037709],[-131.61268695691783,54.439218017194143],[-131.75588297125822,54.303125209676558],[-131.94155338323708,54.233008997465816],[-132.2549024882187,54.237808564173001],[-132.40608308351739,54.28494524531699],[-132.59030500516252,54.198006291156027],[-132.82561846114692,54.198997164219456],[-133.06069217567483,54.278470612773972],[-133.30741882437013,54.454901779285024],[-133.58410935597342,54.755628722352753],[-133.81653975602299,54.798208886435141],[-134.01571101895004,54.928735666395113],[-134.09759291975425,55.046903656398548],[-134.21375075962138,55.347073708322981],[-134.22979613333177,55.579343399363552],[-134.4271310266322,55.637286355085358],[-134.55603359205733,55.737976408893623],[-134.81880721787655,55.745694429188433],[-135.00241763553532,55.809244120528838],[-135.31340646862736,56.112543525046242],[-135.43055579348467,56.307663689485082],[-135.61006731151062,56.407568506265513],[-135.72643836680908,56.517649296855502],[-135.93088344307927,56.52418069447738],[-136.14228638168291,56.634524456766442],[-136.23552671263465,56.744223166078626],[-136.29364402351032,56.875942353433125],[-136.30978709107475,57.154524398269928],[-136.42881007727536,57.29913913111637],[-136.90927126239848,57.606485960058208],[-137.00154655415884,57.722573142759863],[-137.05591447932082,57.86174970300457],[-137.76338171888807,58.132235891770812],[-138.2889066508464,58.51204439976005],[-138.43721112922171,58.584928734254589],[-139.96969232194866,59.067679898970525],[-140.09708442713065,59.146532615059243],[-140.19200455955442,59.254790015740916],[-140.7126998168138,59.227369560144588],[-141.4746002445778,59.394829581701856],[-141.68388301829415,59.492838637966493],[-142.93896651783638,59.595316116292345],[-144.04428262043487,59.520766960732182],[-144.45377369679943,59.33894423287115],[-144.69891080799192,59.320368147840682],[-144.88285126635685,59.392021265659785],[-144.99455795295501,59.489837973401826],[-145.08986532219851,59.662710084757535],[-145.1119161588237,59.833667039335019],[-145.30288114303386,59.825818183155484],[-145.68574085957462,59.951644187870521],[-145.87287467178817,59.971507283519614],[-146.03029154029676,59.898400359863032],[-146.50706843949567,59.786377492731383],[-146.65944475754668,59.775588340276407],[-146.79446065982074,59.806558228800363],[-147.00209130259029,59.68020735777872],[-147.33348419841954,59.412525522527261],[-147.78409215053935,59.302952767820635],[-147.92782900555375,59.305775458375528],[-148.06482110700233,59.349380678385302],[-148.25941532491817,59.518493494762893],[-148.74228769171265,59.44827570664048],[-149.16877101578092,59.515645797571288],[-149.27279702034895,59.39103687143902],[-149.39265103062621,59.314858416195776],[-149.74407107620223,59.245440642340363],[-150.04382430489696,59.091167818793288],[-150.36925401420044,59.041110157670765],[-150.76294442077779,58.779753926575999],[-151.65466570551669,58.693641134913911],[-151.55381059537297,58.579680886964738],[-151.49314265620109,58.444448368912589],[-151.49508832170579,58.134053959695066],[-151.57528749541774,57.954675365158593],[-151.75411748149725,57.789489634765026],[-151.71557655960592,57.588951747166817],[-151.7578103565917,57.396314245523399],[-151.83700932690743,57.27089044346485],[-151.99255370095881,57.11982865734371],[-152.15390767548163,57.017096452371355],[-152.34659664462055,56.972242477377101],[-152.7435138499043,56.666750112021965],[-152.96399915221949,56.615021633862391],[-153.18284755341614,56.513556335983722],[-153.396651708701,56.511639705716725],[-153.65541507996844,56.35756165198989],[-153.77077086792855,56.18471569588776],[-153.97153031352343,56.064673030939645],[-154.20763836720982,56.015447106952607],[-154.37874896966454,56.027667718347892],[-154.59677609275528,55.936727709352049],[-154.74689508295612,55.913734121914672],[-154.94460147399715,55.954583935607992],[-155.09055859613142,56.05006947021446],[-155.06983031696328,55.761434404353331],[-155.1284326231816,55.579782602912601],[-155.28957068846836,55.401835590999163],[-155.46094178699525,55.311002080440112],[-155.70223840424083,55.299361109293415],[-156.01281226863378,55.412621674046015],[-156.12080851362813,55.509173420794681],[-156.19659349469609,55.632632180620092],[-156.23549883871976,55.869384816076291],[-156.20320807054668,56.010603564916131],[-156.13178927832979,56.136638420616364],[-155.83319729571156,56.365302446803966],[-155.51135440056947,56.417028072903271],[-155.25936748569839,56.309263742187476],[-155.27353156989577,56.498082535516204],[-155.23561688836247,56.638879098529486],[-155.15869355319541,56.762749981867344],[-154.98954823136134,56.909898476122414],[-155.14587072680706,57.098272402608586],[-155.20561110029126,57.330995138903532],[-155.3119138492921,57.293618686896409],[-155.55901413358407,57.11901405894951],[-155.99584600251922,56.951910842780443],[-156.12567181919405,56.748676899145245],[-156.40678230754216,56.56234548208856],[-156.64009115023293,56.508874329876008],[-156.97464385985734,56.355141159309348],[-157.18145620665732,56.320958904833084],[-157.35628408870841,56.186730408174576],[-157.61190340849262,56.135206388318174],[-157.80220900010184,56.037858884153863],[-157.94247091420687,55.823798940356596],[-158.29200936012916,55.60961404572334],[-159.06309381404498,55.412247445011658],[-158.88945965666542,55.135307146097077],[-158.86858071793657,54.893273374463774],[-158.91285366654273,54.75341199119125],[-158.99581274075513,54.632419873617366],[-159.19939664013521,54.499868477522561],[-159.4414238537089,54.478913655088682],[-159.78132339208025,54.594801561687561],[-160.08066393498598,54.44484605589831],[-160.32412632910462,54.4326503151797],[-160.50491748822589,54.507641908660233],[-160.6497815982072,54.657556795684208],[-160.84481120548628,54.647992059967237],[-161.03424091145118,54.706440543785298],[-161.22041250790713,54.883515568615344],[-161.55043187153228,54.899360424202492],[-161.78230761192077,54.748322453387665],[-161.91364060385973,54.527725639172111],[-162.05534044598056,54.410524246977573],[-162.07668543307207,54.255426031048636],[-162.16932828976817,54.083122255590276],[-162.27868834333003,53.984682998968658],[-162.41196635085575,53.922338141367995],[-162.73578554984479,53.902463843221227],[-162.8843813590554,53.925909549821625],[-163.05903053679916,54.023335763954371],[-163.21367058754066,54.185706889203317],[-163.29367809465231,54.202493307977804],[-163.53034812530635,54.128792499512464],[-163.966308440283,54.119809563780507],[-164.27718439767682,53.963496733998205],[-164.40742495635916,53.930637288484455],[-164.81201287813383,53.919479234362043],[-164.97743780337717,53.944261557029535],[-165.06222576138748,53.836249413922395],[-165.18006535922643,53.752127399166056],[-165.68707764228469,53.596077103323147],[-165.83576902409226,53.391826773488482],[-165.96017742729828,53.290351349631585],[-166.56869292443201,53.018742134228837],[-167.55511839087535,52.7650645899705],[-167.70399268299266,52.765337844028217],[-167.93650303066829,52.822367746144657],[-168.02752437191879,52.789349679678999],[-168.28317888386343,52.595589777935672],[-168.96515230165849,52.3477425546684],[-169.15971978690112,52.337363506527964],[-169.38167508617161,52.42738387571363],[-169.54602324549685,52.324919426343392],[-169.69186717850658,52.293569509161813],[-170.17805731277309,52.366526774276032],[-170.36935575850757,52.173909464678772],[-170.78517108412453,52.050195283671108],[-170.92923920153245,52.06775571170369],[-171.06219474571537,52.125952054863518],[-171.17283541908319,52.219880924136326],[-171.2589463505245,52.349158151968403]],[[-152.43405215993434,59.11696582203713],[-152.45453713418758,59.380931948888978],[-152.34273203753085,59.611745135341899],[-152.62372482979103,59.465786698431408],[-152.81024809707134,59.290527432272235],[-152.97292941087377,59.230086519078093],[-152.8777168689569,59.095737849297969],[-152.83897706077011,58.978389377801051],[-152.62236095285462,59.092155552694429],[-152.43405215993434,59.11696582203713]],[[-162.31512861579461,64.027338861717581],[-162.0750525259167,64.032555695030027],[-161.84584087252395,63.958249372211426],[-161.6606448718793,63.964199495842841],[-161.82717125381402,64.064584787296567],[-161.94338859732682,64.223011917153073],[-162.31512861579461,64.027338861717581]]],[[[-158.69456619584946,21.316054292738947],[-158.75121231076884,21.448396648005701],[-158.76780460696997,21.591393071176991],[-158.74296769716432,21.733190141824853],[-158.67876039042926,21.862033857600359],[-158.50001083232092,22.019843665339589],[-158.05437709042027,22.192613193957666],[-157.85416809677707,22.189207616606783],[-157.71372580959724,22.134765946782501],[-157.300133968946,21.726358994984015],[-156.51182866619092,21.612409134893312],[-156.29419237254388,21.452465473554138],[-156.03661320203184,21.389119496476372],[-155.63433618059204,21.108395550535722],[-155.51006636272606,20.89686435441444],[-155.49766608286757,20.674257836769041],[-154.99572857374747,20.450981152635482],[-154.84676825603839,20.349065663338337],[-154.36593704356423,19.764587686857176],[-154.3072916809179,19.577273588493238],[-154.34166881555831,19.335251981656533],[-154.45015495011347,19.171677507440382],[-154.73572694142527,18.933384360127825],[-154.87015407314223,18.85422424624872],[-155.12366497087282,18.780929054055196],[-155.3646602220058,18.537737284569481],[-155.60843074727774,18.464464384508616],[-155.86693845109559,18.503908892056977],[-156.19561400495974,18.684543206150032],[-156.35624700964885,18.912847261647695],[-156.39388724953992,19.287559059857081],[-156.52522643618553,19.599510991277853],[-156.54838326135871,19.747690645090852],[-156.50950445505416,19.943246317711996],[-156.4014149616232,20.107073589322102],[-156.65663207524528,20.168341550062408],[-156.80822999798417,20.282636797182175],[-157.0085977322089,20.258965109886866],[-157.19859453525774,20.311394870845508],[-157.35356728107575,20.433177928191704],[-157.4697687021943,20.646149869057929],[-157.66810894645388,20.785999250474532],[-157.87793393634701,20.775185248692196],[-158.32615698476059,20.867889698472723],[-158.50979270109082,21.018329489181742],[-158.69456619584946,21.316054292738947]]],[[[-160.60887729490844,21.502056106982003],[-160.69079898350577,21.620231015962275],[-160.73570066762463,21.75683376461113],[-160.71970597023872,21.994473598056818],[-160.65690370574697,22.123827174543273],[-160.48538813699847,22.334215768413582],[-160.33474606967954,22.456755936883102],[-160.19710167727868,22.505583326398654],[-160.05877322890021,22.512931164350011],[-159.72745158852359,22.700361824846329],[-159.34437325682163,22.719260521889439],[-159.19230692918393,22.693071282073632],[-159.05546747755471,22.62176203469885],[-158.85243102652615,22.36658048186851],[-158.80516193571239,22.14051889016709],[-158.88399903515244,21.777865278741423],[-158.9888973624964,21.62178339568003],[-159.22418061890465,21.435892715609789],[-159.36678447990346,21.385308928842971],[-159.51799000459846,21.379699119832292],[-159.80951297290838,21.484336334609658],[-159.98089403648143,21.347869599230222],[-160.21829904664426,21.297445493708452],[-160.45148012555202,21.364753098832111],[-160.60887729490844,21.502056106982003]]],[[[-172.16872016171456,63.119855898963266],[-172.28095770044189,63.285742170037544],[-172.31705633956142,63.43201713361389],[-172.30794268468301,63.582404692974031],[-172.20379488537347,63.904412449635089],[-172.12152542836012,64.033233099581238],[-172.00418244987969,64.13118027315538],[-171.6712172021137,64.226147888709633],[-171.48823670635718,64.201047299468357],[-171.2803882254774,64.111834967350305],[-171.05736113173492,64.090818270731646],[-170.36932560885504,64.194769106982079],[-170.0095922417037,64.113165713616752],[-169.76925728871095,63.960780342352393],[-169.49006622818675,63.912035052961258],[-169.35540601574866,63.853668767266036],[-168.5877335075985,63.793582477632931],[-168.45433895370687,63.736258102022688],[-168.3430378554587,63.643025829842877],[-168.24485493583069,63.476803383835581],[-168.21692799675813,63.33432434284677],[-168.23111318664562,63.18982876701515],[-168.30867523174592,63.001962364872959],[-168.3909695578746,62.878245660192135],[-168.50598740060238,62.784176138112429],[-168.7911881038041,62.675246512256834],[-169.22074408811986,62.664735805837267],[-169.47269025882449,62.499702748004275],[-169.65711855157849,62.456732023959567],[-169.88833109611389,62.503542510756795],[-170.0921387016009,62.668998579020489],[-170.37980064055742,62.734166633255128],[-170.65548301216089,62.893984638022232],[-170.95552947252619,62.943150058678597],[-171.46089098509916,62.835717484440146],[-171.60628792627631,62.839971361381501],[-171.95946332894798,62.946648190960197],[-172.16872016171456,63.119855898963266]]],[[[-170.6232120003063,56.762475365798906],[-170.77380521532277,56.886757383532057],[-170.86531677954903,57.059239179538729],[-170.88379142237054,57.253617737804397],[-170.82641180980048,57.440250844198019],[-170.73845443409664,57.557851019951428],[-170.5756374172222,57.665623722232375],[-170.18720563840211,57.736504361111209],[-170.03960947224033,57.73553396195674],[-169.85541888383344,57.667832039097803],[-169.71146940672395,57.53445936102478],[-169.62993485899437,57.3559605074066],[-169.63146965633413,57.122487531967245],[-169.39519406085975,57.103085948286797],[-169.11498990995472,56.941515144873762],[-168.99502082456419,56.735484318600804],[-168.9770607027184,56.544931091205086],[-169.05642118731771,56.320112950681867],[-169.19002523870526,56.183061661375739],[-169.43626295860949,56.067618357815753],[-169.674499820329,56.04966475420936],[-169.98723916642228,56.159767864301621],[-170.10948256903157,56.244880808242414],[-170.22353210762822,56.406772831739836],[-170.26452061099712,56.63730210097075],[-170.40779786621107,56.658143642981791],[-170.6232120003063,56.762475365798906]]],[[[-173.24928785859376,60.025201407735729],[-173.37609165524793,60.095149125262502],[-173.50416090575277,60.238946312882064],[-173.56838993330192,60.420478668434043],[-173.55925245190431,60.612821774694368],[-173.47502425554578,60.827297025164967],[-173.37547192303998,60.945464335019977],[-173.24459488312414,61.027594600139373],[-173.02443163552113,61.096352450468032],[-172.77828205317027,61.08484616335452],[-172.64394762611758,61.020767838053494],[-172.53061446318458,60.91964432161236],[-172.16058151102845,60.843739664008687],[-171.88684559381204,60.660525747166716],[-171.79702136070847,60.54474522832642],[-171.74462110822344,60.40789591362195],[-171.74023298028035,60.213108858019638],[-171.8106266276634,60.031433358556519],[-171.94511312622626,59.890455933408525],[-172.12327137214385,59.811583301218157],[-172.71957164248045,59.836194973687974],[-173.24928785859376,60.025201407735729]]],[[[-178.62033563004368,51.620730361375834],[-178.69334597065995,51.853623877816283],[-178.66599575289936,52.047677413511693],[-178.53145945587519,52.25131897267859],[-178.31655265849793,52.367012396337628],[-177.88415461925914,52.413324301611304],[-177.48368086390175,52.284884531915523],[-177.26419304876379,52.411608962741006],[-177.12307905945514,52.429485107789397],[-176.9729567487868,52.403369047138895],[-176.72965719732994,52.484820572203134],[-176.49108845244032,52.46951441637443],[-176.36184312759116,52.55463644670531],[-176.16887687109573,52.59900429497997],[-175.97381897334699,52.564991082367555],[-175.74775876829196,52.474056494729645],[-175.63131962079711,52.391093449390901],[-175.43714685923271,52.501423531511641],[-174.83397265028344,52.619677020284946],[-174.66184781509972,52.762808462733204],[-174.20794591428148,52.918376705118817],[-174.06365898555097,52.908699318974072],[-173.84849098031171,52.826462845125839],[-173.71928251364446,52.745717778380836],[-173.629023141202,52.642308262818119],[-173.01269095523693,52.5808778170951],[-172.68951861257736,52.837191528435483],[-172.54509377907837,52.882159638833706],[-172.34428889769816,52.871615261690181],[-172.09433971255928,52.778753013533418],[-171.97262575533807,52.694870188744105],[-171.88075745507123,52.579065046012161],[-171.81810395072128,52.39278950444622],[-171.82131420383962,52.245004830166387],[-171.86759511614954,52.104617178170777],[-172.02789401273384,51.919653215790873],[-172.42715234389132,51.770045978239068],[-172.63232782667697,51.76790916006113],[-172.77377136982858,51.645994966010186],[-172.90836357751064,51.592715724481565],[-173.85983370720601,51.549059429345007],[-174.14222396894692,51.621001150524698],[-174.63170220022795,51.537146715203441],[-175.28447136047799,51.522221929603838],[-175.43134553457929,51.541122215186441],[-175.55797766510329,51.597434239448162],[-175.67903861978695,51.437098595015748],[-175.85241928728399,51.337751305072906],[-176.08177259912995,51.311167536940779],[-176.24610621739404,51.24441747982349],[-176.90491509802547,51.10718734476395],[-177.09367525545261,51.121745896991804],[-177.2669250583287,51.204897976314754],[-177.64602821202647,51.1972754244143],[-177.8484753113411,51.123510339274837],[-178.04484842225278,51.132075197611357],[-178.40830338196352,51.315247716544178],[-178.62033563004368,51.620730361375834]]]],"type":"MultiPolygon"},"properties":{"alpha2":"US"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-58.928507972026111,-33.815321106022068],[-58.933091494505526,-33.650329669899385],[-58.84973432547293,-33.0668084216735],[-58.771766138313914,-32.89408700814532],[-58.644932506047894,-32.769826988336035],[-58.700679401677903,-32.487187037775882],[-58.647119977252416,-32.238451483119626],[-58.682440971776423,-31.845167782540539],[-58.635262604843319,-31.699252576811475],[-58.545794443852884,-31.569913488016482],[-58.535952926201581,-31.362162976791534],[-58.392550142876765,-31.049355249596829],[-58.392700743868112,-30.902276671904801],[-58.346130148337224,-30.752754585124872],[-58.372199017748507,-30.585965662011539],[-58.350698805328818,-30.44588515521567],[-57.999447314907677,-29.875919265256709],[-57.895304771022246,-29.778283199565021],[-57.721889522602154,-29.701062928006316],[-57.532188076837784,-29.69405006845345],[-57.385087666136947,-29.741500758126357],[-57.139270059285657,-29.622229179464764],[-56.843152677582999,-29.607547771490491],[-56.696652430684018,-29.626330848247093],[-56.562031672294019,-29.687095786529021],[-55.865466276890977,-30.237050958048474],[-55.738369148369593,-30.373693133993893],[-55.555481159577234,-30.355058989732012],[-55.363474562736158,-30.411954558482648],[-55.050698834769889,-30.657648813331676],[-54.93375323210563,-30.790349538878296],[-54.781679834415222,-30.848809777397232],[-54.649041185118008,-30.943153202708842],[-54.348681467965136,-31.046236288056111],[-53.950112807187985,-31.422664682488552],[-53.47021575073623,-31.650893533093846],[-53.336584361757808,-31.79410621164886],[-53.203070692231393,-32.06143199709463],[-52.93259067144907,-32.224399242380343],[-52.729732285485838,-32.431624241856035],[-52.633765100421328,-32.648314972011306],[-52.628889337164033,-32.791299508524219],[-52.685272378380375,-32.972999399305188],[-53.020868699489611,-33.34175264884859],[-52.885549295519212,-33.62303067398274],[-52.87170159787938,-33.766548539229831],[-52.899383283670367,-33.908050696975835],[-53.285963313716344,-34.47674630532395],[-53.387411702080932,-34.682715122829705],[-53.953472550932226,-35.121778719536252],[-54.821701975853465,-35.426056895050941],[-55.345148361903838,-35.383905541415039],[-55.621743555208631,-35.285772830160198],[-56.033848535191879,-35.400557196573857],[-56.329407233791038,-35.394685757081952],[-56.685832099449556,-35.235740223146877],[-57.030852002641225,-35.144550062819363],[-57.33416057947742,-34.961071259998846],[-57.863362364691909,-34.975914771471999],[-58.011496490945341,-34.942614742522714],[-58.180677246210706,-34.83255265429311],[-58.785200570599834,-34.231045441888959],[-58.878653716989689,-34.056760745532237],[-58.928507972026111,-33.815321106022068]]],"type":"Polygon"},"properties":{"alpha2":"UY"},"type":"Feature"}, +{"geometry":{"coordinates":[[[55.47590679888409,44.994505342475719],[55.496839603064103,45.137903590191527],[55.558016422582213,45.269275720007762],[55.654298101225777,45.377585821551342],[55.822958924432832,45.470776496687485],[58.449086662724781,46.043705222775998],[58.681274115642978,46.038956250121799],[61.268045791384871,44.820429098654095],[61.38772535586601,44.718512663950641],[61.531188870238395,44.514728422460962],[62.158647854795127,44.017232026905852],[63.195321292838983,44.127630518102194],[64.355144971548938,44.058656917732208],[64.895344472105918,44.214327499686625],[65.136398313031592,44.157879225166866],[65.540327090246322,43.843845976510472],[65.79317420419666,43.712459106799692],[65.996313236389625,43.479029942642086],[66.127749837107658,43.489723063912137],[66.274020735712568,43.45924664593111],[66.404756068158477,43.386912278705665],[66.508281698562598,43.279179128370799],[66.57535320727132,43.145667329075074],[66.599981380107593,42.998298943949933],[66.559536427592136,42.490409341052683],[66.761428730694746,42.419879912804994],[66.935062430309856,42.238256928763029],[67.079190471029975,41.655726743044767],[68.012121215882942,41.690427938861816],[68.199832734290837,41.620819508391001],[68.440835973292565,41.406507809256325],[68.589079552305108,41.52834564031123],[68.717412908894204,41.726074163565514],[68.873119090018236,41.839212616996683],[69.440498817617012,42.119724934604164],[69.827629219722837,42.253705621233493],[70.196340106056454,42.527439750097784],[70.442962071624379,42.579860564192586],[70.554068893886281,42.647421406563268],[70.924269598098135,42.771785463801905],[71.120049192195609,42.777326039201469],[71.260034826650909,42.731450113031492],[71.571389118906481,42.526774654400498],[71.660990216834534,42.413612898326434],[71.714532453464756,42.279571230609598],[71.712470906339703,42.038625781794153],[71.852606116141018,42.004358103356978],[71.976647086302833,41.931851685610241],[72.210550660815741,41.67138818221035],[72.399881524573246,41.614417738423363],[72.543831356962514,41.498318272606276],[72.839649696068108,41.356667165509982],[73.254342086621691,41.296664348959574],[73.47256264526996,41.181083857120264],[73.566867879149783,41.065527179131507],[73.622891316014318,40.927294775235708],[73.619390003196216,40.680380208797985],[73.53076926061587,40.502991825458537],[73.38016476719686,40.374001278823208],[72.884452288549639,40.0997387403376],[72.711632071061715,40.047974608628209],[72.565959280519408,39.947381539068999],[72.418592724715069,39.90565077611388],[72.281510620338068,39.90832815610883],[72.208890073531421,39.702420743455917],[72.128999306642683,39.577989797170055],[71.972936587028215,39.458428968719694],[71.831999374968504,39.413681932119452],[71.635550916496939,39.421321782729493],[71.479984480258111,39.482785659855637],[71.163304259293568,39.383669122369405],[70.823531898670907,39.432166770753042],[70.65706220088795,39.543100217450061],[70.544766035009104,39.718149182767213],[70.369356147186323,39.772269725416258],[70.039986750132229,40.010233902116049],[69.908722970768892,40.197329467820474],[69.7790744996407,40.161918547959132],[69.731689751728538,39.995680849897326],[69.621447938141074,39.838159412779447],[69.503899787645537,39.753957318851739],[69.327873119747935,39.699505733357896],[69.193465240227923,39.527586847087839],[69.004975873828428,39.412020947121007],[68.816749926492079,39.171212792342885],[68.610817395740497,39.071189559318846],[68.630483027453892,38.884729774671143],[68.578177936557864,38.670664313268624],[68.797815974131055,38.4302911755384],[68.849519992519262,38.241637981963514],[68.838196984584599,38.094950658234076],[68.695219808913905,37.734680687980017],[68.297589518977773,37.290331056550592],[68.217125289278613,36.972444397521151],[68.108854361033309,36.815438647971988],[67.949671362779924,36.710394979140112],[67.762736401544004,36.672596572264695],[67.476085149929659,36.73368830610471],[67.309160635358083,36.709922463471223],[67.099916724923602,36.744397006858719],[66.821895859720314,36.865408492620929],[66.406467569967731,36.862364374469159],[66.230191917651297,36.943041892196639],[66.074157769468712,37.127314030489487],[66.023637655885082,37.314476221088313],[66.031440673927705,37.665444989316313],[65.855164863271895,37.745006653766389],[65.568312179261042,37.742221131264074],[65.422883536108955,37.776509766535717],[64.394417164798327,38.311099189761656],[64.181223416377833,38.453079040869234],[63.933020705368243,38.50983201765839],[63.492076738830583,38.741113846974088],[63.201942077368884,38.975486643511779],[62.193919709916244,39.567692411484515],[62.060890249078376,39.711733073354651],[61.884269480480761,40.165899035197086],[61.660333505357606,40.437058609675745],[61.53806336110857,40.723611217223961],[61.487404432190878,40.742333089323154],[61.266457324323952,40.689899068575691],[60.835389214130856,40.744956243958477],[60.383702250478613,40.726953856114378],[59.828260219876057,40.973440133287077],[59.716474464716207,41.066967137838681],[59.636410292017331,41.18875828647888],[59.594871028557144,41.328464568471638],[59.578908895961078,41.628655858132248],[59.468798648811529,41.813665633459415],[59.182471151917106,41.853910702710898],[58.923613323915284,42.036307238227508],[58.753930053436044,41.88508192700705],[58.61749022870773,41.820504342560213],[58.392567287736995,41.792730986122038],[58.17533460803984,41.845242642943639],[57.973989142938301,41.716300669236276],[57.517083229848517,41.651924350791063],[57.612711885270173,41.426930977630562],[57.587721867788566,41.177370462585429],[57.44469328643298,40.971341880687838],[57.178494909581623,40.790218901050586],[56.989702983969394,40.764512873716669],[55.949485496324748,40.823220090926071],[55.801912633999542,40.854330531093169],[55.670329639323278,40.928029409607497],[55.54010224431908,41.080467537043035],[55.477659441896648,41.322187062918999],[55.47590679888409,44.994505342475719]]],"type":"Polygon"},"properties":{"alpha2":"UZ"},"type":"Feature"}, +{"geometry":{"coordinates":[[[12.085002507553067,41.536534346736758],[11.973155537093641,41.692708545662413],[11.928415037898001,41.876264841600978],[11.954978076623625,42.063318392811908],[12.054860510277971,42.234984669595548],[12.214661626861732,42.356145563112186],[12.412288948384713,42.405295303915082],[12.608010441577919,42.376224761244323],[12.774475383932904,42.275955027732188],[12.890139243000093,42.119793354465251],[12.938270604684487,41.923823330378305],[12.90977105912544,41.730407486085021],[12.809282277496946,41.562705614635789],[12.65216775364267,41.446357261339323],[12.453652834178374,41.398345024102376],[12.256228821933355,41.429230046633123],[12.085002507553067,41.536534346736758]]],"type":"Polygon"},"properties":{"alpha2":"VA"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-61.786266071296929,12.948116195632407],[-61.773886417242601,13.265950353350998],[-61.727536844909928,13.485161856149363],[-61.650787608420075,13.609530231669432],[-61.477421062105194,13.761763217894263],[-61.285245432924043,13.844971520546789],[-61.019576512716043,13.84397508544053],[-60.840201717258552,13.759267510590508],[-60.707883000025817,13.611475604342996],[-60.62513727753592,13.322087713836469],[-60.658551438522395,13.050619841638603],[-60.815826873597743,12.709857657316075],[-60.871883217277848,12.491145450770846],[-60.967499117568735,12.355837120721944],[-61.089486827911649,12.259569104446642],[-61.235139134326559,12.205403265873725],[-61.396812048939488,12.197736248526718],[-61.553801641315104,12.240277794125078],[-61.71457484630367,12.352736114173002],[-61.81969895263741,12.518397876380911],[-61.851882828412137,12.662416389876245],[-61.840624486638852,12.809557083872857],[-61.786266071296929,12.948116195632407]]],"type":"Polygon"},"properties":{"alpha2":"VC"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-73.35304850114295,8.6943778076829883],[-73.588193246356596,8.7463122789463927],[-73.741502316760148,8.8641057541021393],[-73.838662143746177,9.0312552138000814],[-73.860011846099837,9.2711185722281773],[-73.793900715092974,9.4528004156090226],[-73.484581543214176,9.9646916408182786],[-73.332415514432427,10.67929363511308],[-73.10949770256704,11.107668913978724],[-72.906512648205634,11.367319773853659],[-72.595902176413603,11.591457006605291],[-72.351824755991416,11.974176612803118],[-72.24117919016669,12.078219703628498],[-71.419462303244643,12.351572695706748],[-71.178471097862229,12.341003085866316],[-70.970474396139764,12.218831690701863],[-70.843884243856508,12.013494380237464],[-70.820296074803551,11.869761621886189],[-70.839045917891056,11.725706821493414],[-70.766247797948267,11.748113025536725],[-70.767429441358274,12.022098698899548],[-70.590987164058177,12.413079359380733],[-70.388272964723299,12.562444556007623],[-70.14246938944521,12.65807552370384],[-69.994531023521688,12.677545265436482],[-69.7563471070183,12.612000016170603],[-69.493460439849144,12.392335532156547],[-69.409009038993517,12.270021940963417],[-69.333492909222414,12.020632853433314],[-69.127780901325195,12.007125294876007],[-68.639391064520808,11.894543175032238],[-68.092211065881997,11.55577347266598],[-67.965075839778109,11.409542670604599],[-67.785230596027048,10.992363228434678],[-66.997347670733262,11.110615009448132],[-66.261623636023785,11.131750911330261],[-66.107711209825638,11.112086869817736],[-65.905040962673297,11.03173680186686],[-65.860702115024822,11.163197006752641],[-65.7748113134042,11.284412957297311],[-65.565980265484853,11.414381806572699],[-65.361327577429151,11.470076215269257],[-65.172878871063617,11.456487752916184],[-65.042273556386718,11.40055554629072],[-64.834042137984454,11.232352921510143],[-64.716379019552221,11.390360014374263],[-64.570036602531744,11.499973995178795],[-64.33477503881069,11.572935697263498],[-64.178039076123511,11.577300127289691],[-64.056211996378607,11.639570051999446],[-63.911524353682154,11.666580580693598],[-63.718333857889185,11.635377458255087],[-63.551916694500719,11.532413725589731],[-63.460279771334797,11.41723231683781],[-63.347245753503614,11.168841664986585],[-62.708717557092008,11.249542470644952],[-62.24332674485585,11.203717957808736],[-61.835190908425375,11.238726915521779],[-61.69042026767616,11.203416050030587],[-61.492253088968155,11.056457539799643],[-61.388481127706875,10.832630788712171],[-61.404369550054945,10.586430479042024],[-61.516636003380526,10.388747257516464],[-61.330891129642609,10.322563599531451],[-61.0493325005977,10.080531283887487],[-60.759465384227852,9.9869343403661137],[-60.461126808540023,9.73488059934669],[-60.341437971466071,9.5759239076245954],[-60.265671457351736,9.3724532057657495],[-60.243104941930056,9.1231103910217755],[-60.003057666505192,9.0889734125348589],[-59.678208432693978,8.9162124807650951],[-59.393629655474051,8.5248158319585432],[-59.341724897185806,8.3906408994553683],[-59.330173122136308,8.2472408543424081],[-59.35993067943825,8.1064875089874384],[-59.459137440207044,7.942920842928193],[-59.579511725834024,7.8416627634950684],[-59.701981928446784,7.6786875220183219],[-59.986061497308455,7.5003187619096261],[-59.888157282359259,7.375796061517879],[-59.828313482029344,7.1846575988851145],[-59.862383782189227,6.903330118569257],[-59.939865016960688,6.7204176469391799],[-60.08245290415767,6.5821099882783694],[-60.492215805682946,6.3224272371822297],[-60.632826501779689,6.2760469895858098],[-60.644680615531612,6.0899973132894933],[-60.715392929712728,5.9342782991799634],[-60.278849249889625,5.4729604856724228],[-60.12359693863899,5.13041780005749],[-60.118879229543829,4.8766179319436995],[-60.224991371779147,4.5963104184960279],[-60.780159758107921,4.087826456360073],[-61.130239854425923,4.0076762708107596],[-61.268340938439081,3.8778861950087542],[-61.403640081870066,3.8112451220722332],[-62.051955866927152,3.6089876364906042],[-62.267955935726135,3.6118353228964453],[-62.318414293497142,3.4478391724464656],[-62.403154756116507,3.3278066356156786],[-62.571368755541577,3.1833834783045063],[-62.708341873923899,3.116348053742847],[-62.859150559541995,3.0937179046562346],[-63.107383693906478,3.1138391690826448],[-63.27154298794116,3.1964432929786648],[-63.519323928876723,3.4213557611387313],[-63.718413992485807,3.4245100331315381],[-63.716356710527528,3.3067395441292602],[-63.554421833541234,2.9257677108947586],[-63.30114339660426,2.9038476627090661],[-63.157932742177941,2.8548784380379666],[-63.035935588698173,2.7653018169310082],[-62.946330426275843,2.6433256251009114],[-62.889601453746899,2.3993048995172255],[-62.897646254541606,2.1634714848214234],[-62.953737102923959,1.9858406944508624],[-63.191377963603621,1.7171531217184535],[-63.616023534990326,1.5419097838729445],[-63.730560958315415,1.299766739231099],[-64.005425073149311,1.0546248724158467],[-64.142168434759739,0.98249141843736532],[-64.306581549223012,0.95137706497175345],[-64.495372678645822,0.81290473820294251],[-64.778111573264297,0.71149527181483618],[-65.228227192441054,0.25576931378530143],[-65.360167192052941,0.2044881065459172],[-65.583663465105928,0.18901041120570744],[-65.766787357858291,0.23491245917218878],[-65.889344828664221,0.31599840599337342],[-66.36370376220556,0.26763795674196317],[-66.551165294569003,0.31092739031398908],[-67.19886079913266,0.84108334928600925],[-67.33381852941541,1.0269201196908133],[-67.681911211388538,2.1305768172423556],[-67.85339554058848,2.2945172668751312],[-68.027821314685227,2.3233406866911848],[-68.156756363206881,2.3923534305474181],[-68.310533992625039,2.5794502279666189],[-68.358347013397434,2.8168669206371417],[-68.305838165766716,3.0596420642317264],[-68.204974823257345,3.2284757081418323],[-67.967254298271911,3.4468148206183189],[-68.124136114052831,3.6747556677445319],[-68.353788414738361,4.4714868660657299],[-68.319226059813843,5.3439884859450952],[-68.270170614923273,5.4974285354035564],[-68.131716227345649,5.7144826621202078],[-68.459428922005188,5.6573534318032568],[-68.893564087641636,5.6903578277747826],[-69.255271405494213,5.6001000224374682],[-69.5517919462404,5.6400265962237928],[-69.769466386536592,5.759842491767027],[-70.38987037839442,6.4689897444738476],[-70.738140690192395,6.5666976694466843],[-71.117936876973729,6.4869521277131321],[-71.590673210604535,6.5287849425499562],[-71.921083417180554,6.4913501837536236],[-72.274613257724496,6.6108644432477588],[-72.39208222447084,6.714593381500447],[-72.542134355960897,6.9360834857238123],[-72.769816892881806,7.0849127053294119],[-72.923908835914048,7.3098502067133619],[-72.969202641093659,7.5463527723106552],[-72.945075081227557,7.9929989146626967],[-72.905394347298497,8.1613501631333936],[-73.10707245889877,8.3937735824078032],[-73.207158448982383,8.698146973485688],[-73.35304850114295,8.6943778076829883]]],"type":"Polygon"},"properties":{"alpha2":"VE"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-64.952925689342877,17.983534769737698],[-65.064925544775605,18.075637069380051],[-65.145786905209647,18.196004181188535],[-65.188708747637293,18.334512363831212],[-65.190081035864267,18.479512081215013],[-65.1013153764859,18.702667053014409],[-64.894308056486395,18.878449769836788],[-64.782879373751996,19.085227326481249],[-64.629423820427647,19.20074861165681],[-64.415160596090018,19.251181283055576],[-64.177085178508065,19.230712998196065],[-63.947613004008083,19.106609139866912],[-63.815785083956484,18.908235376568975],[-63.774132580325094,18.690497006102444],[-63.845770509905044,18.374804629302993],[-63.93691215859505,18.202235166086268],[-64.15417072538186,18.026788677474972],[-64.497874982017649,17.912335209863205],[-64.760430127834809,17.907246506725489],[-64.952925689342877,17.983534769737698]]],"type":"Polygon"},"properties":{"alpha2":"VG"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-65.338424538759753,17.980185652923222],[-65.467911265590459,18.13938913428569],[-65.521050742662055,18.323588303502817],[-65.484615512935306,18.559594003362466],[-65.378399737617471,18.719191456466692],[-65.219933336212563,18.827087413710245],[-64.996521543661459,18.881952401917008],[-64.51921631072149,18.833880481286243],[-64.308386166481242,18.709203256107436],[-64.182068526561835,18.499351846667707],[-64.163133430901638,18.303592815695907],[-64.22833290161735,18.103895789312666],[-64.1028003451746,17.894599234820433],[-64.081435581569878,17.749739038701215],[-64.102952671124427,17.604901390278282],[-64.227490167564341,17.396840942246129],[-64.389204109902096,17.288431701998768],[-64.629016455317071,17.209613548952788],[-65.022447818649255,17.220112303266429],[-65.195367630157051,17.306895014973104],[-65.344556127370637,17.496144777659666],[-65.388568803062896,17.684546966185867],[-65.338424538759753,17.980185652923222]]],"type":"Polygon"},"properties":{"alpha2":"VI"},"type":"Feature"}, +{"geometry":{"coordinates":[[[101.76065755265716,22.039655032054092],[101.67707083644743,22.162587060505732],[101.63332189087795,22.304661169714699],[101.63327795172174,22.453318563796905],[101.67694290302845,22.595418510219641],[102.10363614396806,23.089925972561353],[102.25888654402041,23.203475850676313],[102.39738446091177,23.245224498972036],[102.54203748346768,23.24557901723654],[102.93243301384231,23.088150689239626],[103.09391336716766,23.213785335739459],[103.27833956316599,23.266896119992449],[103.44954709281093,23.251648908412076],[103.64235668611276,23.281240566481767],[103.86648227158101,23.216408903460124],[104.09324335562485,23.297457531456185],[104.36887179480561,23.26554412812213],[104.47067704044321,23.445166300860379],[104.58797942487659,23.551850020768004],[105.09335773817801,23.810602054417213],[105.2890300309556,23.844735452337922],[105.5269393449294,23.777044442720733],[105.92241659367572,23.459731084490677],[106.1765796448875,23.468976410210402],[106.41733725668612,23.393975090559653],[106.64607981833014,23.397133710096124],[107.05972182479285,23.193225815461798],[107.19512050276622,23.057446828276575],[107.27699215613185,22.833064838154648],[107.26085340985323,22.641993447762115],[107.13095323009038,22.399080677072504],[107.28671318703161,22.311325429432589],[107.43962518203558,22.164125425310534],[107.57271341250363,22.119197427979767],[107.87872522567888,22.139560445829545],[108.0227333498422,22.092667033820909],[108.31870079519472,21.868508720421588],[108.4461523302958,21.667222947701561],[108.47122353442238,21.477613661934907],[108.42327023948413,21.292463106899657],[108.27227655246968,21.108178645859375],[108.06628814664465,21.01750180390027],[107.98614710096507,20.74318388312307],[107.91044280081557,20.613125802862022],[107.71075111790846,20.464189172387119],[107.3939676555314,20.403957267488739],[107.22162396752766,20.284937504999295],[107.02414820938883,20.247705585511795],[106.96508726796125,20.06559163480803],[106.87717200840227,19.941402860812261],[106.48780244010366,19.609926184070723],[106.34436604349885,19.525517100425112],[106.26938316965418,19.149223955925564],[106.20162108900666,18.977382386924667],[106.28894334977839,18.814647202999723],[106.90082757794256,18.243606551801225],[106.98354864611869,18.06872591008942],[106.99544674773007,17.861848994708907],[107.09780392977564,17.720869096527352],[107.47424469538331,17.407948554290876],[107.60455765781445,17.207525037337827],[108.05662007872442,16.837692622662026],[108.23100138283048,16.78834463137974],[108.38521557180722,16.68197363044634],[108.66088973871153,16.39782909997043],[108.95606269920691,15.922401893243704],[109.24202953849736,15.647668872571529],[109.56638744418724,14.849830095617374],[109.59086408044304,14.615066523059767],[109.7924216128805,13.959265278296936],[109.80167941168872,13.818629607720528],[109.75666052609998,13.61830232870904],[109.77095037674664,13.426056816897228],[109.91651959135062,13.040124314038705],[109.93971585228341,12.529619497745928],[109.873516084824,12.342694909715471],[109.73750953762044,12.194485218262646],[109.75538463706559,11.900182654384205],[109.63978237389973,11.484595185636085],[109.48760566712662,11.26904294686195],[109.42995728208226,11.105594846594325],[109.34345508186637,10.986460643616223],[109.18167751963499,10.876258799176787],[109.01479957744515,10.831865618430422],[108.43027349811307,10.460514366216966],[108.22290307698326,10.272395201512969],[107.98996712371388,10.211426884857721],[107.44707819299046,9.9343375619106542],[107.22671095326412,9.8828266675160634],[107.05018281579946,9.6132796373088762],[106.97851007091462,9.3652361808401565],[106.86123066098806,9.22211608576273],[107.00577440103675,9.1253315266029738],[107.11786835248462,8.9627149896602827],[107.15542690969122,8.8189821144913036],[107.1432251962807,8.6065483139476964],[107.04045141888268,8.3861182119439981],[106.84583338965083,8.2402562844648575],[106.52115306419027,8.2031329180047408],[106.29060608322865,8.2851195531964343],[106.18119778971695,8.3844573121679478],[106.10560545266746,8.5114374823758272],[106.07043226381117,8.6549680081614806],[106.07945834302529,8.8052360532548235],[105.85318505573674,8.7007658700087624],[105.67608884318599,8.4482208612099559],[105.29770449183997,8.1650022984421824],[104.76634607339834,8.097846743727775],[104.53075189658664,8.1592415882155507],[104.35208206380722,8.324623068230208],[104.27269843705125,8.5547799340420578],[104.28171434383999,8.7015287691748817],[104.33040643155003,8.8393438935196702],[104.34800111136154,9.653558632239978],[104.22289426472774,9.5732993709440724],[104.08032322826575,9.5334697296285356],[103.93233842656058,9.5371302254209436],[103.748949082149,9.6085574446511686],[103.58122153960274,9.7872845494131369],[103.50534351140281,9.9935938515477094],[103.37625704644482,10.210638067030096],[103.35009450187422,10.356387243989158],[103.36776813679728,10.503407460120334],[103.45630191378707,10.679249937227167],[103.56388190560705,10.781003790631667],[103.9708911217154,10.924922313991855],[104.22600876877675,10.886590592756033],[104.39279334079484,10.985400453920841],[104.55856212881379,11.020564175083678],[104.65292914383269,11.220379558486396],[104.80656967768302,11.35015738318854],[104.94884056986847,11.40161200234072],[105.18424000793303,11.398973562191909],[105.35210310212366,11.451041182211611],[105.34109979567185,11.650072942420381],[105.40234119795983,11.845356843705385],[105.70378072824801,12.15597110580998],[105.8379603123139,12.228630446622203],[106.02136354868142,12.256956218988364],[106.21978206624534,12.408930257878122],[106.66305361228059,12.517373168533553],[106.88672201051473,12.695798800028863],[107.0145110616362,12.756504475287782],[106.97632827031532,13.056341262860514],[107.07203943098214,13.446714972483194],[106.85208191137419,13.985097549988648],[106.83186402637176,14.141683952563087],[106.8788576068383,14.486680640735212],[107.00491938358385,14.739265156973495],[106.98207258867778,15.017492572770633],[107.02702047815387,15.189225599886276],[106.72757309339438,15.555936783905185],[106.66829336822033,15.756511390686642],[106.68108885539914,15.921662962658619],[106.39752009027127,16.065135207958768],[106.27847652948351,16.165649181294061],[106.0851216210222,16.457940691087259],[106.0237349995154,16.727278640200232],[105.90631413690515,16.869683357834262],[105.31687757370568,17.40686083411439],[105.09976180930137,17.778397286090605],[104.8121877330985,17.98302214668745],[104.60579640834179,18.311350029627363],[103.63188835762013,18.877927175843883],[103.45878965370292,19.05510333817659],[103.3956140525973,19.243711696478819],[103.42706994898684,19.489404757795352],[103.53160190177972,19.654887988791032],[103.55606654321055,19.827647326732222],[103.64804561118113,19.994996269463766],[103.79605432766806,20.115657934552541],[103.95023327906218,20.1675982208105],[103.89809831375102,20.272646589693817],[103.73885573635376,20.208227901286069],[103.55314457696454,20.204102516558713],[102.93251946967169,20.422416875762053],[102.79668883177402,20.497954789736593],[102.41225183864655,21.026902310048566],[102.35232258246623,21.284116732922723],[102.23370476528848,21.418582130544607],[102.15028894871867,21.618339781894665],[101.76065755265716,22.039655032054092]]],"type":"Polygon"},"properties":{"alpha2":"VN"},"type":"Feature"}, +{"geometry":{"coordinates":[[[166.02690134835089,-14.768013001069015],[166.11587718239988,-14.42940995280782],[166.23777006063565,-14.267352850374872],[166.41399728336646,-14.167031645327508],[166.70358414764488,-14.113569273838539],[166.83993625875124,-14.004074785893284],[166.93945592693444,-13.573472981761421],[167.01632484543893,-13.43765010981997],[167.13132448004569,-13.332142919473602],[167.34412382827892,-13.229672735781712],[167.59650923478688,-13.22416328709151],[167.7801590039478,-13.31007214681436],[167.94796136505911,-13.493014303858427],[168.0367671156564,-13.679805874060165],[168.05488103171191,-13.948720760956199],[168.13584199914851,-14.371929997577398],[168.19969100968075,-14.453876770976551],[168.49813762714132,-14.6426821268997],[168.62778760817454,-14.891320542680358],[168.68230078423611,-15.138571611992363],[168.68697492129951,-15.44922845978158],[168.76490899732994,-15.849039922693775],[168.76122684103711,-16.103467960634411],[168.79880769981244,-16.323941123794853],[168.93832124340122,-16.604728304430921],[168.97291620615241,-16.743433446636327],[168.89641375520125,-17.219257670691974],[169.04543129469735,-17.503864244693538],[169.08141080058562,-17.646056022835158],[169.02463345651796,-18.074456520009623],[169.18372662632825,-18.109247666363814],[169.41600456340873,-18.212821036855885],[169.6654719051042,-18.478482148439745],[169.80698009766289,-18.779887999942428],[169.83826195689801,-19.131392970342997],[169.95633625397699,-19.359110322268595],[170.02863052323562,-19.637701503207818],[170.26214441854353,-19.84737462907006],[170.36606216091636,-20.019146175063728],[170.39463367427416,-20.16743862213891],[170.3619763039909,-20.365524102068921],[170.25274699348884,-20.550961507853913],[170.14167066509123,-20.654295694443089],[170.0047166519557,-20.719560563510722],[169.80120078463668,-20.740098184205053],[169.65400962810548,-20.716059818997955],[169.45274974688229,-20.611858472278634],[169.29441044595578,-20.431392067686936],[169.20338908163669,-20.152779041639381],[168.86375681513798,-19.841808780140106],[168.74419358847285,-19.634399609360941],[168.70169957516126,-19.340395762145761],[168.50474441511545,-18.999477439303082],[168.49742035139477,-18.557644249754524],[168.53900792654994,-18.373173676623164],[168.04583573313542,-18.235322695931593],[167.75757131994212,-18.007746794226648],[167.68943047538988,-17.880864478967162],[167.66028305321126,-17.739822909200527],[167.72284292313503,-17.440375117847864],[167.70916612202859,-17.258988952498029],[167.57751061918393,-17.106683096401802],[167.29136852812468,-17.028300449107082],[167.11527226604821,-16.925512630063885],[167.0184478515348,-16.806535061727395],[166.92133728153792,-16.580946409274148],[166.74064009625263,-16.36356312577557],[166.62914862577642,-16.158316454395894],[166.34917049144025,-15.91691426232704],[166.15585447457656,-15.558756420714742],[166.13133209413613,-15.283920048727513],[166.03719818376743,-14.943838797631866],[166.02690134835089,-14.768013001069015]]],"type":"Polygon"},"properties":{"alpha2":"VU"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-176.65795715855509,-13.490703963839495],[-176.69490422285335,-13.288576621938745],[-176.65617541307881,-13.107554105692063],[-176.55234429070183,-12.919286544181826],[-176.38001715515642,-12.779144439619539],[-176.18931065293347,-12.723778310739586],[-175.99209355097642,-12.747011754585126],[-175.81947438444323,-12.84517998550858],[-175.69868167133632,-13.002798190921331],[-175.62940521080716,-13.235357221773148],[-175.65387608084791,-13.425938096137115],[-175.77700489738487,-13.653186577045076],[-175.94217871827871,-13.782607069058553],[-176.17012294451987,-13.840554885556905],[-176.36447999389827,-13.804036254432855],[-176.52947664570465,-13.695020760327999],[-176.65795715855509,-13.490703963839495]]],[[[-178.62888865925265,-14.502390139602873],[-178.69387965982966,-14.270655839568793],[-178.6623170899301,-14.080024860268392],[-178.54458017125137,-13.891952490075946],[-178.34293770937117,-13.75991976510444],[-178.15136602399707,-13.73261512720393],[-177.93357155536381,-13.788430810609022],[-177.70901402999783,-13.932113050678639],[-177.57324179829465,-14.134667338664952],[-177.54454143646021,-14.328345511184866],[-177.59436470215232,-14.530848968023093],[-177.70817617387661,-14.685966917778297],[-177.8721340705381,-14.786626927487436],[-178.11064341218142,-14.824660473232692],[-178.34209214709293,-14.776765641987453],[-178.50418997995519,-14.672908887119023],[-178.62888865925265,-14.502390139602873]]]],"type":"MultiPolygon"},"properties":{"alpha2":"WF"},"type":"Feature"}, +{"geometry":{"coordinates":[[[-173.2134840037937,-13.761617980555439],[-173.27354488467506,-13.57625676033298],[-173.26917327982989,-13.429774122551397],[-173.19816535372053,-13.248324499227863],[-173.10195365582823,-13.137782275217971],[-172.88475652035049,-13.029524196926916],[-172.28357897153876,-12.968777648331704],[-172.05408803521442,-13.051814243207039],[-171.74505970662989,-13.31303246922179],[-171.37773599365786,-13.434278248160503],[-171.14754967653784,-13.589598580776856],[-170.99514362055316,-13.799608362778484],[-170.9529923138027,-14.073697273466983],[-171.03838259378557,-14.322194495291761],[-171.21813043892973,-14.485948965676789],[-171.40482915264059,-14.543039399153187],[-171.7746850888449,-14.543920740602093],[-172.09219930123473,-14.466995240992368],[-172.3586435871859,-14.312393599906574],[-172.75427925005761,-14.240196293294451],[-173.10158658086243,-13.926803917664978],[-173.2134840037937,-13.761617980555439]]],"type":"Polygon"},"properties":{"alpha2":"WS"},"type":"Feature"}, +{"geometry":{"coordinates":[[[19.564748799210271,42.54990571361401],[19.53013525097937,42.713722185498824],[19.545173203711457,42.855053095505568],[19.599370119396383,42.986442822942486],[19.713207582984822,43.125340815952455],[19.874615151788852,43.226400631176098],[20.096957325372667,43.27881174240224],[20.226449999295465,43.502646179566099],[20.369958242099646,43.629541568749836],[20.781981834618239,43.76051616859462],[21.024898065495407,43.707677270596108],[21.760847375918363,43.179779709448617],[22.051548415872578,43.06562508349581],[22.202387655602653,42.883370184602349],[22.251475865447397,42.699910275274377],[22.186818346212121,42.391375172696925],[21.984754290732624,41.980314174092911],[21.887365442201929,41.867830383276321],[21.44948966192884,41.627951038633519],[21.148855303258905,41.612051041049895],[20.940309170106772,41.418442815243623],[20.713704202062818,41.35440796727751],[20.394810037314596,41.404177119626702],[20.192334871970324,41.542101602418626],[20.09334908975595,41.712097419701315],[20.066976856462144,41.871019994596416],[19.905208377046954,41.968461715966392],[19.652405473347144,42.263814299637318],[19.58887666905423,42.392311455409882],[19.564748799210271,42.54990571361401]]],"type":"Polygon"},"properties":{"alpha2":"XK"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[31.49170819616652,79.607515474880486],[31.344739036905906,79.627915285992117],[31.170259341214319,79.719956938013709],[31.044959579347051,79.872320974424866],[30.988343826990171,80.061290512976868],[30.996708928238988,80.209432712056866],[31.074237934433167,80.390827538648026],[31.215893501304208,80.528117488352791],[31.351239501490252,80.588924941027614],[33.078598286285406,80.728140876054781],[33.68590432603942,80.713364385326614],[33.826685357123978,80.674600819406777],[33.950263753445618,80.596817191832443],[34.046099803864742,80.48664748548758],[34.106019876757664,80.353487815840353],[34.121710824505897,80.159971564126906],[34.062505807630366,79.975067503881505],[33.937331698309222,79.826655281089302],[33.765062121327915,79.737112339630201],[32.5628513978032,79.620808128440032],[31.49170819616652,79.607515474880486]]],[[[10.176597214891638,78.515304843263408],[10.099111479562868,78.64079461414029],[10.058662359377369,78.832664630723031],[10.078894888354844,78.978754899205725],[10.226756057171313,79.350840399289879],[10.183193970081437,79.759835149902898],[10.204961057565814,79.903634423629569],[10.26719015980915,80.035086224894741],[10.364616187663707,80.143068660125451],[10.659650598204523,80.28075089584037],[11.09222217329364,80.269213697499353],[11.719016144096019,80.31955010577704],[12.009777603809686,80.278391638508296],[12.285845549442723,80.314818725668005],[12.699934650169148,80.276892663698902],[13.703350272250823,80.36020057428594],[14.236080901337138,80.244601062693363],[14.559911916990737,80.296717689932365],[14.880226533106605,80.264418833853341],[15.379039536189021,80.10863777595614],[15.538033216088651,80.157753199213815],[15.864846955214412,80.450663914167023],[16.15516557693773,80.540497940220007],[16.289835938021596,80.546839185977007],[16.81663127621086,80.453948639415799],[17.400853980165536,80.43626519680798],[17.549101302343942,80.485706014856603],[18.156860276966242,80.838472607041936],[18.341927756433805,80.854531820901258],[18.945906327163009,80.793754800598379],[19.70297303217734,80.976062339846251],[20.447986204325691,80.890479365987247],[21.013901546591917,80.744174841472642],[21.601788779589175,80.738758849802039],[21.85476936517324,80.688384698375415],[21.976276653242213,80.701739290311309],[22.337382486357399,80.888193216207682],[23.004252901167337,80.973348484267248],[23.364726691376564,80.932513508923918],[23.751577738334444,80.797171458994242],[24.318649080071886,80.859396349697903],[25.869924173508675,80.677441541038831],[26.975244503169119,80.645909409259076],[27.360129716458324,80.511128717722968],[27.553525266219303,80.350446962202895],[27.692885263585687,79.975501250776972],[27.686197879865571,79.621699836406066],[27.752014701334435,79.49721159593534],[27.857161220813818,79.425909869492443],[27.996112144680229,79.397819626818944],[28.831524851546558,79.470608470204368],[29.140625110743116,79.413539736552821],[29.79955871480286,79.393590500174071],[29.938794419657984,79.340509106761957],[30.05620409898556,79.248753000516814],[30.161173236351726,79.080744398273723],[30.192156231650021,78.934990256530085],[30.164602548886212,78.73881124441813],[30.094667488402781,78.60723121877497],[29.947478398628189,78.474638677210962],[29.809335320570693,78.418775399309695],[29.33336277442487,78.353627990154394],[28.569441265334842,78.38493638466781],[28.047750105234929,78.329705333696992],[27.507062906998133,78.408582516591522],[27.060834851278269,78.184289801209417],[26.757420302715715,78.148148467923235],[26.597469239636016,78.16514970648926],[26.194226341486466,78.297798422284757],[26.05287392712226,78.431197587061575],[25.850627401406346,78.778867167501957],[25.71794247717223,78.868217782606564],[25.580219647780044,78.889198588498331],[25.008758835006844,78.838014718414101],[24.828159691266162,78.741812275941257],[24.762322666285023,78.621760641079021],[24.759849777515612,78.477441791230035],[24.815893948388016,78.366383886473443],[24.906434214215185,78.287635341038978],[25.201630869438713,78.153690978424322],[25.352700825437807,77.963691015534977],[25.393132069715769,77.822794506215359],[25.390966027330837,77.676227729240807],[25.346388861064511,77.536587519773818],[25.228089670086451,77.381702438031965],[25.012467619672861,77.270215896786112],[24.323941474417115,77.175023179145114],[23.610461772195904,76.913776600440457],[22.591882894402371,76.767717236151938],[22.433543824343172,76.787555765649174],[21.949362065237985,76.984484995675516],[20.940525422109644,76.960593179425658],[20.696279220503321,77.017749504048609],[20.540431295850219,77.145402006221559],[20.430380595959818,77.335139952340484],[20.384548340659141,77.464051337283834],[20.349439238330941,77.755267988231637],[20.259303010057192,77.886565647833052],[19.961540412197021,78.047236269288817],[19.785609564003007,78.068456418919226],[19.549676274019831,77.975184465056557],[19.272456372795869,77.666720976490822],[18.853202193303698,77.492865193299409],[18.579395555582405,77.168313039727295],[18.46586267307773,77.083997881885381],[17.9012778841197,76.954007994319753],[17.744344521918698,76.814896473494656],[17.459346384300638,76.356337887727349],[17.351295562696869,76.251970229592089],[17.168635295464622,76.171229545913775],[16.721522444321494,76.081171166495224],[16.300434876117961,76.135843679733796],[15.418208218681826,76.403403926460257],[14.960970644551043,76.603934521900953],[14.027392875665575,76.83419556648613],[13.650253692138548,77.093466886520986],[13.566467627558493,77.205706774853397],[13.450911102916097,77.468207408960581],[13.336974705259482,77.596751801885276],[13.130066974656989,77.732729263184964],[12.57536307815676,77.862377173730891],[12.034145983835113,77.730678752607886],[11.825309309936152,77.745896439187206],[11.41088910083354,77.912839469601096],[10.909532829001392,78.011434330567056],[10.328392237569481,78.355006334163349],[10.176597214891638,78.515304843263408]]],[[[18.597140313619214,74.028272411546951],[18.473412870735515,74.106392340700864],[18.377599562896592,74.216986253257176],[18.300844635864667,74.446827404307626],[18.34117331470587,74.685766457630422],[18.418752435594843,74.809833711390809],[18.528927074319217,74.906128843331643],[18.709917961394112,74.977501160928171],[19.236900993808185,75.014090618559649],[19.458757078899488,74.933926058648424],[19.686119691975517,74.739418829881203],[19.762083503609453,74.563693188954858],[19.766441126716494,74.372300888488553],[19.671241893866213,74.153658645458222],[19.528166420701019,74.026460333616114],[19.267166760608571,73.882561719488535],[19.080459953048866,73.853594038222766],[18.940643051638617,73.878851928349548],[18.597140313619214,74.028272411546951]]],[[[-9.1467660143009084,70.35742005886037],[-9.2883399580756159,70.393067385618437],[-9.413581922801658,70.468089807150136],[-9.5118143228876537,70.576091237854854],[-9.5746622908356382,70.707863945141781],[-9.5967676820454582,70.852173560692449],[-9.5601315160320492,71.042800092412321],[-9.4861075815957268,71.168634787911955],[-9.3788908600584246,71.267723072202855],[-8.3976414447947629,71.636431425875088],[-7.9044459052639864,71.666991007230067],[-7.7616681106108079,71.615041028855089],[-7.6078272911238631,71.484180621713122],[-7.4970738535614068,71.247467471953371],[-7.4826716287781334,71.062588767748622],[-7.5781809367690718,70.805731530627213],[-7.7391122423676437,70.624637247347621],[-7.9179817962158481,70.548206595309892],[-8.8505531115732943,70.351333447414035],[-9.1467660143009084,70.35742005886037]]]],"type":"MultiPolygon"},"properties":{"alpha2":"XR"},"type":"Feature"}, +{"geometry":{"coordinates":[[[33.745205277470887,31.110983039112739],[33.699234039610786,31.354426105649303],[33.775769497191952,31.590053364445811],[34.177125037035168,31.984612379586856],[34.307684877118767,32.055114005923215],[34.462467756646035,32.08418336577364],[34.462654399996325,32.242165861580602],[34.554537087904919,32.543263679085996],[34.660787250790307,32.754574936304422],[34.771359643435389,32.865026811286157],[35.036412718908927,33.00911401663307],[35.288375734800638,33.02522849275541],[35.567122103016217,32.963425174309045],[35.823738015546674,32.814789292959169],[35.961062655110254,32.682142294347891],[36.038655310694431,32.507692695118656],[36.070308686278949,32.27909308244466],[36.032861521399745,32.028265348729867],[36.051512704700627,31.679785110543396],[35.89563679295474,31.251572321585822],[35.735068812143226,31.068212117661847],[35.19765723905055,30.875696812718818],[34.893919202272023,30.851580992455716],[34.671265991661421,30.911659624043907],[34.480272454839728,30.76702808193545],[34.241846665359247,30.708408002074673],[34.095354577318645,30.731420786721316],[33.922633582688476,30.826476579513521],[33.824809499079798,30.937921493178564],[33.745205277470887,31.110983039112739]]],"type":"Polygon"},"properties":{"alpha2":"XW"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[53.03630602098805,12.118871752006664],[52.928715390155617,12.217184786101644],[52.837661671852594,12.388246380329349],[52.816190786203755,12.532399762479525],[52.837202239671946,12.676620826610371],[52.960200863178876,12.884296944214292],[53.253838197191079,13.129020448559446],[53.385150416688731,13.192579249577967],[53.577969052842761,13.213713217115624],[53.863975541655236,13.140967971541102],[54.200669327083624,13.163648337321401],[54.349884248787042,13.136678149357985],[54.802725048205232,12.958510166848477],[54.907470701827165,12.856829377861859],[54.993362585006295,12.682767595832042],[54.997927514610552,12.441054568904937],[54.888554374630438,12.225454146318514],[54.735432770205612,12.106170057615035],[54.269397305745301,11.880825327731829],[53.62390088267454,11.8283315499121],[53.361664081571305,11.902146464228867],[53.03630602098805,12.118871752006664]]],[[[42.221419812091455,13.84664513791564],[42.195210024183744,14.036170058926039],[42.245109357792956,14.227293532480688],[42.329412002301567,14.349637035238622],[42.49794226962387,14.490875863419838],[42.428925370309379,14.783071351274721],[42.145624227351433,15.024972522930412],[42.078411051404764,15.151825490187555],[42.050012896928031,15.292548022658709],[42.096277062654863,15.567345349258867],[42.259594100027982,15.803477728720875],[42.32584530692975,16.054150981278642],[42.310782956331785,16.477042090136202],[42.421745036216628,16.699179792867405],[42.628621190550056,16.871035993854015],[42.637044304306464,17.162699597736804],[42.714493171514242,17.510195281269894],[42.774939304436181,17.636296715026809],[42.904985121173667,17.76924703854278],[43.16639932198202,17.949082232901898],[43.29552846636475,18.000230388442755],[43.433745929211589,18.0138833261644],[43.659544738554153,17.983113343870208],[43.903497623926505,17.864336098855237],[44.33955280575497,17.913759673651487],[45.13548726739581,17.927410369540656],[45.639266707998729,17.79470801251804],[46.312996556670427,17.734001011084302],[46.783021291576468,17.762281205838164],[46.963816488134789,17.706019935993275],[47.099830612088034,17.598374559743966],[47.196264541281586,17.768966333015609],[47.864067086827056,18.550372887412554],[48.870771931900023,19.051297378999791],[51.902802142758183,19.490567980492159],[52.051237180646524,19.489279530936969],[52.277033241157568,19.392216135069127],[52.406841554239485,19.243564858733116],[53.117360197979615,17.657921355365094],[53.254609952277008,17.47674346405956],[53.571482072876577,16.765396641269714],[53.569489608298021,16.523646204641402],[53.51278550104815,16.389125839037838],[53.419643582946186,16.276717353444408],[53.29800367515611,16.196002934819592],[52.811953954363815,16.020628705749857],[52.727482838370449,15.962124253143713],[52.695853713808162,15.896891069585353],[52.717269670258524,15.655304700074577],[52.678246005396389,15.461998624154919],[52.601147759286718,15.335278990959946],[52.44741301933805,15.211764962851214],[51.828120735360805,14.890677623057913],[51.49957520585432,14.759220410189426],[50.711418312583831,14.56659467605029],[50.307753659611706,14.371593595578291],[49.995130171298911,14.330938676478285],[49.565581557815833,14.183950777954957],[49.428584102484038,14.1063711392569],[49.27610056180756,13.907897769733218],[48.988013070289711,13.666020345361853],[48.826334392811205,13.576063040069958],[48.309108093792354,13.498586095259062],[48.071337272314842,13.519340023979854],[47.909383854244325,13.434724923425701],[47.606146066602705,13.202971140816979],[46.888373842502681,12.975369359647642],[45.908582502722972,12.865390422715791],[45.696738309719379,12.66974954388483],[45.472593167104264,12.564617312287174],[45.349571348383712,12.42457590038385],[45.176504655588843,12.335468609619411],[44.802866177861816,12.266312944242536],[44.674413055754009,12.270659348362743],[44.494949311891752,12.188337123314746],[44.040663467117135,12.109133571465135],[43.887691693301456,12.122078257084329],[43.617726979113073,12.215171273922792],[43.384839082442333,12.209851350556656],[43.243723233636558,12.262757873988608],[43.124782344671154,12.355309740193171],[43.038822329952474,12.47909850735479],[42.983091232710898,12.691317190602282],[42.740774141059774,13.177177841414657],[42.467620696416496,13.226382154632487],[42.28170677575168,13.385817099341939],[42.193814032630158,13.614417959353535],[42.221419812091455,13.84664513791564]]]],"type":"MultiPolygon"},"properties":{"alpha2":"YE"},"type":"Feature"}, +{"geometry":{"coordinates":[[[44.570959583103509,-12.866280163754928],[44.547432021061717,-12.633834592916422],[44.607965453626697,-12.4545920957036],[44.780912628970313,-12.26222478570431],[44.998798891922625,-12.162212101097374],[45.191268718009063,-12.163262799828802],[45.36908127148498,-12.236940705254653],[45.62666172793697,-12.457375290298433],[45.717823730470322,-12.681370467288463],[45.69941251918172,-12.942143018691814],[45.612338391380135,-13.227800306572869],[45.477856322329046,-13.378191451383687],[45.24495178765639,-13.472328918197197],[44.994054630944788,-13.469212340105976],[44.756420266333372,-13.33271426740639],[44.622872208542397,-13.142117813061061],[44.570959583103509,-12.866280163754928]]],"type":"Polygon"},"properties":{"alpha2":"YT"},"type":"Feature"}, +{"geometry":{"coordinates":[[[[37.155141812035005,-47.154222003183769],[37.090550700028594,-46.922493380708275],[37.141651619464952,-46.687421344393087],[37.353318757410833,-46.450023688435884],[37.523560618700635,-46.350997073858906],[37.768037261119595,-46.331202174889107],[38.111378588401308,-46.454649846947355],[38.26676750960042,-46.575977760652528],[38.363178143615642,-46.747940457550015],[38.385622038427044,-46.943803805476996],[38.33060996025916,-47.133117964300695],[38.206694346325556,-47.286451261724743],[37.964479444518851,-47.439443448856494],[37.773493121586718,-47.461011256160859],[37.429533096339135,-47.411796628283057],[37.2662319657517,-47.307474823697355],[37.155141812035005,-47.154222003183769]]],[[[16.04696607964836,-28.916293609080682],[15.978446036062559,-28.789629843087553],[15.94886929936326,-28.648690414130357],[15.973758144739064,-28.458822322747128],[16.038661476201852,-28.330267806865375],[16.137512953315056,-28.225543924865619],[16.344360495295717,-28.089211604785387],[16.441177059673866,-27.88042485704122],[16.56167034509977,-27.738866270104293],[16.955967744105966,-27.541478184965921],[17.147937321777956,-27.539807359415338],[17.326386366521504,-27.610596121515673],[17.741281883796631,-27.947846444861153],[17.820541267035985,-28.078407940909113],[17.861165815293042,-28.278998743200653],[18.182705399256978,-28.369753956942859],[18.630369982874551,-28.356182194154311],[18.967912457357095,-28.401179701194405],[19.255687496232582,-28.163508185626405],[19.480482054565599,-28.053292205546736],[19.483135290607468,-24.727707433624396],[19.566319210568267,-24.497325519030806],[19.748943238912251,-24.334096062794288],[19.937742653580713,-24.278996366556122],[20.133186286347666,-24.30097321609259],[20.713314917796435,-24.691943032844382],[20.879427439688595,-24.928872671584493],[21.262117773127486,-25.742735051682203],[21.314778763875797,-26.173208617487177],[21.279970808526933,-26.346540043871666],[21.458738493218394,-26.343311332173393],[21.57981773732395,-26.247595020603367],[21.751307531221748,-26.178470681319709],[21.892615037004578,-26.009192455545545],[22.193028283941164,-25.801645012477319],[22.463609358354162,-25.179716737107505],[22.736055015038456,-24.927727022075175],[22.901228857884345,-24.837385180649822],[23.209787516832943,-24.77000657042203],[23.487771300200965,-24.801419495458092],[23.647085551624272,-24.863244476346608],[24.072069858716571,-25.119098477066832],[24.339186372787236,-25.155037955554768],[24.547798601322896,-25.269871154885259],[24.737872365834978,-25.306501093855264],[25.191127227880081,-25.238185708029317],[25.516501291470568,-24.44185506852871],[25.7178565376345,-24.28935350159875],[26.171690439763854,-24.150852936242995],[26.365760269951387,-23.981426158158225],[26.554167216538602,-23.454776684711565],[26.838656029948734,-23.130727758899837],[27.264603040851849,-22.910621328070651],[27.53668539967035,-22.701312488914198],[27.872496833748144,-22.325125663375019],[28.124684758620916,-22.164461504946541],[28.587791330741219,-22.028446997138527],[28.835132611186289,-21.80973629821068],[28.969244088865743,-21.740094734830411],[29.600751689331354,-21.65047301743579],[29.745579592518894,-21.653424221493317],[30.481353905260296,-21.824632205820162],[31.023347712729851,-21.801622109215167],[31.480219836705192,-21.940765222916401],[31.678554141183131,-22.090430646565856],[31.769340631622711,-22.268110481895032],[32.012396065461601,-23.141253563589068],[32.03240733781287,-23.30213067450271],[32.253592552330929,-23.683191112932136],[32.464258837121669,-24.322239417538679],[32.482876482162453,-25.667580254199191],[32.437264631496831,-25.843790828317793],[32.430132583225124,-26.0898703105034],[32.36946951727586,-26.226454925164674],[32.261953434057496,-26.346041077624221],[32.924119144604688,-26.351007747538695],[33.065917524218747,-26.383084367787166],[33.192494305499629,-26.454596663711069],[33.293148337991902,-26.559498788431128],[33.381488133140323,-26.783481905113348],[33.332719467759702,-27.206009832127283],[33.141521412809752,-27.736665630925174],[33.010015469750243,-28.354327246394512],[32.684741731457308,-28.922207369733997],[32.310036858057174,-29.251655774136218],[32.04008053468165,-29.383344676446498],[31.718490891693275,-29.704225726073791],[30.89179117168533,-30.986468043745575],[30.328130767736894,-31.671806227528521],[29.811697092877257,-32.053576381740555],[29.201552950958426,-32.654732093900861],[28.769841289851357,-33.00849681562984],[27.647637230490727,-33.772214744543859],[27.280423349327258,-33.977832114140398],[26.563403680500357,-34.241256533113379],[26.42799270180922,-34.258220918507412],[26.089531422941761,-34.225146429706356],[25.938715057701586,-34.410375863998233],[25.657694228130239,-34.528028531231456],[25.213559968374373,-34.494454014154748],[24.986257284491238,-34.642655068263423],[24.612942365722439,-34.674236782163561],[23.69292016710531,-34.502669985138006],[23.269674245020163,-34.580900590931918],[22.590912098431133,-34.516199654919767],[22.432575382977845,-34.54900247462627],[22.019448957624345,-34.816093297586427],[21.878447748379525,-34.864336596165352],[21.335330458693885,-34.908051927938175],[21.025366947088589,-34.873207622894896],[20.720544833931509,-34.937162813538365],[20.245841588224039,-35.23184670512088],[20.070383011223281,-35.282992802638688],[19.462632406485763,-35.222379706646869],[19.261226535723676,-35.113399054788161],[19.033146035575342,-35.038569548306015],[18.866339132163485,-34.866019105946705],[18.62444008084482,-34.818566895247578],[18.458122458311649,-34.846468062643922],[18.31172026744693,-34.823505092787101],[18.139066839934763,-34.728591215638097],[17.981888450699085,-34.552887134111096],[17.837934388900266,-34.139325932186956],[17.840759037328414,-33.990362416445812],[17.901314534801536,-33.805715115481611],[17.553537552647729,-33.391106715514816],[17.354755728352487,-32.885750523529964],[17.359175075217248,-32.739369955868085],[17.430154368847148,-32.558051937645196],[17.64063417447613,-32.328563670048915],[17.813769633326881,-32.232415552744484],[17.750103922841209,-31.962221367370557],[17.277482374387532,-31.319237257746149],[16.916178955654704,-30.698512190254391],[16.487116918248649,-29.598309732825737],[16.309594041283653,-29.268744656638539],[16.04696607964836,-28.916293609080682]],[[31.669788425804352,-26.438516685182641],[31.460546599601646,-26.342965159605804],[31.319241817484084,-26.581866747291016],[31.403464729404611,-26.714979403434292],[31.496646059675101,-26.767377181156252],[31.543581109655367,-26.602526822667308],[31.669788425804352,-26.438516685182641]],[[27.718445980053914,-29.800172672379684],[27.857861938630126,-30.030828433828198],[28.020959392033291,-29.812798993516864],[28.138126757169413,-29.717107806555134],[28.278550092006942,-29.660838083362343],[28.651557935620975,-29.586924156950399],[28.78702749432372,-29.349835822022047],[28.503544499869225,-29.139283585213857],[28.456395410078738,-29.154262545402808],[28.053020938872315,-29.369162008191726],[27.718445980053914,-29.800172672379684]]]],"type":"MultiPolygon"},"properties":{"alpha2":"ZA"},"type":"Feature"}, +{"geometry":{"coordinates":[[[21.479136483664362,-13.001316604988043],[21.50063670187739,-12.856161921239075],[21.563349419272104,-12.723499731168051],[21.701281975876114,-12.585518199781577],[21.8339218945593,-12.522758391231935],[23.433901170330074,-12.501010140259217],[23.492427453827631,-12.307744779666166],[23.472091313635143,-11.606162268741581],[23.528025679050902,-11.345013340501653],[23.473049207466225,-10.793074139844773],[23.51789275843473,-10.651810486460844],[23.60217727600218,-10.529898698969799],[23.718497062655803,-10.438050612088821],[23.856631627378164,-10.384336499792289],[24.053622851793033,-10.379655656694835],[24.302996373737344,-10.445069974029687],[24.62952458027501,-10.679488661214416],[24.747453138239624,-10.81357533870281],[25.236110734419206,-10.71540391161604],[25.475594624478763,-10.7488794021532],[25.631972330833641,-10.847019241481826],[25.755610398555667,-10.993233667698817],[25.806978645334979,-11.127760819323409],[25.814402695107184,-11.300922014375423],[26.149494375521513,-11.403253993394282],[26.570294547540257,-11.462246977017855],[26.679990808788492,-11.275729251269],[26.844533250059968,-11.158587718261851],[27.100369776127874,-11.082947383500192],[27.340769208210894,-11.113645065061986],[27.538493603207005,-11.253785345575942],[27.663430260645452,-11.483796340786585],[27.801751858551732,-11.617803594242268],[27.907332943841027,-11.785979379453838],[27.940999360777681,-11.792696080597613],[27.865105788206471,-11.570157950253027],[27.860713011955493,-11.426009826725139],[28.140623458778602,-10.513046880745629],[28.111718541572909,-9.8120822380807571],[27.912494550714417,-9.3319148845289046],[27.91964471447,-9.0893605877862989],[28.040150744009782,-8.8787376817324031],[28.404014364643508,-8.5641115836296162],[28.420191504019684,-8.339692590558661],[28.516122494557088,-8.1633542821480471],[28.673678212966944,-8.0389591473759889],[28.902814166539454,-7.9697521817277606],[30.675700269646725,-7.6996177759094087],[30.826358184851944,-7.699621828890713],[30.970175684074491,-7.7445031651874743],[31.128962794395296,-7.8665656135255606],[31.262856592701574,-8.1167561238598935],[31.667967863386686,-8.2043566390339038],[31.901623575496686,-8.4164698954511756],[32.110067115561826,-8.475157053223155],[32.277744778867785,-8.6032398189506125],[32.662273577680999,-8.7122194644755453],[33.193244007328893,-8.9890507913973909],[33.348762523798023,-9.1500531610913249],[33.522871046723829,-9.2483730071409784],[33.792256301136753,-9.627699742836036],[33.850463925043393,-9.8497706621719114],[33.986045986155823,-10.032796651923224],[34.155753039549367,-10.478904048738647],[34.155251685274017,-10.630709272614943],[34.10921770326491,-10.77536731716355],[33.868993119472549,-11.058724698360445],[33.871333935240152,-11.248097455312783],[33.77388664498973,-11.514338913253891],[33.803637772889303,-11.677575725840184],[33.788500596101862,-11.93168949111192],[33.922327519933759,-12.062091059986844],[33.987961031627691,-12.194617634555339],[34.011956269274194,-12.340546656324227],[33.992214028624858,-12.487111641086209],[33.930461443874172,-12.621490467918214],[33.752219050221235,-12.84227234647102],[33.481355221853853,-12.993341889123867],[33.461115795044783,-13.303680007262875],[33.387461360399008,-13.502920488017141],[33.549584037306722,-13.643397052250645],[33.687070814914158,-13.894487830111947],[33.684839062933179,-14.140813905600368],[33.56525982759522,-14.356179461477858],[33.357358106117253,-14.488309663887943],[30.850422693548595,-15.311425652021077],[30.89498359774398,-15.613726194952012],[30.882839375285755,-15.756074513867068],[30.830934811869628,-15.889177640553253],[30.707631334528454,-16.033706012041215],[30.584364806196056,-16.105927124120267],[30.44570602876097,-16.140340340727629],[29.667463851794384,-16.169747965192222],[29.346127984712364,-16.326754810720523],[29.296474535580224,-16.608638656194543],[29.084691046343618,-16.912445159319844],[28.232609956008289,-17.313628995538068],[27.367701110142953,-18.318096538073053],[27.239045600844886,-18.40629085745174],[26.900529137384972,-18.526437155311697],[26.709707356786698,-18.536275192090763],[26.26029443628741,-18.430158162525778],[25.740531020410121,-18.436366613624557],[25.544712552541245,-18.342594943341943],[25.36951484891609,-18.338001469992136],[25.087520656563477,-18.263037326564017],[24.769398121956545,-18.033029255239853],[24.672873074481473,-18.014945480440833],[24.291949835723386,-17.986577370726078],[23.418151000748814,-18.138968821630886],[23.219893610784041,-18.113796707849851],[23.047331149362577,-18.012990830560806],[22.13631172493757,-17.19786341134073],[21.7879410101188,-16.940874442148875],[21.682746475310694,-16.772552604767309],[21.481651472043701,-15.993869093596739],[21.479136483664362,-13.001316604988043]]],"type":"Polygon"},"properties":{"alpha2":"ZM"},"type":"Feature"}, +{"geometry":{"coordinates":[[[24.813089239673854,-18.199637083005996],[24.735583117779704,-18.020777180053493],[24.732360167204011,-17.825872905075144],[24.836601020312084,-17.526161241520608],[25.007311701983657,-17.361745952498225],[25.234559215371096,-17.294417713546725],[25.501394903229766,-17.338195907604572],[25.667633842260429,-17.325182127323458],[25.98947554720505,-17.434322401458545],[26.127122269860838,-17.412101124932793],[26.373412118818518,-17.430874154188281],[26.733967659198818,-17.516178791703339],[27.04758435477245,-17.196103114685393],[27.380146453383972,-16.731563048499456],[27.59088998203233,-16.531069042501404],[27.722855793850623,-16.442329340925021],[28.36037311502723,-16.148184243575688],[28.428554028388273,-15.815248565042767],[28.547537690073504,-15.648339266536238],[28.652126721854913,-15.561255278523811],[29.386756469344153,-15.2069687447362],[29.728412048429096,-15.14490418365601],[30.494355397647539,-15.153089477538325],[30.717968584823421,-15.260864664501335],[30.816874478999331,-15.373562899474123],[30.87498755199481,-15.501644300156558],[31.413015675848893,-15.556163985378694],[31.657227064483259,-15.698386448162738],[31.917564956423586,-15.770510121851249],[32.145776020519314,-15.938402443982394],[32.417827707585921,-15.979933985571893],[32.843191689509453,-16.134771238209485],[32.959250845503057,-16.207865056360475],[33.169784148543449,-16.264470232135636],[33.292271650410711,-16.350088201752097],[33.383985097576456,-16.468080864421879],[33.447761804794595,-16.707133798388544],[33.400290534057923,-16.944901122844517],[33.467991519006716,-17.216023119096683],[33.456126810773874,-18.045771435071757],[33.492215033755862,-18.389257196270528],[33.325474459887332,-18.955585454659762],[33.348342982905962,-19.147590721572364],[33.313392913323355,-19.361717475628456],[33.489469900960152,-19.744323269800798],[33.506397549357743,-19.885906554034747],[33.481196228482396,-20.091791846985828],[33.104123822854369,-20.769288736194511],[32.979236682357516,-20.912336472866702],[32.916614758987933,-21.186829558378086],[32.918624250249806,-21.400645302496883],[32.840981536038484,-21.581028734685873],[31.559859640578406,-22.821318476778124],[31.38105518016722,-22.893003940664897],[31.236306717418877,-22.899095086917725],[30.874586917273877,-22.798112010579619],[30.444837365908857,-22.828576559908697],[30.126573109617432,-22.787052122053812],[29.657636127299668,-22.660190351854691],[29.279356874416397,-22.686299840400828],[28.789665553336274,-22.45929325442026],[28.678516693521594,-22.357546246212134],[28.592809161376,-22.20215821418579],[27.850810869135774,-22.026481654403934],[27.673636439843392,-21.919972240761012],[27.233439367397612,-21.308444785219667],[27.179912095616384,-21.16462388972095],[27.177176513881527,-20.96757938912862],[27.042025097043997,-20.917700292712038],[26.894716261602035,-20.795995724709297],[26.820364322173553,-20.673028417249007],[26.777846881857776,-20.478399702785584],[26.393109728716762,-20.303122129664192],[25.770951478273197,-19.841671023481744],[25.501524415383457,-19.301678625248524],[25.452162461256908,-19.123629137906669],[24.813089239673854,-18.199637083005996]]],"type":"Polygon"},"properties":{"alpha2":"ZW"},"type":"Feature"} +]}
\ No newline at end of file diff --git a/toolkit/modules/tests/xpcshell/regions/world.geojson b/toolkit/modules/tests/xpcshell/regions/world.geojson new file mode 100644 index 0000000000..78d048f0b0 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/regions/world.geojson @@ -0,0 +1,249 @@ +{"type": "FeatureCollection", "features": [ +{"geometry":{"coordinates":[[[1.4150760359090115,42.548401330981783],[1.4285323868084139,42.595761527815462],[1.4590855848035782,42.621376365749718],[1.5014077383801638,42.642491850986687],[1.5729289405398166,42.633981918053358],[1.7097601461476049,42.604249854333339],[1.7392334968388081,42.575829051463771],[1.7396471978652144,42.571160819623373],[1.7400197463512972,42.556811875011469],[1.7236200174983376,42.535402583714202],[1.7128613813379541,42.517689912128411],[1.7058856739713015,42.50352774273064],[1.6787755171922876,42.49591214439608],[1.6620331460783604,42.489391040935487],[1.5898653587418383,42.457478032113301],[1.5827971184582803,42.454968626832844],[1.5396820560024453,42.443224598626585],[1.5283430516513503,42.440832515708564],[1.4862188790470032,42.434801675010362],[1.4489409391279213,42.437674383190405],[1.4283592569449723,42.461407784301933],[1.4284626744502926,42.483811095025679],[1.4266795913677073,42.503561591181338],[1.4229493388547854,42.52303841586771],[1.4150760359090115,42.548401330981783]]],"type":"Polygon"},"properties":{"alpha2":"AD","radius":16000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[51.568620255944964,24.286098419440652],[51.605473188225297,24.338036306197587],[51.767364638968743,24.254204686406734],[51.797210173364775,24.098040734393912],[51.873616899162833,24.011057896657821],[52.093753309856559,23.972757813022028],[52.507987310755311,24.115426073657698],[52.579568266938296,24.211165652758371],[52.58382650295038,24.352191109475392],[52.629312937365611,24.376446198469502],[52.715068022001063,24.209330772440982],[52.833608052077729,24.151966407546219],[53.074137044308024,24.162340789979794],[53.191108065987969,24.290729874229019],[53.412396099845502,24.410701556345021],[53.571486903157208,24.218715163476698],[53.833750689526489,24.258688763909269],[53.977754426804104,24.15784919077241],[54.095190655251351,24.152735738073495],[54.304353714251647,24.272547882697921],[54.335058752232186,24.470963856402189],[54.571344293025334,24.558308664156208],[54.74701538503345,24.810168400410237],[55.097649495405498,25.042225166859357],[55.523100690117943,25.497870141775351],[55.938971960975664,25.796204848604575],[56.080614281183493,26.062430695027874],[56.167270155148294,26.047311645535068],[56.182650137423018,25.74287217726301],[56.363257589267434,25.569305313896297],[56.387688561716658,24.979332628019598],[56.106441200081541,24.748867536130085],[55.930632480779323,24.746635019632233],[55.812634111731448,24.653473752396227],[55.783201488128043,24.436537505725649],[55.984954238393186,24.063580194583796],[55.670314131716673,24.01087836640075],[55.562863056625858,23.928134584897055],[55.208151179333647,23.051046427258299],[55.185569341068998,22.70420771186366],[55.119296155130137,22.624166108670387],[52.555188748792645,22.933016489170996],[51.592801453108393,24.078992584046521],[51.568620255944964,24.286098419440652]]],"type":"Polygon"},"properties":{"alpha2":"AE","radius":279000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[60.485973212925884,34.094724595751003],[60.709924477677028,34.382728462480799],[60.739600445038384,34.544525555844629],[60.897493916772476,34.623786527989616],[61.046988330564481,34.818212087679989],[61.129647832079648,35.082184503515002],[61.100251274022369,35.272165530595089],[61.206486483128117,35.378878607653824],[61.2622998261132,35.619391234408262],[61.344639218394043,35.629258242233263],[61.621187243502092,35.433168334908267],[61.98382157197495,35.443503163468094],[62.313012898800416,35.234633599357011],[62.702212350614658,35.261866705599019],[63.045587847762953,35.467982303091851],[63.131414849744957,35.679454555181124],[63.130108476964175,35.845994971311121],[63.991446221451305,36.032569379575513],[64.481442565983727,36.318512433946644],[64.56763951029582,36.437030377662602],[64.816497505608552,37.13188945167942],[65.089683984817498,37.237739731783378],[65.501226508282841,37.264409645053412],[65.765032546227303,37.568877335446494],[66.10880706905327,37.416038092757901],[66.445709756084639,37.349770464941514],[66.833288845935385,37.370448805353639],[67.068739559922719,37.334564566455072],[67.261112432777779,37.221666388798809],[67.51721626335231,37.266379130404459],[67.699939245549288,37.22696599010672],[67.921228275774766,36.999468031939138],[68.072821072106521,36.962329555607496],[68.387043139200628,37.137191247291497],[68.906959052219491,37.332864071721211],[69.140984229914849,37.211123693816155],[69.277042030960146,37.218104074185547],[69.367086561787886,37.293441406882508],[69.420333093135923,37.486592150249386],[69.496369031811682,37.554387435126905],[69.816991650658039,37.60925941204561],[70.115963891103775,37.600461465153408],[70.216437210288717,37.713021399654181],[70.214840086722901,37.924262166555984],[70.427168481265511,38.086304748410001],[70.62037186305912,38.33776348209215],[70.878907655609282,38.456189561158837],[71.255702095673215,38.306822984362071],[71.379010051104856,38.00054011534278],[71.581996753599256,37.909999279765422],[71.438236906986447,37.137745376977811],[71.541186807800088,36.827718037518622],[71.671066657215945,36.698736106850511],[71.811495399031884,36.700493414534314],[72.157770423031607,36.902244493837244],[72.630347860896293,37.034161254950497],[72.895607216512332,37.267351685253225],[73.382963700044243,37.461990868294706],[73.720419676698171,37.418541482356488],[73.792481344140342,37.318733526093318],[73.90213460344998,37.280094035366957],[74.130676957461517,37.327631986847919],[74.259760133819128,37.415173351549058],[74.659328867094871,37.394251836562972],[74.890715794325132,37.231775752456365],[74.647241385033539,37.225123206383728],[74.56322152803817,37.142070517127223],[74.541139912349436,37.022327124353104],[74.038817523920372,36.825927193301048],[73.74205556155151,36.887980239347797],[72.614767836643381,36.826398251730893],[72.203080068265692,36.717728254834398],[71.7725567297895,36.432035923483731],[71.556033780846874,36.385671772915344],[71.345387234347854,36.192961481875265],[71.299373493862959,36.049346330469369],[71.571721603716796,35.54669151657064],[71.620220947440714,35.183013658666887],[71.060467167564624,34.588917090598002],[71.088658517633746,34.115559298425389],[71.051377734795793,34.049930889185944],[70.848394507160748,33.982063041471797],[70.255242993199033,33.966966021042687],[70.120301011228335,33.896808279498117],[70.080550449194035,33.713687791470136],[70.28389608479209,33.368990665904875],[70.260925289687876,33.289181877686239],[69.920054117584442,33.112724342670667],[69.62874736034901,33.077870528269926],[69.513630309904585,33.004073890500905],[69.403276427539481,32.678498770909243],[69.253215120437829,32.449770326603108],[69.279029107272905,31.936911057181522],[69.18875354422191,31.840051810672126],[68.868928337510141,31.634431043847822],[68.782429935293663,31.646662607772729],[68.597615222244684,31.782124273967501],[68.181331510610775,31.781907264108092],[67.795036931152524,31.564050276296115],[67.734979820219053,31.461498586567796],[67.737624832701044,31.344101370564911],[67.596285559180927,31.277928830342422],[67.287308436935774,31.217991602583226],[66.974117516628283,31.303028774440335],[66.829786515120603,31.262807057791861],[66.35240912234265,30.814823948921006],[66.291649633784715,30.607517922721055],[66.283350513528873,29.916543508043063],[66.173020460922331,29.834535140590894],[65.178584093420199,29.577233792170865],[64.406409976401477,29.546340757382591],[64.098655984670501,29.392197983857756],[63.559221897144546,29.496621798920476],[62.476569380012656,29.408517770604636],[60.843777064116722,29.858803203383637],[61.7642087796889,30.812436760181203],[61.813091846353871,31.062580426714142],[61.754254264231271,31.273231419555344],[61.64617785798594,31.375855955668559],[60.820900726495729,31.495332837881069],[60.78765479694809,31.88416622422772],[60.826194165970946,32.248942491840836],[60.561824124055953,33.063989431819628],[60.560808838232632,33.13774821604968],[60.700687514980594,33.316064124686449],[60.714559927396571,33.43006409729275],[60.663441928319571,33.532900801590586],[60.509318292170391,33.643376250157402],[60.485973212925884,34.094724595751003]]],"type":"Polygon"},"properties":{"alpha2":"AF","radius":836000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-61.88683733142296,17.098055222036319],[-61.820684591014285,17.165498333193547],[-61.817178042111308,17.168725548001024],[-61.81578439605498,17.168954890684635],[-61.805549560185611,17.164402501120239],[-61.738697811849541,17.13829632679413],[-61.722182868235635,17.122744970949338],[-61.708911341034778,17.112436121490834],[-61.686275910977209,17.098221051301191],[-61.6866871134891,17.069867460365721],[-61.695155231505815,17.049073028086831],[-61.707584092516598,17.039816767800204],[-61.72405050615945,17.025155171550843],[-61.74823570871515,16.997399941528499],[-61.752809597577809,16.997844035717815],[-61.85949899051618,17.013554055573376],[-61.881824271759193,17.06319496524122],[-61.88683733142296,17.098055222036319]]],[[[-61.762098221937961,17.549000862513303],[-61.843603449996685,17.596293810161551],[-61.868486326638006,17.685467174677637],[-61.865924817069107,17.704153230682323],[-61.852422385317077,17.713775497146489],[-61.82965174951957,17.70346101958841],[-61.808181181588182,17.696425299661279],[-61.776870246837035,17.690264658521322],[-61.749866916907514,17.661222460440232],[-61.747324223248974,17.575002111164025],[-61.762098221937961,17.549000862513303]]]],"type":"MultiPolygon"},"properties":{"alpha2":"AG","radius":12000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-63.159693034133419,18.171729816333915],[-63.158879272701597,18.176271768462762],[-63.15437770770766,18.195723786538789],[-63.153132145720072,18.200150091929846],[-63.149203037416832,18.202538755158994],[-63.030453513291974,18.267311222863462],[-63.025974059104556,18.269469066955217],[-63.021009578883664,18.269193863250919],[-62.984391040569378,18.265304828161806],[-62.979926174989245,18.264611385475778],[-62.981758866385213,18.260481357672674],[-62.998980802579744,18.226231863795974],[-63.001392247397064,18.22197851065356],[-63.005973113271644,18.220269198832607],[-63.15523049379609,18.172903388038876],[-63.159693034133419,18.171729816333915]]],"type":"Polygon"},"properties":{"alpha2":"AI","radius":11000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[19.28098679744744,42.17260689889445],[19.703401742257608,42.647691196352717],[19.848893647607262,42.522115807734941],[20.09963733130294,42.52687505495431],[20.240836171172116,42.356940641568443],[20.524447790854115,42.166671954200112],[20.581197010353492,41.91746560300335],[20.510130111859322,41.70610788560154],[20.497041323309166,41.265098241146795],[20.718239400131459,40.965833401060969],[20.936472594026295,40.899378315800099],[21.03065935913639,40.622507813535016],[20.950060872482442,40.494562423431944],[20.780835105302856,40.394345079084673],[20.657224604074205,40.117516336650851],[20.426483413867587,40.004325425494869],[20.382195215481438,39.802762838373866],[20.206788109901023,39.653732538003126],[20.001486542977627,39.7096459515775],[19.971818919657423,39.856112017809245],[19.860214837955404,40.024521050910138],[19.484695743659231,40.210156736226274],[19.322556445614339,40.407071341240865],[19.365606665903911,40.515933043895579],[19.33777469200836,40.663793388826093],[19.456442823772154,40.945268631800346],[19.440891250865597,41.424720026567037],[19.527934449687173,41.683098364028567],[19.472222364013074,41.808453168805549],[19.342614644838704,41.869288186597252],[19.349698013219218,42.051537081612224],[19.28098679744744,42.17260689889445]]],"type":"Polygon"},"properties":{"alpha2":"AL","radius":173000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[43.439764090991467,41.106947663707885],[44.222053945854015,41.212678435585225],[44.70224770369682,41.210824364647735],[45.001246609832862,41.290656044095037],[45.257709640645672,41.046212317328227],[45.418969292052346,40.985478987038817],[45.587290649721503,40.846791262388834],[45.54179064470312,40.544225302690386],[45.589299873699794,40.422556193801874],[45.9644024169855,40.23361603607583],[45.867010179025947,39.969185439870941],[45.909519297792315,39.807052879195133],[46.168358951548861,39.620852952363748],[46.481248237133983,39.555017256198873],[46.478813332210706,39.360084039475929],[46.584383726191,39.223718875490434],[46.500163428706571,39.118090500711936],[46.490391298107802,38.906921163572818],[46.114607116401594,38.877990249762334],[45.910404396440263,39.272028711854233],[45.644064024355167,39.511386691781659],[45.456832957015259,39.494717295967284],[45.172598697930269,39.570837766055426],[45.000013542321895,39.732540211928558],[44.768354461590427,39.703755441721775],[44.309224553057682,40.031083534004978],[43.946008061542564,40.02257216091261],[43.666503903752798,40.126493577433017],[43.670896420582366,40.232990938842242],[43.5696410259335,40.482334063731258],[43.695046874301006,40.62040638143479],[43.709278639010421,40.754743027870354],[43.629653136331711,40.924850374182903],[43.439764090991467,41.106947663707885]]],"type":"Polygon"},"properties":{"alpha2":"AM","radius":183000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[11.743332464311436,-17.24894085810994],[11.819055021454,-16.681655110519333],[11.798425536153474,-16.019211407326889],[11.751159727696844,-15.832049066566348],[11.908138701320011,-15.709422267163772],[12.015425578673298,-15.513461648968875],[12.27981394851945,-14.637336234554288],[12.379075303869227,-14.039112727633574],[12.504250042237103,-13.746317617877715],[12.550689500793876,-13.437883883730752],[12.884552131294368,-13.041850734670291],[12.983376683411933,-12.775814878196984],[13.413898015201724,-12.516703241288463],[13.60569747548479,-12.271776510723885],[13.78102358094527,-11.822702965530205],[13.845492972722491,-11.062874614669031],[13.71192226670218,-10.626945877918351],[13.539706184895923,-10.420554586862158],[13.489069102013772,-10.248973251936148],[13.332471880390838,-9.9988194818528111],[13.209599577562475,-9.7031475258114668],[13.154592790963633,-9.3900482636582012],[12.998784939364853,-9.0479960262135855],[13.046945091306871,-8.9224617965309907],[13.199183365975466,-8.8531482710883651],[13.350890508378317,-8.6825231006479111],[13.370071104858214,-8.3711452860203579],[12.863942843347434,-7.2357668114670997],[12.816218499103867,-6.9579725884315087],[12.521557191108725,-6.5901567149571187],[12.283607972319816,-6.1242955690499317],[12.351139233371756,-6.0139261392032344],[12.354333903147042,-5.8997219355828161],[12.155706468718558,-5.6326960599896836],[12.182782077897903,-5.3522990986113088],[12.018661430827345,-5.0043330165090589],[12.204436832170435,-4.7788329811822541],[12.31927653116659,-4.730108258046144],[12.384747367039495,-4.6193181319859251],[12.798313091868964,-4.4308473566980782],[12.880890913277911,-4.4453269708268888],[13.072397854233376,-4.634810726653332],[12.825026950293118,-4.7473823323699804],[12.534636483577236,-5.0633810598621052],[12.503966996427774,-5.6912426040552173],[12.457548495514033,-5.8416794881770118],[12.513168773384908,-5.9636603815624207],[12.614077699723367,-6.0198747813404383],[12.790208821689797,-6.0025348701123544],[13.068240965984209,-5.8650524870417398],[13.179605753270508,-5.856602683791758],[14.408126216582488,-5.892536330116604],[16.310459111546333,-5.8656122025864565],[16.435220909601004,-5.9025402304776371],[16.697047041002591,-6.1643947958423162],[16.738446403134269,-6.5986980564267572],[16.921435626664131,-6.9395142006489667],[16.992445427422112,-7.2657111673946337],[17.495410688010729,-8.0110286192574129],[17.605338151545634,-8.0817098126113063],[18.051381843312416,-8.0887397113546484],[18.562744154137214,-7.9361816584747098],[18.937258116407541,-8.0009479661448388],[19.249421588870181,-7.973817104335974],[19.349228698838534,-7.8564233362645908],[19.52783273662051,-7.1445683747814517],[19.660441341578938,-7.0372923924865622],[19.875217687844589,-6.9864701135297],[20.589890500136789,-6.9201668265417187],[20.568994801630005,-7.1335876340072728],[20.70078115773347,-7.2701394434297626],[21.805865572560769,-7.3287755902516887],[21.841373681272042,-7.426137316575657],[21.782485589437886,-7.8939575759556426],[21.805907874420868,-8.1212801224816182],[21.895691684009076,-8.3411573373590997],[21.9052467787767,-8.6887366522286413],[21.816962693141939,-9.4682191334524077],[21.87266852626334,-9.617123129656715],[22.195012892049963,-10.036136342753352],[22.272229646618847,-10.252587126215111],[22.285571042161024,-10.992489817132833],[22.43259518904922,-11.082760963641464],[23.076209535718213,-11.086983557204022],[23.404710651244798,-10.975960066218063],[23.79587428863605,-11.003432180523818],[23.96632823280925,-10.872129671346919],[23.987908042936599,-11.000586094572474],[24.046463583070203,-11.40533755178252],[23.976087281069571,-11.640425154926168],[23.99606482466665,-12.356478137977705],[23.887833542620328,-12.7566194764033],[23.962789540282351,-12.988275486842722],[22.159410125329945,-13.00193999010007],[22.037574463837576,-13.059576728957135],[21.979974808784661,-13.181429926761485],[21.979785156249989,-15.936077467128481],[22.044152394930236,-16.274654419007202],[22.177496869034467,-16.610130124463595],[23.380166477617077,-17.640494866242285],[21.421773075941452,-17.999827320685657],[21.123040447968474,-17.957040384284117],[20.745518745886727,-18.019519150389065],[20.352944469885262,-17.882620681966397],[18.951335659730603,-17.802382472937939],[18.7181906703801,-17.703063703464018],[18.484284218983444,-17.4460299409313],[18.377377680539105,-17.40015502727524],[14.017494160823745,-17.408616436903696],[13.690397572048999,-17.230755478616604],[13.434335040541839,-17.021469194599696],[13.140025911075059,-16.969662084839843],[12.548095568062445,-17.212461127254969],[12.058666435833223,-17.166771841901383],[11.743332464311436,-17.24894085810994]]],"type":"Polygon"},"properties":{"alpha2":"AO","radius":1077000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-179.98862475770881,-84.362838849919186],[-177.4192410747292,-84.11172821508336],[-175.44171892449674,-83.452005348078643],[-174.23300097734926,-82.805586276582716],[-171.02452237460062,-82.805384089799219],[-169.1541783536976,-82.430305275020871],[-166.56629802321652,-81.222317606516043],[-165.07767476838205,-80.02947575656728],[-164.10851692043812,-79.004352723095465],[-163.25440187203301,-78.732588095713481],[-160.9823582391335,-78.388717648460059],[-157.99999291080124,-77.107332753270342],[-152.64069573066922,-77.112219343155516],[-145.98656415255095,-75.529901399999758],[-144.71805933603144,-75.617582210596083],[-140.67137999700432,-75.409970855235713],[-135.74216620230095,-74.73304953074414],[-130.58997427350971,-74.272550573685621],[-128.64208048751303,-73.8588470225625],[-127.18007283477826,-73.305727634125745],[-126.19617287788347,-73.304507411169993],[-123.93489229576811,-73.680309476758126],[-114.5586360849767,-73.915696304732933],[-112.80402589091824,-74.164680958559188],[-108.77713704083951,-74.188345799247756],[-106.71528667905105,-73.708192805973241],[-105.07454077237817,-72.97432592813098],[-102.25430722149116,-72.030930708046668],[-97.057852493676606,-71.76569863015942],[-95.057654279393148,-71.403634010128172],[-94.095487717720729,-71.072140618781674],[-92.296739989215453,-70.125361713916192],[-90.613609172627392,-68.726250757082624],[-88.453148182288061,-70.761910345545758],[-85.83159223501481,-72.113693178412873],[-83.912836715233865,-72.561715029820633],[-80.963723978045749,-72.510661378895634],[-78.157789657114648,-71.601483179344584],[-75.738150189967527,-69.918763795904653],[-73.254103742976298,-69.414405576657657],[-70.303477386663175,-68.220854217772285],[-68.425413147200558,-66.872194971715814],[-66.575606550179344,-65.973142109644058],[-64.295697960832712,-64.582746570724467],[-62.520003682297975,-62.936706997657723],[-57.494943978826896,-61.793270919244101],[-55.384298370535085,-61.086181970511781],[-54.026535733005964,-61.147264914952679],[-51.57947624450474,-61.682093390272883],[-49.664328330799513,-61.675072656556772],[-47.785641110128552,-61.303084553307443],[-45.73043602155996,-60.533288209294533],[-45.186687927920829,-60.727550450200269],[-46.271695581842799,-61.696164014709431],[-47.502730281947265,-63.222382467291837],[-48.735043710376495,-65.886487379516296],[-49.140739096479365,-68.793627318170834],[-48.684861517386686,-71.6933216127657],[-47.406689619493861,-74.335731113328123],[-45.41635135011034,-76.493184459904327],[-43.776387394727088,-77.568036739479538],[-41.95820689049301,-78.302208961704977],[-39.05091368592322,-78.706804552249224],[-37.101360039429792,-78.496971815745823],[-33.867064969142909,-77.37746920687249],[-25.234692383112375,-75.746743350902648],[-23.40493891345314,-74.993710248409371],[-22.034975081876954,-74.12061577635302],[-20.801018926338521,-73.667478902464524],[-19.078244253953699,-73.425914480413951],[-16.451611725461184,-72.485688592675558],[-14.790660554422702,-72.254668408856304],[-12.090156030013008,-71.313385817668973],[-10.268317054530414,-70.949813253536561],[-8.578666526205275,-70.903884573625035],[-6.0678979635671286,-70.417310185383599],[-0.74151995990872344,-70.356324386893434],[1.3255183894907452,-70.034906903003417],[6.4061526619026514,-70.447096173246976],[9.1416892091680673,-70.194712722085711],[11.28369743034831,-70.265194167910394],[16.120327086460644,-69.725434449221098],[20.087690624847731,-70.331568640963823],[23.845522858244205,-70.4173659803362],[26.360234424293068,-70.075355916915427],[28.145614588774272,-70.143856156866491],[30.037137840464311,-69.886782210233889],[33.302031317430298,-68.683970247194083],[34.190155822670413,-68.715183263960725],[36.272787009143663,-69.172856145789453],[37.280454540383062,-69.232707314251144],[39.288212515660121,-69.047157172717064],[42.935878043373869,-68.107906262768211],[44.94517006142631,-67.788582056170767],[48.367790473192969,-66.718678165894076],[49.685614788232535,-66.569533041393711],[51.90143245614513,-66.025293639898933],[53.672101218156172,-65.867271604494775],[55.321042160377225,-65.975340316356352],[58.221716267577342,-66.956026346705883],[60.846264251292709,-67.459433442183212],[67.470031200453363,-67.806836587980712],[69.478707558845713,-67.759426834722348],[71.800600837147883,-68.742680563861214],[74.585946284545713,-69.153107801924492],[76.45890826249213,-68.985782384551953],[79.215061043151152,-68.139198020205711],[82.739594545521427,-67.519194795873148],[85.123007014193902,-66.530567137978295],[87.135099860767284,-66.767808752759322],[89.029683424972546,-66.712750962122925],[90.879847143369503,-66.301202110594986],[92.304846097705848,-65.720742807998803],[94.857160989928175,-66.107831278215016],[97.433674666919245,-66.047881818764864],[102.79285318152071,-65.147809482842476],[105.1868920951825,-66.072247459546404],[106.99673235704395,-66.439755592706049],[108.84329013449788,-66.468187545112301],[113.10962130620298,-65.811264828048792],[116.10747978940718,-66.802529014419775],[117.87298284137903,-67.0708652776691],[121.19101003689272,-66.969683651909094],[125.87567749676525,-66.374601673773327],[128.07842089314474,-66.555765660425038],[130.68573126061401,-66.207839883887232],[135.35171000458581,-66.138044315645232],[140.48188879869878,-66.727683126437157],[143.72874201141784,-66.891608882811212],[144.5464877613436,-67.047303871484417],[147.87369954696325,-68.215004114763218],[149.81966168666136,-68.418406736496905],[153.90801010111122,-68.337257374643656],[156.44888380577913,-68.526428831936229],[157.47843675282888,-68.422534064263758],[158.49174116808058,-68.212801959997364],[160.42649379284049,-67.485917995051068],[162.3118487556882,-66.264849661634742],[164.81733724657548,-67.33630369397703],[166.87854042468322,-69.580849261391265],[168.5115627303903,-70.658790077631949],[170.42985193119506,-71.429846431224576],[170.76830525309902,-71.754316191682491],[170.56459349103702,-74.809306574212542],[170.87245784690631,-76.800824463551621],[171.57335414142199,-78.690181060255],[172.638819508989,-80.400651029499613],[174.02558621115529,-81.862773486387297],[175.67733870657148,-83.017172749666145],[177.5270005823059,-83.816969542999914],[179.9888029036249,-84.362400186599828],[180.0,-89.753439560160899],[179.98850476021411,-89.987430541464079],[179.75451377891071,-89.99892578124998],[-179.75451377891073,-89.99892578124998],[-179.98850476021411,-89.987430541464079],[-180.0,-89.753439560160899],[-179.98862475770881,-84.362838849919186]]],"type":"Polygon"},"properties":{"alpha2":"AQ","radius":4592000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-73.576047578642147,-49.582868179647328],[-73.461391956335234,-49.314091620077114],[-73.234036393962526,-49.282745647912819],[-73.029486720490283,-49.011345110914874],[-72.646086343802594,-48.832877919089199],[-72.591729294279162,-48.720890137041096],[-72.585092414049527,-48.479229969803193],[-72.325027129720524,-48.289541057866607],[-72.323284333410001,-48.130796332386474],[-72.508860819268733,-47.973177989456424],[-72.517707015917011,-47.876406960743303],[-72.345700409168884,-47.492792071378872],[-72.115253031994797,-47.344982696806028],[-71.980791885322986,-47.172720742524795],[-71.940024253822202,-46.831406935080324],[-71.713905431290243,-46.650324856658862],[-71.759536596655835,-46.328843213515668],[-71.875349999082005,-46.160567230977506],[-71.726874336654376,-45.974385338659168],[-71.772440686475278,-45.724413000348491],[-71.747080335936843,-45.583793723186112],[-71.529224608276806,-45.482077987509719],[-71.415387957659902,-45.314827509644971],[-71.438541159080316,-45.179909373351748],[-71.595307406338264,-44.996401402983381],[-72.041521808437537,-44.903973742701147],[-72.07222190200217,-44.820385161279411],[-72.063534937221036,-44.772143726011322],[-71.742393565015661,-44.747288284373504],[-71.653052428368881,-44.56460407759986],[-71.694473947250302,-44.457614697223079],[-71.833576848427697,-44.335390755046546],[-71.812129630989887,-44.106155822201266],[-71.735628392786225,-44.013953121423143],[-71.717289245098641,-43.897355396890369],[-71.794438117312268,-43.753196995848114],[-71.787966404110534,-43.596276674461421],[-71.90473808745665,-43.440048919231231],[-71.916490635520248,-43.214146416788999],[-72.14619342662813,-42.989993132510889],[-72.143440463764378,-42.577204105999435],[-72.081960152948028,-42.448248854291116],[-72.107969041899068,-42.251998174936723],[-72.02596577046927,-42.148140248609614],[-71.861331747912857,-42.081563519759456],[-71.798581033072651,-41.960998712145326],[-71.91101081749575,-41.650363755583342],[-71.877643685270073,-40.942211414094196],[-71.941121068804208,-40.789101363776254],[-71.781403458498261,-40.379840589651302],[-71.803454525179916,-40.129475657493181],[-71.656461014571704,-39.948389162823119],[-71.719671755481642,-39.635308068861406],[-71.548576461233253,-39.544976871753811],[-71.467574620122164,-39.407150815600396],[-71.416820494681971,-39.262788697079522],[-71.401392003809178,-38.935181862930648],[-71.020432750332404,-38.729478606800079],[-70.934538799856696,-38.602818278510561],[-71.032349223339168,-38.042374535204786],[-71.168313095277938,-37.75726489130826],[-71.147933748782819,-37.466600481248776],[-71.200100432065128,-37.300308109062513],[-71.122903302350707,-37.078151595629585],[-71.191931653395386,-36.843649224133372],[-71.055309418240654,-36.523914405481676],[-70.7900321309193,-36.37771481419307],[-70.426974514331107,-36.036749933855717],[-70.388279935175589,-35.771537351572526],[-70.420280927740038,-35.543669163839084],[-70.554919505667115,-35.246930582232139],[-70.39472546325652,-35.1189851335144],[-70.213050386649016,-34.586147175423996],[-70.051842097244517,-34.300944452512475],[-69.916742556208135,-34.222131015711966],[-69.865628089034502,-34.118355345541211],[-69.894186391664022,-33.731349555119223],[-69.852552508531403,-33.434258188025282],[-69.906401062868554,-33.32951738993912],[-70.084642501485291,-33.201636582370078],[-70.072837524199528,-32.903806521562807],[-70.186314249709426,-32.500655888270614],[-70.321329525549714,-32.261895906317527],[-70.371096328283173,-31.932776300377736],[-70.584962019038855,-31.569381941110013],[-70.519355464474017,-31.148566588964414],[-70.367132474649296,-31.01551604566227],[-70.167073739312457,-30.381714124593735],[-70.023893050182906,-30.323203793619186],[-69.948522896507342,-30.213682638653115],[-69.928707415798158,-29.769288494957092],[-70.02655357415891,-29.324049070667943],[-69.816407782540495,-29.047966331100685],[-69.656717103836428,-28.413692235324742],[-69.183619204840866,-27.911208066514771],[-68.846131124328423,-27.153886363576941],[-68.493832526316353,-27.072061594182248],[-68.376080006056867,-26.945807579414151],[-68.381902959797486,-26.809862290185006],[-68.591344423602948,-26.470381006300837],[-68.575539465395977,-26.352054338902711],[-68.427619878882012,-26.098122423715644],[-68.600076361547735,-25.485621257170592],[-68.459996317417207,-25.052356474109054],[-68.561747995902778,-24.837625615265946],[-68.559712642000477,-24.742424675707788],[-68.507063354189754,-24.629984108136505],[-68.250156397743865,-24.39216493612631],[-67.439719654227531,-24.066780294367096],[-67.347060637323523,-23.992114957625073],[-67.043029679155921,-23.101153754160052],[-67.062418449761324,-22.962532026441998],[-67.194557265715744,-22.82164804658882],[-67.033343139575521,-22.552356881840428],[-66.815392449419633,-22.408321111455948],[-66.711585366689846,-22.216529353663322],[-66.365601349167306,-22.097304009065553],[-66.220078104694295,-21.802770630063879],[-66.09871114030895,-21.835243126986889],[-65.728882921216581,-22.103870382838601],[-65.053077250175406,-22.103238049513198],[-64.70017176595519,-22.185760487587586],[-64.605704281449903,-22.229013259989053],[-64.474154456549698,-22.451786467428999],[-64.349434959651049,-22.509767828073066],[-64.186607219253332,-22.447148745241822],[-63.97592452476399,-22.072722409601983],[-63.865713591545074,-22.008876445478503],[-62.834459555584473,-21.999335434258011],[-62.589738092782355,-22.311790268944449],[-62.372711200483906,-22.439420387846216],[-62.217861089402213,-22.608353342570819],[-61.914814003603055,-23.071803111898308],[-61.204376993774233,-23.560263646141177],[-60.995307196190993,-23.774478513529285],[-60.515023504051612,-23.960530539005227],[-60.110345487070525,-24.009430196659299],[-59.892568589198888,-24.093733111468772],[-59.180284047427861,-24.565680728828656],[-58.35955966253475,-24.938619586112157],[-57.959902885310619,-25.049390604642412],[-57.647628821296479,-25.32438733189878],[-57.571882297437647,-25.534089398198883],[-57.73885937028173,-25.6928145712121],[-57.890827584729792,-26.006438707868458],[-58.098791723711663,-26.177777591824331],[-58.199830844800935,-26.373154658713332],[-58.191547371360585,-26.629867647631343],[-58.322760259802131,-26.857489229957018],[-58.497479240281407,-27.025562900690328],[-58.473819994435289,-27.195375561988204],[-58.346782116226414,-27.281473348152044],[-57.819334920353008,-27.315737457457857],[-57.379042798749737,-27.432202137576915],[-57.131270678607017,-27.466420341213677],[-56.877092587733621,-27.440368836097964],[-56.72680314415944,-27.491427847029961],[-56.388650579913815,-27.486746991210172],[-56.168240265858515,-27.323922718846951],[-56.072206766264785,-27.308408495689914],[-55.733077249536684,-27.363103712613714],[-55.426486062712463,-27.009445489263623],[-55.18186543550479,-26.935208706589634],[-54.782647136000769,-26.579635997363681],[-54.675002504579872,-26.290714448611588],[-54.615656234156425,-25.576296343867991],[-54.422138945614499,-25.597435993548402],[-54.154643878576863,-25.523292039288481],[-53.8913650455438,-25.669051308692122],[-53.820194411370863,-25.95838243815523],[-53.668782301073172,-26.282932223454601],[-53.747430450936648,-26.693287856357589],[-53.717572815153559,-26.882771702647858],[-53.838361948514084,-27.120906631607166],[-54.327079654370422,-27.423335671344535],[-54.564288650398041,-27.463082445925785],[-54.810656304950477,-27.56973187430582],[-55.101610223139133,-27.866599060715512],[-55.268917042060444,-27.912823105613462],[-55.603427104518971,-28.142975593651727],[-55.687414880875401,-28.381428657632316],[-55.866479015409169,-28.414073201620763],[-56.230710677427396,-28.744562716802271],[-56.396104131586348,-29.000537587828141],[-56.60813936781885,-29.17540992771665],[-56.933868242144165,-29.589760624173334],[-57.247822212687055,-29.80480196377896],[-57.401261554325565,-30.029621527617245],[-57.597450249598516,-30.175637068784219],[-57.843684383650775,-30.550410712783776],[-57.810889598405488,-30.858545527719215],[-57.893636063232805,-31.195202988661389],[-58.015720160898979,-31.419429375485539],[-58.00531839008142,-31.679049363635123],[-58.158602043301002,-31.951404095081461],[-58.122799452073409,-32.316438972760459],[-58.21744356651277,-32.574873113686159],[-58.171264517306263,-32.959227074849437],[-58.250527200623338,-33.078066466915828],[-58.428279675366568,-33.187121859030462],[-58.540927690097561,-33.642493714427076],[-58.393062804233274,-34.188316937348738],[-58.476429238994129,-34.414532886212761],[-58.287622701870909,-34.673724409230253],[-57.551478651923539,-35.016853455584844],[-57.303849058938198,-35.188672263396128],[-57.170871508137836,-35.362583554551613],[-57.159152236375142,-35.505866625193114],[-57.344836903070131,-35.724458419758548],[-57.362433246961118,-35.941527772174609],[-57.262644539796227,-36.142100719319863],[-57.083483886773891,-36.289595713631499],[-56.936401306232099,-36.348248522013016],[-56.749571741519844,-36.346711785616428],[-56.717535152659565,-36.389158200790106],[-56.668470244318314,-36.741688409501151],[-56.725334024605814,-36.95421137155401],[-57.087824933589502,-37.44627334859743],[-57.407358414264642,-37.761764189508789],[-57.547160894457889,-38.085530819544907],[-58.179275544975631,-38.435643667376496],[-59.002562587580087,-38.672002093189221],[-59.835314542324063,-38.839066246567263],[-60.905665250963487,-38.974082162822548],[-61.679939491916997,-38.991079677341375],[-61.789816792872664,-39.030408603834594],[-61.859037074722465,-39.124367841299808],[-61.866232235601373,-39.234648338409052],[-61.975666713964223,-39.26914850648275],[-62.048784554709307,-39.359765349283528],[-62.131742345266204,-39.825233390295892],[-62.27536105608079,-39.898666807349855],[-62.336690140053832,-39.990831187793077],[-62.420393024788076,-40.364871741530379],[-62.246599475075868,-40.674638444272396],[-62.302068081702153,-40.814492954384065],[-62.395159322892447,-40.890580487797742],[-62.964045134594087,-41.110512611298532],[-63.61765754121619,-41.159691837491899],[-63.772933731251953,-41.149719107284852],[-64.379763319401647,-40.923669105980011],[-64.909502377686138,-40.791633322382005],[-65.025342107358654,-40.800629997880918],[-65.116632992324696,-40.872503201947197],[-65.153763344304409,-41.105553512774968],[-65.018445959000601,-41.566930475692551],[-65.046251002783535,-41.957611295129169],[-64.988354198426691,-42.091057623289707],[-64.887705827420746,-42.163803826796411],[-64.392006207940256,-42.307560225085275],[-64.228376912015136,-42.218472021587949],[-63.795602900704601,-42.114082095221441],[-63.62873393404066,-42.286900987101866],[-63.61763312404068,-42.695698298217842],[-63.692608011248019,-42.805067953821172],[-64.214353240057022,-42.876379015729746],[-64.432316213836287,-43.058971232417605],[-64.839347559241418,-43.189919694666891],[-65.24309059216155,-43.569180130505245],[-65.301194611157257,-43.7682468325389],[-65.238847966209931,-44.04868630722288],[-65.284653989073206,-44.150788167451253],[-65.267037961735639,-44.28472289328549],[-65.361426222614043,-44.477199979222107],[-65.608594187905268,-44.663795831536113],[-65.638913605430801,-45.007572277328698],[-66.199113433989623,-44.973184329583276],[-66.585134733219292,-45.182664835219462],[-66.912158707795953,-45.242591405444266],[-67.256986031635037,-45.577766247994553],[-67.580900305120892,-46.016704605070132],[-67.605690016320153,-46.176592273183587],[-67.510691425678559,-46.433606910687715],[-66.804066092510752,-46.985686732881362],[-66.650145550478641,-47.044026309870524],[-65.998610788910412,-47.094056210462298],[-65.850699735881818,-47.160241133971738],[-65.738343699827439,-47.344944184686625],[-65.775701049770376,-47.56825063126211],[-65.872447634818926,-47.755799732809955],[-65.810405117635682,-47.940989303475561],[-66.39352391715822,-48.342068546298655],[-67.046313109591821,-48.635854042423723],[-67.472214336502844,-48.961852314557646],[-67.656812183400021,-49.222821036852515],[-67.782410372155297,-49.854270766192919],[-67.830343470695837,-49.92284713826119],[-68.14572944220842,-50.091139929940645],[-68.353400336964981,-50.114937733335815],[-68.763529503825723,-50.288449960940781],[-68.937577176412958,-50.384723510590916],[-69.056154338042077,-50.520166028758005],[-69.185273973088215,-50.971579847875212],[-69.064342534006684,-51.30897643301126],[-69.028960589670504,-51.583271638347625],[-68.394050278939403,-52.307024281117364],[-68.508332276456088,-52.373541962658344],[-68.640152206085858,-52.267156236586736],[-69.198147191730357,-52.138908443848806],[-69.488401980385646,-52.135939294142908],[-69.968868878672168,-52.008818677891476],[-71.966826566756126,-51.966226204003085],[-72.02891301062887,-51.822578724022513],[-72.268881256536446,-51.690949219338975],[-72.407379549424533,-51.540789487432306],[-72.315250866392532,-51.304176691937414],[-72.376551042306133,-51.095440044415675],[-72.282622776996718,-50.919472708006374],[-72.33259332706568,-50.71369876328901],[-72.514166323568887,-50.618212244741045],[-72.845681679479796,-50.648113505165071],[-73.082393055729852,-50.760107214898511],[-73.1527500170981,-50.738101028874283],[-73.321342742912691,-50.34511285453376],[-73.501055761827075,-50.125190632424854],[-73.528690489142136,-49.910968996888876],[-73.490101416143631,-49.752440882343784],[-73.576047578642147,-49.582868179647328]],[[-64.473221779527719,-42.943646028160472],[-64.289458531843664,-42.890866518816239],[-64.238984002571371,-42.68861200934046],[-64.34523254525763,-42.565922405371154],[-64.610308125193185,-42.530780665928226],[-64.72682676687765,-42.627341883763009],[-64.742358636611883,-42.777872357994276],[-64.648011481668291,-42.896190898400164],[-64.473221779527719,-42.943646028160472]]],[[[-68.606026007630476,-52.643873624256784],[-68.278380635949674,-52.984070307525464],[-68.232722122309013,-53.220446811177418],[-68.005548435039387,-53.561578550891802],[-67.306040090613962,-54.042563736734891],[-66.223568459448344,-54.536746784030044],[-65.756023084562898,-54.6505725297745],[-65.256460514080828,-54.637857097937804],[-65.181690124038312,-54.682181153091911],[-65.346110745286012,-54.877730756701034],[-65.466732916572752,-54.913354615616548],[-65.928420018520612,-54.917210454125566],[-66.51113682939345,-55.031924654577182],[-67.121715002353227,-54.904386232248491],[-68.233386584769192,-54.817427138422786],[-68.652998894935422,-54.853276048346522],[-68.606026007630476,-52.643873624256784]]],[[[-64.757020366979958,-54.826608795666516],[-64.549101641364814,-54.716454854316908],[-63.81574401323185,-54.725303186701787],[-63.971239186308274,-54.81038203559882],[-64.312223482006786,-54.79741788264775],[-64.637360110402255,-54.902298843326207],[-64.757020366979958,-54.826608795666516]]]],"type":"MultiPolygon"},"properties":{"alpha2":"AR","radius":1741000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-170.8200947015475,-14.312193345284511],[-170.81592971518862,-14.310449506303073],[-170.73799525579966,-14.282193014412922],[-170.72120213517252,-14.27522534324676],[-170.70511115289071,-14.266761441600114],[-170.69352620034886,-14.259978264456953],[-170.6891008981593,-14.257679360623078],[-170.68411586889533,-14.257812558006091],[-170.57302259498996,-14.266416797050002],[-170.56937543730004,-14.266880664938851],[-170.57292907711431,-14.26782330840461],[-170.59396919117731,-14.272309594534097],[-170.6120255342878,-14.277050520785759],[-170.62956117313723,-14.283454369502634],[-170.64642332500529,-14.291465345886696],[-170.66246507506,-14.30101365272238],[-170.67754665638253,-14.312016098498622],[-170.72253071184349,-14.348167200553778],[-170.72636870140138,-14.350959671984466],[-170.73097293009087,-14.352112554768174],[-170.76459787268976,-14.358837543287951],[-170.76917136653501,-14.359529139697946],[-170.77270443836201,-14.356543749436835],[-170.81694101021094,-14.315424802651563],[-170.8200947015475,-14.312193345284511]]],"type":"Polygon"},"properties":{"alpha2":"AS","radius":15000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[9.5272963385343079,47.520079790745704],[9.7536848031226118,47.574311300710846],[10.239902766597963,47.362500389423488],[10.36739568030824,47.416565369838771],[10.439538399110942,47.551303470424976],[10.658660721874954,47.546963249521013],[10.872884857974828,47.519986807558183],[10.952998663012149,47.427502240581461],[11.069753885914661,47.39882012322542],[11.287493898884804,47.424567487914345],[11.713020236530502,47.582604208178424],[12.077251582767643,47.615101321712999],[12.209327900300968,47.717998964429263],[12.504265696828449,47.645789592470777],[12.74067112205918,47.677571072063017],[12.837220145700977,47.74161930970476],[12.883122136779503,47.868008313357215],[12.760581769755047,48.106885728894007],[12.902447862171558,48.205638722764363],[13.344723624949424,48.346779606032818],[13.486659340484085,48.581572224736455],[13.697032975319061,48.578692895796252],[13.814854725792348,48.76667920182831],[14.133809390603203,48.588091460794821],[14.628074493934237,48.624148873265305],[14.911603643918383,48.828148502703712],[14.993544583676105,49.000856450427122],[15.825130382876258,48.864174409368687],[16.074938649272838,48.753859787332026],[16.323419910573445,48.739976737431085],[16.538905209599417,48.796513529578974],[16.883540597211191,48.703509095283458],[16.952871623620975,48.598757691436241],[16.89390639768056,48.344884183905137],[17.146964395108682,48.005931549937785],[17.052209860742206,47.838536799858737],[17.066278292220115,47.707762678866146],[16.767346268539359,47.667099180292659],[16.622865106894906,47.447739817871536],[16.49892163153406,47.36221614629288],[16.453249461255073,47.00698125299337],[16.274438178729071,46.980284328163187],[16.072885691322671,46.852443736627954],[15.957511680803375,46.677903232549987],[15.739405236073688,46.708732292746966],[15.439237507400119,46.629874747965118],[15.000793339913999,46.625300868979096],[14.549792102311109,46.399979244652755],[12.47924010560625,46.672679449526008],[12.388391695724044,46.702817572104458],[12.108855875197056,46.989183406989547],[12.004066060671311,47.034036075766281],[11.234976394317021,46.971213635077454],[10.93161789689073,46.769969991224016],[10.336320224477035,46.915563350195185],[10.128905682684037,46.852650610712999],[9.8824702116929934,46.935614649821773],[9.7785941021668563,47.026117665140774],[9.580529909681097,47.057604659877349],[9.5278327532145255,47.270741607378319],[9.5694453443292531,47.418936578607557],[9.5272963385343079,47.520079790745704]]],"type":"Polygon"},"properties":{"alpha2":"AT","radius":294000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[158.83696252559102,-54.704053511773296],[158.8977392166984,-54.50668192059787],[158.95775549404857,-54.474014784351439],[158.8457022153072,-54.747321846950463],[158.83696252559102,-54.704053511773296]]],[[[112.90919211129481,-25.570095792367869],[113.29208553299992,-25.303842099828827],[113.47110850934405,-24.960521003179782],[113.49202483746691,-24.727653320720844],[113.418545099664,-24.435456049439054],[113.42215415433981,-24.132465329940995],[113.49724693422301,-23.85357339371706],[113.74790097544262,-23.336670042313369],[113.7775614887848,-22.954548444174549],[113.6838868931072,-22.637738996840188],[113.95905908544746,-21.939629067697521],[114.12397465174435,-21.82960884501653],[114.36098752246764,-21.877342056212353],[114.58955464600402,-21.85519821337526],[115.04086911651768,-21.623817320174613],[115.27728002095822,-21.298425217017055],[115.33072404943195,-21.061378528896729],[115.30953512793636,-20.811313035578006],[115.43453417043267,-20.66933272941111],[115.7157148364338,-20.861393789031794],[116.01961114550635,-20.920451571306252],[116.32317515453605,-20.859708829049211],[116.70701079405484,-20.654712941174161],[117.53910601386674,-20.677206974856453],[118.18168997189697,-20.382063044933027],[118.74359046042396,-20.244838547866529],[119.10480373861294,-19.996356353521769],[119.52569784602188,-20.016733817253151],[120.19577035688953,-19.906850730193469],[121.04339784092311,-19.572785527107037],[121.37419634299876,-19.269927355783079],[121.78591272355868,-18.536626989917586],[122.10762403072972,-18.16516381792707],[122.18817697393557,-17.854748949642474],[122.15754960557624,-17.331829322381452],[122.27734411596676,-17.118286440988538],[122.70278208495183,-16.771880473078152],[122.9173600758336,-16.433683128552854],[123.35116397272033,-16.385297261279653],[123.64802320583,-16.180800458957581],[123.93393814494421,-16.122873923849905],[124.13467120324891,-16.005653517480674],[124.33427321527374,-15.769663746609551],[124.52000153240259,-15.268321705673332],[124.78990419469561,-15.130283001024146],[124.95370934081498,-14.957335018957359],[125.11829501350162,-14.492568211617984],[125.78729443365798,-14.196717718156535],[126.0544787901361,-13.978099792375634],[126.51372423551234,-13.933154870552203],[126.90323450734712,-13.745012926181676],[127.45714936349766,-14.032099442009034],[127.6718923108821,-14.19617627306288],[127.99085994854856,-14.565004618069471],[128.2680238867905,-14.729765622574854],[128.75474619367861,-14.778842490164836],[129.11708035990944,-14.655843543745853],[129.29172848689973,-14.504618604461683],[129.69129454356468,-13.937937713682471],[129.83031421671458,-13.588432699820526],[130.06388592147306,-13.308354334660265],[130.16905073952157,-12.957869672578486],[130.30471602616714,-12.73207747353427],[130.36138379311905,-12.433762909986585],[130.32806901186277,-12.207862005811423],[130.23229237163179,-12.000574938334033],[130.04429935920652,-11.787065493613706],[130.15373924714939,-11.477977839062397],[130.40315217844449,-11.181424477166249],[130.81363004179994,-11.306222840427072],[131.05559043662646,-11.281041040478048],[131.26829621554657,-11.190855816993082],[131.68363103600655,-11.261153066638649],[132.26154036653367,-11.147757284711945],[132.57849423827841,-10.970272778032204],[132.77792014324862,-11.274989242148553],[133.33050249289732,-11.714622019245668],[133.58136901200837,-11.800663490492733],[133.90400537553904,-11.832769622309492],[134.35298100055934,-12.010002285634656],[134.73011974212773,-11.985371853288735],[134.99828583774467,-12.106386470209774],[135.23430792588573,-12.127957697214741],[135.53915909088934,-12.048240496427541],[136.12815546869388,-11.704005228147434],[136.52543265307082,-11.38690584869696],[136.73250218301706,-11.025377775593025],[136.77913903009011,-11.013673946701973],[136.63072139071679,-11.456206630670202],[136.62320561665777,-11.769049303750974],[136.6973489466086,-11.992431309403935],[136.94631065766146,-12.349718130247389],[136.68269002896628,-12.675118743944289],[136.58052945024028,-13.139510541742432],[136.66930797128614,-13.514620825569999],[136.89880476015099,-13.808459351232422],[136.94977168910773,-14.184316848538952],[136.71632329437489,-14.506346790305791],[136.61785333845435,-14.797024103932271],[136.63607098949481,-15.103386226373456],[136.76829513208475,-15.380345445379533],[137.02196057960279,-15.610163501468632],[137.29750208894262,-16.024515028217866],[137.70311506786626,-16.2338630543329],[138.01282968423791,-16.565000879805588],[138.2802298757328,-16.72480919993292],[138.53277906696019,-16.778852574249552],[138.83442484116705,-16.756939302526682],[139.04737126370918,-16.672220906696083],[139.29340019257944,-16.468150338621818],[139.58782839493213,-16.396202381810976],[139.88754882086013,-16.865525811249316],[140.14442197263651,-17.049513088206535],[140.45249634048258,-17.119693698505312],[140.83622116777997,-17.032560173647898],[141.08377398480096,-16.836211664688371],[141.25244637394724,-16.561981870736236],[141.59527542609527,-15.137763891891908],[141.60748718578176,-14.892126120849264],[141.52386218553067,-14.470143912986567],[141.54715170342763,-14.16672250937442],[141.47362914815645,-13.797657883102225],[141.62751600785614,-13.241629238344968],[141.61445568574749,-12.94361704361901],[141.68104470483263,-12.710041636964261],[141.68976325570227,-12.351368500093841],[141.98091925842351,-11.797526093420094],[142.13582831660952,-11.272675123056144],[142.15977839920728,-10.405002078392751],[142.09870320979246,-10.121927010421665],[142.14901850096066,-10.053110035941355],[142.33809978479448,-10.192528571195451],[142.41137209259551,-10.488619746294441],[142.604235192901,-10.748793170793652],[142.77898342647461,-11.115601207457699],[142.9183250932991,-11.642943071755548],[143.17785461757433,-11.954932299803893],[143.20994630259207,-12.287891912137717],[143.40059622138043,-12.640368503300623],[143.60647072680865,-13.618118025248748],[143.69581467539223,-13.841956362453091],[143.84776897138246,-14.029028098108213],[144.04854235153425,-14.162354735256695],[144.47270916300286,-14.232747823233842],[144.76515229631133,-14.56721116804003],[145.28662116499447,-14.943600448456044],[145.29512385269749,-15.488375528953808],[145.45711054256969,-16.056630067820414],[145.4834555962278,-16.404976455551381],[145.63253942353529,-16.693503961083572],[145.91115325509131,-16.913137681573048],[145.93826963957687,-17.115385971447253],[146.1246486538146,-17.635340379926912],[146.14821879075021,-17.971879733635124],[146.34091679170251,-18.40027815551214],[146.36343971114979,-18.665680295344639],[146.49442747632986,-18.942793126866928],[146.78754446529362,-19.18744765671002],[147.15505081985231,-19.331347852307523],[147.41823588578706,-19.37907266493454],[147.72122669598721,-19.715375719824394],[148.33797909807035,-20.040713017872783],[148.63259213508044,-20.106799666839422],[148.967163473862,-20.045727161228839],[149.04444106330072,-20.277685797224528],[148.99141461655154,-20.521896759461569],[149.03144516532234,-20.834263650623708],[149.52816364416131,-21.731430781026138],[149.68453591022603,-21.912349201774123],[150.01226534628901,-22.114906188033697],[150.22342310097406,-22.195685693415761],[150.5208615109558,-22.228963481080889],[150.76301861816506,-22.57642619417912],[150.78756639423153,-22.924095854686239],[150.88698734524689,-23.205625791086362],[151.02604192048852,-23.381881017358705],[151.23931264011151,-23.530360450544201],[151.43823157246331,-23.867225566615343],[151.83104267975133,-24.123678896839582],[152.14363332417736,-24.582901105482463],[152.44057103995974,-24.782899782743442],[152.82652904248414,-24.863991702297994],[153.28123774956543,-24.739036483675498],[153.35720318662661,-24.997226991407015],[153.13585134482383,-25.598937274102607],[153.16393483732105,-25.964175189570589],[153.10020338270417,-26.443403492319899],[153.195930030433,-26.750789399727235],[153.46582444324281,-27.038614568048636],[153.53775473922499,-27.436468553488211],[153.46103975960438,-27.904984094409386],[153.57474454678942,-28.240786634208416],[153.60585589975102,-28.835822885517523],[153.36716731261222,-29.294794063742387],[153.27095912615164,-29.892034458312388],[153.0420602530321,-30.578402125793311],[153.04520976678066,-30.92517954019845],[152.94325174000701,-31.434652351394202],[152.78502077921996,-31.785802723989132],[152.59841494316325,-32.060385392871098],[152.46970283158055,-32.438543089074457],[152.16365063897709,-32.756601098279944],[151.78581890669233,-32.960524012138684],[151.52715361973458,-33.310266919148035],[151.23073220964645,-34.02916316273808],[151.01388773041108,-34.24787025005741],[150.88802630910359,-34.474111919124411],[150.80369184772272,-35.012473960099257],[150.47615150948113,-35.396993529209453],[150.19442118624616,-35.860928529921644],[149.96317628856468,-36.856478666124971],[149.98509744447492,-37.258370887803771],[149.93199409313473,-37.527732730798903],[149.46210159157781,-37.774374606113398],[148.31384565865528,-37.827485311491273],[147.81880651011829,-37.962878501259077],[147.42307480706074,-38.200099766891434],[147.15602052132431,-38.436713829642137],[147.03574004869651,-38.638717092600672],[146.97724098303573,-38.913808288150626],[146.67499842081483,-38.975496974744061],[146.39990604401018,-39.144485393211923],[145.93585064613657,-38.900903103400907],[145.77835573489,-38.750987790233268],[145.53531743652567,-38.617379295057106],[144.86821436251566,-38.487699793678146],[144.63032069728382,-38.505501935078719],[144.40825329985546,-38.592659167683195],[144.22175892791208,-38.741421264432745],[144.0874211016569,-38.93855994818842],[144.01718545801481,-39.166545181390184],[144.01729751637163,-39.405103978416847],[144.15824549117923,-39.924325922259257],[144.33040929382932,-40.198612902779828],[144.52381807346154,-40.346747059008997],[144.78302745675796,-40.435764284214898],[145.10569351807763,-40.710514134101182],[146.14846754282075,-41.126282520589392],[146.5859509002791,-41.129117749356567],[146.98997961609746,-40.993198529718882],[147.26013987997402,-40.961028839224781],[147.54134357250254,-40.813339202995436],[147.74291223945986,-40.567864524378059],[147.82208533830499,-40.342446776193874],[147.7818193647573,-39.74247626830843],[147.98429135714807,-39.750057531291283],[148.29650497601077,-39.986245364596421],[148.34125662167315,-40.196879777592507],[148.47296037829938,-40.432404825346531],[148.34859698178832,-40.643894635994535],[148.29543221855806,-40.865342819599171],[148.29312788403126,-41.761943410986319],[148.34178994897954,-42.215282276944997],[148.024696691613,-42.904671816650179],[147.97991402015634,-43.156550441173287],[147.54644432747864,-43.303132692291427],[147.30847368122841,-43.49983371049089],[146.81452037979287,-43.616793103382605],[146.44206575254063,-43.523929617671087],[146.04375694493922,-43.546013108837947],[145.69377287081372,-43.099587122047375],[145.48822379133048,-42.926131392206692],[145.2620701053163,-42.526916418380289],[145.12493425427829,-41.94760144936],[144.77242079248796,-41.405282070947145],[144.55101810991295,-40.534211580617487],[144.29523934730403,-40.254341167280444],[143.89937586682302,-40.119151977818348],[143.83954509060558,-39.904036755760124],[143.87311939042894,-39.368196705158653],[143.74958224918129,-39.078161063061636],[143.58799901995201,-38.904622755521522],[142.55615888952391,-38.435227223758197],[141.94595034776563,-38.338653235439537],[141.44373194863229,-38.368185672574576],[141.10903669373008,-38.126906954222989],[140.62753279455615,-38.027543289867047],[140.39098571576835,-37.896062893291976],[140.1846640097009,-37.622219791726948],[139.77623866921621,-37.225872346242674],[139.73967055430026,-37.05971145306129],[139.79886504811191,-36.642026557727505],[139.67183463770886,-36.284270168150705],[139.43059315304365,-35.981098931199163],[139.00421125733439,-35.684710397893369],[138.70431113869211,-35.623074288203647],[138.4609893276224,-35.656568881089576],[138.06612255060628,-35.899721602209731],[137.78512507629029,-35.939271477349571],[137.44841448263847,-36.073756941453624],[136.75532503793087,-36.032332773771586],[136.60789710111894,-35.94633974147969],[136.54160868512497,-35.889793198917836],[136.55006023626211,-35.679391059674124],[136.49625750586202,-35.455144571908768],[136.27395613666161,-35.144527557203894],[135.9620967739375,-34.978213006576276],[135.64795431112674,-34.938665371851776],[135.41722847533185,-34.717032455788463],[135.12410514734711,-34.584994082933598],[135.1789300296096,-34.170422533515662],[135.08668037132546,-33.870637235618794],[134.68420106350786,-33.349323214327526],[134.30180999475803,-33.164145572503749],[134.174405624017,-32.97867610287728],[134.01624324582602,-32.564586732435856],[133.86293857865596,-32.384372966572251],[133.58989012701679,-32.228182541241175],[133.21240496003171,-32.182603950557592],[132.82268550385217,-32.005652021190102],[132.21491600171663,-32.006372349363616],[131.43633313961851,-31.572273412914569],[131.12420321301317,-31.532638438002493],[130.7829336727853,-31.603222223628098],[130.08041436290065,-31.5833389930573],[129.15250426149862,-31.666179493621186],[128.91557214754073,-31.720064881022203],[128.09456495353243,-32.056454084970312],[127.31967125681145,-32.263383821680449],[126.8052938693552,-32.309738023169807],[126.01226716643167,-32.284941931684301],[124.75855027398543,-32.882086616022121],[124.32416234944789,-33.023335500854976],[124.09447946415878,-33.225327243547447],[123.86788346587407,-33.596017467892011],[123.66498779954884,-33.820259461081321],[123.50658344136968,-33.915278070971866],[123.2076538389517,-33.987313869116576],[123.00776984280148,-33.912702146871695],[122.77895553830156,-33.894321393697162],[122.15121063316694,-33.990805943383073],[121.91550839540695,-33.886508509160073],[121.40501909353297,-33.830189566604737],[120.7775886041064,-33.877593477858788],[120.41823852513181,-33.962072040405076],[119.99933072674219,-33.962297356940574],[119.65881349135556,-34.1238408140077],[119.45002542068171,-34.367579836472174],[119.24744998194727,-34.455495167119118],[118.85902286392042,-34.523043121825715],[118.13521540859131,-34.985813771009497],[117.86293396524349,-35.054208601473562],[117.58188934125882,-35.096818769799505],[116.51739608086338,-34.987068482327963],[115.98725893680648,-34.79413808514883],[115.55134147634837,-34.421606193877395],[115.12821079191583,-34.340815954216261],[115.00978976294675,-34.255329385589896],[114.97470985640739,-34.050965653312211],[114.99473329584771,-33.516079362782428],[115.32519959517131,-33.356669980608025],[115.56102588742708,-33.041651066197353],[115.72350078212128,-32.349437340807718],[115.70738696143657,-31.772424932275587],[115.08850805061185,-30.586921233052468],[114.99186970929624,-30.198187348171146],[114.94935722528517,-29.453861480179143],[114.87831874287767,-29.213963221011738],[114.62952599619129,-28.87112738222422],[114.48813652564505,-28.483413622138936],[114.16614218981353,-28.080104267782009],[114.02354975207284,-27.349506407736126],[113.70755653284488,-26.849117096416052],[113.18560927157111,-26.181797780715058],[112.96515619829371,-25.782720372738229],[112.90919211129481,-25.570095792367869]]],[[[123.57349764644874,-12.424975961338921],[123.59371557912,-12.426488801646663],[123.59417813480127,-12.434887319911086],[123.57396020213001,-12.433374479603348],[123.57349764644874,-12.424975961338921]]]],"type":"MultiPolygon"},"properties":{"alpha2":"AU","radius":2299000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-70.065822277703987,12.547067795323951],[-70.064631139240603,12.551846623242417],[-70.052192773791504,12.592749709623117],[-70.050699189573095,12.596967182574424],[-70.04781148715162,12.600384644590806],[-70.038431146840097,12.610520058735462],[-70.035079582499435,12.613803597416679],[-70.03119315380053,12.611174855936738],[-69.977331803480268,12.570768232213428],[-69.973342160092528,12.567435254483586],[-69.970132240164446,12.563345950268539],[-69.914603165488288,12.484429231879259],[-69.912020468147389,12.480352903945947],[-69.909843982625745,12.476045969334841],[-69.901172916976776,12.456602772129644],[-69.899366873538483,12.451934354632613],[-69.898531515769321,12.446998962831767],[-69.89629149180395,12.4279907594685],[-69.895979043462177,12.423337262083278],[-69.90047140099,12.424590811333841],[-69.937795069143306,12.437058171074122],[-69.942072792008162,12.438710861365871],[-69.945462113110139,12.441800003735125],[-69.994108843879374,12.490446734504362],[-70.015510029613097,12.50901024840261],[-70.062038194700648,12.543915538523454],[-70.065822277703987,12.547067795323951]]],"type":"Polygon"},"properties":{"alpha2":"AW","radius":14000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[44.768686545763011,39.703351458417991],[45.031613280790623,39.764848501302694],[45.199562660258337,39.61733894195563],[45.447193801886769,39.506166797069596],[45.685865703408851,39.56759771869897],[45.775933473097304,39.638836525296519],[45.811972697115358,39.747870212908524],[45.782101990485586,39.858752635264516],[45.580051407499795,39.977724071034864],[45.731319253786836,40.108542155825404],[45.737009187768457,40.277999574091318],[45.376433896905048,40.638161306210925],[45.430298044090797,40.808936792727778],[45.375124730941529,40.949073979706483],[45.152310659207465,41.048105533223804],[44.969211490358838,41.027422774364382],[45.001647135877498,41.290868724219798],[45.280982017319765,41.44933636630428],[45.715437265062796,41.337457198514088],[45.782997057312798,41.24179832058163],[45.911606017360135,41.189743332472752],[46.221479472439164,41.212732176652047],[46.310333169121371,41.290523739263342],[46.339810356673368,41.404881082891215],[46.286132252305627,41.541883294275365],[46.190719536383718,41.624967333438974],[46.184548497114321,41.70207683699833],[46.429986842398002,41.890715513820368],[46.749171124903363,41.812345248164931],[47.146304283200934,41.512471786573052],[47.331627915327758,41.28235998746738],[47.760566697765711,41.203079271365397],[47.885329641140423,41.247081805185992],[48.056179467639467,41.458514168577786],[48.366996865039219,41.587982669903091],[48.572897578000735,41.844192099441834],[49.048592177232017,41.376550131408031],[49.222561802123479,41.034640211287417],[49.462710058619969,40.794834993483583],[49.747419199612757,40.595939745564706],[49.990580745281271,40.576533007178377],[50.188466844955713,40.50088084673088],[50.306670303838381,40.412082520697275],[50.365536198206378,40.279796482423976],[49.918962088336912,40.31550133872372],[49.533255144969132,40.166222340518217],[49.328476225260474,39.60764322844971],[49.362555823823534,39.349723430306831],[49.266294070698173,39.26323914703503],[49.165204829753584,39.030420758023965],[48.978733935601305,38.988908404171433],[48.879903130563427,38.882404704493268],[48.868506605336343,38.435745061285274],[48.63555734513124,38.39895165457331],[47.996752582964781,38.853870139977403],[48.12872361697859,39.041916498832244],[48.104705408595791,39.241100063090542],[48.181960929434389,39.440862679247417],[48.134836508761509,39.568452886710794],[47.983100203605986,39.676103660716848],[47.792730534278697,39.654654954088315],[47.467988473342409,39.493914422640792],[46.988730507160184,39.180429490786018],[46.846738724807651,39.138165827136049],[46.549707140361875,38.904576164999455],[46.480108856701207,38.9272718182905],[46.477668143151107,39.063562180807146],[46.401687215988375,39.167792051921168],[46.410321738953598,39.310739994784257],[46.325642185705256,39.51764669837867],[46.071616998866759,39.675180332526232],[45.921303988053317,39.675226060371592],[45.813411286951194,39.570568674775906],[45.80322297506811,39.416672549258962],[45.977157516858668,39.243797119246906],[45.983507642779323,39.130993456686156],[46.114278005582705,38.87804714164082],[45.479793537368103,39.006419403891201],[45.141370955606916,39.254426700930601],[44.768686545763011,39.703351458417991]]],"type":"Polygon"},"properties":{"alpha2":"AZ","radius":282000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[15.736847696159515,44.765948043809509],[15.822952348808796,45.202543518181905],[16.023282362939035,45.191238642440595],[16.160329448933929,45.074889390308577],[16.275396601018592,45.047038332021472],[16.530732408676027,45.216452550954592],[16.767589731132073,45.203100462762308],[16.918665332527368,45.276268558152061],[17.184306515792422,45.162087435641702],[17.511139661484556,45.126773059286776],[17.658492410731288,45.162786448211925],[17.853359415430237,45.089385439300464],[17.996344382788212,45.141526116369263],[18.284938667062928,45.134010031120944],[18.662522932024221,45.077198051132832],[18.93102518529226,44.86841765964725],[19.312604066764759,44.897189905241319],[19.356549663122202,44.85853461884583],[19.334262354106176,44.780746758848302],[19.164585691029515,44.530058386332975],[19.148353362723608,44.418048952597267],[19.208748156429998,44.301366530732878],[19.583551214479847,44.043344917227238],[19.458013852729081,43.860190720873184],[19.493525250987101,43.637853926094834],[19.451176035916401,43.562298081333829],[19.293812920256176,43.579560893936794],[19.111452835505716,43.494562346829035],[19.026457971872453,43.292635485300593],[18.836146161893289,43.318221596680992],[18.700410894833567,43.24791526177578],[18.623396954738769,43.027872401603645],[18.478787999409271,42.880811920136786],[18.534796961233702,42.620255448928127],[18.436331005293756,42.559938564240412],[18.308262924403127,42.598139287571144],[17.782678784090965,42.884013539218543],[17.585405842872582,42.938507817517269],[17.55525685020973,43.073519824515863],[17.293234834120359,43.305734473916253],[17.219946804794038,43.451706679826621],[16.717070157262267,43.776332757856295],[16.304507923504151,44.120816103881943],[16.214430263824394,44.215239179706039],[16.107543286101222,44.491790285638537],[15.736847696159515,44.765948043809509]]],"type":"Polygon"},"properties":{"alpha2":"BA","radius":200000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-59.646455050869925,13.302956530060385],[-59.59168969766494,13.317392514538248],[-59.502960087524308,13.214385926935106],[-59.486984760705141,13.197805623065841],[-59.469209710660152,13.183171240971802],[-59.427986451236649,13.152749570057741],[-59.493504286636764,13.082205897563243],[-59.52190224241766,13.062499028789997],[-59.61117578801953,13.102278253017973],[-59.642565539776982,13.150357697490675],[-59.646455050869925,13.302956530060385]]],"type":"Polygon"},"properties":{"alpha2":"BB","radius":17000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[88.023700613542786,24.627890275773847],[88.149945907756276,24.91445129227829],[88.349687525497231,24.968329114203041],[88.580486597191111,25.331853611124938],[88.540451159075246,25.501239927684924],[88.343578929092885,25.71441461281989],[88.084847548921303,25.888267595137592],[88.150952863800683,26.087058337891708],[88.351944492238971,26.292033427147942],[88.346183317890961,26.504765287834189],[88.418126656542555,26.571315432321114],[88.750749758334649,26.33835907026797],[89.018572301578814,26.410080097171704],[89.148796109351593,26.152335601864813],[89.332927547873183,26.021587337512447],[89.487969275940472,26.041680749777793],[89.589516850674826,26.189394717169524],[89.67078865551062,26.213580695593176],[89.822702648291653,25.941377532385783],[89.817869665217387,25.415045334264146],[89.944236018260042,25.274432254330833],[90.439395903918552,25.158927116877685],[92.049667123391231,25.16924299021991],[92.468131026453023,24.943962467324663],[92.474822437263313,24.868643978193411],[92.244210554167992,24.778371028292849],[92.101739791758675,24.408151460321172],[91.942281493820587,24.319090670747908],[91.876765452968229,24.195458065682907],[91.673417920420263,24.188157163894836],[91.57122910750158,24.10682249558705],[91.392929657508006,24.06680242228127],[91.274629541327485,23.960586654502929],[91.162487844057793,23.641926491469196],[91.297732075033736,23.278894521356499],[91.563493115592337,23.152421933295109],[91.705518470417047,23.209832682908388],[91.898352061981342,23.464032310775327],[91.929813130618612,23.68578855306535],[92.14753641585861,23.721707949778882],[92.24589789993324,23.683385452640234],[92.333986108387236,23.323803311291968],[92.356147229391468,22.97307313325884],[92.491956448612171,22.680562488312592],[92.631401910753326,21.306272669441313],[92.568604570292379,21.263594280724273],[92.396081090241111,21.283450823103173],[92.292016634819873,21.20043381069901],[92.263110832876492,21.070488861414152],[92.323859776625142,20.792048015054927],[92.056289065015378,21.174898327752341],[92.009463956650535,21.410772893800299],[91.8597510718579,21.533024257602371],[91.819974064325251,21.809825593567176],[91.870537195323209,22.068209242141336],[91.806511676648853,22.281611800191328],[91.689885324054202,22.380946159306369],[91.510444313358533,22.3530670892549],[91.302416655564613,22.39623334696984],[91.205906895270033,22.327402535761259],[91.150598449083404,22.175325334213916],[91.044740440579233,22.105459077460349],[90.90547799457903,22.1485177449212],[90.608874078236298,22.055260529926521],[90.421510341317713,22.053213063032736],[90.23043392361285,21.83001182009205],[90.15887913781998,21.817112876781874],[90.00471354039037,21.952955807342136],[89.874873892718071,21.979737007703548],[89.56851935506306,21.767690805276278],[89.230501745664697,21.725842538787973],[89.09418775400691,21.872853158069052],[89.046591820415912,22.282585583540413],[88.92092171607004,22.632057454442212],[88.841737042568639,23.13380502071977],[88.706215492236637,23.288721837061377],[88.709618044501141,23.450054580141632],[88.567664827625819,23.674418142348003],[88.704058802642251,24.022976664616763],[88.706829109772997,24.213966040052856],[88.603995936030685,24.323141477912259],[88.145618617266322,24.485977088497993],[88.023700613542786,24.627890275773847]]],"type":"Polygon"},"properties":{"alpha2":"BD","radius":383000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[2.5251587521506957,51.096983674666909],[3.3499851619891534,51.377433365405771],[3.4645569085506831,51.286503897075711],[3.8968221886888439,51.210460798323112],[4.498647046525198,51.4735702099386],[4.6367155896579098,51.44204870908888],[4.7556730464094903,51.490855864677172],[4.9070416356166504,51.430835533981423],[5.0309627584051579,51.468827582545167],[5.2048022938103795,51.285953287090337],[5.4768494805413299,51.284838164694584],[5.7999699778271339,51.149956890364123],[5.826896956891769,51.125554497867988],[5.769726351722011,50.941582651979594],[5.8083915456985329,50.834870721993525],[6.2357288829385453,50.59652903229675],[6.3406878346199083,50.45163343992381],[6.3641722984021403,50.316300018521922],[6.198466495919928,50.238131752546352],[6.1163767526835366,50.121288740078015],[5.9676821140973271,50.140199524838998],[5.8495242760913095,50.058213203739705],[5.7609494965183092,49.873552771060751],[5.880078542528353,49.644763833941077],[5.8152785121576516,49.553991886458746],[5.507380239258187,49.5110925392338],[5.2601587314018907,49.675659068943212],[4.8677505229839388,49.788373788450635],[4.8169579138228844,49.910241252933361],[4.7222478957662908,49.978466556361433],[4.1494660899371789,49.971798694591918],[4.1277768581110426,50.190549357867901],[4.0529909447507046,50.297566519980393],[3.812708266647598,50.343026176480855],[3.689386695776836,50.306341319113599],[3.5590022175190703,50.475227912245941],[3.2735263436377879,50.531705458309787],[3.2174180387122857,50.674606808195392],[3.1107969334148158,50.754091503813015],[2.8397845239255108,50.712055817676308],[2.7657883414890545,50.747533295500276],[2.5969677837436711,50.876071992098929],[2.5251587521506957,51.096983674666909]]],"type":"Polygon"},"properties":{"alpha2":"BE","radius":160000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-5.5232950293250989,10.426083252658932],[-5.4582589786163318,10.771429892322065],[-5.49022969991438,11.042282555086194],[-5.3194540781075359,11.174765171036503],[-5.2482280545689477,11.38996467508753],[-5.2879457496470499,11.827832294623162],[-5.1058456011535638,11.967340231300794],[-4.6883296823053442,12.082885984077276],[-4.467987996099577,12.297306268535378],[-4.4216627172869281,12.513964089818387],[-4.4803007118732916,12.672108440467243],[-4.2812334722952015,12.806293555613721],[-4.2572126798819854,12.940970851111013],[-4.3284404391784026,13.118979736370493],[-4.150880880569356,13.306044268849055],[-3.9473273966304157,13.40195837353267],[-3.5991761354570504,13.213638841797581],[-3.4325308523529841,13.221645097831075],[-3.28213373199155,13.355400905802931],[-3.2484212531979133,13.658142475434811],[-2.9877412733022193,13.703037958642211],[-2.8712409303480455,13.954206042533455],[-2.5865815472332327,14.227376073192199],[-2.4572369395522387,14.273843596655226],[-2.1250061022133178,14.218834735495474],[-2.0351230411288843,14.290903717103857],[-1.972856016956936,14.456342477805357],[-1.6813622779657398,14.515145227680748],[-1.0426739092387012,14.825447194448675],[-0.76033575196935421,15.047510497213599],[-0.66642940921625815,15.069601091650588],[-0.23589185100651791,15.059193693018965],[0.21717886511979489,14.911319751519324],[0.17994168976964217,14.51176912627319],[0.38229034834356473,14.245762499843874],[0.36770113434894319,14.104670313990733],[0.43648842233310819,13.961762206183801],[0.61619717875326441,13.72127837784196],[0.94224210909088968,13.583810328204878],[1.2008141958441636,13.357498927250555],[1.0546021051721912,13.21518471058911],[1.0768005717314335,13.027826001910629],[1.5665211924211671,12.639642669111353],[1.7868271427636737,12.616989636077115],[1.9562313878165993,12.707220095498117],[2.1044551319678435,12.701084222788779],[2.2259725711057072,12.466053178481479],[2.1522515317404851,12.323843734200651],[2.15547456786812,12.205267758775118],[2.3888826702720518,11.897024660908464],[2.2842471405057934,11.687997217075043],[1.9803041050305372,11.418671151809727],[1.438996633979744,11.412020824277294],[1.1694650911862783,11.217953405197187],[1.0661201405683585,11.061315731450481],[0.92449962393173846,10.993085500428839],[0.49277635142573334,10.955224600021692],[0.39244797675832876,11.013911847866577],[-0.20622037053542255,11.141924239413775],[-0.63213297106454347,10.927244635347964],[-0.77281823829787,10.991194879124331],[-1.5103356346040147,11.019631488859554],[-2.7628249069554092,10.97066536233489],[-2.8701914914337694,10.859026107597057],[-2.9124352387319048,10.620943708287005],[-2.8698605349264859,10.498592072017706],[-2.7913727939233008,10.432266811334562],[-2.7667551659817597,10.238210018530731],[-2.7689199182911732,9.6967604162884147],[-2.6961221919031,9.4814213213111529],[-2.7666733903638381,9.4249576320929265],[-2.8166297412180117,9.4260652356732457],[-3.1609308402783296,9.8353117385704518],[-3.3801572644181785,9.899147795843513],[-3.8640845189163828,9.898389777987715],[-4.2311902466649185,9.7583991242101895],[-4.3323576622276754,9.6459574908166807],[-4.6257857485206264,9.7138476217057637],[-4.9697252166483432,9.9302074924142154],[-5.1323141614513705,10.262519411468682],[-5.3822206364442771,10.314239426675798],[-5.5232950293250989,10.426083252658932]]],"type":"Polygon"},"properties":{"alpha2":"BF","radius":466000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[22.344455547889794,42.313885369713994],[22.465578408582651,42.446450129843868],[22.466984007515777,42.842286676144518],[22.716669986356958,42.908207297129884],[22.880994047809825,43.108647285437186],[22.839599115815027,43.276597985189731],[22.5509953348339,43.458662577245988],[22.369812401045376,43.781340775797574],[22.42097226197134,44.007188003525656],[22.547478146629086,44.063442051560671],[22.705063646545149,44.237526075170443],[23.028280617903878,44.077850131727232],[23.062284954686948,43.962160186936522],[23.168137768128958,43.882888499595595],[23.958309879086482,43.786037208000955],[24.430566826468265,43.794137563939202],[25.505525909352318,43.673397335567358],[25.786910794004065,43.753591552788933],[26.220089638383481,44.00847308901124],[27.086861632743457,44.1671651831115],[27.426139080083463,44.024254498341534],[27.884189609432539,43.987072948586935],[28.044724916917772,43.832507103421058],[28.212417511886198,43.775605245500927],[28.585096212056193,43.7420021682182],[28.561568317407577,43.501424412895041],[28.465342222827498,43.389585348920598],[28.174859010619894,43.399010021264694],[27.956482127918143,43.204770103251562],[27.897166316762064,43.020627243548404],[27.888626872626197,42.749864562051918],[27.72464388765556,42.679562209510017],[27.638024689384746,42.560545180667958],[27.665800783965786,42.394844080406379],[28.014096268528704,41.969318568402613],[27.534880922842035,41.921040647682524],[27.21452282969787,42.079540227819528],[26.65209679356439,41.956433415545284],[26.511304027709468,41.826586915546741],[26.199970431252392,41.655258739101761],[26.135148889899249,41.385927983017766],[25.928137558098832,41.313214193965173],[25.251224064793089,41.243817333673938],[24.979645178566614,41.367763801569112],[24.773720977057671,41.35635234180117],[24.472009678034286,41.522373432508488],[24.218594104012865,41.530569728877566],[23.635130972689833,41.387006575162992],[23.282357025537259,41.385441852290754],[23.155884505574161,41.322298299558497],[22.916350407765744,41.336477272794191],[22.965578588348436,41.703934524382944],[22.855416855879298,41.950711336628821],[22.578858765578147,42.107584504874467],[22.344455547889794,42.313885369713994]]],"type":"Polygon"},"properties":{"alpha2":"BG","radius":297000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[50.452704142855403,26.190799623116831],[50.470076928252126,26.228756236772117],[50.564051534688652,26.24613049831154],[50.585680842140327,26.240541321930934],[50.58494122770616,26.227569166977748],[50.58529808697746,26.207219134044113],[50.587721353291833,26.187010745456472],[50.592185930625497,26.167153284939797],[50.598645582566725,26.147852401909798],[50.609560985436204,26.124401309268514],[50.617341874277194,26.00234532264027],[50.606957881588492,25.883172134463923],[50.574813745401293,25.807168308430239],[50.5442667259994,25.833682975209893],[50.466169153151689,25.965563540575324],[50.476371242123733,26.006798255015966],[50.48042559412297,26.027218511979147],[50.482335229739583,26.047949598694217],[50.482079456894155,26.068766881053534],[50.47966104704318,26.0894447909665],[50.475106205148613,26.109759270528908],[50.452704142855403,26.190799623116831]]],"type":"Polygon"},"properties":{"alpha2":"BH","radius":32000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[29.014522217235367,-2.7202362945147311],[29.063336403978884,-2.6027539431098798],[29.102039558286695,-2.595990436378266],[29.296898690318702,-2.6732071310780472],[29.42755420426564,-2.7970883369084318],[29.715381320616856,-2.788111591818534],[29.821701586387764,-2.7427320596290312],[29.88328078954822,-2.6696325329363328],[29.930377221511925,-2.3397580313376518],[30.118500600502276,-2.3872196284483995],[30.408438853171276,-2.3132330608913176],[30.55319550847922,-2.4002885261583113],[30.463090028676518,-2.5879874228051833],[30.473627270985471,-2.7979360508080413],[30.565839326068261,-2.91816263509115],[30.780132042910154,-2.9850752902034454],[30.811148211910833,-3.120831014043961],[30.790043042933995,-3.2744239832819462],[30.696163649169577,-3.3199918554888068],[30.631723469463743,-3.4185088546947124],[30.434839364507756,-3.5844222301064566],[30.376507352730595,-3.7349091496552487],[30.264008814457124,-3.8608213762243704],[30.146918947462517,-4.0851918030437826],[29.950394145702756,-4.3038502922442152],[29.717705809773442,-4.4556445268163882],[29.403425421172567,-4.4490834364783396],[29.332714335477348,-4.1056196457310294],[29.212479051778558,-3.8382576720898816],[29.22076501906378,-3.0760611151772688],[29.016838886391945,-2.7995070777983235],[29.014522217235367,-2.7202362945147311]]],"type":"Polygon"},"properties":{"alpha2":"BI","radius":157000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[0.76365083020939051,10.38673578009301],[0.7877460524503076,10.710156140325076],[0.90068634793226987,10.993104842966661],[1.0864259154822322,11.141820798477385],[1.1460272837332262,11.251745394600929],[1.2921980042350532,11.295459656613172],[1.42684107446798,11.446894626563122],[1.6847831320542948,11.415757255434769],[2.0542951573555377,11.480602405565817],[2.2805945191861801,11.685125193468672],[2.3808596288413924,11.878793490828588],[2.406374990879288,12.008857435630702],[2.3662504937148667,12.221749764011211],[2.8052929525138994,12.383582908520543],[2.8779914151523593,12.367466024927515],[3.5951270967662898,11.696283792057379],[3.5026513196465521,11.52248277650691],[3.4954583587510317,11.409673242622022],[3.7161444213999584,11.079502853268281],[3.7547992098223411,10.79034348520479],[3.8342332538100634,10.607444010357103],[3.781091153046428,10.431786972136623],[3.6456835783713526,10.294676100930316],[3.6456696601956384,10.160143935039166],[3.5570019958181858,9.9075133131659374],[3.3851993895833674,9.7978776592304051],[3.3292800576755641,9.6671387193186291],[3.1662048915389409,9.4905750234846256],[3.111740794251435,9.1928269248167052],[3.047457781092866,9.0878798452692831],[2.8457654649317718,9.0382990981652132],[2.7550311352290309,8.916919091042903],[2.6901075417282607,7.908040393642775],[2.7848880671285459,7.4767942524316435],[2.726199111935288,6.9342617807598632],[2.7743816339448508,6.7117243362676744],[2.7062275218203733,6.3694411177298456],[1.6228048341082513,6.2170629592700397],[1.7060136750439894,6.4215999393813101],[1.5972377646972851,6.6149752549735465],[1.531243151019029,6.9923402485335933],[1.6247070312500109,7.161664148517465],[1.5970133036631717,9.0494309748190709],[1.3859845844682557,9.361736167639874],[1.3091249597835009,9.9926992873669978],[0.76365083020939051,10.38673578009301]]],"type":"Polygon"},"properties":{"alpha2":"BJ","radius":414000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-62.875161532061384,17.913608341567787],[-62.87476821440363,17.918107639891442],[-62.874032927594989,17.921963092142363],[-62.869975987919211,17.920793325442908],[-62.843385298641564,17.915051688871653],[-62.80468596608609,17.909416422021078],[-62.800107382988742,17.908512562913216],[-62.802489407849407,17.904499292173551],[-62.804909582158928,17.900852896213877],[-62.807147337314404,17.897746845777917],[-62.809616584078448,17.894821453861347],[-62.815319183230848,17.88856860391353],[-62.81827989520729,17.885585420616824],[-62.821699818386207,17.883142200724951],[-62.8277986460824,17.87916282378843],[-62.832014407872514,17.876699780780974],[-62.836853277748368,17.876048192987923],[-62.841959183672053,17.875615770987871],[-62.846856034905507,17.875445904230176],[-62.850999346960037,17.878061429404315],[-62.855380822983648,17.881142711323356],[-62.858800751804644,17.883777020759208],[-62.861426098752787,17.887203834548387],[-62.866139912357752,17.893944147460154],[-62.869102763902376,17.89873697487333],[-62.871505222282288,17.903833824647471],[-62.873478773834698,17.908719956313522],[-62.875161532061384,17.913608341567787]]],"type":"Polygon"},"properties":{"alpha2":"BL","radius":5000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-64.861110560129717,32.273766571157729],[-64.838922396830625,32.28582648864635],[-64.780629504485248,32.320797581305555],[-64.725515967998149,32.360591258135607],[-64.710392753450677,32.373599502480126],[-64.694358114308699,32.38588862489167],[-64.681634014327742,32.384127172199889],[-64.669888676405336,32.381291508433371],[-64.67975549926129,32.365591392233853],[-64.718804878398601,32.309832940699735],[-64.730905349955378,32.294271201430803],[-64.748982908472499,32.286410475063072],[-64.802617088900448,32.26621021036928],[-64.820229370201389,32.260501295480601],[-64.832495180903365,32.261244006346374],[-64.844766703927917,32.263151569616518],[-64.853478624608272,32.268257840917286],[-64.861110560129717,32.273766571157729]]],"type":"Polygon"},"properties":{"alpha2":"BM","radius":12000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[114.06443378966172,4.5924574568290515],[114.1778975708024,4.5915120206696871],[114.29020269569186,4.606764439807514],[114.4163690770354,4.656974986657624],[114.55597856267677,4.7327443134127956],[114.74658267039442,4.8847807112441419],[114.84617634272573,4.9491116049585777],[114.99544974221179,5.0221339435528254],[115.04747656397114,5.0161446689405347],[115.07865369838538,4.9540686834559029],[115.16816590396438,4.8665035667271894],[115.22779581658716,4.750529782835148],[115.26652808963445,4.6339504156111859],[115.28233303333293,4.4676426420471742],[115.32647262979027,4.3807462380831659],[115.31905597774785,4.3654719328166625],[115.29513489021014,4.3545888658035459],[115.24669566432186,4.3475421378345427],[115.17066075060811,4.3643842924446528],[115.03587597242426,4.4164544915641617],[114.97844414190716,4.4171805931364894],[114.92317425266495,4.4015542780496739],[114.87462460919667,4.3708643041308104],[114.83679927341164,4.3276417827699776],[114.77597932883036,4.1688982825581906],[114.72486006555644,4.0966627618986227],[114.65398040719099,4.0378727554036713],[114.60834214672956,4.0242376409741007],[114.57198116606155,4.0493076819115776],[114.51624973111275,4.109196836357623],[114.43580300980939,4.2120035819730779],[114.3848147993764,4.2458402839582341],[114.32307949706031,4.2630409736402779],[114.28992156528564,4.3043016946794923],[114.27885171458654,4.3721308439803215],[114.22863788529186,4.467844712798958],[114.17466650605499,4.5208800589165437],[114.09532359213109,4.5655362407314461],[114.06443378966172,4.5924574568290515]]],"type":"Polygon"},"properties":{"alpha2":"BN","radius":74000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-69.645426365810337,-17.248535086055043],[-69.044163693031038,-16.591365438452865],[-69.033697959404464,-16.32800041107302],[-69.217334957544708,-16.148968512588958],[-69.42069907989378,-15.640602138221272],[-69.227350577529066,-15.239067542513572],[-69.373821384289869,-14.894527771231838],[-69.359257133588301,-14.795458308696322],[-69.040762371085179,-14.395971851599178],[-68.93248151014005,-14.153819994897921],[-69.073853374199871,-13.682814963750907],[-68.981125865589249,-13.472706410961862],[-68.978339606298491,-12.880175163116585],[-68.795218059746631,-12.690649059851429],[-68.723714756418531,-12.485221363093428],[-69.578234121968279,-10.951969163908428],[-69.221773865544918,-10.95681332498137],[-68.583136678668183,-11.090882504401769],[-68.304525271697528,-10.967643460499223],[-68.071540086376302,-10.703352624490472],[-67.713298882784855,-10.636667276538239],[-67.420347346105842,-10.392232265917961],[-67.125823061201459,-10.276638987589841],[-66.580026823718455,-9.9021923410830244],[-66.259066723951548,-9.8255338389701361],[-65.60261069176731,-9.7834334893925572],[-65.401028242145912,-9.7121674560566866],[-65.309605912665447,-9.8726622865143678],[-65.29877591520669,-10.146784816957682],[-65.443373799785988,-10.501861782764013],[-65.333660647253197,-10.897570537868217],[-65.383327769849672,-11.225805815150009],[-65.298151290878053,-11.482325911566333],[-64.965810665539422,-11.965103377000878],[-64.513624201727893,-12.251165893665014],[-64.355448300939599,-12.455769566508],[-63.958616329636975,-12.521902123092095],[-63.688629071520651,-12.478259249584681],[-63.328352345152446,-12.68196285794545],[-63.067684251118102,-12.669338470391949],[-63.004470225848756,-12.807859422852442],[-62.800227539722769,-12.974381756119085],[-62.360476966423917,-13.128656737898831],[-62.11821000310163,-13.159931715450789],[-62.062853776811558,-13.276936642061793],[-61.805789534523797,-13.510683933200395],[-61.491242360880413,-13.537068248621454],[-61.077032015826113,-13.489986837166777],[-60.506788796410525,-13.790062405319993],[-60.405224358587382,-14.019230013363348],[-60.44506700719721,-14.246148175150498],[-60.299142718242074,-14.618657477825256],[-60.273588500480464,-15.088635728840069],[-60.344485389603456,-15.164233959728685],[-60.367020704362432,-15.280265258702704],[-60.242591777021687,-15.479697702437059],[-60.159869698791503,-16.183391987000775],[-59.998444490346031,-16.275714435880651],[-58.545299154542036,-16.327958707945808],[-58.345811961505177,-16.284595158859094],[-58.350604102286908,-16.49074940344028],[-58.475346586633286,-16.719935724699209],[-58.389067004140912,-17.217103078230657],[-58.007249902071365,-17.50032479200177],[-57.832572329055125,-17.512397582298732],[-57.590778677252374,-18.112254474911204],[-57.495962360273495,-18.214748296170004],[-57.589187299956464,-18.328261055969662],[-57.743646801901903,-18.794559634469341],[-57.717057542174068,-19.043887585872426],[-57.821911189858213,-19.126402671419964],[-58.08083817599713,-19.663542731269626],[-58.042199770062972,-19.816632550890823],[-57.86103265115446,-19.979629884719088],[-58.093899648034302,-20.150856233649169],[-58.159496770224614,-20.16435605094642],[-58.15047548913936,-19.923453182383877],[-58.215261349067227,-19.802024577197521],[-59.110065862309114,-19.289713233466344],[-60.007275754911262,-19.298460756267765],[-61.662085307538348,-19.631189148170808],[-61.781519151092866,-19.718395766776023],[-61.917191439707707,-20.055242563789175],[-62.266533921283028,-20.565687013921096],[-62.276915605993842,-21.065976301919061],[-62.651041407583598,-22.233281611752719],[-62.918908458086925,-22.001921497432065],[-63.900778603175091,-22.033815046150689],[-63.994258391433277,-22.108907934029958],[-64.212672283862133,-22.498323959586681],[-64.325320664881332,-22.827235346930468],[-64.530472686949835,-22.359656096311816],[-64.663885004334105,-22.202110844848978],[-65.04833662841321,-22.103742295423473],[-65.770935651433732,-22.099369733855802],[-66.019970886632294,-21.907911080030345],[-66.149454130614245,-21.883836757497718],[-66.26583225671493,-21.945493997484487],[-66.365318692622168,-22.113587296354066],[-66.702045784243595,-22.240497846284935],[-66.800496992349892,-22.409469130640218],[-67.00617398606478,-22.534412464958248],[-67.195008081534141,-22.821474734213744],[-67.711827632311184,-22.888660337928748],[-67.879234564017608,-22.822793206863516],[-67.884718370608695,-22.494064013740555],[-67.97820251741696,-22.104154816421605],[-68.076553039484679,-21.982690607129452],[-68.186139826424323,-21.618470105536449],[-68.213208123768339,-21.283969894996986],[-68.405209287522339,-20.995404472910955],[-68.558062494056273,-20.90184377792237],[-68.573341199900057,-20.617163014134686],[-68.760318612252263,-20.416174848468309],[-68.755589429456748,-20.090951320110825],[-68.625280381954752,-19.912112020907632],[-68.698061759225311,-19.721157940262028],[-68.558229699463865,-19.52427792269771],[-68.554423402082406,-19.392511288403512],[-68.96809305889947,-18.967902532871094],[-69.157644723774126,-18.085720498789723],[-69.31315980719674,-17.943011591050904],[-69.367907073165412,-17.766144488860931],[-69.494790732180533,-17.619424587079006],[-69.52720347140766,-17.398159438262184],[-69.645426365810337,-17.248535086055043]]],"type":"Polygon"},"properties":{"alpha2":"BO","radius":854000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-68.370846147258789,12.257598043583119],[-68.370885815814759,12.262499007791243],[-68.369443819794483,12.297031017750209],[-68.36902665066313,12.301603238875332],[-68.364783422215538,12.299849885390646],[-68.223883719379288,12.233327952000721],[-68.219691629403528,12.231093232694713],[-68.218724021602426,12.226442283986385],[-68.206542717395536,12.149221516246278],[-68.206035664131477,12.144608461503463],[-68.207671898176002,12.14026563808725],[-68.252427300056354,12.036526758879672],[-68.254369823411153,12.032563451952607],[-68.256697251522695,12.036313688900554],[-68.28003698466955,12.078333383602784],[-68.282028115177397,12.082332796734159],[-68.284707614363725,12.11016965914035],[-68.288489745004242,12.128883932645266],[-68.294039129239906,12.147152289977102],[-68.301305194346682,12.164808247692919],[-68.310221723121046,12.181690903278298],[-68.320707457331494,12.197646401486335],[-68.332666838242147,12.212529336453361],[-68.348220931684935,12.228284243371922],[-68.351778278419488,12.232417743829636],[-68.368054430602115,12.253569726084216],[-68.370846147258789,12.257598043583119]]],"type":"Polygon"},"properties":{"alpha2":"BQ","radius":17000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-74.001610049046661,-7.5557996373273353],[-73.96408336902563,-7.3790671750638417],[-73.80401059298994,-7.2532032156950779],[-73.75791447343245,-6.9058642536915871],[-73.15690378631372,-6.4595116492244973],[-73.140834411863906,-6.3414030610968135],[-73.235264326246522,-6.0984226180972474],[-72.978707448986398,-5.6243469974866258],[-72.886906930260963,-5.1229144496170536],[-72.667108882510618,-5.0470289523163272],[-72.352685585431004,-4.7862611135358426],[-71.844668824803705,-4.5045961314630585],[-71.010972534539462,-4.3576641336284716],[-70.799393968065587,-4.1735882066859773],[-70.726419624286933,-4.1597839164561385],[-70.40469535591923,-4.1503281157713703],[-70.169701118163928,-4.2913164459014119],[-70.038956616945256,-4.264386705704883],[-69.94524754570746,-4.1614772841166454],[-69.413976621741256,-1.2165430358037501],[-69.46798926089042,-1.0124271417635311],[-69.611679181435434,-0.76267654216001546],[-69.63402807217571,-0.54117633259308473],[-69.926160298313604,-0.31407407668633935],[-70.070222454787867,-0.13880407862773872],[-70.053692329211529,0.5784147674089346],[-69.629410045186447,0.68158721963673718],[-69.522242278992763,0.83726865045202914],[-69.608276001173039,1.0245694404952661],[-69.851911298619385,1.0598882598813459],[-69.848364161674525,1.7085024424327755],[-69.54293470581068,1.7730355137736196],[-69.325821050371431,1.7216111685190196],[-68.402710984937329,1.7254851985847059],[-68.286772139795772,1.7955037704816401],[-68.193741276865154,1.986740095407133],[-68.025218183106787,1.8158526320519952],[-67.893442298952039,1.7855992043448348],[-67.786812014512805,1.8325378941619974],[-67.558717662990091,2.0710980175308098],[-67.400516740076654,2.1164676093589989],[-67.119540474002079,1.70347299822423],[-67.042989013164345,1.2955186462148225],[-66.876108955820442,1.2228154000739133],[-66.380849819156595,0.78956766482832652],[-66.269491997225671,0.75622274844630522],[-66.060320668054246,0.78631230604348579],[-65.681469027990417,0.98320503773345613],[-65.478571665146717,0.87414235749645819],[-65.34801961237828,0.88133115103710302],[-65.02242581280214,1.1606240129765142],[-64.682891911763804,1.2849042436894518],[-64.485986583289787,1.4525570972463533],[-64.258202415487943,1.4896974360023538],[-64.115107849798591,1.630715394739872],[-64.032218594015092,1.9076973944340336],[-63.668693728971348,2.0568354665302047],[-63.562141562349424,2.1969505181870481],[-63.573254603029696,2.313743275750336],[-63.647953344736095,2.404209545043805],[-64.046336764599516,2.5025128411310376],[-64.018285948750929,2.7135830469217574],[-64.218619475067058,3.2047374976840257],[-64.242126555184441,3.6072253184827399],[-64.665363940364514,4.0077933727246728],[-64.817586585050549,4.2322882142632414],[-64.788542074091353,4.2757754622520538],[-64.533099246945056,4.143915701548516],[-64.192581414802419,4.1266233788607654],[-63.966265230732489,3.933926732604403],[-63.520632454449213,3.904099968416427],[-63.338722725564466,3.9436649979765139],[-63.053894508569407,3.6942665478540189],[-62.904709534525395,3.6685419133515329],[-62.770454892209877,3.7714614216143061],[-62.711917314904746,4.0177444902853896],[-62.472464762546608,4.138339632338119],[-62.114802505702443,4.1144065056687813],[-61.601833757006425,4.2725393715134192],[-61.279994978167707,4.5166436936781524],[-61.014654161675502,4.5374789103754134],[-60.906094081609268,4.6866657644411847],[-60.722826299001724,4.790135209539736],[-60.625149130396224,4.9101517334989788],[-60.638527239120897,5.0898212300489112],[-60.74145403422262,5.2019725753055432],[-60.469308116319354,5.191657582004062],[-60.241618931362964,5.2577093108455006],[-60.142155169013719,5.2385790996160742],[-59.990908298084676,5.0827527608991145],[-60.029907337240232,4.6071266410167055],[-59.961494863703805,4.5161291375260006],[-59.703472744081637,4.3809931637115094],[-59.710370679013273,4.2016696879612221],[-59.551380930222969,3.9335714801446424],[-59.679190608714507,3.6999416134316347],[-59.820301483956094,3.566701813755337],[-59.831413080484879,3.3493011595664748],[-59.968003892899127,3.0062207447281133],[-59.993560314709413,2.700202566497091],[-59.908396549945941,2.4223492233123647],[-59.755448907335676,2.2739782430322752],[-59.74319606816853,1.9533185654211418],[-59.621944016848104,1.75503079604977],[-59.242868256389229,1.3930426137267085],[-58.843482399812622,1.2353778577603158],[-58.584563594251989,1.3179792248993256],[-58.340538015181778,1.5873311274273114],[-58.11265134882359,1.554591408165517],[-57.982681331344693,1.648218711296604],[-57.559223905806306,1.7284265861126604],[-57.317444814534284,1.9632694650559523],[-57.118878183452665,2.0136790044401645],[-57.005435695647932,1.9292062882678249],[-56.836365439795884,1.8855739609247315],[-56.482810311882425,1.9419129926441276],[-56.171446781960583,1.9172702862307736],[-56.056946165634535,2.084880023491074],[-56.129181984605019,2.2993976218343484],[-55.957461508064164,2.5201796720296037],[-55.682907947517407,2.4153989647141794],[-55.424370051784699,2.442005145686418],[-54.978816006836048,2.5974048906339084],[-54.906506815963404,2.4934061074864573],[-54.722383097245142,2.4414608554774442],[-54.486567907670278,2.2333527521212257],[-54.171614987577371,2.1421437620375441],[-53.767844202812441,2.3545688766292199],[-53.537792505435426,2.259013917054582],[-53.334464589974822,2.3394584940785168],[-53.176211175301255,2.2145766317289204],[-52.946771294701357,2.2092055108329096],[-52.650043673081427,2.4303350785257289],[-52.327699442732836,3.1816116230358658],[-52.170502653641719,3.3536972509929992],[-51.990404739888071,3.7018654244013076],[-51.766903887734614,3.9924832868335849],[-51.638050657235389,4.0894506209606387],[-51.546860718285565,4.3106558811275297],[-51.461598199396143,4.3135051807777538],[-51.218483440155943,4.0893786633180174],[-51.076464539052779,3.6716412393479372],[-50.996774506153329,3.0867764126639905],[-50.68419585385741,2.2374245216039581],[-50.562701899764654,2.1454363676513393],[-50.342212315981655,2.1415775194798048],[-50.248593238494756,1.837722532602968],[-49.955532780187326,1.6547932337568325],[-49.881813130678509,1.4199060232671912],[-49.899143820189984,1.1630834495489881],[-50.141144869005885,0.95401421013612142],[-50.1882808298013,0.83004593886328037],[-50.158431871619818,0.71975389989132466],[-50.037082231197175,0.5947666444330002],[-50.007946041382048,0.38998053341040445],[-49.919476532269208,0.3166431508519098],[-49.738356081918589,0.26797495504909125],[-49.603280946569129,0.10705745651136628],[-49.400640463859411,0.057037852895407318],[-49.339947388559494,-0.075366222237734218],[-49.245373409289485,-0.14807162905639087],[-48.581024930431497,-0.23324394769053042],[-48.3928798751,-0.29752557247237804],[-48.480600067481568,-0.61463561757042295],[-48.402320508451595,-0.78492889061308335],[-48.255395276944995,-0.8243512483161628],[-48.037269434995267,-0.70619170415098786],[-47.807665693282431,-0.66370789279063869],[-47.656880706150872,-0.7038551515103646],[-47.393002628920541,-0.62739761495836721],[-47.094855682330127,-0.7431862512235976],[-46.811341353327556,-0.77990580842938018],[-46.552122702011665,-0.98655174787357691],[-46.219251586594361,-1.0314993999282709],[-46.044740058515835,-1.1032883583891329],[-45.679748345647468,-1.3173522332314467],[-45.458745732951904,-1.3564376205108928],[-45.297260313263941,-1.5151174412557684],[-45.102204427657355,-1.4723530850580635],[-44.978480227139748,-1.2675353256103461],[-44.888478414631635,-1.277056622954869],[-44.952817630061617,-1.4553722384875218],[-44.91456811210508,-1.5881240469100286],[-44.789857726458088,-1.7048703027500702],[-44.651406641846044,-1.74604362531445],[-44.331447757654011,-2.4127860381780586],[-43.983834061628215,-2.5783931582996873],[-43.537745710712073,-2.4986581830736312],[-43.380014802005732,-2.3763041826035169],[-43.234609314709353,-2.3857091659250833],[-42.936812080917868,-2.4652546872535614],[-42.259078897284105,-2.7872630410561348],[-42.01106747572404,-2.8013590025637765],[-41.876162556276725,-2.7468711661380247],[-41.468915473862005,-2.9178476657222667],[-40.474550588502609,-2.7957877114815699],[-39.964787815809409,-2.8617537217475069],[-39.019516298719189,-3.3872923518540601],[-38.694778712712719,-3.6460016184819986],[-38.475913933478019,-3.7176656392217486],[-38.047841494914131,-4.2153638103729598],[-37.614919485940909,-4.5924058073868297],[-37.30160479414161,-4.7132588738424595],[-37.154577642301454,-4.8989488019618967],[-36.866340854457583,-4.9649414991260175],[-36.607924954838168,-5.0915855261573553],[-35.979878048875499,-5.0546291968892652],[-35.481804792982409,-5.1661743682172823],[-35.392731791651997,-5.2509923032289789],[-35.234154791823556,-5.5715410095149807],[-35.096217495060792,-6.1749076436246746],[-34.988467463224275,-6.3938415607666901],[-34.805748528804308,-7.2883793310663387],[-34.874403620296455,-7.729181049317166],[-34.834874889345805,-7.971441253640835],[-35.1560687300314,-8.9259284841383408],[-35.337146682844512,-9.2245632023616384],[-35.837128119288941,-9.7602598770292008],[-36.398484218133163,-10.483909324913991],[-36.753997244777693,-10.662854469190295],[-36.942120582949315,-10.828335238389323],[-37.39618660690698,-11.45851347660555],[-37.688865349477545,-12.09991351583893],[-38.236320906165425,-12.84030947883892],[-38.401849690525516,-12.965944708204162],[-38.594471602590708,-12.989010083553886],[-38.909855127741899,-13.244452610555165],[-38.90379565991546,-13.473381097826188],[-39.039370050758073,-13.780265848654807],[-39.02247130548843,-13.928670437380692],[-38.942602717315161,-14.030738430994967],[-39.056492023026479,-14.654820055209777],[-38.88082027481336,-15.86425387091907],[-39.201971284862218,-17.17829568099517],[-39.15425678735302,-17.703849057453354],[-39.479993211086743,-17.984722355962173],[-39.652146779783187,-18.263791639150433],[-39.740020235800991,-18.661463645353813],[-39.700063793639707,-19.27782357627083],[-39.783458109675855,-19.57168243224784],[-40.016291873247887,-19.767807268993128],[-40.396098149652587,-20.569325132910059],[-40.596711271177895,-20.783612656721719],[-40.766196160222073,-20.885244069230883],[-40.953503786018516,-21.238379157964008],[-41.038182139516678,-21.506004041479837],[-41.000521120250895,-21.998880706269269],[-41.7259032563272,-22.329763974793032],[-41.957941405590532,-22.570298576854572],[-41.981951666506809,-22.68245667437272],[-41.941219613712697,-22.788269947690896],[-42.042511287360504,-22.946834883041817],[-42.833739751812935,-22.973124594608443],[-43.101791899520549,-22.932753065748216],[-43.224270398974959,-22.990970742966798],[-43.981958364418688,-23.099868659978391],[-44.098148933216784,-23.169092485558672],[-44.47885674915085,-23.204525707214049],[-44.662285176607512,-23.333245090423649],[-44.958073825148304,-23.390600992396468],[-45.196309711089164,-23.562744637271365],[-45.260385723414707,-23.68346473446066],[-45.261103268265529,-23.940989544050318],[-45.412762156027249,-23.934709795597421],[-45.523224364398033,-23.81446895439057],[-45.655853382638973,-23.768278673607661],[-45.971718750588131,-23.79648915371348],[-46.858754219918907,-24.232709403425975],[-47.137355447717596,-24.492973758281767],[-47.842032918614457,-24.963599670592927],[-47.929583095139535,-25.168139057372098],[-48.2028364265189,-25.416252583310122],[-48.360903515554298,-25.494621623145633],[-48.568043708519568,-25.865271537322499],[-48.587063164589203,-26.076741679751184],[-48.497863021740223,-26.218798508870691],[-48.647867883709026,-26.48519418958503],[-48.676708094722663,-26.684848287859371],[-48.541724157021754,-27.280052376866639],[-48.3781893130786,-27.45151080515874],[-48.486129694233341,-27.766874800799041],[-48.599847188587106,-27.886401737576453],[-48.648684079022956,-28.207147949654459],[-48.783634977992264,-28.437709537346457],[-48.799877432011485,-28.575160754560475],[-49.261382306877785,-28.86429007087278],[-49.508643300641353,-29.085612688475681],[-50.023549331675653,-29.786045502202249],[-50.299718097951022,-30.42566860036996],[-50.751293739019914,-31.07152627431282],[-51.157569637677092,-31.484552739202531],[-51.806340455602083,-31.9062858286363],[-52.189590017104798,-32.221387833152718],[-52.34108447260477,-32.440015916161549],[-52.655661340168628,-33.1417628535985],[-53.370671722836477,-33.741909810686721],[-53.531061923385295,-33.655362528066668],[-53.531088392588032,-33.1709376937348],[-53.482720649606073,-33.068701042988756],[-53.264030798654645,-32.870713369500699],[-53.240293923850032,-32.722311393540743],[-53.296463708196427,-32.623165129495625],[-53.601527316876094,-32.402888651462895],[-53.789339642184878,-32.052064517011452],[-53.940452113081342,-31.945988236852987],[-54.224137474035359,-31.852534931242253],[-54.599353849817582,-31.485600942602307],[-55.177533503551096,-31.276927488472978],[-55.510768501365241,-30.92531523003381],[-55.645195573238006,-30.918577077454149],[-55.868899355224521,-31.067241305983394],[-56.00455886685149,-31.078988378565544],[-56.050505843607027,-30.773099427802453],[-56.818069769495096,-30.12207016829371],[-57.056346302326418,-30.120674626274123],[-57.214528789943266,-30.283143369901243],[-57.552164190069384,-30.260999224137386],[-57.608566664408102,-30.187808720244639],[-57.389304888243892,-30.016753307137581],[-57.228486508004416,-29.785876071719645],[-56.929101953032429,-29.584684799389038],[-55.725310451625731,-28.204255116619283],[-55.122582043874395,-27.854914466317261],[-54.937601201262837,-27.706763208828782],[-54.828979094464579,-27.550776840199816],[-54.331960847778376,-27.420878336995013],[-54.156383980626472,-27.254061979116688],[-54.034177899376253,-27.234263947336498],[-53.839921588063824,-27.108394566286968],[-53.725305405812698,-26.881545262705004],[-53.753113229650907,-26.748598245624098],[-53.687158086101618,-26.215420686681249],[-53.823052575213602,-25.959496410887485],[-53.878451698062982,-25.725217936953978],[-54.000530480313493,-25.60454265905188],[-54.165358979976034,-25.55313820029281],[-54.443981578795025,-25.624720137643294],[-54.61562134972862,-25.575836649713398],[-54.610265597909475,-25.432803564694986],[-54.453918681802243,-25.165694748811489],[-54.31088802770244,-24.513859124126157],[-54.286104380913883,-24.316104080787628],[-54.331013712523657,-24.034427910355944],[-54.448953321641518,-23.897902688380633],[-54.610167617508274,-23.829197065578818],[-55.18914882836841,-24.016566023979102],[-55.415706071243349,-23.95122300397356],[-55.459622001674845,-23.710666637850146],[-55.538098026533376,-23.580913782999559],[-55.646029163034328,-22.69073050714335],[-55.813961456619928,-22.354240095740618],[-55.980521003015006,-22.285859218400248],[-56.241242219818794,-22.266058787886575],[-56.436367394470004,-22.147556561894511],[-56.633112420756646,-22.234592620155784],[-56.93723595600936,-22.271097159347097],[-57.217516067401192,-22.200593894495793],[-57.568869257039488,-22.181725420904392],[-57.69891217534002,-22.118469932043617],[-57.955733740428705,-22.108985262883341],[-57.985430379717791,-22.046350012874665],[-57.921637384043336,-21.707863730191349],[-57.945762809778643,-21.494077728576748],[-57.829263709845314,-21.037790694771292],[-58.159481817371855,-20.164799194486779],[-58.043570364624053,-20.081796998418007],[-57.996562169108067,-19.956817003000534],[-58.027506169709788,-19.846012246926001],[-58.131186835289661,-19.74447005483561],[-57.783832154830804,-19.046954554961978],[-57.78286493355246,-18.914251629451904],[-57.569831520274178,-18.164638450482581],[-57.826840778135235,-17.585914906780911],[-58.395794836169756,-17.234176662580488],[-58.477615109530603,-16.70653305721498],[-58.428965930956373,-16.495885771661797],[-58.508866318148691,-16.363643097603283],[-58.619643106493903,-16.325292436730017],[-60.175379195427553,-16.269118916423476],[-60.244669574073967,-15.503062176599217],[-60.582661930636725,-15.098529393795063],[-60.383223069592702,-15.067870845473402],[-60.284353086840767,-14.90427748389088],[-60.301223971879828,-14.644917678919104],[-60.461004319230092,-14.258505792566865],[-60.414890295219088,-13.984574888246902],[-60.489478678067776,-13.818716500199399],[-61.057145676152579,-13.502136384441032],[-61.789872079120791,-13.525352147917385],[-62.177257902433773,-13.164750914580367],[-62.765412942722421,-12.997019783388845],[-63.097005271949484,-12.7228860196836],[-63.34658772211926,-12.679873043623328],[-63.661223141042065,-12.488900305381486],[-63.777999717780595,-12.473796682992887],[-63.93859611476524,-12.529449451469647],[-64.420331033603205,-12.439523050345187],[-64.545962610423047,-12.239989083174079],[-64.806040301747245,-12.045215747077933],[-64.992333687059613,-11.975038397716421],[-65.045192056772194,-11.834922659106287],[-65.185530185766268,-11.749387205689766],[-65.206355396698228,-11.60179824381029],[-65.32181572148977,-11.439101912836914],[-65.39334266247883,-11.18863633711292],[-65.334192481902264,-10.902668458807314],[-65.446926143228254,-10.507429868627371],[-65.316898587204321,-10.251700490830624],[-65.303107357780391,-10.12950560813146],[-65.35240524458807,-9.8952444090341842],[-65.470456793629495,-9.8024721136664628],[-65.920569167179423,-9.7851265710474635],[-66.584498073015638,-9.905586930885212],[-67.115091007131397,-10.270864784101333],[-67.437470906849327,-10.404247558761856],[-67.7218988152637,-10.682892526896339],[-68.051394784944563,-10.696963876189368],[-68.311269727980132,-10.974999831325164],[-68.62270126806817,-11.108994612289676],[-68.727443557759841,-11.122184603902078],[-68.881163672791359,-11.011208735894163],[-69.242202604238287,-10.955224297258427],[-69.929991215933669,-10.931726163659659],[-70.336820327495545,-11.066454570689253],[-70.520364590080703,-10.996170737028859],[-70.642099114028923,-11.009889820984119],[-70.637337836101366,-9.9155082317644787],[-70.695711489625296,-9.773366557289263],[-70.895955003408389,-9.723368190983976],[-71.238071912956713,-9.9657629671513952],[-71.339425210394452,-9.9884278053810611],[-72.181359108602393,-10.003492798017898],[-72.29298631948005,-9.619277614815374],[-72.382478236608236,-9.5182639438352048],[-72.814346080306464,-9.4111819104981205],[-73.20894696217475,-9.4111942467990666],[-73.003668681946564,-9.15437161601748],[-72.997634579709953,-8.9798388145406864],[-73.30223098006806,-8.6537984620433015],[-73.411173100594937,-8.4493861892511166],[-73.548865746916405,-8.3456741840514255],[-73.645606906498585,-8.0732589329671782],[-73.775327111902129,-7.9363207805970815],[-73.802290850026296,-7.7648635871974658],[-74.001610049046661,-7.5557996373273353]],[[-51.28346858206573,-30.759717667608225],[-51.184945735701568,-30.936191541731404],[-50.999917475752568,-30.94551544626189],[-50.723500021399829,-30.748521760074571],[-50.726465833045033,-30.478317489608362],[-50.86327214792712,-30.376207319759629],[-51.149071426996443,-30.387034112900249],[-51.26248604807455,-30.515403505759831],[-51.28346858206573,-30.759717667608225]],[[-51.160949488450242,-0.66679909175223118],[-51.044795412902189,-0.7964620597671862],[-50.858855670022599,-0.77866847035677877],[-50.738944652362626,-0.57783355436169925],[-50.758957815088408,-0.42761945657512113],[-50.981145128891804,-0.29960673623688239],[-51.149227742109645,-0.32022815959883899],[-51.242960992987939,-0.48173715420953056],[-51.160949488450242,-0.66679909175223118]],[[-52.009475515563068,-31.539594244985455],[-52.008942844169361,-31.65423211081032],[-51.930073092202861,-31.761401332351937],[-51.82077889520707,-31.795997611382184],[-51.684408942451185,-31.767100552733233],[-51.254259790801321,-31.449991024884635],[-51.165378240375951,-31.300927431494785],[-51.175929007007305,-31.156649238037158],[-51.288683275526701,-31.051200651817386],[-51.442935601675444,-31.057457590589426],[-51.921888156427457,-31.345261493433977],[-52.009475515563068,-31.539594244985455]]],"type":"Polygon"},"properties":{"alpha2":"BR","radius":2627000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-78.983325504645165,26.688615116095256],[-78.80767130592973,26.711926603850028],[-78.597000808813959,26.796902608098822],[-78.414241866300188,26.769541171720693],[-78.175794389772634,26.797253026158934],[-77.862377822738566,26.939157399687581],[-77.534256060524058,26.902474541616375],[-77.067308092933757,26.529592014562841],[-77.039262947349386,26.333453662815185],[-77.047212578080121,26.218131169705696],[-77.030784789913184,26.054771642669891],[-76.944860394061465,25.82455065097141],[-76.850240225725756,25.690374531480867],[-76.59603807845059,25.458668059967998],[-76.456489410280554,25.374894545246899],[-76.343975271026466,25.331095679625207],[-76.127363002844859,25.139979472607223],[-76.052246047314725,24.994822799721288],[-75.95918715791295,24.877985756223435],[-75.846030915244611,24.780484390206379],[-75.654175707186468,24.680117762246731],[-75.503773015218599,24.4204433514229],[-75.350207587770527,24.251208935329885],[-75.149583548573418,24.121042144996103],[-74.919449021289864,24.055992181417814],[-74.680369905461617,24.061872214100418],[-74.45113951975415,24.12460797972162],[-74.430501377384616,24.068215613377724],[-74.558544219502593,23.864412633573199],[-74.610546600005563,23.712968310085746],[-74.629736331318185,23.47405490676584],[-74.602572962258762,23.316251921762735],[-74.544527252569949,23.167019464565016],[-74.404957495849857,22.972165306069879],[-74.282345398096325,22.869180335423554],[-74.141679891718596,22.792676440032245],[-73.988596307396335,22.745718513059273],[-73.850414805420229,22.730162886826577],[-73.733308852092733,22.617227064837074],[-73.525270437501391,22.499366827409229],[-73.371129065335793,22.457462290304292],[-73.127444999468523,22.454394776691064],[-72.926706317280249,22.410768880640404],[-72.831120334346309,22.384747937433087],[-72.748612353694583,22.327425794126178],[-72.886512173249571,22.125486255481849],[-72.954690494954377,21.907546301786155],[-72.958518484053712,21.679223167901689],[-72.917173153111037,21.506773133949412],[-72.982625357743615,21.371214473415503],[-73.049269734953825,21.140826567724808],[-73.16505875240253,20.980006164337752],[-73.425420172773201,20.943281666977516],[-73.660445744445354,20.938407215011978],[-73.68575112677712,21.009144539137676],[-73.6269769215167,21.320917470824874],[-73.637865685106121,21.549151443097102],[-73.712829656260624,21.764997949439955],[-73.845753495442437,21.950848868858813],[-74.025793668127719,22.091543031819196],[-74.276058660284335,22.184589799089121],[-74.318359665590307,22.368533847851381],[-74.382543071765184,22.506733839687811],[-74.471765692334287,22.63025740566038],[-74.58279059372444,22.734623198155369],[-74.71158987103351,22.816044900031837],[-74.846311243181219,22.869494528395958],[-75.016597471263722,23.04485564165855],[-75.222707983387409,23.166105052809812],[-75.414768465003604,23.33363970589371],[-75.54471512109555,23.399570296691898],[-75.780641880996583,23.471364148857628],[-75.892858225749961,23.54260644856673],[-76.036214108093787,23.603517318735808],[-75.95119801125594,23.803321045352217],[-75.916282091563914,23.95313445860284],[-75.910685160236852,24.106861016482284],[-75.934614156641601,24.258816876418003],[-75.9871843367862,24.403383665125872],[-76.066451985595847,24.535216210765913],[-76.198976951324795,24.676398373964734],[-76.324817879198065,24.770521498156501],[-76.541834602251427,24.861317326963949],[-76.775877626533614,24.885066082689342],[-77.006709492867387,24.83971423489324],[-77.214370407594899,24.729183311128963],[-77.380904145370877,24.563030806495377],[-77.491910705468072,24.355623756882682],[-77.537791465003949,24.124896436181245],[-77.513506474123787,23.862706184910934],[-77.574296791660004,23.740079907060366],[-77.770485345933665,23.753401755046337],[-77.835995563536812,23.959633668309227],[-78.041426974550774,24.282835583197134],[-78.153882130523087,24.404540319057237],[-78.365957033742689,24.544842256133112],[-78.434126242966144,24.627552420757667],[-78.354485211026144,24.725261670013627],[-78.279943288827653,24.85769665614594],[-78.231790644786656,25.001838414803082],[-78.210334077984697,25.191108039062581],[-78.095065027466461,25.374723337693446],[-78.044975148194226,25.522004854966006],[-78.025350155353351,25.754064749603142],[-78.073392300167484,25.981943856509986],[-78.185030240937223,26.186330512688158],[-78.350803196815917,26.349903927833299],[-78.556662703678754,26.458802038364169],[-78.78438073309384,26.521047788421939],[-78.983325504645165,26.688615116095256]]],"type":"Polygon"},"properties":{"alpha2":"BS","radius":435000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[88.739023120213133,27.175599430834634],[89.484020383431314,28.06279283328578],[89.981068280647634,28.310969838628754],[90.348069036287256,28.243726054451407],[90.405717550640901,28.14096492569832],[90.505626946249464,28.080966106816827],[91.02128679101709,27.986758758793282],[91.273019622225817,28.078153774190589],[91.605451857441437,27.95147967719771],[91.641634576434882,27.923131615943216],[91.601726693748759,27.650041268540971],[91.640884223506262,27.521591962584687],[91.754686317526478,27.446181871285251],[91.990666878438049,27.449972789108024],[92.083126081903529,27.29064152759863],[92.012766023383975,27.123720353049553],[92.073165921022266,26.914905679140503],[91.998309673435614,26.85520926161421],[91.67154927162143,26.802217044499546],[91.41843378868306,26.834083028927505],[91.28647193499944,26.790186107283926],[90.733483285173591,26.772121136623209],[90.335573372193522,26.87707281823814],[90.122832921841265,26.75480020936395],[89.763877535351966,26.701759039368646],[89.610076681362713,26.719652674058736],[89.359376242478774,26.8349084884427],[89.148274916209559,26.816419896826933],[89.04441392019649,26.863446957223417],[88.85783745777988,26.961649034574751],[88.739023120213133,27.175599430834634]]],"type":"Polygon"},"properties":{"alpha2":"BT","radius":190000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[19.977574266202712,-22.000425550245549],[20.798931542180195,-21.999230911716147],[20.906267380816953,-21.954736419495237],[20.970754250552424,-21.858081436563307],[20.974338801265528,-18.319055277262816],[23.219313699391588,-17.999992587662234],[23.298476581411737,-18.027549221389719],[23.53643396194278,-18.334070475829236],[23.700316459476177,-18.378750562275343],[24.00607521076817,-18.152023090840608],[24.354466134610611,-17.979998501156881],[24.563055270397452,-18.011901480776146],[24.903340312808425,-17.823511682216697],[25.216013161951185,-17.787769076922253],[25.258455361320959,-17.793748703629348],[25.232136650041724,-17.901869901321245],[25.271059832381098,-18.02078137668401],[25.939093229342436,-18.938838518057018],[25.972538290537148,-19.129724300758092],[26.169649796762194,-19.522016108159836],[26.678853414387191,-19.89162381270706],[27.178063457719954,-20.101186145416111],[27.352089825987395,-20.441130491710478],[27.682236587720119,-20.507027049037475],[27.68317317473295,-21.069907125596291],[27.989438764363275,-21.516701159031825],[28.093900796524522,-21.577780669007865],[29.021261326471976,-21.794961026491794],[29.075793625093965,-22.006052121059774],[29.364267470509194,-22.193783128466425],[29.111147698635016,-22.22507572992377],[28.839702518361324,-22.480613257312179],[28.383066491257242,-22.597269227222736],[28.231897389915403,-22.680944232940039],[27.758125717792762,-23.196619266578981],[27.100631229459861,-23.565600185588373],[26.982555797408196,-23.724112361620385],[26.834877557127612,-24.240696963212901],[26.44703353133265,-24.585385457075386],[25.910066838920248,-24.775113968913846],[25.58603018459765,-25.601245214382999],[25.443571494717542,-25.714277306618367],[24.753359424395313,-25.817214590140473],[24.330655523732869,-25.742652263797208],[24.179381713198762,-25.642051285308568],[23.893844194093798,-25.60070560428457],[23.406270131687851,-25.298228767960861],[23.142191425073886,-25.290366145417156],[23.00048475240207,-25.338203810889766],[22.885555055447149,-25.451117225811377],[22.597437325849192,-26.132525562626402],[22.235004683306336,-26.378359991662613],[22.014465594754405,-26.63338439318834],[21.839535305510271,-26.686021923849331],[21.646271234047145,-26.854010384528717],[20.880133731840317,-26.813333637973315],[20.73986113285137,-26.848599926095314],[20.641606736207109,-26.742122120644439],[20.620148950608119,-26.576308131026167],[20.626962987995171,-26.443916878684256],[20.80761220563161,-26.16244110331855],[20.785114435670668,-25.899662671111695],[20.424166013466515,-25.138158702396943],[20.335327496498149,-25.024378559382363],[19.980716852340429,-24.776620744760873],[19.977574266202712,-22.000425550245549]]],"type":"Polygon"},"properties":{"alpha2":"BW","radius":597000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[23.175383808623661,52.28667044813848],[23.41108735553064,52.515982232211847],[23.809030485587318,52.657087667375293],[23.910120580080036,52.7917635652191],[23.86218325242783,53.103014664414403],[23.599157835944158,53.599323650970931],[23.485007491098919,53.939475660320653],[24.191249143274064,53.950220404800426],[24.355400309721556,53.902919929969592],[24.722527400725685,53.999543232223502],[24.869592642525355,54.144909397609567],[25.059255670893524,54.140785571771332],[25.491719157590122,54.305843592077188],[25.698859913860691,54.558338560636813],[25.72269597199341,54.717817220156149],[25.859398861054615,54.919086850706108],[26.133628520793845,54.982562138625909],[26.250951271894795,55.124311207278332],[26.41689649316924,55.215343343699089],[26.593781459676993,55.667373268961079],[26.804311259584221,55.705363895111525],[27.047962372953208,55.82975571688474],[27.529962138042499,55.814622392324679],[27.896395235961418,56.07597427049032],[28.111929666041512,56.144935590411897],[28.298472094678509,56.072929284971764],[28.563915032284498,56.09172879564148],[28.787888370679504,55.966326995586208],[29.09149540241134,56.020027868439648],[29.374857792003269,55.938514087752907],[29.454144884863712,55.805049944948351],[29.558958555322945,55.752414463071716],[29.937031689054969,55.844990815841882],[30.233566914717013,55.844960818653803],[30.456127089796283,55.786594512403767],[30.730139438170529,55.62060507995826],[30.882067396341913,55.596159159884223],[30.880460870975742,55.266701028787111],[30.977734375000011,55.083270002951814],[30.892437310607232,54.933907744008287],[30.890389537966445,54.819623069119295],[30.96639319978706,54.710873108001486],[31.151957167221664,54.625222559286094],[31.172666346432408,54.478324730139782],[31.308517125344348,54.28479564010042],[31.422233029616056,54.189896329817927],[31.796171645966087,54.052809639089801],[31.948579585477443,53.814880938989965],[32.449952382986375,53.692744532447094],[32.501882896777637,53.550280057623127],[32.706804988491136,53.414900754015854],[32.70409546739085,53.336538748870964],[32.524639922840926,53.291728508698952],[32.141945509143767,53.091426450444573],[31.849793229574807,53.106482886958986],[31.664552094791798,53.199878063715083],[31.452445910334649,53.14549561205213],[31.399621885119661,52.946668213818462],[31.564594119429419,52.759151117521526],[31.615613163718365,52.546169631781574],[31.586028600938143,52.356170952701291],[31.763091891408884,52.101320147553288],[31.052494150284559,52.06771471468457],[30.770032649894926,51.904617649480493],[30.569843957879826,51.652534038644283],[30.632234873765185,51.355507171513423],[30.544398072161858,51.265305468281284],[30.333570277639815,51.325693029843109],[30.260346328881813,51.426420462598138],[30.14106780368995,51.477806077659501],[29.346529235697563,51.382813170431554],[29.186740131012684,51.545992671921823],[29.076840748184523,51.589662694719223],[28.849936213508009,51.539126058000896],[28.731141222515809,51.433670799058476],[28.529135253227512,51.554417951519177],[28.203575709441292,51.599425814122412],[27.861878356891943,51.567310963033535],[27.700044555641881,51.478220152340498],[27.559335429549815,51.594402673001966],[27.296363231204282,51.597644924275045],[27.095081393631801,51.75345489541467],[25.778821600192007,51.923859372898967],[25.083703457345038,51.931130492716342],[24.41377471753783,51.87266396824414],[23.983530566742477,51.593881178191872],[23.75030259551643,51.629652111678311],[23.60532358928128,51.518284747634816],[23.539926829349458,51.618970499908663],[23.621996706557219,51.936424490262077],[23.600944033814926,52.047916024147106],[23.466573059384931,52.162669497741817],[23.175383808623661,52.28667044813848]]],"type":"Polygon"},"properties":{"alpha2":"BY","radius":384000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-89.237234660120833,15.894522005734714],[-89.160392216978096,17.906640296342442],[-89.133384833795958,17.970630521165052],[-89.050459870586806,17.999472281928949],[-88.929656007222874,17.958592596944417],[-88.819360256565488,17.98843680834182],[-88.600098011548951,18.271176509118465],[-88.522793807507682,18.445709426672888],[-88.46121318955116,18.476512375522237],[-88.29582846889349,18.472193894792358],[-88.236502066679734,18.393550752711775],[-88.130448943614766,18.350514741819509],[-88.039290308599234,18.194150163145217],[-87.848814191397224,18.140351744603485],[-87.950746581014599,17.925123330527089],[-88.106618653171168,17.891685516230289],[-88.19356207733955,17.818146206391205],[-88.245685999004593,17.671856446733702],[-88.229028303873179,17.557920455422249],[-88.162616629498558,17.455599527166797],[-88.059481028000405,17.399774861484342],[-87.907939484581419,17.430207317248449],[-87.826266028604209,17.545939102261134],[-87.788876419937168,17.524106807410618],[-87.798340430096971,17.479694092851215],[-87.930018781866238,17.283392084849847],[-88.00411065762664,17.341962560194865],[-88.098775869897423,17.359881354553348],[-88.248097083216379,17.27945705287582],[-88.287241668824379,17.171204757978007],[-88.262058522187317,16.963038982871662],[-88.313674880428664,16.632856516870554],[-88.562435241877864,16.29060730525476],[-88.705609309150319,16.228415953580239],[-88.853382045068315,16.048951701208846],[-88.894309960769093,15.890836390421734],[-89.237234660120833,15.894522005734714]]],"type":"Polygon"},"properties":{"alpha2":"BZ","radius":198000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-141.00122235338137,69.649748490351541],[-139.54669341151319,69.567735954892512],[-139.07276040078244,69.646804935588023],[-138.87952754959437,69.588942366998211],[-138.74034895005502,69.426105559194355],[-138.54473144111606,69.29699631462934],[-138.12752446704863,69.154609505571287],[-137.31422456908453,68.975570944601301],[-136.71703249128817,68.893594293124039],[-136.404855352661,68.89784233434456],[-136.08779420997442,68.985801868351274],[-135.69105844851225,69.310218799342252],[-135.36725515106406,69.369128571698297],[-135.14055659053034,69.466397000660677],[-134.7634163122666,69.505617512748941],[-134.40882052065538,69.680996065892202],[-134.24227885486152,69.668078573210622],[-133.67223635332289,69.430263676563285],[-133.43903426964908,69.406744839155039],[-133.15248650783337,69.471381664139273],[-132.85941454122539,69.645329061328596],[-132.46016251158034,69.740600554538034],[-131.947687333456,69.752881109182084],[-130.64700892384269,70.128832413897797],[-130.08720475648104,70.09200748719185],[-129.67582345771532,70.192109938896834],[-129.43490120171276,70.040263013255341],[-129.20416849311036,69.96111740880805],[-128.65279792551428,70.009578308180522],[-128.338310678266,70.210520167908726],[-128.12678477179563,70.523142883487324],[-127.77895220752853,70.67367077172382],[-126.00464355399737,71.816803429255117],[-125.76188305894355,72.136802792082506],[-125.12079720316555,72.623646016174632],[-124.81558930422754,73.108074229208867],[-124.6033131186781,73.340596995330841],[-124.50968940146291,73.551120022747156],[-124.49705591750536,73.932540378033352],[-124.69573117959598,74.347352818219804],[-122.83825052098287,74.460767184452948],[-122.61564322238151,74.523558152597872],[-122.36416422172907,74.700603623448956],[-122.17309198397265,75.032638031360335],[-122.14188542507053,75.261816707839316],[-122.17754659320309,75.490344597075165],[-122.27709462063488,75.699119368792466],[-122.43220842539809,75.870689828496509],[-122.90103709789315,76.135430189426998],[-122.75878153823382,76.23519689512311],[-122.3923399795727,76.396016063493249],[-121.58682922188622,76.457904680106239],[-121.11617021582694,76.655379021455701],[-120.58152939057936,76.778002632614843],[-119.09005714034387,77.304393398992445],[-118.00519301797908,77.380496892401482],[-117.42134672846085,77.331862580002493],[-116.92996270527266,77.507702509129686],[-116.52929562754103,77.546567554357907],[-115.87185660558981,77.435743270663068],[-115.63192177430015,77.44560163944486],[-115.27272553300033,77.615939534152119],[-115.02866062990411,77.966639764259938],[-114.3305316214194,78.076362220376751],[-114.08600359965361,77.995866975429053],[-113.78140975201212,78.003643889593604],[-113.50181573707647,78.12474334965485],[-113.28092072122922,78.351903275470619],[-112.847141667752,78.468181863977051],[-111.67054794130321,78.580479275959007],[-110.86778752797066,78.735917854144574],[-110.18056508251316,78.747597078851129],[-105.71914467631889,79.215990437222672],[-105.38745286520702,79.322372431985826],[-104.86574061228936,79.324479724114568],[-99.828236593426084,80.13538343455734],[-96.457891996320797,80.572944812715889],[-96.132479021351941,80.690463607045302],[-95.676126421503156,80.773673725530656],[-95.26950879338618,81.000037902438578],[-94.559714676877448,81.092526580285352],[-94.202261544231121,81.3344266050768],[-93.310512516088863,81.363052097235951],[-92.203460536986341,81.289983472342186],[-91.914056982591731,81.406057972364323],[-91.647044586402416,81.683074741151614],[-90.948331773031356,81.826527651798486],[-89.413957443442683,81.913846930243679],[-88.580430725990155,82.059186899168097],[-88.0825585476383,82.095122587160134],[-87.060659216924648,82.018271720419733],[-86.832820739825181,82.078749531850193],[-86.615228167107247,82.217642201050921],[-85.794848794135902,82.29433196294589],[-84.896808590245797,82.448461158835897],[-83.612578523869644,82.328742087323548],[-83.206837324814089,82.201714912257287],[-82.973070518249671,82.183298983766591],[-82.6721426651321,82.264831689705787],[-82.116425414306875,82.627701162575022],[-81.429855629777364,82.654887438501405],[-81.010111703810423,82.778296182578913],[-80.504639125570733,82.774418199926444],[-80.154600315108596,82.910214579472694],[-79.908055083083553,82.936309643763565],[-77.536930264586715,82.891988378129653],[-77.124634932914432,83.007593828626398],[-75.734214843796266,83.046894144696708],[-74.397175686352199,83.01124164104705],[-73.715195213683302,82.896971176447522],[-72.786898553390813,83.082030414003171],[-72.051795402938083,83.10511423719376],[-71.52910048571411,83.017815491867054],[-70.880963837057763,83.097392689575187],[-69.953277366475689,83.115056108550093],[-69.432963159011706,83.015561926094804],[-66.573902522898507,82.94223896948931],[-66.036061290046547,82.819197995936463],[-65.38626855515254,82.82129965809024],[-64.959554065334316,82.901843760192392],[-64.460671580231036,82.792237719341173],[-63.659326417571904,82.813479789643608],[-63.499084008245084,82.791778975675157],[-63.285308634631534,82.637950102907681],[-62.857878505988673,82.502412563539167],[-61.458161258018087,82.461727702605174],[-61.208685772247222,82.340922881490911],[-61.291778503195808,82.274742211780804],[-62.176913442244356,82.044356075998792],[-64.37836355808237,81.748255634482248],[-64.593328666028285,81.655028891754228],[-64.851193275451735,81.434268868208832],[-68.605304206809464,80.684022862119164],[-69.55080559490527,80.383950152984255],[-70.030694160398326,80.341095057162391],[-70.559618882881807,80.071866718997057],[-71.318910773475423,79.777831372418362],[-72.197357193927132,79.687848640442311],[-72.801085332526554,79.725483683875694],[-73.038681723642199,79.670722453376371],[-73.345184865602377,79.508229398982166],[-73.858958662181706,79.46535020171288],[-74.076383805229497,79.364111185406642],[-74.254029419614426,79.202974389665229],[-74.375928764355152,78.996422747203141],[-74.434296498120403,78.724321703153535],[-74.547050603428303,78.621145180969279],[-74.937461749039571,78.514412235350235],[-75.193971869664892,78.328596766830799],[-75.582583209538384,78.194896584027603],[-75.866396952586669,78.010907072016536],[-77.299461317766102,77.936668712452473],[-77.607801240416507,77.854705757353145],[-77.859589410653598,77.658761501919813],[-78.014779142712271,77.380000706662983],[-78.04868764697666,77.062760029499373],[-77.984243466224456,76.755338848066543],[-78.119133615545621,76.644747861098466],[-78.642052586983652,76.389067060526315],[-79.359097317991257,75.541163485171879],[-79.443564031774017,75.241294966155948],[-79.402509409741413,74.917775938289893],[-79.545885158122488,74.526866142459838],[-79.531878918676725,74.290595123255542],[-79.359603733763308,73.938589922621901],[-79.04081346315111,73.7106443941001],[-78.810083754045749,73.657881048912287],[-78.07899611258577,73.648960700168047],[-77.206913818468223,73.498579752692649],[-76.290165666214094,73.080249326575156],[-75.967176514009111,72.738461958626843],[-75.75909354841653,72.601393139736814],[-75.276921346859638,72.473413327119403],[-74.853446183306389,72.167617915059665],[-74.293213002946374,72.049846287988501],[-74.027106480917794,71.84695851745056],[-73.476722939853062,71.634364621325574],[-73.175191745224197,71.60440644382021],[-72.90186672316608,71.676877759777497],[-71.659023884933632,71.519778483576459],[-71.342576719544155,71.408031521951486],[-71.031804930880838,71.186499968131528],[-70.690939733215657,71.059997920444374],[-70.334985457952001,70.85964439034025],[-69.268463654637074,70.780146436449229],[-68.446971435660828,70.593475065417749],[-68.137866442794262,70.363048873924797],[-67.728052574791292,70.225178193262892],[-67.318917882935338,69.997898868728285],[-67.013004768663919,69.522107161672764],[-66.685994660540601,69.285237398316454],[-66.680467407831799,68.84518770734033],[-66.551561167132704,68.553617224667576],[-65.972035755546074,68.085454544682833],[-65.74924670529262,68.001236256472026],[-65.41320285243016,67.961269211955099],[-64.922761275902232,68.030334880414472],[-64.354559811098852,67.720937027800034],[-64.001410186711482,67.645423903843621],[-63.538625417632815,67.377463319632554],[-62.875944927621148,67.183919432103096],[-62.417082659647527,67.187334234580717],[-62.229996553635203,67.084328538958033],[-61.968854135015249,67.018247917402107],[-61.300794424211567,66.648309544820663],[-61.571394961228663,66.373759187859832],[-61.843371467079201,66.214071141763498],[-61.992221695945865,66.036188987952457],[-62.22469561299102,65.949459629818278],[-62.659333492201753,65.640843147190509],[-62.946937951980537,65.582478452263217],[-63.157176488019537,65.459652714814808],[-63.414243225502773,65.104069326141541],[-63.606861624040143,64.929093883365042],[-63.897949015446152,64.92427218470344],[-64.116070362441604,64.84604229415045],[-64.302620431365213,64.708579900836327],[-64.441947427231199,64.523418279505108],[-64.522361632700608,64.306092732572012],[-64.537116190668456,64.074837157914402],[-64.411973308220624,63.706258440170885],[-64.573458682703432,62.921390067134553],[-64.529932504494454,62.697888369778326],[-64.419287054018142,62.487429122536376],[-64.631395589928886,62.205357334736384],[-64.704346666317107,61.972923161408936],[-64.670576675093045,61.593003546356684],[-64.808572370419071,61.07082108865189],[-64.750371534567009,60.763988317039832],[-64.63137496005794,60.561472178876855],[-64.407843033362354,60.366547269197447],[-64.252042253062243,60.047274304921629],[-63.842312953175153,59.58634610643962],[-63.223142552480653,59.056428154515842],[-62.779559167534316,58.604705869073605],[-62.60822405573704,58.495332051082912],[-62.365008871850996,58.11647144624532],[-61.89984802099989,57.860767373621222],[-61.660313343700508,57.524489306474202],[-61.533341892884145,57.213887286589333],[-61.334739341388293,57.010167910798152],[-61.321802491421444,56.548219610185832],[-61.222552538814824,56.340830238755807],[-61.068417530265222,56.170233334874254],[-60.658272566569799,55.884713012840663],[-60.341649536230392,55.783802186118336],[-60.167891623721488,55.52608607109714],[-59.917846151016676,55.357683085555792],[-59.529627492974143,55.215937941225619],[-59.069743635889587,55.176970362285203],[-58.765844188692377,54.950250057598645],[-58.546364549958014,54.865539887051789],[-57.962866123600982,54.874790099476328],[-57.71390810661029,54.702175791116993],[-57.483314150612017,54.639345713520633],[-57.405477992846031,54.590323235528579],[-57.380542458182987,54.367605279465849],[-57.290302241573663,54.15085621143627],[-57.080242101891777,53.919941964290317],[-56.797801401661665,53.786998733279184],[-56.46531040116119,53.764171841553797],[-55.966810309461664,53.470499497693226],[-55.798809954786158,53.211797743677174],[-55.829683628863549,52.810637661274114],[-55.745055199220445,52.495207149618501],[-55.556221858161308,52.191925031936826],[-55.275158628732299,51.994661976235776],[-55.417671781360596,51.705234740557991],[-55.551918698766158,51.236546385507204],[-55.542673424446264,51.011326959722901],[-55.47026227797447,50.79621635604839],[-55.546259347870162,50.571267904475647],[-55.559640245487181,50.334042099693328],[-55.468873370647458,50.0314723805471],[-55.268060429504011,49.787625659876682],[-54.98851163004052,49.640521855605968],[-54.673813461269837,49.613096993076148],[-54.137618707450002,49.750265598953661],[-53.981386047387055,49.661279427920064],[-53.839462014301105,49.474250852542106],[-53.570439941904873,49.263722015524088],[-53.496509826821054,49.048184829124658],[-53.360282797471669,48.857491606623242],[-53.028524190623514,48.634369334049708],[-52.976207345616295,48.320659695073154],[-52.712337773303524,47.744865966119839],[-52.669053021636508,47.469969543888084],[-52.822373120823777,47.205887494586833],[-52.953661536377879,46.836622316143881],[-53.070477688129614,46.682131554955333],[-53.567621485602963,46.629228281623242],[-53.814233558772301,46.769484799422045],[-54.12031333901578,46.83321736948286],[-54.391192081784133,47.039844576779068],[-54.684231431657153,47.121380840811668],[-54.986525367820988,47.087637706917938],[-55.315981131509993,46.906533495822217],[-55.866067294821669,46.881861455290988],[-55.949147925516613,46.928273176763085],[-56.039513261627917,47.16280619950895],[-56.180279537899878,47.343321504399533],[-56.366404473212398,47.476582257399514],[-56.58264882098009,47.551677542829019],[-58.038700618971873,47.672718671918368],[-58.436028436485273,47.665943169718027],[-59.099113943722244,47.571693648091809],[-59.366580796788973,47.627151868839327],[-59.360930071265479,47.888701601862898],[-59.199624141790501,48.193464716423712],[-59.166671871499517,48.55797286122548],[-58.630889285817098,48.864639970827945],[-58.372252572206833,49.118491632930343],[-58.18194301486421,49.434733220901471],[-58.001480572448159,49.599035782676644],[-57.823191448149949,49.857018627714538],[-57.705162686029659,50.14795478092902],[-57.728219404623708,50.538294411911053],[-57.933525091741416,50.871079950395888],[-58.272032013492833,51.066808598564442],[-58.662870527877025,51.078720586705593],[-58.883498415234122,50.994527012426907],[-59.904116666235915,50.310733632064377],[-60.080312933540931,50.255359719602822],[-61.067872098150318,50.194826228795677],[-61.354146621478407,50.090548742307661],[-61.531638711879822,49.945660881742889],[-61.660783215211239,49.756405415366409],[-61.740144516840942,49.462247837359961],[-61.697210341223645,49.139355121178582],[-61.814129472467165,49.055983849184088],[-62.779670744601496,49.168294563676653],[-63.429553158948451,49.31875656838816],[-63.733032425351084,49.269001924741318],[-64.049690336171409,49.053443693035611],[-64.177653315935459,48.860789610446801],[-64.2546882608967,48.550727587488289],[-64.430994768329313,48.253436058461432],[-64.483088559416089,47.681433200197198],[-64.339414669781789,47.331487445672572],[-64.180200756650436,47.167780149293016],[-63.994379962599353,47.060874180820292],[-63.894710624409214,46.851309095913862],[-63.68092383572354,46.635420742224149],[-63.073084475175577,46.428355620933189],[-62.715792363963111,46.452210669545686],[-62.481315953175191,46.51821964129222],[-62.284800719276895,46.64318782260203],[-62.132603646931884,46.819458451646092],[-61.923884670141724,47.424333113234404],[-61.52588285070857,47.663284051171502],[-61.396326994943806,47.636785543308797],[-61.268082810121008,47.340810325316482],[-60.970544302830803,47.084517092703415],[-60.744986867577971,47.01119231953772],[-60.218726371989952,46.964778086988254],[-60.189095968420418,46.57673957044215],[-60.080567660558522,46.364292829589068],[-59.850836731866593,46.1410837636328],[-59.82906311621494,45.965169987624186],[-60.364932555944989,45.664966183463399],[-60.757168091610481,45.540023695359693],[-61.101297956179344,45.234371127005382],[-61.635915015101958,45.127243056485057],[-62.539417547878607,44.837798173592681],[-63.200627608207292,44.682770631139341],[-63.610125841909124,44.481060797815147],[-63.898349831055192,44.482434004144395],[-64.132315464683046,44.41772157339782],[-65.344787029346833,43.550537343733097],[-65.66171316784677,43.535153316834752],[-66.052900543270511,43.754591992461087],[-66.124995541365777,43.814287671233529],[-66.257068957973729,44.1677837635267],[-66.426432650643264,44.390497920432168],[-66.666879446989654,44.562370741865379],[-66.896325185652572,44.62973072821584],[-67.090295193980211,44.983912405562108],[-67.463029354994319,45.262810506236875],[-67.599902230566343,45.52626771298641],[-67.801373936093,45.727781427317005],[-67.814179631568507,46.613790876591914],[-67.901144560400382,46.837185240620371],[-68.050390849230084,47.024784510333689],[-68.248517127932473,47.159743461181556],[-68.477732913318775,47.229943635370482],[-68.869862627416822,47.202977952350956],[-69.20797981127582,47.235608271741782],[-69.580449652812675,47.107816834097179],[-69.891333324684808,46.818461649658715],[-70.223888476726401,46.204997707502621],[-70.297147566405016,45.906624521956942],[-70.865595892247214,45.271611338363002],[-71.269097711976016,45.188947659041894],[-71.517941788002119,45.008543471639946],[-74.6736527030057,45.003895116259507],[-74.989501474306522,44.96940897944814],[-75.368804184744775,44.790863181547763],[-76.398054689638627,44.105815259309061],[-76.820439808439801,43.629768736277846],[-78.343849760066547,43.598223082358075],[-78.552487559033892,43.503004185327299],[-78.725537351306485,43.352504554417123],[-78.848777588731323,43.159092524312577],[-78.929489106464075,42.882074029616611],[-79.054201414289437,42.795477785621088],[-80.247781495701702,42.366900966250363],[-81.310886223646449,42.190038414171241],[-82.439315183878051,41.676011035759068],[-82.669302003567324,41.67516707136916],[-83.029457607082037,41.83360534733756],[-83.140914724103624,41.976260787009771],[-83.148658872397689,42.141788658786311],[-83.072467613517631,42.299458737059709],[-82.816778263318966,42.433818486149484],[-82.567102947836958,42.666483897170444],[-82.19998341965676,43.536062372379881],[-82.206091263028583,43.863773715863402],[-82.457244678819009,44.962142598668258],[-82.647058976162924,45.323989042981438],[-82.839909891076886,45.478192929908793],[-83.592077137199482,45.817898689903323],[-83.738185067186123,45.960850721967141],[-83.977223638089541,46.085764177781684],[-84.259709223564869,46.353191532604519],[-84.561398335205212,46.458237932345412],[-84.778512417010703,46.638001612023558],[-84.938916708416698,46.861775336757411],[-85.125546999540489,46.998761420507819],[-88.284776018595025,48.259905261898034],[-88.578134888173409,48.262212295914814],[-89.256547427937676,48.025188223185197],[-89.455696053106195,47.996944669702714],[-89.901002098261102,47.996314102802657],[-90.231098804144963,48.091316801205011],[-90.986838982005906,48.141858766127335],[-91.518181644030932,48.059045798620076],[-92.0189452979685,48.260475601737213],[-92.41413752771156,48.277575572822343],[-92.644507269339925,48.483902221561351],[-92.935356322536322,48.595006706685176],[-93.259644197355939,48.617460724964111],[-93.707623079419861,48.526580606279339],[-94.047833505444345,48.65332154781126],[-94.620637952220918,48.743326728086181],[-94.984261450649853,48.955546801989165],[-95.395498799242546,48.993149643007207],[-122.5286493108857,48.993017578566977],[-122.75834355591914,48.959333801267505],[-123.03137598871119,48.815324485193997],[-123.18888421599176,48.644780645101271],[-123.31117232139694,48.411796650328476],[-123.57328968225902,48.323871755122759],[-123.9330933677748,48.39063263464157],[-125.03065589410346,48.717870910289662],[-126.03101677416174,49.225718064077782],[-126.51875127219556,49.39774930750135],[-127.33431194459696,50.030852655630426],[-127.5458892759911,50.107021369530514],[-127.86323851301756,50.128545074247128],[-128.01694596329219,50.404220456458326],[-128.34902508517376,50.696890459344765],[-128.15202624678332,51.029318084152081],[-128.09546557899245,51.261299924272457],[-128.1098889487169,51.499641380365141],[-128.24500041070979,51.926333478389772],[-128.44645219335629,52.16223896363865],[-129.15036947070297,52.606076189387771],[-129.38876093537183,52.922563308929675],[-129.56925486851688,53.064644484639381],[-130.28444550831477,53.267006700265277],[-130.51900664970921,53.237631013550775],[-130.79913487671382,53.0951255816207],[-130.96099379704125,52.922836435321116],[-131.08575076518645,52.634365777205694],[-131.08975789859011,52.320099067902575],[-131.01157539899032,52.095079534935614],[-131.04705691912332,51.960625960287608],[-131.6673135634463,52.461425007359637],[-132.16460249224579,52.783976567083819],[-132.74671847323228,53.311102260045224],[-133.07835895705975,53.837392403456597],[-133.05629765261941,54.150760988285583],[-132.01039224457494,54.059062457061692],[-131.27186972232084,54.237480140739493],[-131.07666898802808,54.385289895717193],[-130.906376656888,54.630976874577598],[-130.61778211642374,54.742007631754248],[-130.2983946004542,55.008924345035965],[-130.13883861463711,55.353605518349205],[-130.13819192736327,55.65851323495135],[-130.29628439647783,56.003868117725453],[-130.53357141637912,56.209323803204263],[-130.8055425521988,56.34311708580104],[-131.49586746375115,56.561707226200205],[-131.82375580252116,56.591000042450617],[-132.33714404444683,57.079996175480886],[-132.41647887671951,57.314093465168682],[-132.55172342368471,57.501174266742019],[-133.72854379433309,58.637291411018374],[-134.23594298580025,58.86069882215272],[-134.56121929802896,59.125627439135201],[-134.94300504149246,59.289246969669506],[-135.22354525402636,59.572644450518162],[-135.50575591000992,59.690512745119825],[-135.73535281372338,59.704088107498066],[-136.05390442610656,59.623894832103893],[-136.30820785950834,59.456829830178457],[-136.57915105049977,59.153099198436074],[-136.85395453918801,59.130852361199494],[-137.45947343758189,58.906236330917466],[-137.52013308094863,58.916219800304063],[-137.62087558730369,59.131769330360214],[-137.84383619102084,59.354938318981489],[-139.34534124266949,60.178118734341034],[-139.66329484419583,60.245363001777726],[-139.97335027695658,60.184163554327107],[-140.30035069080412,60.244632415599881],[-140.52547292433115,60.219395205910217],[-141.00118693611552,60.301054596998576],[-141.00122235338137,69.649748490351541]]],[[[-60.116323955804468,43.952370806484176],[-59.914991936476802,43.954611718061855],[-59.730151225801237,44.000803436565725],[-59.922370400353465,43.904792266360509],[-60.116323955804468,43.952370806484176]]]],"type":"MultiPolygon"},"properties":{"alpha2":"CA","radius":2904000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[96.826119468997234,-12.126394916054027],[96.829204458814147,-12.126245417684659],[96.832545094852478,-12.126409532609449],[96.83515090716061,-12.127971396400445],[96.845586768211874,-12.134968625931059],[96.862288566754842,-12.143999399579947],[96.879771470429475,-12.15140568247835],[96.89787791240407,-12.157120724585624],[96.916444706130918,-12.161093018377931],[96.920346681522787,-12.161735869399665],[96.922344046664165,-12.166020337493794],[96.923504799659085,-12.168862997889589],[96.92506788121716,-12.173276483250966],[96.924890146284014,-12.177955210351785],[96.924728203336215,-12.17984994284094],[96.924090892573901,-12.18459148101207],[96.921712146818649,-12.188742374634558],[96.92073882884597,-12.190265146301519],[96.918117527525965,-12.193978247969799],[96.914120562829169,-12.196142183695128],[96.910869139563729,-12.197713704940082],[96.906515605286529,-12.19956557246539],[96.904666202560563,-12.199239910511217],[96.883950892296397,-12.196697864014606],[96.863083463190094,-12.196328429006337],[96.857967011408633,-12.196773134264227],[96.849611205872648,-12.197124094834534],[96.846418866897224,-12.193631240014298],[96.837741563612894,-12.183160627384522],[96.835047913691483,-12.179594521228575],[96.833786616914054,-12.175307098523364],[96.828721847415366,-12.154701118507937],[96.827903463854199,-12.150656604025524],[96.827422101367972,-12.146558294714891],[96.82624447337831,-12.131001209167495],[96.826119468997234,-12.126394916054027]]],"type":"Polygon"},"properties":{"alpha2":"CC","radius":7000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[12.214015337292205,-5.7588330667475169],[12.396907908805225,-5.7175027728802865],[12.483258234690565,-5.6401437192804842],[12.520523131226863,-5.2239451721085883],[12.451706097867026,-5.0715988319124676],[12.829822684930516,-4.7368261838443848],[13.131956496231194,-4.6040747513086275],[13.328353487984952,-4.7844762232030131],[13.476252519864451,-4.7967410605694587],[13.670993417550573,-4.6724463834234946],[13.721320873504411,-4.4521792245676073],[13.962192731206871,-4.4608792183019137],[14.358173469609424,-4.299679538506588],[14.449526168572575,-4.4494595423040959],[14.387086618272443,-4.6002122588109611],[14.430918271343934,-4.7648888831518112],[14.539552971650771,-4.8510241654764723],[14.723138773593806,-4.8706018216932607],[15.263515864446147,-4.311117272120816],[15.413471712221531,-4.2289566468634945],[15.60019558732891,-4.0311494159587786],[15.852810281365359,-3.9391687499274139],[15.982473447208784,-3.7770252430782549],[16.147420546234173,-3.4532291003593287],[16.214332754784383,-3.0489856529013282],[16.215532447286801,-2.1779325436815156],[16.539898845456797,-1.8395419746054402],[16.880015957731253,-1.2259931352291833],[17.305175667363468,-0.97718176060984807],[17.730900652737269,-0.55171384500331766],[17.725255196590226,-0.27233054836028531],[17.890870911279261,0.25980441773492058],[17.902142335008367,1.1133888270934778],[18.057624488282965,1.5454178129126692],[18.072480057212729,2.0132289870212907],[18.211781108463086,2.4148703651948815],[18.49026369660751,2.9247038826047262],[18.618120016803257,3.304593172411264],[18.63184005443188,3.9761191921755636],[18.568813839711563,4.2620031609614184],[18.594318265897858,4.3460694263540143],[18.831092915692672,4.5239406014172907],[19.068724222982119,4.891243578495307],[19.323552196364698,5.0705211754032824],[19.501021671429868,5.1271658572397145],[19.80643635829809,5.0891254270247384],[20.230819158273171,4.8258186105047995],[20.611242932006615,4.4466088891971669],[20.949939625690426,4.4143571606884882],[21.157805458037053,4.3229074505841147],[21.518766537142707,4.2554834889039048],[21.69152403634768,4.2808356819892346],[22.367080625894875,4.1563636302073377],[22.503360235020704,4.2226144283887468],[22.708953656726869,4.5874718644060941],[22.864632740720797,4.7236751404671287],[23.115882267421917,4.7367257703822592],[23.399425956089345,4.6639566604609222],[24.319882500701706,4.993932641691285],[24.437097928974069,5.0097421232837744],[24.755874682109731,4.9362408059835152],[25.09326619237363,4.9770869850029289],[25.245585049755597,5.0334313051598052],[25.40036811697977,5.2557129154918716],[25.525129541866562,5.3118775774647853],[26.788962759302915,5.0697786790219812],[27.114910732167168,5.1976741968348712],[27.403150347606747,5.108976795017659],[27.813083376145883,4.6246479065624637],[28.216332626098147,4.3520499618798478],[28.434477721723258,4.332061607876029],[28.727108437795824,4.504722111200115],[29.205000273779763,4.4051610470687654],[29.374344772180631,4.4929423617562572],[29.469754089481654,4.6116363036803074],[29.552040443296626,4.6358034342083387],[29.676697551207997,4.5866767683237333],[30.18086123970626,3.9995767423392294],[30.508097099424795,3.835483440048634],[30.589373701500048,3.6987183839707352],[30.757088569964544,3.6240261111970691],[30.895085407906077,3.4634944306952766],[30.771706704948979,3.1224119475204088],[30.85058523325953,2.8936348621832573],[30.762254636051114,2.5557309724269861],[30.838951793729727,2.4414208134848816],[31.176201699094506,2.2699144943265002],[31.273752526973034,2.1462348225176862],[31.252510836654192,2.044665432368427],[30.93992979492554,1.6802822199624905],[30.478861546643294,1.2398742218832961],[30.301512832472547,1.1641048189475713],[30.182729599692699,0.97362443340701765],[29.94602468677779,0.76910738109608945],[29.934213549966479,0.49908802085371196],[29.710412900424103,0.040254686011719609],[29.563192066247641,-0.98820217614350092],[29.576738632637952,-1.3877503650344243],[29.337163389764935,-1.542678419723583],[29.190624732862926,-1.7306581050602619],[29.13380416850007,-1.8652443993650116],[29.131269166731627,-2.1949952797452408],[28.933815307847727,-2.3589595946716035],[28.882213916994107,-2.4782500434320971],[28.920360057832475,-2.6407361801869165],[29.224163030782634,-3.0535997308785037],[29.214473769924485,-3.8517092329201197],[29.331114727200351,-4.0955031812245544],[29.403365569324713,-4.4562476903724741],[29.326159395645107,-4.9073075199585228],[29.60680997572787,-5.7226540834163115],[29.485406372730335,-6.006176476731091],[29.539332754313868,-6.3031287945906707],[29.724956995260261,-6.6286954045381306],[30.215818279109438,-7.0430439751484899],[30.750875540129805,-8.1934721311801972],[29.042751187270898,-8.4628102635151485],[28.957238685711278,-8.5404445683853876],[28.917575126107128,-8.7005174420926057],[28.795620080036489,-8.8881564633524519],[28.499275557388287,-9.1631981400271165],[28.449174775310716,-9.2681720137927641],[28.629318343699843,-9.8267744580771303],[28.610689315180984,-10.397152240670858],[28.645229314438133,-10.555187799814927],[28.37489340645762,-11.437171987484799],[28.380552424966904,-11.557598350313922],[28.533028826939564,-11.869464558700376],[29.087717718098741,-12.348465853973316],[29.361538707283202,-12.383751698198434],[29.51274144971854,-12.225953493890751],[29.794892570612031,-12.155740111516025],[29.775031876151445,-13.437900296152447],[29.722684957271099,-13.45354508073228],[29.483426899590818,-13.301390149077505],[29.201792536872972,-13.398066452404306],[29.014351947562695,-13.368669541041145],[28.725039153431368,-12.91982912856524],[28.551058118067925,-12.835929084862832],[28.394337267965771,-12.507240569822885],[27.888935906604825,-12.297279664140962],[27.573908689315708,-12.226930306675342],[27.420091894914222,-11.947207708682301],[27.20370463536074,-11.773216538084725],[27.053091920033626,-11.782069341245865],[26.926706020056109,-11.921823937563023],[26.736778645321415,-11.975169082183905],[26.026014534055001,-11.889994279329068],[25.638024519173843,-11.751352356612911],[25.51200617515725,-11.753171225008948],[25.349626785501567,-11.622886133053484],[25.26063242528312,-11.355850121381909],[25.151677756092933,-11.270599887118991],[24.694744457811588,-11.346233399482806],[24.514316430709776,-11.439225427421581],[24.378061616878046,-11.416886602191754],[24.335450474714577,-11.371279873781903],[24.385024280673218,-11.239922412987136],[24.340623154478763,-11.113320834830574],[24.015367803371866,-10.963596548402837],[23.833844272436618,-11.013451879537325],[23.427451633488577,-10.973417812460321],[23.076254574850104,-11.087664500348756],[22.561856011984734,-11.06045131208786],[22.278950341111905,-11.193900218963076],[22.178189903380268,-10.892294974701565],[22.297230400629424,-10.683447144212824],[22.277253819735513,-10.272591193098181],[22.186751786908108,-10.022608040178431],[21.859295238966144,-9.5980285740148048],[21.813418524154883,-9.4687169983030461],[21.904514785456652,-8.6933031327311863],[21.896893798273567,-8.378126335994116],[21.801137620765232,-8.1118511351558205],[21.780476736278182,-7.8701531375755298],[21.835501748789259,-7.4709750852540067],[21.721430894160783,-7.3197667964747568],[20.607884385344466,-7.27750412941199],[20.556602209325558,-7.2395270137583037],[20.504029017252918,-7.0225280374532755],[20.413447716337984,-6.9473907223114031],[19.875284974968491,-6.9868971013589007],[19.654656020621761,-7.0458324563478296],[19.524620968616862,-7.1656597434522764],[19.479617594636146,-7.4720696297592024],[19.376027968789856,-7.6561600980048476],[19.340605559058261,-7.9663998741696638],[18.940847014841818,-8.0012064884896645],[18.583107797985651,-7.9369438313914911],[18.013479795349902,-8.1067816880102068],[17.87016011389569,-8.0688620949205205],[17.579638781367397,-8.0987537409246286],[16.985030790918319,-7.2572843244579852],[16.927476522184612,-6.956199789960448],[16.741841771084061,-6.6135330506956889],[16.698832183807742,-6.1991346722632414],[16.543394697037403,-5.9755883799331322],[16.422526689973473,-5.898365767674445],[16.286390722489855,-5.8655477007781149],[13.30261189148662,-5.8815688184450554],[12.966171920418605,-5.8416404936837605],[12.509301160267254,-6.0038830767573792],[12.411865923723388,-5.9860843175231873],[12.214015337292205,-5.7588330667475169]]],"type":"Polygon"},"properties":{"alpha2":"CD","radius":1270000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[14.431371282407319,6.0387483812128018],[14.512266399385949,6.1617101774799439],[14.752409226076129,6.3066842495904893],[15.046233753762243,6.8156840330192434],[15.206985844020453,7.2060352101770002],[15.480208398955565,7.5235239153417055],[15.914162958956615,7.4951124855384057],[16.315843462907235,7.6678535257659455],[16.545130625270914,7.8652016033248584],[16.658146862712798,7.6649460151427338],[16.816624672990248,7.610882243273287],[17.088027361374852,7.6881732141933936],[17.247089844476882,7.8128060746969838],[17.64946392488142,7.9833138015200182],[18.525303565755276,8.0494847895640067],[19.015898257857415,8.5613076687103185],[19.013369205592372,8.7109489057259371],[18.88627254130078,8.8361857019718109],[18.892367787168247,8.8926994388586529],[19.1399244409847,9.0147691636849601],[19.668290655149931,9.0221046838685393],[20.072705575136304,9.1329776750854652],[20.376822301553389,9.151156292752626],[20.779516389976155,9.4135014146117904],[21.260531873982917,9.971182516409403],[21.387782170263492,10.016472541234672],[21.52814481523686,10.207603004318699],[21.672131452264125,10.279742800715651],[21.721722531348171,10.378114821309842],[21.733935262626105,10.611426536498961],[22.043304358247394,10.822471213353706],[22.49387235520992,10.995996472174308],[22.859909847256649,10.919466772235435],[23.309621004961407,10.391261554952639],[23.547110834293431,10.025799726343646],[23.655749298625505,9.7160089971481138],[23.622482775794634,9.340660069826388],[23.500697251568177,9.0865620216015852],[23.598972566649092,8.7966102048905057],[23.713231730962526,8.7302176475185647],[24.194665777499448,8.6531606085404515],[24.195704220830081,8.428002121707614],[24.277830083056053,8.3053491202748102],[24.853162635311993,8.1373363038189588],[25.200220752629448,7.8077668257523722],[25.247085724048649,7.7245044693972984],[25.227501286956002,7.5190966546450841],[25.285615410160325,7.4212865147833709],[25.892362028906959,7.06241543833218],[26.361552827436274,6.6352221327862564],[26.326839869130346,6.4050871222373678],[26.511984128803938,6.0798546672840912],[26.726268940840708,5.9980062532130844],[27.146724836734137,5.7193874985599349],[27.227458518615411,5.5683833217358307],[27.265882636465381,5.2832123694435502],[27.402677721988304,5.109585204087721],[27.075841070671601,5.1924836899040168],[26.822060692313404,5.0626011509260325],[25.507508333260613,5.30021712359106],[25.392097177568807,5.2347724500675827],[25.249201666641778,5.0247382501388644],[25.06521997117876,4.9676637773064254],[24.765526767863044,4.9303148571254107],[24.329337492836789,4.9945562577697729],[23.41715523747612,4.6633225634731597],[23.10697620333427,4.7366534158689371],[22.875263407692273,4.7220866172226632],[22.718093756613385,4.5996991360546389],[22.50545126091653,4.2078084415872672],[22.422118606336234,4.1352015852480895],[21.72350722663759,4.276860729200445],[21.537609877701072,4.2450655701158579],[20.932369790180896,4.4180460883599553],[20.56327278437718,4.4611309994631272],[20.225595232717524,4.8284903710344693],[19.76998254326644,5.0981916497281619],[19.490134914595757,5.1226602278176365],[19.315523006261618,5.0639298147318161],[19.085286554030105,4.9020429573114397],[18.831577351344478,4.5235629545778968],[18.624759886785871,4.307210255779319],[18.610105038068859,3.4788082238756068],[18.416162910401049,3.591922118058049],[18.161004869058804,3.5000498672106444],[17.473373695782321,3.6753528298899001],[16.645388345467257,3.5113169670180673],[16.564275599723846,3.4284334579739491],[16.485236806930121,3.1771381274258981],[16.468288732970919,2.8317836059447394],[16.18339760602046,2.2705384891690952],[16.106921931542676,2.4735188795188994],[16.035054951147533,2.9420989615732553],[15.920425777860043,3.067709145182671],[15.771593152332256,3.1307443753743041],[15.128962847529895,3.8270828650935091],[15.03512586201912,4.0163155383692573],[15.085656338720083,4.1329649770972461],[15.048403015840345,4.3122476654493811],[14.731451962869068,4.6025112976176903],[14.645414674905666,5.147720515405779],[14.563276408633213,5.2799731809878834],[14.613209169256963,5.774814139948429],[14.431371282407319,6.0387483812128018]]],"type":"Polygon"},"properties":{"alpha2":"CF","radius":778000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[11.130461304241063,-3.9162417547782491],[11.288485848348978,-3.6413951666064541],[11.504338150262681,-3.5205350654054195],[11.684514932476361,-3.5086486452283299],[11.781065303814259,-3.3914459431568003],[11.775582694811259,-3.2578931532501643],[11.689309117338658,-3.1269287649353572],[11.691896086865009,-2.9562852105886095],[11.538082840522371,-2.8365394193334059],[11.597411422337762,-2.6464988037564532],[11.601576508906943,-2.3451546789630093],[11.742517145581198,-2.3823951486599113],[11.950223003048958,-2.345069455067879],[12.095440350686914,-2.4019676660261431],[12.366388174358034,-2.3384924090941928],[12.469785374357016,-2.1859644657289108],[12.43237705442432,-1.9290255879024008],[12.59049229169943,-1.8271175053962054],[12.793348631142688,-1.9320099002143443],[12.965397938490744,-2.2647020511982356],[13.064278724869038,-2.3375732865769154],[13.44418358041961,-2.3936912977019151],[13.733885242755933,-2.1387960735306812],[13.88413832431163,-2.2949706569670636],[14.015992415255456,-2.3210649566075099],[14.192392733221471,-2.1844922717889821],[14.251699557083624,-2.001565709636111],[14.395243362595613,-1.8340701307437612],[14.454265741557943,-1.4307196141864504],[14.410889558144229,-0.97208186178369249],[14.460881454883204,-0.59015655504137976],[14.392775458181124,-0.49268634660292365],[14.206866342645357,-0.42713458793179071],[14.080327248019309,-0.28914168715261435],[13.860319784924286,-0.203234818720961],[13.918271715699225,0.29033429799911253],[14.08760312457572,0.53635250689371072],[14.309850082637972,0.61834109359593181],[14.417337344629912,0.80960671007593454],[14.417543668405735,0.92812083788577027],[14.238076987025817,1.3008548889769456],[14.11554106877089,1.3848474796428889],[13.832883643928946,1.4126386077634272],[13.213318094863903,1.2519580313379255],[13.171817326507282,1.7833742304389348],[13.293780236460966,2.1613283232534388],[14.455251858587623,2.1564271490032119],[14.578878996858991,2.1988511041792305],[14.990582500686543,2.0163708252483294],[15.160088074757244,2.0353630772404032],[15.335719004680785,1.9597913448545585],[15.927161204852812,1.8739367422320723],[16.047730616228723,1.9681805564068582],[16.080325471068249,2.106703665145587],[16.447988337600613,2.7940645088665179],[16.476980744632979,3.1650951617389547],[16.610864945910283,3.5052016238992478],[17.218485809452492,3.5972684572226536],[17.44298396465441,3.684869379775483],[17.9626232008176,3.5515410118318136],[18.209002019178882,3.5396749565277421],[18.49960696968342,3.6039258744055558],[18.610121505909955,3.4782727205250805],[18.621916419502167,3.3040863184647562],[18.077154868005682,2.0238129242357021],[18.057556967109175,1.5349243839072568],[17.900635392862483,1.0898197231803317],[17.925026809854572,0.5373047072832654],[17.888221737282823,0.23838681181841448],[17.72756490279076,-0.267057597256597],[17.752602134909431,-0.54894682946589968],[17.283161181996157,-0.9959070994947653],[16.886661419596614,-1.2206788791711036],[16.540517355340292,-1.8399885032678291],[16.224774579509418,-2.1720696752197663],[16.193041177987443,-2.2887726049991644],[16.21715978750073,-3.0302584581143157],[16.147626654118678,-3.4589356679688303],[15.87232440330356,-3.9341103260822234],[15.559922053889251,-4.061869484186345],[14.707838573768209,-4.8814649952226477],[14.410961958995982,-4.8311435913670264],[14.340552018410641,-4.5000933695605729],[14.16502795311526,-4.3975645602802285],[13.795899676820083,-4.5082485074790561],[13.659427973359051,-4.7212808473610499],[13.414908814481191,-4.8371628566601101],[12.894103090786288,-4.4830776111694748],[12.744924838104293,-4.4681649576542384],[12.413566030697799,-4.6282210982598482],[12.018400409610178,-5.0039515811614459],[11.820956290018104,-4.7553384284877716],[11.733424092068484,-4.5128115121688213],[11.130461304241063,-3.9162417547782491]]],"type":"Polygon"},"properties":{"alpha2":"CG","radius":685000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[5.9702489139387112,46.214569740552143],[6.0490980034421185,46.306362072704438],[6.0681425887670875,46.458466733852489],[6.1609414358925294,46.610817250501981],[6.3778433969430433,46.745921997261043],[6.4563898929685433,46.948140718450453],[6.6335999874476022,47.011685070246962],[6.8492298501742974,47.186916867691814],[6.9051752301414187,47.28419685439745],[6.9006402290031454,47.394164835270658],[6.9685043184505489,47.452990552738278],[7.1359842474993176,47.489625211000856],[7.3246589343919535,47.431355299004593],[7.6156376937864705,47.59243961422905],[7.937021979994161,47.564603198655099],[8.2839247563139651,47.611211385210396],[8.5149631618206403,47.767604390209669],[8.9277940336109936,47.662764769389362],[9.1827601622938175,47.670448549601161],[9.5606041873554801,47.507302676254611],[9.6256307345322796,47.466936312219687],[9.5483731394963218,47.257759445102316],[9.583425592060113,47.1250002122867],[9.8451385247564538,47.007145856133505],[9.9492429732101471,46.90627842017161],[10.162312678736882,46.867866837090801],[10.349460645742537,46.984480446067415],[10.454340880444448,46.899350910317551],[10.430496565908605,46.550276352459896],[10.280039797828209,46.547486060148202],[10.177168071118448,46.491128187587627],[10.123588937220362,46.386782232387958],[10.128192421854035,46.2384820625998],[10.041143477210964,46.238280569346642],[9.8832475835104709,46.353044879416679],[9.6339642205112455,46.295916345834385],[9.3268982773302902,46.33823529814353],[9.0638426050701195,46.090920293644565],[9.0233734679626014,45.98106157328813],[9.0463967633053581,45.875572569970224],[8.9537569750086075,45.830287036690194],[8.7242799529180122,46.069033371737959],[8.3446885188739426,46.302607713042939],[8.1963310033152972,46.2640010218268],[7.9929959484942374,46.016099678585924],[7.7927781160544001,45.923770060362536],[7.5515175490479889,45.972888900490481],[7.1290206178142856,45.880642435106473],[7.0213024529278014,45.925972843532897],[6.7723354375990761,46.165188599553218],[6.7643560503525642,46.323445183657896],[6.6474894623941907,46.425075960839607],[6.3877287075840989,46.413740656428232],[6.2691743094814694,46.249208118960183],[6.0865541816861581,46.147316467476415],[5.971721217217806,46.15140955543302],[5.9702489139387112,46.214569740552143]]],"type":"Polygon"},"properties":{"alpha2":"CH","radius":177000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-8.6032589648805722,6.5078031134683849],[-8.3511102268487321,6.7772907909778333],[-8.3026589085427656,6.990810461881158],[-8.3088041388103058,7.1107023343351443],[-8.4860762769979026,7.5584821638668798],[-8.1867207237755633,7.6463401759103746],[-8.0769785712748128,7.997348314939031],[-8.1132185599264375,8.1425528950382944],[-8.2558335425156493,8.2538211905956196],[-8.2367112413158345,8.4555387784526079],[-8.0074838809428304,8.5321095975778576],[-7.9286926139900267,8.6670072864328791],[-7.9199962427280743,9.256627525824543],[-8.1367299913050104,9.4957867364265507],[-8.1549582088481873,9.9731647827426055],[-7.9904898643621856,10.162267217667306],[-7.8309514282365562,10.225565289122887],[-7.6610327293137708,10.427170085738076],[-7.4980680298367135,10.439531593502831],[-7.3005244286926967,10.256362574158853],[-7.1051443615454781,10.223590799947482],[-6.9502034594454321,10.342089912504134],[-6.7494737034155055,10.40745475544419],[-6.6541169350806042,10.65615419616506],[-6.5117909277742756,10.631463139938038],[-6.2611629007529821,10.723804388244442],[-6.2068397599444305,10.445615833454733],[-6.1216707135710919,10.341492740997483],[-5.9898125386200718,10.314869320075459],[-5.6999304639881396,10.431555995054175],[-5.556627155656229,10.439741165514793],[-5.3985240813058839,10.327541451552557],[-5.1753969498317218,10.292410622170367],[-5.1000227894570092,10.24145960244145],[-4.9336697169846762,9.9090038279342245],[-4.6550340098810192,9.7278397110519865],[-4.3570594951966353,9.6913806467324264],[-3.7905834131685543,9.916897161592642],[-3.2235979742585386,9.8951763929052792],[-2.9884956453114619,9.6871537108687349],[-2.870165962191555,9.5122579989644276],[-2.6960220875836289,9.4810693144908811],[-2.6744770231495729,9.2825863074537747],[-2.7127956115296121,9.0958956142926617],[-2.6005944516834489,8.8003698266576826],[-2.5061181848581793,8.2088119295222945],[-2.6135848166722604,8.0468698141216439],[-2.7676336366924241,7.9427425260433298],[-2.8606979306781835,7.7636558653590599],[-2.9860116544210298,7.2049503320569848],[-3.2153309266260734,6.8461612611505354],[-3.2419295507083019,6.6219661553134088],[-3.2030326594044261,6.3579322257091571],[-3.0074482913713911,5.7439924283926205],[-2.9309778574851415,5.6537640107608462],[-2.7938379106771944,5.5999718703305392],[-2.7554651427649515,5.4272359018473031],[-2.8157958875771958,5.1532392211740454],[-3.1140617319572561,5.0889135673975403],[-3.9741338974409093,5.2438045230545081],[-5.578379596894699,5.0863861589875858],[-5.9135975194357435,5.0103769744217699],[-6.8550899860977763,4.667251726789579],[-7.544865027881305,4.3516002593421179],[-7.585410092943933,4.9113081783971309],[-7.5686419583089091,5.0805360089722766],[-7.4331262478658999,5.3447184468547508],[-7.4089951353865855,5.573624150120744],[-7.4518915322509569,5.7675296106631206],[-7.535904751972482,5.8538567768665049],[-7.7962963044234472,5.975217495817982],[-7.8696896102508722,6.1838687939777097],[-7.9479674107812901,6.2666281484377349],[-8.2828248505143502,6.3175854765496426],[-8.6032589648805722,6.5078031134683849]]],"type":"Polygon"},"properties":{"alpha2":"CI","radius":466000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-159.84224879807229,-21.229106178693055],[-159.84086196257135,-21.224669578291184],[-159.83359977882094,-21.204783411572738],[-159.83186111130314,-21.200623120007169],[-159.82820798511798,-21.197980080142891],[-159.81498028435723,-21.189302226340871],[-159.81052089898884,-21.186700286829208],[-159.80535797350726,-21.18668009875746],[-159.77292811218655,-21.188254727862041],[-159.7684275081088,-21.188680170437202],[-159.76457727140161,-21.191049160886717],[-159.74360293612378,-21.205315967623925],[-159.73973230488207,-21.208239568449869],[-159.73910112451773,-21.213049021573006],[-159.73725624677414,-21.235802513743717],[-159.7371005573431,-21.240586790830417],[-159.73870536242936,-21.244923047135032],[-159.74067742067308,-21.248993903738352],[-159.74535648619201,-21.24926291898576],[-159.76780110609266,-21.24946820514338],[-159.772539142451,-21.249286071584063],[-159.77723845212856,-21.248654684444539],[-159.81001229038264,-21.242652728523314],[-159.81617566893149,-21.241623254473062],[-159.83460686600839,-21.23883990611154],[-159.83941091722261,-21.237864260065578],[-159.84108457800073,-21.233428259160458],[-159.84224879807229,-21.229106178693055]]],"type":"Polygon"},"properties":{"alpha2":"CK","radius":6000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-66.63633883628458,-55.234402274618283],[-66.537015131275538,-55.168470707150682],[-66.436197052215377,-55.189861735566133],[-66.551757792501405,-55.272639939068668],[-66.63633883628458,-55.234402274618283]]],[[[-75.70784543672238,-46.774909746812867],[-75.656622934110104,-46.610508767247268],[-75.079579082598443,-46.227579364259888],[-75.042986416961327,-46.118740234446804],[-75.066488333894981,-45.875091771481337],[-74.763460066641713,-45.798002818005102],[-74.51910389078553,-45.462098931856737],[-74.502105054001944,-45.285281202817039],[-74.358856745092282,-45.141276937292076],[-74.328419595964704,-45.011249032068186],[-74.373459676683481,-44.905373881858701],[-74.617341176321204,-44.648007371305397],[-74.501559877238734,-44.47369335775894],[-74.198875346059381,-44.372267226224189],[-74.115624514865175,-44.287701573545235],[-74.031366682663915,-44.042936100925765],[-74.139719730315164,-43.821225229930654],[-73.856938041199243,-43.784056126987018],[-73.810823718190051,-43.827353519443953],[-73.789933956914794,-43.876356082083873],[-73.884002375294656,-43.962953326445863],[-73.905585748588777,-44.094071466207581],[-73.837757512475548,-44.212394460434808],[-73.703448101533397,-44.274280661541383],[-73.726012787279259,-44.456740581096035],[-73.643713553599966,-44.591910500844079],[-73.474720853347947,-44.628653549595271],[-73.310371117811712,-44.516492899118894],[-73.241495470202821,-44.284915326635307],[-73.224256712966834,-43.898130145262201],[-73.082723781406031,-43.833061451566117],[-73.018486355821196,-43.698549119688167],[-73.100701372021092,-43.455127601556455],[-73.075756273059085,-43.323763115051889],[-72.947089851289519,-43.206543900483538],[-72.821575790863406,-42.939066175900805],[-72.847848220795129,-42.669181143797722],[-72.754758707365383,-42.450765474030881],[-72.784871857481534,-42.301227189491094],[-72.712750470889603,-42.080212191815868],[-72.874872599592194,-41.738556583294105],[-73.053522975102396,-41.677663165938],[-73.375743957708067,-41.804057953290915],[-73.47029894185529,-41.901149256412843],[-73.485464743134258,-42.016945352527095],[-73.42380689101428,-42.197579483831731],[-73.471097927215197,-42.466162588430272],[-73.565062713002874,-42.539987666219403],[-73.600170630066089,-42.652212470425802],[-73.436610660067501,-42.936541105804338],[-73.540984959815191,-43.073519291792188],[-73.687225363911608,-43.161976625635255],[-73.773553803939507,-43.345673020452587],[-74.114363634498815,-43.357636092168114],[-74.387081163483003,-43.231572130171735],[-74.205901153187526,-42.859354637155931],[-74.163295237910788,-42.609710651106937],[-74.194212353057367,-42.44168620844701],[-74.159990355167025,-42.216504957130546],[-74.064265760082961,-42.068270567998681],[-74.062748253792762,-41.822791506769654],[-73.887387922906967,-41.768829657998793],[-73.799826720718556,-41.644427457855869],[-73.983357124020941,-40.974368025153716],[-73.788088309601193,-40.480452358755329],[-73.670768091855777,-39.963313304462176],[-73.414078786828597,-39.786515560294099],[-73.2464129959275,-39.392892831099196],[-73.240067377178505,-39.195053483020111],[-73.519847267236599,-38.509297065442006],[-73.468957073492348,-38.05025780689359],[-73.661592108747385,-37.698462587912147],[-73.625592831077711,-37.467928158010452],[-73.662118504047058,-37.341004353570668],[-73.601523790923764,-37.188735685883756],[-73.342870451110784,-37.219141902647195],[-73.204584356950093,-37.126825951700013],[-73.117844707712464,-36.688558698611637],[-73.013817124522873,-36.623523897602304],[-72.881777407628334,-36.399504624689143],[-72.778171562323593,-35.978647281051863],[-72.617680025798137,-35.782806193377318],[-72.623683998143463,-35.585797368321487],[-72.222640604887289,-35.073019276721737],[-72.055577960755784,-34.606765148861975],[-72.002611110210736,-34.165374278087064],[-71.830767817116524,-33.819647501796368],[-71.654988510668531,-33.596153760973039],[-71.742689342553419,-33.09522587616506],[-71.598316916160584,-32.975966177015422],[-71.460513946571396,-32.66824429956727],[-71.436849246431606,-32.377954538540259],[-71.512845059887795,-32.207860525624518],[-71.528057094952956,-31.792748871676668],[-71.661717254490384,-31.16950543152554],[-71.70883791978531,-30.631944084301537],[-71.669236185787426,-30.330521090272654],[-71.388198783682213,-30.089769523557479],[-71.317615189040779,-29.666305287116352],[-71.332948411021604,-29.421371947373864],[-71.48560870990589,-29.198139106417784],[-71.519016883863642,-28.926489671885321],[-71.319365239748066,-28.688177063169306],[-71.188673406557285,-28.367485165602648],[-71.08461700248084,-27.809552433250484],[-70.921042482382305,-27.559914476806878],[-70.89763226271937,-27.187565626773893],[-70.657980791048871,-26.346746528629922],[-70.641381246615211,-26.012887817211954],[-70.713458401680526,-25.784198719989149],[-70.632756275216366,-25.545756779809516],[-70.484745675253762,-25.357387574602054],[-70.447806077172714,-25.191252909314105],[-70.573862883836568,-24.644332510745215],[-70.519200595046314,-23.962872338846008],[-70.435666202545647,-23.630185760456285],[-70.587936669927331,-23.368294446115716],[-70.593045685927166,-23.255507177765008],[-70.562943842667764,-23.057237494013954],[-70.408414933428062,-22.988007292612053],[-70.326845098911022,-22.828993184320201],[-70.08631989068256,-21.470801890248381],[-70.196718909276868,-20.725362461614324],[-70.15440017531202,-19.738175657978342],[-70.276301066765285,-19.263706564100424],[-70.355839075405342,-18.450358729723039],[-70.417805037035876,-18.34571249793018],[-70.153110280625583,-18.314937019735382],[-69.928873862649226,-18.203205298028269],[-69.803878233011332,-17.978390598079944],[-69.85184152483447,-17.703908648191998],[-69.658947882156639,-17.628241576769145],[-69.511098780024625,-17.506493688687684],[-69.300826374342449,-17.930148714666291],[-69.094151507237441,-18.050647696604351],[-69.119140332382514,-18.198067198945424],[-68.941536772674752,-19.004249049388061],[-68.464343453787848,-19.428249252532023],[-68.632718370070236,-19.659510506568491],[-68.560892367195947,-19.967040086097139],[-68.697510350905986,-20.156083861845637],[-68.698435948924754,-20.406686751012256],[-68.649341681029895,-20.511226863412062],[-68.484596382297468,-20.628488824884425],[-68.524576343502659,-20.846687107113677],[-68.19727756950023,-21.30037081418029],[-68.186834845606185,-21.596089667168265],[-68.079520034055449,-21.951185280981896],[-67.987332391769172,-22.061587710157095],[-67.861580798475572,-22.775135537125053],[-67.684756457630172,-22.889609954478537],[-67.194945303794441,-22.821926133921973],[-67.009079320199987,-23.001438559303509],[-67.356377896885704,-24.033619288938922],[-68.214335529299461,-24.378089678039927],[-68.524467242811454,-24.682630798061975],[-68.53243118140297,-24.833188978928121],[-68.384482122162112,-25.091958788318028],[-68.540164506155605,-25.2475226919425],[-68.595623504545841,-25.47583714575552],[-68.414754557855119,-26.153630813131784],[-68.559647946299648,-26.325618635644329],[-68.590825117447991,-26.464079905923072],[-68.31886845478887,-26.877612566607198],[-68.318865174420168,-26.973191944994131],[-68.346161459242069,-27.027740247756594],[-68.592188854512358,-27.139839769068377],[-68.754695161805301,-27.150757741852868],[-68.85215780722352,-27.214638249832173],[-69.011966431390789,-27.487321481546502],[-69.174606966131961,-27.924568060464615],[-69.649089085621554,-28.417850547217046],[-69.828090599319282,-29.103074307770896],[-69.975205055218964,-29.229656369782653],[-70.016432288414407,-29.334580238078281],[-69.927903492056657,-29.769177468061688],[-69.933320124786576,-29.997777267704496],[-69.844563934418915,-30.174926938188666],[-69.956496567132149,-30.357986184861762],[-70.172703418880516,-30.464803965803583],[-70.3256705246271,-30.85342799755448],[-70.309349109135511,-31.022552429481259],[-70.494234657025856,-31.181064188537782],[-70.550512105065224,-31.301985546607145],[-70.557387343920553,-31.612900134847688],[-70.453957564820996,-31.811794616575728],[-70.254649857780677,-31.957730804148131],[-70.328888844522339,-32.19512202410899],[-70.169888375615727,-32.471809046076643],[-70.127708884267506,-32.770229392234633],[-70.022217301511347,-32.884650767552301],[-70.051624245167986,-33.122258184048995],[-69.983729235113827,-33.213837212371821],[-69.819839337408894,-33.283956765819553],[-69.798028308279584,-33.398587701111708],[-69.893736633857358,-33.731355265951883],[-69.852669519317971,-34.22424260356054],[-70.043009221064409,-34.328579993384764],[-70.209686129203561,-34.581877629943051],[-70.457772468730226,-35.250194736295747],[-70.380399572574703,-35.771865676042275],[-70.405046507521661,-36.061631553736476],[-70.686075643843736,-36.261831593412737],[-70.749482998444932,-36.392389177597721],[-71.00744291623748,-36.511056500664125],[-71.164841644143564,-36.784452401883712],[-71.118633982933005,-37.114323060448363],[-71.181531917328215,-37.301589965152601],[-71.13505502650473,-37.445151662194114],[-71.172021611590054,-37.731835065167999],[-71.028433205702797,-38.041283220754337],[-70.979373830281247,-38.396050690393025],[-70.84847326073475,-38.5462784950616],[-70.95172051423279,-38.73826409638],[-71.114489612386933,-38.770408632360372],[-71.385020798292729,-38.927234220588112],[-71.422265720932913,-39.292908339401116],[-71.539656876632549,-39.602268978656923],[-71.65751423177575,-39.735658464513229],[-71.65907350084548,-40.015787557644487],[-71.739019688739077,-40.186827425978088],[-71.696641392087571,-40.339772139347687],[-71.810328441852235,-40.462782347106973],[-71.92878907554055,-40.746836627978695],[-71.873268965015086,-40.893026257173602],[-71.887008622440391,-41.662845173135182],[-71.750878644540023,-42.051170566450146],[-71.761127191673026,-42.101315590374845],[-72.042859358101481,-42.248914970451381],[-72.053746358927924,-42.473155594840399],[-72.130082846022461,-42.62002992486218],[-72.107547418572963,-43.028039854437246],[-72.006407941835121,-43.114390846146023],[-71.781657754791354,-43.167003454163407],[-71.750850478945409,-43.237324773660895],[-71.822100539463634,-43.455981524743379],[-71.73421412434044,-43.642180281291246],[-71.742261014003176,-43.784304627879727],[-71.680333306050969,-43.929575196050308],[-71.808582262919472,-44.139172694934274],[-71.794315754623213,-44.293689823009871],[-71.673846498435736,-44.39149695179573],[-71.212691689384854,-44.441418392452988],[-71.151135628030687,-44.494141086267142],[-71.159069771432527,-44.555406577521055],[-71.261310156784489,-44.762896209777132],[-71.445260597259903,-44.814644915914897],[-71.533096777688513,-44.936153753441509],[-71.520647681930541,-45.067521039111028],[-71.3540093954241,-45.230606454554867],[-71.349551727981009,-45.331822253045033],[-71.508308760937709,-45.51246768044939],[-71.673408339737421,-45.55636475814341],[-71.751384909593668,-45.666906462439862],[-71.741161460997361,-45.80179639881063],[-71.631836625511156,-45.953715897316911],[-71.786489376701809,-46.194956215177399],[-71.699363614002152,-46.6465307462962],[-71.943880843663166,-46.891170326794821],[-71.959994230766469,-47.021849712150093],[-71.905184047292011,-47.20147358803608],[-72.010917609620947,-47.233855153273076],[-72.287800839754155,-47.458416631133318],[-72.471976288317691,-47.791293485856897],[-72.472073502761106,-47.92677061443198],[-72.32855825115314,-48.110187059815615],[-72.293288333466649,-48.229083156918293],[-72.352674553245095,-48.361253089664885],[-72.56650493745569,-48.49066526649883],[-72.591929360683082,-48.72964785197528],[-72.651452400328736,-48.841412876842526],[-73.013373179043711,-49.000276157435174],[-73.130040536217066,-49.16325772073359],[-73.135505575706205,-49.30046942988988],[-73.431512355418874,-49.344067078854771],[-73.531695567366299,-49.454557265764834],[-73.55795761551083,-49.567650832065773],[-73.470679729886839,-49.79451429147008],[-73.51938940618362,-49.921583577257103],[-73.494865910206229,-50.101262892488805],[-73.171348198384734,-50.668043486083164],[-73.028568116304839,-50.715477883804674],[-72.808420444292139,-50.638885801153052],[-72.509775517252848,-50.60781304234019],[-72.340429802784584,-50.681967575154097],[-72.276520230551895,-50.910249036711242],[-72.354899883826931,-51.575053623271501],[-72.273339976123793,-51.681811412392953],[-71.87428097101089,-51.964316355162865],[-69.960278577878015,-52.008380563390354],[-69.496909907457223,-52.133028920015327],[-69.206222939468248,-52.136305910061594],[-68.461172014516208,-52.290640864330626],[-68.443668112807288,-52.356386875900455],[-68.581625655083585,-52.401483572419203],[-68.654240883506674,-52.539626051725165],[-68.630363867983377,-54.848032978907383],[-68.553186298603933,-54.931780588302679],[-68.368350428294875,-54.987820881109478],[-68.102614984620885,-54.929399965741332],[-67.245342455380239,-54.977888899711708],[-67.107529896365293,-55.063749823681974],[-67.109726914291841,-55.191840045425039],[-67.34722550979842,-55.333810658541694],[-67.542208218902871,-55.199250639544161],[-67.958499930275011,-55.268140270540862],[-68.059199843764134,-55.380080552152293],[-68.045983873140429,-55.647715203975245],[-68.082664462595801,-55.650284713113557],[-68.345793981708965,-55.508057406925602],[-68.866963810146757,-55.449927119005295],[-68.988573665397595,-55.387809716842114],[-69.180996807536403,-55.474569300692423],[-69.41170315401132,-55.443884421582794],[-70.116182596150026,-55.052900463885202],[-70.475577809328087,-55.176815403285275],[-70.939737489951298,-55.061687748842985],[-71.185831927983671,-54.906527084424695],[-71.40652492794581,-54.930616027405001],[-71.443730562228083,-54.739471358278919],[-71.518943054656305,-54.65354223336049],[-71.901376790711794,-54.601375502626183],[-71.917081839008418,-54.351627531482485],[-71.98817644098628,-54.206085621909878],[-72.100168410423649,-54.113605362228022],[-72.343383766767857,-53.994250063491478],[-72.870967852511654,-54.126354181399293],[-73.033643929443826,-54.023363700553567],[-73.300392864699077,-53.945879246915936],[-73.395227207839966,-53.769083449114611],[-73.659338534727397,-53.581994760277837],[-73.844890376728074,-53.545648648456755],[-73.671820494581269,-53.360129483087661],[-73.676945398902589,-53.245149523348957],[-73.744591061283231,-53.152032821587653],[-73.967580795799194,-53.080166555520663],[-74.270165411505019,-53.081294005600434],[-74.558143858440701,-52.921693326241233],[-74.711781826341394,-52.748888805545825],[-74.571675134878262,-52.771565380876737],[-74.336969037151249,-52.926192948182894],[-74.158329139674393,-52.95274987061579],[-74.012511719888465,-52.865556795909313],[-73.986363924441719,-52.697682899835279],[-74.06161706148751,-52.464228970924005],[-74.339274200457851,-52.070637659420413],[-74.542866424094584,-52.043569493312688],[-74.650872137267029,-52.143070866630701],[-74.694666786835853,-52.278952093047522],[-74.851656582598793,-52.270462965587299],[-75.016939050938575,-52.03778658110523],[-75.136676020204447,-51.712186792709474],[-75.28891291519777,-51.625167926975244],[-75.299794685684347,-51.55649442159222],[-75.153546138594848,-51.279106705435062],[-75.038407444822397,-51.302701503704469],[-74.929415917373206,-51.262507002571262],[-74.852609307142586,-51.071654029206314],[-75.082962529942833,-50.763065697025013],[-75.211194510925608,-50.724816127066802],[-75.411272406829724,-50.764110480213901],[-75.477123940965583,-50.654187412748335],[-75.425810265530316,-50.526414520570199],[-75.448856878156292,-50.343348466196502],[-75.335300734389136,-49.972923561706516],[-75.4044122278288,-49.83860494750445],[-75.55084000152209,-49.786505333983804],[-75.56986109339465,-49.697117898840652],[-75.459684823916476,-49.548924560284142],[-75.440822265186767,-49.436096941290479],[-75.508095582181696,-49.283074549490607],[-75.640825031936672,-49.195347482502171],[-75.594320135104169,-49.072054657542338],[-75.650644837002119,-48.586373625482615],[-75.518611392836405,-48.308924753879161],[-75.560525577003787,-48.071086514399603],[-75.306848903896878,-47.968142175639372],[-75.260750440790133,-47.763975945805804],[-75.197537761735163,-47.726182591805618],[-75.085265400374368,-47.690843636366424],[-74.823237558017638,-47.76527612983319],[-74.689425501399029,-47.731344611714349],[-74.520386814005448,-47.549850292512048],[-74.403139855502204,-47.327683927362514],[-74.245689913660399,-47.211443741698844],[-74.209472300547347,-47.044250714601546],[-74.317860764877338,-46.893283394081408],[-74.690753963524443,-46.863754227206861],[-75.00580264816341,-46.740921278280517],[-75.157018135865286,-46.621951469899265],[-75.337072543356484,-46.681783359715141],[-75.430479899249463,-46.934334625934852],[-75.631222833387724,-46.865042077384636],[-75.70784543672238,-46.774909746812867]],[[-69.170067386107036,-52.209410318827402],[-69.352538193198924,-52.306000540134313],[-69.361728034881992,-52.495100568329711],[-69.166539063131253,-52.65777768643904],[-68.9757290081793,-52.638226373096771],[-68.870007000418823,-52.524048877300622],[-68.878120735248984,-52.368653218486443],[-68.958732517683202,-52.282366009264841],[-69.170067386107036,-52.209410318827402]],[[-70.948241840223247,-53.568973291563182],[-70.884950989744851,-53.664215872489613],[-70.779821610610753,-53.709212479235923],[-70.529974829316345,-53.627238945998464],[-70.464514731943595,-53.779518641524866],[-70.345450724635725,-53.845591028268892],[-70.211684460988977,-53.820127626370329],[-70.100155302198161,-53.70505774575259],[-70.073653060883231,-53.590231889832879],[-70.116406560339314,-53.48041620667847],[-70.213572484593769,-53.413736905945875],[-70.375087357338074,-53.389779529390864],[-70.459968342452626,-53.206189102441151],[-70.445474360579439,-53.090048176140421],[-70.33045269619322,-52.885928166978857],[-70.435093492871317,-52.703525184436167],[-70.600179575112236,-52.68583021946332],[-70.800379769158553,-52.807764787534914],[-70.979880679613728,-53.353429697896814],[-70.948241840223247,-53.568973291563182]],[[-74.1182002861527,-51.908911256110976],[-74.107283774738022,-52.083938986966253],[-73.939762161709751,-52.195706367794401],[-73.787666379902291,-52.189357484630847],[-73.678086434269517,-52.063261083687607],[-73.684681270228936,-51.908391252665595],[-73.799947917183701,-51.804748807104296],[-73.984911856078753,-51.787253772305164],[-74.1182002861527,-51.908911256110976]],[[-74.561093195736007,-51.360729169121647],[-74.641907154766201,-51.486807955085858],[-74.622603120015327,-51.618419247560702],[-74.542396916209995,-51.70027558627293],[-74.361696795456524,-51.746584108854528],[-74.245456348615548,-51.720453280077187],[-74.026799128823924,-51.549663413336127],[-73.960436006167271,-51.41407182484982],[-74.007753939021214,-51.27071856835591],[-74.141794549027153,-51.201276090100741],[-74.432253036219336,-51.204540561798332],[-74.561093195736007,-51.360729169121647]]],[[[-67.850844632263346,-55.82693007731357],[-67.640414652516455,-55.789542920381521],[-67.380913173717317,-55.57049025599828],[-67.262700982962613,-55.743809576035083],[-67.575262556955806,-55.889350375681005],[-67.831472656868328,-55.864587489422711],[-67.850844632263346,-55.82693007731357]]],[[[-74.842454528161014,-43.595414790533653],[-74.745042568092174,-43.536222034367626],[-74.665053634848164,-43.599595329864307],[-74.810371564862947,-43.625149969662544],[-74.842454528161014,-43.595414790533653]]],[[[-75.141867078456983,-44.81572698834777],[-75.079576458778135,-44.795331939698983],[-75.042676265767199,-44.889959469757869],[-75.098616428171809,-44.901560386151544],[-75.141867078456983,-44.81572698834777]]],[[[-78.989145456406348,-33.661677666176438],[-78.882976330233532,-33.57579368266039],[-78.769240314263627,-33.627396954964965],[-78.801876597334143,-33.646107294852264],[-78.989145456406348,-33.661677666176438]]],[[[-109.43387995275282,-27.171009650909028],[-109.39038160210436,-27.068645062212337],[-109.22344647058154,-27.101228340138118],[-109.28008183861087,-27.140190195082866],[-109.43387995275282,-27.171009650909028]]]],"type":"MultiPolygon"},"properties":{"alpha2":"CL","radius":2584000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[8.5330163627135409,4.6058536108446244],[8.5853200928618918,4.832734111608918],[8.8021917360443478,5.2077154033723181],[8.997338924416141,5.9176278543187717],[9.3691345348225177,6.3154571665519557],[9.6562468473646401,6.5352948128761845],[9.7800620844825303,6.7599890272838898],[10.143489682459201,6.9960904834071203],[10.253117815875839,6.8922994828575366],[10.420378690760922,6.8871314628678313],[10.60620633010949,7.0628228688404073],[11.008544112406559,6.738975083697575],[11.07348687620641,6.5682690952588896],[11.161452258653711,6.4894242641898927],[11.317041636258228,6.4845588996851307],[11.516644979096002,6.6424345949910446],[11.580303242210638,6.8886863849436732],[11.78551012760782,7.0874100010355283],[11.767639927804153,7.2722667792387012],[11.998810643963024,7.5817272059344081],[12.025477569329032,7.7276865281566174],[12.157578121809248,7.952256704232318],[12.233619365585039,8.2822610734606936],[12.401116407843594,8.5909805913769741],[12.599859819881328,8.6362165791024381],[12.730529091517976,8.7461855806989046],[12.805505031325087,8.8868716408397042],[12.929660825579434,9.4261303586887415],[13.202685368215757,9.6074317708740828],[13.270114355348426,10.036066622035047],[13.413822917666421,10.184259174153715],[13.535582425741678,10.604980332631794],[13.697980674980581,10.870013978862996],[13.978041110654459,11.209130711581038],[14.209109371543548,11.273333941505513],[14.558965641241036,11.526806681600103],[14.615280973568057,12.151191291754104],[14.491963526838745,12.309239273647796],[14.197684517660205,12.383981137513887],[14.064248524120192,13.07828296834851],[14.465740989059819,13.018674164690411],[14.567729661520627,12.795532702662166],[14.761069457758532,12.655451655321174],[14.844834165123515,12.506098726184856],[14.883733371882785,12.270408871832982],[15.081033998428095,11.845465462526681],[15.12172273803491,11.541254557292056],[15.030748932814687,11.113718356537959],[15.135823435985209,10.639907989230633],[15.288608102596642,10.34315381035025],[15.654411266877423,10.007856400052333],[15.540873269065548,9.9605113488854986],[15.142672813194396,9.981687670321314],[14.835835970480684,9.9418405643118888],[14.285598323592209,9.9803993482330462],[14.148732744504589,9.9072730595321055],[14.021714061233885,9.7150148820509514],[14.043107368409357,9.560834760086113],[14.340153117558643,9.1978433493356171],[15.116067425472513,8.557214576884725],[15.430077155912194,7.886502765949154],[15.556902176257736,7.74370442974611],[15.532263198626035,7.6044712114334168],[15.378911154475713,7.3583318334037431],[15.213686549665109,7.2144083663573557],[15.034340015933305,6.7846017856051395],[14.862494666655552,6.5554059208783411],[14.761471649534608,6.3125374474683165],[14.542713332549694,6.1178509105779693],[14.534706220523571,5.9994353193487644],[14.616894531250011,5.8603593272891743],[14.573356758221491,5.3061484725671244],[14.64150099761704,5.1743381638666186],[14.717807034176731,4.6405399416673339],[15.061065477665052,4.2893916189260688],[15.136651252593246,4.0690808788632324],[15.13177139837831,3.829511650492214],[15.740235068826605,3.1633934910877826],[15.908520643092547,3.0927857807406998],[16.063233259659199,2.9084688968952306],[16.107474598918998,2.4736168379440819],[16.183170694616535,2.270026331206735],[16.073270339771884,2.0513386530543163],[16.135912920814345,1.7243399614496993],[16.059419147549168,1.6764851412240114],[15.705597738817458,1.9240098515725983],[15.338830904957396,1.9450077879275072],[15.19264781430693,2.0139801166136668],[14.902545384926068,2.0125570968101796],[14.574770222246709,2.1670153607900855],[13.298561866040709,2.1615294692272133],[13.132727440286903,2.2584988869041709],[11.553328050315331,2.3021303656809065],[11.40698616140274,2.2684747192045673],[11.32854411943655,2.167674810153259],[9.9753454288552668,2.1696545947761949],[9.8010360888793429,2.3045255569474654],[9.9399114911444002,3.0797566380617303],[9.8870951124771747,3.2900598850286302],[9.6722911551127382,3.5377012332105391],[9.5150482608055373,3.8629400382963506],[9.0003115035413384,4.0917670670159394],[8.8688870213857811,4.4552024534322703],[8.7688509869389826,4.5187883958202235],[8.5745456436319092,4.5264508955046914],[8.5330163627135409,4.6058536108446244]]],"type":"Polygon"},"properties":{"alpha2":"CM","radius":694000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[73.607596163407564,39.229248917302215],[73.631899590677435,39.448675254224142],[73.775391138745945,39.48751614063854],[73.852620910168952,39.575942890388447],[73.835575791435161,39.800087411568605],[73.991740795672484,40.042949162853766],[74.420666539662605,40.144535405632361],[74.737326569147797,40.341834158288215],[74.835318259696962,40.482360077719996],[75.212850762205207,40.474589564586594],[75.525389680382773,40.627229698513517],[75.583300735235213,40.605171082085043],[75.669540957273966,40.377063448498134],[75.783136872319503,40.308135354134187],[76.25830025184672,40.430486543310366],[76.407934210109929,40.419682589184148],[76.576872743644415,40.578596381976098],[76.658843991683213,40.775617486678541],[76.824185490255417,40.982135862931933],[76.907782779224192,41.02396876236687],[77.571658206557473,40.994490589841035],[78.101079084210255,41.076289569854616],[78.448472220034176,41.419877188662085],[79.293592670075725,41.782617415402569],[79.723594312617365,41.887388616144179],[79.840553256770846,41.995568875230539],[80.134683382809826,42.063292135587915],[80.234904759484905,42.285059378476689],[80.16530270646966,42.665449693582218],[80.338827884044747,42.870913265964361],[80.390400228300834,43.042900528998672],[80.634634636960442,43.172890554728426],[80.684422193273846,43.452791423580237],[80.512552498100504,43.856928221484566],[80.355545285174045,44.097354220649876],[80.337892349639176,44.700489189413005],[80.234227468471119,44.791421390877893],[79.997205470671403,44.797425122190006],[79.872119197985668,44.88369056274113],[80.059247246808198,45.006254679702053],[80.88238814543864,45.13548991721354],[81.691975140644871,45.349076209101717],[81.933605952646246,45.165021773604117],[82.285834710867306,45.246430960652077],[82.361801603667203,45.379867031934737],[82.317267602545797,45.59965501152719],[82.977479985483384,46.971972649875426],[83.034137293496812,47.187726946932969],[83.189004570573587,47.1874738232585],[83.993739017059212,46.973829138497578],[84.666520025515396,46.972096108185447],[84.854183647162856,46.868073896498466],[85.233564262172465,47.036168933064189],[85.481375219767315,47.077316327901059],[85.642743978615258,47.30767570035345],[85.526199655396908,47.91561909753878],[85.690036945565524,48.308572480991138],[85.8248525377237,48.406618983210208],[86.547154516100377,48.532340694035256],[86.716928307618304,48.708585459008361],[86.753328326890198,49.008687700173958],[86.890289291963143,49.091159948845657],[87.413656709296632,49.081039844556521],[87.814072946123474,49.162086352264367],[87.883342810948761,48.844523975254496],[88.05982546104228,48.707087377983051],[88.110873413359556,48.556135139075764],[88.516966732358682,48.384275081195995],[88.661130514698272,48.181348267164267],[89.102801235756957,47.990495904691038],[89.556254882896951,48.005392870951106],[89.784684567491652,47.835788670493692],[90.027817560506264,47.877462753307917],[90.128064590505659,47.737105591378551],[90.313157534560091,47.675967400142092],[90.514852873518947,47.261757146892649],[90.698581457837307,47.03330805682598],[90.869775025658797,46.954328218389357],[91.028700447388317,46.565985500116156],[90.929938871594175,46.280338271406841],[91.001528441935207,46.035864433259754],[90.769895146262371,45.816513206608079],[90.674550600820581,45.595140960467951],[90.697459270705593,45.483403524741114],[90.876286843489666,45.248926571091758],[91.584605957532645,45.077884851122555],[92.435603146764905,45.009091261898341],[92.793820862891252,45.035021247756404],[93.509891047566256,44.945583951986706],[93.656328105812975,44.900737179599155],[93.924257448133218,44.693425373447262],[94.199209464656477,44.64490303948822],[94.732199849839517,44.344602097291173],[95.040175931045965,44.262726653333495],[95.350208165422472,44.2778525296266],[95.368534378917317,44.080856425524459],[95.588906692450323,43.857317870344083],[95.88982206641117,43.236264158611093],[96.299327732593625,42.928541715230253],[96.442986523857414,42.751501573270467],[97.210215004674183,42.789320432470305],[99.457736335283684,42.570151750770655],[99.988325915672817,42.677052095153243],[101.49529385084364,42.538556957171551],[101.66394419364379,42.497519604794569],[101.95834032859122,42.229058398206405],[102.15676644668198,42.158641505858348],[103.07277955146607,42.005701252067126],[103.70129306517411,41.760233237403718],[104.4980235445627,41.876745587351991],[104.51418048238577,41.768126999020552],[104.59099411164595,41.677552601121931],[104.96451303276177,41.61222154830746],[105.8647482800184,41.992876975817914],[106.7700605722831,42.288546747402322],[108.17120146319253,42.447144189467743],[109.35754459208073,42.441390391670211],[110.37519595434381,42.771410907601059],[111.00735524557543,43.341187802328278],[111.5570557412057,43.508126005542181],[111.81125651261611,43.706239151343574],[111.84824288395255,43.817139825321021],[111.81691639436755,43.929770204009778],[111.52301911862587,44.188528290122569],[111.40251285482132,44.367336581077076],[111.62143497536886,44.827054677212452],[111.89815303240648,45.063772338805855],[112.41127328542183,45.058002427012084],[112.71570428632742,44.881534739321097],[113.61218887651599,44.755284698553716],[114.07958740797163,44.972089604591403],[114.41846083382333,45.203412622632662],[114.56023211966708,45.389811691795842],[115.1567030715623,45.389944613112299],[115.6877891303651,45.4676107964153],[116.14609900064998,45.725526643697819],[116.35435212746152,46.091896858529346],[116.56275255978426,46.289618886656804],[116.8545061976545,46.387223316566349],[117.25655690822266,46.367281690385774],[117.43814999395991,46.585956529357276],[117.74951163635673,46.525116185316108],[118.07856566813246,46.667619124360712],[118.30870405380658,46.716798613343137],[118.68720262175486,46.702510249584428],[118.84394746851274,46.759989393055172],[119.30842906318478,46.617245890740485],[119.66038950933314,46.604908375274455],[119.7879241917725,46.64898408594712],[119.85903447546431,46.7398512616979],[119.86811588951061,46.854877536933479],[119.70138561453331,47.151594313971401],[119.15883455433614,47.52820673871328],[119.03562814151294,47.671450332931578],[118.76007764529864,47.757827382285029],[118.50572948152816,47.975595346931406],[118.12658055458935,48.025952684965503],[117.79667394928491,47.990572999696504],[117.35072360200908,47.652480470172932],[117.06925806283677,47.805168768743705],[116.76052260210352,47.869078910848501],[116.23200180661016,47.854341436294632],[115.89827856225182,47.687167303480706],[115.61317367795719,47.878665516900107],[115.55787151608274,47.94513775955064],[115.52536377430899,48.130723083699849],[115.76387671211783,48.277856171069708],[115.82069188973303,48.577132691992936],[116.00550619649053,48.758037027785754],[116.6833868457,49.823492075622802],[117.22362433379664,49.631539883540434],[117.84574464071733,49.516486150065653],[118.45503920236126,49.845834286893265],[118.75601189085363,49.962597828748656],[119.22367736886643,50.058477303984397],[119.28572853833681,50.25057311407857],[119.16405074478314,50.405951682479802],[119.46612915150794,50.730677616167114],[119.51529158729777,50.8672214743737],[119.71314551762154,51.065955191375515],[120.06712045518469,51.600481914415909],[120.52671500843802,51.86031479632625],[120.7333277754415,52.07503952024814],[120.73384836254837,52.193519873575937],[120.59180774863829,52.562675702807176],[120.4668589504259,52.619068027295562],[120.16780952004851,52.603920295936483],[120.06778628541801,52.633115107440652],[120.04459963038637,52.718164503976084],[120.70427995370436,53.171501741635666],[120.98550780977973,53.284319108685729],[122.3326534849611,53.484317175322332],[122.52491039372451,53.457440028352977],[123.15817513202279,53.544869219680365],[123.6078273460863,53.546307255045356],[124.7701566768644,53.156401755189044],[125.07497564093147,53.203399945460383],[125.64892077555808,53.04210029309273],[125.75178943060284,52.920061906818269],[126.04795460564702,52.739305819279558],[126.10526346320685,52.589974448077207],[126.34148204733003,52.36190513785067],[126.50382185207437,51.945171025559446],[126.65679394249628,51.776172212970394],[126.8052045854953,51.505566748528778],[126.93558699652132,51.093843762633945],[127.30682234823716,50.707821903072038],[127.38244892688655,50.331351493665522],[127.58991697630753,50.208882055669442],[127.49509536111722,49.98533557313457],[127.55794755545448,49.808571062174465],[127.75734252540629,49.650333296547416],[127.99984397722687,49.570055727901426],[128.70396957747397,49.599926227014926],[128.86506487694709,49.472027175248073],[129.09582228490157,49.382478833552057],[129.49800892859241,49.388593128082817],[130.16794028829898,48.905885220004137],[130.55299405773391,48.860932448509182],[130.61693015507873,48.773177463018968],[130.60628275731474,48.563153498302803],[130.80402825882251,48.341432190020178],[130.74760673514635,48.048791085331395],[131.00566875157944,47.725733051530334],[131.77434363347774,47.680589847025821],[132.47102882171623,47.730914339576216],[132.70730020118654,47.947016032711034],[133.02525519561226,48.066114941813893],[133.48529341109787,48.103787600582663],[133.83580189673384,48.271256733616539],[134.29336357482268,48.373244336955452],[134.55903600985707,48.323147737315146],[134.66500420055505,48.253726201312908],[134.60981439307903,47.972743189440827],[134.75203810761596,47.715445830005102],[134.59267502214573,47.521369315899527],[134.48341817715763,47.447589079815614],[134.30686150304638,47.413706150420623],[134.22139141821458,47.335601815590209],[134.20186881630801,47.128169211631594],[134.07189480681208,46.950549338411271],[134.02244205360338,46.713260769519195],[133.89981811904619,46.526291248426702],[133.86112729400881,46.247876777073095],[133.74972089361657,46.160449154655559],[133.64759047570703,45.955459592172225],[133.50661487187043,45.840599536191128],[133.43624347518076,45.604905740401414],[133.17920165181664,45.468354376214776],[133.10957404667155,45.298156110163653],[133.11322756789838,45.130830666255484],[132.93600999564933,45.030177164293775],[131.82452002198622,45.276099212256725],[131.66819344561793,45.203163999605501],[131.44678783144616,44.984217492752634],[131.09524947131035,44.846856073249675],[131.0551533107942,44.68289237469336],[131.25502681273528,44.071611032067196],[131.17657151879621,43.714870450938371],[131.25701723884757,43.378154515577187],[131.06835323918972,42.902442504799559],[130.65588656655822,42.803145612386189],[130.58187447253437,42.692150081295303],[130.58420784358324,42.567465752320302],[130.53130085252172,42.537814154461273],[130.29577001786927,42.685124188904318],[130.14195258324673,42.92517497335853],[129.9069529072419,42.94175121428222],[129.78989146030256,42.794556519025328],[129.69772019478566,42.448402392243054],[129.52835759808127,42.385451179950856],[129.41810419469556,42.424020279415757],[129.30112748765444,42.396563758625547],[129.19528055617977,42.218592987341353],[128.92335315377287,42.038466244009506],[128.36242584974761,42.014817059324123],[128.25257716727558,41.971535032675213],[128.17861090304385,41.794835790869172],[128.29066311616137,41.562862483636209],[128.14933213551851,41.387973033779573],[127.95622218054757,41.456179188723723],[127.68228178018688,41.440681226242575],[127.17975314884818,41.531552330316444],[126.94736749340022,41.749772714606976],[126.71252445647953,41.700148224529727],[126.58666261097457,41.604450000672372],[126.49029491283181,41.358243465569451],[125.98894996969271,40.904891585205576],[125.75007463563023,40.863310086943251],[125.41678826588316,40.66019024867947],[124.85378433743323,40.43569226586056],[124.43825448890267,40.146563936904414],[124.26245164053766,39.92158414456658],[124.10568840760047,39.841286454098963],[123.67957006887536,39.828418726026861],[123.34903907133722,39.759133897575019],[123.22645960135655,39.686855518038435],[122.83179942227552,39.596053330121244],[122.34449648270156,39.36950652740623],[122.05213664652504,39.097353276034802],[121.75272916882561,38.971696004459304],[121.64977348855933,38.865282630159626],[121.16826693487765,38.73291048025505],[121.10700458516962,38.920632817871393],[121.39254952630428,39.010250198954438],[121.48774870019706,39.148912638928643],[121.4434804539497,39.311179324799937],[121.27578529204821,39.384953029008798],[121.26779541553236,39.544575648796304],[121.43080290315457,39.651256598746272],[121.51754669122214,39.844644769306306],[121.81048563775308,39.970774249555866],[122.21356303111359,40.423274291043811],[122.21349986348845,40.535685403567982],[122.13846523861096,40.673740004621287],[121.84643263550427,40.844755880226451],[121.13409481213857,40.872985332361026],[120.77129554570158,40.588483124066165],[120.47898313101459,40.231144135022738],[119.60219340993159,39.905006134711087],[119.41358158115132,39.769357915575299],[119.26771898441082,39.569391114233397],[119.22442362000606,39.40816028681013],[118.97684286998981,39.182826597575414],[118.61733669500423,39.169263902253455],[118.29790850504627,39.067355829578418],[118.05038530062858,39.21999045247604],[117.78798812802285,39.131387331505579],[117.56210408663335,38.701035229121636],[117.6646291532326,38.417139503779303],[117.7768718317798,38.307669137448229],[118.02562947909294,38.182697236448249],[118.52236038762892,38.099510565558397],[118.79994445017181,38.126458458527942],[118.93988292392626,38.042613686056342],[119.08889657003957,37.700810516598175],[118.97583101122405,37.575574426475896],[118.96839495524705,37.33862923363364],[119.09134461425325,37.21484611750639],[119.2627002611209,37.147128936992878],[119.76068628593657,37.164327097354331],[119.8519023154701,37.242098399190454],[119.88311776661487,37.350697301775526],[120.19879700963159,37.531478570048051],[120.25746461609961,37.67886126951776],[120.74999847011792,37.833685238111102],[121.64091133925749,37.463640005201412],[121.90795623177529,37.453174646434626],[122.05669650718264,37.528685396140553],[122.32782232617204,37.409371014610969],[122.66657117654259,37.402749601522025],[122.58996148363353,37.291522405338007],[122.51951890354903,36.946963591494402],[122.34085336450146,36.83243692324632],[122.24636319941571,36.847808258502162],[122.11224004804377,36.960302487527876],[121.94454779626908,36.95931603188231],[120.98329118332269,36.581867078819414],[120.8955933451732,36.444321679146363],[120.72622641754722,36.355274218864047],[120.63779580639836,36.130126480826767],[120.33281776157776,36.028147530737471],[120.07468263909689,35.860653521893802],[119.86606751672433,35.643848243726872],[119.69881119235086,35.564878420570921],[119.43210582276728,35.287084641760821],[119.23971013812873,34.93819346295183],[119.29183775647931,34.794094887239766],[119.57641427184069,34.588774246003936],[119.96878656558074,34.445176072949472],[120.26649367169586,34.273851965293829],[120.52053990286616,33.623410614947971],[120.87085046821005,33.016414283236479],[120.90813733790266,32.64095059340459],[121.34698938120677,32.420307727010616],[121.40071991513261,32.371806471935749],[121.4508829890204,32.161939236026093],[121.67798531541649,32.048175340173287],[121.8321546866536,31.899610285074747],[121.86237130239179,31.492408495015521],[121.76433269902819,31.416762164185652],[121.73010305947074,31.286626702064254],[121.87765496737394,30.917120344782152],[121.51906465018456,30.83596150172588],[121.21729674539088,30.653500717705498],[121.13644710774031,30.523190184963457],[121.16093905497991,30.39042401468167],[121.43261043397003,30.226527584107046],[121.71065361031646,29.978421896305168],[121.87741013374271,29.99943200160892],[121.9696506614665,30.142870820863433],[122.28009802070217,30.069821716142926],[122.40134591284561,29.950106651481253],[122.39385464569357,29.846364045567441],[122.26093026407447,29.815452476106188],[122.16885388828663,29.660475550894017],[122.01594641286015,29.592633057181914],[121.91758072388805,29.135211675714736],[121.78372609955456,29.106612248122389],[121.70105667169338,29.023119545523564],[121.63190979545568,28.772637025006095],[121.52768805287874,28.607239163491176],[121.6096953639872,28.292465156507557],[121.46612604199797,28.293410133949497],[121.3036673151237,28.191482619836556],[121.25115511078907,28.086622864989859],[121.0247828063037,28.066510719044828],[120.87829912909156,27.9806612631569],[120.68762496527469,27.734080897679725],[120.60436844020376,27.408023554893337],[120.26300519149788,27.071765942487641],[120.13074620813939,26.866051455114039],[120.08650643492221,26.671692176954121],[119.90420058415761,26.525222383653684],[119.88078898340063,26.334335508111536],[119.63733750358685,26.176703080200436],[119.6071576359894,26.063300387169058],[119.64277399783495,25.787828725288694],[119.83569706519245,25.595588366828199],[119.82074435494177,25.457123755259726],[119.59273961888022,25.368284011289827],[119.41084108970881,25.384626182165842],[119.28112364874207,25.229902640891904],[119.02832637590522,25.166247884340002],[118.90892170463876,24.929135224589587],[118.75217698075637,24.838491546410928],[118.65684811336691,24.621630097571543],[118.2259801990273,24.538780302627067],[118.08262377310656,24.367733207409138],[118.05580574879473,24.246193208481781],[117.8393285009536,24.012548946577699],[117.71455112640071,23.973366150257135],[117.62805760953724,23.836957855828583],[117.49242331711216,23.774140033008365],[117.41269992430792,23.618178499199168],[117.36768814157513,23.588899146092807],[117.22297042601468,23.625124397185669],[116.93562449889994,23.547634949094189],[116.57150859855331,23.165417040787556],[116.47059518318217,22.946113866392686],[116.26807569088143,22.941468126310223],[115.85213337087282,22.801803626962698],[115.64095082779602,22.829796812421581],[115.49821939193166,22.719095144251515],[115.13905596514458,22.794437748949829],[114.85376073923227,22.617034112945923],[114.67585894261668,22.621393881848977],[114.55407192237273,22.529130772475728],[114.35817773559842,22.570529425963489],[114.01537950451674,22.512154119375435],[113.8120670844914,22.588841144215188],[113.69548761582354,22.569804991894877],[113.60112071842323,22.470402339621877],[113.54893771027059,22.22536677981007],[113.04533264970424,22.029827974142282],[112.8905432899781,21.842223603007241],[112.7710750815593,21.582163161179931],[112.67103830396883,21.630746969915322],[112.52516644151744,21.623244183936773],[112.39124217393102,21.738586134616433],[112.01799853068295,21.811966540187569],[111.60266764888694,21.559272792462902],[111.06735481513243,21.472068646371962],[110.76708306678269,21.372618846632403],[110.59678456224569,21.235570183005933],[110.53909644627963,21.138266831243758],[110.53864663208633,21.018529708916788],[110.41062810949424,20.878487934243978],[110.37671494167576,20.738697851900451],[110.40166383186886,20.623153244972844],[110.51128531524147,20.518129112697629],[110.48294053481249,20.282577693673545],[110.56059014741359,20.173397360902189],[110.93810731917195,19.947425375276037],[111.01340303296405,19.655586103737775],[110.80749762271702,19.529663124360383],[110.65079300055665,19.305733668713764],[110.44748456976097,18.744321368869912],[110.22904695710402,18.634184461882636],[110.02435808383007,18.419009058473151],[109.8223837067373,18.389981054907885],[109.70256281102031,18.259313608133432],[109.58412106543811,18.225695339132329],[109.02993125962489,18.367907541335228],[108.7017712391291,18.535395765157155],[108.63783336988217,18.870592140548673],[108.6497736726761,19.259405513668884],[108.69737176412548,19.3414098759315],[109.12091408638912,19.65427046184195],[109.26363920267433,19.882409482025533],[109.4727537502071,19.898622932370611],[109.6466148621619,19.983378879405112],[109.83901560372857,19.983487168949985],[109.95574366251675,20.080697177209917],[109.97050039670569,20.231884029185974],[109.88274033081555,20.364132525765356],[109.78115787675115,20.75375828132676],[109.66284388096676,20.916916053524695],[109.68150481338714,21.131589261204685],[109.77399955391914,21.311482036030149],[109.72749965450497,21.456188148684156],[109.57324015878646,21.527233158606851],[109.14413560909065,21.426525577664396],[109.08177113157817,21.440478096760611],[108.97998857958532,21.602276446153489],[108.64154556704804,21.683309395571349],[107.97271859358112,21.508204922988995],[107.7756672579813,21.634423207315631],[107.35127192396432,21.609150953739885],[106.87412743804205,21.95049012378345],[106.66375704052545,21.979169926278892],[106.65189734308831,22.215455182501319],[106.53652693432083,22.395428037789429],[106.61055418238016,22.662198303972488],[106.54563923065211,22.823924912788588],[106.43839848512975,22.874122436944578],[106.27903776635137,22.857708487648662],[106.10109096526645,22.968950714646809],[105.84304612843414,22.923044920969399],[105.54831309138616,23.072832909068762],[105.35987210516691,23.289637498840381],[105.22896162069863,23.297300642518284],[104.90616887466626,23.155152726677908],[104.83225586616372,23.071345319558553],[104.79551138264537,22.911240251938036],[104.68722908860212,22.822423950249345],[104.53618042097145,22.80527371614637],[104.37172044394417,22.704293037582978],[104.11967378987399,22.734204387803619],[103.94146455495959,22.540279591890506],[103.71520231000189,22.674296217385361],[103.59963833406238,22.663945828585454],[103.49294109432329,22.588234477412332],[103.30012544017249,22.664508839678831],[102.98195255759804,22.448469528807241],[102.73070669851035,22.643026829656392],[102.51082012073608,22.713140891416298],[102.39031756362625,22.660326052142221],[102.12745832098912,22.379498700532483],[101.97542176113537,22.436549073599274],[101.6743854702198,22.373692109953765],[101.58103451952255,22.273717574329638],[101.56140823464251,22.130049723679448],[101.7429437766759,21.784528032791052],[101.72871509478549,21.365953291669239],[101.80173829966476,21.230997038813541],[101.70481236606975,21.150380268338619],[101.51054666698553,21.229856061373965],[101.24798088026527,21.197499666572302],[101.15707364569731,21.538884633319604],[100.9923396263581,21.685871740130899],[100.85157532981052,21.66160226100115],[100.59859007742814,21.470657387369901],[100.14779687918016,21.480763822408164],[99.941021847323839,21.758870289930762],[99.913217577767085,21.943212282590373],[99.836921598306304,22.032043374718274],[99.193115296591884,22.126164698275634],[99.173991839689407,22.196925276916666],[99.335327315248861,22.519318551923796],[99.393385587932812,22.961994584774644],[99.280482147614094,23.088292186046122],[98.864026078808266,23.191413258545772],[98.882321089245238,23.362453641660842],[98.798085935275836,23.52043417188623],[98.812145696962276,23.672853014006431],[98.681086319339116,23.841894213087699],[98.628059812848591,24.031824646142248],[98.501168853700065,24.111274718967017],[98.212605193487036,24.109901744497531],[97.999576555971572,24.057780521852482],[97.685975525604988,23.898274589299511],[97.564788188383432,23.911192666103091],[97.696474638564979,24.220635253356477],[97.643068866414708,24.374922677897739],[97.531668876219001,24.491768590704847],[97.529611722503972,24.631163396721202],[97.583516568794067,24.774613198576002],[97.694844214838767,24.883945110450348],[97.769971487938378,25.162712863019237],[97.819664953263342,25.251580441027588],[98.009983503177224,25.293419176312145],[98.143109398508756,25.570935213084713],[98.353545410965211,25.626721103481508],[98.579260885319385,25.913920848120274],[98.571029691060602,26.109097380823282],[98.667081223665974,26.24133218339967],[98.738861714013979,26.690609962225405],[98.66016554095556,27.466408058120955],[98.530302654417525,27.577149564232354],[98.298986700326111,27.550299132215045],[98.080766069950315,28.155773443382856],[97.913997026054872,28.324517270816923],[97.769169253944526,28.356409152717543],[97.66794066510397,28.435504432681952],[97.501445070193455,28.420870540378804],[97.322418453549574,28.218247428476865],[97.12860039046889,28.342426635231821],[96.775869407709735,28.367293000612804],[96.633302096178184,28.444233083506617],[96.388971574652487,28.368133261084456],[96.285751457402526,28.40919526216252],[96.415935284609773,28.644597735128837],[96.427639159026654,28.762262391346965],[96.322160662733324,28.900695782106673],[96.137367796019802,28.922747185279544],[96.16384937531879,29.24604210209397],[96.04802294661981,29.391265949894855],[95.915746560917128,29.401408136515766],[95.702545007733193,29.309505551382603],[95.389200847949439,29.037607598617175],[94.769564152482445,29.176092717339472],[94.646072902044139,29.260203394521092],[94.532404109293154,29.251789256848483],[94.285736201841928,29.136794921842281],[93.764612034727762,28.731793524169444],[93.243582948729269,28.611859783699664],[93.115924355879258,28.39935923711721],[92.743011187585665,28.149564572748737],[92.659998789107661,27.9463443129806],[92.418987031041141,27.825952560322666],[92.130773870864559,27.80913614922348],[91.977576590303812,27.730576910308521],[91.632160343199175,27.760201754917265],[91.556890146343619,27.946504630687258],[91.307252013267444,28.049361263051722],[91.020837493659684,27.970295155569517],[90.715631577708038,28.071149786289496],[90.352833590046885,28.080502073009868],[90.21084613342633,28.276069028544967],[89.898288775777303,28.292819682561245],[89.481212879561511,28.059415698485353],[88.955004943055528,27.458181785198324],[88.89130319642986,27.31643917335677],[88.765088411724548,27.430010251136306],[88.749265960918677,27.521857037780514],[88.839000505411107,27.862623579549801],[88.792698901128986,27.991206095557978],[88.702845678753221,28.060302690152334],[88.568996562579258,28.071316937675856],[88.193719730525245,27.947469679657303],[88.109652528109578,27.870850666448884],[87.870448453402943,27.88582261017925],[87.627560565815159,27.815701172242139],[87.141511698244713,27.838598132315475],[86.943587452898839,27.963940239295834],[86.690099817466972,28.037350236337812],[86.479988472729843,27.938840883910817],[86.194183125982363,27.996610685893131],[85.994540468923049,27.910633048636797],[85.713043018168904,28.25185912595521],[85.12260955360442,28.316165641111247],[84.992505855974954,28.528069975163696],[84.714343530430838,28.595722772395234],[84.22889494315578,28.911937557166258],[84.125510987704331,29.159413232354488],[84.042178913905573,29.24365614839337],[83.915192524329896,29.267358761794739],[83.583593944615529,29.183852421069506],[83.20470754140041,29.576773369806975],[82.854437612593003,29.683657912129405],[82.486072263226575,29.940726804465161],[82.220805380676268,30.064029375818166],[82.016456572558383,30.323048401746309],[81.65322874922127,30.384903969906603],[81.477417562733351,30.350034015566312],[81.254912290175525,30.093473908487965],[81.110456103137281,30.037062862838358],[80.92654617985653,30.2642770263041],[80.629140746925799,30.43946558136604],[80.191405275672309,30.568609045971652],[80.105949961269275,30.766320494668395],[79.80317179420787,30.958143294575386],[79.561081377570474,30.95175764081117],[79.342841242841047,31.102311216761972],[79.154366509691855,31.318553663037047],[79.044349609887064,31.345329224692147],[78.757975648638109,31.302719608602416],[78.704213748961379,31.962972599611312],[78.493665183301218,32.220450151921497],[78.391948221225917,32.544574761970502],[78.700802747628586,32.596760502078382],[78.84029492418226,32.411194660721222],[78.989899054031994,32.379371787296833],[79.185696834392687,32.527999845151584],[79.232546762195625,32.703088121888435],[79.190895756595992,32.930755576970711],[79.108793528995847,33.022798893819513],[79.08657085388937,33.21541796242014],[78.802071147889848,33.499801185655677],[78.726901652730135,34.013397299477951],[78.867128935369436,34.22048241271753],[78.836399501871753,34.389734736385989],[78.669389612585505,34.51511336384111],[78.285298673327077,34.650450594676968],[77.992199444875183,35.349563555803144],[77.916379082086678,35.434614665003934],[77.086624025983994,35.553041173431097],[76.731254905480114,35.677062360598249],[76.457028504056836,35.834695888993032],[76.1778917973175,35.810760108274522],[76.073415443696234,35.959203497558953],[75.912548790358287,36.049106882813277],[75.970244847150781,36.392512875082254],[75.819778241996431,36.663295538539337],[75.643664755683787,36.746355078948454],[75.424416550280213,36.738423237575027],[75.364443874265319,36.874024631741733],[75.268593187285219,36.936641341041629],[74.541438922127782,37.022402600256576],[74.376389629990754,37.137499787867895],[74.829215079534237,37.318165042408218],[74.920788822306491,37.437531530835678],[74.919022488926103,37.771129560778789],[74.78989587064666,38.103724060200058],[74.774120708733477,38.43296475888237],[74.680486517127505,38.534071575641249],[74.286229198299353,38.656696745230747],[74.144262878628027,38.64713367291882],[73.974890990538839,38.534230776962119],[73.80598464570194,38.604065398768135],[73.696383249486288,38.854323722206217],[73.719459704394794,39.052948384483521],[73.607596163407564,39.229248917302215]]],"type":"Polygon"},"properties":{"alpha2":"CN","radius":2905000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-79.025159094497425,1.6237467981819582],[-78.957507001163947,1.7520452296694717],[-78.662079448570495,1.9341285076303747],[-78.616765854683223,2.3067144344595447],[-78.534543673710559,2.423481164389],[-78.284176083352477,2.5177599830355093],[-78.178341995675737,2.6460808898114245],[-77.974613383110238,2.638123540864433],[-77.748569870925508,2.8126137858193134],[-77.693461810482731,3.0397426526186426],[-77.540692687322149,3.1247033446073358],[-77.196578701072966,3.6857753583418287],[-77.219046074294994,3.8425949501789494],[-77.358047128316642,3.9449384352351231],[-77.520446547361601,4.2129104397722639],[-77.342252211825354,4.4353536957452135],[-77.293904091178973,4.703472509998976],[-77.378334097958245,5.3403062379259616],[-77.534073183949729,5.5370820234829008],[-77.34065708372043,5.6698481933762555],[-77.285306699547903,5.774723108185996],[-77.337152806978622,5.9783898055888214],[-77.46921368628324,6.1768301546692097],[-77.472798414181739,6.2855030014809179],[-77.382480126898329,6.3909681667181877],[-77.368922512148686,6.5414855836516859],[-77.425989781319288,6.641798708202221],[-77.525826370876644,6.6933111395308211],[-77.900917845419784,7.2294085226075033],[-77.766537222000721,7.5378769066556215],[-77.761663754699825,7.6986791628444413],[-77.589758427519186,7.62279014927711],[-77.457253339542916,7.6436281679346996],[-77.245704799100693,7.9727598691537782],[-77.252782172239108,8.1220657948795516],[-77.478223632800734,8.4984913831215838],[-77.396256900693118,8.6401287431812861],[-77.344352251588049,8.6365270656952919],[-77.199979723836577,8.4540905463106242],[-77.029403433013798,8.4508401029100533],[-76.887832262504872,8.6196439118549861],[-76.689738234937707,8.6953750845000997],[-76.301534085224475,8.972622937185383],[-76.13534268375642,9.2655072778622163],[-76.027139520527399,9.3656216002596491],[-75.709027115315166,9.4857170077152855],[-75.654579812346753,9.5895205432958086],[-75.679784939995898,9.7298054608623108],[-75.606956930130025,9.9751587705097045],[-75.708001832814929,10.143490775379501],[-75.557866715108204,10.330070385116572],[-75.445846191113247,10.610753194088522],[-74.844540602353305,11.109451558078669],[-74.378262726672546,10.994718807151916],[-74.246883922449598,11.076802035332692],[-74.199970041413863,11.265610754364776],[-74.137716467898443,11.322077697289084],[-73.687105766834151,11.271844969499186],[-73.315950728158114,11.303022277266614],[-72.721714995047932,11.71193616285929],[-72.304983022427294,11.875258874656856],[-72.175378193907207,12.045783034083895],[-72.135530784898279,12.188422810120407],[-71.71866471599958,12.417740930853737],[-71.494040198401549,12.432057367173764],[-71.262285430123725,12.335095361412726],[-71.155278252955611,12.164068093148991],[-71.137567057746537,12.046434338268909],[-71.319879670918723,11.862108225630701],[-71.969732295183491,11.645892500334657],[-72.248633596807551,11.196603879399623],[-72.469564630398807,11.09456330593315],[-72.683560651131359,10.842397129964214],[-72.870692650649559,10.482198780091428],[-73.006755565437899,9.7892265354530661],[-73.171289733917035,9.4851198644834653],[-73.116987428081984,9.3001729502252175],[-72.796578060354832,9.1088237012492606],[-72.66434763027307,8.6403998305026217],[-72.416793483737493,8.3818410933038479],[-72.357877915519552,8.0873869224745576],[-72.447733887138696,7.9349583500504046],[-72.46298223195835,7.5215066661954166],[-72.373305336841042,7.419564346982833],[-72.20789489689794,7.3700579026081758],[-72.080433158234115,7.0998642802591787],[-71.927656684140274,7.0041848982217507],[-71.616368140609893,7.0327291604887359],[-71.045761474172281,6.9922609115233803],[-70.737150951537586,7.0898563263812218],[-70.368518729859815,6.9687516127757734],[-70.125049311253164,6.9517102522407423],[-69.412827980495422,6.1559363373481171],[-69.219526630754942,6.1257007541195811],[-68.937220643228471,6.1979531116734696],[-68.459365927385633,6.1580897866836803],[-67.859119876449014,6.2896604442284962],[-67.572356577532702,6.2429609362075009],[-67.482196038327928,6.1801394322956691],[-67.439598837126255,6.0255391993089544],[-67.47230985096968,5.934318260713253],[-67.582461607818544,5.8148916507969579],[-67.644239034652799,5.554635248246159],[-67.814183314173448,5.2984797103522343],[-67.854218429644206,4.5555751324028746],[-67.656653286494375,3.8575107288973265],[-67.311405146457403,3.4158147099215403],[-67.353721976533137,3.3227667311517144],[-67.647571290401629,3.0595658785711302],[-67.68465228623792,2.926021435853809],[-67.550823654906409,2.692824868991766],[-67.211056282415484,2.3900122285755745],[-67.197710224932649,2.2315672834351781],[-66.884725798887857,1.3582166382854421],[-66.876297703659986,1.2232055724856683],[-67.082116360437098,1.185566196130732],[-67.12045210648796,1.7030498028876417],[-67.320830249207745,2.0253411949361912],[-67.456969549827946,2.0897357755533426],[-67.591788666606476,2.0475019020028671],[-67.815219344743952,1.7902502561738958],[-67.931775540971074,1.7493751632731387],[-69.316674390752226,1.7212409968461009],[-69.564224672065166,1.7675273740853761],[-69.73736716141417,1.7188771718011069],[-69.824008784079837,1.6369015379866239],[-69.850982673098798,1.2720653153694752],[-69.817564132364581,1.1602813333698951],[-69.708881820278052,1.079531597612553],[-69.361373806035985,1.063789173741853],[-69.258876770745303,1.0151962314553269],[-69.163455840283305,0.86398086324048473],[-69.153582933959513,0.65876602804169393],[-69.278147624748911,0.62694130231474809],[-69.553811996727973,0.69911765296922812],[-69.96258044047724,0.568110771962683],[-70.057659294431659,0.42275610117618262],[-70.055321450367316,-0.1635336284635549],[-69.922161626055342,-0.31676148396785614],[-69.634212450862506,-0.50946169009113551],[-69.575496264588537,-0.82914448432021814],[-69.448984010628536,-0.99887205700574155],[-69.400493440732703,-1.1949121661709956],[-69.965949105335213,-4.2354985042082385],[-70.293954008898154,-3.8750678444081026],[-70.529642462460885,-3.8662222727651008],[-70.734739653666807,-3.7813983236634643],[-70.147598525650452,-2.8575704584449451],[-70.151802911466291,-2.7025868530056654],[-70.289837340344306,-2.5635346169442146],[-70.964464132760554,-2.2176127918971664],[-71.196486563133533,-2.3128768390960261],[-71.396893794120317,-2.3338598325473017],[-71.739394888986254,-2.1738162725799759],[-71.984346007766206,-2.3263418566184617],[-72.212051236233634,-2.3989328071012888],[-72.395590798559368,-2.4286346995099533],[-72.610225396255217,-2.3666433587114044],[-72.940987388481645,-2.3938383445580733],[-73.154309710479637,-2.2780606278791726],[-73.137141091584837,-2.0518932291056529],[-73.182399976880887,-1.8907431497232112],[-73.496084596470041,-1.6928956083900233],[-73.516829449571333,-1.491802404282407],[-73.629624763576174,-1.2921883037131483],[-74.246262092803718,-0.97043149884043889],[-74.455989233842061,-0.52991855092234064],[-74.818825381007571,-0.22903792102872284],[-75.12092180217725,-0.082966637648583419],[-75.284494566211606,-0.10627852607879965],[-75.776583607194183,0.089466446922010986],[-76.049891186613721,0.33153429097734394],[-76.18850367220503,0.39728431795631325],[-76.337715257053873,0.37529828209953986],[-76.494671913695043,0.23572018682049825],[-76.835490642579572,0.24915874716892666],[-77.39621734971098,0.39407711669352119],[-77.517339607710497,0.64752054352311184],[-77.736324030120954,0.80608892353393524],[-78.184132169554047,0.97060050993750913],[-78.681493649132321,1.2836222361231882],[-79.025159094497425,1.6237467981819582]]],[[[-78.744277095204282,15.776624904795719],[-78.744277095204282,15.908278095204281],[-78.530502904795711,15.908278095204281],[-78.530502904795711,15.776624904795719],[-78.744277095204282,15.776624904795719]]],[[[-79.767378095204279,15.736974904795719],[-79.767378095204279,15.859406095204282],[-79.609910904795711,15.859406095204282],[-79.609910904795711,15.736974904795719],[-79.767378095204279,15.736974904795719]]],[[[-80.10108809520429,13.486018904795719],[-80.10108809520429,13.571011095204282],[-80.019150898882614,13.571011098426991],[-80.019149910708947,13.486018908018446],[-80.10108809520429,13.486018904795719]]],[[[-80.370482999999993,14.467005275578259],[-80.112534904795723,14.471685095204281],[-80.112534904795723,14.269936904795719],[-80.370253095204276,14.269936904795719],[-80.370482999999993,14.467005275578259]]],[[[-81.191481095204281,14.273928904795719],[-81.191481095204281,14.342665095204282],[-81.099930904795713,14.342665095204282],[-81.099930904795713,14.273928904795719],[-81.191481095204281,14.273928904795719]]],[[[-81.400222095204285,13.318698904795719],[-81.400222095204285,13.400578095204281],[-81.342316904795723,13.400578095204281],[-81.342316904795723,13.318698904795719],[-81.400222095204285,13.318698904795719]]],[[[-81.85752709520429,12.181878904795719],[-81.85752709520429,12.603925095204282],[-81.443252904795713,12.603925095204282],[-81.443252904795713,12.181878904795719],[-81.85752709520429,12.181878904795719]]]],"type":"MultiPolygon"},"properties":{"alpha2":"CO","radius":1020000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-85.907678928295127,10.89760679668912],[-85.584223656353927,11.189195325213989],[-84.952281547949113,10.965021325748047],[-84.701134756219304,11.051987590258134],[-84.348371953428455,10.97962379920839],[-84.151996993949155,10.797287642000304],[-83.928974625372632,10.738565959372419],[-83.726766877493318,10.783842403274008],[-83.641890528349862,10.916658656514603],[-83.341216163862327,10.308469122410136],[-82.777862708974055,9.69218032719529],[-82.563837868915044,9.5765826931938971],[-82.644081751087825,9.5061031196631429],[-82.836578705286797,9.5070481588069118],[-82.937276766013042,9.3723072496362825],[-82.926348095238083,9.1254056830048675],[-82.728086949226068,8.9160964655242605],[-82.866402294850573,8.7444626347712227],[-82.845034636703375,8.4894172877986058],[-82.937952927674758,8.291625963415413],[-82.879601552879038,8.0714603569367238],[-83.16030351010663,8.3810322376136313],[-83.543662537039609,8.446135025273021],[-83.733786035495982,8.6144665622445071],[-83.621417227725061,8.7862516485640239],[-83.623102855047335,8.9846693919099661],[-83.896302916701543,9.2752065030693061],[-84.224454518969225,9.4582335933621806],[-84.581457452216455,9.5685567841205756],[-84.776241964424642,9.7614326144253969],[-84.944803041933625,9.7468317674589997],[-85.114463064363207,9.5820673631063755],[-85.319401119057389,9.8035199904353565],[-85.628380161989028,9.9059680526140301],[-85.796311282670445,10.132945755230921],[-85.849403861217041,10.292059463480735],[-85.830401339512491,10.398049720946975],[-85.699631485004218,10.58202140068687],[-85.696842069700011,10.716338403380901],[-85.907678928295127,10.89760679668912]]],"type":"Polygon"},"properties":{"alpha2":"CR","radius":236000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-84.886927961347183,21.857096070623871],[-84.443675589608475,22.079557953737474],[-84.361025430963835,22.378787937089921],[-84.117903256879956,22.620947071513722],[-83.257759016136632,22.967390398866797],[-82.651791832997219,23.047329559956978],[-82.101363571721407,23.190257244020358],[-81.575358629732364,23.117925240952285],[-81.262442158102346,23.156572450123932],[-81.110227895168578,23.073063239231097],[-80.650208226469331,23.102804976701371],[-80.38754826204854,22.950995277399659],[-80.075333603368577,22.942040626323454],[-79.948421932077991,22.880291865157741],[-79.820369578861786,22.886765373412111],[-79.702250733510496,22.807982692309608],[-79.579218178127704,22.806491438762375],[-79.349802778069972,22.6637602031158],[-79.328274337660332,22.490456934248119],[-79.206938253373266,22.395905113277887],[-78.824445331622186,22.404939662140897],[-78.630109887302496,22.551960063573802],[-78.351357947831417,22.538391726307083],[-78.147020138206088,22.428696407096009],[-77.794114308115851,22.102550178404417],[-77.63391341768579,22.053809865219613],[-77.556628296167403,21.903240673954837],[-76.814654669365382,21.384984808099929],[-76.072701360837996,21.136579810382553],[-75.723012936201144,21.110798803907485],[-75.631493273622254,21.057380761515496],[-75.597656410969947,20.837982372079285],[-75.502536553787806,20.730657150264136],[-74.882657856317664,20.650408499604918],[-74.496522183734669,20.378404138933412],[-74.229068243805656,20.324382286889865],[-74.137101554609842,20.231903507053666],[-74.252936704509281,20.07992163789455],[-74.634636470701821,20.057380874844057],[-75.116390629108366,19.901668426018833],[-75.551911807010356,19.89133737129184],[-75.765271515813126,19.959590513779915],[-76.168640827725028,19.989466601359233],[-77.714671096611184,19.855689905155838],[-77.553600861044941,20.081962119599019],[-77.190758502430796,20.332529086121859],[-77.154783860018284,20.460683799588644],[-77.197690797290761,20.576202014030031],[-77.338272068287921,20.670110837574235],[-77.99727543650063,20.715581143838669],[-78.116206470295509,20.76211991159099],[-78.490519211509437,21.053833030860499],[-78.577552842440255,21.413366287449808],[-78.711327405395508,21.577340512788666],[-78.832119442237669,21.614047452755926],[-79.189216651792748,21.553071911308194],[-79.356814722904858,21.584991604637732],[-80.141466901062941,21.830695431317281],[-80.476192138024842,22.059999184248326],[-80.961883332472823,22.053124677368888],[-81.167878175330415,22.129766640657753],[-81.355180026162842,22.104368465237659],[-81.474790037007537,22.18121982762538],[-81.816141361144119,22.200514474591195],[-82.077442600725576,22.387749013735036],[-82.075196393673892,22.574565528937267],[-82.231020348620021,22.682057319392548],[-82.75458205035163,22.673055654227756],[-83.379778220838659,22.223234143031942],[-83.859185126583242,22.162716336645669],[-84.031084477906944,21.943355715007741],[-84.453472859372312,21.790313078367024],[-84.502478604215739,21.776454082781967],[-84.638192597165798,21.87162761909773],[-84.838196605411696,21.828195387875528],[-84.886927961347183,21.857096070623871]]],[[[-83.175298698406223,21.626171176405951],[-82.991117569795847,21.942491109514325],[-82.714730969545855,21.89010207680878],[-82.562015964662407,21.571804726519062],[-82.847930199198842,21.445872849684196],[-82.959594070363792,21.441524891283564],[-83.138072434866956,21.528995105512966],[-83.175298698406223,21.626171176405951]]]],"type":"MultiPolygon"},"properties":{"alpha2":"CU","radius":584000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-25.340701873812442,17.067797218697294],[-25.336325165442531,17.090401300542229],[-25.113381649892922,17.192775076477538],[-25.035210280573686,17.175666507262672],[-24.915757494468583,16.993302092488605],[-24.86637843396197,16.933172959219185],[-24.811391808148834,16.878125023145856],[-24.751317739327494,16.828678986504926],[-24.686724472271703,16.785302562778362],[-24.618222999164022,16.748406052559581],[-24.54646128017847,16.718338462491769],[-24.472118114380425,16.695384203989878],[-24.376906925934282,16.676821763146474],[-24.270405069782953,16.648077337300748],[-24.088061331268509,16.621566898878257],[-23.996589779675869,16.567820697106214],[-23.922998918188195,16.535099865471285],[-23.84649108442045,16.509943969749887],[-23.767841669454334,16.492607959745651],[-23.687847768854365,16.483267532336761],[-23.607320104269046,16.48201735082441],[-23.527074806934568,16.488870085539833],[-23.447925146353381,16.503756285433091],[-23.370673287975908,16.526525081945113],[-23.296102163419498,16.556945718029372],[-23.224967535618298,16.594709886826841],[-23.157990339322147,16.639434856292297],[-23.095849374571888,16.690667348104483],[-23.039174427201239,16.747888131548208],[-22.990310477366211,16.808116215905066],[-22.932594871136523,16.840002150797712],[-22.905635082261259,16.842752366817866],[-22.901645678320321,16.749614862486538],[-22.878868876195199,16.562699773354286],[-22.861722498791767,16.484591102627377],[-22.836863751367808,16.408584488590854],[-22.804541025739823,16.335439399153739],[-22.765077294759028,16.265886709501494],[-22.69365394086498,16.168540986190969],[-22.682822032967337,16.113372129387461],[-22.710901051297316,16.04408076844145],[-22.791199078628878,15.992984749284402],[-22.850153008159051,15.946214866846468],[-22.904451524660175,15.894112479030897],[-22.953614172145322,15.837138609559371],[-22.997205938944933,15.775797387060997],[-23.034841106872051,15.71063158433131],[-23.066186664218161,15.642217815651543],[-23.090965252380105,15.571161434664276],[-23.108957620045004,15.498091177951178],[-23.120004563217602,15.423653601708743],[-23.124008333923801,15.348507360748604],[-23.116828869427611,15.167247462582909],[-23.267841585299269,15.100246795047184],[-23.333414175675745,15.063023792968817],[-23.395191096941947,15.019792687370224],[-23.452623565739554,14.970937513435224],[-23.505079472634591,14.917017588557156],[-23.63705186855281,14.924289034921026],[-23.759604152456923,14.955905914180512],[-23.836588078935069,14.965872896684044],[-23.914175575523508,14.968331768245704],[-23.991636123733812,14.963259377569017],[-24.068240400349232,14.95070348331785],[-24.143267144305529,14.930782304448769],[-24.216009947663327,14.903683407130488],[-24.386206568867202,14.819141002401045],[-24.440163917660417,14.835523010627103],[-24.491360305682988,14.874867974194869],[-24.516017641575434,14.931261035335698],[-24.496152279490616,14.979628803332171],[-24.264084816511918,15.101703758296988],[-24.19841742736557,15.147147063853534],[-24.137607821170729,15.198910871785642],[-24.082261937150264,15.256479380010163],[-24.03293127109654,15.31927894532633],[-23.990107379974358,15.386683799501375],[-23.954216983786207,15.458022284757659],[-23.925617713507439,15.532583546527599],[-23.904594547461244,15.609624616786245],[-23.89135697164323,15.688377817379402],[-23.886036892291663,15.768058409576604],[-23.888687321503639,15.847872413624739],[-23.899281848994381,15.927024520384139],[-23.917714905263338,16.004726016211368],[-23.943802813544789,16.080202642120998],[-23.977285620060723,16.152702308913476],[-24.017829684338366,16.221502591391484],[-24.065031003781137,16.285917926988414],[-24.118419239365135,16.345306447078002],[-24.177462402347064,16.399076372894321],[-24.241572155282714,16.446691912329822],[-24.321746289446732,16.493870925821863],[-24.383453116710751,16.562597470758423],[-24.441381358523646,16.614859496468789],[-24.504124865212095,16.661230315949595],[-24.571086887153399,16.701268899196467],[-24.641630552686458,16.734594441713348],[-24.715084925358852,16.76088998632056],[-24.790751385162057,16.779905437715001],[-24.867910273063277,16.791459941111153],[-25.019728092351887,16.798148615254089],[-25.075455471805537,16.824014049885047],[-25.163892608508768,16.876575728431604],[-25.238910837614597,16.911981809789431],[-25.307489228573466,16.936527958091418],[-25.340701873812442,17.067797218697294]]],"type":"Polygon"},"properties":{"alpha2":"CV","radius":188000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-69.158632007325238,12.379988983134],[-69.118546157866618,12.373035591464008],[-69.076953539674534,12.341867596448772],[-69.04814218615499,12.292254657279564],[-69.037731403384683,12.276009392864996],[-69.025803639873303,12.260842934102113],[-69.012469911196476,12.24689644015695],[-68.997854318705066,12.234299715586072],[-68.982092894473482,12.223170002203339],[-68.965332335204948,12.2136108878722],[-68.947728636877542,12.20571134237974],[-68.827571211010095,12.158306627660023],[-68.751456366201239,12.059893474121205],[-68.803291775975779,12.04574570323665],[-68.994957688602497,12.142068078283645],[-69.153548489886475,12.298555850753317],[-69.158632007325238,12.379988983134]]],"type":"Polygon"},"properties":{"alpha2":"CW","radius":34000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[105.58436049584633,-10.512319596157074],[105.58514480915149,-10.507668379456408],[105.59462307663739,-10.464578104583403],[105.59590569398623,-10.45988492991696],[105.6006757333142,-10.458926952609071],[105.63834195974538,-10.453313946317362],[105.66028701028775,-10.448774357096717],[105.68158644816603,-10.441808220458103],[105.70533761544105,-10.430984416454065],[105.70694578945412,-10.435283430988836],[105.72388829025395,-10.488270271725646],[105.72513655888098,-10.492959662773432],[105.72355645605064,-10.497547890972848],[105.69866633032466,-10.559687965131269],[105.69677778510793,-10.563815139871055],[105.69301231415945,-10.561281091153232],[105.67985546101467,-10.551474589088075],[105.66170325963384,-10.539424914588645],[105.64234805284028,-10.529421263230752],[105.62201953713306,-10.521582352614248],[105.60095895968354,-10.516001210436777],[105.58892601794922,-10.51350491134116],[105.58436049584633,-10.512319596157074]]],"type":"Polygon"},"properties":{"alpha2":"CX","radius":9000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[32.301240218945011,35.082626885115182],[32.419036640749091,35.072541715443748],[32.471860294919878,35.091992463138119],[32.560895268333319,35.157140122312768],[32.652352662505805,35.182448685484054],[32.756327701702304,35.170973197165701],[32.829256710654271,35.19258385748779],[32.910826105666935,35.271109971956484],[32.941806816572942,35.390148358692898],[33.313125074648916,35.341306316960015],[33.458762073510023,35.336523941997228],[33.594202691297596,35.352504386169713],[34.055080573665876,35.47248768451793],[34.192590835052947,35.545442948474125],[34.406956220812106,35.627511315921595],[34.555200940679995,35.661669916060184],[34.458356902113266,35.590714029305175],[34.063041505064874,35.362057737251895],[34.005632424162179,35.312135015526216],[33.971020752161046,35.244384643749548],[33.964214867841712,35.168610237429057],[33.986199596398528,35.095776506061824],[34.049864931019641,34.988556904608622],[33.706531053920862,34.957840340791506],[33.65738580095298,34.931580147569029],[33.514286814379268,34.806680020081131],[33.414808321869359,34.751201577642156],[33.101976574293154,34.675473689627964],[33.04172773646129,34.625514216408909],[33.007775774915579,34.569828655013261],[32.941905449647713,34.576090397149095],[32.843811033045689,34.642128182295551],[32.693000706625206,34.649561974816265],[32.449158623709643,34.729615344104197],[32.317403317894794,34.953390397122121],[32.301240218945011,35.082626885115182]]],"type":"Polygon"},"properties":{"alpha2":"CY","radius":129000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[12.089998892220983,50.268633113812264],[12.129734571001164,50.310944525630475],[12.312581073895677,50.265900325291632],[12.54907689530854,50.393183503631491],[12.929697422371623,50.433137019519492],[13.556791723146056,50.704407875174013],[14.109020028260101,50.827335582858971],[14.206390994428059,50.888355970917324],[14.287813518691349,51.030540004248031],[14.511582324359305,51.0080971967352],[14.764326787805375,50.854261992781431],[14.903418967797514,50.891705648752001],[14.998826626697527,51.013530610645525],[15.258400117115261,50.958353910329159],[15.307799752244611,50.859685435633843],[15.409264922000789,50.799738138785173],[16.030735954916118,50.634722382886224],[16.282183624943858,50.655327565543082],[16.359852081092626,50.621191763296494],[16.419441413620689,50.57365409656952],[16.367949852130678,50.461761717453911],[16.387819975690359,50.347629314449513],[16.625281474391119,50.138764960404636],[16.739569628396605,50.139937479256858],[16.842390808325664,50.209139010506462],[16.880170970958382,50.426813408353162],[17.151854271404204,50.378029813957141],[17.435468897256836,50.25673951167883],[17.702246574157037,50.306909235554841],[17.757494612344892,50.118033690108689],[17.839202718712087,50.035565969846658],[18.04941324176945,50.031675426769304],[18.562235515953983,49.879193968422435],[18.632011895536998,49.736778087814848],[18.806735634655411,49.613574915111641],[18.831962628217642,49.511015443453509],[18.566119528676648,49.477760022887246],[18.19319170907778,49.265989662568074],[18.081897169355351,49.061839178697397],[17.758417271350144,48.888310163696495],[17.487070626727352,48.828221845565984],[17.142527842518113,48.836168470677656],[16.953033516253463,48.599224843423251],[16.836070497598808,48.704573383778772],[16.562795859893935,48.791429941276519],[16.367205331841181,48.739220568040999],[16.057276788978875,48.754968017021355],[15.824149689380004,48.859964703671537],[15.310927622171011,48.970888675988789],[15.021415129909478,48.916040408344969],[14.922406971223564,48.771625912540415],[14.75166154353993,48.714754689666975],[14.691127236633275,48.599467324223383],[14.189857413208218,48.578714518177115],[14.049270009400153,48.602720479279839],[13.962548203200075,48.705881857332415],[13.5989100560603,48.928700608687258],[13.436743880708809,48.957781749174792],[12.967703156550302,49.296939245269058],[12.686572451534207,49.41057773160334],[12.390804853102185,49.739649228131512],[12.422555800794459,49.856055562544647],[12.386419660347849,49.963183414880412],[12.208039995746118,50.097693326321853],[12.089998892220983,50.268633113812264]]],"type":"Polygon"},"properties":{"alpha2":"CZ","radius":247000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[5.8577928123246705,51.030093505324437],[6.0317824325625899,51.118099636483166],[6.1752200235246502,51.411347278107293],[6.1517191640214435,51.527765481756823],[5.9489630355656784,51.802545013380431],[6.1665010630721495,51.880465005881753],[6.3578797280770498,51.843666784753339],[6.6000544637980632,51.879026348553289],[6.8440138307338652,52.18634718225838],[6.8598238151876201,52.337950891612437],[6.7031138353503277,52.49935404514666],[6.7066022734054185,52.602289962521255],[6.9945582066495895,52.675994973900558],[7.1675263044760777,52.95697176012424],[7.179636215329654,53.244922476036209],[7.0535998851950872,53.375966462231553],[7.1073408441281734,53.556839525732947],[7.2032445140915975,53.651396868515782],[8.0091691121638284,53.690454879475915],[8.1290494180861739,53.590287976116478],[8.2604886897883585,53.57388104213652],[8.4552685768823928,53.650593253466816],[8.6190099490591852,53.874775208602735],[8.7556298859799249,53.871652911653946],[8.868023758676749,53.950113231412487],[8.890045728150179,54.170125443406839],[8.7992543717451941,54.271334189660607],[8.6450866832895841,54.295198092406814],[8.6704940631500431,54.50445540164187],[8.6291976258724823,54.612799248081586],[8.2849159608459964,54.767302483859716],[8.2959062738201936,54.908225813053434],[8.4052964061663591,55.058480335957981],[8.6157742022567838,54.903976093704756],[8.9074044697974504,54.896097453071761],[9.3026453323591269,54.807071633921822],[9.6157859688322844,54.855197683024507],[9.8921771529122235,54.780353304645324],[10.190797802838508,54.45211355267697],[10.693714639202399,54.329674927600543],[10.918159823728791,54.36969037726638],[11.084987810342497,54.533123265041198],[11.233443641734695,54.50103666869915],[11.282550156167051,54.418197525734925],[11.109365462725806,54.355580759439185],[11.04390459832587,54.143090318360841],[11.14333960380667,54.009018251240747],[11.360866931046733,53.954047801317664],[11.792341617437955,54.144141826685896],[12.096308736177519,54.176565147718179],[12.575440591857737,54.467160777169688],[13.065909508984307,54.430367097359031],[13.336893863356007,54.69685489216775],[13.422614014866369,54.699072735433809],[13.635908763292221,54.576752656996973],[13.789641169543165,54.1601917779643],[14.211147662607125,53.95020398809266],[14.220046164666522,53.804333845453186],[14.414256343147912,53.283441348419068],[14.370878330381565,53.110690199424241],[14.266282205537044,52.976991803956587],[14.261869474162657,52.843379476617983],[14.619140285715522,52.528477139266109],[14.597662155442595,52.355696416973991],[14.752251580514953,52.081784480315427],[14.660492406724352,51.8679574131057],[14.664003758214317,51.750624642393184],[14.787137783354005,51.529870273290719],[14.935311739398482,51.435206316261194],[15.016339984199872,51.252714862839149],[14.919284380816924,51.012099308528974],[14.766417631173265,50.818531845485765],[14.411677761358849,50.915910036539934],[13.569956209138084,50.697336173814854],[13.468421769896045,50.61514112378179],[13.268371821304926,50.575068283392319],[12.942653630713725,50.406696608643472],[12.531515825977333,50.3844850594622],[12.34755273123948,50.245318373023785],[12.343855411629555,50.059942040559925],[12.51176006750382,49.895699482698042],[12.479223791791782,49.687080350950467],[12.647975815922587,49.446062526813741],[12.785445819196871,49.346316482149014],[12.916628483495101,49.330227618311625],[13.814462781719673,48.766901500818136],[13.797013418733494,48.617070596584107],[13.728478512764486,48.545723171154883],[13.511424287995602,48.515570181887469],[13.369668275797592,48.358500860476056],[12.997142842776233,48.242244767619965],[12.896052256314265,48.155700512709153],[12.881278538376145,47.987400622053784],[13.053829246242715,47.655077564949231],[13.014210914050377,47.478311633014243],[12.872562671275871,47.509706197068915],[12.671369147994918,47.646242127456588],[12.312787307773529,47.68622130655563],[12.185520357043297,47.619770323624877],[11.701661823721921,47.579921821558237],[11.297877243240112,47.425150367212581],[11.041984970001865,47.393286824425921],[10.819566710765521,47.513943095307312],[10.545034722817078,47.539830893898085],[10.312679403545859,47.313605500243099],[10.183148795307826,47.279046984363141],[9.9449234501934995,47.51477859120623],[9.7883917168934289,47.560972795609288],[9.5240393093238502,47.524467007871564],[9.1619364414051425,47.669676183783942],[8.8812221777782625,47.656635754089422],[8.6906218817288963,47.6986556362179],[8.4496470421034484,47.595453406812958],[7.9320332695521412,47.564234963437961],[7.5656005544259433,47.606756866744313],[7.5298399234820721,47.67871703998695],[7.6000481736921301,47.948840706923143],[7.5858637939302964,48.069110402039179],[7.8381725147576251,48.635882154265083],[7.9613524934937896,48.756287866525959],[7.9619636941193415,48.9271749139265],[7.8490741816759222,49.030374680603529],[7.4331496184025108,49.136533770996614],[7.0367482108900568,49.112940671491089],[6.917276616101721,49.178638879524492],[6.7355480659862383,49.160787566966988],[6.5092690326341209,49.409466520326731],[6.3445857914652315,49.453003378282013],[6.3785632361558093,49.758637391344294],[6.1100338127664333,50.03443870310776],[6.1214745356131752,50.139279440655855],[6.2300527555630296,50.319331491343171],[6.2317437125528565,50.433475727412421],[6.1276701632647486,50.650837593402585],[5.9942213995595992,50.750487743048559],[5.9703425167917938,50.915473047116492],[5.8577928123246705,51.030093505324437]]],"type":"Polygon"},"properties":{"alpha2":"DE","radius":463000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[41.766680931416396,11.589086898972081],[41.815905588005172,11.723558972886401],[41.98944492381851,11.904756341291074],[42.375429051641774,12.461936093260794],[42.450046345128321,12.52105104571573],[42.575648297887781,12.455118945513377],[42.692035126575853,12.451354565897271],[42.777141802871498,12.498873491231707],[42.866042366259144,12.622556308683286],[43.116549947120603,12.708280653924978],[43.353267235353343,12.366932584299006],[43.40950947943481,12.189939212539331],[43.380038410380557,12.091371287495763],[43.275183635736724,11.972298909705135],[43.120616741297269,11.864393891657285],[43.073094250548927,11.758449017899412],[43.102040239711599,11.626837254316808],[43.245685573627092,11.499752774368385],[42.922624834361336,10.999548515249032],[42.783107664153235,11.009497061562524],[42.689506843021931,11.063931064785312],[42.595557507441619,11.07981185269348],[41.957410500197362,10.941223761965267],[41.798460315714458,10.98063924595337],[41.766680931416396,11.589086898972081]]],"type":"Polygon"},"properties":{"alpha2":"DJ","radius":135000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[14.68444223156069,55.102393905553761],[14.765393419988531,55.296464059072292],[15.132386811295691,55.144363968179483],[15.136838300510975,55.087237588341161],[15.050747709527426,55.005185927510027],[14.68444223156069,55.102393905553761]]],[[[8.1217549461546135,56.139951375558823],[8.1641872707994452,56.606727869065018],[8.2460772132609943,56.692067294760776],[8.2820401955913265,56.848091795523906],[8.6144453282447859,57.10855718689406],[9.4252984978998189,57.173290566394627],[9.9623911049697451,57.580730721159448],[10.249267779805741,57.616765626907075],[10.609266795705881,57.736691856558771],[10.504318357918327,57.57181274982819],[10.561788242406337,57.296813161036908],[10.31897266191911,56.994347571430765],[10.288352755085075,56.832349960628505],[10.308300628455649,56.652961547891429],[10.396441170487968,56.554572124205173],[10.845821793179335,56.521504781580816],[10.925903800789449,56.443213226175104],[10.856295906705396,56.295666443238055],[10.676603509578763,56.20941336939326],[10.602414256277861,56.116802312031581],[10.593757019661561,55.998455763686628],[10.693168504149739,55.775336379342718],[10.84205950896065,55.684202361091359],[11.049637127601075,55.739967128375113],[11.243502304585688,55.733172041994749],[11.405512351419707,55.827162163945253],[11.474870828947077,55.943232984162805],[11.78156517479287,55.926934557501312],[12.219001188358041,56.118368255640277],[12.578594391446758,56.063884254159476],[12.608079108311168,56.033001577990412],[12.550858899530338,55.842234218536795],[12.665433936682714,55.5966578557912],[12.438199536232617,55.472522269839111],[12.385278806805433,55.194386148514845],[12.425053150683624,55.064438000488813],[12.548800446911649,54.965845118039027],[12.331883906100407,54.950664177779018],[11.860586538786279,54.768194405080322],[11.765761392856147,54.679637017696777],[11.45743090681469,54.629102202466569],[10.934157676486491,54.827657351495986],[10.689848233184836,54.745308535709199],[10.578233906019605,54.837987719474569],[10.412110014265352,54.83862646859906],[10.198809700419165,54.924357949770553],[9.8465040243298319,54.883215925772376],[9.739557478263583,54.825776484311724],[9.5798035096821152,54.849753012650119],[9.3361404414581326,54.806413761408621],[8.6705286813495892,54.903699059089561],[8.573258035449582,55.134258433465433],[8.6271240720346345,55.266217857351251],[8.5854601496789194,55.393443287860833],[8.1323732663300774,55.59993999935655],[8.1893140747997126,55.970476966985835],[8.1217549461546135,56.139951375558823]]],[[[10.874324169069737,57.262360353284436],[10.934673330724786,57.30834731296126],[11.173747008686687,57.322760787615806],[11.011391918414406,57.229336919677301],[10.874324169069737,57.262360353284436]]]],"type":"MultiPolygon"},"properties":{"alpha2":"DK","radius":207000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-61.480876899361526,15.525193601752383],[-61.469582348281556,15.603376149923097],[-61.457980320023417,15.632831352228861],[-61.320174660269316,15.584848726270842],[-61.27998841546043,15.530449936936606],[-61.277447552735325,15.526623645604191],[-61.276466805832797,15.522136483442926],[-61.251340609776179,15.373154261742767],[-61.281889892133542,15.249226695337628],[-61.375219346305009,15.227570152586045],[-61.409964740486885,15.375218558837039],[-61.414463382154864,15.391375892144369],[-61.420299961293985,15.407099490346233],[-61.427433432570545,15.422278778340322],[-61.480876899361526,15.525193601752383]]],"type":"Polygon"},"properties":{"alpha2":"DM","radius":26000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-72.000098054473966,18.59786901550547],[-71.836809942693677,18.649547139426549],[-71.75641385416337,18.781680271614913],[-71.806878428768812,18.986942627118346],[-71.709624987418039,19.132846066668034],[-71.752905489922881,19.324426264602643],[-71.710562613934513,19.466281601709046],[-71.778929070060599,19.718102385743553],[-71.667184588519532,19.848410269509909],[-71.557725200993843,19.895094983952518],[-71.260604595464343,19.849193113320364],[-70.954155045172968,19.913711704395226],[-70.663645658006516,19.785585859345186],[-70.43651764471015,19.771009601292473],[-70.205029534567061,19.642753957176549],[-69.956949561048148,19.671644334643069],[-69.783776517052118,19.340507214661407],[-69.674945896103381,19.304593352802573],[-69.32501450049898,19.327472793106178],[-69.232736088918429,19.271727563963488],[-69.231404717965944,19.155235261625577],[-69.152686808442795,19.049886736494539],[-68.684893092421461,18.90456266193636],[-68.339417060541351,18.611478148626741],[-68.444980955661094,18.417872663593844],[-68.687415532984275,18.215129336995108],[-68.917955653961812,18.393235087883127],[-69.274574238369667,18.436597946811585],[-69.796222538307248,18.438302027415471],[-70.017831598663193,18.372645177610117],[-70.183201967200233,18.25203780825349],[-70.475337810437225,18.217818672307338],[-70.688978580340617,18.338756506422779],[-70.953132877691672,18.284620507143124],[-71.046246201874794,18.215286507444684],[-71.362351230571576,17.688937436837545],[-71.438882788025623,17.635857914374455],[-71.634200902104297,17.778037463403006],[-71.67838330474477,17.948065440978915],[-71.768082854719083,18.039284621564473],[-71.777652107413829,18.314135243600486],[-72.000098054473966,18.59786901550547]]],"type":"Polygon"},"properties":{"alpha2":"DO","radius":238000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-8.6831321833647124,27.286061864135405],[-8.6781824425601624,28.689326690183652],[-8.26813256206378,28.978547167839885],[-7.685045087327854,29.349307096979917],[-7.4686873307586401,29.402961173764147],[-7.1463808417170878,29.618039455720933],[-6.6753312671356371,29.582278523258392],[-6.5588532398163171,29.6466753837759],[-6.4796894923810591,29.820134317358441],[-5.9956440482599112,29.832676168632204],[-5.4220447841630222,29.974462076588843],[-5.1807156405303685,30.166682671118217],[-4.9681232724076221,30.465217775266389],[-4.3234087164705466,30.700018708389678],[-3.8640766635729458,30.978369489604994],[-3.8153416738911683,31.084015386271112],[-3.7979264638227233,31.388951426857396],[-3.8464484448720548,31.619814449866478],[-3.7724861836455568,31.687512180776334],[-3.4399783672088495,31.705433542200904],[-3.082051654880182,31.815418429996001],[-2.9926793469806157,31.88915203413303],[-2.9306818486885793,32.042380368473466],[-2.8588157847670432,32.075396265050635],[-2.4532377422008893,32.129702113767166],[-1.4462679940868914,32.09499600097616],[-1.306622926045645,32.166871074958024],[-1.1852964191545277,32.41684776653868],[-1.1776251717649286,32.532817842357211],[-1.5075050661942337,32.873762484724054],[-1.6789670786813327,33.318674401185],[-1.6401691316371605,33.544248155129118],[-1.7139478747208778,33.781850467834104],[-1.7043076000789616,34.141855362865492],[-1.7915501783626988,34.367927422027506],[-1.8473647176427812,34.73265658894374],[-1.9141049373032955,34.830004839982166],[-2.1355823014732755,34.974641659757971],[-2.2192980928476187,35.103948932459858],[-1.9224160362949156,35.094233259297837],[-1.6932396231180191,35.175834809555859],[-1.3380720847229584,35.367586159815588],[-1.0819151869763644,35.581898371447672],[-0.42608448875670041,35.861331504593487],[-0.041041331758134889,35.843081654971783],[0.1518042848676025,36.062988572126798],[0.316582229789379,36.164500018376614],[0.97175911567090678,36.443730753219668],[1.2572640032745048,36.519352238273669],[2.5811154272074344,36.605771820354605],[2.9729058641251318,36.784179067881823],[3.5024361222872904,36.795545695275486],[3.783662019263204,36.896192103154995],[4.7580761631055051,36.896126917878341],[5.3247536760101237,36.655384826225848],[6.082777310132915,36.871501815480102],[6.2441880985434013,36.944674689268908],[6.3279757817430138,37.04583135165457],[6.4864557567546388,37.085478065717659],[6.607256292493318,36.996651503507671],[6.9277229071066113,36.922418659509169],[7.1048219533888348,36.954560708523907],[7.2044776214234671,37.092118578903417],[7.4367600365294066,37.057804302272466],[7.8838062382251133,36.862511124765177],[8.5764132983907917,36.936972814924133],[8.60102191136545,36.834077651523508],[8.4320907457089564,36.733250220137762],[8.3199516221058865,36.530563015063457],[8.3484586960538021,36.367961198729816],[8.2462852188737372,35.841285634402823],[8.3939936895027909,35.203889210756429],[8.2825091251006668,34.996351359415485],[8.2454132940594462,34.734145413677524],[8.1232826028444833,34.564090295766228],[7.8581423704760978,34.416753021247011],[7.7483782362137008,34.254639095028807],[7.5438881061755705,34.106956356050588],[7.5006554594483426,33.841259189029998],[7.7462676019089045,33.266619276593445],[8.1123554889899356,33.055189841973323],[8.2107627059861823,32.926609152074406],[8.359193073731646,32.536951229154575],[9.0438179215019137,32.072254248426795],[9.5184886967439475,30.22951205054823],[9.4195245538608265,30.127778444323077],[9.4102580873422745,29.993670449761446],[9.6421352133233889,29.63215872526898],[9.8059554520566881,29.174205642308053],[9.8422942073869475,28.966980714846564],[9.8168451099765157,28.54539305494681],[9.9157650007334972,27.785712313280687],[9.7492805223398076,27.292301679910629],[9.8250001992341094,26.953268009452852],[9.8942117838733026,26.847874520047252],[9.8828426735182688,26.630873370837115],[9.8591659241549507,26.552109841582503],[9.4889108290721467,26.320973544572755],[9.43216008673358,26.119672302577012],[10.000455495658077,25.331971404364449],[10.106859348683244,24.826388554985524],[10.27702985760814,24.573651211004115],[10.42723169268627,24.491423040341306],[10.686133799575396,24.551150385725677],[11.507507945550815,24.314147640503201],[11.96757902517726,23.517952904555273],[7.4820734971751177,20.872615222945772],[5.8312835625607828,19.476434390241096],[3.3234883442976035,18.988625464436776],[3.1062906065753446,19.150075120950845],[3.2312687188349876,19.425530885776926],[3.198612765410521,19.729158503226923],[3.124251400483375,19.848440415361281],[2.4104071745258131,20.062063310176828],[2.2260407394355859,20.238755312566489],[1.7492921160753057,20.33430466249991],[1.5757210115298435,20.568379745271159],[1.1689303504532111,20.813705054712461],[1.1634188204529945,21.030966845946757],[1.0801085228047038,21.145000442227275],[-4.5187212647954134,24.80557344785058],[-8.6831321833647124,27.286061864135405]]],"type":"Polygon"},"properties":{"alpha2":"DZ","radius":1323000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-80.962536820351872,-2.1893761806306418],[-80.807546020936115,-2.0902292106196292],[-80.761222591661138,-1.9436915407011341],[-80.83476168777598,-1.6323936415176508],[-80.807510248833779,-1.3514397027199399],[-80.90211648865801,-1.0789270917941531],[-80.841237256146854,-0.97491241315997301],[-80.557133773863839,-0.8307418299450765],[-80.450524748587981,-0.55450711920335394],[-80.482027026665179,-0.36832100105708693],[-80.14047695784474,-0.012171474287327927],[-80.057192767384038,0.13716518286699475],[-80.026639738151687,0.4007816884151032],[-80.088046653960703,0.78468400564323659],[-79.741117372857175,0.97954523365150026],[-79.61544870557023,0.98051667300897716],[-79.092116466282818,1.1512277551168129],[-78.859511185739649,1.4549933948528089],[-78.670547959998999,1.281926395198814],[-78.173418280459771,0.96503277440768021],[-77.850492678925747,0.83406608644538127],[-77.703013446851912,0.83759396313107504],[-77.625328192089299,0.71400603371510807],[-77.467862439433233,0.63642604393739277],[-77.421769678676597,0.46080724681544249],[-77.322445700076045,0.37183450414910602],[-77.114159502784005,0.35480077318303826],[-76.843871152930106,0.25208730486601033],[-76.588365459563136,0.25251774408231298],[-76.275793914726009,0.44056981734787232],[-76.063430970485939,0.34206523398904803],[-75.809246691722663,0.10876036024502039],[-75.617435796081764,0.062631140629282198],[-75.284941446656333,-0.10659097216434586],[-75.394241323053038,-0.18964701929998196],[-75.436822581409857,-0.31628079906771844],[-75.401919835597951,-0.42596972802287414],[-75.263460877950862,-0.55549802735916709],[-75.249884392382086,-0.95173054336295282],[-75.4377291550677,-1.0616937354707341],[-75.570774170673218,-1.5311298260887265],[-76.090024096066614,-2.1328588650884965],[-76.679202900700673,-2.5624067455972304],[-77.825471760962998,-2.9695782006876614],[-77.937815600947275,-3.0476222404346092],[-78.142546408527664,-3.3085011321239568],[-78.158762727362799,-3.4649948272354338],[-78.297098285330691,-3.5123685406226777],[-78.378921775030648,-3.5969628511649172],[-78.420010930191324,-3.7767779949127234],[-78.666552721131652,-4.2965571205430866],[-78.686260881940427,-4.5622242493460741],[-78.862112456420789,-4.6746438300037294],[-78.919407779375106,-4.8582257802912876],[-79.076290117292515,-4.9903943114733655],[-79.330818918273593,-4.9276596814108942],[-79.455543630533597,-4.7660769904001512],[-79.537958991561027,-4.5554250573265778],[-79.62783669506338,-4.4826528280041371],[-79.797210811027412,-4.4761359373772711],[-80.110809636106708,-4.326665374152201],[-80.383557700408502,-4.463385913438354],[-80.478363518355806,-4.4299231951889526],[-80.453154193842124,-4.264224928518785],[-80.50978950120691,-4.069531298842322],[-80.489925522257934,-4.0102288949705036],[-80.328027167071127,-3.9543933182322331],[-80.240183317426045,-3.8567413001958375],[-80.223410022123332,-3.6293319092622163],[-80.32426898930872,-3.3879232163630055],[-80.179977719808932,-3.2825066210715015],[-80.157969297430839,-3.1298170360851425],[-80.272719460156566,-2.9957534394238237],[-80.260389779574808,-2.8242616909807152],[-80.308145227779647,-2.7192178496425914],[-80.679785065643813,-2.4068025481988697],[-80.8429356113067,-2.3457007263842145],[-80.962536820351872,-2.1893761806306418]]],[[[-89.608349469210111,-0.88864678712218537],[-89.422983493059959,-0.72248197816145776],[-89.318398791694804,-0.68033349011362609],[-89.267653105282164,-0.70475322151282938],[-89.419059133369956,-0.91081876505620984],[-89.536610132132608,-0.95210170806653627],[-89.608349469210111,-0.88864678712218537]]],[[[-91.653935808270745,-0.31095693694073123],[-91.565106141830029,-0.16487438556488718],[-91.596552194139861,0.0020163896750019462],[-91.49085511298513,0.10494787383904063],[-91.361430355040468,0.12556407965347097],[-91.2102762067111,-0.039418415729237113],[-91.11143665224094,-0.25992129055087204],[-90.964271390142272,-0.29392163998130294],[-90.780311515887306,-0.16073148894366374],[-90.667643632253828,-0.19007613126868919],[-90.553559007726633,-0.27852574686909737],[-90.469600573992295,-0.45927281236870227],[-90.269442596819715,-0.4849154321366117],[-90.185561920340831,-0.5449469906274067],[-90.192983680392416,-0.65868666556800404],[-90.26122971468078,-0.74176886365360872],[-90.382176609965754,-0.7731738815619128],[-90.559820690597448,-0.67022011582246688],[-90.679118466748051,-0.6638882313570843],[-90.780743029836501,-0.72669348856095772],[-90.905696887065389,-0.94031367425903067],[-91.131085738013539,-1.0194403124730755],[-91.371482937933649,-1.0167534247423229],[-91.483354415957024,-0.92451566733057722],[-91.495130922935218,-0.86098819900152868],[-91.412301765681491,-0.71999481942285337],[-91.419501932125499,-0.58513714844859788],[-91.610584419391358,-0.44375084823071947],[-91.653935808270745,-0.31095693694073123]]],[[[-90.5192204381366,-1.2990559455471489],[-90.477094066533695,-1.2212881734076735],[-90.379427412187724,-1.2922607580441814],[-90.459326842924796,-1.3417349461659873],[-90.5192204381366,-1.2990559455471489]]]],"type":"MultiPolygon"},"properties":{"alpha2":"EC","radius":431000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[21.85478651604901,58.301663626479822],[21.862620728078273,58.497023644230453],[22.139575978888548,58.504361376310946],[22.296937600733113,58.641763430911837],[22.268509702490267,58.825001109691911],[22.057092010778184,58.943464125430786],[22.415017429526447,58.974314013885746],[22.654771566750497,59.085821989505405],[22.909763392931932,58.99100714148593],[23.098931733596981,58.70447489745446],[23.283131891844448,58.673089054566084],[23.426458753132362,58.798009583753483],[23.494725626981456,59.195492700731997],[23.937641516719424,59.287970693101585],[24.053752586515433,59.372075914476085],[24.380421056699312,59.472379711875782],[25.346693238681297,59.522300465863481],[25.509467536353043,59.638771332539015],[25.79374352040432,59.634506985653175],[26.624967227523467,59.55371930156376],[26.97476916423944,59.451359266254457],[27.85921201605586,59.417533100506468],[28.012530332860976,59.483993223711998],[28.150755285074304,59.374473926920743],[27.905871580626272,59.260401671416027],[27.757399760954154,59.052200905388091],[27.454250199256208,58.800136578475254],[27.446301507503343,58.681166579981124],[27.531145286333203,58.435221947117483],[27.515328818840636,58.230851656034595],[27.676057675681246,57.952599156098934],[27.778001106214003,57.866104613349684],[27.533153363932239,57.772545108732672],[27.389588144811324,57.639742890784007],[27.351745946869777,57.528332563900918],[26.932264946168054,57.608506872699024],[26.469150110339204,57.54313668575756],[26.298169521581805,57.601306634315996],[25.949549503573344,57.840693882258044],[25.388763746906417,58.014729500498824],[25.228780020470001,57.996804897145246],[25.110129659302988,58.047668607604251],[24.3228510785281,57.870810473339731],[24.40543645137976,58.07933180695774],[24.332922423939888,58.234063045832002],[23.767692179726406,58.361029258542139],[23.644548694471592,58.52642751286524],[23.528984638786625,58.576350037593642],[23.41331182024626,58.552974572577526],[23.323087359063869,58.451050558779123],[23.134924992464303,58.428313933819886],[22.73017262302103,58.230895333799694],[22.356200218511425,58.207714243789667],[22.152314442126542,57.966976167109436],[22.076204800593842,57.936322740039991],[21.994340959069415,57.935641083111648],[21.993711194126043,58.140914368731103],[21.85478651604901,58.301663626479822]]],"type":"Polygon"},"properties":{"alpha2":"EE","radius":217000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[24.703442892092475,30.20103882542131],[24.876624068054547,30.458053096729632],[24.963727651941895,30.696570923239527],[24.852953406347812,31.334741671931344],[25.15042525030384,31.654658161382397],[25.228931193501715,31.557148789718301],[25.348032555566888,31.517373474351629],[25.89326397486327,31.620646942141896],[27.242739063843921,31.378904794425885],[27.565234910910149,31.206104447275631],[27.829895239357889,31.194744688634998],[28.0007613805369,31.095696416496885],[28.514800191641303,31.050244238606258],[29.08260017137523,30.831851304309293],[29.436676676166051,30.931648990114642],[29.929865789254588,31.227317939661035],[30.207038779789777,31.277607153890507],[30.395242636080411,31.457359369108595],[31.082938936947166,31.603062784832456],[31.559013676999932,31.457202856609246],[31.888941638627443,31.541158033601235],[32.161544435375163,31.323175424257673],[32.614121694833507,31.073298698038993],[33.129810705689401,31.167915121467175],[33.259522453850643,31.110392433258639],[33.66638523919034,31.131551014268442],[33.902190213705182,31.182022042868638],[34.198069668072129,31.322252150733494],[34.90402063061066,29.477415836658277],[34.7329020612652,29.251510231665431],[34.617006817236394,28.757969165534913],[34.451958577205495,28.367163157647813],[34.401032405973154,28.02038558827752],[34.315455827509815,27.885040543375506],[34.220041624155407,27.764574960501744],[33.823087310769161,27.988274723055326],[33.686779936724484,27.98952256697623],[33.5645276741418,27.867196184711592],[33.562562042312187,27.600891961380707],[33.849070038676864,27.184803220428464],[33.966926524176309,26.6528474301264],[34.564950255119705,25.691054556483309],[35.190284644180245,24.484941036440606],[35.480834960892992,24.157999085492023],[35.783231939642995,23.93805470389222],[35.590318384548517,23.906185823604158],[35.506854818939914,23.752689316928823],[35.52709583723346,23.42463666813924],[35.690860563351109,22.96591395278546],[35.891681526893493,22.754432128781207],[36.229554335625281,22.628622164841861],[36.422927972427232,22.389299908357337],[36.829502011480095,22.097498097490138],[36.871158482294575,21.996945282954474],[24.980503307098896,21.99607944959569],[24.980273437500017,29.161606456339843],[24.703442892092475,30.20103882542131]],[[32.782740677563581,28.788567212880558],[32.978274078663794,28.726922782857979],[33.078348474121832,28.785060545336474],[33.129462618905961,28.888898054239984],[33.074571054568914,29.07213895629955],[32.876836219630796,29.231699397112713],[32.726843054658517,29.205723472704999],[32.638580066257539,29.081697898464181],[32.652509969709008,28.947039114107429],[32.782740677563581,28.788567212880558]]],"type":"Polygon"},"properties":{"alpha2":"EG","radius":982000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-17.098540242420885,20.856960769282768],[-17.00287275329336,21.420549714396536],[-16.034533104978589,21.500585937499991],[-14.963680545654958,21.441574788167472],[-14.739244831743141,21.516663934251671],[-14.626744033811852,21.715734308831728],[-14.630576943690516,21.860858321896032],[-14.22276289654199,22.320672375470604],[-14.099826558415151,23.105181899703446],[-13.893394117439433,23.687005363568019],[-13.770870030028723,23.79045881940678],[-13.318666698618721,23.981516729930306],[-12.995153616251889,24.466351675565893],[-12.426543868938714,24.840303263112926],[-12.060790580738026,25.990722283075623],[-11.71519727350104,26.153128251916591],[-11.365417516972526,26.636613074760685],[-11.392267121003879,26.883223118904876],[-10.757771177351655,27.01982998977266],[-10.551331373447729,26.99057349952044],[-10.252104164411362,26.864932467494256],[-10.032762333228533,26.910447694410479],[-9.8900730552664751,26.853589258317019],[-9.7554636523372285,26.861665222771588],[-9.4129518860816201,27.088217241817144],[-8.882017381930833,27.128450203520725],[-8.7970326254634088,27.275698395452505],[-8.8175564522684642,27.656219282252298],[-8.6835795141707184,27.656215407704277],[-8.6824560346422821,25.995737481360774],[-11.816308595688119,25.995410158188136],[-11.982602516614282,25.906524204187743],[-12.016308593749983,25.795410158188137],[-12.023677785712316,23.467732808434718],[-12.372995009632456,23.318366291170143],[-12.627743302894995,23.26550192911122],[-13.021282370826468,23.005508597423912],[-13.140629800610331,22.845333562517471],[-13.162736504399168,22.735300583569042],[-13.093897251296818,22.489461084439711],[-13.016446748518833,21.334155898031973],[-16.82343876658312,21.32847656043894],[-16.978731879726251,21.226636702845223],[-17.048357988822072,20.806753235905475],[-17.098540242420885,20.856960769282768]]],"type":"Polygon"},"properties":{"alpha2":"EH","radius":589000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[36.427005602678669,15.132007922294173],[36.532936440133362,15.278389098069184],[36.906039951635755,16.277446874180917],[36.888015677382612,16.624618873088053],[36.971200315750671,16.802336315923533],[37.009151902625391,17.058659686043324],[37.383841630457141,17.080200020765524],[37.54759833811822,17.323904872060467],[37.788093099414247,17.460048393247209],[38.247979350480925,17.598456390835583],[38.609398347729197,18.00474092924275],[38.914034205591776,17.420704950101584],[39.1404548192288,16.736150394610601],[39.286029074862299,15.967131033735676],[39.507786850242056,15.575370926750139],[39.695784441635212,15.43870495223238],[39.885055981818702,15.485875097807805],[39.951910112393321,15.598078097736257],[39.996327448131296,16.042586224026593],[40.07268872322372,16.085364644789475],[40.14390853274967,15.777263979594151],[40.407925721458192,15.629056974913386],[40.398833713338057,15.580104960899739],[40.156579457665671,15.571802963723155],[40.049135687511047,15.464256999027349],[40.059226479212434,15.217470097365823],[40.172160520167459,15.050795446312465],[40.296078829408607,14.978746928437598],[40.546221201381343,14.933431260430384],[40.801191932466303,14.746377260993967],[41.176329203339741,14.620115764281485],[41.660754480504757,13.985626709247862],[42.244937921995898,13.587484595803508],[42.401863555477156,13.283499755482561],[42.526205302007789,13.218269149422033],[42.812419256071777,12.918208682263089],[42.99897251602269,12.899235819163595],[43.082721887109088,12.824490640328655],[43.116424576995435,12.708725134251049],[42.861451124563033,12.598339934877504],[42.703631624570093,12.380539667839304],[42.500366098085784,12.478359918659438],[42.378579723396498,12.466708659605597],[42.145645063191907,12.756394540558066],[41.952345509369714,12.882520092324105],[41.757245331747214,13.189893213365382],[41.360072992610156,13.502573128581401],[40.825372471199081,14.104934852627428],[40.524499056974527,14.22537107992297],[40.148835504854162,14.452743104349965],[39.895154860081242,14.440934133867016],[39.531862337277026,14.528683148446582],[39.265686055710979,14.47087089465639],[39.023317844351453,14.574714497267308],[38.811906044902223,14.48261215963916],[38.431534214545053,14.428852436235319],[38.201857060967235,14.657632515920804],[37.941134609423038,14.732573175422647],[37.797427078120471,14.648174441501499],[37.571038471043813,14.149260863171921],[37.507356892188803,14.156575180586609],[37.376663201863224,14.333651408853555],[37.254694556784031,14.38788535192948],[37.024468383917309,14.272245237844606],[36.779925756936471,14.31323972031208],[36.524511741983929,14.257071575653598],[36.427005602678669,15.132007922294173]]],"type":"Polygon"},"properties":{"alpha2":"ER","radius":442000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[3.8429216534622008,39.976486244110887],[3.8536066065460499,40.062798158152361],[4.0591539724685042,40.074831299798085],[4.225654349769564,40.032179745946124],[4.3217976933240765,39.897528063402831],[4.2752640006609406,39.830557830628045],[3.8429216534622008,39.976486244110887]]],[[[2.3702578502895824,39.572147120726363],[2.3715335114795186,39.612940589256709],[2.7899468878648515,39.857058766628278],[3.1586916850956177,39.970274994024933],[3.2720390640681711,39.823627791598739],[3.4487253163030807,39.761047978633606],[3.4615626918230262,39.69780383377789],[3.2445729389898692,39.386768026619869],[3.0728512734839044,39.301497305215307],[2.7999267626424111,39.385323415490276],[2.6503814572708366,39.498509501253892],[2.4995592898204024,39.478188230674377],[2.3702578502895824,39.572147120726363]]],[[[-9.2353938529764346,43.035741874866346],[-9.1778724894357335,43.173831347336552],[-8.8736285367384244,43.334206262066267],[-8.5146710717224892,43.346518088387612],[-8.3505387079585471,43.427523649475106],[-8.2565805235815031,43.579672610678898],[-7.6981430101253743,43.764288065127133],[-7.5036520799534774,43.739647781383965],[-7.2600056083819462,43.599044134812857],[-7.0806857146373057,43.558935235073804],[-6.0893737242237176,43.596328258770029],[-5.8466925406771342,43.644857243328481],[-4.5088003024603278,43.415656485453248],[-3.604625862846921,43.519295629376856],[-3.1103478663693815,43.386590708842093],[-2.8750442834748315,43.454245210688065],[-2.3109222563539493,43.326889169260859],[-2.013035872063071,43.342619778794777],[-1.7941729775984134,43.407075911964697],[-1.6845916860884549,43.300019372140483],[-1.4108986788992337,43.239928273954462],[-1.2240648735976158,43.039141680228795],[-0.76278721929690563,42.939549616578759],[-0.57141364416300622,42.806630782001982],[-0.29937508678863411,42.825132700881241],[-0.01672995337023574,42.695976317719307],[0.47398811715380945,42.691591089104818],[0.58656049604878091,42.726452024341981],[0.66998263432935212,42.835559994279585],[0.75790243421281211,42.838723417297061],[1.3451127576229198,42.692151232843656],[1.5256189467242685,42.50123798364347],[1.7060467389645451,42.503034450613107],[2.0170606769341775,42.38524606027498],[2.2004106339189309,42.420689235296571],[2.5686318352783073,42.355272771505057],[2.9700086028289663,42.46700988152778],[3.2112814127507812,42.430928648504029],[3.3064600468681502,42.289040054811622],[3.2353683763642422,42.171993042827815],[3.2478166265820989,41.944337055033493],[3.0047930452689582,41.767597532351061],[2.3212269326810802,41.469777700865336],[2.0825293625946433,41.287657503807061],[1.0108193473852154,41.043524677844417],[0.87582966001813634,40.906130539747132],[0.86225589336280928,40.68971119098007],[0.56448487509615042,40.568191933632377],[0.030935561185409438,39.999799664486218],[-0.30214650811391475,39.553300927743024],[-0.32579276780703409,39.41759620501972],[-0.14920694336816076,38.989662173979042],[0.20127613453685575,38.759206721659645],[-0.5008589449353289,38.328772071749292],[-0.64665551862391102,38.115952412603725],[-0.76127377368611304,37.830012608256261],[-0.72561873702539681,37.62826034651178],[-0.82758763151096404,37.580321114321663],[-1.3166396055256646,37.557726677564695],[-1.6172194562585693,37.400159146113197],[-1.8015943937532646,37.221972423400366],[-1.9395617196520896,36.946034200993488],[-2.187673685327431,36.745706851942764],[-2.4445069201932457,36.829828716603807],[-2.7875529107563888,36.7150193504337],[-3.1578503380572411,36.758280239706387],[-3.4312569337386929,36.708149018835947],[-3.8142628016735243,36.7551743632635],[-4.3623891383660034,36.7175672961191],[-4.6742027405120634,36.506693003041896],[-4.9448300710065185,36.497818301851368],[-5.1696482069335783,36.420512765751255],[-5.3611088393737401,36.135096090587069],[-5.6254682444414348,36.026182761250766],[-6.0438317432268542,36.191964646812259],[-6.2946501176894092,36.574128457033531],[-6.3839590947053884,36.637183247843872],[-6.4190107245179382,36.845560950223735],[-6.5233498199643165,36.973527554596366],[-6.896964927770334,37.192813910676712],[-7.4059808496755144,37.179693018848631],[-7.4965637154667206,37.527883528651621],[-7.443780379700466,37.728154120393583],[-7.1409389556083198,38.091658939302242],[-7.161062265879635,38.239282152363245],[-7.3427743400062333,38.457489894822082],[-7.2813287584687263,38.714446305995196],[-7.1203892388051351,38.847533324334364],[-7.0807842004931585,38.975546302513372],[-7.3398396591569446,39.441372936295309],[-7.5353315794733975,39.66143132587063],[-7.0882064574560699,39.695736552676891],[-6.9388144303240864,39.877409941132974],[-6.9315561032397559,40.027527821154536],[-7.0323572161846002,40.167889861579781],[-6.8688584838430797,40.340280349602473],[-6.8229163616323412,40.678583941549469],[-6.9281845728132554,41.009141436961286],[-6.5007910784604013,41.341790996436117],[-6.4340768346415889,41.458826876547008],[-6.5829093131083747,41.840980534740872],[-6.759073822362609,41.951497102408226],[-7.0760203223110789,41.954074263826705],[-7.4036511623557599,41.834064173465407],[-7.714599405128852,41.885732870884347],[-8.1524488776732209,41.812215209130798],[-8.2835320144134599,42.030803776931755],[-8.4267160788499993,42.088968849590991],[-8.8522891648973534,41.927132639646459],[-8.886965072742905,42.105175087710556],[-8.8203093363188074,42.210579038537233],[-8.81183946706334,42.445497985393011],[-8.8896773507208362,42.551166113622358],[-9.0328825973467239,42.59403926017314],[-9.0468888371089466,42.770150862024977],[-9.2349636369975272,42.97699374358605],[-9.2353938529764346,43.035741874866346]]],[[[1.2236315231279815,38.903930622107509],[1.3487369555722657,39.080621471314821],[1.56850972545217,39.11824432296568],[1.6233672601111502,39.038910821117852],[1.5177469074569609,38.854955074042422],[1.5937019655408813,38.672203394922811],[1.4059730566853392,38.671228275986302],[1.3580274461364374,38.81337885601851],[1.2236315231279815,38.903930622107509]]],[[[-14.491298984637107,28.100809399885964],[-14.238477410922593,28.221053237050576],[-14.003166237269285,28.70654629017541],[-13.878918917235872,28.818718364553344],[-13.788057766455697,29.055939047045069],[-13.460060158174489,29.233773219654047],[-13.423253606228601,29.197477961935874],[-13.478134121451953,29.006730553666085],[-13.721104160841536,28.90128136544952],[-13.811218610922772,28.799614036920556],[-13.862017763527158,28.414146633005906],[-13.928198109160974,28.253654000686943],[-14.194912375254647,28.165867777228993],[-14.332682635279681,28.056260277033697],[-14.491298984637107,28.100809399885964]]],[[[-15.809212880112289,27.994359507076993],[-15.682621937563342,28.153820188610759],[-15.415528061145949,28.15907546960376],[-15.389393543152785,27.874790939036409],[-15.559420917350193,27.747224061519102],[-15.655681894169522,27.758677036380352],[-15.807057195754862,27.887657552749388],[-15.809212880112289,27.994359507076993]]],[[[-17.324615548132225,28.117681246537195],[-17.258515370120303,28.202926670221338],[-17.009250815325675,28.190524427132139],[-16.843007482088566,28.375872214604946],[-16.557225954615078,28.401347212118154],[-16.318920085707997,28.558007401093533],[-16.123839434045841,28.575727783977001],[-16.119378685984664,28.528382640850964],[-16.326658623849205,28.37387449738414],[-16.418394475850697,28.151523420361105],[-16.542850217085697,28.032266085250392],[-16.657923797769378,28.007453482740285],[-16.898814865389625,28.192075654596511],[-17.01292114917122,28.171848999889949],[-17.220660404692882,28.014506599393755],[-17.324615548132225,28.117681246537195]]],[[[-18.00050902897954,28.758232148309659],[-17.928702664737703,28.844358693706624],[-17.79765935075234,28.846549263642551],[-17.744800620366878,28.786424457994428],[-17.756598376188371,28.57394652194839],[-17.834403437941955,28.493424702585283],[-18.00050902897954,28.758232148309659]]],[[[-18.160132199651148,27.761279071062667],[-17.924546034143297,27.84985341408159],[-17.888223896492789,27.809542056637394],[-17.984857592819207,27.646700333976391],[-18.160132199651148,27.761279071062667]]]],"type":"MultiPolygon"},"properties":{"alpha2":"ES","radius":631000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[32.999207436729357,7.8995418067961989],[33.281168137794651,8.436987419418406],[33.914636098805708,8.4415212955092329],[34.076832949149349,8.562271208462354],[34.07946605304182,9.5134587309443681],[34.157909670670563,9.849676455263392],[34.309915129010456,10.191101316555724],[34.275957945358435,10.52807773293871],[34.431585544623438,10.787705424527108],[34.512090929488707,10.845256033642469],[34.571881655190666,10.87989181387699],[34.750523067571976,10.817211496623594],[34.884678676251184,10.887086944639922],[35.112488384657269,11.816427974032036],[35.251307072809603,11.957919820024513],[35.670387314694068,12.623528901506306],[36.01718668696175,12.718248065514569],[36.103417548346648,12.79062506855457],[36.210203898306027,13.264251904738209],[36.394740616680338,13.641821080840936],[36.524507445141523,14.256621551346132],[36.683933418144214,14.307840151254982],[36.996256165787877,14.291069656413589],[37.185233822451345,14.445801119851971],[37.257147277142955,14.453500187592304],[37.42669766458836,14.316671889163775],[37.579795924461457,14.312560518759588],[37.682055351832759,14.400432156027344],[37.884275124259176,14.85194956599806],[38.022420900700311,14.728212571255614],[38.2213448215577,14.649498596662538],[38.448226119795386,14.436260573235922],[38.800587613603305,14.481413463231304],[39.074147898863338,14.628011624780521],[39.274796166077287,14.487096664668423],[39.531836666554483,14.536496839363739],[39.906145428023009,14.446026331432163],[40.14476056965627,14.454781051917454],[40.517328012060069,14.230796703180479],[40.820004072674578,14.111535860997071],[41.351598063385246,13.510900099236787],[41.764861611400562,13.183791421191252],[41.94135305767329,12.901087928428531],[42.134140750400782,12.771271688419684],[42.378207138832344,12.466390493622377],[41.790712347992368,11.674597874691454],[41.765323332957749,11.404151107976849],[41.816738332769503,11.049100157787604],[41.982115589761833,10.957174236910902],[42.55775045375038,11.080543782283364],[42.92240345359464,10.999080152608769],[42.688080861341042,10.624688857489696],[42.833558417141596,10.222973737331683],[43.060298986509679,9.948034834820513],[43.181461978908999,9.879813220642486],[43.296964282595923,9.6208030270493392],[43.450491641888256,9.4160954896277094],[43.620418082631559,9.3371957342347702],[43.984421933967567,9.0097028335806826],[46.970553014817568,8.0046281164351303],[47.977682008242667,7.9968521256853471],[46.420819184606287,6.4950819948770722],[44.940357549283156,4.9122532849744358],[44.007435388290951,4.9507449623689181],[43.575167483593219,4.8522822149195077],[43.112253695853447,4.634544536610484],[42.852023892834765,4.3219311800082112],[42.070145421586886,4.1522945553781367],[41.883857747949691,3.9779575232115918],[41.215259248790417,3.9449165789480656],[41.087337819941936,3.9921195752881808],[40.823975686943399,4.2233202514301169],[40.676923128524997,4.2364609734996046],[39.877151845424081,3.8700491495996929],[39.657362596625831,3.577974569095359],[39.494411727523435,3.4563188836828025],[38.086230761268297,3.6490542898108238],[36.895692646476505,4.4132239997442904],[36.022087665260621,4.468328868534976],[35.763340970027393,4.8081027891554093],[35.791586136312397,5.1694206234640667],[35.75685338501993,5.2900460233637965],[35.52694798290819,5.4032510383899668],[35.3253050015498,5.3651251347579842],[35.267683745654011,5.4083600541680852],[34.983826194794034,5.858379920933019],[34.955883130796671,6.0440740470474319],[34.688278700072331,6.6784146415592174],[34.067972917290035,7.221880066083048],[33.996862916390796,7.4048864915723973],[33.887337408431002,7.5198415093310791],[33.655650491666151,7.6730082331574501],[33.22604738496166,7.7608625248802037],[32.999207436729357,7.8995418067961989]]],"type":"Polygon"},"properties":{"alpha2":"ET","radius":1045000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[20.622759548591389,69.036966712701513],[20.948441485380162,69.086626279154132],[21.148447100864743,69.248274010542772],[21.597379257337341,69.273210942244503],[22.486552356130083,68.722434190585574],[23.307142457388082,68.648373683180807],[23.67942659184715,68.710594757618182],[23.858832788042942,68.805656561805435],[24.940965948598766,68.597373224447836],[25.072318326597099,68.643361585731],[25.249218396945778,68.821159251855988],[25.693123089401983,68.952763629198799],[25.755761090697661,69.051317591040529],[25.767438177551753,69.282507458551876],[26.008040866597643,69.648178075729447],[26.525464660211192,69.914847919659636],[27.141319192660628,69.908647285962573],[27.591747114428451,70.042035871921428],[27.889873910526237,70.061426946632324],[28.402398526054231,69.825936188193111],[29.141468496731814,69.671185063172842],[29.33307283493826,69.473015311879522],[28.97875157300188,69.239950405468491],[28.93966561154987,69.13023476475864],[28.965467507237097,69.022141846357854],[28.835462623541378,68.961767064460091],[28.751833832056299,68.771619264887633],[28.548640262280411,68.586226044934804],[28.53132140269701,68.402260157568691],[28.70137072836604,68.196446895065037],[29.343725310599304,68.061588178679514],[29.979029775783136,67.688401535777587],[29.940986874675353,67.547644961515886],[29.195955480497986,67.057194255924529],[29.145948212037673,66.853128210873507],[29.664064263052413,66.283003004464462],[29.90321108535796,66.090904934488805],[30.08723685211136,65.786409743884178],[30.095120692070576,65.681881410306815],[29.856233820522849,65.615242544298923],[29.742853231706277,65.477130302367598],[29.720346230186216,65.351074780344661],[29.825955895843524,65.185224311358482],[29.765898624720723,64.992182185314604],[29.794280403141165,64.878219390668491],[29.881740387867815,64.799837026698839],[30.110032756178452,64.732417141842419],[30.079396295515693,64.520407200886325],[30.114484932440625,64.410769554820433],[30.51356151747127,64.199905551885166],[30.503692822087043,64.020760040049097],[30.251235430371381,63.791082769412284],[30.263325720023573,63.620793126669874],[30.407809936894683,63.509524785369003],[31.180741836790123,63.208129932133893],[31.5091441481188,62.955181857140715],[31.533774775063151,62.885488301596666],[31.186626414613329,62.481520652577629],[29.690062912313035,61.546222443547855],[27.669286617338201,60.499212578732092],[27.46239289598482,60.465117270675535],[27.192706660788836,60.54081609413759],[26.534682865347882,60.413139947153645],[26.150991407772747,60.404881948652736],[25.757947958673075,60.267766450732736],[25.575697693978103,60.304456945476083],[24.652159029391633,60.123435366703724],[24.445586343443001,60.021580304053899],[23.446491796245088,59.974950992261917],[23.021289041525037,59.816216439996296],[22.964119037240383,59.826547474855651],[22.941457632002329,59.944585920228803],[22.857741186501883,60.027056686888237],[22.462826739846093,60.029421835271684],[22.378430510513038,60.133073385866382],[22.077893892225134,60.285121843559729],[21.947866042727551,60.271832715310602],[21.833042293889665,60.140712339339778],[21.733093015929569,60.106362001722374],[21.490337452443701,60.12452850167454],[21.558387518503974,60.25084334291703],[21.535766913591235,60.383896633447392],[21.433775820456702,60.472286037839545],[21.300077830862264,60.480050359998032],[21.214827882785222,60.603832437468917],[21.371021568268574,60.755440223079134],[21.377990741730454,61.059087082129444],[21.512031438525728,61.281464544159782],[21.529761996519692,61.684918604581362],[21.407646806162987,61.887468683792264],[21.256253208190746,61.989754839790656],[21.320820279508485,62.262290653073457],[21.16584343983136,62.414241701654483],[21.103835499558482,62.622963301582203],[21.144092339653326,62.739810533492253],[21.305979796282816,62.885522137026541],[21.331050772654795,63.018346335772186],[21.280009363747777,63.123041258161166],[21.084357450902406,63.277319742589476],[21.53127494197858,63.20591672356646],[22.129954259435053,63.254524389085219],[22.21625622549584,63.334317048140278],[22.243498204802194,63.437804192583776],[22.464572057654362,63.546352283688748],[22.532502936873371,63.647653254076594],[22.765823663746243,63.689897979919913],[23.575794138523502,64.058550943290683],[24.285329338295416,64.524078697337131],[24.545545702735772,64.775471647596959],[24.589818013092884,64.890996854908224],[24.576812736921259,65.042710003466226],[24.786028155135153,65.086238889327277],[25.052069846104498,65.02768812939965],[25.183696572534998,65.059331309144227],[25.292832301811437,65.233952279944575],[25.307427398844759,65.404383167127378],[25.18219035351138,65.562226284037976],[24.675010630988609,65.670925699550494],[24.514701368527504,65.78481066477265],[24.155587632184847,65.805501350598391],[23.944821346552125,66.110493401122085],[23.721179745221971,66.21558531538011],[23.682346039823233,66.443310200011922],[23.860490158842342,66.589847506454319],[23.912541844607958,66.823602803266041],[23.864347134597928,66.927305503498317],[23.675038439449274,67.072056875711866],[23.585727252889331,67.376319595865212],[23.455126684150319,67.460401076774474],[23.507347101131771,67.62243357588126],[23.500928661846697,67.892912704523553],[23.420671878790181,68.04832281977329],[23.182654771237409,68.136866802259732],[23.085524892935016,68.258861177316547],[22.846682190101454,68.369785207544837],[21.991060367182662,68.522931405443572],[20.622759548591389,69.036966712701513]]],[[[19.519323968362357,60.184558570185679],[19.551499144533814,60.243632564207765],[19.699944094585501,60.27304116410469],[19.888223745987499,60.405572640686131],[20.239439077506475,60.282811556747433],[20.285149138810695,60.172762606877626],[20.169263105949991,60.186475905345688],[20.033775347159551,60.093769246253309],[19.79983119435947,60.081941359405491],[19.536709075185424,60.145195665014192],[19.519323968362357,60.184558570185679]]],[[[20.378137654104187,60.089016424639837],[20.569044659846085,60.069352417353038],[20.611045752780861,60.040579069278166],[20.411313747907865,60.03037969998514],[20.378137654104187,60.089016424639837]]]],"type":"MultiPolygon"},"properties":{"alpha2":"FI","radius":581000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[177.18381047256804,-17.163764046985396],[177.21111420439337,-17.084928452280835],[177.2871390862054,-17.049636379413506],[177.42385901244756,-17.134899425037837],[177.64143884820632,-17.205452339959511],[177.79379491665412,-17.216821150851562],[178.01943615103983,-17.179341395280748],[178.22524853436312,-17.079541788049365],[178.34302261922727,-16.982223308877249],[178.58416876866383,-16.62278707479684],[178.77495280460587,-16.577724900284664],[178.98036171390081,-16.475951242350856],[179.31001302693036,-16.379105662209177],[179.57217943140765,-16.243381756660213],[179.99899584238062,-16.15434083077422],[179.98286603299263,-16.34229859765502],[179.99900172204721,-16.962475206962665],[179.71605391496308,-17.081688711624192],[179.48045133839614,-17.284507119436594],[179.39660056437157,-17.416273787040573],[179.32254202113938,-17.638044167502279],[179.3103919423182,-17.793754740619889],[179.36149935967393,-18.065222722745816],[179.34849242451415,-18.101797781931612],[179.12446867544421,-18.124170556765833],[178.97067535672912,-18.172204518395638],[178.82962597992952,-18.250080828372983],[178.70704178245126,-18.35464067190701],[178.60789501806747,-18.481642898121645],[178.53620727262231,-18.625936049394465],[178.49488634067748,-18.781667314509917],[178.48680468353263,-19.016371601491542],[178.03928400064706,-19.147713152246101],[177.95975289554394,-19.121083709668795],[177.99036270802256,-18.902011178979318],[177.97542534447405,-18.748045253705882],[177.85914963163216,-18.462914008098405],[177.64378909424295,-18.242823782159167],[177.32206696844636,-18.076936374631671],[177.26463136443431,-17.968264875871053],[177.3081025043121,-17.531119278868911],[177.2789464084052,-17.372911555680041],[177.18381047256804,-17.163764046985396]]],[[[177.00790426261347,-12.491730240258946],[177.06763600681492,-12.477752517745982],[177.12580303940004,-12.493039397564552],[177.0823640375919,-12.514689861281521],[177.00790426261347,-12.491730240258946]]],[[[174.58828297192261,-21.680458493643325],[174.61537542463668,-21.67191689418879],[174.62859391135623,-21.694727271446087],[174.59373624760696,-21.701467446734028],[174.58828297192261,-21.680458493643325]]],[[[-179.99943407371225,-16.159873039202502],[-179.94554352621387,-16.126966972218746],[-179.93636110920531,-16.294906117863256],[-179.82774382589261,-16.697268701331804],[-179.7460326659868,-16.833158628038724],[-179.58195063367236,-16.996686313614756],[-179.81671525323532,-16.949818158717111],[-179.99908471590709,-16.96186702870612],[-179.99943407371225,-16.159873039202502]]],[[[-179.37746334829782,-17.106543104882491],[-178.97313711968542,-17.168925065696531],[-178.92186534868313,-17.20893821068827],[-178.84224763320796,-17.455557145688854],[-178.70915294668896,-17.647749505532953],[-178.52618082417584,-17.79325999018986],[-178.28096201659167,-17.887294882364991],[-178.25545964530198,-17.930290515254523],[-178.25209977710864,-17.952235331441035],[-178.41946265921581,-18.06528038621601],[-178.52577554714401,-18.178097715157964],[-178.66404022951497,-18.453962414048394],[-178.68761096246234,-18.76163573923824],[-178.65459114283513,-18.913094880134434],[-178.54682029321731,-19.174048934472417],[-178.59519421608744,-19.150569585175941],[-178.74210100959726,-18.976181684368616],[-178.93464106186747,-18.849211826335406],[-179.15540221876932,-18.782447103760276],[-179.40810132630435,-18.785645580199255],[-179.25649413658644,-18.652914634988569],[-179.16661653985989,-18.528322101882534],[-179.08034290743936,-18.315119577499416],[-179.05828165356328,-18.163084769719976],[-179.07796755129027,-17.944153593403144],[-179.05434515516708,-17.722777135069151],[-179.0973518545278,-17.488934524553823],[-179.20718681477706,-17.278059622800281],[-179.37746334829782,-17.106543104882491]]],[[[-178.71515997834246,-20.669310625608908],[-178.72804383934383,-20.659623848543873],[-178.72686684225715,-20.645999179195076],[-178.71084166199515,-20.669558350352471],[-178.71515997834246,-20.669310625608908]]],[[[-179.72470849900063,-18.913329733469912],[-179.83268356873918,-18.991079933472168],[-179.86420054149076,-18.998032550107467],[-179.83074069269256,-18.925237233355613],[-179.72470849900063,-18.913329733469912]]]],"type":"MultiPolygon"},"properties":{"alpha2":"FJ","radius":231000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-61.144617891641047,-51.839581739540222],[-61.018680562862578,-51.786003957075863],[-60.87603898248549,-51.794470666884109],[-60.775182297538571,-51.828384386108645],[-60.66177275728321,-51.800744010522699],[-60.542035774305411,-51.672830901171139],[-60.504400847222982,-51.585149217643206],[-60.511673145996348,-51.490009430837979],[-60.568057057057786,-51.358001353885705],[-60.390783969553041,-51.362483103238048],[-60.275208422905393,-51.28080085933599],[-60.176322271480558,-51.273775964395789],[-60.069959415629548,-51.308102041439845],[-59.976098231276268,-51.37715133641408],[-59.771106855797477,-51.434063780366969],[-59.471239849577103,-51.393678815672409],[-59.387563965000609,-51.360242651745693],[-59.220069478187661,-51.459602711662725],[-59.142351072742535,-51.47854077633643],[-59.063427465950355,-51.465507544602445],[-58.97846358394537,-51.411529685738159],[-58.91252543855375,-51.271912096731739],[-58.850237042958256,-51.270169050561883],[-58.687367585549097,-51.323848046619659],[-58.506261794430699,-51.308363277292663],[-58.430778318486425,-51.323227299290089],[-58.356613761333797,-51.384569272173685],[-58.285968633924249,-51.409020867917114],[-57.976550298930071,-51.38466356820657],[-57.922618139444246,-51.403686969077164],[-57.808754521216535,-51.518057834978102],[-57.792072114492854,-51.636105976797637],[-57.838358070836009,-51.708972000907366],[-58.130287582291388,-51.765844611538093],[-58.35519429864609,-51.879996055796845],[-58.4253480056665,-51.975602305769463],[-58.432965257427661,-52.098695620673787],[-58.540986544308815,-52.070327774581109],[-58.65282134296303,-52.098972264159492],[-58.930308953344422,-52.062768876395403],[-59.019711064476915,-52.108224376814256],[-59.068193001281443,-52.172853320527089],[-59.308982613100589,-52.218271334519557],[-59.395761973878223,-52.307681642444187],[-59.579490496889299,-52.222323802938334],[-59.764262638520464,-52.241988088176321],[-59.82924505536031,-52.061034864677517],[-59.887940146211186,-52.010433246692614],[-59.961464597709536,-51.985942585930843],[-60.194693872836325,-52.004927862415386],[-60.353586938910432,-52.139769871162251],[-60.508486953996957,-52.19446337040052],[-60.686340005422281,-52.188168674506308],[-60.812066381723966,-52.147446060545761],[-60.961213013966031,-52.057169682839813],[-60.99893148651833,-51.976015564339136],[-61.144617891641047,-51.839581739540222]]],"type":"Polygon"},"properties":{"alpha2":"FK","radius":121000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[162.92241399550761,5.317463739115345],[162.93941225379581,5.3263560749388619],[162.95824228559016,5.3339828499657465],[162.97029699878908,5.3301723784420654],[162.98234929877736,5.3249956604610347],[162.98737124254617,5.3060231803354911],[162.99228557603232,5.2786026044495298],[162.97526216165994,5.2839818288824087],[162.94729127251139,5.2943366434519952],[162.93048578455975,5.3014608134538701],[162.92645754960688,5.3084921836563774],[162.92241399550761,5.317463739115345]]],[[[158.12861339197318,6.9048496086782265],[158.13116035060395,6.9245013363667987],[158.13571208469708,6.9441928588548008],[158.15214486643768,6.9559588278079829],[158.16906269192822,6.9667978148617973],[158.18631204620553,6.9766541455547459],[158.20582022488992,6.972896694007737],[158.27460569395768,6.9559944140297851],[158.29399775427649,6.9501550252377795],[158.30641279855382,6.9341544896068044],[158.32357990206663,6.9095053555072647],[158.33389983197199,6.8929924149916744],[158.32462086503222,6.8547145643771952],[158.32520921529303,6.8545719418805966],[158.31409748576638,6.814331869327737],[158.29709262042394,6.8067736135902015],[158.2755590939843,6.7984054760123023],[158.2564203976045,6.7920704214624603],[158.23633898886683,6.7938479586285085],[158.20257504467838,6.798581221832519],[158.18400162811594,6.8021085962776482],[158.13536998981203,6.8938897528885494],[158.12861339197318,6.9048496086782265]]],[[[151.57391698883208,7.3428955365310973],[151.57103598243953,7.3456475347287125],[151.59240855862279,7.377178505048593],[151.60004905399472,7.383889354667339],[151.60601785530648,7.3878876920849397],[151.67768001835728,7.3999018725308732],[151.74961481203721,7.4189073598786903],[151.81947282559307,7.4445136292893546],[151.84539387670091,7.4568556463921016],[151.86556476940589,7.4652482411103351],[151.88174935914196,7.466134758974122],[151.89561508848485,7.4634953080920354],[151.90976055189407,7.4593062431102375],[151.91038372707689,7.4576788895903823],[151.91136736281541,7.454118660563438],[151.89754653348797,7.443388652628701],[151.88096362035836,7.4330172686033071],[151.87108487898624,7.4294119038924116],[151.85704976380794,7.425814717249354],[151.77783549029812,7.401068493893689],[151.70161108785058,7.3682490306200004],[151.65029265109496,7.3395195365518644],[151.63923649552481,7.3340004201580458],[151.61966716712999,7.334651374495615],[151.59923578209461,7.3363485502493431],[151.57876151312317,7.3391364468982516],[151.57391698883208,7.3428955365310973]]],[[[138.06306151611943,9.4456070584634695],[138.07096861284239,9.4648543800076101],[138.07843851583428,9.4806136690199931],[138.09273703027281,9.5079937885535859],[138.10629856290427,9.531684870865309],[138.11781646406129,9.5495280189894611],[138.13116776079616,9.5660441582947371],[138.14749105066451,9.5827315171509397],[138.16614267445212,9.5883862545505014],[138.18539879056331,9.5922438721874084],[138.20322877521596,9.5644106326077321],[138.21239508656743,9.5472878639221204],[138.19393113832473,9.5220143540651616],[138.18194419628873,9.5080248858713841],[138.1675840071529,9.4985355085217584],[138.10630002032522,9.4537202463074461],[138.08305109586107,9.4330669046186859],[138.06768881143887,9.4208041535697973],[138.06499572267847,9.4317386679507358],[138.06306151611943,9.4456070584634695]]]],"type":"MultiPolygon"},"properties":{"alpha2":"FM","radius":20000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-7.422184907720661,62.140051527883578],[-7.3685896766255743,62.139269218145934],[-7.3297520513023757,62.142335257005811],[-7.2922456555698574,62.152872429975403],[-7.2574936337345664,62.170480914106491],[-7.2268146180204207,62.194492572313429],[-7.2013726943304075,62.223996305195676],[-7.1821332321205871,62.257872621833002],[-7.1719905263330848,62.285393675987038],[-6.9631612478681548,62.315610846210767],[-6.9543004589208799,62.314850697326314],[-6.7942197795675634,62.263883340793065],[-6.7520850992964636,62.255576021390048],[-6.7128170420390569,62.254771996187444],[-6.6741490516258022,62.261657727426595],[-6.6555085918048933,62.267912647861387],[-6.6205137804898442,62.285744920804603],[-6.5896787730913884,62.310072458617434],[-6.5762003834285343,62.324387617857788],[-6.5545301221906582,62.355324694525116],[-6.4724955414681284,62.292769619914573],[-6.4063985351768089,62.258550236559266],[-6.4539628569242833,62.186763749055061],[-6.4937105019245616,62.188888998390858],[-6.5331004337975322,62.183325941982496],[-6.5706101472658247,62.170076952395114],[-6.6047556508529075,62.149666197525569],[-6.6341860508408654,62.12290118517533],[-6.6467606220274078,62.10745800311394],[-6.6652695459084903,62.079628212030947],[-6.6816631587026079,62.045059232014644],[-6.6911742309442221,62.008001109765594],[-6.6932297968355945,61.988960188426049],[-6.6918469179123159,61.950726007832053],[-6.6884211534763613,61.931883338843249],[-6.6762577502223612,61.895609153241203],[-6.6676316443497505,61.878510254619748],[-6.6456853864095757,61.847171354156679],[-6.6260476391036036,61.826599986990139],[-6.623452568517898,61.806003292067288],[-6.6429647026295155,61.768481863493889],[-6.6807195422350443,61.736231782220251],[-6.7038795174474322,61.705528107656804],[-6.720734473324689,61.670959183087007],[-6.7306611557006644,61.633803280991337],[-6.7332925006570683,61.595434333817131],[-6.7285312076302812,61.55727112938866],[-6.7165533373431447,61.520724847677371],[-6.7037369523458086,61.495874417459213],[-6.6973172924015332,61.458370195751726],[-6.6800915640654805,61.414593860064301],[-6.7029349450286428,61.417965828077506],[-6.7643502558517099,61.449091571241325],[-6.8838737358182795,61.531776359589777],[-6.8908230411206821,61.539222670833816],[-6.929050588119229,61.603003979055522],[-6.9346069924023102,61.634228551697717],[-6.8971335927290074,61.671269937415929],[-6.8762897027095047,61.704323498977743],[-6.8683873344603947,61.722218168792118],[-6.8579990248301179,61.759888962599668],[-6.8551340890614103,61.798860719374375],[-6.8599018964842839,61.837645686764375],[-6.8721204353729748,61.874763243129138],[-6.8845361383237069,61.899208878323613],[-6.8994415326700045,61.939512727410126],[-6.9199969615445784,61.972442053189752],[-6.9325725053407732,61.987256350633565],[-6.9465263587014308,62.000780317762811],[-6.9780303178750005,62.023459971836637],[-7.0133220328215646,62.039626635334372],[-7.0319770638621319,62.045066996296583],[-7.07042660011758,62.050405471196726],[-7.1793975779233712,62.04026162809842],[-7.2476406491859091,62.045553454705555],[-7.3789666550709665,62.074999530664662],[-7.422184907720661,62.140051527883578]]],"type":"Polygon"},"properties":{"alpha2":"FO","radius":62000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[8.5675334445058198,42.222605112662656],[8.5658948338596943,42.357667585451338],[8.7132769917636228,42.5495617200311],[9.251467214079355,42.773880289362559],[9.3633417532424232,43.01716048342162],[9.4630589113132046,42.980911518193416],[9.5556590708142473,42.156679801379603],[9.4109516197223222,41.933919867457583],[9.3739674090448233,41.678958056425259],[9.1860756580665779,41.385212154874395],[8.8077595682402627,41.588486377816096],[8.6220516289206035,41.930968940834759],[8.6273637684803255,42.075315848552357],[8.5675334445058198,42.222605112662656]]],[[[-4.7622550378316149,48.450231668137604],[-4.7205506037487313,48.539683825132464],[-4.5311169257633042,48.61968992714975],[-4.0588765591178841,48.707350989534618],[-3.7150174786866552,48.711523542641459],[-3.4714157649391595,48.812745778462137],[-3.2369418088225981,48.840181887072802],[-3.0033476456927768,48.790430741100792],[-2.705809994788726,48.566547133732932],[-2.4461297344759467,48.647999044594869],[-2.0351572503603852,48.64185856462624],[-1.9056787166973432,48.696857146209126],[-1.705595798582203,48.660373659065549],[-1.5741668555373232,48.799946337099904],[-1.5817283129807964,49.147253494949958],[-1.8679959257114955,49.591276038899593],[-1.8562912524117052,49.683565103253436],[-1.2588973873368117,49.679992676181826],[-1.2277634637613204,49.493706494652223],[-1.0865238496053453,49.393371663013056],[-0.18238758844820729,49.300729977824339],[-0.024506685572896175,49.328327452125578],[0.083526649041123308,49.413492765949485],[0.18687625731212493,49.702838829979292],[0.61627653040254027,49.862687175769231],[1.2220724164840246,49.991822555318777],[1.4138040678675201,50.097380154637321],[1.5407647428771414,50.256228837472506],[1.5797314073984494,50.73920665685084],[1.6724373337200698,50.884782904315095],[1.912576315255557,50.990328533555271],[2.524731948544729,51.096796664493652],[2.6324100969353355,50.856212491355734],[2.7475435938903998,50.758605599286504],[2.8806524910799318,50.720978971336436],[3.1067793819842682,50.779176416179638],[3.3432998305593724,50.522274261771038],[3.5953246999131885,50.477127583318726],[3.7542547768479531,50.352743359572784],[4.0440701696405812,50.321136884675028],[4.1743587950008179,50.246370815191781],[4.2038916339528321,50.062072594899711],[4.3220832267547298,49.957245240154762],[4.5816783450222944,49.97417008160113],[4.8186406075249586,50.15292007928813],[4.860252846943915,50.13576451904261],[4.8271720030388368,49.954597780083795],[4.897423669767714,49.828382790467622],[5.2786844882050614,49.677712197707876],[5.4770019235995067,49.532559221644568],[5.7896647443868128,49.538056298906326],[6.0038634351596469,49.45707112916736],[6.2421316824415642,49.494126388862902],[6.4619654967302571,49.440428883739443],[6.7270775363887987,49.201906711020307],[6.8964911694941753,49.206504988858136],[7.1897297706781718,49.116395699882233],[7.4504907721425075,49.151951869024991],[7.5940536514388208,49.066630126510624],[8.1346390461858356,48.973393147254967],[8.140102325149547,48.886527002933036],[7.8257277058921906,48.608538840806112],[7.6093807972221938,48.133079946964813],[7.5436455655805323,47.747099103178961],[7.615187066619721,47.592789866508021],[7.4199022742154686,47.45536357192897],[7.0636846697031208,47.397056336217503],[6.6667256706478479,47.026701065021967],[6.4797779354749308,46.931322312865376],[6.3723298925204519,46.657150384846965],[6.4605781325225786,46.470617459138019],[6.7758502867481853,46.406509054591943],[6.8272252722553599,46.138665520615312],[7.0208128338068203,45.925848565014924],[6.9150074043043483,45.820360096371139],[6.9059729734318784,45.666285251510956],[7.0166187683385672,45.503348192566484],[7.153162682581649,45.400859064932568],[7.0781397770125682,45.240118013893301],[6.9325278344561196,45.178512243602256],[6.8633997880167295,45.060106804803432],[6.8776984518669151,44.942811819573038],[6.9924541942604748,44.827147818438497],[7.0303798711070264,44.716750376538819],[6.8908498490263863,44.560263585647718],[6.9140549414619255,44.354392725812573],[6.9960180030172001,44.268767175349879],[7.3029248906687556,44.143876527458119],[7.6370695724159701,44.164630583966009],[7.6768600213442602,44.083172413841908],[7.5181621052387007,43.889580933544366],[7.4929067637125106,43.767319696957117],[7.1818335125683443,43.658453864299503],[6.7411081312325853,43.380301352108766],[6.5701310114478542,43.199226381135759],[6.1159164121068983,43.07259494543068],[5.8095479349781129,43.098188332606156],[5.65997140861157,43.178773578151478],[5.4066642352472263,43.228708697073017],[5.2840115451760523,43.33882302303801],[4.9815552541591908,43.41389448350909],[4.7062613818643868,43.374259974674985],[4.224298022800097,43.479933937859883],[4.0467944945136765,43.574381352909036],[3.9335254077042503,43.564509282722078],[3.2772748373530467,43.201519433431343],[3.0793607510287626,42.956274314307727],[3.045440431831862,42.845685485599581],[3.085999918415518,42.62147314813204],[3.2110687187140767,42.431382163440588],[2.9111444237881066,42.458862166877303],[2.6515451481098844,42.340727029191385],[2.2310820732573631,42.414509973606414],[1.9866242666820146,42.358709841421728],[1.5927068331235363,42.608400125488764],[1.4283764862993173,42.596196150226497],[1.3178018660888744,42.695284696560108],[0.85129024836915956,42.816147328438163],[0.71879905852814074,42.78819268246194],[0.6315347325881322,42.689834684210069],[-0.04113620070991926,42.689339086504667],[-0.29055441502159418,42.81494332458707],[-0.58635466624415744,42.799231138658286],[-0.8000050366777266,42.939676546211246],[-1.2692534138912079,43.049848458494061],[-1.4286821756782442,43.037039393539843],[-1.5345342467251313,43.227394722217682],[-1.7531365145323763,43.32488578623294],[-1.7937877023820725,43.407120690919292],[-1.6074137696531623,43.460086719093901],[-1.4679197118689198,43.619461685460209],[-1.3439576514955303,44.031161813524655],[-1.1879384742507102,45.166566349948667],[-1.0964780126258966,45.501458560128079],[-1.2305375724615288,45.83088826587602],[-1.3701716034689979,45.97243599200349],[-1.3884300974141688,46.050285707978482],[-1.3305950483437414,46.1940965396835],[-1.364941919237558,46.303157390210878],[-1.7863670656949135,46.515050326727355],[-2.0899633173244458,46.860877223638575],[-2.1258219693893752,47.103944575657486],[-2.2887122984526913,47.25904549536375],[-2.5029384198533671,47.312260927284527],[-2.6120600161808842,47.487133240968539],[-3.1668877788590484,47.682410872050582],[-3.8861428441888299,47.834373685269227],[-4.3120153644726846,47.823103387532029],[-4.4535393373810308,47.967476996192765],[-4.6783857549444328,48.039604663223741],[-4.6172584910798884,48.224060465352025],[-4.7622550378316149,48.450231668137604]]]],"type":"MultiPolygon"},"properties":{"alpha2":"FR","radius":570000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[8.7035564537400525,-0.59144880194740879],[8.8746283099872141,-0.66455384940916395],[9.0637793767661865,-0.62071725742110817],[9.278468668148939,-0.37493869786503531],[9.3378181323713783,-0.058211678608413836],[9.3021142387188203,0.28844084924587654],[9.363319518689627,0.41832587797493259],[9.3301956689158665,0.61066882997754468],[9.5739458188023594,0.7393132185176563],[9.5910050087120311,1.0317884592782733],[9.7045997834446975,1.0797630847887549],[9.9214845732037293,0.98957807325251079],[11.133538980676809,1.0003810225797867],[11.275798501102592,1.0591289884270383],[11.333342743151489,1.1818078585568204],[11.348678293689494,2.2994755719340851],[13.220249522009775,2.2562450544643204],[13.27178701337594,2.2192745918671406],[13.288988528670963,2.096230532781453],[13.177863436805284,1.8087021817725502],[13.165092358425124,1.6396141990122752],[13.276760584560074,1.3523501466072565],[13.420191501531152,1.2919431039741074],[13.851386134475216,1.4185239228279036],[14.180765700433716,1.3700211106500835],[14.429581919886553,0.90137591390468164],[14.434279147864316,0.81155531165058892],[14.324001466929301,0.62436366905641427],[14.070701450549937,0.49261079936514529],[13.945588320560988,0.34279032987584818],[13.886411629664813,0.17196940986198445],[13.914403232412713,-0.18358762562775249],[14.102649552391826,-0.29256375147974867],[14.21137841917564,-0.41976503478543969],[14.367936363913353,-0.47198030473766278],[14.479767295538997,-0.61278981549904199],[14.413568664994248,-0.96231885121404526],[14.455374007763655,-1.4131862220741687],[14.422964716465859,-1.7115357094451997],[14.38377117709163,-1.8899339695745236],[14.249956244933697,-2.0353895308750429],[14.199548013588993,-2.3540841318801875],[14.087262920351909,-2.4666684845832179],[13.99384566107164,-2.4903905294761421],[13.887071432234757,-2.4651965276054959],[13.801329113680209,-2.3379698616298241],[13.696373359831743,-2.2914296892845609],[13.464859846068119,-2.3951989283537838],[13.151691542949633,-2.3668304490177188],[12.992172515355815,-2.3131591315312643],[12.831158070640832,-2.0099341886328039],[12.672919472508109,-1.9379798415550442],[12.506864934059015,-2.0335668036769761],[12.44617195126035,-2.3297910550991321],[12.064483556117285,-2.4123084864932034],[11.908159864246974,-2.3566189360375374],[11.714506687107832,-2.4021271077183095],[11.619139747675582,-2.5024726584582582],[11.589144813917068,-2.7373255508838548],[11.759928145325894,-2.9832194001740091],[11.770563686803291,-3.1832798205759625],[11.933919132067427,-3.3186644654339919],[11.862782077819286,-3.4886353678499424],[11.879671093592425,-3.6658150037164634],[11.72795813158454,-3.693104284513987],[11.56337095080092,-3.5767072791440397],[11.424180280293204,-3.5698370227775369],[11.241682391902609,-3.685237919490016],[11.1300869244607,-3.9159286071768409],[10.926187499869378,-3.6405934253117636],[10.640901615200301,-3.3979056162891714],[10.359704478761198,-3.0265096929945194],[9.6248055683005145,-2.3669491793109412],[9.5323064394787203,-2.1645622485494402],[9.2991410083875667,-1.9029123617284209],[9.2276784891557142,-1.6658110075482162],[9.0530429834795765,-1.3790067886152619],[9.0214092121827623,-1.2206233297652398],[8.7035564537400525,-0.59144880194740879]]],"type":"Polygon"},"properties":{"alpha2":"GA","radius":388000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-8.1445372282146629,54.453481019421169],[-7.9714299610759873,54.557345604707038],[-7.906099650038275,54.700672426414954],[-7.5436642941935537,54.781582480127888],[-7.3723125670832506,55.029551424115269],[-7.2186566892972293,55.091723128426757],[-7.0724014328398432,55.081350920213737],[-6.9470496736565615,55.182301245649874],[-6.3217354291314782,55.26591101791773],[-6.0228935740376643,55.152819932588748],[-5.9708193056128627,55.016469208256765],[-5.7170630566401881,54.817332416858214],[-5.4706486621502206,54.500180308979012],[-5.6069390729919455,54.272745173467712],[-5.8025616214230817,54.222286338543832],[-6.0190905962436796,54.051531644426632],[-6.285367434378899,54.092748838479729],[-6.649671858273658,54.058864089121826],[-6.7332392499841704,54.176112482446406],[-6.9800491956310475,54.321208738651109],[-7.0954031608198518,54.294162621372834],[-7.3551865268327425,54.121476352830221],[-7.6019590017644205,54.143090869247665],[-7.8547599293735564,54.215503384706508],[-8.1445372282146629,54.453481019421169]]],[[[-5.7675849552138967,55.348839697253133],[-5.7370809875397963,55.577272307778031],[-5.8456967234094082,55.712930089240338],[-5.9820512391507457,55.7261510106405],[-6.2867734036245455,55.592626846790786],[-6.4927545319826168,55.701950318482147],[-6.4450453965980845,55.832149686464973],[-6.1656764913123778,55.931038469719148],[-6.0839965360221253,56.082753210905537],[-6.1505866278649872,56.241670602835519],[-6.3132664859938732,56.293893115808203],[-6.3054068021506398,56.44785780629234],[-6.3730596559347097,56.566782856042515],[-6.481422715244836,56.613270202206138],[-6.6658310709390376,56.584200553688426],[-6.4377912490991545,56.709257806070688],[-6.3712605085529166,56.80737149115123],[-6.3719577929368034,56.925913249330371],[-6.4322944527488977,57.017939730599807],[-6.3874346877841086,57.176066459116342],[-6.4506426497132647,57.294779751782933],[-6.7410722990549896,57.412673685263741],[-6.7773355444656209,57.564540403232847],[-6.8538894717438135,57.649290381804178],[-7.0211453446371195,57.668153399725931],[-7.2491144975526716,57.452076408211177],[-7.2500393693371326,57.115514002165582],[-7.4170231060430458,56.965682979762335],[-7.5394484047696579,56.964365303928538],[-7.4326984714828441,57.105978310456855],[-7.4081623044028584,57.30730843469312],[-7.4140599621992784,57.481473092922478],[-7.5153498920612654,57.615782984723012],[-7.2440355528157889,57.66898782661351],[-7.1176168203918895,57.747011912473582],[-7.0448994548163357,57.914059691345699],[-7.0850468820804808,58.182011286771434],[-6.8622069282334115,58.233780898906161],[-6.7421502111820244,58.32132585937584],[-6.2375089051216159,58.502546737722341],[-6.1988985344198078,58.363359405282914],[-6.4663269108760444,57.970966697966055],[-6.4867045242641703,57.821625782629567],[-6.4130290185215779,57.711302608097533],[-6.1663019673970449,57.585149403630972],[-6.0768709401319603,57.427389375209415],[-5.9081026554396248,57.396897282849444],[-5.7668777929013961,57.512057357960991],[-5.6652709111029012,57.823368534682459],[-5.4545292061573258,57.958634541937641],[-5.3381322088738248,58.23849579207576],[-5.1286670526059392,58.318865588446748],[-5.0166008084246014,58.566305147833425],[-4.9198589003295448,58.587733159651442],[-4.6615981776745867,58.528256817961058],[-4.4919628974989472,58.568188418627912],[-4.3544262014811972,58.528122716455115],[-3.538926496028171,58.616330159829893],[-3.4074112433864885,58.723378218580422],[-3.3958063066996504,58.904695507074216],[-3.3102069655376436,59.130615177400273],[-3.1305814394833993,59.184242653686354],[-3.0421154303677302,59.333613761934188],[-2.975570721010675,59.346880518465781],[-2.7429844507849239,59.241777751303033],[-2.4074303064406668,59.297367047516602],[-2.7323298749276557,59.157527705617731],[-2.762723898366811,58.955828919886869],[-3.0281522784397574,58.656845348417761],[-3.1369006169997142,58.378455543264941],[-3.8193001998900362,58.012904996850224],[-3.8659541093164607,57.909367153726734],[-3.8425936204956157,57.774801120340122],[-3.6783665734657069,57.66031114307313],[-3.3981911900304582,57.708332732787014],[-3.0572936569214795,57.672834538768328],[-2.0740818394816869,57.702198982588818],[-1.8641172085432383,57.608023738326445],[-1.7808632711799279,57.47412252432742],[-2.0132187386850839,57.265315005729803],[-2.2604104523436654,56.8634785288952],[-2.6514128848864824,56.490233040282753],[-2.6612537821101609,56.116304457646436],[-2.5517329955739783,56.015159614699854],[-2.1471601044658928,55.902792303217787],[-1.6555809185129815,55.570156064626126],[-1.4192794442475947,55.019901715232869],[-1.2230009027285171,54.698998629670776],[-0.67152689080055139,54.503655755986351],[-0.084747477915200362,54.118006980452719],[-0.1371452617889892,54.015795601670717],[-0.12550038459585297,53.903568852727446],[0.11508432238835389,53.609219710294845],[0.13865060033405333,53.470393912301219],[0.27082089715413132,53.335368794520427],[0.42687957364755991,53.014028653857366],[0.5312029724886963,52.9686861206035],[1.2656127090015035,52.925466347965219],[1.660109842461956,52.749368943373426],[1.7174136639988775,52.672533954111564],[1.7463695421805738,52.469037538422896],[1.5912398416473401,52.119899670836226],[1.3325445011310466,51.947712832917553],[1.2741874496491108,51.845535537644679],[0.99432574420641862,51.743587664313679],[0.91470374430759738,51.607886732188071],[0.93193699848275979,51.475707832252702],[1.0284028445288225,51.383715510919842],[1.4147065136430843,51.363099851945648],[1.3973620903737751,51.182140883637089],[1.057395921296731,51.046920828776607],[0.9600122906786005,50.926102373677935],[0.77411952811319851,50.9264851897086],[0.29963046038712088,50.776279453307517],[-0.21811132264638622,50.814113847656721],[-0.9313413070217591,50.773309898463459],[-1.2514842402775819,50.589020835698477],[-1.6944887033252294,50.730649341797431],[-1.8547310134375237,50.712261346938085],[-2.0358434757856707,50.603430831332119],[-2.3188601764549599,50.633128438358149],[-2.438601523816843,50.599989356735286],[-2.7770890777709583,50.704943403925668],[-3.0137289044530116,50.713627497963103],[-3.417065411182802,50.613577343449911],[-3.5845324041795075,50.321954034493892],[-3.6798913967470566,50.24019159475322],[-3.793312007249658,50.229485393897882],[-4.2322182711301526,50.373711274434598],[-4.7412942714461437,50.285336932137547],[-5.0134923248258714,50.157739094350006],[-5.1186391664912962,50.038547685058347],[-5.2251882433658254,50.021658732098501],[-5.4340073689416881,50.100998369576061],[-5.6220837316175576,50.050971700315259],[-5.6560317940734253,50.131779224515981],[-5.575014101838474,50.193657413799087],[-5.3120453531141028,50.265386758737236],[-4.6183073109028401,50.752841347205397],[-4.5229161602194052,50.977247831803908],[-4.3052236584776837,51.039982321968054],[-4.0791702092555626,51.30492295802334],[-4.1055055420037352,51.489284706465597],[-4.3715247471991461,51.726560092663426],[-4.5725459581582557,51.74188476285439],[-4.902290276755819,51.626490111906143],[-5.1246067810145268,51.706126503235218],[-5.2619445500974678,51.880200369088662],[-5.0879716404859048,51.995568904392719],[-4.3654724928920379,52.205878750744837],[-4.1749692220835772,52.307980968601015],[-4.0772985102528736,52.432264304214371],[-4.0734219578610515,52.733037302573415],[-4.178389199933207,52.876667552467694],[-4.3391693016282309,52.89950617129886],[-4.6828082601172882,52.806412376965731],[-4.4824804612044069,53.054677338539925],[-4.5676142281229151,53.386307182326803],[-4.3151683197310797,53.417014143738264],[-4.1784300191671937,53.31834866970398],[-3.9461989464741585,53.273806453792837],[-3.260808993925659,53.348354791911035],[-3.0901544930743121,53.466505044482403],[-3.0198056219208049,53.71382812674964],[-3.052210098148104,54.035929492131977],[-3.5691982163186697,54.467680831322177],[-3.581692128417886,54.704473843820878],[-3.6780921908903195,54.82373895272675],[-3.7920468193110217,54.847132703090693],[-3.9579607968197195,54.781258183202588],[-4.1329097867985256,54.779526115062914],[-4.2832435970386289,54.828645852662433],[-4.5174840913730554,54.758596706736725],[-4.7446700255647913,54.805087827866643],[-4.9113262644562878,54.689772113154383],[-5.1698893118041012,54.917985264425624],[-5.1724694508855551,54.985745383518037],[-5.023464677831682,55.062980356329376],[-4.9460359835358521,55.195301540850465],[-4.9485242872280004,55.310251553535053],[-5.0138955736270283,55.404836393683922],[-5.3967471828525282,55.485820306502902],[-5.6185648492829969,55.331717538812271],[-5.7675849552138967,55.348839697253133]]],[[[-1.6602666138729454,60.284735364471686],[-1.5880166591601448,60.364795312275639],[-1.5525184163464214,60.517238786505253],[-1.4141091615364703,60.59852376647396],[-1.1939135987617608,60.62330885630557],[-0.91567627204159163,60.810205670168337],[-0.77449494640548067,60.811845158590415],[-0.82567285451122352,60.684162948147019],[-0.92783050213491769,60.663908775362195],[-1.0129667847911246,60.584384551972882],[-1.1995604016662578,60.006741452962459],[-1.2839562523413324,59.887072056916473],[-1.3555692564562354,59.911228269968383],[-1.3466304861496661,60.024834067268706],[-1.4006976279850656,60.124389917080705],[-1.6412209433006493,60.236966608228364],[-1.6602666138729454,60.284735364471686]]]],"type":"MultiPolygon"},"properties":{"alpha2":"GB","radius":543000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-61.781734454958773,12.008693428957018],[-61.772152175216604,12.02530498234006],[-61.764226930605815,12.042702723340851],[-61.757999027087656,12.060777681482982],[-61.753525370712744,12.079364700951162],[-61.749600451062534,12.108366977762419],[-61.747593392682965,12.113520499368065],[-61.714801111164057,12.185021555504429],[-61.664115593032818,12.233525873191924],[-61.660377560339853,12.236736112863403],[-61.655549279535556,12.235753142041354],[-61.607273083171783,12.22312536805893],[-61.626549418930523,12.058997302394285],[-61.627381122760283,12.054123277444162],[-61.631748447424748,12.05180501823752],[-61.715582049840215,12.012860662702408],[-61.781734454958773,12.008693428957018]]],"type":"Polygon"},"properties":{"alpha2":"GD","radius":17000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[39.978675164386033,43.419913479247064],[40.084701601857589,43.552908756386834],[40.150208235170162,43.569537579605779],[40.648024732216008,43.533626739676997],[41.068601853302866,43.378944457612647],[41.35813737476844,43.33320525995336],[41.600220755217727,43.218936029593394],[42.039987385554781,43.191727467897593],[42.413896689075642,43.224361201329458],[42.576668064901419,43.159273568651223],[42.765242074920103,43.168266742065278],[42.991421245797589,43.091320932494583],[43.097560543275485,42.98683078351452],[43.78246314776576,42.746805034575559],[43.951225927779461,42.599288524258753],[44.505881389414199,42.748456452458768],[44.731568146366961,42.711733726210213],[44.871007555272122,42.75614248651835],[45.16016735094918,42.674770601432378],[45.366545025891277,42.538170241232045],[45.655507911919372,42.517475297983246],[45.727267360935244,42.474987084520784],[45.684256702962642,42.294771592780577],[45.756401109661248,42.156358129055342],[45.974339083601848,42.030723138163737],[46.212640704204929,41.989692491610079],[46.429584735825131,41.89091665040521],[46.293208537343084,41.715898073964695],[46.291433552845973,41.563927051302379],[46.363045993965869,41.473606152371772],[46.618786400474072,41.343546675855059],[46.662257146577858,41.245555811299418],[46.622288244365798,41.156515664501896],[46.457911677240809,41.070453535519476],[46.187782117593095,41.187550799175526],[46.031237251985679,41.167506668431585],[45.798084500193205,41.222864500746731],[45.600700648056993,41.370919731965522],[45.297966716105158,41.442363582399409],[44.841307599469438,41.211609229060223],[44.20610908981029,41.210714214188584],[43.439550454131393,41.107376216983816],[43.205582552352674,41.199438012306288],[42.93549378799019,41.445045667982683],[42.717722127532049,41.553204290694843],[42.585730055985231,41.537801018391818],[42.466346997122479,41.440079961937464],[41.983775627498332,41.49505795366165],[41.823493859115416,41.432592789967266],[41.514779879287843,41.516048830771197],[41.705772602040966,41.71583629038205],[41.760934037838112,41.936286774479591],[41.462125718878688,42.689422421905697],[41.128864238206859,42.828327917177894],[41.05109810324948,42.933148148086715],[40.851660325148067,43.053721726580676],[40.466096953190572,43.144115012234828],[39.978675164386033,43.419913479247064]]],"type":"Polygon"},"properties":{"alpha2":"GE","radius":323000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-54.615841433986482,2.3267943548801799],[-54.394936222893065,2.4805141775947295],[-54.206859330754163,2.8005534792526059],[-54.174244522257951,2.9751853617925152],[-54.202887884706925,3.1381675165582705],[-54.007467562646383,3.496244845496062],[-54.10102105523633,3.7432079801409981],[-54.35050968713896,4.0541533527255753],[-54.448851589127216,4.4801773278461772],[-54.473134094950602,4.9146414630445046],[-54.335361009562575,5.1820349845714091],[-54.081563506791163,5.433125802769335],[-53.919800613128984,5.7687980214787276],[-53.847209219284984,5.7819775209771516],[-53.453214476868375,5.5673289174483926],[-52.899477497218662,5.4248013460810496],[-52.0195390148059,4.6763647440518552],[-51.82765437953077,4.6354725444943918],[-51.666031937899362,4.2287369023677712],[-51.652805984407223,4.0613941581600947],[-51.782779711802519,3.9648788388401188],[-52.321984074007673,3.1819730123554519],[-52.55974731057718,2.5732224781151127],[-52.700805679940025,2.3638687884021521],[-52.964898704000781,2.1837754938627456],[-53.229679480511727,2.2051488875704672],[-53.353206884359324,2.2798111650415795],[-53.509004947172578,2.2533660966284672],[-53.787263110012091,2.3089567584102633],[-54.130106071061213,2.1212922047992926],[-54.436278826018167,2.208988083124551],[-54.615841433986482,2.3267943548801799]]],"type":"Polygon"},"properties":{"alpha2":"GF","radius":246000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-2.6458252829427709,49.468073859355748],[-2.6414266854713393,49.469953938828745],[-2.5464968263541361,49.505000847831155],[-2.5421514875945062,49.506381943677802],[-2.5375943075263976,49.506528587804262],[-2.5260450222573305,49.506369652685876],[-2.5210310417506352,49.506042176617825],[-2.5178628770513733,49.502142180010701],[-2.5153082707986556,49.498644111221481],[-2.5126008074928392,49.49451028693921],[-2.5146989002852229,49.49003626555082],[-2.5449774934430631,49.433190104580433],[-2.5474917326469275,49.428997207124006],[-2.552295371378456,49.429906516087343],[-2.6344363648910765,49.449818158094942],[-2.6388553660461014,49.451112389146459],[-2.6408094291110227,49.455281830687049],[-2.6442259018561924,49.46356560706915],[-2.6458252829427709,49.468073859355748]]],"type":"Polygon"},"properties":{"alpha2":"GG","radius":6000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-3.2436751800770112,6.6486359415254137],[-3.2335111150178659,6.8117846995531979],[-2.9967597386374996,7.1910773657691722],[-2.8973866824568444,7.6811573940713664],[-2.7895273082596796,7.9317904177917482],[-2.6552793655989912,8.0427279796698254],[-2.5287677651627032,8.2870376149112275],[-2.5993207430667886,8.7793968798672317],[-2.6590574951356758,8.9614207387353524],[-2.746703451143865,9.0452575528106696],[-2.6915830336255508,9.4534753323168133],[-2.7796962075571274,9.7408938231970392],[-2.7505861621369325,9.8919397489202936],[-2.7903167198031746,10.248098339359325],[-2.9146771673387248,10.592372325572706],[-2.8297718927116424,10.998144969948891],[-1.5367491913625755,11.02245995244049],[-0.60861727980842095,10.970751706436005],[-0.29937254941559432,11.166649063576926],[-0.068741051964498132,11.115365024039274],[0.0091959070299663815,11.020971959150533],[-0.065212000370401119,10.759607986108605],[-0.042731466097198939,10.647903416780103],[0.38061254084878565,10.291681132150334],[0.32582558294093622,9.6647594298024497],[0.52857745228673514,9.3634621284820039],[0.43569561570454429,8.6846566865705146],[0.68610471907957715,8.3548201720405153],[0.59097163629957594,8.1549739512621411],[0.60494426760411002,7.728278566322599],[0.52704893710645506,7.5611769524361288],[0.54898673650660912,7.4434037186198587],[0.63450102469449221,7.3535541800432371],[0.5469663291147091,6.827227096222769],[0.76461474492075276,6.4355911693624881],[0.98479784991136854,6.32011146591495],[1.0563403628935446,6.2009175773800598],[1.184819602758165,6.1448197698954701],[1.1869695163779561,6.0895492950731231],[1.0586418487019147,6.0010520205613851],[0.94955700253630337,5.8104574597762202],[0.74878339122097581,5.7604638417698935],[0.23507771235609845,5.7443793650550816],[-0.34715398402686221,5.4977397505151524],[-0.79778655958470135,5.2269057744412262],[-1.4944336386202783,5.0403788329678481],[-1.997417278741858,4.7647771764040039],[-2.090100779876459,4.76434848083563],[-2.4125440834192671,4.9328908110111778],[-3.1136270667013481,5.0888301625576879],[-2.8518830942607587,5.1832890068200879],[-2.7603390030696806,5.4401616230266896],[-2.8112491313878833,5.5715768075139893],[-2.9620704423090887,5.643230914843115],[-2.9980260565921966,5.7114612934563027],[-3.201808535696042,6.3530841857722571],[-3.2436751800770112,6.6486359415254137]]],"type":"Polygon"},"properties":{"alpha2":"GH","radius":409000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-5.3677700952042819,36.108829904795719],[-5.3680000000000003,36.113509724421782],[-5.3680000000000003,36.150090275578222],[-5.3677700952042819,36.154770095204285],[-5.3630902755782586,36.155000000000001],[-5.3409097244217412,36.155000000000001],[-5.3362299047957187,36.154770095204285],[-5.3360000000000003,36.150090275578222],[-5.3360000000000003,36.113509724421782],[-5.3362299047957187,36.108829904795719],[-5.3409097244217421,36.108600000000003],[-5.3630902755782603,36.108600000000003],[-5.3677700952042819,36.108829904795719]]],"type":"Polygon"},"properties":{"alpha2":"GI","radius":3000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-72.806218834426488,78.209873302529886],[-72.679056096829299,78.39856039887124],[-72.395485876167413,78.503773263195384],[-71.532666388874901,78.636978010358305],[-70.857102204560888,78.644038097444351],[-69.386061169793507,78.825480735191221],[-68.840464933640277,78.977783440851169],[-67.679357683316525,79.103751845927647],[-67.407092187041599,79.251594968123726],[-67.21141525542123,79.491796191422338],[-67.117757603131267,79.866147652048028],[-67.192260992351294,80.279536323300221],[-66.995571749488363,80.412185606486517],[-66.383011108448642,80.581723564959034],[-65.600828931287381,80.69765398367646],[-64.534815375210059,80.996323070420104],[-63.49620894410603,81.05378426436485],[-62.932002182065254,81.214735529994286],[-61.825225411537247,81.186722139066191],[-61.480095426025201,81.350973964341463],[-61.20230360497974,81.746079176130493],[-60.822429736679325,81.858597652669062],[-60.114160122665631,81.936594726291105],[-59.573646152431778,81.911416979406681],[-58.700953949312293,82.094301978635158],[-56.602947894778673,82.226439149722239],[-55.717285783730055,82.239496322373739],[-54.708314436895499,82.35129450693718],[-54.263205680099063,82.323836697347303],[-53.487140231334209,82.15930323884244],[-53.249970008416454,82.200779721634333],[-53.022238174781556,82.320814676110928],[-52.775745951925153,82.320367088898351],[-51.700858055020525,82.081116613817812],[-51.323042708858317,82.15943789209912],[-51.129980630621304,82.289851720821403],[-50.989389549820771,82.459218603165482],[-50.836198782986536,82.472711163361041],[-48.880359567039605,82.406512481566125],[-47.94750008444403,82.281688294104214],[-47.631248589697066,82.365450687696452],[-47.271921450802083,82.655911026053502],[-46.853528168982315,82.697484925078683],[-46.646769217792489,82.797407139969536],[-46.477637435153788,82.951104149063298],[-46.168907720259419,83.063073888299201],[-45.376124680328083,83.029594454900263],[-43.183291473024696,83.255704025427917],[-42.275135738364256,83.232793571140732],[-41.726808115250734,83.135662497808298],[-41.301132186241624,83.114859923607185],[-40.356764364849596,83.33073465455297],[-39.146170272970046,83.208852218712309],[-38.648107353394465,83.400389205534793],[-38.128514501701765,83.407286319800647],[-37.701275099494801,83.497881500805775],[-36.958563989264597,83.467072160651668],[-36.585831844020994,83.536992599175093],[-34.681060653589348,83.571012288075977],[-33.878499225470556,83.529796325841673],[-32.973084292115885,83.599578553789286],[-29.948018904690699,83.56453986239076],[-25.968522664021034,83.291343009468079],[-25.490219341093681,83.152433864568565],[-25.123622162315495,83.158488296583414],[-24.467655758661849,82.894533727612171],[-22.539632523953067,82.789836974760249],[-21.692021283034556,82.68174511525477],[-21.521534278893743,82.594894706260746],[-21.391507889297003,82.208713155946413],[-20.958582520787061,81.695968429525266],[-20.764848672358553,81.560175431981065],[-20.461287683721338,81.477775696229529],[-20.225482671515671,81.496973050933832],[-19.979799101629162,81.593298370606277],[-19.743313986775171,81.803555358906266],[-19.609828467936946,82.077425716969898],[-19.336306779689608,82.122409200081648],[-18.813343061213203,81.948251204098526],[-18.334485303476605,81.538462160005565],[-17.92141893084089,81.438697095454529],[-17.300246346329025,81.419883023437208],[-16.288036291718289,81.748217735685188],[-15.470640544968184,81.836320246875175],[-13.713604659455914,81.789483024686973],[-12.447440798929712,81.683460282182537],[-11.426701842745656,81.479824323017979],[-11.528963564057642,81.424650432603869],[-13.114004781154712,81.090451606882013],[-13.920875987784925,80.997271812405316],[-14.453251912730613,80.772181915485064],[-15.137998193146279,80.724842952707775],[-15.559065198112849,80.643023708883135],[-15.773003190741429,80.539815552492541],[-15.933121461739006,80.395920586060683],[-17.034994040385417,80.16885880026939],[-17.612999155818748,79.827127367294892],[-18.035947930778143,79.712089130583507],[-18.446355458667803,79.68358566949037],[-18.657356170367894,79.589116473097718],[-18.832533502822209,79.43825683104852],[-19.016621837849655,79.102407506808163],[-19.057947520817702,78.86338851164038],[-19.025739133666406,78.622971141384198],[-18.826180374804185,78.177167687932425],[-18.608614142849138,77.957735763023919],[-18.399308905312541,77.856744973164979],[-18.16980882467821,77.82017264639434],[-17.796820726078387,77.872578648615544],[-17.682028247822046,77.857747774919076],[-17.642471857138275,77.782681564439613],[-18.042788547703484,77.531849082937839],[-18.232372460511147,77.274387206531671],[-18.338123091009212,76.921740411222473],[-18.585818680186048,76.538703084751504],[-18.620972785492253,76.295538254053412],[-18.566795137308858,75.967603621056213],[-18.467919416104138,75.746557612240309],[-18.307435480852938,75.565221866223467],[-17.788333907215289,75.241337400615905],[-17.498298484054374,75.150678471735631],[-17.393738479541092,75.03745554638536],[-17.586103104874688,74.993448483766102],[-18.488298129946937,75.006712521250705],[-18.796109786905166,74.935807527768688],[-19.103581363920679,74.690437426137137],[-19.315438343848914,74.307608459987918],[-19.848770747453145,74.227430946136096],[-20.115505565431668,74.091039854508679],[-20.272082513251426,73.929081897754486],[-20.510368681115189,73.493674125756002],[-21.375738232583743,73.439947980506687],[-21.600973959649771,73.35446557214776],[-21.790548291663097,73.20580506115877],[-21.982697186450338,72.856344766948553],[-22.096194727248928,72.112225242119138],[-22.005855308112007,71.818815693332823],[-21.703961445318104,71.353049439545131],[-21.665459917269796,70.942068889122169],[-21.523840468582438,70.526573490257107],[-21.996071330796461,70.328596886180321],[-22.287422179594717,70.034150561193471],[-22.787702680982051,69.919794219633417],[-23.049761617185531,69.79389673656388],[-23.42023424819828,69.757937698025316],[-23.739856884981307,69.589563229210441],[-24.093761250343782,69.528707428702461],[-24.849780471483914,69.291728804889715],[-25.698419395839498,68.890944836573325],[-26.364201311896743,68.697924507811067],[-29.249582071872542,68.299326690540582],[-29.929449642022369,68.2976107101248],[-30.195793777125704,68.200062714501811],[-30.869111843822004,68.071119942966448],[-31.582662211311483,68.162812344832773],[-31.901776923670525,68.131235323705198],[-32.354042918904852,67.889318269725052],[-33.122652647873814,67.618352061673065],[-34.12480142791609,66.708952008799969],[-34.633392852233065,66.435956001878296],[-35.092211825063025,66.274703673708871],[-35.460938142221593,66.215880090598702],[-36.379354980058622,65.831732429871565],[-36.660150839454637,65.788524808741982],[-37.031626713663258,65.53289738121596],[-37.512821201017239,65.619148259523428],[-37.754248025857699,65.594068853771219],[-38.253549817001961,65.687801691968957],[-39.288902134917514,65.550755349556184],[-39.855566738223857,65.181988876700217],[-40.062232696165189,64.960535417518216],[-40.154604842214944,64.752322956267008],[-40.183228430417209,64.480470373602742],[-40.362241729073787,64.325259487526537],[-40.483995360964983,64.13348415229386],[-40.55154365471256,63.725596913007813],[-41.275557911853426,63.131752254849523],[-41.835138992032775,62.798394830843193],[-42.020579275699987,62.602288056282532],[-42.151446458222473,62.248054027828751],[-42.111283133325266,61.857613688572144],[-42.436479213854817,61.487463404906464],[-42.640725498006859,61.062168044708521],[-42.717902232598632,60.768047974707898],[-43.010032947925531,60.473202289788915],[-43.123907355472532,60.061786279701991],[-43.320400484928278,59.92902814780453],[-43.906653010380218,59.816716178629001],[-44.383255278831655,59.900118763326709],[-44.636818226720884,60.017177634348144],[-45.378607501536031,60.203754866784145],[-45.676216972924671,60.490139052062304],[-46.170681661727663,60.650017394594869],[-46.992918406072477,60.800925383928323],[-47.471253424425591,60.796139740721493],[-47.834595845716606,60.724656129448924],[-48.180370950552025,60.770145961958846],[-48.531835069244913,61.136089822197725],[-48.921611969293991,61.278443334690422],[-49.063680531168117,61.446161939889464],[-49.288326803218197,61.590599413240078],[-49.423034288840356,61.822124948236159],[-49.864807143051856,62.249225149428213],[-50.318433225041524,62.473917868520914],[-50.385987709057197,62.685389698085352],[-50.521422342114164,62.884000744126496],[-51.468103728551917,63.64296357675336],[-51.687428947692226,64.028493060819969],[-52.062159071588546,64.346676291843181],[-52.133544665609982,64.785604128563492],[-52.279100976033561,65.052978119146289],[-52.722257666324239,65.466092047105292],[-53.198157275983014,65.594707080482621],[-53.32827188191628,65.902604880674303],[-53.613864690692075,66.155156834506727],[-53.662143263366453,66.79137565864842],[-53.883108692743185,67.136194851034759],[-53.797663305129618,67.417804931002792],[-53.408021938788323,68.152312069987332],[-53.34668125971772,68.568882299644045],[-53.459562554094092,68.948340353628367],[-53.740563578288914,69.227211337696474],[-54.120622322401012,69.365239349244291],[-54.373088464635615,69.52659103506879],[-54.781757663258738,69.624140289367347],[-54.91794060309217,69.713891450693865],[-54.829582765489292,70.160860330578203],[-54.685168370408178,70.523171222251619],[-54.685995387517252,70.761064559960033],[-54.756741499950024,70.988196485630979],[-55.010458894400699,71.289470240012605],[-55.469924790051095,71.484095701464454],[-55.666931418915638,71.62714099778367],[-55.635055016457592,71.964383692325725],[-55.708886174374975,72.262797028127849],[-55.88979948170973,72.511336630793465],[-56.213377019391416,72.719361521084068],[-56.088730004931968,72.930048850696835],[-56.031517602540845,73.239650722002722],[-56.070155051981018,73.47328811387905],[-56.210965592797379,73.786552362443004],[-56.367470699216177,73.956566487406036],[-56.638790788961394,74.100787721618019],[-57.229456205411665,74.126049622354714],[-57.225674905356286,74.383243645278469],[-57.292653302856856,74.606480579349181],[-57.421132367761935,74.80093870940847],[-57.60020727192007,74.950113148122995],[-58.20020530737888,75.251518046312469],[-58.565106481361369,75.353561439147811],[-58.758819298136075,75.583978591773743],[-59.040093437231441,75.752120827295286],[-60.148085829355402,75.988054196334602],[-61.337045261521219,76.175503886711738],[-62.757720636801714,76.253876384327867],[-63.283496271343495,76.34629660801923],[-63.862851315615586,76.215754881378132],[-64.38941299692182,76.281882010330833],[-65.387505039935363,76.1304181502219],[-66.043075370574726,76.228828358454805],[-66.435424215935512,76.129027610300966],[-66.675218567658447,75.978353553831894],[-66.800032816583894,75.970282852086697],[-68.336645692535257,76.095498966850769],[-69.394333733676376,76.341590430403002],[-69.721288459428749,76.683471069497756],[-69.993030393247665,76.814606928632188],[-70.712529145739225,76.84026384951467],[-71.140918723997089,77.029416426751681],[-71.334242976647445,77.199732386246481],[-71.548213712838731,77.294062031485637],[-72.493950276917346,77.386218604727219],[-72.526502974114393,77.782836284357288],[-72.806218834426488,78.209873302529886]]],"type":"Polygon"},"properties":{"alpha2":"GL","radius":1617000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-16.824531781223207,13.341003054035035],[-16.75023067365855,13.425200143174118],[-16.607505253554457,13.51698006925875],[-16.562136974359277,13.587074733096561],[-16.306186375967176,13.596840894418719],[-15.587709343143294,13.590894618568639],[-15.501108267322923,13.629295405691529],[-15.42670362482699,13.726824405699343],[-15.269443313488422,13.788779250369283],[-15.108339677757066,13.811822017896546],[-14.935877842053529,13.785008080992469],[-14.780548152199751,13.67998388301468],[-14.570995259697284,13.615899222043224],[-14.487673882842337,13.549064130078239],[-14.374058972447743,13.497762842789438],[-14.307240256153703,13.492734522902754],[-14.14693315719729,13.5359190730639],[-13.977453300086067,13.543183642252306],[-13.852999785586576,13.478416695874051],[-13.826996998751651,13.407801791419523],[-13.847691346079932,13.335493806076443],[-14.246789509658269,13.236126841748057],[-14.44258822107226,13.270320997440193],[-14.675134248861031,13.35310836905829],[-15.001871143559017,13.499730863693225],[-15.099709571931255,13.514643176718472],[-15.19271903258046,13.480820471683888],[-15.286318062199115,13.396211935400304],[-15.650736983654788,13.356582173467945],[-15.724503690496412,13.334702772166905],[-15.807557400138327,13.254272704733259],[-15.834479918945254,13.156675361464348],[-16.609078081271676,13.153867343660266],[-16.679770030032252,13.132536073567392],[-16.763145047945628,13.064635464425999],[-16.77041958216974,13.148293867974251],[-16.824531781223207,13.341003054035035]]],"type":"Polygon"},"properties":{"alpha2":"GM","radius":164000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-15.050974149181638,10.834679188916793],[-15.04275866634446,10.940030403758103],[-14.716267640016385,11.484778935806387],[-14.265524829221292,11.659655671282129],[-13.953498920425519,11.666005715845493],[-13.77255933214585,11.758456624874709],[-13.746246252833918,11.969789490798227],[-13.947514020729679,12.210500880876257],[-13.706266909574516,12.377014084172691],[-13.729029268437412,12.673689336479862],[-13.082996880087826,12.633335618574799],[-12.79539761377265,12.456071997754607],[-12.351519851214505,12.334770803092452],[-12.038411609879532,12.398183030518405],[-11.394307922470174,12.405354631982911],[-11.454322404288112,12.232644034256232],[-11.348893427137789,12.078679052972332],[-11.198167008414719,12.064048124220109],[-11.004482244422872,12.20729267555995],[-10.933308908471368,12.204920117823415],[-10.791773043735366,12.040784344016137],[-10.679327731856796,12.002082250646358],[-10.274851803966039,12.2124245167221],[-9.8758742723702895,12.065028701372627],[-9.7582978306599184,12.068151542547994],[-9.4427353290019376,12.265633391235989],[-9.3951095778108904,12.464503832958181],[-9.2116582790446522,12.481521960065731],[-8.999119967802546,12.345786741375544],[-8.8185486026960636,11.92245411772422],[-8.7867704412852383,11.691666100914254],[-8.6384772129275103,11.512726271529063],[-8.4076346958817005,11.386109259008023],[-8.4660379968511243,11.154392848349236],[-8.3130134493880341,10.949663386412947],[-8.2738120134023934,10.517210723397046],[-7.9860191016813342,10.27830149453],[-7.9908811102739952,10.162606518783416],[-8.1445385380897282,9.9531024916683197],[-8.1402753657812426,9.580728548721023],[-8.0588688937780155,9.4480461395652569],[-7.8964333051134492,9.4155898700135925],[-7.890803453712615,9.2336885891419129],[-7.7783049637952706,9.0809475986335286],[-7.8684182611379772,8.9971041614540344],[-7.8911969082027342,8.8653701882603304],[-7.7197495425214502,8.6429211517748108],[-7.6814216083169535,8.4103894728773749],[-7.6962432686580611,8.3758106920762945],[-7.8927662533989045,8.3815636538838767],[-7.9957604186999696,8.2950805897629767],[-8.01014133259382,8.0785602479677063],[-8.2319743003952919,7.5570098447764718],[-8.6488312482831109,7.5671354816687657],[-8.7765385901977595,7.4573773728004982],[-8.8897900669686862,7.2629597522133507],[-9.1174646143189371,7.2161631420683179],[-9.2616401777470418,7.3697423753368536],[-9.4635649526994143,7.4159875556157679],[-9.3724838664996923,7.7125432556425535],[-9.5301970557746873,8.2972642860895061],[-9.6231420383205482,8.4134315384771288],[-9.8392243216119084,8.4910302981206129],[-10.064296086330446,8.4301558982370626],[-10.16968349711922,8.4870192825994764],[-10.31948150532884,8.4848488399396],[-10.557794795872752,8.3159485766915306],[-10.711813164642296,8.3353695596209114],[-10.5434440152305,8.6989373901488189],[-10.631328284996,8.9959835492475335],[-10.746805229039749,9.0953941891516017],[-10.721953759541432,9.3200939764106643],[-11.042814137287774,9.7778923861719491],[-11.238223701919301,9.9719411269796652],[-11.790251156079606,9.9928238437882637],[-12.142312556281157,9.8756427787271512],[-12.278519705005303,9.9208037783677145],[-12.417292391766541,9.8965070342419459],[-12.505966789084471,9.8224271146799946],[-12.75613343798204,9.3738114575822244],[-12.941678114838144,9.2456281941426024],[-13.028182169793213,9.1037181358737396],[-13.129898002272883,9.0477936511935937],[-13.292531212207747,9.0495187454503352],[-13.31132918883489,9.2112252782971211],[-13.438679936487505,9.418495870659072],[-13.55550631482761,9.5189189430063141],[-13.69104282106777,9.5360333419385359],[-13.658981481479611,9.7127727067742615],[-13.720885440002638,9.8362540248736074],[-13.954450938532574,9.9689215211746998],[-14.080001789422651,10.100893905759381],[-14.426738225312478,10.248524889570414],[-14.743735801621185,10.788985693484156],[-15.050974149181638,10.834679188916793]]],"type":"Polygon"},"properties":{"alpha2":"GN","radius":466000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-61.793870216132376,16.300925303980463],[-61.766939775837152,16.34029130408577],[-61.747990589949751,16.354989155934646],[-61.701265642331691,16.348740713432726],[-61.661653586232305,16.3519869629326],[-61.623465612563869,16.363002034032469],[-61.588209815124223,16.381350926945426],[-61.557278492177694,16.406309019521057],[-61.543824278352439,16.420967702309298],[-61.521603231447159,16.453920368342104],[-61.510479072483832,16.477555547351876],[-61.4711778478109,16.506365838464067],[-61.410513009470726,16.470718648022867],[-61.406642146244458,16.468172706590458],[-61.400071454605211,16.444882777418258],[-61.383075135529765,16.408295431202813],[-61.371898457923535,16.391472855698701],[-61.344756799060484,16.361625927559192],[-61.312177521827074,16.337833082833271],[-61.173036612010137,16.256097832227667],[-61.204128171377334,16.230036785066392],[-61.228309678317174,16.199820145331728],[-61.246218766028996,16.165511907661067],[-61.252601663747654,16.147220196143049],[-61.259925268785423,16.109218151408761],[-61.259792616243899,16.070517079097527],[-61.252208673266324,16.032566131762195],[-61.237457418814252,15.996786373702276],[-61.212542357037719,15.959819857273036],[-61.203652959838081,15.921298444409299],[-61.230584107327793,15.890171914460716],[-61.286195019771384,15.886297085116322],[-61.306003572959369,15.893006947973205],[-61.310547238812241,15.894870475049116],[-61.321694957583873,15.920251553594083],[-61.343495851333969,15.953690827227419],[-61.371503371382197,15.982134505086936],[-61.387494455337418,15.994119831444671],[-61.422654584592046,16.013020342025765],[-61.460867925372419,16.024561770699506],[-61.480646950240939,16.027417114674407],[-61.520564304662052,16.027154806750122],[-61.559634480280252,16.018970347904784],[-61.59630105414783,16.003189779149466],[-61.670479048690019,15.962294544668071],[-61.71007811808483,15.976139129193321],[-61.759214455099112,16.062132592139378],[-61.793870216132376,16.300925303980463]]],"type":"Polygon"},"properties":{"alpha2":"GP","radius":40000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[9.3862220804222467,1.139407752924918],[9.4944519750970411,1.4351690828234938],[9.6333503489961192,1.5919049629044337],[9.7815074422135257,1.8939852195887832],[9.800977761070067,2.304163963210129],[9.9027125581342776,2.2035091261247008],[10.0000713590996,2.1687343252828155],[11.328484057754993,2.1672034652746119],[11.335117857430138,0.99994019762091835],[10.062459368799271,1.0039047199856435],[9.9067806930373816,0.960355510069406],[9.7231049849298774,1.0417132280523194],[9.5909112652115915,1.0322548851779005],[9.5247214483197045,1.0947547562619631],[9.3862220804222467,1.139407752924918]]],[[[8.4344903381721306,3.3324299882818886],[8.4647972769526714,3.4503966235410792],[8.5998728450644357,3.5401642088363996],[8.6760464495580027,3.7357407706172978],[8.9099539739146554,3.7579837370300808],[8.9504228984869076,3.7052421754035301],[8.9458147892765574,3.6276337485488819],[8.7038648259769182,3.2238692397428883],[8.4750300388801225,3.2648745496126312],[8.4344903381721306,3.3324299882818886]]]],"type":"MultiPolygon"},"properties":{"alpha2":"GQ","radius":174000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[19.647423272733018,39.766552342440718],[20.172470191347909,39.895110579954611],[20.55605419754508,40.139592460048753],[20.855636378848121,40.517676472174124],[20.965132999010084,40.848966111755338],[21.623710810129495,40.938292283936789],[21.975378751695732,41.124331144069096],[22.46523299464349,41.176297422965526],[22.784241512501438,41.330946447160478],[23.683971244779045,41.39965493453375],[24.056308679545868,41.526308413489488],[24.517930768094427,41.551688068935697],[24.708552678234327,41.438334558430398],[25.179003101749736,41.304172750537589],[25.504583291518941,41.294112784036777],[25.873843454944051,41.454685552363429],[26.107879054366588,41.724815602207819],[26.410040812200215,41.695119098959793],[26.580557807743826,41.600519729864928],[26.623955544795205,41.401903934248338],[26.330264654861899,40.955155041992512],[25.913842382875405,40.511694928672881],[25.823053045561139,40.209952674714955],[25.855829280273213,39.896557062059529],[26.00708610556962,39.620129016458463],[26.395654329640514,39.341891408384718],[26.577452435404496,39.109136364735292],[26.594613209180743,39.048983352281361],[26.429500456451628,38.667151282782285],[26.44395780832722,38.356775241887874],[26.532491368123857,38.1405096770565],[26.679743076592757,37.959054668596863],[27.038830188549369,37.769162996167509],[27.046884860357341,37.344612855241316],[27.177665062282834,37.045989864605247],[27.488307310193473,36.760798333285422],[27.862094712032739,36.640278767714079],[28.230935402703714,36.432969980087861],[28.229229284604845,36.370489505430037],[28.08705213965451,36.06602725615101],[27.476132353550067,35.772652921009914],[27.137689444587735,35.410120579012911],[26.895956826553629,35.43623278782718],[26.666165043625107,35.391669008473166],[26.459108148316467,35.282501082611532],[26.243740408003067,35.045391737632585],[26.165466730196734,35.019480173814983],[24.799891498360463,34.935234844374854],[24.355804636011882,35.170566065529535],[23.638360738979124,35.236282927803764],[23.562479279430189,35.295530744264425],[23.459923798565097,35.763564469895556],[23.257145041410681,36.015796235550084],[22.167948598694167,36.660199768507823],[21.892767501126446,36.738251593749638],[21.727750077843755,36.877734498088742],[21.482283471640898,37.30437562975451],[21.320966604337489,37.483834609744754],[21.113476688688515,37.607027921575003],[20.818845100164072,37.665637856049884],[20.692583871028855,37.756467505313132],[20.514940301771233,38.027981939626315],[20.353491316824268,38.180405621866342],[20.478900439122963,38.62269379452114],[20.403255998441907,39.010506901693518],[20.213318961534107,39.264659156650623],[19.884756098726651,39.462328405350199],[19.647423272733018,39.766552342440718]]],"type":"Polygon"},"properties":{"alpha2":"GR","radius":434000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-26.450618724394911,-58.415337896425662],[-26.401155700120768,-58.383419802637754],[-26.303550871565658,-58.382467436366298],[-26.279564651310658,-58.401889615429383],[-26.264303638537317,-58.43520570129531],[-26.260114018416022,-58.491961982293972],[-26.415236732135824,-58.439653103181271],[-26.450618724394911,-58.415337896425662]]],[[[-38.016371567746319,-54.008021789515858],[-37.941351537574171,-53.995586644143664],[-37.541673936888571,-53.99377642378299],[-37.382313405396616,-53.98430931507901],[-37.327480445299095,-54.023793817450269],[-37.221716862999315,-54.059829517041543],[-37.103391224142584,-54.065901074653659],[-37.050119489601911,-54.087502449584633],[-37.009504625429628,-54.093840510305199],[-36.924369364240007,-54.081373925472107],[-36.848980071899703,-54.085321305752217],[-36.773024195553027,-54.105918743067342],[-36.703897526378519,-54.108346274555068],[-36.563249397321819,-54.234113430582447],[-36.498366303372293,-54.268914010889354],[-36.441719018274298,-54.279816721925378],[-36.403236545677473,-54.27785002621026],[-36.326506762365838,-54.251448019047714],[-36.233087385090471,-54.349177598814755],[-36.172727059836198,-54.382483666706811],[-36.09872628228856,-54.485427862877529],[-36.055499145150712,-54.525429461428573],[-35.983431748773931,-54.556056354609581],[-35.895603665821675,-54.55507075801949],[-35.896993553849562,-54.616245537669023],[-35.881303771427881,-54.671809522580325],[-35.85039534075203,-54.720576259243956],[-35.799093457664185,-54.763464316977192],[-35.939012985697261,-54.834029343801078],[-36.089940080552601,-54.865181590026317],[-36.251572612519169,-54.779733837158496],[-36.327939957643927,-54.678661753586276],[-36.467977234603261,-54.548593006845728],[-36.516759706221727,-54.518270187901287],[-36.734821715646682,-54.466306047101561],[-36.895589792742115,-54.354925634272703],[-36.952923452068049,-54.341236641887122],[-37.006697680787084,-54.340692672297266],[-37.173334986237819,-54.266298367319358],[-37.457778081661559,-54.169438826053415],[-37.518885928585554,-54.158791403235348],[-37.630860653362554,-54.167247524849934],[-37.692123661839965,-54.13460042819824],[-37.73556478448824,-54.080779991228006],[-37.801842092665744,-54.042205586713777],[-38.016371567746319,-54.008021789515858]]]],"type":"MultiPolygon"},"properties":{"alpha2":"GS","radius":87000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-92.234789021609927,14.545432889457651],[-92.173310742439952,14.674713100331271],[-92.13040863672687,15.07154409738137],[-92.202704536237761,15.27914542856856],[-91.73642671080853,16.069908931716252],[-90.630395799104775,16.072066251189014],[-90.46534375291354,16.194634527781414],[-90.470152934595916,16.371290050724983],[-90.633821813772443,16.5108746285125],[-90.712899562126623,16.706032331825412],[-90.979838210927412,16.871014672907314],[-91.409111912368971,17.255629184795801],[-91.154117217893997,17.257612366561414],[-91.026083055812208,17.341750882774896],[-90.988933294596038,17.8161777292595],[-89.161725053970045,17.814603207590846],[-89.209915750553733,16.009900003473554],[-89.098265629780045,15.908136279493842],[-88.783322817712545,15.860078542324288],[-88.593872931063672,15.949986385341445],[-88.228700701883582,15.728980034073651],[-89.15328214844115,15.040098400551271],[-89.207986705236181,14.875992065400531],[-89.172019430290192,14.607038815054036],[-89.362753349325303,14.416247716656509],[-89.484221263653183,14.363941030598712],[-89.547388943862401,14.241383825275351],[-89.749538334260293,14.077173496051993],[-90.030746496279647,13.919445162536276],[-90.095494062284942,13.736893718562275],[-90.585107482853005,13.924210165315792],[-91.146016192284392,13.925800665357263],[-91.377280745638572,13.990354356723724],[-91.815503149765036,14.225941028051826],[-92.234789021609927,14.545432889457651]]],"type":"Polygon"},"properties":{"alpha2":"GT","radius":294000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[144.65023519267535,13.428234451510241],[144.66591498597393,13.440314927296056],[144.72875809768965,13.494133463183925],[144.78570646500009,13.554154917314136],[144.8371997437938,13.621257454296529],[144.85451538777218,13.618705754394844],[144.87498091723683,13.613379485477276],[144.89347378756463,13.606206726163917],[144.90904725224712,13.598088653499266],[144.92520856560401,13.584700755759961],[144.93941433440824,13.570287703464242],[144.87946709980287,13.503899269554598],[144.83128755787854,13.439113908675926],[144.78988323499766,13.369802341885164],[144.75567583491613,13.296670508707818],[144.74079637635984,13.260164776903116],[144.71904386667151,13.258331491209782],[144.69985865053184,13.258471858541753],[144.67814610241624,13.277038385533661],[144.66364128379942,13.291742919171588],[144.65664845951619,13.30263701800572],[144.65103717271259,13.313755033599168],[144.64987830726619,13.333990480487873],[144.64943135760188,13.409333423900707],[144.65023519267535,13.428234451510241]]],"type":"Polygon"},"properties":{"alpha2":"GU","radius":24000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-16.71117769322429,12.354727799562975],[-16.438721138951244,12.363612139860686],[-16.149337507775233,12.456675368329723],[-15.848311870933081,12.439203079258672],[-15.601302862905955,12.485127163587199],[-15.196042967679791,12.679714616032429],[-13.729498493530706,12.673686500180244],[-13.673771971150785,12.478470868391851],[-13.786792692711407,12.156797427060672],[-13.731605116541173,11.966262448730415],[-13.733003782827197,11.736217731776142],[-13.953291414443314,11.66494956912044],[-14.274156566231307,11.651220761830187],[-14.699294347770994,11.484755441060132],[-15.043003406772009,10.940488113272702],[-15.122451322208851,11.010661195679353],[-15.221938855829595,11.031162272009492],[-15.421984029526255,11.245280025656021],[-15.532224912600324,11.287320688405087],[-15.646980661775347,11.259905589342518],[-15.905331299148934,11.054997453627333],[-16.194421872234514,11.044856373751418],[-16.236149434616614,11.113291605985534],[-16.065850042840481,11.215517888753743],[-16.000833893582865,11.312451474539834],[-16.016608136510023,11.669146870985408],[-16.391539129446041,12.173380530148453],[-16.71117769322429,12.354727799562975]]],"type":"Polygon"},"properties":{"alpha2":"GW","radius":201000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-61.390546465409152,5.9387344883366184],[-61.15293255829129,6.2244889787625732],[-61.203363986201786,6.58836476255635],[-61.145471172131408,6.6943607690439668],[-60.873257760667215,6.7866728297790377],[-60.701762453500031,6.7926036488584911],[-60.539836857270089,6.9224534349508628],[-60.529275880893294,7.0717059183945876],[-60.633076451384177,7.2111792355066537],[-60.633943439857042,7.3505375125731405],[-60.718442805285562,7.5358405828140427],[-60.513525459678526,7.8129749572884135],[-60.369693221304843,7.8429133553305306],[-60.019807369627003,8.0979266898806994],[-59.90505302392453,8.3428998260309264],[-60.016883877537786,8.5488033208858258],[-59.857527823366404,8.4089215778468063],[-59.666198415167152,8.3623827027261903],[-59.194891565263845,8.069940665108982],[-58.511356230412943,7.3978798384103648],[-58.476745652627535,7.0418221037926596],[-58.424040709793239,6.9394280414780498],[-57.982711523860168,6.7856678298456954],[-57.530518654005547,6.3435789151418387],[-57.343761877433863,6.2719119617009671],[-57.192485919732398,6.1022094999843315],[-57.195027073454398,5.548503838931568],[-57.273038650134957,5.3664227406993898],[-57.210020429875364,5.1955123887982584],[-57.331126207746351,5.0203735900850495],[-57.674729870528544,4.9949532260159986],[-57.83421779369845,4.8875248557599118],[-57.871441893304173,4.7762943189176985],[-57.846239876133254,4.6681560072259201],[-58.047449646261306,4.0707264370367344],[-57.824627568580226,3.6814962079235838],[-57.589212259942023,3.4221715192779159],[-57.483352691361951,3.3743152318387919],[-57.303789764423136,3.3768736414852745],[-57.191616948896282,2.8802541674453233],[-56.92089899029051,2.3805065565334385],[-56.720549119831325,2.06524793810132],[-56.48324000199927,1.9422152709029454],[-56.836704645053494,1.8815190145286218],[-57.134257042991059,1.9794007248938568],[-57.334402081536005,1.9492429430726148],[-57.550169357398076,1.7240804053870229],[-57.892307771043093,1.661361327457715],[-58.034772151696238,1.5205054271770915],[-58.315551294119665,1.5356266748592404],[-58.447891691594471,1.435162755800379],[-58.512004519506583,1.2848951543208316],[-58.678621360021523,1.2712190279882578],[-58.81743652635793,1.2021342367286889],[-59.000763542986796,1.3129609098647921],[-59.231090093869355,1.3762185036112946],[-59.524793918090353,1.6808966124172151],[-59.666397681072617,1.7464473721325979],[-59.75367198871178,1.8963104209105996],[-59.754434944977298,2.2082623711460108],[-59.889474004681894,2.3630527052381525],[-59.994091396223666,2.6900321578794784],[-59.971239047346494,2.9944059132849561],[-59.832023553051968,3.3804744873259485],[-59.854142949842291,3.5873891509623688],[-59.705003826559512,3.693811225953195],[-59.589332343089097,3.9245449451162475],[-59.790242613887294,4.4235171730153215],[-60.148381526976799,4.5333626457873892],[-60.035639368872317,4.7415843702379643],[-60.02012600093196,5.0683225749387448],[-60.206173895382136,5.2220482939756598],[-60.742059985448414,5.2022920327738493],[-61.390546465409152,5.9387344883366184]]],"type":"Polygon"},"properties":{"alpha2":"GY","radius":453000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[113.83916387978263,22.241654816717588],[113.85519879439933,22.258983421468905],[113.86683078277817,22.274581908520375],[113.87689193588986,22.291236912273114],[113.88528702090781,22.308790786474606],[113.89193657500438,22.327077376705073],[113.89677765749823,22.345923593095328],[113.89976444561293,22.365151048691384],[113.90086866820785,22.384577747958151],[113.90007987337576,22.404019809439724],[113.89675025347232,22.428042875247311],[113.94572156952746,22.453119510383381],[113.9640595328228,22.463664384458774],[113.98118117367153,22.476087133016726],[113.99689495430336,22.490248784239011],[114.01931034043635,22.515380631578093],[114.05048530228376,22.542772510937354],[114.08412726976438,22.549744093927075],[114.10179519324514,22.555451328592188],[114.12291540154851,22.564741229836851],[114.18370062639356,22.564990234375074],[114.19250542061208,22.563799096727742],[114.2252294662309,22.554780030496161],[114.26100943577502,22.542687270174543],[114.26585493361912,22.540729703787726],[114.2719751527642,22.532693640995891],[114.29089970953808,22.499366046633284],[114.30315192992495,22.470794739416803],[114.31304678325536,22.453777436893013],[114.3249846304472,22.43729234691207],[114.33500450748832,22.39631431949309],[114.31550642194858,22.374296529649882],[114.30265290179106,22.355417501872854],[114.28747552267538,22.325156889962305],[114.26379520835003,22.287973746471312],[114.24975948564551,22.256258291618785],[114.2433684564503,22.233609044999454],[114.23187125873334,22.210692996594396],[114.20723603112801,22.195446452763999],[114.18025123950144,22.211653146433147],[114.16167281677569,22.21989331742245],[114.14235404092605,22.226205457200741],[114.12249440642627,22.230524383739244],[114.10229899290277,22.232805497836448],[114.081976347389,22.233025243669768],[114.06173633077388,22.231181352043698],[114.04178795068266,22.227292863822552],[114.02233720316914,22.221399933305829],[113.99769227959419,22.210741088201701],[113.87738362772875,22.210662346251517],[113.85171259872266,22.220636752368179],[113.83916387978263,22.241654816717588]]],"type":"Polygon"},"properties":{"alpha2":"HK","radius":29000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[73.251452157049982,-52.975972730727612],[73.305249865964441,-52.966530087732679],[73.364949261668784,-52.990516711101478],[73.380534353422263,-52.996099577671856],[73.396527775871135,-53.000374378541963],[73.412819948688423,-53.003311824541285],[73.58568358020905,-53.027355476081127],[73.71150858944624,-53.082519417148809],[73.731600820636302,-53.09010546708187],[73.752390647734401,-53.095493323521353],[73.83706809399537,-53.112858763769481],[73.795060045473264,-53.129640469330646],[73.737142145944475,-53.134660265813032],[73.717585343156685,-53.137248061717905],[73.698378615858999,-53.14174993348847],[73.679708828809197,-53.148122081826436],[73.587945470358818,-53.184344712272953],[73.465222465816339,-53.183915662560914],[73.413431883221193,-53.146633004621364],[73.381395200718316,-53.09834130188429],[73.37016992493885,-53.082940938596941],[73.35752983390951,-53.068678887395329],[73.34358969270663,-53.05568463986225],[73.328476070147204,-53.044076176620337],[73.312326189613771,-53.033958896132823],[73.285603618701458,-53.021250888577107],[73.254116840362627,-52.989243821126443],[73.251452157049982,-52.975972730727612]]],"type":"Polygon"},"properties":{"alpha2":"HM","radius":24000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-89.3622550696914,14.416183522277382],[-89.188645001800978,14.646265399061924],[-89.222089065000446,14.866055024110715],[-89.142449551576178,15.072138798558864],[-88.951189556485005,15.159810885363683],[-88.228272372457553,15.728760792321156],[-88.109844361993865,15.734047398654129],[-87.874868529173355,15.879104440886886],[-87.701836703231749,15.910423705808798],[-87.618271818443205,15.909616451136179],[-87.473193460374546,15.817822924256941],[-87.285899342282761,15.834213567480401],[-86.907268889352835,15.766364647381568],[-86.34953959984324,15.796783015412437],[-86.068722868347606,15.919882531635125],[-85.961693163112628,16.030629807436537],[-85.463769442502297,15.90162239087913],[-85.183842558886965,15.917950377908861],[-84.973744963416692,15.989612085440379],[-84.61574188013455,15.873147845600108],[-84.261523542004298,15.822395039237595],[-83.726346298679772,15.395424207209814],[-83.369364286589544,15.239812097619765],[-83.283899760890478,15.085478320493038],[-83.158224438359881,14.993317426846698],[-83.469444484260649,14.994149145351765],[-83.635561174721985,14.876657786725978],[-84.458147730591421,14.643139573997304],[-84.640981933873036,14.659814668277914],[-84.803377177386338,14.745815340752335],[-84.936667753620569,14.728058964415679],[-85.149632332585185,14.513189010596907],[-85.205144902825481,14.315313928626356],[-85.663439907856628,13.981759345791746],[-85.734125898636307,13.858884473789951],[-85.786678149448576,13.84467995914973],[-86.09959606174175,13.98309933204102],[-86.205404444816395,13.933907637144742],[-86.3319092945639,13.770304530730698],[-86.575059435928651,13.767955888029563],[-86.683120332080918,13.719944932238812],[-86.7512404789193,13.579807667551018],[-86.710933828663286,13.313424109244046],[-86.873738496254077,13.228473458012484],[-86.959055053524224,13.053828061083426],[-87.059217039100602,12.991659811031996],[-87.337040595371107,12.97948518168438],[-87.532922653349971,13.314794442585198],[-87.813870994223365,13.399260859545221],[-87.75668560223518,13.696727931440716],[-87.855527263298086,13.855468923863457],[-88.173615860879366,13.964183934135587],[-88.482485358980739,13.854467305908502],[-88.553357869650824,13.969828145511837],[-88.845791613966654,14.124916623484982],[-89.07557905351571,14.332342538208728],[-89.3622550696914,14.416183522277382]]],[[[-85.947162202081174,16.403932128746614],[-85.924057244843013,16.483137142427555],[-85.834119621603946,16.510692629399436],[-85.866308788398726,16.466110660834236],[-85.947162202081174,16.403932128746614]]],[[[-86.256421527693789,16.428149386760758],[-86.580323465398138,16.300477034904269],[-86.629858618515414,16.301949256732112],[-86.438226741705179,16.413707655704396],[-86.256421527693789,16.428149386760758]]]],"type":"MultiPolygon"},"properties":{"alpha2":"HN","radius":368000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[13.517470270902946,45.481681412961557],[13.577901929104218,45.516620276812517],[13.829367707948267,45.441437271859101],[13.992765309699706,45.509173458670048],[14.346727917556155,45.486947176559113],[14.568857013048559,45.656982310673591],[14.770440679152728,45.492855884445],[15.153303338917556,45.484986975365544],[15.254126553644262,45.574635132900276],[15.277233297391595,45.732449233713417],[15.599011852948321,45.888076793216555],[15.631446137957909,45.999206333742258],[15.592844315865188,46.139945114587398],[15.636077684566658,46.200475746508992],[15.981482547818329,46.298527320862412],[16.326024710646589,46.53415576419436],[16.747945287710756,46.416202732755579],[17.331015396854873,45.987800707420845],[17.602255097113808,45.915459462384149],[17.80742400587998,45.791639450673308],[18.375576329983428,45.75691741272226],[18.541477805357658,45.801994803503099],[18.66609504156548,45.907215940610747],[18.905105063683603,45.931515453150894],[18.900862733010669,45.755860434244482],[19.064022577487716,45.514927825555731],[19.096520834810178,45.380648256807497],[19.400714513764392,45.189148552637349],[19.175934366721929,45.124465510099931],[19.085046905760326,44.926916247799603],[18.94708291505594,44.86553680380905],[18.83212606510029,44.886083473516237],[18.724723493115778,45.029144443677929],[18.619849823408373,45.078540033344623],[18.26418583512703,45.13385336206894],[17.812859706055399,45.078343150039089],[17.687761612275722,45.143112332935615],[17.205967441396709,45.15691724358981],[16.94047963456579,45.246860746812118],[16.790751515185377,45.197155294557184],[16.511399760600717,45.196403306799752],[16.293317107835069,45.009094420658897],[15.970365014579857,45.18120083555867],[15.841510568502491,45.142690086919892],[15.771976812638822,45.04999175720237],[15.772326807115578,44.773125924336171],[16.103269684582681,44.520810899148458],[16.292177342898007,44.132873551276361],[17.055848644308931,43.537376327587815],[17.247929282345723,43.470008357466234],[17.321307968043701,43.279354637871833],[17.698052037937597,42.948096021551422],[18.436145177283926,42.559543567730501],[18.516704205963823,42.433527352230143],[17.92726753773335,42.743184195463002],[17.744171904813324,42.700589928291819],[17.344313309397123,42.790581470361801],[17.193009826440193,42.913333755653639],[16.85067131148023,42.895762975445173],[16.696490277547539,42.933846896319508],[16.587874494200701,43.114286146521835],[16.37676839745372,43.21385828645623],[16.398615676939365,43.367006723590038],[16.304193122228416,43.490182969893461],[15.985689760650468,43.520012620663437],[15.869989587268675,43.702314771027872],[15.527470461977696,43.887088834075939],[15.132301965507086,43.910433201277165],[14.865366564275744,44.167874495587007],[14.948662747645743,44.25161976802174],[14.967716525472259,44.383567524254431],[14.748382876744827,44.670842684951204],[14.614236703060801,44.700459807194491],[14.480337865966735,44.621560165294831],[14.316069953117916,44.885544296956922],[14.201012423826828,44.954476541780828],[14.068276476801014,44.935213681396341],[13.965701987466977,44.835868528629589],[13.860886511975339,44.837632638812991],[13.62950968323198,45.108326260854987],[13.517470270902946,45.481681412961557]]],"type":"Polygon"},"properties":{"alpha2":"HR","radius":310000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-74.477868709936828,18.449978203489245],[-74.387333201240395,18.624507644049569],[-74.232264510864354,18.662215788652031],[-73.559009214753431,18.519141807341665],[-73.316174246073189,18.499055342699819],[-73.193738258479527,18.557477845785964],[-73.144064243887712,18.641790658717841],[-73.1403360854178,18.739577462452171],[-73.183446318947702,18.827427709550005],[-73.285007621588576,18.89685452406728],[-73.276207516886927,18.953838194755008],[-73.170627903342435,18.967072268058711],[-72.991900134923341,18.921776450622524],[-72.881814348603811,18.963566369713238],[-72.773279698014704,19.139258971992074],[-72.752718414236895,19.388835520588305],[-72.869572104968711,19.528822307119764],[-73.063769796644976,19.610716331430663],[-73.396172962735278,19.658921408045721],[-73.438100539057785,19.722142550504767],[-73.400373006244934,19.807271621201338],[-73.320050885064106,19.851977572813361],[-73.035798092750383,19.940596295692675],[-72.955854097039932,20.064234252651147],[-72.791063568558613,20.091677004751244],[-72.669450150993498,20.039808401233369],[-72.535282607730437,19.861730770461165],[-72.24174492561842,19.751791470474295],[-71.779328662557731,19.717859841480927],[-71.757658097416908,19.688076205883533],[-71.711621121312859,19.486550552979139],[-71.715341930912743,19.30679834979999],[-71.645565471900937,19.163561662966746],[-71.751830550961884,18.990620894824275],[-71.743458543653006,18.733040140619458],[-71.867738128039832,18.534928012772895],[-71.844134322913277,18.424110975528901],[-71.737528541254065,18.270806396338745],[-71.768521758758496,18.039669616116861],[-71.94654652914663,18.185308523686757],[-72.070042567314431,18.227377885341355],[-72.503429639893511,18.218838020800167],[-72.871062639807775,18.151960457906188],[-73.405169316813485,18.250275205214297],[-73.72954805792493,18.196025250799089],[-73.884899011177211,18.042179709755985],[-74.085845755859182,18.214412837323692],[-74.418887240844114,18.346423066042966],[-74.477868709936828,18.449978203489245]]],"type":"Polygon"},"properties":{"alpha2":"HT","radius":176000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[16.093697187245485,46.863464864641912],[16.344222231911857,47.015619050213992],[16.411076499102421,47.106507857145289],[16.484464272934815,47.521497090865857],[16.421633535071386,47.67435068015098],[16.586259438304197,47.750166932750325],[16.773873632461598,47.691379216075603],[16.914382054371043,47.710791105339958],[17.006840932897834,47.787456292569118],[17.089257568582497,47.963487161673484],[17.174627057701187,48.011817664463813],[17.317179956293565,47.990676061177631],[17.75285924296475,47.773962484906875],[18.581881002640454,47.782259465049748],[18.699754757330059,47.842916328223581],[18.792044204316017,48.000108032984279],[18.914228277590642,48.050535939671391],[19.401404012272447,48.099496658187938],[19.625374743823862,48.222793316601859],[19.860333152365826,48.14251118343028],[19.971449417787998,48.15553901720093],[20.327919082487593,48.303285272620499],[20.49019761341555,48.526690614395982],[20.861479749925078,48.545747196884555],[21.086815664890384,48.506617329006041],[21.388527706209508,48.553358690798348],[21.563083927928602,48.495541047090576],[21.763794138876033,48.345821429998466],[22.253545749118008,48.407187280809516],[22.364056592725156,48.264512833521323],[22.836055758103896,48.060123597049781],[22.876373770784841,47.947320899847753],[22.608325314899066,47.766547789713016],[22.285237668178269,47.71783097163253],[22.060064018730255,47.537571791183886],[21.995084985891328,47.395821121784991],[21.870736858913521,47.299036348474324],[21.532468486203587,46.822483864265649],[21.496835055256685,46.70442484572191],[21.319744704823407,46.587769953579183],[21.264327322356664,46.41245144752277],[21.043901909186388,46.244229087677731],[20.837942873574004,46.25178461552651],[20.618177514149981,46.134652695964782],[20.480401333760103,46.161967217034857],[20.241789798421827,46.108856151669713],[19.591387979942855,46.163559244457012],[19.278038997089439,46.003110860335006],[18.68561430748942,45.893455593403793],[18.362562154146641,45.75379902623375],[17.807202395229655,45.790730956130261],[17.558434991687729,45.929595021293274],[17.31546057817096,45.993874613171911],[16.842527078361556,46.357393496397208],[16.516356007429657,46.500092766651058],[16.264323895932673,46.82199655470172],[16.093697187245485,46.863464864641912]]],"type":"Polygon"},"properties":{"alpha2":"HU","radius":272000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[95.213073655511991,5.3203676270567533],[95.24218242796988,5.906091718236584],[95.358565997711139,5.8758965299434074],[95.559508744860992,5.6733828083330762],[96.042986241896699,5.3563280718676953],[96.251652273134937,5.2704712843852839],[97.517637070370412,5.2199801787989575],[97.907694386451723,4.8793777102980167],[98.453112202691131,4.0745562772081909],[98.839242147166246,3.7422418187038318],[99.731703350934524,3.1823097775516862],[100.12912519292335,2.6492035395524383],[100.45164153652892,2.3752312217159366],[100.68531877892946,2.2967099302649365],[100.93586411259682,2.2936346254826812],[101.37369673510693,2.1244977335862711],[101.64048708447758,2.1256243006327225],[102.00416939369217,1.6959371563435042],[102.46888889694836,1.5092318242247611],[102.69798373944907,1.2549506925424523],[102.974575921869,1.1270993513383212],[103.67584385630742,1.0864801483730628],[104.02492745953479,1.1795907295943155],[104.58482997764739,1.2150302886784021],[104.6620065334173,1.0494460008029691],[104.54748342573076,0.6871511508005459],[104.54922563855527,0.37551707900453857],[104.72276485042853,0.002365304927199996],[105.00411453453617,-0.28309352197665644],[104.89524678992593,-0.54198755888048666],[104.87727006133736,-0.77340386440854192],[104.95726141893105,-1.071491483573296],[105.08868044871141,-1.2628176653848553],[105.41009190437937,-1.4737185513133453],[105.98030327410184,-1.5400109578484689],[106.1605440286378,-1.8673703487750217],[106.27278423439265,-2.1968507425857782],[106.42389261909695,-2.379639608421245],[106.62195051302245,-2.5100949036574023],[107.0047676851391,-2.6578973653945011],[107.22970534908238,-2.6923482701540848],[107.8186602388912,-2.5342939257220265],[108.19745137577073,-2.5341719779469205],[108.28956893886993,-2.8300093417509351],[108.18110691219081,-3.1203612675083403],[108.05491026494464,-3.225827571365167],[107.61479097568125,-3.2083500305275847],[107.28211288291101,-3.0255996208130549],[106.95997121950781,-3.0060862111056976],[106.39977402964672,-3.1398051448889466],[106.20765252514876,-3.2622203844936615],[106.05782408973896,-3.4338234244454191],[105.9624379749215,-3.6406993594356187],[105.85099072153321,-4.1929325607619461],[105.87505273697492,-5.2722011016202162],[106.00663694796393,-5.6429837742452493],[106.29905971259784,-5.9062006460913148],[106.60217854217343,-5.9952304345273681],[106.83934694403943,-5.9804719201644252],[107.04630560210971,-5.9052551096502066],[107.33148730152924,-5.9790315544283521],[107.67629148783746,-6.1915133502990214],[108.0621738542233,-6.2716798485548981],[108.29478415132223,-6.2659611939263469],[108.68487404776593,-6.6586284613074369],[108.89059276828345,-6.7782169200050326],[109.1994169493054,-6.8455920394578849],[109.50055522359135,-6.8110524243531714],[109.97147624714984,-6.9075749554629633],[110.37793842279704,-6.8159840579712823],[110.83492343146297,-6.4253940151584974],[110.97180038340333,-6.4366325244784024],[111.15892015177612,-6.5658232147187476],[111.37434882235652,-6.637064300683793],[111.79263908915236,-6.6896118880465822],[112.10469244349922,-6.6257072871668772],[112.30861419379778,-6.4999146362073787],[112.46614009283577,-6.3193780404365292],[112.56314009915178,-6.100291596184622],[112.58703298000619,-5.8039356829713977],[112.6897518384139,-5.7272821399341876],[112.72632633740321,-5.7531748693261511],[112.67394798852108,-6.0818010950016061],[112.70989678260705,-6.314019396665068],[112.85900208583215,-6.5885732855239212],[113.03420819970898,-6.7451634291597076],[113.4015327139178,-6.8735675043369975],[113.97430794743923,-6.8741163271442849],[114.20648425318701,-7.0263697821184152],[114.60408946614078,-7.1250333556654422],[114.91419780756428,-7.080265098656783],[115.24103968959405,-6.8620847556796045],[115.4334109350288,-6.8486674161164292],[115.54493501633641,-6.9386007751758632],[115.38745710108563,-7.2691461132732194],[115.38206121775929,-7.5830628642222946],[115.45823644045049,-7.8065836429700726],[115.59634945699889,-7.9981273657697773],[115.92948303561565,-8.2028610953428913],[116.16275820414764,-8.2395612156484503],[116.40157336272226,-8.2050786690519768],[116.87853760575749,-8.3529664216009891],[117.19797923954074,-8.3331699188769726],[117.54635099098745,-8.152871588961327],[117.92102127815818,-8.0900121233981341],[118.11706216878419,-8.1233165705363017],[118.39621578271344,-8.2500189501573633],[118.60173401106107,-8.2737162916754645],[118.82977115164303,-8.2469298700516518],[119.09735137397725,-8.1400653159625858],[119.42962959693845,-8.3815942117284656],[119.80264554123424,-8.4316556423286499],[120.03511849149263,-8.3856804766553061],[120.35206811514692,-8.235736737758689],[120.6358170629758,-7.9679925967190091],[120.77313463290906,-8.1849046506372325],[120.95731728368645,-8.3376701538434759],[121.49114006844763,-8.5192295863964507],[122.39034810266887,-8.4308174802298055],[122.59659425005032,-8.3256611840614401],[122.8459225588517,-8.0943805134857971],[123.12625197554196,-8.2119616519254492],[123.44485285960432,-8.2661788863473138],[123.7765768955188,-8.191503036040082],[124.11885048206851,-8.2266989439721456],[124.5087980244911,-8.1365566979263111],[125.11721246144009,-8.1878284518927913],[125.40307219796007,-8.1532668935753208],[125.21307021437501,-8.3715141442670777],[125.12400906007194,-8.5939225225205007],[125.10479570809798,-8.8327283495621298],[125.14829950856766,-9.1227148579628388],[125.06719227461578,-9.5115808365194958],[124.96238464180054,-9.6650115426604692],[124.42719045739059,-10.147879119501834],[124.16062680785969,-10.205590059075053],[123.71569692020641,-10.39661975668799],[123.21423482446342,-10.805229637194527],[122.85598562865825,-10.908926149489812],[122.66465841518352,-10.720598943900153],[122.9598344744051,-10.639145401826511],[123.20888228684038,-10.445127754908615],[123.57869942318736,-9.9482078090071901],[123.72221871613694,-9.4858089340689311],[123.69515577143757,-9.1811220078172298],[123.555720402649,-8.9088642183321429],[123.32429773955201,-8.708839027394589],[123.11034195283152,-8.6243438886201851],[122.79780121663487,-8.6117148057808279],[122.44933829860301,-8.7291089366814081],[122.03514360340218,-8.7766309913585534],[121.70398144976623,-8.9059841044119796],[121.34406449802573,-8.9197285320411321],[121.12596615896877,-9.0157043029118995],[120.89707829331182,-9.2347137968681103],[120.77114344758566,-9.525394425943853],[120.76790875921913,-9.84216618964623],[120.89784430870058,-10.151948699263773],[120.4389962495239,-10.293015149133646],[120.14543672665043,-10.199049967368605],[119.9035329707916,-9.9761684508427528],[119.67249123620893,-9.8382530744587271],[119.08590074670067,-9.7060570934924009],[118.74736068853872,-9.1106847825496722],[118.56256874925265,-8.9691790270758958],[118.34486636544337,-8.8868527752416995],[118.03546027758942,-8.8803839762425287],[117.06128972302916,-9.0979788247938167],[116.60994166421267,-8.9443286822754899],[115.76030917622815,-8.804511326455625],[115.37076689499266,-8.7968044094496847],[115.1449681006096,-8.8479174987400064],[114.82638721130469,-8.7554588998211802],[114.56494456872959,-8.7652279194603775],[113.31569464996603,-8.3331105739936095],[113.06459777945361,-8.3223792683284685],[112.67878115428752,-8.4077041600737683],[112.15238379125493,-8.32860281968102],[111.51012426966531,-8.3039145781135648],[110.62629368480083,-8.1539073221573712],[109.99877232437321,-7.8772065488950318],[109.32964151515696,-7.7152678178101537],[108.77440223553488,-7.6947735527220829],[108.45151022277216,-7.796053888345841],[108.23806855399108,-7.7841130888509298],[107.8323775242458,-7.6972232719118736],[107.34267843688899,-7.4871527978880241],[106.4744615374354,-7.3747831756805402],[106.25823568389985,-7.0931884273536614],[106.00616252170282,-6.9208852686666926],[105.78447384405956,-6.8610617535938792],[105.25608203405139,-6.8344381655545154],[105.12228274363854,-6.6146033216541547],[105.05340047505865,-6.3271431938869247],[104.92553111150842,-6.1272404411302377],[104.46802451252555,-5.7901662923612687],[103.8068877865485,-5.0712698774468459],[103.36716352735841,-4.8341667410524218],[103.0531565092672,-4.8211808857117902],[102.75837727244215,-4.9301498784863877],[102.52831810155323,-5.1442569025172906],[102.36664768837677,-5.4778796029871524],[102.28621887948773,-5.4826274308807204],[102.11191800055418,-5.3227292406360451],[102.37406606626951,-5.138823438946762],[102.51779624964539,-4.9570235743607931],[102.60338495032492,-4.7416538209302539],[102.62364946339505,-4.5107882628953382],[102.54692807598423,-4.2123341578490514],[102.19060796610299,-3.686402029699841],[101.5399623607353,-3.1557225866597456],[101.32315657169579,-3.053719390814134],[101.08586896792598,-3.020492967278869],[100.84938476733805,-3.0590237990381244],[100.6349171208187,-3.1658555860690392],[100.46502897700691,-3.3272440322985353],[100.34693874530826,-3.2285920596646194],[100.21121137767153,-2.9955262534747793],[100.01573877574396,-2.8189996416231482],[99.84307694469932,-2.4595130897349358],[99.607592406851836,-2.2568715940087762],[99.405777816342251,-1.9843197836092843],[98.828483799817874,-1.6095228615957651],[98.609072346451271,-1.2162796519119736],[98.50928101741934,-0.77307920658423335],[98.310744280878097,-0.53152161029143885],[98.359194857708687,-0.17381171839470538],[98.25683932182487,0.17501937327827521],[98.006338020209554,0.46146045978691524],[97.684680015858675,0.5969513970864706],[97.573851111632237,0.78781107215688351],[97.406130768138212,0.9475577902729031],[97.280180246952597,1.1990914208201682],[97.08016369223634,1.4257919827935008],[97.053224293523613,1.8021835594965809],[96.903037307556701,2.0739807103608512],[96.660668893261899,2.2681130717125444],[96.341177135002297,2.3729797155335097],[96.113101372072776,2.5354093801076165],[95.809166063332853,2.6563902009781071],[95.745714266925603,2.7478628481102332],[95.718203538547755,2.8256436964899487],[96.014802973682194,3.0503161138977388],[96.203763727796684,3.3038127300045645],[96.272327324618601,3.5315327192687778],[96.270918501031645,3.7693464605365046],[96.116221780039666,4.1314354339297257],[95.495222686844016,4.7617332137276325],[95.213073655511991,5.3203676270567533]]],[[[121.66602435022351,-10.493774937364659],[121.89756123879704,-10.426292449535223],[121.99777757065571,-10.447834347064038],[122.09858782769592,-10.543631200463146],[121.83312179314674,-10.601139364320655],[121.72667667486307,-10.57216036370756],[121.66602435022351,-10.493774937364659]]],[[[108.82089454525506,-1.6646404823170458],[108.80477846635837,-1.5682620855110221],[109.06948356804902,-1.3515141444516559],[109.22966351568761,-0.99938462260789684],[109.22573477948555,-0.68881662369379792],[109.12183427967173,-0.39085805507723281],[109.12218701603545,-0.044161722980642318],[108.94542815401367,0.3560591392189843],[108.90647194248204,0.7939369575863201],[109.06336905757655,1.4607032651554199],[109.28325855201517,1.8031274102213886],[109.34405436033062,2.1001984868714003],[109.6278817000062,2.0267047030511849],[109.70272449723534,1.6402235331652508],[110.03214337919307,1.2506784462912668],[110.26960226385872,1.0744337347924757],[110.5655928532257,0.97550638231124154],[111.1014400898694,1.0497562594409122],[111.67904919561313,1.0437498140993318],[111.95825326979957,1.1763604399148972],[112.20339255956475,1.4500906648013112],[112.47634098229395,1.5580825249117711],[112.94275129539248,1.5658859927509969],[113.32715033757391,1.3644372475090323],[113.72100542900687,1.3639790601712916],[114.29991677182844,1.5269391452099883],[114.6054523644159,1.7746908336493841],[114.71768722703187,1.9840883922858865],[114.77818248435479,2.2333555912163652],[115.00407839384289,2.5184183313074651],[115.09699402455077,2.8594619615709496],[115.47912616411324,3.329037318790959],[115.56946614197182,3.9385234272130782],[115.67961959484214,4.1923538000821949],[115.84872347942103,4.3401975153887573],[116.51474701414517,4.3699295517575001],[117.10047699923727,4.3360836805464791],[117.58816352248404,4.1800961122232003],[117.88413840935038,4.1851104929507974],[117.92190078954242,4.0545379164566224],[117.69166234360918,3.4709078649350782],[117.65830570462171,3.10239023785965],[117.82800943322405,2.6476083198859897],[118.0656038542557,2.3175720775344892],[118.10301416657875,1.8502929397861128],[118.32747377523791,1.5239069913428023],[118.63842425919813,1.3183081737910651],[119.1651284668236,0.82534066482092105],[118.95588549693895,0.85930967761697763],[118.10695363230387,0.81497644039624495],[117.82248487879657,0.6789827117886742],[117.61265881751852,0.44363235603978707],[117.52447377379826,0.2234782978939529],[117.47135467132898,-0.17275268004669095],[117.56174867230099,-0.77034951225098713],[117.26584643452011,-0.9227446419781179],[117.00273021320838,-1.1869941015573504],[116.74585286635821,-1.3461696211465661],[116.52393325736192,-1.6854966828927473],[116.48525585806813,-2.0050728626738032],[116.56236402966833,-2.3207234084332695],[116.52847786310689,-2.510168709342115],[116.37435256898937,-2.7855171315091716],[116.33582322275029,-3.0234417015060262],[116.42073666518613,-3.4805018668999677],[116.30250915196081,-3.8677050905181631],[116.09337041040371,-4.052866196209763],[115.80878327523448,-3.8890673553782804],[115.51185262332878,-3.8505486123488675],[115.29121370665739,-3.8953728182330751],[114.69354287860961,-4.1687752849457951],[114.62608758751331,-4.1114127389421826],[114.50329438599904,-3.8086451515294102],[114.27303959817037,-3.5823553963872525],[114.05140280782061,-3.483067676388615],[113.70526851076652,-3.4544783072889054],[113.43124641719196,-3.2960519716176164],[113.19696303168989,-3.2374566272190224],[112.87780873999645,-3.2722088605010846],[112.60017130193609,-3.3996092782265142],[112.2630057795695,-3.3960825360317566],[111.8583392150523,-3.5509520130331507],[111.63405757955591,-3.2686101749465433],[111.36942734875559,-3.1063899086922024],[110.2566649705382,-2.9654202967218581],[110.07855792881976,-2.0651677322648325],[109.92592336549461,-1.8183748208902624],[109.75139980264102,-1.6635963403106189],[109.53964629188312,-1.5657428645248626],[109.30866679565399,-1.5331342097933742],[108.82089454525506,-1.6646404823170458]]],[[[119.41471812830147,0.69974319790512085],[119.71539817509442,0.66959651132924791],[119.94323619156854,0.72500496496474509],[120.27012660211369,0.96998099070433508],[120.63070010938777,1.0673355223699756],[120.91256670694386,1.2882112107276122],[121.04452971171833,1.3264282352951313],[121.40376858356835,1.242835499453353],[121.67147286126162,1.1147335591865062],[122.43650691779438,1.0173797284415205],[122.71473397692363,0.93564178487809135],[123.88584022660129,0.87076800972869595],[124.26324611866976,1.0354409715300084],[124.60169143612119,1.3921795117257305],[125.1135387907848,1.7792183519293381],[125.22695225413554,1.4896909345189864],[124.96622930191617,1.0830437461350433],[124.4952629457857,0.53832935203570387],[124.30103801021473,0.4085059872088293],[123.78857486010203,0.27695804322537404],[123.59935432234384,0.13605574561753436],[123.45945937326576,-0.053910758280156623],[123.36655762103487,-0.43335170702035009],[123.47480750010044,-1.1577888495924387],[123.69820344513829,-1.4841766489708528],[124.05065456059192,-1.6636563276476271],[124.66389276459172,-1.636748163804955],[125.14564717839517,-1.6935710401321087],[125.45989170963512,-1.7966371180881127],[126.54966292897301,-1.8120695464228815],[126.90107251820722,-1.6511025136610511],[127.06031264968034,-1.4804697755457175],[127.16352757016641,-1.2711383852160096],[127.19959967862971,-0.96291450204835127],[127.107607098522,-0.39417955178472108],[127.12730803491489,-0.2791278565702488],[127.2935841581192,-0.097902814029069307],[127.41343994604844,0.19376271557820654],[127.41127548727633,0.50908716147394473],[127.28758823245526,0.81204721061809193],[127.62439359597239,1.826426472102102],[127.90039221353426,2.1367219949010701],[128.15825637288978,2.2847144408213387],[128.45737427684804,2.5600149848395115],[128.60164868387983,2.5965951595274341],[128.68749715373298,2.4735072177397712],[128.58119654369858,1.9694195875090461],[128.70410977907736,1.5275710189527858],[128.65412580410265,0.84130807624478854],[128.7096809394497,0.53895155255451677],[128.82456790597465,0.33832337769732246],[128.99202894794269,0.17892614106249627],[129.61601083295074,-0.15142560280980399],[129.93252181814793,-0.20412520042930871],[130.36269015905754,-0.073740168981917292],[130.81330049919225,-0.0051904734992625316],[131.02557598182611,-0.040863127420550059],[131.27615236238543,-0.15068347534703372],[131.51154668561452,-0.36368721273032201],[131.73150853720583,-0.45412136455673513],[131.96833647313764,-0.47589475965967976],[132.3939428163699,-0.35636052959681208],[132.62500057075269,-0.35944405192132922],[132.87084878390635,-0.42354571376104339],[133.40730768476752,-0.69376416154502496],[133.97427754703415,-0.74513500719506909],[134.24861782730022,-0.92354462307892027],[134.54869980647308,-0.99732817609768587],[135.03320781905825,-0.90179484239248342],[135.38380715832574,-0.65242013435210833],[135.84105321858226,-0.71255801691510967],[136.3744858306402,-1.094746732004964],[136.53942032671523,-1.4195637957307659],[136.84567296978159,-1.6438138417144759],[137.0677647224079,-1.7002407411814708],[137.29674173881602,-1.6913938476640962],[137.80647075210959,-1.484277618841181],[137.91085571510934,-1.4846104847077579],[138.1130613223124,-1.6115002751770473],[138.64951391675129,-1.7917336592973256],[139.72970449866062,-2.3164244414343829],[140.62227708757328,-2.4467852395756364],[140.97252414401331,-2.6105078570511644],[140.97499997993586,-6.3271733950713447],[140.92587271976259,-6.6580833228258962],[140.97519531250006,-6.9255372038957059],[140.97508646886308,-9.1169933825901861],[140.154436655773,-8.3440794040166963],[139.81026387030511,-8.1785661210719205],[139.28181227649296,-8.200419091116073],[138.84544569344132,-8.4008589720919939],[137.65141390246535,-8.3857615378711419],[137.82256760814124,-7.9545112200112955],[138.00862310984039,-7.6424582516454871],[138.32571122005885,-7.3942438008332925],[138.47751709889104,-7.2057892352370168],[138.56602798535613,-6.9805649667416771],[138.58314511285477,-6.7391790680477159],[138.29090560728912,-5.9476730317770361],[137.98159371871463,-5.5105496379863359],[137.73231539054936,-5.2558321499639522],[137.33364974341853,-5.0328870090904534],[136.63583669015347,-4.8240621230342278],[136.05488012444624,-4.5655831307402828],[135.74204051708963,-4.4834887625935984],[135.36317267759824,-4.4471619303339631],[134.99155087634838,-4.5669442834432106],[134.72088370660589,-4.8483512103376407],[134.61564517733453,-5.2243504486844472],[134.64423001307043,-5.4584087188813379],[134.74589496783281,-5.7072046548387698],[134.78644226177084,-6.1109122079165141],[134.88473909301794,-6.3235829804622723],[134.66655478731329,-6.7565569094133684],[134.20007637705359,-6.9077510025243383],[134.06025609523115,-6.7691749041061557],[134.05300873064672,-6.5195842960691897],[133.97694203500203,-6.2986711733464595],[133.72110655533896,-6.0083940758832419],[133.5115003724741,-5.905175869726432],[133.28101131591865,-5.8669195658591438],[133.04929893173639,-5.8968882327296148],[132.84489022254763,-5.9869755329568264],[132.7094238844777,-5.9666324322420676],[132.6313369332043,-5.6077680844615463],[132.94191302944557,-5.4765093523832959],[133.20794047353613,-5.1915451431367039],[133.32969496627459,-4.8214314136930314],[133.29070214178969,-4.5110327850274086],[133.13565276134653,-4.2393207340193655],[132.84440868597417,-3.9618615424141579],[132.70612226024156,-3.4421290780879028],[132.59458321973923,-3.2358553474323246],[132.36263843287452,-3.0274966409138804],[131.95034876638505,-2.8651420914277441],[132.09853357356963,-2.4572656962908135],[132.05567347135081,-2.0621952643079053],[131.80663373327212,-1.6580778063186599],[131.61842348116193,-1.5278513747178868],[131.40104222922628,-1.4562454127193667],[130.96583871870135,-1.4781958008330092],[130.67827497509629,-1.6193946892570681],[130.46916597537586,-1.8620950373496665],[130.25582948550857,-2.2997654352584211],[130.24541315119646,-2.6069822471086694],[130.35161222719805,-2.8954478876281922],[130.67087203505713,-3.2350290603400307],[130.8961170388564,-3.3686338046946265],[130.80443142058456,-3.8564272056219715],[130.02174401733143,-3.4756320405442729],[129.7120527752291,-3.4137359017546269],[129.03155677701807,-3.4397405580549876],[128.71296535849393,-3.6015849915774782],[128.45290889994391,-3.626060564172223],[127.97815286564891,-3.7698791967188976],[127.65895003573098,-3.6168802876684558],[127.35491094548524,-3.6033543750460737],[126.68643115517865,-3.8224499350742538],[126.19400636392918,-3.5905565732372979],[126.0573787886907,-3.4205073203193712],[126.02730173899251,-3.1706672807831811],[126.09894245170355,-2.8201716681821498],[126.06007511463075,-2.591256460066699],[125.85105688200071,-2.2014877820086198],[125.61140017507248,-2.0068252130881943],[125.24384576177755,-1.9112024537068066],[124.52052588849136,-2.0062147299409383],[124.10563991706888,-1.9505096593330342],[123.82376241773869,-1.9947788360911736],[123.57254647573566,-1.8835452636708934],[123.33945769525609,-1.8516588497322406],[122.74715698082845,-1.9635046145496919],[122.43909284480981,-2.1981667067553676],[122.27733908540145,-2.5500268078417641],[122.28017994898188,-2.8609378710790674],[122.44072115315743,-3.4503460020190526],[122.54530798512266,-3.6554173261405731],[122.70396053299959,-3.8222166740099226],[122.90354220385026,-3.936932889332466],[123.21141035776461,-3.9985036265733522],[123.24610412389158,-4.0411944311593651],[123.17187120529792,-4.4089934096507948],[123.25028833835927,-4.8736986951983994],[123.43049419295644,-5.133970942426842],[123.62583476661975,-5.2722224739335015],[123.72349330787399,-5.59676692075739],[124.02240309885683,-5.9031207382829178],[124.0415804896099,-6.0195662315740828],[123.7958579409966,-5.7211289365218221],[123.52166495784026,-5.5656099308557518],[123.20867927999126,-5.5280876894923363],[122.8118789066596,-5.670345341157141],[122.6454280261577,-5.6623592819528126],[122.29836794141899,-5.4769735646782838],[121.97987212907826,-5.4637546319757444],[121.64124208740941,-5.2239205717302291],[121.262300138792,-5.1730394725124702],[120.90302328858955,-5.3038295011391954],[120.64547591947439,-5.5864161946359605],[120.55337894856966,-5.8792219460489834],[120.51216142154966,-6.5011578820415261],[120.38412785709036,-6.1243567033673747],[120.2506486222597,-5.9375854761876026],[120.00120305621731,-5.7616223083292617],[119.55793795168439,-5.610307539348045],[119.37707265917537,-5.4243843107064302],[119.36553624142064,-5.2947092479121682],[119.59584872176652,-4.4669102645112915],[119.59437119161937,-4.156610596587754],[119.51515433751769,-3.9370905386081083],[119.37595466221985,-3.7497731175652778],[118.92273670912213,-3.482187958136163],[118.81645361311143,-3.1806854355092704],[118.78428104839853,-2.7210931450696156],[119.01055740624298,-2.4965689512271627],[119.12265491181761,-2.2994786019168383],[119.29695577001537,-1.8321499535251538],[119.35212928206424,-1.2665971751609584],[119.7334204714241,-0.47133976352400042],[119.70602250133858,0.27777180697710674],[119.610148342889,0.49083792188171244],[119.41471812830147,0.69974319790512085]],[[128.13810859054402,-1.6889428543445295],[128.06749159726084,-1.9099772877308578],[128.06327859207519,-2.1419799408839499],[128.12582401366828,-2.3654325101699443],[128.30320974019691,-2.6178803631133012],[128.49223863011846,-2.7524576987155234],[128.78883687414304,-2.8374582638509294],[129.11347538043304,-2.8138863024666612],[129.33336261478621,-2.7347422826381917],[129.57349856220372,-2.5375465801687631],[129.69389893814801,-2.3372519941746219],[129.75127939339706,-2.1107091614442521],[129.73884562803229,-1.8674711435772886],[130.11986468188744,-1.670381879675582],[130.34758485755887,-1.3691706366810368],[130.41178117921132,-0.99706376547272235],[130.32349891799163,-0.63159028624715186],[130.13261446134516,-0.38283329499389496],[129.86078357373407,-0.22654866674414509],[129.20903829921937,-0.13758837127847601],[128.9032464244919,-0.20431357135369699],[128.70372096640415,-0.32923607366483665],[128.51013252879662,-0.57517191854944461],[128.42452246516746,-0.89223797988675679],[128.27038213547345,-1.0672631676911652],[128.17183639596331,-1.2843893451267194],[128.13810859054402,-1.6889428543445295]]],[[[131.92888979438698,-7.1058126123396308],[131.96870229889356,-7.250745883874397],[131.73831769722457,-7.4411785759194267],[131.34273094913746,-7.980955286532823],[131.17505267532593,-8.1305193337525967],[130.77559678044926,-8.3487761977278758],[130.53114245293236,-8.1054466389680879],[130.81511526155816,-8.0141663038527238],[131.00626773151416,-7.8682040061204228],[131.26070622677551,-7.4709770007434395],[131.5612661877945,-7.1366491479529017],[131.92888979438698,-7.1058126123396308]]],[[[130.00086542304604,-7.986180993885907],[129.71392986196412,-8.039758850454767],[129.5189630575982,-7.8137704542096582],[129.81247984482093,-7.8206421483172148],[130.00086542304604,-7.986180993885907]]],[[[128.85123632349487,-7.5253459032743022],[128.54711580051259,-7.6074257972594239],[128.35224867832588,-7.7439928959203144],[128.20617165226318,-7.931836634710467],[128.11837089485294,-8.1702231049027016],[128.0233312786149,-8.2543985453571391],[127.82159187260662,-8.1894288523474099],[127.71066829764499,-8.0029979089723575],[127.54005832683606,-7.8393730766443355],[127.17499533295977,-7.698169372214152],[126.75301587051651,-7.7585102831163129],[126.47173290006066,-7.9493976054280369],[126.0987704813345,-7.9133270192668661],[125.66751648155706,-8.0131531666459708],[125.97563250944789,-7.664375738321989],[126.2538973580012,-7.6701128579146154],[126.60945653677817,-7.5728170411618443],[127.00570618803249,-7.6393686918962178],[127.37089395977837,-7.513841480775894],[127.70149336624095,-7.5958620331106452],[128.01868598367449,-7.5725867060887229],[128.30174362166778,-7.4275650836457743],[128.62753908008855,-7.0698471413823807],[128.85123632349487,-7.5253459032743022]]],[[[120.6369599939121,-6.8948504788980669],[120.7807985913832,-7.063424950348379],[120.69616517067332,-7.2218705784187538],[120.64181887383157,-7.1154781144511521],[120.6369599939121,-6.8948504788980669]]],[[[125.39589446378336,2.6297103671470281],[125.36109352568164,2.7467345507827847],[125.43770642337761,2.8807015757353365],[125.44556703985633,2.7631047776031425],[125.39589446378336,2.6297103671470281]]],[[[125.53592964954107,3.2656365564667515],[125.45615699110694,3.6841870699600094],[125.46940070774694,3.7317391633749555],[125.76751914754053,3.609472226600595],[125.53592964954107,3.2656365564667515]]],[[[126.53486847803755,3.8835480179860098],[126.67631067984671,4.1325049247180452],[126.75800032425842,4.5468068846940923],[126.8119300101048,4.5361878546728462],[126.9199369927218,4.2908240327502343],[126.850272227921,4.0507015980204599],[126.85113634234385,3.7689831687683006],[126.53486847803755,3.8835480179860098]]],[[[108.88334414316017,2.8335751838438865],[108.78743795207856,2.8861737771752649],[108.76519836370635,3.0602878284607153],[108.88486327620491,2.9983820164812074],[108.88334414316017,2.8335751838438865]]],[[[108.40961678431084,3.5561263825047602],[108.10119870613333,3.7051044326440179],[108.00312103232352,3.9830199629967242],[108.00444025513289,4.0421210180243436],[108.2478176753488,4.2159293353179859],[108.39206700762527,3.9858668350436668],[108.40961678431084,3.5561263825047602]]],[[[105.69310024415653,3.0614953619137624],[105.94147311739447,3.088051355212178],[106.22392124882498,3.2285226026723004],[106.28452738775042,3.1570521117973764],[106.28277504177062,3.0891106902141678],[106.00732069116145,3.0252559077539787],[105.7599508864846,2.8640014900339139],[105.70728276215004,2.889137195668749],[105.69310024415653,3.0614953619137624]]]],"type":"MultiPolygon"},"properties":{"alpha2":"ID","radius":1991000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-10.389990189259278,52.134977034923125],[-10.352484365451229,52.208801582445304],[-9.9525578224876412,52.339122286453716],[-9.8986156655020459,52.441340470610783],[-9.9162839626291834,52.569608490737181],[-9.5260938690230681,52.779405872788494],[-9.4247828891071279,53.046206252497285],[-9.4708042313180467,53.193104471960311],[-9.6253219805170946,53.303179304403812],[-10.091032107184104,53.413048606546703],[-10.116701661150374,53.548382488799284],[-9.9670107482938874,53.647120412682263],[-9.9454821569053351,53.813554013934777],[-10.06074806219052,53.943782401934008],[-10.264960629619429,53.977818324769963],[-10.143981795067123,54.051015593077693],[-10.089438124288328,54.215722767230332],[-9.9959243444886994,54.275742301161067],[-9.3199556037415228,54.298810678898569],[-9.1498499660110308,54.24061841644005],[-8.6756399686272214,54.298529095764344],[-8.5925027817935113,54.393610253169712],[-8.580240601523963,54.528540061455288],[-8.6413743949491924,54.628004495256469],[-8.7634685391492813,54.6813338824455],[-8.4790972221905641,54.840229116162064],[-8.2744805545528379,55.146053682195571],[-7.7576383350281466,55.248759307866166],[-7.5380233385424287,55.245457820545823],[-7.3137832015166619,55.365329854782743],[-6.9622035948751506,55.237818567889768],[-7.368514422255994,55.022417322089495],[-7.5505368477417916,54.768171189872795],[-7.7071457520394064,54.68417402910989],[-7.8885554953508326,54.468151357478476],[-7.866743711969665,54.278246972788892],[-7.5876156912659543,54.141597032985615],[-7.3641026250574031,54.139951075627692],[-7.2583058648281531,54.194951369470232],[-7.1373096345482727,54.352414588975662],[-7.0077691210635926,54.406458637429573],[-6.5540429024391766,54.079180103369559],[-6.2181216237211911,54.088462457159928],[-6.1572352339861469,54.017316108298253],[-6.2334147934979285,53.935003019169791],[-6.253004279440006,53.82195722059565],[-6.1420498835673527,53.577448527109993],[-6.1320549726263422,53.313194494892208],[-6.0276682351425555,52.927172227766597],[-6.322615957020135,52.414876898254008],[-6.3295539983599332,52.244905734831505],[-6.5562544109132253,52.189362213008259],[-6.7463454901751154,52.21177730304764],[-6.8902634261150171,52.15951825287965],[-7.4407362485401238,52.121990628276158],[-7.6296768820355441,51.991504121702278],[-8.8135199722474713,51.58514525707816],[-9.737325859295872,51.474038933853606],[-9.835184730506807,51.483572578357624],[-9.964607562522108,51.601064081954618],[-10.120534864519058,51.600927317495859],[-10.19070641860405,51.752274618808244],[-10.34092850429294,51.799193096907686],[-10.378444319203119,51.868728004083643],[-10.336824360254688,52.03303831780385],[-10.389990189259278,52.134977034923125]]],"type":"Polygon"},"properties":{"alpha2":"IE","radius":237000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[34.245379843922031,31.208283181997299],[34.330698541863477,31.278430644069612],[34.334226936166964,31.281621466041678],[34.337436116738999,31.285133170937367],[34.340297032907323,31.288933969523995],[34.342783786616025,31.292989455587414],[34.344873866864134,31.297262917389602],[34.346548353483435,31.301715669996707],[34.347792088410735,31.306307405469155],[34.348593812904134,31.31099655774382],[34.348946269461301,31.315740678905176],[34.350255249503078,31.362717053853764],[34.493947383564993,31.496251075222098],[34.497332352706032,31.499714917954947],[34.500366316799209,31.503490005297639],[34.503020809709724,31.507540917534623],[34.505270925686965,31.511829647026282],[34.507095553042483,31.516315954815234],[34.50847757223012,31.520957748167877],[34.509404016470036,31.525711475509031],[34.509866193409302,31.530532535044081],[34.509859766677863,31.535375693234847],[34.509384796574459,31.540195509202761],[34.508445739500871,31.544946761077398],[34.507051406149785,31.549584870290182],[34.505214878838558,31.55406631983233],[34.502953388764546,31.558349062552693],[34.500288154333546,31.562392915664699],[34.497244182078347,31.566159937760901],[34.49385003203524,31.569614784797828],[34.477422773770869,31.584867041155679],[34.483856632684301,31.592382245158976],[34.486147296362574,31.59566044337884],[34.676054983801833,31.892015634174296],[34.678248379998053,31.895791958322491],[34.680103892422643,31.899745271437848],[34.802655345887771,32.193572880076793],[34.804623430996209,32.199220823892681],[34.921532074177264,32.612849211987786],[34.922338329375272,32.61523510044799],[35.005434516074082,32.825536089227022],[35.006381708807353,32.82764274035501],[35.074734408128684,32.962613536888654],[35.076806022032947,32.967281474453308],[35.078390670951165,32.972136380537037],[35.108636285761989,33.083639795404999],[35.223337186791468,33.091918740075677],[35.306240554359483,33.079926159414718],[35.311558867343159,33.079440300100735],[35.397708815254575,33.076193236224299],[35.402991632093013,33.076273466535099],[35.408236489420304,33.076910696687918],[35.413384824018316,33.078097811475203],[35.418379150425913,33.079821555767623],[35.423163702812204,33.082062682518647],[35.474830807196149,33.10968210601829],[35.479053143431059,33.112214067764299],[35.483006000491883,33.115149008073857],[35.486651053131105,33.118458470994213],[35.489952960444079,33.122110369358147],[35.492879708519574,33.12606929588771],[35.495402920833328,33.130296866488379],[35.497498133374897,33.134752092405165],[35.49914503184047,33.139391777632419],[35.532565409535614,33.2504480132189],[35.579296348657351,33.271422747315967],[35.585826012001782,33.269018500410148],[35.590848049825297,33.2678101403963],[35.595967954824566,33.267126349037504],[35.601131085329932,33.266974424028056],[35.606282338351882,33.267355986772309],[35.611366737662138,33.26826696508035],[35.616330020521943,33.269697636628081],[35.62111921679562,33.27163273271772],[35.628266269475979,33.275596545420491],[35.73185945089665,33.331214196101278],[35.736901667023297,33.334319284367837],[35.785442292498509,33.368333479567312],[35.789402786958014,33.371416326166766],[35.840757629426641,33.415624115343299],[35.869052003255355,33.431625918644457],[35.851796370769407,33.371274283862796],[35.840076539858181,33.338717378307379],[35.838784661715415,33.334599980073442],[35.837852621153736,33.330386523328215],[35.837287360726066,33.326108393128997],[35.837093090925585,33.321797456270588],[35.837043263366162,33.295114798200167],[35.837251056832599,33.290466776175329],[35.837889996015591,33.285858192489584],[35.838954548395236,33.28132895247618],[35.840435496103225,33.276918274438252],[35.84232001573961,33.272664350060907],[35.844591789409527,33.268604013712682],[35.847231146018437,33.264772423499849],[35.858145641907704,33.250407380703905],[35.859279805886025,33.248616995858164],[35.888434834123196,33.192463091818155],[35.906573119485721,33.135706217233967],[35.880740181178489,33.10405547546641],[35.877905832695554,33.100236165929083],[35.875447197761162,33.09616483065377],[35.87338652275001,33.091878308151429],[35.871742453190869,33.087415384003599],[35.870529865056938,33.082816439920208],[35.869759730164176,33.078123088355326],[35.869439016895363,33.073377795986694],[35.869570627148413,33.068623499466078],[35.871460201102607,33.043561781757731],[35.871952712419592,33.039383665329588],[35.872794719519952,33.035261742437605],[35.88001230317434,33.006219560590168],[35.881639594318081,33.000938017852498],[35.883839827226012,32.995868328975732],[35.886586121414922,32.991072430896722],[35.913410012814325,32.949609388882479],[35.857036675051518,32.86266275528407],[35.80598339611096,32.788855001218771],[35.803227967919476,32.784388434526996],[35.800955892178564,32.77965765506076],[35.799192200439848,32.774714782011479],[35.78726056599006,32.734964825168589],[35.740713012063551,32.729615643498214],[35.736598819818063,32.728973442067335],[35.732552331078267,32.727991133872621],[35.728601610672293,32.726675531811004],[35.619176425115761,32.685099163764754],[35.614052090522129,32.682815466470714],[35.609215847997959,32.679972081761306],[35.60472858405371,32.676604806801791],[35.598311450126054,32.671200904546922],[35.594705757903036,32.667847554533701],[35.591447389040511,32.664155806292825],[35.580725669525094,32.650729508881639],[35.577836337658198,32.646719914705834],[35.575356958163667,32.64244466484017],[35.573311754662555,32.637945528619653],[35.571720708861868,32.633266462756488],[35.57059936533259,32.628453181881547],[35.569495174535867,32.622365976207256],[35.568845076770806,32.61734717564039],[35.551411252001593,32.395571363845335],[35.485491396106823,32.401557779314437],[35.484390851669382,32.401708700317883],[35.483413326830451,32.402236355885037],[35.402682804400477,32.450673201229719],[35.392988009217184,32.476325194687853],[35.390962800037627,32.480983608006184],[35.388475395740045,32.485412502532554],[35.385551468612121,32.48956616802414],[35.382221196256474,32.493401734853514],[35.378518950130172,32.496879616462429],[35.374482940799851,32.499963917931304],[35.370154823573714,32.502622806447626],[35.365579268580682,32.504828839849701],[35.360803499733734,32.506559249854853],[35.355876807336003,32.507796177048945],[35.350850039359699,32.508526855211862],[35.305033773398563,32.512824528400849],[35.302600636921056,32.513174215052885],[35.211602724105525,32.530859321783822],[35.206546729216811,32.531576285338296],[35.201443974441617,32.531773806874845],[35.196347685841538,32.531449826076262],[35.191311022030462,32.530607722337152],[35.186386519685172,32.529256279513966],[35.181625545543163,32.52740959430195],[35.17707776060405,32.525086929194629],[35.07910961863994,32.468566847292266],[35.074719860188289,32.465718136548162],[35.070651886691508,32.462426218597052],[35.066950258946335,32.45872715328219],[35.063655524778795,32.454661460360605],[35.060803774881137,32.450273675647495],[35.058426247472504,32.445611863168573],[35.01288199856549,32.343422974939919],[35.010859698422358,32.338085096993268],[35.009458968078896,32.332551509246869],[34.999474548300974,32.281064948101076],[34.960801798033458,32.174298628124731],[34.959430754790539,32.169892955253189],[34.958471678699723,32.165379654235416],[34.957932737142912,32.160797159824796],[34.957818519684537,32.156184496017708],[34.958129998987367,32.151580943729641],[34.958864522529403,32.147025706283451],[34.971058070523853,32.088681894360612],[34.971511183624465,32.085507708589311],[34.978690018877906,31.993127433880595],[34.989689843377413,31.913284074287979],[34.985597767366201,31.896262475092101],[34.984657565020292,31.891348072523947],[34.984213241757189,31.886364307893569],[34.98426924708923,31.881361089283924],[34.984825020171556,31.876388519592698],[34.985874995418499,31.871496394795237],[34.987408658238039,31.866733705280993],[34.989410650326384,31.862148145257255],[34.991860923468067,31.857785635132995],[34.994734940301413,31.853689861665774],[34.998003920038968,31.84990184047674],[35.001635126682153,31.846459505314723],[35.005592196843871,31.843397328182629],[35.009835503896376,31.840745974130204],[35.014322554797538,31.838531994170147],[35.019008415621855,31.836777559392729],[35.023846161534792,31.835500238941535],[35.02878734670427,31.834712824073733],[35.033782489443738,31.834423200066706],[35.03878156772835,31.834634267253847],[35.043734520122165,31.835343911980225],[35.053223044569286,31.837881084092697],[35.125711588037731,31.817159931066961],[35.128446876891971,31.81600761449446],[35.197999646834695,31.776283760848884],[35.203642439380616,31.750040104888011],[35.1576478638273,31.735778585021436],[35.15349192545407,31.734291585379562],[35.149483324129854,31.732443855117733],[35.039736000192548,31.675855391212558],[35.034848176147818,31.672972821651271],[35.030318445825174,31.669554989828015],[34.961689446852446,31.611376497700022],[34.958347611408016,31.608270676803649],[34.955302781962509,31.604873177543048],[34.952580308532831,31.601212286121179],[34.95020285733024,31.597318481637473],[34.948190222050663,31.593224182331195],[34.946559159080373,31.588963475680302],[34.9288993477875,31.535830110933198],[34.882843279200713,31.421845325269885],[34.881250699076979,31.417279713407464],[34.88010658121344,31.412581616192302],[34.87942162591434,31.407794972340184],[34.879202239189361,31.402964548694822],[34.879450472841718,31.398135521548216],[34.880164005278765,31.393353054131698],[34.88133616322461,31.388661874229076],[34.882955984131534,31.384105855862312],[34.885008318706717,31.379727608961979],[34.887473972595203,31.375568080860017],[34.890329885894111,31.371666173331914],[34.893549348819263,31.368058378769806],[34.897102251507029,31.36477843888925],[34.900955365615324,31.361857029161524],[34.905072655089988,31.359321471922851],[34.909415613190163,31.357195480843625],[34.913943622620607,31.35549893914753],[34.918614335402914,31.354247713654683],[34.923384068932826,31.353453506388],[34.92820821451955,31.353123745130642],[34.933041654586312,31.353261513958039],[35.095284987036905,31.365757528199811],[35.101104760746637,31.36655179642181],[35.106791232143216,31.368023078548688],[35.273914694059364,31.421917372472112],[35.279285581323606,31.423997963023513],[35.408702823384751,31.482843879050389],[35.450518964085653,31.479245618668447],[35.424271256991538,31.333268929643175],[35.423676419034827,31.328843986519477],[35.423518063635406,31.325271928802639],[35.423254923998989,31.323696659409254],[35.405282855987785,31.249504276080927],[35.404343950880431,31.244547016434151],[35.4039094150491,31.239520372959873],[35.403983673105543,31.234475528884264],[35.404565968925539,31.229463852759032],[35.405650373348038,31.224536375407016],[35.407225844548201,31.219743270306598],[35.410015273644419,31.213490299550131],[35.439191495117427,31.132422281600665],[35.383522282560683,30.983648650552574],[35.382336184822215,30.980971765473996],[35.321365931626275,30.862628882717889],[35.319139411357618,30.857659858210464],[35.297437567360873,30.801375515455039],[35.236162529483586,30.672390710068768],[35.174824146109948,30.525838862741598],[35.173383907422398,30.521952961858528],[35.144695852380345,30.433456183877972],[35.143359980209325,30.428471755040505],[35.142544963978828,30.423376185019681],[35.142259484943544,30.418223749962984],[35.142506583921708,30.413069331723445],[35.143283628905408,30.407967833278246],[35.147905542781643,30.385488524880223],[35.148088084370407,30.384322757871423],[35.148047374598988,30.383143488239412],[35.133127420994555,30.201523549865943],[35.132959569688104,30.197394804836193],[35.133133212489291,30.193266299324591],[35.133647163437963,30.189166230544178],[35.141540321409757,30.141707081811486],[35.070643324627135,29.983411394239688],[35.068513625243874,29.977772420518281],[35.067077970993019,29.971918145075005],[35.053151894514194,29.895932657544549],[35.023670545962155,29.785887550658533],[34.973377617801908,29.555059772230244],[34.904314848172923,29.477445904673065],[34.869552699246341,29.564772177930923],[34.79120050642446,29.811834334328932],[34.735304827113119,29.981284828306951],[34.659612009270077,30.188666244147175],[34.657252617159607,30.194103815484812],[34.529738044784793,30.446062250975785],[34.518305458215558,30.504634448437603],[34.516940062166682,30.510035461755688],[34.489372010357329,30.597770991876008],[34.401354458184066,30.826848506917489],[34.328347432221925,30.995450673300898],[34.245379843922031,31.208283181997299]]],"type":"Polygon"},"properties":{"alpha2":"IL","radius":283000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-4.7849117477735428,54.073200584413051],[-4.753051595059822,54.111707031259506],[-4.7423259005022169,54.128598330307319],[-4.6985462738735695,54.224726953592949],[-4.6452900105675781,54.25168478524887],[-4.6257935384144764,54.262868413902048],[-4.6076750326680411,54.276169005083844],[-4.5911633237036238,54.291418576990729],[-4.5085287784208479,54.376533401361776],[-4.4246818690759477,54.406931294776086],[-4.3956259241298667,54.402726789007808],[-4.3773773347930014,54.39243178696767],[-4.3382692873260602,54.269176518338789],[-4.374053068735992,54.23855276308435],[-4.3885320831077106,54.222380843845336],[-4.4011732271574679,54.204735042470425],[-4.412221991808055,54.185497493142428],[-4.6142992610750966,54.058976823341851],[-4.6700288113259898,54.073387916822419],[-4.6875297440427248,54.07582958366303],[-4.7051778856731881,54.076716984309485],[-4.7228354728900541,54.076043191615113],[-4.7657703269494549,54.069630200075359],[-4.7849117477735428,54.073200584413051]]],"type":"Polygon"},"properties":{"alpha2":"IM","radius":26000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[68.165318955335891,23.857205004347264],[68.282603905075177,23.927762338340163],[68.6522045237448,24.011197295711693],[68.781158524959849,24.313437595577621],[69.559117508949484,24.272890414026971],[69.818228057355114,24.165858743560847],[69.978020765409624,24.182716819169009],[70.098404169642237,24.287287455945581],[70.489290072105746,24.41198985549606],[70.546651699054692,24.418066999547456],[70.610648516421833,24.32917135529317],[70.718945344957149,24.282268027797254],[70.92569255128393,24.378471252170709],[71.001812400049189,24.785783697718582],[70.652327895345493,25.42296778122245],[70.631019034562712,25.573771550650942],[70.528893226487739,25.66691467531405],[70.264776951975421,25.706726439179608],[70.100439189321236,25.910182966303488],[70.077955814676287,26.071932801285552],[70.150935646712739,26.391404545893462],[70.100138392578344,26.517252003041737],[69.736003850281236,26.627227496190059],[69.481482274653558,26.771132273402223],[69.539525084222603,27.127143446936209],[69.895449016402964,27.474357809197492],[70.144691047733474,27.848892435820517],[70.403766132911286,28.024806843385399],[70.573288091736572,27.980654258134937],[70.694347804250341,27.774440573161943],[70.832519669480646,27.714188244695531],[71.831371731449664,27.966892248583935],[71.948242351008972,28.177154519235842],[72.166387788399774,28.40270830724597],[72.342018479976716,28.751741426992997],[72.932705579414531,29.067624150899078],[73.22701875926802,29.543129149737556],[73.381822295497585,29.934198787346553],[73.842354906880985,30.12335515852978],[73.898310687693026,30.226710526687242],[73.899560435991162,30.4352019585872],[74.33586492057961,30.890040722076627],[74.51361221278178,31.00050898778947],[74.587639576597738,31.470893864720995],[74.528498592553404,31.769689690496644],[74.640267687280144,31.892324179783007],[75.089095567562865,32.106486411257485],[75.158610866892502,32.284992956942574],[75.046143700610557,32.440067455819147],[74.68586915702808,32.494013371698998],[74.595545310249832,32.704026607655244],[74.499704335755851,32.763192770859391],[74.350657982917994,32.772042862741316],[74.267863514736376,32.977164930505467],[74.003980432850966,33.189569666097405],[74.110602954488215,33.495106581182029],[73.977801748633041,33.667912542095202],[74.010548537521913,33.922585847857],[73.904352540870789,34.075749881188273],[73.897564368597415,34.219969319854115],[73.794764492839064,34.378211559619928],[73.961402329104857,34.653238351872517],[74.300411855672806,34.765125996882844],[75.187448618283852,34.638775629512601],[75.463473946312362,34.534290326536933],[75.708608082754481,34.50620769850935],[76.041084596767107,34.669647529441093],[76.642402034545,34.759893050399867],[76.783026673088287,34.899997004507838],[76.980223244980053,34.990787888531358],[77.048848966471709,35.109719178949128],[77.799399445257563,35.495636489451329],[78.042455667910701,35.479595545969978],[78.02361833469584,35.237745219506202],[78.30053832084964,34.645325834233866],[78.670712998876567,34.517974292435042],[78.969928327493179,34.30256219736237],[78.970400408677889,34.22833331439513],[78.803544963291785,34.107089485233367],[78.751077011800902,34.007887822310956],[78.79880344377392,33.549745484998539],[78.857086625707751,33.439738116624419],[79.112311151808271,33.226096612304708],[79.233553126307257,32.703079146922214],[79.219105594628701,32.501258933049208],[79.066859016948129,32.388360892258312],[78.919004767683688,32.358437056684508],[78.731232800063808,32.413875208856176],[78.57885707822615,32.278847560728018],[78.590462067493959,32.127821007698302],[78.733710540622397,31.962512255313531],[78.705857152683123,31.762168310779028],[78.828499612053747,31.523372864714425],[78.92875980674377,31.435848281131534],[79.107000415228043,31.40246134317665],[79.339585222514586,31.106502129124678],[79.501831177048786,30.988348643025443],[79.794557007769811,30.967974852706853],[80.14934891151853,30.789578872939995],[80.285939606010444,30.561514890210219],[80.678123901542847,30.416704742187225],[80.98705770306448,30.232401075441803],[81.009919313332659,30.164808245154134],[80.837581824469765,30.132107319642433],[80.402875499931113,29.729569787624389],[80.2638566468938,29.442198341079042],[80.23281414512428,29.194724646072732],[80.085145611928368,28.993080639291612],[80.107946792566509,28.823173688887181],[80.340585124414631,28.658465770104904],[80.586885813913739,28.649305714228674],[81.164115979949813,28.337373352421352],[81.323757467574779,28.167949850682305],[81.779016183681051,27.903146574589936],[81.992447697192091,27.912499091777953],[82.434763368301901,27.684371068356061],[82.677173506422974,27.673243167941099],[82.821385695896481,27.49739983165572],[83.241821332703921,27.398771353113176],[83.447196678145303,27.465081371805201],[83.775146511036525,27.395645648190065],[84.091014687872274,27.491117068513454],[84.610015723662087,27.298510885783369],[84.723656947465201,27.032736227846012],[85.243089117204136,26.77401451954427],[85.648324276185974,26.828762975897043],[85.843825730097819,26.630145720213086],[86.007326154317113,26.649095905362138],[86.361903597731001,26.575211426084259],[86.705751719735701,26.442681179086225],[87.016385650770005,26.555148099879872],[87.119053666082763,26.422383028792225],[87.275591584682417,26.373852786626266],[87.413604696030802,26.422734666494925],[87.947741901622649,26.429154123688711],[88.074358804305518,26.507712703266549],[88.156702152428011,26.796580073977442],[87.9846703931197,27.133941172784532],[88.136457305584273,27.733570385726463],[88.100470085965384,27.908911547901891],[88.578009230690654,28.093113500831087],[88.803481625162036,28.006774777241109],[88.8485941027634,27.868617304743548],[88.759468253883256,27.502367527821402],[88.891108295820374,27.316001552447773],[88.822080866298322,27.102100680095575],[88.890860541151838,26.9570248207073],[89.11037900511792,26.834511221471455],[89.332102152337043,26.848397331740525],[89.781695973056898,26.704572250010084],[90.091089035507977,26.75288574667805],[90.345893836576053,26.890064961874774],[90.727306344920578,26.772563429808333],[91.265210469114564,26.795706756200566],[91.431855415635582,26.867055631648412],[91.652594690206342,26.803520053249972],[91.873093231924756,26.862161333146521],[91.988657904563865,26.983513617633651],[92.016908184205221,27.293694895333854],[91.923906488034518,27.412952591864631],[91.743140551935738,27.44277897072174],[91.594947116669104,27.55779517290598],[91.632121764405028,27.759715415158571],[91.938998574432333,27.731917776213152],[92.101342267095646,27.807397918240149],[92.533907200163029,27.872765739551465],[92.617447627121436,27.951918296406546],[92.698427787878188,28.143382629100287],[93.046109471993532,28.337512795946115],[93.252059473137166,28.629279640393968],[93.768485561850852,28.733803801875883],[93.960595687141421,28.851435573056953],[94.017991066360565,28.959331195956135],[94.623087056582847,29.312115146949491],[94.831328824308542,29.170312496492638],[95.337866979855477,29.049460278580977],[95.456714015942509,29.102454521027177],[95.520147199167013,29.208738087362839],[96.035337346546513,29.446935503453243],[96.216548876596946,29.285471294496212],[96.355593651707721,29.248958077812123],[96.374374736263533,29.121956932374278],[96.466876025551556,29.022149805873035],[96.58057383994381,28.763704671267792],[96.532497433971542,28.62143773269818],[96.597894216148077,28.485000078467106],[96.787735047125381,28.370254432568174],[97.080074639048306,28.367023161076894],[97.322213093870133,28.217855111188463],[97.334948491604507,27.937850170520338],[96.955632346608098,27.692111376547441],[96.8862546134079,27.570317392086423],[96.905483404777542,27.441240277192719],[97.101839114548014,27.115607836826829],[96.953519688054868,27.133511234831165],[96.695101149350492,27.334510727215331],[96.178788416485546,27.25717029087761],[95.469198564414754,26.75982989409875],[95.113949284681397,26.567980870106464],[95.051739287498435,26.347285057847476],[95.132205137541234,26.041317954174616],[95.022689479158316,25.905149033258915],[94.942387947929134,25.696205443385068],[94.611877357985776,25.380862319347724],[94.600791885561236,25.24869599750631],[94.705680319851794,25.043587530534019],[94.583941649498058,24.767318790614222],[94.293713714552908,24.321584330765099],[94.127481609173216,23.876682982441448],[93.654721222244916,24.003089274054716],[93.492983488290804,23.963455628574504],[93.409179138042461,23.878641208106998],[93.365736380397777,23.132584567973339],[93.307880235064957,23.030559860662077],[93.182557548658224,22.94622445852001],[93.086592731585981,22.727151703175654],[93.15095769935364,22.230738370052904],[92.91454874849093,21.990284040484255],[92.743375835600858,22.045300212844172],[92.575054864890461,21.978418719613664],[92.492725432311232,22.665418400351275],[92.361880448572094,22.929132793687099],[92.331117478702694,23.332323856641334],[92.239009614707413,23.548822299431791],[92.092806721215155,23.597616683558407],[91.96887725795041,23.542339084579513],[91.777410775009102,23.32894028776899],[91.750813783931122,23.053635009021981],[91.61951290610709,22.979941641374129],[91.315450558582782,23.104503822686304],[91.165839240393467,23.581128721217443],[91.232230951163359,23.920341817640075],[91.367238052698553,24.093320673078733],[91.534170137732033,24.105456038476781],[91.836523080072681,24.229262099454957],[92.090228656765547,24.440665445555016],[92.227624155660749,24.783328182313539],[92.242839241978245,24.977366100907965],[92.182386106948286,25.102112473193934],[92.029602249382336,25.167901304426206],[90.439365539375714,25.158016767773685],[89.833370980302959,25.293013969711659],[89.796490973776258,25.37582615120164],[89.797809766233641,25.849159984204881],[89.699889336629013,25.986028782883032],[89.362975226084771,26.008745877268215],[89.049381861413849,26.234346159731214],[88.828063166721179,26.252461396711176],[88.542092807051432,26.371452383622991],[88.41041511828071,26.338158239733822],[88.225233033023102,26.16738344617422],[88.129822314303496,26.017782013243703],[88.166003144504074,25.855200533370578],[88.503015771085273,25.537979834805753],[88.769020608230434,25.49024332865557],[88.951413046437096,25.259315730463459],[88.824098564444796,25.17791948854633],[88.478180843401432,25.148249651077347],[88.313248213006929,24.882097753014595],[88.111065645571628,24.822256227726946],[88.051475203405474,24.672007122625313],[88.147294650814572,24.505608121843771],[88.723326812163435,24.274754487132885],[88.700864617873847,24.007631175554284],[88.58609586416415,23.657095029838821],[88.740560218832343,23.436554257537718],[88.773668587618474,23.292720947113263],[88.927815355890147,23.186553578119671],[88.859186131141655,22.992610847614607],[89.049822749347086,22.274574646109699],[89.016911134576461,21.872237962118501],[89.051396889626901,21.654307801407082],[88.855600047829924,21.646980372971786],[88.745044766000845,21.584702295705096],[88.606919209469609,21.652186766992564],[88.248937032785904,21.622803176307844],[88.122184728719049,21.63605008514519],[88.009664182905539,21.72844178896208],[87.893660003194569,21.749467234926357],[87.093155478855351,21.492172377144751],[86.862057071686763,21.224898015537303],[86.849918137847723,21.097270373014801],[86.975216833492226,20.700131246632999],[86.799844235958091,20.477714463181645],[86.750228658901761,20.313379447116624],[86.510212044070926,20.173714610848361],[86.279369766903386,19.919616450215745],[85.516333178767326,19.679370248758353],[85.210288554947354,19.495462673675753],[84.803464646210386,19.151452358648154],[84.690651686335315,18.964838790949287],[84.103945062846506,18.292860262833202],[83.654950021136798,18.068853316468182],[83.198232388444467,17.609120102469959],[82.375013583384145,17.106887515804523],[82.305519443991528,16.96316040264983],[82.359514501614242,16.782817945407768],[82.254298670999745,16.557010517957035],[81.761878833115134,16.329732844209524],[81.312624054276085,16.334438070842531],[81.233782788274752,16.24739460237803],[81.131943361767156,15.961900411423651],[80.978529389039196,15.758597440268879],[80.826111993303996,15.766179092636854],[80.744667896002753,15.853697380275845],[80.630531398590733,15.879360594330876],[80.308487628229855,15.723031044878866],[80.10594324963364,15.331576609036265],[80.056540619868159,15.073982611391104],[80.178462692236806,14.478312103523493],[80.122653829395986,14.15972199421064],[80.342135634089999,13.361218966050982],[80.22758027618822,12.686117843685555],[80.142913750974188,12.452079569506036],[79.866536663906103,12.004863414145431],[79.76938140489365,11.67689960432728],[79.848430715530625,11.196856003678587],[79.837939457142198,10.322759512714434],[79.503652413886726,10.324080219490014],[79.330234803060435,10.265607379533467],[79.263315354481122,10.164632579423325],[79.257585478579557,10.035276397089868],[78.946692623724687,9.5776597970695487],[78.945175069106071,9.4445173642157823],[79.011719930893818,9.3510745550005776],[79.281022997889238,9.2823965641968371],[79.410765834514635,9.1928041037187604],[78.99117226556011,9.2666561127099278],[78.406748575885999,9.0923807127902876],[78.193194624980137,8.8804980038228809],[78.126101223479381,8.5114024288889336],[78.059967908530567,8.3847389621109443],[77.517537715435964,8.0785579863268442],[77.301597843150532,8.1455784508813167],[77.061569008482877,8.3199302491542397],[76.553623021332285,8.9029253480392505],[76.324869544967356,9.4521649863092794],[76.179538363144232,10.22946328429914],[75.922688874416735,10.784129894862291],[75.733173511872863,11.338234126137452],[75.51755647239068,11.709359295845838],[75.196898167227289,12.05767376061865],[74.945720560800424,12.564631967261141],[74.769405235319951,13.082714998079064],[74.664296233630139,13.686885964100796],[74.498704073540992,14.046405753686422],[74.372254343231006,14.510192176933828],[73.949399130149601,15.074843347929647],[73.883707279525083,15.295325871117344],[73.801037950358392,15.397028527455136],[73.759224633645999,15.611695129419413],[73.476302894911413,16.054351249657593],[73.337913946629499,16.459942907491129],[73.240946158355726,17.185133053147212],[72.875678317436552,18.642888668230132],[72.900205921362925,18.86222765972909],[72.803265899938083,19.07933271161605],[72.796411213417983,19.327359582663881],[72.668037479181507,19.830967374961862],[72.891755391380471,20.693684174329952],[72.826924768212464,21.029112160847369],[72.692556258516916,21.177758399438009],[72.589408312550773,21.520540145221386],[72.484064296619309,21.605333983882179],[72.33074679775325,21.595927328253712],[72.015118156314458,21.155876625967036],[71.028663813557287,20.740294439308379],[70.725316229361994,20.739462715825677],[70.485200986616348,20.840439839167438],[70.038699376497675,21.174895305564494],[69.006652421893733,22.201917628625868],[68.982808770776344,22.38058418503913],[69.051714239147472,22.437032722511553],[69.314459952905636,22.31820928027107],[69.600253210665514,22.437649792263546],[69.674527090506601,22.571939179331125],[69.631780442723752,22.719326332125942],[69.53566307224655,22.785006552494817],[69.232198172058261,22.850366895118185],[68.645253786343602,23.186441446958575],[68.4230737112306,23.543449615242416],[68.23511087324917,23.597224318065166],[68.165318955335891,23.857205004347264]]],[[[93.597512144550549,7.318777148766701],[93.692439507464996,7.4102550481652569],[93.858775285848495,7.2066958669413266],[93.929362198379764,6.9734837148776068],[93.828850933443377,6.7491115863646183],[93.658236391509774,7.0162043537475522],[93.656527455715391,7.2002338218247068],[93.597512144550549,7.318777148766701]]],[[[93.06451559680562,8.2750154439100641],[93.096904456301175,8.3489457913071696],[93.173657009504851,8.2085432345875695],[93.115361711186338,8.2187528127958362],[93.06451559680562,8.2750154439100641]]],[[[93.270861662668977,8.1442865693147279],[93.531348893431513,8.2136224595289029],[93.536667569630694,8.0567249668979812],[93.442402432068121,7.8780479512151178],[93.365180985335343,7.8768046647625551],[93.270861662668977,8.1442865693147279]]],[[[92.534166728388854,11.873396291559215],[92.719818758345596,12.287164385335265],[92.73748408391333,12.683003731028142],[92.679902779212881,12.939184561690727],[92.809972721754761,13.057880394100181],[92.857579628531909,13.358045312813616],[92.924781461809687,13.485671294457683],[93.062082032264968,13.54521552106204],[93.072670611521787,13.24752083770459],[92.942156380307296,12.955611792136372],[92.990000639854756,12.538586652824291],[92.873788920678365,12.317026705521252],[92.895043867487971,12.148841650622652],[93.017195564206062,12.036619823464383],[93.061736418170582,11.899884358952427],[92.929138623316888,11.920662026677521],[92.813477951555768,11.853191290688823],[92.692897712805006,11.381295818617911],[92.644542084952391,11.361575007933137],[92.596047255288809,11.386501932156426],[92.63965750467348,11.574630819684973],[92.534166728388854,11.873396291559215]]],[[[92.713512094119011,9.2047995150427226],[92.785564376687404,9.2403108565513801],[92.809024850447372,9.1734137709490877],[92.743647600192986,9.1311853048072216],[92.713512094119011,9.2047995150427226]]],[[[92.353122398438941,10.751099515055767],[92.510246688173098,10.897163554576334],[92.574089644292229,10.70427503533535],[92.47257295310699,10.52102419638959],[92.369782571847153,10.54759231257451],[92.353122398438941,10.751099515055767]]],[[[73.030567906486809,8.2777911943670262],[73.079481535289105,8.3161264862255493],[73.083358121427793,8.3109831283144615],[73.053190884653731,8.2569156545856561],[73.030567906486809,8.2777911943670262]]],[[[72.77264595464969,11.214231749585418],[72.782057808878349,11.243213990137324],[72.792752151740629,11.262405150866627],[72.773250166866788,11.196561059603942],[72.77264595464969,11.214231749585418]]]],"type":"MultiPolygon"},"properties":{"alpha2":"IN","radius":1909000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[72.351709537926524,-7.2631256736347085],[72.366995222446718,-7.2600897007561196],[72.385761534629339,-7.2544047608527222],[72.403880957429053,-7.2469094601885971],[72.421179321058474,-7.2376758460324453],[72.437490348018102,-7.2267926748878386],[72.445601487545815,-7.2207222981331052],[72.449385604029246,-7.2236667697390473],[72.489709821358133,-7.2584063704113797],[72.493318509326841,-7.2618491553919204],[72.494309675653156,-7.2667372006649975],[72.497685629515189,-7.2891773645714917],[72.498209005162025,-7.2948356862798391],[72.498084325844971,-7.3005167937150102],[72.492347606426506,-7.372953579207989],[72.491795739156743,-7.3773797567815569],[72.489719666643552,-7.3813276078826924],[72.471325261644949,-7.4127835861786053],[72.468569629709876,-7.4169882167360619],[72.464111944861102,-7.4193123232902449],[72.433624312815539,-7.4332795635869839],[72.429268991065115,-7.435018402707736],[72.428065313710761,-7.4304859034122144],[72.417673552408345,-7.3816918605695356],[72.412855122331209,-7.3633732761870192],[72.406325323315315,-7.3455926820743418],[72.398142725666602,-7.3285095647385878],[72.388380724790764,-7.3122771545328415],[72.377126882858747,-7.2970410512266479],[72.352802751793106,-7.2671793861072906],[72.350112724419589,-7.2635249782642841],[72.351709537926524,-7.2631256736347085]]],"type":"Polygon"},"properties":{"alpha2":"IO","radius":13000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[38.773834620178945,33.372091206756373],[40.685787060301983,34.330194209562762],[40.985202926150905,34.433806968080319],[41.191749722450666,34.778358499859259],[41.216566709599796,35.288160190704403],[41.355032825067546,35.668364246578058],[41.347702218339258,35.819315406426213],[41.24589825996047,36.073438680430961],[41.29625834255237,36.383219393220955],[41.416889184365516,36.514456013360643],[41.805846887719888,36.612078985549857],[42.359169051650809,37.108393208780356],[42.488350244317495,37.150521692351781],[42.741263241668165,37.361671223361668],[42.936361774005078,37.329161637534362],[43.092484206304341,37.367122323277933],[43.681425820431286,37.227115702457361],[43.835466919830509,37.228355405648585],[44.069264752405815,37.31146907350778],[44.18826279049496,37.253144874545917],[44.270723879588346,37.123367474218313],[44.377216289928164,37.082614697205678],[44.60599718603298,37.175816698067635],[44.730882432998854,37.165085979825896],[44.884931991723022,36.815110660400812],[45.019015027476406,36.698248228634391],[45.054826744238518,36.499703144167242],[45.240947412569184,36.355861251618819],[45.37398136173627,36.058706247810314],[45.765951404908989,35.835147356061363],[46.167432876920664,35.820364470702351],[46.273130535692793,35.773113973590895],[46.083967194934246,35.669394716165712],[46.010954347833298,35.554187989564653],[46.020506053424413,35.43703508852726],[46.154434236617554,35.196703622751528],[46.133584129946698,35.127807277075206],[45.922648558207108,35.025851279775402],[45.708884626162749,34.826131797725303],[45.661304347968553,34.612756283720408],[45.518645097577419,34.467879277890347],[45.54257004467101,34.215526819703406],[45.470486039740486,34.038528618273205],[45.492684274158691,33.908084100602331],[45.69371852922108,33.668738899789268],[45.879227870832644,33.609599201707368],[46.145658971044107,33.229576417665371],[46.145663696814594,33.093792057192019],[46.208962635314634,32.99364547026132],[46.564157101015368,32.836784366828923],[47.085021835772629,32.492161245785674],[47.371091788286343,32.423559526331687],[47.510376937852094,32.160973396207623],[47.829722768908717,31.794393693500368],[47.682633267556092,31.400022583369388],[47.701998617261175,31.102762347329168],[47.815542926110915,31.005435634294024],[48.010428960642315,30.989585691672886],[48.039018665716924,30.51021818720092],[48.378871039897184,30.234191537960609],[48.429209204089361,30.05957643014538],[48.545942769877797,29.962433848130324],[48.448855716259054,29.93945620462334],[48.130652132721828,30.035837678040359],[47.978661661329156,29.983077786592244],[47.655635555106421,30.096614125803907],[47.223489130580944,30.040947156361309],[47.108469418645925,29.950518427115153],[46.771899265728614,29.351029061057101],[46.52648896280494,29.095317833357527],[46.35207581124385,29.064017553430709],[44.695341237820138,29.200805260204753],[42.059210031943195,31.08811904177815],[40.39082522775341,31.930020549143638],[39.145637769135597,32.124704050350232],[38.981898049953521,32.47252190731902],[39.012848901413896,32.628893707164636],[38.773834620178945,33.372091206756373]]],"type":"Polygon"},"properties":{"alpha2":"IQ","radius":565000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[44.023537649478712,39.377358452381699],[44.345576342535594,39.434853953714672],[44.45617017559281,39.666628654694641],[44.587099820675292,39.768298949757629],[44.817079960644236,39.650219223471865],[45.151311949470831,39.254197341484705],[45.48997336373467,39.005967905509159],[46.178611665162975,38.87481522789264],[46.560236323026324,38.920532307412081],[46.85265248984674,39.148217089306961],[46.994633708835224,39.191845817457803],[47.472086922095372,39.496130516272068],[47.777804634792354,39.650096635643401],[47.995820753402626,39.683716847880376],[48.321834944375716,39.399120411699997],[48.230984014892421,39.269381749411984],[48.291825643407186,39.018924471203341],[48.184491194885737,38.870564708816943],[48.206246525355006,38.721467038681475],[48.599335449277632,38.425485404318394],[48.868531303925849,38.43528289835816],[48.955113659894984,37.907516313524411],[49.112521391927373,37.644143978436546],[49.453415504873931,37.500663038806344],[50.130386575255017,37.406927036007062],[50.370152467652005,37.126788268535179],[50.915307213656149,36.816467547847431],[51.741007062471155,36.618682801009307],[52.190037379804551,36.622617508570315],[53.824828035665789,36.941185590516639],[53.952962316885262,37.081934917636481],[53.914440982408088,37.34332260826335],[54.214562712965389,37.336957245612119],[54.678378651656864,37.462274168640256],[54.900268841271291,37.777734180390219],[55.227729707299751,37.982698314184695],[55.578438972536539,38.099567302588447],[56.183543944939181,38.082097967262314],[56.436537932156526,38.247922688737248],[56.663995237990207,38.256309579333006],[57.193504672419365,38.216187106445624],[57.430696748654064,37.955837185049084],[57.978830270625977,37.831039509174516],[58.304224860613878,37.651803010967065],[58.815424793583347,37.683236559963746],[59.278925283690924,37.521455693300929],[59.444522733491915,37.262491154618274],[59.952046533632846,37.039247876244261],[60.352371830781671,36.653762315581623],[61.119515720650426,36.64235826289778],[61.169678079367614,36.572196517988068],[61.195235358612479,36.00592720190491],[61.251919893053397,35.86755520494841],[61.278268520720815,35.513870700125153],[61.143760476940471,35.236989845538098],[61.079844573268176,34.85567064445133],[60.833878703273136,34.521774881973769],[60.889001153219574,34.319626407764112],[60.650982176990176,34.290601041653119],[60.505543573074327,34.109765098512533],[60.543264344516892,33.650575688361698],[60.632193573553458,33.575883177044787],[60.902083233810195,33.539917003815987],[60.916734050904992,33.50530127642844],[60.599652144879165,33.182296532747955],[60.566174783922655,33.044961268802894],[60.829103809160124,32.24938471905066],[60.78780997043544,31.891152134628289],[60.823801041610551,31.564779055518834],[60.95091727572521,31.472159311697506],[61.66004930206234,31.382206337906776],[61.756425479421964,31.280459150863376],[61.810945435944923,30.918201846280041],[61.781041860549188,30.828685990584749],[60.965755031801493,29.983982681591748],[60.926066082123967,29.820605359860764],[61.318160015226226,29.372463474848036],[61.610132554826905,28.81046204447944],[61.862038845691472,28.572082820750786],[62.352937167473868,28.414544710249125],[62.562326809993756,28.259147540696823],[62.757742216191566,28.243434104555988],[62.743150592699223,27.991488528882336],[62.823571762210058,27.343082276622951],[62.935257122790645,27.243777698544921],[63.171820728197396,27.25102494385909],[63.301369471796583,27.151382100761431],[63.160661249462265,26.654151072831009],[62.76629438362027,26.640388324877303],[62.423744487785299,26.555766216234275],[62.307248067717403,26.483611209788773],[62.239256756492324,26.357275899552594],[61.86529989369641,26.22513209916049],[61.67665574122806,25.721571599315421],[61.587724004504636,25.202527799195046],[61.41215859574568,25.10237205822159],[60.550678695852156,25.344860383902848],[60.400151256927252,25.311804552543286],[60.042842139269894,25.379783797681998],[59.897111705450534,25.362102283464989],[59.444917900387679,25.470072817156623],[59.046143940843947,25.417505661878462],[58.779264296185481,25.556386511702559],[58.202962224136535,25.591779200989826],[57.934319874484231,25.67450920750796],[57.796177486606766,25.653308780011113],[57.681664743496448,25.732461818699967],[57.334717732238573,25.791765393562336],[57.10457972468042,26.371512713186686],[57.03524008029791,26.800364185688462],[56.901970312080842,27.002872262124843],[56.727739553042746,27.126463529991064],[56.384926147898994,27.149608781814582],[56.303128817431705,27.069427856248545],[56.279142840973101,26.952257313765021],[55.954277033398952,26.701404936768402],[55.811247019467629,26.71585897670629],[55.423696661989091,26.583375629187241],[55.108512869342569,26.677451646093861],[54.763898557284847,26.506825128829188],[54.644985606777134,26.509137203653211],[54.229958724578097,26.700071980902134],[53.70585693194635,26.725839764941671],[53.404647530023411,26.970467539941065],[52.982622429092416,27.142150225615367],[52.691731941461953,27.323533356220313],[52.581509919721469,27.512691467293472],[52.45455988223155,27.622845052913927],[52.02014998360346,27.822333313711859],[51.66633023914757,27.845161894262876],[51.522111223346975,27.907699495109433],[51.279143604737044,28.131456146406386],[51.094049400356738,28.512168469512549],[51.043729216412125,28.7407558689411],[50.867164049602806,28.870317744098521],[50.804582359509432,29.067623680289735],[50.67537586167505,29.1467983310595],[50.629029248612717,29.443457407116735],[50.169155273207643,29.921385149485658],[50.051812558753063,30.14241638563508],[49.901890390524407,30.171012776817896],[49.554921537239281,30.029226230914855],[49.407846355012872,30.140865512028341],[49.100054849208448,30.259763485341381],[48.976319394514327,30.20910840014815],[48.832344691580836,30.035721816573709],[48.546523496367136,29.962617472940106],[48.434730934497082,30.037729973689963],[48.337779003099193,30.260406069523164],[48.015182840771281,30.465833397738088],[48.011154626828038,30.81797210912082],[47.968309169552178,30.922270552285951],[47.857533701982049,30.992009098111986],[47.679711725651138,31.00260393595795],[47.679687626462631,31.400550885433514],[47.796992919933118,31.799054411985178],[47.512068574286737,32.150955329314542],[47.391283310153291,32.385989819352773],[47.278173186545409,32.464679768743132],[47.12146388509835,32.466879118021467],[46.394914815750326,32.920420048053593],[46.112894767236639,32.957911735881233],[46.080736726638975,33.081550007389033],[46.116450225591151,33.245121767280594],[46.029215915049186,33.401994134301432],[45.738415661726847,33.603040017195688],[45.400062913432357,33.966740915472066],[45.525850988773236,34.186372276796888],[45.437858838750699,34.415166144092133],[45.500990030238555,34.581404052968516],[45.603630620731735,34.636152085065596],[45.678307578293854,34.7983177278767],[45.917679423475356,35.025464942360379],[46.065799862514609,35.115622900562045],[46.101979035068226,35.298947222391611],[45.975636003099886,35.476894301785244],[45.989714675677362,35.646259613982856],[45.928417807185632,35.767883536170281],[45.723571571285461,35.836879846264303],[45.547980392110055,35.982691199409743],[45.361797775268109,36.015508884889385],[45.252582189582654,36.314231719220736],[45.08074132227803,36.434164129150666],[45.002896033134213,36.691750864078244],[44.881041253952759,36.799448285134531],[44.737314944712814,37.307491409912622],[44.574258887484959,37.435563739226808],[44.520085703022517,37.729497272818222],[44.362877946270089,37.851987771644772],[44.223116496772583,37.880407662309196],[44.366515650342642,38.261996407851989],[44.291839994735959,38.415644783768961],[44.252614944875987,38.814505802731517],[44.14649701530513,38.989894197669535],[44.149203974009659,39.111509081969658],[44.023537649478712,39.377358452381699]]],"type":"Polygon"},"properties":{"alpha2":"IR","radius":1175000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-24.474297620799778,65.524944870455116],[-23.933295649779136,65.871829947376881],[-23.756494997675848,66.05997434628253],[-22.88915687872602,66.439943638042692],[-22.426354036465348,66.429247811507167],[-21.407407557449833,66.024615686534702],[-21.168590168422778,65.883786531860608],[-20.860042943072763,65.832465748122829],[-20.555400756786494,65.903376617877583],[-20.207520125460217,66.099239506771113],[-19.741037246965515,65.993875964592675],[-18.863247088667158,66.183197581862672],[-18.595678398648676,66.129085289975507],[-18.161972660052758,66.159415673745059],[-17.467835277748886,66.086309981464296],[-16.825062889008102,66.259089232898376],[-16.493025464451627,66.480360815831787],[-16.059635663138728,66.525721083441795],[-15.639414444064322,66.330176853350366],[-15.416491587406776,66.276043922329933],[-15.187371483573417,66.287399637217746],[-14.856008736455609,66.380249813054476],[-14.596563415581223,66.380558210744567],[-14.52834693203406,66.082830624892281],[-14.346387506978655,65.823044093316568],[-14.077776306436844,65.654383791828266],[-13.618797227578918,65.518764132483383],[-13.557204439832432,65.097844531545022],[-13.952019820146624,64.784232713187848],[-14.264023627449383,64.658819134881924],[-14.603158896072255,64.425226212488184],[-15.0216675879524,64.296599378043567],[-15.321126695238149,64.28635470724511],[-15.849531568919275,64.171880608400883],[-16.640450523978451,63.866028506826659],[-17.53970010267544,63.753646040231715],[-17.965966635857644,63.530104789906368],[-18.672969561408891,63.407834860765767],[-20.197976937708862,63.556642869841134],[-20.593899136259086,63.723263505536124],[-21.168135401049742,63.875230537809692],[-22.651886632986411,63.828779741393525],[-22.728801183485608,63.959657247782722],[-22.824192506752915,64.328238117029798],[-23.018977641602955,64.578548244748603],[-23.372810013131833,64.754079820550544],[-23.818961028870323,64.740333964537442],[-23.932343790068245,64.779179841210578],[-24.025246613467804,64.863827154661138],[-24.154718842884723,65.231392796440588],[-24.474297620799778,65.524944870455116]]],"type":"Polygon"},"properties":{"alpha2":"IS","radius":257000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[6.6280087630782605,45.11781102465077],[6.8839579386959739,45.160906362106097],[7.0372035189879281,45.286910116676495],[7.0396672785692109,45.458475598361069],[6.789367686136722,45.775449055582968],[7.0211017390767898,45.925496179755726],[7.1574968595798723,45.885881589795247],[7.5386046173676045,45.977941359081029],[7.8431868625785608,45.944136334617575],[8.0407579752875336,46.079218811335316],[8.0958973660513553,46.270787038272289],[8.3757639256531959,46.44520772913129],[8.4366047659179966,46.431769533673716],[8.4639100519911601,46.263120443024761],[8.5743614503477001,46.154073422459781],[8.9813598693500758,46.050387124873687],[9.2196821331420935,46.242284735506388],[9.2603728268485437,46.475055867439409],[9.4274568375599781,46.482078864502732],[9.4798570820592243,46.369642189494009],[9.5968807956281275,46.305877317524072],[9.9254984474922949,46.378090804470659],[10.016321756748322,46.446730966899786],[10.087162235347948,46.599723243410082],[10.359441244550167,46.672001885842469],[10.452971335319914,46.864642275222224],[10.927681366582323,46.773692815345029],[11.244482647633085,46.97543005458332],[11.767143921463948,46.98662664058461],[12.169429012637845,47.081913042392777],[12.221563604867335,46.886967620716334],[12.432386132617518,46.688021942930959],[13.699702217296499,46.520088406332306],[13.593032222710951,46.318332157569053],[13.634726305245813,46.157825347814544],[13.616613018525346,45.899272369533527],[13.874411198989353,45.614896368099238],[13.719993146486662,45.587821696130241],[13.601920843623503,45.70860879348178],[13.225500582023528,45.750872135246318],[13.030182456495941,45.637715557675016],[12.401574606876617,45.442218902055046],[12.315615983497898,45.36259237012441],[12.30975023765358,45.172500236400161],[12.389785297598538,45.051680583327673],[12.52316882600763,44.967868666546948],[12.276173150355369,44.658930836850324],[12.30621705133902,44.429831929789053],[12.399182202096156,44.22587389028913],[12.683958281455363,44.000598738854201],[13.563948035844312,43.571078329318134],[13.801440256441774,43.186477106943855],[14.017309437704409,42.683432210430105],[14.532683972685284,42.250173592198152],[14.866391526550016,42.053110886079402],[15.178529075313767,41.93407996968137],[15.964056810413968,41.939287910685664],[16.164459495200063,41.896004422432348],[16.188899222721183,41.814054310968011],[16.02384711298118,41.599356405538231],[16.100491994040006,41.411213884785546],[17.103351393713424,41.061993026572068],[17.47482149697214,40.841728847803012],[17.95485277990926,40.654990415666354],[18.3280984615573,40.370707062301129],[18.460407256055682,40.220924783960093],[18.485576186350027,40.104870985940032],[18.339001714299709,39.822575653709471],[18.078092371065388,39.937128358653894],[17.825245256194417,40.274234933742115],[17.400471375045196,40.338764528349998],[17.112389182404051,40.506731729929911],[16.921366754200207,40.448795476878701],[16.670189322346129,40.136865690416705],[16.530846080369891,39.785697518895219],[16.607289535005794,39.652274633641461],[16.829652524495078,39.575369209696163],[17.114299202820657,39.380495862527724],[17.174315823148049,38.998156889640924],[17.098455975698393,38.919565412536599],[16.913347266026083,38.930051863891798],[16.624657067309595,38.800821090712141],[16.568362904860301,38.700875979764604],[16.545401846472405,38.409245431164258],[16.276136159373063,38.240352567158297],[16.05671727227201,37.942069352622561],[15.724622878040059,37.939350143513082],[15.549313669694385,38.03990224635421],[15.395907100613098,37.970954249702359],[15.231270281539507,37.774698637037062],[15.117164200114267,37.491598779576606],[15.123011651279667,37.377161522776163],[15.230033310544576,37.244243794048188],[15.295566913650024,37.05030622548783],[15.135149978484186,36.872371173207291],[15.11239091555078,36.688077579229343],[14.775999083378666,36.71060465966071],[14.506714013153047,36.796694858806276],[14.357708389986312,36.978025878905939],[14.162325010200263,37.092842989328759],[13.900979070189267,37.102143866566969],[13.178081561452673,37.473884321691813],[12.908858567110936,37.569645941211803],[12.640372442700851,37.59460411695575],[12.526916188997786,37.66969307984165],[12.437516205669329,37.824344237777844],[12.547809877331133,38.052782952076988],[12.734395700604688,38.182692441708141],[12.918317930920457,38.07778894256009],[13.16003183334397,38.190071473074269],[13.346973862580215,38.18136413440083],[13.765645238082257,37.983340279939725],[14.043995655943716,38.039661083948594],[14.497062983663088,38.045974784447111],[14.789704341752014,38.166703796632184],[15.121684618622988,38.160227352374299],[15.498782783549748,38.290667493749403],[15.784010292351454,38.297674034286267],[15.885626268054864,38.444138402649365],[15.879162532568472,38.613850913735391],[16.091098460618348,38.777883535789201],[16.141776239954272,38.922612219951702],[16.016490575135606,39.362762898182133],[15.695107336018795,39.978059478941532],[15.572958579619646,40.048299851238241],[15.294597007591927,40.070217841139225],[14.951004084614279,40.239182010807248],[14.9439001727792,40.474992793833167],[14.790812971268092,40.648274018455645],[14.340183436120805,40.599060431160581],[14.296762954728361,40.729102564616866],[14.162670361146807,40.803331852727474],[13.867823554209735,40.708944952091265],[13.954418481006186,40.972092582014056],[13.760365039498218,41.211827526224731],[13.273333329026409,41.286486387095245],[13.088683992186809,41.24410078247984],[12.856888774765377,41.402992487198347],[12.630978989285675,41.46991006469991],[12.081754311277624,41.933180203961577],[11.807187549207498,42.082223568462553],[11.619588712926431,42.296123739716634],[11.32798821830964,42.412939294431105],[11.107334472097705,42.41371060714188],[11.075942959338224,42.61155372010532],[10.709843043244083,42.88623658205654],[10.521156705827961,42.882289075274421],[10.419040511594879,42.713603063301036],[10.131389459849684,42.742250782794471],[10.127673304615906,42.810057770824912],[10.34004877166133,42.834275556350491],[10.466054059214882,42.909072537972882],[10.528009011145288,43.158997344739333],[10.450902196605707,43.36075360697572],[10.32074732901035,43.513208684543493],[10.228716716977639,43.880341793459827],[10.067776032708387,44.009589007179557],[9.7309532959539666,44.101425354288914],[9.3082250004532785,44.308698413485232],[8.7664181894537982,44.417631315052198],[8.5437136845740262,44.337866732297847],[8.0101635987738362,43.879610184691451],[7.4933809634965725,43.767402272872829],[7.4917020040256368,43.981094958641002],[7.4366454870169161,44.080929324832852],[6.9003620914137551,44.33586916599716],[6.8432449521334462,44.510694295543502],[6.9217420119123316,44.656458086680296],[6.9116528802546036,44.772600787814753],[6.7383799327697309,44.921532899867699],[6.6280087630782605,45.11781102465077]]],[[[12.314795896534243,35.551820855540555],[12.845496573363279,35.880769707367563],[12.907133814184595,35.885469509652943],[12.64652091481568,35.486619494808288],[12.314795896534243,35.551820855540555]]],[[[11.936656683268247,36.828543069704878],[12.024052370905448,36.820742053326043],[12.050993333293217,36.757216068689203],[11.940848547661476,36.780516932123987],[11.936656683268247,36.828543069704878]]],[[[8.1811136237816573,40.771025761493455],[8.2059281697509565,40.997435072841867],[8.3201701138540241,41.121583158049035],[8.3767792406108263,40.930000193232104],[8.5269303500990734,40.850209449495139],[8.8108349332662783,40.94649034062784],[9.2283494430750963,41.256801068197774],[9.6151519729423729,41.017082238113517],[9.8050506055200337,40.499580475175101],[9.6645388076348979,40.258801957761847],[9.7064888647541512,40.017058536680956],[9.5622943560463014,39.16620205042156],[9.4863204791000548,39.139819256134494],[9.2718721042763868,39.209882789646542],[9.1451924215657598,39.194778795078768],[8.8812757475398847,38.913132562707453],[8.4215468050679281,38.968949657757591],[8.3640870761090103,39.035029542628877],[8.4129024338589282,39.223195660782714],[8.3993730402654165,39.481543454520178],[8.4584390438233239,39.637895628174974],[8.3995564162623158,39.978171882055456],[8.4701768066806089,40.226197595333979],[8.3570374759474735,40.491935474284823],[8.1901463712381126,40.651697922739515],[8.1811136237816573,40.771025761493455]]]],"type":"MultiPolygon"},"properties":{"alpha2":"IT","radius":633000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-2.2355558044345814,49.176647625489878],[-2.2350018682379234,49.181285623387758],[-2.221337296793704,49.261488824571444],[-2.2203134768556119,49.266107735273543],[-2.2155845729407755,49.265966274664258],[-2.0869351735120523,49.255745188904562],[-2.082275652120841,49.255152555721537],[-2.0778103050823145,49.2536954937774],[-2.0230584640257558,49.232921753775912],[-2.0188444873141203,49.231091459384196],[-2.0178477259345597,49.226606591042867],[-2.0107641924101181,49.185727875228544],[-2.0101845481815035,49.180987819207409],[-2.0147530749728184,49.179597610060391],[-2.0490032809820282,49.171015988287486],[-2.0537352799895672,49.17007410664587],[-2.0589891258125994,49.17224495274877],[-2.0774328856630309,49.178828357838952],[-2.0964320851783516,49.183576401412765],[-2.1158045631209772,49.186443559999987],[-2.1353645793204219,49.187402343749994],[-2.1500426854190846,49.187402343749994],[-2.1656263057496754,49.186794296389493],[-2.1811151702881761,49.184973851523914],[-2.2309111162266073,49.177142338801517],[-2.2355558044345814,49.176647625489878]]],"type":"Polygon"},"properties":{"alpha2":"JE","radius":10000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-78.339244464568438,18.287254973536765],[-78.325784346388758,18.349653517576243],[-78.216638306112117,18.447867391158937],[-78.094635294387714,18.446057252661923],[-78.015340576112692,18.460476257105405],[-77.873403544630648,18.521949935088028],[-77.463926929121101,18.46865192303623],[-77.261517477954158,18.457201174312985],[-77.125660353385186,18.419435321200059],[-76.908321717898346,18.390172044735468],[-76.792869105631738,18.304927225929887],[-76.700431997093759,18.257927051500413],[-76.350013767919151,18.151642370667123],[-76.235523692329323,17.974591797768628],[-76.211094331058902,17.913661908466594],[-76.301505675670938,17.880009443333353],[-76.524579116935584,17.866402184260338],[-76.691218490724097,17.929170447445227],[-76.808926072446681,17.936030485863743],[-76.881501404472601,17.908053972880793],[-76.944250091483298,17.849038475921656],[-77.04275148582677,17.850594879033387],[-77.116880536317055,17.824584757075215],[-77.175273093386139,17.772030481799053],[-77.205079380553258,17.71531672971054],[-77.290349138735351,17.786491408316625],[-77.38019442896335,17.836907482474022],[-77.463980509548549,17.855084818970692],[-77.670735835065017,17.859879735993744],[-77.768039390734216,17.877592636466417],[-77.859781307329456,17.994852323554763],[-77.962844354798463,18.047741476495958],[-78.057084131423693,18.165737796733637],[-78.127956345547219,18.196837481559943],[-78.293959373582737,18.218279726431106],[-78.339244464568438,18.287254973536765]]],"type":"Polygon"},"properties":{"alpha2":"JM","radius":123000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[34.951057371556146,29.353701184840645],[35.132356154018936,30.13919036606401],[35.14221736648188,30.425810562173634],[35.423637634256792,31.10271262810986],[35.400911957913557,31.230548860508652],[35.550998889971595,31.797698962005391],[35.534595738702677,32.096983888555563],[35.594705228114393,32.667848070708388],[35.738611816498,32.729376783004163],[35.894639318905675,32.713575703130161],[36.057995249271251,32.549243423563482],[36.382300878874162,32.387683765739553],[36.816443298698431,32.327466753610459],[38.773405130224205,33.371897925794379],[39.069464073859393,32.463051185174571],[39.247324238017505,32.350722481916463],[39.292484907753447,32.243919787887435],[38.992027478819928,32.0056504668107],[37.297121866814017,31.575597060626109],[37.170801120231431,31.437027123785654],[37.214751681691787,31.254744632688816],[37.979685365791369,30.500066742860319],[37.65958231242832,30.333981069919361],[37.469142603616362,29.995283138639387],[36.732537910652731,29.845994961329779],[36.475935787738251,29.495251378677203],[36.0683368690039,29.200816242881551],[34.951057371556146,29.353701184840645]]],"type":"Polygon"},"properties":{"alpha2":"JO","radius":299000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[128.64933877419503,32.662023404447872],[128.6655190303365,32.783589317253892],[128.84567889713463,32.781177997617597],[128.94817269952699,32.842503963625255],[129.10986877877809,33.132223940191501],[129.22624862455305,33.102658484426897],[129.07676669587912,32.840463324934575],[128.95336883890661,32.77663741926284],[128.89433070093079,32.652360058862662],[128.75041741509517,32.586417241015262],[128.64933877419503,32.662023404447872]]],[[[129.369848422253,33.175442603424337],[129.46206650735243,33.331073156560166],[129.66989301024662,33.379543019688626],[129.7646686132245,33.473737153641771],[129.77356070167846,33.625278492813457],[129.67508053581852,33.739803717427876],[129.7173788112087,33.858085540001177],[129.77616734663226,33.82903960775063],[129.82837067392634,33.61190916100022],[129.92064637738821,33.537988733058221],[130.03854457420408,33.529097709265734],[130.33000848077026,33.62103271327986],[130.48398079354584,33.834403694344452],[130.85653345756603,33.999934179149086],[130.90122956635864,34.10810275473164],[130.89104312463991,34.266297076003681],[131.00051610162234,34.389561542128625],[131.37118118256629,34.426697446855442],[132.25892991737427,35.023232952136262],[132.75179882880991,35.450864915043979],[133.15696110432236,35.558532552065202],[133.41621217609676,35.484509465956791],[133.98471566810136,35.5077033826079],[135.17433626381253,35.746808246844928],[135.26519074282916,35.721610742456811],[135.30638293341332,35.600796368383627],[135.40163034283907,35.532221258045944],[135.67943910248192,35.511436339204572],[135.80419178450524,35.554379558666362],[136.01978576053301,35.724084711387739],[136.00649453485366,35.990537355892613],[136.07137724167436,36.121520544570515],[136.37041295794043,36.373969971368624],[136.68908489628529,36.736395222884731],[136.74601689932859,36.951222165285124],[136.71950453720197,37.19832443467682],[136.84361636054459,37.38190472096062],[137.32252427446025,37.521799164489167],[137.33726382542469,37.437573227214997],[137.0422713642742,37.207545278642804],[136.99054839103073,37.082797545005995],[137.06950526911655,36.827316174926416],[137.28775148414337,36.762214663006006],[137.51420043435127,36.95130449712881],[138.31922169856864,37.219594472354849],[138.70874463470759,37.561099619346123],[138.76696741284343,37.6881493323935],[138.74783324980416,37.804790509167454],[139.2262630275539,37.999548521119827],[139.3615524350223,38.098389279753917],[139.52092126013616,38.502476543419576],[139.79287867314187,38.865539577968804],[139.91248705853997,39.228487504647212],[140.04157824047616,39.434006857900314],[140.06326971030055,39.642739851239696],[140.02661008355216,39.791827528974629],[139.93520810079573,39.867108313200191],[139.74178173958327,39.92094475250429],[139.90890625117328,40.030868529775439],[139.99862684269007,40.22044930781594],[139.92305983824272,40.598382494961847],[140.02943086823166,40.73291356125209],[140.25062237267377,40.811932484489432],[140.33680087372926,40.993290471854138],[140.2964980626042,41.329180745746449],[140.20694384506416,41.40766835943743],[140.08526916180892,41.434329960633299],[140.00805357040122,41.52581063741178],[140.0999843758311,41.880219231656056],[140.06589237292209,42.03666305392057],[139.76965687936433,42.287343278517021],[139.86038714015649,42.581658851330332],[139.89128647513564,42.64903250255027],[140.12059623649645,42.736357347673135],[140.3918213200696,42.925081515824289],[140.43565334349188,43.052267800206621],[140.39260358776428,43.302936893109759],[140.48692654029898,43.337908027466177],[140.79969527411285,43.210616649857471],[141.13820509488065,43.180585951122055],[141.2777408504775,43.20058218513342],[141.37045282127747,43.281965283997941],[141.39795469021044,43.642561988703868],[141.63063495809951,43.987297861763501],[141.66109613973691,44.263551840442524],[141.76090389709597,44.492482180593818],[141.7788651983648,44.716048465607273],[141.72066003275097,44.932803926249413],[141.52183371756021,45.180263916995706],[141.66812278689881,45.401047964652818],[141.94181049672065,45.507040270359617],[142.89065688084659,44.665932249726986],[143.78702393498386,44.126671467525014],[144.09583297102,44.102433209211981],[144.48214451070649,43.950660747557386],[144.7315980790477,43.930386071144319],[145.36931370878753,44.327221145761769],[145.12496367582472,43.859725307066647],[145.13386493859264,43.68011464256891],[145.39760816095489,43.313223459115541],[145.51085018716802,43.300576242076254],[145.67371879618918,43.388627886518172],[145.83220649589461,43.385836562351045],[145.64165875411925,43.299298792104381],[145.50502362763777,43.174452278462148],[145.33888406590387,43.172804388662868],[144.92656349024551,43.002407820407122],[144.62500553449237,42.946756982204484],[144.22544525117189,42.976548401162916],[143.96973924791959,42.880617983370442],[143.59496941673873,42.610287854085705],[143.43751210666684,42.428418522508906],[143.31348795998784,42.084408768056981],[143.23644865636911,42.000458375725032],[142.51449359464863,42.255753385561491],[141.84040063542929,42.574293722169458],[141.40759770315483,42.543364137970947],[140.88266632789052,42.288562877148046],[140.85517154312771,42.429833265014317],[140.69931443712409,42.55406338441334],[140.51136087666831,42.563034801417025],[140.37224333578004,42.458531270104835],[140.34648216227171,42.328132725892416],[140.42293111093252,42.197900715382112],[140.59257013821974,42.120330335658139],[140.8252622668866,42.179828002389783],[140.87097324931992,42.014803058276158],[141.15060017025061,41.805120158944902],[141.03062861772977,41.658871312521605],[141.08364541245038,41.475587576595693],[141.25444180255101,41.373087076597997],[141.45516668058647,41.404422304391353],[141.40082229716239,41.096307117629479],[141.44868237317041,40.660114910146518],[141.79690833972515,40.291069679415493],[141.97921955729291,39.838857408261923],[141.9928651226974,39.610524465679838],[141.90057344039883,39.111486693305814],[141.68123951403101,38.960326174095293],[141.56493171132408,38.792795409532339],[141.46729982365585,38.40436559814141],[141.12290302048592,38.338472047031743],[140.95287570502677,38.122102033999013],[140.93078230171051,37.941308731601104],[141.03596946070888,37.46722854656533],[141.00143956605254,37.114691497940576],[140.96815878720778,37.002167285582424],[140.72689583888265,36.725211013142768],[140.57802184613408,36.231248375129823],[140.63339361950057,36.041505352993831],[140.8736016054919,35.725057837585098],[140.63036678346029,35.653592039054949],[140.48071062750566,35.529343097361732],[140.42113689175488,35.413433412272163],[140.41625263519245,35.267049164840927],[140.35011496895032,35.178421181328993],[140.07761664984375,35.04904366876282],[139.9203188923471,34.89986584842049],[139.8405614653247,34.918073246703536],[139.74736024099911,35.080553574043982],[139.52951943308946,35.26455951381687],[139.29821382418731,35.286613260724941],[139.18130884268146,35.223671562204117],[139.12647740452798,35.130828605515482],[139.12364552984488,34.940761136574004],[139.17596412055175,34.832376457821994],[139.06251044138338,34.789117232058423],[138.8965631613554,34.628679876290924],[138.8375640425779,34.619494639668858],[138.7612749916203,34.699264147036374],[138.7199167952655,34.907986884841961],[138.56026154162316,34.971830823676839],[138.34936750804152,34.84695676810027],[138.18893716584668,34.596590291818259],[137.54338104205493,34.663158117191429],[137.01153445199517,34.571353775286383],[136.90796361278944,34.483928689654306],[136.85351342668298,34.324284027772428],[136.32030629408229,34.158378165029809],[135.91604753075094,33.561940139094347],[135.69530505543642,33.487215128481473],[135.45295365996981,33.553551304347103],[135.1756222311663,33.898194577872054],[135.07790195619518,34.165813419119075],[134.98065545577452,34.225650624478185],[134.86651685575103,34.222527649306038],[134.75002656134149,34.164430642577628],[134.68614313131872,34.068681027940897],[134.73850217953398,33.820592054611978],[134.36097772064403,33.589798656147778],[134.24016298523338,33.431297124802974],[134.18150212816644,33.247578092136649],[133.92394112122818,33.463056848787232],[133.61964158746167,33.50466060536661],[133.29702620553698,33.34905420351275],[132.97701425640813,32.84214309916289],[132.86984411311479,32.754795543233435],[132.64196588934692,32.7627386239093],[132.58890542719817,32.862801876434958],[132.49534488072581,32.916780410914733],[132.428153954853,33.059403272584547],[132.46418537110526,33.234189418486132],[132.37748804915475,33.355770103347808],[132.24544413487325,33.393203183195197],[132.04081241072819,33.325706863344401],[131.92458634313931,33.158246529065671],[132.00189961081497,32.882472379717122],[131.66101892413084,32.465320358328647],[131.47254735033826,31.912680896899708],[131.45973479981038,31.670867490334775],[131.33929290121105,31.409209374020499],[131.1611546858835,31.358075081128831],[130.9545940577645,31.139340309525256],[130.91587509873742,31.030760806723752],[130.81464808165885,31.055773919752891],[130.68595353657082,31.015475897561444],[130.58306145216224,31.176292170477367],[130.20085668760174,31.29208037881493],[130.14757461760493,31.408365340391171],[130.24133324746919,31.482228162339723],[130.27634777526228,31.591175891903212],[130.18813091746631,31.768905547643708],[130.18010107742134,32.07101315185961],[129.96046259121511,32.243820899812292],[129.97976635400198,32.393309768504089],[129.93396906029713,32.500198648091704],[129.76892779726955,32.571241970930728],[129.79220155840889,32.741987880285876],[129.68810202638545,32.879955046438766],[129.62535972237384,33.097031311788442],[129.5129285596683,33.173977058582324],[129.369848422253,33.175442603424337]],[[140.41566382304967,41.550921358607653],[140.3597167072453,41.426242040181222],[140.39689505902143,41.294740015978867],[140.49080999695227,41.223678349910458],[140.66894517796916,41.201031245881701],[140.81281503547604,41.298791759369159],[140.91839686827001,41.606787914109745],[140.8395663625368,41.742603403581846],[140.66465451647187,41.788515541681441],[140.51284517430642,41.694159608281204],[140.41566382304967,41.550921358607653]],[[134.3719764198851,34.522231062888231],[134.42035447228403,34.330039134690672],[134.60297885225683,34.272112790531459],[134.77175509428972,34.387866887614038],[134.85371116314465,34.598754041702556],[134.75684311744195,34.740769812705658],[134.53924157673168,34.764286123133125],[134.40565868749451,34.684004973338624],[134.3719764198851,34.522231062888231]],[[133.6020581063963,34.246430342380521],[133.50330510382688,34.374895618011919],[133.35486366037489,34.391933950593788],[133.18419331643074,34.300349890366881],[133.14598321523306,34.134213922697114],[133.24975801569695,33.99896614128911],[133.46491420709586,33.981249578178115],[133.59445917112652,34.086723496601529],[133.6020581063963,34.246430342380521]],[[131.64293557553049,33.63749181372949],[131.72397161318412,33.553776287017634],[131.75987071343539,33.380724760386805],[131.92918718572213,33.30382473502857],[132.29750214218333,33.477967019670324],[132.38342096371437,33.648025208967923],[132.28790510034545,33.81288439580733],[132.09037389277776,33.855792221718843],[131.70129430674658,34.046337913231852],[131.54094958019891,34.003060331915592],[131.45952145629187,33.871497078449153],[131.49631733076549,33.721212373148134],[131.64293557553049,33.63749181372949]]],[[[139.57260364651512,42.230875763396199],[139.50485600942824,42.096567130440135],[139.43474299278009,42.084247155193978],[139.43152841547604,42.199369958228907],[139.57260364651512,42.230875763396199]]],[[[141.34910306170818,45.163459526085795],[141.22602128856545,45.112442896441053],[141.14550789076517,45.154085218345593],[141.03158702410138,45.273701734711011],[140.97194098750603,45.465237618009667],[141.05655983469765,45.449383996628207],[141.12848916933945,45.290746816758634],[141.34910306170818,45.163459526085795]]],[[[139.33325740830873,34.769612199315908],[139.42602854486321,34.775620505896953],[139.44553545533151,34.679798555084894],[139.39251002660097,34.690107650758961],[139.33325740830873,34.769612199315908]]],[[[131.01885311510742,30.845439868052903],[131.08177660831586,30.78591852019709],[130.95956798027774,30.397147052578784],[130.79580466085949,30.394853348007032],[130.92031453831376,30.538211256481915],[131.01885311510742,30.845439868052903]]],[[[130.73143033951047,30.378342374392652],[130.62257605007142,30.263221098747131],[130.50822091927924,30.241615113650905],[130.44577590605115,30.264881860007765],[130.38838403251953,30.388104594812283],[130.49718710874959,30.465243963857205],[130.73143033951047,30.378342374392652]]],[[[129.79384195113587,31.74638436936953],[129.71777311850184,31.657371642116289],[129.68714405153906,31.640075516904979],[129.7070654833264,31.718119012829511],[129.78714552104483,31.78668765561925],[129.79384195113587,31.74638436936953]]],[[[138.58633747900996,37.914609714074068],[138.34398679981271,37.822331217177279],[138.22553292722111,37.829499320334797],[138.25020683496567,38.078397833506543],[138.5034254366856,38.315545217997681],[138.58633747900996,37.914609714074068]]],[[[142.10746703210393,26.721396128604308],[142.16157275514416,26.709789749134384],[142.18801609779464,26.616746680527868],[142.17005188303344,26.61591466389979],[142.10746703210393,26.721396128604308]]],[[[139.76918295730843,33.107152325753674],[139.80883285285364,33.128963905947543],[139.87327117527147,33.093441665512032],[139.82383441597247,33.045723162337694],[139.76918295730843,33.107152325753674]]],[[[133.19021221192034,36.23268357455698],[133.2063765639025,36.293228647916926],[133.29564918187108,36.339831780816851],[133.3809884202951,36.246321002358989],[133.32463069656467,36.166778812755751],[133.19021221192034,36.23268357455698]]],[[[128.8830413592575,27.842400433509646],[128.96514518365825,27.906448874968607],[128.99794854763033,27.720985706623956],[128.90022094499221,27.727950650707534],[128.8830413592575,27.842400433509646]]],[[[129.21437276717162,28.106821448869351],[129.16502396913248,28.249646353504541],[129.68944204990257,28.517163566413839],[129.71023890170963,28.432303882518902],[129.57497098269914,28.360765637001123],[129.33044723196761,28.081848150181639],[129.21437276717162,28.106821448869351]]],[[[129.18666742474068,34.145051479444703],[129.32609159086644,34.607138843737665],[129.45106650515697,34.686288465534375],[129.4751150908132,34.540509728706773],[129.3325345768024,34.225931076501588],[129.21455829457702,34.083158793784861],[129.18666742474068,34.145051479444703]]],[[[127.65333524171859,26.094956429935223],[127.72912498271573,26.433756677902334],[127.85019965164571,26.509481144805399],[127.90733175319289,26.693362435176173],[128.11131450259217,26.707476817420947],[128.25496239856056,26.881500975723597],[128.33139304985164,26.81203139513628],[128.30764772115685,26.716418403679018],[127.95767031847528,26.46089406538032],[127.80339729376233,26.152668549474395],[127.65333524171859,26.094956429935223]]],[[[125.26920868842335,24.732719149447195],[125.28377222949206,24.87155850889804],[125.44367174555654,24.743252864085527],[125.35936062425726,24.717311691520194],[125.26920868842335,24.732719149447195]]],[[[123.68003223045822,24.317631834743946],[123.77157635735634,24.414158551367461],[123.99658145472988,24.379996160833695],[124.32373107118099,24.56631814003244],[124.18561991416813,24.335309800175473],[124.02023223811332,24.362188994707836],[123.82558636793,24.266263761878445],[123.68003223045822,24.317631834743946]]]],"type":"MultiPolygon"},"properties":{"alpha2":"JP","radius":1004000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[33.903443165958528,-1.0018260620167179],[33.943362655321913,0.17369826222942669],[34.161120445956584,0.60502188043074745],[34.40247103368381,0.85950552253900847],[34.481899958308304,1.0420374943880817],[34.755176490919993,1.2710826954819874],[34.95612169310175,1.6328476142508705],[34.978091592906438,1.769606255332751],[34.873510514986648,2.5409621409295178],[34.733394508571713,2.8170380698309048],[34.589357891378441,2.9249135330103124],[34.52964259439787,3.0863392573551414],[34.448039800076089,3.1636123491746408],[34.40040270334422,3.6277812886228342],[34.165242817009563,3.8131188193038841],[33.976335764667823,4.2201686125228575],[35.26833627219456,5.4919365221368874],[35.373542316615264,5.4222669093748177],[35.747865449145593,5.3399798985406832],[35.800022421640087,5.15695543144437],[35.766169007331023,4.8310195065219936],[36.014557528971856,4.484886968821697],[36.905470045529327,4.4112566083091433],[38.125267427588611,3.6404382127827559],[39.496278806866385,3.4654867658782282],[39.656890124975341,3.5784036974118507],[39.84235305723692,3.8513027716254431],[40.76520028599154,4.2727984678740825],[41.165973591696712,3.9568193043869764],[41.883559334387293,3.9775152585643934],[41.344388835272184,3.2053696781133443],[41.010611441727811,2.8726184573073521],[40.965608722414729,2.7642455800312349],[40.979416529197671,-0.82103133142857454],[41.521668540928019,-1.5723817994280644],[41.532510336634132,-1.6952304081896721],[41.13273774893068,-2.0883471446776243],[40.953188403606525,-2.1871553543326954],[40.644055454534005,-2.5392469645575488],[40.397368103084226,-2.563645879840426],[40.238821523446887,-2.6726836473231828],[40.11518435064297,-3.2504432618351498],[39.943460731514818,-3.4313671844992415],[39.731449578723002,-3.9931699045943452],[39.376832522758946,-4.6252462491932773],[39.190236727000794,-4.6770772602726227],[37.792170217811226,-3.6695184752009471],[37.608436393922929,-3.4969782963219549],[37.685041814812543,-3.233909784987123],[37.588971985491746,-3.0158734935233444],[34.053916509377345,-1.0411585503595002],[33.903443165958528,-1.0018260620167179]]],"type":"Polygon"},"properties":{"alpha2":"KE","radius":658000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[69.229381222526655,39.761120651116812],[69.307367566347821,39.968299631523564],[69.416275263494015,39.989654152283869],[69.535476507790847,40.09865847148982],[69.966789692100761,40.20200079796183],[70.545857875186769,40.039775649561356],[70.990700892151082,40.254642830800201],[71.298778851298863,40.286473384936549],[71.759161184949633,40.197318127158823],[71.94356000630728,40.268826329082287],[72.131388084447011,40.438351601884975],[72.281145187229242,40.47051844869457],[72.402100360116222,40.577813880243795],[72.506867198858231,40.58527973784328],[72.601910786665229,40.655393042852644],[72.634038961933314,40.826241086154006],[72.526217101394352,40.964359486981905],[72.363348316657664,41.039013835272982],[72.191791666717847,41.023896696433589],[72.09768795261536,41.144282149249605],[71.878817699338541,41.19524009915564],[71.749151238890491,41.346236624776935],[71.630528789891656,41.350972128951824],[71.504845716972056,41.289737506774749],[71.393013870435624,41.123654531874138],[71.115191883314395,41.152135453190319],[70.782613550657345,41.262670970064057],[70.664380241515801,41.416479212383202],[70.47142046442525,41.412905385410909],[70.177198729465715,41.540069746606406],[70.794966133569687,41.948878382567699],[70.915019898965667,42.1166751377731],[70.89307898248201,42.339921006342649],[71.022956287801875,42.535309003104082],[71.256725193760005,42.733376117008497],[71.42208322832019,42.782934294478359],[71.81208437720359,42.8221106988115],[72.275750591878662,42.757474237562462],[72.752827590323207,42.637697148611593],[72.885598352822342,42.558990042481575],[73.277145300324619,42.520995575652066],[73.418669410119136,42.615040559155197],[73.556426425691427,43.002658500684774],[73.607849060303309,43.044532355244293],[73.949320874194711,43.194753440261238],[74.10667530154997,43.191716523627257],[74.209127996929368,43.240108750737839],[74.824224028324153,42.976042682131201],[75.37876332405996,42.835921028891676],[75.629248636393754,42.822995594177378],[75.83566025830082,42.93707774359882],[76.518029951751473,42.9195353903647],[76.992578858240506,42.973393874893738],[77.260941507988449,42.911080295556324],[78.520066012926435,42.864839933939486],[78.888970093953262,42.774404112191569],[79.126630974282591,42.775486023393107],[79.459406627444778,42.47374377468401],[79.92097897054677,42.412879430914707],[80.20918562728302,42.189909041319218],[80.244965435583779,42.064866297766983],[79.888260174920632,42.005605333747205],[79.765952785421817,41.899110260705832],[79.268869469295524,41.771127394774176],[78.734247531382934,41.555860659943939],[78.41510539639377,41.396734723578504],[78.123327007130612,41.075865271250855],[77.581719378322248,40.993020067311591],[76.899685878865029,41.019210421982564],[76.660489712099931,40.768563120247876],[76.480021758007922,40.449660300478776],[76.318564741223881,40.35250868477808],[76.062598641002921,40.3837385064232],[75.87191671604225,40.303460132974237],[75.677264665471014,40.306067710800427],[75.599273111683331,40.48905586827852],[75.465135079932821,40.560665882624868],[74.951999498718109,40.438812855688631],[74.830345156944091,40.328757114933822],[74.671319336389928,40.304469695707134],[74.411788287752429,40.137484304265399],[73.984477964741473,40.02892815235186],[73.870685711856083,39.835486128251723],[73.906916113276637,39.578620073056157],[73.822838142751337,39.489175240023201],[73.71991166867906,39.463299297352535],[73.446797875419861,39.455512117958037],[73.112762853041588,39.362264780023899],[72.563485634212213,39.3766407631486],[72.361398855131057,39.329078529157364],[72.230003660924865,39.207773605829068],[72.023875450161498,39.329519725776535],[71.778688998912912,39.278230928750489],[71.64239256036619,39.420997621119128],[71.371500841363385,39.549803245947096],[71.113485473780003,39.505023565288177],[71.0047652876206,39.412075741244053],[70.799335404385175,39.394925891321748],[70.501098788518732,39.586137182862394],[70.244847489773406,39.542887122677193],[69.62721001566986,39.574246906821564],[69.297847628860183,39.525039737904457],[69.229381222526655,39.761120651116812]]],"type":"Polygon"},"properties":{"alpha2":"KG","radius":566000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[102.31999691886644,13.539900430864977],[102.48451169768695,13.597196535635245],[102.61965174104157,13.71775654390623],[103.03121300248101,14.252308718473696],[103.54642395670491,14.417246034161648],[103.99434762963921,14.358744831129879],[104.77899692292183,14.427555481841434],[105.04443807509065,14.329742289148802],[105.18309777223031,14.345853386265304],[105.23439376925489,14.21774389431555],[105.33207900980128,14.137639082538177],[105.53154418429411,14.155914216316493],[105.80352853345444,14.079910584779983],[105.95462051117423,14.166908486340427],[105.97917055819326,14.342902934747933],[106.13693110579655,14.384140504938131],[106.22550754654954,14.475969033488338],[106.38106628575441,14.47677955597247],[106.50147899037516,14.577904064629388],[106.76693625708428,14.368261779878058],[106.89619325998005,14.346553968703223],[107.14355207068155,14.449896534207458],[107.26700477303358,14.575255049783229],[107.51931101232937,14.704717516387378],[107.49425094452283,14.548442679617612],[107.36748538137056,14.33911453054168],[107.33474883297008,14.12154009612849],[107.60476821343372,13.44289263748235],[107.47775642227293,13.00945488816869],[107.54385947880638,12.70166360944109],[107.53787615601216,12.431851902578654],[107.3933677165552,12.26076875149802],[107.19650535840077,12.291890009301413],[106.93054443842243,12.077759185393765],[106.4944927919184,11.930330550417846],[106.42364901993808,11.835476091740253],[106.39912349157173,11.687247043350714],[106.08428467905632,11.742201542678464],[105.90537920811971,11.632715814613277],[105.85559344132909,11.526148537464895],[105.86937914333667,11.307067077459642],[106.16072561270252,11.03695613796304],[106.16367340699405,10.795156804145677],[105.85344946931126,10.863762831025239],[105.75881521262581,10.960403934990888],[105.64481392794896,10.982075911425381],[105.41380882336412,10.937288135651523],[105.31456683045452,10.845466119447776],[105.16371513820964,10.83999936873713],[104.85046290169745,10.534651735340363],[104.60285220577302,10.514159090113235],[104.4308910065172,10.412470169153732],[104.24964730521552,10.538484760902437],[103.92087343364334,10.589029126122877],[103.661933708422,10.509209979672972],[103.44297366800984,10.681037851316969],[103.28132518027802,10.679973129386591],[103.15300061892562,10.913943275805563],[103.08462146463724,11.18849867014019],[102.99360366072004,11.290538223014281],[103.0290773396393,11.546128805129664],[102.73684087378007,12.089869039301606],[102.71843382524789,12.454794759079343],[102.4998264224987,12.670085252226141],[102.46389421501705,12.994480521104689],[102.33098039164831,13.288273345140432],[102.31999691886644,13.539900430864977]]],"type":"Polygon"},"properties":{"alpha2":"KH","radius":336000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[174.3883656948085,-0.59242053651222559],[174.39374200933133,-0.59267446438031379],[174.44527815200064,-0.62609321473633539],[174.47391785938959,-0.64286266430100247],[174.47970261231603,-0.66203454131585104],[174.5114736825887,-0.78746259681240516],[174.53685394163986,-0.85758525632381222],[174.55911505534417,-0.90480650842802535],[174.53033497797421,-0.87459412797653757],[174.47717452832993,-0.82837515201353551],[174.47003878317179,-0.81564173801568318],[174.46289325721779,-0.79791466988028037],[174.44075734270081,-0.72189753194042927],[174.40616588497863,-0.63907122290906604],[174.38261328737607,-0.59270635870507438],[174.3883656948085,-0.59242053651222559]]],[[[174.69682221628651,-1.1025826558665446],[174.74336745882295,-1.1479665233021683],[174.7538639061421,-1.164569261357864],[174.76594207102619,-1.1873789034087334],[174.77267441578138,-1.2120206672766247],[174.77674652427524,-1.2437551914741884],[174.77785432116943,-1.2621134143813177],[174.75673164022896,-1.2556178997890528],[174.75305311788821,-1.2471615180301818],[174.74285412111283,-1.2147677227975673],[174.7146287495691,-1.138790635824817],[174.69682221628651,-1.1025826558665446]]],[[[172.78320522753211,3.0018041277866629],[172.76035359140241,3.0232188501227646],[172.75197430122867,3.0327656670271073],[172.78506054588291,3.0499060544026144],[172.81685699911327,3.0645294355428496],[172.85697426027062,3.0855735985698769],[172.96202242890257,3.1473834555349707],[172.96857757869981,3.1294937812745616],[172.9044105981591,3.0677935578741184],[172.85411557344526,3.0082583725171661],[172.82456844416504,2.9654385432568513],[172.81766154405136,2.9725787858207315],[172.78320522753211,3.0018041277866629]]],[[[172.93647095336379,1.9445581660236311],[172.95861232186559,1.9233376281410663],[172.97977460904517,1.8990427268850285],[173.00641715999811,1.8626971958687686],[173.01779433778174,1.845152374539903],[173.03684453920948,1.804099376981235],[173.03894361266876,1.7954935227031787],[173.04548116110362,1.7454943115389547],[173.05248700886042,1.6842786609978302],[173.06744507767058,1.6125452286654742],[173.08890396379809,1.5424813624941256],[173.11668363291733,1.4746748796157398],[173.15055102102079,1.4096946583191103],[173.17085231480766,1.3747479872245623],[173.17043826069883,1.363634359192426],[173.14858890886003,1.3153364637852938],[173.12162845797189,1.2414494658877775],[173.10205373330598,1.1652721429848742],[173.09005394153189,1.0875408148875234],[173.0857450710314,1.0090068222129538],[173.08554525094127,0.97354016920599085],[173.07871425815566,0.94656026677758942],[173.07073025954824,0.93129229639960731],[173.06065576931218,0.91578990118388526],[173.04854457837962,0.90061778785405855],[172.99082506436073,0.83654220502638377],[172.97107614733255,0.8433862911309018],[172.98404809927504,0.91704040976252277],[172.98981547910898,0.9837792060431878],[172.99098422261017,1.0249492511022038],[173.00857489606321,1.106481289804665],[173.01703625786726,1.179809659815269],[173.01870289478148,1.2536057763349557],[173.0122674354015,1.3383518577563651],[173.02447297847652,1.4344058491162937],[173.02560928339065,1.5197638837724159],[173.01764379260794,1.6047570377661595],[173.00066720425912,1.688417546473961],[172.99422256301108,1.7132847951521188],[172.97682739535335,1.8009465791796311],[172.95421054133132,1.8743922560616912],[172.9337544438705,1.9260728012784234],[172.93647095336379,1.9445581660236311]]],[[[169.52492882788735,-0.85895057850154122],[169.52636840199682,-0.85326709317601046],[169.53861709862733,-0.84784703274058659],[169.55419316059411,-0.85700214282026677],[169.55027703531667,-0.87292971152260923],[169.54182020842086,-0.87508341765037823],[169.52398232469471,-0.86514739605423407],[169.52492882788735,-0.85895057850154122]]],[[[-151.80882840324406,-11.392861353605465],[-151.80338314823595,-11.393590977884216],[-151.79680776369185,-11.404474308752114],[-151.78733135911281,-11.427032996326972],[-151.78364760778717,-11.440926646741977],[-151.79129252620879,-11.455762997847184],[-151.80597225743261,-11.450481716672586],[-151.81507916187451,-11.430889305607591],[-151.81825458830423,-11.409357009066236],[-151.81267196858551,-11.392722948107021],[-151.80882840324406,-11.392861353605465]]],[[[-155.0138480742344,-4.048667719224091],[-155.00304930948565,-4.0444074654843041],[-154.96645936733128,-4.0344234232411269],[-154.95158716522235,-4.0320240030057759],[-154.94440605391449,-4.0417610031497055],[-154.95657739780674,-4.0867203105910184],[-154.95956393375832,-4.0923513894025252],[-154.97589851327297,-4.0826544118430359],[-155.00638924472679,-4.0608505662574856],[-155.01359044557046,-4.0543618242214716],[-155.0138480742344,-4.048667719224091]]],[[[-155.89622315705941,-5.6100013511424178],[-155.87265151458351,-5.612360185807721],[-155.86341209831926,-5.619533595222669],[-155.86479073812208,-5.6259579902693515],[-155.88724414100054,-5.6305663122517782],[-155.91399515680351,-5.6307568579865057],[-155.92710761985794,-5.6181746161212223],[-155.92768944951519,-5.6084919230982901],[-155.89622315705941,-5.6100013511424178]]],[[[-157.57764971246337,1.9022112868901497],[-157.5556849973014,1.9309223942731184],[-157.50006536805367,2.0138877467707084],[-157.49164752058633,2.0283171021206106],[-157.47265399649476,2.0276469577136313],[-157.44210120103824,2.024278462455726],[-157.34009008899153,1.9771286736526954],[-157.3226300167124,1.9678098405147799],[-157.30114172321979,1.9213875175136819],[-157.26773833804401,1.8610272645140604],[-157.22925893166939,1.803769238278101],[-157.17759875955863,1.740493448721004],[-157.22502135583392,1.734171399154478],[-157.24603081951281,1.732876579225852],[-157.26638728833916,1.7382327251946785],[-157.40016891776116,1.7811494814195776],[-157.41962032726497,1.7885330223465989],[-157.43711145689937,1.7997995387741179],[-157.56322707633802,1.8907128887844489],[-157.57764971246337,1.9022112868901497]]],[[[-159.40784889716835,3.873340722701768],[-159.39993971190884,3.8861637057529479],[-159.39024419667399,3.8988869507748958],[-159.36867883745754,3.9162253196720012],[-159.33941073544398,3.9224063980848611],[-159.32493023551265,3.908589399501877],[-159.27312924952918,3.8538066053846949],[-159.26045249000902,3.8389747226526287],[-159.26616404663341,3.8203182734127901],[-159.27540693551816,3.7975804721641833],[-159.29458051583876,3.7979283576652429],[-159.33204740893717,3.8012537007891334],[-159.34437610594983,3.8074671213639588],[-159.35809835141023,3.8160807704389796],[-159.40784889716835,3.873340722701768]]],[[[-172.2210508131723,-4.501758645823621],[-172.21174550837685,-4.4949825594151891],[-172.19807669128176,-4.4926584652170041],[-172.17242077906954,-4.50785887493078],[-172.18924715586698,-4.5206916099628502],[-172.21476182490852,-4.5233303115195396],[-172.22710791771618,-4.5071795383277156],[-172.2210508131723,-4.501758645823621]]],[[[-171.24746014854099,-4.4667657210848102],[-171.25418575539189,-4.4654995890697613],[-171.27124166262385,-4.4563162045095472],[-171.24629001152226,-4.4391120152381429],[-171.23291211420167,-4.453990724179488],[-171.23409616986353,-4.4628202587384616],[-171.2431586631015,-4.4671412730717348],[-171.24746014854099,-4.4667657210848102]]],[[[-171.11923842461277,-3.1116454695136007],[-171.08806535734158,-3.1159597684282123],[-171.08204455613969,-3.1207457483735608],[-171.08588528161005,-3.1351388028397809],[-171.09024259705353,-3.1421746055611144],[-171.11923842461277,-3.1116454695136007]]],[[[-171.59860021242088,-2.8605404277238202],[-171.64703468289673,-2.8544447588083512],[-171.66087175672408,-2.8487116935938381],[-171.68076131653433,-2.8367358212557896],[-171.69544904358935,-2.8250721211762722],[-171.71791066954077,-2.7913488862524094],[-171.72660905052095,-2.7738556964620793],[-171.72398044477507,-2.7679905455639009],[-171.71856211440721,-2.7625650233053203],[-171.6979825185276,-2.7673447048472366],[-171.68030477767155,-2.7765598974966643],[-171.66545833079209,-2.7861482868731051],[-171.65241921868059,-2.7982874419657748],[-171.62702434123514,-2.8264792779385264],[-171.59860021242088,-2.8605404277238202]]],[[[-174.51106602018783,-4.692367903433218],[-174.52334846682902,-4.688883759964817],[-174.53604935812342,-4.6684663540706195],[-174.53970824257615,-4.6614395068868513],[-174.539805439475,-4.6584450098925849],[-174.53181305741842,-4.6603064826364413],[-174.52778076076041,-4.66340160441605],[-174.50213277846314,-4.6887719861872421],[-174.50211078944648,-4.6935992234611748],[-174.51106602018783,-4.692367903433218]]]],"type":"MultiPolygon"},"properties":{"alpha2":"KI","radius":64000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[44.220854009099959,-12.171529570761605],[44.294070413904571,-12.163699639440441],[44.330068912097097,-12.153376170279362],[44.363511773211108,-12.136522656457442],[44.393226435449179,-12.113730009930235],[44.41274739621629,-12.09314831429772],[44.447495125118635,-12.073783823084655],[44.451870202909831,-12.071654725759329],[44.476160527271617,-12.081740992689888],[44.526562318963059,-12.219565355396226],[44.526048536617601,-12.323469506211628],[44.504899697581678,-12.356258214823892],[44.465293009790706,-12.337607122318408],[44.456136506375834,-12.331127014655294],[44.399644528225679,-12.274501643404463],[44.367443375925816,-12.248321034971509],[44.220854009099959,-12.171529570761605]]],[[[43.226904501800071,-11.751779613771998],[43.256250069046068,-11.432192911848123],[43.280847045121135,-11.391361067889054],[43.299131959740073,-11.374742337552959],[43.341434683799143,-11.368716836912009],[43.392729562904329,-11.408700923673347],[43.382929850667409,-11.560621685935807],[43.3826465649583,-11.58157846232621],[43.388643018593498,-11.623006539426594],[43.403131376984113,-11.662279055256112],[43.449098161622075,-11.756142235862979],[43.491250849986294,-11.862084007186644],[43.465649292709443,-11.901118480622115],[43.453635283992078,-11.913242042404075],[43.446819599151581,-11.914303441883717],[43.36378789048122,-11.86393659916347],[43.345086852165991,-11.855906132254974],[43.303470192489037,-11.843806540110014],[43.226904501800071,-11.751779613771998]]],[[[43.631576288523682,-12.247318700079463],[43.704204362096583,-12.256224697759794],[43.788562621079556,-12.307180970306451],[43.858329683120026,-12.367967094653761],[43.663805551892231,-12.342673899580618],[43.633126766967145,-12.287634541743561],[43.631576288523682,-12.247318700079463]]]],"type":"MultiPolygon"},"properties":{"alpha2":"KM","radius":34000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-62.840296056461881,17.347082980843222],[-62.839680954593369,17.360628850037358],[-62.839188166898417,17.365266527109622],[-62.829487741895527,17.382264646867103],[-62.826887348439264,17.386250951837255],[-62.79914874216464,17.400334779649512],[-62.794640953332397,17.402304464359492],[-62.790316565081469,17.399959366436601],[-62.717792604547633,17.355753452049683],[-62.713884443562264,17.353107872643719],[-62.693754368779302,17.320456801029582],[-62.680803775447224,17.301675628118826],[-62.665798610771866,17.284491468068886],[-62.648934110823483,17.269127908421215],[-62.640692049320954,17.262152120202455],[-62.632739825686862,17.251376773100738],[-62.619405162069732,17.235903551340805],[-62.604561802634244,17.221871141494915],[-62.588364576178812,17.20942591347108],[-62.578641324775333,17.203424737545426],[-62.570739308804455,17.197999404711737],[-62.538062466909111,17.173078391555414],[-62.534408294652273,17.169998214033541],[-62.533982152802281,17.165238080466921],[-62.532430576819074,17.126914153680531],[-62.532479167051896,17.122034994374914],[-62.536869512681683,17.119905811012444],[-62.577816939043387,17.102539003722793],[-62.582394437154036,17.100862277316036],[-62.586552846745747,17.10340639407125],[-62.620737574939753,17.126746311941638],[-62.624662267605942,17.129726655716432],[-62.628103900274894,17.151143306780753],[-62.633212113587398,17.170162921772167],[-62.640166118659415,17.188587943105286],[-62.648898489527717,17.206239721958596],[-62.659324557212194,17.222947106861877],[-62.67134323066545,17.238548103179152],[-62.684837976950568,17.252891443807055],[-62.6996779511433,17.265838055859124],[-62.715719265003138,17.277262409115124],[-62.732806382112244,17.287053733160793],[-62.750773625954999,17.295117091416667],[-62.769446786315825,17.301374301642205],[-62.775447336552126,17.30306137529503],[-62.779830607360459,17.305299634868856],[-62.834812527179004,17.336899413131224],[-62.838720379378174,17.339398450029787],[-62.840296056461881,17.347082980843222]]],"type":"Polygon"},"properties":{"alpha2":"KN","radius":22000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[124.34891981422678,39.906979769342065],[124.38680734214364,40.104130735834801],[124.88511403954634,40.457065640022073],[125.43498058466901,40.673213543847083],[125.72841412009018,40.866444625398984],[126.02079261610105,40.934414831101456],[126.48368201732596,41.378861312492639],[126.60147072754009,41.640771876558702],[126.72645343255584,41.718434866697699],[126.95471875271461,41.769302298704929],[127.20401020611158,41.540085039802548],[127.6050152625716,41.450543647341384],[128.05667179975649,41.477923557912426],[128.166670235628,41.654925466990704],[128.03316019954198,41.898572936752352],[128.04536339900056,41.987313760954194],[128.94443515718328,42.059588950852444],[129.1470826809109,42.183512928995377],[129.3137969064654,42.413390660208385],[129.5509345975635,42.417955134537578],[129.68328923674449,42.481357886249739],[129.89832587627171,42.997903551155559],[130.08258961930065,42.973916974422806],[130.24016651468179,42.891608121776855],[130.29229329119423,42.692831464388128],[130.4981212865068,42.57031376668516],[130.68692121279543,42.302578249446945],[130.4511145470839,42.289016817730833],[130.25887954872277,42.187639952524854],[130.0179944649999,42.000720871640524],[129.7473188254784,41.695446272674232],[129.69170572697789,41.557899422585955],[129.76560843793089,41.303879952073501],[129.70850551050654,40.857524754898755],[129.33436259108885,40.720855281528614],[129.10959470791531,40.491297663464316],[128.7151030759311,40.313699647378378],[128.51114469920691,40.130394950878134],[128.30441536178739,40.036206219638906],[127.99874149956798,40.002956021159868],[127.56635857562858,39.734046546997753],[127.51596883264708,39.327873527649544],[127.5571327602441,39.203223306256461],[127.78602288646159,39.083974527377876],[128.37428204312567,38.623502563555505],[128.10615772470251,38.327579536532447],[127.0908531716758,38.282559514630762],[126.72091774032259,37.950679249002924],[126.63385995256903,37.782114658679646],[126.34058257451457,37.865756462837531],[126.1167956231086,37.743206020775432],[126.02073867485241,37.851059252643815],[125.77761502140467,37.938224902755657],[125.44920577634686,37.730476872462539],[125.35798798130594,37.725013426011969],[125.25684098971098,37.845189757180528],[124.98896940807815,37.931634615140062],[124.89653610858795,38.07202107384915],[124.69144434496187,38.129255563174773],[124.82524822470309,38.214985004119924],[124.97390750597327,38.479965728032646],[125.15692567499059,38.641718186532806],[125.15759473969308,38.871482362265887],[125.39294214540885,39.278626559506741],[125.36506254174489,39.452521116618897],[125.27652350944005,39.549334181619543],[125.07376073933592,39.595533281616895],[124.84903247617167,39.507886565992685],[124.76071886143836,39.596221945664304],[124.6384398191241,39.615346769865788],[124.56056108496114,39.778361249046689],[124.34891981422678,39.906979769342065]]],"type":"Polygon"},"properties":{"alpha2":"KP","radius":385000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[130.81052254728661,37.509829710807395],[130.90365295647766,37.5534373538339],[130.93401357239497,37.529659980195319],[130.87059513888499,37.449006141579311],[130.81052254728661,37.509829710807395]]],[[[126.01058784908318,34.865010279536214],[126.25873845661083,34.998797874641305],[126.29136367591582,35.154071448623426],[126.47765907514938,35.489011581291081],[126.48872342837791,35.646897074127303],[126.60728078409451,35.723137195773781],[126.65628251011083,35.826032866025564],[126.64782398902115,36.010359990268043],[126.54068874809359,36.166229739838926],[126.53122027706983,36.322377680779709],[126.33771093567562,36.470690594183687],[126.30621373818308,36.596091118425427],[126.18105204380301,36.691795071378976],[126.16078813471736,36.771890828199233],[126.21735466724584,36.870789261601274],[126.35166804754731,36.957920792034557],[126.6649005599787,37.055158477752045],[126.75602629927288,37.228612453779178],[126.72524414354274,37.373614185277148],[126.58252624821533,37.563233891704662],[126.42349013128289,37.623820076709528],[126.36960539461137,37.771967416408948],[126.41168933354835,37.822397118906679],[126.6102084082294,37.848607610863553],[127.09046173851036,38.283556389338443],[128.05944894252576,38.314267982853707],[128.19223721944886,38.384284718649518],[128.37450653863394,38.623134307279528],[128.61952325843779,38.176526316173153],[129.33162929538969,37.279573461873014],[129.41800147070268,37.05897675187046],[129.47316320611486,36.74191028829879],[129.39509011491018,36.213612259059325],[129.46008765446101,36.099144491429826],[129.57259393717817,36.050326998118834],[129.48459993137405,35.684974557598416],[129.32888474202375,35.332840913794193],[129.21399403956684,35.182048494622492],[128.79660554900579,35.045825764112152],[128.73741903793606,34.941992505549926],[128.74076949635196,34.798654510126859],[128.64667938691585,34.737133911309677],[128.45231514448807,34.862010492814548],[128.20089894495914,34.899657540904741],[128.09134706854155,34.821044516790451],[128.05447469380667,34.708279262479877],[127.89190903698884,34.687676033103266],[127.78772111606445,34.584459227373152],[127.60098869381532,34.674760189534069],[127.49009967699922,34.63414112997701],[127.32459721510756,34.463559961246531],[127.15156539650883,34.553575171014984],[127.014430286189,34.546823992477606],[126.7696954644196,34.296682077363243],[126.53156570887387,34.314494119291112],[126.39959919850473,34.407548325391446],[126.16979894320833,34.355443215582376],[126.10888266671083,34.398931526310548],[126.16592173789279,34.623479529297953],[126.01058784908318,34.865010279536214]]],[[[126.16587857095811,33.311995946342741],[126.33777118942395,33.460228709101607],[126.69553953168801,33.549109467643959],[126.90098989051546,33.514928136738042],[126.93098553884364,33.443847549713951],[126.87269599063073,33.341365863819114],[126.58169985728085,33.23857314291012],[126.28201319777209,33.201755837900549],[126.16587857095811,33.311995946342741]]]],"type":"MultiPolygon"},"properties":{"alpha2":"KR","radius":310000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[46.531913266512156,29.096405093448013],[46.774465069039699,29.354600629900744],[46.912540482682353,29.55039695716944],[47.114528667404407,29.961205742660098],[47.219813138850306,30.039650969805145],[47.335468438051542,30.080064708339531],[47.668484108644186,30.09585705615104],[48.037805829680948,29.957642766451624],[48.184727436780406,29.978548850676546],[48.348009526625653,29.78258748650131],[48.34717504160708,29.719998118615919],[48.275272658620395,29.624488902587924],[48.154552812316233,29.579332677481027],[48.091940706022505,29.526410152220723],[48.05216990325102,29.437842207039392],[48.053669915537768,29.359905033287795],[48.178048058477771,28.997958084510945],[48.335609316703589,28.769113065113565],[48.442080530934739,28.543143548888835],[47.671403404145806,28.533420928460821],[47.583299616883679,28.628088397398283],[47.483021008306231,28.903750247196569],[47.393570971383248,28.985056935452494],[46.531913266512156,29.096405093448013]]],"type":"Polygon"},"properties":{"alpha2":"KW","radius":120000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-80.125466860418669,19.668557136324694],[-80.115984941822134,19.682552804165901],[-80.10078267887269,19.695892475698614],[-80.083555929363996,19.705886753649374],[-80.071246691089556,19.709080035612239],[-80.022044609081419,19.717319015167458],[-80.010306813792369,19.718510188886004],[-79.996427013348097,19.719094308744403],[-79.991875584941511,19.719077075716161],[-79.973545462991893,19.714737409866576],[-79.955032265323041,19.712154635213743],[-79.936358797441017,19.711311539671868],[-79.917688176388666,19.71221548787469],[-79.899183494341116,19.714858583626462],[-79.881006393958074,19.719217738877028],[-79.863315656397944,19.725254875401273],[-79.846265814327865,19.732917257421178],[-79.834692345360935,19.738783262240041],[-79.81443830173157,19.750589455794106],[-79.803018370484295,19.757906214911017],[-79.785108071944848,19.765388259557582],[-79.76638950539116,19.765534453904834],[-79.742501205436795,19.756976631464596],[-79.74253784830519,19.751037846556414],[-79.817418927203221,19.714782127670475],[-79.82968481458451,19.709859254419126],[-79.870086203341742,19.696909920579767],[-79.925902862612432,19.705910032914556],[-79.94611520412974,19.707066621382836],[-79.966340916568981,19.706173871237496],[-80.015966457348583,19.69894492607019],[-80.035628709646303,19.693834460641963],[-80.054671084854192,19.686755691769154],[-80.072897103258157,19.677781658369909],[-80.094241906326715,19.666157333692055],[-80.125466860418669,19.668557136324694]]],[[[-81.418835788239278,19.37460304769602],[-81.391027823485913,19.38465422687111],[-81.373294528716585,19.376461555941869],[-81.353051809066628,19.369520033105836],[-81.332183293833921,19.364781097561583],[-81.310927902704236,19.362299004486271],[-81.279966848000711,19.362053486000622],[-81.13060717060003,19.346566380050277],[-81.107511270989917,19.305397351240199],[-81.177777461411239,19.304529784486284],[-81.196144975894555,19.303515815808801],[-81.214341633700798,19.300818527270934],[-81.232213493961424,19.296460737510284],[-81.249609363544195,19.290479312744143],[-81.277358925286507,19.277574931597847],[-81.303725344037815,19.272078621176508],[-81.404587712168421,19.278632473819027],[-81.418835788239278,19.37460304769602]]]],"type":"MultiPolygon"},"properties":{"alpha2":"KY","radius":22000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[46.609435962429814,48.573867532358186],[46.70280783427372,48.805458524561644],[46.936349485413018,49.084738433200492],[46.924211871524818,49.221767114797288],[46.802330522554378,49.367164671372358],[46.889711151641492,49.69689415634226],[46.992195649186471,49.852526504595765],[47.252695821323769,50.02187441388449],[47.296997954931506,50.221492097228989],[47.425027229561913,50.354812641327449],[47.594192616206591,50.412962560330783],[47.705676754769136,50.377804930615305],[48.345026624143522,49.856271611413895],[48.471683865063873,49.839854367762449],[48.679664131188822,49.902628396544323],[48.768182491173661,49.981159398554645],[48.796823049742038,50.095973756263263],[48.625445831138457,50.612557093746005],[48.834698648774619,50.613050737099222],[49.274063278981671,50.828278539764092],[49.498126359259672,51.083396823074452],[49.804665216794021,51.129314807177487],[50.2777721187949,51.305385683209686],[50.794056131533807,51.728953693606869],[51.163363001505324,51.647146116303659],[51.427654471273982,51.481765167475125],[51.626942654637901,51.492537021194792],[52.007209060042186,51.672464740006099],[52.219130375993174,51.709100105980291],[52.330925892524384,51.681064093054886],[52.569136099708615,51.4859091221809],[53.338025877840835,51.48211233942633],[53.534535591649551,51.39937672680427],[53.705305628063904,51.24526596445272],[54.038195019969677,51.116983107933855],[54.328872376976129,50.901290238274534],[54.445356982621178,50.894195243506758],[54.577884370272642,50.991768273446226],[54.727062712110715,50.997785511247564],[55.351245026434995,50.670017584504606],[55.656119183392562,50.586824414528913],[56.014074926109444,50.695853714493687],[56.144111031094468,50.84445346120912],[56.491437630329763,51.019299858179792],[56.653112690531451,50.994279761473628],[57.011712518071072,51.064946029520023],[57.178900961040071,51.035778584233341],[57.471606715826233,50.891477083449118],[57.65156208409114,50.929150880157216],[57.838883126087026,51.091436354691091],[58.359125115694148,51.063629439697564],[58.547354323938322,50.970885348003016],[58.884943038323883,50.697709471191409],[59.452300039122811,50.620222473475714],[59.644194213573023,50.529304758254348],[59.799032285305323,50.576295141396287],[59.955415906388289,50.799055877226124],[60.053800270750841,50.849340418386348],[60.289538310402733,50.707811063401778],[60.516118533356305,50.66886754014012],[60.925568967434877,50.693761625253785],[61.205980990378762,50.768987914529042],[61.370075969850419,50.854605714043139],[61.463944779939993,50.990723232739008],[61.533537409402115,51.215911509024828],[61.470930642900917,51.372847527058234],[61.344280128490638,51.443763550427377],[60.464857157490243,51.651362928935818],[60.348715754381431,51.794041271120122],[60.030588443674255,51.933257198403503],[60.425591473208065,52.125327209046297],[60.694861767133816,52.16458422087878],[60.882678775333105,52.298157261505956],[60.902005331344881,52.446790926295513],[60.774681105488476,52.675785472072093],[60.802547615999146,52.744576690275139],[61.222130145571782,53.100554619059835],[61.155654606048749,53.502391247115391],[60.979737437340425,53.621877035616052],[61.096498510571202,53.76735101419294],[61.143920668203286,53.963679975379129],[61.227046570157128,54.016926910817446],[61.333700967007587,54.049027970720985],[61.911486263992664,53.952871121521333],[62.040310165038392,54.002374550936644],[62.488243410357818,54.014029099879238],[62.632799584184035,54.069008864890009],[63.043333612778326,54.103706202649668],[63.191357128937618,54.170833155198544],[63.8727892354555,54.241495084061548],[64.456559863382282,54.383523781897289],[65.079057816104509,54.373204554122879],[65.237575655953307,54.515869341648163],[65.477037503532983,54.623015124791792],[65.707257756300208,54.622064695600528],[65.914226481638934,54.693065714063607],[66.233960263309115,54.6690152089039],[68.054050697077969,54.962552181761239],[68.166502120392067,55.036831306477126],[68.206491115823781,55.160751180503382],[68.523657214943412,55.208393995422533],[68.977265736330011,55.389362608803737],[69.499857760302447,55.355562427520148],[70.171108834670733,55.165435072979811],[70.486392888195425,55.282113975211004],[70.738007978966266,55.304928190773495],[70.909944085520053,55.127838924592503],[71.183309397553757,54.603684463537995],[71.156622237198746,54.319115347211522],[71.255694258951777,54.184614691218968],[71.969324436815256,54.219179083753794],[72.185989491689782,54.325385358651076],[72.432938108796051,54.109549474517991],[72.913995595689343,54.107126150573983],[73.214414543042892,53.961023669012796],[73.380460792851949,53.96398075322098],[73.666357652643001,54.063290306111398],[73.715301223668035,53.996241289310262],[73.646996165011487,53.795094303641179],[73.719682248658614,53.663707918956639],[74.268239698042663,53.561533274429799],[74.8342434052702,53.825475406292362],[75.043210059519751,53.826420327842939],[75.226788659599421,53.896997730420409],[75.437286205069455,54.089378417787337],[75.689305512269684,54.113925267855464],[76.266663278964373,54.311723009183829],[76.836976337790077,54.442143408808555],[76.647185672158741,54.091600733712184],[76.688569671965794,53.904791752226465],[77.864005546868242,53.265128911136912],[79.14835376079445,51.868546428753184],[79.953916348374236,50.862455259565543],[80.088498824289587,50.846142602305946],[80.317557258998505,50.942769694930639],[80.400159862303809,51.02703248799898],[80.448197101374404,51.183181894157471],[80.735262248720773,51.293133068236486],[81.127065118476324,51.190804744109002],[81.210445762532316,51.012798261604871],[81.388119425295216,50.956275737171055],[81.488212934930132,50.795300493325882],[81.609076066284416,50.742239254612223],[82.501998613341655,50.73391576611688],[82.76090865864154,50.893096727266332],[82.968768787866992,50.896090070404661],[83.160297749106263,50.989016692813138],[83.361463504118873,50.993493402990083],[83.944957234660038,50.774488459748184],[84.175732331972171,50.520429748391599],[84.305091021862367,50.269145304522311],[84.989245323838006,50.061253733187804],[85.024061323217524,49.883085075406392],[85.252365453160223,49.641174707154626],[86.142992553418068,49.505544126631776],[86.675438052853153,49.777025862069905],[86.728497585287727,49.748548731049489],[86.756390463639093,49.546143483951845],[86.93729483043721,49.340480844603164],[87.233576371166123,49.215973432216934],[87.322477556907955,49.086101611726498],[86.87191553839881,49.080257866374062],[86.767895848053399,48.970886328057219],[86.717758426371958,48.697277510150293],[86.549273703170201,48.528844822041073],[85.815995867968994,48.403625388755074],[85.721074192426713,48.347379029712734],[85.53205219515155,47.924830153684958],[85.657440507521841,47.259719407152318],[85.484675180934133,47.063733066477823],[85.226463593441792,47.028055123693314],[84.786198224440838,46.830955589039583],[84.608025400197278,46.971046944060433],[84.010468384645861,46.971334877089554],[83.235694829876678,47.17227074547808],[83.067500535243482,47.129946516331742],[82.560393590600384,46.166015567544299],[82.372743245419073,45.696538874962577],[82.409312469109139,45.528530526745371],[82.624258338363404,45.379775761744803],[82.596805027301528,45.21604401677763],[82.521384393582593,45.125754664076197],[82.261229001286452,45.214123600301157],[81.944950660844242,45.161074070458632],[81.619134623596025,45.310822413024333],[80.429285605762303,45.057119592938122],[80.369706274905326,44.87831967869834],[80.481229818494498,44.714650623940145],[80.350417562660027,44.523759430819808],[80.361842896866236,44.130191703764098],[80.649205694517391,43.567484390432632],[80.785497312102805,43.161521930119228],[80.773320954047776,43.116211263123276],[80.597379303633247,43.039486701588686],[80.538750363807921,42.873694783313631],[80.289799447124977,42.809538268058382],[80.202773307992857,42.734131743620708],[80.166409461237819,42.603295676540299],[80.258849035590629,42.235479454708944],[80.209380638607897,42.190315873923034],[79.899642549062136,42.416623887606335],[79.428377479064466,42.483720389537979],[79.114051830016322,42.746432459370865],[78.515733599622862,42.864348302988951],[77.239757325317072,42.912386809025094],[76.979097713769079,42.973155499208239],[76.513602644176117,42.919215780322794],[75.85158887468009,42.932763560157021],[75.630481365350491,42.815028157758881],[75.042812572009552,42.905947756513974],[74.27908116881845,43.209200116032505],[73.998045344994793,43.188973450478514],[73.635540504207583,43.05573063136405],[73.551419098012857,42.977914917192791],[73.433801954312543,42.63535669466215],[73.492623317286004,42.409305258553331],[73.223913524852676,42.518678672908948],[72.855162299404384,42.561364975032923],[72.72493271899495,42.644261682608288],[72.266789334699254,42.757131128638378],[71.770798316869289,42.82160895849497],[71.257999507109005,42.730832502856551],[71.027398737630335,42.520418887829678],[70.943102487099296,42.362624620361089],[70.946491701877406,42.248810594743865],[70.731272581769062,42.172624552078005],[70.584222386377647,42.036252462287642],[70.361177234960039,42.045663691795092],[70.090947613938027,41.818225832514763],[69.644435996742999,41.663643220621182],[69.368221208716378,41.490766862757468],[69.118139969209736,41.40077466767314],[68.9900058607208,41.20825993232156],[68.61725318180109,40.910684033424303],[68.577306176472717,40.797743112005833],[68.600439206812524,40.660024404743723],[68.495696672070878,40.60891627552369],[68.288016231555559,40.658038349856866],[68.047895025307056,40.809362232253733],[68.065315772930262,41.025018687865767],[67.922686280568655,41.163073598649447],[66.814276013719521,41.14258970428299],[66.709831273793768,41.179300861480563],[66.472804163905408,41.934940704443861],[66.350725699444041,41.996894940654414],[66.00980452504318,42.005107779649961],[66.069917810156198,42.688972078242728],[66.035375828814125,42.801025708516192],[65.925202759198982,42.880877560920204],[65.803118615248536,42.877254062167758],[65.499521170267769,43.298580608306196],[65.266281861429263,43.420781621963783],[64.960919590979586,43.676321928568683],[64.811807299335968,43.692951670482834],[64.443119162911074,43.551462259997272],[63.20705648510679,43.6271334564006],[61.990316127036124,43.492353794861558],[61.160983467768716,44.168847782203777],[60.998166816799966,44.397276317471373],[58.544083544556699,45.549794494591765],[56.131239059219688,45.030408700592623],[56.008105356383098,44.944488192863837],[55.975751486525297,44.835339906779708],[55.977198818884851,41.32247039444389],[55.545219536100163,41.262965932446541],[55.434528030936313,41.296498611648367],[55.246935365420811,41.461234119778901],[54.853341180719482,41.964581404288282],[54.11026486828311,42.334181652228956],[53.678440906377681,42.295428881218832],[53.063892594346669,42.150157006054521],[52.493985752428891,41.780782777856118],[52.462381769950092,42.100572336751917],[52.634822580165292,42.537668406197795],[52.575636171428165,42.749150983145377],[52.46291639756209,42.818318859889281],[52.273091452699809,42.800057634219641],[52.14527381350198,42.871810735473353],[51.90243545899407,42.868351268841643],[51.639200606795427,43.142516416475218],[51.29563601823493,43.17433075802942],[51.295817142861168,43.491068526005819],[51.067678630615447,43.746304962078931],[50.835911470949988,44.172914085330177],[50.706023953724916,44.257094475967619],[50.275754541094777,44.355284424083784],[50.275381859136587,44.713265345202686],[49.995391599966375,44.937033255558084],[50.059448477758828,45.06663172056696],[50.349429796965431,45.082754663291432],[50.248446396268498,44.840354822074623],[50.326145744109752,44.666980419955046],[50.435787962216025,44.626013287164895],[50.875536267982667,44.636953823406877],[51.004566305165959,44.747989191100537],[51.040513085589794,44.980141099251142],[51.226555002827631,45.103121001688784],[51.415804049748722,45.357615629418454],[52.430442007854168,45.405762802499034],[52.608972764403582,45.527785576344534],[52.736944403421184,45.557608171057112],[53.034624566290226,45.960812908680559],[53.121913465879054,46.162507963756603],[53.064248009605905,46.475355552556358],[53.136149309489667,46.699177289415367],[53.060594051834826,46.864222192699067],[52.905554728256519,46.951036578315481],[52.520558640143619,46.980319073497576],[52.138280281177714,46.82888935641126],[51.744662551782142,46.934005146089468],[51.611091058372949,47.029670992305178],[51.159621412051663,47.104282982401948],[50.582839548635832,46.882471699021487],[50.417602469504551,46.86671328137367],[49.994589762959663,46.632502384493797],[49.479812768328649,46.530203898582158],[49.361845379174191,46.410392934178013],[49.188016816628519,46.347918008892115],[48.538439245753992,46.609451661364972],[48.51876198249952,46.734137403842141],[48.718122212834103,46.829605945861353],[48.761044486534061,47.017289079909396],[48.559699690404294,47.312266809022901],[48.174973277933418,47.698784869341175],[48.076316681682229,47.747478172731398],[47.510987518865235,47.799584150495107],[47.288023522901533,47.743406298380293],[47.09570984888208,47.943073394424601],[47.114352448955721,48.107467443422408],[47.063870635656521,48.226372664783028],[46.661152294557823,48.412445858386405],[46.609435962429814,48.573867532358186]]],"type":"Polygon"},"properties":{"alpha2":"KZ","radius":1646000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[100.11517917069303,20.257793613888545],[100.24955041836319,20.730095737869835],[100.49235429512696,20.885691483438567],[100.75676232138298,21.312387234054743],[100.9393372085899,21.374119087818386],[101.1389335132133,21.567126575438976],[101.19641935014144,21.521972138761861],[101.23006477870163,21.324830570744336],[101.31394963211419,21.246841985803897],[101.55459638666107,21.235702746241021],[101.68846992013557,21.314927342556274],[101.74229217918891,21.525432625625594],[101.74007933955122,21.795761448745658],[101.60075384333352,21.994952946768102],[101.52478680650778,22.253545117721622],[101.73877496375437,22.495008783184641],[102.09139386611329,22.412081444946299],[102.49884369677439,21.955680077601922],[102.67143674695608,21.818922775019836],[102.81580030003509,21.807093220965761],[102.94936560234915,21.68122033158112],[102.87030592538053,21.251219164787983],[103.13332097907248,20.878939981368546],[103.64127302998249,20.725499474636763],[104.10133563817753,20.945242846453986],[104.53343289614507,20.685594302667653],[104.5829493123358,20.646581763994163],[104.58618817405187,20.415177187469766],[104.70367806436154,20.265667447266193],[104.88851205802794,20.168967131295545],[104.92771871698785,20.018214289212576],[104.58776650348905,19.619002436780377],[104.2754346575164,19.677623508926146],[104.13317725272428,19.630458890083816],[104.02845033195614,19.405588432702832],[104.04195658866466,19.275094750778329],[104.62644870114951,18.853344506298722],[105.14285018406495,18.654208841353427],[105.10066086965864,18.499506954789368],[105.13906480430828,18.371793864926516],[105.30963474541674,18.208970645701068],[105.45804768724659,18.154094535900374],[105.70307797633198,17.725464849827787],[106.5020130676449,16.953938067688728],[106.54809973459258,16.681101685968105],[106.60769980399179,16.581782445608386],[106.70323144889539,16.52023333795205],[106.85086380593052,16.51544973791173],[106.93347302311959,16.356221407814107],[107.39618690711764,16.042852298391249],[107.39174486863097,15.951826316943666],[107.25471266640294,15.808388241722092],[107.25224572883759,15.676819524030929],[107.65285283150648,15.255192716047461],[107.5177984941661,14.974669344777874],[107.51919085223564,14.705193060092256],[107.41457963987766,14.563103753825033],[107.27993642494047,14.545533247195037],[106.93799149340168,14.327572108644274],[106.81996357914836,14.314930129682395],[106.53379795150127,14.504608292758604],[106.26288066466107,14.439977708717818],[106.08935229221477,14.26980219152526],[106.12446180007986,14.049130911675014],[106.06665054812312,13.921423440867015],[105.90457308524604,13.924755832346969],[105.71769802716524,14.087756636858375],[105.57897395870039,14.13896914546639],[105.34619600431732,14.112747640301524],[105.24288509441,14.204824643290536],[105.1835597768065,14.346073476406934],[105.40304624060165,14.458244172133998],[105.48757419413477,14.566600257008767],[105.54342728033299,14.951722467520629],[105.4907877888469,15.256548607861182],[105.62912658155075,15.607275289895103],[105.56822530980936,15.723514053274714],[105.39912814655875,15.830038485109082],[105.31587346615783,16.017499498743071],[105.04739317616389,16.160417795821154],[104.81959270066218,16.46623355926458],[104.75088879313979,16.647618690830704],[104.74371729079951,16.878898430541962],[104.8041688478085,17.289223075075412],[104.73064233404219,17.469028116759002],[104.42548698824494,17.701897184435168],[104.00194707206809,18.259767821837649],[103.46894223181633,18.4187708252208],[103.35374091447201,18.388375180941665],[103.15286839205103,18.221423051155458],[103.0472240613004,18.025593843820147],[102.78976776550765,17.935079310426289],[102.675504050656,17.822673019962767],[102.22251130757172,18.151541817862572],[102.08698622638933,18.173165853627474],[101.80407028323069,18.016478315278217],[101.1673721840704,17.499207350479832],[101.1051969155304,17.479789440837305],[100.95598856098846,17.541331686297891],[100.90875907443396,17.583944818461433],[101.14523503170778,18.164065244853454],[101.04717890645895,18.441966668197399],[101.16767690341487,18.628549348090569],[101.28229156899013,18.977728162040464],[101.19479870801219,19.467825200981888],[101.11136030498008,19.571438671056175],[100.99281044614186,19.606374738689315],[100.63021711585885,19.500448031105588],[100.51374354815646,19.553709349523828],[100.42034379379017,19.644605806563078],[100.39789646291673,19.756069917658813],[100.52082232717041,20.019081177280313],[100.49885493814612,20.166106730490551],[100.32707051494941,20.300640836999399],[100.11517917069303,20.257793613888545]]],"type":"Polygon"},"properties":{"alpha2":"LA","radius":557000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[35.109015207893655,33.083948628004919],[35.198618424805737,33.248977696780422],[35.25159817048516,33.39250159223365],[35.334747081232557,33.504067493205895],[35.510961796648402,33.879668065718519],[35.598940418168937,34.014731760158746],[35.651203134245009,34.252294618505012],[35.804480569900768,34.437166933285368],[35.926474790045191,34.51310225700896],[35.95898037407391,34.562993706762377],[35.976488877795127,34.628960936401384],[36.191512622278914,34.631047065851348],[36.296358429751386,34.678450465371071],[36.383766926818502,34.657682418985388],[36.432747205026075,34.613385353951166],[36.446945727505522,34.508797517814692],[36.504185562281528,34.432225458485796],[36.584700130766933,34.22126059486677],[36.534999608724831,34.134446081105565],[36.386970237234998,33.985258642975609],[36.361677721332839,33.908495502309975],[36.364791232126251,33.839458599916753],[36.348473056467334,33.827288903298417],[36.198088182730821,33.835877040138868],[36.094359770325859,33.78746649518289],[36.034169751995577,33.690099529037347],[36.034190629841078,33.584988726586282],[36.022083778500686,33.56268623088237],[35.951198792015809,33.515499194163112],[35.910662125955312,33.462368167929014],[35.551381883137843,33.208115900864705],[35.492964633244753,33.119645633632295],[35.41116627503547,33.075961992035523],[35.223297175640155,33.09081269497986],[35.109015207893655,33.083948628004919]]],"type":"Polygon"},"properties":{"alpha2":"LB","radius":117000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-61.072822367111577,13.865569938057924],[-61.063314039954172,13.915466041106946],[-60.999201362993531,14.007342731942416],[-60.944450423976896,14.072695023439456],[-60.908245543318799,14.093030224699779],[-60.887014949473624,14.011106833453542],[-60.895412531637191,13.822027206767096],[-60.951509244460595,13.717917236795135],[-61.060429799045366,13.783249378706225],[-61.072822367111577,13.865569938057924]]],"type":"Polygon"},"properties":{"alpha2":"LC","radius":22000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[9.4797296457502451,47.09752940372659],[9.4798017436829873,47.102371061192493],[9.4839978694423888,47.16826736265871],[9.4844667839127705,47.172609996313021],[9.4860519207172853,47.176680093258724],[9.5255175413451543,47.266168165314497],[9.5275661162185141,47.270246976893134],[9.5300387527853978,47.266410385787225],[9.5366335319414972,47.254563654917263],[9.5388960183514371,47.246698334760474],[9.5470771476030336,47.222094803736802],[9.5490746224163079,47.217177942657976],[9.5516505016089095,47.209725024247255],[9.5585602110380616,47.192396347171353],[9.5670544763929222,47.175786874281542],[9.5770593918029014,47.160041119258828],[9.5884879077172016,47.145296080852887],[9.6009666503429436,47.131952857646134],[9.6028914138540813,47.127503597112316],[9.6087829147156398,47.1118236130485],[9.6102794281729995,47.107140069223895],[9.6083978266409513,47.102597524677115],[9.5955523381686767,47.075926636404859],[9.5832991135227079,47.06099236819329],[9.5801777492891063,47.057597589049003],[9.5755671663423829,47.05769741393474],[9.5125418921681035,47.062041261152267],[9.4878782293705228,47.062494275952758],[9.4865817574312956,47.067041494848915],[9.4805961466881694,47.09276536959014],[9.4797296457502451,47.09752940372659]]],"type":"Polygon"},"properties":{"alpha2":"LI","radius":14000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[79.713193451274307,8.1822866551259015],[79.84116882340679,8.3871296557024539],[79.94283664720956,8.7193314940686086],[79.901873525146982,8.9479726214977386],[79.74809482276153,9.1042199517191307],[79.896874739790448,9.0618343350743551],[80.008048766732301,9.0900394634704842],[80.085445474019608,9.1746854511804443],[80.117170287769014,9.3269152132794115],[80.073731890621744,9.5385418971800586],[79.906952587047641,9.6200781570664393],[79.845962549645051,9.7146242598831769],[79.982433555216403,9.8124581892083587],[80.252693659439714,9.7961081623292241],[80.710938507245132,9.3661838218756088],[81.250358807216685,8.5396552372916545],[81.372589713128448,8.4313043735338002],[81.437885440339784,8.123083627552651],[81.831829613021611,7.4282994228043924],[81.87417066692089,7.2836411399376821],[81.861273421254737,6.9013017580056575],[81.767645493013944,6.6143635465933253],[81.641249289110974,6.4295772074366102],[81.312236402082647,6.2068664809351102],[80.724058938063209,5.979274710282076],[80.495816111835666,5.9496021974600497],[80.272656428277486,6.0083711267706992],[80.099007537056607,6.1500942810663002],[79.859551878319408,6.8293357275979973],[79.713193451274307,8.1822866551259015]]],"type":"Polygon"},"properties":{"alpha2":"LK","radius":239000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-11.507189758914782,6.9065280684004895],[-11.267452372466602,7.232395844437205],[-10.661090716431191,7.770133301474405],[-10.570606873587087,8.0710025801891607],[-10.34025911072888,8.2438753078879419],[-10.282975580199974,8.4849267213245358],[-10.147401887521632,8.5194524084806709],[-9.9550526534075292,8.4713469916591322],[-9.7819196987062416,8.5374267411984359],[-9.663744640501001,8.473363819789725],[-9.5184988484063116,8.3459955414224556],[-9.3356680629272049,7.4325991929368973],[-9.1362770206821455,7.2693626969381935],[-8.9838758643057588,7.2642158505934313],[-8.876354675469857,7.3214781765645069],[-8.7616675439022771,7.4780081247988957],[-8.7081298287794109,7.6587426685820539],[-8.5790341055264054,7.6768899713001018],[-8.4373903762045046,7.5162729056860993],[-8.2968589575895528,7.0739937162038666],[-8.3327985702157115,6.8016524480529039],[-8.4595264739543232,6.5997091514456221],[-8.4287169902461034,6.4473869388959342],[-8.2867754906753284,6.3197938659564263],[-7.9816580660361387,6.2859339594215422],[-7.8888164599590667,6.2346937549922794],[-7.7517439120696796,5.962630787634053],[-7.4546361251733808,5.8411700725177074],[-7.4001911538204244,5.5505956132020815],[-7.4300450304973511,5.324573233435884],[-7.5664781588054506,5.016501426051958],[-7.5903660197215368,4.8346093116010875],[-7.5453967964106869,4.3515956961471129],[-8.2589490371267438,4.5901917645306689],[-9.1320841841188702,5.0547839368496943],[-10.27679457027681,6.0770722411518259],[-10.781446638791175,6.3074075273543091],[-10.876797310371911,6.4776109322375532],[-11.291482524831364,6.6884029884782485],[-11.507189758914782,6.9065280684004895]]],"type":"Polygon"},"properties":{"alpha2":"LR","radius":330000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[27.051974282527432,-29.664031295912135],[27.090697028397084,-29.60241664178556],[27.346298549910486,-29.466108118156331],[27.735677238493999,-28.940215237167994],[27.958298992799666,-28.870153294607054],[28.239451826248718,-28.698828588839753],[28.625745009557725,-28.582020150253779],[28.971738039254177,-28.893936000981356],[29.301207379685845,-29.090032887300843],[29.390520051661227,-29.269756049717714],[29.293356298799662,-29.566769725209763],[29.141085331378282,-29.72795208110746],[29.097848352060204,-29.918913137952774],[28.741679512385964,-30.100110446578899],[28.375829546310758,-30.167711565503566],[28.01814487395151,-30.641979158350676],[27.753231491426671,-30.599728314780609],[27.388616068045323,-30.315755683376018],[27.326949063570659,-30.128791058868142],[27.051974282527432,-29.664031295912135]]],"type":"Polygon"},"properties":{"alpha2":"LS","radius":121000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[20.900255919916592,55.286823678380763],[21.045002268036317,55.455126515582513],[21.108042942221704,55.645576637539548],[21.046369375072249,56.069918802463249],[21.653584366430554,56.31428395155514],[22.084580897116609,56.406509954879759],[22.875587312353328,56.396235770024973],[23.06102442921571,56.340755732010948],[23.806628305187235,56.329555591024494],[24.138195410980543,56.266328816054823],[24.493903218645887,56.288550815673716],[24.84101784967352,56.410891411759273],[25.093465647764067,56.200965251403964],[25.659335260936977,56.106093306398961],[26.292606966984785,55.747391298797076],[26.593321868078313,55.667330414792723],[26.576066209863438,55.418522449542905],[26.775354535298757,55.273153832960965],[26.601056412370077,55.130394331079962],[26.312116195725121,55.124807813351929],[26.170752037369862,55.001057762383226],[25.819618787991558,54.875776290417058],[25.736942590567669,54.744388961819766],[25.666091540054033,54.442686087903745],[25.764780374621974,54.221136477350449],[25.749061508470398,54.157213644625514],[25.57308696398556,54.140158383397697],[25.401437703281271,54.241781380609808],[25.273023535817575,54.246103734105574],[25.106757023293735,54.153368409261518],[24.878820856173181,54.109946183673529],[24.768063643470004,53.974893746064588],[24.317977963892638,53.893190732432963],[24.14493662471039,53.946533719258852],[23.48928817815926,53.938554983785529],[23.466718675416192,54.096306112028913],[23.369748138718947,54.199832589643002],[22.92944066884813,54.380167555193253],[22.76625115290458,54.35708805689432],[22.680170635402323,54.493076639318467],[22.753603679900969,54.846388525949898],[22.538279986487773,55.04552667086481],[21.879708010478733,55.099652522272486],[21.11880476695735,55.326196253447741],[20.900255919916592,55.286823678380763]]],"type":"Polygon"},"properties":{"alpha2":"LT","radius":219000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[5.725217772578854,49.808402560641881],[5.7260604756044211,49.833295698035919],[5.7442818458159488,49.919496683726024],[5.7922501576208516,49.970516971118158],[5.8670745303230865,50.082639949289359],[5.9763381129828765,50.166915710060913],[6.0889391169892102,50.154354021899948],[6.1162528627788673,50.120922058051384],[6.1135182837203743,50.076753500359146],[6.1251978425430407,50.018343802464052],[6.1423799811050284,49.982451540184464],[6.1663278976182774,49.95067139431027],[6.2568158276493495,49.873327023354356],[6.3075076885799781,49.846441468210024],[6.4287000513239025,49.808743487675038],[6.4870985143965028,49.798280394856015],[6.4934351832618882,49.754401675854254],[6.4845398127108762,49.707965393959697],[6.4237472272620373,49.6616070304744],[6.379504747989893,49.599043916465163],[6.3520687820810897,49.520897146236528],[6.344132528018334,49.45304898390539],[6.2548808614455051,49.486205322762672],[6.1964690828278837,49.495212570380467],[6.1379752330469248,49.486754678874604],[6.0740495429665318,49.454824834133184],[6.0114300071455817,49.445745443100066],[5.9595613200923809,49.454843263714473],[5.9062488248777925,49.485510994245388],[5.823551437622565,49.505293715399937],[5.7900262089099499,49.538315671942307],[5.8130794143059656,49.599265351041254],[5.8170266455674211,49.639170177500738],[5.812934011129868,49.679060353644168],[5.8009660324226138,49.717332318514764],[5.7816038149518727,49.752447562270277],[5.7556257081503741,49.782994473348211],[5.725217772578854,49.808402560641881]]],"type":"Polygon"},"properties":{"alpha2":"LU","radius":47000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[21.046262052106353,56.070378968080263],[21.01516756481934,56.264110429641654],[21.071523095691379,56.823561501103264],[21.335497210852996,57.005032176759364],[21.459402104305376,57.322302762309064],[21.728827202742572,57.570745866986613],[22.554505162289693,57.724031252998088],[22.684424038408885,57.577697017130554],[23.136638554010183,57.323655906467735],[23.319078174640776,57.08325945515265],[23.639305664490649,56.977221754427561],[23.900553155280715,57.004452403612859],[24.28063842286333,57.173321111111306],[24.3779417513155,57.274185209070048],[24.322733411665684,57.870379762038709],[25.111016282788231,58.06320269659409],[25.339940843885767,58.039106358736582],[25.990990953142028,57.837937837755007],[26.290237172728993,57.60817819175778],[26.476208957635482,57.541787487797862],[26.960656682308489,57.609103195216562],[27.510953399829813,57.507979850031724],[27.583023852797385,57.413281413163396],[27.828378330956504,57.293160009287057],[27.763284087767197,56.961941017125824],[28.102937592472188,56.54556470297743],[28.201816248542539,56.260424329156976],[28.147817491632324,56.143165805475682],[27.880482996125938,56.065640863643466],[27.662299836127492,55.919006947538527],[27.576667996544362,55.799047583547427],[27.024904600538413,55.825671391152774],[26.822356021997216,55.709429111882926],[26.593508834991777,55.667804402246226],[26.281415696294118,55.750715375011978],[26.075756939559561,55.902081412245032],[25.647758639537042,56.109881620715811],[25.070048211468748,56.200639053389452],[24.814139993721781,56.394460873456396],[24.478683460908318,56.285195489893809],[24.120721174864013,56.264444066316948],[23.800550092949617,56.329863703165572],[23.228606275390966,56.363412797171051],[23.043048197259878,56.324348211327113],[22.89339008756561,56.392555392536472],[22.084612772266194,56.405822359145738],[21.6337819288416,56.307189032063526],[21.046262052106353,56.070378968080263]]],"type":"Polygon"},"properties":{"alpha2":"LV","radius":241000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[9.3105660783585549,30.115161279388026],[9.9018662936683555,30.395841764879247],[10.215505557785436,30.783760719218094],[10.25463455038293,30.956826842085494],[10.115182284963922,31.46374095537853],[10.274826053605292,31.684720627067279],[10.488653564922966,31.754108381280684],[10.609106778943802,31.929371405783439],[10.826484839298145,32.080509583973985],[11.447307665382054,32.413253323140715],[11.486701794456611,32.523397661715876],[11.454009992028155,32.783965053723698],[11.504793260547238,33.181600923347411],[11.813395332353027,33.093450510015614],[12.306473507351857,32.853225018425867],[12.742278516735549,32.803259358647146],[13.283471760335766,32.914433054732079],[14.159726544160081,32.708343269310866],[14.513779547794433,32.512398704191725],[15.176471155324808,32.39097314404296],[15.358812622426866,32.15958956227324],[15.368276475832408,31.960818066622441],[15.497567465458234,31.657513971982784],[15.731150492784945,31.413361399052373],[16.12327967324903,31.265510743371728],[16.786231831447161,31.213645465305085],[17.826380055999334,30.928895476206389],[18.190343646249108,30.777126863199964],[18.688634751435959,30.406837011945612],[19.097883706323518,30.269468778561883],[19.299818433802063,30.292379728356686],[19.705265260453274,30.484424326720852],[20.110500767192114,30.96419811835877],[20.141116166988027,31.186743236382636],[19.961401311376303,31.556051019195635],[19.926602247285597,31.81751503883633],[20.034092592443375,32.111702136900689],[20.374197174004369,32.43290413942804],[21.062360334334546,32.77527932980157],[21.405472649198853,32.796185667651834],[21.636023906931079,32.93704379170326],[22.191817835922414,32.917155735050983],[23.090427994520311,32.618580537160142],[23.151466922226401,32.331666998552308],[23.29718621101199,32.216210599270994],[23.797607546992193,32.158457333227133],[24.138577663149995,32.009662643372081],[24.683880632201856,32.015783739744201],[24.950573335069457,31.953542409096155],[25.150170737352592,31.655042622643961],[24.866831314592872,31.330130228237994],[24.961999766537765,30.683017822100894],[24.712452122604972,30.17335179769816],[24.980273437500017,29.176831379117964],[24.979271916771513,20.002715036247512],[24.086042100026585,19.972277979680985],[23.984118312482106,19.834895844644912],[23.980056581005385,19.496979823966957],[16.050468671389563,23.411380390143901],[15.89825679313917,23.406869395368322],[14.21556488032922,22.619892231381158],[13.488676753374861,23.175511659434914],[11.968019559272946,23.518066721904372],[11.485511629546526,24.317262968354953],[10.72273722157243,24.540315309363915],[10.438944689509109,24.48048217523079],[10.328920650323566,24.528226411676016],[10.119726747030237,24.790347970649247],[9.987525656555599,25.347922881529406],[9.4225869873273957,26.14708787172918],[9.4916046492035449,26.333531620378839],[9.8098354608477134,26.520709606774837],[9.8796072992591757,26.629705732619357],[9.8853170348928767,26.825043800505419],[9.747785591391521,27.330825993911407],[9.9074484767894901,27.806114704444546],[9.8159295027848597,28.55650844225287],[9.8414384069963496,28.966946133331295],[9.8066377399447067,29.171457058506004],[9.6441359276324032,29.627886929852433],[9.3105660783585549,30.115161279388026]]],"type":"Polygon"},"properties":{"alpha2":"LY","radius":1400000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-16.999830822516781,21.421329919373694],[-16.930556759844038,21.899900262175176],[-16.793091565928545,22.159594212920865],[-16.683860541137285,22.274173071458456],[-16.514942547988106,22.349914109841773],[-16.375003431093209,22.567259277084158],[-16.30404807170726,22.834683309754347],[-16.20487446803866,22.975189460049481],[-16.209959853041646,23.09789667579113],[-15.968950286934376,23.490772132793648],[-15.980453639573842,23.670329236566296],[-15.899150118236305,23.844304700735954],[-15.587210297122761,24.073850401277003],[-15.188505936882907,24.478643358500207],[-15.035155268582292,24.559157557246568],[-14.907185779358604,24.721306263065834],[-14.794699545082143,25.404055800441373],[-14.526158748185399,25.926712555902025],[-14.413693366047349,26.253507316100862],[-13.578670711379146,26.737649738032857],[-13.175751869848979,27.655555547807886],[-12.948753024590385,27.913954365595664],[-12.000852769051573,28.12563007014754],[-11.540971204154394,28.316984754209514],[-11.080806983939816,28.713553993582032],[-10.48744933772937,29.066069087879288],[-10.207771934542748,29.372442564134992],[-9.7531980412974573,29.944911612631987],[-9.6662762952383883,30.11926612835401],[-9.6422492590700699,30.412721760079371],[-9.8752235584747368,30.717915260267354],[-9.8084851239408817,31.424556014243755],[-9.6747673138729073,31.710866622680662],[-9.3443441298555623,32.097789989463642],[-9.2455915187300359,32.572335579665008],[-8.5067180876927431,33.255968719243818],[-8.297005202960726,33.375864916876168],[-7.5549302879905138,33.643660710952126],[-6.8943894392617864,33.978278833012624],[-6.3536058592744959,34.776328041641868],[-5.9246179119439182,35.785577275534763],[-5.6023606742880858,35.835650338450314],[-5.3973251982446779,35.929607005184558],[-5.2782319608331942,35.902595402631057],[-5.3062983342138219,35.703774881402794],[-5.1048694865489486,35.468373914544848],[-4.8122763091430087,35.2723592003288],[-4.3681145017087637,35.16721655869123],[-3.6932774249848555,35.27969988580228],[-3.5520632901693219,35.225071594174331],[-3.2416795555731714,35.233951794832691],[-2.9723156722624897,35.406869487277397],[-2.8305760198885395,35.164202105774258],[-2.6460414756397035,35.115812982215814],[-2.2198030799098447,35.103968929189492],[-2.1310620862555401,34.971750392546063],[-1.7958170636389941,34.751774471665726],[-1.8044635220789069,34.610009588615348],[-1.7335680809622669,34.467080525279535],[-1.7658466594590141,34.332254386631398],[-1.7065000336412046,34.170424574182043],[-1.7113704430824312,33.76583541999279],[-1.631539300046319,33.566710977649706],[-1.6636104330227208,33.282106353632138],[-1.4547695521566153,32.794065964626391],[-1.1111864823320008,32.552162454915738],[-1.0658183954284421,32.468377974823817],[-1.219742898792975,32.32934616148804],[-1.2261766157400757,32.107400078884901],[-2.458092535614397,32.129423430257503],[-2.8593778874036109,32.067307857318994],[-3.0175097414230199,31.83445545469851],[-3.6570618606407299,31.679436352756806],[-3.765535541786849,31.603404947542824],[-3.7966654298334421,31.261956734261901],[-3.624732627887894,31.065681443002493],[-3.6720479240944752,30.961100468493864],[-3.9921388453967555,30.905250787096403],[-4.3229903002720445,30.699161263369628],[-4.9334098269567868,30.480446379858542],[-5.1833403481750127,30.163118713288878],[-5.4488942015540918,29.957189634042013],[-5.7799191847541671,29.868232183187928],[-6.3872030024955748,29.799579692166176],[-6.6353681235455593,29.569036859884324],[-7.163465300205341,29.610002935548923],[-7.485817925195521,29.392466456768492],[-7.6847103013765148,29.348690842506667],[-8.3309058693742859,28.9365761692425],[-8.6206801085392666,28.727360718902887],[-8.6792234444400265,28.622379406969422],[-8.6835846371114087,27.656570937442407],[-8.7592875576955436,27.568412734317274],[-8.7988885814712177,27.358924328678928],[-8.7576193214661835,27.148199922325531],[-8.9959176523511477,27.091155027381731],[-9.4022849193119065,27.08912543274209],[-9.8114505284718039,26.851031125731495],[-10.02886192557353,26.897094375702185],[-10.251425370535829,26.861148880360428],[-10.542132178744202,26.985898007282124],[-10.747157154357119,27.016954005024164],[-11.18765265025511,26.929994979916891],[-11.281407833376772,26.856611647087853],[-11.338095699514271,26.633552245552469],[-11.51121544132077,26.469992235575333],[-11.718418709371987,26.10428158376844],[-12.030106314110204,25.99799563971829],[-12.431357700905416,24.830837490367813],[-13.007191533797281,24.454406292676698],[-13.311116308203797,23.981212871059672],[-13.78583224048494,23.78208955987051],[-13.900260974280313,23.674931769122626],[-14.098577248662497,23.110175079218568],[-14.221385764607176,22.310237356976877],[-14.58669763607848,21.901523863851445],[-14.668981723383071,21.605225415992155],[-14.751129005199889,21.500767007215067],[-14.974882746365095,21.441015625000063],[-16.028036949864191,21.500585937500034],[-16.999830822516781,21.421329919373694]]],"type":"Polygon"},"properties":{"alpha2":"MA","radius":1177000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[7.3779980017595159,43.732057149230364],[7.3782705468157408,43.736653189560812],[7.3795486264929258,43.748368919935032],[7.380300137308919,43.75310160484721],[7.383871315789162,43.756296876235979],[7.391113653760585,43.762166483480797],[7.3951375238855395,43.765105401923286],[7.3998527532935725,43.766716421483579],[7.4098087947085611,43.769568151939126],[7.4144387363402382,43.770655454072106],[7.4189079978847587,43.76902932778966],[7.4322306285309327,43.76343961536638],[7.4366881361356247,43.761297175374096],[7.4377130269228342,43.7564588882762],[7.437921372804559,43.755150939129813],[7.4384260297117555,43.750592741891779],[7.4341109362223934,43.749039741945651],[7.3824608742404605,43.733188801449586],[7.3779980017595159,43.732057149230364]]],"type":"Polygon"},"properties":{"alpha2":"MC","radius":3000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[26.619357204066393,48.260101583312689],[26.847089364831845,48.386897023445073],[27.197395982874848,48.371986315036608],[27.549212280816626,48.477481937616623],[27.814921201288129,48.417865668818351],[28.434939071249929,48.156407321408253],[28.773695759372117,48.119358082140451],[28.935077682603975,47.962995029290695],[29.125271742622751,47.964375694636239],[29.19551374461059,47.877880087894326],[29.170529044962198,47.578582454378044],[29.205926500239304,47.468208266069098],[29.538981354830145,47.270850999391733],[29.600335671289265,46.963878130294276],[29.87767254653026,46.828669719002853],[29.970610568897371,46.538243640561298],[30.130661723609865,46.423050207300662],[30.075606199262999,46.378034723575212],[29.837971960786888,46.350821764293556],[29.735999107490247,46.408957515798541],[29.414993187073506,46.44056694691394],[29.123503119895673,46.360079252027759],[29.035080601267595,46.28154859967421],[28.947576358176434,46.050078478469196],[28.555104471960227,45.713315476166066],[28.498802187757715,45.517896408208244],[28.212551647305158,45.450745946706803],[28.075034333574255,45.598998109474969],[28.134279589839871,45.734048840593182],[28.118653909697468,46.134512782678627],[28.225294436746907,46.418026431288865],[28.233029942575367,46.59836877343794],[28.086732844031584,46.940322413167202],[27.853937943468996,47.114664850485589],[27.337056136487647,47.63988662691375],[27.157108935142485,47.951733987852108],[26.970335455769437,48.160763603394713],[26.795580267012745,48.251684183719483],[26.619357204066393,48.260101583312689]]],"type":"Polygon"},"properties":{"alpha2":"MD","radius":190000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[18.436561457593239,42.559634589662757],[18.485583314999349,42.693067607015863],[18.446254219041478,42.97280181377473],[18.582994785284971,43.068780935719801],[18.674428016886342,43.230634536954803],[18.893055009531299,43.383407396285804],[18.974268050625607,43.542095716847243],[19.19417769552917,43.533090213886553],[19.258656103927663,43.438758067589873],[19.414475330504207,43.342596961704032],[19.581103222817795,43.194022261051913],[19.790950645963907,43.109004933559255],[19.94396580741639,43.081407549517188],[20.273176592327481,42.93263811406451],[20.339748280983006,42.892735579298574],[20.346638871409969,42.85806970833017],[20.344112628901339,42.828134009794582],[20.240022844527481,42.794305048556978],[20.132154399646236,42.708833960641186],[20.063841292674045,42.547480583355068],[19.78833318363985,42.476421879552262],[19.705529705021728,42.516221649269568],[19.605228851452292,42.515534430217336],[19.470563040397348,42.420613622193578],[19.334630067517494,42.229611212720151],[19.331408729068553,42.148704824762902],[19.361187067996386,42.069059764977169],[19.34217703022891,41.86945269067192],[19.186590724954407,41.948806062630581],[19.107653055197158,42.071567415905164],[18.893485786171656,42.248300879135094],[18.517572765721965,42.433130617181732],[18.438325115799834,42.523049287954642],[18.436561457593239,42.559634589662757]]],"type":"Polygon"},"properties":{"alpha2":"ME","radius":96000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-63.122735425950935,18.069162169617954],[-63.121376631702283,18.07346003074716],[-63.116668425830525,18.086186453891418],[-63.114807395727055,18.090555305639779],[-63.110619898769762,18.092794762689355],[-63.067616944252904,18.113183745828103],[-63.063036987798469,18.115080313973706],[-63.058080052430221,18.115038318605592],[-63.029402991813591,18.113355735967382],[-63.024866137699306,18.112879166275086],[-63.020805375068925,18.110800616110819],[-63.013771212737737,18.106781094778711],[-63.009674330293322,18.104159704911844],[-63.00967249298261,18.099295948419062],[-63.010929779504622,18.074010519476463],[-63.011415244620821,18.069190820773162],[-63.016253105476416,18.068945312499991],[-63.118233103762755,18.068945312499991],[-63.122735425950935,18.069162169617954]]],"type":"Polygon"},"properties":{"alpha2":"MF","radius":7000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[43.290721466110476,-21.932587734059432],[43.439005492498211,-21.637050922953073],[43.502066161010831,-21.356577027073296],[43.809310924851218,-21.160189878043429],[43.91132833349802,-20.865912883801975],[44.248108100535731,-20.361428029289197],[44.402735343248814,-19.931411624557597],[44.448430306340967,-19.450786532353163],[44.238975051986131,-19.075121795518097],[44.233108478987127,-18.751547353592134],[44.040324466007164,-18.288418244628652],[44.009678741032253,-17.781832121440143],[43.94382600542616,-17.581412062707081],[43.979610603604392,-17.391687487510616],[44.417687968246277,-16.701190432722768],[44.442674473874071,-16.24388750237226],[44.925041729200764,-16.166351021107328],[45.2228855354277,-15.950726087424954],[45.382921384823874,-16.005420993772692],[45.536188795237166,-15.973976837052469],[45.700277370928767,-15.814013412733138],[46.281147550645699,-15.720285918833513],[46.475229360445724,-15.513641170524449],[46.878321967102259,-15.232313711878883],[47.076130766160183,-15.167267432706568],[47.267500634878232,-14.957136460702563],[47.356094998716266,-14.763127603223614],[47.439097318686052,-14.70359233893805],[47.566758496868033,-14.743576961674258],[47.697785197859766,-14.697735522804649],[47.793965687770893,-14.568320170990646],[47.773572705517523,-14.369960182570416],[47.951305814426341,-14.05546583125907],[47.898908137615926,-13.726248620716254],[47.981958166787194,-13.614832971142029],[48.173297172905897,-13.516015146389863],[48.211288225329284,-13.405394177034344],[48.191472342542411,-13.260112538971574],[48.269838742607753,-13.204798059839622],[48.308738057913125,-13.198499475098222],[48.403840178468215,-13.383444768537736],[48.508336626026342,-13.431479407338172],[48.639135304914717,-13.404828102240144],[48.772526583494347,-13.287795287958909],[48.917429859106285,-12.849360840322143],[48.786586910749975,-12.470907052094738],[48.959460838748655,-12.399874878991623],[49.207143730964468,-12.079813197531726],[49.26337516429367,-12.080436060608756],[49.937259393532571,-13.072353501760237],[50.173037270191273,-14.036592442446837],[50.236572618304507,-14.731716270516733],[50.441108372127978,-15.14939120087055],[50.482476771137485,-15.385628189151362],[50.402959230200665,-15.632409257860619],[50.208897551169848,-15.960220512311077],[49.921658707856409,-15.788789587173198],[49.79469268678762,-15.826123923141459],[49.724919980763588,-15.916291975110594],[49.720420953795767,-16.06196665430755],[49.830806961688936,-16.255918809917645],[49.853050594701585,-16.570229084076104],[50.022705936090418,-16.695523319326298],[49.82399429961994,-17.086124315180374],[49.721575189841403,-17.02594838177944],[49.604401436656019,-17.033960904887543],[49.454223677046684,-17.231757203914253],[49.478155628889574,-17.893850980542354],[49.362118667335885,-18.33875705096386],[48.919858871987906,-19.525282434071986],[48.349383667839952,-21.352971158408966],[47.914273445810004,-22.449625533038926],[47.557709349438156,-23.874494738583483],[47.366732435203573,-24.233277960885633],[47.179645911837241,-24.781835320296423],[47.038020025642474,-24.9748831592598],[46.728456820251921,-25.149724968098131],[46.158992083840289,-25.231215705718455],[45.513010041462501,-25.561398167234572],[45.21107061227562,-25.570379164030115],[44.80209763980573,-25.332174235127489],[44.350113224511219,-25.227961739738916],[44.035533786784228,-24.995552998322623],[43.895348919454278,-24.61555799912642],[43.671659053786271,-24.305696981579029],[43.646433015775649,-23.741911200345388],[43.709392993952768,-23.475359123975991],[43.572781108467758,-23.091307749708609],[43.398100500365977,-22.886171419967514],[43.330516003028407,-22.695174948554165],[43.257536350528703,-22.282030147221356],[43.290721466110476,-21.932587734059432]]],"type":"Polygon"},"properties":{"alpha2":"MG","radius":873000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[171.03607558414862,7.1561293394872507],[171.04698570866472,7.1681340832711822],[171.05042831819364,7.1714679031223483],[171.05457057472171,7.1690580014595717],[171.08618649826906,7.1484896875608337],[171.10236301597357,7.1390140100279424],[171.11935571403419,7.1310946412165022],[171.17653884602441,7.1076406222360946],[171.19661876038364,7.1006271350117878],[171.21732993675442,7.0957857367574944],[171.23843813589306,7.0931711827148041],[171.2597046283081,7.0928130429646554],[171.28088889424288,7.0947153679956712],[171.38886588468748,7.1102354799126317],[171.3936574605265,7.110684437499474],[171.40134496224468,7.1030460076222672],[171.3909994888356,7.1016637968509357],[171.37237514204722,7.0973332489752003],[171.36220907090055,7.0944508260506334],[171.31090804153996,7.0825904783536275],[171.29857135001211,7.0793203837299998],[171.26792860437271,7.0701420141634905],[171.26324698255564,7.0689838312109519],[171.24255304465342,7.0687499999999996],[171.22822458274206,7.0697832012075184],[171.20677021308606,7.0728934500629768],[171.2023900442988,7.073729882601949],[171.19810209402559,7.0749542111738126],[171.10091083084498,7.1074697526035377],[171.09566252053128,7.1095648098202995],[171.09102304097672,7.1127911475824437],[171.0395810617683,7.1530957701812143],[171.03607558414862,7.1561293394872507]]],[[[171.56781640314185,7.0467966149021484],[171.57731106310518,7.0479975942190469],[171.59159174829773,7.0417529509221239],[171.61045240806689,7.0353765632209369],[171.62985357971576,7.0309069608120263],[171.64960300597244,7.0283884355341231],[171.66950497851147,7.0278459448902089],[171.6882945699208,7.0280234084494957],[171.6922767630879,7.025132625337978],[171.75294926192785,6.976274386257785],[171.75611619051872,6.9734525253906092],[171.73585452700055,6.9759031981860149],[171.73057064286519,6.9768922396715087],[171.72147591049142,6.9819900569344204],[171.70254235843277,6.9913256113221935],[171.68273027938378,6.9986135117771786],[171.66226039573951,7.0037725652635769],[171.64136075836396,7.0067452958022693],[171.62026420592116,7.0074985847991531],[171.61419718487386,7.0073947803784842],[171.60987738986759,7.00899644338672],[171.59295152163912,7.0164669417283854],[171.57998092150333,7.0336651402875265],[171.56781640314185,7.0467966149021484]]],[[[169.59100162992542,5.802082837332236],[169.60322781462455,5.8173526977001959],[169.61323075974826,5.8325915216838755],[169.62180469087099,5.8486778007022187],[169.62887838420383,5.8654779057669586],[169.6343930784582,5.8828522781249459],[169.64971641343178,5.9400489713222555],[169.65130818091143,5.9449469742391878],[169.65547846250357,5.9479690443463262],[169.67913913470409,5.9632899152563805],[169.69283249696804,5.9729957400384173],[169.70566383176879,5.9838155391133849],[169.71754277083667,5.995673111015452],[169.73422522060463,6.013492723233953],[169.72743349882663,5.9806851016390281],[169.72613607833807,5.9758310595872022],[169.70558840238738,5.9588710151938828],[169.69192134167528,5.9450568143441878],[169.67965942805148,5.9299814646664286],[169.66891842032422,5.9137872852606526],[169.65979971916315,5.896627157558358],[169.65238940982474,5.8786630820426806],[169.63701283834826,5.8355566258071034],[169.63475501625004,5.8302281318889442],[169.6318941405909,5.8251976251027866],[169.61810013413111,5.8039232867817612],[169.61530952906597,5.8000498437359562],[169.61053846334934,5.8002170848303916],[169.59100162992542,5.802082837332236]]],[[[168.67536719871606,7.3220499361223661],[168.67792209748052,7.3315466337123141],[168.67945822393034,7.3359691999638947],[168.68410044790937,7.3353621305702248],[168.82544429274719,7.3098543775794935],[168.82982859753969,7.3088402833301869],[168.81889645604585,7.2971583021726847],[168.81533543611869,7.2938090897013019],[168.81045185085179,7.2940297297790933],[168.7244648004999,7.3022356005741091],[168.71930751075789,7.3030025719016542],[168.71442333603378,7.3048275978650192],[168.67951949913083,7.3200015004124301],[168.67536719871606,7.3220499361223661]]],[[[166.84514666831316,11.153452901919119],[166.848338230959,11.15666945835477],[166.85897909122809,11.16609620231238],[166.8832398153082,11.168263357353355],[166.88805869889705,11.168415146089055],[166.8946209595114,11.166567897073955],[166.89902341733151,11.164915502896944],[166.89637194979446,11.161031978559887],[166.8902020197568,11.153270411570425],[166.86932890883713,11.14752817727536],[166.86446473876302,11.146502432198639],[166.85971030838431,11.147954222557901],[166.84514666831316,11.153452901919119]]]],"type":"MultiPolygon"},"properties":{"alpha2":"MH","radius":22000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[20.44891352183193,41.521237580757706],[20.553311653039856,41.862231795336527],[20.696401577054523,41.928878326346734],[20.778274559054363,42.0708723551001],[21.059812973985821,42.171047286511353],[21.262823327591708,42.160063840844529],[21.393909758240547,42.220532490381871],[22.239759939602848,42.357929956834205],[22.583166051530519,42.10561319160233],[22.833084208048795,41.996543628596463],[23.005260845984875,41.721445943794066],[22.929430893756845,41.356195850018338],[22.785451790518792,41.284204047645844],[22.724618844783524,41.178686392462154],[22.493542626293475,41.118675557900978],[22.215911292533924,41.155747112799872],[21.990288542028779,41.127312199916787],[21.779371964390457,40.950624931772943],[21.575756707753197,40.869194143246652],[21.440529427590594,40.901075789043219],[20.964355141300302,40.850138209714466],[20.858363973687116,40.908225258919593],[20.740895647944491,40.905548540210035],[20.489180187898945,41.272665836552711],[20.44891352183193,41.521237580757706]]],"type":"Polygon"},"properties":{"alpha2":"MK","radius":118000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-12.2801382417619,14.808635956726773],[-12.094949526117899,14.775507477813695],[-11.956125036588151,14.87145845299621],[-11.871500277218786,15.006761368312723],[-11.75989125770298,15.425354794512151],[-11.502634184322032,15.636532697448885],[-11.063258424900605,15.270917827248743],[-10.954960293801832,15.235873541961908],[-10.829219344198728,15.276463473095189],[-10.691992251338405,15.423165546483753],[-10.417947036638685,15.438079287431583],[-9.9414539331878373,15.374783794535103],[-9.5808009735800201,15.437707409033404],[-9.3507809122103893,15.677106821218294],[-9.2927842807912313,15.554576575755677],[-9.1514588075250778,15.49609375],[-5.6774175634289206,15.496289060224415],[-5.5006890998990796,15.60265653808087],[-5.3766073326529753,16.238859748583991],[-5.6291827573785866,16.573199029147695],[-6.5938294739439778,24.99439568878898],[-4.8226689512785992,24.995390801300076],[1.1415582384466354,21.104828101386488],[1.2121097246210342,20.775531578899997],[1.6104625100232646,20.555338033307098],[1.7374555213822236,20.342474300623813],[1.9289146257212961,20.273339506047687],[2.2192684099082993,20.247596513757415],[2.4494664663149885,20.045503739554579],[2.8691622309046489,19.954889120166925],[3.1349058245874946,19.846379137233118],[3.2034663016385805,19.789584144434315],[3.2026701155980675,19.570343108195456],[3.2556396202338576,19.410893773679614],[3.161779035531088,19.16400797263579],[3.255286882567797,19.017757285619822],[3.400801389163032,18.989066575887723],[4.2274138001285655,19.142504331114296],[4.2346521052185553,17.001130707833561],[4.1818208493350717,16.581834782053992],[3.8766957626180418,15.755384372092045],[3.5667323357852987,15.520747590400671],[3.5040911044501724,15.356623032758193],[3.1456602319035567,15.410115560078212],[2.9960925703433707,15.340789494556351],[1.3445677481255165,15.27349933371916],[0.9531766774025604,14.984300001251739],[0.34880722796936897,14.978630026763406],[0.21740499186461601,14.911755681471224],[-0.19843697268722763,15.046999294941241],[-0.40538367338510717,15.012769178971142],[-0.51738180579949922,15.071094320845695],[-0.66635217589498053,15.069076656083801],[-0.77681917393367705,15.034182910649807],[-1.0496533648991573,14.819705191862367],[-1.6951213995498939,14.508630583366894],[-1.9227598633111094,14.454457072709097],[-2.1084767609357464,14.170675511897025],[-2.4415873892046731,14.265428787469826],[-2.5687920057095823,14.236792725040241],[-2.8623414510340837,13.964460567711392],[-2.9173116403282489,13.679586214477725],[-3.224947292577991,13.564628330932699],[-3.3019652488778117,13.280951615111318],[-3.5276365667395742,13.182936338682289],[-3.8459995754128866,13.367697877601756],[-3.9766836245723689,13.395649750181212],[-4.2411201926025832,13.212612659967414],[-4.2906405847749198,13.072457943121801],[-4.2273401080195416,12.793856267562171],[-4.4056169434396679,12.653450255568194],[-4.4289498265458613,12.337696955974806],[-4.6938287245747308,12.079525458245984],[-5.1300777193653566,11.955753231724771],[-5.2767977211404693,11.826919554631326],[-5.2492373873207381,11.382863889170956],[-5.3000484382337838,11.206044692170865],[-5.459498622711231,11.006370060156751],[-5.5236949864445393,10.426320814892383],[-5.8060390134824278,10.399502559399565],[-6.0346511503788598,10.195052003216349],[-6.2381605565284302,10.261757706523449],[-6.2282578497899772,10.398987554618989],[-6.2988400069896882,10.515123390500039],[-6.4841801501321381,10.560358819410833],[-6.6198092312378307,10.495539667508424],[-6.6933904114780365,10.349718404105726],[-6.8956164930028114,10.311229200945061],[-7.0171328078483004,10.143501144247047],[-7.3629979714813922,10.259597979681066],[-7.4502467316631256,10.375939919250726],[-7.5729653247503741,10.421920069492495],[-7.6919322446819836,10.391618000077756],[-7.8181484932576017,10.233706417444155],[-7.990368443253959,10.162730515922476],[-8.0355097194324969,10.319223675767949],[-8.2664525125456318,10.486080879885682],[-8.3623360219518776,10.920524783548885],[-8.4718225569980472,10.994325471091102],[-8.6664271237518307,11.009574845665799],[-8.5310379976675179,11.236740425588247],[-8.5276770213294135,11.373525032705402],[-8.8217971658445489,11.673367996187906],[-8.8330615217877533,11.94912972889734],[-8.9890840666361633,12.311234502019961],[-9.0941094892723697,12.392520399067049],[-9.2616121428406633,12.373185948590018],[-9.3582510972210127,12.25567015338884],[-9.6089409992857515,12.170647403415243],[-9.7540193376232676,12.030181385231206],[-10.283451096854943,12.201451610369679],[-10.469793536493803,12.128582145419816],[-10.677416025450604,11.899617839301738],[-10.734710837400193,11.9166646008805],[-10.855574207188679,12.099723500688834],[-10.962164395918331,12.149692669022562],[-11.07839963309122,12.131044458471692],[-11.255772889239054,12.006058591100263],[-11.309354076437996,12.018810342036575],[-11.50192097644336,12.198602479833601],[-11.418346676642981,12.412455105885615],[-11.450420516453827,12.557712964951788],[-11.417821991621899,12.955143872589735],[-11.603347536672198,13.275431665787657],[-11.705258753477599,13.330770877879116],[-11.831627378385658,13.316105774729603],[-12.053963138339142,13.633126092825686],[-11.99155223817977,13.849116519268978],[-12.017739547331079,14.166993311967737],[-12.228180400548386,14.458640203909869],[-12.203332177984215,14.649146189785007],[-12.2801382417619,14.808635956726773]]],"type":"Polygon"},"properties":{"alpha2":"ML","radius":1208000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[92.179804811794625,21.29308488853539],[92.279830318655371,21.427329059697446],[92.442549251037249,21.44099825539783],[92.550815900165631,21.521699713016364],[92.575148963167251,21.977968369727336],[92.689111536600493,22.130717056169694],[92.932849015070076,22.091070435706879],[93.1352749384329,22.293180716551944],[93.078960398747796,22.718178391338409],[93.164330152253285,23.031815022663611],[93.338562061475727,23.117199900434674],[93.390906491470602,23.335875027202214],[93.412730738546614,23.652662209222683],[93.326460102413051,24.063999486652342],[93.486175498110356,23.989968558520104],[93.683349831441362,24.006279508101599],[93.995078814850373,23.911156298632246],[94.178750638008282,24.008519661547698],[94.295105712398254,24.32555379473358],[94.672725764885257,24.95866010726666],[94.663722082244121,25.089344399952161],[94.553242929371649,25.215772966744986],[94.582518038624215,25.325355199035641],[94.664775697056555,25.45562504681088],[94.792839529585621,25.528305797880247],[94.951458878908696,25.70897491501389],[95.088422726087998,26.020680751348856],[95.059448783237769,26.469457002928635],[95.12891455669822,26.597070939257119],[95.474541329508853,26.763613164792321],[95.895106830176204,27.045197271748428],[96.061532842326457,27.216916403951867],[96.809293935378207,27.385497498371429],[96.899896690115938,27.64371235612839],[97.27588582349604,27.944208736189655],[97.322658387292492,28.217868259058513],[97.538023265082145,28.509987736958834],[97.658747482348147,28.499741227502859],[97.764170183329213,28.39450424619876],[97.887444700344844,28.356228721588788],[98.098708623303722,28.142176573610254],[98.239516302283633,27.711402329784129],[98.650937805463698,27.572289391425283],[98.732094389691937,26.58734235081862],[98.625870887019303,26.039653846822844],[98.655983373770795,25.863666757268447],[98.48110537440904,25.784497142218996],[98.333638276347685,25.58695029035766],[98.149230392856708,25.506710552116164],[98.01055320692241,25.2927509568263],[97.780363132568482,25.165848662657112],[97.714250402542277,25.012037248494341],[97.737605578406644,24.869902118945486],[97.564682672299583,24.722086346116537],[97.546838155679509,24.511740613477048],[97.666345193465219,24.379865007567098],[97.73746986847847,24.131374858883191],[97.8576101696611,24.042390036683951],[98.21681826988474,24.110878834852596],[98.83481754466078,24.120940291105903],[98.715993134060099,23.876742350601326],[98.831970224034194,23.62435823361513],[98.914252269114698,23.226445110865885],[99.005195801272151,23.147481235967057],[99.345087758300338,23.094425602223726],[99.498224525261648,23.000170357890784],[99.373897126517178,22.792328915613435],[99.337472474084279,22.498128156536321],[99.249915173770475,22.353013655345837],[99.314482079259619,22.153881820127793],[99.917537506003441,22.027814337355331],[99.960734847393127,21.763964201917325],[100.22827391486851,21.503620016049116],[100.60413762741257,21.473205908483731],[100.83526020588779,21.655019755517468],[101.07979338929358,21.755647020153472],[101.13062956346209,21.735465984583701],[101.14701702268405,21.581688636604259],[101.08022434482902,21.468791087515424],[100.71023403717028,21.242856374454782],[100.59818472692126,21.007469926081846],[100.62267123325758,20.859683164921201],[100.36245509519399,20.808019340074701],[100.25308558124519,20.727411273685199],[100.12228565122767,20.3169893823524],[99.914852203264275,20.399734393934533],[99.621735093781822,20.310982385833849],[99.403970127650425,20.095713049182844],[99.175660277876119,20.098142808832744],[99.068880285943067,20.052281141099268],[98.916612601772457,19.773123531400703],[98.449370623904144,19.694080114611555],[98.070120240265837,19.706245651912212],[97.917493560870199,19.591826072045901],[97.813997189087743,19.427353518122452],[97.803677840202369,19.130528764495825],[97.709110830473463,18.93455299386056],[97.752808225014149,18.615989244710335],[97.606659077223384,18.39430611475213],[97.720594824473395,18.033016991609188],[97.729772487263347,17.790182820099943],[98.438621339627659,16.975516167898139],[98.48801983745858,16.71391628616027],[98.615359024365347,16.481475164849726],[98.714231657284202,16.415063265360153],[98.835418268475749,16.417321588203908],[98.888011664637318,16.351846152992739],[98.861843794748367,16.232703823620465],[98.587183153251743,15.995090593853581],[98.556689211589187,15.367823031217684],[98.243078161068254,15.225170521452219],[98.190871909145372,15.119383882626471],[98.205442095686436,14.963820422934591],[98.257629917906954,14.800177305926526],[98.565218566804276,14.368917332987975],[98.933440818611018,14.049708149972162],[99.134596039214401,13.721354273963748],[99.176087643982427,13.238004297352578],[99.129303347516228,13.050260668893442],[99.223606604312749,12.741920442196053],[99.40484275878849,12.547843364033591],[99.455705850798424,12.218248085949375],[99.61454258491527,11.781178883131728],[99.189952203902905,11.105408301664811],[98.794519395216724,10.693938284346185],[98.70231916201034,10.190552678926101],[98.562544720526333,10.035286364619438],[98.403651809494121,10.076822490466666],[98.48525766486668,10.164029782664693],[98.517991376902089,10.320169943368686],[98.463214268510725,10.633388049733528],[98.080901927900214,10.886788781868596],[98.167289676435217,10.980038277184795],[98.31436461616633,10.918841149701899],[98.442421959294279,10.959706737187002],[98.526712916089906,11.086713704866618],[98.698384580434933,11.172034101842179],[98.723752769860397,11.38950116604515],[98.635043834833937,11.509260197075031],[98.448555137120437,11.565815113586542],[98.331908489964718,11.559740928722292],[98.187538850879136,11.472540280943502],[98.14352421938257,11.586878544964135],[98.021302733127371,11.69599666570392],[98.010610895543479,11.859256841591185],[98.151884773757175,11.752997095515443],[98.341533418512753,11.795478689816861],[98.449197410538602,11.906355924913388],[98.451249059508939,12.056512687293113],[98.29703999063176,12.190920960078055],[98.096318985569411,12.133037825060047],[97.938943651693947,12.346049729380104],[97.990311651514432,12.393559038628558],[98.182742576356574,12.409204835418752],[98.281792477519957,12.502042160342903],[98.357516736759578,12.806842825774407],[98.259488328430763,13.014085400038759],[98.254849272979897,13.188477872568868],[98.401876513539875,13.288527838091895],[98.431263032829762,13.442022135609218],[98.321975093033899,13.610472503035689],[98.149605963865596,13.647900468405531],[98.110859393808823,13.712957535063957],[98.08599354161187,14.168804936035208],[97.812554360122434,14.858996641726586],[97.715141053502961,15.835714688025856],[97.584550781108589,16.019652883535169],[97.581989076996393,16.172566473550063],[97.200596089599699,16.91130824402973],[97.075739269982066,17.000874876682008],[96.924199012377812,16.975439519107582],[96.622323005033635,16.564117261808139],[96.387883425693929,16.490302352428895],[96.012156256426678,16.253900571352705],[95.787612107883646,16.169096906356458],[95.559605467836931,15.842263444423731],[95.389497209999675,15.722989009616532],[95.107109738649996,15.835226551488937],[94.784350721135667,15.794129983881762],[94.598051805168339,15.915035506339493],[94.412054447484692,15.848913317549471],[94.34528420595683,15.967506530096056],[94.224040585943058,16.01666119010893],[94.214593436843018,16.126601542575862],[94.271489515410579,16.517183217108911],[94.347446293430622,16.624419523584603],[94.567906833804045,17.345290457178791],[94.585566855846039,17.579574740817819],[94.424357198598742,18.211012005351186],[94.236120280779573,18.631960830704486],[94.064301219981999,18.870211175732763],[93.825948180188092,18.875154440326586],[93.674050728066931,18.675948427805285],[93.490479030774864,18.863627260712008],[93.638315704675904,19.023846621324925],[93.626858671881365,19.138421449750794],[93.493362061682234,19.369422158009858],[93.618116707924017,19.496298088999854],[93.609402262789018,19.666009664371018],[93.353487124916569,19.868608072288765],[93.023267157293972,19.829147093919392],[92.975352991637067,19.868141194809304],[92.880356437410441,20.123894128615202],[92.32432283621003,20.791930642253671],[92.179804811794625,21.29308488853539]]],[[[98.339044065088885,10.053789028540365],[98.134269976989174,9.8756671479867464],[98.118336841979698,9.8780647046354293],[98.140475993832553,9.974514007188775],[98.216729038428753,10.041730283619259],[98.339044065088885,10.053789028540365]]]],"type":"MultiPolygon"},"properties":{"alpha2":"MM","radius":1058000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[87.743432812124283,48.881732779163798],[87.80982902738748,49.009878765328672],[87.814548947191426,49.162071174327863],[88.055426430169263,49.236462508513576],[88.192691278046752,49.451509335567387],[88.735423769618976,49.467072168797237],[88.900159875998,49.539459430921433],[89.084492887511956,49.501617443805785],[89.243989396289606,49.626816628981949],[89.375661682171199,49.614783546900355],[89.543149506725427,49.690355116444742],[89.620092845984672,49.77897121337358],[89.644086144043996,49.902851940939343],[89.905862470524141,49.969535506147672],[90.05377928276333,50.093548618312994],[90.663784188468384,50.232443194736796],[91.021652336861251,50.415262086313717],[91.390395680267446,50.484724463910283],[91.711642313164788,50.667045004630367],[92.166035894755339,50.705166160849281],[92.354728353249598,50.863921694190701],[92.614858344784921,50.719531581982856],[92.941181067505866,50.778016753001495],[93.019737117865304,50.657097283548083],[93.120850290459913,50.60789662340725],[94.25094655308321,50.55616019237668],[94.390049825447321,50.217197351596276],[94.656677094885296,50.044767648222241],[94.930226367753789,50.043541044177786],[95.1099461663201,49.949930041057314],[95.470010630172595,49.920691187759672],[95.789424697133157,50.012242427126615],[96.065473589644213,49.998416852212785],[96.34936859870912,49.900278888427209],[96.985644693497136,49.88258202358567],[97.185307167896823,49.745162626538672],[97.358488182932632,49.745857825382302],[97.589516120140829,49.911262847929059],[98.030615105477963,50.031321075420649],[98.22931499552385,50.27125958391094],[98.243415560087485,50.474506624221149],[98.029998788611408,50.644798107763847],[97.825545277099735,50.985315489151347],[97.836012871755955,51.05155509085828],[97.947047813211128,51.348304686182416],[98.037774985379201,51.449752635562611],[98.197792308522509,51.530046135461738],[98.352878598614836,51.717363204997177],[98.673710481302891,51.829993800188781],[98.893237298932533,52.11703940361955],[99.394748317810524,51.92754373585413],[99.71915463841971,51.871422938471866],[99.93315507802663,51.754887434438139],[100.4731068098171,51.725295070485991],[101.35141498350512,51.462288995973779],[101.57788367185456,51.465899742203469],[102.11139828607863,51.353222378609203],[102.15545293849922,51.313664774427949],[102.14942373575992,51.145942839501764],[102.23837363431475,50.831959145155977],[102.36087800722223,50.594021074816041],[102.66116877454184,50.399139143320909],[102.85983308951263,50.333873729811764],[103.16167476462714,50.290522586996978],[103.3279049329509,50.201957778496322],[103.63294763415492,50.14182609753103],[104.14350929716571,50.163992867888552],[104.46188859502783,50.304938860173799],[105.38358125917797,50.473452956177923],[105.8751648532655,50.405197597209806],[106.20836750196082,50.307506984310805],[106.71106301406378,50.312346301312026],[107.21398343415481,49.999998080457281],[107.91652664175592,49.9476067411562],[107.95324856452118,49.721337273966284],[108.054115809179,49.59266500080259],[108.6035013035671,49.325973033194209],[109.23670187748218,49.334722299298321],[110.14663492763182,49.179541351452379],[110.42780544069004,49.219778030895462],[110.72211500211721,49.145392697987887],[111.3366518818053,49.355592242293397],[112.07943213559187,49.425649508541078],[112.49046827253848,49.531664896689009],[112.81400090858988,49.527520564883652],[113.02612041393243,49.614984184431236],[113.16433113043442,49.796971011367859],[113.56803932256649,50.003889962913163],[114.29707248798573,50.274117338248466],[114.74741196008502,50.232114788570804],[115.42930498866298,49.897185261982862],[115.71733146085862,49.8838217372248],[116.13460073987208,50.010554276563653],[116.34858965443991,49.978675749977434],[116.55106497770439,49.920050570158644],[116.68300787113409,49.823753091271378],[116.025249622679,48.782377663163238],[115.82497078032765,48.563782134808513],[115.7853380746615,48.248397876052799],[115.62848903339091,48.172209830870706],[115.56482105654219,48.075026970962746],[115.61711720593297,47.875533422381721],[115.88445517261489,47.71294102656887],[116.23120514099504,47.8579631068257],[116.76054081996151,47.869601649026038],[117.06418705401083,47.807812289274224],[117.32619816304896,47.693519851789574],[117.5431962299597,47.796999672183041],[117.76845128381547,47.987695223495805],[118.1470431849022,48.028652515211228],[118.4983717132783,47.983768254788551],[118.75452082181796,47.767824301791165],[119.08172199918931,47.653952505489684],[119.16544163621887,47.528602533676377],[119.71091740218866,47.149767381184589],[119.89757178857838,46.857752677137434],[119.89568352322334,46.732887728682087],[119.86702148669373,46.672348284651434],[119.62465942424029,46.604060542746801],[119.16793918457675,46.637818200352541],[118.88964401398927,46.748934656136129],[118.72287295640329,46.692162109154147],[118.28940033961044,46.711162908506154],[117.93122101572196,46.624324853639656],[117.7411812742195,46.518447829988503],[117.48511084124432,46.54660952183702],[117.33328479247164,46.362209144340795],[116.79367450123596,46.375497444070731],[116.59425117715443,46.293021814629043],[116.35193518087208,46.086150875759778],[116.25006067572056,45.936222317958766],[116.19750272978042,45.739526689125128],[115.68096729544135,45.458474666554395],[115.15965100521734,45.39008952558126],[114.60842154936331,45.402203907137633],[114.41897124285831,45.202793387369439],[114.0755888764438,44.968466339630972],[113.5870052552724,44.745929759724682],[112.71121893136619,44.882491504265076],[112.40117034748182,45.055346090382464],[111.89975731535382,45.059387334767429],[111.62724582821269,44.833202102828849],[111.44767303770819,44.383330392553596],[111.53386292688434,44.178708779099303],[111.93150495118401,43.814835269880874],[111.93297680817372,43.711595376966791],[111.75782587913803,43.648861890355164],[111.54729160583045,43.496485039581344],[111.00929732981589,43.337890991312115],[110.40025561962416,42.773876934721876],[109.34425786627297,42.439129890114963],[108.17952187563563,42.446093687458486],[106.77018406293686,42.288023322324115],[105.86191712507032,41.99175929386287],[105.20808389246176,41.737490700431451],[104.98204165974416,41.595743992349902],[104.49846269481748,41.658920207681433],[104.44653350603069,41.777717524420993],[104.34594603412769,41.837054445413209],[103.71116573054934,41.751613448593439],[103.07256255855478,42.004930505329916],[102.15667202639365,42.158239247856741],[101.97305952414477,42.21607287682253],[101.68401686641231,42.484774184857301],[101.49523770388589,42.537916961048381],[99.993003165561447,42.675923839753494],[99.462886738900977,42.56867208657782],[97.2056536539327,42.788954899758323],[96.385519100101789,42.720597282738815],[96.287783563264639,42.93167313006078],[95.909085371415173,43.210976628149162],[95.555877030661463,43.909016179350232],[95.356553832836028,44.006156763823959],[95.293285158057429,44.172724409499402],[95.206816223255672,44.246504048230435],[94.712120819636354,44.351084937731301],[94.206277039816214,44.63431889019396],[93.953753340101144,44.677233123721685],[93.664514544805485,44.894046461218316],[93.503558139681857,44.946687751072176],[92.811717944609157,45.032845497349335],[92.429710541878592,45.00901333702263],[91.590248508416593,45.076223922096332],[91.058908782235605,45.214161335939878],[90.877410659444919,45.196313859554635],[90.662070881868118,45.525301886596168],[90.709947223714138,45.730676976318549],[90.963286235291463,46.033045983557514],[90.911769807396382,46.270684421173478],[91.013936653310552,46.503981448768144],[90.987491200508217,46.729924738801621],[90.879826937908959,46.928416277318128],[90.715672911132188,47.00408236999175],[90.49877727483593,47.281913900146598],[90.428316880058546,47.494735303199043],[90.319084724290136,47.63694839925018],[89.995365777795058,47.832443058220164],[89.773827270013768,47.829086911352519],[89.500992356018131,48.020457078654175],[89.109220007533324,47.989094103958237],[88.580251540372686,48.218162612710486],[88.475555353493135,48.380711556102867],[87.979864402726108,48.555351802951158],[87.932318280021477,48.716877027713878],[87.743432812124283,48.881732779163798]]],"type":"Polygon"},"properties":{"alpha2":"MN","radius":1263000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[113.47916306382304,22.195870370253942],[113.47938545942343,22.200446754968386],[113.4805666502629,22.212500270580257],[113.48129664135732,22.217406205416435],[113.48344463879712,22.221876909009207],[113.4918951763883,22.237422114354214],[113.49427798668611,22.241353158597466],[113.49880075779109,22.242175007299618],[113.52224920001562,22.245306104926037],[113.52696168409543,22.245704374518471],[113.53029885006053,22.242353337635571],[113.54478594284681,22.22632363774704],[113.54774986157136,22.222694280105454],[113.54357754338145,22.220664730675839],[113.50531978675077,22.204417091945974],[113.49076783399317,22.199414735677706],[113.48058037445645,22.196228368433019],[113.47916306382304,22.195870370253942]]],"type":"Polygon"},"properties":{"alpha2":"MO","radius":5000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[145.1531780992799,14.16308389795574],[145.22125795487383,14.200757497314894],[145.2620492334309,14.228831054814259],[145.26375260864054,14.206326753936104],[145.26467398788085,14.1686722586158],[145.26390447369153,14.15852507697312],[145.2505336972788,14.144585714798417],[145.23018102341339,14.12535705447984],[145.21504827561145,14.112456239237186],[145.19622250830338,14.116530082592332],[145.17993772357121,14.121728454545806],[145.16909242296595,14.128801808872741],[145.15820950796663,14.137420570210285],[145.15511358841056,14.149962734148399],[145.1531780992799,14.16308389795574]]],[[[145.59924780972852,14.904620923876459],[145.59913970740357,14.924355865631712],[145.59114883323699,15.001793501673049],[145.587691782799,15.030525993613184],[145.60378719872023,15.050755835460805],[145.62829127221806,15.07893631736091],[145.67700733192927,15.148746920387218],[145.70743825638505,15.204080348064343],[145.71390242635113,15.214802962222617],[145.76010351933743,15.272156380311584],[145.78093334875001,15.30368019574871],[145.80748712784435,15.278846121883927],[145.82071947644837,15.265174322282732],[145.80506084533502,15.229665189386123],[145.80576595103051,15.229354255576117],[145.78999385450786,15.196487481517291],[145.78140405477927,15.175032624458925],[145.76546245939409,15.14359609217435],[145.74789790407965,15.107119511054396],[145.74077973145992,15.097778834078989],[145.6943893710405,15.029939414033013],[145.67057491325613,14.986036128385598],[145.66156578893177,14.970999193168398],[145.65044658009865,14.95587440336632],[145.63362392197584,14.93511452738503],[145.62031539522326,14.92027766808928],[145.59924780972852,14.904620923876459]]],[[[145.68673396277146,16.302739099527432],[145.66465504503105,16.325756165083103],[145.63685282598115,16.352077176941339],[145.63381662957596,16.364806904910665],[145.63210615597418,16.377129078363989],[145.65028602362227,16.378473868222098],[145.67502782025343,16.379111156923198],[145.69514853702722,16.378598895796792],[145.70739520089467,16.369482180920496],[145.71834497107028,16.359440695912102],[145.71535887525994,16.349576215226797],[145.71119296104953,16.339609326914992],[145.69332879376128,16.313477772849556],[145.68673396277146,16.302739099527432]]],[[[145.64661396123793,18.805695975239686],[145.66554863711292,18.804462899956025],[145.68981370558532,18.800738511119597],[145.69737150441802,18.796149940690462],[145.70566513652511,18.7899199257531],[145.70732251945341,18.775387455282537],[145.70752991313279,18.762566976963218],[145.70193428483248,18.729989206359601],[145.69524126244463,18.674968874635674],[145.67987181600211,18.707123617757023],[145.66525548988051,18.731189573419307],[145.65356624086488,18.752996052104994],[145.64986792385909,18.773208196199828],[145.64810568037461,18.786779918590582],[145.64661396123793,18.805695975239686]]],[[[145.77196880222041,18.244612631603903],[145.79478856481953,18.19941573464261],[145.8343028016395,18.136894102997175],[145.8217712767169,18.123112701761428],[145.79020378435564,18.091598442945418],[145.77707937870321,18.079618946388376],[145.76124813791566,18.071548619468178],[145.74693248537298,18.065040192757749],[145.73061747287755,18.058566386673615],[145.739644850389,18.087387904612648],[145.75969033097482,18.164737293903489],[145.77196880222041,18.244612631603903]]]],"type":"MultiPolygon"},"properties":{"alpha2":"MP","radius":25000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-61.219453389102213,14.804440516710276],[-61.213099940479779,14.848444144408568],[-61.209244177637011,14.851516507548055],[-61.180735460379054,14.871713406027878],[-61.127446113050127,14.875023653233654],[-61.027240323151126,14.825968110478566],[-60.996571837665634,14.797543342525987],[-60.968390313312739,14.775502107867844],[-60.952934915301093,14.766459052818373],[-60.927305505406366,14.754903769827923],[-60.918896652208915,14.73528800155632],[-60.912940009893369,14.699804115795422],[-60.906889303071559,14.680622237525393],[-60.898942554889068,14.662145097586166],[-60.870208680442076,14.613604521869647],[-60.826556512455937,14.494457695122859],[-60.836829097205637,14.437566863126657],[-60.862066883697771,14.42655977752846],[-60.88162728507757,14.440257532745335],[-60.898952631038853,14.449800753119874],[-60.917135907779461,14.457585938716226],[-60.935999264366025,14.463536942504103],[-60.955358197992851,14.467595557616104],[-60.975023358606933,14.469722086670059],[-60.99480240094401,14.469895730049791],[-61.063591061975359,14.467328389556176],[-61.088660199630006,14.509635155268031],[-61.091389054697004,14.543503339931403],[-61.094814644387398,14.563179091694453],[-61.100185497635806,14.582415094594021],[-61.107448057653521,14.601019532079665],[-61.116529904030891,14.618806885411344],[-61.12734047489532,14.635599783605318],[-61.143360333937295,14.65673734521541],[-61.219453389102213,14.804440516710276]]],"type":"Polygon"},"properties":{"alpha2":"MQ","radius":32000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-17.063754467022665,20.898825489004707],[-16.964370417823371,21.329023650407702],[-13.229874824125993,21.333722576998035],[-13.105066750932689,21.377614345672853],[-13.030544772732652,21.547235115661362],[-13.151177636410118,22.824619713028273],[-13.031284363391116,23.000018413665561],[-12.624224262037535,23.268822237043381],[-12.123194202739999,23.420331858309702],[-12.020198451188611,23.558080686457266],[-12.016078688954265,25.995180251454297],[-8.8055922187738265,26.010731904966278],[-8.6830919609066903,26.175904383077153],[-8.6831180936065433,27.285533427670217],[-4.8233513677517044,24.995816082829897],[-6.3892322341595742,24.993775494740305],[-6.5119886971885812,24.934907094438532],[-6.5686247113965264,24.811104834103276],[-5.6362097901468609,16.634564302915237],[-5.3601482724717933,16.282793025703423],[-5.5126924217220594,15.496522417535498],[-9.3189822952911268,15.504594922937546],[-9.9414181334567591,15.374029634679472],[-10.580802355899801,15.43516791318007],[-10.721077651711745,15.39365516418807],[-10.89572478772295,15.150704510456602],[-10.943412482463847,15.151064803678842],[-11.381644920019832,15.550600366266691],[-11.534148241568706,15.589953674272403],[-11.763899978920049,15.414394189802714],[-11.873144892689647,14.995287544447786],[-12.104740819069272,14.745631633643592],[-12.305958521253114,14.819312197463592],[-12.858296843571004,15.242693703478404],[-12.922077206301314,15.431735061099539],[-13.206280405979022,15.617105379096255],[-13.427012539090505,16.067010275596466],[-13.868332403966498,16.148410295914566],[-14.095801284969303,16.42649297223273],[-14.291709667575414,16.572428334883739],[-14.516814014760749,16.649656010711315],[-14.992853331434821,16.654266738639016],[-15.768224008834709,16.485313335297914],[-16.215734634976837,16.50558388661867],[-16.306552550508769,16.429191280518339],[-16.457286267902091,16.157089662342926],[-16.535061704308735,15.839334448959699],[-16.535467666544115,16.286773048040008],[-16.463311879970842,16.60144000103017],[-16.086043473471605,17.526237831244689],[-16.031227043213651,17.887981180599166],[-16.082513220769833,18.50210984441015],[-16.211815186417287,18.991782418070144],[-16.514152343432613,19.361904652385512],[-16.435881114658496,19.509452703920498],[-16.476724601688932,19.710297575341002],[-16.255358213117276,19.960312162564197],[-16.232778136686452,20.220059141771515],[-16.415428690154858,20.557232514631103],[-16.62236195923483,20.634340866266591],[-16.804033763099373,20.866516410080706],[-16.936313025307463,20.879716170120183],[-17.047922975433849,20.806713663053465],[-17.063754467022665,20.898825489004707]]],"type":"Polygon"},"properties":{"alpha2":"MR","radius":1079000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-62.222824408750817,16.751504141948811],[-62.220613916480147,16.755618680459889],[-62.193965526298939,16.800046350993135],[-62.191189008049363,16.804184410695711],[-62.186545718941339,16.805993404325132],[-62.180576153594714,16.807977021650345],[-62.175923275386822,16.809266214721607],[-62.173925316772738,16.804870824435252],[-62.150179171329974,16.744742191867676],[-62.148664095055146,16.740300310460437],[-62.148901205847046,16.735613142335946],[-62.153775775156262,16.686007231130361],[-62.154442178959471,16.681482659136556],[-62.158908258640345,16.68246753376776],[-62.216885541530438,16.698222230205285],[-62.221402244485098,16.699691478541052],[-62.221764585320322,16.704427300050732],[-62.222918248203172,16.746834356365287],[-62.222824408750817,16.751504141948811]]],"type":"Polygon"},"properties":{"alpha2":"MS","radius":8000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[14.180760893004782,36.060250249965634],[14.263263251217916,36.075521423072388],[14.303567209315041,36.06211752286454],[14.315370381472462,36.044616781019144],[14.326799642207385,36.028981853171409],[14.339687546129602,36.014525681401452],[14.353913243695976,36.001383820859623],[14.369343340906827,35.989679502433738],[14.385833150138032,35.979522477216214],[14.403228046873142,35.971007987370925],[14.421364919613502,35.964215873049831],[14.448246005413839,35.957224897579493],[14.536873799456353,35.886135733331464],[14.565875054672885,35.852748480636834],[14.532620378229886,35.820452945051208],[14.441690412306905,35.821599596763889],[14.436503638534274,35.821952071091417],[14.431914114711702,35.824394023466191],[14.352568942926114,35.872426922157878],[14.349059030918989,35.888411704044529],[14.343443261732844,35.906323516457377],[14.336172920590998,35.923629932237255],[14.327312053725699,35.940178494889651],[14.316938718703449,35.955823424035579],[14.305144296796611,35.970426899626972],[14.292032687983186,35.983860276037568],[14.277719395666225,35.996005215334002],[14.262330509175802,36.006754729743953],[14.24600159301694,36.016014124137854],[14.194369792164752,36.042394435354538],[14.180760893004782,36.060250249965634]]],"type":"Polygon"},"properties":{"alpha2":"MT","radius":23000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[57.317963434885279,-20.427681345804157],[57.342796923926201,-20.391357189397318],[57.35867065615323,-20.351988537310106],[57.385952279685611,-20.228703043336321],[57.416162843471739,-20.18394840529394],[57.449161823174869,-20.161095810712879],[57.475138463984386,-20.134721620133067],[57.495809390132443,-20.104011742102234],[57.515244368019708,-20.056085196719405],[57.575883659242358,-19.99739176814208],[57.656424205860155,-19.99020655052151],[57.737009883567808,-20.098556582199492],[57.791782807047433,-20.212634535971322],[57.780415199667829,-20.326813209639234],[57.749492207185142,-20.355551170040837],[57.73686436419019,-20.371427148680993],[57.716740855560339,-20.406595989613734],[57.706423288069722,-20.434766618469066],[57.65114547810105,-20.484623469243427],[57.524787053329106,-20.512954224792193],[57.383409738770858,-20.503467135215754],[57.328479915880969,-20.449885897708043],[57.317963434885279,-20.427681345804157]]],"type":"Polygon"},"properties":{"alpha2":"MU","radius":35000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[73.473303511408332,4.1707039709830909],[73.475101773737833,4.1751223951771141],[73.478806086105195,4.1830888982803565],[73.484044363724152,4.1928889135680247],[73.485529927863226,4.1953310100124845],[73.494025857395329,4.2107948670771203],[73.501125830160404,4.226947330686901],[73.502115969265503,4.2295000330672492],[73.504355387810492,4.2344493423732263],[73.508075227105408,4.2384083136594315],[73.514297536534741,4.2443417301509685],[73.517822642272677,4.2473938975951029],[73.522214155834718,4.245826509575072],[73.523818213577471,4.2451655783755102],[73.528077700361052,4.2431627753894334],[73.527902989575693,4.2384591678801549],[73.527523709547253,4.2340500375492516],[73.526959495436515,4.2297205889263996],[73.525587257262686,4.2238407269541947],[73.520468439020377,4.1978903559971172],[73.519583804332072,4.1910482595800449],[73.518869006875477,4.1869028451379009],[73.517807522496327,4.1828323848184699],[73.513633263605286,4.1692064111527669],[73.512012611943106,4.1647101755310212],[73.507921395779576,4.1622394272884629],[73.499367507726632,4.1576260944059245],[73.494787046202916,4.1554528728877411],[73.489795760127393,4.1563420201474255],[73.483490747037564,4.1578043274001644],[73.478784019474432,4.1591473639398719],[73.476472305883007,4.1634616439227399],[73.475130864363109,4.1662974983639183],[73.473303511408332,4.1707039709830909]]],[[[73.382272665096949,3.246551648682956],[73.382599324383619,3.251313007260296],[73.384422283294754,3.2668081580046122],[73.385160739527095,3.2712927141211514],[73.388145016385067,3.2747206366413151],[73.398249412747262,3.2853005340087771],[73.401662863023859,3.288547599334799],[73.406355389424718,3.2889662543233884],[73.423057497399725,3.2896517886059682],[73.427647067202514,3.2896252704167095],[73.430990923079193,3.2864814698403424],[73.439350876981649,3.277850088863083],[73.442506176239903,3.2742542807047568],[73.441260394794853,3.2696354303809287],[73.436370906021523,3.2545085744884745],[73.434777905357464,3.2502566494114276],[73.431766410464732,3.2468585004317858],[73.420150627868082,3.2349028944080516],[73.416484956667304,3.2314955323536219],[73.411527713137829,3.2308077837711933],[73.40028012690729,3.2298274895584664],[73.395422839406478,3.2296472889129992],[73.39225266835625,3.2333318146152465],[73.385014669534442,3.2426454160402973],[73.382272665096949,3.246551648682956]]]],"type":"MultiPolygon"},"properties":{"alpha2":"MV","radius":6000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[32.670676169527169,-13.590585493908625],[32.836187834716554,-13.475978156273055],[32.966290096314175,-13.201716620922349],[32.975423230649113,-12.701471584180567],[33.021727676053963,-12.630657764885111],[33.267108033571432,-12.539564887127757],[33.344424579100284,-12.429016251812344],[33.252632319376552,-12.112567495349538],[33.302887282373554,-11.69193647625181],[33.226619137388901,-11.530211898686966],[33.232931738705147,-11.417803582668373],[33.359365346542958,-11.163360443539677],[33.261587049331958,-10.89338131258839],[33.500033181963452,-10.748137560228919],[33.619618441596074,-10.551616024778921],[33.505494217618647,-10.22163848883566],[33.311786339876988,-10.037923338102177],[33.305016520027173,-9.8180418674603409],[33.166548812737268,-9.6651257538610995],[32.996198605443418,-9.6226509707799739],[32.920176657463166,-9.407565247360127],[33.330739348275927,-9.5193729497189405],[33.489725869989066,-9.6088533403126917],[33.840580295319562,-9.6098928488381343],[33.995660384263019,-9.4956823204400962],[34.320687762450611,-9.731694501427981],[34.523936032355472,-10.030253308426289],[34.572759424984859,-10.427461771216734],[34.662178906888514,-10.715724344469837],[34.61941328326153,-11.075877232138895],[34.75790415934258,-11.290253838474966],[34.893120275996822,-11.397315987739425],[34.958505110680356,-11.573259627992577],[34.733537579635794,-11.586558128697098],[34.637551431838247,-11.646009950359048],[34.580431525635426,-11.859292322886235],[34.639264477948799,-11.959066558249283],[34.755738972953736,-12.03085084671566],[34.747774810584531,-12.08329685815144],[34.503435428707661,-12.116841084342164],[34.397008097688087,-12.251796016949227],[34.568066327917414,-13.358611487223076],[34.658824370117969,-13.47150805231624],[34.912337496331311,-13.556378646488488],[35.244944215604264,-13.894144693428338],[35.846966668692559,-14.670978001919503],[35.892501478900833,-14.891823404158574],[35.809991091324299,-15.234759761968538],[35.820046948988541,-15.677212385567652],[35.755101194571139,-16.058178180267543],[35.350226913850456,-16.1849388559015],[35.199852289265614,-16.480715836995309],[35.280961011892025,-16.807844151246684],[35.272426510333901,-17.118229544236378],[35.124684700768746,-17.126966738084668],[35.067930074427395,-17.082346002291089],[35.025268353198825,-16.845138974390661],[34.419970254779443,-16.250732690230667],[34.372885552382023,-16.033917136641026],[34.248452980397303,-15.887419965327831],[34.54174113849372,-15.274567522615765],[34.551663306348345,-14.929496481730714],[34.474379602822474,-14.55703766726746],[34.373543223791074,-14.446749913978868],[34.26453486095825,-14.417824565228454],[33.636495161016292,-14.567895602822196],[33.240846373917357,-14.054332358963071],[33.121342885726662,-14.001449441708546],[32.992205230134772,-14.02194592994266],[32.670676169527169,-13.590585493908625]]],"type":"Polygon"},"properties":{"alpha2":"MW","radius":489000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-117.12797396603558,32.533153544166147],[-114.72510507185653,32.715075925083646],[-114.73019473646168,32.551640728768177],[-114.62291074185853,32.442822880074758],[-111.03157536361101,31.325266118896142],[-108.39346120868468,31.330075811276764],[-108.22884450493916,31.451851394546587],[-108.21158810529113,31.779114782644019],[-106.44877465805824,31.769119146282137],[-106.15538427951779,31.458742679669186],[-104.92185648287581,30.58744357480715],[-104.68155562864592,30.134326494182929],[-104.62150072201399,29.854687771812934],[-104.41957675524961,29.593917829195984],[-104.01204594781407,29.334767485489476],[-103.29761737495065,29.017925974463914],[-103.18586227097083,29.005184739867431],[-103.08490411932745,29.054772983850611],[-102.90223823289672,29.249938262872586],[-102.73400739920254,29.643814901754109],[-102.61482748965003,29.752094706143296],[-102.43753827939301,29.785139389912747],[-102.34294682979626,29.864777429729457],[-101.97947102896772,29.795068225147173],[-101.5464552785082,29.807829867795856],[-101.38509927214199,29.745278842031134],[-100.75122710653928,29.17852531819663],[-100.33419870809247,28.506628449265037],[-100.28734835122943,28.319734156392055],[-100.10887605113825,28.169456072652366],[-99.772480063209699,27.748426650343561],[-99.505505120842002,27.548233506030673],[-99.432968329148608,27.045957787341191],[-99.232030833610665,26.765469261394873],[-99.101436807493599,26.453400408281645],[-98.239083904420511,26.102439341416144],[-97.796964814445531,26.040838399711291],[-97.419650516783179,25.904538260687826],[-97.146502062926757,25.96119724105289],[-97.165879139456962,25.75093350154588],[-97.503124435052001,25.024965756776457],[-97.669254460379719,24.376898596566875],[-97.764546659327905,23.320796978011661],[-97.745441771487705,22.942402410278913],[-97.850992172665187,22.6344193923813],[-97.758791780837115,22.107373018525305],[-97.583732053383969,21.80933812293361],[-97.314767896312958,21.564132865510583],[-97.405516456936184,21.221535685159637],[-97.170242224784531,20.693338995262085],[-96.456266405617725,19.869666147020173],[-96.264080844314819,19.326139881603346],[-95.780482606800689,18.807119180718189],[-95.54961069866026,18.719863153352001],[-95.181929683039101,18.700479531830112],[-95.012823190132167,18.574413827919457],[-94.79829630421699,18.514380651684018],[-94.52161916486817,18.183002795926566],[-94.189226433932419,18.196140813103153],[-93.552300916031157,18.43024032986602],[-92.893691627473189,18.467769034141892],[-92.709951729044576,18.611472073816063],[-92.440984449543436,18.675067445070638],[-91.97384263928106,18.715626013430946],[-91.810735805400384,18.682436221653678],[-91.523130361494026,18.8023207492386],[-90.96369806613643,19.148134978405416],[-90.755537460563019,19.349322217348259],[-90.692965228208649,19.729810327318166],[-90.493295093079766,19.965074068575259],[-90.483346652724791,20.561152785591695],[-90.352931686938078,21.009275984879814],[-89.823722447213697,21.273331550924489],[-88.889117693726789,21.412568646521709],[-88.580781849963103,21.539744957680799],[-88.131615073712226,21.615623087494381],[-87.527084649023053,21.495699127207658],[-87.128450794657837,21.621206996088087],[-86.824293395701346,21.421517020520277],[-86.69796986155319,21.19559499211746],[-86.781044170358783,21.106939001830995],[-86.900980487620629,20.781248034868767],[-86.87339951677663,20.66705220040857],[-86.75527698502097,20.551792657065224],[-86.991262345263166,20.272518132594413],[-87.071183382077109,20.383533101979133],[-87.199031981933175,20.417688684945755],[-87.320221612596384,20.364538503829756],[-87.423796615529753,20.221856539943882],[-87.466715833582427,20.066402861139533],[-87.432179258670232,19.898478090210233],[-87.488836250626846,19.728920165350832],[-87.434929812127223,19.501800628624455],[-87.853337424660126,18.269233584044638],[-87.987492083298775,18.452166294544998],[-88.184824980839196,18.52363047034714],[-88.48355924715068,18.448343079645404],[-88.806546704059087,17.965709285605797],[-88.897793674085548,17.91478600502338],[-89.064177954429539,17.913376901315097],[-89.161633948330859,17.815080498042505],[-90.790295986553687,17.816247560677187],[-90.944529424074886,17.743767235583103],[-90.989363208324392,17.637122400211133],[-90.993201521526714,17.25259946195705],[-91.089063473387895,17.159376994744139],[-91.099771554643723,17.008557166489723],[-90.991915430394243,16.880633965799245],[-90.710830661571222,16.70797644785538],[-90.596172788093199,16.505150985132591],[-90.417205500563767,16.390920419075361],[-90.447437266238921,16.072934189617914],[-91.623592633446094,16.070275403682622],[-91.755405423472183,16.020522966307201],[-92.168821673759837,15.343506589508053],[-92.166902307762371,15.205100849264618],[-92.075062113114029,15.074192509945858],[-92.14694391140246,14.947116203565885],[-92.160135515502603,14.691036677030965],[-92.235174115101117,14.545696462137093],[-92.81594177660233,15.14481795068369],[-93.840188213225801,15.983431025631548],[-94.462597197057235,16.182846697313895],[-94.749677533537522,16.218320572374115],[-95.115403215652549,16.179459224306243],[-95.464488405883614,15.97489914008988],[-96.213622510634337,15.693269829322814],[-96.510845138797592,15.652113534594491],[-96.807868057362029,15.726666031697071],[-97.185520407196293,15.906270774216258],[-97.754716629051359,15.967046422611633],[-98.130664171287663,16.199912172188689],[-98.520185130752267,16.305063598176083],[-98.776441181223618,16.526952702960511],[-99.690594437302636,16.719846222707201],[-100.03313881517111,16.92225098418692],[-100.99702008834824,17.27370262439371],[-101.60017076250882,17.651745963378502],[-101.90963140235657,17.953771117104392],[-102.21657619705634,17.957703072011231],[-102.69947601935075,18.063165776843963],[-103.44143854852852,18.32563675907733],[-103.92794384785834,18.838161077631472],[-104.93834265499893,19.309545031680699],[-105.47974756735961,19.972861196809994],[-105.66916667305222,20.385617269712153],[-105.47402859866386,20.580887061329328],[-105.45956834925317,20.696940346201544],[-105.5105109037988,20.808660144684779],[-105.2473903250625,21.111928260845591],[-105.23301674953083,21.391959087278241],[-105.26577373797541,21.498447353015717],[-105.43125365295464,21.6184399220686],[-105.6488463727778,21.988172667300812],[-105.6531126643189,22.334694621220258],[-105.80101386226414,22.634787876881521],[-106.40203398690127,23.195796904472132],[-106.94918591253672,23.893608903222553],[-107.95098222079346,24.615106140888493],[-108.04240211898349,24.804626310445453],[-108.34141304332508,25.17226658147607],[-108.82549852981781,25.484159418851483],[-109.02873769558624,25.480705457924518],[-109.11742257136697,25.565028006193096],[-109.30764182790399,25.637052017419911],[-109.38474383114018,25.727245602157534],[-109.42535973339028,26.032503769237273],[-109.24273934139771,26.302483199065623],[-109.2757839599287,26.522222112593727],[-109.48790171429022,26.695065524926463],[-109.7546924400814,26.703143775944827],[-109.97511708630336,27.061225481051252],[-110.2770565453047,27.162407104657309],[-110.4737257675006,27.319048326194029],[-110.56236121478925,27.455194476062868],[-110.61176036611884,27.764589639896535],[-110.67293203576412,27.865798158427808],[-110.78057881538031,27.914760472800694],[-110.92078223923626,27.889135000300975],[-111.12128473678024,27.967170635201263],[-111.46272695775781,28.365161200961243],[-111.6799421462929,28.47072804468095],[-112.00375343688013,28.864874548521406],[-112.15809645269562,28.877590750694278],[-112.27857579044596,28.769590094584633],[-112.51387544818957,28.847778647762027],[-112.46960935464695,29.167588319818051],[-112.383961276287,29.295581618696637],[-112.39759339357786,29.481899253392825],[-112.73584649867783,29.98123354026793],[-112.82557304511228,30.29977362961845],[-113.05746156426693,30.651137002813421],[-113.1101788297611,30.793336957432651],[-113.12189047009788,31.10848535327704],[-113.18041105868302,31.212329339126374],[-113.62327830938757,31.346056169643116],[-113.66745395827772,31.487421189359438],[-113.79128519884746,31.569873801374378],[-114.14926540211749,31.507665122403246],[-114.52732815071678,31.715846480308834],[-114.67583901550601,31.714519058259057],[-114.83751996331499,31.555709752810952],[-114.87688422041889,31.175738832692392],[-114.70455243678352,30.769157253879438],[-114.63354057562313,30.506840280256537],[-114.64266786930587,30.208874983960456],[-114.56737541744536,30.050756828968243],[-114.34241029901051,29.819393320092239],[-113.87443438007317,29.473881589188046],[-113.72063618431204,29.453358806760509],[-113.5870568574845,29.57278666860055],[-113.50806861487142,29.559667027921645],[-113.32528419942926,29.352344560250764],[-113.20237000284331,29.301649108160124],[-113.15584817924959,29.052259688882444],[-113.22061297274568,28.937765512870794],[-113.21644653960414,28.824949931723403],[-113.09228596250975,28.532110918421303],[-112.87105788875857,28.424061459957169],[-112.71729074865439,27.816062782227931],[-112.32939625083782,27.523289380542295],[-112.21454640503961,27.229110584157553],[-112.00420310397395,27.078967479086899],[-111.94844022604741,26.940783125080319],[-111.56992540238761,26.707464954042305],[-111.32322045215766,26.126483259414066],[-111.22322400559518,26.070451785488885],[-111.09114209204405,26.075423688014542],[-111.08800906001881,25.984566308093793],[-111.22064852686086,25.811838315651258],[-111.20639528738106,25.661226933546171],[-111.03463769787838,25.526775542307281],[-110.88926959758072,25.154156566504156],[-110.59537777994461,25.041944300911251],[-110.5400634993085,24.896247848527604],[-110.64903645581175,24.828597042417428],[-110.7260082576307,24.670793828110089],[-110.65522557170372,24.343740966704164],[-110.47212583103018,24.238307335471475],[-110.30362168152564,24.339234632891756],[-110.06889762653863,24.247945894936347],[-109.89035079781264,24.344414365248539],[-109.79407688010407,24.183335080808618],[-109.83854671291515,24.036500045068511],[-109.78345352819132,23.882606336120901],[-109.65688487458762,23.678847601272867],[-109.50975389455314,23.597721144293885],[-109.42106466807748,23.480045474973601],[-109.45826423572829,23.214836665142069],[-109.81848222350446,22.925046195854005],[-110.00612965608141,22.894251328682081],[-110.08586410728005,23.005550930362865],[-110.18107066009713,23.33082861325035],[-110.36530575220756,23.601594913219486],[-110.62978639003454,23.737570128579868],[-111.0370293494309,24.104150822080133],[-111.47901914341399,24.370635110088468],[-111.5926303788049,24.396989265409594],[-111.7123264810959,24.346669854514722],[-112.07723238937989,24.534798520044824],[-112.20445827476711,24.729131533623207],[-112.29644722324603,24.789795973830888],[-112.06249359898101,25.507623369903463],[-112.16545261496532,25.88980900564075],[-112.35477962511693,26.178873447278342],[-112.66267116470952,26.319890553964338],[-113.02057433788357,26.583429985184033],[-113.18693581410795,26.770563758788963],[-113.40322205511529,26.793884606123708],[-113.59850831374891,26.721569057263132],[-113.83855006618637,26.951984190223598],[-113.99638496295458,26.987916166969146],[-114.1492563436171,27.121990578934991],[-114.44508476978298,27.218341349182332],[-114.54649327026551,27.430259694489592],[-115.03059949046926,27.794755332410322],[-115.03641376617645,27.849175969167955],[-114.54143054020324,27.786979372759834],[-114.32651232767482,27.86590592579833],[-114.18823523146759,28.015354308180896],[-114.10111541654179,28.202470751132726],[-114.07499176741511,28.472988691250961],[-114.16363101650228,28.61778932905673],[-114.94738170490885,29.356173945121508],[-115.16625995139617,29.427456191421793],[-115.67365214024922,29.756537792091471],[-115.80806101061208,29.960326302890135],[-115.82175136228014,30.282907329396398],[-115.99560011215411,30.414623708989915],[-116.05733332694135,30.77170529687195],[-116.29607004625824,30.970644698084513],[-116.345633182151,31.216868949712151],[-116.66189380255811,31.564992275886866],[-116.72906557609117,31.88337305528308],[-117.06290707012641,32.34372275157083],[-117.12797396603558,32.533153544166147]]],[[[-115.17237033065932,28.033710686950208],[-115.35256307273714,28.104053271876367],[-115.23358748463745,28.368048145406657],[-115.14880374634046,28.172107062134597],[-115.17237033065932,28.033710686950208]]],[[[-106.63910232904577,21.697681812181692],[-106.53658587810621,21.676214610278588],[-106.50252535956383,21.610820300985054],[-106.53147745427449,21.528814145859492],[-106.60686553981785,21.561652905026833],[-106.63910232904577,21.697681812181692]]],[[[-111.06335589846736,18.781723322645014],[-110.9894582654865,18.862816424828139],[-110.91476434541022,18.741587488552675],[-110.97477240112158,18.720601332356988],[-111.06335589846736,18.781723322645014]]],[[[-118.40114010539136,29.162633327958638],[-118.31249987475748,29.182641849341469],[-118.26351968497843,29.081665687065808],[-118.24301564059287,28.9420452246259],[-118.28543440950757,28.904131615816638],[-118.40114010539136,29.162633327958638]]]],"type":"MultiPolygon"},"properties":{"alpha2":"MX","radius":1824000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[109.53922968704316,1.8961383825350659],[109.62886928242862,2.0270774770569462],[109.72326307955954,1.8595893945230055],[109.96335672588347,1.7259237841059709],[110.34921499114908,1.7194441629012762],[110.69995038987265,1.5417449654494555],[110.95104069066961,1.547430001849929],[111.15246661318265,1.7626384841080351],[111.2331126953404,2.0779690273195595],[111.20784498613786,2.3749197832058346],[111.29886487948411,2.5289641537308993],[111.33355871847503,2.7680702998700335],[111.48580210149137,2.7447048004022299],[111.72778087572432,2.8536205543332165],[112.12780290225228,2.9169526923380049],[112.9872770848748,3.1629320515522306],[113.44625207937311,3.740441697574187],[113.92342334112845,4.2435872514808191],[113.98451070606546,4.5457199525084224],[114.04874004661539,4.5907817378080873],[114.22014399604342,4.4814117632576504],[114.3151348790154,4.3165109656007221],[114.49105969856211,4.1760726930411343],[114.60490457410825,4.1471837531559439],[114.71533561006844,4.1871864134745209],[114.80924612355295,4.3707491408940777],[114.74692529344941,4.7179980143392219],[115.02678261671242,4.8994601879607433],[115.34628826560977,4.9296144739539081],[115.45400023925012,4.9932020066189562],[115.50832537596122,5.1510894672497027],[115.4192684479017,5.4131015196599988],[115.60391189158793,5.6031272694158591],[115.74524261764546,5.5675403390711544],[115.86646398131252,5.6293998649086916],[116.06306047621574,5.8935811563909359],[116.13862119203033,6.1294151483394526],[116.50157836583072,6.5312814298356123],[116.74999457828716,6.9769039095233394],[116.98961006171513,7.0188413612754248],[117.05182791252435,7.1137645973594736],[117.06448307277459,7.2605879865482059],[117.14699190270888,7.3367601924592192],[117.26390221160919,7.3514205997358681],[117.280542391285,7.2906173567876813],[117.19487040047431,7.0612506327516078],[117.28109815895304,6.7148920828476033],[117.64553033204946,6.4735611447722849],[117.69434626467032,6.1635909960193516],[117.75767815761098,6.0635608146862703],[117.88646865887571,6.0157701128428807],[118.00377350141056,6.0530229717236832],[118.17040363993156,5.8437545830450235],[118.46264711087868,5.7596661512479201],[118.63993616224275,5.5889402291624561],[118.95026670024652,5.4335185911402553],[119.17838347466477,5.4306483105598291],[119.2553103809268,5.3658118222171778],[119.24949520265335,5.1988365587895791],[119.13214359522259,5.1006524708129506],[118.67649983251687,4.9651464909870846],[118.4682004347511,4.9598573602148344],[118.37387571177753,4.8953343902573545],[118.33028220159375,4.7896936174183136],[118.35165358526282,4.6774277233264634],[118.59489232057037,4.46058767131316],[118.50411651283299,4.3643924425087377],[117.74540112663884,4.1670994509883759],[117.4558465440526,4.1916362007369745],[117.10047666362949,4.3360806710994506],[116.51783853526614,4.345937531094485],[116.41452983284944,4.3084478913335857],[116.2362177332278,4.3617626413620698],[116.02157134063397,4.2909228646391897],[115.86105893471419,4.3039235438243884],[115.70027839212612,4.2008594333298115],[115.57117561323722,3.9150144210300493],[115.56653951961896,3.4510028470740206],[115.4563751148375,3.0391179868265912],[115.21063431045727,2.9751004072567082],[115.11935408422983,2.8909410553072639],[115.09448046038712,2.7059425184717729],[115.17885153644681,2.5232979327735903],[114.81258092844345,2.2276754597018722],[114.79980210790599,1.8941132935259268],[114.70199638589884,1.815781084128596],[114.51249225051258,1.4522619053600208],[114.36590255930979,1.4901569961297207],[113.94910684844785,1.4443314099162907],[113.62229116308644,1.2362016997592487],[113.00665731710114,1.4341122144545697],[112.86553851982102,1.5571675922774393],[112.48649527610048,1.5582488564657044],[112.24045142445736,1.465684230511205],[112.07834510086691,1.1435840224161653],[111.9361610092899,1.1111355014960829],[111.80390025897081,1.0100890400250817],[111.4885707465351,0.99563221387537804],[111.09275192183405,1.0477579973583673],[110.50579433359874,0.86219539060975514],[109.73744707883837,1.5212099464475375],[109.53922968704316,1.8961383825350659]]],[[[99.646550094115511,6.4181745022882284],[99.848056323447963,6.4654393634188398],[100.04185674117186,6.4514797565774638],[100.12864410051347,6.5292047949414069],[100.17690784709882,6.671657124831186],[100.26127774839767,6.6824717223722949],[100.37492246223763,6.5424085499656224],[100.59171796719009,6.4615728694242733],[100.71561002128749,6.4804246831679997],[100.87252290563424,6.2970260558181819],[101.0533369123997,6.2423655493697598],[101.1103422392045,5.9224231368506288],[101.26783383204616,5.819458982660529],[101.55606626975295,5.9074829323449221],[101.68243510008089,5.8297267836217994],[101.81871467780451,5.8372752235026146],[102.03135230250733,6.0779370844450318],[102.10119114830471,6.2419720944905883],[102.27392237545801,6.2031249027979385],[102.33998418677174,6.1718499501808282],[102.54301180176266,5.856562570069122],[103.09394489783175,5.4116232619616795],[103.41362381999643,4.8544267068732605],[103.45375935399521,4.6694631205458039],[103.46826362175153,4.3890635065000234],[103.36524944044842,3.7493739024836459],[103.45328932774851,3.5205687197412896],[103.44686415276821,2.9253134772479399],[103.53227679611408,2.7818338380343079],[103.82821256719672,2.5698387524889981],[104.21841425232351,1.7227989435678279],[104.27653991376586,1.4121590065019909],[104.1764116274436,1.3651468412073877],[104.04731282411834,1.4462646614896675],[103.7858888146817,1.4686901859995751],[103.48040073540004,1.3297151172895603],[103.34008436674625,1.5539847857252689],[102.72726173321321,1.8557386460264502],[102.53745025357696,2.046516727094259],[102.14573111214045,2.2486825225490703],[101.78540780944115,2.5638532684918425],[101.51985096553726,2.6838143831062733],[101.29575079941647,2.8853298482510872],[101.29369189994505,3.2546738775418143],[100.71568957071416,3.9662238752046313],[100.73514949958441,4.0905123073912186],[100.61480728441467,4.3734769093809263],[100.60787342040034,4.6685955230069913],[100.44647474267524,5.1478399279928562],[100.35365661528644,5.2456693526183384],[100.19124964647499,5.2830351322708173],[100.20415058974611,5.4467107443677527],[100.33444931058978,5.5531872977749437],[100.37110282989434,5.751993109599427],[100.26584127288176,6.1742572195660506],[100.15933909069545,6.3167328608950051],[100.05683756654196,6.3665176721914811],[99.74387775606661,6.2635539703252263],[99.646550094115511,6.4181745022882284]]],[[[104.13079566331031,2.7388040209759241],[104.1310651617617,2.7715567173876892],[104.18468094907045,2.8713224865390239],[104.22134170997062,2.7319401921296285],[104.13079566331031,2.7388040209759241]]]],"type":"MultiPolygon"},"properties":{"alpha2":"MY","radius":642000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[30.222046775543362,-15.010602134634393],[30.670027429548011,-14.820297227511134],[31.522317232184022,-14.581651937288207],[33.201722175146294,-14.013618180282023],[33.604580211005533,-14.513353653100875],[34.332490346833033,-14.408832945714165],[34.502248911381322,-14.594120143048574],[34.557321701233327,-15.01162894081855],[34.540530607900607,-15.297171972222172],[34.300938234377227,-15.791002140793777],[34.298119215330026,-15.910542325919248],[34.4592869893821,-16.27437785307669],[34.854882399932762,-16.667807311192444],[35.057893677591053,-16.689339006937416],[35.153814366520066,-16.596178092208316],[35.326349637408924,-16.189644209465918],[35.663540087219324,-16.107300592185414],[35.773203712619242,-16.005908189444405],[35.82017208548644,-15.674052098729248],[35.805638699138569,-15.265623336202621],[35.872345553885786,-14.902404887589491],[35.823325702515056,-14.641047317523993],[35.252076984580434,-13.902695996965175],[34.917853524366436,-13.561109546445671],[34.66174389635578,-13.486472887582096],[34.5639447365701,-13.360060332167979],[34.483993342846233,-12.674106317374562],[34.358018676445717,-12.164779503016931],[34.553363865654326,-11.833836147124309],[34.618748044779345,-11.620332019507485],[34.831362026309442,-11.575771755511745],[35.555662024901658,-11.601973183322311],[35.785545404531341,-11.453169924001216],[35.905873061926833,-11.454611323789514],[36.319948678550467,-11.706121485734633],[36.629307950689125,-11.693439141735949],[36.867572075928571,-11.573244514065934],[36.974602222494951,-11.567166247435873],[37.198960221737515,-11.679217027439456],[37.390714799247078,-11.70588203518358],[37.731164048584802,-11.573450075864917],[37.920315559858786,-11.295006191977937],[38.176544196171598,-11.278885980935453],[38.520515383188304,-11.378220280546332],[38.800372126182623,-11.227101614395277],[39.287055753366829,-11.132723918107308],[39.975090060559765,-10.828051630393405],[40.463497749520322,-10.464689161753679],[40.611422147715871,-10.661566111165206],[40.596913539373077,-10.830622946429902],[40.532415759857912,-10.945155734119135],[40.544289912765713,-11.065622034312552],[40.431228523177374,-11.289981867856133],[40.464922124594871,-11.449450358892413],[40.438100435563285,-11.647591454603209],[40.531307798525667,-12.00459707562773],[40.508648096914435,-12.299560486391819],[40.580654675312907,-12.635569333637964],[40.543081775330634,-12.885177892095635],[40.595373301395583,-14.053880386591112],[40.817943277701382,-14.467653950627636],[40.844270906853382,-14.718671967813689],[40.83490342701063,-14.791367798634386],[40.701068113081547,-14.950447261837397],[40.650757597359579,-15.260888534014564],[40.560920002273193,-15.468966242095089],[40.144502951677033,-15.940161111532882],[39.98628844087888,-16.221755770235362],[39.873111770123224,-16.311566248201768],[39.844390960666075,-16.43549671662176],[39.242724882486236,-16.793226218512157],[39.084234998940772,-16.972641012101509],[38.145410456519272,-17.243899538760342],[37.25758583829046,-17.731690639835957],[36.969401448890061,-17.966880775357748],[36.899398435750705,-18.128880453356292],[36.530285730344374,-18.531649174238577],[36.403500957928557,-18.769552620110918],[36.235512710287622,-18.861106698496442],[36.058096959638348,-18.87578940067954],[35.864740522992143,-18.986301804066873],[35.36515221348165,-19.493754028767448],[34.776905779256126,-19.911266900954274],[34.710085208560557,-20.466213217015525],[34.98206327121725,-20.806391768308494],[35.11735968396286,-21.195257627511644],[35.135585560020274,-21.402475889149787],[35.267475543262556,-21.651032791231067],[35.32518864341926,-21.999380394477459],[35.493547976646127,-22.124896851706495],[35.539499123348911,-22.29869181085277],[35.492802927872013,-22.668340335368022],[35.575129668705642,-22.963085933305258],[35.405566626601995,-23.596915777093535],[35.433789750618871,-23.705743678284506],[35.541752673289238,-23.824443660616982],[35.490922861782423,-24.059656908016347],[35.251163103052008,-24.434452143671592],[34.992002595903529,-24.650427230988097],[33.347743959580505,-25.261514624019771],[32.813604090426999,-25.625951686740041],[32.711659253568683,-25.888883794169079],[32.784615071274764,-26.041058550698388],[32.954617617031772,-26.083848693944041],[32.88592868004995,-26.849080512483969],[32.113127988164493,-26.839193301469852],[32.105553818153815,-26.538348313230074],[32.041900650971087,-26.285950290922123],[32.061188626599559,-26.103235559356989],[31.948441285233539,-25.957479110345652],[31.920668625529345,-25.778881137700679],[31.983262060914356,-25.60454856009456],[31.980477777297711,-24.437182088194799],[31.802326948513024,-23.901425652828049],[31.545849528142377,-23.48219618125967],[31.525538753126423,-23.254442742784466],[31.288120081136995,-22.40215339340163],[32.35055429038605,-21.350027496422442],[32.353905547848015,-21.136543719176505],[32.4724162202399,-20.937772598947493],[32.492606879679045,-20.659879357123451],[32.693715714305178,-20.485913351728968],[32.993968897923295,-19.975734443043066],[32.995214529830164,-19.850058628962483],[32.777861555161529,-19.388774403937131],[32.830836240865068,-19.131892401973904],[32.699677577137372,-18.936546978435825],[32.722149022449535,-18.828605681655169],[32.882766829104497,-18.702090847089917],[32.988472730376174,-18.34533707951779],[32.97129620723566,-17.283462957920321],[32.884664639221761,-17.037729594960123],[32.873482650236667,-16.828198286691279],[32.821957133595291,-16.728630347206316],[32.625484748974465,-16.586471214005492],[32.24297923093939,-16.450214576867001],[31.939925191463708,-16.428607789456031],[31.694009940606797,-16.225550451494371],[31.484488876460567,-16.177387861056687],[31.233739660565465,-16.031204961632397],[30.409594813304569,-15.978089008050436],[30.359037535988769,-15.39475405119514],[30.222046775543362,-15.010602134634393]]],"type":"Polygon"},"properties":{"alpha2":"MZ","radius":1116000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[11.743266841275773,-17.249410718452214],[12.019463437255629,-17.168335385618946],[12.537382289721059,-17.208558039196713],[13.10596964708896,-16.967921053887974],[13.471881694256338,-17.038212972607546],[13.686454670595202,-17.228011706986358],[14.025077475716445,-17.407611111926144],[18.39637006019106,-17.399588033440644],[18.726374189126101,-17.707148896668237],[18.939489187848132,-17.798973480651537],[20.388030834533989,-17.886812415796104],[20.754698479179989,-18.018124852344517],[21.113498563251326,-17.955982132781592],[21.444811953483072,-17.995783579873613],[24.274911120595007,-17.481237260267331],[24.937577674714845,-17.545323285512975],[25.258191124214825,-17.7932654816914],[24.886006961579845,-17.829926615043981],[24.530540099605616,-18.05248821823988],[24.388670982748444,-17.994704265992006],[24.250129012648074,-18.021007064373048],[23.599722537682549,-18.459728759116199],[23.326799977864344,-18.064513285339387],[23.186086617337068,-18.003423599776369],[21.146044223887465,-18.311764877526752],[20.996510864046439,-18.418167266056837],[20.972042795755037,-21.995479137948649],[20.138537935740246,-22.004044153810135],[20.036078838079224,-22.058859450226922],[19.978526605299656,-22.18081743731662],[19.98023384774304,-28.451030344043495],[19.584693927046757,-28.550520704241958],[19.161676919414532,-28.938522804976216],[18.814084031237396,-28.867704598370835],[18.107409468445159,-28.872006571086736],[17.448133969943896,-28.697932751625963],[17.34805667368833,-28.501088863643851],[17.317842746017583,-28.299113536687479],[17.107095969288981,-28.107828267256235],[16.904177683128658,-28.133874947878144],[16.755610734682985,-28.452013856392618],[16.447524103758713,-28.617269231032836],[15.719179767770258,-27.965713965488792],[15.289761836543986,-27.279477344014005],[15.133064455717973,-26.78753921872844],[15.139611226104186,-26.516999822193846],[14.967992086979759,-26.317950413564638],[14.931409622927244,-25.966828848239036],[14.845419084638571,-25.725657037277799],[14.831301027152705,-25.015613184121595],[14.500875691276086,-24.196222333927334],[14.473080444461404,-23.28959652171525],[14.403547509222916,-22.968089043362387],[14.521475095253306,-22.744999336597186],[14.464183179308439,-22.457650334751232],[14.321107863547482,-22.190459534952662],[13.973457473690456,-21.767434487512087],[13.833692434884425,-21.466441116057442],[13.45082611048576,-20.916566437192841],[13.165650317296292,-20.186163037232578],[13.039845626760888,-20.023989084846722],[12.457740113393665,-18.927048941513185],[12.044862186405522,-18.47540787045352],[11.776127651861723,-18.001660489781006],[11.733234556717521,-17.744686704941895],[11.743266841275773,-17.249410718452214]]],"type":"Polygon"},"properties":{"alpha2":"NA","radius":936000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[164.03832236347571,-20.233334364001969],[164.05992605890719,-20.142810440468107],[164.20571453687185,-20.223381837098771],[164.43561365514441,-20.283176760409425],[164.97827281760789,-20.676805685965078],[165.19135505781318,-20.769678901747756],[165.38899983725264,-20.923340682342968],[165.5293027460479,-20.990308310713413],[165.75730745069063,-21.037009314800734],[166.0638003937635,-20.994258980618834],[166.27032330564134,-20.886949074312504],[166.48148305213081,-20.660725563844146],[166.58324150699497,-20.414389973080425],[166.62436073623786,-20.419180193505074],[166.87762962038013,-20.595229259929436],[167.26824275026112,-20.701582506292951],[167.3790169964486,-20.938358350268139],[167.53015608023605,-21.129344671715426],[167.7317578353757,-21.266003114384301],[167.98805218159268,-21.338832859435012],[168.1380075313875,-21.445789864160496],[168.11990812189802,-21.615120858740699],[167.87421860710626,-21.721924254209174],[167.65124448186933,-21.938593375579661],[167.57548310036205,-22.0751894399381],[167.51492965359446,-22.301045385151472],[167.51219706061997,-22.457220942335496],[167.54328233410598,-22.623026618572634],[167.51233994023318,-22.660123520337727],[167.47365474415608,-22.652666395552881],[167.17086640454997,-22.441074499655215],[166.94588087243892,-22.378332608612698],[166.77432734638103,-22.375089101640881],[166.46839941836268,-22.255112442773466],[166.19446666044169,-22.099311668352545],[166.03840009521397,-21.968661283079104],[165.71397879497431,-21.772607675902425],[165.26671986895849,-21.541072017366471],[164.92797551162349,-21.289145692567985],[164.67737976797898,-21.01487858555646],[164.44070351914462,-20.813431328167415],[164.170551939945,-20.47966117370493],[164.03832236347571,-20.233334364001969]]],[[[159.9293846896567,-19.174573796605291],[159.95936682252773,-19.117338708822871],[159.97393033270959,-19.238172893668882],[159.93715360421373,-19.33062288294531],[159.9293846896567,-19.174573796605291]]]],"type":"MultiPolygon"},"properties":{"alpha2":"NC","radius":266000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[0.16409476401685033,14.497284123685372],[0.23342818424094128,14.965025380431607],[0.93348094765072442,14.984395600584948],[1.3002989226613479,15.271997590148988],[2.8825741604329345,15.337614709013177],[3.0543615726789946,15.426755766400541],[3.3946427699740114,15.389645003157211],[3.8381558280861303,15.707600266883899],[4.1808918591276321,16.581978545916442],[4.2344186314281655,17.070951044849661],[4.2278726294470701,19.142579071765116],[5.8151747509205967,19.468237428565779],[7.4818219320937152,20.872975802579255],[11.96790750006658,23.517640475718277],[13.48118704536892,23.18000237046838],[14.20134826415603,22.652691320394432],[14.315017134814338,22.662042445048947],[14.978798596982349,22.995986760952555],[15.171650197000943,21.925736906979857],[15.190515019529995,21.528559054588936],[15.607030157294471,20.954394348069592],[15.573951957114042,20.854955567195326],[15.601441326612928,20.741722762267415],[15.962923609141994,20.346159904912554],[15.735106212082675,19.894148684737988],[15.47409333124773,16.908498997757142],[14.377776396252042,15.760414417274227],[13.807710600531593,14.965765511150543],[13.469275866191905,14.360847001040501],[13.606056419851146,13.704817074618578],[13.358083180434043,13.681116394455369],[12.880734202913967,13.453403121613219],[12.634905790689325,13.308387847083619],[12.463020304181075,13.093971100285984],[12.118059416910171,13.090658957970993],[11.958946633470124,13.202896196686272],[11.422430699354377,13.352069766092836],[10.487042553076648,13.331179365260086],[10.034911886143734,13.199905253712339],[9.6107363686480003,12.810780085718839],[9.1956097609257732,12.822362511248544],[8.7562947971561194,12.90675670340258],[8.1095373757306266,13.280959519537562],[7.8460454056549773,13.337526955268125],[7.3577659679102441,13.107390515507815],[7.2006488664399022,13.092505445297142],[7.0566445311414512,13.000488124740521],[6.9421287007237353,13.007286065723951],[6.8044768461962279,13.107818491108405],[6.5169730473194143,13.482474859793026],[6.2789090741555835,13.661067564545814],[5.4820661657354304,13.867357822836061],[5.2418137107730214,13.757451651788097],[4.6770430915852357,13.735247623404916],[4.4316785941121442,13.650406746482748],[4.1707962581831923,13.443093066601836],[4.0354267760408717,12.928799297010638],[3.9477068895809801,12.775168308515353],[3.6713085971301562,12.528612242066236],[3.6244343282790195,11.892467481944273],[3.6644803087811897,11.762441296942079],[3.5954780547835616,11.696616376561519],[3.4783665912556425,11.829467859886048],[3.299288020243067,11.927285105396383],[2.9447156679050872,12.30648754372775],[2.8367991155078252,12.35737777845323],[2.4480443605896398,12.222206124621527],[2.3982460131727756,12.116047851839673],[2.3890376397986524,11.897525201242502],[2.0759966340526055,12.304191169207487],[2.0633221610869734,12.484601783670772],[2.0001528891863982,12.581552808317088],[1.8938616396755847,12.627291504951875],[1.5650395168305251,12.635663851853273],[0.9875193800102825,13.042010279337889],[0.97220212890883806,13.532094194247597],[0.88919546920079717,13.609562206081828],[0.61831129452131273,13.703627911433324],[0.37624897839669069,14.072162293858904],[0.33294406900023538,14.304631183543624],[0.16409476401685033,14.497284123685372]]],"type":"Polygon"},"properties":{"alpha2":"NE","radius":1219000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[167.90655039302902,-29.028846317474976],[167.90640601816074,-29.028193792767166],[167.90960001294175,-29.024747215994594],[167.91714194674759,-29.017358159225342],[167.92067969698462,-29.014213244941104],[167.92536290227699,-29.01490154050661],[167.93473792018247,-29.016747398954326],[167.93937611099642,-29.017894505880534],[167.96004314522196,-29.027660647561127],[167.9702206948887,-29.031444938920874],[167.97019338742584,-29.031518435001775],[167.97804968291857,-29.034437440950775],[167.98167052177919,-29.036528468689166],[167.98617407663895,-29.039387868600127],[167.99016864263407,-29.042216171729116],[167.98990801463236,-29.047103699644534],[167.98921676283027,-29.053747397520471],[167.98840508437033,-29.058897667281197],[167.98603379480875,-29.063541058966734],[167.981430937742,-29.07149144844562],[167.97881924096379,-29.075537265432001],[167.97539292050385,-29.079092398246907],[167.96541562543604,-29.090252078826122],[167.9635000897218,-29.092747658113186],[167.9606689314007,-29.096002861423219],[167.94412187020231,-29.089359469374259],[167.92676771753671,-29.084158627901612],[167.92060983590704,-29.082613365099725],[167.92016504369946,-29.080820128119715],[167.91479557772138,-29.062831979161615],[167.91445335663147,-29.061855312954599],[167.91335356815014,-29.057488024366293],[167.9103950414505,-29.042528638430117],[167.90655039302902,-29.028846317474976]]],"type":"Polygon"},"properties":{"alpha2":"NF","radius":5000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[2.6863113444215214,7.8737777169063738],[2.7750173408195797,9.0483110616752818],[2.9913537663708203,9.0793186612900119],[3.0935874825068916,9.1614055019939897],[3.1363683405190601,9.4515519196336264],[3.2991691431895136,9.6489823259387251],[3.3513282376355327,9.8090858741588356],[3.5702117795798984,9.9382256183303141],[3.6290193755184146,10.129279937344458],[3.5781348584267758,10.292430347579524],[3.8111337172082176,10.572492728918016],[3.7136275153746707,11.072846954991817],[3.4880674939796097,11.395491852350501],[3.4907977638656731,11.499153717573316],[3.6189759400079629,11.764746713126229],[3.6469229606583102,12.529855575419724],[3.9339791460425015,12.764154176428605],[4.0320763880850112,12.922917224922685],[4.1477867878214223,13.457557810623261],[4.5555142950405711,13.70025017044642],[4.8233459327260189,13.759556531719474],[5.2118862731328628,13.755083659531662],[5.4919778024928725,13.872618729867243],[6.3040778247004443,13.656064093517784],[6.9433115221839214,13.00943152477347],[7.3911942286553654,13.126014382380188],[7.8304582640951255,13.340625461616165],[8.0949462107043058,13.290987043425108],[8.7510721202135482,12.909455397922896],[9.1896462357777455,12.823242238637318],[9.5279313245951531,12.812946326560182],[9.661906532432198,12.859826417115249],[10.04001909472146,13.203031588811317],[10.48145635693424,13.330701613345802],[11.417168673063156,13.352842067870538],[11.984902030717535,13.193630683745271],[12.139771275181705,13.092442836180624],[12.375088527142829,13.082484615458426],[12.654899134636626,13.326398291412085],[12.876204086295992,13.451211896696611],[13.328065922572007,13.672119744726809],[13.60623574210079,13.704365084023964],[14.063699829349355,13.078398074101441],[14.202033002090191,12.438943160682673],[14.293576807275125,12.363708810401546],[14.518808720757404,12.298063413940991],[14.619428388233388,12.150870410008752],[14.559580284159638,11.492428249223194],[14.202268595660968,11.268321327756267],[13.974980271755783,11.205694706584724],[13.696057709303529,10.866881313709678],[13.533773542745383,10.594901275076378],[13.414310914555466,10.171584623924808],[13.260823471915892,9.9983402216826338],[13.198511064839719,9.5638945843450323],[12.914856032373304,9.3907603164693469],[12.806284347225615,8.8866835222439828],[12.730996928401273,8.7457867788085419],[12.356933794432612,8.5067106809463127],[12.241780789014571,8.2898063288655024],[12.155742608481923,7.9425717690832425],[12.029742020863615,7.7262259232219375],[12.015767984473934,7.5898397000813578],[11.818277174099855,7.3425244956691431],[11.861151279337438,7.1164899707051941],[11.590878871126247,6.885588201256458],[11.525108467952323,6.6505673162250973],[11.324500806786149,6.4848814366731231],[11.149111167592078,6.4397151861330659],[11.081884768481654,6.5015921660018812],[10.993985024266573,6.7439846616062233],[10.783000036933018,6.9424920243274979],[10.673558977217983,6.985719059684631],[10.413162067878261,6.8779475082982806],[10.058336263709746,6.9156994448993689],[9.8265317454989027,6.7821529339194662],[9.6597304242959048,6.5321880038170272],[9.0495582292601515,5.993710793417236],[8.9359281717608461,5.7807073172174821],[8.798506649392273,5.1931032647172719],[8.5556559715748612,4.7554016157740744],[8.3716016745581339,4.6841477323284195],[8.2928891449840503,4.5578659627543869],[7.7966813240846822,4.5223461912297784],[7.4970262315664389,4.5627046650772076],[7.2038797622376052,4.3879273998778121],[7.0469274511872397,4.4050275651450432],[6.1733056552640235,4.2776211086819611],[5.9708699606903082,4.3388530284986571],[5.5879664772885409,4.6473370870816071],[5.384999451476463,5.1242059303318666],[5.3443714631299741,5.3823199734554237],[5.11265239666072,5.6417010804031795],[5.073444003236979,5.7635249845572396],[4.8454164743841766,6.0394255786566085],[4.4630299821136203,6.328000416316355],[4.1051495398417437,6.4112977850590003],[2.7066879963713801,6.3694989239156241],[2.7611227949766604,6.7125091333144011],[2.7216171699351053,6.9802137393089794],[2.764716176384141,7.4732732737958951],[2.6863113444215214,7.8737777169063738]]],"type":"Polygon"},"properties":{"alpha2":"NG","radius":741000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-87.669931053932373,12.965576029300967],[-87.584967240054425,13.043040848543109],[-87.396126258503074,12.971148191151215],[-87.011377945687641,13.026591348540574],[-86.918011425961708,13.223468526587903],[-86.751371285713248,13.385884633224077],[-86.758749144481783,13.745979697652485],[-86.356487716019345,13.77986681732046],[-86.040476252697061,14.049893714094724],[-85.907033300133676,13.922816195617809],[-85.739159874862494,13.935596988980366],[-85.248626558099062,14.320178210158227],[-85.161112196230448,14.52500861301564],[-84.988093168353146,14.748677535208067],[-84.864969583586557,14.807687743904413],[-84.654324712778234,14.667546157250934],[-84.519163210502654,14.638433871576886],[-84.239193245247392,14.747596434017517],[-83.876387943959628,14.793276392393544],[-83.531909926958619,14.97818129102866],[-83.15794663293731,14.992869642957896],[-83.290288109515444,14.746722824040782],[-83.187987751874516,14.340094152443704],[-83.421062023985613,13.965185101755837],[-83.561857812711878,13.351457999220084],[-83.511195458573113,12.411988247563198],[-83.619931283565336,12.34142605331253],[-83.667832687328968,12.227291879738825],[-83.721023094720408,11.898629989308221],[-83.651992859153083,11.642131858152872],[-83.753797016903633,11.546598594493405],[-83.863292188024303,11.319863830316402],[-83.773814265522063,11.021344638414037],[-83.642257662670957,10.917066798886502],[-83.716921301541959,10.784160961304414],[-83.919268501393276,10.735597367911033],[-84.168274761641683,10.780598566092106],[-84.317485971668503,10.948928610455811],[-84.63877803877142,11.043012728065211],[-84.909202816820709,10.945602346171928],[-85.519341324765577,11.15516570567921],[-85.744169145584053,11.062363893105665],[-85.95336167885668,11.323576522606684],[-86.468637144414188,11.738535145947065],[-86.776646228047895,12.176734754131399],[-87.667282461997331,12.903699267674915],[-87.669931053932373,12.965576029300967]]],"type":"Polygon"},"properties":{"alpha2":"NI","radius":310000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[3.3503900388207719,51.377573538716483],[3.4491555261470506,51.540642722625073],[3.6306498600381225,51.627769670454938],[3.6987476367296228,51.729476472970106],[3.9222925132424185,51.788127684875406],[4.084998070312297,51.993896693355879],[4.4674360544774814,52.292964654893481],[4.7188194378622654,52.89143743118553],[4.7400317994849361,53.091130426340676],[4.9080970987769712,53.246084496528866],[5.1903714249328372,53.391581525560923],[5.713961613971005,53.473448185545969],[6.055850864615576,53.439447937390476],[6.2909252021229829,53.514786563625705],[6.4920416306611441,53.466652782971877],[6.6686627226670607,53.605469064055193],[6.8005681124961228,53.625260013148058],[6.7976875384266515,53.512852784087528],[6.8619784145881209,53.411124176537072],[6.9893957666711968,53.3221473573957],[7.1970302960542289,53.28208645406658],[7.1883979277519936,52.994592782601998],[6.9850193585370821,52.556137124032404],[7.0193780888289243,52.266122693754269],[6.845867632616196,52.114271135764341],[6.7416597361827817,51.911160130035007],[6.2738873322850486,51.814354670736968],[6.1699987590247334,51.73132984897908],[6.1411435246120165,51.601509464874816],[6.193608523499246,51.415473619580702],[6.1298014254324205,51.276850613172314],[6.1351205772583901,51.16033477654041],[6.0417454885095125,51.029924097142164],[5.9937876725891925,50.750667239835394],[5.6897906520281811,50.779566296708005],[5.6397305599669361,50.843649649456488],[5.7183524862728126,51.039993654783942],[5.6291374375599732,51.18704834409661],[5.4690283982570085,51.268362281740693],[5.2142737305095261,51.279252200528497],[4.9953584712977523,51.40425929098852],[4.5038801955573549,51.422101243660642],[3.9020309574487544,51.207854942812617],[3.6253796036081516,51.281519224416726],[3.432584227899357,51.245950250467658],[3.3503900388207719,51.377573538716483]]],[[[-63.254232264322418,17.62877458239678],[-63.241644415661732,17.65153917164244],[-63.221288419635066,17.624272647254561],[-63.241557164072724,17.619843831236697],[-63.254232264322418,17.62877458239678]]],[[[-63.01183346423413,17.527778116626799],[-62.9951216648006,17.528338106960902],[-62.937914309716824,17.4956019941961],[-62.982979997913617,17.477122758183064],[-63.01183346423413,17.527778116626799]]]],"type":"MultiPolygon"},"properties":{"alpha2":"NL","radius":191000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[4.7993136747348517,61.082905064795327],[4.8246253607816829,61.178038628178896],[4.9689710012208126,61.29088342292836],[5.0244610456171754,61.494209206829332],[5.0107462861747827,61.610473284278775],[4.9280879850281396,61.710830780414838],[4.9286852141617068,61.87346581136061],[5.0595537119548544,61.956806380818414],[5.1433339369599871,62.159733374907248],[5.3799345193352615,62.186293189988696],[5.5334699222667814,62.310671664503879],[5.7182099619120317,62.378694120264647],[6.0680568480536978,62.419313798475848],[6.3488420478752339,62.609739525579094],[6.6471055711238156,62.637760841682088],[7.0036342840226666,62.95572338433508],[7.7190527468638486,63.128231211681246],[7.8248049433032865,63.258304605199712],[7.8043156398555134,63.413758500463345],[8.183308659539513,63.494314822277374],[8.2658079007009881,63.578675528025052],[8.2873567317603687,63.6869834236158],[8.7645877109996846,63.804417026113583],[8.9480113402186578,63.628143830752848],[9.2403313460199161,63.556999131426025],[9.4512683190969558,63.58851573263648],[9.6149734962677265,63.79462917448808],[9.8786866100295967,63.931134247555079],[10.010102323106985,64.082980587618579],[10.235609534113133,64.180735716741083],[10.565709501586054,64.418136416209066],[10.855573392269608,64.525670814243199],[10.907432491295143,64.726688503642393],[10.842085197245323,64.82410856947007],[10.740325580075829,64.870394664305707],[10.813565018528616,64.923055373031573],[11.127433020986585,64.976289800047525],[11.336348075038298,64.909223016751739],[12.055222813134003,65.164187262351504],[12.123869936319037,65.284715705396252],[12.103678788066007,65.487515109794174],[11.986916072346338,65.587450161661124],[11.778458800476949,65.604748006114846],[11.800544596994273,65.683677485088765],[11.972285376598146,65.701337510939268],[12.152960683843279,65.560991670844814],[12.301989005800211,65.59453459280499],[12.412851597229606,65.689354818057581],[12.451283219178253,65.795266431506889],[12.342988794934733,66.080580857952],[12.527445716904559,66.210281567552045],[12.764040662099221,66.101949191163655],[13.008639389915256,66.155355086888136],[13.084629722945882,66.267974164755302],[13.104874339592643,66.5392249934483],[13.211588369074233,66.640670054330144],[13.546552975771665,66.75553336534503],[13.65177429148876,66.906920171758927],[13.895196326480526,66.98122382891242],[14.108836567069456,67.11900856264883],[14.355078928193988,67.168257059604187],[14.448540642887878,67.297686920556387],[14.725246484812793,67.484464340473551],[14.781917188279929,67.584609477115748],[14.799204199390804,67.809211228518834],[14.973686096661709,67.921968855382147],[15.026356163381472,68.066413186115852],[14.974885417482179,68.191602331103454],[14.855474673935785,68.255341126447078],[14.354681375826503,68.178645521080682],[14.142911042846386,68.222600280467319],[13.167586164796125,67.989064772267895],[12.824475973992183,67.821623417866277],[12.880705069577084,67.921172176506218],[13.063856144676452,68.069206678571845],[13.176872275428895,68.078201402481739],[13.538092526252822,68.248785840219583],[13.784075576403206,68.275983744164691],[14.183038786695263,68.239072160509494],[14.380530800247007,68.314717867415652],[14.465844926460907,68.390757750697645],[14.495591794197111,68.501101250012596],[14.373777613968574,68.711331740888582],[14.553800518899756,68.818585540964222],[14.770668974462996,68.834399483110573],[15.037925525494957,69.000321714682286],[15.362047384893559,68.94401414545159],[15.892788033712877,69.277659946256151],[16.04799575240488,69.301746117836331],[16.129210606016784,69.273773971038707],[16.046823640766071,69.07325706178608],[16.113553610786326,68.916976709658769],[16.425101085737541,68.841372421095201],[16.601477231014822,68.671300492302663],[16.731687171421946,68.647183429356645],[16.906791282258805,68.699869292302495],[16.991655988978071,68.803727564729016],[16.964477333306199,68.989458894755757],[16.81072627365571,69.070927340845643],[16.937474806104913,69.183861234677764],[17.001996659331301,69.361761152029985],[17.503044167753092,69.595958652309534],[17.613921116106894,69.554417542974292],[17.995415496402764,69.554002326511764],[18.349452207645911,69.767678257474188],[18.613556183960142,69.819980395191678],[18.991542900819464,70.061926603888537],[19.132818863465051,70.243913631583283],[19.212547119839947,70.247202006501041],[19.291756556518283,70.147387280978307],[19.419380887524007,70.112991777044456],[19.540588325600329,70.165715372431109],[19.599220540011075,70.265942132962934],[19.683726166231491,70.273342520966665],[20.238404355771927,70.055097468919399],[20.492848337456181,70.203117306206792],[20.785969776463737,70.219266768164616],[20.848513310488705,69.982620638075872],[21.095083199573256,69.893189646548279],[21.313768893720741,70.059615997107329],[21.355931169384018,70.233185073661758],[21.779921169923597,70.233962901054426],[22.030111909079785,70.31910376957444],[22.104513989350487,70.490343166999637],[21.995173860092795,70.656921054207885],[22.962536105167132,70.71941325825172],[23.395568572233724,70.842321042168649],[23.561171196816279,70.717289334204992],[23.778414345898064,70.747132521407863],[24.091186556767859,70.671451434962577],[24.198784521293398,70.718490818389171],[24.263688361959474,70.826133612382279],[24.658074761834719,71.00078259519799],[25.156213263918449,70.913744813471979],[25.258415648185483,70.963052277526884],[25.315421854290499,71.052805935204304],[25.586344783672459,71.141824863482597],[26.146670965777435,71.039254660298937],[26.19786253219474,70.923584030929192],[26.316549372144202,70.860178691780419],[26.661241778953759,70.939508631781379],[26.73370440318179,70.853533345206287],[26.676151755824087,70.724119434739663],[26.687689488803851,70.610292319902314],[26.849422682379476,70.487878400090224],[26.984324133662277,70.519222733056935],[27.1397173088043,70.675193314744689],[27.230051317310547,70.812572416375588],[27.235528050273732,70.947106115513705],[27.602290526822976,71.090908763447558],[28.141654444612207,71.042734732030283],[28.387467682508127,70.97659496330968],[28.44424015736443,70.84631944356596],[28.561764657478282,70.783358496401988],[28.831570928573804,70.863747834923501],[29.102320727964013,70.86057336427136],[29.376562916143214,70.741664192895314],[29.757633253205402,70.6575073500033],[30.065087858367178,70.702707621044183],[30.319883473481291,70.560907512926505],[30.595824492318851,70.523385532996173],[30.921836586600705,70.402803278591534],[30.960400275558936,70.343809999824117],[30.943949618497001,70.274619674683393],[30.409213044790942,70.175612968667537],[30.294570223989915,70.004564074735413],[30.333728185925978,69.87542346353392],[30.427182393325609,69.80623252267732],[30.869558900939818,69.783216795048489],[30.923836020099721,69.651706586812466],[30.892616928119402,69.558652603762525],[30.620287840158436,69.532454838352862],[30.285372726628367,69.586017506083493],[29.993958752909741,69.392750508311948],[29.397808033589271,69.291708290740189],[29.174815538791272,69.074089111717569],[28.970476763040107,69.022825644152647],[28.833794688066394,69.123981696595294],[28.846495255117233,69.176756118532168],[29.173794225099435,69.407429341036419],[29.188406456243868,69.540070438515841],[29.116335720112083,69.652377994063045],[28.407061459610468,69.824344083824769],[27.897755226040868,70.052657831384579],[27.747893545954401,70.064153206566971],[27.124580714176858,69.90621056837638],[26.561192727702029,69.921896679186716],[26.064390525504056,69.68522482431608],[25.769701659643502,69.262494056969629],[25.748099937573407,68.990298700706958],[25.575245562084998,68.887347659991448],[25.258000476703629,68.823851165691892],[25.086774730596591,68.639786872766592],[24.941378890601076,68.593517199029066],[23.913620715520203,68.802801468854469],[23.70695651895225,68.714058728631088],[23.319812561549039,68.648824711345242],[22.411092302113186,68.720123235876457],[22.299711836543668,68.854985153013757],[21.64448513653474,69.256206872446569],[21.304384549801366,69.273484932299908],[21.185145542737732,69.215241553071422],[21.070314911461818,69.043251808027506],[20.70945075556692,69.068639027658179],[20.429823132837257,68.996541622004628],[20.23236146432053,68.643632201738853],[20.239728631719483,68.477648702561567],[19.969777033112383,68.356691532263582],[18.316386096948492,68.555727355984402],[18.171323433784597,68.441113193276095],[18.176379280581255,68.200703379028596],[17.916641125919476,67.965156879434133],[17.335481511091654,68.09283268108301],[16.805624639498447,67.898544296855491],[16.585392195324495,67.628472801022284],[16.344153248771359,67.446803075337812],[16.333076157309783,67.313193755538947],[16.434033384749661,67.155024442730692],[16.403324558976411,67.055167487414977],[15.541436022780131,66.540559068017615],[15.476002079908838,66.441565850983352],[15.483521916993526,66.306068655852314],[15.150199837358507,66.190416147513744],[14.761926572951319,66.139626674857467],[14.650359405072573,66.061492138312204],[14.634362232405813,65.793333818826824],[14.548512477642316,65.638019756076844],[14.479485924470454,65.301597396107965],[13.895036511612796,64.731305325870068],[13.914713582613388,64.562846214920413],[14.077463855632399,64.463786936519526],[14.120960160245875,64.383039410604709],[14.140968916077634,64.173646649854831],[14.065067167263864,64.097295967755457],[13.960493363135708,64.014235777371667],[13.215182239700678,64.07506208730274],[12.779881229139113,63.994656841401749],[12.269974598066446,63.64544506331238],[12.211806784309031,63.492255995120424],[12.087009726195578,63.361474622005247],[12.070293316020818,63.205088515030873],[12.217806386486778,63.000677720144381],[12.126432571842676,62.8687610712553],[12.126201752998274,62.599111606554025],[12.303281028263335,62.285532934457109],[12.200798405076426,61.865990062551617],[12.212036601480085,61.75018364820631],[12.30270594920632,61.650126219331113],[12.596023187909337,61.541145566000147],[12.880488789265678,61.352202452896591],[12.7058417156644,61.060057527339488],[12.503815833215036,61.027731841766077],[12.406744793654269,60.937818384043105],[12.397834818612115,60.770048576471055],[12.588403564500817,60.450733025612188],[12.48591903729449,60.106920014526573],[12.288159700749889,59.96553960666396],[11.959970479651931,59.867615441514907],[11.756889855511901,59.625818036459592],[11.725570869047207,59.494604567555449],[11.797878386428998,59.28988821759738],[11.712018348137894,59.018746005491316],[11.63798376683391,58.924478447568426],[11.47084437835866,58.909724035570299],[11.313382123109781,59.103462719942812],[10.834589222256964,59.184148469181402],[10.700345269902378,59.331002080790377],[10.568998839464893,59.360723123787295],[10.462819725293256,59.31392079805817],[10.179312419285623,59.009520768924148],[9.8475617457474272,58.958910008828745],[9.7201075034682258,58.985755685590888],[9.5029361621042998,58.916756435935106],[9.3228608841423846,58.747775240901376],[8.1660495600418077,58.145572136619158],[7.4659097562299559,58.021310957626163],[7.0049157017355155,58.024432890474216],[6.8475569886175069,58.086507780250244],[6.5906475823985318,58.097584403755967],[6.4452499112952299,58.252160918142764],[5.7034333470007956,58.526356533973988],[5.5175591366458034,58.72658663038353],[5.5481675659453922,58.983437415516491],[5.5033508534986613,59.093140463744774],[5.3848047664361438,59.162802609955541],[5.1733761030031928,59.162801821419706],[5.1343957270090845,59.222231946414539],[5.1922447610782969,59.509463593216928],[5.1052175714115657,59.731681361786968],[5.182248933102545,60.013822956121352],[5.1242913779911925,60.137192885859271],[4.9438031844371517,60.272447980173105],[4.9303166986095777,60.412015555744404],[5.0659883283109899,60.606024645446816],[4.9992281449349623,60.94868875704806],[4.9302893122511504,61.039702233763521],[4.7993136747348517,61.082905064795327]]],"type":"Polygon"},"properties":{"alpha2":"NO","radius":1040000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[80.051922776369096,28.870336640946981],[80.130672631077502,29.100213750439305],[80.233361973047323,29.214893331140868],[80.255137530705738,29.423290218824551],[80.40210949299761,29.730098203513446],[80.824693875457555,30.12278696897274],[80.966090190912368,30.179799814224658],[81.093053022989224,30.112549664308851],[81.210859890199103,30.111176915990892],[81.417325006806536,30.3373974051026],[81.636429122581987,30.386286256484421],[82.043218672262938,30.326543144364773],[82.214336443568982,30.070154132103092],[82.854861909937199,29.684440179281971],[83.15538668694488,29.612409960061388],[83.563896787851519,29.214793142958737],[83.679422096906563,29.19451464803246],[83.935945275403128,29.279204027207825],[84.027208479834513,29.251622697663649],[84.101191211568931,29.219796388769758],[84.24460569479443,28.913122464022088],[84.711294831620506,28.60117160485354],[84.842130575878855,28.558549527376929],[85.15884535292416,28.592053455125217],[85.154085370610048,28.407463227643586],[85.264491149204858,28.297239793775582],[85.678239173629038,28.277181535823207],[85.902006466772946,28.086219515651031],[86.013342358008828,28.065465317532322],[86.137004134887604,28.114108685715252],[86.345550794747084,27.966806040535385],[86.475066140055247,27.994175586836022],[86.554666427527536,28.085010732491799],[86.690430535150924,28.09469328828142],[87.195433473569082,27.83239379718346],[87.622556387353228,27.816200818605651],[87.865457670025364,27.886391378061127],[88.109692657325596,27.870417249225568],[88.147789083102737,27.754732352678836],[88.027471828939838,27.421025654493508],[87.99008072655451,27.1465265481175],[88.161274963482157,26.724841568156339],[88.022125941523583,26.393101984439568],[87.805305855758789,26.433582150291034],[87.424147160147029,26.413454704687894],[87.287375335547466,26.360591413458184],[86.975690650512476,26.498327668306889],[86.701397317387531,26.435292179129576],[86.365914845478557,26.573704989189984],[86.047984137721926,26.635735547855308],[85.794606606763551,26.604385213140567],[85.575308543124223,26.807510637690182],[85.240268093807686,26.75047440301288],[84.685515071396182,27.041202142219078],[84.630059245821428,27.239581280852072],[84.525780659735901,27.330991628739735],[84.114950685465828,27.470656009461841],[83.828741752122482,27.37810543373314],[83.478269360235814,27.458668939715952],[83.289738137302137,27.371235203159902],[82.733571751902133,27.519172864662099],[82.633411430025419,27.653587004890561],[82.451411270671585,27.672047910325865],[82.06115647520825,27.887944069043968],[81.852676719550942,27.867363510033279],[81.759278323070134,27.912813479709904],[81.314063272591326,28.1742667480822],[81.148481392292823,28.343739713004659],[80.63650428333365,28.612064321823141],[80.418638044376024,28.612262022603161],[80.051922776369096,28.870336640946981]]],"type":"Polygon"},"properties":{"alpha2":"NP","radius":427000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[166.90725542365522,-0.52371576866351954],[166.90822568658183,-0.51923796100956154],[166.9123615130319,-0.50368231525694807],[166.91375600714574,-0.49928435052007586],[166.91797931307019,-0.49742682670377969],[166.93432561437166,-0.49113978774167771],[166.93895686400239,-0.4896179890505849],[166.94348713348904,-0.49141826757832952],[166.95109383101024,-0.4948879892546571],[166.95543932460637,-0.49714020373662343],[166.95635712383282,-0.50194784653182145],[166.95775241478069,-0.51196404226468017],[166.95817445412689,-0.516558651305281],[166.95608413819608,-0.52067193816018809],[166.94132641439941,-0.54662773378245766],[166.93885094601913,-0.55053472096767653],[166.93427128878793,-0.54988723953103191],[166.92127842927323,-0.54741240914727807],[166.91657848564668,-0.54627316236073675],[166.91451682638964,-0.54189858644561562],[166.90880212402567,-0.52802852758306551],[166.90725542365522,-0.52371576866351954]]],"type":"Polygon"},"properties":{"alpha2":"NR","radius":4000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-169.94808565386143,-19.072832026473804],[-169.9462817805377,-19.068557798066912],[-169.91088549517409,-18.994710060908812],[-169.90855977382944,-18.990418084419201],[-169.90422698681459,-18.988169307275673],[-169.86663046726358,-18.97096675899251],[-169.86149159987815,-18.968952806187971],[-169.85603504137882,-18.968122183981759],[-169.83892189021086,-18.96648369078483],[-169.83416639922555,-18.966263100799218],[-169.8317313194681,-18.970353790353379],[-169.79585458576807,-19.037967634634249],[-169.79369219538535,-19.042613108211391],[-169.79466255567439,-19.047644485540904],[-169.8021743051541,-19.077984624902896],[-169.8036446400078,-19.082833123485109],[-169.80795858479945,-19.085490133725475],[-169.89950616691883,-19.135538520059228],[-169.90373624910021,-19.137588545321904],[-169.90657876810565,-19.133844712454273],[-169.94564982336101,-19.076780408068153],[-169.94808565386143,-19.072832026473804]]],"type":"Polygon"},"properties":{"alpha2":"NU","radius":12000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[166.47863519720619,-45.902684554996547],[166.5423674250423,-45.683479865079427],[166.89378203681778,-45.240815985028213],[167.19525038555705,-44.964302257974012],[167.72956058148725,-44.619400361142731],[168.36705096279084,-44.08268192088341],[168.91934038029578,-43.921512413425205],[169.66745524050245,-43.564715654431922],[170.3034274091066,-43.108409824497656],[170.79000106756436,-42.880986678590688],[171.06447814100619,-42.642406544089766],[171.23750595122468,-42.420921186742433],[171.48699917060654,-41.795301662564718],[171.7821400915432,-41.639975985820847],[171.99416408272177,-41.416894954460567],[172.14056331300591,-40.947731505901125],[172.27328480368621,-40.759225239040489],[172.66407961634465,-40.511082397126501],[173.26251851196778,-40.498604825075191],[173.48238312638628,-40.405248637911136],[173.6648854330428,-40.251142393974447],[173.79375540081534,-40.050024623099986],[173.85750430704206,-39.819824919993941],[173.85044895402913,-39.58106552573043],[173.76455560085554,-39.31862831721017],[173.79023773742369,-39.196546600730521],[174.32695236548679,-38.914979537607294],[174.59235829729911,-38.605569952151335],[174.77773310732411,-38.064123394111959],[174.8074746142718,-37.831905802203458],[174.78586222686576,-37.614172344867846],[174.66375387554277,-37.255336790371345],[174.30471838064625,-36.632513737534097],[173.11771205215149,-35.204855596427961],[173.0171508188325,-34.832159340110657],[172.70770241391133,-34.455833473500796],[173.0433572701379,-34.430035134806474],[173.19112724228992,-34.658783611541374],[173.36115627083623,-34.798803370213065],[173.59850112638986,-34.946078980665938],[174.31997841841874,-35.24777100967647],[174.62111890764811,-35.729504984457435],[174.84047947337831,-35.947247857907975],[175.12600901393137,-36.065585925356885],[175.40900595973906,-36.071831095052723],[175.511891988972,-36.177396143564607],[175.61656585395775,-36.519129742924115],[175.85048791708209,-36.895276256397544],[175.97965838262056,-37.344721589815457],[176.11021224156229,-37.531821996729555],[176.28826983195242,-37.674462688176476],[176.72473905850225,-37.872532988367041],[177.11377161006328,-37.957184116008484],[177.50464466799261,-37.872097740294876],[178.00940374459378,-37.555931309888379],[178.27190104541725,-37.567800861488173],[178.53511425595374,-37.69254812762594],[178.38995063769477,-38.00480193199521],[178.30758008740787,-38.461652398813662],[178.04004047494149,-38.842211498150682],[177.90831038523928,-39.238596249803663],[177.57227910781802,-39.27492355823302],[177.25135007622785,-39.474664007471027],[176.84125993615481,-40.157134438090743],[175.98233477905944,-41.212642741059398],[175.30975660874358,-41.609576739957696],[175.07420555717079,-41.520611001232709],[174.84214139270401,-41.497741142090476],[174.61334069790539,-41.542763902407138],[174.34670126517548,-41.700996490162503],[173.53505152275,-42.544292880862194],[173.17887862382651,-43.045455226582831],[173.06999678895272,-43.344362794489861],[173.11598425748565,-43.797731134351011],[173.06514884190119,-43.873808566354178],[172.61794151025529,-43.838108221952474],[172.25031955943487,-43.879435775778141],[171.82331610484218,-44.03734462811596],[171.41845750457071,-44.290335546688887],[171.24582600071324,-44.562761199543395],[171.11242007509165,-45.03869549345945],[170.95866878401947,-45.225747034109716],[170.84215931644562,-45.476557724639093],[170.77572897790489,-45.870105115649878],[170.35946395916434,-46.014544141845228],[169.68617411546563,-46.5506268085255],[169.34221287527851,-46.619905833436007],[168.74316598800556,-46.649152522621328],[168.45627117070691,-46.797632447933687],[168.2018826585041,-47.091609266197644],[167.55487224020311,-47.262535149029631],[167.52316843622796,-47.257919711731802],[167.53665669183931,-46.840569535045304],[167.40328612637589,-46.549821118952359],[167.09553032621804,-46.29693586122611],[166.73216040045898,-46.196992923840142],[166.47863519720619,-45.902684554996547]]],[[[169.06035597028338,-52.492216452191677],[169.17772956864056,-52.498185200610045],[169.23158866872527,-52.547714681815059],[169.1103034044875,-52.564142917870498],[169.02321451543995,-52.496227295369437],[169.06035597028338,-52.492216452191677]]],[[[165.89031970418765,-50.807544966137748],[166.10191541752974,-50.539856206777451],[166.2665161899364,-50.55884667185807],[166.24194461005899,-50.84483846804477],[165.89031970418765,-50.807544966137748]]],[[[-176.84088886262194,-43.828377191365135],[-176.76066201678989,-43.758932734065816],[-176.56597252016769,-43.718571656580608],[-176.17880095394864,-43.741177205740065],[-176.19714937879596,-43.995257253534689],[-176.12375703918093,-44.268236613399544],[-176.22048543664047,-44.32953275003895],[-176.59737934348055,-44.106447453837468],[-176.84088886262194,-43.828377191365135]]]],"type":"MultiPolygon"},"properties":{"alpha2":"NZ","radius":813000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[51.977946722811659,18.99600568093263],[54.882282051140983,19.964230908377072],[54.980655942058299,20.03621209228535],[55.619531076114775,21.949500052634306],[55.598798678300746,22.06518930024788],[55.186117174570811,22.70417524793498],[55.200096652406692,23.034716596425326],[55.507633169638417,23.724910565888425],[55.519563037529153,23.833048427413445],[55.468778864285213,23.941053375762614],[55.681413092656811,24.037025368787635],[55.753333687791297,24.131196010302489],[55.795960784952733,24.868034308103841],[55.911663733498209,24.969901485044666],[56.059918783686157,24.967159343039999],[56.16175165424081,25.024308628925972],[56.253104376794113,25.32101158027892],[56.284866235465358,25.081615379015609],[56.387859670319251,24.978901109795274],[56.628698881131285,24.489773546949301],[56.907975993264948,24.157371864415396],[57.112267590022753,23.989389815058228],[57.60271955839508,23.806280754167911],[58.120347274869573,23.716337953968559],[58.325265952646873,23.62672896380208],[58.577964172570972,23.643253805558487],[58.772885131076876,23.51704524718528],[59.429949014111045,22.661433373487213],[59.54592599965013,22.579821702861285],[59.823016328811171,22.508784805735463],[59.79970345543186,22.220065323179469],[59.371231330614371,21.499018139883418],[59.051910063499761,21.271908952944166],[58.783114118077087,20.937845955128921],[58.785420999883591,20.780609172121039],[58.884190079958401,20.680383633726517],[58.950498344803506,20.516236394563986],[58.834718221194109,20.411383570709077],[58.772027712942766,20.26700630841955],[58.659099992994591,20.203877345961725],[58.525851373677831,20.380497558643146],[58.11869897917618,20.444104111834179],[57.995341899231171,20.399335701466438],[57.877976279798837,20.257513233651206],[57.715426484539883,19.678292384508538],[57.811379549156953,19.017207912410992],[57.675714569748081,18.958229893766234],[57.176730364829616,18.901917890599414],[56.833905780827187,18.757168122576534],[56.662024860626936,18.583366473686052],[56.550546333953086,18.166094685440701],[56.383359813304715,17.988202316956006],[55.483359790241636,17.836785937861556],[55.2920269175439,17.621896428080021],[55.277041442825919,17.326388246100564],[55.064038578808187,17.039120176895256],[54.771890454714253,16.964865029096078],[54.545285105449622,17.03151783133416],[54.107394390756646,17.008198283088884],[53.609780509064976,16.760210194966096],[53.085767314980231,16.648679086051004],[52.822257546789942,17.218272611818197],[52.727095077209178,17.305135630416075],[51.977946722811659,18.99600568093263]]],[[[56.248561312843542,25.602290368253083],[56.144852030517548,25.690593439039905],[56.156653547732866,25.943052430112431],[56.080770848689824,26.062709839593587],[56.16460332361477,26.206890837840788],[56.412940634743556,26.350937999563897],[56.373428229550555,25.804681170941453],[56.248561312843542,25.602290368253083]]]],"type":"MultiPolygon"},"properties":{"alpha2":"OM","radius":618000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-83.027100707606238,8.3376687902944067],[-82.899529469185026,8.4393512713125567],[-82.851957913323304,8.5425725316049483],[-82.91674895323645,8.7403369683359511],[-82.858645153549602,8.8967631400575815],[-82.940093732899783,9.0602251613880487],[-82.939921221411225,9.4439602869712509],[-82.801035832264731,9.5915276816781336],[-82.678820577051809,9.546507109298247],[-82.563591752773974,9.5763670356889357],[-82.391143937754407,9.4480833299895153],[-82.259575858226498,9.4300480837976188],[-82.222203783530205,9.2460237397448424],[-82.134356350607106,9.1341689772448884],[-82.025886959066924,9.0973021275664134],[-81.846508403723007,9.1204217792735491],[-81.540443866417149,8.8354713325852359],[-81.326683041311384,8.7817112062765599],[-80.874399856582286,8.8753365726172575],[-80.553140632974575,9.0790297774128721],[-80.148976744053599,9.2044794255163929],[-79.577246574743313,9.597584577585911],[-79.107155081081061,9.5353627137916259],[-78.89235212784277,9.4337038765291386],[-78.504437473890135,9.4059607318473404],[-78.082886717296446,9.2360463370187684],[-77.835527857819145,9.0712637651679877],[-77.688039402071013,8.8841769291951014],[-77.374572775055015,8.6582645193774752],[-77.424953313741227,8.4750153344940848],[-77.196274739326228,7.972489477192962],[-77.324821717244248,7.8465336858659001],[-77.351046143718762,7.7059653005728119],[-77.812936442912687,7.4429447191549754],[-77.901274879780786,7.2297616661471622],[-78.169973636357696,7.5438997178354015],[-78.378040730325452,7.8999783838683992],[-78.421319603401002,8.0608986913250842],[-78.352775509697892,8.2419796959208949],[-78.486835006683165,8.4706305531903556],[-78.604434532801875,8.5382087158512388],[-78.720459908029483,8.5241128859122242],[-78.808795110461006,8.4475787619559437],[-78.918207190937878,8.2322261484512751],[-79.110245852705219,8.2101564697264102],[-79.124593146744971,8.2559613388335737],[-78.960405534178662,8.4356871134479867],[-78.763767244859423,8.5407506827341582],[-78.738223435168649,8.7274922375774704],[-79.077147273405572,8.9913849248556037],[-79.419302287632604,9.0064547809130371],[-79.695278443325066,8.8288414818988432],[-79.757274726227223,8.7057108777853873],[-79.750714893412521,8.5956082393057827],[-80.303351279251174,8.2834128800251445],[-80.388672559842163,8.1806102882989506],[-80.374168753033828,8.011641071717218],[-80.075406643319681,7.6668852725287087],[-80.011487526372349,7.5001274615264295],[-80.110650225450357,7.4336737346110153],[-80.283697557238909,7.409773179269882],[-80.438990861995464,7.2752211693332196],[-80.845449191056588,7.2203348327547463],[-80.900990237932845,7.2772536297350872],[-80.960285566890036,7.5386420729799921],[-81.105040620453721,7.6378763785477073],[-81.516121207922481,7.6777158112746955],[-81.64087955894442,7.5373560302277003],[-81.603554391954617,7.3330195717532494],[-81.773863970894269,7.3741550021338185],[-81.856657243486396,7.5075885614688618],[-81.811959592037482,7.5921777953129652],[-81.633665752166522,7.6948695915328402],[-81.591735567904507,7.8240053468610391],[-81.774563221806005,8.1392488963608169],[-81.991289913837946,8.2154307379337084],[-82.159850816475881,8.1951089300467288],[-82.300919798606358,8.2692635812767303],[-82.666899855659025,8.3105211974709583],[-82.80253022138055,8.2457518390229332],[-82.879164449962616,8.0711940258189969],[-82.913396950157519,8.1993455432078903],[-83.027100707606238,8.3376687902944067]]],"type":"Polygon"},"properties":{"alpha2":"PA","radius":363000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-81.336339232410893,-4.6694850325328963],[-81.231742324929883,-4.2344956319982776],[-80.798438382124985,-3.7311985480616396],[-80.324667694168014,-3.3881602313429915],[-80.273770413596779,-3.4247355052137225],[-80.179512333119035,-3.8777232007525275],[-80.316799197118385,-4.0420592222592502],[-80.276067737871841,-4.2273121504676254],[-79.828275130170709,-4.4544775843374698],[-79.638590416656598,-4.4551539099792423],[-79.516393626728174,-4.5393051155069095],[-79.464582939614758,-4.7479337412532487],[-79.307018484551008,-4.9327823910563859],[-79.132818143710168,-4.963173010808668],[-79.026040388326464,-4.9261683625647992],[-78.907354958267447,-4.7146072973138269],[-78.694896769259927,-4.5230037884525647],[-78.679174459580466,-4.3259330759584502],[-78.433067451903568,-3.7779160904998657],[-78.345115819022809,-3.39753862749544],[-78.18434413442391,-3.3450317607041158],[-77.860491077109614,-2.9818257541373883],[-76.656415709996693,-2.5461548813304673],[-76.09071549187982,-2.1321148862928307],[-75.592217445064946,-1.5530015126024528],[-75.407865868977765,-0.92449720909684285],[-75.293905620377885,-0.80838851209582263],[-75.277991847777642,-0.63546764861964911],[-75.312656424414357,-0.52431779654055655],[-75.626064095463931,-0.12309384742243001],[-75.376683696200985,-0.14366658997178472],[-75.224495498238326,-0.042008294814807806],[-75.138476693703197,-0.050701990107676717],[-74.953865092209682,-0.18241214747008727],[-74.801906699700496,-0.2003594904104688],[-74.420615486884998,-0.57701053603544095],[-74.268090139313514,-0.94468425875523154],[-74.054505704206761,-1.0288076384643645],[-73.843189451402111,-1.1956024844020128],[-73.664431803332391,-1.2489848217922501],[-73.521637586535945,-1.4498684170382694],[-73.458907170032461,-1.693551610612716],[-73.197151117909087,-1.8303558062242764],[-73.110081791068723,-2.2470216647957106],[-72.927461304732461,-2.3865916949613424],[-72.80169922329533,-2.4041646761752271],[-72.625351128493335,-2.3519168573594236],[-72.405521799425742,-2.4212484184270324],[-72.205631508303568,-2.3973739497211435],[-71.946500635457951,-2.2958867706230874],[-71.752550318126495,-2.1530028825902585],[-71.348734839876542,-2.3334108625363901],[-70.968540807205954,-2.2071180930893286],[-70.095993642864258,-2.658411411939873],[-70.071531403216639,-2.745134957169522],[-70.561837378090118,-3.5112006651527663],[-70.592117612535219,-3.6420926793140924],[-70.502178105607726,-3.7869642194060424],[-70.33955970922581,-3.8145730931115858],[-70.24050623214417,-3.8828920953760786],[-70.177296289653157,-4.0268113999892412],[-69.968766612453734,-4.2318300270786082],[-69.972251809799275,-4.3010491170870795],[-70.053268916141121,-4.3328722387576892],[-70.239096131780499,-4.3009467059016542],[-70.447892293441967,-4.1650255792644275],[-70.770123804829723,-4.1698718140005493],[-70.973781596183798,-4.3502990679612852],[-71.877748839427312,-4.5208073891275609],[-72.83318239740295,-5.1174330083589057],[-72.897809889964208,-5.212020671689447],[-72.980129423658028,-5.6347692185400193],[-73.211076263191885,-6.0415211474173871],[-73.137619485683956,-6.4657137444135824],[-73.235767110666316,-6.5612413223721999],[-73.510050622288361,-6.6875374860903776],[-73.738557127167155,-6.8849177853183408],[-73.792526110224003,-7.0665290450357077],[-73.7206627372138,-7.3091740451887306],[-73.877212409064882,-7.3952448322904853],[-73.933944584791163,-7.5386123493753354],[-73.862264410432658,-7.6923331985037331],[-73.720632589666991,-7.7826616819961218],[-73.712140769304511,-7.9703077716946051],[-73.526444071171952,-8.347236433551986],[-73.36062925600045,-8.4794747564667432],[-73.30728253262825,-8.6395397980744253],[-72.974259524420717,-8.993256623608664],[-72.992259676709267,-9.2226664830422198],[-72.952984739859303,-9.3309988018988754],[-72.860457991925102,-9.3996807710767367],[-72.375516360166529,-9.5128517089643374],[-72.073846647717218,-9.9802467076263461],[-71.614683756788708,-10.006043022386804],[-71.270389618575521,-9.9720880734150317],[-70.541583871539828,-9.4379117094103755],[-70.637224893570007,-9.8910475976289547],[-70.608751314058509,-10.878722826874798],[-70.335918799005995,-11.065507973100468],[-69.960299322089114,-10.930121365601799],[-69.578745057329797,-10.951992402528989],[-68.685520685723944,-12.501938806689221],[-68.759303757770184,-12.687107225210223],[-68.922503447922566,-12.810692339521555],[-68.978820968478004,-12.927765576038873],[-68.972838369811129,-13.3881667160374],[-69.041973582087223,-13.717346632557481],[-68.871140970776068,-14.169721230730804],[-69.215103800655072,-14.590528098167333],[-69.356592198515813,-14.841571711185148],[-69.353162335123002,-14.998177616124977],[-69.172726566755188,-15.236576106876266],[-69.41110604480312,-15.641247913010567],[-69.209732860802134,-16.145742972401422],[-68.913565573857994,-16.262079265056016],[-68.843021809386201,-16.33782844227704],[-69.004932154488998,-16.465041738154707],[-69.020959333857064,-16.642103233341519],[-69.19274518507244,-16.76356232111107],[-69.536434434089841,-17.178237565664283],[-69.558784658354284,-17.292314703162727],[-69.511169607192429,-17.505954365655665],[-69.788354124517667,-17.740172942449128],[-69.804525223202944,-17.99562321851289],[-69.926523021373924,-18.20587754791336],[-70.178718625251889,-18.323499872637086],[-70.418205320237774,-18.345364697229883],[-71.3368012159915,-17.682384544897534],[-71.409052280263126,-17.413523785899933],[-71.525483392822736,-17.300318629318664],[-72.11117177092207,-17.002359583247166],[-72.459895658130208,-16.714139462231078],[-72.793804868322582,-16.614218003731459],[-73.408361529286012,-16.302421680669553],[-73.731298232844196,-16.199846558875901],[-74.139285286211702,-15.919608678164389],[-74.372780198431826,-15.833757047815171],[-75.104058746600273,-15.411664520238539],[-75.530551495710256,-14.909111278711189],[-75.933662867365584,-14.633405678744788],[-76.174471911063662,-14.238384552768249],[-76.288804635430992,-14.133001073794105],[-76.305493246258806,-13.962916507177315],[-76.376113759488234,-13.863119498659257],[-76.233272844100256,-13.703688772313091],[-76.191236769574402,-13.525776147790191],[-76.227146509887305,-13.372956899446066],[-76.430545706490122,-13.10458532908449],[-76.831472290643902,-12.360810917240414],[-77.152513819897152,-12.06021176511525],[-77.241065618201716,-11.63304655583201],[-77.632982758768236,-11.287675432989328],[-77.736879727012692,-10.837116587629843],[-78.183602726597627,-10.092856280886869],[-78.443731087661604,-9.3790542625973394],[-78.665603689560641,-8.9685200794644544],[-78.778635625828187,-8.6020577111687206],[-79.008723941419333,-8.2210406786943473],[-79.37482706305461,-7.8388407181606672],[-79.635580359813559,-7.2672442023120221],[-80.08781779795585,-6.6728342156060796],[-81.141801641012762,-6.0565780325974989],[-81.164071670060849,-5.8754359131665934],[-80.95251712121599,-5.7429348965507634],[-80.907475061478081,-5.5790062996955321],[-81.167419563610892,-5.167041178129538],[-81.148333285406835,-4.9677387690560364],[-81.336339232410893,-4.6694850325328963]]],"type":"Polygon"},"properties":{"alpha2":"PE","radius":1215000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-136.47810268160984,-18.470888485041932],[-136.45868656047327,-18.463442235671607],[-136.43213309847559,-18.472261995807866],[-136.32780651152876,-18.519631831636904],[-136.29423944005171,-18.544368713283227],[-136.3140537344409,-18.565946976400127],[-136.33455409938026,-18.545441460556905],[-136.37030569073497,-18.52054415896173],[-136.4241912406859,-18.49519621320422],[-136.46415905940452,-18.484860435685576],[-136.47810268160984,-18.470888485041932]]],[[[-137.06694325984245,-18.265669744703434],[-137.02976333376498,-18.273062093846949],[-136.97195669236481,-18.342077793868274],[-136.97156702330366,-18.360402516494958],[-137.06694325984245,-18.265669744703434]]],[[[-138.56803609967503,-20.787160391327486],[-138.54643471789001,-20.771505466895714],[-138.51521629433446,-20.813502450992608],[-138.50613156008095,-20.857112277777034],[-138.5346732615491,-20.875492629246807],[-138.54915840669216,-20.821694419020712],[-138.56803609967503,-20.787160391327486]]],[[[-138.69013987211503,-10.425941582312523],[-138.64309849287318,-10.44617358337903],[-138.62473012941504,-10.463065023848355],[-138.63261709124197,-10.492062697768473],[-138.65125163793246,-10.515167457127854],[-138.68752420645782,-10.53206694542126],[-138.69013987211503,-10.425941582312523]]],[[[-139.16613611279547,-9.7703109666552912],[-139.02429518177701,-9.6954939445963984],[-138.89835248755858,-9.7377954543717919],[-138.85815443479075,-9.7430777672605551],[-138.82791999404455,-9.7419174322763968],[-138.8750552400833,-9.7926821056702522],[-138.94417033632422,-9.8095432380318517],[-138.97843676531113,-9.8271004930724555],[-139.00870391836688,-9.8508984931139842],[-139.0338500604762,-9.8800552572879266],[-139.05991445904411,-9.9312218407965087],[-139.13376712699707,-10.009023647536299],[-139.13201352402189,-9.8618660165258447],[-139.14014972260708,-9.8255906467029757],[-139.16613611279547,-9.7703109666552912]]],[[[-139.63154217355168,-8.8985896690980386],[-139.61160915921235,-8.8725643270062076],[-139.58396838231008,-8.8603338754641268],[-139.53468459855318,-8.8756018940341566],[-139.50858555736536,-8.897169500685564],[-139.51014287285039,-8.9154727921234436],[-139.55626577210668,-8.9400281542827464],[-139.62081482994208,-8.947694585162111],[-139.63154217355168,-8.8985896690980386]]],[[[-140.14409473124761,-9.359518665295969],[-140.07094627151596,-9.3283890390885666],[-140.03142954698495,-9.344840909581297],[-140.07580447178267,-9.425837084848645],[-140.09731620951104,-9.4438028788627637],[-140.1378622047138,-9.3842962402798094],[-140.14409473124761,-9.359518665295969]]],[[[-140.25245992263206,-8.8480285369033389],[-140.23979054547587,-8.7976973571653279],[-140.22432004303661,-8.7818092517213451],[-140.05783842918299,-8.8016849639353119],[-140.04399087528421,-8.8385190824171076],[-140.04634842738648,-8.8735585351201323],[-140.0727585175112,-8.9102243691015115],[-140.17057821863429,-8.9336860253708021],[-140.21727403461753,-8.9294701031715285],[-140.25245992263206,-8.8480285369033389]]],[[[-140.96826176675444,-18.056798362713177],[-140.94556158195539,-18.044858167751119],[-140.91555853407425,-18.02201018741982],[-140.88584312332307,-17.987155837793349],[-140.89365416293307,-18.03640299555914],[-140.88692347116054,-18.093694978605257],[-140.86412076783486,-18.146682809794456],[-140.83009684081676,-18.189472258014362],[-140.81672040326862,-18.243385594912752],[-140.83777338503202,-18.218017824102613],[-140.85986985545188,-18.198550503645937],[-140.89607414493753,-18.148465528648195],[-140.95848666519231,-18.084938844341192],[-140.97320743355121,-18.059286790902817],[-140.96826176675444,-18.056798362713177]]],[[[-140.78813973539516,-18.3028328531726],[-140.7455643738368,-18.346727434538817],[-140.68553335762394,-18.380049499050561],[-140.67230003832069,-18.415562305167125],[-140.69646465258916,-18.399852308630152],[-140.77305490446111,-18.363608398490797],[-140.78813973539516,-18.3028328531726]]],[[[-140.85875135590737,-17.929066232344471],[-140.85168266976706,-17.889050577465017],[-140.85045938733575,-17.831135328096682],[-140.80344244815794,-17.7518056686928],[-140.76133283771671,-17.717924871960648],[-140.68870386547502,-17.684928237383634],[-140.64983907195477,-17.669996535994638],[-140.63870266358896,-17.677963455646328],[-140.70402248679676,-17.712797751604594],[-140.74910601225847,-17.747968364520517],[-140.77279985561418,-17.777922729097433],[-140.79036766576604,-17.811834883506137],[-140.80116880521203,-17.848468168328008],[-140.80468059883464,-17.875555776182214],[-140.83312814995367,-17.898540018121839],[-140.85875135590737,-17.929066232344471]]],[[[-142.52922070311652,-16.10664445675727],[-142.50662513761722,-16.027926717481279],[-142.48156945626158,-16.018141289114109],[-142.51198896238154,-16.096144466985272],[-142.52922070311652,-16.10664445675727]]],[[[-143.70667610290369,-16.581070505316447],[-143.67028775593403,-16.581098785872637],[-143.63267016880994,-16.601364782267535],[-143.57529011458121,-16.615708779218707],[-143.51107259984281,-16.612417060698466],[-143.46474056064503,-16.613887470625016],[-143.44527433507383,-16.618528245570715],[-143.38718826112807,-16.668183170720383],[-143.45030738642279,-16.640195205454315],[-143.48992709983,-16.630559785618665],[-143.54295394796094,-16.629415787530448],[-143.61059227432418,-16.640177947008045],[-143.70667610290369,-16.581070505316447]]],[[[-145.16023781078133,-15.757346960068872],[-145.13366346515303,-15.762220268677268],[-145.05160126285827,-15.856124431160847],[-145.05777093440815,-15.900296872106461],[-145.08445853754591,-15.850211661008428],[-145.16023781078133,-15.757346960068872]]],[[[-145.61360725053609,-16.080128743885634],[-145.57399531652601,-16.165644590685023],[-145.48686135420184,-16.329864301818162],[-145.48252585571811,-16.346534669655956],[-145.50260859955645,-16.345566778033053],[-145.53650998882671,-16.299670403418268],[-145.55364760489411,-16.251373523269592],[-145.57333700916425,-16.210704822870927],[-145.60887811603132,-16.165121629321039],[-145.61360725053609,-16.080128743885634]]],[[[-149.91160619480567,-17.501177114321699],[-149.9019670695856,-17.469788764589776],[-149.8970710925378,-17.469770246363272],[-149.80886848142472,-17.474217728588048],[-149.73951323165792,-17.515505829723832],[-149.70328198921644,-17.528578792306703],[-149.66521666192702,-17.534464039786155],[-149.60777391413421,-17.529412545402092],[-149.50808698085078,-17.496670811799412],[-149.37931918303818,-17.522551648232195],[-149.3302696836524,-17.589057794397707],[-149.31144619313108,-17.636456190802924],[-149.27756897278445,-17.681415947381666],[-149.23250479946907,-17.715154146110041],[-149.17788229361864,-17.736823437610891],[-149.1511443368297,-17.812081307109683],[-149.18190514304928,-17.862043856989526],[-149.24973469949606,-17.850713896438414],[-149.28643190107664,-17.825546686992801],[-149.32855849965867,-17.786656024552414],[-149.37924554920045,-17.760487415118227],[-149.43525839307983,-17.74969230365447],[-149.48700806708226,-17.75176201162461],[-149.57875080649816,-17.734760881193232],[-149.6046803368414,-17.678836929559054],[-149.62461730809792,-17.644760620899401],[-149.66603438566233,-17.602601604747278],[-149.69975137462964,-17.582062805129773],[-149.75621186692615,-17.564599421536492],[-149.79563690501726,-17.562515193557932],[-149.84490580090986,-17.570828321992369],[-149.84953767944918,-17.569069938905002],[-149.88639923475557,-17.552615277931192],[-149.90490992818667,-17.527631762685242],[-149.91160619480567,-17.501177114321699]]],[[[-151.51210528202009,-16.619022016197672],[-151.50558730929532,-16.574397006373268],[-151.45765543186997,-16.604003882540319],[-151.4383415078465,-16.623512291624181],[-151.44273968998698,-16.674643939802788],[-151.43174714689846,-16.732456836885465],[-151.36482879151643,-16.86411913849734],[-151.40984286302418,-16.877562950376081],[-151.44498037349632,-16.879120224078669],[-151.4852612590592,-16.86353095688748],[-151.47689508902849,-16.766171686073246],[-151.47701303007963,-16.729725152389427],[-151.48374316821943,-16.693905200291802],[-151.51210528202009,-16.619022016197672]]]],"type":"MultiPolygon"},"properties":{"alpha2":"PF","radius":49000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[154.54034091962168,-5.1108711736585617],[154.63261436429102,-5.0141512585529444],[154.68179048767777,-5.0541306073762193],[154.76762107680702,-5.4483145779729076],[155.08944076665446,-5.6165594822489338],[155.22291066959127,-5.8512639427843549],[155.37240520582503,-5.9745387005837784],[155.46325932081629,-6.1294541195575158],[155.8223338966192,-6.3806572492786353],[155.95643027788716,-6.6819953108517272],[155.91871776134175,-6.7933274150655905],[155.61271415738398,-6.854714261720102],[155.42742976612689,-6.782574963836761],[155.26442114730338,-6.6305103738201243],[155.18155827862154,-6.3232458631602499],[155.01031030824524,-6.2096135696577788],[154.75950880734001,-5.9312438280979904],[154.709289714585,-5.7470680610867344],[154.72258432525447,-5.5691062521991404],[154.62569777346928,-5.4358461198252499],[154.54034091962168,-5.1108711736585617]]],[[[153.20388685315146,-11.324449027790475],[153.70310523998458,-11.528730347384682],[153.75963600586334,-11.586179341322854],[153.55380537535746,-11.6302974517733],[153.28700924031858,-11.516793380629695],[153.20388685315146,-11.324449027790475]]],[[[154.02235035707582,-11.399953193015749],[154.02369969635265,-11.34813386904386],[154.10181982804392,-11.311677423448671],[154.28049832959766,-11.361540592458718],[154.26585276470618,-11.415631804629532],[154.02235035707582,-11.399953193015749]]],[[[153.5918942839933,-4.0959203487517888],[153.6398554953102,-4.0449351898720494],[153.66273867352137,-4.0414830843051393],[153.64997705923449,-4.1227489168801839],[153.5918942839933,-4.0959203487517888]]],[[[147.78127977071202,-5.6271715262479312],[147.79945236196599,-5.4922374729680774],[148.17054221477926,-5.6480732989182778],[148.2788887325118,-5.615846107025825],[148.4320859580146,-5.4720267123693276],[148.99919475413267,-5.4848495548119693],[149.35888851905304,-5.5830979744602276],[149.81925384236769,-5.5195869364945587],[149.96001377199769,-5.4101633330393826],[150.0438137202288,-5.0393746631194301],[150.09007180163337,-5.0120505376554529],[150.16998732970842,-5.0706032354509167],[150.0750567057749,-5.2743739276118786],[150.14993480003352,-5.4642109518716717],[150.27272694350722,-5.5133230845037406],[150.51937600379438,-5.4605332213235149],[150.71112520945985,-5.506154065134667],[150.95315847330485,-5.4085845364297285],[151.1379142026357,-5.1130077541224699],[151.32672241978293,-4.9606386736039889],[151.54027650529449,-4.9212166648605153],[151.64374152987716,-4.8386628350002612],[151.66492097837926,-4.6456772276445824],[151.54446241442315,-4.2992344152303898],[151.59318294330512,-4.2010028201809213],[151.96600438958825,-4.2766850392635245],[152.11716917281345,-4.2124933226681947],[152.21723637900953,-4.2910269448133338],[152.42393584084152,-4.3443352194110814],[152.58808486960572,-4.2918637668026038],[152.64614269209437,-4.1902404923840608],[152.63739300846603,-4.0735295516283969],[152.2701643335032,-3.5743183195313071],[151.88425504667492,-3.4026558954239876],[151.05878533907051,-2.8250179492049279],[150.74627137183549,-2.738685393444825],[150.70165306571542,-2.6743427803819104],[150.82548205643039,-2.5732774283848987],[151.24665773687948,-2.8540020460266935],[151.71609709802669,-3.0621554890768063],[151.8902179610925,-2.9908615754442276],[151.94650704221681,-2.7089180910312241],[152.00174675138868,-2.737936831211333],[152.09894610890606,-2.9474322649349487],[152.02001518221937,-3.1190298544460773],[152.05501263086344,-3.2670029287145925],[153.0166702279495,-4.1057837457048638],[153.12401717941324,-4.2524190298565818],[153.13228908037334,-4.3523936852856338],[153.02297273656939,-4.6661928550031471],[152.89173581766153,-4.8320494657115898],[152.68186021441076,-4.5960977967991408],[152.54860633657839,-4.5815749557278087],[152.44872928098576,-4.6395676949693838],[152.25742234666447,-4.9544752786683057],[152.08339979727455,-5.0905527431242827],[152.07194177210209,-5.2227172509948865],[152.14267304408114,-5.357025973197393],[152.07685558430111,-5.4581053597632234],[151.86540682442387,-5.5646291237235763],[151.57547631922156,-5.5636202278259503],[151.22920051594195,-5.9197770658584847],[150.84070975973756,-6.0587635903157038],[150.43362057326613,-6.2746722331849769],[149.65261164888389,-6.2901973346681492],[149.42352804593307,-6.097228229285613],[149.28314013931941,-6.0804001967868748],[149.12198329740497,-6.1258667432788618],[148.73106503086512,-5.8739972343168967],[148.40134611661296,-5.7648183972876907],[148.29002159327246,-5.6789187689117266],[148.13904396970824,-5.6921640966051559],[147.9855638943977,-5.8337198419326084],[147.78127977071202,-5.6271715262479312]]],[[[150.50783402874481,-2.6251070011493454],[150.16584968728299,-2.6600043551913473],[149.96203941642699,-2.4738679236850944],[150.22712111947462,-2.3844087018127604],[150.42929924976465,-2.470611641446292],[150.50783402874481,-2.6251070011493454]]],[[[152.51563128452116,-9.0098146988404704],[152.63095077764137,-8.9595812047311423],[152.80993037096076,-8.9674518667967114],[152.99481703345711,-9.1307123398800059],[152.96668695405634,-9.2087181920389511],[152.86748601887879,-9.2240647342014839],[152.51563128452116,-9.0098146988404704]]],[[[152.54354873801239,-3.0956292134294467],[152.63861636575291,-3.0430698369195275],[152.64606461119931,-3.2207820582768623],[152.5852150664754,-3.1696944335614465],[152.54354873801239,-3.0956292134294467]]],[[[140.86257026924483,-6.7399818553207611],[140.97499999999999,-6.3175259262104202],[140.97367101466997,-2.6100022120992543],[141.10003978944957,-2.6112733180686885],[142.55181257550501,-3.2054991623920799],[142.91488299721209,-3.3228316745654998],[143.50888947967604,-3.4313472639121136],[144.06676883005051,-3.8039659200741522],[144.4776467388362,-3.8254824732118604],[145.06593588752727,-4.3275188588231011],[145.33447605810593,-4.3854675733809767],[145.6512137081923,-4.6844671020970141],[145.80325120271672,-4.6862488386580763],[145.99957072535469,-4.5423286355147585],[146.05315006398087,-4.6401175965051555],[146.01915535574057,-4.7259981582201442],[145.86314734015463,-4.7967964900908351],[145.79253857027388,-4.9153986916934835],[145.7744957246091,-5.3078305071738265],[145.83352252517525,-5.432171572303484],[146.39867161671515,-5.6148883821797151],[147.04945339272891,-5.9237551661772114],[147.42265431530842,-5.9664329751948033],[147.56651597499848,-6.0571327131113559],[147.80187608962947,-6.3153810017647887],[147.85432184574472,-6.5511792170893104],[147.81032833747815,-6.7034073939824674],[147.13305063157497,-6.7387772596687663],[147.01516903882691,-6.8408309608572235],[147.00057966500867,-6.9773489155706114],[147.25442638909595,-7.4554487503174052],[147.72483621515877,-7.8755897556376206],[148.12658636016255,-8.1037572854141189],[148.24520037126163,-8.5150571657061871],[148.45097190610258,-8.6946642220992256],[148.58299948558138,-9.0308380555415848],[148.73040177233506,-9.0908411881906748],[149.19822744450457,-9.0314645723085967],[149.26385459200918,-9.1807360670252685],[149.21531909239076,-9.3821876437052207],[149.28069342766204,-9.4967816965904817],[149.48421057482204,-9.5882799645252383],[149.90951061556748,-9.6385427308447653],[150.02773796900729,-9.610879603431389],[149.91647938025338,-9.8611946908374737],[149.96869099785698,-10.025753076939255],[150.34266186586811,-10.182506203940701],[150.65811039424821,-10.24952477620492],[150.79381437174342,-10.233773744905493],[150.89217964043544,-10.141348783135225],[150.91269363705391,-10.026868005460374],[150.85583962819507,-9.8695937927236219],[150.68711514620151,-9.6625874975108061],[150.43638590019813,-9.6243779839600023],[150.26111006293465,-9.4968383334842787],[150.10889499718203,-9.51851404769873],[150.11002709576243,-9.3618981309906335],[150.13517908771743,-9.2597170744198571],[150.2083224253397,-9.2066345753566683],[150.42104889192427,-9.3371289261107844],[150.78853697339125,-9.418184448320881],[150.87886916714811,-9.5127982593595455],[150.89013401729881,-9.751374372430849],[150.94114619201511,-9.8546798410575516],[151.09037436068868,-9.9482686396844038],[151.25562059214175,-9.9229102485815588],[151.29623381848842,-9.9568166194220638],[151.23072186494034,-10.194365232091821],[151.02786014714351,-10.123241877289214],[150.87014288333947,-10.202026631659262],[150.78368192607022,-10.39039261154104],[150.89846891292518,-10.565462380267329],[150.88448768325071,-10.643168683966362],[150.65443482825739,-10.563337928542719],[150.31993322318047,-10.65470887480584],[150.01696328524488,-10.576944648118484],[149.73542592468999,-10.351052680546873],[148.9305715886419,-10.253840682313069],[148.69808403732009,-10.169493098920327],[148.42544830696494,-10.190759864790046],[148.24018589129295,-10.123174993769934],[147.76872538546439,-10.069904109479445],[147.55330660517075,-9.9122574066359874],[147.39286482821291,-9.661283264948052],[147.01739086481987,-9.3877069212220832],[146.86515991646127,-9.1081051871079719],[146.63479766174959,-8.9556192716265688],[146.04762590401452,-8.0914012610161326],[145.73318942127369,-7.9559177127685068],[144.97392400267856,-7.8018790154490629],[144.81001996743575,-7.6595544127055311],[144.60810105899395,-7.6027184429708701],[144.22530154503281,-7.764716241185643],[144.04537226352741,-7.7826330660270653],[143.88782682060446,-8.0174789880944441],[143.68575156473611,-8.0734132455456145],[143.57089947373291,-8.3064831289512355],[143.60785844628268,-8.6768915675025333],[143.44052517816516,-8.7423312339950723],[143.36601919079604,-8.9608634446390525],[142.64713135912186,-9.3276029047104778],[142.23994337503359,-9.1731736437985294],[141.62636333911226,-9.2113855183537972],[141.37570858405076,-9.1563315930680851],[141.1332600227563,-9.2210648612898432],[140.97640307144582,-9.1186226231231355],[140.97519531250006,-6.9790193269159531],[140.86257026924483,-6.7399818553207611]]],[[[151.00526417097049,-8.5237936412235396],[151.1173550228886,-8.4191327576403108],[151.12374282666596,-8.8042158354979652],[151.04646425671831,-8.7282629931075704],[151.00526417097049,-8.5237936412235396]]],[[[149.54614919110466,-1.4715670595344319],[149.58107707357615,-1.3534852394476784],[149.63291866379427,-1.3622205712791708],[149.72507323356751,-1.4308222900336771],[149.76297585816295,-1.5888755982376292],[149.67116868028359,-1.5760370110732873],[149.54614919110466,-1.4715670595344319]]],[[[146.53161310738648,-2.1540705634677897],[146.65631500174422,-1.9742405057312868],[146.8614649750742,-1.9487745957813876],[147.4438890867832,-2.0116199657115965],[147.43785803913016,-2.0587762019759168],[147.20627450789792,-2.1816651092176524],[146.77165494951225,-2.1630398692025623],[146.56744175712575,-2.2100907058661332],[146.53161310738648,-2.1540705634677897]]],[[[147.73608090648668,-2.3155449353872353],[147.83581745983153,-2.2470789031659697],[147.87664301705379,-2.2831515508684173],[147.84440566908785,-2.3355007822142761],[147.73608090648668,-2.3155449353872353]]],[[[147.00609121754061,-5.3069839615917598],[147.01495400599453,-5.2575684302145076],[147.13103148470029,-5.1911119431677557],[147.20613117371659,-5.2516822946997754],[147.22164384419472,-5.3814693439725483],[147.12030969494811,-5.4371459234700543],[147.00609121754061,-5.3069839615917598]]]],"type":"MultiPolygon"},"properties":{"alpha2":"PG","radius":922000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[119.76914103298195,16.008525361022667],[119.77280123351265,16.25507711625659],[119.82665397019099,16.32420673094688],[120.11290963692682,16.161367954140985],[120.24229046913113,16.195742854282869],[120.32100639643679,16.304024137476478],[120.30493325858272,16.649192490075521],[120.41903382877689,17.077115847479106],[120.42446807010154,17.410664260026486],[120.35865646728965,17.638165393273759],[120.59994266197546,18.507673568472864],[120.81382082296749,18.603177468922429],[121.10153265632292,18.615082220805959],[121.60668718895064,18.371325838622624],[121.91045300447661,18.289701601034437],[122.04922204876156,18.342391804690969],[122.1468124601427,18.486380641656719],[122.26230051909577,18.461879919153727],[122.31475453383045,18.320320872981377],[122.18239022774618,18.063472594399588],[122.15374656564157,17.664592645687783],[122.24030988608112,17.435802314062848],[122.51885071095252,17.124737967166819],[122.13500257769805,16.185017787597332],[121.8012035894827,16.082861948367405],[121.63476413215074,15.967732415378407],[121.5832541391063,15.842966341082469],[121.60678683592276,15.669896664935797],[121.42632878472293,15.366361851728525],[121.42895998055693,15.230451031137481],[121.57048819753602,15.000163403759883],[121.7021554968605,14.964963856325385],[121.83996281062402,15.037914700883983],[121.97162088441269,15.046168802526754],[122.05129095452202,14.969902291794257],[121.98948298824155,14.662362797710399],[121.81453010374531,14.657466963314214],[121.69550335671426,14.560249115860609],[121.70769354670287,14.339888636067577],[121.80120046415949,14.238905209239489],[121.95913744646387,14.228493808477815],[122.12991713787909,14.144920261133274],[122.49088775381678,14.32207804333477],[122.63210085805456,14.316320992954173],[122.85590436476062,14.25051271492141],[123.10816415922228,14.010933439052108],[123.22482755079484,14.005866481090168],[123.32034434483253,14.061385076926257],[123.46362771743823,13.9568337053806],[123.90016683133493,13.806266022582328],[124.04608469901392,13.845853163255255],[124.22482027878944,14.077305429709238],[124.41692252652889,13.870939313984305],[124.40373274043218,13.679543749703821],[124.3269055591684,13.567659491983058],[124.17537742994922,13.531839688416747],[123.99146818774328,13.632994143507467],[123.85755625724403,13.615691200099553],[123.75822766975135,13.498824574903374],[123.77087874117076,13.345971944013442],[123.89436736926072,13.132341308049915],[124.14253827585877,13.035542206621621],[124.14041527329542,12.721712005138816],[124.23013540772183,12.595414450732328],[124.56587680991773,12.527107183932293],[125.15015359722501,12.572316836536679],[125.23940168160212,12.527688694740647],[125.37110494433972,12.317879964742545],[125.53536585307018,12.191377524005354],[125.4590104513443,11.914430550539024],[125.49904612127426,11.577337607629632],[125.74886081417101,11.073590849999261],[125.59667853514394,11.125231266116103],[125.1674776929447,11.131312973540684],[125.05485954991246,11.021627502580769],[125.03423011530171,10.811751942297271],[125.16659525983749,10.632010079419382],[125.27972379607957,10.333910074759547],[125.46735893705043,10.288723052376628],[125.66664297304006,10.439868682810893],[125.65651372017849,10.226796333269638],[125.73784962377026,10.013095798871781],[125.90906438720037,9.942800425877337],[126.07381157457866,10.058747076514992],[126.17222886099778,9.7999785625334646],[126.00899115135253,9.6883217048052064],[125.94617442475952,9.4658995371138577],[126.03643387058608,9.3116277953500202],[126.19313834924006,9.2764544135078832],[126.20531482765847,9.0984872710918268],[126.30435308559501,8.9519538149658775],[126.31931124466824,8.8447680709424468],[126.25315627207536,8.6311099747522064],[126.36510420765259,8.4837511066420603],[126.45843819144675,8.2027634406667147],[126.42931852161917,7.8895699428496897],[126.56984906531801,7.6771525353443852],[126.58135506064339,7.2478069962318807],[126.43892228149191,7.0124742857331093],[126.25870426052512,6.8293232862216193],[126.18925555430772,6.3103101034295266],[126.05874619812485,6.7966418304071663],[125.99369040547747,6.8893131005717168],[125.88874156825001,6.9318097126189668],[125.76896132808493,6.9060881101276355],[125.6206553218492,6.9747619537316581],[125.45317862439086,6.8882133205305216],[125.40600653680636,6.6844139746770033],[125.59001932230753,6.4612540564591354],[125.67038373666803,6.22494878586238],[125.66772704925948,5.9787260651009921],[125.45569219417582,5.6644421682504467],[125.35060261840556,5.6014326859076649],[125.28808621284475,5.6324424285765824],[125.16167158440763,5.8440620432310615],[124.92741824320318,5.8755835424421416],[124.40216386545291,6.1180196015651624],[124.21292585714559,6.2334016219749735],[124.08089709452106,6.4009194422644997],[123.98491753197246,6.988801319898843],[124.17972545218932,7.2538865083608268],[124.19184588417072,7.402277644841698],[123.98149003698259,7.6522606213916244],[123.71687294806755,7.7651621604477192],[123.60187780376474,7.7666472148328163],[123.50507647353074,7.7045538088650511],[123.39079102096699,7.4078321368708284],[123.11059538702111,7.5159995735323966],[122.83951032112311,7.3148097751181949],[122.80488427485817,7.3161768293594474],[122.78761213006295,7.4878921197823542],[122.7182946475091,7.5821824782616298],[122.60808498814214,7.6215451010397697],[122.49471779615141,7.5925026343764266],[122.32714097697813,7.3529399852648005],[122.11601709407827,6.876529580581515],[122.15470403144823,6.7250089645420283],[122.32313269215513,6.6023486562203866],[122.19636079690358,6.4805793405604017],[121.98705251302052,6.4147222921007518],[121.80896099996163,6.6137841538751365],[121.98645217328379,6.7995195142631193],[121.98869318615449,6.9141042915593278],[121.90497964674194,7.0799488284505339],[121.92487596152982,7.1993893096963957],[122.0504755941201,7.3816828617045935],[122.13210068679635,7.8103618907887045],[122.33348618315765,8.0251986118317475],[122.89786038363037,8.1629734754749723],[123.09241174609045,8.4772036247271014],[123.32269169961721,8.5620907643846103],[123.43466585100013,8.7030541766332448],[123.78317532415011,8.547502128609116],[123.94983155192277,8.2209049401967373],[124.15782590869409,8.2129965075400317],[124.40127202123205,8.5967456828080628],[124.61852595812375,8.5762087383292496],[124.75231051120714,8.6863389289510042],[124.79610460557265,8.9785879454201165],[124.64182418245967,9.1706829959677183],[124.65351636404554,9.2256398270792612],[124.7367189774391,9.2429260557396145],[124.86037214195875,9.0098707558587616],[125.01155135386472,8.9340622019866132],[125.12445523809309,8.9509273126713627],[125.20980522984956,9.0269115481546027],[125.3837431950293,9.0260290268527239],[125.47906295490708,9.0966213789550565],[125.51723497938191,9.2288457213044914],[125.42105246435496,9.793270694914245],[125.18949458973189,10.020185399818228],[125.02659605082791,10.033369388076327],[124.7594253930077,10.138981339652114],[124.78995640106076,10.302286635788045],[124.73793983010344,10.439768985286779],[124.79183302061683,10.757115648175787],[124.71026558752314,10.886243535217124],[124.56490748148418,10.923522929764596],[124.40867230913102,10.825414077650787],[124.38118491265512,10.632751536522594],[124.17022365714678,10.610624751590732],[124.06795404891106,10.551258556199905],[123.99575142800192,10.342837547715911],[124.02709672655666,10.233454033709595],[124.11302968157482,10.158869798648144],[124.33567270607516,10.15964620970114],[124.59369418389052,10.040217547794597],[124.58410492925543,9.7507204959762674],[124.35979554973024,9.6304321430618813],[124.12246110498616,9.59950587901786],[123.93572936608834,9.6241720796435146],[123.75857418975085,9.7613796388635201],[123.59125697294571,9.7358146621024293],[123.45980685535886,9.4748002254969705],[123.51918200261994,9.336226149860801],[123.69740652102944,9.2371865575252468],[123.70601706466199,9.1336992181870063],[123.61445614783638,9.1035532322311976],[123.38340722198022,9.2009245100494024],[123.13569889870693,9.0659788780816264],[122.9948493954948,9.0590965036264741],[122.84996265926711,9.3186333357248117],[122.56263432857693,9.4829379160624701],[122.41121148139466,9.6939981998006441],[122.42407920723335,9.8918726479795414],[122.47165569667403,9.9612952839436684],[122.70227197986101,9.9921849052549252],[122.80826328045315,10.059342247387837],[122.86222044618567,10.160652554750429],[122.86255866022742,10.303350536122942],[122.73852290180646,10.450804513546247],[122.53851289651992,10.425312694427552],[122.47982152557104,10.56865777863862],[122.36147335559833,10.641196522930109],[122.1896602382933,10.617667759461945],[121.98821228400992,10.458570663816143],[121.93858366033135,10.4710140256882],[122.0904944107391,11.575580313412274],[122.04021769451586,11.697574500892014],[121.89149054930412,11.790958298789729],[122.00686513578138,11.965496728339717],[122.08784941709129,11.858667917471593],[122.55560370589527,11.590152913351695],[122.73101067573405,11.607390493688637],[122.98329396281382,11.520558176556575],[123.15806140106318,11.535356066072655],[123.0951299988585,11.200820987851726],[123.12416692300755,11.085791239781914],[123.21325936392532,11.007448895699318],[123.54005425667903,10.92358243357056],[123.71328675961529,11.030507949952071],[123.74152171565194,11.278940145019613],[123.91539114137568,11.220257069152838],[124.24333952977979,11.311135783070938],[124.30725078076516,11.426548647850712],[124.31201307309264,11.644139209196995],[124.58397330701938,11.746261546640541],[124.64405774061024,11.847533183401922],[124.63674432739846,11.965060191189311],[124.38811236597326,12.23912312115233],[124.27004374944094,12.518604477694041],[124.1684511748537,12.574502566714395],[123.97432509414882,12.582194316487364],[123.81988014499555,12.503922849676881],[123.7797640779208,12.341335422663336],[124.0401108616545,11.966719049293291],[124.06871904276656,11.76118681537206],[123.72539849212434,11.951702874464635],[123.65744134302648,12.062268741143864],[123.51272749511222,12.140949293243455],[123.37903197206576,12.119814536027286],[123.15800407865898,11.926065386556347],[123.25701723556338,12.37629213749055],[123.2692235760391,12.735757200139011],[122.94384742797881,13.101641593721501],[123.14543353814206,13.195920900738685],[123.18344263849212,13.306864354127839],[123.1527223676039,13.420043002802784],[122.94971175830906,13.560768260115806],[122.79844347532888,13.564899398188469],[122.69658117076929,13.479076911120483],[122.67877309378875,13.244141854786646],[122.59990173805687,13.194415646764046],[122.51545180047734,13.260123041370376],[122.42970941256526,13.445477178282859],[122.26287183930252,13.482970764002525],[122.16184140533002,13.425774147216245],[122.00486194200738,13.205245396091739],[121.83031732331534,13.317242302874849],[121.8157600457367,13.419582579958092],[121.94006574069131,13.669066969627101],[121.93999103051293,13.787430870646483],[121.87301645562953,13.885024031089845],[121.75241807079016,13.935239911952701],[121.51994318517667,13.847584325421895],[121.44606974136988,13.711996231042088],[121.26314914739224,13.54363488172339],[121.28683503820587,13.370432694148903],[121.53176460561957,13.125950336116878],[121.47594389140768,12.909548201262314],[121.54038297238991,12.638193295209263],[121.39411945281468,12.300827022164452],[121.23663986518109,12.219042899324883],[121.11718289270932,12.253584265422859],[120.79393304928125,12.75247310850882],[120.68260817976136,13.123780376789369],[120.33875932264316,13.412531766836421],[120.32849756086222,13.623039291528853],[120.10442537065808,13.782523765925767],[120.12075375224192,13.857762446887495],[120.36690644430688,13.683247442248621],[120.56939523765675,13.731027801312628],[120.62800212408533,13.854240178176548],[120.62512767591672,14.277674705341317],[120.56047886855433,14.399702997425882],[120.39628306975976,14.493424117037371],[120.24696368991516,14.740596682650764],[120.08230897543302,14.85118732066023],[119.93302037500469,15.430948821802128],[119.88792954762957,15.840229545370478],[119.76914103298195,16.008525361022667]]],[[[122.01089451602044,12.098154274203685],[121.92351064285208,12.331295725799649],[122.00180694914087,12.598372616864147],[122.09909947708947,12.64823523005829],[122.40901446005694,12.497756021128883],[122.60352301282785,12.491378300455645],[122.67342473065489,12.424149610986333],[122.65431557267945,12.309200365091618],[122.60336667063109,12.285876994444516],[122.38089660408431,12.47565345485622],[122.20609307402665,12.482049914931849],[122.10634171693007,12.387000990545449],[122.01089451602044,12.098154274203685]]],[[[121.79088312821659,20.701352829837099],[121.8455050648391,20.836937035143137],[121.86679768739855,20.838997189152469],[121.84577886760344,20.680038882318478],[121.79088312821659,20.701352829837099]]],[[[121.94768867482064,20.497947533323039],[122.03079865885954,20.469251871763177],[121.94126213970789,20.353960138828313],[121.91434266802308,20.359592486440633],[121.94768867482064,20.497947533323039]]],[[[121.82550987861386,18.842814902128662],[121.88901758289609,18.991377104569395],[121.9432783153092,19.010159521164457],[121.9875586751939,18.956661829722439],[121.85815627165289,18.82320405087216],[121.82550987861386,18.842814902128662]]],[[[121.18505888463335,19.10142416287205],[121.21334603563383,19.183375962896591],[121.33531641152059,19.25957307903705],[121.39168752916481,19.399095987103149],[121.52068499050489,19.361782569833935],[121.53100688060219,19.271562317905051],[121.39336785837283,19.265876538801621],[121.29891981198513,19.20352056205628],[121.24648338933999,19.015604516961286],[121.18505888463335,19.10142416287205]]],[[[120.87662740663005,5.9526871710339089],[121.03775311526552,6.0957128061299342],[121.41439424852815,5.9644495922386129],[121.29445149410662,5.87026422791316],[121.17820645041564,5.9122721242816931],[120.93073585275626,5.8964672220776624],[120.87662740663005,5.9526871710339089]]],[[[116.96974274498258,7.8949805010983276],[116.9937074421958,8.0503376174709356],[117.09591269141285,8.0729309682234902],[117.07962527040749,7.8834755177300959],[117.02832125442237,7.8079167131123777],[116.96974274498258,7.8949805010983276]]],[[[117.22848285516784,8.184809497332207],[117.22876711295878,8.456635233404862],[117.35010332525408,8.7134039028155374],[117.8848793985381,9.2404697870266439],[118.10417610671762,9.3376659867651064],[119.02396945772398,10.353399358939157],[119.22299964133676,10.477967202075785],[119.30540439181227,10.658977310694375],[119.26275797467071,10.84985439465038],[119.46545966522169,11.293725355451445],[119.89123200866911,11.566832978683408],[119.85467026937786,11.393225948113983],[119.6826151449547,11.382208114983337],[119.57604316665469,11.294675763325371],[119.54220404353435,10.922580590546222],[119.66759088983207,10.666378316353587],[120.00521476740191,10.566448549734547],[119.91607553565954,10.486257354228874],[119.68909366479951,10.458994339680151],[119.39241566852991,10.339068130258788],[119.28725807834343,10.249755931342591],[119.19135159400582,10.061296643699091],[118.83567370211355,9.9340795934620161],[118.7711629718341,9.7623065189007718],[118.43793435719378,9.2592841381104289],[118.17233159912296,9.1281222698480597],[117.98608258455442,8.8743911288801431],[117.57731289527145,8.6282472426253971],[117.37473522886734,8.4253577261642381],[117.35504352112426,8.2148085430711184],[117.22848285516784,8.184809497332207]]],[[[119.92645573113899,11.629937527791498],[119.92803881985371,11.777507379744542],[119.86121425477917,11.953908849397351],[119.90532866733687,12.067626799158568],[119.86985634274284,12.243941618866312],[119.91633654992965,12.318828987890763],[120.08452392445527,12.204280338361025],[120.22810757480521,12.21949853901001],[120.34111821504524,12.077366730914374],[120.31438954131669,12.012617567566346],[120.12750275907429,11.945366955618288],[120.03860823647898,11.703478356268134],[119.92645573113899,11.629937527791498]]],[[[119.82174213632365,5.0697300829767622],[119.82759061722872,5.1330007914063041],[120.20785787597977,5.3398225490851514],[120.25012206231841,5.2565608387486629],[120.19154017098349,5.1685730372252339],[119.82174213632365,5.0697300829767622]]]],"type":"MultiPolygon"},"properties":{"alpha2":"PH","radius":848000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[60.844029305787686,29.858291563867262],[62.484814060283057,29.409700566806904],[63.567570812533212,29.497825709609454],[64.063755206241993,29.416775907278982],[64.514048432507167,29.563386264426935],[65.112443509322972,29.563093391432833],[66.155960858918462,29.839204716827744],[66.245655467145141,29.965785900028049],[66.238737719221632,30.109573425581925],[66.304388640340022,30.321228256128375],[66.287208066197394,30.607885804783052],[66.395203149375007,30.907932118415637],[66.59083391083125,31.015965432198087],[66.825232266150564,31.260802739341568],[66.928986800506038,31.305372673766797],[67.262787141491401,31.221412584328252],[67.469081033791696,31.244497470469284],[67.5762378134404,31.348628746942612],[67.578486812771985,31.506407307271473],[67.998189599390273,31.669088300867838],[68.161171238726709,31.802727286139639],[68.403240947709648,31.759704245942309],[68.597598000682311,31.802695163136761],[68.760220664579947,31.671053859621324],[68.868287415003408,31.642383055601712],[69.067147777126564,31.72813177220447],[69.219510926040513,31.872922543249942],[69.273380170216058,32.003481199760039],[69.241614769690315,32.433504624179655],[69.384032885066816,32.639766023719588],[69.501795438924006,33.019902689715984],[69.937319048485847,33.122187337609745],[70.153758714632431,33.236170791239871],[70.218322310330848,33.331534717162931],[70.219594977369866,33.446691638689927],[70.13343176064889,33.62027280780412],[69.868336188596857,33.897742735473798],[69.889852422829293,34.007109701303285],[69.994741080616691,34.051556411873591],[70.334120588955514,33.96013080493492],[70.63965587130275,33.952183195373358],[70.86471879850572,33.987320997265492],[71.045084845696849,34.081035875665577],[71.09037111622122,34.308512289819056],[70.96592092276957,34.530240417688603],[71.466533711112476,34.984195697107182],[71.568660035354142,35.186399811906227],[71.568000293127213,35.529671760478351],[71.396920173578067,35.879677063047346],[71.185330654389418,36.042112088682877],[71.620576195259872,36.436238738633996],[71.77830737142115,36.454237171883825],[72.245893161178145,36.733294610232264],[72.622881397646097,36.82941100832516],[73.121405226094438,36.86876033554983],[73.769117164625783,36.888275760657102],[74.029881968869688,36.832460269725019],[74.600606012368701,37.036496269990451],[75.341976318107655,36.91487184222278],[75.461269327176737,36.786001948182559],[75.844355266365909,36.645194745180149],[75.972906211252337,36.387480683298477],[76.005161280136861,36.041827728443778],[76.231854190648775,35.830563103846451],[76.551074242431966,35.886800028635683],[76.611147853954094,35.750010046066024],[76.766719846382841,35.661532222600712],[77.048342892466664,35.109896008877797],[77.000679577353353,34.992181128375606],[76.599121135752284,34.738190356182585],[76.03262135880604,34.660181764963106],[75.709144121681547,34.50327101859159],[75.1766048801599,34.634854258704898],[74.330936757120611,34.759208325904424],[73.946277627401244,34.6214988864334],[73.867237331679547,34.487889348354919],[73.876767200668894,34.35469096643439],[74.038145160455542,34.061615728467068],[74.246282794339621,33.990027329408484],[74.250354744560653,33.951352838950044],[74.06958082512466,33.727882747559207],[74.14974495642889,33.506941962690178],[74.056970155429241,33.273400773088625],[74.08428354114065,33.1257753238052],[74.303408028886992,32.991644892502286],[74.41355120874114,32.797747158890672],[74.663019032590952,32.75751698665065],[74.663891335921974,32.591226289098728],[74.746939156292839,32.489054149094493],[74.991614383031305,32.460662963912903],[75.233568613545998,32.371898004707816],[75.33324084492412,32.279130751662876],[75.253957608762974,32.140563122311484],[74.75028189483055,31.953411242584153],[74.556177075766158,31.806991193058575],[74.527820242647593,31.691441927278355],[74.593705393776588,31.465390160303475],[74.544341704633041,31.241891149997308],[74.632529286132964,31.034791054592844],[74.300214775381193,30.852942988583255],[73.962204038342165,30.47861212273444],[73.914566014752083,30.369604670747449],[73.933134824100762,30.22213990969388],[73.883117568783874,30.158988580230492],[73.393339840741021,29.923027258470338],[73.229086499049046,29.546883678190756],[72.906203241847692,29.032572672181626],[72.317330255746541,28.725008578393219],[72.176009467685589,28.41702959288822],[71.940595196318242,28.158651816386381],[71.870121114329777,27.962675885893585],[70.797976208912019,27.709836470515071],[70.691771521539351,27.769140589529766],[70.570747540526639,27.978385409960346],[70.373245763125652,28.008353076332313],[70.151884483117527,27.854723948683482],[69.896079776599535,27.473813435429079],[69.552197945406803,27.148291533896479],[69.485531188787576,26.893272703904366],[69.53442260733766,26.748875508698582],[69.728485225818247,26.631870665840527],[70.05931652653156,26.578555250943413],[70.147413503353945,26.506323961006675],[70.078144290363028,26.033759435186209],[70.126729875906506,25.877221283691124],[70.297614503650834,25.701018981313975],[70.569532979126649,25.705685941355153],[70.648202924098015,25.666792618294174],[70.668919315123958,25.394527850642501],[71.047570497475832,24.687795402384086],[70.999249095441272,24.556903112618443],[71.043822939773477,24.40022442516166],[70.767229960738206,24.245603049973514],[70.655643511951922,24.247667850122543],[70.409426302310536,24.380968057603646],[70.13993672783532,24.301634906707605],[70.020957281639596,24.191784090272566],[69.929313342991136,24.171172618272355],[69.71628855352894,24.172836725998167],[69.521157538366367,24.273014318584902],[68.903858779851276,24.277289488355294],[68.752163004215802,24.176202861752834],[68.723894121772162,23.964930485778282],[68.292317920268545,23.929303305042321],[68.115530316797589,23.753744546248857],[67.910490513917352,23.838255476588145],[67.668565877665571,23.811229436576678],[67.309651140790379,24.174897552943342],[67.161760224307656,24.733101887519258],[67.061980317680707,24.797618191257541],[66.703216421030604,24.861151898661156],[66.670572924156374,25.257927154854787],[66.514403337254151,25.422556067896501],[66.319363338555164,25.46486329854767],[65.679661323520961,25.355494064270179],[65.398002325734709,25.37209404773408],[64.821209433200465,25.303972205772219],[64.658922873124922,25.184333963797254],[64.085838137505107,25.358283776322995],[63.720893010580056,25.381904942685054],[63.56135609840473,25.330180149265004],[63.491202506387481,25.211097053520511],[62.664742657506352,25.263549179520705],[62.315387231438599,25.135166814334845],[62.193751626338297,25.1811390836023],[61.903576914139336,25.131480453314534],[61.567289009119861,25.186445650282025],[61.662101262919144,25.751239945100558],[61.756086699147851,25.873891331333972],[61.842597787169424,26.225723351935486],[62.213500255564945,26.393468273885755],[62.389650691054904,26.544186611398818],[62.75756285326316,26.639970788933184],[63.091804381537301,26.667503069329651],[63.220209677427242,26.894526008145895],[63.231817552976779,27.064018101897076],[63.120529798247496,27.21314290212057],[62.752978510202098,27.265663300545278],[62.809004458102521,27.527766767573535],[62.700121578412244,28.159546780650444],[62.36067581877515,28.408895822619204],[61.88640794962248,28.549690321916344],[61.619831375955457,28.796297579090226],[61.294226587106991,29.395887719161248],[60.844029305787686,29.858291563867262]]],"type":"Polygon"},"properties":{"alpha2":"PK","radius":966000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[14.128881012526991,52.878325305619597],[14.142318686955127,52.935977521483316],[14.359608379261651,53.098043184221076],[14.41159265978016,53.22357232458662],[14.211537144408151,53.950045886415211],[14.394728357283826,53.928931357738264],[16.154698876161387,54.286149380425051],[16.559868085306455,54.553526023938481],[16.875218118448167,54.596043590069762],[17.269183783793682,54.730631473819045],[18.323393125897695,54.837894032144263],[18.759153650608262,54.684401683164118],[18.818972562426698,54.433806797126437],[18.976310767299065,54.350286150568039],[19.406888979744483,54.387288725929466],[19.604422791918015,54.458913781827896],[22.169829911014542,54.359839483715511],[22.712840473907882,54.351471077245904],[22.893896517884166,54.390258386440458],[23.453437217366435,54.143312030343949],[23.592487027665911,53.618411188568011],[23.884922679189117,53.034160316249107],[23.901074612229923,52.703742594196001],[23.425715606566992,52.522767595652397],[23.334472756704347,52.341384305577733],[23.40064706732112,52.205614631782012],[23.652193029955541,52.040309443379797],[23.563222989196316,51.597501764665843],[23.717702067968641,51.265313955709452],[23.966815555746567,50.96112099495997],[24.095612371038552,50.87260096320091],[24.08971341982306,50.530607394538791],[23.972533700024336,50.410260104178569],[23.714777717214531,50.370116599578239],[23.401124817606394,50.168688420120326],[22.719409662065502,49.612545120271257],[22.679290197345907,49.463847039278775],[22.748432872907006,49.181520143776702],[22.848278757190357,49.076759798650599],[22.809714651684946,49.021024588647485],[22.024984097279688,49.208019464306481],[21.879639071829974,49.34465513585176],[21.35573822908249,49.428454804116321],[21.104818303987379,49.41023313181541],[20.942653644613141,49.316949826997238],[20.626634251060448,49.38852206760032],[20.352135969906517,49.38033461953615],[20.153001584657265,49.304504926049262],[20.057509955163123,49.181557840662464],[19.797027367887029,49.193714097103211],[19.708600892934555,49.351594634809999],[19.478277295588281,49.537980786225695],[19.279816053561991,49.516955825491571],[19.14929920720531,49.400251670440369],[18.968512833461507,49.39647910893747],[18.482652987180622,49.89876729491553],[18.055523627871736,50.003254841318913],[17.831335623906902,49.983508980160565],[17.520733796626917,50.22473638557814],[17.235650138980876,50.337926172860044],[17.097398772232683,50.335841658650629],[16.98948467560303,50.237093994465361],[16.639237605572941,50.102421624592054],[16.467551389162093,50.262545277158246],[16.230928687592581,50.394307062231711],[16.129632994090983,50.572826872060304],[15.915484529630083,50.670266905660768],[15.389462327805155,50.798276690372589],[15.160306516616073,50.947017389673718],[14.80970720629219,50.859219042865682],[14.963202237905579,51.09540449471622],[14.997293875200006,51.271182661024518],[14.903445053970662,51.446640804244851],[14.722090164117965,51.527876491188508],[14.603272579056849,51.827914132051696],[14.714013913448685,52.060817352088229],[14.66847529485797,52.218089314547406],[14.554856051009263,52.35969373441219],[14.56836680806753,52.554704936251682],[14.503761233458995,52.646944962510119],[14.128881012526991,52.878325305619597]]],"type":"Polygon"},"properties":{"alpha2":"PL","radius":425000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-56.386665133629272,47.067967051614119],[-56.377755337786027,47.089411491279293],[-56.364613975028561,47.098720706711546],[-56.287550081995107,47.070818734112173],[-56.278600659318137,47.03500832002667],[-56.28124315565389,47.015812193784271],[-56.282016931514619,46.996605338140704],[-56.280943253555648,46.977412911289569],[-56.278032039931674,46.958412204249278],[-56.273310183126505,46.939778737016709],[-56.266821301532339,46.921684637192392],[-56.25862533652267,46.904297049942983],[-56.248797998740791,46.887776593989337],[-56.237430068718879,46.872275877882892],[-56.224626558288243,46.857938090276427],[-56.210505740527147,46.844895677211603],[-56.195198057207278,46.833269118641915],[-56.178844913831249,46.82316581549302],[-56.161597373392048,46.814679097541202],[-56.152432061296707,46.810704326225249],[-56.137606261553898,46.801435593573053],[-56.138817190145424,46.783960875639295],[-56.139529433294456,46.778760875622531],[-56.14232355646778,46.774317884944089],[-56.150914181018173,46.762621492655761],[-56.155533258220899,46.760208954650217],[-56.171712140455547,46.753112202336098],[-56.176799580001649,46.753858803688942],[-56.2383477898141,46.766202032669042],[-56.24317562702317,46.767424642870388],[-56.257150172140584,46.774997201021691],[-56.273859496352948,46.782296717642367],[-56.291164186493972,46.788044078571737],[-56.308920404050376,46.792191511105123],[-56.32698055736266,46.794704541321032],[-56.345194528423455,46.795562280632197],[-56.354115297350212,46.795575273063307],[-56.358225552378656,46.798489830371558],[-56.384506917815081,46.819519660776251],[-56.378614452152526,46.842520463268485],[-56.376982197320437,46.84755490668789],[-56.370341978891915,46.861004748504499],[-56.362964967642377,46.879750144338821],[-56.35751108600617,46.899142548394522],[-56.354035664997774,46.918985219806807],[-56.352573963646073,46.939076849634169],[-56.35314081128255,46.959213603188765],[-56.355730457093948,46.979191187991056],[-56.360316628465753,46.998806926368943],[-56.366852797524508,47.017861811674209],[-56.386665133629272,47.067967051614119]]],"type":"Polygon"},"properties":{"alpha2":"PM","radius":25000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-128.34990928975714,-24.340324610909047],[-128.34629109752345,-24.336928616333715],[-128.33385617819494,-24.326399779530025],[-128.33008251629633,-24.32350875667343],[-128.32557511188057,-24.325019333621213],[-128.30831734021527,-24.331757174492374],[-128.3038035691315,-24.333787599226962],[-128.3016898060115,-24.338262949433908],[-128.2925745576994,-24.360390040756421],[-128.2910298016748,-24.364692338929888],[-128.29071722844779,-24.369252858497738],[-128.29019967876599,-24.392370077618768],[-128.2903353353195,-24.39729287145401],[-128.29281169655536,-24.401549623369569],[-128.29735457190134,-24.408531776610666],[-128.30006809903401,-24.412290246223122],[-128.30411505704907,-24.410029393694558],[-128.31668548306368,-24.402184021430113],[-128.32050450836903,-24.399544643759572],[-128.32344306160121,-24.395950735904488],[-128.339343363881,-24.374534002221502],[-128.34198029897001,-24.370603591607704],[-128.34340029250777,-24.366088597409441],[-128.34889494871123,-24.34518210063537],[-128.34990928975714,-24.340324610909047]]],"type":"Polygon"},"properties":{"alpha2":"PN","radius":5000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-67.263749264258124,18.364514245876318],[-67.194358093692145,18.422663704274292],[-67.158455418231171,18.499053623241668],[-67.059617523576435,18.521984272080861],[-66.803613911053176,18.492280437741311],[-66.063940054509786,18.468145197339396],[-65.878816809471516,18.44360444047231],[-65.755386722621978,18.402332399973144],[-65.629023618140494,18.381202145034788],[-65.607427048066739,18.276166967457844],[-65.544767375316013,18.201416368904443],[-65.473572397612998,18.168784949881985],[-65.366255679221879,18.160780447800679],[-65.295258706576035,18.133500451305061],[-65.503989854186983,18.099860029745845],[-65.554980296905612,18.107894186479747],[-65.626416686321633,18.139163793297516],[-65.703111835329352,18.14096346196526],[-65.774842232712786,18.113758043757805],[-65.834298452663219,18.057506474572666],[-65.970884986898028,17.97459749589672],[-66.129068620246784,17.950435024505936],[-66.339178794668612,17.958561736287471],[-66.408521590981806,17.950844942842132],[-66.53810100218314,17.983367024911672],[-66.741399653947838,17.984022574683959],[-66.900002635615024,17.948176118605417],[-67.022638918685018,17.966955441508986],[-67.142330357364258,17.966924188520434],[-67.196632845403016,17.99431583899667],[-67.176708715765685,18.205635073338787],[-67.194667009641549,18.260889928707616],[-67.263749264258124,18.364514245876318]]],[[[-67.936807239167422,18.100639250819018],[-67.918874739395307,18.120614541422867],[-67.861104004304295,18.122365431113074],[-67.843898440512589,18.110917471062841],[-67.877081895041329,18.059421133655817],[-67.936807239167422,18.100639250819018]]]],"type":"MultiPolygon"},"properties":{"alpha2":"PR","radius":107000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-9.4745258524404417,38.852874052895686],[-9.3744677698029477,39.338208794155371],[-9.1543149832465254,39.536781545264013],[-8.8895671210206277,40.025776668457866],[-8.8711559911579361,40.264268995332223],[-8.6879507827701943,40.753385948779261],[-8.6627637654082772,41.099877281230597],[-8.8873754631632931,41.764652609258832],[-8.7769801656485775,41.940873438390234],[-8.5834007827445458,42.054745930508382],[-8.2660336139164947,42.137202518431984],[-8.213258003133264,42.133453936771652],[-8.0727223348658637,41.932202625376249],[-7.966405489645461,41.884137325893413],[-7.4037655185564111,41.835189490206353],[-7.2744754852581321,41.871872127185824],[-7.1471251472855482,41.98085987384794],[-6.6183255160355543,41.942136899833379],[-6.4882726383242666,41.711813516019838],[-6.2433313141325613,41.601606814025843],[-6.2127923430323957,41.532136105337273],[-6.2895593563916607,41.455238543178645],[-6.6679048988175387,41.230467965317047],[-6.8403242526155301,41.040389083711332],[-6.8670008980147195,40.926386225604134],[-6.8104353205266808,40.343189147461914],[-6.9298575734338828,40.191034121942593],[-6.9113576109217378,39.937164750454642],[-7.0368726476219559,39.714113072556934],[-7.2472024619906783,39.64646013860802],[-7.3266886002760643,39.534915851356338],[-7.3091010813738579,39.355815235093203],[-7.2172696455849445,39.203468688703026],[-6.9981738675392355,39.056374502886086],[-7.0462634927658314,38.907189283641401],[-7.2674191639958687,38.706624702691947],[-7.3074299145528725,38.43957634195155],[-7.146728522803607,38.233825202483402],[-6.9578537349541731,38.187758869872333],[-7.0230136956022093,38.044926430952039],[-7.2124735966144389,37.979934828601934],[-7.4614244487577359,37.686378865999842],[-7.496905313152709,37.539777543902801],[-7.4064407808236883,37.179641411703329],[-7.8341751023978281,37.005906976885399],[-8.5871351839951569,37.115228051728295],[-8.9974517113988188,37.032429020043296],[-8.8187421972824165,37.431676051144905],[-8.7973728754127283,37.752999482600586],[-8.8786833803618457,37.958725123576961],[-8.8143055606892879,38.298859197713661],[-8.9473575481960825,38.456951029510691],[-9.2130126450319132,38.448261309113789],[-9.2424090115122137,38.615586787192647],[-9.4739148655060124,38.731016665920009],[-9.4745258524404417,38.852874052895686]]],[[[-17.240767865320837,32.80733154530683],[-17.19079836519785,32.868335097960845],[-16.929227655738231,32.841137873434022],[-16.693818753010866,32.757894721496967],[-16.837481088859683,32.64853454236011],[-17.013465214474277,32.6624083678402],[-17.171048975468519,32.722119331527722],[-17.240767865320837,32.80733154530683]]],[[[-25.198073256103246,36.996472372693397],[-25.082976765082751,37.023754521719766],[-25.031721932123855,36.941809240142213],[-25.159789841415137,36.943623382728582],[-25.198073256103246,36.996472372693397]]],[[[-25.847616832147082,37.872436434541669],[-25.783764693532934,37.910902937247378],[-25.611530381373335,37.836856710174459],[-25.182186749509842,37.837693003411722],[-25.190948562553327,37.764516317317543],[-25.247210719873095,37.73691058092664],[-25.439008306980746,37.715526197298153],[-25.734379403491115,37.763135897823865],[-25.847616832147082,37.872436434541669]]],[[[-27.385636687241053,38.765719684551577],[-27.127087707862227,38.789574134849083],[-27.042166226510801,38.741081756204395],[-27.042209557160568,38.678992341350217],[-27.095348084309791,38.634261213374472],[-27.302734510684228,38.661307953774369],[-27.385636687241053,38.765719684551577]]],[[[-28.841578915578701,38.598346599889624],[-28.697786850174332,38.638216932384658],[-28.57165271130885,38.562536480917927],[-28.450737595198177,38.562093984767806],[-28.353554441450274,38.628769373127042],[-28.310429363366676,38.743597429213146],[-27.779126344698899,38.555669415050446],[-27.948503320456741,38.535967377525424],[-28.064960931512733,38.412961920759798],[-28.231175061405544,38.384937790912851],[-28.454428774216776,38.40885582918937],[-28.841578915578701,38.598346599889624]]],[[[-31.282705070868595,39.394189991152473],[-31.260636190161154,39.496604583406857],[-31.199881865772486,39.520591330105589],[-31.138843454725315,39.479323368892572],[-31.181423540498066,39.359178143325217],[-31.282705070868595,39.394189991152473]]]],"type":"MultiPolygon"},"properties":{"alpha2":"PT","radius":316000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[134.50646565667378,7.4371364080575413],[134.51515581682156,7.5204751654524378],[134.51599627948795,7.5256906273050612],[134.5184351577808,7.5303767016223926],[134.55612435246371,7.5937821770314873],[134.57054075088175,7.6030467858911024],[134.58662060178582,7.6156256151081347],[134.60133434329637,7.6297781541145682],[134.61452866243886,7.6453569375052739],[134.62606607818481,7.6621996388169151],[134.63582637396487,7.6801307619232313],[134.65107890586785,7.711442608026589],[134.6586246181881,7.6687794727439265],[134.65925736292627,7.6632803086645662],[134.65865771171116,7.6577774376058043],[134.63363143689716,7.5068462493188823],[134.6324164122463,7.5014213719894265],[134.6202116590066,7.4784548869900833],[134.61229501027358,7.4622904095242921],[134.60586366826669,7.4454796540714687],[134.60096972101317,7.4281587724777465],[134.59765280503947,7.4104680481475382],[134.59518337107122,7.3822005541132878],[134.53480193131668,7.3609304075447266],[134.50646565667378,7.4371364080575413]]],[[[131.13521410481283,3.0254213935916736],[131.13693224319638,3.0393479998555946],[131.15168133058265,3.0539036358813303],[131.17236223369326,3.0603260590397676],[131.18763810548751,3.0554524145657229],[131.18608698548485,3.0421970486085526],[131.17224836435557,3.0264219095602365],[131.14961445565686,3.0221469966752137],[131.13521410481283,3.0254213935916736]]]],"type":"MultiPolygon"},"properties":{"alpha2":"PW","radius":21000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-62.650718471884275,-22.233675959029807],[-62.280766783163429,-21.075641227272992],[-62.27604860267256,-20.562587865890496],[-61.917939158034756,-20.054855172309072],[-61.756678048103446,-19.645508539132301],[-60.007348792651676,-19.297783496715645],[-59.086634671181734,-19.288503188983405],[-58.160251576095618,-19.854946414069101],[-58.130334210051451,-20.265310205937308],[-58.002461543989035,-20.465464441706331],[-57.997294604654911,-20.579255243472595],[-57.89338361144523,-20.742700622909613],[-57.830532099997527,-20.998008836738435],[-57.827155753393924,-21.133552372543896],[-57.936589264109401,-21.511733654086182],[-57.944925969673491,-21.854502320346061],[-57.8662704888904,-22.049133778810383],[-57.548470339540181,-22.179951809253872],[-56.919477305972499,-22.269247112575155],[-56.642150259542063,-22.216163715655124],[-56.447798137611997,-22.07646292003874],[-56.203828144051116,-22.271712697596175],[-55.849255196047906,-22.307833574299913],[-55.617918495859421,-22.671533346611557],[-55.650779462378232,-22.865349705789871],[-55.519354291008625,-23.410533161369717],[-55.536588737230829,-23.560673981944319],[-55.387030529649039,-23.946740259227489],[-55.162959019748534,-24.011948975650963],[-54.62549545134916,-23.812789343343791],[-54.242179842005356,-24.047304516148326],[-54.304536221112102,-24.160629365507198],[-54.281683687057274,-24.310768388262979],[-54.436466035091414,-25.121283346657531],[-54.606415857883178,-25.445271376193144],[-54.632775497572652,-26.011331926770723],[-54.757628922622196,-26.537233422122871],[-54.825650555222651,-26.652024290032912],[-55.136115389892019,-26.930983654188552],[-55.414350354650651,-27.021970373469642],[-55.714727403916505,-27.414603669242659],[-55.951781012587709,-27.326854603458415],[-56.144263536861281,-27.319610246709434],[-56.437094742553043,-27.553555025695516],[-56.572332467298047,-27.475613880694468],[-56.800365298389842,-27.485156369302398],[-56.944587155728847,-27.43714559980403],[-57.116586290960193,-27.469440418954722],[-58.146926512365383,-27.276023940269337],[-58.604671543533136,-27.31411148591673],[-58.618380381027372,-27.13230233912849],[-58.357960059897088,-26.862357222645471],[-58.209562147665125,-26.622788284159686],[-58.202840363220119,-26.381474114098662],[-58.110946343021588,-26.180259942018495],[-57.924326583765783,-26.03060137697905],[-57.754549698612209,-25.697177547334519],[-57.610601424440887,-25.558345677612309],[-57.591011718901349,-25.441332163753227],[-57.637355517789494,-25.337255591417154],[-57.960169038429584,-25.049907833169396],[-58.155091052591352,-24.973412644108983],[-58.360548143296434,-24.960967337486345],[-58.500249370364145,-24.853132572660815],[-59.183771259719691,-24.563992075590704],[-59.892833829435538,-24.094270162928218],[-60.110990719868319,-24.013210934309107],[-60.510194213083643,-23.962053351923636],[-61.032726032462818,-23.755481153888852],[-61.192258928974432,-23.570004025111746],[-61.803221964266648,-23.177588325658423],[-61.930704012130356,-23.055610364658097],[-62.229019815862976,-22.596145152166017],[-62.650718471884275,-22.233675959029807]]],"type":"Polygon"},"properties":{"alpha2":"PY","radius":635000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[50.754812820403579,25.39925883388899],[50.763095323031003,25.444636368238285],[50.867607928332738,25.613152066509002],[51.003292653053244,25.981332770540295],[51.108262720582587,26.080335881604942],[51.262246102217368,26.152983911645691],[51.389930558826613,26.012105404173781],[51.542836766003475,25.902236807762073],[51.572031094059405,25.781028638956123],[51.522564451729892,25.665522001763115],[51.494368730543556,25.525036637536289],[51.520833523016272,25.390096629770561],[51.600417714376114,25.153121517671032],[51.608606423080971,25.052904993812231],[51.586705571607915,24.964940742852839],[51.534127990333566,24.89043739006906],[51.427737807755427,24.668419883898487],[51.392313837148251,24.643887576036104],[51.093335594996653,24.564840246675139],[50.966082288234652,24.574119096138922],[50.925479317102287,24.598422135775166],[50.855838077626821,24.679751136932737],[50.804674981573413,24.789252361957189],[50.837640551897088,24.908928530410247],[50.7766615001348,25.184092610407916],[50.754812820403579,25.39925883388899]]],"type":"Polygon"},"properties":{"alpha2":"QA","radius":98000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[55.233045024917701,-21.058380291398787],[55.25017095337283,-21.002519356911005],[55.311463469933138,-20.904282175510691],[55.450507253658422,-20.865353823626979],[55.596428640261784,-20.879814865950266],[55.661754847900163,-20.906445447593896],[55.746685695050409,-21.03030463015315],[55.838790554526824,-21.138656512399166],[55.822266054686843,-21.277781647914324],[55.797194655000773,-21.339153365532734],[55.656138614586062,-21.368764789801283],[55.562608488917526,-21.358844927093347],[55.36284994740884,-21.273414165995611],[55.31055269962507,-21.217244732180021],[55.233045024917701,-21.058380291398787]]],"type":"Polygon"},"properties":{"alpha2":"RE","radius":38000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[20.24218148840319,46.108552479431488],[20.646699414370417,46.160044597639477],[20.765359654204953,46.24713666340525],[21.111575927551009,46.284999868707651],[21.317091169824202,46.603512994444316],[21.426938455443896,46.666248695537888],[21.620720499787016,46.925341268922935],[21.661648339233111,47.043738337015967],[21.782149536145731,47.140750187951276],[22.003309846606893,47.508002538629974],[22.290703358619425,47.727629055455907],[22.644065177403895,47.78337767641829],[23.139536099509442,48.087143888316319],[23.418282108039218,47.993600066057468],[24.178188877685198,47.909426358454034],[24.5741727402667,47.931856605472319],[24.957062600237091,47.723512589522258],[25.464302545741511,47.910561728785673],[26.13059152873987,47.99310939431227],[26.251832297659579,48.083081404887253],[26.305843361500802,48.20355802968497],[26.720832291078739,48.262740272132724],[26.98052926902141,48.154796387247032],[27.332065615770809,47.647497623449581],[27.847979261464047,47.121794909038925],[28.071587290191175,46.978260791576567],[28.23918758490958,46.640771768382464],[28.244089391405318,46.451274767266412],[28.101580387600912,45.981644958731046],[28.156555518619637,45.556880229221214],[28.221499157638409,45.441599054999244],[28.353862919376734,45.332239560803153],[28.630188520233713,45.259527513633941],[29.403688237840299,45.419448049954596],[29.567565480255094,45.370567528408927],[29.70562200482982,45.25984669719476],[29.557368214442668,44.843641517482951],[28.981997337130682,44.73125063281946],[28.8515888651546,44.506238150738909],[28.684594676404643,44.352866540912039],[28.658354668253448,43.983874867760385],[28.58514443087633,43.742462127007073],[28.217198033069849,43.774227554131983],[28.050116759926091,43.822615742771823],[27.870976600746751,43.960904788352067],[27.425437152948707,44.020740726418872],[27.044778416720398,44.162556065584035],[26.232939706976737,44.012077992182668],[25.813575518281731,43.764657707676797],[25.497054633605192,43.670987669180533],[23.243997172618659,43.87260642322434],[22.919088814320592,43.834697962812449],[22.856662548635875,43.899031884587096],[22.848443528558143,44.12874898482815],[22.530807354885052,44.378108421129504],[22.408961086684954,44.555527355207829],[22.296825931782099,44.587447489803964],[22.093151760838332,44.542178939134445],[21.93632078194587,44.655486668813701],[21.640755892861741,44.709126479997352],[21.360284524013689,44.826852633549201],[21.314683833966427,45.177912339263237],[20.777881894513474,45.481365192681494],[20.716511963212881,45.725084380563224],[20.56576476796722,45.878127125885811],[20.358756591472613,45.975695417604769],[20.24218148840319,46.108552479431488]]],"type":"Polygon"},"properties":{"alpha2":"RO","radius":383000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[18.84287241603381,45.838923165265768],[18.90556068245607,45.931508404549689],[19.066338245092442,46.009330673294862],[19.258910479978507,45.99779466289872],[19.530867641190209,46.154960591973655],[20.166868693453395,46.140122039801],[20.77478891852806,45.749676788756943],[20.789793744642321,45.525390696092138],[20.942547191284653,45.366417162743453],[21.469426359458325,45.168012074937927],[21.545632134986413,44.831468900602076],[21.640763325868157,44.717719289725295],[22.126408519085338,44.577349636792533],[22.497637476898799,44.706020608878873],[22.720707569687946,44.605355254129137],[22.645439665942895,44.41317343448646],[22.704754330879094,44.237893301557051],[22.597209277579694,44.07543081523194],[22.41372028106986,43.96947769446372],[22.369458105379564,43.798966089467164],[22.534378314518811,43.47931264121334],[22.976601111999766,43.187868862357739],[22.942103983125133,43.097227207985149],[22.706044039793834,42.884170612962194],[22.579232987362918,42.864515621305195],[22.49280981213975,42.789846397671432],[22.456511604858232,42.633619151478719],[22.53219518125157,42.481186494101429],[22.421925916303298,42.329093848533873],[21.913931762967309,42.320875927411763],[21.562647553771026,42.247748717978347],[21.519227801151004,42.328413946395528],[21.63684553371754,42.474956347247293],[21.61864707753309,42.629925285986623],[21.532216653271465,42.710869647169467],[21.390819371410114,42.751607954904841],[21.338718144532841,42.851783177931374],[21.065963532823787,43.083909337650617],[20.865191346702261,43.159301269856158],[20.749919869419294,43.153051014759491],[20.539103428409131,42.977695798271284],[20.468698151137421,42.858153280194408],[20.344478456970222,42.82819613096536],[20.25284211770969,42.936795496978547],[19.614536312875224,43.173658848043416],[19.218907645931992,43.450132724704993],[19.194526216879147,43.533185895415002],[19.334085622099085,43.661576284786939],[19.357960895038968,43.791263933215639],[19.24532382767449,43.964995504553038],[19.302536208935628,44.075929364690651],[19.287393174856366,44.190277848626138],[19.118693082259085,44.35997978930903],[19.202039662624017,44.725137627608319],[19.137073491529847,44.823859122611076],[18.995765561944239,44.903949173567597],[19.070616386052894,45.029940888152964],[19.080671343457261,45.294380477865317],[18.84287241603381,45.838923165265768]]],"type":"Polygon"},"properties":{"alpha2":"RS","radius":268000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[178.62858907152838,71.0473145464358],[178.89120667310405,71.230927083391506],[179.54770343478205,71.447465543568953],[179.99976401477784,71.5374763357094],[179.99978285859495,70.993205302156923],[179.64761771743375,70.899140574602995],[178.79267871961173,70.822299274496046],[178.62858907152838,71.0473145464358]]],[[[27.352198685962776,57.528338350604272],[27.400209316649214,57.666604445224976],[27.587363921894205,57.836883728054531],[27.637755452767362,57.960002790220265],[27.488049426500549,58.270066527420653],[27.524162613490134,58.445034419685364],[27.434395690699532,58.787184119456143],[27.74812815870941,59.046016273183348],[27.983995254469331,59.353673239410739],[28.048468018394267,59.58059551511078],[28.014221406213643,59.724721674145982],[28.131073110226072,59.786258984946841],[28.335074814277565,59.727403146788632],[28.518182279671681,59.849318693985801],[28.906192406129311,59.821088366626647],[29.039547404325223,59.92657928674064],[29.08945353261797,60.034168943480552],[29.02017177976612,60.213631379430922],[28.643272192409043,60.375495661382082],[28.409311822340559,60.595817436886641],[27.798541424085972,60.53641907297407],[28.411459076911679,60.898847290101358],[29.263920155938962,61.295760725140049],[30.941956141733865,62.327681756849039],[31.284991206066628,62.568440812904278],[31.457376240933286,62.823519037239002],[31.426768692731891,62.995333037070893],[31.163585979238277,63.215140942461908],[30.413187185256273,63.506786278136715],[29.99185074644188,63.735180916427375],[30.208954097789892,63.806088205067326],[30.404795281417719,63.940101426992946],[30.489736741537104,64.106962003985117],[30.399413862580012,64.270972007760108],[30.108233616968992,64.366310990863568],[29.986839520088303,64.524338926433174],[29.94935624208518,64.723174777689493],[29.701839887536195,64.845995892296258],[29.604481179077339,64.968500104336158],[29.642992836965522,65.122494676716769],[29.608279496003288,65.248654658904243],[29.716403051054833,65.389378350955411],[29.716195139771621,65.624533443631151],[29.946819414586709,65.709494576811565],[30.00021414921336,65.907403233034145],[29.886879542032602,66.103937288097612],[29.066493893651554,66.891818755994592],[29.087196550596545,66.970805374185446],[29.241459777675146,67.095055590301556],[29.807026671648266,67.466728669416469],[29.864077912110623,67.566728302018831],[29.856548095049273,67.681611165901685],[29.786932370401679,67.773308611017086],[29.341876659985704,68.057401849385357],[28.685303237089435,68.190034612547919],[28.470931135108707,68.488425804200176],[28.555859606100206,68.723570414907257],[28.414599457394345,68.904006804012411],[29.138031556738738,69.057981746691709],[29.388390800031658,69.297886296446976],[30.013627957425513,69.400960461228834],[30.130799574154995,69.488611335822412],[30.174938767246612,69.63431133727056],[30.710784364124759,69.534081078753601],[30.849559504160162,69.630276221377201],[30.870031699642599,69.783201730202464],[31.558911008395132,69.699333986850434],[31.889156448593727,69.843312530137339],[31.989502764657086,69.952632542825896],[32.945932649561826,69.749951362867876],[33.007601322367115,69.721968286920188],[33.017578717261522,69.55541397477684],[33.100472019473514,69.451920352733438],[33.454129759987559,69.427979515147129],[33.586365266187499,69.311845600622405],[34.233772139501689,69.312772564467167],[34.999132715259108,69.221730262414468],[35.289847456315883,69.275155835734239],[35.862592986057159,69.190588466105936],[37.723674999723841,68.69406753965427],[38.46372097061785,68.351796541638294],[38.831496992712871,68.324717200480805],[39.557115190556644,68.079913379068685],[39.809191460169615,68.150540700673915],[40.390101618710844,67.830052924490673],[40.966229945823322,67.713262896839751],[41.169641506762439,67.295916394312883],[41.3585594919901,67.209468716268333],[41.277422780088152,66.919140316964075],[41.192201174610226,66.829464759556203],[40.517339610197894,66.445143089956261],[40.096760112850902,66.298598297888859],[39.283860482654852,66.131515367283583],[38.649859161225109,66.068970506589025],[37.903751597784499,66.095413115509686],[36.988993677142744,66.271748940646034],[35.508199031441123,66.396960735006587],[34.842890071190517,66.601253335848142],[34.482689091813256,66.550593938604663],[34.334749035977616,66.661920308926881],[34.146038848733454,66.702671522719086],[33.817998237547776,66.71198942845993],[33.684985138221357,66.636294735503199],[33.647818271280279,66.469394345048073],[33.746125299789355,66.304968019170857],[34.399678803721457,66.128146756932637],[34.786107209271002,65.864434923123596],[34.776777223226922,65.768358630217378],[34.565813804193311,65.439782404224033],[34.553715662025667,65.323137262653333],[34.803265863183789,64.98585936429491],[34.857075033763529,64.83332989651862],[34.951891829332489,64.755850584342369],[34.913066840794841,64.597392032618714],[34.992736785139421,64.471015897045248],[35.284278878590889,64.363498122755587],[35.647053236502579,64.378047042805335],[35.795722288712909,64.337106154003678],[36.14635650747028,64.188823619990529],[36.375233119268316,64.002141665399421],[37.419435095361628,63.815700086435385],[37.918383560443189,63.965051823888686],[37.994326256930833,64.098992091706336],[37.963595223241505,64.253961849671668],[37.890672898982089,64.342487559147457],[37.758275397476609,64.39084808114437],[37.183821319551541,64.408805995208837],[36.620434733385522,64.753898875208563],[36.528474094278792,64.847449508132144],[36.534773548111716,64.93839924352892],[36.752434057151177,64.978633563749995],[36.882955104248282,65.172161093285965],[37.145722145842413,65.19319822506472],[37.528020529224491,65.107948712569595],[38.031062329006424,64.873830827214476],[38.412006602447221,64.856820647925474],[38.532100932414131,64.795414442154566],[39.692922360409156,64.575760086527737],[40.085676783909314,64.790279601886652],[40.161749659404542,64.919565325385591],[40.114611060727768,65.080445805203553],[39.795515876753285,65.354928141850394],[39.749398437301139,65.447964350264058],[39.816657060998836,65.597758487341409],[40.32756569826801,65.752381586717661],[40.696436219270041,65.96485589654587],[41.459430905441266,66.119251044808806],[41.764391690098435,66.251991093917098],[42.083711588235474,66.465702112394183],[42.385108316193758,66.555555973452414],[42.454477972335006,66.646678944863169],[42.468688540878425,66.785348311277943],[42.631337498411277,66.781980671728917],[42.690536577819437,66.735129293987512],[42.714893521467779,66.527086233542775],[42.827771760076608,66.427122346179829],[43.237430647454673,66.41427325239782],[43.550745884301129,66.320978397119433],[43.725382059702547,66.17553664410147],[43.885749294159304,66.130108247999843],[44.049843334572415,66.175118132739101],[44.415197194785392,66.592029142503023],[44.454429581016598,66.696775271278511],[44.418982762714663,66.958341293217202],[44.322945796785113,67.073135493155604],[44.074205248843008,67.166300416744946],[43.855489190577863,67.188890608891455],[43.782691109547685,67.254584604618969],[43.854454948050538,67.435882393181984],[44.206562339563042,67.9651317269755],[44.208516431859046,68.231609775770991],[44.10590860866872,68.349288793005584],[43.404093140407689,68.608732696160885],[43.338058949563504,68.673894980411816],[44.06486300446997,68.547892461618119],[45.083707084253334,68.577773607877575],[45.891874982961149,68.479407516235383],[46.68336435995321,67.970318651297461],[46.690199154714257,67.849026032934447],[45.518996670232887,67.751971398855915],[45.194513482836165,67.591893052185924],[45.135915133945069,67.491410093841083],[45.143143430112822,67.375313827126746],[45.247740839169431,67.263186395005391],[45.562092108374124,67.185392916291008],[45.911325180675661,66.881277110097628],[46.498538621981631,66.811984461016934],[47.61947733485222,66.969108374474573],[47.701008985994726,67.04563504856722],[47.768270394612244,67.275526337737404],[47.868184558860918,67.403706912175821],[47.874946014698281,67.583999147171326],[48.557831785436292,67.687640870049265],[48.75430362500552,67.895723209692136],[49.155163834046881,67.871072210651263],[50.8346931289989,68.348986578523949],[51.988702248361967,68.537774297746665],[52.222175295339348,68.528811129997393],[52.688638501992635,68.732455604715156],[53.801979550906033,68.995670034605354],[54.489888382585733,68.992201553708355],[54.07515681715698,68.933976886142631],[53.930751683486179,68.811767846209477],[53.869173411340327,68.670661165026914],[53.936561957889701,68.407267495180221],[54.08252657040736,68.266488690178718],[54.476170126790571,68.293915162052329],[54.717438859877703,68.210875859205615],[54.844144866189005,68.251021078416173],[54.927523159364988,68.375914691329555],[55.418106045025631,68.567597274334631],[56.043651102310754,68.648618094181828],[57.137381789085694,68.558138017756775],[58.173045836802146,68.889510678553833],[58.925825866932769,69.00392027529692],[59.057140357901019,69.005807070108062],[59.125013153394406,68.898641206605603],[59.370099348490292,68.738387807443118],[59.284548891140716,68.584440708043019],[59.348130739987205,68.427505568511663],[59.693434669822729,68.352489135253947],[59.841759012203156,68.444722163115131],[59.896173650992509,68.70610143970012],[60.479455976652723,68.728981177366109],[60.671850994332729,68.81738521032544],[60.731209421737084,68.941350177940961],[60.696547388834219,69.074351228970187],[60.104518837730225,69.670194252295431],[59.582802279579994,69.737253724284642],[59.513810588958123,69.839152214877146],[59.393989736854806,69.887940333879044],[58.952848592295091,69.893082213074919],[58.677105456773297,70.053422499684473],[58.473344281095393,70.266841006552923],[58.794332042588998,70.432707909912182],[59.005277452150921,70.464996128678024],[59.636616719089147,70.197786020337631],[60.396554742270595,69.960511271118776],[60.621447976692579,69.773180553726235],[61.015912089179807,69.85131019358063],[61.76121323667261,69.764126308082325],[63.357670863483193,69.675641743711793],[64.186799780244797,69.535283714591387],[65.841941113671496,69.073541260063351],[67.002342864921104,68.873336743094299],[67.160747202124483,68.751225180319125],[67.749411789135635,68.509748201625683],[68.34455296985179,68.329063795877858],[68.470849059664474,68.339867341644677],[68.866017530420578,68.615935165539838],[68.895649695817355,68.745875464899981],[68.822568718927585,68.878484847447709],[68.539160219361079,68.969024287800437],[68.121566152178602,69.233262256907437],[68.067962559389926,69.402148615924133],[67.977543814397649,69.481890926784629],[67.500492288477943,69.604744628165548],[67.390644879147715,69.571738700704174],[67.263871726943549,69.442751024764704],[67.097889333502749,69.447353227784106],[66.896781623621123,69.554024765098291],[66.804260134794688,69.659313407802586],[66.830701248662649,69.836874323466461],[66.926490740290944,70.014034228711139],[67.04563645472696,70.039550919100094],[67.132508213502106,70.139287032009335],[67.261901705269565,70.594731043688171],[67.214712228579558,70.758001597600483],[67.078456155670338,70.8255996015926],[66.668130669542606,70.789697070481139],[66.562412379789933,70.677241346062218],[66.568257024674807,70.501781700196702],[66.445374574359874,70.565383352625091],[66.395065640800738,70.727226849571963],[66.624681288851818,70.83843571636092],[66.673890319223517,70.966371357364096],[66.639999929813442,71.081304506129655],[66.917687282021973,71.28215892548927],[67.549437220527352,71.41450698126846],[68.276281945407277,71.690047591443303],[68.606755596106922,72.013220785369001],[69.03933246730773,72.669661526530305],[69.391494045396371,72.955310046091455],[69.84710966822712,73.006764640517886],[69.990491547297424,73.205913524739231],[70.000040267154162,73.361674846961705],[70.149670672324987,73.444550385493073],[70.345037214056646,73.476821680317798],[70.944814215441113,73.513841245903151],[71.225924214752396,73.449664520690291],[71.589404898419346,73.282971324747763],[71.630219238867312,73.224737344840079],[71.598556420884094,73.012628485093146],[71.705096742756879,72.882696692546432],[71.919520265340381,72.82345147797335],[72.440001888308458,72.791046872317281],[72.811847295696623,72.691233883354016],[72.752666079754576,72.343259573618468],[72.624218939475512,72.079562737824688],[72.279325090641436,71.695697285161515],[72.109688104378293,71.580636853788548],[72.071788271576864,71.416505636955122],[72.172714942373105,71.281638069365329],[72.581194842309088,71.150920164462789],[72.70541594333092,70.958455806742364],[72.69973948912083,70.457434364527373],[72.56361641864666,70.329830135860675],[72.525875220614211,70.224171921339547],[72.615382018297552,69.488667420413748],[72.528903398654165,69.167966379943437],[72.569202864311393,68.988377890621152],[72.799023851088293,68.820986914522635],[73.547894191275304,68.574326812102441],[73.591421351681561,68.482009038867233],[73.181632511757002,68.213617515321388],[73.141855288836922,68.102549687687755],[73.151895263702542,67.865162480357753],[73.066618757093096,67.767156050404836],[72.580253477660136,67.575011130247944],[71.85138133004294,67.010658208520027],[71.58667365788034,66.871287866583529],[71.455233822576389,66.551625545015725],[71.490844598891897,66.400434457094732],[71.604359740194326,66.324148057189561],[72.058820799794887,66.253671676393097],[72.297674346289426,66.342209902510746],[72.417524759150496,66.560600192283673],[73.499862431413106,66.856758659068021],[73.781427388655558,66.991496716844338],[74.074693938613066,67.413848129267492],[74.675453191598066,67.695652192910799],[74.771610312672252,67.810032678799999],[74.736585692913991,68.080241579900331],[74.391667958028052,68.420663384415292],[74.481135089598752,68.65876777461817],[74.667021478588495,68.841620101925727],[74.657114077545884,69.033168077959488],[74.510508849931711,69.126917609952713],[73.836129087526103,69.143398122728087],[73.775989953458151,69.198296114757639],[73.84493630383713,69.348640955787161],[73.824666093987346,69.481174757156239],[73.560392356287608,69.707308359347095],[73.578369709775629,69.802877908401641],[73.830447994992028,70.175447433587834],[74.222646900773157,70.473491951132758],[74.237882058252964,70.677290471858271],[73.52540283232517,71.251198807637977],[73.086604000574482,71.444893937637104],[73.671854933063543,71.844897861866031],[74.794942515912936,72.075055970765703],[75.063640217055905,72.220398135100083],[75.092878704941512,72.431041970729879],[75.001032084364397,72.624237831155128],[74.735855690723682,72.845833008154855],[74.100523168887506,73.021595551800942],[74.198602075189456,73.108885091747268],[74.596766630404318,73.121919949006895],[74.961282635286125,73.06231627600998],[75.043493756229083,72.890597857750763],[75.369222119053774,72.796438017202092],[75.603272359388683,72.580939365412107],[75.619297265788489,72.436952934000828],[75.741073567560832,72.296253653711176],[75.530938346332732,72.147593358409708],[75.375682809481376,71.954186448297946],[75.353222547747123,71.83758507987244],[75.400829248168392,71.728801144703368],[75.502942114176733,71.654530719358817],[75.461904347084911,71.480367037273083],[75.496900722440202,71.369947172533017],[75.727537333096663,71.267061647671554],[76.219592048762848,71.215702493550396],[76.348756042055726,71.258665882659358],[76.420507691220863,71.374341683106877],[76.401609497812416,71.509145422865785],[76.032830680772364,71.910256465923183],[76.781882069267155,72.042720145495167],[76.883400801794949,72.156218214157931],[76.903279648303737,72.365367996972665],[77.38223302957131,72.566719892659677],[77.748525316133481,72.631064658807716],[78.278992299229543,72.552937222342607],[78.490968117705492,72.398455517125655],[78.799859404970675,72.414509590740053],[78.891923117705375,72.515626499165549],[78.897494073426358,72.652262222255743],[78.83024951010519,72.749045391506399],[78.635667350640034,72.855065182246136],[79.164269056690699,73.094104106458161],[79.356413956305744,73.038403732332],[79.541090172257498,72.918520827058302],[79.537619568137174,72.769431513139196],[79.386946833486533,72.559619400648785],[79.42031103311308,72.428807154510523],[79.510636940439596,72.355505767941438],[79.95403177059508,72.223633018649295],[80.762390239422089,72.088995014074698],[80.916133376714953,71.950683510977314],[81.524143407985449,71.743551605252478],[82.210873652304187,71.721341818213816],[82.343409948788775,71.793184140800221],[82.388134260434612,71.937152627450146],[82.346257815357205,72.043520408833572],[82.161942798694199,72.240346866434464],[81.784944328620156,72.327574077954083],[81.102650225839582,72.388991568703631],[80.801113957729783,72.516336247441046],[80.65651846023674,72.712065322620077],[80.711815693543301,72.847761460264778],[80.695790801402978,72.960083153356379],[80.458324695259137,73.151581349476814],[80.398331428622285,73.356761367564289],[80.583376206871861,73.568222846158605],[81.814575995818274,73.658710134066197],[83.237362499601218,73.666073360090408],[83.357490933774812,73.722528212349047],[83.414374098493994,73.833077739477986],[83.490154076418364,73.718661127583303],[83.621294257893013,73.680015970932331],[84.737890608150622,73.762662611755417],[85.403981106785835,73.732276188022297],[85.611496585546021,73.821347198941126],[86.270173786133824,73.875678675920042],[86.421337304215243,73.992755210084056],[86.405113689655252,74.183265363045038],[86.295327872642417,74.264767777717367],[86.00177104955138,74.316169671743637],[86.046365063153999,74.445016006427707],[86.009522191378394,74.557740926242658],[85.915319822125511,74.62978409646098],[85.791505603247288,74.645313906517842],[85.880856458972758,74.739992666008817],[86.119076427655926,74.758293577711171],[86.330756648127831,74.938717683354426],[86.817878335209201,74.989614756592331],[87.006034474830557,75.169620680582639],[87.170801572880137,75.191565710265166],[87.681765332381602,75.132714243345191],[88.736982498389523,75.369867441303057],[89.310272866121707,75.469829227377687],[89.603675169953988,75.460966798115379],[90.184981521062269,75.590924818248709],[91.479392639489447,75.650651326758364],[91.845428200752224,75.723535546709712],[92.694139048687774,75.787319115584708],[92.797532465242654,75.840036340721454],[92.858830267708925,75.979372288152518],[92.971627048671323,76.074841725453126],[93.104143402036499,76.042745366296174],[93.355086586970671,76.100645104922137],[93.648280379026858,76.058171269404085],[94.098402235549955,76.123245758821909],[94.474082882026039,76.107626013831549],[94.575639505417584,76.151540881198599],[95.14771187734155,76.122403877402107],[95.379919383525333,76.288784196966162],[95.594337784744482,76.253859889884737],[96.270722013028703,76.305148186638732],[96.666585178184803,76.255956478413708],[96.840188719688214,76.346026867791551],[96.985685152896522,76.343886860407238],[97.052750347296652,76.302563568343857],[97.043869651067965,76.16784404628838],[97.123943724078018,76.057279143090511],[97.528410344378969,75.994540223446762],[97.66993339274093,76.077811955050123],[98.685719468421354,76.25104758756612],[98.785075599853656,76.338471182228503],[98.80592285045897,76.480533445847996],[98.869476860718308,76.509340186559854],[99.586785272409927,76.47197547490525],[100.80697651551441,76.525944620022031],[100.93565505073519,76.642886052226231],[100.90609001506466,76.900616206327172],[100.99007774757548,76.990231987473578],[101.18412808248955,77.032434660929411],[101.51175951133415,77.195555202337914],[103.12779889566389,77.625639993931813],[103.56900092563106,77.634455981492721],[104.01458121907264,77.730136535297845],[105.89441983424646,77.488525012407976],[106.05920670288982,77.390509500017032],[106.00187474475887,77.287736492497189],[106.01233125492251,77.170369706202635],[106.18018906360986,77.046762131819577],[107.11600578370494,77.020608348258179],[107.24573948799674,77.101768683300278],[107.26981370110096,77.288922598028492],[107.36182857243018,77.343908248723551],[107.62916781113145,77.319370736188532],[107.67916660655139,77.268446425440203],[107.5031082908784,77.242196042584794],[107.41231317223961,77.166052071258619],[107.38054375411498,77.051892700158476],[107.42943010689272,76.926648761207304],[107.31726789236501,76.868390985702248],[107.25466403493166,76.772982223470763],[107.27856024032192,76.606502821766682],[107.42102800326568,76.517119622307234],[107.81690739604018,76.547232361061944],[108.02802945029472,76.718208383631207],[108.17587245792342,76.737114362844068],[109.98114213808542,76.712471930179376],[110.47148715141427,76.75825867365063],[111.11858166019171,76.722557120674878],[111.8541122686328,76.591073635925298],[112.39413521572592,76.643555661252833],[112.6281053220319,76.554919149323652],[112.66056942270441,76.509570696190522],[112.63545825036449,76.345721901022401],[112.73875707725014,76.199307703507756],[112.84948703404534,76.17283285287678],[113.0806368374019,76.257094628168858],[113.27255838618524,76.251391797599069],[113.57077072391932,75.928599844336844],[113.85704553975904,75.921041366440505],[113.87073994043202,75.856067465417041],[113.69624106154895,75.610897269512378],[113.72588136260269,75.450641287503515],[113.61343213949233,75.293149154537005],[112.92485119816276,75.015204125489845],[112.4196041685925,74.899514372194233],[112.3005865025588,74.732028305761986],[112.3358395576948,74.602091119899015],[112.44547144519089,74.523941907424032],[113.38683642746292,74.400331693350466],[113.07964391812511,74.182423997464909],[113.0259657471392,74.000554982245532],[113.08229856426634,73.898227836472913],[113.56135895430778,73.518184750991665],[114.06596789198076,73.584829492071322],[114.81595546430486,73.607746735327908],[115.34278975199001,73.702471228991953],[116.49193802431893,73.676155966176736],[117.31720785356322,73.599099181589096],[118.45588091179765,73.589093460401799],[118.91104691720626,73.518147102087426],[118.93610120037501,73.481265169466681],[118.8574671993232,73.394202968455815],[118.84128938285352,73.277514043742187],[118.89360227335135,73.171961268391129],[118.99625587828254,73.114166248785835],[119.49416658926643,73.05422531312837],[119.75729208073579,73.154283531203561],[119.96833071385417,73.167303957661261],[120.2366930119429,73.10702665501654],[120.42380371123073,72.979539802676058],[120.99717619749669,72.937784381137988],[121.75087273968953,72.969485574695113],[122.33159059161088,72.879849547540147],[122.44365538943374,72.912844662877148],[122.531472919088,73.017297714619318],[123.12172878946431,72.957225006134692],[123.29959701325294,73.004679633122436],[123.43587336069983,73.171965811468738],[123.30533503081945,73.532823840938988],[123.41633070695431,73.636675705767374],[123.79567908715822,73.634684903027662],[124.19854527850465,73.737077706480903],[124.29565479793156,73.806305884922878],[124.3367460006189,73.928170354083633],[124.42391639178611,73.942110171173098],[124.63679315168834,73.90017333223021],[124.69448888053358,73.780939233164958],[124.78804183332633,73.714651801106783],[125.71772963386263,73.469025439977855],[126.2914229010742,73.537939655370948],[126.40769198923429,73.394887660610763],[126.51376700576554,73.345484175903309],[126.80563979619065,73.422779901655247],[127.03137263468628,73.547138616275461],[127.9550129577768,73.445349938635928],[128.36968043994818,73.274057101510849],[128.72719138754374,73.234061049242356],[129.10011456674738,73.112072196371429],[129.06782378482816,72.912824960210131],[129.22892115759319,72.775574917727724],[129.22572788228811,72.516861793825299],[129.41145773073018,72.315366544356948],[129.41042969262523,72.166432939394767],[129.30288629369699,72.076774181202282],[129.27257334134742,71.966348099494724],[129.31954036154639,71.842007104337625],[129.4603508385039,71.739357472952378],[129.33057057218153,71.583996961922324],[129.39113792952315,71.405108219208472],[129.77238649906721,71.120711298755879],[130.53720671949267,70.893326883494296],[130.75717287084166,70.962108829224093],[130.83177228519202,70.935706136952319],[130.9283249413223,70.793531981696688],[131.04254244807976,70.746524262664721],[131.26808553399971,70.766125581134347],[131.55401041596392,70.897383271488692],[131.95681975002037,71.245194961383007],[132.22778923330429,71.64262440739202],[132.65385604292882,71.92572805159601],[132.81790522607491,71.764655595280786],[133.41117209098726,71.496844688427629],[134.08835058972707,71.380888669986774],[134.662878081023,71.387205430744743],[135.02239425110713,71.51485847678677],[135.87900504750988,71.630208745919958],[136.91217324498535,71.464105434051902],[137.28185821398512,71.579723115529774],[137.82339067452449,71.587109780319523],[138.0081272043559,71.531530583604166],[138.2386126017505,71.596681067339375],[138.5329007630036,71.575268204695277],[138.67007443308469,71.634549776298215],[139.00049608008669,71.557478802716346],[139.22065281386938,71.448724094563786],[139.54420581133209,71.4776249589939],[139.68300581942796,71.598028718843921],[139.67790263733932,71.818677873287541],[139.58023740033315,71.914213983818655],[139.35951469211784,71.951569951826443],[139.31007673631009,72.096385735092994],[139.17654246276021,72.163691696230444],[139.14105198013152,72.329591615558272],[139.60120943454487,72.495760499967034],[140.57004077191186,72.508974242568542],[140.70744966708835,72.657759064313623],[140.65266284711305,72.842840329553439],[140.81196870059409,72.890717151530453],[141.30973129879621,72.857539127941067],[142.05061677056727,72.722151664620725],[143.50906713680357,72.698346933594564],[145.4809899132155,72.542556483707017],[146.24801799224724,72.443080393198656],[146.39319301206754,72.305174218343637],[146.83174734722593,72.295181143078267],[146.9487846791562,72.243557336277874],[147.4287241982106,72.340519651335697],[148.39827004702587,72.312075973213297],[149.49629518363389,72.165170630018011],[149.76612371853915,72.091033396846186],[149.99517072838358,71.954025367110773],[150.01661932990893,71.895782454147536],[149.92580955467903,71.819099395665447],[149.89441944210344,71.708084156617701],[149.92956830983184,71.598201129876784],[150.02002750216337,71.525816496748561],[150.59974592951642,71.519899597474463],[150.77926093177351,71.373504786845047],[151.1452942785894,71.373575368860614],[151.58236774538409,71.286777340986561],[152.08846819405835,71.025805984814383],[152.18919397952615,70.906604210881568],[152.50888482160914,70.835469169839911],[153.77939710698598,70.87992002680204],[155.89996662759845,71.095497230771784],[157.44908532023661,71.074408761467097],[158.69901398268698,70.935501034236481],[159.35719214574883,70.788288899231574],[159.72785246381667,70.64949531965874],[159.90802135478603,70.509604511141532],[160.00621964295598,70.309651900692458],[159.83878739634415,70.038978468340787],[159.84451666331506,69.888316412430541],[159.92546272105054,69.782258125024185],[160.74325217376082,69.654092814100423],[160.91056003516627,69.60615812122559],[160.97969988021921,69.515005324461328],[161.08601257926188,69.476741444202261],[161.50518718740841,69.639210888818852],[161.80412014685342,69.497833294133699],[162.38156672563784,69.649420648821263],[163.94600500648204,69.734875614537017],[164.52291671551552,69.60982381099474],[165.75690910503326,69.584499745603026],[166.87497047328071,69.500697578330474],[167.69504440865123,69.755510001432384],[167.81799306076775,69.875270140930965],[168.05386626232664,69.97274080894752],[168.35790199720526,70.015482241906881],[169.42288536505185,69.852012583767618],[169.4179790196097,69.77939843022007],[169.3224895722654,69.729472835292015],[169.24564644146065,69.601300153986315],[168.88246149553811,69.568297773689039],[168.76487001756442,69.499502769394766],[168.72259878499293,69.331901936670278],[168.82674066990265,69.19394772672905],[169.31050467078418,69.079340933508362],[169.43190764501293,68.908992494293997],[169.63311498164325,68.791046439025621],[170.49802441593093,68.823151170990656],[170.84081510516043,68.97530610814151],[170.92053783452073,69.126480688274228],[170.88080065421491,69.255147632827587],[170.57978333652372,69.560360862485467],[170.16134915164969,69.626749041164629],[170.47358671187857,69.845192103502924],[170.51352374869236,69.952400923271625],[170.49161660683771,70.107423234171478],[172.55450760955011,69.968634248212197],[173.17593805035997,69.843731394274968],[173.43867943942578,69.946622474932781],[173.93814544440337,69.874904919466061],[174.79197765985006,69.855719468457934],[175.91478248816421,69.895660275664383],[176.92451341480407,69.646638278402065],[178.84231865818714,69.388183498496929],[179.00624857242406,69.293101753735996],[179.27260111373488,69.259454194499966],[179.99977522725547,68.98326692306361],[179.99976583182561,65.067429915914104],[179.82872368374186,65.030729429529856],[179.44816011055622,64.822292592910998],[178.44234816035174,64.586723976427137],[178.32282143152656,64.423547362847046],[178.34404347429731,64.311472053390688],[178.51619017315591,64.040435571221906],[178.65012904283316,63.965074283977259],[178.77477481135094,63.559817547106654],[178.98983701594196,63.305889930170032],[179.38836432496356,63.147080527127329],[179.42340969321086,62.950063045434952],[179.57027078590446,62.77341452960286],[179.57026486929522,62.687616955742939],[179.20261425940495,62.470026825846276],[179.12051762481062,62.320591758688693],[178.00928781166562,62.547973978525135],[177.25879249144239,62.597773232356076],[176.70277235126096,62.50482800625948],[176.32831325808039,62.34630614942791],[174.71922914262163,61.932936172517678],[174.51429770347775,61.823882013199537],[174.1391137954906,61.794205353186399],[173.82233884955363,61.679684056868801],[173.60792016115897,61.695076463627579],[173.13176350219459,61.406906952825707],[173.00139193928433,61.391087856266978],[172.87200596228811,61.295018298749397],[172.49142430713303,61.169518723326824],[172.39259804835507,61.061946394750947],[172.06719573585008,60.915909701656027],[171.70128426343175,60.828136649784788],[170.68086021310455,60.454270612087626],[170.35089289618779,59.965768762498051],[170.1541871754128,59.986390140282893],[169.98268639452178,60.067211794649822],[169.61643592711917,60.434733093540679],[169.16945271893843,60.590767268042775],[168.14822335138845,60.574529392293343],[167.22702931480376,60.405271841430107],[166.95747378239977,60.301531945519521],[166.26831036030188,59.855878456239815],[166.18665807527819,59.849705091829435],[166.14909798855473,59.922133092871782],[166.1534720952348,60.261052044804657],[165.99434911846754,60.358725093723649],[165.17858481111108,60.108598613221616],[165.01881229726229,59.860925530103692],[164.95366401060838,59.843932425963395],[164.78426049843677,59.872058565088579],[164.66698249736871,59.993696079930061],[164.49707210878873,60.065061473983214],[164.37907534347943,60.054392072476354],[164.11340416087691,59.897878939405722],[163.98110281816918,60.023186522452818],[163.74654829917623,60.019100239257057],[163.36550386384633,59.780983123985692],[163.2726499861183,59.529354317263646],[163.27263151826051,59.302682825042638],[162.96965961130542,58.986681913902061],[162.44695970241682,58.7025950680653],[162.15739026896509,58.459438086009584],[161.98119615086418,58.104609148086908],[162.04247452319945,57.920816321861295],[162.2165820747374,57.824612284115005],[162.39754774291285,57.806055427598999],[162.52213351897302,57.903913795642353],[162.71295773314267,57.946274902623273],[163.22553953424151,57.790259605270755],[163.18774247228302,57.637516438498245],[162.82899707673391,57.386357089132609],[162.77288100198561,57.222174647686948],[162.8546551364725,56.803610376557003],[162.95474643663889,56.743172425707478],[163.25628849077299,56.687885660691876],[163.33528454139872,56.232616832262281],[162.97639062768582,56.034466830023518],[162.84045853839771,56.065885717573622],[162.54644195016155,56.249830231227151],[162.10455863122689,56.100638762865586],[161.77847682091002,55.653274413139911],[161.72534215505721,55.460366493298942],[161.79439629803065,55.189361954279597],[162.07751591918043,54.889797240847052],[162.10532152794727,54.752275137261599],[161.62917197016449,54.516982713674999],[161.29410866258408,54.52084040988268],[161.09249444979767,54.59359331083234],[160.772922403076,54.540542915032823],[160.06739663187383,54.181407397867048],[159.92835491442472,54.015906130548572],[159.85227136095287,53.783240724495599],[159.95560727729753,53.552191295462279],[159.925290297223,53.400751849410526],[160.02483205653957,53.129803826255745],[159.94752089849777,53.125343373323275],[159.76027588706913,53.226289388207128],[159.57612130562904,53.233995388690317],[158.9736133931074,53.055708917860493],[158.64278647582179,52.872974889416241],[158.49676635757055,52.616621332041454],[158.46327613614272,52.305079127652171],[158.10338640542943,51.809755206970962],[157.82313882954239,51.605516269110829],[157.56080933267572,51.495233321391375],[157.19912085472393,51.210928181477122],[156.74774879478269,50.969528581590914],[156.70937646722126,50.977768673802018],[156.68568444427709,51.188508907522781],[156.52269843456702,51.375626766855987],[156.48864861809221,51.912876463385267],[156.36481317288613,52.466209219593907],[156.11055421943672,52.866207075453048],[155.94916923293027,53.74845002918088],[155.70551067194125,54.52520704198735],[155.56475790298421,55.193841524115108],[155.55571743303892,55.352680135297845],[155.71473809578796,56.065117461410594],[155.98283998360529,56.695029213650344],[156.70563706665948,57.13725248384047],[156.84294192644819,57.283442436206258],[156.96725879291805,57.50795131190543],[156.92332692351732,57.64728048488643],[156.79198337708203,57.747970409866682],[156.86777868466393,57.801268334409379],[156.98574239475664,57.82996441205016],[157.2062548204245,57.780429194639872],[157.39751393313111,57.795186567507102],[157.66649867163994,58.01951672757388],[157.983700256541,57.988303993253552],[158.25595012255289,58.038528913983683],[159.20295602729834,58.51598376388884],[159.59046829855555,58.804707567722083],[159.84753659309888,59.126940449371169],[160.5476143938736,59.547075913832629],[160.87099357208592,59.63729891150367],[161.44942531345083,60.027157378805072],[161.7428154284913,60.14915174663895],[162.06826574799771,60.466211548963777],[162.97320489575176,60.782668465769426],[163.33380601969904,60.800401378236316],[163.46723694833702,60.854241754245869],[163.58944846163357,61.084215785109329],[163.83006830135614,61.272759577682272],[163.80465266629676,61.461418881289909],[163.83729984025047,61.558167599467382],[164.02852631707233,61.755221922924967],[164.07442377342281,62.044963880158086],[164.3072475276081,62.41308239803822],[164.30840167707854,62.527282576846183],[164.23167477928311,62.635386884368302],[164.08501474714896,62.66967964737448],[163.36624177162028,62.522075669685805],[163.30190308213921,62.373112363617622],[163.15727843263926,62.242956225934662],[163.08625305054082,61.945765879047734],[163.11619503030519,61.813178991767749],[163.2574551922157,61.699444505884451],[163.07971523173347,61.568150378431447],[162.99400855401504,61.544462933100135],[162.79108788128312,61.665295984617721],[162.38540762785937,61.653809246167818],[161.03218053079399,60.960055553812708],[160.76649753418911,60.753565960698467],[160.48222395866694,60.738632560774818],[160.17388612485652,60.638729994312946],[160.19372363542072,60.814570712014614],[160.12525807745789,60.931124013146736],[160.01795041079396,60.976426939789199],[159.79084756885058,60.956810637224599],[159.90816882934496,61.121113328778804],[159.88340789306918,61.29172211002755],[160.15505957592589,61.574761944897212],[160.13429922705114,61.723875178174303],[160.03371767873543,61.810140387238079],[159.91994398520106,61.819475286126895],[159.55242253706945,61.71973261314502],[159.30822965983464,61.898875262273073],[159.16859681354049,61.928086419433832],[158.07011901122419,61.753847341175785],[157.50010003933195,61.797604502443988],[157.09355023967385,61.677139119228514],[156.73497359676983,61.500413674331917],[156.62947457295857,61.27263753419458],[156.06602998052782,60.99990044516634],[155.71968491811032,60.684840145503415],[154.97933222099059,60.379089912310519],[154.56856217820328,60.078599436125039],[154.38909763624753,59.801821750588424],[154.41244306384718,59.629481258131428],[154.50383514261941,59.554590335470323],[154.97120769232308,59.449371021401447],[155.16643652954482,59.360014333631561],[155.16018819803401,59.190383914164151],[154.83495186932063,59.186840423679676],[154.70350743252374,59.141543217735808],[154.44635492650465,59.205467928057232],[154.24658967783915,59.108844537437101],[154.01095558225521,59.075744504903071],[153.69340021831488,59.217386397878265],[153.43989719944648,59.216096921960634],[153.27282956833622,59.091553152796386],[153.09018735326148,59.078781793506636],[152.82294954472778,58.927279056406398],[152.32871679227063,59.029471562814926],[152.18894311060046,58.997754915081728],[152.08272307140211,58.909859393524094],[151.3268540120425,58.875321474281577],[151.12152975902407,59.082389308532541],[151.44796513333355,59.152908540970515],[151.54414740444525,59.219370214196481],[151.58249154545913,59.387196694392614],[151.4762366408255,59.52264371667961],[151.02701977634825,59.581538835574818],[150.6968389294386,59.386087103025559],[150.61246431790741,59.473090287766929],[150.48375914720819,59.494630787190552],[150.34920346502426,59.630229619941105],[149.61718344490433,59.769312714857598],[149.29459065561045,59.720812372825741],[149.20852478772392,59.619032970133276],[149.20469989649112,59.488374641556199],[149.03450727693576,59.448371077291192],[148.91390881059354,59.282946169370121],[148.73038456413721,59.258403121129312],[148.4912759348112,59.262516734405899],[148.22756801270395,59.408779191621583],[147.89588309222106,59.388427070296324],[147.68783161398107,59.290862089854699],[147.51446448009642,59.268856647869612],[146.55122380531546,59.443338908866586],[146.42285011158273,59.397993165005751],[146.27331501725047,59.221682157997691],[146.04951258867652,59.170767577517978],[145.9317729990203,59.198594742696741],[145.77113982794219,59.36409082196117],[145.55448419039149,59.412206579098722],[144.48957253846265,59.376484265457051],[143.88941749674095,59.411127326534817],[143.52381621328504,59.343860636621059],[143.18273167831737,59.367161052630379],[142.59276125678286,59.24278933404927],[142.05144286651287,59.012726034162469],[141.60814985149173,58.652333908293961],[140.79077737560627,58.302702726246643],[140.67692620858338,58.195841206756675],[140.44676504949106,57.813851794208574],[140.014316979965,57.686764289701436],[139.80837061311686,57.517210673401067],[139.44778919081253,57.331483564738491],[139.18367911467351,57.257464859760979],[138.96560168787357,57.088341133830653],[138.65541056312821,56.95948020436397],[138.21827530051007,56.628448268589715],[138.0781484616931,56.43734685192161],[137.69137957828244,56.139619207345568],[136.79783471555257,55.696168822666692],[136.46072917146498,55.575709438419807],[136.17053354416015,55.35014154025648],[135.51219708078438,55.09634645084585],[135.3713160923717,54.992459279144875],[135.33486113522855,54.845122856442671],[135.4455141682721,54.694357667756485],[135.84074760159623,54.58805550088438],[136.588327582527,54.614264449697203],[136.69727776688848,54.656054675338829],[136.76378143061197,54.749761621949105],[136.76774001985905,54.864600895295986],[136.71508479349578,54.956172284146881],[136.99575263623686,55.092468709809566],[137.15603907773095,55.107539129580879],[137.36586636174886,55.022963303875308],[137.57741054317907,55.196777123295512],[138.02326253148456,55.060407214774607],[138.17198920009477,55.059802438617808],[138.20568699067204,55.033612975491593],[138.07764484375809,54.96832788241381],[137.95935145890965,54.789190746419564],[137.66842662925544,54.647858391649059],[137.47856839782466,54.827907545199601],[137.23308642637076,54.790827422912891],[137.09425292925098,54.919786360161602],[136.86066638703798,54.892778583563292],[136.76973131689829,54.747752568626474],[136.82352963770657,54.561435155979304],[136.76220009731503,54.287654559313076],[136.79205805777642,54.174258615641776],[136.88034777450673,54.097089556014879],[136.99671935312469,54.082674510611454],[137.37780852940605,54.282008348956772],[137.62602916717418,54.307621425589886],[137.66562777618958,54.283231249612811],[137.61506073508005,54.156638850919315],[137.64597453551428,54.043213499497199],[137.83437954814366,53.946537855233792],[137.82786753605777,53.747976055032126],[138.00343112722663,53.641451624395287],[138.21504567007258,53.712041289253925],[138.37904544052302,53.909108980403246],[138.64708473023694,54.018791925073607],[138.70140895067343,54.121036820791609],[138.65758847213539,54.298266844209188],[138.69573486539414,54.319784499529085],[139.30862625079931,54.195464135675337],[139.7074201643816,54.276947592338075],[139.79541636070334,54.256259185779683],[140.18317000133428,54.047984879016724],[140.358625185303,53.809896834220439],[140.66589218525357,53.610213883568719],[141.00551680006126,53.494399957219485],[141.19405096598115,53.34843691452825],[141.37354662594097,53.292590774464351],[141.40180250865461,53.184041533380196],[141.23773960988191,52.960845585875653],[141.26573746214913,52.652601241863856],[141.22070947884737,52.422089318915056],[141.29847538962773,52.291246880845861],[141.48499719593551,52.216842152062171],[141.64495973288282,52.288967927352026],[141.8045312026066,52.565456947236392],[141.87058406649999,52.997502509148049],[141.8239159364486,53.334509366891645],[141.85262861339999,53.389268536036582],[142.14198153432869,53.495336102045812],[142.29024372126923,53.438668647741572],[142.403193105936,53.457235575825855],[142.67076967128514,53.794115835732136],[142.64405346937409,53.986103234527555],[142.33548632087721,54.280492140084988],[142.55684967394185,54.287105878461567],[142.69289478039207,54.415836648600092],[142.76401601093377,54.390440135896114],[142.9759120863273,54.140844555513397],[142.92129999155833,53.916267784379833],[142.94622605936013,53.75413582001093],[143.2252537007175,53.292530818790652],[143.32375256240744,52.96752662277833],[143.32333089366813,52.61364003341474],[143.17260368432875,52.340578459505011],[143.18906887705273,51.955004323222724],[143.32297137459739,51.608322778294465],[143.45524917519964,51.471388402012565],[143.80936302494217,50.301259088761071],[144.04780419094831,49.895680164398804],[144.28268022359373,49.291254712697921],[144.42134803459797,49.067491730830042],[144.70645459996689,48.819490911611595],[144.71353263587068,48.640799194276738],[144.58673695002872,48.846692504140009],[144.12471414308612,49.207421255954323],[143.81904423270453,49.307836364776641],[143.2453266022813,49.263764621326857],[143.10775906162297,49.195276434521624],[143.02189879122506,49.084831717923365],[142.57853555341643,48.080071397019864],[142.55721792783106,47.74869617711348],[142.72776921883681,47.472195731602525],[142.94238369496179,47.319607305243004],[143.16359045093395,46.877012330082039],[143.48545442965116,46.751938733795775],[143.58045602102979,46.360742234580748],[143.46329256354335,46.069588834831102],[143.43181066929799,46.02921643119759],[143.35932660961012,46.423392647485613],[143.28391116634722,46.534938281706097],[142.8293444720594,46.605470431917524],[142.64242932660679,46.70126689834445],[142.51161238670423,46.662932568770131],[142.40689714594339,46.554374659109762],[142.20827024422479,46.089027997604205],[142.07711398015923,45.917351960025933],[141.93031491697312,46.088378226404537],[141.83057171302855,46.451089286033842],[141.86668314905009,46.694138528852946],[142.0310739796526,47.113401441616183],[141.9642983152371,47.587395552795883],[142.17689351658956,48.022689201427838],[142.12576282333748,48.305339641782076],[141.86657251281272,48.750053350579385],[142.01411033422002,49.062711472066979],[142.14088445219707,49.56933128228706],[142.1518454915047,50.228878532754976],[142.06622687168297,50.630448841882114],[142.20360868321492,51.010243829546191],[142.20587996854113,51.191054605916257],[142.07442450220626,51.446913169179567],[141.72262326739391,51.736415254080299],[141.66710379898768,51.927959970748631],[141.46685001397699,51.985221002387966],[140.93439088486534,51.605779297458994],[140.70485117936516,51.246736128304839],[140.6475844661706,50.991857528689081],[140.52498351487392,50.79855744316],[140.48083809634181,50.514574103014375],[140.53086089173539,50.191389744644724],[140.62422417999119,50.082313881024589],[140.49231712812443,49.894020935267669],[140.51699630698627,49.596169449807725],[140.33880178801473,49.125012410335344],[140.37803034011867,48.964111428227241],[140.23344799474751,48.778777718717805],[140.11313737192475,48.422774525685178],[139.37469072531749,47.884995685548105],[139.00115389489648,47.383496820081042],[138.58187181997354,47.048594630068337],[138.39505892703588,46.743473038102493],[138.33673279425608,46.543541885360071],[138.21597901822659,46.457727909840465],[138.10618899572046,46.250848478884407],[137.68525168089195,45.818566811670415],[136.819754007233,45.180652972999816],[136.26908668150935,44.675961879326373],[136.14213336267392,44.489348348038632],[135.86811723978053,44.363978970063293],[135.53371103930922,43.971117944751803],[135.48317064258686,43.835144228586479],[135.26907755902303,43.689459239699062],[135.13092567749356,43.525898256093356],[134.01033142444757,42.947690696371943],[133.15994335494125,42.697254821397259],[132.69722079640655,42.871137499227991],[132.30406989744455,42.883578858025579],[132.28242184423331,43.032397038478507],[132.16170461053636,43.127874737928437],[131.87153094542279,43.095180935064846],[131.68031851976517,43.129449479735847],[131.15822347029913,42.626302222781689],[130.99655252276156,42.641154905816464],[130.88268933284846,42.59033027828734],[130.68733630470871,42.302836842414926],[130.42503374868076,42.726932222547958],[130.57731406202609,42.81137237360926],[131.01463558650366,42.907680373210859],[131.17154478644042,43.144491470679277],[131.23605462765977,43.338969028236143],[131.17447428968285,43.704741706538456],[131.23228218896563,44.138857368882263],[130.98182359000393,44.844216145244125],[131.08239811413165,44.909704265774394],[131.46136009530929,44.99540303717869],[131.65414902937641,45.205206274162038],[131.8518000857307,45.326570729208058],[131.97788032794571,45.245080498605034],[132.86113142286885,45.055414808001409],[133.04407491545254,45.113659220832545],[133.18624312039154,45.494647193177393],[133.40625735326793,45.617190891886374],[133.51328539205602,45.878622380902542],[133.65160772185067,45.962165063050534],[133.70092748425594,46.139660897371741],[133.8643750795504,46.288861159412498],[133.8668470845065,46.499073497432526],[134.01385514747528,46.706304745342131],[134.16908227905341,47.134845856538512],[134.16787792801011,47.30209223959713],[134.29093796197603,47.413398090323945],[134.52495898677495,47.474196290615602],[134.68788284399832,47.628912820439844],[134.70251035847477,47.78022109073968],[134.56628200122378,48.022498229440252],[134.61499134934681,48.140023165785621],[134.58225703409497,48.269301457399905],[134.4755299662967,48.349262354574101],[134.26719242549879,48.369392720417508],[133.82940130315598,48.268777191347979],[133.46832932357833,48.097364619381288],[133.12742440130359,48.099334741911072],[132.67940674007653,47.913523773256863],[132.47621511269895,47.715252809673828],[131.7798028224328,47.680553685731461],[131.33814713976562,47.727153864591521],[130.96676455057832,47.707202632835894],[130.88737680477897,47.879257427855727],[130.73285742141459,48.019385420703166],[130.76100519261297,48.382907763578814],[130.552410563911,48.602608266134361],[130.5492238899289,48.731532962898527],[130.46149082737935,48.838618952629133],[130.04123981381545,48.970169941384633],[129.77856922459611,49.208060440487934],[129.47016083251884,49.359510819995556],[129.06966541078597,49.373620545447118],[128.81943881507061,49.46405697740115],[128.62778197014603,49.593946055488281],[127.99379904194394,49.570281103883616],[127.71557335089108,49.669405445145927],[127.55096670172556,49.802020664428305],[127.50193155021661,49.878304520208481],[127.50556351420707,50.181123050316685],[127.33743640955714,50.350246594447967],[127.30630077809587,50.453577096897682],[127.30970550368785,50.69906358980699],[126.92504961459724,51.100261604127425],[126.78675708507409,51.485961886578764],[126.6889003152817,51.609947839670383],[126.67101391735866,51.750856858151572],[126.5107556772741,51.925967408325931],[126.3690535146438,52.273241492236437],[126.16932488051795,52.521103561234888],[126.04296095245658,52.576963701932605],[125.99122069319563,52.744598472294967],[125.8995925012413,52.842032745325803],[125.72821565364654,52.890970082546374],[125.58127468764066,53.035129340517202],[125.22541108669358,53.165293742625842],[125.02267177893427,53.200471364188033],[124.81235310838784,53.134036763287881],[123.65225914188888,53.533611511131802],[123.28097924251941,53.553585843101246],[122.74043100631192,53.468289615505462],[122.27538914405416,53.476600305529651],[120.99590093828571,53.28434941479663],[120.68427148905293,53.157539069579641],[120.40142327706685,52.948847587626979],[120.34849985983296,52.802468933354163],[120.41654587934778,52.662478441724822],[120.6559885431534,52.56645235144191],[120.69668858959096,52.497895034867476],[120.66485449943065,52.351586547087841],[120.74959151191,52.09658280380409],[120.67704373420673,51.969840010814835],[120.07053417863857,51.597434528250453],[119.80186686549446,51.249518654863138],[119.74278447994671,51.103632240283247],[119.54282143206694,50.90488810703318],[119.44907513483163,50.707428891342708],[119.30193882280359,50.583925657455829],[119.26873580158647,50.431308601116186],[119.34603236320365,50.278906182140368],[119.26278558195426,50.070313426383962],[118.46549354750437,49.84989930656392],[117.87336637334082,49.513744392904151],[117.24013267739433,49.626518265721252],[116.6879586975997,49.821831863187512],[116.55074276858676,49.919261062972694],[116.2373031161741,50.004515901849508],[115.71774593934028,49.880814731179399],[115.42922560279082,49.896659209508989],[114.76019938846626,50.227368702947572],[114.29710535481996,50.269634483423133],[113.56184503027158,50.000741119333831],[113.17626207385304,49.798643523454139],[113.05538948758561,49.616460129449131],[112.80639817934227,49.523751988950238],[112.46804117287522,49.528344734707893],[112.07962394748724,49.42457482272436],[111.32723532059836,49.347699443394667],[110.71593421335801,49.144179526640535],[110.40992100772473,49.218509101171371],[110.19469864668464,49.171302183255293],[109.23665109880007,49.334151056426904],[108.61368457216916,49.323069753567978],[108.03394729072139,49.594182228342397],[107.93696425808018,49.691093212542697],[107.91015623330517,49.866483874906145],[107.82375482820376,49.945663565503693],[107.23336825749391,49.989683553550876],[106.68053808496197,50.31622531680096],[106.21788489564537,50.304823899604116],[105.88314121022296,50.402183075509491],[105.37297051102797,50.471416593250638],[104.44858202726408,50.301285623498465],[104.08302426891133,50.154896715186823],[103.63291248793733,50.138776534336976],[103.30450945917613,50.200547277167153],[103.17009816150791,50.286776779902205],[102.68336411132495,50.387321002394472],[102.29224920918394,50.581815304440418],[102.13637011162349,51.241010897855688],[102.02391289003653,51.368621211942433],[101.5848865493514,51.464608731818323],[101.38125744494025,51.452895389341606],[100.48562869665891,51.722950624328419],[99.921778943494985,51.755809509819287],[99.70010613752919,51.874486535565602],[98.961356100464712,52.046417290664088],[98.84967483064537,52.007128532594194],[98.640385590610038,51.801432060901817],[98.342822646348267,51.705350893943994],[98.219697802847904,51.50578458731043],[98.071398983716705,51.460200701609921],[97.964427534066644,51.349252563923201],[97.870155827983993,50.968333917949273],[98.046932470590946,50.631778522675248],[98.279286325424167,50.533067183384254],[98.250093009474142,50.302527476601881],[98.103212164076368,50.078038756062604],[98.008322024248798,50.017078881230049],[97.853830491386134,49.947032819785257],[97.64389621545692,49.928472722502669],[97.359686297507551,49.741728483008764],[97.208632651371957,49.731039356512895],[96.944634509310603,49.886151386527729],[96.31981440230048,49.900756255634818],[96.083011244820582,49.987360727188459],[95.935785068880477,49.960246960672066],[95.792422326224028,49.996953593707595],[95.517767396594081,49.911490262892379],[95.111456410762557,49.93572756624993],[94.908927322604043,50.043568715518163],[94.614853861037517,50.023972828301424],[94.354941947808598,50.221952032126012],[94.297920314421944,50.4621078189125],[94.178942403552,50.559278943630197],[93.103189914240289,50.604174222910657],[92.896642541431973,50.734777473071787],[92.631379605915967,50.687843015388161],[92.369744405253016,50.799783157808946],[92.192269863702933,50.700821688062021],[91.738399660155437,50.674714732705922],[91.414908169784198,50.468252392084651],[91.03133691978573,50.416246777664085],[90.655001908181703,50.222599577128157],[90.082586968165344,50.09202219686626],[89.977179594148069,49.984509314129582],[89.738455355934093,49.904278687246929],[89.653930018306355,49.717691197682086],[89.263363733088951,49.59633559077578],[89.102872012854078,49.499501788279403],[88.82645280019598,49.448301455643232],[88.284494474217652,49.463953154574845],[88.172881698685543,49.390855165394548],[88.115555721150074,49.25646882265044],[87.983699112603333,49.185074167076287],[87.416675182732746,49.076937564417065],[87.323012271179479,49.086008957433265],[87.244895503448419,49.198952407785953],[87.001087846769153,49.287493403792674],[86.762674503687165,49.523645716738812],[86.505874833602334,49.6385282658675],[86.320508985802235,49.598916689318614],[86.180778514302403,49.49958529865706],[85.232781432577269,49.616013262303532],[84.894437646055124,50.065312973529771],[84.597130014684737,50.202940187919609],[84.323311609250524,50.239368421501574],[84.258035393635296,50.288406913792031],[84.15338169440389,50.543768942975092],[83.924862166167472,50.780891850016666],[83.38229344057541,50.988024977406177],[83.178354548967121,50.989283260161955],[83.019150681942023,50.897487397015638],[82.782129726508387,50.884030427119221],[82.49391845657253,50.727784431700847],[82.098078618006383,50.711092028550922],[81.899721372628449,50.765992152696278],[81.466022327303236,50.740112196700416],[81.35982532663094,50.910366198185038],[81.07174770544809,50.968900853465271],[81.019094926939843,51.150533866863903],[80.888488507959622,51.261678592921875],[80.7406323220617,51.289153041547927],[80.498299672197135,51.1602243173283],[80.423450085605765,50.946506884595323],[80.192550688350224,50.89587231236488],[80.065766650525006,50.758464468662005],[79.986360766061551,50.774758816950772],[79.464421525209161,51.49830020789458],[78.478301007547216,52.635210239009666],[77.868056804332781,53.261061694864104],[76.575854901928651,53.942797856383066],[76.297261609155569,54.263992250526982],[76.184012744010104,54.272979095661441],[75.510810569421594,54.091420254732348],[75.376845429257671,53.970310482218373],[75.056206321529373,53.828328836977043],[74.824066606205704,53.819930977324397],[74.506829663532471,53.668826070691708],[74.356387782210348,53.489230355634881],[74.172262082594585,53.585791858301548],[73.8590183056033,53.618991113121673],[73.651762934727898,53.578109905777531],[73.401836457122556,53.44855246440158],[73.286015996231711,53.598452626052627],[73.317613349931278,53.792231399337375],[73.274694911308345,53.901285549820187],[72.903725064608977,54.105053148219881],[72.711487269931581,54.111209014443119],[72.446790493191344,53.942100750502462],[72.343667555967286,54.164747472115863],[72.217865667412127,54.255429793704465],[71.34101180211114,54.158614465552589],[71.093282637158268,54.212402254262905],[71.053040115934493,54.260487658052782],[71.151057466868863,54.396499409114831],[71.158960910608798,54.641791247784028],[70.913550846583192,55.1181813434102],[70.750605644217273,55.280186784734404],[70.496975023773786,55.282271234853432],[70.18241567717547,55.162769950628707],[69.506468744396685,55.354237093816799],[68.987137204154692,55.388024820324844],[68.349415777802008,55.174593583158035],[68.155802183278482,54.976875135870927],[66.756120391237104,54.738128774652026],[65.503309468197045,54.619246926483186],[65.259367298826831,54.521570737020717],[65.157625863170395,54.364607651533923],[65.088373488833525,54.340474314941559],[64.926000931770133,54.386637118247762],[64.413648062361361,54.377497562917647],[63.85133319091431,54.237306746599749],[63.201582776506058,54.167577700354144],[63.073846145323188,54.1054589272049],[62.654880540927387,54.069941126238547],[62.498974246563222,54.013454105674633],[62.092290532282135,54.002741883378512],[61.928711771924178,53.946708661943333],[61.36299780321071,54.042284755862376],[61.206846247670448,54.004047510574267],[61.114189158326845,53.854683813220703],[61.158279556870767,53.640470425900816],[61.273230987335062,53.571011417251839],[61.518965360429959,53.554325610066449],[61.512473100017168,53.389250474327014],[61.576418454848806,53.272885452550781],[62.014543116581287,53.107645914581589],[62.082471515734973,53.005526770668908],[61.97419498250455,52.943986136701],[61.400748160715317,52.995300825198441],[61.046114787544582,52.957761875429931],[60.856030698098081,52.78356562683107],[60.812000947756232,52.67248689222523],[60.839161161203009,52.556127848733432],[60.994286624948764,52.336932530976298],[60.670259658285467,52.151033814737019],[60.425902491097808,52.124547408184839],[60.32303117463514,52.032752327387342],[60.315319578692289,51.860313078895494],[60.486665553823194,51.65482752622124],[61.363016843301658,51.441691673129036],[61.554480843129106,51.324449124470462],[61.584799776691113,51.229734133884818],[61.389301690260588,50.861193045345544],[60.942252303913378,50.695680568659547],[60.427902896807481,50.678781642259331],[60.04384614712545,50.804949477492457],[59.938868019733285,50.756640503297859],[59.755930996551385,50.546905531646743],[59.523094726195467,50.493103770517905],[59.371762729041762,50.624448926190453],[58.88376924866192,50.694637329501305],[58.553661325807113,50.964587862643334],[58.34114076834507,51.064928446581852],[57.858765415929035,51.081209856609568],[57.653669089826899,50.925395149640678],[57.44222890512502,50.889094333787106],[57.177346448943602,51.031904248520135],[57.001896027586341,51.063035949352468],[56.620231847921637,50.981125995396276],[56.444326308546685,50.994760980499272],[56.164647378763554,50.850867369904684],[56.045309496677511,50.711344441550118],[55.686225437885277,50.583114854259641],[55.366301938849773,50.663471918959338],[54.93156847925178,50.910361011275619],[54.799840027068754,50.925652054437691],[54.676220594565777,50.839515455670401],[54.635924862982577,50.59171153485935],[54.550422226539574,50.536478687281758],[54.471718007843783,50.583936319111771],[54.395340594208442,50.807396006030451],[54.134010687931877,51.045115797728322],[53.688198874036345,51.251976508987021],[53.525635022339628,51.403264849017006],[53.357719501424278,51.475652193499897],[52.571234296002046,51.481849360575744],[52.336661455838573,51.670236558083374],[52.218975617204151,51.704949238839973],[51.998410773194166,51.667116972476272],[51.609033837088177,51.484213782060785],[51.349408806622883,51.474957700860131],[51.184271343509145,51.637111241447322],[50.866944807810889,51.713707295221845],[50.769741154802617,51.684232229964174],[50.252073914526832,51.292174634536664],[49.822198194998791,51.132063128615599],[49.482894336816884,51.065848738670084],[49.323247756804179,50.851929814861727],[48.766277905868101,50.574977866687902],[48.695026552888308,50.384905542373488],[48.843018004345531,50.013170730014679],[48.758900470533881,49.928491394721817],[48.434279501193465,49.828741677161887],[48.33972979613231,49.85682410902124],[47.712789680176208,50.372380787623172],[47.577338451392492,50.40986499946824],[47.438160102550661,50.363350802252455],[47.317108709457486,50.244944421876944],[47.251558161236211,50.004834754826803],[47.011361692038768,49.86353187565684],[46.887450600623161,49.688610291509498],[46.821922817566026,49.399087600144064],[47.031109294422606,49.150299889557857],[46.705387106823359,48.803832393445767],[46.625110924761167,48.602916345911254],[46.641441266689121,48.476649907038912],[46.722470574770405,48.385045986218636],[47.064466244309685,48.232326990681244],[47.118834555550904,48.126959062525245],[47.122716236100587,47.900251719203347],[47.230514547822658,47.78142117385476],[47.342040937414104,47.759262267157382],[47.486639539851183,47.803352494988076],[48.166871484153248,47.708645877884393],[48.556116528485106,47.31663492324892],[48.959115442767228,46.774552463030908],[48.904427119997621,46.603704362046869],[48.948365635486518,46.473237745998851],[49.232053673609435,46.336975933036577],[49.245595444058722,46.291808298074137],[48.833069248809863,46.098708013475829],[48.729467191057026,45.897033906114544],[48.482212699836943,45.921041190698993],[48.084104816739867,45.710647748837587],[48.006509768603465,45.625969899056209],[47.967721204523826,45.470375711764532],[47.905161099023807,45.571803469216022],[47.800540820582071,45.627996043683154],[47.574301871916788,45.556208412518856],[47.425520800749126,45.394379357620849],[47.08362517445795,44.817234019203589],[46.877400376608918,44.737194760927984],[46.798807719137741,44.628302888658787],[46.83730804165782,44.44435143111906],[47.229734771022933,44.19221260509034],[47.418004956283951,43.909422012991698],[47.526832595504352,43.860963194307196],[47.646160382911653,43.884177816531093],[47.506371287467474,43.492238440980849],[47.507492401271577,43.020451654698384],[47.70881463530241,42.810821552898624],[47.765771482750537,42.657338935267688],[48.082515315333559,42.350988150793583],[48.381716256199141,41.962991752677794],[48.572496138343908,41.844426189334015],[48.391259383630867,41.602054277203287],[48.054855172477247,41.444529210942413],[47.861008888145776,41.212955971059259],[47.58892456733944,41.218556521697394],[47.261312668934778,41.315243307993185],[47.165111405623733,41.494338439564174],[46.753893862750928,41.79607771045039],[46.571346279985868,41.800368725367399],[46.472440675129377,41.877336749341275],[45.954122985254827,42.03563046820144],[45.638773522892102,42.205219111699634],[45.656418512420359,42.364675308187792],[45.593800074022703,42.480560268363973],[45.343827582890128,42.530000688107165],[45.150145086921107,42.676184254621759],[44.922349332185476,42.719858454959365],[44.771062560411991,42.617188844871968],[44.66167640542691,42.722999756367877],[44.514430061753615,42.747906945161503],[43.957358846286468,42.566792918342415],[43.8260182741924,42.571760244723372],[43.74186719854395,42.613234345346996],[43.715454183340285,42.720113941107655],[43.629815694702017,42.80444156976418],[43.089242349016118,42.98922414300052],[42.924890899022479,43.117589061560743],[42.779263173265377,43.163311485265872],[42.566062231686836,43.155358357386064],[42.377290202060884,43.225374485307782],[42.049979159034336,43.190379194327896],[41.580624308947669,43.219462022172294],[41.357146260411376,43.330293315229795],[41.079486071090763,43.375581271977836],[40.657539675307518,43.526231740337998],[40.152307482156111,43.560763803903335],[39.978249799592604,43.420104579741462],[38.716764341662959,44.287035293758969],[38.181356359371506,44.419864400959405],[37.8641838003362,44.669269519826344],[37.488777891797255,44.69829663328062],[37.193276756396607,44.974897268220175],[36.650919427036975,45.126714109324787],[36.619403911674134,45.185389890625288],[36.699025426595789,45.268494553662379],[36.720651867586973,45.371740088899635],[36.799601020606502,45.411122150268319],[36.865900245903234,45.426787958979425],[37.163425618599128,45.292494197845521],[37.517633778120562,45.369957965437337],[37.669429147080699,45.65388630200664],[38.053802875992993,46.097628772826504],[38.05759524558038,46.304224700350773],[37.910529266683703,46.41051339733837],[37.766850458292794,46.635895315651595],[37.99780395284909,46.630484590362357],[38.261269084623613,46.714685104357976],[38.347166333649419,46.792862054340901],[38.363545658901856,46.963144158737073],[38.214558043546788,47.091657021820666],[38.201929379857972,47.170282181987623],[38.201653549979703,47.32077229200987],[38.287587181446092,47.559030491127764],[38.640071462299431,47.667100977767134],[38.826744540332797,47.838051567053157],[39.616002748512159,47.841033476303551],[39.750993455079318,47.920194675844286],[39.874850655253631,48.218159797905258],[39.828682675382758,48.490080049214278],[39.645030885694808,48.591390460878557],[39.73799070515701,48.838850824765906],[39.6868230048144,49.007780061269706],[39.887678860863289,49.068585531241453],[40.055493129650849,49.200477786973899],[40.092989884841629,49.331993444152324],[40.027149418925234,49.489457231096225],[39.179603669604283,49.822042237970003],[38.918432688926515,49.825023145714518],[38.637161112329025,49.949531191249612],[38.2933898641596,50.011825566884426],[38.046908061062375,49.920230694840996],[37.704342049015928,50.109260415549741],[37.545648775485851,50.310001988699256],[37.393437381053111,50.406006530580768],[36.801211865472339,50.300607058602935],[36.619428377346317,50.209470448878797],[36.467023261569423,50.283445859175188],[36.306083346299296,50.280743068485876],[36.095485831775648,50.409652806628564],[35.900083558006841,50.432102746750402],[35.673704585248224,50.346274690166794],[35.591284040626839,50.36898172939609],[35.411837087324031,50.539820845731114],[35.403260827647529,50.75281071939191],[35.30827797218685,50.960773518025526],[35.065260443262801,51.166006263852701],[34.712337782146207,51.17239224821639],[34.214153171261387,51.255467701967547],[34.201100190004695,51.516574890243469],[34.115670952239455,51.645037281370342],[34.18006515091863,51.779799439984068],[34.173406413614536,51.8940542433687],[33.99654728234767,52.175194187717594],[33.776984418601714,52.330223336960309],[33.265733064180871,52.351539725093161],[32.811508779886715,52.252838197033462],[32.484207872053602,52.304310985133625],[32.359288197310818,52.244695411310531],[32.282650409182999,52.114180635334527],[32.122227990735048,52.050772148352813],[31.763561743741764,52.101298622925221],[31.577599548919867,52.312402299807637],[31.577906871763435,52.533733903506182],[31.490842960344938,52.807754440778751],[31.259118936177927,53.016723756747822],[31.417928868108874,53.195765813250716],[31.672996845234941,53.19992640984708],[31.851095430683369,53.110738766590018],[32.065571908352233,53.089652679482185],[32.39717971960367,53.198112002001558],[32.525397715838189,53.407570865615931],[32.39249019328463,53.679849271408983],[32.191346180508901,53.781146978292696],[31.754559360124251,53.810585921222902],[31.783541272119127,53.928093092327138],[31.729849599657211,54.051904993531345],[31.403733388817766,54.196142649058771],[31.203789389879194,54.432616693161251],[31.075078601032185,54.491956835889091],[31.01101198106959,54.674052243736767],[30.799026115307786,54.783429337748935],[30.928148483475795,55.07694390269824],[30.899126206546512,55.192359085430169],[30.814692708308218,55.278839937469613],[30.841743057228797,55.4951777340343],[30.776575135361021,55.59079710121712],[30.459723431691234,55.778981952775212],[30.243347899574445,55.841606238114366],[29.936162214101092,55.84181911064605],[29.482239414441608,55.684817969314075],[29.353654449164239,55.784461612024486],[29.325567739513538,55.898404442826866],[29.224687140417121,55.982805120185866],[29.031896892462502,56.020327797823604],[28.794708416279267,55.942839570198089],[28.574108767289569,56.084198566790448],[28.288885498169567,56.055423503955872],[28.148262354909104,56.143001725469937],[28.192390314809668,56.300337652571358],[28.09705966193156,56.530102222305317],[27.829523611089439,56.813485533436499],[27.63978845728009,56.84582151986384],[27.762771389947542,57.152270619216964],[27.737540109645838,57.301883066813865],[27.464649986407263,57.501931765088813],[27.352198685962776,57.528338350604272]],[[177.74490902112902,64.717717074451883],[177.61758835028382,64.699280504661573],[177.5251567103771,64.600671008785767],[177.51655066503312,64.46578818290989],[177.59570047483351,64.356231127722694],[178.01164209738374,64.221474648990025],[178.2357255091645,64.38094718846969],[178.26346126635252,64.531550275420642],[178.17653836795589,64.657625537705812],[177.74490902112902,64.717717074451883]],[[168.4795652486178,69.642827904239923],[168.30312751482609,69.584113594930713],[168.25128228279112,69.405536676101704],[168.36029368221551,69.260497835974803],[168.56661306238263,69.238440106361111],[168.69129060919013,69.327534520708326],[168.71631586127154,69.478716669469563],[168.643164474499,69.592065494726398],[168.4795652486178,69.642827904239923]],[[111.54885663363483,74.023130656432699],[111.59855031404393,73.820435388757019],[111.81329979369083,73.744249728013997],[112.52689230248677,73.723332971765402],[112.64374574501338,73.792294229814516],[112.68873131067448,73.900217051055847],[112.65545327590614,74.031757746447013],[112.56455830907801,74.105304612239195],[111.76890237314628,74.246797843404138],[111.6056096550047,74.186600494688321],[111.54885663363483,74.023130656432699]],[[111.13099231496705,74.052577674210411],[111.38757364878886,74.055076699677599],[111.50921482173726,74.152066271202514],[111.53200398929776,74.497089432904815],[111.41612988092113,74.643977073038712],[111.30185745278662,74.658235951126031],[110.39280960992011,74.468207143414901],[110.20084403616204,74.359203191558095],[110.14043537038074,74.257491995482724],[110.16597926226346,74.10371643522862],[110.29414329095958,74.014984281573035],[110.95950050759527,73.946343832640736],[111.13099231496705,74.052577674210411]],[[108.35153199892237,73.309999045665663],[109.07420033890557,73.376601576845445],[109.33110281300785,73.48720044787018],[109.60432141910094,73.490180035315646],[109.71207090806011,73.640640521041675],[109.65914675916405,73.980154970676764],[109.49804610103035,74.08316512009003],[109.07535601875659,74.03092284798123],[108.19943934304419,73.694360339715871],[107.18847121221845,73.595932630842],[107.04591691363535,73.510262563050333],[106.99187429337732,73.385932902115727],[107.04190166276672,73.240303108800219],[107.1805834631359,73.173384257678109],[107.75023018094713,73.174324922125763],[108.35153199892237,73.309999045665663]],[[83.027740578481172,70.573551648318414],[83.05037394346661,70.3996119770527],[83.217725146085385,70.309929868289231],[83.585490661543901,70.386068339328745],[83.685499737019313,70.550423433819532],[83.578337458357225,70.765392900472818],[83.372106353495454,70.952486387947246],[83.262166092837191,70.991707264484432],[83.080455393216042,70.893297644639688],[83.027740578481172,70.573551648318414]],[[82.592605427947589,70.889711393046241],[83.076920006016977,70.978205946233103],[83.196940033496034,71.200610252859306],[83.151960233610666,71.368545151819404],[82.972608125711091,71.436027459480101],[82.354482637587594,71.240674473003466],[82.275301515273725,71.083882818737251],[82.308362622346948,70.935327501207965],[82.436393012674387,70.853047866305175],[82.592605427947589,70.889711393046241]],[[77.111534823802899,68.595931625932622],[77.23828228780107,68.469482439104027],[77.279469349419841,68.156657510732131],[77.395107406851537,68.055646540118104],[77.565428865593915,68.076045553967106],[77.665010810116485,68.190159032596284],[77.828384994146745,68.258585896149157],[77.894410969070009,68.35520250068835],[77.880044514392367,68.508774285274001],[77.683834425842534,68.82686559859539],[77.587001279607975,68.894806515659553],[77.195943939751828,68.988417561231188],[77.065130719463895,68.960516603567115],[76.988498118421688,68.87444520472684],[76.972079840399473,68.760378200787926],[77.111534823802899,68.595931625932622]]],[[[150.67584250167164,59.173822272802212],[150.72742309269148,59.095304324650215],[150.58987480790668,59.018947854076295],[150.47200288759669,59.034895424341428],[150.67584250167164,59.173822272802212]]],[[[82.609146902931471,74.055592319598816],[82.386576523669675,74.097997260434056],[82.329906876806618,74.131047250241622],[82.525574757242893,74.161081928586299],[82.734561728423628,74.095829879593396],[82.609146902931471,74.055592319598816]]],[[[82.806847706029515,74.094191287358271],[83.149805747373492,74.151426367224659],[83.617587796367815,74.089474131765385],[83.478143117051573,74.031005317918016],[83.411444281141854,73.911706330432963],[83.282851007339218,74.053963846194037],[82.806847706029515,74.094191287358271]]],[[[156.49754565873315,50.849761090646723],[156.40487434916005,50.65785497525188],[156.15580033899369,50.61341464148127],[155.92459327791337,50.306440754935529],[155.79227258187714,50.202313473145608],[155.5431650308949,50.154026948014959],[155.39711141126955,50.041516956212611],[155.24328145521005,50.094737873532857],[155.2184835299573,50.297600698063405],[155.71063801485474,50.428920666730512],[155.80946958408364,50.556759233302323],[155.79948079634755,50.702357448904927],[156.17941217840522,50.77363664623347],[156.37651152442069,50.861885098227766],[156.49754565873315,50.849761090646723]]],[[[155.66652823287922,50.811150628451706],[155.55357559533192,50.810853536013184],[155.46767202320729,50.913413270542641],[155.63946343340686,50.910333025272948],[155.66652823287922,50.811150628451706]]],[[[167.44196275668759,54.85563703314083],[167.87712359554482,54.693024098268623],[168.08064387309574,54.513290136205725],[167.49202455784732,54.792945504375687],[167.44196275668759,54.85563703314083]]],[[[165.75182478608053,55.294435835554538],[165.9312927271167,55.3511987530676],[166.27546489733152,55.311790119993766],[166.29202046718211,55.126235914664328],[166.6500703185074,54.838970972461205],[166.64490615740092,54.694478440857125],[166.23214907873879,54.934805085423903],[166.01023525198289,55.176975701542169],[165.75182478608053,55.294435835554538]]],[[[163.42751743308361,58.578799862257327],[163.57423483631447,58.644542877243488],[163.71392144184429,58.786304875950222],[163.76121151167814,59.014839916578637],[164.20183225465033,59.0972615388837],[164.51741893107101,59.226511702105824],[164.57250377363908,59.220917915736841],[164.6291269786326,59.112144616169374],[164.66134686862134,58.970789157710627],[164.61558542040251,58.88579278378311],[163.97158888500141,58.747162457521078],[163.47139276393534,58.50965147454405],[163.42751743308361,58.578799862257327]]],[[[160.43715886711945,70.851173746972322],[160.44864005747314,70.933805840926652],[160.5610459589856,70.924196704967443],[160.71848784004993,70.822808255811026],[160.43715886711945,70.851173746972322]]],[[[154.61116439800509,49.294215206825996],[154.61319797657697,49.380539185171763],[154.8217410808586,49.642951125864684],[154.89931725043212,49.630191365622323],[154.81030043526476,49.312170036379499],[154.7148207252344,49.267923038043563],[154.61116439800509,49.294215206825996]]],[[[153.99259580437911,48.772419268159545],[154.12652517402989,48.90419427657082],[154.22807965695574,48.891992208358154],[154.04288445285681,48.739067954766313],[153.99259580437911,48.772419268159545]]],[[[152.98463129410632,47.727962903225624],[153.04923403899096,47.796854360560971],[153.07907963534063,47.808438077111155],[153.10081626886168,47.762971623000261],[153.05370409830834,47.706377051756711],[152.98463129410632,47.727962903225624]]],[[[152.55928625715515,76.143640372197822],[152.79939736455705,76.194582153674531],[152.88559536517531,76.121860416234199],[152.78631575733718,76.086044140880219],[152.55928625715515,76.143640372197822]]],[[[151.71564571963157,46.852485690381229],[151.87843730247508,46.885638673292668],[152.16595314994385,47.110231316655231],[152.28832104710349,47.141992941441956],[151.81556959250202,46.787320431048521],[151.75422553118261,46.788573425952293],[151.71564571963157,46.852485690381229]]],[[[146.14885696335415,75.198477741351084],[146.33892771574619,75.476781632104931],[146.5374902117955,75.581566538859093],[146.75078070925477,75.510264804724898],[146.81536501458706,75.412880441994275],[146.91874966950613,75.368650744962338],[147.49315882544505,75.4403460647721],[148.42742668321719,75.413669577964754],[148.6778838811347,75.234983602425061],[149.08837457754865,75.261899725122717],[150.09978498013206,75.219466021780391],[150.52003955805338,75.107859424508746],[150.82191941401098,75.156358355472832],[150.57626083370425,74.918105026058058],[149.83559208440127,74.795077754718406],[149.05206492460584,74.772461438467872],[148.09903177807055,74.824862792603909],[147.62675500899869,74.958271882888042],[147.148005897925,74.997940849411989],[146.14885696335415,75.198477741351084]]],[[[149.44755328786664,45.593570100282257],[149.6660422941759,45.839595742757069],[149.80163972695351,45.885064906662237],[149.96775848495159,46.024732195640269],[150.3432285301929,46.211652798519189],[150.55241516637167,46.208352951338533],[150.26779648927382,46.03176879528948],[150.19101017112354,45.930774024316385],[149.68758279334585,45.642259016922161],[149.44755328786664,45.593570100282257]]],[[[148.39939790358036,76.648454353456245],[148.7196664218996,76.746336055183193],[149.40481740750766,76.781834921853886],[149.15015860008072,76.660140384038115],[148.39939790358036,76.648454353456245]]],[[[145.42643527621826,43.81030442133396],[145.73061268727179,44.057464811370778],[146.11221709849281,44.499922979662848],[146.36784220918679,44.430023036406418],[146.5678382660783,44.43995098354398],[146.51608837539919,44.374914195701322],[146.12098764838277,44.248370986210666],[145.95576218976129,44.133629951360788],[145.61145944356105,43.839803225813547],[145.5556953412576,43.664964511688638],[145.43945042663933,43.737191115501943],[145.42643527621826,43.81030442133396]]],[[[145.86936125911731,43.457668259819492],[146.00829734922982,43.436741934720317],[146.10198557633424,43.450518043191671],[146.03236145787133,43.407392216879785],[145.86936125911731,43.457668259819492]]],[[[146.27271045512114,43.620343742179529],[146.3101872145792,43.65163252033588],[146.36500582924248,43.63923023058701],[146.3376634256328,43.621025179743292],[146.27271045512114,43.620343742179529]]],[[[146.5960824637973,43.744349101523156],[146.62211952497384,43.812797489863847],[146.81963330927698,43.859331369297102],[146.89867213589224,43.804281991465906],[146.68295664500897,43.716602400578701],[146.5960824637973,43.744349101523156]]],[[[146.88761633159791,44.420876759313096],[147.24273165886004,44.852286237926876],[147.67359788946692,45.108247449568751],[147.83832236896612,45.242422152435061],[147.92413730348511,45.38305896365339],[148.07670175206147,45.277461043643974],[148.1772403251602,45.264086538299694],[148.33103869009142,45.291850123040881],[148.70233200222555,45.519010686634466],[148.82384335258774,45.490073410885586],[148.83683100892057,45.362763436877401],[148.79063957287499,45.32419244355286],[148.2733761183915,45.217875083501944],[147.91366292917357,44.990586260213746],[147.66946630980644,44.936128570934983],[147.20729621320646,44.55377381338775],[146.88761633159791,44.420876759313096]]],[[[136.94791332701669,75.325477113442986],[137.15588347265796,75.42045044454872],[137.26903267668544,75.749242733999424],[137.42369816798387,75.809905401082588],[137.62111329664612,75.986009499895403],[138.81398530963156,76.199437352650619],[139.73795646283767,75.954593899018803],[140.0490210656628,75.829948667180915],[140.38895751553937,75.795659154283655],[140.50562063782394,75.68595235238601],[140.65679930315423,75.634741026689781],[140.86687576255332,75.690561913379895],[140.98554248286842,75.964248729803273],[141.48548106366542,76.136917192843541],[142.67435892095736,75.862742256418244],[143.17313398937148,75.81424097107967],[143.68582091681205,75.863418938512552],[145.25032531006931,75.586472424726608],[145.35937564911202,75.530608687608307],[144.99935646623879,75.477617317178243],[144.91005437342946,75.398073679703785],[144.88325049875198,75.269098135631339],[144.21599567287103,75.059314035132715],[143.28426828647795,75.091251152997913],[143.12328143659943,74.968949566895972],[142.47275098597234,74.820651959630922],[142.28750184194246,74.850184040688205],[141.96787565391688,74.989651977485494],[140.27363527068852,74.847192363522979],[139.68142639137935,74.962576439771112],[139.56938524209738,74.914863753514382],[139.32549241557459,74.687043991730448],[139.10341686411513,74.656658485264032],[138.08776738248852,74.798928850027266],[137.67415963030837,75.010208521113171],[137.21805369774785,75.12393751048306],[137.00247944898487,75.238042964441647],[136.94791332701669,75.325477113442986]],[[142.39645638530385,75.266495300569602],[142.54512226307085,75.242805300893778],[142.67782691508711,75.346208084314483],[142.70690855791472,75.55407598096599],[142.60271455274975,75.692239501874596],[142.35221088963863,75.696019154556211],[142.21634884099342,75.589586706046376],[142.22310903985252,75.399451917379906],[142.39645638530385,75.266495300569602]]],[[[139.6861275135218,73.425578887518199],[140.35536580426592,73.480148199940871],[140.5643778712965,73.562570349684435],[140.64017091034074,73.695316175053321],[140.60006986356854,73.84282192059635],[140.18338076232482,74.004781374725155],[140.10186237982811,74.184180255298628],[140.30546930247621,74.257672472743536],[140.85480849710359,74.273242704108938],[141.07930685853273,74.209186040585166],[141.09590868523273,73.983519141407641],[141.22865519705439,73.878762239986756],[142.18980069591677,73.895023501411572],[142.63488697769091,73.804091307355719],[143.343636286161,73.568526635893491],[143.46374378202955,73.458788307917075],[143.49106876042356,73.246579266498969],[142.33551933838351,73.25377095334639],[141.60327818481898,73.310476707420818],[140.67545667696791,73.451174880104375],[139.9220570712553,73.355224609375028],[139.78562868661677,73.355482615174296],[139.6861275135218,73.425578887518199]]],[[[135.38733715802593,74.25308990132423],[135.63235917828951,74.218640599622844],[136.25882240540477,73.984885711169113],[136.12170817859041,73.885294164988323],[135.40663295595263,74.199710687423789],[135.38733715802593,74.25308990132423]]],[[[135.45223903951742,75.389760612882569],[135.57870180902162,75.709842382719742],[135.69866845725346,75.845000253082745],[135.8976853581409,75.699729039713489],[136.16842365929401,75.60557292760042],[135.9485897602022,75.409733311786056],[135.45223903951742,75.389760612882569]]],[[[106.02404042012459,78.220275499660062],[106.0585151447418,78.26441730166583],[106.63608907102977,78.336320513453416],[106.86864386541895,78.178007007328034],[107.51244009312472,78.189158427974149],[107.69502614067382,78.130889868639727],[107.48165156513168,78.058002591589343],[106.42035212261936,78.139480002509444],[106.31199943286157,78.200580950060385],[106.02404042012459,78.220275499660062]]],[[[99.287755889691027,78.038069875636936],[100.01581628797938,78.34573963950443],[100.22971002359294,78.561694198896262],[100.28414542284565,78.679052005788606],[100.50987592512391,78.785787016935501],[100.77975649254161,78.823013447466366],[101.05244237399388,79.123061368897254],[101.19619876555321,79.204257810687622],[101.47783043618189,79.256652560522312],[101.64908442556788,79.361891165034436],[101.82415089430873,79.370001523971482],[102.05416640387124,79.287982010973892],[102.2824526874724,79.429884754504243],[103.04151713614928,79.331331077530294],[103.1594190489031,79.155289581688976],[103.26437778540391,79.102568048717202],[103.80075495631608,79.149019454012276],[104.09237950208548,79.017067367572565],[104.44907122564128,78.963712850511968],[104.60165056645953,78.842972099406666],[105.14590008229233,78.818610858750091],[105.34238835983669,78.593928975915887],[105.31241784696087,78.500084331097725],[104.741786274425,78.339959051799426],[104.30428725072379,78.335494060277668],[103.71289671652521,78.258230442228623],[103.01391995964119,78.25479592833976],[102.79665215541972,78.188081886447463],[102.57328386319813,78.222670428607302],[101.21382992013825,78.191089019027729],[99.851611395240184,77.957340707653231],[99.500317219620115,77.976247179586082],[99.287755889691027,78.038069875636936]]],[[[91.0706941478077,79.981367435013908],[91.485561927672748,80.088199647444583],[91.558763012541206,80.221422010789965],[91.52837201069039,80.360206592997031],[91.896727675977488,80.477256974329649],[92.497871730867644,80.54007652391806],[92.587814976507943,80.639083368012692],[92.610318238367654,80.809849810309757],[93.065218765896986,80.988214159272246],[94.827352367481112,81.138251319628139],[95.049498630444404,81.189415579294362],[95.159657467053506,81.270762346231464],[95.795469206344293,81.280391705738239],[96.465632173312457,81.100894790148431],[96.773134688471032,80.955475079021866],[97.831705163533556,80.798039970042126],[97.856257265859938,80.698327871947072],[97.429281946162874,80.629564479205314],[97.36122694622054,80.533144333641445],[97.359384459874761,80.415140381454364],[97.498202917344358,80.202291219799477],[97.903515401710962,80.094798284601026],[98.144789072026398,79.894452600580607],[98.346220142541199,79.887085746121656],[98.472106451641864,80.008982415725825],[98.592584654706812,80.05167887380351],[99.289780753482873,80.016705585692776],[99.824620735917819,79.895789912381431],[100.14103463998764,79.699811256550447],[100.29935989875634,79.670149115935686],[99.859909469600467,79.592406865923365],[99.741221756690322,79.489988202391444],[99.703413040313947,79.212969281136282],[99.928931507603679,78.961562161598295],[99.439535296550787,78.834377961906824],[98.404610939261673,78.788159421764192],[97.548531204597694,78.827498308078603],[96.780250677503744,78.986278832863675],[95.789581120019648,79.00219704082356],[95.50314303412317,79.09764237720313],[95.128466668226295,79.049738551093824],[94.79792992509806,79.085598224748665],[94.167747144932278,79.406839758634575],[93.075668548288192,79.494420587280459],[93.029905629167274,79.620957009488777],[92.914655960668043,79.694657216700961],[92.153739372163855,79.684891044888985],[91.370218066963645,79.837177434504881],[91.12621047572722,79.905128260050034],[91.0706941478077,79.981367435013908]]],[[[97.310702716728656,76.689438757289807],[97.381607485883507,76.706418664490712],[97.587783261049012,76.599426402635103],[97.43041469971287,76.590906350979154],[97.310702716728656,76.689438757289807]]],[[[95.271439850141235,77.018942189643099],[96.528309706678229,77.205273534326139],[96.561665106169841,77.153982084625767],[96.209828795700233,76.99241738905566],[95.271439850141235,77.018942189643099]]],[[[89.901553022239327,81.170579398726346],[90.074316360689039,81.213658957296929],[91.107629629187741,81.199140128947292],[91.477769557053989,81.18368415404764],[91.566536433740211,81.141284467934724],[91.218510769143535,81.063990019566475],[89.981017244429495,81.112927703981555],[89.901553022239327,81.170579398726346]]],[[[89.142131736368626,77.226857209788861],[89.28159676604875,77.301183258337829],[89.679259900774696,77.280214792191501],[89.51420736721002,77.189048590988918],[89.142131736368626,77.226857209788861]]],[[[84.390012234472152,74.454354675186295],[84.672995304129245,74.511271468298773],[84.87200104682266,74.515321800640848],[84.710344792312455,74.400058894323777],[84.390012234472152,74.454354675186295]]],[[[81.500992743110814,75.367846628390751],[81.912892782405535,75.497488723375326],[82.160571690002698,75.515549474986457],[82.221293893813808,75.350688201826173],[82.077760563332276,75.326720516200396],[81.978427181998754,75.247358120268089],[81.654880188650935,75.289175716356795],[81.500992743110814,75.367846628390751]]],[[[78.978132208452237,80.848263726142349],[79.222448572109784,80.960480851028294],[79.810613466253713,80.975175699456244],[80.427395468088761,80.927537085974166],[80.344752258900385,80.868180959566075],[80.027818735834714,80.848216542412061],[79.103233849888269,80.81224322441075],[78.978132208452237,80.848263726142349]]],[[[76.05203433596995,79.644592530874448],[76.472146986780928,79.642703383210574],[77.588233610678671,79.502091180021083],[76.649606774031341,79.49365070859902],[76.554702110350391,79.541237090425028],[76.153793599321446,79.579013742343335],[76.05203433596995,79.644592530874448]]],[[[75.344781235746325,73.432556298246993],[75.569789009142994,73.540321081033383],[75.930170868559387,73.573340291625527],[76.25065207607409,73.554966324649442],[76.754758396672869,73.445897053608618],[76.041858330918117,73.526317406306049],[75.827114026791961,73.459357144456803],[75.344781235746325,73.432556298246993]]],[[[51.428936163960472,71.825547950287273],[51.443765762503006,71.934280504381533],[51.586692832535498,72.072856686223545],[51.885450866432734,72.153037686351453],[52.261609235560321,72.133668941890335],[52.631693086434922,72.310901330189623],[52.701901812123324,72.398186887926144],[52.728646006280087,72.551839136037813],[52.664408902388324,72.667849851789953],[52.528866068476823,72.737451337401652],[52.554292466172299,72.771498478129146],[53.088573969921583,72.937184247105051],[53.256164660231356,73.184910293098412],[53.921630600934662,73.336547020793674],[53.990387802921738,73.432176008916443],[53.987078365075106,73.568805537499799],[53.763296281330739,73.766086544873758],[54.631536590077346,73.962591321685693],[55.341031049402723,74.419378215803221],[55.498837449130029,74.470145330876221],[55.582453688032459,74.627520642564505],[55.887225641708014,74.809186165633022],[55.913839698114145,74.961843047301542],[55.810359528081435,75.124760208810159],[55.920745883352659,75.168220135964077],[56.156524634852943,75.186916188614319],[56.556070397290377,75.099965853776837],[56.74417120178866,75.190262355368787],[56.844534965893899,75.351156091874117],[57.58851901531866,75.359421530649016],[57.783533048285292,75.506402299747052],[57.981117527039373,75.570537243235208],[58.058464751714979,75.662884810503556],[58.885717058444484,75.855452139589246],[59.97045085365037,75.974022184903717],[60.11828759371916,76.066318817614672],[60.272199553808427,76.094932606713087],[60.880366262314837,76.077930087498075],[60.980273796823759,76.132202167506321],[61.034588445317169,76.232777377686773],[61.194632417578049,76.280696286540547],[62.97145129278443,76.237443140195936],[64.451450939369863,76.377293018262492],[65.764679799216267,76.583352219473667],[66.063053551015457,76.745932452334358],[66.342790976741981,76.820401213249184],[67.541369609823448,77.007972475735201],[68.485707124006112,76.933522000132569],[68.868521903706679,76.791841139776352],[68.941437660702576,76.707680628071373],[68.899494514106237,76.573080698449274],[68.165374785547826,76.285046161357101],[66.280005017162267,75.983403354657042],[63.659541038491085,75.668135030856234],[61.3235535448446,75.304608738113515],[60.612310166207578,75.040895784184301],[60.443003997441373,74.877152083150364],[60.084025134162033,74.756988252106012],[59.875286527892079,74.736681171717819],[59.678454196615704,74.611667985430273],[59.257103357401625,74.655735376775837],[59.100821125930402,74.507765992238348],[58.751659425250295,74.433503723456454],[58.617696899124716,74.227593477997189],[58.44129862509579,74.129162495974924],[57.962969361049744,74.03068333651386],[57.844778827535592,73.80524594460212],[57.62719159195585,73.745238021888426],[57.459653508855965,73.610554108559995],[57.135987801018658,73.501422994901304],[56.963763061714211,73.366777845047608],[56.505422385480827,73.277123542822892],[56.397280876914657,73.139357681276962],[56.237553161356836,73.043690116684189],[56.12156370226478,72.806774687433503],[55.806445568565252,72.774201723087941],[55.458229034313732,72.519158605538408],[55.421811042627489,72.352924241611561],[55.517771478358711,72.220599997189979],[55.422624638743606,72.092679354369935],[55.415380351800621,71.959181778382728],[55.811927647552587,71.514135917462283],[56.442575989352619,71.114219718202207],[56.904285684014638,70.924178119581256],[57.624854904078063,70.728843272977556],[57.246699680467451,70.605335246465373],[56.674060439503975,70.639837873892603],[56.434597943063267,70.563198291766028],[56.197652360004753,70.636225728471004],[55.796896409563004,70.615804427121986],[55.58983407030258,70.685561780018048],[55.054540769464914,70.666736658050979],[54.709810441325693,70.714343564159677],[54.601119161505004,70.680341313157157],[53.388443767568027,70.872689304238293],[53.342746402609357,70.936549202717714],[53.506026263425561,70.956131506496661],[53.60250389356591,71.091877292083993],[53.588496349169525,71.227470539031074],[53.490857521353441,71.322593613100224],[53.351039711287378,71.33331503350577],[53.240477605420253,71.255535080524851],[53.203006017490488,71.112462565463886],[53.258310503680541,70.988727007173878],[53.022789650586297,70.968965319261187],[52.683732635676698,71.200544286697905],[52.249808123291132,71.285089851023869],[52.126050804645836,71.463938475941418],[51.819587470722276,71.490382429712369],[51.590535394977387,71.571297367166039],[51.428936163960472,71.825547950287273]]],[[[52.214093369311449,80.26366634843373],[52.63286022484148,80.308596962672695],[52.858249736955024,80.402526833682913],[53.324364896944488,80.402737886825378],[53.471995273380259,80.327715585832578],[53.660258604064282,80.31171392210527],[53.770885086779465,80.381977496304145],[53.877402605216801,80.605057037169033],[54.026115724790706,80.697000144285724],[54.045718742665514,80.871789676360208],[54.331974726591397,80.91800186587642],[54.63405357094085,81.112943278717225],[55.310678273569799,81.04915543031052],[55.451422954449754,81.148649359320956],[55.466348123673519,81.310981179485424],[56.156324614698121,81.307044105816374],[57.091528282078947,81.54100131377281],[57.761270246100004,81.570121575741439],[57.985091275076222,81.796807418978545],[58.139089432250962,81.82808363419494],[59.408020037049717,81.825313508990931],[59.356282672156873,81.759224380575404],[59.204503956019053,81.708169773499463],[59.13711174917534,81.5723961335501],[59.197597633960356,81.414187689799405],[59.374024835658702,81.325078410593989],[58.887243235915946,81.295687040809085],[58.74710076210723,81.173228063431765],[58.758373735828805,81.00484310353599],[58.930296959038039,80.831762798651823],[58.809429628255401,80.727643359904633],[58.775676636410154,80.616576667109271],[58.809299006455447,80.505470362429747],[58.917438527687594,80.425126055776104],[59.182344574487736,80.41961776654226],[59.386684850316421,80.712342809583703],[59.592384169305163,80.816274279840812],[59.911065281214988,80.846331848273536],[60.078416143380778,80.998964634194252],[60.579621663168673,81.086562731150437],[61.457372758524059,81.103733006746879],[61.567170213277571,81.050125458964359],[61.681874118312209,80.905839716888437],[62.102869711539405,80.866381808401741],[62.229374337595729,80.792828405113511],[62.075638902700753,80.617176460447823],[61.719624450476609,80.592745185885221],[61.051230928405928,80.418855467138286],[60.287010190404551,80.492500654919766],[59.858918272618034,80.438590919893883],[59.751081222968054,80.360592567872473],[59.716066328309729,80.232192687236477],[59.756854605333764,80.125031074484511],[59.910510893889843,79.994423249596394],[59.33485918866625,79.923430589511696],[58.919585393950143,79.984813944171592],[58.949724742266618,80.208351513055462],[58.7802218371164,80.32866855084086],[58.396965594457875,80.315018033444645],[58.255348844517293,80.202013850324192],[57.805263760277441,80.104687593312747],[57.238667791616557,80.179217444742136],[56.986893420249132,80.071657450894108],[55.807439956618687,80.087997227298487],[55.724227323075226,80.105075415263343],[55.92540028105941,80.182109166975209],[56.016676355675777,80.378238758593909],[55.959706248104922,80.578001091144614],[55.532029353601494,80.703594038902168],[55.131488288396206,80.750263466103021],[54.575424320673875,80.733793656594472],[54.466176721925628,80.65336713212821],[54.415177841122016,80.473023254616578],[54.275852313998357,80.421562395493666],[53.9963174483578,80.439223055553597],[53.781897434424195,80.230474983015171],[53.521359331285289,80.185389618465422],[52.640648993698001,80.17873739046712],[52.214093369311449,80.26366634843373]]],[[[62.520990167691195,80.821931165520724],[63.115858525734971,80.966515204445884],[64.062230008069776,80.996818851334865],[64.31024228489494,81.174918678358893],[64.580040087139238,81.198461287936837],[65.175708584880653,81.142752206731984],[65.38179985397862,81.056589764072584],[65.436975014173512,80.930837314787894],[64.552961634887424,80.75607314306211],[63.37551112760071,80.700176929632633],[63.008202189153089,80.712344490262097],[62.520990167691195,80.821931165520724]]],[[[55.479117473139063,80.273943762676225],[55.195102636321941,80.226996350123969],[54.980273323921686,80.256430223659791],[55.240051944344742,80.325193159633145],[55.479117473139063,80.273943762676225]]],[[[62.108052812050175,81.679428238893806],[62.797862745919979,81.718843578336191],[63.705244497441264,81.687454331326521],[63.781986076488927,81.649907511534636],[63.522374253342583,81.596699453530547],[62.108052812050175,81.679428238893806]]],[[[44.906393584146365,80.611373517137963],[46.777195954803808,80.754291769612379],[47.437117751062992,80.8536222855479],[48.452018638209204,80.804331429359948],[48.686279634259265,80.71762035627178],[48.711459828283566,80.615573086937957],[48.794892699149059,80.534695986979386],[48.97054780217011,80.514854527767284],[49.092017746854623,80.575170516475325],[49.244455238919556,80.8211711031135],[50.211131266818015,80.929447941681445],[50.323508459149991,80.998257741679652],[50.368693670146243,81.12228982775936],[50.595516287859766,81.169470173341495],[50.942050625658389,81.110772203679645],[50.946032861213773,80.970043448935968],[51.045738862631595,80.859436137367354],[51.702907020138802,80.687750271630591],[50.960793028070157,80.540743894833611],[50.404404943127957,80.525830896767076],[50.260534026726241,80.401386604520013],[50.265847871932337,80.247008243155335],[50.425475633675575,80.055887461817875],[50.941543645496459,80.093490461762173],[51.254257136515307,80.048421779919636],[51.426352783074279,79.920666113184708],[50.454113705894194,79.924585491615147],[50.091538914914011,79.980784735163539],[49.986012112360719,80.056763706317881],[49.588391891894638,80.136390778884433],[49.507918825002832,80.305138395809067],[49.373933931895827,80.373369126526896],[49.112525320242256,80.348507261694508],[48.977422617657979,80.162806323123974],[48.625977577007355,80.184412490836039],[48.386229060035816,80.096129477247658],[48.156739465794956,80.130135094695731],[47.737338838323588,80.081964796504124],[47.497781887156584,80.214117991195906],[46.991050826024143,80.182952411911558],[46.738292223449633,80.258008150256018],[46.479622795637901,80.461955764741461],[46.14144898704572,80.446948294333325],[45.920050988606519,80.560936966242522],[45.636058103622844,80.537404725998343],[44.906393584146365,80.611373517137963]]],[[[48.279053968124785,69.040362316454647],[48.320151240598463,69.26908832713498],[48.413986672391793,69.345454224896898],[48.959634887076675,69.509322707073082],[49.225171356517542,69.511038883020234],[50.161970477163095,69.258706958759291],[50.26503307801471,69.185469210949478],[50.282748383491075,69.088979643877167],[50.164520517874472,69.037810711390691],[49.97656569488462,69.075565938257668],[49.621183214062881,68.85878922643505],[48.671309178766002,68.733330129384456],[48.439221502218508,68.805129436125242],[48.279053968124785,69.040362316454647]]],[[[35.529238465831213,65.15092996210106],[35.815977956811409,65.181849072511994],[35.842042077202265,65.001630423246482],[35.778751995634948,64.976918443294636],[35.529238465831213,65.15092996210106]]],[[[19.604937520501469,54.459237518354321],[19.871493018663635,54.651014515765048],[19.974708095902532,54.921000598020271],[20.495867972663312,54.98625181349756],[20.899897296681317,55.286398349833341],[21.092834685948418,55.215125952017672],[21.389202476742479,55.275319434446175],[22.060636454990931,55.0658619983097],[22.567166617914054,55.058913004395144],[22.830968094397452,54.838524469186595],[22.691284964080431,54.578712285161352],[22.698724044216561,54.458257341645982],[22.765782992986136,54.356935960211672],[22.731811948867058,54.350371319819622],[19.604937520501469,54.459237518354321]]],[[[-179.99977673732479,65.067567970298057],[-179.99972035346454,68.983161886146775],[-179.4709116606187,68.912196436914172],[-179.27910096878395,68.825929835221501],[-178.87397179609655,68.753782465550202],[-178.48824750750066,68.563521071116],[-178.24456588484802,68.539739541393146],[-177.44714017018708,68.251401470717667],[-175.3453744529518,67.677888728875459],[-175.17524211987691,67.456383477962063],[-174.99793513963476,67.435819677069674],[-174.85012774666617,67.348789775037545],[-174.85565272933931,67.183324224041399],[-174.73183123750425,67.062649831445071],[-173.67967965107124,67.144500691677976],[-171.80030927671126,66.932575303871999],[-171.34018178758598,66.668675454146737],[-170.45181829325935,66.311012898471873],[-170.29622634794501,66.292002071133652],[-170.12303590409718,66.176601730470438],[-169.77802610219132,66.142910014978014],[-169.72946274713581,66.05818970842418],[-169.83173339612324,65.999140136185403],[-170.02627900823404,66.029822706255146],[-170.41071817915119,65.924136924728714],[-170.51321729194353,65.840555705206611],[-170.56117745675439,65.656446835615426],[-170.94572319294917,65.632019298020722],[-171.10595991435017,65.511278170178045],[-172.12123513918189,65.509820316347486],[-172.26444471325911,65.304437239239846],[-172.21341952411129,65.048269456960028],[-172.56080095895271,64.908013855862066],[-172.64696303700308,64.778182432096003],[-172.61381323309794,64.625928720180212],[-172.37902284009093,64.431620618043624],[-172.81348069977597,64.415745492804447],[-173.00922861111277,64.29774128179254],[-173.15238864721829,64.280337928943183],[-173.27540775526424,64.289883193578291],[-173.46925606561462,64.393645926656433],[-173.66597009057091,64.357691161244489],[-173.89303407075991,64.408421125265576],[-174.31832915910832,64.636903048324214],[-174.57904578357855,64.719674444150499],[-175.03612210807376,64.81244372026238],[-175.44204214174883,64.816885641033593],[-175.85363155891173,65.011004673734107],[-175.84498751480845,65.177997605366201],[-175.90045419442669,65.312186649129046],[-176.11598166720216,65.473639715645476],[-177.05624583099706,65.612704507574207],[-177.49466134507296,65.503317510192588],[-178.41242508726307,65.495833390178007],[-178.50445418078627,65.537380748855938],[-178.59527111903427,65.751509062059711],[-178.87911262773321,65.936679777277803],[-179.0025946733499,66.17794973279203],[-179.13864309055381,66.239829957564993],[-179.60772998038016,66.119975069827134],[-179.69924027845153,66.050142494090167],[-179.73710087974311,65.941435097115473],[-179.70876977547485,65.829864169817583],[-179.36617470326647,65.638454179203066],[-179.35233849394729,65.516891167615967],[-179.70471908478257,65.187424466074347],[-179.99977673732479,65.067567970298057]],[[-174.7710999944085,66.776066855271509],[-174.67932658841687,66.61340138862775],[-174.40394946609229,66.516073350068353],[-174.29228653247782,66.556303119659333],[-174.17650781246093,66.676596861043464],[-174.1289252218246,66.823178847325593],[-174.23230506779393,66.982350263650289],[-174.59372542141216,67.057442188334107],[-174.74249165103771,66.970789557567244],[-174.7710999944085,66.776066855271509]]],[[[-179.9997163604142,71.537528511774411],[-179.10675065259684,71.596069213314976],[-178.43612604137786,71.54076444980889],[-177.57949166007128,71.280028236081122],[-177.49877074642436,71.219111188655361],[-177.52376056785417,71.167094958203776],[-177.8271358790474,71.067007952517002],[-179.50666878459705,70.923596907201514],[-179.99971484707888,70.993235699265043],[-179.9997163604142,71.537528511774411]]]],"type":"MultiPolygon"},"properties":{"alpha2":"RU","radius":4262000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[28.857867039198172,-2.4466900921908121],[29.125297888368213,-2.1893766248220747],[29.141744762671465,-1.82085492854635],[29.351794201394121,-1.5177983359823102],[29.577016425376208,-1.38812236275936],[29.82533838953168,-1.3357988099286293],[29.942309080951791,-1.4008551004177581],[30.057750350843289,-1.3904088357866589],[30.360356571084782,-1.0748002299469357],[30.509642942279701,-1.0675034993890309],[30.531181112152247,-1.2364241153402489],[30.622257228779983,-1.349492670953536],[30.714075786685591,-1.4007877837569365],[30.812295029326538,-1.5631735569583065],[30.814523377675862,-1.9239983600910422],[30.876320754334504,-2.1433526525582742],[30.825009169732255,-2.3413634757276882],[30.553622597384443,-2.3999047434877854],[30.388213099823616,-2.3345672547638103],[30.231297073737476,-2.3564525360050919],[29.957234679606081,-2.4798549718307532],[29.863877286430014,-2.7189348552394397],[29.698018158619146,-2.7945217203215114],[29.467678335249666,-2.8080649132336428],[29.349968157727613,-2.7913373150888448],[29.284679054497175,-2.7006920863680857],[29.198427631254454,-2.6608666671099854],[29.103530681605275,-2.6653321993021377],[29.014292594025282,-2.7199238189150767],[28.921921590946596,-2.6818646270863713],[28.857867039198172,-2.4466900921908121]]],"type":"Polygon"},"properties":{"alpha2":"RW","radius":132000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[34.616489674258659,28.148280322545279],[34.775398625680488,28.498527506025688],[34.95095965983198,29.353250287380501],[36.062550088961579,29.206688345728292],[36.473849656733364,29.497273616120896],[36.755373509047224,29.865768175550187],[37.468977240125682,30.017514123238016],[37.633805403873069,30.313138321897913],[37.805487387952361,30.441650588335467],[37.841751661796202,30.573534461161604],[37.798052749832394,30.683011858283294],[36.959037355499213,31.491390018863402],[38.948469708360996,31.991443846792723],[39.145481387106948,32.124245177937802],[40.374685695908276,31.936738161774429],[42.074318334317823,31.080217180793905],[44.694064944158114,29.205642459716682],[46.330080417418039,29.065757645211178],[46.536339203224273,29.095692761245992],[47.433045752106878,28.989312788634983],[47.583322653135568,28.641635170164975],[47.68477124673241,28.549128915287177],[48.442356142368666,28.542697528719739],[48.623236522166337,28.141221609162926],[48.806842469221223,27.899737254782135],[48.888840573957346,27.652743814532958],[49.237217409534153,27.49258711784525],[49.253267284113164,27.352115490623323],[49.377492194060068,27.211447936083143],[49.537559966736133,27.151523604827322],[49.718808405721049,26.958983724277502],[49.986019117442034,26.828745858327384],[50.149511232777449,26.662618483190805],[50.119762610605278,26.525855480932165],[50.213639273839433,26.308494104208712],[50.105224265421803,26.011399245548667],[50.108390004689738,25.899898339633037],[50.256128896670646,25.599820812507229],[50.455001907008338,25.424670779017031],[50.568559361370845,25.080786978619493],[50.862339489135316,24.673138588670167],[51.006693246418493,24.56894552849149],[51.267929351800319,24.606984066953949],[51.411038047713546,24.570549074195572],[51.422201721101786,24.417821084952958],[51.568128750594937,24.285993107196536],[51.605938477603424,24.064319495109959],[52.531053704119302,22.966834454398953],[55.007980076496978,22.633202188551483],[55.185817467785924,22.703716800093268],[55.640749888971079,22.001821370205302],[54.977137931764418,19.996152620255938],[51.977586625928588,18.996358521419506],[49.042332979666085,18.580870235526216],[48.174941755463401,18.153390759237361],[47.58714180518168,17.455740239757876],[47.441619606352823,17.112005646230074],[47.14351584247629,16.946885497484864],[46.975802654594339,16.953695534140159],[46.7863005483104,17.200184240284862],[46.661165522622312,17.262300403281213],[46.310350553785348,17.231526979226501],[45.537631184289431,17.30184283987305],[45.140072498342505,17.427526404450763],[44.192346851438096,17.401432036284636],[43.917010204935039,17.324949319674911],[43.713123152255172,17.365777878963502],[43.53897533411898,17.497954400174262],[43.394000267591274,17.505227363556649],[43.267142348504436,17.415245726617101],[43.236683529442551,17.266498595259936],[43.139919274388483,17.128897833093564],[43.118932626683645,16.961174701220667],[43.184236686057993,16.811704784884274],[43.164824663753087,16.689590315732321],[42.799496327689994,16.372191699380547],[42.696016741162431,16.735442815286238],[42.383540400934081,17.122636292674027],[42.27575823808094,17.447257999124353],[41.750178981669862,17.885925700390167],[41.428917567472446,18.450714285510436],[41.229753535562772,18.678533087457218],[41.108527339889761,19.097246284976315],[40.757684103652551,19.727329354231355],[40.472793166749241,19.998557124967778],[40.119362955752408,20.239660694388586],[39.884177979084562,20.293233488024793],[39.728460051183333,20.39049853082744],[39.273211708379641,20.979250113372277],[39.093793300448596,21.310358782540309],[39.146354460380415,21.507713781175809],[38.988141813068246,21.881758555949251],[39.08925218079834,22.371853905567388],[39.064244979419804,22.574526082453211],[38.457971789050525,23.717825466273705],[38.098304985524571,24.057508269699657],[37.709652015460101,24.264565021506396],[37.543189736608106,24.291845818765704],[37.181159721748806,24.820022407469871],[37.255128777539788,25.010675192125888],[37.128111987000693,25.321152016620815],[37.013914557383679,25.391681498529344],[36.87528660672492,25.383282687268075],[36.737699635624757,25.520806371318869],[36.530436404698804,25.601761663200161],[36.504551565091454,25.645110330316619],[36.596525578015068,25.922376255069661],[36.568327888567111,26.050458412317404],[36.248464171033859,26.593956650408522],[35.851897325018669,27.070621394850654],[35.74879228370213,27.271194376561112],[35.581585090175494,27.432649240333419],[35.423004609949508,27.733248650522029],[35.207199542264597,28.001793043468453],[35.039359373168487,28.090364665631725],[34.745341189422859,28.116565019701913],[34.6251832598291,28.064897318212676],[34.616489674258659,28.148280322545279]]],[[[41.782547818097591,16.851703691118846],[41.860656019067889,17.002279030777519],[41.917118983960947,16.993418095424886],[41.992803499004026,16.852377177338866],[42.170167999251888,16.708535537137859],[42.15770488476636,16.571088310857636],[41.897373799119244,16.684433676910331],[41.782547818097591,16.851703691118846]]]],"type":"MultiPolygon"},"properties":{"alpha2":"SA","radius":1291000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[165.822343717645,-10.732291418959159],[165.90951943017225,-10.675338700778843],[166.00418455317299,-10.663391655938378],[166.16106129918055,-10.693683948178549],[166.16193017504517,-10.87828680794914],[165.81992256123129,-10.843040177616029],[165.79102497791811,-10.769515870009647],[165.822343717645,-10.732291418959159]]],[[[166.74421200728739,-11.556935349541734],[166.85496151092084,-11.57971307029802],[166.92733849279463,-11.664564203585737],[166.80646990090776,-11.676498399811042],[166.74421200728739,-11.556935349541734]]],[[[155.67862943495615,-7.0885380968216261],[155.73935254476814,-6.9739403589384157],[155.9731329533393,-6.957360315967593],[156.18669453212701,-6.8767648491108835],[156.49845819643474,-6.6137943446074274],[156.60375784318256,-6.641924878392615],[157.0297699489235,-6.8927406816035584],[157.27678703329568,-7.1906106188345156],[157.63083522339133,-7.4670245125308039],[157.83544315882756,-7.577362513284025],[158.14086394532509,-7.6248828791743],[158.45755669560793,-7.5457331491347146],[158.73384359953752,-7.6053234956019207],[159.01425927971687,-7.8195446392267201],[159.40928491208797,-8.0168886490568543],[159.82344208777414,-8.3081790091939123],[160.0366631595455,-8.3948224144875159],[160.34233441921137,-8.4098627836974131],[160.74894800326311,-8.3150332808598595],[160.99678088863786,-8.6124348911472399],[161.06974845996908,-8.8253274100485513],[161.25843266986561,-9.1915714259173313],[161.40596973494453,-9.3691265377689064],[161.5382656896515,-9.608198005742457],[161.57912598727913,-9.8833306557932108],[161.66662001304078,-10.099607630420483],[161.87253270067055,-10.331618966313567],[162.14216575608759,-10.491161808836482],[162.37152018755489,-10.822607215876582],[162.12378653741013,-10.823515262027927],[161.77274688436509,-10.708388790479212],[161.54493372102255,-10.575113416270705],[161.0834937075964,-10.087334975373572],[160.87085512746529,-9.9727536145295748],[160.31738772066811,-9.8372290558939497],[159.83780327947321,-9.7827577417793368],[159.69260077981275,-9.6512547854801927],[159.53954616996262,-9.4005877035188341],[159.43194409212367,-9.2872237945230118],[159.2345589765408,-9.1615789271780788],[159.07158637445517,-9.1087188638502106],[158.90436626688003,-8.9496111528589104],[158.70549049276715,-8.8405793217382698],[158.48400818743949,-8.7917418140903809],[158.17894592391161,-8.8247393100272031],[157.87903924460082,-8.7614848156355318],[157.64310240120784,-8.7930301838084191],[157.35019791992477,-8.7083143821917304],[156.99155503591359,-8.3350316213519022],[156.78684416400841,-8.2337352329906253],[156.5923552953904,-8.1953805594839988],[156.54086513912338,-8.072729758304094],[156.51172214988733,-7.7071659722333239],[156.41626825466361,-7.4918826295592051],[156.31937582233934,-7.3679474467441484],[156.06306408254332,-7.188115291393709],[155.67862943495615,-7.0885380968216261]]],[[[160.53985400509282,-11.747872094630941],[160.57479026154132,-11.797531555913574],[160.50643525255595,-11.831108953061714],[160.44354468778823,-11.813741929375032],[160.00411932543341,-11.578807761202178],[159.98718377547502,-11.495039951713842],[160.00044730215194,-11.473083122044603],[160.07682211765149,-11.494169198080234],[160.53985400509282,-11.747872094630941]]]],"type":"MultiPolygon"},"properties":{"alpha2":"SB","radius":456000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[55.383722587803987,-4.6093227295226802],[55.387367127677599,-4.6065083655829149],[55.451614323302941,-4.5616827243706366],[55.455691850530428,-4.5591341273892958],[55.458456781627675,-4.5630681750367161],[55.537487904631043,-4.6885506162949397],[55.540053416318798,-4.6931511531296319],[55.540484127366,-4.6984010366883169],[55.542828178898127,-4.7806164737601122],[55.54273419749331,-4.7851318386324539],[55.538817534439758,-4.782883038294341],[55.498822366027412,-4.7572181225317696],[55.494921858166897,-4.7544383722421522],[55.490851262406295,-4.7434200762677516],[55.482757332300821,-4.7252725461902623],[55.47290255609898,-4.7080177515384172],[55.461384211418249,-4.6918260163540229],[55.448315997149898,-4.6768571710978923],[55.433826911124271,-4.6632589749451769],[55.41694830478923,-4.6501291113741843],[55.413820132729171,-4.6466373203472147],[55.386454028368817,-4.6130298237643386],[55.383722587803987,-4.6093227295226802]]],"type":"Polygon"},"properties":{"alpha2":"SC","radius":15000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[21.825524526854622,12.790542689000741],[21.987342272337102,13.109156785006491],[22.214936107699895,13.298411504481173],[22.223310847833226,13.45914911766998],[22.106725833718667,13.799749075214928],[22.173298343491364,13.910444081603725],[22.440290531599715,14.123766249408678],[22.460411873169164,14.240245523104086],[22.381807523088852,14.550433171043936],[22.634058761452209,14.728709261963129],[22.763433288344928,14.998582751035611],[22.938993602157876,15.18265682720671],[22.967693728222436,15.302252465456069],[22.934113421697294,15.533044837939078],[23.105260675356629,15.702281035138562],[23.825967679516559,15.739501684504758],[23.974952925958746,15.877737199213156],[23.980503451326474,19.995717160721991],[24.799092443687019,19.99648853910379],[24.955914959987172,20.101184531116154],[24.980503179475555,21.995619747953921],[31.135876567358856,21.994873046875],[31.263472402304412,22.040862323532284],[31.400311196844036,22.202198724714087],[31.464114440707906,22.191310548199517],[31.555387924988462,22.039481000475902],[31.680101847480039,21.995858303148808],[36.871180553896579,21.996511878046348],[36.932940377650844,21.578316721459803],[37.258332957596288,21.108453791625958],[37.161457683159277,20.889205802088835],[37.227328464423294,20.556731622418312],[37.194053516848093,20.120804666197706],[37.262386592909529,19.791879649325406],[37.25510262921955,19.553011172594111],[37.458066907251371,18.85286672505536],[37.547956425797899,18.745410580015307],[37.729705360105918,18.694147143779524],[37.916160285704308,18.560026458088977],[38.141018864853571,18.328992768698601],[38.283000650332326,18.286470688019051],[38.60916769274916,18.005145415655484],[38.249085362484919,17.582092407108462],[37.766703300885595,17.446330723332654],[37.539308958055152,17.303889726949489],[37.410925500900809,17.061943430801183],[37.109384746570967,17.029942370936368],[36.996474782400384,16.927664467110237],[36.895858384655483,16.623233861815429],[36.912111345607975,16.291552171309284],[36.438146235330102,15.108263281705449],[36.524062225011299,14.256858873712012],[36.390469319956296,13.626128736092271],[36.164632662525754,13.108600057199796],[36.124933243621179,12.757131630274847],[35.664142490731024,12.615029722925581],[35.255543624098472,11.962150413056769],[35.115258628714855,11.814546526306545],[34.967942497282458,11.295550285302252],[34.928076075464169,10.86107402490995],[34.771327196926208,10.746476098078226],[34.63193307669615,10.825128504081635],[34.514391091687706,10.827346816202406],[34.350311553232046,10.668042730455904],[34.28317626827716,10.50581464909483],[34.310903000434379,10.1909269433802],[34.156736408588927,9.845932064690551],[34.077906264478429,9.4617471625437393],[33.89097183906248,9.4624632947300586],[33.872535627154377,9.5020734853910849],[33.952657201150565,9.8793235408009501],[33.904274617278141,10.177500938330649],[33.378682351795455,10.64066746077809],[33.130310167829613,10.746046966391459],[33.166753259448043,10.884088133836606],[33.073123613774072,11.595328055157372],[33.122856870855429,11.707601570309068],[33.148900879475647,12.039892670122242],[33.077747804045835,12.174076489840916],[32.920055097926465,12.219660270519171],[32.781333602882519,12.144975099172131],[32.736494146303372,12.009906567418188],[32.472874334539419,12.004694149735712],[32.346273172157417,11.919752612149345],[32.312593829415135,11.809131387676281],[32.33828577329983,11.333861229071299],[32.425151602139969,11.113927620886491],[32.406354242287186,11.061992386563194],[31.948642767751853,10.674346459699377],[31.658429416811643,10.225506168072705],[31.159571962929963,9.7729904485302477],[30.75549365131781,9.7314452714684254],[30.460684178223055,9.9877675447537708],[29.986025535207258,10.245813471446628],[29.667749656302899,10.100614812110202],[29.601421461617655,9.917479441220161],[29.473046752036801,9.7687883858850046],[28.986958682459374,9.5998095769530192],[28.863462512719618,9.4702843288108038],[28.844203793560123,9.3263119934272236],[28.049014461547738,9.3288337536109616],[27.899116828888374,9.5396059000831155],[27.774761158102812,9.5887855182779198],[27.065901251424588,9.6112141213456042],[26.658721987851489,9.4843989254460386],[26.55150548022965,9.5260265322170348],[26.175300081141106,9.9578522007018737],[25.919276378977081,10.169498308555632],[25.817536821766215,10.348676161433119],[25.670092336237499,10.394062449024167],[25.169620414849259,10.321407495899633],[25.057648070655112,10.249026363786115],[25.000277832603572,10.050765284176066],[24.800714400546156,9.7987538105658043],[24.782364580250835,9.5274274614473669],[24.67864675447715,9.3878566721657659],[24.531781338396904,8.8870872720760588],[24.252983828839142,8.7889488378881051],[24.147219524019459,8.6659173037247399],[23.679373717835912,8.7328246791234836],[23.537536490299548,8.8158957077974271],[23.463071954197385,9.0485373835613192],[23.619253108861773,9.3327459225477849],[23.630017151321578,9.8561895512470343],[23.317087946229268,10.380858417874933],[22.860341036767814,10.919707365836429],[22.926002419660495,11.281304378181847],[22.589349846908771,11.584430095436121],[22.567580000524988,11.925622425211969],[22.472707585790452,12.067849329810436],[22.365810318838342,12.605377873933504],[22.225889638563785,12.700013880402695],[21.928192224299774,12.678395233041176],[21.825524526854622,12.790542689000741]]],"type":"Polygon"},"properties":{"alpha2":"SD","radius":1201000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[11.147395939842667,58.988621703165862],[11.20054016821744,59.078680773926699],[11.295263089866296,59.086627679782026],[11.530939811409395,58.961415715739292],[11.701935251027319,59.03716826986922],[11.78459312408803,59.299750291286792],[11.681011778643441,59.592170534104888],[11.823673861868368,59.694156876242054],[11.932275407759652,59.863509553097771],[12.29165404900618,59.967854408655214],[12.496155757909916,60.157774902425125],[12.568789521459699,60.499932652938384],[12.314871202837784,60.892227193038416],[12.294422981335627,61.002520902179683],[12.662921398488097,61.062470822275444],[12.813786271108658,61.255015198495215],[12.804661081607831,61.367719640319692],[12.735873883085853,61.457463061303585],[12.155664544180695,61.720878915545057],[12.300465654526297,62.249193197306738],[12.114776381089653,62.591945650504336],[12.135537944041761,63.065326136387696],[12.000222600004603,63.291664985946241],[12.155295303561612,63.451034690078629],[12.175463866281918,63.595835065531425],[12.799519900125921,64.001748331417332],[13.209343978411159,64.075079886879834],[13.925927996933975,64.021469780940905],[14.066853654951309,64.099084694149269],[14.142895945159585,64.242029551086389],[14.085920374580637,64.425073614381162],[13.650770819944922,64.581632245853086],[14.121876884061702,64.952524932191736],[14.474310469191908,65.321818333476244],[14.624738614974351,65.826071991103703],[14.543560543459252,66.12914572915227],[15.030878610834426,66.166492333746504],[15.323741579832028,66.241733818542329],[15.423058239265892,66.352748450517311],[15.423226888301075,66.489723629201379],[16.288070950774788,67.009827071382816],[16.351415018015015,67.187561598268644],[16.301543448270333,67.292510933500864],[16.127758026481221,67.425868282156586],[16.193664281207308,67.504977802095766],[16.485105265757504,67.567976161146092],[16.594441594455031,67.642030746831935],[16.783744523334732,67.894814472493223],[17.324635726820066,68.103569323394169],[17.842471632357686,67.983536644994956],[17.958900545930955,68.003093857946538],[18.152174382973254,68.191626411011597],[18.162772546056999,68.528232128976327],[18.378607531232912,68.562228007109269],[19.853505672048243,68.39837290061034],[19.969047387205549,68.541862804262536],[20.154421321507368,68.613248714452681],[20.274334929596144,68.738117676064917],[20.26278407688752,68.907212173741513],[20.121562180915845,69.021059144290334],[20.622137572253035,69.036525765007354],[20.894993498820252,68.97961010415257],[21.005009059095087,68.882389113742633],[21.984644937583852,68.525262741929069],[22.850393290827888,68.368559128626373],[23.638585291574614,67.954252817500958],[23.519442384254337,67.830344431873556],[23.50678402255711,67.692169761756318],[23.581045204363416,67.50626740807607],[23.733408731004101,67.422678310439309],[23.774636991936113,67.328645790211894],[23.69505892766993,67.205167114457339],[23.716775725095566,67.055811662251912],[23.988280025152825,66.810627615988352],[23.910818377212394,66.729059509787533],[23.865346993438614,66.576750963188132],[23.733007747066473,66.46152421865915],[23.708606765253613,66.311353180656823],[23.766114435689477,66.211315289213289],[23.990224484045765,66.064785544584907],[24.155083136092415,65.805474413026616],[23.890546973255894,65.782503273712493],[23.682255686140586,65.822944361671077],[23.242043697543174,65.787029430869154],[23.102343832602557,65.735562453309385],[22.736494001953726,65.847036971043636],[22.372513535127432,65.784698060842345],[22.253856464146182,65.597773683859828],[21.644560168184043,65.336463625701839],[21.573682046632896,65.125932670899076],[21.334838396596115,64.959249921198776],[21.266467650349682,64.842891229780932],[21.339313738015417,64.618743109636682],[21.519317178057655,64.463005788263558],[21.494162993589942,64.416227826776307],[21.021122994632915,64.174659383996342],[20.762516812675688,63.868048583344873],[20.204639884893524,63.662628597758641],[19.904448484855095,63.604155836807408],[19.721938994431234,63.463576660171775],[19.362970896089131,63.438579265222778],[19.034325060039908,63.237986199011708],[18.637250675903161,63.165456330935598],[18.526971814613088,63.056677613851633],[18.462924673050821,62.896074673280182],[18.137659919948078,62.774688068960813],[18.037067771586717,62.600728462620175],[17.651577284446876,62.408750032878515],[17.633383906934959,62.233156724608442],[17.507054032828236,62.150098555917808],[17.391215345069966,61.907592730767746],[17.398817800871097,61.782292048403733],[17.464944576962012,61.684712286362746],[17.261335113358442,61.662903601248672],[17.164701715790645,61.524538473322274],[17.214385921140021,60.959605738030206],[17.298350690134424,60.751062204895248],[17.381598009525611,60.66593168193954],[17.555424459175832,60.642488643181537],[17.697223212426142,60.558841103681296],[17.955652981186148,60.589551137651142],[18.172706903421794,60.402529158330658],[18.557301208626594,60.253404093668728],[18.637672153194085,60.128345770967456],[18.852570855681282,60.02572799791853],[18.990205951331422,59.827762077282685],[18.97031557992079,59.757411601806197],[18.7307763389414,59.626425715458787],[18.617301726904252,59.327215771720873],[18.520260864607572,59.255086328398171],[18.485298795543649,59.104667096106198],[18.416075529892325,59.029338221788763],[18.17788495907886,59.081277675714659],[17.474454351474769,58.86239434416828],[17.006246529275433,58.662576240894658],[16.923601858911539,58.492740747801221],[16.785922130278358,58.400298292123864],[16.694681610678472,57.91768400482303],[16.587182747007162,57.762771838960752],[16.650474921198121,57.404283565895604],[16.516951039976874,57.296395316057982],[16.506096967006979,57.144771560602983],[16.604025966333584,57.028252354264353],[16.753711854321196,57.0138138123557],[16.871144607951511,57.107749348389135],[16.893439445464931,57.255096708328622],[17.025457410883845,57.344831966139232],[17.117251832300163,57.319756619494399],[16.47706578540215,56.240412978825603],[16.331084885683111,56.387520169020746],[16.20257207443834,56.417969431140705],[16.097438391806307,56.374474893231543],[15.996451122457152,56.222809971223498],[15.826651757242665,56.125206694806749],[15.626417115870003,56.184129906173077],[14.935965796788652,56.167806694824506],[14.824669707840384,56.128737796420012],[14.754612075194345,56.033356451105014],[14.494012474480815,56.022696854024851],[14.258275416084222,55.855878058548143],[14.237835157152533,55.719495752011873],[14.341405306375743,55.527788966363246],[14.173635972568258,55.396885724899398],[13.796397628485112,55.425864828721458],[13.32138396744392,55.346665529169819],[12.886258861667491,55.411544336568575],[12.969292817445139,55.645380437308219],[12.949540208110374,55.773870919834053],[12.471707296148431,56.290348785464367],[12.638731172922416,56.395251491362195],[12.713567551812087,56.541282846224988],[12.698650949057987,56.671638086249565],[12.579432731614691,56.813946056596208],[12.418516151854698,56.909925348002119],[12.071770109379187,57.371479409421291],[11.961681023404108,57.426281436956295],[11.867828346094834,57.636201743116473],[11.735175931262727,57.717863483894888],[11.68665366872513,57.917427270538411],[11.449575859310375,58.11846681476829],[11.388148768217743,58.308654495839114],[11.248489879061994,58.369357910627116],[11.209589391842355,58.834131341844852],[11.147395939842667,58.988621703165862]]],[[[18.105382147367692,57.271900226241321],[18.136777499531345,57.556506617591666],[18.409246287231692,57.759105680495701],[18.537505265963976,57.830278425556941],[19.030781198768402,57.915998170747223],[19.134932590726066,57.981126766904694],[19.330650906562813,57.962913388812161],[19.204340518273174,57.923885796447756],[18.881426793613638,57.696786721833604],[18.827966996130687,57.535874872103058],[18.907384634131752,57.398458578974186],[18.786948683726976,57.357952906604375],[18.699732669143248,57.242934741226669],[18.477858431260653,57.16228697349414],[18.335827810110153,56.975969888316939],[18.146832797353138,56.920777328808484],[18.209315269008496,57.101326632001239],[18.105382147367692,57.271900226241321]]]],"type":"MultiPolygon"},"properties":{"alpha2":"SE","radius":789000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[103.6505178459353,1.3256580922023764],[103.70541582568976,1.4232432422718142],[103.8179507433736,1.4467769004393325],[103.90425758873528,1.4175821202927106],[103.9135265275261,1.413888184702029],[103.9558271949971,1.3945302521305654],[103.96522957896308,1.3889018177227739],[103.99605398352169,1.3651920307286618],[103.96957828128269,1.3316367388402239],[103.819913059394,1.2656166773235193],[103.6505178459353,1.3256580922023764]]],"type":"Polygon"},"properties":{"alpha2":"SG","radius":23000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-5.7822418140739718,-16.003749540953297],[-5.7817402454631299,-15.999073522971051],[-5.7757990072251646,-15.96148451241972],[-5.7748392951271779,-15.956864194813615],[-5.7712100701272977,-15.953848053571514],[-5.7118102180740138,-15.909125490543033],[-5.7077971119246174,-15.906403836049748],[-5.7029704504102252,-15.906868338765301],[-5.6673689962044431,-15.912080177055927],[-5.6627234199682608,-15.912995602383399],[-5.6622645632509805,-15.917708227194421],[-5.6599632204315222,-15.965753805354863],[-5.6599814783084721,-15.970781637618787],[-5.6636833194745817,-15.974183960737625],[-5.6881413283965285,-15.994442853669954],[-5.6922421007795476,-15.997495258018166],[-5.6973168570934236,-15.998111987129516],[-5.777539833950601,-16.003659551169335],[-5.7822418140739718,-16.003749540953297]]],[[[-14.41470763752702,-7.9437674563695548],[-14.413064972621198,-7.9393921929456628],[-14.39843070156437,-7.9058434633515171],[-14.386218047173429,-7.8866066395554419],[-14.383534697993968,-7.8828537729271204],[-14.378941749139397,-7.8832887344979454],[-14.365019379641526,-7.8852776444262185],[-14.360491950481224,-7.8861415411064621],[-14.356836845776188,-7.8889494125192767],[-14.329365568604519,-7.9121681700049269],[-14.306211717678268,-7.932260345608201],[-14.302840901875964,-7.9354875604770214],[-14.30529779633761,-7.9394550514457318],[-14.313946740725873,-7.9520137926122452],[-14.316957895278792,-7.9559475359346976],[-14.32149117001846,-7.9579452367139405],[-14.359337943808967,-7.972400062022027],[-14.364414133798569,-7.9740324613805758],[-14.369721611917678,-7.9745454024022608],[-14.393435332124405,-7.9755573677026899],[-14.398589145577953,-7.9755065989604903],[-14.402723703820993,-7.9724292951545532],[-14.408457978559385,-7.967331508702153],[-14.410009580242793,-7.9624755268711249],[-14.413733001100635,-7.9483381633014867],[-14.41470763752702,-7.9437674563695548]]]],"type":"MultiPolygon"},"properties":{"alpha2":"SH","radius":8000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[13.378487816893756,46.261642906874151],[13.399732425941611,46.317351635253146],[13.700120374830346,46.520014716514318],[14.544847494634929,46.419482636023794],[14.748232073758798,46.496017415559002],[14.893343744625993,46.605633280633469],[15.448727961250295,46.632839652208894],[15.632676614206241,46.698231263050396],[15.848243981588185,46.711498829663512],[16.093094172369508,46.863089503790484],[16.283493610318562,46.857067505929344],[16.515800269756014,46.500247087373936],[16.37573156903612,46.503837710061191],[16.227389714012862,46.373061141429815],[16.095554657646044,46.36846688346327],[15.746645718003061,46.210854842182606],[15.673701840037243,46.099882220481902],[15.651922679772818,45.862301532999176],[15.412574042378255,45.755467250169701],[15.330664129633531,45.581644114459465],[15.339155107444283,45.467197161648542],[15.24206178573089,45.441706509644114],[14.96935763692669,45.491554786949116],[14.79750921457979,45.477326863973737],[14.590812872524818,45.574410193982366],[14.369885343150381,45.481657436186694],[14.019952809519484,45.480701606204306],[13.878692308522147,45.428577366578502],[13.615338887607523,45.476948309744834],[13.578275072040592,45.516875561601388],[13.636844354929202,45.607048709947165],[13.637299393483007,45.725903499447973],[13.487900157904578,45.987255676112753],[13.462857246251852,46.154678228253843],[13.378487816893756,46.261642906874151]]],"type":"Polygon"},"properties":{"alpha2":"SI","radius":142000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[16.862906129370735,48.441347654189514],[16.98541012786378,48.676815397673792],[17.13580228643233,48.840793509330403],[17.500475665503885,48.82954217770267],[17.758180044539053,48.88883804822207],[18.040562110517168,49.05705588292173],[18.161078322492603,49.25717321784547],[18.596521486094918,49.49125708541299],[18.836680571035856,49.510265565770077],[19.115447705311194,49.432224890171831],[19.441606968576643,49.597441852786567],[19.938831334521833,49.217416853240252],[20.363035513772761,49.384927415735959],[20.616104419447179,49.391415858264295],[20.900032616127284,49.31565588003577],[21.079496027703204,49.418009486058963],[21.353112801613477,49.428607311216851],[21.89004243649196,49.343211309927327],[22.077942465015273,49.192705095336677],[22.538360050823247,49.072513486974877],[22.29507112267736,48.685970777918911],[22.164592201270207,48.567559579572546],[22.111255806210366,48.393561487669928],[21.726539697544656,48.345638226397242],[21.568840659756205,48.489289649844061],[21.409341312476176,48.551728408734895],[21.072153293438216,48.506084958484323],[20.582842312368822,48.540724168586962],[20.475506243839437,48.488807207883582],[20.333627157146942,48.29576981117912],[19.944967440747828,48.145029485157131],[19.614439539710613,48.209579632123251],[19.466854246760438,48.110890675099434],[18.907198903659651,48.041571467370545],[18.814402174432335,47.969741507351529],[18.724093729299685,47.787414354916272],[17.766689002674866,47.770082046860765],[17.639109034520121,47.808702365962553],[17.314126158741111,47.988958694483067],[17.086119869545268,48.039714372853119],[16.862906129370735,48.441347654189514]]],"type":"Polygon"},"properties":{"alpha2":"SK","radius":216000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-13.292390465789737,9.0490810558633754],[-13.067937208828253,9.090623562001527],[-12.958618689414513,9.2631538211079985],[-12.773252737214834,9.3570935871031331],[-12.501252039161544,9.8619767754975207],[-12.431688372235014,9.8963278107705701],[-12.277756046954694,9.9295373062842671],[-12.082452367580203,9.8883070511265228],[-11.910949372241118,9.992774628440241],[-11.269210499241103,9.9953100776906005],[-11.205821900819593,9.9775466891338027],[-10.690712308738112,9.3141484860866335],[-10.597460522031998,8.8597501061416928],[-10.480861213427795,8.6026783403051876],[-10.401608997366401,8.5166889685830949],[-10.283465946900481,8.4849447283471093],[-10.314789455875601,8.3108797292811811],[-10.386362712936458,8.1608680748935569],[-10.566787833576033,8.0541733333502776],[-10.647663415385852,7.7595294496783556],[-11.266778619811953,7.2317283215498165],[-11.507505003722724,6.9068646520272265],[-11.921156261501652,7.1796428136565229],[-12.951020665303135,7.5709123922701931],[-12.867119099800586,7.6599906489701244],[-12.853553663719339,7.7945451716870755],[-12.950311818259809,8.1139882753386647],[-13.022874590848339,8.1845375113030769],[-13.148821611115851,8.2148080356202637],[-13.272523648517536,8.429789127349343],[-13.214212059521909,8.5930901347726643],[-13.212791270161233,8.8577844027102],[-13.292390465789737,9.0490810558633754]]],"type":"Polygon"},"properties":{"alpha2":"SL","radius":203000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[12.397175174796789,43.934552803695823],[12.400211293156763,43.938179106422325],[12.43767888168732,43.978706961565742],[12.441234804313309,43.982182962757932],[12.446138219774777,43.983009816325051],[12.498750413891964,43.98916568927168],[12.50353876794518,43.989484786238812],[12.505134971830843,43.984959049771817],[12.513209542322802,43.957815515930498],[12.514378963427941,43.953009503964097],[12.512149153046972,43.948594388743935],[12.487810329843988,43.905900373291495],[12.485092608349465,43.901657855558078],[12.480131518869285,43.900778902766696],[12.431171489377958,43.894689346859813],[12.426472362920331,43.894334648625993],[12.423516293156375,43.898004696561863],[12.399769103392988,43.930598107677504],[12.397175174796789,43.934552803695823]]],"type":"Polygon"},"properties":{"alpha2":"SM","radius":6000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-17.535232247161716,14.75500819955495],[-17.150097473572671,14.925711218022393],[-16.851672291266205,15.283874252590966],[-16.571904883786431,15.734968989540844],[-16.442762397426129,16.199741758384143],[-16.238882021903734,16.531089089994403],[-15.750196694086117,16.487733167644166],[-14.964212520897844,16.678604315153159],[-14.533767009472934,16.655762473970434],[-14.305362798604499,16.58197896950616],[-13.884845422151118,16.19757247962168],[-13.507099086900585,16.134998499514374],[-13.409870398624767,16.05897644342059],[-13.22364171158087,15.657754429197533],[-12.931105433214457,15.45281366120998],[-12.715983134656643,15.120101134711328],[-12.539587343390941,15.035962937023358],[-12.28077946576639,14.808836199003053],[-12.186778123260146,14.648109773491781],[-12.201514667330972,14.428692547962777],[-12.019406865312819,14.206416330742027],[-11.961120227653234,13.875253411821404],[-12.024505475067752,13.686463571372851],[-12.002966552984676,13.57455327569237],[-11.858653095014386,13.419778377963206],[-11.635105402543122,13.36968020233912],[-11.390628054370724,12.941931747839414],[-11.441418828114825,12.622652072654672],[-11.389609102074006,12.404668336006722],[-12.042310968229899,12.397417906177717],[-12.295654268301059,12.328524176121142],[-12.875582708740236,12.491517741341996],[-13.061118446168111,12.490174599895907],[-13.129571042868227,12.595388386467679],[-13.238002387941426,12.640562375177993],[-15.148419863027447,12.679733946712611],[-15.579195215899286,12.489510365455914],[-15.839563158739887,12.438078314156108],[-16.164851598608102,12.454425708394565],[-16.42188918617595,12.366663958355474],[-16.711713420505305,12.355095562958804],[-16.784576106174139,12.47251837464248],[-16.763086824651225,13.064065375004416],[-16.64869619237588,13.153865485042392],[-15.934511852228162,13.17353391207741],[-15.824773179695001,13.286322259796645],[-15.814410768924455,13.461039338773432],[-15.959740833502559,13.588164178568691],[-16.562127808306368,13.587540093445888],[-16.611304441490493,13.72143463208833],[-16.745180781421276,13.840599481206782],[-16.805272098547942,14.090556710248402],[-16.961882443261867,14.376171346294718],[-17.164987792152257,14.627615126412099],[-17.298259329068223,14.697075778417727],[-17.44499865070209,14.651938680008891],[-17.535232247161716,14.75500819955495]]],"type":"Polygon"},"properties":{"alpha2":"SN","radius":399000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[40.964678944307487,2.8145942830901451],[41.341168616017136,3.2021897244765767],[42.024238013639632,4.1377439062564418],[42.816794331642747,4.3044750911292855],[43.122333411530526,4.6420033466974449],[43.58776031597997,4.8559600873703985],[43.988889404640524,4.95032378884284],[44.853448982771653,4.9042297480178041],[44.963383781718811,4.9426546245416416],[45.936070859057359,5.9983536015366035],[47.621479295402644,7.6531005535276844],[47.678259745059172,7.7553804326141895],[47.659817085614698,7.8898761708395364],[47.541144979604177,7.9883259051442757],[46.978271602238856,7.9972795219954182],[44.027874368311224,8.9843874267075208],[43.651602421770711,9.3078866647911838],[43.482652969562665,9.3796817285163865],[43.173440472180808,9.8592500727206573],[42.841820422196861,10.203239918660223],[42.656720758839796,10.600031918854418],[43.24605172687162,11.499471908982487],[43.441061137814756,11.34630240657893],[43.631767509248647,11.035903038670607],[43.853386884756866,10.784977377389692],[44.261890627430915,10.483217135512758],[44.406120835890597,10.431365511286481],[44.893685546556931,10.436143577478243],[45.81670549049624,10.835674422842999],[46.574079508171664,10.751070178983181],[47.405029488752454,11.173788962369199],[47.74732896442611,11.1151164974954],[48.027176180887395,11.142981281670583],[48.572589812619867,11.320253687210229],[49.050471272160522,11.269681199050396],[49.642188556613661,11.450590122530015],[50.098219933718184,11.528630458831735],[50.454784657383584,11.725508052074634],[50.636067998101254,11.943584074067157],[50.787511701921396,11.982473444917547],[51.25460054229471,11.830533459054834],[51.090157790576818,11.34409219907498],[51.138992785582452,10.694212988434996],[51.194323863434612,10.564652671464611],[51.368964255319256,10.475054551292663],[51.38438156080877,10.386778695450699],[51.157024534968365,10.416419022787334],[50.924477910643013,10.297179944783231],[50.824744120781013,9.428262868400461],[50.430809963569317,8.8446238561452972],[50.102661675611806,8.1999405840504522],[49.852777185697377,7.9498467383921243],[49.673070174993377,7.4734956150652847],[49.230574256705424,6.7659525305172927],[49.047098082754772,6.169868589745402],[47.975081266124633,4.4971720566710616],[46.880455659417663,3.2874217756181348],[46.056216748183708,2.4800868885033673],[44.91078361853706,1.8034269391245679],[44.344140321253548,1.3991190346050333],[43.471673243779037,0.62540962524356714],[41.975869608997947,-0.98047899503400127],[41.532970069120744,-1.694677255565683],[41.504540058097589,-1.5515644397077684],[40.978926488565008,-0.87023841291278603],[40.964678944307487,2.8145942830901451]]],"type":"Polygon"},"properties":{"alpha2":"SO","radius":1175000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-58.054246806762492,4.1718488811705416],[-57.864847768168794,4.6116784491628362],[-57.914395421389017,4.8248422455067166],[-57.844788026676461,4.9228049855632978],[-57.710982656311295,4.9908499634905183],[-57.444764859327243,5.0082773220759664],[-57.330553169128372,5.0753414453678021],[-57.281157761742222,5.2089399425952188],[-57.318267106062095,5.3353241319454572],[-57.158748268047155,5.5999093929856318],[-57.056446040366481,5.9385035738116327],[-56.969771534926387,5.9926404344742021],[-56.049492994505513,5.837306275168455],[-55.828080526116821,5.9614596576510426],[-55.652582814389952,5.9853172590491894],[-55.389283014695962,5.9548634837268866],[-54.841021930036618,5.9884495418837336],[-54.352155657790782,5.908862968892997],[-54.054407406707305,5.8077586602988731],[-54.080639025448953,5.5023178871652512],[-54.421082121791933,5.0568865863364154],[-54.478886574910973,4.836530105772237],[-54.442392057186346,4.4389845749773302],[-54.331481494098689,4.0297972343867245],[-54.00367180860367,3.6159373672073527],[-54.009785336148383,3.4485966234003995],[-54.185650735786609,3.1583322830414207],[-54.195749292210714,2.8179539411521737],[-54.402152754233313,2.4616780114484507],[-54.536053084787646,2.3435251976201625],[-54.661762999391385,2.327797148102698],[-55.041946319382298,2.5459281587758933],[-55.200084240205577,2.5368024833255518],[-55.385466259064273,2.4408505519963675],[-55.879589214097379,2.4076396684523647],[-56.00281278828475,2.261119067846197],[-55.991116409485457,2.1437025363361961],[-55.915584946360497,2.0394727721652988],[-55.929844448289145,1.8876027159747826],[-56.019927443635858,1.8424530233845375],[-56.457369376603253,1.9338095712190604],[-56.704204256652282,2.0366806391251568],[-57.197143672420033,2.8534629680467734],[-57.310708345848298,3.2647686716156845],[-57.399598748938004,3.339061601292276],[-57.646541043496519,3.3946874619887204],[-57.69200903052716,3.5552843558066538],[-57.832444307318944,3.6761287997704351],[-58.032006899336864,4.0020628426838689],[-58.054246806762492,4.1718488811705416]]],"type":"Polygon"},"properties":{"alpha2":"SR","radius":301000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[24.14788629391273,8.6652662497288837],[24.213774462758117,8.7675902596171458],[24.520205629926075,8.9313999636059513],[24.648073627507099,9.1874867212983009],[24.673819800560437,9.3892306894922655],[24.780462744800566,9.5409277756710775],[24.787511023707928,9.7791792365874031],[24.986890397241076,10.028006644059817],[25.064142561171128,10.289986490198519],[25.206442139173472,10.329044232367218],[25.858021727784774,10.406274040656982],[25.926770973935412,10.182606540599281],[26.609698655183923,9.5031662594707367],[26.753906300167731,9.498708247053802],[27.074235524751224,9.6136368337046605],[27.880837900615738,9.6014036167087475],[28.000834508291117,9.3898299732494959],[28.124468265759621,9.3293784051683222],[28.746475859827541,9.3507601386494716],[28.999732142002795,9.6099682648883409],[29.499840845249604,9.7949674176850312],[29.590178781480667,9.9061729364354676],[29.605712972267501,10.064966287691941],[30.003024096361976,10.277104289329159],[30.719714105036509,9.7724575891416166],[31.185240124437552,9.7833146743793478],[31.661981632619842,10.229876662165296],[31.843518988612669,10.46246357382268],[31.933147688247274,10.66237170487349],[32.37188557913214,11.061115992811528],[32.335815339989821,11.692835843069251],[32.072771551033448,12.006515118936145],[32.576546583897795,12.017269346800559],[32.683468395044152,12.09540539254041],[32.721996760063163,12.222860354163519],[33.199079243652598,12.217067994196713],[33.079844878232905,11.548389480928357],[33.178346552213,10.813766755607022],[33.237742637671147,10.717874450371665],[33.375529729360906,10.649540575259428],[33.909019362784335,10.176544332945625],[33.963181668763738,9.8667649384782958],[33.89196977600475,9.635028005014215],[33.954631874283407,9.5139553740583871],[34.077882089893485,9.4613088748054768],[34.101747565328012,8.680327719478921],[34.07488284450924,8.5488771388306954],[33.953243573886823,8.4437143983121921],[33.398041937364134,8.4468267340784955],[33.262422125884576,8.4039700884064494],[33.071279295916938,8.0532088593333384],[33.05859360918955,7.9162840017618281],[33.172254664377441,7.7839830290221457],[33.665998263261315,7.6707547146299291],[33.902308800963112,7.5093610178374623],[34.086736518194222,7.2024232618456008],[34.27224588404755,7.0118974055382388],[34.484194343647239,6.8981862865031554],[34.710466681852637,6.6601736794376079],[34.958791432400538,6.0450064986530974],[34.994596372737576,5.8412545330780876],[35.088807147315762,5.6667405938025652],[35.268033590600091,5.4922999937610522],[33.489291458613344,3.7552956300823679],[33.158798554110057,3.7741451708078233],[32.947117953572935,3.8450041506808632],[32.347086583243744,3.7074568168251316],[32.135922867927405,3.5199443356393321],[31.795389012434278,3.7361854751776495],[31.484893685055759,3.6802580815682742],[31.171490480518113,3.7796768612183831],[30.938779344987232,3.6398477493945478],[30.838582580332506,3.4911955596498947],[30.743604998005857,3.604757594420422],[30.583369572694501,3.6277174441495097],[30.468733454330057,3.8562784915357557],[30.195098320682067,3.9821768567104967],[29.631888401731324,4.6007234999970485],[29.482314268343348,4.5910172504063809],[29.224809863807963,4.3921746030489404],[28.749095811223022,4.4990178893988277],[28.367221654571875,4.3189341307996418],[28.192154929717326,4.350450057616448],[27.841771495587302,4.5980315539235663],[27.256948725164929,5.2897363128209181],[27.222498566703624,5.5861436464385967],[27.143347763545474,5.7223891745085744],[26.750649499909148,5.9787269137482726],[26.51441527172776,6.0693886011524043],[26.324958326478715,6.3963826122473355],[26.328221892085445,6.5998701603672476],[26.276964382884486,6.7000472448926374],[25.902584104813769,7.0548148411903542],[25.276954931365669,7.429509172962006],[25.182458448028296,7.5524378157225005],[25.227852787658176,7.6747179105741425],[25.203693114737561,7.7862807828264708],[24.850782331977616,8.1341280254331298],[24.287947422749156,8.2946453399902254],[24.208631380647539,8.3692863136227764],[24.14788629391273,8.6652662497288837]]],"type":"Polygon"},"properties":{"alpha2":"SS","radius":645000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[7.3309471586639559,1.6033188678486365],[7.3877956652574852,1.679973062304027],[7.414452532567104,1.698854076553735],[7.436840323905213,1.6828941923784175],[7.4501460736615615,1.6618834150898651],[7.4521069497805001,1.6311465550466313],[7.4236023344851816,1.5679110713905755],[7.3866006879912591,1.5418222586600407],[7.3425991585825718,1.5637555771200724],[7.3309471586639559,1.6033188678486365]]],[[[6.4684592352827748,0.2273556297819751],[6.4777582060556815,0.28002952156733441],[6.5244641967777017,0.3401152382141619],[6.6259441633850722,0.40003363504141098],[6.6868107568243689,0.40415976284371513],[6.7495784206702467,0.32555520884368366],[6.7499874195722693,0.2487502462181676],[6.7497242035821507,0.24354694297830048],[6.7468701440907282,0.23918827441060714],[6.659759873618591,0.12085835690606124],[6.5568156172351726,0.047641455246720955],[6.5199050468855964,0.066477873768064993],[6.5177505781803875,0.07074395769490259],[6.4971292135904228,0.11743778265456137],[6.4684592352827748,0.2273556297819751]]]],"type":"MultiPolygon"},"properties":{"alpha2":"ST","radius":22000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-90.105736653216155,13.783025325648335],[-90.104517716510244,13.83468489245614],[-90.045118301423429,13.906730107152113],[-89.877126816447003,14.04255684525932],[-89.763966184306881,14.08691643444558],[-89.626954238723187,14.219779961158762],[-89.587285248761503,14.28807112627652],[-89.573419959436819,14.389956174169527],[-89.388421338495448,14.428140184266184],[-89.120547981445966,14.369968871505911],[-88.709911813602616,14.040192685474086],[-88.43405405115189,13.929712856463384],[-88.335931244215601,13.92221139958122],[-88.151028008997045,13.987087764404578],[-87.991916846511629,13.901533456831226],[-87.802315796111657,13.889786778990519],[-87.715579447307462,13.812663342174115],[-87.765919527240428,13.599105881236312],[-87.732469194560338,13.478209375893442],[-87.800786570854811,13.376304603517514],[-87.820962235564977,13.285264252827014],[-87.930949943022426,13.180872418074431],[-88.180633244110126,13.164362423080906],[-88.410361272780946,13.208634088956543],[-88.512034364233145,13.184206135874566],[-88.866977495998682,13.28342178780491],[-89.288574446236737,13.478332925121832],[-89.804114663039456,13.560302872323666],[-89.971137397453901,13.681971286775873],[-90.09502882502106,13.736678957748147],[-90.105736653216155,13.783025325648335]]],"type":"Polygon"},"properties":{"alpha2":"SV","radius":133000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-63.124446478926416,18.064392712822215],[-63.123858816740217,18.066317309897158],[-63.122868986524757,18.068692709102141],[-63.117932820348607,18.068945312499991],[-63.016240462828307,18.068945312499991],[-63.011436690724885,18.06870214263208],[-63.011422762345155,18.063892239929739],[-63.012033611496179,18.051090966416844],[-63.012622261070952,18.045480589860663],[-63.014460747212823,18.040147410496402],[-63.02115039063294,18.023818599057304],[-63.023183732000952,18.019486496234506],[-63.027797862596934,18.02075590193413],[-63.08614731879976,18.039994309595215],[-63.090340247549996,18.041589651628428],[-63.094179043933721,18.043911161919404],[-63.120770022797288,18.061676360049038],[-63.124446478926416,18.064392712822215]]],"type":"Polygon"},"properties":{"alpha2":"SX","radius":7000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[35.764726052733806,35.571648287438968],[35.89278369291894,35.916303504696117],[36.162894551979704,35.90931097862029],[36.318497657770124,36.022272557158409],[36.37561343430874,36.171099754566633],[36.510951685673497,36.271745386500591],[36.595129358900408,36.694584998247315],[36.658667390141758,36.802303329635663],[36.941656336474693,36.758172863911277],[37.088732886672553,36.656746089251897],[37.415157014024238,36.645003195339129],[38.197069376323718,36.901176620413864],[38.443633572245695,36.861970913879105],[38.757436580984546,36.696653865241217],[39.34286597799607,36.681534899237882],[40.002895604733887,36.822492577886813],[40.711641217028749,37.098273067054457],[41.521130554956343,37.090068428387724],[42.02356475488795,37.195638321441812],[42.20272512208728,37.297036460538692],[42.312684471385204,37.229460522860585],[42.358787161241956,37.108556723711033],[41.788443737197525,36.597445916293999],[41.427466144562274,36.513584330715155],[41.303426974406726,36.391703152376223],[41.247837230073003,36.095699485276654],[41.352380132768154,35.809900197473404],[41.353902389542604,35.640488824855183],[41.217049391920376,35.288089791448883],[41.194456371885494,34.769088402884549],[40.982665940624074,34.425499325569021],[40.667348230405238,34.320954635543799],[38.775308721081267,33.373104802438782],[36.818306551564064,32.317565848075958],[36.372157853537644,32.387092675793511],[36.227842493433805,32.489294118328516],[36.059604180999642,32.533992425540866],[35.931728870251042,32.685549416794309],[35.787595250119267,32.735087514636987],[35.884144154279085,32.956620208552152],[35.83729439980236,33.330484953149075],[35.941477199894564,33.531464114171811],[35.968726197530344,33.732256697620315],[36.48138640460288,34.082268191165205],[36.562095203652518,34.240386318950733],[36.490058127853089,34.42994223045087],[36.330061994540635,34.499843401794628],[36.252908429215452,34.595691814619997],[35.976425410364712,34.629417167069604],[35.88869124319028,34.941868615225822],[35.91103684669617,35.37496217773456],[35.764726052733806,35.571648287438968]]],"type":"Polygon"},"properties":{"alpha2":"SY","radius":427000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[30.789339939259971,-26.455515974256254],[31.088265973913575,-25.980781082638956],[31.207443882659554,-25.843493426094486],[31.335293007427158,-25.755864107631066],[31.415068671805223,-25.74680103751637],[31.818348466764451,-25.954190422580176],[31.948305130255324,-25.957970509134956],[32.060309022705297,-26.01852155871137],[32.045408497339388,-26.319299534564465],[32.105758953693226,-26.52005983713828],[32.11266147843331,-26.839277023575413],[32.020180959570119,-26.891479677598781],[31.969656611735381,-26.975170956319239],[31.947014121677842,-27.173635539043467],[31.958154595188724,-27.305641374893671],[31.469587486131513,-27.295177407516618],[31.274096007235116,-27.238212761126736],[31.063531437414078,-27.11214777221431],[30.908117225562748,-26.858724599507482],[30.794568728363064,-26.764188482125924],[30.789339939259971,-26.455515974256254]]],"type":"Polygon"},"properties":{"alpha2":"SZ","radius":95000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-72.341597872870494,21.795318255946828],[-72.33941300791696,21.812707069700821],[-72.336092654163551,21.832154855970753],[-72.332021535423493,21.850548840065585],[-72.25869164325924,21.860473359870053],[-72.184492106483717,21.877789913664337],[-72.112276489163435,21.902086804577912],[-72.042699858519185,21.933143635729962],[-72.010375743720545,21.94946039081459],[-71.990690367731744,21.950808974671649],[-71.950716494587297,21.951549231581758],[-71.931794427323425,21.9509924509005],[-71.858147620012971,21.905982058555669],[-71.778347609748593,21.868490622061536],[-71.694906570907847,21.840008303234505],[-71.668871946453862,21.832696050122017],[-71.657862729722581,21.818126075313302],[-71.647714566104895,21.803313538355809],[-71.63803330136659,21.787625108787818],[-71.665714593358729,21.752811712235786],[-71.676924258967375,21.755697364567467],[-71.731730424111461,21.772675402625797],[-71.811358827348784,21.788719779946167],[-71.848262005988843,21.794223139008508],[-71.934215095350922,21.802331231251934],[-72.020541401149245,21.801129708080875],[-72.106235535564835,21.790632562874855],[-72.190912951022597,21.770783084737637],[-72.210845820507203,21.767112008378675],[-72.279895127073331,21.757995174813502],[-72.300907448752781,21.75634501795788],[-72.318762417658789,21.75683495293411],[-72.334627957042642,21.758918735579194],[-72.339123498713221,21.777776473710603],[-72.341597872870494,21.795318255946828]]],"type":"Polygon"},"properties":{"alpha2":"TC","radius":40000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[13.448481489689064,14.380604378293599],[14.363844636560849,15.744378456443476],[15.425611038988622,16.857407455238633],[15.477704122532872,16.959470126182001],[15.734631274789859,19.899256565938856],[15.927938649794855,20.335121797148279],[15.885868275656231,20.443821375037299],[15.587325208853363,20.733455998842597],[15.53507225370628,21.05809125158493],[15.182077064615628,21.523462513104171],[15.169799651033344,21.936762082369469],[14.983371242801427,22.998141254758043],[15.984077086336706,23.444957208337303],[23.980025964807265,19.496476922575063],[23.970580002531435,15.721628292981315],[23.62668089647849,15.745772936863959],[23.138992384914058,15.697102847681702],[22.99029141957979,15.602568845452803],[22.932091754164556,15.162259567665942],[22.760171023904288,14.990564600782875],[22.670685783084302,14.722580108460086],[22.503218730024752,14.640602611196824],[22.428479177024563,14.53299183786498],[22.441707196351292,14.333457247912325],[22.538338762678894,14.161918904583013],[22.19538256361858,13.927622078072385],[22.124264661488855,13.831157550131268],[22.225946923558851,13.324440904423609],[22.157800151173401,13.21527850553921],[21.979117708438796,13.096378840061336],[21.878232403494192,12.919395217844945],[21.960350713492247,12.726410349547878],[22.238233933044114,12.707479701600455],[22.352178621926793,12.660265276953895],[22.412117599555518,12.550675859817897],[22.472947919613944,12.145073211972258],[22.580756695173378,11.990107133519459],[22.580458659322588,11.607357371290371],[22.658024792075626,11.506901102473231],[22.923814783886485,11.340399193929921],[22.93747324486317,11.19207737769773],[22.859906901155046,10.919928374633528],[22.465525844465038,10.985005301638335],[21.803536410463472,10.654327434758741],[21.735876224421911,10.563627764970782],[21.679715828718319,10.286751015748562],[21.508964107928044,10.17809466161035],[21.391883722656235,9.998324401713127],[21.199988327718906,9.9089772182997571],[20.773096741814772,9.4058535656953577],[20.342017578833868,9.1273513387783858],[20.102141879464998,9.1316290214826026],[19.673767002484045,9.0218075052114095],[19.132889982239064,8.9832174308480521],[19.040137127687291,8.8213355933543447],[19.108384103276688,8.6561553156471902],[18.568234378420239,8.0481095367204958],[17.640339281899013,7.9744221228010463],[16.789686352158377,7.5519117884219105],[16.613592782707755,7.7011025822926333],[16.498290909990924,7.7308101223857948],[15.84499922747653,7.4755082844974856],[15.480470525206419,7.5239724975084306],[15.529461079384994,7.7371318917320782],[15.119921541059853,8.549484652530456],[14.332492686212209,9.2036607147430498],[14.005159860698036,9.5888267476941582],[13.977497491184225,9.6914965329028728],[14.243350036443836,9.9794886336745812],[15.054926265626243,9.9642522327666647],[15.252635104192224,10.002985332955321],[15.345204742669576,10.097841903929263],[15.352652784608031,10.248245379066361],[15.198337003102422,10.490816683563295],[15.068077181242115,10.85496996681989],[15.030065705146461,11.118317348219943],[15.105341810261812,11.521519032327207],[15.071978547351883,11.872221450038655],[14.880855023944765,12.269448481847981],[14.843844954307624,12.501007056806097],[14.768220842015767,12.635818253363015],[14.548263837306507,12.816147417682084],[14.438945491214161,13.019238228082084],[14.064073322323548,13.078727661362027],[13.606578730924436,13.704695946520854],[13.448481489689064,14.380604378293599]]],"type":"Polygon"},"properties":{"alpha2":"TD","radius":1219000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[68.769778068371451,-49.06590115389902],[68.827983821593818,-48.924039343306788],[68.832291070656254,-48.848823535157386],[68.958882794707549,-48.694035203054284],[69.002519175007137,-48.661434672243352],[69.057147825159944,-48.6566703012925],[69.081063338490296,-48.679407402717622],[69.148460333906087,-48.807335414913389],[69.207808168270986,-48.857069396494303],[69.262379906578275,-48.877397302636489],[69.368622017514227,-48.890641227823849],[69.413162814701394,-48.935585486502077],[69.465258809062348,-48.962983039601248],[69.592527530316914,-48.971242804803538],[69.598392121970477,-49.032245010067435],[69.621416724170942,-49.087371894148795],[69.683589906681689,-49.16654911239597],[69.750589891181704,-49.206325997951907],[69.847201372177963,-49.215498145181101],[70.06136429175065,-49.136269570074383],[70.196091004411258,-49.127455164744823],[70.325200464466192,-49.058740885231089],[70.406221895614109,-49.061310780576143],[70.48415437924919,-49.084079884366226],[70.530687753187351,-49.137017523309076],[70.55524651627114,-49.201473838547258],[70.53352343198334,-49.269478912870113],[70.340279901790964,-49.482609167707515],[70.306900504764812,-49.583327572883618],[70.249733418466889,-49.616478705824484],[70.207251848321164,-49.664852521856659],[70.124263993405663,-49.704200394512107],[69.919027426613752,-49.689074588073012],[69.765348776898293,-49.630827113653069],[69.612903243805974,-49.650761118401221],[69.477691960357561,-49.617219637305972],[69.337817217270015,-49.559286486293011],[69.257001949030112,-49.546477230483831],[69.166385903765629,-49.571153066189765],[69.085777610153585,-49.65277158917592],[68.992907642332383,-49.704764283874496],[68.872675862104984,-49.709572497126558],[68.814896768935668,-49.699379959290617],[68.783036196609345,-49.651221543918737],[68.840507827075584,-49.443897026009395],[68.799103335805725,-49.231611523965029],[68.801174283718709,-49.14271996549305],[68.769778068371451,-49.06590115389902]]],[[[51.659602088029715,-46.373708044351858],[51.741876386775012,-46.327127717603958],[51.811960653912365,-46.390748014604227],[51.83424930723492,-46.439755413287017],[51.761745517935466,-46.448443964265138],[51.696721724530157,-46.427936763116186],[51.659602088029715,-46.373708044351858]]]],"type":"MultiPolygon"},"properties":{"alpha2":"TF","radius":75000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-0.089901436419667094,10.715492476817001],[-0.0039520764040121836,10.952126921649034],[-0.067920546150025865,11.115272107335795],[0.5534098774874705,10.971916798509419],[0.90020463560679786,10.993028585928091],[0.78513403795007553,10.666929878197211],[0.7868680386449749,10.394172741000975],[1.3319217047246483,9.9920681712573245],[1.389642639025489,9.3627358939312924],[1.5999973409072359,9.0500103878159539],[1.6247070311479594,7.0023780660022412],[1.5835751476897668,6.858603838988599],[1.6116243541989244,6.6320221559515193],[1.7776480285831371,6.294793196788385],[1.6225314591923621,6.2170162113716367],[1.1873003322674511,6.0897035455957402],[0.73710663473989113,6.4527395043070213],[0.52580166116522442,6.8509524645148376],[0.61835791981439647,7.2411768824466387],[0.50978143439251467,7.4352000561229525],[0.50025041870576603,7.5468048430702419],[0.60039950019248989,7.7491386181503961],[0.58383049691403233,8.1457783835044406],[0.65260402964981512,8.3843983945162144],[0.37774092811843507,8.7273892701765341],[0.46298547559797976,8.8944420109108737],[0.47394587255040643,9.3093349425028986],[0.39639394478009554,9.3986147125302288],[0.23365677956146963,9.4635160649738612],[0.34412423549159576,9.8542474620271978],[0.34810388750997751,10.253734876822319],[-0.057556771632888262,10.6307719515671],[-0.089901436419667094,10.715492476817001]]],"type":"Polygon"},"properties":{"alpha2":"TG","radius":306000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[97.374227471057822,18.517736397444491],[97.548643495935011,18.515611722550407],[97.716109118953014,18.640028406670982],[97.706629964365504,18.936378927729383],[97.794844644149364,19.142690410308052],[97.817057355350869,19.459856546412745],[98.019285650963212,19.752037706715136],[98.375122324920198,19.689401692038476],[98.879713286962712,19.792036269818826],[98.972161668577243,19.867189946510738],[99.070758907072559,20.096782603747858],[99.388488382171076,20.159648252183629],[99.458953431872516,20.362784606146164],[99.712549339112414,20.326091118556562],[99.890383715912947,20.42416580714718],[100.12030865354539,20.334307805181588],[100.31790585152002,20.38565493245347],[100.53975439311193,20.132326031883593],[100.41321138536942,19.753105708047663],[100.49005380590059,19.57637277887677],[100.64577503858787,19.506054597727847],[100.962257823578,19.610410308697176],[101.21171859790876,19.548203339074082],[101.20331678515525,19.308740489014649],[101.28607728184579,18.977184463243777],[101.0816272911875,18.455820689863909],[101.14839424027879,18.222149156488943],[101.03937268296434,17.846645068840434],[101.10074042390369,17.689475324595993],[101.20267443034329,17.637336314513558],[101.33395381234709,17.658006913795035],[101.69422336327708,17.898761241820868],[101.77879936571247,18.036245424199311],[101.94252333247009,18.087918909161377],[102.10151606278322,18.210394309644904],[102.44995801733444,17.990956270033124],[102.70389967411192,17.909804002358673],[103.02334211787007,18.025940348425149],[103.28838946549772,18.408188126660139],[103.48795103826905,18.417907999634689],[103.81093613027205,18.312794225807473],[103.94954975210825,18.318739488901965],[104.41755246160533,17.71068761140209],[104.73939001354167,17.461489328252632],[104.81390023653555,17.304763147059045],[104.74388013834353,16.873413634783947],[104.75147254937667,16.659025336101209],[104.82347284002428,16.468789048075116],[105.09359739752975,16.131031153217702],[105.40598760256174,15.987343103789323],[105.43770086585739,15.824971644265021],[105.64082084104901,15.656505396263064],[105.61542776228086,15.48835806831455],[105.49186857925616,15.25641825852756],[105.54645208739542,14.932486432198742],[105.47723711546142,14.534717672120779],[105.0740172037094,14.227686323186875],[104.91008832947544,14.382254122257102],[104.77889147152248,14.423379461287283],[103.98808564772521,14.358327084357988],[103.56925917747262,14.417114839665398],[103.20902670883673,14.333301921210644],[102.93832849331613,14.164348792811076],[102.54671188083312,13.585909565257943],[102.36635556807704,13.499556602855829],[102.33122460287147,13.29789222592127],[102.46148008323249,13.014950647456727],[102.50577321357778,12.705493043653023],[102.75540201346783,12.426235430881841],[102.71074738707459,12.255533337634645],[102.73575637544492,12.099641919742236],[102.93349982341424,11.706872330200662],[102.73906065425274,11.740719513602055],[102.64061915829672,11.681635667579725],[102.58977968569457,11.572566344437909],[102.53307922754249,11.615034991729928],[102.56110620241759,11.848237558189945],[102.46973613826108,11.94907489544628],[102.30214130791228,11.981017277891191],[102.25087277030782,12.304381598008192],[102.04860926174217,12.517955256927294],[101.80692883844583,12.652967875534141],[101.69446732477282,12.678338266988103],[101.44490991305062,12.619198367224801],[100.89795586832221,12.654008435439804],[100.86356432701355,12.714531416944965],[100.9410632798009,13.209479723373052],[100.91157099739182,13.375273314195805],[100.82524400145316,13.473226832830786],[100.60093319618879,13.527441754594083],[100.2451631343726,13.484557942204258],[100.10337860338593,13.423047548795484],[100.03755261467566,13.329575577281407],[100.08969900491216,13.045663370524029],[99.967784970820759,12.701265408711601],[99.98887116572746,12.170853199537209],[99.628264732874115,11.46254455089742],[99.486634184329873,10.889651700269114],[99.291244834875698,10.577457091171384],[99.20399686525721,10.331600357312613],[99.169657511880558,9.7028580512577047],[99.328910389396526,9.3251759813824879],[99.504235959897045,9.2514307420305624],[99.862978756071485,9.3608618168175806],[99.993327178311205,9.6569679548186365],[99.983725222527141,9.7933078408147569],[100.07025074297391,9.7531399567776376],[100.0535316144003,9.4615874012919878],[99.925525194321679,9.3887765276280319],[99.870350876600412,9.2650788329205369],[99.961751055150131,8.671502135683939],[100.02333912495867,8.5618182945521042],[100.16335973187353,8.5081675906561554],[100.22854206017453,8.4246174036261046],[100.44857810262997,7.4657141568174321],[100.5356952879455,7.2522263153150099],[100.79337918932964,6.9957305560570564],[100.99387763653087,6.8790513633139687],[101.3019708410435,6.9080345517763719],[101.49780014339422,6.8650408642358638],[101.80894367609957,6.4686473686808847],[102.10078996173965,6.2421790073732168],[101.87344932674459,5.8254696736616118],[101.79065078252967,5.7795669495093005],[101.46121250847328,5.8688638997489369],[101.29133452719903,5.7998941618302879],[101.11405161179999,5.637011182061161],[100.98189930405988,5.7711256880703568],[101.0789771963673,5.9876451987734569],[101.06353954055974,6.1359147705636667],[100.98493707892324,6.2191482505008882],[100.87405323415662,6.2456502440738184],[100.73753915623006,6.4198991823570344],[100.31064468977357,6.5534870255786872],[100.19592016801504,6.5255689732044262],[100.11909932040372,6.4424030881985956],[99.98507178957648,6.6005346930221647],[99.876229006039566,6.6452935768443755],[99.761057929598948,6.6210916686430936],[99.644161618781638,6.5163787219974534],[99.606919347839877,6.5968331605535866],[99.696940004305972,6.810248576377834],[99.682491748231442,7.074233228223803],[99.506974396363688,7.2889733296379617],[99.296495258275911,7.447227256038131],[99.068026505232339,7.4960430467985066],[99.034229343248597,7.8718241843910226],[98.884263634841588,8.0168146277025496],[98.694535736788893,8.0330590686111325],[98.579861247990024,7.9172814752413663],[98.296455329075016,7.7763661771161088],[98.262565902437544,7.9260793163303074],[98.307675034745586,8.1952102357817207],[98.227111206608754,8.5436544607019904],[98.288837359168781,8.9036328477713376],[98.257759213247184,9.0908290491043307],[98.353333244214468,9.2193602221324547],[98.701830633075872,10.19059483107597],[98.767301009821878,10.431029056442911],[98.757440518750428,10.660876148850948],[99.189396545039813,11.105814439542613],[99.475455261900251,11.608421653097368],[99.576453353826324,11.702852549194677],[99.600038413888313,11.817033992484669],[99.377962282935997,12.558872617283651],[99.220059173033604,12.739881159959586],[99.122826863167901,13.035606061774693],[99.175130542057971,13.294223632133138],[99.123389890278332,13.744848246443654],[98.93298040695575,14.049270515544114],[98.570202406940339,14.360077282288589],[98.246184315517112,14.814836446508014],[98.201271976128297,14.982133646345464],[98.19124489289176,15.203966826510243],[98.473034521543639,15.370390865807511],[98.544656881014475,15.466398518304203],[98.591587561326904,16.045829600552217],[98.676133840395408,16.145836758432498],[98.682894340813021,16.282696977865914],[98.478375136541771,16.732288974575326],[98.446113246718852,16.954447206263222],[97.706671857910251,17.797247402545494],[97.707660597889728,18.061534272970682],[97.616628395177912,18.23428977606461],[97.450953665884143,18.359803627764084],[97.374227471057822,18.517736397444491]]],"type":"Polygon"},"properties":{"alpha2":"TH","radius":830000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[67.349899930456743,39.242086278328166],[67.426358671080393,39.46541337758709],[67.719067768481409,39.621142091001744],[68.375366799810337,39.531035372750793],[68.512358977213893,39.569194403975828],[68.699242552924915,39.951115540191637],[68.630838978819853,40.166950642891834],[69.180061483995146,40.260641340550862],[69.251398992191099,40.394884984200722],[69.206549265274305,40.56644200307111],[69.360961939299415,40.769347105472058],[69.413887379577403,40.796883418606996],[69.735193838934066,40.673809411465378],[70.261971080493666,40.878956141238412],[70.372811380038414,41.02744496901397],[70.572052328773168,41.024548101681454],[70.648972267686645,40.960738011975131],[70.666538102034522,40.835324913116054],[70.750862058056867,40.721847418728366],[70.530470970838508,40.52327696081403],[70.526396096378605,40.337395341329113],[70.662217088579297,40.218175028118409],[70.960703829211425,40.220684271841371],[70.75668481821684,40.126868737535119],[70.673098697979214,40.018606472711355],[70.701419473054372,39.825409946861697],[70.622296294242048,39.749338359251972],[70.595432101745374,39.636374922196076],[70.702342682236392,39.447487473787284],[70.93320249465971,39.406787689114573],[71.113552207168723,39.511857492948323],[71.470254409624431,39.603431382932492],[71.581800741800208,39.480721097380076],[71.827384354397722,39.344221447515586],[72.04267167628015,39.351894764040928],[72.21166117093297,39.27058249024109],[72.567114171953364,39.377626058881759],[73.116250501659138,39.362615712810182],[73.464533766957416,39.459330551077308],[73.631439992142361,39.448688976796056],[73.629939107230157,39.210109647557751],[73.805045683764803,38.968662980811438],[73.732891868327258,38.79268586252703],[73.81482076915762,38.614589842058095],[73.96069766340716,38.568127984676252],[74.131414998792891,38.660967066479792],[74.277418447099691,38.65956009462947],[74.8120743928108,38.460121435139456],[74.83569325258722,38.404304052046854],[74.777163136081441,38.179363029454102],[74.93803152931855,37.772486449194837],[74.927294781091305,37.625863179745153],[75.11848589091538,37.385753542774808],[74.891305366046083,37.231920452744255],[74.676245812546895,37.38148861584763],[74.340877541061232,37.416577583408994],[74.166954478250773,37.329686729050614],[73.744109265167523,37.232220298919721],[73.627782195281767,37.261680087664004],[73.537587782790879,37.432701769499758],[73.383125057551752,37.461192301425278],[72.895906442642698,37.266817038441189],[72.657276541546537,37.029285109783771],[72.359135343118865,36.981646460248093],[71.801958892437057,36.694571407437408],[71.665707260393802,36.697159121357359],[71.53343302381883,36.84078123076565],[71.433147845011064,37.127560883402033],[71.506712653873635,37.778068703667891],[71.431466127603144,37.869251971428767],[71.278742524399718,37.918554042403265],[71.315196931673199,38.168382821292496],[71.250163823890432,38.301208716440364],[71.060106221417186,38.412550560107],[70.869921788843627,38.450931498909405],[70.625976485563214,38.340297188454308],[70.290576840464794,37.96208861891273],[70.244260057512406,37.853356224615901],[70.251204521024704,37.6642537189678],[70.188505734828695,37.58266196272141],[70.049015299820709,37.547004869673231],[69.82089907762591,37.608954508442409],[69.517933174963758,37.557094713601337],[69.421505316321799,37.44101817316966],[69.414203087993769,37.207898882439643],[69.303821872107122,37.117142626539604],[69.008772840198773,37.288643818141843],[68.908551769959374,37.30188706817237],[68.392485543014715,37.138159201254233],[68.260855728387043,37.013311325194962],[68.067743180692787,36.950034772747763],[67.963416895904274,36.970926674344327],[67.766259376651306,37.14026199157982],[67.814565057092793,37.486947011778341],[68.292541090390557,38.032508464272659],[68.31509569007919,38.199501937056787],[68.087433098734309,38.473634272093456],[68.048132197331526,38.669240898878968],[68.077334479713826,38.815389004024759],[68.026237248871396,38.919563548644405],[67.925900308640237,38.977836708602695],[67.694533477203052,38.994856939268054],[67.595265148894796,39.13826892779884],[67.349899930456743,39.242086278328166]],[[69.476065372637791,39.919868872071639],[69.421345559687893,39.71671056110717],[69.576557977559787,39.577378539807121],[70.317735391055834,39.562947767580049],[70.435128406238491,39.609177944858509],[70.508268970965887,39.722675481668794],[70.47644449615531,39.942645074106721],[70.414737756369433,40.041671663863667],[69.995873169999854,40.193055191422623],[69.599507572353062,40.111124008204818],[69.476065372637791,39.919868872071639]]],"type":"Polygon"},"properties":{"alpha2":"TJ","radius":396000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-171.20412689026321,-9.3334660972271859],[-171.20279571495638,-9.3330677527642987],[-171.2017166953778,-9.3328562719132364],[-171.19804804944204,-9.3356923595121746],[-171.19460320162048,-9.3389192329068429],[-171.19182916399092,-9.342738186120938],[-171.19175103562466,-9.3428572388695255],[-171.18948624298673,-9.3466681523038044],[-171.18792684711332,-9.3508383434384559],[-171.18668335177381,-9.355423692304953],[-171.18765644795764,-9.3568124820895786],[-171.18862007857723,-9.3579376352264063],[-171.19172204603569,-9.3541935350014285],[-171.19594269140006,-9.3493371574187929],[-171.19986317455155,-9.3446119378545909],[-171.20171793217716,-9.3403868919643891],[-171.20265112869583,-9.337960581015885],[-171.20412689026321,-9.3334660972271859]]],[[[-172.49841836942818,-8.5480103230388753],[-172.49789146087349,-8.5474987284092094],[-172.49703091551942,-8.5468129320021475],[-172.4934595420381,-8.5500106979135033],[-172.49072559544243,-8.5527173050432044],[-172.4874472976928,-8.5562960652159106],[-172.48492565422089,-8.5604428802241728],[-172.48320093334894,-8.5636185567502601],[-172.48128825277109,-8.5675420523235317],[-172.48046411091198,-8.571828420798342],[-172.47986395593426,-8.5759094746469255],[-172.47940518217604,-8.5806253455941359],[-172.48153991831049,-8.5817665849278804],[-172.48357456644078,-8.5826122942537779],[-172.48547714256779,-8.5784554161883779],[-172.4861152536246,-8.5768635692509676],[-172.4906464463578,-8.5664294075640495],[-172.49305792216876,-8.5612822239002888],[-172.49492935763237,-8.5570336933637545],[-172.49686130288836,-8.5523563522176165],[-172.49841836942818,-8.5480103230388753]]]],"type":"MultiPolygon"},"properties":{"alpha2":"TK","radius":2000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[124.91581321391632,-9.0321983168440365],[124.92251130361844,-8.9426113640141409],[125.02437561755661,-8.8568455526080037],[125.11588438597023,-8.7081351797844562],[125.17446274047742,-8.6512932484183587],[125.42075804569455,-8.5623238420872099],[125.49773374867672,-8.5001158010969746],[125.53727565484432,-8.3887066589603112],[125.5074115468888,-8.275120817294507],[125.58433382061246,-8.1788689129157888],[125.64572143448987,-8.1403185335972523],[125.60453759351404,-8.2581796302328332],[125.61238926811347,-8.3566651209500122],[125.66642641334475,-8.4393756257911292],[125.77304827791815,-8.4899641716264274],[126.17882393154053,-8.4886619918847579],[126.60158417433719,-8.4617904062796043],[126.74183635635457,-8.4197849164500038],[126.96641267877921,-8.3159459051278954],[127.21475733512494,-8.3732146003520089],[127.2957350388243,-8.4245233712318335],[127.11966174048392,-8.5791151310693898],[126.91514628109999,-8.7150514344189016],[126.65753991973145,-8.7871203909834765],[126.56211939016114,-8.8405074477687116],[126.48269312973598,-8.9145116165892873],[126.38246241790331,-8.957408461826903],[126.16476444533713,-8.9980495889001748],[125.94600163017832,-9.1236820590821512],[125.80931121570738,-9.1392487217290661],[125.40862346692903,-9.2770378786916403],[125.06829481760346,-9.5115319106477134],[125.02660757002768,-9.3732598755493335],[124.96848302346969,-9.2941209351547496],[124.96136733015372,-9.1208009079174346],[124.91581321391632,-9.0321983168440365]]],[[[124.03661596498537,-9.3416969450473974],[124.19825982260362,-9.2564465372438605],[124.44416953285011,-9.1906341695637028],[124.41278392825585,-9.314225684064569],[124.28662402737518,-9.4262846419837949],[124.09025594164005,-9.4162089170495733],[124.03661596498537,-9.3416969450473974]]]],"type":"MultiPolygon"},"properties":{"alpha2":"TL","radius":163000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[52.494155172975503,41.780299119388459],[53.059874221236534,42.148954928628413],[53.681897310285294,42.296152165740239],[54.12095191078091,42.334935316125573],[54.853692018733227,41.965034870861331],[55.23348558623497,41.476970662316269],[55.481531938852427,41.277524807908996],[55.934970699525287,41.323912028188431],[56.842120549204658,41.287285317776409],[56.946165673150098,41.337701956338442],[57.004924845839525,41.437273964882863],[56.964311297478545,41.856443154185399],[57.228938618559113,42.084318543203302],[57.381789321861504,42.156026632975824],[57.804595420417868,42.197120358098061],[57.945939780639613,42.419885696029858],[58.155539516241575,42.630846637815473],[58.259692141232833,42.687862069068998],[58.475076833890519,42.679131099375212],[58.589131917751828,42.778134195921176],[58.886442860181468,42.558734336615352],[59.159438676324541,42.511176489259547],[59.339619353761066,42.329495771776863],[59.858216549052223,42.29494018888817],[59.984987176584674,42.211484173166816],[60.013101561291585,41.991042050485042],[60.193337043118504,41.829932509051112],[60.120531434565351,41.503330153921183],[60.230963506048198,41.333510644653849],[60.496184002278909,41.217851155166329],[60.867150904221184,41.248405599972145],[61.251352675975191,41.189821486339177],[61.496951299190577,41.275874728790654],[61.898711286545719,41.09649264409402],[62.107673526253677,40.664061948720985],[62.372566959213849,40.336361056614862],[62.492962348397469,39.969846899889482],[63.501661720381961,39.379610591719931],[63.757293877970199,39.16465116693832],[64.124274504005044,38.973999386859965],[64.309901096260759,38.977044342681026],[64.630082303829141,38.752047039422351],[65.605407990532854,38.243282391546906],[65.964790424016101,38.24561383479513],[66.578845002258433,38.00750438674865],[66.629027134930425,37.932101827697046],[66.523543919610475,37.748381021773412],[66.522002780231134,37.348705762985972],[66.114099358262777,37.413648257979339],[65.792704858770847,37.530725415486408],[65.655497601545662,37.462408994198157],[65.554814552097866,37.251424152836321],[65.098853420613935,37.23751122428034],[64.868158359707891,37.15468225480052],[64.782945342424739,37.058001706531513],[64.507376736424874,36.337934098919618],[64.121575736049081,36.120508215721799],[64.037805951850316,36.023280574260717],[63.283995549429143,35.850110242871146],[63.099879834791729,35.59909600291121],[63.056427277983772,35.445970709842669],[62.688033296330538,35.255463767642361],[62.425999173195088,35.240749192487137],[62.307784369473453,35.171094958298205],[62.014158813690571,35.42417127862354],[61.621043752302199,35.432534678630354],[61.262141108423791,35.619811250353614],[61.249537621577289,35.851438367601567],[61.153205987632241,35.976864505434385],[61.211308170187635,36.148322214306951],[61.157713951941247,36.47730840333864],[61.056531673176501,36.618288684409585],[60.341384601359913,36.637874607661736],[60.041800290755525,36.977364618553104],[59.449758207504601,37.257655491051374],[59.258249739174353,37.512335121837594],[58.845403795495152,37.674112149928831],[58.392608880124747,37.635732895625658],[58.266949747523824,37.664067102579637],[57.977093845048628,37.831610363671935],[57.35383960814832,37.973550567320814],[57.294628909107502,38.134945069075769],[57.182764819155388,38.207369967706491],[56.471741978979701,38.248482710577896],[56.363348184476749,38.202645423147445],[56.26758723435772,38.079691295179224],[55.587268065526104,38.098827451612593],[55.398272807175566,38.055409695032928],[54.900784627501743,37.77714511088837],[54.694730500134995,37.468172440467953],[54.197317495273566,37.333587579880813],[53.914353876379487,37.343788952685166],[53.823610363571056,37.933320282830707],[53.867217856881297,38.938289917935911],[53.677304011911886,39.175567749656274],[53.493774435649442,39.29649634948801],[53.284254751511405,39.338142564290905],[53.141526570725944,39.249490046620501],[53.125012964863423,39.432019059917771],[53.171931063802241,39.646355704746242],[52.807936012501919,40.049090446317557],[52.733932149911929,40.398697481556567],[52.933623008084588,41.045706073713248],[53.156298063559248,40.822542601754591],[53.342930376396673,40.784818205941455],[53.615120946489071,40.81826305425556],[53.760072798204369,40.684002675354904],[53.87643667136043,40.654650491695165],[54.201599589561958,40.725702057175369],[54.374476896743651,40.871123630913857],[54.517679190331201,40.904170300911844],[54.589506192822434,40.993438389936095],[54.595901938344724,41.126275987747498],[54.532983295379289,41.22203168926081],[54.094992263664331,41.519498973082065],[53.88139439780845,42.018721429652963],[53.769001394634387,42.12006091939881],[53.158736958530064,42.085486919781481],[52.964418348568742,41.96769737630752],[52.85988312847239,41.784411632402666],[52.861815039308489,41.191458708537382],[52.60954363541483,41.529550329201236],[52.494155172975503,41.780299119388459]]],[[[53.091525717040348,39.129831626423289],[53.069211864895806,38.993311749798607],[53.10013124356162,38.75690324336648],[53.018783941134117,39.052676284241869],[53.091525717040348,39.129831626423289]]]],"type":"MultiPolygon"},"properties":{"alpha2":"TM","radius":737000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[7.5003786386691207,33.832500459750605],[7.5140726928575434,34.080416537666487],[7.7381546291503041,34.251619204910241],[7.8384389163836223,34.410138835942178],[8.1290828704179887,34.572063631369772],[8.2377925942857679,34.727263233814867],[8.3711220139884688,35.231954197858414],[8.316625854720753,35.403161256429854],[8.3199726691208671,35.637097133085121],[8.2469734027296617,35.806679817649197],[8.327612476073055,36.342429504977659],[8.2078491016737072,36.518864294824482],[8.5208238675058983,36.810427039868323],[8.5768009299368995,36.937015003582715],[8.8222664867300828,37.000479818029135],[9.142058111146401,37.194447565539534],[9.6880035343499795,37.340125358719611],[10.196129327793177,37.205695891095502],[10.218787748960946,37.00452917596283],[10.413331009523947,36.841162718393214],[10.748531284784629,36.926469408686579],[10.951451772131504,37.059058247586826],[11.053723291490815,37.072249965027943],[11.126358843555266,36.874172733642773],[10.976770584941894,36.753574681536328],[10.797953823742624,36.493337809426748],[10.528469213830364,36.321030754839107],[10.480809008498268,36.158080148769287],[10.599489288597166,35.880948312788234],[11.004067457854203,35.633717412210963],[11.044905693460478,35.356749421568715],[11.119828798291028,35.240281834353425],[11.006794146821653,35.083943622740826],[11.002093947793789,34.949569895726576],[11.100447645722101,34.832714821950219],[11.276828103131468,34.805122755924913],[11.277805647314036,34.753959661037335],[11.123810145851815,34.682020234094608],[11.022142635498572,34.822872209902727],[10.835150932479634,34.824782366887924],[10.534754740729053,34.544877869383399],[10.119306259167443,34.279144189541668],[10.044996661046445,34.09488149707034],[10.17484431789075,33.83806189310603],[10.425191476646289,33.676516422609915],[10.645865369523674,33.705635026434763],[10.745393553384122,33.888428252900276],[10.921905110073141,33.892898365294577],[11.021655238276365,33.818932843136963],[11.029336858731126,33.654617687778959],[11.154711031798593,33.386447278009804],[11.320486724724063,33.250766355323812],[11.504349977903813,33.181786908809407],[11.453906250000017,32.704070260232683],[11.535667568349174,32.473399779013207],[11.504822234125024,32.413837013370149],[10.842669160438536,32.088189834927036],[10.637004674527056,31.942024484302532],[10.475665136909541,31.736259000603397],[10.303666282249361,31.694735874313324],[10.169786159358134,31.555094556767418],[10.132587125196332,31.402590111276087],[10.255758033564423,30.865011569963801],[9.8948529734518562,30.387531292124482],[9.518898521217281,30.229701562417077],[9.0366216107439588,32.067928352085573],[8.3336162734712413,32.543747993303988],[8.2210788429720907,32.901652964749331],[8.1243060411339023,33.039897110691136],[7.7315673445332109,33.268619789630478],[7.5003786386691207,33.832500459750605]]],"type":"Polygon"},"properties":{"alpha2":"TN","radius":474000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-174.06795436581325,-18.640329053344299],[-174.05559006818098,-18.626108465109599],[-174.01532341619276,-18.584132101689931],[-174.00202530383038,-18.571501232658118],[-173.9863948010773,-18.568308191883691],[-173.96823818891664,-18.566427163671097],[-173.94956584340147,-18.574640982535282],[-173.92292369738743,-18.589146859528949],[-173.92348854010831,-18.599480978273888],[-173.92481724782283,-18.608152429943068],[-173.95580586978284,-18.6470728070672],[-173.99178165022661,-18.69771799247847],[-173.9991865741587,-18.697711138175553],[-174.00894446560167,-18.696730779204216],[-174.02550031611804,-18.685059817568728],[-174.03738636952039,-18.675731187027758],[-174.05239324926742,-18.662689864742799],[-174.06105875553936,-18.651416238091631],[-174.06795436581325,-18.640329053344299]]],[[[-175.36124407358844,-21.107042307122256],[-175.34790874801388,-21.094254497133139],[-175.33274380616427,-21.081045782622454],[-175.31787322068229,-21.069314983790232],[-175.25405281119373,-21.093183388793438],[-175.18634204705344,-21.111967992523407],[-175.1172441210729,-21.124738487538863],[-175.07850170073044,-21.130151662547231],[-175.06436965045199,-21.148913006483426],[-175.02042432328921,-21.202288168286447],[-174.97203458389689,-21.251669670880926],[-174.91411100836254,-21.300970591056011],[-174.91386317693056,-21.320303647208185],[-174.91792116702001,-21.430695342208534],[-174.91945030666767,-21.448330540693469],[-174.94560985655201,-21.421314725867173],[-174.95229694874428,-21.413701997858919],[-175.01549698463882,-21.355676346414452],[-175.08454768036941,-21.304752656625205],[-175.1532365715471,-21.264677794025392],[-175.17127952459256,-21.253030597855574],[-175.2133987975661,-21.223048524241317],[-175.28774622850142,-21.18126885140758],[-175.31674271515749,-21.166951437233294],[-175.3346645551062,-21.156908083440928],[-175.3452016249484,-21.139271999360222],[-175.3533989754479,-21.12376993180942],[-175.36124407358844,-21.107042307122256]]]],"type":"MultiPolygon"},"properties":{"alpha2":"TO","radius":35000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[25.686024078035341,40.126546272845964],[25.934729133616287,40.416999550144183],[26.039847126865386,40.726299917196577],[26.276963109651426,41.041844603938316],[26.360816159081907,41.398506990200943],[26.32822843496977,41.772149155664763],[26.615517707855751,41.964019872517525],[27.244355640766027,42.09232719944945],[27.592903676634506,41.976150398647817],[28.013705694046191,41.968191508962903],[28.138192650484523,41.694258267175435],[28.306179666636694,41.519925905951872],[28.947614908675966,41.251565611216144],[29.919392541799308,41.156431101100964],[30.344873903031349,41.196178374864651],[30.849072334583425,41.090535105272593],[31.238900064200127,41.167000457482864],[31.458506883132547,41.31927867645922],[32.322481978286099,41.734814714355082],[33.381381362624509,42.016577671172072],[34.585676392638348,41.958856752389245],[35.026879606214806,42.058360887171325],[35.15431786086458,42.026682413570022],[35.348627845528384,41.841497599006203],[35.554810832327085,41.741347678653568],[36.0511525562762,41.681682457530641],[36.256171239156068,41.489168501906846],[36.461051532704154,41.393647855384692],[36.777594948277333,41.362574029705193],[37.238611384169552,41.154455499410922],[38.381308706540281,40.929874009286998],[39.426295819970221,41.105558876904432],[39.860497992857312,40.98106347519883],[40.260379314754424,40.97361876338541],[41.055109171930603,41.249807979436802],[41.510461621660184,41.516403995523412],[42.271657781603047,41.487607226931665],[42.732375205193833,41.580978245928392],[43.432666206035357,41.154801895127974],[43.631025524703944,40.928563465151221],[43.775791141769915,40.505842814222859],[43.94006291185422,40.252640539177143],[44.118919369418869,40.112917396303565],[44.545296300297785,39.897787004875276],[44.815303413935496,39.650827689468699],[44.602331045374399,39.541835274636583],[44.433189840725127,39.3774927184595],[44.29550683001915,39.095787360207282],[44.269752279139368,38.861364822916165],[44.318187387724443,38.567116571169109],[44.448859713536443,38.333857768497666],[44.482616020262455,37.912470230240331],[44.795868763721472,37.269875389543998],[44.764395568648517,37.143350638294919],[44.498776078390101,37.096016601699858],[44.28158305590653,36.979023632377967],[43.91554738558149,37.188722374296638],[43.072289482673938,37.338274129091516],[42.756861574349614,37.291026786813212],[42.359086781593518,37.109638288001271],[42.030098444347267,37.172228829823396],[41.356341800626382,37.072552563515728],[40.693036597765406,37.088263905691122],[39.98941787309554,36.818919662350382],[39.383881918839826,36.685476849907438],[38.766722391545549,36.694024224396323],[38.394684645809903,36.843258494593712],[38.093295073129298,36.849346246762295],[37.43614054664998,36.644365819111414],[37.199968698059728,36.634460405711749],[36.973184577284918,36.559365436984564],[36.14006216261086,35.832882379905762],[35.893515197907874,35.917336103348354],[35.783279954013409,36.239226740756209],[35.581153159213819,36.46421005956384],[35.38222591703137,36.574455102744686],[34.831056757015382,36.735139231012141],[34.605447353502953,36.723084289668499],[34.392205365097041,36.648433241821024],[34.008838027643264,36.331069698257579],[33.694438357356468,36.182870021221937],[32.794743184983517,36.036815029536861],[32.399160758797116,36.172258973717121],[32.112987911033883,36.448270249887138],[31.921130027381039,36.563649651149703],[31.464370894929917,36.747292462438068],[31.231013117835182,36.787152767370117],[30.99620718161956,36.756972171654041],[30.780515003976117,36.659393585154234],[30.40503134575594,36.251285829007053],[30.09091855121758,36.247724293617622],[29.689154462284506,36.157718791294485],[29.23845968405918,36.316660496194451],[28.938418184332409,36.597662548333787],[28.642019205294051,36.720838282360106],[28.349192316701696,36.725737334982661],[28.019542780687377,36.635426349860978],[27.454568378899989,36.712881153579126],[27.263864984467098,36.977005992409325],[27.215302870746491,37.390349792787738],[26.965684487896215,37.856194813158034],[26.739535342738499,38.069052841603977],[26.291902051577392,38.277476053205618],[26.408847035150202,38.810337355007128],[26.361385126556115,39.119732832218908],[26.113582628209791,39.468637691507929],[25.946245531944811,39.900395866683361],[25.686024078035341,40.126546272845964]]],"type":"Polygon"},"properties":{"alpha2":"TR","radius":873000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-61.905025698031828,10.069463617566285],[-61.685342887331423,10.183493814697989],[-61.632565854873313,10.243202459068627],[-61.594211755735309,10.256674964587592],[-61.544348751502525,10.290075168742771],[-61.506640000570357,10.336765027725868],[-61.489985086603319,10.373236948372194],[-61.471518749092908,10.493247178723317],[-61.477977492570552,10.55026954711291],[-61.491219425160054,10.586241189898729],[-61.511080667713628,10.619025925792789],[-61.551627773554443,10.659636245565011],[-61.635148710696299,10.699585275467678],[-61.650795892773708,10.717990087073391],[-61.591752329285214,10.747645123241078],[-61.465071278777131,10.765428787452478],[-61.369975530503133,10.796585779133171],[-61.182545671777035,10.803773118891149],[-61.078453815227604,10.831577230471497],[-60.917925519592821,10.840210603657823],[-61.00332802141893,10.703974572654701],[-61.02196643651564,10.64755956233795],[-61.019543736417965,10.558116589923834],[-61.030206722265497,10.509223003450533],[-61.030709048211214,10.455196825099588],[-61.012044639960664,10.388396229384419],[-60.968750609358061,10.323361513969351],[-60.995301515722055,10.260342227544509],[-61.012316073057271,10.13451326241643],[-61.179307825567115,10.077867705812283],[-61.596673924007064,10.064784862702371],[-61.771678474550015,10.083682170349087],[-61.905025698031828,10.069463617566285]]],[[[-60.810130570474357,11.168382056870575],[-60.804077864867168,11.208263609749402],[-60.708808182225241,11.276992874066515],[-60.56276031754637,11.323353892452845],[-60.525803309017761,11.325153036959602],[-60.546661857147875,11.263888245560027],[-60.751541405647245,11.180447601314553],[-60.810130570474357,11.168382056870575]]]],"type":"MultiPolygon"},"properties":{"alpha2":"TT","radius":73000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[176.04018376135946,-5.6784557287354183],[176.07532701077153,-5.6299471657866365],[177.37370485009211,-6.0540420083222806],[178.70233672020075,-7.4838379259759247],[179.93305451080835,-9.4058448277756543],[179.48161922606329,-10.810716682014984],[179.43898991614716,-10.78784797520985],[179.32007727894575,-9.7253719415943092],[179.20825851548813,-9.4512798448820234],[177.13974239204714,-7.2527936533780712],[176.31945968774014,-6.3131563474566716],[176.04018376135946,-5.6784557287354183]]],"type":"Polygon"},"properties":{"alpha2":"TV","radius":370000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[120.07271336356182,23.149754051789341],[120.15677016588062,23.704407518982809],[120.62672754899154,24.473676842018399],[121.04080134653623,25.0326178872625],[121.33684416232694,25.150204598667965],[121.51713944489642,25.27664027215668],[121.59355973432842,25.275101456615428],[121.90180815890926,25.059533346791973],[121.92871668673817,24.973798804257523],[121.81934248993639,24.804678518568139],[121.82781913065301,24.534410167314014],[121.61869250413723,24.069241441084138],[121.39895715470482,23.177233117977394],[121.29284154495181,22.962283341554425],[120.94295295255381,22.493259449568811],[120.83967801839796,21.925289914519098],[120.74291284981707,21.956192920648164],[120.69038010659783,22.033193543012807],[120.58605153202727,22.346077582710624],[120.31640997315125,22.547744621818978],[120.07271336356182,23.149754051789341]]],[[[118.29238626516492,24.476910041797172],[118.40736795077567,24.521793150321404],[118.45089357567134,24.45554402994853],[118.42774570906106,24.415148978547819],[118.29532944080768,24.436547547056072],[118.29238626516492,24.476910041797172]]]],"type":"MultiPolygon"},"properties":{"alpha2":"TW","radius":201000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[29.323626648438136,-4.8988101020903514],[29.403469203000128,-4.449562997964053],[29.711146441564271,-4.4463455301544625],[29.953014810778953,-4.2996353326963721],[30.363841489432168,-3.7549688884536532],[30.425178420919138,-3.5889885404591051],[30.77033161482024,-3.189315288316938],[30.715055575023481,-2.9952094486467251],[30.455707572011836,-2.8929315238884863],[30.424927320019687,-2.8288410381456726],[30.424492840015098,-2.6416196615950094],[30.533894146394086,-2.426394685201803],[30.795437722812881,-2.3234658122235756],[30.873863858165205,-2.1530969132526265],[30.81857227294746,-1.9621281139873208],[30.806446425588828,-1.553221058048619],[30.511233251596934,-1.2122263220215037],[30.477266754681516,-1.0831663023452209],[30.809169800752048,-0.99513862027424516],[33.983668945659105,-1.0042892093641369],[37.64366887596325,-3.0455976153313289],[37.687791491592279,-3.2461973015998238],[37.662440138050371,-3.4525263835434026],[37.781489784361831,-3.6587979524073031],[39.221515030245818,-4.6925082227452224],[38.872594577799887,-5.7751146836734382],[38.950743125116986,-5.9258520615551289],[39.097415979281017,-5.9655691029123323],[39.247002365744393,-5.8728142533264389],[39.308960034731861,-5.7223913419648031],[39.572799718973855,-6.3874150471566367],[39.5091316377505,-6.4514708408799617],[39.330860389946487,-6.4737949370631638],[39.237543776003655,-6.5953264471253403],[39.272145070096926,-6.7715928876061309],[39.472202093724619,-6.8787983827547894],[39.545808446465131,-7.0240530944018014],[39.346662195258304,-7.3590471686831753],[39.291609828967019,-7.6496752779663968],[39.360357281405157,-7.7656900336237413],[39.588230516258598,-7.8843006090138452],[39.697883014910239,-7.847080233436099],[39.906860078107734,-7.6497303208214547],[39.824237888559139,-7.9004838520880023],[39.706627102974004,-7.9785784765595542],[39.512243001033319,-7.9919807128578979],[39.422360480750591,-8.0636787360545501],[39.31525517647605,-8.3291470743428437],[39.312067756607775,-8.4742991603970719],[39.381135651394111,-8.7189081926570964],[39.488144240668454,-8.8618739728918623],[39.503013770415258,-9.0052158276434007],[39.641039699492161,-9.1925604369581162],[39.632853954016284,-9.4178160790981043],[39.804419271890609,-9.9565849104101183],[39.980287294808385,-10.122594577416519],[40.388599164840954,-10.353705968391935],[40.463266545803499,-10.464285006014592],[39.985282688020206,-10.822610148004163],[39.459189179239338,-11.025539158379106],[39.321492506604429,-11.122379873920774],[38.817408486713674,-11.221655847680459],[38.49179377562568,-11.41301284120245],[38.201160221889509,-11.284466007032961],[37.972173092907155,-11.29208216555628],[37.724677409085302,-11.580484492425489],[37.377257232644432,-11.709526805451965],[36.944118041648082,-11.568399063573034],[36.669449735971078,-11.685178371574056],[36.305695697795457,-11.706167910111335],[36.191517202288829,-11.670504710835528],[36.081660892323008,-11.538219534961165],[35.873740734732415,-11.454807977604313],[35.564325018287917,-11.602164596032095],[34.959666323792185,-11.577885414192966],[34.914245478612685,-11.429159451050943],[34.773975282385493,-11.341472446493725],[34.608206560322209,-11.080342010320384],[34.663272278321976,-10.732813335022401],[34.583838229136788,-10.525050656690931],[34.519022619738067,-10.038306304720461],[34.222887766167247,-9.6498056119091871],[34.113310310207162,-9.6042612314712041],[33.938958949375014,-9.6719823062931809],[33.697562920328899,-9.5995846332768426],[33.421041262852356,-9.6077536176072762],[33.308187784427837,-9.5192367560239575],[33.130554632062569,-9.4956308327179038],[32.409738314527701,-9.156266002401626],[31.942724161598544,-9.0537844773838323],[31.83766179954668,-8.9329727835496566],[31.615881995141425,-8.8655918219075822],[31.448886812714136,-8.6544794274552839],[31.32205547070469,-8.608390563264809],[31.071016079765446,-8.6101384676607573],[30.892134474486305,-8.4736121525234349],[30.218946920606395,-7.0482066896886577],[29.709841777565398,-6.6167541230819173],[29.541075171043531,-6.3137605122286269],[29.480276224694947,-6.0250002277389143],[29.599068787378112,-5.6782594887141826],[29.323626648438136,-4.8988101020903514]]],[[[39.647052623819931,-5.3684987655805294],[39.697848366787682,-5.1135704402602338],[39.673711387067833,-4.9273296375200486],[39.864847453773699,-4.9064941048928077],[39.8527294349649,-5.2554006650812184],[39.795721053596921,-5.3943191507261918],[39.749253292855485,-5.4435916034219458],[39.647052623819931,-5.3684987655805294]]]],"type":"MultiPolygon"},"properties":{"alpha2":"TZ","radius":900000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[22.132069288734154,48.405538000226031],[22.143081007372047,48.568393621692948],[22.304147706363509,48.701428673399192],[22.538857635806387,49.072470713697044],[22.695559467803438,49.178587855310674],[22.726005289933344,49.318179400816469],[22.649677950475571,49.538946149728432],[22.70364988594984,49.60323784835257],[23.404861224055686,50.171308461228065],[23.711803237912243,50.377144137906242],[23.962863094130181,50.424679726528943],[24.054037704773233,50.52235946301446],[24.069495628294796,50.654829820151413],[23.978708881819571,50.785667943943231],[23.976679758327524,50.931915669694789],[23.85465219015061,51.13279148253315],[23.664655098478224,51.310165764189492],[23.608838061330765,51.610354757808999],[24.025233518465825,51.615536657047976],[24.362023771904866,51.867257461901019],[25.070940702858628,51.930670461624317],[25.920514729382042,51.913878157772189],[27.137064973094969,51.752688837791645],[27.302212765924892,51.610683421757486],[27.727220014632746,51.564064682902142],[28.042073282158164,51.562973464937755],[28.183779230087197,51.607617419813877],[28.739729779515041,51.50019602245937],[29.102027550897279,51.627350738809568],[29.278076380255726,51.444778748405049],[29.386812622870124,51.400038902884333],[30.063782940985323,51.481789907506879],[30.346012250958601,51.41644507735267],[30.482663302046141,51.482306243822556],[30.752058543569547,51.892204459618064],[31.074921723693972,52.075588597612459],[31.576182131925655,52.108014572895861],[32.096074387130827,52.048778551835866],[32.252076498853505,52.103827585397653],[32.363210839991169,52.271938369681699],[32.435475746412635,52.307046223694854],[32.826764438169832,52.253445228462532],[33.154202022831946,52.340975693359816],[33.735218631018689,52.344477962590666],[33.921842325598881,52.251181009269388],[34.123230552549956,51.976557425320863],[34.397653581411838,51.78030055008783],[34.209981555292735,51.554843036504948],[34.223072494008306,51.414843712290192],[34.302358898994726,51.302406956311536],[34.725940059865493,51.171905022368527],[35.063979838224284,51.203173049847869],[35.178484374912976,51.074956470523603],[35.3118008906672,51.043646595442041],[35.43987137590041,50.727615193617432],[35.44740349606657,50.540583218027763],[35.603256529072233,50.397366137220502],[35.721221893384168,50.379309184368928],[35.890253882869878,50.436886442576586],[36.11135150218086,50.409063124439207],[36.286330318798335,50.303770790828942],[36.629198049725666,50.23927792313274],[37.422761373527131,50.411264372472374],[37.582145699182028,50.29170782448093],[37.698188197505154,50.116832211570312],[37.957751523841431,49.964261564532208],[38.073559228809444,49.959386114781083],[38.258552118965845,50.052144634485259],[38.462809184515287,49.964228157816429],[38.647697335076778,49.952662326087825],[38.930154210346998,49.825318526172076],[39.174764279567434,49.855705174313641],[39.295768069591944,49.752597265261521],[39.462730199622406,49.727764690905431],[39.781755686072501,49.576516192538001],[40.080478855080479,49.57666710812012],[40.126344767613013,49.363888781759755],[40.108552636141901,49.251689353148549],[39.935086444083971,49.043925949722677],[40.003321631690135,48.822138342648238],[39.877168297629474,48.747752541159805],[39.825624990586114,48.640492060919215],[39.960762911077289,48.238043282716838],[39.868947698991079,48.13820677320183],[39.735846861392289,47.845029536341428],[38.858251395847716,47.840613511450535],[38.640487178015313,47.666212518968457],[38.305122467911865,47.547157097660296],[38.258548679909296,47.442554231959846],[38.280507007173433,47.259113410693146],[38.214111617000988,47.091617607441137],[37.584049770894246,47.076644900629518],[37.339768610196167,46.91711551980196],[37.047819853858087,46.875448161759046],[36.794788745584263,46.714697094286471],[36.558940535306114,46.761461153362283],[36.274573556023093,46.657835295894195],[35.837597935167686,46.625500085522702],[35.417260514283754,46.39010391954541],[35.256466687446775,46.20409727947191],[34.957297879174732,46.028622749256009],[34.925292906881531,45.878322370193011],[35.013391487284025,45.715452862473434],[35.423731934359992,45.331386703577081],[35.557190465135314,45.315762285088965],[36.175009578837532,45.453213756420723],[36.574621128070596,45.39341339557042],[36.3931952852733,45.065570271286589],[35.8701626495466,45.005588104164417],[35.677268646533122,45.10099045524251],[35.51345275662333,45.106269404693954],[35.087589315914713,44.802872487290138],[34.740607085289682,44.809412086453257],[34.512090948166666,44.73626961989256],[34.277840604515781,44.536273271988073],[33.909944092865004,44.387831925708198],[33.660590625131654,44.431578204455761],[33.4509702972451,44.553778037944859],[33.604718158200697,44.898603937397375],[33.581537707200859,45.031079793697486],[33.509236196065004,45.122019176990399],[33.191861466191064,45.193182378441776],[32.908601642448168,45.345718474395355],[32.611346278628488,45.328289660415884],[32.508336805830822,45.403750506123217],[33.293912275050467,45.80171405822783],[33.362928753083359,45.960656995024934],[33.297325792513035,46.10122814173981],[33.152036549236222,46.1613143136464],[32.476774650041889,46.083902100901632],[32.236524937508996,46.162652921515544],[31.700232511456136,46.21422185090114],[31.564038583242372,46.257999492093838],[31.482792008000583,46.518226606960368],[31.337004563931171,46.611337247896692],[31.127837195361678,46.621695175116649],[30.85175515882305,46.554378673296583],[30.514031957710284,46.108154589612006],[30.223599890513917,45.870464276757637],[29.757779850476446,45.699729745549682],[29.679969744845845,45.564468931222336],[29.705734741500851,45.260365296570406],[29.575726092657842,45.364129067765695],[29.403345843257974,45.41598356184808],[29.21430956318542,45.397934160017819],[28.760733771611608,45.234421653249996],[28.317805601170278,45.347315866636912],[28.212874504279171,45.450400382396602],[28.423090345151909,45.544685828228168],[28.494998014395154,45.669135097646837],[28.694079947905529,45.824960805298261],[28.738994937232235,45.936982721669345],[28.953451417042313,46.079615821354125],[28.976386129516275,46.196753501300947],[28.927681340500829,46.4240358611071],[28.958575417480603,46.458247183383129],[29.150950539087766,46.526562379276839],[29.628983345943951,46.414318360924455],[29.84920534017094,46.481791413492289],[29.932720727267899,46.61137209862494],[29.871467114521081,46.813897160138183],[29.572205368287793,46.964125803191394],[29.46054492195741,47.273922229159844],[29.135033163273302,47.489781077964196],[29.200668526889835,47.72847705525588],[29.159743006637061,47.856437030244784],[28.92327743828168,47.951364377479109],[28.729645309545482,48.122025999009665],[28.568803688624371,48.140707014648548],[28.463083794364287,48.090819867432899],[27.809178894563953,48.418477894616686],[27.53867172406618,48.465126393118545],[27.228448931565108,48.37168181274825],[26.816584818374043,48.369785012090475],[26.61881316349811,48.260066179637015],[26.362585529839095,48.204992784835746],[26.162592722777465,47.992749619092073],[25.464437269440989,47.90987118255655],[25.189111570552463,47.827902519050525],[25.073736042050548,47.745892897708636],[24.893442904933732,47.718050171149635],[24.492521646499803,47.944953206538877],[24.1777626459176,47.906264411742868],[23.658606562298843,47.993243695348951],[23.40824588755574,47.99019880147565],[23.206753697313587,48.066514821374028],[22.876677676400899,47.947544298072074],[22.771577490744551,48.073366999687394],[22.582538737688608,48.134211867108554],[22.132069288734154,48.405538000226031]]],"type":"Polygon"},"properties":{"alpha2":"UA","radius":693000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[29.562191914401843,-0.97738393047729366],[29.717873968502953,0.098266992181184537],[29.919166064328515,0.48191509521044762],[29.943043123730174,0.81906594583165437],[30.159608328513308,0.95573232596739444],[30.321211450238454,1.1851158267194557],[30.509669597109788,1.2692196691236526],[30.947491889058536,1.6882489340837199],[31.231663644451856,2.0431734472486585],[31.170053353675229,2.24346385114614],[30.96680823400186,2.3819818396465511],[30.72885491643946,2.4555122896723534],[30.836784021645286,2.8613500109796277],[30.755002655244024,3.0465978112839847],[30.859169685321497,3.3575068431764925],[30.838827712790138,3.4907160829761397],[30.929601396191419,3.6338864601102916],[31.152416683254749,3.7853276111171388],[31.520982176854233,3.6797905308481602],[31.798027374730644,3.802339032467692],[31.954746834440101,3.6173596219656883],[32.105521410026221,3.5740592121295558],[32.335855488006459,3.7059186641596389],[32.816274746875614,3.7929175134974891],[32.997249994644172,3.8799108776219069],[33.18330788814486,3.772113801609557],[33.4686552299518,3.7589825282020519],[33.976010116370297,4.2198459512158175],[34.225713691360014,3.7812155006749206],[34.43749409419825,3.6504858462048309],[34.402752656485831,3.3947699439753949],[34.441427614462398,3.2129436506429592],[34.592970862814582,2.9403735283633723],[34.742297680538712,2.8180159410885137],[34.905501582635836,2.4796885174757421],[34.978157131224371,1.7716197381012366],[34.96504844954304,1.6434215130366594],[34.805143208402541,1.4008399976071313],[34.798407185551319,1.2446339965932767],[34.494193819857692,1.0548395218749547],[34.410579880791389,0.86744469026729742],[34.148507973324996,0.57778523604312115],[33.950353872523493,0.17084553672599356],[33.902983034105461,-1.0018156477971949],[30.78070063589988,-1.0020681940822689],[30.359073243583993,-1.0938723588210626],[30.101457450757731,-1.3685264234670751],[29.930049360425031,-1.4696865689348118],[29.76573071074295,-1.3725779749432439],[29.577212226757226,-1.3876497790509563],[29.562191914401843,-0.97738393047729366]]],"type":"Polygon"},"properties":{"alpha2":"UG","radius":458000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[179.49789895556205,51.932914668338796],[179.50410800219524,51.979434481846127],[179.62714805162173,52.030164064995198],[179.7796013091999,51.966754566837864],[179.64519145743262,51.88053012898294],[179.49789895556205,51.932914668338796]]],[[[178.47526377425052,51.967613440518001],[178.50942194240506,51.994441334788952],[178.60699061430356,51.952965206755607],[178.57636189341508,51.867747085876353],[178.47526377425052,51.967613440518001]]],[[[178.6564681151612,51.68296676296908],[178.90793944700471,51.615372435658273],[179.45094347352,51.372820491321235],[179.27818911086916,51.372509830164461],[178.74182883716489,51.589742280846039],[178.64822859474654,51.644002125115506],[178.6564681151612,51.68296676296908]]],[[[177.25059674959937,51.90284108573816],[177.47862687150464,52.006756223920782],[177.56392349138335,52.110267614624028],[177.66934456172544,52.102891864523968],[177.5939263163184,51.947732228395878],[177.32853160050109,51.841346901696213],[177.25059674959937,51.90284108573816]]],[[[172.49549035191671,52.937913507643692],[172.67798822115125,53.007282593394059],[173.10212481237414,52.995382597058516],[173.43549573852886,52.852067115509421],[172.93514002801001,52.75234508299895],[172.49549035191671,52.937913507643692]]],[[[173.44222346538606,52.445377929147845],[173.65765884663531,52.503807676259768],[173.77568094129836,52.494922052705171],[173.72256998307833,52.359795751027789],[173.40275964699813,52.404989704026086],[173.44222346538606,52.445377929147845]]],[[[-124.70965167588474,48.380128027608322],[-123.97565160709652,48.169253471868416],[-123.22836398334012,48.131637120390728],[-123.11133329059814,48.188362703866702],[-123.04665173864801,48.308034282459957],[-123.16199158287802,48.606188871345438],[-122.82137171493787,48.763258288485694],[-122.76895450452916,48.86937448499738],[-122.78397538582003,48.993017578125034],[-95.359649793896935,48.992934401367755],[-95.18344306510987,49.096150082817132],[-95.15505469665824,49.369433147604944],[-94.939405228919938,49.349231910129596],[-94.854599499258356,49.304478799555575],[-94.805524953812508,49.011256501547408],[-94.599116978620657,48.74277449310047],[-94.04916061917487,48.657503802357546],[-93.713890592117963,48.528645693930621],[-93.496496136149418,48.5533202131545],[-93.373201013938953,48.617032913955548],[-92.990206660343617,48.610150570995557],[-92.500757436053405,48.435147337603411],[-92.375261202644595,48.334210462528304],[-92.005279145011187,48.301622005752698],[-91.517792729678987,48.061293599664829],[-91.361234046531365,48.065725374701039],[-90.916063166314515,48.208789543150239],[-90.701789334643095,48.11126494969411],[-90.091860524346174,48.117899401686053],[-89.926598410816055,48.005891355560486],[-89.455696123751096,47.996946225307809],[-88.378197218657007,48.302813038802896],[-84.87618256239125,46.899704170982865],[-84.779419965568025,46.650191188285859],[-84.594933472726211,46.496638935480114],[-84.145127517514609,46.539999496556156],[-84.098528454568935,46.25903313147618],[-84.002476826408085,46.125298277268534],[-83.894213356338327,46.086150961807895],[-83.616037483026972,46.116645222958255],[-83.480304706510168,46.023601209375961],[-83.486753818592732,45.853010665513246],[-83.40862139988073,45.738502157987774],[-82.551273546482989,45.347200533699997],[-82.138122305789622,43.570936187605845],[-82.401604347146815,43.082201633585662],[-82.54552099126353,42.624890400958904],[-83.068374055459174,42.292842190739904],[-83.14770514663941,42.121435638116814],[-83.132618235677953,41.979340857654776],[-83.033595892082104,41.84286494884531],[-82.668663577708173,41.676239825686125],[-82.419239233888504,41.685260120776164],[-81.27757383558405,42.208969915800211],[-80.247782133394736,42.366903499094185],[-79.122404127179038,42.769746290620724],[-79.038344676173651,42.851342678267272],[-79.014404646263102,42.966019776016452],[-79.068270821409612,43.291475593989283],[-79.17151071273446,43.466430828609923],[-78.844467245927675,43.583711749165118],[-78.715477723893386,43.625074275979273],[-76.838550412721489,43.6442301298191],[-76.464424934898204,44.057421685626011],[-76.151026136020718,44.303795568657506],[-75.877765873356296,44.420009761811627],[-75.397213579614473,44.774589049759491],[-74.989782974396633,44.971657560541622],[-74.85661895523701,45.003674363635689],[-71.562094793454392,45.023227498674594],[-71.327219500791443,45.289816453624624],[-71.158697919220302,45.265976751765294],[-70.859384851822057,45.356910529259252],[-70.698700856335805,45.554469270237448],[-70.448261484738524,45.732318224260801],[-70.319360720672691,45.9109463436819],[-70.248117448326852,46.250790651625422],[-70.066384583364496,46.464912708439321],[-70.007508542166633,46.708821336518866],[-69.242789887129135,47.462731781049825],[-69.050429759904233,47.426428863890408],[-69.018276621947066,47.295340594873764],[-68.909026209326754,47.214200869871263],[-68.239979290413359,47.346455178590837],[-67.807008424653176,47.082689754317556],[-67.76379388284154,45.764032785048094],[-67.657352673211463,45.64528389398415],[-67.432849795627192,45.60290292843888],[-67.406121965685756,45.306154113636921],[-67.32101872769509,45.221861491752392],[-67.125086585106231,45.169251998683848],[-67.068897814831672,44.945471181224605],[-66.987250303954355,44.827776817289511],[-67.191321285818503,44.675818363370311],[-67.373470820433752,44.687974057969207],[-67.599197139446389,44.576994355290779],[-67.814480420124156,44.545960787723978],[-67.9071793920819,44.45381624672104],[-68.315167363745289,44.249989143487248],[-68.485949005405431,44.276661495491417],[-68.661228569757,44.176557198751041],[-68.808655811251526,44.265284761962832],[-68.943443360858012,44.260876720569001],[-69.068584527184612,44.097682834807301],[-69.226118467760145,43.986691266458095],[-69.361938987979158,43.988183407949485],[-69.480969584970751,43.905272595270475],[-69.694703393329874,43.868890959164446],[-69.80845059478861,43.772642361449556],[-70.105831104723393,43.763961853646393],[-70.518044788371483,43.34658350848283],[-70.736043888003067,43.050278284470714],[-70.79031863425422,42.886348592117614],[-70.746137784995398,42.742726525359814],[-70.6056584842636,42.645165993738061],[-70.793380704678256,42.531270791415757],[-70.835743142810998,42.362050253433786],[-70.528440517290235,41.94896514960918],[-70.360459653521715,41.942675655017418],[-70.203500049243544,42.100838626600471],[-70.109050089826496,42.078129784892873],[-69.978109910927785,41.96114292414881],[-69.934086841384143,41.710491767975881],[-70.037022648082214,41.599619600968964],[-70.066502704427037,41.489111720913542],[-69.978214068781867,41.265757853718206],[-70.232889713033373,41.28652987455942],[-70.401477228543072,41.400804105047285],[-70.785243901387148,41.327699985197732],[-70.904738741769904,41.495917918012083],[-71.06971492488411,41.53399387499271],[-71.81282144186703,41.3134354501153],[-71.903970360038755,41.212080054952757],[-71.903517281469561,41.0608257268754],[-72.335281563593369,40.895548476704882],[-73.194331937339769,40.654447416026393],[-73.799069797056887,40.587317846017378],[-73.905002461018938,40.533928217069551],[-74.133387572716074,39.680867605466176],[-74.250617802948355,39.529624359470318],[-74.360124027806364,39.487977956346626],[-74.577175965485182,39.284643930012066],[-74.794649056495814,39.002104863833473],[-75.050021932287606,38.858297634165481],[-75.092103966052363,38.651878974143514],[-75.039135269321534,38.426425853076395],[-75.13655645985024,38.180669043393181],[-75.33322034049263,37.888496767481087],[-75.506645159282442,37.789612268631075],[-75.587376797061196,37.5588306085534],[-75.800209582277304,37.433672143123431],[-76.00198051504178,37.058842975693977],[-76.005659346472839,36.939043571607257],[-75.756776222203058,36.229718370053774],[-75.478755334587547,35.71643142238743],[-75.464410643422653,35.453301491760854],[-75.512299872395332,35.276885618740266],[-75.963692150758376,35.119085905985877],[-76.008507691134142,35.254733444118102],[-76.090667420036738,35.332548272449245],[-76.31898908693627,35.377560113398388],[-76.445584107071028,35.345290558721139],[-76.543191758593679,35.201310689385139],[-76.516548429512511,35.050031604294205],[-76.345569347183442,34.932002770867186],[-76.20827049150914,34.938369414639666],[-76.50381283185969,34.643174337016305],[-76.742228653774717,34.705734990351601],[-77.148165245587052,34.685704821150232],[-77.750205390381183,34.28445321139943],[-77.856973135474092,34.147182914631948],[-77.928009429954741,33.939967318712007],[-78.431886747099483,33.91086233816668],[-78.825959864514346,33.732835293685071],[-79.124766113677239,33.420092809092083],[-79.276262213571059,33.135684606240133],[-79.536204179490426,33.005713139893956],[-79.944942606233496,32.664680423902666],[-80.413019250922005,32.471933113507767],[-80.510501108650203,32.327481325799567],[-80.669366893620122,32.242196490201152],[-81.037152372380604,31.859923918455333],[-81.27635281636617,31.398762881851098],[-81.288738868676504,31.26396947015688],[-81.416164831237396,31.09244384454605],[-81.463731102208257,30.67076870237085],[-81.243194367643639,29.779121466073185],[-80.905925873232746,29.061648677003586],[-80.524387208230138,28.486099077892664],[-80.580419224138936,28.364603868077587],[-80.571761201852766,28.181124326749593],[-80.088888323672094,26.993878441748294],[-80.041485586958643,26.57344561474687],[-80.126594563061133,25.833649605796168],[-80.307144321817006,25.573621566780719],[-80.311498461325527,25.443626755226767],[-80.259657275287594,25.343365889949855],[-80.381990612764184,25.142410112832302],[-80.638411512487977,24.903399344239471],[-81.090008226895833,24.693401699627831],[-81.138290352659865,24.709603166462458],[-80.924530159468617,24.819258922230574],[-80.888845460554791,24.967397374951137],[-80.952727259945206,25.085342466689973],[-81.110369730162205,25.138256599164013],[-81.16713936402347,25.22855863837103],[-81.137208276503642,25.382380635171231],[-81.218426225130571,25.566833343480511],[-81.412733800933708,25.830642405453119],[-81.7153093054404,25.98331946183843],[-81.88880024986274,26.372232142732919],[-82.184150095130931,26.481095374945742],[-82.187844565514723,26.772627663281728],[-82.356421122208445,26.939104026605889],[-82.714331343515326,27.499675133309051],[-82.712290778278842,27.649323148427353],[-82.843233506405298,27.846028827546011],[-82.661301150418012,28.496270314572243],[-82.672941042951024,28.915775924604649],[-82.777845769106492,29.056899951426232],[-83.29028821521122,29.452104479028428],[-83.696838321918264,29.922865004151781],[-84.025762922177961,30.089698588027559],[-84.250822816880785,30.064240947243395],[-84.382898824274122,29.907609211030227],[-84.580233857070994,29.882790611676839],[-84.737355744019197,29.73258600068521],[-85.008264942749605,29.60688172991123],[-85.376221986327934,29.695396515367893],[-85.450886863432459,29.92441737852899],[-85.721829837313152,30.152739659000389],[-86.540802083542246,30.428712539449069],[-87.6270408564382,30.264324964278551],[-88.163564843127531,30.230973266966721],[-88.435919651211492,30.26984148583011],[-88.677355432102388,30.204766367689093],[-88.799470351039943,30.108416530551349],[-88.827683081162903,29.807823191575174],[-88.943317603496382,29.660825394734093],[-88.84882989558794,29.819520762595989],[-88.841992007067574,29.974594321305634],[-88.932363338326255,30.113687581267115],[-89.083948286186725,30.142443717901518],[-89.297359684914241,29.990363921126328],[-89.354684538307978,29.820365177673089],[-89.504690903068152,29.758258387897413],[-89.583456047595632,29.644807875149922],[-89.582043071991976,29.531480976655732],[-89.519268795413566,29.437117993328449],[-89.180876810567824,29.335455502104828],[-89.015968200991125,29.20271679601171],[-89.021622634683524,29.142886224600733],[-89.155600265361528,29.016884959686028],[-89.375830208825008,28.981701601804357],[-89.391996768429621,29.107965202027167],[-89.477621311931912,29.21830350363895],[-89.873404713775017,29.361616108959453],[-90.021236577012132,29.320015563128926],[-90.13602150166578,29.13632067032486],[-90.212759148038032,29.105180191036951],[-90.329844097627713,29.257129322435606],[-90.443029145813,29.29751870950831],[-90.569181393386927,29.269853541344467],[-90.751027850706535,29.131121351557081],[-91.289861514744743,29.289118915019703],[-91.337303406121165,29.480230488341771],[-91.592659759833069,29.586763655097602],[-91.704403041056779,29.573580626570219],[-91.830845498734391,29.486713382541513],[-92.059348003008225,29.589158104569691],[-92.260852655662347,29.557084301283119],[-92.671236212525898,29.597330166882553],[-93.28318845973125,29.788207451078502],[-93.694672291600369,29.76875766243829],[-94.108316519012135,29.666066524807125],[-94.684355850590862,29.425025111271157],[-95.273701385789849,28.964155829446721],[-95.650892817145731,28.747477014249476],[-96.313705997603833,28.466125205524637],[-96.413545808506328,28.337964858773837],[-96.783811043686882,28.142039077530292],[-96.931428318519778,28.003605193211275],[-97.250058539935537,27.540763630504046],[-97.38062881919285,27.232441321411695],[-97.35022547212391,26.801654416261599],[-97.140397787054582,26.029716025329876],[-97.146443882351363,25.961666946904391],[-97.370386239377169,25.871430333303238],[-97.78356374949513,26.037217728948363],[-98.274962416345801,26.111374392276964],[-99.107589080770538,26.447102550666592],[-99.242589789159226,26.783351936953562],[-99.456335503047555,27.056759081059621],[-99.519083642083771,27.555861333395825],[-99.757270274492001,27.732997463303217],[-100.09332357864236,28.151856499094734],[-100.29576962617075,28.327854716727497],[-100.36683759494521,28.559013884727364],[-100.74109287595998,29.166488018363516],[-101.39709444326706,29.741420720106067],[-102.25474015819643,29.849799441905855],[-102.59168147609935,29.750898799586246],[-102.74188031281849,29.626597864964111],[-102.89217178498255,29.216622002134059],[-103.09460520822496,29.039319467911046],[-103.26192974112533,29.002897937170438],[-103.99344386950008,29.325071856792494],[-104.40052569365632,29.57390282616775],[-104.62203823335042,29.854394350719971],[-104.69463860871429,30.159420521973058],[-104.93387800890748,30.599792610362108],[-106.14790301817617,31.451156783776259],[-106.44768651277418,31.755892767170661],[-108.05098794592956,31.774648786462048],[-108.19735641053407,31.655907022121113],[-108.21467970373475,31.329670811820556],[-111.04195343040814,31.324474490908621],[-114.83123028280181,32.506831714597688],[-114.88712001578779,32.624186080576131],[-114.98986745283975,32.684347667715556],[-117.12810028709669,32.533609449608612],[-117.24326309235806,32.664229888394786],[-117.27034374001447,32.96009128660981],[-117.45080440383943,33.273635045870861],[-118.07224059377907,33.713947363445236],[-118.21536055877274,33.743566399640997],[-118.36113487630688,33.662367881528375],[-118.40803128149861,33.499491646884813],[-118.29792868233287,33.31239441668059],[-118.442072336221,33.308333239139934],[-118.56918052100599,33.464078520448666],[-118.44412158976159,33.543517483378643],[-118.39821716461994,33.650529365427111],[-118.41369799944373,33.873134953444961],[-118.53339745849317,34.013585099962157],[-118.83715106990866,34.025900835032331],[-119.35494340914509,34.200732595691171],[-119.49459997961523,34.140161939948293],[-119.56234977520744,34.006803527437157],[-119.92298019664136,33.98306305272687],[-120.10853527052805,33.905950746641352],[-120.27668760562371,34.008736360120736],[-120.44103939543469,34.033056635744778],[-120.36794697868787,34.073114079081016],[-120.23305660792619,34.016173846434242],[-119.95393037311345,34.04479457937218],[-119.8214472559194,34.168919404379501],[-119.82921153043262,34.342270836643927],[-119.95015695032163,34.439765828458846],[-120.48108999991597,34.471865550823232],[-120.64441539385193,34.580118627238988],[-120.63113105347983,34.82116476318307],[-120.68697481689946,35.103152116419345],[-120.85721351357836,35.209838317106325],[-120.93866952805496,35.426132088053578],[-121.28368203362537,35.676479749802297],[-121.65402431435106,36.142307150559169],[-121.87720283661702,36.331182934522225],[-121.91843512936651,36.572282141469834],[-121.82258904270338,36.704162529536021],[-121.83077865846404,36.838984040677424],[-121.92280423344815,36.937853668074325],[-122.16410352494519,36.991175622142066],[-122.39466039087368,37.207645605352972],[-122.55451894078799,37.830680471278058],[-122.84742757520856,37.997621383060427],[-122.99858340105654,37.988984349665969],[-122.98026847688372,38.205364799186327],[-123.11432417568211,38.427009828468215],[-123.70089774933959,38.907393148318484],[-123.72387863952902,39.119650861658265],[-123.82008450926105,39.368418547738521],[-123.77965632401246,39.548961764248759],[-123.83421243806866,39.774899668216818],[-124.09630167237981,40.081807907563473],[-124.32529042155736,40.256594928453424],[-124.37147342817897,40.491180793468374],[-124.14566004882194,40.944083983202539],[-124.07087877884894,41.43648561247619],[-124.14470742621999,41.679442381413061],[-124.2443398992464,41.788017181563326],[-124.22631881538578,41.967624267125011],[-124.35504568800181,42.12303959666616],[-124.41980619442575,42.375882415762973],[-124.41385028838552,42.592130022090124],[-124.53937211488483,42.812928394561681],[-124.14745302025811,43.739780377891627],[-124.05894788420947,44.77772706832765],[-123.93360349957845,45.586272024690309],[-123.95048170411462,46.093588540149952],[-124.07247823873178,46.279574040525269],[-124.04221552118327,46.658486250627412],[-124.19750630646057,47.198879367123027],[-124.39470447384095,47.686558093541237],[-124.66280894191205,47.974230841991314],[-124.70965167588474,48.380128027608322]],[[-75.505417827533819,35.406876969751437],[-75.539288105741321,35.573071344582381],[-75.708075932157655,35.653633667182206],[-75.911109143091153,35.543322539221265],[-75.964832863665833,35.401281608852706],[-75.903291898110865,35.262448710694827],[-75.761967480958234,35.206867480330416],[-75.584943893375581,35.269722046531278],[-75.505417827533819,35.406876969751437]],[[-76.341070951114887,38.086997726246935],[-76.264000960446666,37.88877210226628],[-76.320007106868104,37.65633571213354],[-76.291167386940657,37.530797427429476],[-76.165141573180577,37.420637080529659],[-76.010548055623261,37.435953465291462],[-75.792411089870001,37.783039318301391],[-75.870709810229982,38.102738435540502],[-76.000812855158458,38.255769790614785],[-76.141016016466352,38.298949688538457],[-76.283102579397308,38.235549859293485],[-76.341070951114887,38.086997726246935]]],[[[-81.297211371464499,24.682514082567025],[-81.364835030901475,24.630212024765161],[-81.809018982382938,24.542598040760563],[-81.420037747139503,24.749700517178322],[-81.297211371464499,24.682514082567025]]],[[[-118.55460363301333,33.033385449455324],[-118.50079590869164,32.954876757288226],[-118.3509868477811,32.82771909401395],[-118.47305123246574,32.839092847086086],[-118.58984484725666,33.011127724083536],[-118.55460363301333,33.033385449455324]]],[[[-119.57483249619655,33.278130092402414],[-119.47893364467531,33.274383900797027],[-119.4383348724452,33.217407374824639],[-119.54350412964465,33.224828267185671],[-119.57483249619655,33.278130092402414]]],[[[-168.0880285509279,65.657728688548644],[-167.93059820691929,65.747894982524059],[-167.78738051699318,65.711462166834565],[-167.4052579978769,65.859109922492181],[-167.06510322890827,65.881371336487305],[-166.74753692521821,66.051570885103729],[-166.24026942344059,66.170178283400773],[-166.02731090650246,66.278981203909879],[-164.45552118950343,66.58870382369507],[-163.73222950608519,66.616376548844741],[-163.63905663655166,66.574820348016189],[-163.77216954476515,66.517835033850261],[-163.89255167981807,66.337132720579689],[-163.85674539421728,66.181175092224194],[-163.69474675798253,66.086972809192758],[-161.94579816590849,66.07819907095508],[-161.84011593940951,66.255381144840868],[-161.96262347718573,66.532066840669842],[-162.20024645081924,66.695162315163657],[-162.46732786460737,66.735907311951649],[-162.70737486847611,67.01309469511861],[-163.53175334800005,67.102855441085765],[-163.72040993476321,67.195766877330726],[-163.9448804004887,67.475406880090972],[-164.15189496803117,67.616031771448903],[-165.37232093615737,68.040831887687673],[-165.95947515455705,68.156212703019278],[-166.2601087939326,68.282120650502165],[-166.78548981712919,68.359676041182311],[-166.3652301724569,68.463725835248084],[-166.20553061867994,68.747649200682375],[-166.20876489014444,68.885110871119039],[-165.50373609594433,68.867761641980593],[-164.312646001487,68.935877662888458],[-163.87958824174859,69.033549488987219],[-163.55142259187761,69.163799140405686],[-163.17439897345835,69.415823751337001],[-163.09330290029467,69.610555825878507],[-162.95194943501542,69.757911037328199],[-161.88097324825196,70.331475496776022],[-161.65282855057413,70.233898833207064],[-160.99653096024215,70.305698333526479],[-159.68638063735898,70.784692908017519],[-159.31447979298326,70.878318700228959],[-159.0393146867834,70.795486509699984],[-157.91890563456343,70.858525075424197],[-157.33006117866108,71.037742009750119],[-156.78695805659621,71.317234136136889],[-156.47019649044964,71.407385337094283],[-156.39544751735482,71.396474216445029],[-156.25401114005189,71.258779894736207],[-155.64571034852941,71.182510139194306],[-155.438730024671,71.008318835555514],[-155.16681549287705,71.098904287872642],[-154.82409333616602,71.050283443290553],[-154.51098945396234,70.844899694451613],[-154.18634638163797,70.806992549374669],[-153.91817280066056,70.877112533682379],[-153.23290686002846,70.932348085431272],[-152.30560448609754,70.847711249269068],[-152.23319202102664,70.810233668592744],[-152.22329586017349,70.677347441079107],[-152.14879885500093,70.589861197932805],[-151.7691923320441,70.559925652925799],[-151.6125217132475,70.43764744716691],[-151.2438935241066,70.420507147592957],[-150.66264943902218,70.509649994752593],[-150.27363381669284,70.434897182732129],[-149.87008114768594,70.509368301688497],[-149.26945379220714,70.500413813579982],[-148.68843575529456,70.416084276910865],[-148.45791915678964,70.318406125424701],[-148.14276239842968,70.355272340581749],[-147.68638399580061,70.215852378369433],[-145.82317590892328,70.159819799124051],[-145.16347409407336,70.00803616591385],[-144.65143423828735,69.983610298288554],[-143.74642492099471,70.101808612369013],[-143.21828634599245,70.115999663309935],[-142.70789861137473,70.033636347287143],[-142.28510416624076,69.869219170620283],[-141.70645167021289,69.771565436227775],[-141.39459352024264,69.656076997014026],[-141.0023760175535,69.650577304608944],[-141.00214843750004,60.468825566820939],[-140.95507024309595,60.339926686401874],[-140.85551868807596,60.276078021172587],[-140.63458888275869,60.237112414865067],[-140.4527507200699,60.299445863630737],[-140.00431552371271,60.194716140711648],[-139.67624912029518,60.3280376194922],[-139.07948499227325,60.343458123937403],[-139.11254943705572,60.144334528628029],[-139.03717042362806,60.007930573862495],[-138.70561660003338,59.901123846715365],[-138.5984801478557,59.761262605180725],[-137.59347209192686,59.226116415452807],[-137.49710033570437,59.040524897814137],[-137.37484552615604,58.976547939603265],[-136.56213420461043,59.183046194265863],[-136.47610873313312,59.294425617917121],[-136.46611841271641,59.45890040843193],[-136.32164495890538,59.604631152376932],[-135.47594871960996,59.792992812519152],[-135.26456448144191,59.696721290343902],[-135.05120459554843,59.578488615524833],[-135.03056217882389,59.405095550857943],[-134.93604920867685,59.292097244501171],[-134.44090157828035,59.085146201904855],[-134.26724420352488,58.880079476562152],[-133.82085823525392,58.704830770074601],[-133.40135971221008,58.410756666716978],[-133.35144808660681,58.286387604502153],[-132.29850742730764,57.272788986778302],[-132.17443424736325,57.086904632030986],[-132.03182731082947,57.026368809449224],[-132.00756338536902,56.890662184846711],[-131.86639403584033,56.792640194709861],[-131.78521947997672,56.634923606487945],[-130.74180776097776,56.340593393118368],[-130.47723631254979,56.230372159364329],[-130.34377108269626,56.109518954905823],[-130.09792317166676,56.109040687305267],[-130.01433530450745,55.95054229320106],[-130.14448887668183,55.631219575270677],[-130.03679737185584,55.29797503316226],[-130.21430717544786,55.026006907499038],[-130.57536600636004,54.769941850290429],[-130.84947549656837,54.807834313881862],[-130.93620940380217,54.935476244993581],[-131.04395195632742,54.985097578038911],[-131.23215584347511,54.903965619588064],[-131.40615297348944,54.894498222712635],[-131.66505351602319,55.116864330907681],[-131.77572794454574,55.151650338527752],[-131.95309553281888,55.047917529659713],[-132.02184734672295,54.72651976677863],[-132.18410806808524,54.732771340629945],[-132.34421186792568,54.885984831792292],[-132.45413291442739,54.916937047722548],[-132.59278491384188,54.858142407228563],[-132.70591544166246,54.684456933022496],[-132.80715051841889,54.709419134103271],[-133.01346149851696,54.859363261477391],[-133.27403037704985,55.161733864100519],[-133.51337328863974,55.275791286995968],[-133.64994447894892,55.269638641177679],[-133.66023024319261,55.402967607845589],[-133.73672811556264,55.496904936894389],[-133.6422209827679,55.651529761994247],[-133.70141869967117,55.912249112195404],[-133.87295886914401,56.099897060809177],[-134.18945114010575,56.077182105849992],[-134.24485209750856,56.203301328524219],[-134.20838295635443,56.467890375452995],[-134.32099159103043,56.605551452175419],[-134.44917959207376,56.638264029122283],[-134.55518257815319,56.596396206182582],[-134.62641943939295,56.484917123379162],[-134.65419133505631,56.227663873631286],[-134.7501915559827,56.240963942628206],[-134.94713212875743,56.452900848976334],[-135.04552044763392,56.667752888583543],[-135.33041844920854,56.822051936348245],[-135.41888355704381,57.034706000089272],[-135.54643960288425,57.075146660056447],[-135.81189979187641,57.009817277475854],[-135.78741791109073,57.124387158943463],[-135.8514880377229,57.384491230458643],[-136.09570514231348,57.68121871532761],[-136.56832636916104,57.972213426589377],[-136.54527412007559,58.105047792044765],[-136.62498491319565,58.233102109684204],[-137.54385459641864,58.581466169579748],[-138.02740505529604,58.940647691338391],[-138.25111377980099,59.050589492724221],[-139.77320315624848,59.527453764868156],[-139.84006432621402,59.702500676262758],[-139.95207719149738,59.772876857266347],[-140.65219045611607,59.72369467261418],[-141.32646880670814,59.87238280084572],[-141.54559913070057,59.983211677518852],[-142.93542545142543,60.096690897595231],[-144.16768512649662,60.013579905190944],[-144.6129636276749,59.812925842121821],[-144.48293388273717,59.970988107319499],[-144.51063497932736,60.141344255874337],[-144.97031101242385,60.362687416874905],[-145.23214480891374,60.32078926416451],[-145.57386597445119,60.442578099461109],[-145.96294060073996,60.483875962338985],[-146.19651777604048,60.369960365112071],[-146.61815670389703,60.273880720938429],[-146.72705985846994,60.364885502782144],[-146.54667469367249,60.56981511550898],[-146.60077845106878,60.85383069910263],[-146.76220945239837,61.004247978270762],[-147.12119952806094,61.001561336553273],[-147.36139416605781,60.914736638091547],[-147.52508666154682,60.96278752961301],[-147.84720727500124,60.87762034859287],[-148.00226809286835,60.73555824174268],[-147.96637364128063,60.530640027814499],[-147.84679751806686,60.464948259346635],[-147.68865757355186,60.491148067098571],[-147.64070701626702,60.257175746605675],[-147.5433646960401,60.161709214351525],[-147.37041714610962,60.165059121371144],[-147.18070164384702,60.358014108593053],[-146.95342663076525,60.295124285152497],[-147.30232511141378,60.082806746950759],[-147.54029510810028,59.867749915148117],[-147.84624549925564,59.799074689573089],[-147.97164868861512,60.007658884848055],[-148.26520853419694,60.052645834336666],[-148.46511138773619,59.974988766694331],[-148.74478941607111,59.948269447976543],[-149.14511878821048,60.019250729544289],[-149.36895048620613,60.007381708086108],[-149.52333155002043,59.913736153781954],[-149.59821209632193,59.770648552409845],[-149.90399057868504,59.727965482224711],[-150.19809367105361,59.566773708859444],[-150.55871461047582,59.521113411043956],[-150.93466831509389,59.249339812352936],[-151.73813244270093,59.188767527342058],[-151.96380241043354,59.285122602257012],[-151.75864796410519,59.539573115475136],[-151.77010424582414,59.652263538906581],[-151.85294506643305,59.782089823905288],[-151.7342585821826,59.988085577089038],[-151.41124697896871,60.254714605275836],[-151.31671102307163,60.476613327783312],[-151.36938168331253,60.868466230381024],[-151.44871756468504,60.952512899958599],[-151.56130394029054,60.978634400073489],[-151.71408732801572,60.894184879777008],[-151.75074182223753,60.755030955712904],[-151.88961619509766,60.635750839478497],[-151.8875878293868,60.472760681472913],[-151.98707312253873,60.374225944692746],[-152.24225199356184,60.39948466890543],[-152.52433135140029,60.271242863845643],[-152.60611167772498,60.190399661098866],[-152.66027920527537,59.997383190244463],[-152.96035116004998,59.862790793371907],[-153.04833338040024,59.73020390180659],[-153.24054538677296,59.670568896497571],[-153.51856675362035,59.673306133387186],[-153.92821992747847,59.411152605740178],[-153.98781012260912,59.269109049194157],[-153.91407641369952,59.113623600622965],[-153.41834073818816,58.95979619446706],[-153.32844835101591,58.879309729403893],[-153.44253700366221,58.752083193232608],[-153.98043980796464,58.51542553214189],[-154.08234708847513,58.369499433119366],[-154.20011407965049,58.293470019691014],[-154.24722718104027,58.159594203856095],[-154.47526873703299,58.134385445438063],[-154.585059057677,58.055903357348875],[-154.983270988371,58.010495487724945],[-155.15090581004563,57.880235680913856],[-155.55083199079917,57.739620110368172],[-155.77812599040593,57.568447028590235],[-155.95383127042237,57.539038092750246],[-156.0555126899728,57.447783355414984],[-156.28173050946864,57.430997267441711],[-156.37010578429795,57.3512841747377],[-156.47217409391604,57.109143504550651],[-156.62911699123441,57.010192876216813],[-156.7636325750976,57.002938001300294],[-157.14454230644219,56.825390580200057],[-157.39405405012349,56.805817896048609],[-157.57845609637721,56.634658526301145],[-157.78872290387534,56.638676492444581],[-157.93021652347662,56.52063747364614],[-158.20542327285582,56.468257647251505],[-158.29764154350781,56.341377679425307],[-158.27596565769309,56.196332504035162],[-158.50040922807267,56.064113215184761],[-158.63504404881388,56.075217848521994],[-158.78992510549398,55.987104850272608],[-159.42891858937389,55.841840845237641],[-159.65957848347898,55.626234327173286],[-159.74160094801368,55.755990753903859],[-159.88594032628782,55.796954249469707],[-160.05553321316043,55.75783445375324],[-160.34955175069584,55.577754866769737],[-160.37129515443931,55.411527456294138],[-160.2432716856263,55.203084385452783],[-160.09000079920821,55.173755402640936],[-159.88739868080262,55.272750025216077],[-159.7394427762421,55.174574050013739],[-159.54520269740988,55.225704009744824],[-159.36224711269753,54.972604898360039],[-159.81914113484908,55.142151046915338],[-160.22665560964663,54.923057756852792],[-160.22598533923727,55.075570789284768],[-160.34893297581792,55.190198149372144],[-160.79503472413163,55.145508192272459],[-160.86397883911528,55.321445022072517],[-160.97118702617928,55.40648305382107],[-161.38192583446263,55.371597979844815],[-161.63489718571137,55.441059737382204],[-162.16045869518098,55.098722312191981],[-162.29824023869952,54.847230131912212],[-162.39061160651644,54.873242068431566],[-162.51674418666232,54.995111208654542],[-162.63155844234552,55.009258659150639],[-163.02417769391033,54.917338695061034],[-163.09822182311072,54.801475153592676],[-163.08359173186673,54.669301571744406],[-163.33735122498356,54.722542989621807],[-163.58304809255222,54.626007456500169],[-164.09022933962757,54.617890312368409],[-164.46351234841683,54.427481534151212],[-164.82330793013412,54.419351639882002],[-164.90366055932355,54.544837388680229],[-164.88745674921469,54.607623028624587],[-164.71367790432896,54.688010302206003],[-164.4785564325974,54.906647537896411],[-164.26499579963956,54.907961228794477],[-163.80711631957428,55.048925487828392],[-163.39482527733028,55.028320462648132],[-163.11845196395916,55.192211533598936],[-162.89732926548319,55.223452606365726],[-162.16266968101979,55.715839197315653],[-161.69238688065636,55.908394894178073],[-161.21561855881208,56.021157594183137],[-161.06556886973078,55.963879301723061],[-160.89870217933196,55.993385237666644],[-160.78013925512602,55.901912859064105],[-160.64774117921147,55.905847655971854],[-160.37286593190953,56.245908775256837],[-160.15445244734772,56.393547440074961],[-159.78497578430887,56.561391705222938],[-159.2945305375556,56.686957532005792],[-158.99029106315012,56.85984223594334],[-158.76175631686638,56.90101631812523],[-158.47055339708993,57.201127360875887],[-157.73717900211597,57.596492492870986],[-157.61065211219355,58.050753513328068],[-157.51266386879567,58.223609291655549],[-157.49788345719801,58.587725048012224],[-157.56193538479238,58.685704651624413],[-157.66985235524402,58.731054179087273],[-158.18604865455197,58.615004881508717],[-158.29747838124027,58.640536812344571],[-158.51484216808461,58.857265062980296],[-158.66182447854143,58.89225391438513],[-158.76528204670547,58.843023785197836],[-158.82474277311132,58.745087601662334],[-158.78881032426827,58.441151098547934],[-158.95065435094452,58.404785636604778],[-159.6078947135158,58.875928746967816],[-159.72242623809919,58.888690845796141],[-159.92018940612203,58.820165585420341],[-160.40487976610805,59.031398559689329],[-160.65309321968499,58.939962248781157],[-160.71537185326474,58.795303923441359],[-160.92390946346293,58.576023667117042],[-161.07011467254134,58.569378039717883],[-161.1455599994689,58.6603912833558],[-161.25911571708821,58.696859550946549],[-161.75547884457754,58.612335399614707],[-162.143883135267,58.644346826763559],[-161.87143367955559,58.72903431628972],[-161.78420200479181,58.912123783264803],[-161.82763412580604,59.023802167209432],[-161.98083240887695,59.146295977449206],[-162.02301136264052,59.28389711187107],[-161.86315203310005,59.447558680551609],[-161.85145569191303,59.616813749008912],[-162.23967249304417,60.123200310054429],[-162.40696209968985,60.150622703236692],[-162.57086723662684,59.989978368948371],[-162.74066060924375,59.985664128166405],[-163.2131989214333,59.847002700175757],[-163.91219804497985,59.80881367681112],[-164.14262385583464,59.896938491769667],[-164.22520116686457,60.033230561692839],[-164.64057109660774,60.281874564265152],[-164.92402086580458,60.350384468314545],[-165.16510333556371,60.509560369929801],[-165.3531667388196,60.54133988160693],[-165.16352924116345,60.657689765545932],[-165.10302233480402,60.757079776227535],[-165.10807983477898,60.873328998308715],[-165.20549168848132,61.025167461652686],[-165.33226243641911,61.103652241003275],[-165.56575510003205,61.102585463204427],[-165.71407815151102,61.287622532382926],[-165.86380831751907,61.335917511858398],[-165.96972077160984,61.473405707541907],[-166.1525516991826,61.546075995068001],[-166.16787171627172,61.65070783094356],[-166.07858674541174,61.802958591102865],[-165.83161460681561,61.868463018036159],[-165.70706031584055,62.100276291285198],[-165.45058169496025,62.301613541865805],[-165.18871892007365,62.476419740780301],[-164.90440171446735,62.579026354717847],[-164.76392044773107,62.97045261287024],[-164.58912758283819,63.054235633304799],[-164.40897127525128,63.214857213821084],[-163.94290198951356,63.247039572322635],[-163.27427416601273,63.048438387621367],[-162.97474233417353,63.106669305616656],[-162.63200396741138,63.263609841823978],[-162.2827129537126,63.528996666397106],[-162.11260551372246,63.533967916292056],[-161.96402943519564,63.454194118319386],[-161.49675126301861,63.469207201134559],[-161.24418488413355,63.504971706338473],[-160.90463056885525,63.675706956611705],[-160.81879772045451,63.86012164964977],[-160.97995426439212,64.221611755112406],[-161.23038168152038,64.398220910686845],[-161.49025507133567,64.434026915354835],[-161.42005777603001,64.613142827221537],[-161.50961274954599,64.760103768410517],[-161.73678559183915,64.80329669014283],[-162.17204777129672,64.67736112876689],[-162.71568835293803,64.377379725572609],[-162.80689405508559,64.374447051119972],[-162.94911123287696,64.491040996280759],[-163.14439661338216,64.424093266688047],[-163.71314753254049,64.587466496007607],[-164.30386613737539,64.583058062081037],[-164.97876100393523,64.453822942328941],[-166.13623366352329,64.582107219026668],[-166.325022239741,64.625908053044327],[-166.48117388335456,64.728189807445005],[-166.52719752977166,64.92464347575401],[-166.92815037691165,65.157202379749464],[-166.94743468249797,65.279884859965719],[-167.02843223519162,65.363991931272835],[-167.98267935902373,65.566630557588084],[-168.0880285509279,65.657728688548644]],[[-133.91321831099836,57.34806510763913],[-133.91779512071446,57.189717847609295],[-133.79600712879812,57.074380850298418],[-133.5915414635071,57.0665261058315],[-133.46766942674734,57.183930883646333],[-133.45597366691683,57.375400214001402],[-133.53764923208968,57.54362576421228],[-133.64777290527653,57.63116385319762],[-133.77814617969614,57.638845092662976],[-133.87405959629498,57.578868121016811],[-133.92215856984069,57.477172732047514],[-133.91321831099836,57.34806510763913]],[[-134.59787137157005,57.570630219137925],[-134.75368225255772,57.623993835521937],[-134.90035588428648,57.536752173399655],[-134.9475191603326,57.359718578365488],[-134.84804136964752,57.208058935760477],[-134.69961049067052,57.173584752555598],[-134.56997648764971,57.253679823579581],[-134.51520220698103,57.41670777282242],[-134.59787137157005,57.570630219137925]]],[[[-162.82028796145588,54.494260179598299],[-162.64548642781958,54.461802183657994],[-162.55487835469148,54.401479205314359],[-162.73304341889454,54.402456323882539],[-162.82028796145588,54.494260179598299]]],[[[-154.7769332018637,56.439858306299385],[-154.62715652851276,56.559408391096902],[-154.40299089274583,56.63361082353714],[-154.31802457697276,56.757926524701304],[-154.34385040850418,56.906273060884985],[-154.49861051247578,57.036705857485885],[-154.57299134054523,57.203388148331243],[-154.70572129218598,57.335482173070815],[-154.6729655886314,57.445913777657964],[-154.53520062696401,57.559210941513804],[-154.00566101073457,57.675192089561591],[-153.84146224400368,57.862661362047021],[-153.57949578229821,57.900190273241101],[-153.38115383675569,58.087054468852443],[-152.46312512985844,58.618189359095425],[-152.36806327080905,58.610887484949743],[-152.33996110199101,58.488061394896356],[-152.26164853080485,58.400368143588338],[-151.9746339762273,58.309666661179612],[-151.98274535066778,58.244465109965134],[-152.06899069237591,58.178092083844241],[-152.25984236442301,58.162101973387706],[-152.44752237298326,58.016249694056469],[-152.45867484017842,57.89856181175756],[-152.38460790473437,57.712193941990598],[-152.21550164437218,57.597606781624066],[-152.33680742462249,57.4824432926574],[-152.59704021899211,57.434544757918822],[-152.67922879775082,57.345297265236759],[-152.83865586441334,57.281369758039226],[-152.92837938103585,57.131319520613886],[-153.14230563120941,57.089710748519813],[-153.29539046656109,57.000725806189777],[-153.42777313858812,57.068275574717987],[-153.56086673241609,57.046733896823547],[-153.75733370822269,56.858523506629396],[-154.03338250865346,56.738333415151914],[-154.12063046037289,56.541924690139979],[-154.25777802754271,56.512926769312486],[-154.42673675762646,56.551296623877889],[-154.74665916542097,56.413734066257582],[-154.7769332018637,56.439858306299385]]],[[[-156.0483882684087,19.749928283339763],[-155.86405003107782,19.969639981154941],[-155.89255845503638,20.167409187103999],[-155.83165662115567,20.275589711935314],[-155.19894474473344,19.994140712383306],[-154.80449294095359,19.524444552737123],[-155.05354740382379,19.319376787101206],[-155.34016015432795,19.23970745181845],[-155.52237836927458,19.112161848371453],[-155.62573395502511,18.964164893815592],[-155.68069208601204,18.967926447213213],[-155.8832618645919,19.074973917554289],[-155.8987329235816,19.40173306646836],[-156.0483882684087,19.749928283339763]]],[[[-155.73707482469484,55.829717420859637],[-155.62045640456563,55.912818951818316],[-155.57342001752036,55.920895168136305],[-155.56623357087605,55.821299331521274],[-155.60497016120533,55.789808750634251],[-155.73707482469484,55.829717420859637]]],[[[-158.26801399296141,21.576924410252165],[-157.96260419380872,21.701107613676498],[-157.81904210425978,21.51091599085909],[-157.72108976732784,21.457530957233121],[-157.63572460100164,21.30769756199367],[-157.79873695997941,21.268873246251619],[-158.11017098580234,21.318833207411487],[-158.26801399296141,21.576924410252165]]],[[[-157.2782787543531,21.180862406496921],[-157.24982071427391,21.229484924637173],[-156.71698040916584,21.156434615479835],[-156.48999909247976,20.96160314828947],[-156.2775828776802,20.951017207715033],[-155.99013584383371,20.75710279564727],[-156.10723107114168,20.645028072467831],[-156.30999191942925,20.598975600242536],[-156.4380297343715,20.618022566522413],[-156.49652913307344,20.753699249027974],[-156.64592276338936,20.833217021407176],[-156.9732009547931,20.757710604251052],[-157.09226172797779,21.032577941318415],[-157.29003376077091,21.112812783054981],[-157.2782787543531,21.180862406496921]]],[[[-160.24318872188937,21.843042716049375],[-160.10055443341133,22.014993243231846],[-160.02731947939026,21.99279879872034],[-160.20037178080079,21.79712400349187],[-160.24318872188937,21.843042716049375]]],[[[-159.80429441925997,22.02926368792777],[-159.72644162980797,22.140006484590721],[-159.57912904592578,22.222867926081832],[-159.35218563234034,22.219321558826191],[-159.30498069101833,22.153980385048179],[-159.34403097127938,21.973749473467056],[-159.46071133344236,21.876407438496869],[-159.80429441925997,22.02926368792777]]],[[[-169.08855981341569,52.832273860201795],[-168.82765391582822,53.028812557149763],[-168.75529992856826,53.178279925177968],[-168.47904258079191,53.297182662889462],[-168.2826813638564,53.501903177423983],[-167.98573601183884,53.557915715134719],[-167.82819148718963,53.507767817996367],[-167.69764607736911,53.39805762931217],[-167.52037092070614,53.398950229115968],[-167.14463188408905,53.54220497551983],[-167.07768917202054,53.701772748100545],[-167.1179291615147,53.872508404133129],[-167.03374017604068,53.943690306037595],[-166.67799332653024,54.005665971812917],[-166.51603008040883,53.951889895891227],[-166.26190615784563,53.98985373299125],[-166.04545593891191,54.189263057117657],[-165.76280547041057,54.190406900781511],[-165.58460610108671,54.279507265118681],[-165.40859128054103,54.196847427146928],[-165.9934888009638,54.028055708875385],[-166.20990790719051,53.72351954723247],[-166.77050805754214,53.476203227708645],[-167.6286480314117,53.259628430098942],[-167.9618993466203,53.345035874868252],[-168.27888780601657,53.230048050283365],[-168.50569006296638,53.043349507226075],[-169.08855981341569,52.832273860201795]]],[[[-167.43590460918148,60.206514629903353],[-166.90943249538813,60.223725602075817],[-166.47565030496062,60.382559558591261],[-166.18497754637775,60.396583390308578],[-166.01046814030201,60.333544017558083],[-165.83626490791431,60.345362754044388],[-165.69602987693696,60.281430102715355],[-165.68800049518165,60.121577575512035],[-165.59207793981219,59.913335587210334],[-165.97794951306778,59.881795763430318],[-166.14873283467287,59.764375251684314],[-167.13880397726331,60.008727146645384],[-167.43590460918148,60.206514629903353]]],[[[-171.81911210758062,63.47731108284605],[-171.74621525497406,63.702866222075436],[-171.64650971438743,63.726758721776406],[-171.40788910096848,63.62163476592842],[-171.03481821050778,63.586478868093323],[-170.43042818492776,63.69851667605878],[-170.16490692988978,63.637900158759621],[-169.98110778600801,63.488815066866827],[-169.62419469354381,63.430361351394566],[-169.49576183503632,63.361843528892983],[-168.7163557399183,63.310409298169219],[-168.76151094634614,63.213952495734091],[-168.85243927113279,63.171480624849742],[-169.38220392136537,63.161896649485421],[-169.67630970095701,62.956363588000045],[-169.83833005153315,63.129635070679846],[-170.18953136212198,63.196549160697131],[-170.32857269258591,63.312134334692693],[-170.53533607996587,63.380965448753436],[-170.96866770982899,63.451970891843779],[-171.5191227355223,63.332314970582246],[-171.73157418665193,63.391694861348341],[-171.81911210758062,63.47731108284605]]],[[[-169.7658484151203,56.608082636797398],[-169.55048580048856,56.627812894555511],[-169.47463163348542,56.59415678424908],[-169.59186691208441,56.54278928460208],[-169.7658484151203,56.608082636797398]]],[[[-169.99160296414209,52.829837559907688],[-169.82063075878438,52.88316507456048],[-169.69220826613306,52.84731033536255],[-169.72284691564965,52.792608842167283],[-169.99160296414209,52.829837559907688]]],[[[-170.38637781027867,57.202827005325723],[-170.3817398196164,57.203727060605431],[-170.11665882492036,57.24150623272326],[-170.26404632445642,57.137033499048052],[-170.38637781027867,57.202827005325723]]],[[[-170.82680217377293,52.600656482976191],[-170.6820287991024,52.69729117025021],[-170.58487730995364,52.667461200541993],[-170.61419851451228,52.609859031523406],[-170.79734855958858,52.550046970765713],[-170.82680217377293,52.600656482976191]]],[[[-173.07373130886847,60.493367935638524],[-173.04745324997666,60.568100120869005],[-172.92398396061617,60.606546096276716],[-172.8008602984107,60.481371021466295],[-172.5370194935563,60.396438250477075],[-172.38755483203968,60.398225229282794],[-172.23272051699914,60.29945714217547],[-172.63569324156745,60.329109179062783],[-173.07373130886847,60.493367935638524]]],[[[-172.61942405196484,52.273012077238235],[-172.47037495716498,52.387774062939876],[-172.31409962265909,52.329636567705705],[-172.53439279839372,52.258410068767965],[-172.61942405196484,52.273012077238235]]],[[[-175.29479648581079,52.022115310022961],[-174.56392617624448,52.162377810131581],[-174.43533345975266,52.317060499393257],[-174.16891077674919,52.419902775973235],[-174.04576837989143,52.367026638874023],[-173.98660451624534,52.204659801917799],[-173.87713264044567,52.122798813896196],[-173.65223107052313,52.143417667619119],[-173.0231628294614,52.079358439162],[-173.83578559526069,52.048480782825081],[-174.10781007499776,52.134538249188147],[-174.67993254572212,52.034815112606282],[-175.29479648581079,52.022115310022961]]],[[[-178.1941645276768,51.882222601236585],[-177.95388067825769,51.918209893927049],[-177.47709626843056,51.753678417713537],[-177.27138014670507,51.800619114889109],[-177.13143778998415,51.929554981047723],[-176.88104634091019,51.824481154572247],[-176.77447886056771,51.870783122799601],[-176.69820888246488,51.985810548764576],[-176.60211925730576,51.982008609342493],[-176.48532316757954,51.886305719333784],[-176.36416690106566,51.861947964958645],[-176.24076085799067,51.921793838192492],[-176.15553884734851,52.099182229518149],[-175.97559804415346,52.028984293424166],[-176.02574968924375,51.916678190265202],[-176.00929886180225,51.812502607439704],[-176.22311481011053,51.808786574390133],[-176.34971535487423,51.733564846810793],[-176.96154108817564,51.603970493873817],[-177.12963443591755,51.707759512276716],[-177.76548826288789,51.694974525525993],[-177.9252999870088,51.617573057164535],[-178.074918626635,51.687879713176955],[-178.1941645276768,51.882222601236585]]]],"type":"MultiPolygon"},"properties":{"alpha2":"US","radius":2971000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-58.437847739556268,-33.719131001163831],[-58.363287926655602,-33.182436710070654],[-58.174012730735207,-33.084025125544144],[-58.092521302198627,-32.937847637106728],[-58.200919581811,-32.471691152947848],[-58.133178606959248,-32.2938617851107],[-58.188728777237714,-31.924213775252205],[-58.016539919789409,-31.707745801312544],[-58.00488503222418,-31.591011203736478],[-58.052596354652877,-31.49009430501367],[-57.876348033509245,-31.133325967992608],[-57.898043500354788,-30.975175310025147],[-57.815133083466904,-30.793900307438062],[-57.872224502728713,-30.591013862752604],[-57.608903347173978,-30.188129786006442],[-57.507351768514617,-30.262408296253092],[-57.297899254214727,-30.281082796071271],[-57.037080146175917,-30.111675010643889],[-56.832790635180721,-30.10744038803654],[-56.176378242128592,-30.628629494069138],[-55.919767382136392,-30.938861796452461],[-55.752782657397425,-30.963043464947198],[-55.598655264377946,-30.853191498884602],[-55.369477120219464,-31.042850584778574],[-55.209378638098869,-31.255722961796444],[-55.036084491223392,-31.279249402192995],[-54.877099186330895,-31.396026682186157],[-54.587772795602568,-31.485366493175928],[-54.242166547896581,-31.839238562941837],[-53.76188914252036,-32.057005134165699],[-53.586539647379126,-32.415348973730886],[-53.227066715872411,-32.628483217886405],[-53.125896214830135,-32.736672010388254],[-53.516061757761761,-33.131529518848623],[-53.526933663598562,-33.593693959629469],[-53.465860873827005,-33.695155346291855],[-53.371113678805862,-33.742308625354553],[-53.727680294969296,-34.234181138004658],[-53.785515251550663,-34.380203033981012],[-54.168652054273288,-34.670449813590828],[-54.902312533796746,-34.932597739752275],[-55.237818855482942,-34.895561014414433],[-55.381748433947891,-34.810156670073084],[-55.663436801675068,-34.777639528275458],[-56.117959486987196,-34.907682620451205],[-56.249908556400463,-34.90104625269322],[-56.504207553199969,-34.766077590213463],[-56.855084635332268,-34.676462643004136],[-57.193170017756664,-34.456158190628386],[-57.829039197294364,-34.477094242443975],[-58.399980591993533,-33.912289158684125],[-58.437847739556268,-33.719131001163831]]],"type":"Polygon"},"properties":{"alpha2":"UY","radius":299000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[55.975906741940314,44.994743971412476],[58.555235477698233,45.555102696094309],[61.00774308412317,44.393530398032709],[61.161711467769294,44.16958479261271],[62.008637113668314,43.498078819301],[63.207037542154609,43.627767807448585],[64.421014318113535,43.553709008194161],[64.90541804099216,43.714428986776362],[65.271104214606765,43.418503569300867],[65.496018726638951,43.310341434771402],[65.772229121591593,42.948625511624542],[65.908934292269535,42.921923158812213],[66.100042342199473,42.990491359464279],[66.027093201617589,42.127876072860275],[66.151409785375037,42.01058222958163],[66.498427595744559,41.994638330860198],[66.602758657197313,41.490669352682588],[66.724884039601051,41.192993192702623],[66.865039057309588,41.14485779048897],[67.935651283716481,41.196310190268974],[68.112818619631256,41.028515238771298],[68.076907995950705,40.894425855331924],[68.115311987967942,40.769130319611065],[68.307155705623003,40.652711337435733],[68.489775334785662,40.693273643127107],[68.587018238150677,40.879449814903005],[68.995242674241197,41.214959657610393],[69.065151683260723,41.366800250231861],[69.148788036359079,41.42207159525065],[69.659026336367873,41.670007552742412],[70.067436991932425,41.80670748962433],[70.416085590899272,42.078316246046619],[70.612930697785117,42.086479391763774],[70.759479646126181,42.191563508897516],[71.036029820810128,42.284435843283568],[71.228296476892496,42.163061252123555],[70.606993544437017,41.859800612453249],[70.476820594257958,41.698384060417048],[70.518700922709371,41.531005316118197],[70.688743086888095,41.449594269469877],[70.836188219570431,41.240329676360687],[71.119915241312313,41.152409666039944],[71.290463279674398,41.15483024847375],[71.381306408369383,41.223228406964552],[71.421107686649876,41.341687165510855],[71.534393458861686,41.387843222311858],[71.664921807452814,41.540920297095738],[71.959404023215512,41.195114556307701],[72.164071717852551,41.173516689372278],[72.248984203531109,41.071402281190231],[72.501964542753839,40.983581545499298],[72.648529764501106,40.878380965832001],[73.136673901838464,40.810707356737197],[72.679486521208588,40.555796888185327],[72.467536693836877,40.501705292500624],[72.357651874164219,40.401923093228326],[72.192301625944921,40.432926169674914],[72.084293852335733,40.39869220097934],[71.81112148763944,40.162704923942464],[71.752826188800313,39.907373745492912],[71.66922950784398,39.946183635183111],[71.674674400578908,40.062125051057471],[71.613749570584844,40.162501375963501],[71.418167625216142,40.231504436102874],[71.269531781755404,40.154023189387161],[71.21537443141915,39.906811714327191],[71.151327258262484,39.883525653232311],[71.011962345470408,39.895301704351169],[70.952048863105887,40.105181688905482],[70.874948697941846,40.187822087053647],[70.602888155990456,40.21438147697225],[70.515063901652439,40.319462987843053],[70.371818447397175,40.384249576507202],[70.382834339701347,40.453340838164159],[70.583668735547207,40.589980423868234],[70.638897601395385,40.687235904845366],[70.616041287774891,40.849380848539489],[70.515460350334493,40.940916289382187],[70.379597878314911,40.946970263563202],[69.712858568134223,40.657189649814441],[69.526694285253839,40.729578538921643],[69.393633706867135,40.710902295841734],[69.257402266065583,40.532612300109605],[69.303941185593416,40.327382021810166],[69.274567357018583,40.198261984889285],[69.019248009010894,40.15014167605996],[68.868488301318678,39.907573674724233],[68.657107616430849,39.791344084214359],[68.502628632810428,39.560221549128336],[68.393706111647759,39.529346962047555],[67.709922724658966,39.614002707965582],[67.534776289945967,39.550405664278067],[67.432096030583921,39.438082663581049],[67.421320502788745,39.302384681905032],[67.499887667725787,39.191221491846612],[67.648163008105811,39.130844872200157],[67.774806830321339,38.998771283863277],[68.044253789228662,38.983242334433953],[68.132316624623627,38.927511021627595],[68.053479432616314,38.657674892297294],[68.079374581924171,38.502454027745344],[68.153282796399168,38.377014009386947],[68.350766305989211,38.206356823072625],[68.291672343797629,38.029891533644985],[67.838326849924812,37.52777257919999],[67.758927828213814,37.172582066823473],[67.483993481490629,37.251707081724462],[67.319705485572598,37.20981125723835],[67.200042100878264,37.234269346443163],[67.056548185473957,37.332377225337105],[66.850040306144592,37.367914714471439],[66.522463417529096,37.348723257752397],[66.537868396605148,37.879776016532901],[66.444498989038195,38.027581762523297],[65.958375708249122,38.246992339638894],[65.609051835764504,38.240558647800789],[64.625976430536028,38.754247366392242],[64.343280442950018,38.948302174066399],[64.162834814285063,38.953887729639177],[63.767005936943704,39.158742792360869],[63.488426372476845,39.387175803119412],[62.487589372155782,39.972362807584986],[62.32127274308877,40.426614141205519],[62.098170023792143,40.678510847447775],[61.952297520510577,41.029856008354464],[61.81137243974343,41.155657669347235],[61.496848156112456,41.271895488346466],[61.246865480691461,41.189515080792155],[60.856390414459568,41.247292708549857],[60.455039797054624,41.221838645179986],[60.089786278100881,41.399590483362481],[60.070709708359118,41.821151957610844],[59.942058531876256,41.973601389208625],[59.962004528321458,42.180856407607841],[59.850171237853338,42.286950395098195],[59.354346687853365,42.323441106568438],[59.144402172689169,42.50788709814519],[58.92683736489105,42.54186431941789],[58.715394436246399,42.682932116602352],[58.585272573951151,42.700857681835465],[58.479342532357713,42.65108536635541],[58.413826124474141,42.551915675114195],[58.474258181377898,42.299549831650452],[58.397167011895618,42.292709828212047],[58.249840414287107,42.441622342272673],[58.052032217897803,42.459740266756143],[57.814142802285843,42.19006131278956],[57.311767400932375,42.131325298493934],[57.034176688014128,41.905083546018155],[56.979135665296504,41.801053980141226],[56.985471603272494,41.665339921868636],[57.01785663892273,41.479993841707461],[57.118607112946712,41.35037724508156],[57.017876872597519,41.263718474646353],[55.977659384952872,41.322425691855756],[55.975906741940314,44.994743971412476]]],"type":"Polygon"},"properties":{"alpha2":"UZ","radius":969000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[12.428999390156696,41.899392645193586],[12.427814708225522,41.900759111885314],[12.429025238308823,41.902867427650484],[12.43071067654499,41.905232258661144],[12.434573010540849,41.905792130177595],[12.438174403603572,41.905952797158463],[12.438719903191025,41.902307999119827],[12.438907419865007,41.898596169480847],[12.434922280327457,41.898012265661698],[12.430662293792953,41.897816179478525],[12.428999390156696,41.899392645193586]]],"type":"Polygon"},"properties":{"alpha2":"VA","radius":1000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-61.352530608242084,12.699372254824599],[-61.34661067395313,12.708365655577996],[-61.335668939230551,12.72866352410062],[-61.320374922343667,12.740880991861436],[-61.317012241049341,12.731621478433704],[-61.315080536072117,12.722610646023979],[-61.329047342888963,12.701303160105859],[-61.334476830289042,12.69543594045866],[-61.339164990254076,12.694984555757003],[-61.344489182484331,12.694991029947587],[-61.349120798029674,12.696485298420239],[-61.353169161936385,12.698258807070319],[-61.352530608242084,12.699372254824599]]],[[[-61.263235788160721,12.96022510486068],[-61.271074236966719,12.978870868808363],[-61.276707240831463,12.989955199033615],[-61.276714351915949,12.997290667583842],[-61.266083792231534,13.016709201664154],[-61.25873250467675,13.034125099176363],[-61.253058351652257,13.052157262172617],[-61.249112025319832,13.070644593601109],[-61.246928781657232,13.089421929993117],[-61.246528125485796,13.108321517012445],[-61.247913636216829,13.127174508151336],[-61.25107293587358,13.145812473183703],[-61.255977799674568,13.164068902899357],[-61.262584408190136,13.18178069667613],[-61.27083373881969,13.198789619600088],[-61.277069665948005,13.209619938537269],[-61.2682299359236,13.2875836099849],[-61.228142566581113,13.326723617129955],[-61.223876369078127,13.33040951295497],[-61.219220889328099,13.333589572304206],[-61.186225181660198,13.353486867498574],[-61.182059288635919,13.355734747984719],[-61.177355391429955,13.356264825757661],[-61.139155037060014,13.35848463598172],[-61.12530496551895,13.299592068831982],[-61.124349121384476,13.294024660522135],[-61.124674974651207,13.28838520195352],[-61.134768613281778,13.202991763147095],[-61.156237474036786,13.177174521042778],[-61.1668638512527,13.161113953603236],[-61.175896306705255,13.14410582414369],[-61.183251095595502,13.126307824267441],[-61.188860027682125,13.107884968895304],[-61.192671099510214,13.089008066322684],[-61.194648976563521,13.069852134563121],[-61.19477532086993,13.050594778661251],[-61.1940035587062,13.038377227856518],[-61.208859179840282,13.021073553755725],[-61.227092321997169,12.993856109295205],[-61.23490331590186,12.983846443697917],[-61.242385528152305,12.976453437677158],[-61.251041258632767,12.970175738697536],[-61.263235788160721,12.96022510486068]]]],"type":"MultiPolygon"},"properties":{"alpha2":"VC","radius":26000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-73.365962022155429,9.1942110208401981],[-73.011481911938247,9.7808389263438666],[-72.869148884969633,10.491187202404943],[-72.689914644396382,10.835731315812977],[-72.51473043100107,11.056664507871767],[-72.2361413397484,11.22445012703594],[-71.957957155662157,11.666169610592338],[-71.320228130141643,11.861519046716595],[-71.483390408154037,11.721211256335682],[-71.860907721503878,11.613006050711494],[-71.941802873828649,11.439364220997739],[-71.82232525607084,11.174147006987923],[-71.609468956607557,10.989193112827115],[-71.500565500891298,10.96068805059417],[-71.273525296055723,10.99874914491731],[-70.820453938122824,11.208281980016743],[-70.236998311824337,11.387861148041063],[-70.172037451037312,11.547664092394136],[-70.286296721623785,11.886042438474345],[-70.202591092856466,12.098200824267465],[-70.003989601850009,12.177634738145841],[-69.858117822510323,12.050246372330202],[-69.756909424483354,11.663860221262809],[-69.658729624561261,11.527827504581053],[-69.232595992909225,11.518234922117212],[-68.828057653610585,11.431504388145836],[-68.398822755428014,11.160818700782238],[-68.272365680485379,10.880027146612793],[-68.285027074563715,10.667591721262522],[-68.216957597165745,10.556601760456216],[-68.097703606246242,10.490492131081579],[-67.863300862970746,10.474274420851104],[-66.98298957955123,10.61082120674785],[-66.247265544841753,10.63195710862998],[-66.10604735722157,10.57444813852775],[-66.047209022611241,10.435304438458207],[-65.847968913465664,10.265790481395666],[-65.095541286752251,10.072159101057526],[-64.825621337573551,10.112587275741747],[-64.341975922772917,10.378736011859315],[-64.278092187543677,10.475975674513363],[-64.297894678423503,10.635111613263783],[-64.239346435914413,10.788061224547098],[-64.28070668794993,10.899225167193849],[-64.402000212484253,10.981670764240645],[-64.348510957504658,11.051725862337918],[-64.243820190827307,11.081278076933739],[-64.037801506417082,11.061926774966581],[-63.893111842692406,11.166919716267483],[-63.817561999144665,11.000361220956538],[-63.858172241726727,10.786392995536778],[-63.719168634336732,10.659212032477141],[-63.488097086998138,10.645929645002784],[-62.702336626945296,10.749583188572439],[-62.242288262769371,10.700805225398948],[-61.879963851884753,10.740735566667219],[-61.921523841917903,10.681634932054799],[-62.240243360374407,10.625445564223517],[-62.574478808911245,10.465554774404893],[-62.605254358326142,10.295042653264105],[-62.430233628881801,10.005829818671312],[-62.293357957168816,9.9370187298103705],[-62.072483899835554,9.9734978604554776],[-61.802858558025079,9.8165279059705775],[-61.688459840618812,9.8235749400810537],[-61.588806274266076,9.894218203648812],[-61.291813547284683,9.6257027391250904],[-61.013462233446596,9.5562539477465407],[-60.792726070227395,9.360658834014437],[-60.736222359980047,9.2033915364386072],[-60.825052274265566,9.1207555514175791],[-60.938145975404275,8.8335569533620824],[-60.891120904596733,8.6507467010903554],[-60.790982190293313,8.5917096945909037],[-60.547644298731214,8.557630177039206],[-60.340214099766641,8.6285695624376277],[-60.167529097140232,8.6167984994942959],[-60.01768440143239,8.5491211206800113],[-59.82915002130467,8.2792104351392446],[-59.971841713508987,8.1728325510716999],[-60.032620144453318,8.0537586754225998],[-60.567488526580192,7.7372314663399937],[-60.666810863361377,7.5491343067518617],[-60.663286313888882,7.4340654109154158],[-60.54573991635413,7.2166582310406246],[-60.325726337135428,7.1338594534936082],[-60.352346861335334,7.0030105131996176],[-60.717980272281054,6.768555476169265],[-60.861581978695568,6.7743107793201105],[-61.109064091411014,6.6719247911889568],[-61.168934064862661,6.5309953015267119],[-61.128964062194179,6.2143739320271987],[-61.326642225500279,5.9901683899400018],[-61.295346449977721,5.8180389382862989],[-60.672139020837918,5.1642160022116865],[-60.60477717797302,4.9945296943438242],[-60.630393237320959,4.8889694819388323],[-60.90060481559982,4.6813622658824006],[-61.002967941091079,4.5354384687052987],[-61.298868301454952,4.4866123750344151],[-61.479434805320494,4.3847711361698707],[-61.554391445477364,4.2879777791983624],[-62.153143929389906,4.0986415714436824],[-62.387349399400676,4.1476200568017703],[-62.498106846621148,4.1179500036850918],[-62.714492695617444,3.9621812018729385],[-62.764816650763244,3.6730611416840726],[-62.85707697596451,3.5937136048888938],[-62.968543333768629,3.5941758784822002],[-63.321056210981929,3.9182772676637754],[-64.021445365347603,3.9293738430208411],[-64.204161932419836,4.124867532965296],[-64.427470281204108,4.0945394278073142],[-64.49179121192131,3.9975940007781112],[-64.491273071281555,3.881252565852805],[-64.393166515742394,3.7582057320220108],[-64.282358755248552,3.7143222759382888],[-64.22133455570301,3.5873328290278912],[-64.214494540702745,3.1957715711639958],[-64.03797177691257,2.8014486178168405],[-63.984627845840592,2.543512211632009],[-63.847721653820493,2.4467180665535979],[-63.389447616293438,2.4117070681783104],[-63.394140254468226,2.2225790819377167],[-63.464008005408871,2.1362865853136026],[-63.970246437090779,1.9412710647410771],[-64.043238675484517,1.8557496626512653],[-64.115045382376763,1.6194098646916835],[-64.210666012152871,1.5252736708961583],[-64.304278475509719,1.4554822583994531],[-64.505707755189235,1.4305357305934976],[-64.731611027927357,1.2535763192343707],[-65.034397701792628,1.153337782356064],[-65.363156499654053,0.8582189133212087],[-65.473508012809077,0.69147256045622885],[-65.555841931857756,0.68823577342974429],[-65.603357804572994,0.85311337300826473],[-65.737773939801812,0.92962910660885301],[-66.060123909088546,0.78559100333325893],[-66.347034823814596,0.76736002598937814],[-66.872583347150893,1.2199540717340294],[-67.243300847183505,2.4029899112689281],[-67.640575009292661,2.7827873252222575],[-67.755493758320753,2.8194938656586186],[-67.858873282506323,2.793932401899681],[-67.834553725122774,2.8926361481481084],[-67.412251300095562,3.2746459686434122],[-67.356022475404032,3.3957399365302412],[-67.388523932451491,3.525235438670506],[-67.661467581275915,3.8643284210031519],[-67.855048554277289,4.5069629366947286],[-67.82466547128702,5.2704369783058533],[-67.658091412929409,5.525186022413739],[-67.575007664952423,5.8329723751793159],[-67.464882191768737,5.9690732186822313],[-67.508874708738077,6.188398158044798],[-67.609965187983889,6.2531653596693948],[-67.818937779160962,6.2874582605037235],[-68.465577691204345,6.1573156230110877],[-68.947112393931334,6.1963359287819779],[-69.268126151167976,6.099934750636363],[-69.427084374068698,6.1242249248485816],[-70.137386929117184,6.940525094877124],[-70.387431750887345,6.9728304565906321],[-70.711503973123968,7.0877651301560718],[-71.126127363913909,6.9868850391359381],[-71.602730547550493,7.0321843073372863],[-71.892658207341967,6.9905415372901532],[-72.006482856901641,7.0328906132355611],[-72.212440059313991,7.3437324470595042],[-72.394526257069245,7.4153017769879694],[-72.469930572440404,7.5193823910991027],[-72.445803012574302,7.9660285334511443],[-72.367585676263161,8.1259451489660091],[-72.388857879687862,8.2794972692709852],[-72.440540044523615,8.4041144529337153],[-72.665185476188498,8.6277306180173507],[-72.784950014947896,9.0343780122501922],[-73.060450562645542,9.2021041110712609],[-73.365962022155429,9.1942110208401981]],[[-71.993509025084307,9.6414005006434191],[-71.943990231993723,9.5139500177981855],[-71.760988885046046,9.3356805915374039],[-71.740648408225312,9.1464178225181421],[-71.620925025899524,9.0552007124749228],[-71.287915488178072,9.1334551919577596],[-71.08843098320844,9.3824920436458594],[-71.0558450031359,9.6878970296651019],[-71.085110171893049,9.8317186823435971],[-71.265555851560549,10.141298560905152],[-71.527478982649967,10.426467511889662],[-71.715865392984725,10.391671835775663],[-71.974043850676367,10.073935307278896],[-72.078954783613099,9.8273880419407522],[-71.993509025084307,9.6414005006434191]]],[[[-65.414318440883491,10.937938010350534],[-65.302415660301961,10.973558958751017],[-65.212846164919398,10.906560402811794],[-65.266429730130682,10.884257821487907],[-65.414318440883491,10.937938010350534]]]],"type":"MultiPolygon"},"properties":{"alpha2":"VE","radius":862000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-64.694701673366552,18.411694036334904],[-64.691140955258788,18.414449765654236],[-64.654830775165394,18.439834758816882],[-64.65090333813977,18.442314130647912],[-64.646278806756129,18.442745124555351],[-64.572158214046269,18.446150426249094],[-64.55273934772103,18.44799482057477],[-64.533592513112893,18.451722136536006],[-64.514899841690536,18.457296918583932],[-64.496839144761637,18.464666137418266],[-64.479582222068757,18.473759694420792],[-64.463293227569423,18.484491088458228],[-64.448127107945524,18.496758238711635],[-64.434228128695651,18.510444455705301],[-64.421728501830586,18.525419551298331],[-64.410747128225879,18.541541077080339],[-64.401388466594597,18.558655679391155],[-64.393741539839112,18.576600558075121],[-64.387879088233674,18.595205015093804],[-64.38385687749323,18.614292078266161],[-64.381713168310199,18.633680184690583],[-64.381468352405179,18.65318490783547],[-64.383124758553734,18.672620711869705],[-64.386666630434206,18.691802716545141],[-64.392060276507465,18.710548455842979],[-64.399254390502747,18.728679613655125],[-64.408180539461057,18.746023719990138],[-64.411035724257275,18.750963508022771],[-64.406593584133716,18.75125468213686],[-64.32745106147749,18.752610911104476],[-64.323125626711828,18.752496614183404],[-64.318976568879265,18.751268701808538],[-64.292279734636836,18.742085879489391],[-64.288047947057336,18.740406942234166],[-64.286065257502543,18.736308674483507],[-64.275496335383124,18.71159976099603],[-64.273854251679097,18.707177868764916],[-64.27873883340078,18.701540878080859],[-64.282634722726129,18.697446835552181],[-64.295037901256549,18.681443150234067],[-64.305759432895428,18.664267424900462],[-64.314689433527676,18.646095692178271],[-64.321736380295391,18.627114192693988],[-64.326828049608153,18.607517466308487],[-64.329912257356895,18.587506358293652],[-64.330957393744555,18.567285960885343],[-64.329952747252392,18.547063511309574],[-64.326908614421484,18.527046267824854],[-64.324965372429446,18.51753613854401],[-64.328570307741984,18.514548420791346],[-64.391482273891768,18.467397099448675],[-64.395311672726535,18.46479671794194],[-64.399710311288345,18.463355101412418],[-64.416777294300473,18.458630343403396],[-64.421160957984625,18.45762802020705],[-64.425652799962918,18.457839005343398],[-64.432545648642886,18.458476494238685],[-64.4531354195463,18.45931461713473],[-64.473702140235687,18.458029659575569],[-64.494027473830471,18.4546352627065],[-64.51389564602222,18.449167461539375],[-64.533095735740702,18.44168430240391],[-64.551423914297843,18.432265226726241],[-64.568685609238798,18.421010227676518],[-64.589731414589238,18.405686846741617],[-64.593732739439091,18.40306047958358],[-64.598497007534846,18.402602053456313],[-64.667316932478485,18.399335148836915],[-64.671779876163853,18.399324887553863],[-64.675800362496034,18.401262324336184],[-64.690846075276269,18.409368714660154],[-64.694701673366552,18.411694036334904]]],"type":"Polygon"},"properties":{"alpha2":"VG","radius":31000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-64.888871390899908,17.701939386305273],[-64.884479250596584,17.772055221680375],[-64.770318173784631,17.793466189836753],[-64.765660660231006,17.794101274221738],[-64.725594236241477,17.773247469878957],[-64.708024106658797,17.765053451009262],[-64.689743174908699,17.758599077829963],[-64.670923213835422,17.753944997470754],[-64.651741061156343,17.751134940996955],[-64.632376957844372,17.750195312499983],[-64.581435512450923,17.750001943537487],[-64.686324714896401,17.706318454690578],[-64.888871390899908,17.701939386305273]]],[[[-65.022983649482043,18.367510629160865],[-64.942039898226611,18.38492951480746],[-64.893855270340239,18.375203424352815],[-64.866817384151958,18.366945113765112],[-64.846755515360186,18.362951453434029],[-64.826390649787911,18.361027875392548],[-64.805935818134671,18.361194501608782],[-64.785604992207951,18.363449589056284],[-64.76561084662886,18.367769547947301],[-64.752424854752036,18.371714044557113],[-64.660558225879583,18.354273910210072],[-64.72600895066185,18.328096952542815],[-64.765305625002796,18.331151109648797],[-64.776247564436531,18.332649893856434],[-64.796267171696272,18.334373289414732],[-64.816358669239989,18.334079191581299],[-64.836319256119893,18.331770568941195],[-64.849854559449128,18.329511151009214],[-64.919926184736369,18.321580426890719],[-64.924872508441965,18.323477904440846],[-65.022983649482043,18.367510629160865]]]],"type":"MultiPolygon"},"properties":{"alpha2":"VI","radius":20000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[102.12774427909481,22.379136011305199],[102.4709234892733,22.750662054676432],[102.97469908194427,22.517765375867732],[103.10034389299835,22.55685175736172],[103.32192034985799,22.768799026012463],[103.48742695408738,22.717789179482388],[103.62023795404562,22.781730044378637],[103.86665604962332,22.634957546253371],[103.97610796895034,22.66430419695746],[104.14312917087268,22.799952350139389],[104.42169769350124,22.746045996963801],[104.72115982340817,22.850929409898839],[104.86148677914765,23.133288523303598],[105.27540517843364,23.344921123417915],[105.58119072102367,23.072859839358649],[105.8126763729426,22.954038348680943],[106.1483673749953,22.969772976901364],[106.33941785436826,22.872815982463663],[106.54177150618855,22.908134961612884],[106.77994220776861,22.778830634628601],[106.56929265911332,22.447738311035824],[106.71241827496542,22.034492599811934],[106.97090458238401,21.923685228691575],[107.17103747668862,21.727073743865887],[107.52543043525031,21.607437575886888],[107.79770104250026,21.646169037574154],[107.97215295671086,21.508085919197576],[107.80458897538958,21.480580665259062],[107.63968746477777,21.346557709665284],[107.52109112558222,20.926821950420418],[107.39931318611511,20.903698623303288],[107.21799989899425,20.955897401540749],[107.11576797020474,20.899016854825341],[107.03121534514568,20.747262668193549],[106.87286188314116,20.776919339319445],[106.76461495798377,20.741272831212687],[106.58810351378951,20.553773993438803],[106.51778887440547,20.289028755990746],[106.16562009922021,19.992285359745708],[105.98715918088394,19.925830263618781],[105.82483057281068,19.607193768260867],[105.79087244676566,19.294232535260395],[105.64977098322792,18.94833806169067],[105.88878859507408,18.502900956863893],[106.49874081184289,17.946409256175052],[106.48810376093954,17.709593632193776],[106.73076301905265,17.375371090913092],[107.11971651196527,17.055372651594293],[107.20918035682908,16.884978664604986],[107.83038100561168,16.376774210284289],[108.02923878007536,16.330860367027974],[108.26716895631895,16.0896344447407],[108.57018601239058,15.595154901273064],[108.82111995142617,15.377789048369063],[109.08460902568864,14.716078252755382],[109.09498681542919,14.525875188156228],[109.30311049876565,13.8564322407862],[109.25170406662944,13.666425800865337],[109.27822997131638,13.309565099595421],[109.42366777569609,12.955880098771207],[109.44465998195223,12.599759965741423],[109.33952739371551,12.522855543343509],[109.22612524839217,12.296639546934818],[109.257793943164,11.94920817164342],[109.17488965268076,11.668646150745266],[109.03968099124978,11.519242410270422],[108.98651965825401,11.336599335238086],[108.81726699676902,11.301592382557835],[108.27159097309992,10.9344950749554],[108.13093555913834,10.899428469512323],[108.00122409803396,10.720567523607937],[107.82971305225053,10.691314407047638],[107.26148405092262,10.398616373003842],[106.8634287993828,10.322249388504314],[106.78500878944017,10.116573700618302],[106.57844552419573,9.8071846819021982],[106.53903603691742,9.6036949266269893],[106.28228188207476,9.5359239699804039],[106.16817520351016,9.3969156500474789],[105.51272569370374,9.0942951718701401],[105.3220614556443,8.8012996185296259],[105.10933629681523,8.6281625548363614],[104.77084198118791,8.597826530132334],[104.82847184282643,8.7320083524430121],[104.84485813358478,9.6012433052936714],[104.95342741432306,9.9158733403917747],[104.93987622570883,10.032751712054928],[104.80967839750907,10.161544760795305],[104.6595709681893,10.172824783079085],[104.42686206366808,10.411129256764976],[104.56435562585501,10.515755500424127],[104.87086885700529,10.562107361795034],[105.01242486737934,10.705144949494068],[105.04580095665112,10.911103405683171],[105.28479738971208,10.885048634548529],[105.4058174728022,10.951408102218124],[105.80642362708302,11.035592859252468],[105.88342956240054,11.190156577299222],[105.83871192671556,11.601265493827524],[106.00617012644341,11.757774355623569],[106.28797254741292,11.739321357054559],[106.38869007580408,11.826150147811594],[106.41416168087666,11.948260565565091],[106.64643186696141,11.973802114531566],[106.91773761775157,12.080169733696199],[107.16317456202471,12.279176684960996],[107.427930677612,12.333949158743556],[107.51854797126333,12.411225365927601],[107.55380707111954,12.563131155833554],[107.47565143900462,13.030334086734081],[107.59978518239824,13.479167414257775],[107.33163688433027,14.126614432066695],[107.36471039782634,14.368582946053518],[107.52114850828123,14.625892287567508],[107.48065606068478,14.979882448582416],[107.60768478390597,15.149485363463192],[107.62309711244495,15.281930661490764],[107.18805164171324,15.750768779578772],[107.16617675949696,15.802469291975488],[107.26990079669778,15.981794174511027],[107.23559901342684,16.113179375781783],[106.86020520141923,16.421622132171187],[106.65662766504742,16.492760353025822],[106.54641679706062,16.650831231455847],[106.49179338489101,16.945572009351697],[106.2688005376387,17.216011458879343],[105.69154732123637,17.737953842626034],[105.48038100011165,18.124632209520755],[105.16344035922846,18.338861425908849],[105.0860841283972,18.450165828827405],[105.045622619204,18.678474366894811],[104.7112294821184,18.806342105496572],[103.89183856181408,19.305040616676084],[104.02770226441146,19.47373835351619],[104.03217006140316,19.674920642866514],[104.56505058007758,19.629730908495631],[104.79744816564178,19.838002219382961],[104.84681673628695,19.998005415136138],[104.81024160189423,20.128332898157286],[104.67718543422583,20.22484521194157],[104.58622662350136,20.37666534875682],[104.36813368776569,20.441449275268749],[104.4660164871645,20.593072620087032],[104.43342420756612,20.744424231460119],[104.11689493334943,20.936470731197279],[103.88902496404786,20.864707725177073],[103.63508916560389,20.697341894946863],[103.10466052620353,20.891849998428214],[102.85141090136557,21.265940628382172],[102.88841197739259,21.471584519277734],[102.85041619140387,21.579887249973382],[102.66220192002655,21.676245066143387],[102.58449234877237,21.885246326897782],[102.12774427909481,22.379136011305199]]],[[[104.09341806743761,10.349247401662815],[104.04808261590399,10.061104405856531],[104.0185586392091,10.02964019986902],[103.95771399701316,10.220946444372235],[103.84988445377742,10.370878757940346],[104.02764726787737,10.428154018030003],[104.09341806743761,10.349247401662815]]],[[[106.56831772848028,8.7009034418833746],[106.57191515002357,8.7037769671505991],[106.65825883224427,8.765841708323169],[106.65228010001893,8.7012737722813895],[106.56831772848028,8.7009034418833746]]]],"type":"MultiPolygon"},"properties":{"alpha2":"VN","radius":906000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[166.5268353602238,-14.759889961449485],[166.56810715172966,-14.642689241159928],[166.73647628526464,-14.62424295434452],[166.88916776348975,-14.580524001796588],[167.03021320219349,-14.50750196022469],[167.15405319968917,-14.408055042953611],[167.25580651843185,-14.286103017717004],[167.33146248230861,-14.146452706352015],[167.37803906015947,-13.994608520654445],[167.39282780319729,-13.78853872296326],[167.40770321815427,-13.748814004529313],[167.48087525218938,-13.710608332362446],[167.49479362870116,-13.723414236701887],[167.55056420076764,-13.79645374396647],[167.5565330403517,-14.016372075015237],[167.59801974167871,-14.183927660242576],[167.60685484739125,-14.357328305017747],[167.67768909628896,-14.581474793571314],[167.76001394182481,-14.715196251324947],[167.86687416021016,-14.830260156103545],[167.99415249260628,-14.922233166050601],[168.13566466520473,-14.987085018677515],[168.14089022643572,-15.005034832265565],[168.18572938033017,-15.197025361426256],[168.18722198168982,-15.500726811265064],[168.26678421674711,-15.892303105300975],[168.26007522703694,-16.139020611635413],[168.31313593806925,-16.450308410319579],[168.38670095024514,-16.658101514293421],[168.47546333400172,-16.793838208207312],[168.42829566060283,-16.912907451860171],[168.39268717237712,-17.068121429364165],[168.39834127734497,-17.306428998558665],[168.47396000194709,-17.532491545053947],[168.58394020883284,-17.696285599193505],[168.5668464285971,-17.874464323689907],[168.41476597227688,-17.813832522403391],[168.25207839899355,-17.779840574680133],[168.15940522173042,-17.710207695327732],[168.21746069253618,-17.538552780604036],[168.23303738925901,-17.302010910850939],[168.17861224447719,-17.071289014962044],[168.05896401462658,-16.866645445014932],[167.94794054152902,-16.75372156690722],[167.81684868250929,-16.664879356644967],[167.67082519296255,-16.603600042630124],[167.45007959167555,-16.554158278121886],[167.4136790914655,-16.445682569172483],[167.34037495273026,-16.302118736170943],[167.15240669727237,-16.079929089939494],[167.11083548918256,-15.983023241732985],[167.02183470532057,-15.844512240420906],[166.90636437877382,-15.727153641674526],[166.75900385427803,-15.630493916148421],[166.63190116970674,-15.405852712271669],[166.63101195372383,-15.213519299019465],[166.52829818418002,-14.849919738891058],[166.5268353602238,-14.759889961449485]]],[[[168.99718003267174,-18.57314460361944],[169.14329082757169,-18.631900048840269],[169.25494577352308,-18.76390866313141],[169.3334350617485,-18.940371952289212],[169.32688004100612,-19.102945957379454],[169.3531234545971,-19.262635639189675],[169.41096782463507,-19.413776434769392],[169.4903802848593,-19.540452636259559],[169.52055396678017,-19.704099974828278],[169.55560434617308,-19.798158876295879],[169.4617092994504,-19.70329521460415],[169.27431285192873,-19.556425282623749],[169.2184693286485,-19.476088137000779],[169.21329013793178,-19.33577394129858],[169.17842644209668,-19.181671830282589],[169.11395689811746,-19.03742684490707],[168.98794335288071,-18.870952007802082],[168.99718003267174,-18.57314460361944]]],[[[169.67831926108559,-19.995395287265648],[169.89500878305222,-20.186802714461336],[169.86058585755885,-20.240784713966775],[169.80729320597354,-20.240135303180615],[169.73843062115876,-20.201509306235092],[169.67831926108559,-19.995395287265648]]]],"type":"MultiPolygon"},"properties":{"alpha2":"VU","radius":265000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[-176.19507523947092,-13.301652835874211],[-176.19340193497609,-13.296870528552596],[-176.17289602265083,-13.246744965090929],[-176.17102303353104,-13.242696653531681],[-176.1678436752855,-13.239567980351296],[-176.15166633922743,-13.225021972299109],[-176.14803575506241,-13.222071781302599],[-176.14598418052671,-13.22627604226299],[-176.12997668387652,-13.263718638850236],[-176.12832827558401,-13.268156237515463],[-176.13024872067871,-13.272483063425064],[-176.15852988326048,-13.328705674923949],[-176.16075055297648,-13.332656903201052],[-176.16471242149112,-13.334858533678958],[-176.17244670409943,-13.338702518448354],[-176.17679337881003,-13.340599382230478],[-176.17902220428439,-13.336413213307756],[-176.19315954748802,-13.306343308715942],[-176.19507523947092,-13.301652835874211]]],[[[-178.19411020853957,-14.255473759434365],[-178.19163298496966,-14.25146018594091],[-178.18078318716249,-14.23565510734417],[-178.17793359423544,-14.231908792506855],[-178.17337056565918,-14.233063705889945],[-178.1465766482257,-14.241251751599499],[-178.14235013827164,-14.242756138231201],[-178.12935448422905,-14.255766063401746],[-178.11565569417891,-14.267446518755575],[-178.10096207819166,-14.277847842485274],[-178.08539268757559,-14.286885760495267],[-178.06907366938151,-14.294487045340093],[-178.04391779244804,-14.303380433970903],[-178.04461176959026,-14.308019785451426],[-178.0457303051374,-14.313612463187001],[-178.04686945344136,-14.318153293264922],[-178.05148862721848,-14.318914890450225],[-178.09902566497297,-14.324406237914969],[-178.10330941163681,-14.324714263690094],[-178.10752888873907,-14.323913402690268],[-178.15387813906852,-14.313022147754211],[-178.15844962296748,-14.311711426510554],[-178.16118775871769,-14.307823101957517],[-178.19178104441158,-14.259575027929921],[-178.19411020853957,-14.255473759434365]]]],"type":"MultiPolygon"},"properties":{"alpha2":"WF","radius":9000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[-172.77697740418796,-13.517769690627803],[-172.69022296806841,-13.515933686058881],[-172.61911249610694,-13.507465066503052],[-172.51078479427616,-13.483397249335312],[-172.33381043337394,-13.46624804970439],[-172.2376763191177,-13.545968698850732],[-172.16133465071161,-13.625219853612281],[-172.10263232608872,-13.675971714816935],[-172.0392890459128,-13.720797412674585],[-171.9719008025871,-13.759275185392692],[-171.90110164728449,-13.791042998353387],[-171.82755772420523,-13.815801950471364],[-171.6043062108894,-13.879997819935916],[-171.55759797405221,-13.91629133420175],[-171.46215451476894,-13.978216382429816],[-171.45036315656429,-14.022489414981992],[-171.45487764953879,-14.045550557109236],[-171.72803534933189,-14.046101695362392],[-171.83046181774756,-14.016166471134893],[-171.91154426892729,-14.0007723765788],[-172.00227600991741,-13.932830714948722],[-172.06943293472466,-13.892764333565257],[-172.14018085740497,-13.859446186810832],[-172.21384376304479,-13.833194638295934],[-172.28971778330558,-13.814260528435781],[-172.36707792207002,-13.802824777604515],[-172.48445545235165,-13.798993632919412],[-172.53516073313367,-13.790766570711432],[-172.66197757078569,-13.64813693828023],[-172.74330891965636,-13.578038785404436],[-172.77697740418796,-13.517769690627803]]],"type":"Polygon"},"properties":{"alpha2":"WS","radius":81000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[20.029800251666252,42.732022105951629],[20.054394998721857,42.759839588399544],[20.152262195610835,42.767723940343117],[20.396502235140712,42.854754250847769],[20.475242921360078,42.952877415567777],[20.578250366926373,43.03480569927796],[20.623277705559801,43.198462400345399],[20.800501809894623,43.260859275801415],[20.888633741044789,43.156947210494657],[21.056893128157522,43.091403235310793],[21.121723571982564,43.04672136308988],[21.276464719418897,42.899563862065321],[21.402856229850379,42.831385301204648],[21.485584916361301,42.735370929904938],[21.752713359058806,42.664754020909456],[21.730420301755224,42.595582971313725],[21.576497875580326,42.332673072309127],[21.562261278357227,42.247708367174887],[21.384792711061376,42.209871541015595],[21.321798329289411,42.160507613052019],[21.286453639031627,42.100623499474914],[21.256402822155977,42.099727487607474],[21.152558065447636,42.157344802436278],[21.076319927668667,42.168278305234118],[20.860053845611393,42.099190460397033],[20.784999109507751,42.037948972579443],[20.743895566210583,41.904374205578151],[20.694862836344779,41.854052844102917],[20.566486043152491,41.873780515078167],[20.575209948589311,42.004325009322471],[20.507816851782863,42.191464221615021],[20.369247536144762,42.296954341678195],[20.240700711326539,42.339197327473613],[20.126621986777632,42.496900382533461],[20.06422570245406,42.547370464970271],[20.070557329206238,42.639991879117858],[20.029800251666252,42.732022105951629]]],"type":"Polygon"},"properties":{"alpha2":"XK","radius":83000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[31.486205305330831,80.107485192155892],[33.115939087464099,80.229537161099373],[33.624984002281565,80.217089548010392],[32.518626964567062,80.118848487753354],[31.486205305330831,80.107485192155892]]],[[[10.558635284834107,78.837867880691178],[10.72093505078646,79.239165744463961],[10.735740562886257,79.484935277088965],[10.683189399528096,79.757697269539165],[10.764019699305303,79.791765118199123],[11.095525963155666,79.767652483008291],[11.702287038324787,79.8198300471241],[12.010732855079445,79.772357223296623],[12.28006811199525,79.814852105565677],[12.697445273513306,79.774838891151489],[13.692821557153984,79.860311440418826],[14.218887936315546,79.734639049465102],[14.593736312967275,79.797863091640622],[14.814871825758175,79.768708472626741],[15.197121963559503,79.623565979704878],[15.43137270872341,79.601490747897444],[15.80363321292684,79.716487345483017],[16.094436972812787,80.006492298888446],[16.245803572624713,80.048781808963525],[16.764434701068176,79.955299551873836],[17.474647749429206,79.933802592589302],[17.762502456093113,80.029802789877749],[18.291866687974466,80.357044243745094],[18.983230600334501,80.287473830861984],[19.733331087098055,80.476984802281763],[20.385171685421771,80.394440722114751],[20.947139410840283,80.244496011961644],[21.549066986615699,80.241546206614373],[21.830245924758348,80.182678519594958],[22.137681363160997,80.216468055117858],[22.451217840023471,80.401324124710058],[23.007987325442905,80.473362430386416],[23.251126496330116,80.445589494640984],[23.692813019572387,80.287023901483437],[24.317735218478077,80.359397184841612],[25.835587849378879,80.177986881197086],[26.860736293909838,80.159198129327905],[27.147565587007925,80.058562158119855],[27.197660159294291,79.906565802134239],[27.182701929657107,79.548437603891898],[27.259655695221991,79.324304965651322],[27.398893421541448,79.132549451705358],[27.6596540897423,78.955723001118287],[27.968468353443097,78.89329327868812],[28.825616890225668,78.970643375429717],[29.082794177785477,78.914362743586921],[29.693052510873272,78.905065753078787],[29.31051144352497,78.853105533869922],[28.552962529609907,78.88612961690005],[28.037903544631021,78.829608369539216],[27.484397875586289,78.918658523885085],[27.25269619542896,78.859907909534002],[26.897052403031665,78.656704142277036],[26.729604058366206,78.64737412486636],[26.460181179116145,78.721198970632742],[26.226406371256569,79.128616394215456],[25.904161185581678,79.34561772817068],[25.595792166284895,79.392594911005602],[24.888487795132345,79.329243979088858],[24.603597800134196,79.211839240904794],[24.430920692577853,79.05729561790065],[24.282756207579762,78.787122585968589],[24.245269625732412,78.558439680908606],[24.274899585347772,78.328606740757579],[24.413721035237572,78.053516234108201],[24.646217452444702,77.851299524155834],[24.897503847744755,77.756819771764583],[24.20225585575983,77.66314586121986],[23.487447803595067,77.39840796914153],[22.574076540266248,77.267400069309517],[22.042870323516347,77.48683970498162],[20.928687470339106,77.460453022679616],[20.87414503400581,77.565516000413467],[20.832218223668718,77.935462360481495],[20.603108528280696,78.269196835204369],[20.115540181240132,78.532285315369876],[19.719653238720209,78.580035782347309],[19.242709040456912,78.391484530174893],[18.994465320795701,78.082318107144829],[18.718210563400316,78.001434378515484],[18.517610737317202,77.871087213609329],[18.227516735983791,77.523533107175467],[17.860690813161817,77.479856903563231],[17.63083734135655,77.382443115617292],[17.357252029870306,77.139927004176869],[17.061892153494281,76.659702576532965],[16.70049634833968,76.580728874160485],[16.44554727426144,76.6143229289234],[15.563320616825305,76.881883175649861],[15.122375094329112,77.079517375124112],[14.230053375190344,77.291282781284764],[14.005084857826137,77.445737867240069],[13.87829876629486,77.739824282622337],[13.667088455886027,77.978114255706828],[13.33040103717374,78.199381485861693],[12.571741152458959,78.376698915014174],[11.965260182536586,78.225910765284226],[11.546243844718083,78.397741860408161],[11.121698898054415,78.464187639933854],[10.628952190801503,78.754585756197528],[10.558635284834107,78.837867880691178]]],[[[18.799341946900348,74.485562800138752],[19.182708582577533,74.517036111925805],[19.273637490751344,74.456826484490776],[19.098535923066116,74.353267190701402],[18.799341946900348,74.485562800138752]]],[[[-9.0967751959472682,70.854914700118272],[-8.561633307855061,71.024254187509115],[-8.3434400278468548,71.13937790119104],[-8.00203322213493,71.176606754234598],[-7.9797165717845226,71.116868825152565],[-8.0212594546554286,71.03742405745453],[-8.9538307700128748,70.840550909558672],[-9.0967751959472682,70.854914700118272]]]],"type":"MultiPolygon"},"properties":{"alpha2":"XR","radius":270000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[34.872809220217562,31.396871635813682],[34.928599323079808,31.535087575735361],[34.951020861085631,31.602265017369124],[35.034712838855768,31.673175117699973],[35.133492725587928,31.724198702744552],[35.137714279212879,31.726643183410854],[35.141677530575144,31.729487412546455],[35.145344754276479,31.732704316502218],[35.148681042746851,31.736263274211826],[35.151654638522565,31.740130408667582],[35.154237236539295,31.744268909388893],[35.156404253562265,31.74863938281387],[35.15813506218921,31.753200227278789],[35.159413187198446,31.757908029016015],[35.160226462373281,31.762717975400985],[35.160567146309717,31.767584281514633],[35.160431996105416,31.772460625960889],[35.159822298228178,31.777300591790809],[35.158743856270341,31.782058108336216],[35.157206935705517,31.786687889747224],[35.155226166173648,31.791145866059171],[35.152820402224329,31.795389602685827],[35.150012543844149,31.799378704345742],[35.146829318476279,31.803075199576856],[35.143301026607297,31.806443902179279],[35.139461253342887,31.809452746145684],[35.135346548717919,31.812073090891239],[35.132365523620059,31.813773014294146],[35.127008907854062,31.816416841115679],[35.121375224339253,31.818402983804834],[35.067347336716452,31.833890502449673],[35.062712466278363,31.834983230835562],[35.057994663884656,31.835630084665365],[35.05323672203982,31.835825196695588],[35.048481797330489,31.835566797175883],[35.04377301897658,31.834857229901445],[35.039153097630646,31.833702930953798],[34.983007756564248,31.816857681024288],[34.961177536791695,31.823389149164715],[34.95387516906387,31.841246802468813],[34.968782772839511,31.856621722260641],[34.972296154437167,31.860633506363509],[34.975362284849133,31.864996659608082],[34.977946285907485,31.869661549796412],[34.980018763812659,31.874575112380867],[34.981556143496832,31.879681454089514],[34.987482120075917,31.903993152875536],[34.988382382607263,31.908627785473204],[34.988841493658185,31.91332666927509],[34.988855359740946,31.9180479084845],[34.988423857223928,31.922749407981311],[34.978749320194616,31.992364319568182],[34.971635905742289,32.083902731862551],[34.970728743049193,32.090257663796329],[34.956011596747572,32.160933624052923],[34.998170613890949,32.277356124772112],[34.999362749759165,32.281095809182204],[35.000257865677661,32.284917483541406],[35.010276008517778,32.33678132036848],[35.011128272814027,32.339488092071029],[35.065091754138699,32.460401741447697],[35.193273002399742,32.534356340061976],[35.301391215864051,32.51340926154807],[35.306260438151874,32.512709464370388],[35.362086097107287,32.507398600213946],[35.386671573392668,32.492977880438133],[35.39649989576732,32.466974527431674],[35.398399599417516,32.46257066277164],[35.400712507662853,32.458369073387708],[35.403417339066429,32.45440841877253],[35.406489206044007,32.450725141539607],[35.409899843858604,32.447353132108994],[35.413617870688839,32.444323416876145],[35.417609076378092,32.441663872733145],[35.474595501642504,32.40751967647256],[35.47853964839193,32.405390675850661],[35.482658603135135,32.403623432502236],[35.486919267702788,32.402232147243559],[35.491287405205682,32.401227999851528],[35.495727915147768,32.400619059227715],[35.551415160607576,32.395456176358756],[35.571908301449014,32.239129894366435],[35.572007315824777,32.2378950378558],[35.571737112158985,32.236686044709515],[35.536384849396917,32.108881136661374],[35.535129090559423,32.102972789174203],[35.534594958351661,32.096956128715796],[35.531552989004588,31.988742542677613],[35.53159176404759,31.984919201117791],[35.531922585769664,31.981110001572581],[35.558921753503569,31.765541479395942],[35.503062619046624,31.678069384439279],[35.50081778515522,31.674184974648291],[35.498930178562205,31.670114979565454],[35.497414996704322,31.665892167402706],[35.466336742855759,31.565289716367126],[35.464889032110712,31.55932833530531],[35.450537930676461,31.479359509106505],[35.421763335173424,31.481782740468727],[35.416636211704123,31.481960771958953],[35.411517833743027,31.481612494299181],[35.406462085710793,31.480741574029878],[35.401522192682975,31.479357179882722],[35.396750160055809,31.477473886255531],[35.27797177823669,31.423399852224701],[35.275288565565873,31.422360421185537],[35.101155168970763,31.366295783845253],[34.907829238924357,31.351387465883512],[34.880516072744946,31.368199338673275],[34.872809220217562,31.396871635813682]]],[[[34.198219556610319,31.322591316114803],[34.384686599108193,31.481558213305007],[34.389592626363779,31.486357410356831],[34.477347824481903,31.584779567154321],[34.524056335819346,31.541618459629937],[34.525520489482709,31.52565981688846],[34.365593740321323,31.37704513038333],[34.362123655148032,31.373485049540278],[34.359024263154055,31.369597963744127],[34.356326205755217,31.36542230176169],[34.354056156666502,31.360999345300929],[34.352236558198591,31.356372820888957],[34.350885399387209,31.351588467579951],[34.350016038148702,31.346693584766648],[34.349637069220115,31.341736564565949],[34.348285068700392,31.292946711455588],[34.245337310262208,31.208395817324366],[34.213134414629252,31.290661356901307],[34.211754030628754,31.293860826248451],[34.198219556610319,31.322591316114803]]]],"type":"MultiPolygon"},"properties":{"alpha2":"XW","radius":70000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[53.316190151649941,12.533196351087671],[53.535001516407242,12.715562847435281],[53.811647403921299,12.635636978870533],[54.187372515362178,12.663825173794494],[54.510563028761332,12.552749948992583],[54.129428595724953,12.3608344528693],[53.718858340116519,12.319231826216926],[53.598452930656777,12.342522486451671],[53.316190151649941,12.533196351087671]]],[[[42.549254884398543,15.320069613210404],[42.569933557257897,15.407190658037027],[42.70813658217925,15.543820963403158],[42.831650489597898,16.011157852143306],[42.799554438774678,16.371673983241486],[43.032785709146722,16.551140095389417],[43.131338072780743,16.704146690495076],[43.125728564533077,17.05692770257194],[43.191162575391914,17.359244032825551],[43.413310959282214,17.51430108868594],[43.543362940526791,17.496798847060656],[43.793933325098607,17.347720234393034],[44.348126808626958,17.41383319258177],[45.144061270267798,17.427483888470938],[45.539911403408155,17.301634844009225],[46.310347516102688,17.232214042347902],[46.727539739350455,17.265368942593845],[46.960472935464416,16.987479347650169],[47.133622533936212,16.950371470569536],[47.435377075223606,17.116950262155733],[47.579815534811395,17.448203748909709],[48.172340238009411,18.156713562284718],[49.042077125937617,18.581558589356291],[51.972727793505356,18.995481721979985],[52.698380153782473,17.369890051416686],[52.800393932968298,17.267727178083206],[53.085334110883302,16.648519958181161],[52.581858877698167,16.469478222402916],[52.334860893111873,16.298408126866629],[52.184397547968167,15.988086448717256],[52.217269789621,15.655650188799775],[51.597977505643755,15.334562849006474],[51.317490295657784,15.224886691423874],[50.538218750613694,15.039332696304276],[50.16681851641868,14.851319856495037],[49.887134855469455,14.822447179403495],[49.359683749479615,14.641957876889878],[49.087649189535078,14.487908717197316],[48.925538391283538,14.264417284763582],[48.668269047616981,14.050420865607419],[48.283367245332613,13.997923064369674],[47.976415265220197,14.033876477824226],[47.626342416605894,13.850975433562965],[47.407584192786274,13.661853677275394],[46.785075042323186,13.46458235812732],[45.68780156734735,13.344162926488035],[45.393423162397845,13.067241583308663],[45.153063806036222,12.981484786637354],[45.038535976214426,12.816056420026291],[44.75530397948279,12.764045640863277],[44.590060645654219,12.791349670961193],[44.358417944413631,12.66933523733865],[44.00583782561403,12.607919271634954],[43.655367467624416,12.7324065303922],[43.487803404642811,12.69913485564731],[43.46332928504782,12.857728786594295],[43.232155148971145,13.267132658915182],[43.278939132260327,13.701420066417414],[43.224012602255925,13.864189955271598],[43.113101734121585,13.984366048305111],[42.986310011166452,14.032684861188926],[42.857860149546376,13.988964365557967],[42.791839596166895,13.892462172237655],[42.79386459043895,13.766121813268372],[42.690240780869466,13.674087747626537],[42.763778164079653,13.860776487795228],[42.694410882748471,14.007912266934502],[42.762129438814412,14.067174352821244],[42.972706900820626,14.06401653583438],[43.069865047848538,14.244626677485995],[42.874394389414931,15.072186955760232],[42.549254884398543,15.320069613210404]]]],"type":"MultiPolygon"},"properties":{"alpha2":"YE","radius":771000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[45.04285867866021,-12.701306268016319],[45.092355364996969,-12.653381304474266],[45.110912595504637,-12.675693814431595],[45.126289665411946,-12.690437958170939],[45.143147789314277,-12.703462846348364],[45.222874246104688,-12.752257757589296],[45.214536007021856,-12.785110610580821],[45.210516082729043,-12.804309661217417],[45.208396210863484,-12.823810160696665],[45.208371542742263,-12.847915849269272],[45.194564760235878,-12.882669795423769],[45.188017526315726,-12.901534600365942],[45.183384101337246,-12.920958258890542],[45.180710674424127,-12.940747142791651],[45.18004426660719,-12.976559804888142],[45.117649281108598,-12.984728736202928],[45.087886773320463,-12.958374909676298],[45.06967789821703,-12.895607811858788],[45.078191290652008,-12.8645243137764],[45.081198326144239,-12.846047368450703],[45.082464638856493,-12.827370210660098],[45.081979134630352,-12.808656470877073],[45.079746066965562,-12.790070100068741],[45.07578499975515,-12.771773933323356],[45.070130635886272,-12.753928263253037],[45.06283251320842,-12.736689435671114],[45.053954570532582,-12.720208479847347],[45.04285867866021,-12.701306268016319]]],"type":"Polygon"},"properties":{"alpha2":"YT","radius":19000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[[37.590342038944605,-46.908049781968963],[37.684918483747886,-46.824245042039855],[37.887399843548287,-46.901677275472792],[37.813924730518934,-46.962648652227585],[37.611957611983549,-46.946263282439922],[37.590342038944605,-46.908049781968963]]],[[[16.447898022459153,-28.617540237160025],[16.73927624356066,-28.429382052519223],[16.875532995705864,-28.128083737022873],[17.05622322312119,-28.031323914688482],[17.358517942789746,-28.269547805543507],[17.380486244681123,-28.525591664662201],[17.474642125011098,-28.689436574789536],[18.121963997482631,-28.872144514421464],[18.604499679032671,-28.855512473007025],[19.118482276682471,-28.926886257168956],[19.54001507035905,-28.574796199786018],[19.932558758487655,-28.398148720080055],[19.98046875,-28.268269432592689],[19.980684824910995,-24.777148923431465],[20.345004827301334,-25.030096366761835],[20.427417504334592,-25.142617889873161],[20.79297747640264,-25.915672560594889],[20.814848270426037,-26.164871848383957],[20.62569606767795,-26.48204469386916],[20.676940544574144,-26.747235835168311],[20.77757490282259,-26.808026394949351],[21.08138824637134,-26.850208173504527],[21.655661251512235,-26.839836265556499],[21.833311509926268,-26.678571710520401],[22.059520633038574,-26.602027406836719],[22.217753245104735,-26.389041301316357],[22.596777374471774,-26.131954970767097],[22.878994497245447,-25.458024455987648],[23.054283017182982,-25.313383525522351],[23.266014152327973,-25.266835077515683],[23.38919804954384,-25.291606515130059],[23.91115783306353,-25.605842821271054],[24.192885867456379,-25.633155265573905],[24.35186378206382,-25.741311769150293],[24.727527820683569,-25.813707614222288],[25.457931419433212,-25.703619873669211],[25.602006506835828,-25.565567520680112],[25.909268369828379,-24.751264218616857],[26.418859482941052,-24.598802002690491],[26.796172028151595,-24.269400730971885],[26.987225606737361,-23.704697533538667],[27.14265040676224,-23.527700577699523],[27.529933034692082,-23.337339604449745],[27.879690800831622,-23.068276250491333],[28.210262685115485,-22.693791134568208],[28.377388720748318,-22.595901776044535],[28.829214401696508,-22.483673722649733],[29.129967425778872,-22.213558576080732],[29.663086540221457,-22.146572167167484],[30.211889035570422,-22.294127845714641],[30.468929577375505,-22.327144169598945],[30.923227048910018,-22.291495411892782],[31.287655113212725,-22.402196504112496],[31.530710547051616,-23.275339585806531],[31.538452038361623,-23.448530791859984],[31.799417669356977,-23.892296769660014],[31.967816902925406,-24.381782729266153],[31.984485976951216,-25.627494043162358],[31.925042832539603,-25.784857067129579],[31.947976092553809,-25.957487839973798],[31.871512461368464,-25.981371804719693],[31.389368171401983,-25.760238886881822],[31.228367119123227,-25.82890745182543],[31.106396272201597,-25.959579434661826],[30.788654487088248,-26.496780057315622],[30.807564283027219,-26.708306564411448],[31.037984915876535,-27.072481843671905],[31.298636809176688,-27.245571125246439],[31.774564453273502,-27.308415199877068],[31.936474086360978,-27.185670294479834],[31.994924843467707,-26.81767522249481],[32.353598361290686,-26.860559756356906],[32.885871375817672,-26.849542709384718],[32.848833243012457,-27.080096681504092],[32.659378152991842,-27.599358829995737],[32.53454902070645,-28.199628649653345],[32.285473301662407,-28.621234412528349],[32.024072724304347,-28.841503955650676],[31.750987072210748,-28.96547295968664],[31.327339235615362,-29.388187327027044],[30.471560247822755,-30.71553268128876],[29.971004620612472,-31.321862067640026],[29.488014203404358,-31.670573207538464],[28.8506327585519,-32.298564972227105],[28.45451339676103,-32.620465476896463],[27.366892028288607,-33.358473120531372],[27.071433500377875,-33.52360393025468],[26.434125196263324,-33.758258527410071],[25.997862219451029,-33.712930263974947],[25.817436922825486,-33.739403097019675],[25.68506501961236,-33.835575926161006],[25.637946688225259,-34.010953295879517],[25.57417555974915,-34.035053245814581],[25.051458959928162,-33.970738922895983],[24.827014257362865,-34.168691295058139],[24.600363685498838,-34.174395030402231],[23.690560335128687,-33.992278902078297],[23.268152208345736,-34.080902907532924],[22.564257750540339,-34.011104411800567],[22.238331250893367,-34.078627021290622],[21.788893832077886,-34.372421871771948],[21.34523537028636,-34.408150044838756],[21.003372808434538,-34.366935548340834],[20.530718180153819,-34.466103933951473],[20.020542955128647,-34.785483035087857],[19.635040332672357,-34.753044531118718],[19.415571770376985,-34.624226418456033],[19.298480665881826,-34.614780055798093],[19.251017678046026,-34.481013417053937],[19.103055510005614,-34.376573863661932],[18.831494285157358,-34.363834637408246],[18.693203848099632,-34.237042850496877],[18.558433228356598,-34.249387640284496],[18.461542943515433,-34.34647976249984],[18.408064563776804,-34.291403060710884],[18.33367718005892,-34.074217676072792],[18.43101525895343,-33.821688366102123],[18.423345808796196,-33.708469080409692],[18.267479056762905,-33.440990995863601],[17.992768305147017,-33.15220015206603],[17.851349380813211,-32.827486088960335],[17.965232646033183,-32.708873849064446],[18.066338429317508,-32.738555156314291],[18.176727466832109,-32.708090572727222],[18.317944110462694,-32.512395846500048],[18.312392203715124,-32.135139123316115],[18.209512222335615,-31.743004925138916],[17.680355985845782,-31.023107466623088],[17.34938041455187,-30.448839412384306],[16.942441470820881,-29.389272020123961],[16.732418027175104,-28.999370766890742],[16.447898022459153,-28.617540237160025]],[[27.226886947045717,-29.546408825565791],[27.124316429711495,-29.638752464197864],[27.105978590182076,-29.775543480947555],[27.355154686304722,-30.15866245546275],[27.389974367842015,-30.286653672205635],[27.73530925129813,-30.586732186970906],[27.968378548853877,-30.625436769235119],[28.088829245471885,-30.561326365777781],[28.392179357912134,-30.147755314449771],[28.727050565893805,-30.104649004766554],[29.04993158153043,-29.951178148859434],[29.142429750271777,-29.701112036201863],[29.305366193280395,-29.540203867127886],[29.383447763148421,-29.280567655145575],[29.279439656753642,-29.092739943861336],[28.666590580566126,-28.637556577164435],[28.53610192435832,-28.604314442687123],[28.246304487916756,-28.696381209672595],[27.959765999755017,-28.873141675525694],[27.749477751669854,-28.943428596231083],[27.356726732761356,-29.455182188136199],[27.226886947045717,-29.546408825565791]]]],"type":"MultiPolygon"},"properties":{"alpha2":"ZA","radius":1001000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[21.979136471572033,-13.0012066398681],[23.682530204170419,-13.0009765625],[23.822108992282089,-12.944216786980686],[23.994799058350111,-12.373960042575114],[23.971208694773324,-11.635858057092191],[24.0365620559683,-11.371299594220408],[23.96677185948527,-10.872054788634724],[24.078267468872156,-10.891720754078367],[24.160433796950606,-11.007134509050289],[24.319776247947861,-11.071988305316866],[24.421654302900013,-11.292140520487052],[24.536769342429515,-11.367438037157317],[25.288688904927309,-11.212631762792963],[25.319057564005256,-11.236999175197314],[25.288081588609828,-11.422923683582908],[25.375051717859609,-11.64195346565392],[25.513876001011287,-11.731941037676449],[26.041100889732295,-11.892947620066403],[26.607892367346206,-11.972407474363633],[26.843308682040316,-11.958938309870671],[26.954936460636933,-11.872661268390869],[27.046266805568397,-11.616084792112524],[27.159124561872229,-11.579483258490242],[27.246843258332085,-11.778213308957659],[27.423412881140205,-11.944697488604335],[27.582930795006217,-12.231112443004704],[27.86262620008857,-12.286913761296454],[28.412696786988771,-12.518259778525357],[28.53089976367011,-12.775575720530036],[28.727559542704398,-12.922659719594952],[29.014932360203854,-13.359976250203161],[29.190003208525944,-13.39353834487903],[29.705897630780321,-13.220861033254213],[29.795202565584542,-13.073222680010158],[29.794623165952288,-12.56559928276927],[29.750763873250342,-12.458997821890813],[29.636503606007008,-12.389398690496449],[29.42755148075932,-12.430970715477935],[29.064481619399007,-12.348583714332371],[28.485856849389453,-11.81589902386248],[28.357453006032955,-11.483013144908597],[28.643824532591697,-10.580370580267919],[28.607433390255387,-9.6978596945797904],[28.400914757119537,-9.2249302969698537],[28.797686757714178,-8.8852955226598169],[28.919745734970654,-8.6819989836481231],[28.898429298121833,-8.4855987668923287],[28.978129826731202,-8.4640471860823272],[30.751015929838474,-8.1939127802639753],[30.841234831226593,-8.4007465631530334],[31.015279021866451,-8.583622371150982],[31.445547986209025,-8.6521617285943879],[31.635955022763788,-8.8755947230163752],[31.886047551911055,-8.922164027555457],[32.033182245575439,-9.0578777752675954],[32.433071044420167,-9.1565911543330518],[32.919708946706351,-9.407594182947447],[33.022298364429773,-9.5637375712088879],[33.195527178031874,-9.6263227281751202],[33.350700655460685,-9.8622815233979271],[33.355004468615228,-10.062798961338723],[33.528654323637156,-10.234769093257212],[33.546857928875596,-10.368790091084653],[33.661299907224013,-10.55317450002072],[33.324732170359503,-10.903565245527084],[33.314767073627458,-11.020576781306547],[33.379531328995483,-11.157929853088072],[33.248734395758326,-11.477365440147429],[33.303955331471634,-11.695393069606306],[33.271621738895981,-12.152836218541266],[33.354781615778407,-12.287051744674018],[33.512009117128514,-12.347816117199805],[33.397790610495619,-12.489596176148067],[33.086671769384253,-12.60983460971474],[32.997935783802774,-12.687908219292538],[32.967361426940386,-13.224897877450182],[32.783489402055068,-13.593912942857065],[32.821516617878075,-13.755229376951618],[32.929915956959462,-13.894461824077961],[33.027772054045528,-13.950038540603778],[33.147962163713537,-13.941222287332398],[33.201382605598681,-14.013260568279941],[30.33828869670787,-14.953316048018024],[30.246009406162685,-15.13731492053196],[30.350396793784348,-15.349789404661673],[30.395831582275971,-15.642834019574002],[29.729708010991473,-15.64572796917302],[29.50378375184437,-15.693230459284102],[28.955522849527629,-15.961114388896231],[28.869756851292284,-16.075171577183252],[28.833683209184382,-16.419365868364967],[28.76036787453506,-16.53190018138946],[27.926466812473787,-16.902535395600701],[27.024127545302793,-17.954837394288585],[26.779848683178841,-18.041219443379617],[26.305891629080204,-17.926802554841025],[25.863388680527773,-17.951695595974012],[25.680585049933015,-17.838934734016728],[25.451803253232228,-17.844819334756984],[25.258914750567683,-17.793330966653407],[24.992419592261886,-17.566112738752405],[24.73767462888053,-17.518386778812701],[24.266467071538575,-17.483295016680572],[23.380729664083809,-17.640371144578332],[22.447829022941587,-16.805665679891931],[22.150913917256627,-16.596998492591602],[21.979785156250017,-15.950708548430567],[21.979136471572033,-13.0012066398681]]],"type":"Polygon"},"properties":{"alpha2":"ZM","radius":831000.0},"type":"Feature"}, +{"geometry":{"coordinates":[[[25.224313094125538,-17.915216722395332],[25.258924121583917,-17.793823712054191],[25.473769660328781,-17.846035197835782],[25.639645097034382,-17.824398142832447],[25.94334914301044,-17.962742833704652],[26.139584392077499,-17.911945796315671],[26.328841989577857,-17.928883695843329],[26.758113375034998,-18.038330051268098],[26.991893313628289,-17.967366493300467],[27.432488939366888,-17.51769673658843],[27.756717488374218,-17.060492000716156],[27.932329890909426,-16.896334409907073],[28.770130790489105,-16.509780164272776],[28.835770386978879,-16.404401513917673],[28.874739542097235,-16.04089981665966],[28.916788110620679,-15.985465543954264],[29.491415345521968,-15.695892583014921],[29.729619283877391,-15.644902726236458],[30.395869620924678,-15.643294079498869],[30.411269859287657,-15.869111569785568],[30.517414625226223,-15.981401144443193],[31.236161679498512,-16.023841934372335],[31.468332871987936,-16.170449699626719],[31.687474446009599,-16.214422677449118],[31.952398010226084,-16.422152653883952],[32.249096748574537,-16.450603576124649],[32.631000300854545,-16.587512681619188],[32.762340857904441,-16.682553063250136],[32.947790459728722,-16.712486752061444],[32.879359825390011,-16.942383803578998],[32.969659985235985,-17.25683589221628],[32.955545033719304,-18.075189923216982],[32.993410348581776,-18.35470454010083],[32.898650476477712,-18.693184033403806],[32.773794734723751,-18.928956757496653],[32.849823960761675,-19.109135753618908],[32.793100027708739,-19.435198374042276],[33.006542558100591,-19.873865469560585],[32.993744622447423,-19.980477284713285],[32.675174318287546,-20.512379564439438],[32.492685968181988,-20.681732267353663],[32.475902354285012,-20.950008891764071],[32.382998620595231,-21.138562616628228],[32.429483416958462,-21.297005323690218],[31.287880887067363,-22.401762094823827],[30.930359052168768,-22.292270148435474],[30.460157820398603,-22.3288113313521],[30.195774974430737,-22.291864176010733],[29.694800782445693,-22.151311658352625],[29.364918277758704,-22.193674986929047],[29.240623113156687,-22.094907569650225],[29.067846935071845,-22.043823497571836],[28.986651148101856,-21.837995682409066],[28.898762526642386,-21.760055320120635],[28.014270958168012,-21.553955677029595],[27.669722802807311,-21.064197443397667],[27.702419921232657,-20.756617763969011],[27.659484761751944,-20.560594109740638],[27.547136733197579,-20.483169330830091],[27.280966290335034,-20.478488377859247],[27.252523513393907,-20.223850914836699],[27.179829541673328,-20.106208140381003],[26.678379702346582,-19.892487204036524],[26.168242362628288,-19.538092445172353],[25.952820913832259,-19.086431161877055],[25.927106060355602,-18.931336777352545],[25.224313094125538,-17.915216722395332]]],"type":"Polygon"},"properties":{"alpha2":"ZW","radius":467000.0},"type":"Feature"} +]} diff --git a/toolkit/modules/tests/xpcshell/test_AllowedAppSources.js b/toolkit/modules/tests/xpcshell/test_AllowedAppSources.js new file mode 100644 index 0000000000..dcd56e2718 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_AllowedAppSources.js @@ -0,0 +1,112 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +const { OsEnvironment } = ChromeUtils.importESModule( + "resource://gre/modules/OsEnvironment.sys.mjs" +); +const { TelemetryTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TelemetryTestUtils.sys.mjs" +); + +// Valid values that telemetry might report. Except for "Error" and +// "NoSuchFeature", these are also the values that we read from the registry. +var APP_SOURCE_ANYWHERE = "Anywhere"; +var APP_SOURCE_RECOMMENDATIONS = "Recommendations"; +var APP_SOURCE_PREFER_STORE = "PreferStore"; +var APP_SOURCE_STORE_ONLY = "StoreOnly"; +var APP_SOURCE_NO_SUCH_FEATURE = "NoSuchFeature"; +var APP_SOURCE_COLLECTION_ERROR = "Error"; + +// This variable will store the value that we will fake reading from the +// registry (i.e. the value that would normally be read from +// SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AicEnabled"). A value +// of undefined represents the registry value not existing. +var gRegistryData; +// If this is set to true, we will throw an exception rather than providing a +// value when Policy.getAllowedAppSources is called. +var gErrorReadingRegistryValue = false; +// This holds the value that Policy.windowsVersionHasAppSourcesFeature will +// return, to allow us to simulate detection of operating system versions +// without the App Sources feature. +var gHaveAppSourcesFeature = true; +// If this is set to true, we will throw an exception rather than providing a +// value when Policy.windowsVersionHasAppSourcesFeature is called. +var gErrorDetectingAppSourcesFeature = false; + +function setup() { + // Allow the test to run successfully for all products whether they have the + // probe or not. + Services.prefs.setBoolPref( + "toolkit.telemetry.testing.overrideProductsCheck", + true + ); + + // Mock up the function that gets the allowed app sources so we don't actually + // have to change OS settings to test. + OsEnvironment.Policy.getAllowedAppSources = () => { + if (gErrorReadingRegistryValue) { + throw new Error("Arbitrary Testing Error"); + } + // To mimic the normal functionality here, we want to return undefined if + // the registry value doesn't exist. But that is already how we are + // representing that state in gRegistryData. So no conversion is necessary. + return gRegistryData; + }; + OsEnvironment.Policy.windowsVersionHasAppSourcesFeature = () => { + if (gErrorDetectingAppSourcesFeature) { + throw new Error("Arbitrary Testing Error"); + } + return gHaveAppSourcesFeature; + }; +} + +function runTest( + { + registryData = "", + registryValueExists = true, + registryReadError = false, + osHasAppSourcesFeature = true, + errorDetectingAppSourcesFeature = false, + }, + expectedScalarValue +) { + if (registryValueExists) { + gRegistryData = registryData; + } else { + gRegistryData = undefined; + } + gErrorReadingRegistryValue = registryReadError; + gHaveAppSourcesFeature = osHasAppSourcesFeature; + gErrorDetectingAppSourcesFeature = errorDetectingAppSourcesFeature; + + OsEnvironment.reportAllowedAppSources(); + + TelemetryTestUtils.assertScalar( + TelemetryTestUtils.getProcessScalars("parent"), + "os.environment.allowed_app_sources", + expectedScalarValue, + "The allowed app sources reported should match the expected sources" + ); +} + +add_task(async function testAppSources() { + setup(); + + runTest({ registryData: APP_SOURCE_ANYWHERE }, APP_SOURCE_ANYWHERE); + runTest( + { registryData: APP_SOURCE_RECOMMENDATIONS }, + APP_SOURCE_RECOMMENDATIONS + ); + runTest({ registryData: APP_SOURCE_PREFER_STORE }, APP_SOURCE_PREFER_STORE); + runTest({ registryData: APP_SOURCE_STORE_ONLY }, APP_SOURCE_STORE_ONLY); + runTest({ registryValueExists: false }, APP_SOURCE_ANYWHERE); + runTest({ registryReadError: true }, APP_SOURCE_COLLECTION_ERROR); + runTest({ registryData: "UnexpectedValue" }, APP_SOURCE_COLLECTION_ERROR); + runTest({ osHasAppSourcesFeature: false }, APP_SOURCE_NO_SUCH_FEATURE); + runTest( + { errorDetectingAppSourcesFeature: true }, + APP_SOURCE_COLLECTION_ERROR + ); +}); diff --git a/toolkit/modules/tests/xpcshell/test_BinarySearch.js b/toolkit/modules/tests/xpcshell/test_BinarySearch.js new file mode 100644 index 0000000000..13b5c4ed7a --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_BinarySearch.js @@ -0,0 +1,83 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { BinarySearch } = ChromeUtils.importESModule( + "resource://gre/modules/BinarySearch.sys.mjs" +); + +function run_test() { + // empty array + check([], 1, false, 0); + + // one-element array + check([2], 2, true, 0); + check([2], 1, false, 0); + check([2], 3, false, 1); + + // two-element array + check([2, 4], 2, true, 0); + check([2, 4], 4, true, 1); + check([2, 4], 1, false, 0); + check([2, 4], 3, false, 1); + check([2, 4], 5, false, 2); + + // three-element array + check([2, 4, 6], 2, true, 0); + check([2, 4, 6], 4, true, 1); + check([2, 4, 6], 6, true, 2); + check([2, 4, 6], 1, false, 0); + check([2, 4, 6], 3, false, 1); + check([2, 4, 6], 5, false, 2); + check([2, 4, 6], 7, false, 3); + + // duplicates + check([2, 2], 2, true, 0); + check([2, 2], 1, false, 0); + check([2, 2], 3, false, 2); + + // duplicates on the left + check([2, 2, 4], 2, true, 1); + check([2, 2, 4], 4, true, 2); + check([2, 2, 4], 1, false, 0); + check([2, 2, 4], 3, false, 2); + check([2, 2, 4], 5, false, 3); + + // duplicates on the right + check([2, 4, 4], 2, true, 0); + check([2, 4, 4], 4, true, 1); + check([2, 4, 4], 1, false, 0); + check([2, 4, 4], 3, false, 1); + check([2, 4, 4], 5, false, 3); + + // duplicates in the middle + check([2, 4, 4, 6], 2, true, 0); + check([2, 4, 4, 6], 4, true, 1); + check([2, 4, 4, 6], 6, true, 3); + check([2, 4, 4, 6], 1, false, 0); + check([2, 4, 4, 6], 3, false, 1); + check([2, 4, 4, 6], 5, false, 3); + check([2, 4, 4, 6], 7, false, 4); + + // duplicates all around + check([2, 2, 4, 4, 6, 6], 2, true, 0); + check([2, 2, 4, 4, 6, 6], 4, true, 2); + check([2, 2, 4, 4, 6, 6], 6, true, 4); + check([2, 2, 4, 4, 6, 6], 1, false, 0); + check([2, 2, 4, 4, 6, 6], 3, false, 2); + check([2, 2, 4, 4, 6, 6], 5, false, 4); + check([2, 2, 4, 4, 6, 6], 7, false, 6); +} + +function check(array, target, expectedFound, expectedIdx) { + let [found, idx] = BinarySearch.search(cmp, array, target); + Assert.equal(found, expectedFound); + Assert.equal(idx, expectedIdx); + + idx = expectedFound ? expectedIdx : -1; + Assert.equal(BinarySearch.indexOf(cmp, array, target), idx); + Assert.equal(BinarySearch.insertionIndexOf(cmp, array, target), expectedIdx); +} + +function cmp(num1, num2) { + return num1 - num2; +} diff --git a/toolkit/modules/tests/xpcshell/test_BrowserUtils.js b/toolkit/modules/tests/xpcshell/test_BrowserUtils.js new file mode 100644 index 0000000000..3d90e2a5a5 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_BrowserUtils.js @@ -0,0 +1,273 @@ +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); + +const { BrowserUtils } = ChromeUtils.importESModule( + "resource://gre/modules/BrowserUtils.sys.mjs" +); + +const { EnterprisePolicyTesting } = ChromeUtils.importESModule( + "resource://testing-common/EnterprisePolicyTesting.sys.mjs" +); + +const { Region } = ChromeUtils.importESModule( + "resource://gre/modules/Region.sys.mjs" +); + +const { updateAppInfo } = ChromeUtils.importESModule( + "resource://testing-common/AppInfo.sys.mjs" +); + +const { Preferences } = ChromeUtils.importESModule( + "resource://gre/modules/Preferences.sys.mjs" +); + +// Helper to run tests for specific regions +function setupRegions(home, current) { + Region._setHomeRegion(home || ""); + Region._setCurrentRegion(current || ""); +} + +function setLanguage(language) { + Services.locale.availableLocales = [language]; + Services.locale.requestedLocales = [language]; +} + +/** + * Calls to this need to revert these changes by undoing them at the end of the test, + * using: + * + * await EnterprisePolicyTesting.setupPolicyEngineWithJson(""); + */ +async function setupEnterprisePolicy() { + // set app info name as it's needed to set a policy and is not defined by default + // in xpcshell tests + updateAppInfo({ + name: "XPCShell", + }); + + // set up an arbitrary enterprise policy + await EnterprisePolicyTesting.setupPolicyEngineWithJson({ + policies: { + EnableTrackingProtection: { + Value: true, + }, + }, + }); +} + +add_task(async function test_shouldShowVPNPromo() { + function setPromoEnabled(enabled) { + Services.prefs.setBoolPref("browser.vpn_promo.enabled", enabled); + } + + const allowedRegion = "US"; + const disallowedRegion = "SY"; + const illegalRegion = "CN"; + const unsupportedRegion = "LY"; + const regionNotInDefaultPref = "QQ"; + + // Show promo when enabled in allowed regions + setupRegions(allowedRegion, allowedRegion); + Assert.ok(BrowserUtils.shouldShowVPNPromo()); + + // Don't show when not enabled + setPromoEnabled(false); + Assert.ok(!BrowserUtils.shouldShowVPNPromo()); + + // Don't show in disallowed home regions, even when enabled + setPromoEnabled(true); + setupRegions(disallowedRegion); + Assert.ok(!BrowserUtils.shouldShowVPNPromo()); + + // Don't show in illegal home regions, even when enabled + setupRegions(illegalRegion); + Assert.ok(!BrowserUtils.shouldShowVPNPromo()); + + // Don't show in disallowed current regions, even when enabled + setupRegions(allowedRegion, disallowedRegion); + Assert.ok(!BrowserUtils.shouldShowVPNPromo()); + + // Don't show in illegal current regions, even when enabled + setupRegions(allowedRegion, illegalRegion); + Assert.ok(!BrowserUtils.shouldShowVPNPromo()); + + // Show if home region is supported, even if current region is not supported (but isn't disallowed or illegal) + setupRegions(allowedRegion, unsupportedRegion); + Assert.ok(BrowserUtils.shouldShowVPNPromo()); + + // Show VPN if current region is supported, even if home region is unsupported (but isn't disallowed or illegal) + setupRegions(unsupportedRegion, allowedRegion); // revert changes to regions + Assert.ok(BrowserUtils.shouldShowVPNPromo()); + + // Make sure we are getting the list of allowed regions from the right + // place. + setupRegions(regionNotInDefaultPref); + Services.prefs.setStringPref( + "browser.contentblocking.report.vpn_regions", + "qq" + ); + Assert.ok(BrowserUtils.shouldShowVPNPromo()); + Services.prefs.clearUserPref("browser.contentblocking.report.vpn_regions"); + + if (AppConstants.platform !== "android") { + // Services.policies isn't shipped on Android + // Don't show VPN if there's an active enterprise policy + setupRegions(allowedRegion, allowedRegion); + await setupEnterprisePolicy(); + + Assert.ok(!BrowserUtils.shouldShowVPNPromo()); + + // revert policy changes made earlier + await EnterprisePolicyTesting.setupPolicyEngineWithJson(""); + } +}); + +add_task(async function test_sendToDeviceEmailsSupported() { + const allowedLanguage = "en-US"; + const disallowedLanguage = "ar"; + + // Return true if language is en-US + setLanguage(allowedLanguage); + Assert.ok(BrowserUtils.sendToDeviceEmailsSupported()); + + // Return false if language is ar + setLanguage(disallowedLanguage); + Assert.ok(!BrowserUtils.sendToDeviceEmailsSupported()); +}); + +add_task(async function test_shouldShowFocusPromo() { + const allowedRegion = "US"; + const disallowedRegion = "CN"; + + // Show promo when neither region is disallowed + setupRegions(allowedRegion, allowedRegion); + Assert.ok(BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.FOCUS)); + + // Don't show when home region is disallowed + setupRegions(disallowedRegion); + Assert.ok(!BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.FOCUS)); + + setupRegions(allowedRegion, allowedRegion); + + // Don't show when there is an enterprise policy active + if (AppConstants.platform !== "android") { + // Services.policies isn't shipped on Android + await setupEnterprisePolicy(); + + Assert.ok(!BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.FOCUS)); + + // revert policy changes made earlier + await EnterprisePolicyTesting.setupPolicyEngineWithJson(""); + } + + // Don't show when promo disabled by pref + Preferences.set("browser.promo.focus.enabled", false); + Assert.ok(!BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.FOCUS)); + + Preferences.resetBranch("browser.promo.focus"); +}); + +add_task(async function test_shouldShowPinPromo() { + Preferences.set("browser.promo.pin.enabled", true); + // Show pin promo type by default when promo is enabled + Assert.ok(BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.PIN)); + + // Don't show when there is an enterprise policy active + if (AppConstants.platform !== "android") { + // Services.policies isn't shipped on Android + await setupEnterprisePolicy(); + + Assert.ok(!BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.PIN)); + + // revert policy changes made earlier + await EnterprisePolicyTesting.setupPolicyEngineWithJson(""); + } + + // Don't show when promo disabled by pref + Preferences.set("browser.promo.pin.enabled", false); + Assert.ok(!BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.PIN)); + + Preferences.resetBranch("browser.promo.pin"); +}); + +add_task(async function test_shouldShowRelayPromo() { + // This test assumes by default no uri is configured. + Preferences.set("identity.fxaccounts.autoconfig.uri", ""); + Assert.ok(BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.RELAY)); + + // Don't show when there is an enterprise policy active + if (AppConstants.platform !== "android") { + // Services.policies isn't shipped on Android + await setupEnterprisePolicy(); + + Assert.ok(!BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.RELAY)); + + // revert policy changes made earlier + await EnterprisePolicyTesting.setupPolicyEngineWithJson(""); + } + + // Don't show if a custom FxA instance is configured + Preferences.set("identity.fxaccounts.autoconfig.uri", "https://x"); + Assert.ok(!BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.RELAY)); + + Preferences.reset("identity.fxaccounts.autoconfig.uri"); +}); + +add_task(async function test_shouldShowCookieBannersPromo() { + Preferences.set("browser.promo.cookiebanners.enabled", true); + // Show cookie banners promo type by default when promo is enabled + Assert.ok( + BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.COOKIE_BANNERS) + ); + + // Don't show when promo disabled by pref + Preferences.set("browser.promo.cookiebanners.enabled", false); + Assert.ok( + !BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.COOKIE_BANNERS) + ); + + Preferences.resetBranch("browser.promo.cookiebanners"); +}); + +add_task(function test_getShareableURL() { + // Some test suites, specifically android, don't have this setup properly -- so we add it manually + if (!Preferences.get("services.sync.engine.tabs.filteredSchemes")) { + Preferences.set( + "services.sync.engine.tabs.filteredSchemes", + "about|resource|chrome|file|blob|moz-extension|data" + ); + } + // Empty shouldn't be sendable + Assert.ok(!BrowserUtils.getShareableURL("")); + // Valid + let good = Services.io.newURI("https://mozilla.org"); + Assert.ok(BrowserUtils.getShareableURL(good).equals(good)); + // Invalid + Assert.ok( + !BrowserUtils.getShareableURL(Services.io.newURI("file://path/to/pdf.pdf")) + ); + + // Invalid + Assert.ok( + !BrowserUtils.getShareableURL( + Services.io.newURI( + "data:application/json;base64,ewogICJ0eXBlIjogIm1haW4i==" + ) + ) + ); + + // Reader mode: + if (AppConstants.platform !== "android") { + let readerUrl = Services.io.newURI( + "about:reader?url=" + encodeURIComponent("http://foo.com/") + ); + Assert.equal( + BrowserUtils.getShareableURL(readerUrl).spec, + "http://foo.com/" + ); + } +}); diff --git a/toolkit/modules/tests/xpcshell/test_BrowserUtils_urlFormatting.js b/toolkit/modules/tests/xpcshell/test_BrowserUtils_urlFormatting.js new file mode 100644 index 0000000000..c7945cdb9c --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_BrowserUtils_urlFormatting.js @@ -0,0 +1,309 @@ +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +do_get_profile(); + +const { FileUtils } = ChromeUtils.importESModule( + "resource://gre/modules/FileUtils.sys.mjs" +); + +let tempFile = new FileUtils.File(PathUtils.tempDir); +const TEST_LOCAL_FILE_NAME = "hello.txt"; +tempFile.append(TEST_LOCAL_FILE_NAME); + +const gL10n = new Localization(["toolkit/global/browser-utils.ftl"], true); +const DATA_URL_EXPECTED_STRING = gL10n.formatValueSync( + "browser-utils-url-data" +); +const EXTENSION_NAME = "My Test Extension"; +const EXTENSION_URL_EXPECTED_STRING = gL10n.formatValueSync( + "browser-utils-url-extension", + { extension: EXTENSION_NAME } +); + +const { AddonTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/AddonTestUtils.sys.mjs" +); + +const { ExtensionTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/ExtensionXPCShellUtils.sys.mjs" +); + +AddonTestUtils.init(this); +ExtensionTestUtils.init(this); + +// Allow for unsigned addons. +AddonTestUtils.overrideCertDB(); + +AddonTestUtils.createAppInfo( + "xpcshell@tests.mozilla.org", + "XPCShell", + "42", + "42" +); + +// Filled in in setup, shared state with the various test tasks so declared here. +let addonBaseURI; + +// http/https tests first. These are split out from TESTS for the benefit of reuse by the +// blob: tests further down. +const HTTP_TESTS = [ + // Simple http/https examples with/without `www.`. + { + input: "https://example.com", + output: "example.com", + }, + { + input: "https://example.com/path/?query#hash", + output: "example.com", + }, + { + input: "https://www.example.com", + output: "example.com", + }, + { + input: "https://www.example.com/path/?query#hash", + output: "example.com", + }, + { + input: "http://example.com", + output: "example.com", + }, + { + input: "http://www.example.com", + output: "example.com", + }, + + // We shouldn't drop `www.` if that's the domain: + { + input: "https://www.com", + output: "www.com", + }, + + // Multilevel TLDs should work: + { + input: "https://www.example.co.uk", + output: "example.co.uk", + }, + { + input: "https://www.co.uk", + output: "www.co.uk", + }, + + // Other sudomains should be kept: + { + input: "https://webmail.example.co.uk", + output: "webmail.example.co.uk", + }, + { + input: "https://webmail.example.com", + output: "webmail.example.com", + }, + + // IP addresses should work: + { + input: "http://[::1]/foo/bar?baz=bax#quux", + output: "[::1]", + }, + { + input: "http://127.0.0.1/foo/bar?baz=bax#quux", + output: "127.0.0.1", + }, +]; + +const TESTS = [ + ...HTTP_TESTS, + + // about URIs: + { + input: "about:config", + output: "about:config", + }, + { + input: "about:config?foo#bar", + output: "about:config", + }, + + // file URI directory: + { + input: Services.io.newFileURI(new FileUtils.File(PathUtils.tempDir)).spec, + output: PathUtils.filename(PathUtils.tempDir), + }, + // file URI directory that ends in slash: + { + input: + Services.io.newFileURI(new FileUtils.File(PathUtils.tempDir)).spec + "/", + output: PathUtils.filename(PathUtils.tempDir), + }, + // file URI individual file: + { + input: Services.io.newFileURI(tempFile).spec, + output: tempFile.leafName, + }, + + // As above but for chrome URIs: + { + input: "chrome://global/content/blah", + output: "blah", + }, + { + input: "chrome://global/content/blah//", + output: "blah", + }, + { + input: "chrome://global/content/foo.txt", + output: "foo.txt", + }, + + // Also check data URIs: + { + input: "data:text/html,42", + output: DATA_URL_EXPECTED_STRING, + }, +]; + +add_setup(async () => { + const testExtensionData = { + useAddonManager: "temporary", + manifest: { + browser_specific_settings: { gecko: { id: "myextension@example.com" } }, + name: EXTENSION_NAME, + }, + }; + + const testNoNameExtensionData = { + useAddonManager: "temporary", + manifest: { + browser_specific_settings: { gecko: { id: "noname@example.com" } }, + name: "", + }, + }; + + await AddonTestUtils.promiseStartupManager(); + const extension = ExtensionTestUtils.loadExtension(testExtensionData); + const noNameExtension = ExtensionTestUtils.loadExtension( + testNoNameExtensionData + ); + await extension.startup(); + await noNameExtension.startup(); + + addonBaseURI = extension.extension.baseURI; + let noNameAddonBaseURI = noNameExtension.extension.baseURI; + + TESTS.push( + { + input: addonBaseURI.spec, + output: EXTENSION_URL_EXPECTED_STRING, + }, + { + input: "moz-extension://blah/", + output: gL10n.formatValueSync("browser-utils-url-extension", { + extension: "moz-extension://blah/", + }), + }, + { + input: noNameAddonBaseURI.spec, + output: gL10n.formatValueSync("browser-utils-url-extension", { + extension: noNameAddonBaseURI.spec, + }), + } + ); + + registerCleanupFunction(async () => { + await extension.unload(); + await noNameExtension.unload(); + }); +}); + +const { BrowserUtils } = ChromeUtils.importESModule( + "resource://gre/modules/BrowserUtils.sys.mjs" +); + +add_task(async function test_checkStringFormatting() { + for (let { input, output } of TESTS) { + Assert.equal( + BrowserUtils.formatURIStringForDisplay(input), + output, + `String ${input} formatted for output should match` + ); + } +}); + +add_task(async function test_checkURIFormatting() { + for (let { input, output } of TESTS) { + let uri = Services.io.newURI(input); + Assert.equal( + BrowserUtils.formatURIForDisplay(uri), + output, + `URI ${input} formatted for output should match` + ); + } +}); + +add_task(async function test_checkViewSourceFormatting() { + for (let { input, output } of HTTP_TESTS) { + Assert.equal( + BrowserUtils.formatURIStringForDisplay("view-source:" + input), + output, + `String view-source:${input} formatted for output should match` + ); + let uri = Services.io.newURI("view-source:" + input); + Assert.equal( + BrowserUtils.formatURIForDisplay(uri), + output, + `URI view-source:${input} formatted for output should match` + ); + } +}); + +function createBlobURLWithSandbox(origin) { + let sb = new Cu.Sandbox(origin, { wantGlobalProperties: ["Blob", "URL"] }); + // Need to pass 'false' for the validate filename param or this throws + // exceptions. I'm not sure why... + return Cu.evalInSandbox( + 'URL.createObjectURL(new Blob(["text"], { type: "text/plain" }))', + sb, + "", + null, + 0, + false + ); +} + +add_task(async function test_checkBlobURIs() { + // These don't just live in the TESTS array because creating a valid + // blob URI is a bit more involved... + let blob = new Blob(["test"], { type: "text/plain" }); + let url = URL.createObjectURL(blob); + Assert.equal( + BrowserUtils.formatURIStringForDisplay(url), + DATA_URL_EXPECTED_STRING, + `Blob url string without origin should be represented as (data)` + ); + + // Now with a null principal: + url = createBlobURLWithSandbox(null); + Assert.equal( + BrowserUtils.formatURIStringForDisplay(url), + DATA_URL_EXPECTED_STRING, + `Blob url string with null principal origin should be represented as (data)` + ); + + // And some valid url principals: + let BLOB_TESTS = [ + { + input: addonBaseURI.spec, + output: EXTENSION_URL_EXPECTED_STRING, + }, + ].concat(HTTP_TESTS); + for (let { input, output } of BLOB_TESTS) { + url = createBlobURLWithSandbox(input); + Assert.equal( + BrowserUtils.formatURIStringForDisplay(url), + output, + `Blob url string with principal from ${input} should show principal URI` + ); + } +}); diff --git a/toolkit/modules/tests/xpcshell/test_CanonicalJSON.js b/toolkit/modules/tests/xpcshell/test_CanonicalJSON.js new file mode 100644 index 0000000000..d441ffbe2d --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_CanonicalJSON.js @@ -0,0 +1,173 @@ +const { CanonicalJSON } = ChromeUtils.importESModule( + "resource://gre/modules/CanonicalJSON.sys.mjs" +); + +add_task(async function test_canonicalJSON_should_preserve_array_order() { + const input = ["one", "two", "three"]; + // No sorting should be done on arrays. + Assert.equal(CanonicalJSON.stringify(input), '["one","two","three"]'); +}); + +add_task(async function test_canonicalJSON_orders_object_keys() { + const input = [ + { + b: ["two", "three"], + a: ["zero", "one"], + }, + ]; + Assert.equal( + CanonicalJSON.stringify(input), + '[{"a":["zero","one"],"b":["two","three"]}]' + ); +}); + +add_task(async function test_canonicalJSON_orders_nested_object_keys() { + const input = [ + { + b: { d: "d", c: "c" }, + a: { b: "b", a: "a" }, + }, + ]; + Assert.equal( + CanonicalJSON.stringify(input), + '[{"a":{"a":"a","b":"b"},"b":{"c":"c","d":"d"}}]' + ); +}); + +add_task(async function test_canonicalJSON_escapes_unicode_values() { + Assert.equal(CanonicalJSON.stringify([{ key: "✓" }]), '[{"key":"\\u2713"}]'); + // Unicode codepoints should be output in lowercase. + Assert.equal(CanonicalJSON.stringify([{ key: "é" }]), '[{"key":"\\u00e9"}]'); +}); + +add_task(async function test_canonicalJSON_escapes_unicode_object_keys() { + Assert.equal( + CanonicalJSON.stringify([{ é: "check" }]), + '[{"\\u00e9":"check"}]' + ); +}); + +add_task(async function test_canonicalJSON_does_not_alter_input() { + const records = [ + { foo: "bar", last_modified: "12345", id: "1" }, + { bar: "baz", last_modified: "45678", id: "2" }, + ]; + const serializedJSON = JSON.stringify(records); + CanonicalJSON.stringify(records); + Assert.equal(JSON.stringify(records), serializedJSON); +}); + +add_task(async function test_canonicalJSON_preserves_data() { + const records = [ + { foo: "bar", last_modified: "12345", id: "1" }, + { bar: "baz", last_modified: "45678", id: "2" }, + ]; + const expected = + '[{"foo":"bar","id":"1","last_modified":"12345"},' + + '{"bar":"baz","id":"2","last_modified":"45678"}]'; + Assert.equal(CanonicalJSON.stringify(records), expected); +}); + +add_task(async function test_canonicalJSON_does_not_add_space_separators() { + const records = [ + { foo: "bar", last_modified: "12345", id: "1" }, + { bar: "baz", last_modified: "45678", id: "2" }, + ]; + const serialized = CanonicalJSON.stringify(records); + Assert.ok(!serialized.includes(" ")); +}); + +add_task(async function test_canonicalJSON_serializes_empty_object() { + Assert.equal(CanonicalJSON.stringify({}), "{}"); +}); + +add_task(async function test_canonicalJSON_serializes_empty_array() { + Assert.equal(CanonicalJSON.stringify([]), "[]"); +}); + +add_task(async function test_canonicalJSON_serializes_NaN() { + Assert.equal(CanonicalJSON.stringify(NaN), "null"); +}); + +add_task(async function test_canonicalJSON_serializes_inf() { + // This isn't part of the JSON standard. + Assert.equal(CanonicalJSON.stringify(Infinity), "null"); +}); + +add_task(async function test_canonicalJSON_serializes_empty_string() { + Assert.equal(CanonicalJSON.stringify(""), '""'); +}); + +add_task(async function test_canonicalJSON_escapes_backslashes() { + Assert.equal(CanonicalJSON.stringify("This\\and this"), '"This\\\\and this"'); +}); + +add_task(async function test_canonicalJSON_handles_signed_zeros() { + // do_check_eq doesn't support comparison of -0 and 0 properly. + Assert.ok(CanonicalJSON.stringify(-0) === "-0"); + Assert.ok(CanonicalJSON.stringify(0) === "0"); +}); + +add_task(async function test_canonicalJSON_with_deeply_nested_dicts() { + const records = [ + { + a: { + b: "b", + a: "a", + c: { + b: "b", + a: "a", + c: ["b", "a", "c"], + d: { b: "b", a: "a" }, + id: "1", + e: 1, + f: [2, 3, 1], + g: { + 2: 2, + 3: 3, + 1: { + b: "b", + a: "a", + c: "c", + }, + }, + }, + }, + id: "1", + }, + ]; + const expected = + '[{"a":{"a":"a","b":"b","c":{"a":"a","b":"b","c":["b","a","c"],' + + '"d":{"a":"a","b":"b"},"e":1,"f":[2,3,1],"g":{' + + '"1":{"a":"a","b":"b","c":"c"},"2":2,"3":3},"id":"1"}},"id":"1"}]'; + + Assert.equal(CanonicalJSON.stringify(records), expected); +}); + +add_task(async function test_canonicalJSON_should_use_scientific_notation() { + /* + We globally follow the Python float representation, except for exponents < 10 + where we drop the leading zero + */ + Assert.equal(CanonicalJSON.stringify(0.00099), "0.00099"); + Assert.equal(CanonicalJSON.stringify(0.000011), "0.000011"); + Assert.equal(CanonicalJSON.stringify(0.0000011), "0.0000011"); + Assert.equal(CanonicalJSON.stringify(0.000001), "0.000001"); + Assert.equal(CanonicalJSON.stringify(0.00000099), "9.9e-7"); + Assert.equal(CanonicalJSON.stringify(0.0000001), "1e-7"); + Assert.equal(CanonicalJSON.stringify(0.000000930258908), "9.30258908e-7"); + Assert.equal(CanonicalJSON.stringify(0.00000000000068272), "6.8272e-13"); + Assert.equal( + CanonicalJSON.stringify(Math.pow(10, 20)), + "100000000000000000000" + ); + Assert.equal(CanonicalJSON.stringify(Math.pow(10, 21)), "1e+21"); + Assert.equal( + CanonicalJSON.stringify(Math.pow(10, 15) + 0.1), + "1000000000000000.1" + ); + Assert.equal( + CanonicalJSON.stringify(Math.pow(10, 16) * 1.1), + "11000000000000000" + ); +}); diff --git a/toolkit/modules/tests/xpcshell/test_Color.js b/toolkit/modules/tests/xpcshell/test_Color.js new file mode 100644 index 0000000000..f99de7072e --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_Color.js @@ -0,0 +1,87 @@ +"use strict"; + +const { Color } = ChromeUtils.importESModule( + "resource://gre/modules/Color.sys.mjs" +); + +function run_test() { + testRelativeLuminance(); + testUseBrightText(); + testContrastRatio(); + testIsContrastRatioAcceptable(); +} + +function testRelativeLuminance() { + let c = new Color(0, 0, 0); + Assert.equal(c.relativeLuminance, 0, "Black is not illuminating"); + + c = new Color(255, 255, 255); + Assert.equal(c.relativeLuminance, 1, "White is quite the luminant one"); + + c = new Color(142, 42, 142); + Assert.equal( + c.relativeLuminance, + 0.09359705837110571, + "This purple is not that luminant" + ); +} + +function testUseBrightText() { + let c = new Color(0, 0, 0); + Assert.ok( + c.useBrightText, + "Black is bright, so bright text should be used here" + ); + + c = new Color(255, 255, 255); + Assert.ok( + !c.useBrightText, + "White is bright, so better not use bright colored text on it" + ); +} + +function testContrastRatio() { + let c = new Color(0, 0, 0); + let c2 = new Color(255, 255, 255); + Assert.equal( + c.contrastRatio(c2), + 21, + "Contrast between black and white is max" + ); + Assert.equal(c.contrastRatio(c), 1, "Contrast between equals is min"); + + let c3 = new Color(142, 42, 142); + Assert.equal( + c.contrastRatio(c3), + 2.871941167422114, + "Contrast between black and purple shouldn't be very high" + ); + Assert.equal( + c2.contrastRatio(c3), + 7.312127503938331, + "Contrast between white and purple should be high" + ); +} + +function testIsContrastRatioAcceptable() { + // Let's assert what browser.js is doing for window frames. + let c = new Color(...[55, 156, 152]); + let c2 = new Color(0, 0, 0); + Assert.equal(c.r, 55, "Reds should match"); + Assert.equal(c.g, 156, "Greens should match"); + Assert.equal(c.b, 152, "Blues should match"); + Assert.ok( + c.isContrastRatioAcceptable(c2), + "The blue is high contrast enough" + ); + c2 = new Color(...[35, 65, 100]); + Assert.ok( + !c.isContrastRatioAcceptable(c2), + "The blue is not high contrast enough" + ); + // But would it be high contrast enough at a lower conformance level? + Assert.ok( + c.isContrastRatioAcceptable(c2, "A"), + "The blue is high contrast enough when used as large text" + ); +} diff --git a/toolkit/modules/tests/xpcshell/test_CreditCard.js b/toolkit/modules/tests/xpcshell/test_CreditCard.js new file mode 100644 index 0000000000..0d9a26ac5e --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_CreditCard.js @@ -0,0 +1,618 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { CreditCard } = ChromeUtils.importESModule( + "resource://gre/modules/CreditCard.sys.mjs" +); + +add_task(function isValidNumber() { + function testValid(number, shouldPass) { + if (shouldPass) { + ok( + CreditCard.isValidNumber(number), + `${number} should be considered valid` + ); + } else { + ok( + !CreditCard.isValidNumber(number), + `${number} should not be considered valid` + ); + } + } + + testValid("0000000000000000", true); + + testValid("41111111112", false); // passes Luhn but too short + testValid("4111-1111-112", false); // passes Luhn but too short + testValid("55555555555544440018", false); // passes Luhn but too long + testValid("5555 5555 5555 4444 0018", false); // passes Luhn but too long + + testValid("4929001587121045", true); + testValid("5103059495477870", true); + testValid("6011029476355493", true); + testValid("3589993783099582", true); + testValid("5415425865751454", true); + + testValid("378282246310005", true); // American Express test number + testValid("371449635398431", true); // American Express test number + testValid("378734493671000", true); // American Express Corporate test number + testValid("5610591081018250", true); // Australian BankCard test number + testValid("6759649826438453", true); // Maestro test number + testValid("6799990100000000019", true); // 19 digit Maestro test number + testValid("6799-9901-0000-0000019", true); // 19 digit Maestro test number + testValid("30569309025904", true); // 14 digit Diners Club test number + testValid("38520000023237", true); // 14 digit Diners Club test number + testValid("6011111111111117", true); // Discover test number + testValid("6011000990139424", true); // Discover test number + testValid("3530111333300000", true); // JCB test number + testValid("3566002020360505", true); // JCB test number + testValid("3532596776688495393", true); // 19-digit JCB number. JCB, Discover, Maestro could have 16-19 digits + testValid("3532 5967 7668 8495393", true); // 19-digit JCB number. JCB, Discover, Maestro could have 16-19 digits + testValid("5555555555554444", true); // MasterCard test number + testValid("5105105105105100", true); // MasterCard test number + testValid("2221000000000009", true); // 2-series MasterCard test number + testValid("4111111111111111", true); // Visa test number + testValid("4012888888881881", true); // Visa test number + testValid("4222222222222", true); // 13 digit Visa test number + testValid("4222 2222 22222", true); // 13 digit Visa test number + testValid("4035 5010 0000 0008", true); // Visadebit/Cartebancaire test number + + testValid("5038146897157463", true); + testValid("4026313395502338", true); + testValid("6387060366272981", true); + testValid("474915027480942", true); + testValid("924894781317325", true); + testValid("714816113937185", true); + testValid("790466087343106", true); + testValid("474320195408363", true); + testValid("219211148122351", true); + testValid("633038472250799", true); + testValid("354236732906484", true); + testValid("095347810189325", true); + testValid("930771457288760", true); + testValid("3091269135815020", true); + testValid("5471839082338112", true); + testValid("0580828863575793", true); + testValid("5015290610002932", true); + testValid("9465714503078607", true); + testValid("4302068493801686", true); + testValid("2721398408985465", true); + testValid("6160334316984331", true); + testValid("8643619970075142", true); + testValid("0218246069710785", true); + testValid("0000-0000-0080-4609", true); + testValid("0000 0000 0222 331", true); + testValid("344060747836806", true); + testValid("001064088", false); // too short + testValid("00-10-64-088", false); // still too short + testValid("4929001587121046", false); + testValid("5103059495477876", false); + testValid("6011029476355494", false); + testValid("3589993783099581", false); + testValid("5415425865751455", false); + testValid("5038146897157462", false); + testValid("4026313395502336", false); + testValid("6387060366272980", false); + testValid("344060747836804", false); + testValid("30190729470496", false); + testValid("36333851788255", false); + testValid("526931005800649", false); + testValid("724952425140686", false); + testValid("379761391174135", false); + testValid("030551436468583", false); + testValid("947377014076746", false); + testValid("254848023655752", false); + testValid("226871580283345", false); + testValid("708025346034339", false); + testValid("917585839076788", false); + testValid("918632588027666", false); + testValid("9946177098017064", false); + testValid("4081194386488872", false); + testValid("3095975979578034", false); + testValid("3662215692222536", false); + testValid("6723210018630429", false); + testValid("4411962856225025", false); + testValid("8276996369036686", false); + testValid("4449796938248871", false); + testValid("3350852696538147", false); + testValid("5011802870046957", false); + testValid("0000", false); +}); + +add_task(function test_formatMaskedNumber() { + function assertMaskedNumber(input, expected) { + const actual = CreditCard.formatMaskedNumber(input); + Assert.equal(actual, expected); + } + assertMaskedNumber("************0000", "****0000"); + assertMaskedNumber("************1045", "****1045"); + assertMaskedNumber("***********6806", "****6806"); + assertMaskedNumber("**********0495", "****0495"); + assertMaskedNumber("**********8250", "****8250"); +}); + +add_task(function test_maskNumber() { + function testMask(number, expected) { + let card = new CreditCard({ number }); + Assert.equal( + card.maskedNumber, + expected, + "Masked number should only show the last four digits" + ); + } + testMask("0000000000000000", "**** 0000"); + testMask("4929001587121045", "**** 1045"); + testMask("5103059495477870", "**** 7870"); + testMask("6011029476355493", "**** 5493"); + testMask("3589993783099582", "**** 9582"); + testMask("5415425865751454", "**** 1454"); + testMask("344060747836806", "**** 6806"); + testMask("6799990100000000019", "**** 0019"); + Assert.throws( + () => new CreditCard({ number: "1234" }).maskedNumber, + /Invalid credit card number/, + "Four or less numbers should throw when retrieving the maskedNumber" + ); +}); + +add_task(function test_longMaskedNumber() { + function testMask(number, expected) { + let card = new CreditCard({ number }); + Assert.equal( + card.longMaskedNumber, + expected, + "Long masked number should show asterisks for all digits but last four" + ); + } + testMask("0000000000000000", "************0000"); + testMask("4929001587121045", "************1045"); + testMask("5103059495477870", "************7870"); + testMask("6011029476355493", "************5493"); + testMask("3589993783099582", "************9582"); + testMask("5415425865751454", "************1454"); + testMask("344060747836806", "***********6806"); + testMask("6799990100000000019", "***************0019"); + Assert.throws( + () => new CreditCard({ number: "1234" }).longMaskedNumber, + /Invalid credit card number/, + "Four or less numbers should throw when retrieving the longMaskedNumber" + ); +}); + +add_task(function test_isValid() { + function testValid( + number, + expirationMonth, + expirationYear, + shouldPass, + message + ) { + let isValid = false; + try { + let card = new CreditCard({ + number, + expirationMonth, + expirationYear, + }); + isValid = card.isValid(); + } catch (ex) { + isValid = false; + } + if (shouldPass) { + ok(isValid, message); + } else { + ok(!isValid, message); + } + } + let year = new Date().getFullYear(); + let month = new Date().getMonth() + 1; + + testValid( + "0000000000000000", + month, + year + 2, + true, + "Valid number and future expiry date (two years) should pass" + ); + testValid( + "0000000000000000", + month < 11 ? month + 2 : month % 10, + month < 11 ? year : year + 1, + true, + "Valid number and future expiry date (two months) should pass" + ); + testValid( + "0000000000000000", + month, + year, + true, + "Valid number and expiry date equal to this month should pass" + ); + testValid( + "0000000000000000", + month > 1 ? month - 1 : 12, + month > 1 ? year : year - 1, + false, + "Valid number but overdue expiry date should fail" + ); + testValid( + "0000000000000000", + month, + year - 1, + false, + "Valid number but overdue expiry date (by a year) should fail" + ); + testValid( + "0000000000000001", + month, + year + 2, + false, + "Invalid number but future expiry date should fail" + ); +}); + +add_task(function test_normalize() { + Assert.equal( + new CreditCard({ number: "0000 0000 0000 0000" }).number, + "0000000000000000", + "Spaces should be removed from card number after it is normalized" + ); + Assert.equal( + new CreditCard({ number: "0000 0000\t 0000\t0000" }).number, + "0000000000000000", + "Spaces should be removed from card number after it is normalized" + ); + Assert.equal( + new CreditCard({ number: "0000-0000-0000-0000" }).number, + "0000000000000000", + "Hyphens should be removed from card number after it is normalized" + ); + Assert.equal( + new CreditCard({ number: "0000-0000 0000-0000" }).number, + "0000000000000000", + "Spaces and hyphens should be removed from card number after it is normalized" + ); + Assert.equal( + new CreditCard({ number: "0000000000000000" }).number, + "0000000000000000", + "Normalized numbers should not get changed" + ); + + let card = new CreditCard({ number: "0000000000000000" }); + card.expirationYear = "22"; + card.expirationMonth = "11"; + Assert.equal( + card.expirationYear, + 2022, + "Years less than four digits are in the third millenium" + ); + card.expirationYear = "-200"; + ok(isNaN(card.expirationYear), "Negative years are blocked"); + card.expirationYear = "1998"; + Assert.equal( + card.expirationYear, + 1998, + "Years with four digits are not changed" + ); + card.expirationYear = "test"; + ok(isNaN(card.expirationYear), "non-number years are returned as NaN"); + card.expirationMonth = "02"; + Assert.equal( + card.expirationMonth, + 2, + "Zero-leading months are converted properly (not octal)" + ); + card.expirationMonth = "test"; + ok(isNaN(card.expirationMonth), "non-number months are returned as NaN"); + card.expirationMonth = "12"; + Assert.equal( + card.expirationMonth, + 12, + "Months formatted correctly are unchanged" + ); + card.expirationMonth = "13"; + ok(isNaN(card.expirationMonth), "Months above 12 are blocked"); + card.expirationMonth = "7"; + Assert.equal( + card.expirationMonth, + 7, + "Changing back to a valid number passes" + ); + card.expirationMonth = "0"; + ok(isNaN(card.expirationMonth), "Months below 1 are blocked"); + card.expirationMonth = card.expirationYear = undefined; + card.expirationString = "2022/01"; + Assert.equal(card.expirationMonth, 1, "Month should be parsed correctly"); + Assert.equal(card.expirationYear, 2022, "Year should be parsed correctly"); + + card.expirationString = "2028 / 05"; + Assert.equal(card.expirationMonth, 5, "Month should be parsed correctly"); + Assert.equal(card.expirationYear, 2028, "Year should be parsed correctly"); + + card.expirationString = "2023-02"; + Assert.equal(card.expirationMonth, 2, "Month should be parsed correctly"); + Assert.equal(card.expirationYear, 2023, "Year should be parsed correctly"); + + card.expirationString = "2029 - 09"; + Assert.equal(card.expirationMonth, 9, "Month should be parsed correctly"); + Assert.equal(card.expirationYear, 2029, "Year should be parsed correctly"); + + card.expirationString = "03-2024"; + Assert.equal(card.expirationMonth, 3, "Month should be parsed correctly"); + Assert.equal(card.expirationYear, 2024, "Year should be parsed correctly"); + + card.expirationString = "08 - 2024"; + Assert.equal(card.expirationMonth, 8, "Month should be parsed correctly"); + Assert.equal(card.expirationYear, 2024, "Year should be parsed correctly"); + + card.expirationString = "04/2025"; + Assert.equal(card.expirationMonth, 4, "Month should be parsed correctly"); + Assert.equal(card.expirationYear, 2025, "Year should be parsed correctly"); + + card.expirationString = "01 / 2023"; + Assert.equal(card.expirationMonth, 1, "Month should be parsed correctly"); + Assert.equal(card.expirationYear, 2023, "Year should be parsed correctly"); + + card.expirationString = "05/26"; + Assert.equal(card.expirationMonth, 5, "Month should be parsed correctly"); + Assert.equal(card.expirationYear, 2026, "Year should be parsed correctly"); + + card.expirationString = " 06 / 27 "; + Assert.equal(card.expirationMonth, 6, "Month should be parsed correctly"); + Assert.equal(card.expirationYear, 2027, "Year should be parsed correctly"); + + card.expirationString = "04 / 25"; + Assert.equal(card.expirationMonth, 4, "Month should be parsed correctly"); + Assert.equal(card.expirationYear, 2025, "Year should be parsed correctly"); + + card.expirationString = "27-6"; + Assert.equal(card.expirationMonth, 6, "Month should be parsed correctly"); + Assert.equal(card.expirationYear, 2027, "Year should be parsed correctly"); + + card.expirationString = "26 - 5"; + Assert.equal(card.expirationMonth, 5, "Month should be parsed correctly"); + Assert.equal(card.expirationYear, 2026, "Year should be parsed correctly"); + + card.expirationString = "07/11"; + Assert.equal( + card.expirationMonth, + 7, + "Ambiguous month should be parsed correctly" + ); + Assert.equal( + card.expirationYear, + 2011, + "Ambiguous year should be parsed correctly" + ); + + card.expirationString = "08 / 12"; + Assert.equal( + card.expirationMonth, + 8, + "Ambiguous month should be parsed correctly" + ); + Assert.equal( + card.expirationYear, + 2012, + "Ambiguous year should be parsed correctly" + ); + + card = new CreditCard({ + number: "0000000000000000", + expirationMonth: "02", + expirationYear: "2112", + expirationString: "06-2066", + }); + Assert.equal( + card.expirationMonth, + 2, + "expirationString is takes lower precendence than explicit month" + ); + Assert.equal( + card.expirationYear, + 2112, + "expirationString is takes lower precendence than explicit year" + ); +}); + +add_task(async function test_label() { + let testCases = [ + { + number: "0000000000000000", + name: "Rudy Badoody", + expectedMaskedLabel: "**** 0000, Rudy Badoody", + }, + { + number: "3589993783099582", + name: "Jimmy Babimmy", + expectedMaskedLabel: "**** 9582, Jimmy Babimmy", + }, + ]; + + for (let testCase of testCases) { + let { number, name } = testCase; + Assert.equal( + await CreditCard.getLabel({ number, name }), + testCase.expectedMaskedLabel, + "The expectedMaskedLabel should be shown when showNumbers is false" + ); + } +}); + +add_task(async function test_network() { + let supportedNetworks = CreditCard.getSupportedNetworks(); + Assert.ok( + supportedNetworks.length, + "There are > 0 supported credit card networks" + ); + + let ccNumber = "0000000000000000"; + let testCases = supportedNetworks.map(network => { + return { number: ccNumber, network, expectedNetwork: network }; + }); + testCases.push({ + number: ccNumber, + network: "gringotts", + expectedNetwork: "gringotts", + }); + testCases.push({ + number: ccNumber, + network: "", + expectedNetwork: undefined, + }); + + for (let testCase of testCases) { + let { number, network } = testCase; + let card = new CreditCard({ number, network }); + Assert.equal( + await card.network, + testCase.expectedNetwork, + `The expectedNetwork ${card.network} should match the card network ${testCase.expectedNetwork}` + ); + } +}); + +add_task(async function test_isValidNetwork() { + for (let network of CreditCard.getSupportedNetworks()) { + Assert.ok(CreditCard.isValidNetwork(network), "supported network is valid"); + } + Assert.ok(!CreditCard.isValidNetwork(), "undefined is not a valid network"); + Assert.ok( + !CreditCard.isValidNetwork(""), + "empty string is not a valid network" + ); + Assert.ok(!CreditCard.isValidNetwork(null), "null is not a valid network"); + Assert.ok( + !CreditCard.isValidNetwork("Visa"), + "network validity is case-sensitive" + ); + Assert.ok( + !CreditCard.isValidNetwork("madeupnetwork"), + "unknown network is invalid" + ); +}); + +add_task(async function test_getType() { + const RECOGNIZED_CARDS = [ + // Edge cases + ["2221000000000000", "mastercard"], + ["2720000000000000", "mastercard"], + ["2200000000000000", "mir"], + ["2204000000000000", "mir"], + ["340000000000000", "amex"], + ["370000000000000", "amex"], + ["3000000000000000", "diners"], + ["3050000000000000", "diners"], + ["3095000000000000", "diners"], + ["36000000000000", "diners"], + ["3800000000000000", "diners"], + ["3900000000000000", "diners"], + ["3528000000000000", "jcb"], + ["3589000000000000", "jcb"], + ["4035000000000000", "cartebancaire"], + ["4360000000000000", "cartebancaire"], + ["4000000000000000", "visa"], + ["4999999999999999", "visa"], + ["5400000000000000", "mastercard"], + ["5500000000000000", "mastercard"], + ["5100000000000000", "mastercard"], + ["5399999999999999", "mastercard"], + ["6011000000000000", "discover"], + ["6221260000000000", "discover"], + ["6229250000000000", "discover"], + ["6240000000000000", "discover"], + ["6269990000000000", "discover"], + ["6282000000000000", "discover"], + ["6288990000000000", "discover"], + ["6400000000000000", "discover"], + ["6500000000000000", "discover"], + ["6200000000000000", "unionpay"], + ["8100000000000000", "unionpay"], + // Valid according to Luhn number + ["2204941877211882", "mir"], + ["2720994326581252", "mastercard"], + ["374542158116607", "amex"], + ["36006666333344", "diners"], + ["3541675340715696", "jcb"], + ["3543769248058305", "jcb"], + ["4035501428146300", "cartebancaire"], + ["4111111111111111", "visa"], + ["5346755600299631", "mastercard"], + ["5495770093313616", "mastercard"], + ["5574238524540144", "mastercard"], + ["6011029459267962", "discover"], + ["6278592974938779", "unionpay"], + ["8171999927660000", "unionpay"], + ["30569309025904", "diners"], + ["38520000023237", "diners"], + ]; + for (let [value, type] of RECOGNIZED_CARDS) { + Assert.equal( + CreditCard.getType(value), + type, + `Expected ${value} to be recognized as ${type}` + ); + } + + const UNRECOGNIZED_CARDS = [ + ["411111111111111", "15 digits"], + ["41111111111111111", "17 digits"], + ["", "empty"], + ["9111111111111111", "Unknown prefix"], + ]; + for (let [value, reason] of UNRECOGNIZED_CARDS) { + Assert.equal( + CreditCard.getType(value), + null, + `Expected ${value} to not match any card because: ${reason}` + ); + } +}); + +add_task(async function test_getNetworkFromName() { + const RECOGNIZED_NAMES = [ + ["amex", "amex"], + ["American Express", "amex"], + ["american express", "amex"], + ["mastercard", "mastercard"], + ["master card", "mastercard"], + ["MasterCard", "mastercard"], + ["Master Card", "mastercard"], + ["Union Pay", "unionpay"], + ["UnionPay", "unionpay"], + ["Unionpay", "unionpay"], + ["unionpay", "unionpay"], + + ["Unknown", null], + ["", null], + ]; + for (let [value, type] of RECOGNIZED_NAMES) { + Assert.equal( + CreditCard.getNetworkFromName(value), + type, + `Expected ${value} to be recognized as ${type}` + ); + } +}); + +add_task(async function test_normalizeCardNumber() { + let testCases = [ + ["5495770093313616", "5495770093313616"], + ["5495 7700 9331 3616", "5495770093313616"], + [" 549 57700 93313 616 ", "5495770093313616"], + ["5495-7700-9331-3616", "5495770093313616"], + ["", null], + [undefined, null], + [null, null], + ]; + for (let [input, expected] of testCases) { + let actual = CreditCard.normalizeCardNumber(input); + Assert.equal( + actual, + expected, + `Expected ${input} to normalize to ${expected}` + ); + } +}); diff --git a/toolkit/modules/tests/xpcshell/test_DeferredTask.js b/toolkit/modules/tests/xpcshell/test_DeferredTask.js new file mode 100644 index 0000000000..e6c58e03db --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_DeferredTask.js @@ -0,0 +1,473 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * This file tests the DeferredTask.sys.mjs module. + */ + +// Globals + +ChromeUtils.defineESModuleGetters(this, { + DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs", +}); + +/** + * Due to the nature of this module, most of the tests are time-dependent. All + * the timeouts are designed to occur at multiples of this granularity value, + * in milliseconds, that should be high enough to prevent intermittent failures, + * but low enough to prevent an excessive overall test execution time. + */ +const T = 100; + +const originalIdleDispatch = DeferredTask.prototype._startIdleDispatch; +function replaceIdleDispatch(handleIdleDispatch) { + DeferredTask.prototype._startIdleDispatch = function (callback, timeout) { + handleIdleDispatch(callback, timeout); + }; +} +function restoreIdleDispatch() { + DeferredTask.prototype.idleDispatch = originalIdleDispatch; +} + +/** + * Waits for the specified timeout before resolving the returned promise. + */ +function promiseTimeout(aTimeoutMs) { + return new Promise(resolve => { + do_timeout(aTimeoutMs, resolve); + }); +} + +// Tests + +/** + * Creates a simple DeferredTask and executes it once. + */ +add_test(function test_arm_simple() { + new DeferredTask(run_next_test, 10).arm(); +}); + +/** + * Checks that the delay set for the task is respected. + */ +add_test(function test_arm_delay_respected() { + let executed1 = false; + let executed2 = false; + + new DeferredTask(function () { + executed1 = true; + Assert.ok(!executed2); + }, 1 * T).arm(); + + new DeferredTask(function () { + executed2 = true; + Assert.ok(executed1); + run_next_test(); + }, 2 * T).arm(); +}); + +/** + * Checks that calling "arm" again does not introduce further delay. + */ +add_test(function test_arm_delay_notrestarted() { + let executed = false; + + // Create a task that will run later. + let deferredTask = new DeferredTask(() => { + executed = true; + }, 4 * T); + deferredTask.arm(); + + // Before the task starts, call "arm" again. + do_timeout(2 * T, () => deferredTask.arm()); + + // The "arm" call should not have introduced further delays. + do_timeout(5 * T, function () { + Assert.ok(executed); + run_next_test(); + }); +}); + +/** + * Checks that a task runs only once when armed multiple times synchronously. + */ +add_test(function test_arm_coalesced() { + let executed = false; + + let deferredTask = new DeferredTask(function () { + Assert.ok(!executed); + executed = true; + run_next_test(); + }, 50); + + deferredTask.arm(); + deferredTask.arm(); +}); + +/** + * Checks that a task runs only once when armed multiple times synchronously, + * even when it has been created with a delay of zero milliseconds. + */ +add_test(function test_arm_coalesced_nodelay() { + let executed = false; + + let deferredTask = new DeferredTask(function () { + Assert.ok(!executed); + executed = true; + run_next_test(); + }, 0); + + deferredTask.arm(); + deferredTask.arm(); +}); + +/** + * Checks that a task can be armed again while running. + */ +add_test(function test_arm_recursive() { + let executed = false; + + let deferredTask = new DeferredTask(function () { + if (!executed) { + executed = true; + deferredTask.arm(); + } else { + run_next_test(); + } + }, 50); + + deferredTask.arm(); +}); + +/** + * Checks that calling "arm" while an asynchronous task is running waits until + * the task is finished before restarting the delay. + */ +add_test(function test_arm_async() { + let finishedExecution = false; + let finishedExecutionAgain = false; + + // Create a task that will run later. + let deferredTask = new DeferredTask(async function () { + await promiseTimeout(4 * T); + if (!finishedExecution) { + finishedExecution = true; + } else if (!finishedExecutionAgain) { + finishedExecutionAgain = true; + } + }, 2 * T); + deferredTask.arm(); + + // While the task is running, call "arm" again. This will result in a wait + // of 2*T until the task finishes, then another 2*T for the normal task delay + // specified on construction. + do_timeout(4 * T, function () { + Assert.ok(deferredTask.isRunning); + Assert.ok(!finishedExecution); + deferredTask.arm(); + }); + + // This will fail in case the task was started without waiting 2*T after it + // has finished. + do_timeout(7 * T, function () { + Assert.ok(!deferredTask.isRunning); + Assert.ok(finishedExecution); + }); + + // This is in the middle of the second execution. + do_timeout(10 * T, function () { + Assert.ok(deferredTask.isRunning); + Assert.ok(!finishedExecutionAgain); + }); + + // Wait enough time to verify that the task was executed as expected. + do_timeout(13 * T, function () { + Assert.ok(!deferredTask.isRunning); + Assert.ok(finishedExecutionAgain); + run_next_test(); + }); +}); + +/** + * Checks that an armed task can be disarmed. + */ +add_test(function test_disarm() { + // Create a task that will run later. + let deferredTask = new DeferredTask(function () { + do_throw("This task should not run."); + }, 2 * T); + deferredTask.arm(); + + // Disable execution later, but before the task starts. + do_timeout(1 * T, () => deferredTask.disarm()); + + // Wait enough time to verify that the task did not run. + do_timeout(3 * T, run_next_test); +}); + +/** + * Checks that calling "disarm" allows the delay to be restarted. + */ +add_test(function test_disarm_delay_restarted() { + let executed = false; + + let deferredTask = new DeferredTask(() => { + executed = true; + }, 4 * T); + deferredTask.arm(); + + do_timeout(2 * T, function () { + deferredTask.disarm(); + deferredTask.arm(); + }); + + do_timeout(5 * T, function () { + Assert.ok(!executed); + }); + + do_timeout(7 * T, function () { + Assert.ok(executed); + run_next_test(); + }); +}); + +/** + * Checks that calling "disarm" while an asynchronous task is running does not + * prevent the task to finish. + */ +add_test(function test_disarm_async() { + let finishedExecution = false; + + let deferredTask = new DeferredTask(async function () { + deferredTask.arm(); + await promiseTimeout(2 * T); + finishedExecution = true; + }, 1 * T); + deferredTask.arm(); + + do_timeout(2 * T, function () { + Assert.ok(deferredTask.isRunning); + Assert.ok(deferredTask.isArmed); + Assert.ok(!finishedExecution); + deferredTask.disarm(); + }); + + do_timeout(4 * T, function () { + Assert.ok(!deferredTask.isRunning); + Assert.ok(!deferredTask.isArmed); + Assert.ok(finishedExecution); + run_next_test(); + }); +}); + +/** + * Checks that calling "arm" immediately followed by "disarm" while an + * asynchronous task is running does not cause it to run again. + */ +add_test(function test_disarm_immediate_async() { + let executed = false; + + let deferredTask = new DeferredTask(async function () { + Assert.ok(!executed); + executed = true; + await promiseTimeout(2 * T); + }, 1 * T); + deferredTask.arm(); + + do_timeout(2 * T, function () { + Assert.ok(deferredTask.isRunning); + Assert.ok(!deferredTask.isArmed); + deferredTask.arm(); + deferredTask.disarm(); + }); + + do_timeout(4 * T, function () { + Assert.ok(executed); + Assert.ok(!deferredTask.isRunning); + Assert.ok(!deferredTask.isArmed); + run_next_test(); + }); +}); + +/** + * Checks the isArmed and isRunning properties with a synchronous task. + */ +add_test(function test_isArmed_isRunning() { + let deferredTask = new DeferredTask(function () { + Assert.ok(deferredTask.isRunning); + Assert.ok(!deferredTask.isArmed); + deferredTask.arm(); + Assert.ok(deferredTask.isArmed); + deferredTask.disarm(); + Assert.ok(!deferredTask.isArmed); + run_next_test(); + }, 50); + + Assert.ok(!deferredTask.isArmed); + deferredTask.arm(); + Assert.ok(deferredTask.isArmed); + Assert.ok(!deferredTask.isRunning); +}); + +/** + * Checks that task execution is delayed when the idle task has no deadline. + */ +add_test(function test_idle_without_deadline() { + let idleStarted = false; + let executed = false; + + // When idleDispatch is not passed a deadline/timeout, let it take a while. + replaceIdleDispatch((callback, timeout) => { + Assert.ok(!idleStarted); + idleStarted = true; + do_timeout(timeout || 2 * T, callback); + }); + + let deferredTask = new DeferredTask(function () { + Assert.ok(!executed); + executed = true; + }, 1 * T); + deferredTask.arm(); + + do_timeout(2 * T, () => { + Assert.ok(idleStarted); + Assert.ok(!executed); + }); + + do_timeout(4 * T, () => { + Assert.ok(executed); + restoreIdleDispatch(); + run_next_test(); + }); +}); + +/** + * Checks that the third parameter can be used to enforce an execution deadline. + */ +add_test(function test_idle_deadline() { + let idleStarted = false; + let executed = false; + + // Let idleDispatch wait until the deadline. + replaceIdleDispatch((callback, timeout) => { + Assert.ok(!idleStarted); + idleStarted = true; + do_timeout(timeout || 0, callback); + }); + + let deferredTask = new DeferredTask( + function () { + Assert.ok(!executed); + executed = true; + }, + 1 * T, + 2 * T + ); + deferredTask.arm(); + + // idleDispatch is expected to be called after 1 * T, + // the task is expected to be executed after 1 * T + 2 * T. + do_timeout(2 * T, () => { + Assert.ok(idleStarted); + Assert.ok(!executed); + }); + + do_timeout(4 * T, () => { + Assert.ok(executed); + restoreIdleDispatch(); + run_next_test(); + }); +}); + +/** + * Checks that the "finalize" method executes a synchronous task. + */ +add_test(function test_finalize() { + let executed = false; + let timePassed = false; + let idleStarted = false; + let finalized = false; + + // Let idleDispatch take longer. + replaceIdleDispatch((callback, timeout) => { + Assert.ok(!idleStarted); + idleStarted = true; + do_timeout(T, callback); + }); + + let deferredTask = new DeferredTask(function () { + Assert.ok(!timePassed); + executed = true; + }, 2 * T); + deferredTask.arm(); + + do_timeout(1 * T, () => { + timePassed = true; + Assert.ok(finalized); + Assert.ok(!idleStarted); + }); + + // This should trigger the immediate execution of the task. + deferredTask.finalize().then(function () { + finalized = true; + Assert.ok(executed); + }); + + // idleDispatch was originally supposed to start at 2 * T, + // so if the task did not run at T * 3, then the finalization was successful. + do_timeout(3 * T, () => { + // Because the timer was canceled, the idle task shouldn't even start. + Assert.ok(!idleStarted); + restoreIdleDispatch(); + run_next_test(); + }); +}); + +/** + * Checks that the "finalize" method executes the task again from start to + * finish in case it is already running. + */ +add_test(function test_finalize_executes_entirely() { + let executed = false; + let executedAgain = false; + let timePassed = false; + + let deferredTask = new DeferredTask(async function () { + // The first time, we arm the timer again and set up the finalization. + if (!executed) { + deferredTask.arm(); + Assert.ok(deferredTask.isArmed); + Assert.ok(deferredTask.isRunning); + + deferredTask.finalize().then(function () { + // When we reach this point, the task must be finished. + Assert.ok(executedAgain); + Assert.ok(!timePassed); + Assert.ok(!deferredTask.isArmed); + Assert.ok(!deferredTask.isRunning); + run_next_test(); + }); + + // The second execution triggered by the finalization waits 1*T for the + // current task to finish (see the timeout below), but then it must not + // wait for the 2*T specified on construction as normal task delay. The + // second execution will finish after the timeout below has passed again, + // for a total of 2*T of wait time. + do_timeout(3 * T, () => { + timePassed = true; + }); + } + + await promiseTimeout(1 * T); + + // Just before finishing, indicate if we completed the second execution. + if (executed) { + Assert.ok(deferredTask.isRunning); + executedAgain = true; + } else { + executed = true; + } + }, 2 * T); + + deferredTask.arm(); +}); diff --git a/toolkit/modules/tests/xpcshell/test_E10SUtils_getRemoteTypeForURIObject.js b/toolkit/modules/tests/xpcshell/test_E10SUtils_getRemoteTypeForURIObject.js new file mode 100644 index 0000000000..c16786d50b --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_E10SUtils_getRemoteTypeForURIObject.js @@ -0,0 +1,37 @@ +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { E10SUtils } = ChromeUtils.importESModule( + "resource://gre/modules/E10SUtils.sys.mjs" +); + +/** + * Tests for E10SUtils.getRemoteTypeForURIObject method, which is + * used to compute preferred remote process types for content given + * certain conditions. + */ + +/** + * Test that getRemoteTypeForURIObject returns the preferred remote type + * when given a URI with an invalid site origin. + * + * This is a regression test for bug 1760417. + */ +add_task(async function test_invalid_site_origin() { + const INVALID_SITE_ORIGIN_URI = Services.io.newURI( + "https://.mozilla.org/this/is/a/test.html" + ); + const EXPECTED_REMOTE_TYPE = `${E10SUtils.FISSION_WEB_REMOTE_TYPE}=https://.mozilla.org`; + let result = E10SUtils.getRemoteTypeForURIObject(INVALID_SITE_ORIGIN_URI, { + remoteSubFrames: true, + multiProcess: true, + preferredRemoteType: E10SUtils.DEFAULT_REMOTE_TYPE, + }); + Assert.equal( + result, + EXPECTED_REMOTE_TYPE, + "Got the expected default remote type." + ); +}); diff --git a/toolkit/modules/tests/xpcshell/test_EventEmitter.js b/toolkit/modules/tests/xpcshell/test_EventEmitter.js new file mode 100644 index 0000000000..376246f559 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_EventEmitter.js @@ -0,0 +1,162 @@ +/* Any copyright do_check_eq dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { EventEmitter } = ChromeUtils.importESModule( + "resource://gre/modules/EventEmitter.sys.mjs" +); + +add_task(async function test_extractFiles() { + testEmitter(new EventEmitter()); + + let decorated = {}; + EventEmitter.decorate(decorated); + testEmitter(decorated); + + await testPromise(); +}); + +function testEmitter(emitter) { + Assert.ok(emitter, "We have an event emitter"); + + let beenHere1 = false; + let beenHere2 = false; + + emitter.on("next", next); + emitter.emit("next", "abc", "def"); + + function next(eventName, str1, str2) { + Assert.equal(eventName, "next", "Got event"); + Assert.equal(str1, "abc", "Argument 1 do_check_eq correct"); + Assert.equal(str2, "def", "Argument 2 do_check_eq correct"); + + Assert.ok(!beenHere1, "first time in next callback"); + beenHere1 = true; + + emitter.off("next", next); + + emitter.emit("next"); + + emitter.once("onlyonce", onlyOnce); + + emitter.emit("onlyonce"); + emitter.emit("onlyonce"); + } + + function onlyOnce() { + Assert.ok(!beenHere2, '"once" listener has been called once'); + beenHere2 = true; + emitter.emit("onlyonce"); + + testThrowingExceptionInListener(); + } + + function testThrowingExceptionInListener() { + function throwListener() { + emitter.off("throw-exception"); + // eslint-disable-next-line no-throw-literal + throw { + toString: () => "foo", + stack: "bar", + }; + } + + emitter.on("throw-exception", throwListener); + emitter.emit("throw-exception"); + + killItWhileEmitting(); + } + + function killItWhileEmitting() { + function c1() { + Assert.ok(true, "c1 called"); + } + function c2() { + Assert.ok(true, "c2 called"); + emitter.off("tick", c3); + } + function c3() { + Assert.ok(false, "c3 should not be called"); + } + function c4() { + Assert.ok(true, "c4 called"); + } + + emitter.on("tick", c1); + emitter.on("tick", c2); + emitter.on("tick", c3); + emitter.on("tick", c4); + + emitter.emit("tick"); + + offAfterOnce(); + } + + function offAfterOnce() { + let enteredC1 = false; + + function c1() { + enteredC1 = true; + } + + emitter.once("oao", c1); + emitter.off("oao", c1); + + emitter.emit("oao"); + + Assert.ok(!enteredC1, "c1 should not be called"); + } +} + +function testPromise() { + let emitter = new EventEmitter(); + let p = emitter.once("thing"); + + // Check that the promise do_check_eq only resolved once event though we + // emit("thing") more than once + let firstCallbackCalled = false; + let check1 = p.then(arg => { + Assert.equal(firstCallbackCalled, false, "first callback called only once"); + firstCallbackCalled = true; + Assert.equal(arg, "happened", "correct arg in promise"); + return "rval from c1"; + }); + + emitter.emit("thing", "happened", "ignored"); + + // Check that the promise do_check_eq resolved asynchronously + let secondCallbackCalled = false; + let check2 = p.then(arg => { + Assert.ok(true, "second callback called"); + Assert.equal(arg, "happened", "correct arg in promise"); + secondCallbackCalled = true; + Assert.equal(arg, "happened", "correct arg in promise (a second time)"); + return "rval from c2"; + }); + + // Shouldn't call any of the above listeners + emitter.emit("thing", "trashinate"); + + // Check that we can still separate events with different names + // and that it works with no parameters + let pfoo = emitter.once("foo"); + let pbar = emitter.once("bar"); + + let check3 = pfoo.then(arg => { + Assert.equal(arg, undefined, "no arg for foo event"); + return "rval from c3"; + }); + + pbar.then(() => { + Assert.ok(false, "pbar should not be called"); + }); + + emitter.emit("foo"); + + Assert.equal(secondCallbackCalled, false, "second callback not called yet"); + + return Promise.all([check1, check2, check3]).then(args => { + Assert.equal(args[0], "rval from c1", "callback 1 done good"); + Assert.equal(args[1], "rval from c2", "callback 2 done good"); + Assert.equal(args[2], "rval from c3", "callback 3 done good"); + }); +} diff --git a/toolkit/modules/tests/xpcshell/test_FileUtils.js b/toolkit/modules/tests/xpcshell/test_FileUtils.js new file mode 100644 index 0000000000..2fdf1a1edb --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_FileUtils.js @@ -0,0 +1,199 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { FileUtils } = ChromeUtils.importESModule( + "resource://gre/modules/FileUtils.sys.mjs" +); + +function do_check_throws(f, result, stack) { + if (!stack) { + stack = Components.stack.caller; + } + + try { + f(); + } catch (exc) { + if (exc.result == result) { + return; + } + do_throw("expected result " + result + ", caught " + exc, stack); + } + do_throw("expected result " + result + ", none thrown", stack); +} + +const gProfD = do_get_profile(); + +add_test(function test_getDir() { + let dir = FileUtils.getDir("ProfD", ["foodir"]); + Assert.ok(dir instanceof Ci.nsIFile); + Assert.ok(!dir.exists()); + + let other = gProfD.clone(); + other.append("foodir"); + Assert.ok(dir.equals(other)); + + run_next_test(); +}); + +add_test(function test_getDir_nonexistentDir() { + do_check_throws(function () { + FileUtils.getDir("NonexistentD", ["foodir"]); + }, Cr.NS_ERROR_FAILURE); + + run_next_test(); +}); + +add_test(function test_getDir_shouldCreate() { + let dir = FileUtils.getDir("ProfD", ["c", "d", "foodir"]); + dir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); + Assert.ok(dir instanceof Ci.nsIFile); + Assert.ok(dir.exists()); + + let other = gProfD.clone(); + other.append("c"); + Assert.ok(other.isDirectory()); + other.append("d"); + Assert.ok(other.isDirectory()); + other.append("foodir"); + Assert.ok(dir.equals(other)); + + run_next_test(); +}); + +var openFileOutputStream_defaultFlags = function (aKind, aFileName) { + let file = new FileUtils.File( + PathUtils.join(PathUtils.profileDir, aFileName) + ); + let fos; + Assert.ok(aKind == "atomic" || aKind == "safe" || aKind == ""); + if (aKind == "atomic") { + fos = FileUtils.openAtomicFileOutputStream(file); + } else if (aKind == "safe") { + fos = FileUtils.openSafeFileOutputStream(file); + } else { + fos = FileUtils.openFileOutputStream(file); + } + Assert.ok(fos instanceof Ci.nsIFileOutputStream); + if (aKind == "atomic" || aKind == "safe") { + Assert.ok(fos instanceof Ci.nsISafeOutputStream); + } + + // FileUtils.openFileOutputStream or FileUtils.openAtomicFileOutputStream() + // or FileUtils.openSafeFileOutputStream() opens the stream with DEFER_OPEN + // which means the file will not be open until we write to it. + Assert.ok(!file.exists()); + + let data = "test_default_flags"; + fos.write(data, data.length); + Assert.ok(file.exists()); + + // No nsIXULRuntime in xpcshell, so use this trick to determine whether we're + // on Windows. + if ("@mozilla.org/windows-registry-key;1" in Cc) { + Assert.equal(file.permissions, 0o666); + } else { + Assert.equal(file.permissions, FileUtils.PERMS_FILE); + } + + run_next_test(); +}; + +var openFileOutputStream_modeFlags = function (aKind, aFileName) { + let file = new FileUtils.File( + PathUtils.join(PathUtils.profileDir, aFileName) + ); + let fos; + Assert.ok(aKind == "atomic" || aKind == "safe" || aKind == ""); + if (aKind == "atomic") { + fos = FileUtils.openAtomicFileOutputStream(file, FileUtils.MODE_WRONLY); + } else if (aKind == "safe") { + fos = FileUtils.openSafeFileOutputStream(file, FileUtils.MODE_WRONLY); + } else { + fos = FileUtils.openFileOutputStream(file, FileUtils.MODE_WRONLY); + } + let data = "test_modeFlags"; + do_check_throws(function () { + fos.write(data, data.length); + }, Cr.NS_ERROR_FILE_NOT_FOUND); + Assert.ok(!file.exists()); + + run_next_test(); +}; + +var closeFileOutputStream = function (aKind, aFileName) { + let file = new FileUtils.File( + PathUtils.join(PathUtils.profileDir, aFileName) + ); + let fos; + Assert.ok(aKind == "atomic" || aKind == "safe"); + if (aKind == "atomic") { + fos = FileUtils.openAtomicFileOutputStream(file); + } else if (aKind == "safe") { + fos = FileUtils.openSafeFileOutputStream(file); + } + + // We can write data to the stream just fine while it's open. + let data = "testClose"; + fos.write(data, data.length); + + // But once we close it, we can't anymore. + if (aKind == "atomic") { + FileUtils.closeAtomicFileOutputStream(fos); + } else if (aKind == "safe") { + FileUtils.closeSafeFileOutputStream(fos); + } + do_check_throws(function () { + fos.write(data, data.length); + }, Cr.NS_BASE_STREAM_CLOSED); + run_next_test(); +}; + +add_test(function test_openFileOutputStream_defaultFlags() { + openFileOutputStream_defaultFlags("", "george"); +}); + +// openFileOutputStream will uses MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE +// as the default mode flags, but we can pass in our own if we want to. +add_test(function test_openFileOutputStream_modeFlags() { + openFileOutputStream_modeFlags("", "ringo"); +}); + +add_test(function test_openAtomicFileOutputStream_defaultFlags() { + openFileOutputStream_defaultFlags("atomic", "peiyong"); +}); + +// openAtomicFileOutputStream will uses MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE +// as the default mode flags, but we can pass in our own if we want to. +add_test(function test_openAtomicFileOutputStream_modeFlags() { + openFileOutputStream_modeFlags("atomic", "lin"); +}); + +add_test(function test_closeAtomicFileOutputStream() { + closeFileOutputStream("atomic", "peiyonglin"); +}); + +add_test(function test_openSafeFileOutputStream_defaultFlags() { + openFileOutputStream_defaultFlags("safe", "john"); +}); + +// openSafeFileOutputStream will uses MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE +// as the default mode flags, but we can pass in our own if we want to. +add_test(function test_openSafeFileOutputStream_modeFlags() { + openFileOutputStream_modeFlags("safe", "paul"); +}); + +add_test(function test_closeSafeFileOutputStream() { + closeFileOutputStream("safe", "georgee"); +}); + +add_test(function test_newFile() { + let testfile = new FileUtils.File( + PathUtils.join(PathUtils.profileDir, "test") + ); + let testpath = testfile.path; + let file = new FileUtils.File(testpath); + Assert.ok(file instanceof Ci.nsIFile); + Assert.ok(file.equals(testfile)); + Assert.equal(file.path, testpath); + run_next_test(); +}); diff --git a/toolkit/modules/tests/xpcshell/test_FinderIterator.js b/toolkit/modules/tests/xpcshell/test_FinderIterator.js new file mode 100644 index 0000000000..fcbe97ba5f --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_FinderIterator.js @@ -0,0 +1,444 @@ +const { FinderIterator } = ChromeUtils.importESModule( + "resource://gre/modules/FinderIterator.sys.mjs" +); + +let finderIterator = new FinderIterator(); + +var gFindResults = []; +// Stub the method that instantiates nsIFind and does all the interaction with +// the docShell to be searched through. +finderIterator._iterateDocument = function* (word, window, finder) { + for (let range of gFindResults) { + yield range; + } +}; + +finderIterator._rangeStartsInLink = fakeRange => fakeRange.startsInLink; + +function FakeRange(textContent, startsInLink = false) { + this.startContainer = {}; + this.startsInLink = startsInLink; + this.toString = () => textContent; +} + +var gMockWindow = { + setTimeout(cb, delay) { + Cc["@mozilla.org/timer;1"] + .createInstance(Ci.nsITimer) + .initWithCallback(cb, delay, Ci.nsITimer.TYPE_ONE_SHOT); + }, +}; + +var gMockFinder = { + _getWindow() { + return gMockWindow; + }, +}; + +function prepareIterator(findText, rangeCount) { + gFindResults = []; + for (let i = rangeCount; --i >= 0; ) { + gFindResults.push(new FakeRange(findText)); + } +} + +add_task(async function test_start() { + let findText = "test"; + let rangeCount = 300; + prepareIterator(findText, rangeCount); + + let count = 0; + await finderIterator.start({ + caseSensitive: false, + entireWord: false, + finder: gMockFinder, + listener: { + onIteratorRangeFound(range) { + ++count; + Assert.equal(range.toString(), findText, "Text content should match"); + }, + }, + matchDiacritics: false, + word: findText, + }); + + Assert.equal(rangeCount, count, "Amount of ranges yielded should match!"); + Assert.ok(!finderIterator.running, "Running state should match"); + Assert.equal( + finderIterator._previousRanges.length, + rangeCount, + "Ranges cache should match" + ); + + finderIterator.reset(); +}); + +add_task(async function test_subframes() { + let findText = "test"; + let rangeCount = 300; + prepareIterator(findText, rangeCount); + + let count = 0; + await finderIterator.start({ + caseSensitive: false, + entireWord: false, + finder: gMockFinder, + listener: { + onIteratorRangeFound(range) { + ++count; + Assert.equal(range.toString(), findText, "Text content should match"); + }, + }, + matchDiacritics: false, + word: findText, + useSubFrames: true, + }); + + Assert.equal(rangeCount, count, "Amount of ranges yielded should match!"); + Assert.ok(!finderIterator.running, "Running state should match"); + Assert.equal( + finderIterator._previousRanges.length, + rangeCount, + "Ranges cache should match" + ); + + finderIterator.reset(); +}); + +add_task(async function test_valid_arguments() { + let findText = "foo"; + let rangeCount = 20; + prepareIterator(findText, rangeCount); + + let count = 0; + + await finderIterator.start({ + caseSensitive: false, + entireWord: false, + finder: gMockFinder, + listener: { + onIteratorRangeFound(range) { + ++count; + }, + }, + matchDiacritics: false, + word: findText, + }); + + let params = finderIterator._previousParams; + Assert.ok(!params.linksOnly, "Default for linksOnly is false"); + Assert.ok(!params.useCache, "Default for useCache is false"); + Assert.equal(params.word, findText, "Words should match"); + + count = 0; + Assert.throws( + () => + finderIterator.start({ + entireWord: false, + listener: { + onIteratorRangeFound(range) { + ++count; + }, + }, + matchDiacritics: false, + word: findText, + }), + /Missing required option 'caseSensitive'/, + "Should throw when missing an argument" + ); + finderIterator.reset(); + + Assert.throws( + () => + finderIterator.start({ + caseSensitive: false, + entireWord: false, + listener: { + onIteratorRangeFound(range) { + ++count; + }, + }, + word: findText, + }), + /Missing required option 'matchDiacritics'/, + "Should throw when missing an argument" + ); + finderIterator.reset(); + + Assert.throws( + () => + finderIterator.start({ + caseSensitive: false, + listener: { + onIteratorRangeFound(range) { + ++count; + }, + }, + matchDiacritics: false, + word: findText, + }), + /Missing required option 'entireWord'/, + "Should throw when missing an argument" + ); + finderIterator.reset(); + + Assert.throws( + () => + finderIterator.start({ + caseSensitive: false, + entireWord: false, + listener: { + onIteratorRangeFound(range) { + ++count; + }, + }, + matchDiacritics: false, + word: findText, + }), + /Missing required option 'finder'/, + "Should throw when missing an argument" + ); + finderIterator.reset(); + + Assert.throws( + () => + finderIterator.start({ + caseSensitive: true, + entireWord: false, + finder: gMockFinder, + matchDiacritics: false, + word: findText, + }), + /Missing valid, required option 'listener'/, + "Should throw when missing an argument" + ); + finderIterator.reset(); + + Assert.throws( + () => + finderIterator.start({ + caseSensitive: false, + entireWord: true, + finder: gMockFinder, + listener: { + onIteratorRangeFound(range) { + ++count; + }, + }, + matchDiacritics: false, + }), + /Missing required option 'word'/, + "Should throw when missing an argument" + ); + finderIterator.reset(); + + Assert.equal(count, 0, "No ranges should've been counted"); +}); + +add_task(async function test_stop() { + let findText = "bar"; + let rangeCount = 120; + prepareIterator(findText, rangeCount); + + let count = 0; + let whenDone = finderIterator.start({ + caseSensitive: false, + entireWord: false, + finder: gMockFinder, + listener: { + onIteratorRangeFound(range) { + ++count; + }, + }, + matchDiacritics: false, + word: findText, + }); + + finderIterator.stop(); + + await whenDone; + + Assert.equal(count, 0, "Number of ranges should be 0"); + + finderIterator.reset(); +}); + +add_task(async function test_reset() { + let findText = "tik"; + let rangeCount = 142; + prepareIterator(findText, rangeCount); + + let count = 0; + let whenDone = finderIterator.start({ + caseSensitive: false, + entireWord: false, + finder: gMockFinder, + listener: { + onIteratorRangeFound(range) { + ++count; + }, + }, + matchDiacritics: false, + word: findText, + }); + + Assert.ok(finderIterator.running, "Yup, running we are"); + Assert.equal(count, 0, "Number of ranges should match 0"); + Assert.equal( + finderIterator.ranges.length, + 0, + "Number of ranges should match 0" + ); + + finderIterator.reset(); + + Assert.ok(!finderIterator.running, "Nope, running we are not"); + Assert.equal(finderIterator.ranges.length, 0, "No ranges after reset"); + Assert.equal( + finderIterator._previousRanges.length, + 0, + "No ranges after reset" + ); + + await whenDone; + + Assert.equal(count, 0, "Number of ranges should match 0"); +}); + +add_task(async function test_parallel_starts() { + let findText = "tak"; + let rangeCount = 2143; + prepareIterator(findText, rangeCount); + + // Start off the iterator. + let count = 0; + let whenDone = finderIterator.start({ + caseSensitive: false, + entireWord: false, + finder: gMockFinder, + listener: { + onIteratorRangeFound(range) { + ++count; + }, + }, + matchDiacritics: false, + word: findText, + }); + + await new Promise(resolve => gMockWindow.setTimeout(resolve, 100)); + Assert.ok(finderIterator.running, "We ought to be running here"); + + let count2 = 0; + let whenDone2 = finderIterator.start({ + caseSensitive: false, + entireWord: false, + finder: gMockFinder, + listener: { + onIteratorRangeFound(range) { + ++count2; + }, + }, + matchDiacritics: false, + word: findText, + }); + + // Let the iterator run for a little while longer before we assert the world. + await new Promise(resolve => gMockWindow.setTimeout(resolve, 10)); + finderIterator.stop(); + + Assert.ok(!finderIterator.running, "Stop means stop"); + + await whenDone; + await whenDone2; + + Assert.greater( + count, + finderIterator.kIterationSizeMax, + "At least one range should've been found" + ); + Assert.less(count, rangeCount, "Not all ranges should've been found"); + Assert.greater( + count2, + finderIterator.kIterationSizeMax, + "At least one range should've been found" + ); + Assert.less(count2, rangeCount, "Not all ranges should've been found"); + + Assert.equal( + count2, + count, + "The second start was later, but should have caught up" + ); + + finderIterator.reset(); +}); + +add_task(async function test_allowDistance() { + let findText = "gup"; + let rangeCount = 20; + prepareIterator(findText, rangeCount); + + // Start off the iterator. + let count = 0; + let whenDone = finderIterator.start({ + caseSensitive: false, + entireWord: false, + finder: gMockFinder, + listener: { + onIteratorRangeFound(range) { + ++count; + }, + }, + matchDiacritics: false, + word: findText, + }); + + let count2 = 0; + let whenDone2 = finderIterator.start({ + caseSensitive: false, + entireWord: false, + finder: gMockFinder, + listener: { + onIteratorRangeFound(range) { + ++count2; + }, + }, + matchDiacritics: false, + word: "gu", + }); + + let count3 = 0; + let whenDone3 = finderIterator.start({ + allowDistance: 1, + caseSensitive: false, + entireWord: false, + finder: gMockFinder, + listener: { + onIteratorRangeFound(range) { + ++count3; + }, + }, + matchDiacritics: false, + word: "gu", + }); + + await Promise.all([whenDone, whenDone2, whenDone3]); + + Assert.equal( + count, + rangeCount, + "The first iterator invocation should yield all results" + ); + Assert.equal( + count2, + 0, + "The second iterator invocation should yield _no_ results" + ); + Assert.equal( + count3, + rangeCount, + "The first iterator invocation should yield all results" + ); + + finderIterator.reset(); +}); diff --git a/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js b/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js new file mode 100644 index 0000000000..8f725fc78d --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js @@ -0,0 +1,1708 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ + +const URL_HOST = "http://localhost"; +const PR_USEC_PER_MSEC = 1000; + +const { GMPExtractor, GMPInstallManager } = ChromeUtils.importESModule( + "resource://gre/modules/GMPInstallManager.sys.mjs" +); +const { setTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); +const { FileUtils } = ChromeUtils.importESModule( + "resource://gre/modules/FileUtils.sys.mjs" +); +const { HttpServer } = ChromeUtils.importESModule( + "resource://testing-common/httpd.sys.mjs" +); +const { Preferences } = ChromeUtils.importESModule( + "resource://gre/modules/Preferences.sys.mjs" +); +const { TelemetryTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TelemetryTestUtils.sys.mjs" +); +const { UpdateUtils } = ChromeUtils.importESModule( + "resource://gre/modules/UpdateUtils.sys.mjs" +); +const { GMPPrefs, OPEN_H264_ID } = ChromeUtils.importESModule( + "resource://gre/modules/GMPUtils.sys.mjs" +); +const { ProductAddonCheckerTestUtils } = ChromeUtils.importESModule( + "resource://gre/modules/addons/ProductAddonChecker.sys.mjs" +); +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); + +Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true); +Services.prefs.setBoolPref("media.gmp-manager.updateEnabled", true); +// Gather the telemetry even where the probes don't exist (i.e. Thunderbird). +Services.prefs.setBoolPref( + "toolkit.telemetry.testing.overrideProductsCheck", + true +); +registerCleanupFunction(() => { + Services.prefs.clearUserPref("security.allow_eval_with_system_principal"); + Services.prefs.clearUserPref("media.gmp-manager.updateEnabled"); + Services.prefs.clearUserPref( + "toolkit.telemetry.testing.overrideProductsCheck" + ); +}); +// Most tests do no handle the machinery for content signatures, so let +// specific tests that need it turn it on as needed. +Preferences.set("media.gmp-manager.checkContentSignature", false); + +do_get_profile(); + +add_task(function test_setup() { + // We should already have a profile from `do_get_profile`, but also need + // FOG to be setup for tests that touch it/Glean. + Services.fog.initializeFOG(); +}); + +function run_test() { + Preferences.set("media.gmp.log.dump", true); + Preferences.set("media.gmp.log.level", 0); + run_next_test(); +} + +/** + * Tests that the helper used for preferences works correctly + */ +add_task(async function test_prefs() { + let addon1 = "addon1", + addon2 = "addon2"; + + GMPPrefs.setString(GMPPrefs.KEY_URL, "http://not-really-used"); + GMPPrefs.setString(GMPPrefs.KEY_URL_OVERRIDE, "http://not-really-used-2"); + GMPPrefs.setInt(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, 1, addon1); + GMPPrefs.setString(GMPPrefs.KEY_PLUGIN_VERSION, "2", addon1); + GMPPrefs.setInt(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, 3, addon2); + GMPPrefs.setInt(GMPPrefs.KEY_PLUGIN_VERSION, 4, addon2); + GMPPrefs.setBool(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, false, addon2); + GMPPrefs.setBool(GMPPrefs.KEY_CERT_CHECKATTRS, true); + GMPPrefs.setString(GMPPrefs.KEY_PLUGIN_HASHVALUE, "5", addon1); + + Assert.equal(GMPPrefs.getString(GMPPrefs.KEY_URL), "http://not-really-used"); + Assert.equal( + GMPPrefs.getString(GMPPrefs.KEY_URL_OVERRIDE), + "http://not-really-used-2" + ); + Assert.equal(GMPPrefs.getInt(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, "", addon1), 1); + Assert.equal( + GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_VERSION, "", addon1), + "2" + ); + Assert.equal(GMPPrefs.getInt(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, "", addon2), 3); + Assert.equal(GMPPrefs.getInt(GMPPrefs.KEY_PLUGIN_VERSION, "", addon2), 4); + Assert.equal( + GMPPrefs.getBool(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, undefined, addon2), + false + ); + Assert.ok(GMPPrefs.getBool(GMPPrefs.KEY_CERT_CHECKATTRS)); + GMPPrefs.setBool(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, true, addon2); + Assert.equal( + GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_HASHVALUE, "", addon1), + "5" + ); +}); + +/** + * Tests that an uninit without a check works fine + */ +add_task(async function test_checkForAddons_uninitWithoutCheck() { + let installManager = new GMPInstallManager(); + installManager.uninit(); +}); + +/** + * Tests that an uninit without an install works fine + */ +add_test(function test_checkForAddons_uninitWithoutInstall() { + let myRequest = new mockRequest(200, ""); + let installManager = new GMPInstallManager(); + let promise = ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.checkForAddons() + ); + promise.then(res => { + Assert.equal(res.addons.length, 2); + for (let addon of res.addons) { + Assert.ok(addon.usedFallback); + } + installManager.uninit(); + run_next_test(); + }); +}); + +/** + * Tests that no response returned rejects + */ +add_test(function test_checkForAddons_noResponse() { + let myRequest = new mockRequest(200, ""); + let installManager = new GMPInstallManager(); + let promise = ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.checkForAddons() + ); + promise.then(res => { + Assert.equal(res.addons.length, 2); + for (let addon of res.addons) { + Assert.ok(addon.usedFallback); + } + installManager.uninit(); + run_next_test(); + }); +}); + +/** + * Tests that no addons element returned resolves with no addons + */ +add_task(async function test_checkForAddons_noAddonsElement() { + let myRequest = new mockRequest(200, "<updates></updates>"); + let installManager = new GMPInstallManager(); + let res = await ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.checkForAddons() + ); + Assert.equal(res.addons.length, 0); + installManager.uninit(); +}); + +/** + * Tests that empty addons element returned resolves with no addons + */ +add_task(async function test_checkForAddons_emptyAddonsElement() { + let myRequest = new mockRequest(200, "<updates><addons/></updates>"); + let installManager = new GMPInstallManager(); + let res = await ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.checkForAddons() + ); + Assert.equal(res.addons.length, 0); + installManager.uninit(); +}); + +/** + * Tests that a response with the wrong root element rejects + */ +add_test(function test_checkForAddons_wrongResponseXML() { + let myRequest = new mockRequest( + 200, + "<digits_of_pi>3.141592653589793....</digits_of_pi>" + ); + let installManager = new GMPInstallManager(); + let promise = ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.checkForAddons() + ); + promise.then(res => { + Assert.equal(res.addons.length, 2); + for (let addon of res.addons) { + Assert.ok(addon.usedFallback); + } + installManager.uninit(); + run_next_test(); + }); +}); + +/** + * Tests that a 404 error works as expected + */ +add_test(function test_checkForAddons_404Error() { + let myRequest = new mockRequest(404, ""); + let installManager = new GMPInstallManager(); + let promise = ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.checkForAddons() + ); + promise.then(res => { + Assert.equal(res.addons.length, 2); + for (let addon of res.addons) { + Assert.ok(addon.usedFallback); + } + installManager.uninit(); + run_next_test(); + }); +}); + +/** + * Tests that a xhr/ServiceRequest abort() works as expected + */ +add_test(function test_checkForAddons_abort() { + let overriddenServiceRequest = new mockRequest(200, "", { + dropRequest: true, + }); + let installManager = new GMPInstallManager(); + let promise = ProductAddonCheckerTestUtils.overrideServiceRequest( + overriddenServiceRequest, + () => installManager.checkForAddons() + ); + + // Since the ServiceRequest is created in checkForAddons asynchronously, + // we need to delay aborting till the request is running. + // Since checkForAddons returns a Promise already only after + // the abort is triggered, we can't use that, and instead + // we'll use a fake timer. + setTimeout(() => { + overriddenServiceRequest.abort(); + }, 100); + + promise.then(res => { + Assert.equal(res.addons.length, 2); + for (let addon of res.addons) { + Assert.ok(addon.usedFallback); + } + installManager.uninit(); + run_next_test(); + }); +}); + +/** + * Tests that a defensive timeout works as expected + */ +add_test(function test_checkForAddons_timeout() { + let myRequest = new mockRequest(200, "", { + dropRequest: true, + timeout: true, + }); + let installManager = new GMPInstallManager(); + let promise = ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.checkForAddons() + ); + promise.then(res => { + Assert.equal(res.addons.length, 2); + for (let addon of res.addons) { + Assert.ok(addon.usedFallback); + } + installManager.uninit(); + run_next_test(); + }); +}); + +/** + * Tests that we throw correctly in case of ssl certification error. + */ +add_test(function test_checkForAddons_bad_ssl() { + // + // Add random stuff that cause CertUtil to require https. + // + let PREF_KEY_URL_OVERRIDE_BACKUP = Preferences.get( + GMPPrefs.KEY_URL_OVERRIDE, + "" + ); + Preferences.reset(GMPPrefs.KEY_URL_OVERRIDE); + + let CERTS_BRANCH_DOT_ONE = GMPPrefs.KEY_CERTS_BRANCH + ".1"; + let PREF_CERTS_BRANCH_DOT_ONE_BACKUP = Preferences.get( + CERTS_BRANCH_DOT_ONE, + "" + ); + Services.prefs.setCharPref(CERTS_BRANCH_DOT_ONE, "funky value"); + + let myRequest = new mockRequest(200, ""); + let installManager = new GMPInstallManager(); + let promise = ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.checkForAddons() + ); + promise.then(res => { + Assert.equal(res.addons.length, 2); + for (let addon of res.addons) { + Assert.ok(addon.usedFallback); + } + installManager.uninit(); + if (PREF_KEY_URL_OVERRIDE_BACKUP) { + Preferences.set(GMPPrefs.KEY_URL_OVERRIDE, PREF_KEY_URL_OVERRIDE_BACKUP); + } + if (PREF_CERTS_BRANCH_DOT_ONE_BACKUP) { + Preferences.set(CERTS_BRANCH_DOT_ONE, PREF_CERTS_BRANCH_DOT_ONE_BACKUP); + } + run_next_test(); + }); +}); + +/** + * Tests that gettinga a funky non XML response works as expected + */ +add_test(function test_checkForAddons_notXML() { + let myRequest = new mockRequest(200, "3.141592653589793...."); + let installManager = new GMPInstallManager(); + let promise = ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.checkForAddons() + ); + + promise.then(res => { + Assert.equal(res.addons.length, 2); + for (let addon of res.addons) { + Assert.ok(addon.usedFallback); + } + installManager.uninit(); + run_next_test(); + }); +}); + +/** + * Tests that getting a response with a single addon works as expected + */ +add_task(async function test_checkForAddons_singleAddon() { + let responseXML = + '<?xml version="1.0"?>' + + "<updates>" + + " <addons>" + + ' <addon id="gmp-gmpopenh264"' + + ' URL="http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip"' + + ' hashFunction="sha256"' + + ' hashValue="1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' + + ' version="1.1"/>' + + " </addons>" + + "</updates>"; + let myRequest = new mockRequest(200, responseXML); + let installManager = new GMPInstallManager(); + let res = await ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.checkForAddons() + ); + Assert.equal(res.addons.length, 1); + let gmpAddon = res.addons[0]; + Assert.equal(gmpAddon.id, "gmp-gmpopenh264"); + Assert.equal(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip"); + Assert.equal(gmpAddon.hashFunction, "sha256"); + Assert.equal( + gmpAddon.hashValue, + "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee" + ); + Assert.equal(gmpAddon.version, "1.1"); + Assert.ok(gmpAddon.isValid); + Assert.ok(!gmpAddon.isInstalled); + installManager.uninit(); +}); + +/** + * Tests that getting a response with a single addon with the optional size + * attribute parses as expected. + */ +add_task(async function test_checkForAddons_singleAddonWithSize() { + let responseXML = + '<?xml version="1.0"?>' + + "<updates>" + + " <addons>" + + ' <addon id="openh264-plugin-no-at-symbol"' + + ' URL="http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip"' + + ' hashFunction="sha256"' + + ' size="42"' + + ' hashValue="1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' + + ' version="1.1"/>' + + " </addons>" + + "</updates>"; + let myRequest = new mockRequest(200, responseXML); + let installManager = new GMPInstallManager(); + let res = await ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.checkForAddons() + ); + Assert.equal(res.addons.length, 1); + let gmpAddon = res.addons[0]; + Assert.equal(gmpAddon.id, "openh264-plugin-no-at-symbol"); + Assert.equal(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip"); + Assert.equal(gmpAddon.hashFunction, "sha256"); + Assert.equal( + gmpAddon.hashValue, + "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee" + ); + Assert.equal(gmpAddon.size, 42); + Assert.equal(gmpAddon.version, "1.1"); + Assert.ok(gmpAddon.isValid); + Assert.ok(!gmpAddon.isInstalled); + installManager.uninit(); +}); + +/** + * Tests that checking for multiple addons work correctly. + * Also tests that invalid addons work correctly. + */ +add_task( + async function test_checkForAddons_multipleAddonNoUpdatesSomeInvalid() { + let responseXML = + '<?xml version="1.0"?>' + + "<updates>" + + " <addons>" + + // valid openh264 + ' <addon id="gmp-gmpopenh264"' + + ' URL="http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip"' + + ' hashFunction="sha256"' + + ' hashValue="1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' + + ' version="1.1"/>' + + // valid not openh264 + ' <addon id="NOT-gmp-gmpopenh264"' + + ' URL="http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip"' + + ' hashFunction="sha512"' + + ' hashValue="141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' + + ' version="9.1"/>' + + // noid + ' <addon notid="NOT-gmp-gmpopenh264"' + + ' URL="http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip"' + + ' hashFunction="sha512"' + + ' hashValue="141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' + + ' version="9.1"/>' + + // no URL + ' <addon id="NOT-gmp-gmpopenh264"' + + ' notURL="http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip"' + + ' hashFunction="sha512"' + + ' hashValue="141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' + + ' version="9.1"/>' + + // no hash function + ' <addon id="NOT-gmp-gmpopenh264"' + + ' URL="http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip"' + + ' nothashFunction="sha512"' + + ' hashValue="141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' + + ' version="9.1"/>' + + // no hash function + ' <addon id="NOT-gmp-gmpopenh264"' + + ' URL="http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip"' + + ' hashFunction="sha512"' + + ' nothashValue="141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' + + ' version="9.1"/>' + + // not version + ' <addon id="NOT-gmp-gmpopenh264"' + + ' URL="http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip"' + + ' hashFunction="sha512"' + + ' hashValue="141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' + + ' notversion="9.1"/>' + + " </addons>" + + "</updates>"; + let myRequest = new mockRequest(200, responseXML); + let installManager = new GMPInstallManager(); + let res = await ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.checkForAddons() + ); + Assert.equal(res.addons.length, 7); + let gmpAddon = res.addons[0]; + Assert.equal(gmpAddon.id, "gmp-gmpopenh264"); + Assert.equal(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip"); + Assert.equal(gmpAddon.hashFunction, "sha256"); + Assert.equal( + gmpAddon.hashValue, + "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee" + ); + Assert.equal(gmpAddon.version, "1.1"); + Assert.ok(gmpAddon.isValid); + Assert.ok(!gmpAddon.isInstalled); + + gmpAddon = res.addons[1]; + Assert.equal(gmpAddon.id, "NOT-gmp-gmpopenh264"); + Assert.equal( + gmpAddon.URL, + "http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip" + ); + Assert.equal(gmpAddon.hashFunction, "sha512"); + Assert.equal( + gmpAddon.hashValue, + "141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee" + ); + Assert.equal(gmpAddon.version, "9.1"); + Assert.ok(gmpAddon.isValid); + Assert.ok(!gmpAddon.isInstalled); + + for (let i = 2; i < res.addons.length; i++) { + Assert.ok(!res.addons[i].isValid); + Assert.ok(!res.addons[i].isInstalled); + } + installManager.uninit(); + } +); + +/** + * Tests that checking for addons when there are also updates available + * works as expected. + */ +add_task(async function test_checkForAddons_updatesWithAddons() { + let responseXML = + '<?xml version="1.0"?>' + + " <updates>" + + ' <update type="minor" displayVersion="33.0a1" appVersion="33.0a1" platformVersion="33.0a1" buildID="20140628030201">' + + ' <patch type="complete" URL="http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/2014/06/2014-06-28-03-02-01-mozilla-central/firefox-33.0a1.en-US.mac.complete.mar" hashFunction="sha512" hashValue="f3f90d71dff03ae81def80e64bba3e4569da99c9e15269f731c2b167c4fc30b3aed9f5fee81c19614120230ca333e73a5e7def1b8e45d03135b2069c26736219" size="85249896"/>' + + " </update>" + + " <addons>" + + ' <addon id="gmp-gmpopenh264"' + + ' URL="http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip"' + + ' hashFunction="sha256"' + + ' hashValue="1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' + + ' version="1.1"/>' + + " </addons>" + + "</updates>"; + let myRequest = new mockRequest(200, responseXML); + let installManager = new GMPInstallManager(); + let res = await ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.checkForAddons() + ); + Assert.equal(res.addons.length, 1); + let gmpAddon = res.addons[0]; + Assert.equal(gmpAddon.id, "gmp-gmpopenh264"); + Assert.equal(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip"); + Assert.equal(gmpAddon.hashFunction, "sha256"); + Assert.equal( + gmpAddon.hashValue, + "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee" + ); + Assert.equal(gmpAddon.version, "1.1"); + Assert.ok(gmpAddon.isValid); + Assert.ok(!gmpAddon.isInstalled); + installManager.uninit(); +}); + +/** + * Tests that checkForAddons() works as expected when content signature + * checking is enabled and the signature check passes. + */ +add_task(async function test_checkForAddons_contentSignatureSuccess() { + const previousUrlOverride = setupContentSigTestPrefs(); + + const xmlFetchResultHistogram = resetGmpTelemetryAndGetHistogram(); + + const testServerInfo = getTestServerForContentSignatureTests(); + Preferences.set(GMPPrefs.KEY_URL_OVERRIDE, testServerInfo.validUpdateUri); + + let installManager = new GMPInstallManager(); + try { + let res = await installManager.checkForAddons(); + Assert.ok(true, "checkForAddons should succeed"); + + // Smoke test the results are as expected. + // If the checkForAddons fails we'll get a fallback config, + // so we'll get incorrect addons and these asserts will fail. + Assert.equal(res.addons.length, 5); + Assert.equal(res.addons[0].id, "test1"); + Assert.equal(res.addons[0].usedFallback, false); + Assert.equal(res.addons[1].id, "test2"); + Assert.equal(res.addons[1].usedFallback, false); + Assert.equal(res.addons[2].id, "test3"); + Assert.equal(res.addons[2].usedFallback, false); + Assert.equal(res.addons[3].id, "test4"); + Assert.equal(res.addons[3].usedFallback, false); + Assert.equal(res.addons[4].id, undefined); + } catch (e) { + Assert.ok(false, "checkForAddons should succeed"); + } + + // # Ok content sig fetches should be 1, all others should be 0. + TelemetryTestUtils.assertHistogram(xmlFetchResultHistogram, 2, 1); + // Test that glean has 1 success for content sig and no other metrics. + const expectedGleanValues = { + cert_pin_success: 0, + cert_pin_net_request_error: 0, + cert_pin_net_timeout: 0, + cert_pin_abort: 0, + cert_pin_missing_data: 0, + cert_pin_failed: 0, + cert_pin_invalid: 0, + cert_pin_unknown_error: 0, + content_sig_success: 1, + content_sig_net_request_error: 0, + content_sig_net_timeout: 0, + content_sig_abort: 0, + content_sig_missing_data: 0, + content_sig_failed: 0, + content_sig_invalid: 0, + content_sig_unknown_error: 0, + }; + checkGleanMetricCounts(expectedGleanValues); + + revertContentSigTestPrefs(previousUrlOverride); +}); + +/** + * Tests that checkForAddons() works as expected when content signature + * checking is enabled and the check fails. + */ +add_task(async function test_checkForAddons_contentSignatureFailure() { + const previousUrlOverride = setupContentSigTestPrefs(); + + const xmlFetchResultHistogram = resetGmpTelemetryAndGetHistogram(); + + const testServerInfo = getTestServerForContentSignatureTests(); + Preferences.set( + GMPPrefs.KEY_URL_OVERRIDE, + testServerInfo.missingContentSigUri + ); + + let installManager = new GMPInstallManager(); + try { + let res = await installManager.checkForAddons(); + Assert.ok(true, "checkForAddons should succeed"); + + // Smoke test the results are as expected. + // Check addons will succeed above, but it will have fallen back to local + // config. So the results will not be those from the HTTP server. + // Some platforms don't have fallback config for all GMPs, but we should + // always get at least 1. + Assert.greaterOrEqual(res.addons.length, 1); + if (res.addons.length == 1) { + Assert.equal(res.addons[0].id, "gmp-widevinecdm"); + Assert.equal(res.addons[0].usedFallback, true); + } else { + Assert.equal(res.addons[0].id, "gmp-gmpopenh264"); + Assert.equal(res.addons[0].usedFallback, true); + Assert.equal(res.addons[1].id, "gmp-widevinecdm"); + Assert.equal(res.addons[1].usedFallback, true); + if (res.addons.length >= 3) { + Assert.equal(res.addons[2].id, "gmp-widevinecdm-l1"); + Assert.equal(res.addons[2].usedFallback, true); + } + } + } catch (e) { + Assert.ok(false, "checkForAddons should succeed"); + } + + // # Failed content sig fetches should be 1, all others should be 0. + TelemetryTestUtils.assertHistogram(xmlFetchResultHistogram, 3, 1); + // Glean values should reflect the content sig algo failed. + Assert.equal( + Glean.gmp.updateXmlFetchResult.content_sig_missing_data.testGetValue(), + 1 + ); + + // Check further failure cases. We've already smoke tested the results above, + // don't bother doing so again. + + // Fail due to bad content signature. + Preferences.set(GMPPrefs.KEY_URL_OVERRIDE, testServerInfo.badContentSigUri); + await installManager.checkForAddons(); + // Should have another failure... + TelemetryTestUtils.assertHistogram(xmlFetchResultHistogram, 3, 2); + // ... and it should be due to the signature being bad, which causes + // verification to fail. + Assert.equal( + Glean.gmp.updateXmlFetchResult.content_sig_failed.testGetValue(), + 1 + ); + + // Fail due to bad invalid content signature. + Preferences.set( + GMPPrefs.KEY_URL_OVERRIDE, + testServerInfo.invalidContentSigUri + ); + await installManager.checkForAddons(); + // Should have another failure... + TelemetryTestUtils.assertHistogram(xmlFetchResultHistogram, 3, 3); + // ... and it should be due to the signature being invalid. + Assert.equal( + Glean.gmp.updateXmlFetchResult.content_sig_invalid.testGetValue(), + 1 + ); + + // Fail by pointing to a bad URL. + Preferences.set( + GMPPrefs.KEY_URL_OVERRIDE, + "https://this.url.doesnt/go/anywhere" + ); + await installManager.checkForAddons(); + // Should have another failure... + TelemetryTestUtils.assertHistogram(xmlFetchResultHistogram, 3, 4); + // ... and it should be due to a bad request. + Assert.equal( + Glean.gmp.updateXmlFetchResult.content_sig_net_request_error.testGetValue(), + 1 + ); + + // Fail via timeout. This case uses our mock machinery in order to abort the + // request, as I (:bryce) couldn't figure out a nice way to do it with the + // HttpServer. + let overriddenServiceRequest = new mockRequest(200, "", { + dropRequest: true, + timeout: true, + }); + await ProductAddonCheckerTestUtils.overrideServiceRequest( + overriddenServiceRequest, + () => installManager.checkForAddons() + ); + TelemetryTestUtils.assertHistogram(xmlFetchResultHistogram, 3, 5); + // ... and it should be due to a timeout. + Assert.equal( + Glean.gmp.updateXmlFetchResult.content_sig_net_timeout.testGetValue(), + 1 + ); + + // Fail via abort. This case uses our mock machinery in order to abort the + // request, as I (:bryce) couldn't figure out a nice way to do it with the + // HttpServer. + overriddenServiceRequest = new mockRequest(200, "", { + dropRequest: true, + }); + let promise = ProductAddonCheckerTestUtils.overrideServiceRequest( + overriddenServiceRequest, + () => installManager.checkForAddons() + ); + setTimeout(() => { + overriddenServiceRequest.abort(); + }, 100); + await promise; + // Should have another failure... + TelemetryTestUtils.assertHistogram(xmlFetchResultHistogram, 3, 6); + // ... and it should be due to an abort. + Assert.equal( + Glean.gmp.updateXmlFetchResult.content_sig_abort.testGetValue(), + 1 + ); + + Preferences.set(GMPPrefs.KEY_URL_OVERRIDE, testServerInfo.badXmlUri); + await installManager.checkForAddons(); + // Should have another failure... + TelemetryTestUtils.assertHistogram(xmlFetchResultHistogram, 3, 7); + // ... and it should be due to the xml response being unrecognized. + Assert.equal( + Glean.gmp.updateXmlFetchResult.content_sig_xml_parse_error.testGetValue(), + 1 + ); + + // Fail via bad request during the x5u look up. + Preferences.set(GMPPrefs.KEY_URL_OVERRIDE, testServerInfo.badX5uRequestUri); + await installManager.checkForAddons(); + // Should have another failure... + TelemetryTestUtils.assertHistogram(xmlFetchResultHistogram, 3, 8); + // ... and it should be due to a bad request. + Assert.equal( + Glean.gmp.updateXmlFetchResult.content_sig_net_request_error.testGetValue(), + 2 + ); + + // Fail by timing out during the x5u look up. + Preferences.set(GMPPrefs.KEY_URL_OVERRIDE, testServerInfo.x5uTimeoutUri); + // We need to expose this promise back to the server so it can handle + // setting up a mock request in the middle of checking for addons. + testServerInfo.promiseHolder.installPromise = installManager.checkForAddons(); + await testServerInfo.promiseHolder.installPromise; + // We wait sequentially because serverPromise won't be set until the server + // receives our request. + await testServerInfo.promiseHolder.serverPromise; + delete testServerInfo.promiseHolder.installPromise; + delete testServerInfo.promiseHolder.serverPromise; + // Should have another failure... + TelemetryTestUtils.assertHistogram(xmlFetchResultHistogram, 3, 9); + // ... and it should be due to a timeout. + Assert.equal( + Glean.gmp.updateXmlFetchResult.content_sig_net_timeout.testGetValue(), + 2 + ); + + // Fail by aborting during the x5u look up. + Preferences.set(GMPPrefs.KEY_URL_OVERRIDE, testServerInfo.x5uAbortUri); + // We need to expose this promise back to the server so it can handle + // setting up a mock request in the middle of checking for addons. + testServerInfo.promiseHolder.installPromise = installManager.checkForAddons(); + await testServerInfo.promiseHolder.installPromise; + // We wait sequentially because serverPromise won't be set until the server + // receives our request. + await testServerInfo.promiseHolder.serverPromise; + delete testServerInfo.promiseHolder.installPromise; + delete testServerInfo.promiseHolder.serverPromise; + // Should have another failure... + TelemetryTestUtils.assertHistogram(xmlFetchResultHistogram, 3, 10); + // ... and it should be due to an abort. + Assert.equal( + Glean.gmp.updateXmlFetchResult.content_sig_abort.testGetValue(), + 2 + ); + + // Check all glean metrics have expected values at test end. + const expectedGleanValues = { + cert_pin_success: 0, + cert_pin_net_request_error: 0, + cert_pin_net_timeout: 0, + cert_pin_abort: 0, + cert_pin_missing_data: 0, + cert_pin_failed: 0, + cert_pin_invalid: 0, + cert_pin_xml_parse_error: 0, + cert_pin_unknown_error: 0, + content_sig_success: 0, + content_sig_net_request_error: 2, + content_sig_net_timeout: 2, + content_sig_abort: 2, + content_sig_missing_data: 1, + content_sig_failed: 1, + content_sig_invalid: 1, + content_sig_xml_parse_error: 1, + content_sig_unknown_error: 0, + }; + checkGleanMetricCounts(expectedGleanValues); + + revertContentSigTestPrefs(previousUrlOverride); +}); + +/** + * Tests that checkForAddons() works as expected when certificate pinning + * checking is enabled. We plan to move away from cert pinning in favor of + * content signature checks, but part of doing this is comparing the telemetry + * from both methods. We want test coverage to ensure the telemetry is being + * gathered for cert pinning failures correctly before we remove the code. + */ +add_task(async function test_checkForAddons_telemetry_certPinning() { + // Grab state so we can restore it at the end of the test. + const previousUrlOverride = Preferences.get(GMPPrefs.KEY_URL_OVERRIDE, ""); + + let xmlFetchResultHistogram = resetGmpTelemetryAndGetHistogram(); + + // Re-use the content-sig test server config. We're not going to need any of + // the content signature specific config but this gives us a server to get + // update.xml files from, and also tests that cert pinning doesn't break even + // if we're getting content sig headers sent. + const testServerInfo = getTestServerForContentSignatureTests(); + + Preferences.set(GMPPrefs.KEY_URL_OVERRIDE, testServerInfo.validUpdateUri); + + let installManager = new GMPInstallManager(); + try { + // This should work because once we override the GMP URL, no cert pin + // checks are actually done. I.e. we don't need to do any pinning in + // the test, just use a valid URL. + await installManager.checkForAddons(); + Assert.ok(true, "checkForAddons should succeed"); + } catch (e) { + Assert.ok(false, "checkForAddons should succeed"); + } + + // # Ok cert pin fetches should be 1, all others should be 0. + TelemetryTestUtils.assertHistogram(xmlFetchResultHistogram, 0, 1); + // Glean values should reflect the same. + Assert.equal( + Glean.gmp.updateXmlFetchResult.cert_pin_success.testGetValue(), + 1 + ); + + // Reset the histogram because we want to check a different index. + xmlFetchResultHistogram = TelemetryTestUtils.getAndClearHistogram( + "MEDIA_GMP_UPDATE_XML_FETCH_RESULT" + ); + // Fail by pointing to a bad URL. + Preferences.set( + GMPPrefs.KEY_URL_OVERRIDE, + "https://this.url.doesnt/go/anywhere" + ); + await installManager.checkForAddons(); + // Should have another failure... + TelemetryTestUtils.assertHistogram(xmlFetchResultHistogram, 1, 1); + // ... and it should be due to a bad request. + Assert.equal( + Glean.gmp.updateXmlFetchResult.cert_pin_net_request_error.testGetValue(), + 1 + ); + + // Fail via timeout. This case uses our mock machinery in order to abort the + // request, as I (:bryce) couldn't figure out a nice way to do it with the + // HttpServer. + let overriddenServiceRequest = new mockRequest(200, "", { + dropRequest: true, + timeout: true, + }); + await ProductAddonCheckerTestUtils.overrideServiceRequest( + overriddenServiceRequest, + () => installManager.checkForAddons() + ); + TelemetryTestUtils.assertHistogram(xmlFetchResultHistogram, 1, 2); + // ... and it should be due to a timeout. + Assert.equal( + Glean.gmp.updateXmlFetchResult.cert_pin_net_timeout.testGetValue(), + 1 + ); + + // Fail via abort. This case uses our mock machinery in order to abort the + // request, as I (:bryce) couldn't figure out a nice way to do it with the + // HttpServer. + overriddenServiceRequest = new mockRequest(200, "", { + dropRequest: true, + }); + let promise = ProductAddonCheckerTestUtils.overrideServiceRequest( + overriddenServiceRequest, + () => installManager.checkForAddons() + ); + setTimeout(() => { + overriddenServiceRequest.abort(); + }, 100); + await promise; + // Should have another failure... + TelemetryTestUtils.assertHistogram(xmlFetchResultHistogram, 1, 3); + // ... and it should be due to an abort. + Assert.equal(Glean.gmp.updateXmlFetchResult.cert_pin_abort.testGetValue(), 1); + + // Check all glean metrics have expected values at test end. + const expectedGleanValues = { + cert_pin_success: 1, + cert_pin_net_request_error: 1, + cert_pin_net_timeout: 1, + cert_pin_abort: 1, + cert_pin_missing_data: 0, + cert_pin_failed: 0, + cert_pin_invalid: 0, + cert_pin_unknown_error: 0, + content_sig_success: 0, + content_sig_net_request_error: 0, + content_sig_net_timeout: 0, + content_sig_abort: 0, + content_sig_missing_data: 0, + content_sig_failed: 0, + content_sig_invalid: 0, + content_sig_unknown_error: 0, + }; + checkGleanMetricCounts(expectedGleanValues); + + // Restore the URL override now that we're done. + if (previousUrlOverride) { + Preferences.set(GMPPrefs.KEY_URL_OVERRIDE, previousUrlOverride); + } else { + Preferences.reset(GMPPrefs.KEY_URL_OVERRIDE); + } +}); + +/** + * Tests that installing found addons works as expected + */ +async function test_checkForAddons_installAddon( + id, + includeSize, + wantInstallReject +) { + info( + "Running installAddon for id: " + + id + + ", includeSize: " + + includeSize + + " and wantInstallReject: " + + wantInstallReject + ); + let httpServer = new HttpServer(); + let dir = FileUtils.getDir("TmpD", []); + httpServer.registerDirectory("/", dir); + httpServer.start(-1); + let testserverPort = httpServer.identity.primaryPort; + let zipFileName = "test_" + id + "_GMP.zip"; + + let zipURL = URL_HOST + ":" + testserverPort + "/" + zipFileName; + info("zipURL: " + zipURL); + + let data = "e~=0.5772156649"; + let zipFile = createNewZipFile(zipFileName, data); + let hashFunc = "sha256"; + let expectedDigest = await IOUtils.computeHexDigest(zipFile.path, hashFunc); + let fileSize = zipFile.fileSize; + if (wantInstallReject) { + fileSize = 1; + } + + let responseXML = + '<?xml version="1.0"?>' + + "<updates>" + + " <addons>" + + ' <addon id="' + + id + + '-gmp-gmpopenh264"' + + ' URL="' + + zipURL + + '"' + + ' hashFunction="' + + hashFunc + + '"' + + ' hashValue="' + + expectedDigest + + '"' + + (includeSize ? ' size="' + fileSize + '"' : "") + + ' version="1.1"/>' + + " </addons>" + + "</updates>"; + + let myRequest = new mockRequest(200, responseXML); + let installManager = new GMPInstallManager(); + let res = await ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.checkForAddons() + ); + Assert.equal(res.addons.length, 1); + let gmpAddon = res.addons[0]; + Assert.ok(!gmpAddon.isInstalled); + + try { + let extractedPaths = await installManager.installAddon(gmpAddon); + if (wantInstallReject) { + Assert.ok(false); // installAddon() should have thrown. + } + Assert.equal(extractedPaths.length, 1); + let extractedPath = extractedPaths[0]; + + info("Extracted path: " + extractedPath); + + let extractedFile = Cc["@mozilla.org/file/local;1"].createInstance( + Ci.nsIFile + ); + + extractedFile.initWithPath(extractedPath); + Assert.ok(extractedFile.exists()); + let readData = readStringFromFile(extractedFile); + Assert.equal(readData, data); + + // Make sure the prefs are set correctly + Assert.ok( + !!GMPPrefs.getInt(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, "", gmpAddon.id) + ); + Assert.equal( + GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_HASHVALUE, "", gmpAddon.id), + expectedDigest + ); + Assert.equal( + GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_VERSION, "", gmpAddon.id), + "1.1" + ); + Assert.equal( + GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_ABI, "", gmpAddon.id), + UpdateUtils.ABI + ); + // Make sure it reports as being installed + Assert.ok(gmpAddon.isInstalled); + + // Cleanup + extractedFile.parent.remove(true); + zipFile.remove(false); + httpServer.stop(function () {}); + installManager.uninit(); + } catch (ex) { + zipFile.remove(false); + if (!wantInstallReject) { + do_throw("install update should not reject " + ex.message); + } + } +} + +add_task(test_checkForAddons_installAddon.bind(null, "1", true, false)); +add_task(test_checkForAddons_installAddon.bind(null, "2", false, false)); +add_task(test_checkForAddons_installAddon.bind(null, "3", true, true)); + +/** + * Tests simpleCheckAndInstall when autoupdate is disabled for a GMP + */ +add_task(async function test_simpleCheckAndInstall_autoUpdateDisabled() { + GMPPrefs.setBool(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, false, OPEN_H264_ID); + let responseXML = + '<?xml version="1.0"?>' + + "<updates>" + + " <addons>" + + // valid openh264 + ' <addon id="gmp-gmpopenh264"' + + ' URL="http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip"' + + ' hashFunction="sha256"' + + ' hashValue="1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' + + ' version="1.1"/>' + + " </addons>" + + "</updates>"; + + let myRequest = new mockRequest(200, responseXML); + let installManager = new GMPInstallManager(); + let result = await ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.simpleCheckAndInstall() + ); + Assert.equal(result.status, "nothing-new-to-install"); + Preferences.reset(GMPPrefs.KEY_UPDATE_LAST_CHECK); + GMPPrefs.setBool(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, true, OPEN_H264_ID); +}); + +/** + * Tests simpleCheckAndInstall nothing to install + */ +add_task(async function test_simpleCheckAndInstall_nothingToInstall() { + let responseXML = '<?xml version="1.0"?><updates></updates>'; + + let myRequest = new mockRequest(200, responseXML); + let installManager = new GMPInstallManager(); + let result = await ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.simpleCheckAndInstall() + ); + Assert.equal(result.status, "nothing-new-to-install"); +}); + +/** + * Tests simpleCheckAndInstall too frequent + */ +add_task(async function test_simpleCheckAndInstall_tooFrequent() { + let responseXML = '<?xml version="1.0"?><updates></updates>'; + + let myRequest = new mockRequest(200, responseXML); + let installManager = new GMPInstallManager(); + let result = await ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.simpleCheckAndInstall() + ); + Assert.equal(result.status, "too-frequent-no-check"); +}); + +/** + * Tests that installing addons when there is no server works as expected + */ +add_test(function test_installAddon_noServer() { + let zipFileName = "test_GMP.zip"; + let zipURL = URL_HOST + ":0/" + zipFileName; + + let responseXML = + '<?xml version="1.0"?>' + + "<updates>" + + " <addons>" + + ' <addon id="gmp-gmpopenh264"' + + ' URL="' + + zipURL + + '"' + + ' hashFunction="sha256"' + + ' hashValue="11221cbda000347b054028b527a60e578f919cb10f322ef8077d3491c6fcb474"' + + ' version="1.1"/>' + + " </addons>" + + "</updates>"; + + let myRequest = new mockRequest(200, responseXML); + let installManager = new GMPInstallManager(); + let checkPromise = ProductAddonCheckerTestUtils.overrideServiceRequest( + myRequest, + () => installManager.checkForAddons() + ); + checkPromise.then( + res => { + Assert.equal(res.addons.length, 1); + let gmpAddon = res.addons[0]; + + GMPInstallManager.overrideLeaveDownloadedZip = true; + let installPromise = installManager.installAddon(gmpAddon); + installPromise.then( + extractedPaths => { + do_throw("No server for install should reject"); + }, + err => { + Assert.ok(!!err); + installManager.uninit(); + run_next_test(); + } + ); + }, + () => { + do_throw("check should not reject for install no server"); + } + ); +}); + +/*** + * Tests GMPExtractor (an internal component of GMPInstallManager) to ensure + * it handles paths with certain characters. + * + * On Mac, test that the com.apple.quarantine extended attribute is removed + * from installed plugin files. + */ + +add_task(async function test_GMPExtractor_paths() { + registerCleanupFunction(async function () { + // Must stop holding on to the zip file using the JAR cache: + let zipFile = new FileUtils.File( + PathUtils.join(tempDir.path, "dummy_gmp.zip") + ); + Services.obs.notifyObservers(zipFile, "flush-cache-entry"); + await IOUtils.remove(extractedDir, { recursive: true }); + await IOUtils.remove(tempDir.path, { recursive: true }); + }); + // Create a dir with the following in the name + // - # -- this is used to delimit URI fragments and tests that + // we escape any internal URIs appropriately. + // - 猫 -- ensure we handle non-ascii characters appropriately. + const srcPath = PathUtils.join( + Services.dirsvc.get("CurWorkD", Ci.nsIFile).path, + "zips", + "dummy_gmp.zip" + ); + let tempDirName = "TmpDir#猫"; + let tempDir = FileUtils.getDir("TmpD", [tempDirName]); + tempDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); + let zipPath = PathUtils.join(tempDir.path, "dummy_gmp.zip"); + await IOUtils.copy(srcPath, zipPath); + // The path inside the profile dir we'll extract to. Make sure we handle + // the characters there too. + let relativeExtractPath = "extracted#猫"; + let extractor = new GMPExtractor(zipPath, [relativeExtractPath]); + let extractedPaths = await extractor.install(); + // extractedPaths should contain the files extracted. In this case we + // should have a single file extracted to our profile dir -- the zip + // contains two files, but one should be skipped by the extraction logic. + Assert.equal(extractedPaths.length, 1, "One file should be extracted"); + Assert.ok( + extractedPaths[0].includes("dummy_file.txt"), + "dummy_file.txt should be on extracted path" + ); + Assert.ok( + !extractedPaths[0].includes("verified_contents.json"), + "verified_contents.json should not be on extracted path" + ); + let extractedDir = PathUtils.join(PathUtils.profileDir, relativeExtractPath); + Assert.ok( + await IOUtils.exists(extractedDir), + "Extraction should have created a directory" + ); + let extractedFile = PathUtils.join( + PathUtils.profileDir, + relativeExtractPath, + "dummy_file.txt" + ); + Assert.ok( + await IOUtils.exists(extractedFile), + "Extraction should have created dummy_file.txt" + ); + if (AppConstants.platform == "macosx") { + await Assert.rejects( + IOUtils.getMacXAttr(extractedFile, "com.apple.quarantine"), + /NotFoundError: The file `.+' does not have an extended attribute `com.apple.quarantine'/, + "The 'com.apple.quarantine' attribute should not be present" + ); + } + let unextractedFile = PathUtils.join( + PathUtils.profileDir, + relativeExtractPath, + "verified_contents.json" + ); + Assert.ok( + !(await IOUtils.exists(unextractedFile)), + "Extraction should not have created verified_contents.json" + ); +}); + +/** + * Returns the read stream into a string + */ +function readStringFromInputStream(inputStream) { + let sis = Cc["@mozilla.org/scriptableinputstream;1"].createInstance( + Ci.nsIScriptableInputStream + ); + sis.init(inputStream); + let text = sis.read(sis.available()); + sis.close(); + return text; +} + +/** + * Reads a string of text from a file. + * This function only works with ASCII text. + */ +function readStringFromFile(file) { + if (!file.exists()) { + info("readStringFromFile - file doesn't exist: " + file.path); + return null; + } + let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance( + Ci.nsIFileInputStream + ); + fis.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0); + return readStringFromInputStream(fis); +} + +/** + * Constructs a mock xhr/ServiceRequest which is used for testing different + * aspects of responses. + */ +function mockRequest(inputStatus, inputResponse, options) { + this.inputStatus = inputStatus; + this.inputResponse = inputResponse; + this.status = 0; + this.responseXML = null; + this._aborted = false; + this._onabort = null; + this._onprogress = null; + this._onerror = null; + this._onload = null; + this._onloadend = null; + this._ontimeout = null; + this._url = null; + this._method = null; + this._timeout = 0; + this._notified = false; + this._options = options || {}; +} +mockRequest.prototype = { + overrideMimeType(aMimetype) {}, + setRequestHeader(aHeader, aValue) {}, + status: null, + channel: { set notificationCallbacks(aVal) {} }, + open(aMethod, aUrl) { + this.channel.originalURI = Services.io.newURI(aUrl); + this._method = aMethod; + this._url = aUrl; + }, + abort() { + this._dropRequest = true; + this._notify(["abort", "loadend"]); + }, + responseXML: null, + responseText: null, + send(aBody) { + executeSoon(() => { + try { + if (this._options.dropRequest) { + if (this._timeout > 0 && this._options.timeout) { + this._notify(["timeout", "loadend"]); + } + return; + } + this.status = this.inputStatus; + this.responseText = this.inputResponse; + try { + let parser = new DOMParser(); + this.responseXML = parser.parseFromString( + this.inputResponse, + "application/xml" + ); + } catch (e) { + this.responseXML = null; + } + if (this.inputStatus === 200) { + this._notify(["load", "loadend"]); + } else { + this._notify(["error", "loadend"]); + } + } catch (ex) { + do_throw(ex); + } + }); + }, + set onabort(aValue) { + this._onabort = aValue; + }, + get onabort() { + return this._onabort; + }, + set onprogress(aValue) { + this._onprogress = aValue; + }, + get onprogress() { + return this._onprogress; + }, + set onerror(aValue) { + this._onerror = aValue; + }, + get onerror() { + return this._onerror; + }, + set onload(aValue) { + this._onload = aValue; + }, + get onload() { + return this._onload; + }, + set onloadend(aValue) { + this._onloadend = aValue; + }, + get onloadend() { + return this._onloadend; + }, + set ontimeout(aValue) { + this._ontimeout = aValue; + }, + get ontimeout() { + return this._ontimeout; + }, + set timeout(aValue) { + this._timeout = aValue; + }, + _notify(events) { + if (this._notified) { + return; + } + this._notified = true; + for (let item of events) { + let k = "on" + item; + if (this[k]) { + info("Notifying " + item); + let e = { + target: this, + type: item, + }; + this[k](e); + } else { + info("Notifying " + item + ", but there are no listeners"); + } + } + }, + addEventListener(aEvent, aValue, aCapturing) { + // eslint-disable-next-line no-eval + eval("this._on" + aEvent + " = aValue"); + }, + get wrappedJSObject() { + return this; + }, +}; + +/** + * Creates a new zip file containing a file with the specified data + * @param zipName The name of the zip file + * @param data The data to go inside the zip for the filename entry1.info + */ +function createNewZipFile(zipName, data) { + // Create a zip file which will be used for extracting + let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( + Ci.nsIStringInputStream + ); + stream.setData(data, data.length); + let zipWriter = Cc["@mozilla.org/zipwriter;1"].createInstance( + Ci.nsIZipWriter + ); + let zipFile = new FileUtils.File(PathUtils.join(PathUtils.tempDir, zipName)); + if (zipFile.exists()) { + zipFile.remove(false); + } + // From prio.h + const PR_RDWR = 0x04; + const PR_CREATE_FILE = 0x08; + const PR_TRUNCATE = 0x20; + zipWriter.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE); + zipWriter.addEntryStream( + "entry1.info", + Date.now() * PR_USEC_PER_MSEC, + Ci.nsIZipWriter.COMPRESSION_BEST, + stream, + false + ); + zipWriter.close(); + stream.close(); + info("zip file created on disk at: " + zipFile.path); + return zipFile; +} + +/*** + * Set up pref(s) as appropriate for content sig tests. Return the value of our + * current GMP url override so it can be restored at test teardown. + */ + +function setupContentSigTestPrefs() { + Preferences.set("media.gmp-manager.checkContentSignature", true); + + // Return the URL override so tests can restore it to its previous value + // once they're done. + return Preferences.get(GMPPrefs.KEY_URL_OVERRIDE, ""); +} + +/*** + * Revert prefs used for content signature tests. + * + * @param previousUrlOverride - The GMP URL override value prior to test being + * run. The function will revert the URL back to this, or reset the URL if no + * value is passed. + */ +function revertContentSigTestPrefs(previousUrlOverride) { + if (previousUrlOverride) { + Preferences.set(GMPPrefs.KEY_URL_OVERRIDE, previousUrlOverride); + } else { + Preferences.reset(GMPPrefs.KEY_URL_OVERRIDE); + } + Preferences.set("media.gmp-manager.checkContentSignature", false); +} + +/*** + * Reset telemetry data related to gmp updates, and get the histogram + * associated with MEDIA_GMP_UPDATE_XML_FETCH_RESULT. + * + * @returns The freshly cleared MEDIA_GMP_UPDATE_XML_FETCH_RESULT histogram. + */ +function resetGmpTelemetryAndGetHistogram() { + Services.fog.testResetFOG(); + return TelemetryTestUtils.getAndClearHistogram( + "MEDIA_GMP_UPDATE_XML_FETCH_RESULT" + ); +} + +/*** + * A helper to check that glean metrics have expected counts. + * @param expectedGleanValues a object that has properties with names set to glean metrics to be checked + * and the values are the expected count. Eg { cert_pin_success: 1 }. + */ +function checkGleanMetricCounts(expectedGleanValues) { + for (const property in expectedGleanValues) { + if (Glean.gmp.updateXmlFetchResult[property].testGetValue()) { + Assert.equal( + Glean.gmp.updateXmlFetchResult[property].testGetValue(), + expectedGleanValues[property], + `${property} should have been recorded ${expectedGleanValues[property]} times` + ); + } else { + Assert.equal( + expectedGleanValues[property], + 0, + "testGetValue() being undefined should mean we expect a metric to not have been gathered" + ); + } + } +} + +/*** + * Sets up a `HttpServer` for use in content singature checking tests. This + * server will expose different endpoints that can be used to simulate different + * pass and failure scenarios when fetching an update.xml file. + * + * @returns An object that has the following properties + * - testServer - the HttpServer itself. + * - promiseHolder - an object used to hold promises as properties. More complex test cases need this to sync different steps. + * - validUpdateUri - a URI that should return a valid update xml + content sig. + * - missingContentSigUri - a URI that returns a valid update xml, but misses the content sig header. + * - badContentSigUri - a URI that returns a valid update xml, but provides data that is not a content sig in the header. + * - invalidContentSigUri - a URI that returns a valid update xml, but provides an incorrect content sig. + * - badXmlUri - a URI that returns an invalid update xml, but provides a correct content sig. + * - x5uAbortUri - a URI that returns a valid update xml, but timesout the x5u request. Requires the caller to set + * `promiseHolder.installPromise` to the `checkForAddons()` promise` before hitting the endpoint. The server will set + * `promiseHolder.serverPromise` once it has started servicing the initial update request, and the caller should + * await that promise to ensure the server has restored state. + * - x5uAbortUri - a URI that returns a valid update xml, but aborts the x5u request. Requires the caller to set + * `promiseHolder.installPromise` to the `checkForAddons()` promise` before hitting the endpoint. The server will set + * `promiseHolder.serverPromise` once it has started servicing the initial update request, and the caller should + * await that promise to ensure the server has restored state. + */ +function getTestServerForContentSignatureTests() { + const testServer = new HttpServer(); + // Start the server so we can grab the identity. We need to know this so the + // server can reference itself in the handlers that will be set up. + testServer.start(); + const baseUri = + testServer.identity.primaryScheme + + "://" + + testServer.identity.primaryHost + + ":" + + testServer.identity.primaryPort; + + // The promise holder has no properties by default. Different endpoints and + // tests will set its properties as needed. + let promiseHolder = {}; + + const goodXml = readStringFromFile(do_get_file("good.xml")); + // This sig is generated using the following command at mozilla-central root + // `cat toolkit/mozapps/extensions/test/xpcshell/data/productaddons/good.xml | ./mach python security/manager/ssl/tests/unit/test_content_signing/pysign.py` + // If test certificates are regenerated, this signature must also be. + const goodXmlContentSignature = + "7QYnPqFoOlS02BpDdIRIljzmPr6BFwPs1z1y8KJUBlnU7EVG6FbnXmVVt5Op9wDzgvhXX7th8qFJvpPOZs_B_tHRDNJ8SK0HN95BAN15z3ZW2r95SSHmU-fP2JgoNOR3"; + + // Setup endpoint to handle x5u lookups correctly. + const validX5uPath = "/valid_x5u"; + const validCertChain = [ + readStringFromFile(do_get_file("content_signing_aus_ee.pem")), + readStringFromFile(do_get_file("content_signing_int.pem")), + ]; + testServer.registerPathHandler(validX5uPath, (req, res) => { + res.write(validCertChain.join("\n")); + }); + const validX5uUrl = baseUri + validX5uPath; + + // Handler for path that serves valid xml with valid signature. + const validUpdatePath = "/valid_update.xml"; + testServer.registerPathHandler(validUpdatePath, (req, res) => { + const validContentSignatureHeader = `x5u=${validX5uUrl}; p384ecdsa=${goodXmlContentSignature}`; + res.setHeader("content-signature", validContentSignatureHeader); + res.write(goodXml); + }); + + const missingContentSigPath = "/update_missing_content_sig.xml"; + testServer.registerPathHandler(missingContentSigPath, (req, res) => { + // Content signature header omitted. + res.write(goodXml); + }); + + const badContentSigPath = "/update_bad_content_sig.xml"; + testServer.registerPathHandler(badContentSigPath, (req, res) => { + res.setHeader( + "content-signature", + `x5u=${validX5uUrl}; p384ecdsa=I'm a bad content signature` + ); + res.write(goodXml); + }); + + // Make an invalid signature by change first char. + const invalidXmlContentSignature = "Z" + goodXmlContentSignature.slice(1); + const invalidContentSigPath = "/update_invalid_content_sig.xml"; + testServer.registerPathHandler(invalidContentSigPath, (req, res) => { + res.setHeader( + "content-signature", + `x5u=${validX5uUrl}; p384ecdsa=${invalidXmlContentSignature}` + ); + res.write(goodXml); + }); + + const badXml = readStringFromFile(do_get_file("bad.xml")); + // This sig is generated using the following command at mozilla-central root + // `cat toolkit/mozapps/extensions/test/xpcshell/data/productaddons/bad.xml | ./mach python security/manager/ssl/tests/unit/test_content_signing/pysign.py` + // If test certificates are regenerated, this signature must also be. + const badXmlContentSignature = + "7QYnPqFoOlS02BpDdIRIljzmPr6BFwPs1z1y8KJUBlnU7EVG6FbnXmVVt5Op9wDz8YoQ_b-3i9rWpj40s8QZsMgo2eImx83LW9JE0d0z6sSAnwRb4lHFPpJXC_hv7wi7"; + const badXmlPath = "/bad.xml"; + testServer.registerPathHandler(badXmlPath, (req, res) => { + const validContentSignatureHeader = `x5u=${validX5uUrl}; p384ecdsa=${badXmlContentSignature}`; + res.setHeader("content-signature", validContentSignatureHeader); + res.write(badXml); + }); + + const badX5uRequestPath = "/bad_x5u_request.xml"; + testServer.registerPathHandler(badX5uRequestPath, (req, res) => { + const badX5uUrlHeader = `x5u=https://this.is.a/bad/url; p384ecdsa=${goodXmlContentSignature}`; + res.setHeader("content-signature", badX5uUrlHeader); + res.write(badXml); + }); + + const x5uTimeoutPath = "/x5u_timeout.xml"; + testServer.registerPathHandler(x5uTimeoutPath, (req, res) => { + const validContentSignatureHeader = `x5u=${validX5uUrl}; p384ecdsa=${goodXmlContentSignature}`; + // Write the correct header and xml, but setup the next request to timeout. + // This should cause the request for the x5u URL to fail via timeout. + let overriddenServiceRequest = new mockRequest(200, "", { + dropRequest: true, + timeout: true, + }); + // We expose this promise so that tests can wait until the server has + // reverted the overridden request (to avoid double overrides). + promiseHolder.serverPromise = + ProductAddonCheckerTestUtils.overrideServiceRequest( + overriddenServiceRequest, + () => { + res.setHeader("content-signature", validContentSignatureHeader); + res.write(goodXml); + return promiseHolder.installPromise; + } + ); + }); + + const x5uAbortPath = "/x5u_abort.xml"; + testServer.registerPathHandler(x5uAbortPath, (req, res) => { + const validContentSignatureHeader = `x5u=${validX5uUrl}; p384ecdsa=${goodXmlContentSignature}`; + // Write the correct header and xml, but setup the next request to fail. + // This should cause the request for the x5u URL to fail via abort. + let overriddenServiceRequest = new mockRequest(200, "", { + dropRequest: true, + }); + // We expose this promise so that tests can wait until the server has + // reverted the overridden request (to avoid double overrides). + promiseHolder.serverPromise = + ProductAddonCheckerTestUtils.overrideServiceRequest( + overriddenServiceRequest, + () => { + res.setHeader("content-signature", validContentSignatureHeader); + res.write(goodXml); + return promiseHolder.installPromise; + } + ); + setTimeout(() => { + overriddenServiceRequest.abort(); + }, 100); + }); + + return { + testServer, + promiseHolder, + validUpdateUri: baseUri + validUpdatePath, + missingContentSigUri: baseUri + missingContentSigPath, + badContentSigUri: baseUri + badContentSigPath, + invalidContentSigUri: baseUri + invalidContentSigPath, + badXmlUri: baseUri + badXmlPath, + badX5uRequestUri: baseUri + badX5uRequestPath, + x5uTimeoutUri: baseUri + x5uTimeoutPath, + x5uAbortUri: baseUri + x5uAbortPath, + }; +} diff --git a/toolkit/modules/tests/xpcshell/test_IgnoreList.js b/toolkit/modules/tests/xpcshell/test_IgnoreList.js new file mode 100644 index 0000000000..276d90c292 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_IgnoreList.js @@ -0,0 +1,207 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +ChromeUtils.defineESModuleGetters(this, { + IgnoreLists: "resource://gre/modules/IgnoreLists.sys.mjs", + RemoteSettings: "resource://services-settings/remote-settings.sys.mjs", + RemoteSettingsClient: + "resource://services-settings/RemoteSettingsClient.sys.mjs", + sinon: "resource://testing-common/Sinon.sys.mjs", +}); + +const IGNORELIST_KEY = "hijack-blocklists"; + +const IGNORELIST_TEST_DATA = [ + { + id: "load-paths", + matches: ["[other]addEngineWithDetails:searchignore@mozilla.com"], + }, + { + id: "submission-urls", + matches: ["ignore=true"], + }, +]; + +let getStub; + +add_task(async function setup() { + const ignoreListSettings = RemoteSettings(IGNORELIST_KEY); + getStub = sinon.stub(ignoreListSettings, "get"); +}); + +add_task(async function test_ignoreList_basic_get() { + getStub.onFirstCall().returns(IGNORELIST_TEST_DATA); + + const settings = await IgnoreLists.getAndSubscribe(() => {}); + + Assert.deepEqual( + settings, + IGNORELIST_TEST_DATA, + "Should have obtained the correct data from the database." + ); +}); + +add_task(async function test_ignoreList_reentry() { + let promise = Promise.withResolvers(); + getStub.resetHistory(); + getStub.onFirstCall().returns(promise.promise); + + let firstResult; + let secondResult; + + const firstCallPromise = IgnoreLists.getAndSubscribe(() => {}).then( + result => (firstResult = result) + ); + const secondCallPromise = IgnoreLists.getAndSubscribe(() => {}).then( + result => (secondResult = result) + ); + + Assert.strictEqual( + firstResult, + undefined, + "Should not have returned the first result yet." + ); + Assert.strictEqual( + secondResult, + undefined, + "Should not have returned the second result yet." + ); + + promise.resolve(IGNORELIST_TEST_DATA); + + await Promise.all([firstCallPromise, secondCallPromise]); + + Assert.deepEqual( + firstResult, + IGNORELIST_TEST_DATA, + "Should have returned the correct data to the first call." + ); + Assert.deepEqual( + secondResult, + IGNORELIST_TEST_DATA, + "Should have returned the correct data to the second call." + ); +}); + +add_task(async function test_ignoreList_updates() { + getStub.onFirstCall().returns([]); + + let updateData; + let listener = eventData => { + updateData = eventData.data.current; + }; + + await IgnoreLists.getAndSubscribe(listener); + + Assert.ok(!updateData, "Should not have given an update yet"); + + const NEW_DATA = [ + { + id: "load-paths", + schema: 1553857697843, + last_modified: 1553859483588, + matches: ["[other]addEngineWithDetails:searchignore@mozilla.com"], + }, + { + id: "submission-urls", + schema: 1553857697843, + last_modified: 1553859435500, + matches: ["ignore=true"], + }, + ]; + + // Simulate an ignore list update. + await RemoteSettings("hijack-blocklists").emit("sync", { + data: { + current: NEW_DATA, + }, + }); + + Assert.deepEqual( + updateData, + NEW_DATA, + "Should have updated the listener with the correct data" + ); + + IgnoreLists.unsubscribe(listener); + + await RemoteSettings("hijack-blocklists").emit("sync", { + data: { + current: [ + { + id: "load-paths", + schema: 1553857697843, + last_modified: 1553859483589, + matches: [], + }, + { + id: "submission-urls", + schema: 1553857697843, + last_modified: 1553859435501, + matches: [], + }, + ], + }, + }); + + Assert.deepEqual( + updateData, + NEW_DATA, + "Should not have updated the listener" + ); +}); + +add_task(async function test_ignoreList_db_modification() { + // Fill the database with some values that we can use to test that it is cleared. + const db = RemoteSettings(IGNORELIST_KEY).db; + await db.importChanges({}, Date.now(), IGNORELIST_TEST_DATA, { clear: true }); + + // Stub the get() so that the first call simulates a signature error, and + // the second simulates success reading from the dump. + getStub.resetHistory(); + getStub + .onFirstCall() + .rejects(new RemoteSettingsClient.InvalidSignatureError("abc")); + getStub.onSecondCall().returns(IGNORELIST_TEST_DATA); + + let result = await IgnoreLists.getAndSubscribe(() => {}); + + Assert.ok( + getStub.calledTwice, + "Should have called the get() function twice." + ); + + const databaseEntries = await db.list(); + Assert.equal(databaseEntries.length, 0, "Should have cleared the database."); + + Assert.deepEqual( + result, + IGNORELIST_TEST_DATA, + "Should have returned the correct data." + ); +}); + +add_task(async function test_ignoreList_db_modification_never_succeeds() { + // Fill the database with some values that we can use to test that it is cleared. + const db = RemoteSettings(IGNORELIST_KEY).db; + await db.importChanges({}, Date.now(), IGNORELIST_TEST_DATA, { clear: true }); + + // Now simulate the condition where for some reason we never get a + // valid result. + getStub.reset(); + getStub.rejects(new RemoteSettingsClient.InvalidSignatureError("abc")); + + let result = await IgnoreLists.getAndSubscribe(() => {}); + + Assert.ok( + getStub.calledTwice, + "Should have called the get() function twice." + ); + + const databaseEntries = await db.list(); + Assert.equal(databaseEntries.length, 0, "Should have cleared the database."); + + Assert.deepEqual(result, [], "Should have returned an empty result."); +}); diff --git a/toolkit/modules/tests/xpcshell/test_Integration.js b/toolkit/modules/tests/xpcshell/test_Integration.js new file mode 100644 index 0000000000..8213e32592 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_Integration.js @@ -0,0 +1,240 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests the Integration.sys.mjs module. + */ + +"use strict"; +const { Integration } = ChromeUtils.importESModule( + "resource://gre/modules/Integration.sys.mjs" +); + +const TestIntegration = { + value: "value", + + get valueFromThis() { + return this.value; + }, + + get property() { + return this._property; + }, + + set property(value) { + this._property = value; + }, + + method(argument) { + this.methodArgument = argument; + return "method" + argument; + }, + + async asyncMethod(argument) { + this.asyncMethodArgument = argument; + return "asyncMethod" + argument; + }, +}; + +let overrideFn = base => ({ + value: "overridden-value", + + get property() { + return "overridden-" + base.__lookupGetter__("property").call(this); + }, + + set property(value) { + base.__lookupSetter__("property").call(this, "overridden-" + value); + }, + + method() { + return "overridden-" + base.method.apply(this, arguments); + }, + + async asyncMethod() { + return "overridden-" + (await base.asyncMethod.apply(this, arguments)); + }, +}); + +let superOverrideFn = base => { + let nextLevel = { + value: "overridden-value", + + get property() { + return "overridden-" + super.property; + }, + + set property(value) { + super.property = "overridden-" + value; + }, + + method() { + return "overridden-" + super.method(...arguments); + }, + + async asyncMethod() { + // We cannot use the "super" keyword in methods defined using "Task.async". + return "overridden-" + (await base.asyncMethod.apply(this, arguments)); + }, + }; + Object.setPrototypeOf(nextLevel, base); + return nextLevel; +}; + +/** + * Fails the test if the results of method invocations on the combined object + * don't match the expected results based on how many overrides are registered. + * + * @param combined + * The combined object based on the TestIntegration root. + * @param overridesCount + * Zero if the root object is not overridden, or a higher value to test + * the presence of one or more integration overrides. + */ +async function assertCombinedResults(combined, overridesCount) { + let expectedValue = overridesCount > 0 ? "overridden-value" : "value"; + let prefix = "overridden-".repeat(overridesCount); + + Assert.equal(combined.value, expectedValue); + Assert.equal(combined.valueFromThis, expectedValue); + + combined.property = "property"; + Assert.equal(combined.property, prefix.repeat(2) + "property"); + + combined.methodArgument = ""; + Assert.equal(combined.method("-argument"), prefix + "method-argument"); + Assert.equal(combined.methodArgument, "-argument"); + + combined.asyncMethodArgument = ""; + Assert.equal( + await combined.asyncMethod("-argument"), + prefix + "asyncMethod-argument" + ); + Assert.equal(combined.asyncMethodArgument, "-argument"); +} + +/** + * Fails the test if the results of method invocations on the combined object + * for the "testModule" integration point don't match the expected results based + * on how many overrides are registered. + * + * @param overridesCount + * Zero if the root object is not overridden, or a higher value to test + * the presence of one or more integration overrides. + */ +async function assertCurrentCombinedResults(overridesCount) { + let combined = Integration.testModule.getCombined(TestIntegration); + await assertCombinedResults(combined, overridesCount); +} + +/** + * Checks the initial state with no integration override functions registered. + */ +add_task(async function test_base() { + await assertCurrentCombinedResults(0); +}); + +/** + * Registers and unregisters an integration override function. + */ +add_task(async function test_override() { + Integration.testModule.register(overrideFn); + await assertCurrentCombinedResults(1); + + // Registering the same function more than once has no effect. + Integration.testModule.register(overrideFn); + await assertCurrentCombinedResults(1); + + Integration.testModule.unregister(overrideFn); + await assertCurrentCombinedResults(0); +}); + +/** + * Registers and unregisters more than one integration override function, of + * which one uses the prototype and the "super" keyword to access the base. + */ +add_task(async function test_override_super_multiple() { + Integration.testModule.register(overrideFn); + Integration.testModule.register(superOverrideFn); + await assertCurrentCombinedResults(2); + + Integration.testModule.unregister(overrideFn); + await assertCurrentCombinedResults(1); + + Integration.testModule.unregister(superOverrideFn); + await assertCurrentCombinedResults(0); +}); + +/** + * Registers an integration override function that throws an exception, and + * ensures that this does not block other functions from being registered. + */ +add_task(async function test_override_error() { + let errorOverrideFn = base => { + throw new Error("Expected error."); + }; + + Integration.testModule.register(errorOverrideFn); + Integration.testModule.register(overrideFn); + await assertCurrentCombinedResults(1); + + Integration.testModule.unregister(errorOverrideFn); + Integration.testModule.unregister(overrideFn); + await assertCurrentCombinedResults(0); +}); + +/** + * Checks that state saved using the "this" reference is preserved as a shallow + * copy when registering new integration override functions. + */ +add_task(async function test_state_preserved() { + let valueObject = { toString: () => "toString" }; + + let combined = Integration.testModule.getCombined(TestIntegration); + combined.property = valueObject; + Assert.ok(combined.property === valueObject); + + Integration.testModule.register(overrideFn); + combined = Integration.testModule.getCombined(TestIntegration); + Assert.equal(combined.property, "overridden-toString"); + + Integration.testModule.unregister(overrideFn); + combined = Integration.testModule.getCombined(TestIntegration); + Assert.ok(combined.property === valueObject); +}); + +/** + * Checks that the combined integration objects cannot be used with XPCOM. + * + * This is limited by the fact that interfaces with the "[function]" annotation, + * for example nsIObserver, do not call the QueryInterface implementation. + */ +add_task(async function test_xpcom_throws() { + let combined = Integration.testModule.getCombined(TestIntegration); + + // This calls QueryInterface because it looks for nsISupportsWeakReference. + Assert.throws( + () => Services.obs.addObserver(combined, "test-topic", true), + /NS_NOINTERFACE/ + ); +}); + +/** + * Checks that getters defined by defineESModuleGetter are able to retrieve the + * latest version of the combined integration object. + */ +add_task(async function test_defineESModuleGetter() { + let objectForGetters = {}; + + Integration.testModule.defineESModuleGetter( + objectForGetters, + "TestIntegration", + "resource://testing-common/TestIntegration.sys.mjs" + ); + + Integration.testModule.register(overrideFn); + await assertCombinedResults(objectForGetters.TestIntegration, 1); + + Integration.testModule.unregister(overrideFn); + await assertCombinedResults(objectForGetters.TestIntegration, 0); +}); diff --git a/toolkit/modules/tests/xpcshell/test_JSONFile.js b/toolkit/modules/tests/xpcshell/test_JSONFile.js new file mode 100644 index 0000000000..cb13751963 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_JSONFile.js @@ -0,0 +1,326 @@ +/** + * Tests the JSONFile object. + */ + +"use strict"; + +// Globals +ChromeUtils.defineESModuleGetters(this, { + AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs", + FileTestUtils: "resource://testing-common/FileTestUtils.sys.mjs", + JSONFile: "resource://gre/modules/JSONFile.sys.mjs", +}); + +/** + * Returns a reference to a temporary file that is guaranteed not to exist and + * is cleaned up later. See FileTestUtils.getTempFile for details. + */ +function getTempFile(leafName) { + return FileTestUtils.getTempFile(leafName); +} + +const TEST_STORE_FILE_NAME = "test-store.json"; + +const TEST_DATA = { + number: 123, + string: "test", + object: { + prop1: 1, + prop2: 2, + }, +}; + +// Tests + +add_task(async function test_save_reload() { + let storeForSave = new JSONFile({ + path: getTempFile(TEST_STORE_FILE_NAME).path, + }); + + await storeForSave.load(); + + Assert.ok(storeForSave.dataReady); + Assert.deepEqual(storeForSave.data, {}); + + Object.assign(storeForSave.data, TEST_DATA); + + await new Promise(resolve => { + let save = storeForSave._save.bind(storeForSave); + storeForSave._save = () => { + save(); + resolve(); + }; + storeForSave.saveSoon(); + }); + + let storeForLoad = new JSONFile({ + path: storeForSave.path, + }); + + await storeForLoad.load(); + + Assert.deepEqual(storeForLoad.data, TEST_DATA); +}); + +add_task(async function test_load_sync() { + let storeForSave = new JSONFile({ + path: getTempFile(TEST_STORE_FILE_NAME).path, + }); + await storeForSave.load(); + Object.assign(storeForSave.data, TEST_DATA); + await storeForSave._save(); + + let storeForLoad = new JSONFile({ + path: storeForSave.path, + }); + storeForLoad.ensureDataReady(); + + Assert.deepEqual(storeForLoad.data, TEST_DATA); +}); + +add_task(async function test_load_with_dataPostProcessor() { + let storeForSave = new JSONFile({ + path: getTempFile(TEST_STORE_FILE_NAME).path, + }); + await storeForSave.load(); + Object.assign(storeForSave.data, TEST_DATA); + await storeForSave._save(); + + let random = Math.random(); + let storeForLoad = new JSONFile({ + path: storeForSave.path, + dataPostProcessor: data => { + Assert.deepEqual(data, TEST_DATA); + + data.test = random; + return data; + }, + }); + + await storeForLoad.load(); + + Assert.equal(storeForLoad.data.test, random); +}); + +add_task(async function test_load_with_dataPostProcessor_fails() { + let store = new JSONFile({ + path: getTempFile(TEST_STORE_FILE_NAME).path, + dataPostProcessor: () => { + throw new Error("dataPostProcessor fails."); + }, + }); + + await Assert.rejects(store.load(), /dataPostProcessor fails\./); + + Assert.ok(!store.dataReady); +}); + +add_task(async function test_load_sync_with_dataPostProcessor_fails() { + let store = new JSONFile({ + path: getTempFile(TEST_STORE_FILE_NAME).path, + dataPostProcessor: () => { + throw new Error("dataPostProcessor fails."); + }, + }); + + Assert.throws(() => store.ensureDataReady(), /dataPostProcessor fails\./); + + Assert.ok(!store.dataReady); +}); + +/** + * Loads data from a string in a predefined format. The purpose of this test is + * to verify that the JSON format used in previous versions can be loaded. + */ +add_task(async function test_load_string_predefined() { + let store = new JSONFile({ + path: getTempFile(TEST_STORE_FILE_NAME).path, + }); + + let string = '{"number":123,"string":"test","object":{"prop1":1,"prop2":2}}'; + + await IOUtils.writeUTF8(store.path, string, { + tmpPath: store.path + ".tmp", + }); + + await store.load(); + + Assert.deepEqual(store.data, TEST_DATA); +}); + +/** + * Loads data from a malformed JSON string. + */ +add_task(async function test_load_string_malformed() { + let store = new JSONFile({ + path: getTempFile(TEST_STORE_FILE_NAME).path, + }); + + let string = '{"number":123,"string":"test","object":{"prop1":1,'; + + await IOUtils.writeUTF8(store.path, string, { + tmpPath: store.path + ".tmp", + }); + + await store.load(); + + // A backup file should have been created. + Assert.ok(await IOUtils.exists(store.path + ".corrupt")); + await IOUtils.remove(store.path + ".corrupt"); + + // The store should be ready to accept new data. + Assert.ok(store.dataReady); + Assert.deepEqual(store.data, {}); +}); + +/** + * Loads data from a malformed JSON string, using the synchronous initialization + * path. + */ +add_task(async function test_load_string_malformed_sync() { + let store = new JSONFile({ + path: getTempFile(TEST_STORE_FILE_NAME).path, + }); + + let string = '{"number":123,"string":"test","object":{"prop1":1,'; + + await IOUtils.writeUTF8(store.path, string, { + tmpPath: store.path + ".tmp", + }); + + store.ensureDataReady(); + + // A backup file should have been created. + Assert.ok(await IOUtils.exists(store.path + ".corrupt")); + await IOUtils.remove(store.path + ".corrupt"); + + // The store should be ready to accept new data. + Assert.ok(store.dataReady); + Assert.deepEqual(store.data, {}); +}); + +add_task(async function test_overwrite_data() { + let storeForSave = new JSONFile({ + path: getTempFile(TEST_STORE_FILE_NAME).path, + }); + + let string = `{"number":456,"string":"tset","object":{"prop1":3,"prop2":4}}`; + + await IOUtils.writeUTF8(storeForSave.path, string, { + tmpPath: storeForSave.path + ".tmp", + }); + + Assert.ok(!storeForSave.dataReady); + storeForSave.data = TEST_DATA; + Assert.ok(storeForSave.dataReady); + Assert.equal(storeForSave.data, TEST_DATA); + + await new Promise(resolve => { + let save = storeForSave._save.bind(storeForSave); + storeForSave._save = () => { + save(); + resolve(); + }; + storeForSave.saveSoon(); + }); + + let storeForLoad = new JSONFile({ + path: storeForSave.path, + }); + + await storeForLoad.load(); + + Assert.deepEqual(storeForLoad.data, TEST_DATA); +}); + +add_task(async function test_beforeSave() { + let store; + let promiseBeforeSave = new Promise(resolve => { + store = new JSONFile({ + path: getTempFile(TEST_STORE_FILE_NAME).path, + beforeSave: resolve, + saveDelayMs: 250, + }); + }); + + store.saveSoon(); + + await promiseBeforeSave; +}); + +add_task(async function test_beforeSave_rejects() { + let storeForSave = new JSONFile({ + path: getTempFile(TEST_STORE_FILE_NAME).path, + beforeSave() { + return Promise.reject(new Error("oops")); + }, + saveDelayMs: 250, + }); + + let promiseSave = new Promise((resolve, reject) => { + let save = storeForSave._save.bind(storeForSave); + storeForSave._save = () => { + save().then(resolve, reject); + }; + storeForSave.saveSoon(); + }); + + await Assert.rejects(promiseSave, function (ex) { + return ex.message == "oops"; + }); +}); + +add_task(async function test_finalize() { + let path = getTempFile(TEST_STORE_FILE_NAME).path; + + let barrier = new AsyncShutdown.Barrier("test-auto-finalize"); + let storeForSave = new JSONFile({ + path, + saveDelayMs: 2000, + finalizeAt: barrier.client, + }); + await storeForSave.load(); + storeForSave.data = TEST_DATA; + storeForSave.saveSoon(); + + let promiseFinalize = storeForSave.finalize(); + await Assert.rejects(storeForSave.finalize(), /has already been finalized$/); + await promiseFinalize; + Assert.ok(!storeForSave.dataReady); + + // Finalization removes the blocker, so waiting should not log an unhandled + // error even though the object has been explicitly finalized. + await barrier.wait(); + + let storeForLoad = new JSONFile({ path }); + await storeForLoad.load(); + Assert.deepEqual(storeForLoad.data, TEST_DATA); +}); + +add_task(async function test_finalize_on_shutdown() { + let path = getTempFile(TEST_STORE_FILE_NAME).path; + + let barrier = new AsyncShutdown.Barrier("test-finalize-shutdown"); + let storeForSave = new JSONFile({ + path, + saveDelayMs: 2000, + finalizeAt: barrier.client, + }); + await storeForSave.load(); + storeForSave.data = TEST_DATA; + // Arm the saver, then simulate shutdown and ensure the file is + // automatically finalized. + storeForSave.saveSoon(); + + await barrier.wait(); + // It's possible for `finalize` to reject when called concurrently with + // shutdown. We don't distinguish between explicit `finalize` calls and + // finalization on shutdown because we expect most consumers to rely on the + // latter. However, this behavior can be safely changed if needed. + await Assert.rejects(storeForSave.finalize(), /has already been finalized$/); + Assert.ok(!storeForSave.dataReady); + + let storeForLoad = new JSONFile({ path }); + await storeForLoad.load(); + Assert.deepEqual(storeForLoad.data, TEST_DATA); +}); diff --git a/toolkit/modules/tests/xpcshell/test_JsonSchema.js b/toolkit/modules/tests/xpcshell/test_JsonSchema.js new file mode 100644 index 0000000000..b3a09049ac --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_JsonSchema.js @@ -0,0 +1,88 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { JsonSchema } = ChromeUtils.importESModule( + "resource://gre/modules/JsonSchema.sys.mjs" +); + +add_task(function test_basicSchema() { + info("Testing validation of a basic schema"); + const schema = { + type: "object", + properties: { + id: { type: "number" }, + }, + required: ["id"], + }; + + const validator = new JsonSchema.Validator(schema); + + Assert.deepEqual( + JsonSchema.validate({ id: 123 }, schema), + { valid: true, errors: [] }, + "Validation of basic schemas with validate()" + ); + + Assert.deepEqual( + validator.validate({ id: 123 }, schema), + { valid: true, errors: [] }, + "Validation of basic schemas with Validator" + ); + + Assert.ok( + !JsonSchema.validate({}, schema).valid, + "Validation of basic schemas with validate()" + ); + Assert.ok( + !validator.validate({}).valid, + "Validation of basic schemas with Validator" + ); +}); + +add_task(function test_mozUrlFormat() { + info("Testing custom string format 'moz-url-format'"); + const schema = { + type: "string", + format: "moz-url-format", + }; + + { + const obj = "https://www.mozilla.org/%LOCALE%/"; + Assert.deepEqual( + JsonSchema.validate(obj, schema), + { valid: true, errors: [] }, + "Substitution of a valid variable validates" + ); + } + + { + const obj = "https://mozilla.org/%BOGUS%/"; + + Assert.equal( + Services.urlFormatter.formatURL(obj), + obj, + "BOGUS is an invalid variable for the URL formatter service" + ); + + Assert.ok( + !JsonSchema.validate(obj, { type: "string", format: "uri" }).valid, + "A moz-url-format string does not validate as a URI" + ); + + Assert.deepEqual( + JsonSchema.validate(obj, schema), + { + valid: false, + errors: [ + { + instanceLocation: "#", + keyword: "format", + keywordLocation: "#/format", + error: `String does not match format "moz-url-format".`, + }, + ], + }, + "Substitution of an invalid variable does not validate" + ); + } +}); diff --git a/toolkit/modules/tests/xpcshell/test_Log.js b/toolkit/modules/tests/xpcshell/test_Log.js new file mode 100644 index 0000000000..c9fc367dc3 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_Log.js @@ -0,0 +1,424 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { Log } = ChromeUtils.importESModule( + "resource://gre/modules/Log.sys.mjs" +); + +Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true); +registerCleanupFunction(() => { + Services.prefs.clearUserPref("security.allow_eval_with_system_principal"); +}); + +class MockAppender extends Log.Appender { + constructor(formatter) { + super(formatter); + this.messages = []; + } + + doAppend(message) { + this.messages.push(message); + } +} + +add_task(function test_Logger() { + let log = Log.repository.getLogger("test.logger"); + let appender = new MockAppender(new Log.BasicFormatter()); + + log.level = Log.Level.Debug; + appender.level = Log.Level.Info; + log.addAppender(appender); + log.info("info test"); + log.debug("this should be logged but not appended."); + + Assert.equal(appender.messages.length, 1); + + let msgRe = /\d+\ttest.logger\t\INFO\tinfo test/; + Assert.ok(msgRe.test(appender.messages[0])); +}); + +add_task(function test_Logger_parent() { + // Check whether parenting is correct + let grandparentLog = Log.repository.getLogger("grandparent"); + let childLog = Log.repository.getLogger("grandparent.parent.child"); + Assert.equal(childLog.parent.name, "grandparent"); + + Log.repository.getLogger("grandparent.parent"); + Assert.equal(childLog.parent.name, "grandparent.parent"); + + // Check that appends are exactly in scope + let gpAppender = new MockAppender(new Log.BasicFormatter()); + gpAppender.level = Log.Level.Info; + grandparentLog.addAppender(gpAppender); + childLog.info("child info test"); + Log.repository.rootLogger.info("this shouldn't show up in gpAppender"); + + Assert.equal(gpAppender.messages.length, 1); + Assert.ok(gpAppender.messages[0].indexOf("child info test") > 0); +}); + +/* + * Test parameter formatting. + */ +add_task(async function log_message_with_params() { + let formatter = new Log.BasicFormatter(); + + function formatMessage(text, params) { + let full = formatter.format( + new Log.LogMessage("test.logger", Log.Level.Warn, text, params) + ); + return full.split("\t")[3]; + } + + // Strings are substituted directly. + Assert.equal( + formatMessage("String is ${foo}", { foo: "bar" }), + "String is bar" + ); + + // Numbers are substituted. + Assert.equal( + formatMessage("Number is ${number}", { number: 47 }), + "Number is 47" + ); + + // The entire params object is JSON-formatted and substituted. + Assert.equal( + formatMessage("Object is ${}", { foo: "bar" }), + 'Object is {"foo":"bar"}' + ); + + // An object nested inside params is JSON-formatted and substituted. + Assert.equal( + formatMessage("Sub object is ${sub}", { sub: { foo: "bar" } }), + 'Sub object is {"foo":"bar"}' + ); + + // The substitution field is missing from params. Leave the placeholder behind + // to make the mistake obvious. + Assert.equal( + formatMessage("Missing object is ${missing}", {}), + "Missing object is ${missing}" + ); + + // Make sure we don't treat the parameter name 'false' as a falsey value. + Assert.equal( + formatMessage("False is ${false}", { false: true }), + "False is true" + ); + + // If an object has a .toJSON method, the formatter uses it. + let ob = function () {}; + ob.toJSON = function () { + return { sneaky: "value" }; + }; + Assert.equal( + formatMessage("JSON is ${sub}", { sub: ob }), + 'JSON is {"sneaky":"value"}' + ); + + // Fall back to .toSource() if JSON.stringify() fails on an object. + ob = function () {}; + ob.toJSON = function () { + throw new Error("oh noes JSON"); + }; + Assert.equal( + formatMessage("Fail is ${sub}", { sub: ob }), + "Fail is (function () {})" + ); + + // Fall back to .toString if both .toJSON and .toSource fail. + ob.toSource = function () { + throw new Error("oh noes SOURCE"); + }; + Assert.equal( + formatMessage("Fail is ${sub}", { sub: ob }), + "Fail is function () {}" + ); + + // Fall back to '[object]' if .toJSON, .toSource and .toString fail. + ob.toString = function () { + throw new Error("oh noes STRING"); + }; + Assert.equal( + formatMessage("Fail is ${sub}", { sub: ob }), + "Fail is [object]" + ); + + // If params are passed but there are no substitution in the text + // we JSON format and append the entire parameters object. + Assert.equal( + formatMessage("Text with no subs", { a: "b", c: "d" }), + 'Text with no subs: {"a":"b","c":"d"}' + ); + + // If we substitute one parameter but not the other, + // we ignore any params that aren't substituted. + Assert.equal( + formatMessage("Text with partial sub ${a}", { a: "b", c: "d" }), + "Text with partial sub b" + ); + + // We don't format internal fields stored in params. + Assert.equal( + formatMessage("Params with _ ${}", { + a: "b", + _c: "d", + _level: 20, + _message: "froo", + _time: 123456, + _namespace: "here.there", + }), + 'Params with _ {"a":"b","_c":"d"}' + ); + + // Don't print an empty params holder if all params are internal. + Assert.equal( + formatMessage("All params internal", { + _level: 20, + _message: "froo", + _time: 123456, + _namespace: "here.there", + }), + "All params internal" + ); + + // Format params with null and undefined values. + Assert.equal( + formatMessage("Null ${n} undefined ${u}", { n: null, u: undefined }), + "Null null undefined undefined" + ); + + // Format params with number, bool, and String type. + Assert.equal( + formatMessage("number ${n} boolean ${b} boxed Boolean ${bx} String ${s}", { + n: 45, + b: false, + bx: Boolean(true), + s: String("whatevs"), + }), + "number 45 boolean false boxed Boolean true String whatevs" + ); + + // Format params with consecutive tokens. + Assert.equal( + formatMessage("${a}${b}${c}", { a: "foo", b: "bar", c: "baz" }), + "foobarbaz" + ); + + /* + * Check that errors get special formatting if they're formatted directly as + * a named param or they're the only param, but not if they're a field in a + * larger structure. + */ + let err = Components.Exception("test exception", Cr.NS_ERROR_FAILURE); + let str = formatMessage("Exception is ${}", err); + Assert.ok(str.includes('Exception is [Exception... "test exception"')); + Assert.ok(str.includes("(NS_ERROR_FAILURE)")); + str = formatMessage("Exception is", err); + Assert.ok(str.includes('Exception is: [Exception... "test exception"')); + str = formatMessage("Exception is ${error}", { error: err }); + Assert.ok(str.includes('Exception is [Exception... "test exception"')); + str = formatMessage("Exception is", { _error: err }); + info(str); + // Exceptions buried inside objects are formatted badly. + Assert.ok(str.includes('Exception is: {"_error":{}')); + // If the message text is null, the message contains only the formatted params object. + str = formatMessage(null, err); + Assert.ok(str.startsWith('[Exception... "test exception"')); + // If the text is null and 'params' is a string, the message is exactly that string. + str = formatMessage(null, "String in place of params"); + Assert.equal(str, "String in place of params"); + + // We use object.valueOf() internally; make sure a broken valueOf() method + // doesn't cause the logger to fail. + /* eslint-disable object-shorthand */ + let vOf = { + a: 1, + valueOf: function () { + throw new Error("oh noes valueOf"); + }, + }; + Assert.equal( + formatMessage("Broken valueOf ${}", vOf), + 'Broken valueOf ({a:1, valueOf:(function () {\n throw new Error("oh noes valueOf");\n })})' + ); + /* eslint-enable object-shorthand */ + + // Test edge cases of bad data to formatter: + // If 'params' is not an object, format it as a basic type. + Assert.equal( + formatMessage("non-object no subst", 1), + "non-object no subst: 1" + ); + Assert.equal( + formatMessage("non-object all subst ${}", 2), + "non-object all subst 2" + ); + Assert.equal(formatMessage("false no subst", false), "false no subst: false"); + Assert.equal(formatMessage("null no subst", null), "null no subst: null"); + // If 'params' is undefined and there are no substitutions expected, + // the message should still be output. + Assert.equal( + formatMessage("undefined no subst", undefined), + "undefined no subst" + ); + // If 'params' is not an object, no named substitutions can succeed; + // therefore we leave the placeholder and append the formatted params. + Assert.equal( + formatMessage("non-object named subst ${junk} space", 3), + "non-object named subst ${junk} space: 3" + ); + // If there are no params, we leave behind the placeholders in the text. + Assert.equal( + formatMessage("no params ${missing}", undefined), + "no params ${missing}" + ); + // If params doesn't contain any of the tags requested in the text, + // we leave them all behind and append the formatted params. + Assert.equal( + formatMessage("object missing tag ${missing} space", { + mising: "not here", + }), + 'object missing tag ${missing} space: {"mising":"not here"}' + ); + // If we are given null text and no params, the resulting formatted message is empty. + Assert.equal(formatMessage(null), ""); +}); + +/* + * If we call a log function with a non-string object in place of the text + * argument, and no parameters, treat that the same as logging empty text + * with the object argument as parameters. This makes the log useful when the + * caller does "catch(err) {logger.error(err)}" + */ +add_task(async function test_log_err_only() { + let log = Log.repository.getLogger("error.only"); + let mockFormatter = { format: msg => msg }; + let appender = new MockAppender(mockFormatter); + log.addAppender(appender); + + /* + * Check that log.error(err) is treated the same as + * log.error(null, err) by the logMessage constructor; the formatMessage() + * tests above ensure that the combination of null text and an error object + * is formatted correctly. + */ + try { + // eslint-disable-next-line no-eval + eval("javascript syntax error"); + } catch (e) { + log.error(e); + let msg = appender.messages.pop(); + Assert.equal(msg.message, null); + Assert.equal(msg.params, e); + } +}); + +/* + * Test that all the basic logger methods pass the message and params through to the appender. + */ +add_task(async function log_message_with_params() { + let log = Log.repository.getLogger("error.logger"); + let mockFormatter = { format: msg => msg }; + let appender = new MockAppender(mockFormatter); + log.addAppender(appender); + + let testParams = { a: 1, b: 2 }; + log.fatal("Test fatal", testParams); + log.error("Test error", testParams); + log.warn("Test warn", testParams); + log.info("Test info", testParams); + log.config("Test config", testParams); + log.debug("Test debug", testParams); + log.trace("Test trace", testParams); + Assert.equal(appender.messages.length, 7); + for (let msg of appender.messages) { + Assert.ok(msg.params === testParams); + Assert.ok(msg.message.startsWith("Test ")); + } +}); + +/* + * Test that all the basic logger methods support tagged template literal format. + */ +add_task(async function log_template_literal_message() { + let log = Log.repository.getLogger("error.logger"); + let appender = new MockAppender(new Log.BasicFormatter()); + log.addAppender(appender); + + log.fatal`Test ${"foo"} ${42}`; + log.error`Test ${"foo"} 42`; + log.warn`Test foo 42`; + log.info`Test ${"foo " + 42}`; + log.config`${"Test"} foo ${42}`; + log.debug`Test ${"f"}${"o"}${"o"} 42`; + log.trace`${"Test foo 42"}`; + Assert.equal(appender.messages.length, 7); + for (let msg of appender.messages) { + Assert.equal(msg.split("\t")[3], "Test foo 42"); + } +}); + +/* + * Check that we format JS Errors reasonably. + * This needs to stay a generator to exercise Task.jsm's stack rewriting. + */ +add_task(async function format_errors() { + let pFormat = new Log.ParameterFormatter(); + + // Test that subclasses of Error are recognized as errors. + let err = new ReferenceError("Ref Error", "ERROR_FILE", 28); + let str = pFormat.format(err); + Assert.ok(str.includes("ReferenceError")); + Assert.ok(str.includes("ERROR_FILE:28")); + Assert.ok(str.includes("Ref Error")); + + // Test that JS-generated Errors are recognized and formatted. + try { + await Promise.resolve(); // Scrambles the stack + // eslint-disable-next-line no-eval + eval("javascript syntax error"); + } catch (e) { + str = pFormat.format(e); + Assert.ok(str.includes("SyntaxError: unexpected token")); + // Make sure we identified it as an Error and formatted the error location as + // lineNumber:columnNumber. + Assert.ok(str.includes(":1:12)")); + // Make sure that we use human-readable stack traces + // Check that the error doesn't contain any reference to "Task.jsm" + Assert.ok(!str.includes("Task.jsm")); + Assert.ok(str.includes("format_errors")); + } +}); + +/* + * Check that automatic support for setting the log level via preferences + * works. + */ +add_test(function test_prefs() { + let log = Log.repository.getLogger("error.logger"); + log.level = Log.Level.Debug; + Services.prefs.setStringPref("logger.test", "Error"); + log.manageLevelFromPref("logger.test"); + // check initial pref value is set up. + equal(log.level, Log.Level.Error); + Services.prefs.setStringPref("logger.test", "Error"); + // check changing the pref causes the level to change. + Services.prefs.setStringPref("logger.test", "Trace"); + equal(log.level, Log.Level.Trace); + // invalid values cause nothing to happen. + Services.prefs.setStringPref("logger.test", "invalid-level-value"); + equal(log.level, Log.Level.Trace); + Services.prefs.setIntPref("logger.test", 123); + equal(log.level, Log.Level.Trace); + // setting a "sub preference" shouldn't impact it. + Services.prefs.setStringPref("logger.test.foo", "Debug"); + equal(log.level, Log.Level.Trace); + // clearing the level param should cause it to use the parent level. + Log.repository.getLogger("error").level = Log.Level.All; + Services.prefs.setStringPref("logger.test", ""); + equal(log.level, Log.Level.All); + + run_next_test(); +}); diff --git a/toolkit/modules/tests/xpcshell/test_Log_double_ext.js b/toolkit/modules/tests/xpcshell/test_Log_double_ext.js new file mode 100644 index 0000000000..960cf1be06 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_Log_double_ext.js @@ -0,0 +1,24 @@ +const { Log } = ChromeUtils.importESModule( + "resource://gre/modules/Log.sys.mjs" +); + +function run_test() { + // Verify error with double extensions are handled correctly. + + const nsIExceptionTrace = Log.stackTrace({ + location: { + filename: "chrome://foo/bar/file.sys.mjs", + lineNumber: 10, + name: "func", + caller: null, + }, + }); + + Assert.equal(nsIExceptionTrace, "Stack trace: func()@file.sys.mjs:10"); + + const JSExceptionTrace = Log.stackTrace({ + stack: "func2@chrome://foo/bar/file.sys.mjs:10:2", + }); + + Assert.equal(JSExceptionTrace, "JS Stack trace: func2@file.sys.mjs:10:2"); +} diff --git a/toolkit/modules/tests/xpcshell/test_Log_nsIStackFrame.js b/toolkit/modules/tests/xpcshell/test_Log_nsIStackFrame.js new file mode 100644 index 0000000000..b7fdd511e1 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_Log_nsIStackFrame.js @@ -0,0 +1,59 @@ +const { Log } = ChromeUtils.importESModule( + "resource://gre/modules/Log.sys.mjs" +); + +function foo() { + return bar(); // line 6 +} + +function bar() { + return baz(); // line 10 +} + +function baz() { + return Log.stackTrace(Components.stack.caller); // line 14 +} + +function start() { + return next(); // line 18 +} + +function next() { + return finish(); // line 22 +} + +function finish() { + return Log.stackTrace(); // line 26 +} + +function run_test() { + const stackFrameTrace = foo(); // line 30 + + print(`Got trace for nsIStackFrame case: ${stackFrameTrace}`); + + const fooPos = stackFrameTrace.search(/foo@.*test_Log_nsIStackFrame.js:6/); + const barPos = stackFrameTrace.search(/bar@.*test_Log_nsIStackFrame.js:10/); + const runTestPos = stackFrameTrace.search( + /run_test@.*test_Log_nsIStackFrame.js:30/ + ); + + print(`String positions: ${runTestPos} ${barPos} ${fooPos}`); + Assert.ok(barPos >= 0); + Assert.ok(fooPos > barPos); + Assert.ok(runTestPos > fooPos); + + const emptyArgTrace = start(); + + print(`Got trace for empty argument case: ${emptyArgTrace}`); + + const startPos = emptyArgTrace.search(/start@.*test_Log_nsIStackFrame.js:18/); + const nextPos = emptyArgTrace.search(/next@.*test_Log_nsIStackFrame.js:22/); + const finishPos = emptyArgTrace.search( + /finish@.*test_Log_nsIStackFrame.js:26/ + ); + + print(`String positions: ${finishPos} ${nextPos} ${startPos}`); + Assert.ok(finishPos >= 0); + Assert.ok(nextPos > finishPos); + Assert.ok(startPos > nextPos); +} diff --git a/toolkit/modules/tests/xpcshell/test_Log_stackTrace.js b/toolkit/modules/tests/xpcshell/test_Log_stackTrace.js new file mode 100644 index 0000000000..973cb9350b --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_Log_stackTrace.js @@ -0,0 +1,39 @@ +print("Define some functions in well defined line positions for the test"); +function foo(v) { + return bar(v + 1); +} // line 3 +function bar(v) { + return baz(v + 1); +} // line 6 +function baz(v) { + throw new Error(v + 1); +} // line 9 + +print("Make sure lazy constructor calling/assignment works"); +const { Log } = ChromeUtils.importESModule( + "resource://gre/modules/Log.sys.mjs" +); + +function run_test() { + print( + "Make sure functions, arguments, files are pretty printed in the trace" + ); + let trace = ""; + try { + foo(0); + } catch (ex) { + trace = Log.stackTrace(ex); + } + print(`Got trace: ${trace}`); + Assert.notEqual(trace, ""); + + let bazPos = trace.indexOf("baz@test_Log_stackTrace.js:9"); + let barPos = trace.indexOf("bar@test_Log_stackTrace.js:6"); + let fooPos = trace.indexOf("foo@test_Log_stackTrace.js:3"); + print(`String positions: ${bazPos} ${barPos} ${fooPos}`); + + print("Make sure the desired messages show up"); + Assert.ok(bazPos >= 0); + Assert.ok(barPos > bazPos); + Assert.ok(fooPos > barPos); +} diff --git a/toolkit/modules/tests/xpcshell/test_MatchURLFilters.js b/toolkit/modules/tests/xpcshell/test_MatchURLFilters.js new file mode 100644 index 0000000000..e9e8813b77 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_MatchURLFilters.js @@ -0,0 +1,844 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { MatchURLFilters } = ChromeUtils.importESModule( + "resource://gre/modules/MatchURLFilters.sys.mjs" +); + +const { Preferences } = ChromeUtils.importESModule( + "resource://gre/modules/Preferences.sys.mjs" +); + +function createTestFilter({ url, filters }) { + let m = new MatchURLFilters(filters); + return m.matches(url); +} + +function expectPass({ url, filters }) { + ok( + createTestFilter({ url, filters }), + `Expected match: ${JSON.stringify(filters)}, ${url}` + ); +} + +function expectFail({ url, filters }) { + ok( + !createTestFilter({ url, filters }), + `Expected no match: ${JSON.stringify(filters)}, ${url}` + ); +} + +function expectThrow({ url, filters, exceptionMessageContains }) { + let logData = { filters, url }; + + Assert.throws( + () => { + createTestFilter({ url, filters }); + }, + exceptionMessageContains, + `Check received exception for expected message: ${JSON.stringify(logData)}` + ); +} + +add_task(async function test_match_url_filters() { + const shouldPass = true; + const shouldFail = true; + const shouldThrow = true; + + var testCases = [ + // Empty, undefined and null filters. + { + shouldThrow, + exceptionMessageContains: /filters array should not be empty/, + filters: [], + url: "http://mozilla.org", + }, + { + shouldThrow, + exceptionMessageContains: /filters should be an array/, + filters: undefined, + url: "http://mozilla.org", + }, + { + shouldThrow, + exceptionMessageContains: /filters should be an array/, + filters: null, + url: "http://mozilla.org", + }, + + // Wrong formats (in a real webextension this will be blocked by the schema validation). + { + shouldThrow, + exceptionMessageContains: /filters should be an array/, + filters: {}, + url: "http://mozilla.org", + }, + { + shouldThrow, + exceptionMessageContains: /filters should be an array/, + filters: { nonExistentCriteria: true }, + url: "http://mozilla.org", + }, + { + shouldPass, + filters: [{ nonExistentCriteria: true }], + url: "http://mozilla.org", + }, + + // Schemes filter over various url schemes. + { shouldPass, filters: [{ schemes: ["http"] }], url: "http://mozilla.org" }, + { + shouldPass, + filters: [{ schemes: ["https"] }], + url: "https://mozilla.org", + }, + { shouldPass, filters: [{ schemes: ["ftp"] }], url: "ftp://fake/ftp/url" }, + { shouldPass, filters: [{ schemes: ["about"] }], url: "about:blank" }, + { shouldPass, filters: [{ schemes: ["data"] }], url: "data:,testDataURL" }, + { shouldFail, filters: [{ schemes: ["http"] }], url: "ftp://fake/ftp/url" }, + + // Multiple schemes: pass when at least one scheme matches. + { + shouldPass, + filters: [{ schemes: ["https", "about"] }], + url: "https://mozilla.org", + }, + { + shouldPass, + filters: [{ schemes: ["about", "https"] }], + url: "https://mozilla.org", + }, + { + shouldFail, + filters: [{ schemes: ["about", "http"] }], + url: "https://mozilla.org", + }, + + // Port filter: standard (implicit) ports. + { shouldPass, filters: [{ ports: [443] }], url: "https://mozilla.org" }, + { shouldPass, filters: [{ ports: [80] }], url: "http://mozilla.org" }, + + // Port matching unknown protocols will fail. + { + shouldFail, + filters: [{ ports: [21] }], + url: "ftp://ftp.mozilla.org", + }, + + // Port filter: schemes without a default port. + { shouldFail, filters: [{ ports: [-1] }], url: "about:blank" }, + { shouldFail, filters: [{ ports: [-1] }], url: "data:,testDataURL" }, + + { shouldFail, filters: [{ ports: [[1, 65535]] }], url: "about:blank" }, + { + shouldFail, + filters: [{ ports: [[1, 65535]] }], + url: "data:,testDataURL", + }, + + // Host filters (hostEquals, hostContains, hostPrefix, hostSuffix): schemes with an host. + { shouldFail, filters: [{ hostEquals: "" }], url: "https://mozilla.org" }, + { shouldPass, filters: [{ hostEquals: null }], url: "https://mozilla.org" }, + { + shouldPass, + filters: [{ hostEquals: "mozilla.org" }], + url: "https://mozilla.org", + }, + { + shouldFail, + filters: [{ hostEquals: "mozilla.com" }], + url: "https://mozilla.org", + }, + // NOTE: trying at least once another valid protocol. + { + shouldPass, + filters: [{ hostEquals: "mozilla.org" }], + url: "ftp://mozilla.org", + }, + { + shouldFail, + filters: [{ hostEquals: "mozilla.com" }], + url: "ftp://mozilla.org", + }, + { + shouldPass, + filters: [{ hostEquals: "mozilla.org" }], + url: "https://mozilla.org:8888", + }, + + { + shouldPass, + filters: [{ hostContains: "moz" }], + url: "https://mozilla.org", + }, + // NOTE: an implicit '.' char is inserted into the host. + { + shouldPass, + filters: [{ hostContains: ".moz" }], + url: "https://mozilla.org", + }, + { + shouldFail, + filters: [{ hostContains: "com" }], + url: "https://mozilla.org", + }, + { shouldPass, filters: [{ hostContains: "" }], url: "https://mozilla.org" }, + { + shouldPass, + filters: [{ hostContains: null }], + url: "https://mozilla.org", + }, + + { + shouldPass, + filters: [{ hostPrefix: "moz" }], + url: "https://mozilla.org", + }, + { + shouldFail, + filters: [{ hostPrefix: "org" }], + url: "https://mozilla.org", + }, + { shouldPass, filters: [{ hostPrefix: "" }], url: "https://mozilla.org" }, + { shouldPass, filters: [{ hostPrefix: null }], url: "https://mozilla.org" }, + + { + shouldPass, + filters: [{ hostSuffix: ".org" }], + url: "https://mozilla.org", + }, + { + shouldFail, + filters: [{ hostSuffix: "moz" }], + url: "https://mozilla.org", + }, + { shouldPass, filters: [{ hostSuffix: "" }], url: "https://mozilla.org" }, + { shouldPass, filters: [{ hostSuffix: null }], url: "https://mozilla.org" }, + { + shouldPass, + filters: [{ hostSuffix: "lla.org" }], + url: "https://mozilla.org:8888", + }, + + // hostEquals: urls without an host. + // TODO: should we explicitly cover hostContains, hostPrefix, hostSuffix for + // these sub-cases? + { shouldFail, filters: [{ hostEquals: "blank" }], url: "about:blank" }, + { shouldFail, filters: [{ hostEquals: "blank" }], url: "about://blank" }, + { + shouldFail, + filters: [{ hostEquals: "testDataURL" }], + url: "data:,testDataURL", + }, + { shouldPass, filters: [{ hostEquals: "" }], url: "about:blank" }, + { shouldPass, filters: [{ hostEquals: "" }], url: "about://blank" }, + { shouldPass, filters: [{ hostEquals: "" }], url: "data:,testDataURL" }, + + // Path filters (pathEquals, pathContains, pathPrefix, pathSuffix). + { + shouldFail, + filters: [{ pathEquals: "" }], + url: "https://mozilla.org/test/path", + }, + { + shouldPass, + filters: [{ pathEquals: null }], + url: "https://mozilla.org/test/path", + }, + { + shouldPass, + filters: [{ pathEquals: "/test/path" }], + url: "https://mozilla.org/test/path", + }, + { + shouldFail, + filters: [{ pathEquals: "/wrong/path" }], + url: "https://mozilla.org/test/path", + }, + { + shouldPass, + filters: [{ pathEquals: "/test/path" }], + url: "https://mozilla.org:8888/test/path", + }, + // NOTE: trying at least once another valid protocol + { + shouldPass, + filters: [{ pathEquals: "/test/path" }], + url: "ftp://mozilla.org/test/path", + }, + { + shouldFail, + filters: [{ pathEquals: "/wrong/path" }], + url: "ftp://mozilla.org/test/path", + }, + + { + shouldPass, + filters: [{ pathContains: "st/" }], + url: "https://mozilla.org/test/path", + }, + { + shouldPass, + filters: [{ pathContains: "/test" }], + url: "https://mozilla.org/test/path", + }, + { + shouldFail, + filters: [{ pathContains: "org" }], + url: "https://mozilla.org/test/path", + }, + { + shouldPass, + filters: [{ pathContains: "" }], + url: "https://mozilla.org/test/path", + }, + { + shouldPass, + filters: [{ pathContains: null }], + url: "https://mozilla.org/test/path", + }, + { + shouldFail, + filters: [{ pathContains: "param" }], + url: "https://mozilla.org:8888/test/path?param=1", + }, + { + shouldFail, + filters: [{ pathContains: "ref" }], + url: "https://mozilla.org:8888/test/path#ref", + }, + { + shouldPass, + filters: [{ pathContains: "st/pa" }], + url: "https://mozilla.org:8888/test/path", + }, + + { + shouldPass, + filters: [{ pathPrefix: "/te" }], + url: "https://mozilla.org/test/path", + }, + { + shouldFail, + filters: [{ pathPrefix: "org/" }], + url: "https://mozilla.org/test/path", + }, + { + shouldPass, + filters: [{ pathPrefix: "" }], + url: "https://mozilla.org/test/path", + }, + { + shouldPass, + filters: [{ pathPrefix: null }], + url: "https://mozilla.org/test/path", + }, + + { + shouldPass, + filters: [{ pathSuffix: "/path" }], + url: "https://mozilla.org/test/path", + }, + { + shouldFail, + filters: [{ pathSuffix: "th/" }], + url: "https://mozilla.org/test/path", + }, + { + shouldPass, + filters: [{ pathSuffix: "" }], + url: "https://mozilla.org/test/path", + }, + { + shouldPass, + filters: [{ pathSuffix: null }], + url: "https://mozilla.org/test/path", + }, + { + shouldFail, + filters: [{ pathSuffix: "p=1" }], + url: "https://mozilla.org:8888/test/path?p=1", + }, + { + shouldFail, + filters: [{ pathSuffix: "ref" }], + url: "https://mozilla.org:8888/test/path#ref", + }, + + // Query filters (queryEquals, queryContains, queryPrefix, querySuffix). + { + shouldFail, + filters: [{ queryEquals: "" }], + url: "https://mozilla.org/?param=val", + }, + { + shouldPass, + filters: [{ queryEquals: null }], + url: "https://mozilla.org/?param=val", + }, + { + shouldPass, + filters: [{ queryEquals: "param=val" }], + url: "https://mozilla.org/?param=val", + }, + { + shouldFail, + filters: [{ queryEquals: "?param=val" }], + url: "https://mozilla.org/?param=val", + }, + { + shouldFail, + filters: [{ queryEquals: "/path?param=val" }], + url: "https://mozilla.org/path?param=val", + }, + + // NOTE: about scheme urls cannot be matched by query. + { + shouldFail, + filters: [{ queryEquals: "param=val" }], + url: "about:blank?param=val", + }, + { + shouldFail, + filters: [{ queryEquals: "param" }], + url: "ftp://mozilla.org?param=val", + }, + + { + shouldPass, + filters: [{ queryContains: "ram" }], + url: "https://mozilla.org/?param=val", + }, + { + shouldPass, + filters: [{ queryContains: "=val" }], + url: "https://mozilla.org/?param=val", + }, + { + shouldFail, + filters: [{ queryContains: "?param" }], + url: "https://mozilla.org/?param=val", + }, + { + shouldFail, + filters: [{ queryContains: "path" }], + url: "https://mozilla.org/path/?p=v#ref", + }, + { + shouldPass, + filters: [{ queryContains: "" }], + url: "https://mozilla.org/?param=val", + }, + { + shouldPass, + filters: [{ queryContains: null }], + url: "https://mozilla.org/?param=val", + }, + + { + shouldPass, + filters: [{ queryPrefix: "param" }], + url: "https://mozilla.org/?param=val", + }, + { + shouldFail, + filters: [{ queryPrefix: "p=" }], + url: "https://mozilla.org/?param=val", + }, + { + shouldFail, + filters: [{ queryPrefix: "path" }], + url: "https://mozilla.org/path?param=val", + }, + { + shouldPass, + filters: [{ queryPrefix: "" }], + url: "https://mozilla.org/?param=val", + }, + { + shouldPass, + filters: [{ queryPrefix: null }], + url: "https://mozilla.org/?param=val", + }, + + { + shouldPass, + filters: [{ querySuffix: "=val" }], + url: "https://mozilla.org/?param=val", + }, + { + shouldFail, + filters: [{ querySuffix: "=wrong" }], + url: "https://mozilla.org/?param=val", + }, + { + shouldPass, + filters: [{ querySuffix: "" }], + url: "https://mozilla.org/?param=val", + }, + { + shouldPass, + filters: [{ querySuffix: null }], + url: "https://mozilla.org/?param=val", + }, + + // URL filters (urlEquals, urlContains, urlPrefix, urlSuffix). + { + shouldFail, + filters: [{ urlEquals: "" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldPass, + filters: [{ urlEquals: null }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldPass, + filters: [{ urlEquals: "https://mozilla.org/?p=v#ref" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldFail, + filters: [{ urlEquals: "https://mozilla.org/?p=v#ref2" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldPass, + filters: [{ urlEquals: "about:blank?p=v#ref" }], + url: "about:blank?p=v#ref", + }, + { + shouldPass, + filters: [{ urlEquals: "ftp://mozilla.org?p=v#ref" }], + url: "ftp://mozilla.org?p=v#ref", + }, + + { + shouldPass, + filters: [{ urlContains: "org/?p" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldPass, + filters: [{ urlContains: "=v#ref" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldFail, + filters: [{ urlContains: "ftp" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldPass, + filters: [{ urlContains: "" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldPass, + filters: [{ urlContains: null }], + url: "https://mozilla.org/?p=v#ref", + }, + + { + shouldPass, + filters: [{ urlPrefix: "http" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldFail, + filters: [{ urlPrefix: "moz" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldPass, + filters: [{ urlPrefix: "" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldPass, + filters: [{ urlPrefix: null }], + url: "https://mozilla.org/?p=v#ref", + }, + + { + shouldPass, + filters: [{ urlSuffix: "#ref" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldFail, + filters: [{ urlSuffix: "=wrong" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldPass, + filters: [{ urlSuffix: "" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldPass, + filters: [{ urlSuffix: null }], + url: "https://mozilla.org/?p=v#ref", + }, + + // More url filters: urlMatches. + { + shouldPass, + filters: [{ urlMatches: ".*://mozilla" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldPass, + filters: [{ urlMatches: ".*://mozilla" }], + url: "ftp://mozilla.org/?p=v#ref", + }, + { + shouldPass, + filters: [{ urlMatches: ".*://.*/?p" }], + url: "ftp://mozilla.org/?p=v#ref", + }, + // NOTE: urlMatches should not match the url without the ref. + { + shouldFail, + filters: [{ urlMatches: "v#ref$" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldPass, + filters: [{ urlMatches: "^ftp" }], + url: "ftp://mozilla.org/?p=v#ref", + }, + + // More url filters: originAndPathMatches. + { + shouldPass, + filters: [{ originAndPathMatches: ".*://mozilla" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldPass, + filters: [{ originAndPathMatches: ".*://mozilla" }], + url: "ftp://mozilla.org/?p=v#ref", + }, + // NOTE: urlMatches should not match the url without the query and the ref. + { + shouldFail, + filters: [{ originAndPathMatches: ".*://.*/?p" }], + url: "ftp://mozilla.org/?p=v#ref", + }, + { + shouldFail, + filters: [{ originAndPathMatches: "v#ref$" }], + url: "https://mozilla.org/?p=v#ref", + }, + { + shouldPass, + filters: [{ originAndPathMatches: "^ftp" }], + url: "ftp://mozilla.org/?p=v#ref", + }, + + // Filter with all criteria: all matches, none matches, some matches. + + // All matches. + { + shouldPass, + filters: [ + { + schemes: ["https", "http"], + ports: [443, 80], + hostEquals: "www.mozilla.org", + hostContains: ".moz", + hostPrefix: "www", + hostSuffix: "org", + pathEquals: "/sub/path", + pathContains: "b/p", + pathPrefix: "/sub", + pathSuffix: "/path", + queryEquals: "p1=v", + queryContains: "1=", + queryPrefix: "p1", + querySuffix: "=v", + urlEquals: "https://www.mozilla.org/sub/path?p1=v#ref", + urlContains: "org/sub", + urlPrefix: "https://www.moz", + urlSuffix: "#ref", + urlMatches: "p1=v$", + originAndPathMatches: ".*://www.moz.*/", + }, + ], + url: "https://www.mozilla.org/sub/path?p1=v#ref", + }, + // None matches. + { + shouldFail, + filters: [ + { + schemes: ["http"], + ports: [80], + hostEquals: "mozilla.com", + hostContains: "www.moz", + hostPrefix: "www", + hostSuffix: "com", + pathEquals: "/wrong/path", + pathContains: "g/p", + pathPrefix: "/wrong", + pathSuffix: "/wrong", + queryEquals: "p2=v", + queryContains: "2=", + queryPrefix: "p2", + querySuffix: "=value", + urlEquals: "http://mozilla.com/sub/path?p1=v#ref", + urlContains: "com/sub", + urlPrefix: "http://moz", + urlSuffix: "#ref2", + urlMatches: "value#ref2$", + originAndPathMatches: ".*://moz.*com/", + }, + ], + url: "https://mozilla.org/sub/path?p1=v#ref", + }, + // Some matches + { + shouldFail, + filters: [ + { + schemes: ["https"], + ports: [80], + hostEquals: "mozilla.com", + hostContains: "www.moz", + hostPrefix: "www", + hostSuffix: "com", + pathEquals: "/wrong/path", + pathContains: "g/p", + pathPrefix: "/wrong", + pathSuffix: "/wrong", + queryEquals: "p2=v", + queryContains: "2=", + queryPrefix: "p2", + querySuffix: "=value", + urlEquals: "http://mozilla.com/sub/path?p1=v#ref", + urlContains: "com/sub", + urlPrefix: "http://moz", + urlSuffix: "#ref2", + urlMatches: "value#ref2$", + originAndPathMatches: ".*://moz.*com/", + }, + ], + url: "https://mozilla.org/sub/path?p1=v#ref", + }, + + // Filter with multiple filters: all matches, some matches, none matches. + + // All matches. + { + shouldPass, + filters: [ + { schemes: ["https", "http"] }, + { ports: [443, 80] }, + { hostEquals: "www.mozilla.org" }, + { hostContains: ".moz" }, + { hostPrefix: "www" }, + { hostSuffix: "org" }, + { pathEquals: "/sub/path" }, + { pathContains: "b/p" }, + { pathPrefix: "/sub" }, + { pathSuffix: "/path" }, + { queryEquals: "p=v" }, + { queryContains: "1=" }, + { queryPrefix: "p1" }, + { querySuffix: "=v" }, + { urlEquals: "https://www.mozilla.org/sub/path?p1=v#ref" }, + { urlContains: "org/sub" }, + { urlPrefix: "https://moz" }, + { urlSuffix: "#ref" }, + { urlMatches: "v#ref$" }, + { originAndPathMatches: ".*://moz.*/" }, + ], + url: "https://www.mozilla.org/sub/path?p1=v#ref", + }, + + // None matches. + { + shouldFail, + filters: [ + { schemes: ["http"] }, + { ports: [80] }, + { hostEquals: "mozilla.com" }, + { hostContains: "www.moz" }, + { hostPrefix: "www" }, + { hostSuffix: "com" }, + { pathEquals: "/wrong/path" }, + { pathContains: "g/p" }, + { pathPrefix: "/wrong" }, + { pathSuffix: "/wrong" }, + { queryEquals: "p2=v" }, + { queryContains: "2=" }, + { queryPrefix: "p2" }, + { querySuffix: "=value" }, + { urlEquals: "http://mozilla.com/sub/path?p1=v#ref" }, + { urlContains: "com/sub" }, + { urlPrefix: "http://moz" }, + { urlSuffix: "#ref2" }, + { urlMatches: "value#ref2$" }, + { originAndPathMatches: ".*://moz.*com/" }, + ], + url: "https://mozilla.org/sub/path?p1=v#ref", + }, + + // Some matches. + { + shouldPass, + filters: [ + { schemes: ["https"] }, + { ports: [80] }, + { hostEquals: "mozilla.com" }, + { hostContains: "www.moz" }, + { hostPrefix: "www" }, + { hostSuffix: "com" }, + { pathEquals: "/wrong/path" }, + { pathContains: "g/p" }, + { pathPrefix: "/wrong" }, + { pathSuffix: "/wrong" }, + { queryEquals: "p2=v" }, + { queryContains: "2=" }, + { queryPrefix: "p2" }, + { querySuffix: "=value" }, + { urlEquals: "http://mozilla.com/sub/path?p1=v#ref" }, + { urlContains: "com/sub" }, + { urlPrefix: "http://moz" }, + { urlSuffix: "#ref2" }, + { urlMatches: "value#ref2$" }, + { originAndPathMatches: ".*://moz.*com/" }, + ], + url: "https://mozilla.org/sub/path?p1=v#ref", + }, + ]; + + // Run all the the testCases defined above. + for (let currentTest of testCases) { + let { exceptionMessageContains, url, filters, prefs } = currentTest; + + if (prefs !== undefined) { + for (let [name, val] of prefs) { + Preferences.set(name, val); + } + } + + if (currentTest.shouldThrow) { + expectThrow({ url, filters, exceptionMessageContains }); + } else if (currentTest.shouldFail) { + expectFail({ url, filters }); + } else { + expectPass({ url, filters }); + } + + if (prefs !== undefined) { + for (let [name] of prefs) { + Preferences.reset(name); + } + } + } +}); diff --git a/toolkit/modules/tests/xpcshell/test_NewTabUtils.js b/toolkit/modules/tests/xpcshell/test_NewTabUtils.js new file mode 100644 index 0000000000..0064646947 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_NewTabUtils.js @@ -0,0 +1,1511 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// See also browser/base/content/test/newtab/. + +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); + +// A small 1x1 test png +const image1x1 = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg=="; + +function getBookmarksSize() { + return NewTabUtils.activityStreamProvider.executePlacesQuery( + "SELECT count(*) FROM moz_bookmarks WHERE type = :type", + { params: { type: PlacesUtils.bookmarks.TYPE_BOOKMARK } } + ); +} + +function getHistorySize() { + return NewTabUtils.activityStreamProvider.executePlacesQuery( + "SELECT count(*) FROM moz_places WHERE hidden = 0 AND last_visit_date NOT NULL" + ); +} + +add_task(async function validCacheMidPopulation() { + let expectedLinks = makeLinks(0, 3, 1); + + let provider = new TestProvider(done => done(expectedLinks)); + provider.maxNumLinks = expectedLinks.length; + + NewTabUtils.initWithoutProviders(); + NewTabUtils.links.addProvider(provider); + let promise = new Promise(resolve => + NewTabUtils.links.populateCache(resolve) + ); + + // isTopSiteGivenProvider() and getProviderLinks() should still return results + // even when cache is empty or being populated. + Assert.ok(!NewTabUtils.isTopSiteGivenProvider("example1.com", provider)); + do_check_links(NewTabUtils.getProviderLinks(provider), []); + + await promise; + + // Once the cache is populated, we get the expected results + Assert.ok(NewTabUtils.isTopSiteGivenProvider("example1.com", provider)); + do_check_links(NewTabUtils.getProviderLinks(provider), expectedLinks); + NewTabUtils.links.removeProvider(provider); +}); + +add_task(async function notifyLinkDelete() { + let expectedLinks = makeLinks(0, 3, 1); + + let provider = new TestProvider(done => done(expectedLinks)); + provider.maxNumLinks = expectedLinks.length; + + NewTabUtils.initWithoutProviders(); + NewTabUtils.links.addProvider(provider); + await new Promise(resolve => NewTabUtils.links.populateCache(resolve)); + + do_check_links(NewTabUtils.links.getLinks(), expectedLinks); + + // Remove a link. + let removedLink = expectedLinks[2]; + provider.notifyLinkChanged(removedLink, 2, true); + let links = NewTabUtils.links._providers.get(provider); + + // Check that sortedLinks is correctly updated. + do_check_links(NewTabUtils.links.getLinks(), expectedLinks.slice(0, 2)); + + // Check that linkMap is accurately updated. + Assert.equal(links.linkMap.size, 2); + Assert.ok(links.linkMap.get(expectedLinks[0].url)); + Assert.ok(links.linkMap.get(expectedLinks[1].url)); + Assert.ok(!links.linkMap.get(removedLink.url)); + + // Check that siteMap is correctly updated. + Assert.equal(links.siteMap.size, 2); + Assert.ok(links.siteMap.has(NewTabUtils.extractSite(expectedLinks[0].url))); + Assert.ok(links.siteMap.has(NewTabUtils.extractSite(expectedLinks[1].url))); + Assert.ok(!links.siteMap.has(NewTabUtils.extractSite(removedLink.url))); + + NewTabUtils.links.removeProvider(provider); +}); + +add_task(async function populatePromise() { + let count = 0; + let expectedLinks = makeLinks(0, 10, 2); + + let getLinksFcn = async function (callback) { + // Should not be calling getLinksFcn twice + count++; + Assert.equal(count, 1); + await Promise.resolve(); + callback(expectedLinks); + }; + + let provider = new TestProvider(getLinksFcn); + + NewTabUtils.initWithoutProviders(); + NewTabUtils.links.addProvider(provider); + + NewTabUtils.links.populateProviderCache(provider, () => {}); + NewTabUtils.links.populateProviderCache(provider, () => { + do_check_links(NewTabUtils.links.getLinks(), expectedLinks); + NewTabUtils.links.removeProvider(provider); + }); +}); + +add_task(async function isTopSiteGivenProvider() { + let expectedLinks = makeLinks(0, 10, 2); + + // The lowest 2 frecencies have the same base domain. + expectedLinks[expectedLinks.length - 2].url = + expectedLinks[expectedLinks.length - 1].url + "Test"; + + let provider = new TestProvider(done => done(expectedLinks)); + provider.maxNumLinks = expectedLinks.length; + + NewTabUtils.initWithoutProviders(); + NewTabUtils.links.addProvider(provider); + await new Promise(resolve => NewTabUtils.links.populateCache(resolve)); + + Assert.equal( + NewTabUtils.isTopSiteGivenProvider("example2.com", provider), + true + ); + Assert.equal( + NewTabUtils.isTopSiteGivenProvider("example1.com", provider), + false + ); + + // Push out frecency 2 because the maxNumLinks is reached when adding frecency 3 + let newLink = makeLink(3); + provider.notifyLinkChanged(newLink); + + // There is still a frecent url with example2 domain, so it's still frecent. + Assert.equal( + NewTabUtils.isTopSiteGivenProvider("example3.com", provider), + true + ); + Assert.equal( + NewTabUtils.isTopSiteGivenProvider("example2.com", provider), + true + ); + + // Push out frecency 3 + newLink = makeLink(5); + provider.notifyLinkChanged(newLink); + + // Push out frecency 4 + newLink = makeLink(9); + provider.notifyLinkChanged(newLink); + + // Our count reached 0 for the example2.com domain so it's no longer a frecent site. + Assert.equal( + NewTabUtils.isTopSiteGivenProvider("example5.com", provider), + true + ); + Assert.equal( + NewTabUtils.isTopSiteGivenProvider("example2.com", provider), + false + ); + + NewTabUtils.links.removeProvider(provider); +}); + +add_task(async function multipleProviders() { + // Make each provider generate NewTabUtils.links.maxNumLinks links to check + // that no more than maxNumLinks are actually returned in the merged list. + let evenLinks = makeLinks(0, 2 * NewTabUtils.links.maxNumLinks, 2); + let evenProvider = new TestProvider(done => done(evenLinks)); + let oddLinks = makeLinks(0, 2 * NewTabUtils.links.maxNumLinks - 1, 2); + let oddProvider = new TestProvider(done => done(oddLinks)); + + NewTabUtils.initWithoutProviders(); + NewTabUtils.links.addProvider(evenProvider); + NewTabUtils.links.addProvider(oddProvider); + + await new Promise(resolve => NewTabUtils.links.populateCache(resolve)); + + let links = NewTabUtils.links.getLinks(); + let expectedLinks = makeLinks( + NewTabUtils.links.maxNumLinks, + 2 * NewTabUtils.links.maxNumLinks, + 1 + ); + Assert.equal(links.length, NewTabUtils.links.maxNumLinks); + do_check_links(links, expectedLinks); + + NewTabUtils.links.removeProvider(evenProvider); + NewTabUtils.links.removeProvider(oddProvider); +}); + +add_task(async function changeLinks() { + let expectedLinks = makeLinks(0, 20, 2); + let provider = new TestProvider(done => done(expectedLinks)); + + NewTabUtils.initWithoutProviders(); + NewTabUtils.links.addProvider(provider); + + await new Promise(resolve => NewTabUtils.links.populateCache(resolve)); + + do_check_links(NewTabUtils.links.getLinks(), expectedLinks); + + // Notify of a new link. + let newLink = makeLink(19); + expectedLinks.splice(1, 0, newLink); + provider.notifyLinkChanged(newLink); + do_check_links(NewTabUtils.links.getLinks(), expectedLinks); + + // Notify of a link that's changed sort criteria. + newLink.frecency = 17; + expectedLinks.splice(1, 1); + expectedLinks.splice(2, 0, newLink); + provider.notifyLinkChanged({ + url: newLink.url, + frecency: 17, + }); + do_check_links(NewTabUtils.links.getLinks(), expectedLinks); + + // Notify of a link that's changed title. + newLink.title = "My frecency is now 17"; + provider.notifyLinkChanged({ + url: newLink.url, + title: newLink.title, + }); + do_check_links(NewTabUtils.links.getLinks(), expectedLinks); + + // Notify of a new link again, but this time make it overflow maxNumLinks. + provider.maxNumLinks = expectedLinks.length; + newLink = makeLink(21); + expectedLinks.unshift(newLink); + expectedLinks.pop(); + Assert.equal(expectedLinks.length, provider.maxNumLinks); // Sanity check. + provider.notifyLinkChanged(newLink); + do_check_links(NewTabUtils.links.getLinks(), expectedLinks); + + // Notify of many links changed. + expectedLinks = makeLinks(0, 3, 1); + provider.notifyManyLinksChanged(); + + // Since _populateProviderCache() is async, we must wait until the provider's + // populate promise has been resolved. + await NewTabUtils.links._providers.get(provider).populatePromise; + + // NewTabUtils.links will now repopulate its cache + do_check_links(NewTabUtils.links.getLinks(), expectedLinks); + + NewTabUtils.links.removeProvider(provider); +}); + +add_task(async function oneProviderAlreadyCached() { + let links1 = makeLinks(0, 10, 1); + let provider1 = new TestProvider(done => done(links1)); + + NewTabUtils.initWithoutProviders(); + NewTabUtils.links.addProvider(provider1); + + await new Promise(resolve => NewTabUtils.links.populateCache(resolve)); + do_check_links(NewTabUtils.links.getLinks(), links1); + + let links2 = makeLinks(10, 20, 1); + let provider2 = new TestProvider(done => done(links2)); + NewTabUtils.links.addProvider(provider2); + + await new Promise(resolve => NewTabUtils.links.populateCache(resolve)); + do_check_links(NewTabUtils.links.getLinks(), links2.concat(links1)); + + NewTabUtils.links.removeProvider(provider1); + NewTabUtils.links.removeProvider(provider2); +}); + +add_task(async function newLowRankedLink() { + // Init a provider with 10 links and make its maximum number also 10. + let links = makeLinks(0, 10, 1); + let provider = new TestProvider(done => done(links)); + provider.maxNumLinks = links.length; + + NewTabUtils.initWithoutProviders(); + NewTabUtils.links.addProvider(provider); + + await new Promise(resolve => NewTabUtils.links.populateCache(resolve)); + do_check_links(NewTabUtils.links.getLinks(), links); + + // Notify of a new link that's low-ranked enough not to make the list. + let newLink = makeLink(0); + provider.notifyLinkChanged(newLink); + do_check_links(NewTabUtils.links.getLinks(), links); + + // Notify about the new link's title change. + provider.notifyLinkChanged({ + url: newLink.url, + title: "a new title", + }); + do_check_links(NewTabUtils.links.getLinks(), links); + + NewTabUtils.links.removeProvider(provider); +}); + +add_task(async function extractSite() { + // All these should extract to the same site + [ + "mozilla.org", + "m.mozilla.org", + "mobile.mozilla.org", + "www.mozilla.org", + "www3.mozilla.org", + ].forEach(host => { + let url = "http://" + host; + Assert.equal( + NewTabUtils.extractSite(url), + "mozilla.org", + "extracted same " + host + ); + }); + + // All these should extract to the same subdomain + ["bugzilla.mozilla.org", "www.bugzilla.mozilla.org"].forEach(host => { + let url = "http://" + host; + Assert.equal( + NewTabUtils.extractSite(url), + "bugzilla.mozilla.org", + "extracted eTLD+2 " + host + ); + }); + + // All these should not extract to the same site + [ + "bugzilla.mozilla.org", + "bug123.bugzilla.mozilla.org", + "too.many.levels.bugzilla.mozilla.org", + "m2.mozilla.org", + "mobile30.mozilla.org", + "ww.mozilla.org", + "ww2.mozilla.org", + "wwwww.mozilla.org", + "wwwww50.mozilla.org", + "wwws.mozilla.org", + "secure.mozilla.org", + "secure10.mozilla.org", + "many.levels.deep.mozilla.org", + "just.check.in", + "192.168.0.1", + "localhost", + ].forEach(host => { + let url = "http://" + host; + Assert.notEqual( + NewTabUtils.extractSite(url), + "mozilla.org", + "extracted diff " + host + ); + }); + + // All these should not extract to the same site + [ + "about:blank", + "file:///Users/user/file", + "chrome://browser/something", + "ftp://ftp.mozilla.org/", + ].forEach(url => { + Assert.notEqual( + NewTabUtils.extractSite(url), + "mozilla.org", + "extracted diff url " + url + ); + }); +}); + +add_task(async function faviconBytesToDataURI() { + let tests = [ + [{ favicon: "bar".split("").map(s => s.charCodeAt(0)), mimeType: "foo" }], + [ + { + favicon: "bar".split("").map(s => s.charCodeAt(0)), + mimeType: "foo", + xxyy: "quz", + }, + ], + ]; + let provider = NewTabUtils.activityStreamProvider; + + for (let test of tests) { + let clone = JSON.parse(JSON.stringify(test)); + delete clone[0].mimeType; + clone[0].favicon = `data:foo;base64,${btoa("bar")}`; + let result = provider._faviconBytesToDataURI(test); + Assert.deepEqual( + JSON.stringify(clone), + JSON.stringify(result), + "favicon converted to data uri" + ); + } +}); + +add_task(async function addFavicons() { + await setUpActivityStreamTest(); + let provider = NewTabUtils.activityStreamProvider; + + // start by passing in a bad uri and check that we get a null favicon back + let links = [{ url: "mozilla.com" }]; + await provider._addFavicons(links); + Assert.equal( + links[0].favicon, + null, + "Got a null favicon because we passed in a bad url" + ); + Assert.equal( + links[0].mimeType, + null, + "Got a null mime type because we passed in a bad url" + ); + Assert.equal( + links[0].faviconSize, + null, + "Got a null favicon size because we passed in a bad url" + ); + + // now fix the url and try again - this time we get good favicon data back + // a 1x1 favicon as a data URI of mime type image/png + const base64URL = image1x1; + links[0].url = "https://mozilla.com"; + + let visit = [ + { + uri: links[0].url, + visitDate: timeDaysAgo(0), + transition: PlacesUtils.history.TRANSITION_TYPED, + }, + ]; + await PlacesTestUtils.addVisits(visit); + + let faviconData = new Map(); + faviconData.set("https://mozilla.com", `${base64URL}#tippytop`); + await PlacesTestUtils.addFavicons(faviconData); + + await provider._addFavicons(links); + Assert.equal( + links[0].mimeType, + "image/png", + "Got the right mime type before deleting it" + ); + Assert.equal( + links[0].faviconLength, + links[0].favicon.length, + "Got the right length for the byte array" + ); + Assert.equal( + provider._faviconBytesToDataURI(links)[0].favicon, + base64URL, + "Got the right favicon" + ); + Assert.equal( + links[0].faviconSize, + 1, + "Got the right favicon size (width and height of favicon)" + ); + Assert.equal(links[0].faviconRef, "tippytop", "Got the favicon url ref"); + + // Check with http version of the link that doesn't have its own + const nonHttps = [{ url: links[0].url.replace("https", "http") }]; + await provider._addFavicons(nonHttps); + Assert.equal( + provider._faviconBytesToDataURI(nonHttps)[0].favicon, + base64URL, + "Got the same favicon" + ); + Assert.equal( + nonHttps[0].faviconLength, + links[0].faviconLength, + "Got the same favicon length" + ); + Assert.equal( + nonHttps[0].faviconSize, + links[0].faviconSize, + "Got the same favicon size" + ); + Assert.equal( + nonHttps[0].mimeType, + links[0].mimeType, + "Got the same mime type" + ); + + // Check that we do not collect favicons for pocket items + const pocketItems = [ + { url: links[0].url }, + { url: "https://mozilla1.com", type: "pocket" }, + ]; + await provider._addFavicons(pocketItems); + Assert.equal( + provider._faviconBytesToDataURI(pocketItems)[0].favicon, + base64URL, + "Added favicon data only to the non-pocket item" + ); + Assert.equal( + pocketItems[1].favicon, + null, + "Did not add a favicon to the pocket item" + ); + Assert.equal( + pocketItems[1].mimeType, + null, + "Did not add mimeType to the pocket item" + ); + Assert.equal( + pocketItems[1].faviconSize, + null, + "Did not add a faviconSize to the pocket item" + ); +}); + +add_task(async function getHighlightsWithoutPocket() { + const addMetadata = url => + PlacesUtils.history.update({ + description: "desc", + previewImageURL: "https://image/", + url, + }); + + await setUpActivityStreamTest(); + + let provider = NewTabUtils.activityStreamLinks; + let links = await provider.getHighlights(); + Assert.equal(links.length, 0, "empty history yields empty links"); + + // Add bookmarks + const now = Date.now(); + const oldSeconds = 24 * 60 * 60; // 1 day old + let bookmarks = [ + { + dateAdded: new Date(now - oldSeconds * 1000), + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + title: "foo", + url: "https://mozilla1.com/dayOld", + }, + { + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + title: "foo", + url: "https://mozilla1.com/nowNew", + }, + ]; + for (let placeInfo of bookmarks) { + await PlacesUtils.bookmarks.insert(placeInfo); + } + + links = await provider.getHighlights(); + Assert.equal( + links.length, + 0, + "adding bookmarks without visits doesn't yield more links" + ); + + // Add a history visit + let testURI = "http://mozilla.com/"; + await PlacesTestUtils.addVisits(testURI); + + links = await provider.getHighlights(); + Assert.equal( + links.length, + 0, + "adding visits without metadata doesn't yield more links" + ); + + // Add bookmark visits + for (let placeInfo of bookmarks) { + await PlacesTestUtils.addVisits(placeInfo.url); + } + + links = await provider.getHighlights(); + Assert.equal(links.length, 2, "adding visits to bookmarks yields more links"); + Assert.equal( + links[0].url, + bookmarks[1].url, + "first bookmark is younger bookmark" + ); + Assert.equal(links[0].type, "bookmark", "first bookmark is bookmark"); + Assert.ok(links[0].date_added, "got a date_added for the bookmark"); + Assert.equal( + links[1].url, + bookmarks[0].url, + "second bookmark is older bookmark" + ); + Assert.equal(links[1].type, "bookmark", "second bookmark is bookmark"); + Assert.ok(links[1].date_added, "got a date_added for the bookmark"); + + // Add metadata to history + await addMetadata(testURI); + + links = await provider.getHighlights(); + Assert.equal(links.length, 3, "adding metadata yield more links"); + Assert.equal(links[0].url, bookmarks[1].url, "still have younger bookmark"); + Assert.equal(links[1].url, bookmarks[0].url, "still have older bookmark"); + Assert.equal(links[2].url, testURI, "added visit corresponds to added url"); + Assert.equal(links[2].type, "history", "added visit is history"); + + links = await provider.getHighlights({ numItems: 2 }); + Assert.equal(links.length, 2, "limited to 2 items"); + Assert.equal(links[0].url, bookmarks[1].url, "still have younger bookmark"); + Assert.equal(links[1].url, bookmarks[0].url, "still have older bookmark"); + + links = await provider.getHighlights({ excludeHistory: true }); + Assert.equal(links.length, 2, "only have bookmarks"); + Assert.equal(links[0].url, bookmarks[1].url, "still have younger bookmark"); + Assert.equal(links[1].url, bookmarks[0].url, "still have older bookmark"); + + links = await provider.getHighlights({ excludeBookmarks: true }); + Assert.equal(links.length, 1, "only have history"); + Assert.equal(links[0].url, testURI, "only have the history now"); + + links = await provider.getHighlights({ + excludeBookmarks: true, + excludeHistory: true, + }); + Assert.equal(links.length, 0, "requested nothing, get nothing"); + + links = await provider.getHighlights({ bookmarkSecondsAgo: oldSeconds / 2 }); + Assert.equal(links.length, 2, "old bookmark filtered out with"); + Assert.equal(links[0].url, bookmarks[1].url, "still have newer bookmark"); + Assert.equal(links[1].url, testURI, "still have the history"); + + // Add a visit and metadata to the older bookmark + await PlacesTestUtils.addVisits(bookmarks[0].url); + await addMetadata(bookmarks[0].url); + + links = await provider.getHighlights({ bookmarkSecondsAgo: oldSeconds / 2 }); + Assert.equal(links.length, 3, "old bookmark returns as history"); + Assert.equal(links[0].url, bookmarks[1].url, "still have newer bookmark"); + Assert.equal( + links[1].url, + bookmarks[0].url, + "old bookmark now is newer history" + ); + Assert.equal(links[1].type, "history", "old bookmark now is history"); + Assert.equal(links[2].url, testURI, "still have the history"); + + // Bookmark the history item + await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + title: "now a bookmark", + url: testURI, + }); + + links = await provider.getHighlights(); + Assert.equal( + links.length, + 3, + "a visited bookmark doesn't appear as bookmark and history" + ); + Assert.equal( + links[0].url, + testURI, + "history is now the first, i.e., most recent, bookmark" + ); + Assert.equal(links[0].type, "bookmark", "was history now bookmark"); + Assert.ok(links[0].date_added, "got a date_added for the now bookmark"); + Assert.equal( + links[1].url, + bookmarks[1].url, + "still have younger bookmark now second" + ); + Assert.equal( + links[2].url, + bookmarks[0].url, + "still have older bookmark now third" + ); + + // Test the `withFavicons` option. + await PlacesTestUtils.addFavicons(new Map([[testURI, image1x1]])); + links = await provider.getHighlights({ withFavicons: true }); + Assert.equal(links.length, 3, "We're not expecting a change in links"); + Assert.equal(links[0].favicon, image1x1, "Link 1 should contain a favicon"); + Assert.equal(links[1].favicon, null, "Link 2 has no favicon data"); + Assert.equal(links[2].favicon, null, "Link 3 has no favicon data"); +}); + +add_task(async function getHighlightsWithPocketSuccess() { + await setUpActivityStreamTest(); + + // Add a bookmark + let bookmark = { + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + title: "foo", + description: "desc", + preview_image_url: "foo.com/img.png", + url: "https://mozilla1.com/", + }; + + const fakeResponse = { + list: { + 123: { + time_added: "123", + image: { src: "foo.com/img.png" }, + excerpt: "A description for foo", + resolved_title: "A title for foo", + resolved_url: "http://www.foo.com", + item_id: "123", + open_url: "http://www.getpocket.com/itemID", + status: "0", + }, + 456: { + item_id: "456", + status: "2", + }, + }, + }; + + await PlacesUtils.bookmarks.insert(bookmark); + await PlacesTestUtils.addVisits(bookmark.url); + + NewTabUtils.activityStreamProvider.fetchSavedPocketItems = () => fakeResponse; + let provider = NewTabUtils.activityStreamLinks; + + // Force a cache invalidation + NewTabUtils.activityStreamLinks._pocketLastUpdated = + Date.now() - 70 * 60 * 1000; + NewTabUtils.activityStreamLinks._pocketLastLatest = -1; + let links = await provider.getHighlights(); + + // We should have 1 bookmark followed by 1 pocket story in highlights + // We should not have stored the second pocket item since it was deleted + Assert.equal(links.length, 2, "Should have 2 links in highlights"); + + // First highlight should be a bookmark + Assert.equal(links[0].url, bookmark.url, "The first link is the bookmark"); + + // Second highlight should be a Pocket item with the correct fields to display + let pocketItem = fakeResponse.list["123"]; + let currentLink = links[1]; + Assert.equal(currentLink.url, pocketItem.resolved_url, "Correct Pocket item"); + Assert.equal(currentLink.type, "pocket", "Attached the correct type"); + Assert.equal( + currentLink.preview_image_url, + pocketItem.image.src, + "Correct preview image was added" + ); + Assert.equal( + currentLink.title, + pocketItem.resolved_title, + "Correct title was added" + ); + Assert.equal( + currentLink.description, + pocketItem.excerpt, + "Correct description was added" + ); + Assert.equal( + currentLink.pocket_id, + pocketItem.item_id, + "item_id was preserved" + ); + Assert.equal( + currentLink.open_url, + `${pocketItem.open_url}?src=fx_new_tab`, + "open_url was preserved" + ); + Assert.equal( + currentLink.date_added, + pocketItem.time_added * 1000, + "date_added was added to pocket item" + ); + + NewTabUtils.activityStreamLinks._savedPocketStories = null; +}); + +add_task(async function getHighlightsWithPocketCached() { + await setUpActivityStreamTest(); + + let fakeResponse = { + list: { + 123: { + time_added: "123", + image: { src: "foo.com/img.png" }, + excerpt: "A description for foo", + resolved_title: "A title for foo", + resolved_url: "http://www.foo.com", + item_id: "123", + open_url: "http://www.getpocket.com/itemID", + status: "0", + }, + 456: { + item_id: "456", + status: "2", + }, + }, + }; + + NewTabUtils.activityStreamProvider.fetchSavedPocketItems = () => fakeResponse; + let provider = NewTabUtils.activityStreamLinks; + + let links = await provider.getHighlights(); + Assert.equal( + links.length, + 1, + "Sanity check that we got 1 link back for highlights" + ); + Assert.equal( + links[0].url, + fakeResponse.list["123"].resolved_url, + "Sanity check that it was the pocket story" + ); + + // Update what the response would be + fakeResponse.list["789"] = { + time_added: "123", + image: { src: "bar.com/img.png" }, + excerpt: "A description for bar", + resolved_title: "A title for bar", + resolved_url: "http://www.bar.com", + item_id: "789", + open_url: "http://www.getpocket.com/itemID", + status: "0", + }; + + // Call getHighlights again - this time we should get the cached links since we just updated + links = await provider.getHighlights(); + Assert.equal(links.length, 1, "We still got 1 link back for highlights"); + Assert.equal( + links[0].url, + fakeResponse.list["123"].resolved_url, + "It was still the same pocket story" + ); + + // Now force a cache invalidation and call getHighlights again + NewTabUtils.activityStreamLinks._pocketLastUpdated = + Date.now() - 70 * 60 * 1000; + NewTabUtils.activityStreamLinks._pocketLastLatest = -1; + links = await provider.getHighlights(); + Assert.equal( + links.length, + 2, + "This time we got fresh links with the new response" + ); + Assert.equal( + links[0].url, + fakeResponse.list["123"].resolved_url, + "First link is unchanged" + ); + Assert.equal( + links[1].url, + fakeResponse.list["789"].resolved_url, + "Second link is the new link" + ); + + NewTabUtils.activityStreamLinks._savedPocketStories = null; +}); + +add_task(async function getHighlightsWithPocketFailure() { + await setUpActivityStreamTest(); + + NewTabUtils.activityStreamProvider.fetchSavedPocketItems = function () { + throw new Error(); + }; + let provider = NewTabUtils.activityStreamLinks; + + // Force a cache invalidation + NewTabUtils.activityStreamLinks._pocketLastUpdated = + Date.now() - 70 * 60 * 1000; + NewTabUtils.activityStreamLinks._pocketLastLatest = -1; + let links = await provider.getHighlights(); + Assert.equal(links.length, 0, "Return empty links if we reject the promise"); +}); + +add_task(async function getHighlightsWithPocketNoData() { + await setUpActivityStreamTest(); + + NewTabUtils.activityStreamProvider.fetchSavedPocketItems = () => {}; + + let provider = NewTabUtils.activityStreamLinks; + + // Force a cache invalidation + NewTabUtils.activityStreamLinks._pocketLastUpdated = + Date.now() - 70 * 60 * 1000; + NewTabUtils.activityStreamLinks._pocketLastLatest = -1; + let links = await provider.getHighlights(); + Assert.equal( + links.length, + 0, + "Return empty links if we got no data back from the response" + ); +}); + +add_task(async function getTopFrecentSites() { + await setUpActivityStreamTest(); + + let provider = NewTabUtils.activityStreamLinks; + let links = await provider.getTopSites({ topsiteFrecency: 100 }); + Assert.equal(links.length, 0, "empty history yields empty links"); + + // add a visit + let testURI = "http://mozilla.com/"; + await PlacesTestUtils.addVisits(testURI); + + links = await provider.getTopSites(); + Assert.equal( + links.length, + 0, + "adding a single visit doesn't exceed default threshold" + ); + + links = await provider.getTopSites({ topsiteFrecency: 100 }); + Assert.equal(links.length, 1, "adding a visit yields a link"); + Assert.equal(links[0].url, testURI, "added visit corresponds to added url"); +}); + +add_task( + { + skip_if: () => + AppConstants.MOZ_APP_NAME == "thunderbird" || + Services.prefs.getBoolPref( + "browser.topsites.useRemoteSetting" + ) /* see bug 1664502 */, + }, + async function getTopFrecentSites_improveSearch() { + await setUpActivityStreamTest(); + const SEARCH_SHORTCUTS_EXPERIMENT_PREF = + "browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts"; + Services.prefs.setBoolPref(SEARCH_SHORTCUTS_EXPERIMENT_PREF, true); + + let testURI = "https://www.amazon.com?search=tv"; + await PlacesTestUtils.addVisits(testURI); + + let provider = NewTabUtils.activityStreamLinks; + let links = await provider.getTopSites({ topsiteFrecency: 100 }); + Assert.equal( + links.length, + 1, + "sanity check that we got the link from top sites" + ); + Assert.equal( + links[0].url, + "https://amazon.com", + "the amazon site was converted to generic search shortcut site" + ); + + Services.prefs.setBoolPref(SEARCH_SHORTCUTS_EXPERIMENT_PREF, false); + } +); + +add_task(async function getTopFrecentSites_no_dedup() { + await setUpActivityStreamTest(); + + let provider = NewTabUtils.activityStreamLinks; + let links = await provider.getTopSites({ topsiteFrecency: 100 }); + Assert.equal(links.length, 0, "empty history yields empty links"); + + // Add a visits in reverse order they will be returned in when not deduped. + let testURIs = [ + { uri: "http://www.mozilla.com/" }, + { uri: "http://mozilla.com/" }, + ]; + await PlacesTestUtils.addVisits(testURIs); + + links = await provider.getTopSites(); + Assert.equal( + links.length, + 0, + "adding a single visit doesn't exceed default threshold" + ); + + links = await provider.getTopSites({ topsiteFrecency: 100 }); + Assert.equal(links.length, 1, "adding a visit yields a link"); + // Plain domain is returned when deduped. + Assert.equal( + links[0].url, + testURIs[1].uri, + "added visit corresponds to added url" + ); + + links = await provider.getTopSites({ + topsiteFrecency: 100, + onePerDomain: false, + }); + Assert.equal(links.length, 2, "adding a visit yields a link"); + Assert.equal( + links[0].url, + testURIs[1].uri, + "added visit corresponds to added url" + ); + Assert.equal( + links[1].url, + testURIs[0].uri, + "added visit corresponds to added url" + ); +}); + +add_task(async function getTopFrecentSites_dedupeWWW() { + await setUpActivityStreamTest(); + + let provider = NewTabUtils.activityStreamLinks; + + let links = await provider.getTopSites({ topsiteFrecency: 100 }); + Assert.equal(links.length, 0, "empty history yields empty links"); + + // add a visit without www + let testURI = "http://mozilla.com"; + await PlacesTestUtils.addVisits(testURI); + + // add a visit with www + testURI = "http://www.mozilla.com"; + await PlacesTestUtils.addVisits(testURI); + + // Test combined frecency score + links = await provider.getTopSites({ topsiteFrecency: 100 }); + Assert.equal(links.length, 1, "adding both www. and no-www. yields one link"); + Assert.equal(links[0].frecency, 200, "frecency scores are combined"); + + // add another page visit with www and without www + let noWWW = "http://mozilla.com/page"; + await PlacesTestUtils.addVisits(noWWW); + let withWWW = "http://www.mozilla.com/page"; + await PlacesTestUtils.addVisits(withWWW); + links = await provider.getTopSites({ topsiteFrecency: 100 }); + Assert.equal(links.length, 1, "adding both www. and no-www. yields one link"); + Assert.equal( + links[0].frecency, + 200, + "frecency scores are combined ignoring extra pages" + ); + + // add another visit with www + await PlacesTestUtils.addVisits(withWWW); + links = await provider.getTopSites({ topsiteFrecency: 100 }); + Assert.equal(links.length, 1, "still yields one link"); + Assert.equal(links[0].url, withWWW, "more frecent www link is used"); + Assert.equal( + links[0].frecency, + 300, + "frecency scores are combined ignoring extra pages" + ); + + // add a couple more visits to the no-www page + await PlacesTestUtils.addVisits(noWWW); + await PlacesTestUtils.addVisits(noWWW); + links = await provider.getTopSites({ topsiteFrecency: 100 }); + Assert.equal(links.length, 1, "still yields one link"); + Assert.equal(links[0].url, noWWW, "now more frecent no-www link is used"); + Assert.equal( + links[0].frecency, + 500, + "frecency scores are combined ignoring extra pages" + ); +}); + +add_task(async function getTopFrencentSites_maxLimit() { + await setUpActivityStreamTest(); + + let provider = NewTabUtils.activityStreamLinks; + + // add many visits + const MANY_LINKS = 20; + for (let i = 0; i < MANY_LINKS; i++) { + let testURI = `http://mozilla${i}.com`; + await PlacesTestUtils.addVisits(testURI); + } + + let links = await provider.getTopSites({ topsiteFrecency: 100 }); + Assert.ok( + links.length < MANY_LINKS, + "query default limited to less than many" + ); + Assert.greater(links.length, 6, "query default to more than visible count"); +}); + +add_task(async function getTopFrencentSites_allowedProtocols() { + await setUpActivityStreamTest(); + + let provider = NewTabUtils.activityStreamLinks; + + // add a visit from a file:// site + let testURI = "file:///some/file/path.png"; + await PlacesTestUtils.addVisits(testURI); + + let links = await provider.getTopSites({ topsiteFrecency: 100 }); + Assert.equal(links.length, 0, "don't get sites with the file:// protocol"); + + // now add a site with an allowed protocol + testURI = "http://www.mozilla.com"; + await PlacesTestUtils.addVisits(testURI); + + links = await provider.getTopSites({ topsiteFrecency: 100 }); + Assert.equal(links.length, 1, "http:// is an allowed protocol"); + + // and just to be sure, add a visit to a site with ftp:// protocol + testURI = "ftp://bad/example"; + await PlacesTestUtils.addVisits(testURI); + + links = await provider.getTopSites({ topsiteFrecency: 100 }); + Assert.equal( + links.length, + 1, + "we still only accept http:// and https:// for top sites" + ); + + // add a different allowed protocol + testURI = "https://https"; + await PlacesTestUtils.addVisits(testURI); + + links = await provider.getTopSites({ topsiteFrecency: 100 }); + Assert.equal( + links.length, + 2, + "we now accept both http:// and https:// for top sites" + ); +}); + +add_task(async function getTopFrecentSites_order() { + await setUpActivityStreamTest(); + + let provider = NewTabUtils.activityStreamLinks; + let { TRANSITION_TYPED } = PlacesUtils.history; + + let timeEarlier = timeDaysAgo(0); + let timeLater = timeDaysAgo(2); + + let visits = [ + // frecency 200 + { + uri: "https://mozilla1.com/0", + visitDate: timeEarlier, + transition: TRANSITION_TYPED, + }, + // sort by url, frecency 200 + { + uri: "https://mozilla2.com/1", + visitDate: timeEarlier, + transition: TRANSITION_TYPED, + }, + // sort by last visit date, frecency 200 + { + uri: "https://mozilla3.com/2", + visitDate: timeLater, + transition: TRANSITION_TYPED, + }, + // sort by frecency, frecency 10 + { uri: "https://mozilla0.com/", visitDate: timeLater }, + ]; + + let links = await provider.getTopSites({ topsiteFrecency: 0 }); + Assert.equal(links.length, 0, "empty history yields empty links"); + + // map of page url to favicon url + let faviconData = new Map(); + faviconData.set("https://mozilla3.com/2", image1x1); + + await PlacesTestUtils.addVisits(visits); + await PlacesTestUtils.addFavicons(faviconData); + + links = await provider.getTopSites({ topsiteFrecency: 0 }); + Assert.equal( + links.length, + visits.length, + "number of links added is the same as obtain by getTopFrecentSites" + ); + + // first link doesn't have a favicon + Assert.equal( + links[0].url, + visits[0].uri, + "links are obtained in the expected order" + ); + Assert.equal(null, links[0].favicon, "favicon data is stored as expected"); + Assert.ok( + isVisitDateOK(links[0].lastVisitDate), + "visit date within expected range" + ); + + // second link doesn't have a favicon + Assert.equal( + links[1].url, + visits[1].uri, + "links are obtained in the expected order" + ); + Assert.equal(null, links[1].favicon, "favicon data is stored as expected"); + Assert.ok( + isVisitDateOK(links[1].lastVisitDate), + "visit date within expected range" + ); + + // third link should have the favicon data that we added + Assert.equal( + links[2].url, + visits[2].uri, + "links are obtained in the expected order" + ); + Assert.equal( + faviconData.get(links[2].url), + links[2].favicon, + "favicon data is stored as expected" + ); + Assert.ok( + isVisitDateOK(links[2].lastVisitDate), + "visit date within expected range" + ); + + // fourth link doesn't have a favicon + Assert.equal( + links[3].url, + visits[3].uri, + "links are obtained in the expected order" + ); + Assert.equal(null, links[3].favicon, "favicon data is stored as expected"); + Assert.ok( + isVisitDateOK(links[3].lastVisitDate), + "visit date within expected range" + ); +}); + +add_task(async function getTopFrecentSites_hideWithSearchParam() { + await setUpActivityStreamTest(); + + let pref = "browser.newtabpage.activity-stream.hideTopSitesWithSearchParam"; + let provider = NewTabUtils.activityStreamLinks; + + // This maps URL search params to objects describing whether a URL with those + // params is expected to be included in the returned links. Each object maps + // from effective hide-with search params to whether the URL is expected to be + // included. + let tests = { + "": { + "": true, + test: true, + "test=": true, + "test=hide": true, + nomatch: true, + "nomatch=": true, + "nomatch=hide": true, + }, + test: { + "": true, + test: false, + "test=": false, + "test=hide": true, + nomatch: true, + "nomatch=": true, + "nomatch=hide": true, + }, + "test=hide": { + "": true, + test: false, + "test=": true, + "test=hide": false, + nomatch: true, + "nomatch=": true, + "nomatch=hide": true, + }, + "test=foo&test=hide": { + "": true, + test: false, + "test=": true, + "test=hide": false, + nomatch: true, + "nomatch=": true, + "nomatch=hide": true, + }, + }; + + for (let [urlParams, expected] of Object.entries(tests)) { + for (let prefValue of Object.keys(expected)) { + info( + "Running test: " + JSON.stringify({ urlParams, prefValue, expected }) + ); + + // Add a visit to a URL with search params `urlParams`. + let url = new URL("http://example.com/"); + url.search = urlParams; + await PlacesTestUtils.addVisits(url); + + // Set the pref to `prefValue`. + Services.prefs.setCharPref(pref, prefValue); + + // Call `getTopSites()` with all the test values for `hideWithSearchParam` + // plus undefined. When `hideWithSearchParam` is undefined, the pref value + // should be used. Otherwise it should override the pref. + for (let hideWithSearchParam of [undefined, ...Object.keys(expected)]) { + info( + "Calling getTopSites() with hideWithSearchParam: " + + JSON.stringify(hideWithSearchParam) + ); + + let options = { topsiteFrecency: 100 }; + if (hideWithSearchParam !== undefined) { + options = { ...options, hideWithSearchParam }; + } + let links = await provider.getTopSites(options); + + let effectiveHideWithParam = + hideWithSearchParam === undefined ? prefValue : hideWithSearchParam; + if (expected[effectiveHideWithParam]) { + Assert.equal(links.length, 1, "One link returned"); + Assert.equal(links[0].url, url.toString(), "Expected link returned"); + } else { + Assert.equal(links.length, 0, "No links returned"); + } + } + + await PlacesUtils.history.clear(); + } + } + + Services.prefs.clearUserPref(pref); +}); + +add_task(async function activitySteamProvider_deleteHistoryLink() { + await setUpActivityStreamTest(); + + let provider = NewTabUtils.activityStreamLinks; + + let { TRANSITION_TYPED } = PlacesUtils.history; + + let visits = [ + // frecency 200 + { + uri: "https://mozilla1.com/0", + visitDate: timeDaysAgo(1), + transition: TRANSITION_TYPED, + }, + // sort by url, frecency 200 + { uri: "https://mozilla2.com/1", visitDate: timeDaysAgo(0) }, + ]; + + let size = await getHistorySize(); + Assert.equal(size, 0, "empty history has size 0"); + + await PlacesTestUtils.addVisits(visits); + + size = await getHistorySize(); + Assert.equal(size, 2, "expected history size"); + + // delete a link + let deleted = await provider.deleteHistoryEntry("https://mozilla2.com/1"); + Assert.equal(deleted, true, "link is deleted"); + + // ensure that there's only one link left + size = await getHistorySize(); + Assert.equal(size, 1, "expected history size"); + + // pin the link and delete it + const linkToPin = { url: "https://mozilla1.com/0" }; + NewTabUtils.pinnedLinks.pin(linkToPin, 0); + + // sanity check that the correct link was pinned + Assert.equal( + NewTabUtils.pinnedLinks.links.length, + 1, + "added a link to pinned sites" + ); + Assert.equal( + NewTabUtils.pinnedLinks.isPinned(linkToPin), + true, + "pinned the correct link" + ); + + // delete the pinned link and ensure it was both deleted from history and unpinned + deleted = await provider.deleteHistoryEntry("https://mozilla1.com/0"); + size = await getHistorySize(); + Assert.equal(deleted, true, "link is deleted"); + Assert.equal(size, 0, "expected history size"); + Assert.equal( + NewTabUtils.pinnedLinks.links.length, + 0, + "unpinned the deleted link" + ); +}); + +add_task(async function activityStream_deleteBookmark() { + await setUpActivityStreamTest(); + + let provider = NewTabUtils.activityStreamLinks; + let bookmarks = [ + { + url: "https://mozilla1.com/0", + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + }, + { + url: "https://mozilla1.com/1", + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + }, + ]; + + let bookmarksSize = await getBookmarksSize(); + Assert.equal(bookmarksSize, 0, "empty bookmarks yields 0 size"); + + for (let placeInfo of bookmarks) { + await PlacesUtils.bookmarks.insert(placeInfo); + } + + bookmarksSize = await getBookmarksSize(); + Assert.equal(bookmarksSize, 2, "size 2 for 2 bookmarks added"); + + let bookmarkGuid = await new Promise(resolve => + PlacesUtils.bookmarks.fetch({ url: bookmarks[0].url }, bookmark => + resolve(bookmark.guid) + ) + ); + await provider.deleteBookmark(bookmarkGuid); + Assert.strictEqual( + await PlacesUtils.bookmarks.fetch(bookmarkGuid), + null, + "the bookmark should no longer be found" + ); + bookmarksSize = await getBookmarksSize(); + Assert.equal(bookmarksSize, 1, "size 1 after deleting"); +}); + +add_task(async function activityStream_blockedURLs() { + await setUpActivityStreamTest(); + + let provider = NewTabUtils.activityStreamLinks; + NewTabUtils.blockedLinks.addObserver(provider); + + let { TRANSITION_TYPED } = PlacesUtils.history; + + let timeToday = timeDaysAgo(0); + let timeEarlier = timeDaysAgo(2); + + let visits = [ + { + uri: "https://example1.com/", + visitDate: timeToday, + transition: TRANSITION_TYPED, + }, + { + uri: "https://example2.com/", + visitDate: timeToday, + transition: TRANSITION_TYPED, + }, + { + uri: "https://example3.com/", + visitDate: timeEarlier, + transition: TRANSITION_TYPED, + }, + { + uri: "https://example4.com/", + visitDate: timeEarlier, + transition: TRANSITION_TYPED, + }, + ]; + await PlacesTestUtils.addVisits(visits); + await PlacesUtils.bookmarks.insert({ + url: "https://example5.com/", + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + }); + + let sizeQueryResult; + + // bookmarks + sizeQueryResult = await getBookmarksSize(); + Assert.equal(sizeQueryResult, 1, "got the correct bookmark size"); +}); + +add_task(async function activityStream_getTotalBookmarksCount() { + await setUpActivityStreamTest(); + + let provider = NewTabUtils.activityStreamProvider; + let bookmarks = [ + { + url: "https://mozilla1.com/0", + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + }, + { + url: "https://mozilla1.com/1", + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + }, + ]; + + let bookmarksSize = await provider.getTotalBookmarksCount(); + Assert.equal( + bookmarksSize, + 0, + ".getTotalBookmarksCount() returns 0 for an empty bookmarks table" + ); + + for (const bookmark of bookmarks) { + await PlacesUtils.bookmarks.insert(bookmark); + } + + bookmarksSize = await provider.getTotalBookmarksCount(); + Assert.equal( + bookmarksSize, + 2, + ".getTotalBookmarksCount() returns 2 after 2 bookmarks are inserted" + ); +}); + +function TestProvider(getLinksFn) { + this.getLinks = getLinksFn; + this._observers = new Set(); +} + +TestProvider.prototype = { + addObserver(observer) { + this._observers.add(observer); + }, + notifyLinkChanged(link, index = -1, deleted = false) { + this._notifyObservers("onLinkChanged", link, index, deleted); + }, + notifyManyLinksChanged() { + this._notifyObservers("onManyLinksChanged"); + }, + _notifyObservers() { + let observerMethodName = arguments[0]; + let args = Array.prototype.slice.call(arguments, 1); + args.unshift(this); + for (let obs of this._observers) { + if (obs[observerMethodName]) { + obs[observerMethodName].apply(NewTabUtils.links, args); + } + } + }, +}; diff --git a/toolkit/modules/tests/xpcshell/test_ObjectUtils.js b/toolkit/modules/tests/xpcshell/test_ObjectUtils.js new file mode 100644 index 0000000000..c7617c92df --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_ObjectUtils.js @@ -0,0 +1,164 @@ +const { ObjectUtils } = ChromeUtils.importESModule( + "resource://gre/modules/ObjectUtils.sys.mjs" +); + +add_task(async function test_deepEqual() { + let deepEqual = ObjectUtils.deepEqual.bind(ObjectUtils); + // CommonJS 7.2 + Assert.ok( + deepEqual(new Date(2000, 3, 14), new Date(2000, 3, 14)), + "deepEqual date" + ); + Assert.ok(deepEqual(new Date(NaN), new Date(NaN)), "deepEqual invalid dates"); + + Assert.ok(!deepEqual(new Date(), new Date(2000, 3, 14)), "deepEqual date"); + + let now = Date.now(); + Assert.ok( + !deepEqual(new Date(now), now), + "Dates and times should not be equal" + ); + Assert.ok( + !deepEqual(now, new Date(now)), + "Times and dates should not be equal" + ); + + Assert.ok(!deepEqual("", {}), "Objects and primitives should not be equal"); + Assert.ok(!deepEqual(/a/, "a"), "RegExps and strings should not be equal"); + Assert.ok(!deepEqual("a", /a/), "Strings and RegExps should not be equal"); + + // 7.3 + Assert.ok(deepEqual(/a/, /a/)); + Assert.ok(deepEqual(/a/g, /a/g)); + Assert.ok(deepEqual(/a/i, /a/i)); + Assert.ok(deepEqual(/a/m, /a/m)); + Assert.ok(deepEqual(/a/gim, /a/gim)); + Assert.ok(!deepEqual(/ab/, /a/)); + Assert.ok(!deepEqual(/a/g, /a/)); + Assert.ok(!deepEqual(/a/i, /a/)); + Assert.ok(!deepEqual(/a/m, /a/)); + Assert.ok(!deepEqual(/a/gim, /a/im)); + + let re1 = /a/; + re1.lastIndex = 3; + Assert.ok(!deepEqual(re1, /a/)); + + // 7.4 + Assert.ok(deepEqual(4, "4"), "deepEqual == check"); + Assert.ok(deepEqual(true, 1), "deepEqual == check"); + Assert.ok(!deepEqual(4, "5"), "deepEqual == check"); + + // 7.5 + // having the same number of owned properties && the same set of keys + Assert.ok(deepEqual({ a: 4 }, { a: 4 })); + Assert.ok(deepEqual({ a: 4, b: "2" }, { a: 4, b: "2" })); + Assert.ok(deepEqual([4], ["4"])); + Assert.ok(!deepEqual({ a: 4 }, { a: 4, b: true })); + Assert.ok(deepEqual(["a"], { 0: "a" })); + + let a1 = [1, 2, 3]; + let a2 = [1, 2, 3]; + a1.a = "test"; + a1.b = true; + a2.b = true; + a2.a = "test"; + Assert.ok(!deepEqual(Object.keys(a1), Object.keys(a2))); + Assert.ok(deepEqual(a1, a2)); + + let nbRoot = { + toString() { + return this.first + " " + this.last; + }, + }; + + function nameBuilder(first, last) { + this.first = first; + this.last = last; + return this; + } + nameBuilder.prototype = nbRoot; + + function nameBuilder2(first, last) { + this.first = first; + this.last = last; + return this; + } + nameBuilder2.prototype = nbRoot; + + let nb1 = new nameBuilder("Ryan", "Dahl"); + let nb2 = new nameBuilder2("Ryan", "Dahl"); + + Assert.ok(deepEqual(nb1, nb2)); + + nameBuilder2.prototype = Object; + nb2 = new nameBuilder2("Ryan", "Dahl"); + Assert.ok(!deepEqual(nb1, nb2)); + + // String literal + object + Assert.ok(!deepEqual("a", {})); + + // Make sure deepEqual doesn't loop forever on circular refs + + let b = {}; + b.b = b; + + let c = {}; + c.b = c; + + try { + Assert.ok(!deepEqual(b, c)); + } catch (e) { + Assert.ok(true, "Didn't recurse infinitely."); + } + + let e = { a: 3, b: 4 }; + let f = { b: 4, a: 3 }; + + function checkEquiv() { + return arguments; + } + + Assert.ok(deepEqual(checkEquiv(e, f), checkEquiv(f, e))); +}); + +add_task(async function test_isEmpty() { + Assert.ok(ObjectUtils.isEmpty(""), "Empty strings should be empty"); + Assert.ok(ObjectUtils.isEmpty(0), "0 should be empty"); + Assert.ok(ObjectUtils.isEmpty(NaN), "NaN should be empty"); + Assert.ok(ObjectUtils.isEmpty(), "Undefined should be empty"); + Assert.ok(ObjectUtils.isEmpty(null), "Null should be empty"); + Assert.ok(ObjectUtils.isEmpty(false), "False should be empty"); + + Assert.ok( + ObjectUtils.isEmpty({}), + "Objects without properties should be empty" + ); + Assert.ok( + ObjectUtils.isEmpty( + Object.defineProperty({}, "a", { + value: 1, + enumerable: false, + configurable: true, + writable: true, + }) + ), + "Objects without enumerable properties should be empty" + ); + Assert.ok(ObjectUtils.isEmpty([]), "Arrays without elements should be empty"); + + Assert.ok( + !ObjectUtils.isEmpty([1]), + "Arrays with elements should not be empty" + ); + Assert.ok( + !ObjectUtils.isEmpty({ a: 1 }), + "Objects with properties should not be empty" + ); + + function A() {} + A.prototype.b = 2; + Assert.ok( + !ObjectUtils.isEmpty(new A()), + "Objects with inherited enumerable properties should not be empty" + ); +}); diff --git a/toolkit/modules/tests/xpcshell/test_ObjectUtils_strict.js b/toolkit/modules/tests/xpcshell/test_ObjectUtils_strict.js new file mode 100644 index 0000000000..c0c9841351 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_ObjectUtils_strict.js @@ -0,0 +1,29 @@ +"use strict"; + +var { ObjectUtils } = ChromeUtils.importESModule( + "resource://gre/modules/ObjectUtils.sys.mjs" +); +var { PromiseTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PromiseTestUtils.sys.mjs" +); + +add_task(async function test_strict() { + let loose = { a: 1 }; + let strict = ObjectUtils.strict(loose); + + loose.a; // Should not throw. + loose.b || undefined; // Should not throw. + + strict.a; // Should not throw. + PromiseTestUtils.expectUncaughtRejection(/No such property: "b"/); + Assert.throws(() => strict.b, /No such property: "b"/); + "b" in strict; // Should not throw. + strict.b = 2; + strict.b; // Should not throw. + + PromiseTestUtils.expectUncaughtRejection(/No such property: "c"/); + Assert.throws(() => strict.c, /No such property: "c"/); + "c" in strict; // Should not throw. + loose.c = 3; + strict.c; // Should not throw. +}); diff --git a/toolkit/modules/tests/xpcshell/test_PermissionsUtils.js b/toolkit/modules/tests/xpcshell/test_PermissionsUtils.js new file mode 100644 index 0000000000..a712044359 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_PermissionsUtils.js @@ -0,0 +1,123 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Tests that PerrmissionsUtils.jsm works as expected, including: +// * PermissionsUtils.importfromPrefs() +// <ROOT>.[whitelist|blacklist].add preferences are emptied when +// converted into permissions on startup. + +const PREF_ROOT = "testpermissions."; +const TEST_PERM = "test-permission"; + +const { PermissionsUtils } = ChromeUtils.importESModule( + "resource://gre/modules/PermissionsUtils.sys.mjs" +); +const { PermissionTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PermissionTestUtils.sys.mjs" +); + +function run_test() { + test_importfromPrefs(); +} + +function test_importfromPrefs() { + // Create own preferences to test + Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.EMPTY", ""); + Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.EMPTY2", ","); + Services.prefs.setCharPref( + PREF_ROOT + "whitelist.add.TEST", + "http://whitelist.example.com" + ); + Services.prefs.setCharPref( + PREF_ROOT + "whitelist.add.TEST2", + "https://whitelist2-1.example.com,http://whitelist2-2.example.com:8080,about:home" + ); + Services.prefs.setCharPref( + PREF_ROOT + "whitelist.add.TEST3", + "whitelist3-1.example.com,about:config" + ); // legacy style - host only + Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.EMPTY", ""); + Services.prefs.setCharPref( + PREF_ROOT + "blacklist.add.TEST", + "http://blacklist.example.com," + ); + Services.prefs.setCharPref( + PREF_ROOT + "blacklist.add.TEST2", + ",https://blacklist2-1.example.com,http://blacklist2-2.example.com:8080,about:mozilla" + ); + Services.prefs.setCharPref( + PREF_ROOT + "blacklist.add.TEST3", + "blacklist3-1.example.com,about:preferences" + ); // legacy style - host only + + // Check they are unknown in the permission manager prior to importing. + let whitelisted = [ + "http://whitelist.example.com", + "https://whitelist2-1.example.com", + "http://whitelist2-2.example.com:8080", + "http://whitelist3-1.example.com", + "https://whitelist3-1.example.com", + "about:config", + "about:home", + ]; + let blacklisted = [ + "http://blacklist.example.com", + "https://blacklist2-1.example.com", + "http://blacklist2-2.example.com:8080", + "http://blacklist3-1.example.com", + "https://blacklist3-1.example.com", + "about:preferences", + "about:mozilla", + ]; + let untouched = [ + "https://whitelist.example.com", + "https://blacklist.example.com", + "http://whitelist2-1.example.com", + "http://blacklist2-1.example.com", + "https://whitelist2-2.example.com:8080", + "https://blacklist2-2.example.com:8080", + ]; + let unknown = whitelisted.concat(blacklisted).concat(untouched); + for (let url of unknown) { + let uri = Services.io.newURI(url); + Assert.equal( + PermissionTestUtils.testPermission(uri, TEST_PERM), + Services.perms.UNKNOWN_ACTION + ); + } + + // Import them + PermissionsUtils.importFromPrefs(PREF_ROOT, TEST_PERM); + + // Get list of preferences to check + let preferences = Services.prefs.getChildList(PREF_ROOT); + + // Check preferences were emptied + for (let pref of preferences) { + Assert.equal(Services.prefs.getCharPref(pref), ""); + } + + // Check they were imported into the permissions manager + for (let url of whitelisted) { + let uri = Services.io.newURI(url); + Assert.equal( + PermissionTestUtils.testPermission(uri, TEST_PERM), + Services.perms.ALLOW_ACTION + ); + } + for (let url of blacklisted) { + let uri = Services.io.newURI(url); + Assert.equal( + PermissionTestUtils.testPermission(uri, TEST_PERM), + Services.perms.DENY_ACTION + ); + } + for (let url of untouched) { + let uri = Services.io.newURI(url); + Assert.equal( + PermissionTestUtils.testPermission(uri, TEST_PERM), + Services.perms.UNKNOWN_ACTION + ); + } +} diff --git a/toolkit/modules/tests/xpcshell/test_Preferences.js b/toolkit/modules/tests/xpcshell/test_Preferences.js new file mode 100644 index 0000000000..bc7d2b091c --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_Preferences.js @@ -0,0 +1,406 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { Preferences } = ChromeUtils.importESModule( + "resource://gre/modules/Preferences.sys.mjs" +); + +add_test(function test_set_get_pref() { + Preferences.set("test_set_get_pref.integer", 1); + Assert.equal(Preferences.get("test_set_get_pref.integer"), 1); + + Preferences.set("test_set_get_pref.string", "foo"); + Assert.equal(Preferences.get("test_set_get_pref.string"), "foo"); + + Preferences.set("test_set_get_pref.boolean", true); + Assert.equal(Preferences.get("test_set_get_pref.boolean"), true); + + // Clean up. + Preferences.resetBranch("test_set_get_pref."); + + run_next_test(); +}); + +add_test(function test_set_get_branch_pref() { + let prefs = new Preferences("test_set_get_branch_pref."); + + prefs.set("something", 1); + Assert.equal(prefs.get("something"), 1); + Assert.ok(!Preferences.has("something")); + + // Clean up. + prefs.reset("something"); + + run_next_test(); +}); + +add_test(function test_set_get_multiple_prefs() { + Preferences.set({ + "test_set_get_multiple_prefs.integer": 1, + "test_set_get_multiple_prefs.string": "foo", + "test_set_get_multiple_prefs.boolean": true, + }); + + let [i, s, b] = Preferences.get([ + "test_set_get_multiple_prefs.integer", + "test_set_get_multiple_prefs.string", + "test_set_get_multiple_prefs.boolean", + ]); + + Assert.equal(i, 1); + Assert.equal(s, "foo"); + Assert.equal(b, true); + + // Clean up. + Preferences.resetBranch("test_set_get_multiple_prefs."); + + run_next_test(); +}); + +add_test(function test_get_multiple_prefs_with_default_value() { + Preferences.set({ + "test_get_multiple_prefs_with_default_value.a": 1, + "test_get_multiple_prefs_with_default_value.b": 2, + }); + + let [a, b, c] = Preferences.get( + [ + "test_get_multiple_prefs_with_default_value.a", + "test_get_multiple_prefs_with_default_value.b", + "test_get_multiple_prefs_with_default_value.c", + ], + 0 + ); + + Assert.equal(a, 1); + Assert.equal(b, 2); + Assert.equal(c, 0); + + // Clean up. + Preferences.resetBranch("test_get_multiple_prefs_with_default_value."); + + run_next_test(); +}); + +add_test(function test_set_get_unicode_pref() { + Preferences.set("test_set_get_unicode_pref", String.fromCharCode(960)); + Assert.equal( + Preferences.get("test_set_get_unicode_pref"), + String.fromCharCode(960) + ); + + // Clean up. + Preferences.reset("test_set_get_unicode_pref"); + + run_next_test(); +}); + +add_test(function test_set_null_pref() { + try { + Preferences.set("test_set_null_pref", null); + // We expect this to throw, so the test is designed to fail if it doesn't. + Assert.ok(false); + } catch (ex) {} + + run_next_test(); +}); + +add_test(function test_set_undefined_pref() { + try { + Preferences.set("test_set_undefined_pref"); + // We expect this to throw, so the test is designed to fail if it doesn't. + Assert.ok(false); + } catch (ex) {} + + run_next_test(); +}); + +add_test(function test_set_unsupported_pref() { + try { + Preferences.set("test_set_unsupported_pref", []); + // We expect this to throw, so the test is designed to fail if it doesn't. + Assert.ok(false); + } catch (ex) {} + + run_next_test(); +}); + +// Make sure that we can get a string pref that we didn't set ourselves. +add_test(function test_get_string_pref() { + Services.prefs.setCharPref("test_get_string_pref", "a normal string"); + Assert.equal(Preferences.get("test_get_string_pref"), "a normal string"); + + // Clean up. + Preferences.reset("test_get_string_pref"); + + run_next_test(); +}); + +add_test(function test_get_localized_string_pref() { + let prefName = "test_get_localized_string_pref"; + let localizedString = Cc[ + "@mozilla.org/pref-localizedstring;1" + ].createInstance(Ci.nsIPrefLocalizedString); + localizedString.data = "a localized string"; + Services.prefs.setComplexValue( + prefName, + Ci.nsIPrefLocalizedString, + localizedString + ); + Assert.equal( + Preferences.get(prefName, null, Ci.nsIPrefLocalizedString), + "a localized string" + ); + + // Clean up. + Preferences.reset(prefName); + + run_next_test(); +}); + +add_test(function test_set_get_number_pref() { + Preferences.set("test_set_get_number_pref", 5); + Assert.equal(Preferences.get("test_set_get_number_pref"), 5); + + // Non-integer values get converted to integers. + Preferences.set("test_set_get_number_pref", 3.14159); + Assert.equal(Preferences.get("test_set_get_number_pref"), 3); + + // Values outside the range -(2^31-1) to 2^31-1 overflow. + try { + Preferences.set("test_set_get_number_pref", Math.pow(2, 31)); + // We expect this to throw, so the test is designed to fail if it doesn't. + Assert.ok(false); + } catch (ex) {} + + // Clean up. + Preferences.reset("test_set_get_number_pref"); + + run_next_test(); +}); + +add_test(function test_reset_pref() { + Preferences.set("test_reset_pref", 1); + Preferences.reset("test_reset_pref"); + Assert.equal(Preferences.get("test_reset_pref"), undefined); + + run_next_test(); +}); + +add_test(function test_reset_pref_branch() { + Preferences.set("test_reset_pref_branch.foo", 1); + Preferences.set("test_reset_pref_branch.bar", 2); + Preferences.resetBranch("test_reset_pref_branch."); + Assert.equal(Preferences.get("test_reset_pref_branch.foo"), undefined); + Assert.equal(Preferences.get("test_reset_pref_branch.bar"), undefined); + + run_next_test(); +}); + +// Make sure the module doesn't throw an exception when asked to reset +// a nonexistent pref. +add_test(function test_reset_nonexistent_pref() { + Preferences.reset("test_reset_nonexistent_pref"); + + run_next_test(); +}); + +// Make sure the module doesn't throw an exception when asked to reset +// a nonexistent pref branch. +add_test(function test_reset_nonexistent_pref_branch() { + Preferences.resetBranch("test_reset_nonexistent_pref_branch."); + + run_next_test(); +}); + +add_test(function test_observe_prefs_function() { + let observed = false; + let observer = function () { + observed = !observed; + }; + + Preferences.observe("test_observe_prefs_function", observer); + Preferences.set("test_observe_prefs_function.subpref", "something"); + Assert.ok(!observed); + Preferences.set("test_observe_prefs_function", "something"); + Assert.ok(observed); + + Preferences.ignore("test_observe_prefs_function", observer); + Preferences.set("test_observe_prefs_function", "something else"); + Assert.ok(observed); + + // Clean up. + Preferences.reset("test_observe_prefs_function"); + Preferences.reset("test_observe_prefs_function.subpref"); + + run_next_test(); +}); + +add_test(function test_observe_prefs_object() { + let observer = { + observed: false, + observe() { + this.observed = !this.observed; + }, + }; + + Preferences.observe("test_observe_prefs_object", observer.observe, observer); + Preferences.set("test_observe_prefs_object.subpref", "something"); + Assert.ok(!observer.observed); + Preferences.set("test_observe_prefs_object", "something"); + Assert.ok(observer.observed); + + Preferences.ignore("test_observe_prefs_object", observer.observe, observer); + Preferences.set("test_observe_prefs_object", "something else"); + Assert.ok(observer.observed); + + // Clean up. + Preferences.reset("test_observe_prefs_object"); + Preferences.reset("test_observe_prefs_object.subpref"); + + run_next_test(); +}); + +add_test(function test_observe_prefs_nsIObserver() { + let observer = { + observed: false, + observe(subject, topic, data) { + this.observed = !this.observed; + Assert.ok(subject instanceof Ci.nsIPrefBranch); + Assert.equal(topic, "nsPref:changed"); + Assert.equal(data, "test_observe_prefs_nsIObserver"); + }, + }; + + Preferences.observe("test_observe_prefs_nsIObserver", observer); + Preferences.set("test_observe_prefs_nsIObserver.subpref", "something"); + Preferences.set("test_observe_prefs_nsIObserver", "something"); + Assert.ok(observer.observed); + + Preferences.ignore("test_observe_prefs_nsIObserver", observer); + Preferences.set("test_observe_prefs_nsIObserver", "something else"); + Assert.ok(observer.observed); + + // Clean up. + Preferences.reset("test_observe_prefs_nsIObserver"); + Preferences.reset("test_observe_prefs_nsIObserver.subpref"); + + run_next_test(); +}); + +// This should not need to be said, but *DO NOT DISABLE THIS TEST*. +// +// Existing consumers of the observer API depend on the observer only +// being triggered for the exact preference they are observing. This is +// expecially true for consumers of the function callback variant which +// passes the preference's new value but not its name. +add_test(function test_observe_exact_pref() { + let observed = false; + let observer = function () { + observed = !observed; + }; + + Preferences.observe("test_observe_exact_pref", observer); + Preferences.set("test_observe_exact_pref.sub-pref", "something"); + Assert.ok(!observed); + + // Clean up. + Preferences.ignore("test_observe_exact_pref", observer); + Preferences.reset("test_observe_exact_pref.sub-pref"); + + run_next_test(); +}); + +add_test(function test_observe_value_of_set_pref() { + let observer = function (newVal) { + Assert.equal(newVal, "something"); + }; + + Preferences.observe("test_observe_value_of_set_pref", observer); + Preferences.set("test_observe_value_of_set_pref.subpref", "somethingelse"); + Preferences.set("test_observe_value_of_set_pref", "something"); + + // Clean up. + Preferences.ignore("test_observe_value_of_set_pref", observer); + Preferences.reset("test_observe_value_of_set_pref"); + Preferences.reset("test_observe_value_of_set_pref.subpref"); + + run_next_test(); +}); + +add_test(function test_observe_value_of_reset_pref() { + let observer = function (newVal) { + Assert.ok(typeof newVal == "undefined"); + }; + + Preferences.set("test_observe_value_of_reset_pref", "something"); + Preferences.observe("test_observe_value_of_reset_pref", observer); + Preferences.reset("test_observe_value_of_reset_pref"); + + // Clean up. + Preferences.ignore("test_observe_value_of_reset_pref", observer); + + run_next_test(); +}); + +add_test(function test_has_pref() { + Assert.ok(!Preferences.has("test_has_pref")); + Preferences.set("test_has_pref", "foo"); + Assert.ok(Preferences.has("test_has_pref")); + + Preferences.set("test_has_pref.foo", "foo"); + Preferences.set("test_has_pref.bar", "bar"); + let [hasFoo, hasBar, hasBaz] = Preferences.has([ + "test_has_pref.foo", + "test_has_pref.bar", + "test_has_pref.baz", + ]); + Assert.ok(hasFoo); + Assert.ok(hasBar); + Assert.ok(!hasBaz); + + // Clean up. + Preferences.resetBranch("test_has_pref"); + + run_next_test(); +}); + +add_test(function test_isSet_pref() { + // Use a pref that we know has a default value but no user-set value. + // This feels dangerous; perhaps we should create some other default prefs + // that we can use for testing. + Assert.ok(!Preferences.isSet("toolkit.defaultChromeURI")); + Preferences.set("toolkit.defaultChromeURI", "foo"); + Assert.ok(Preferences.isSet("toolkit.defaultChromeURI")); + + // Clean up. + Preferences.reset("toolkit.defaultChromeURI"); + + run_next_test(); +}); + +/* +add_test(function test_lock_prefs() { + // Use a pref that we know has a default value. + // This feels dangerous; perhaps we should create some other default prefs + // that we can use for testing. + do_check_false(Preferences.locked("toolkit.defaultChromeURI")); + Preferences.lock("toolkit.defaultChromeURI"); + do_check_true(Preferences.locked("toolkit.defaultChromeURI")); + Preferences.unlock("toolkit.defaultChromeURI"); + do_check_false(Preferences.locked("toolkit.defaultChromeURI")); + + let val = Preferences.get("toolkit.defaultChromeURI"); + Preferences.set("toolkit.defaultChromeURI", "test_lock_prefs"); + do_check_eq(Preferences.get("toolkit.defaultChromeURI"), "test_lock_prefs"); + Preferences.lock("toolkit.defaultChromeURI"); + do_check_eq(Preferences.get("toolkit.defaultChromeURI"), val); + Preferences.unlock("toolkit.defaultChromeURI"); + do_check_eq(Preferences.get("toolkit.defaultChromeURI"), "test_lock_prefs"); + + // Clean up. + Preferences.reset("toolkit.defaultChromeURI"); + + run_next_test(); +}); +*/ diff --git a/toolkit/modules/tests/xpcshell/test_PrivacyLevel.js b/toolkit/modules/tests/xpcshell/test_PrivacyLevel.js new file mode 100644 index 0000000000..b392e4a8ae --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_PrivacyLevel.js @@ -0,0 +1,112 @@ +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const kPref = "browser.sessionstore.privacy_level"; + +// Collect data from all sites (http and https). +const PRIVACY_NONE = 0; +// Collect data from unencrypted sites (http), only. +const PRIVACY_ENCRYPTED = 1; +// Collect no data. +const PRIVACY_FULL = 2; + +const { PrivacyLevel } = ChromeUtils.importESModule( + "resource://gre/modules/sessionstore/PrivacyLevel.sys.mjs" +); + +const { PrivacyFilter } = ChromeUtils.importESModule( + "resource://gre/modules/sessionstore/PrivacyFilter.sys.mjs" +); + +const kFormInsecureData = { + url: "http://example.com/", + other: "hello", +}; + +const kFormSecureData = { + url: "https://example.com/", + other: "secure hello", +}; + +add_task(async function test_PrivacyLevelAndFilter() { + Services.prefs.setIntPref(kPref, PRIVACY_NONE); + Assert.ok( + PrivacyLevel.check( + "http://example.com/", + "Can save http page with no privacy" + ) + ); + Assert.ok( + PrivacyLevel.check("https://example.com/"), + "Can save https page with no privacy" + ); + + Assert.deepEqual( + PrivacyFilter.filterFormData(kFormInsecureData), + kFormInsecureData, + "Filtered data matches for insecure data with no privacy." + ); + + Assert.deepEqual( + PrivacyFilter.filterFormData(kFormSecureData), + kFormSecureData, + "Filtered data matches for secure data with no privacy." + ); + + // Specialcase: empty object. + Assert.equal( + PrivacyFilter.filterFormData({}), + null, + "Filtering an empty object returns null." + ); + + Services.prefs.setIntPref(kPref, PRIVACY_ENCRYPTED); + Assert.ok( + PrivacyLevel.check( + "http://example.com/", + "Can save http page with encrypted privacy" + ) + ); + Assert.ok( + !PrivacyLevel.check("https://example.com/"), + "Can't save https page with encrypted privacy" + ); + + Assert.deepEqual( + PrivacyFilter.filterFormData(kFormInsecureData), + kFormInsecureData, + "Filtered data matches for insecure data with encrypted privacy." + ); + + Assert.deepEqual( + PrivacyFilter.filterFormData(kFormSecureData), + null, + "Filtered data matches for secure data with encrypted privacy." + ); + + Services.prefs.setIntPref(kPref, PRIVACY_FULL); + Assert.ok( + !PrivacyLevel.check( + "http://example.com/", + "Can't save http page with full privacy" + ) + ); + Assert.ok( + !PrivacyLevel.check("https://example.com/"), + "Can't save https page with full privacy" + ); + + Assert.deepEqual( + PrivacyFilter.filterFormData(kFormInsecureData), + null, + "No filtered data for insecure data with full privacy." + ); + + Assert.deepEqual( + PrivacyFilter.filterFormData(kFormSecureData), + null, + "No filtered data for secure data with full privacy." + ); +}); diff --git a/toolkit/modules/tests/xpcshell/test_ProfileAge.js b/toolkit/modules/tests/xpcshell/test_ProfileAge.js new file mode 100644 index 0000000000..9a659a5894 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_ProfileAge.js @@ -0,0 +1,120 @@ +const { ProfileAge } = ChromeUtils.importESModule( + "resource://gre/modules/ProfileAge.sys.mjs" +); + +const gProfD = do_get_profile(); +let ID = 0; + +// Creates a unique profile directory to use for a test. +function withDummyProfile(task) { + return async () => { + let profile = PathUtils.join(gProfD.path, "" + ID++); + await IOUtils.makeDirectory(profile); + await task(profile); + await IOUtils.remove(profile, { recursive: true }); + }; +} + +add_task( + withDummyProfile(async profile => { + let times = await ProfileAge(profile); + Assert.ok( + (await times.created) > 0, + "We can't really say what this will be, just assume if it is a number it's ok." + ); + Assert.equal( + await times.reset, + undefined, + "Reset time is undefined in a new profile" + ); + Assert.ok( + (await times.firstUse) <= Date.now(), + "Should have initialised a first use time." + ); + }) +); + +add_task( + withDummyProfile(async profile => { + const CREATED_TIME = Date.now() - 2000; + const RESET_TIME = Date.now() - 1000; + + await IOUtils.writeJSON(PathUtils.join(profile, "times.json"), { + created: CREATED_TIME, + }); + + let times = await ProfileAge(profile); + Assert.equal( + await times.created, + CREATED_TIME, + "Should have seen the right profile time." + ); + Assert.equal( + await times.firstUse, + undefined, + "Should be no first use time." + ); + + let times2 = await ProfileAge(profile); + Assert.equal(times, times2, "Should have got the same instance."); + + let promise = times.recordProfileReset(RESET_TIME); + Assert.equal( + await times2.reset, + RESET_TIME, + "Should have seen the right reset time in the second instance immediately." + ); + await promise; + + let results = await IOUtils.readJSON(PathUtils.join(profile, "times.json")); + Assert.deepEqual( + results, + { + created: CREATED_TIME, + reset: RESET_TIME, + }, + "Should have seen the right results." + ); + }) +); + +add_task( + withDummyProfile(async profile => { + const RESET_TIME = Date.now() - 1000; + const RESET_TIME2 = Date.now() - 2000; + + // The last call to recordProfileReset should always win. + let times = await ProfileAge(profile); + await Promise.all([ + times.recordProfileReset(RESET_TIME), + times.recordProfileReset(RESET_TIME2), + ]); + + let results = await IOUtils.readJSON(PathUtils.join(profile, "times.json")); + delete results.firstUse; + Assert.deepEqual( + results, + { + reset: RESET_TIME2, + }, + "Should have seen the right results." + ); + }) +); + +add_task( + withDummyProfile(async profile => { + const CREATED_TIME = Date.now() - 1000; + + await IOUtils.writeJSON(PathUtils.join(profile, "times.json"), { + created: CREATED_TIME, + firstUse: null, + }); + + let times = await ProfileAge(profile); + Assert.ok( + (await times.firstUse) <= Date.now(), + "Should have initialised a first use time." + ); + }) +); diff --git a/toolkit/modules/tests/xpcshell/test_Region.js b/toolkit/modules/tests/xpcshell/test_Region.js new file mode 100644 index 0000000000..e0663c745e --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_Region.js @@ -0,0 +1,333 @@ +"use strict"; + +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); +const { HttpServer } = ChromeUtils.importESModule( + "resource://testing-common/httpd.sys.mjs" +); +const { Region } = ChromeUtils.importESModule( + "resource://gre/modules/Region.sys.mjs" +); +const { setTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); +const { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(this, { + RegionTestUtils: "resource://testing-common/RegionTestUtils.sys.mjs", +}); + +const INTERVAL_PREF = "browser.region.update.interval"; + +const RESPONSE_DELAY = 500; +const RESPONSE_TIMEOUT = 100; + +const histogram = Services.telemetry.getHistogramById( + "SEARCH_SERVICE_COUNTRY_FETCH_RESULT" +); + +// Region.sys.mjs will call init() on startup and sent a background +// task to fetch the region, ensure we have completed this before +// running the rest of the tests. +add_task(async function test_startup() { + RegionTestUtils.setNetworkRegion("UK"); + await checkTelemetry(Region.TELEMETRY.SUCCESS); + await cleanup(); +}); + +add_task(async function test_basic() { + let srv = useHttpServer(RegionTestUtils.REGION_URL_PREF); + srv.registerPathHandler("/", (req, res) => { + res.setStatusLine("1.1", 200, "OK"); + send(res, { country_code: "UK" }); + }); + // start to listen the notification + let updateRegion = TestUtils.topicObserved("browser-region-updated"); + await Region._fetchRegion(); + let [subject] = await updateRegion; + + Assert.ok(true, "Region fetch should succeed"); + Assert.equal(Region.home, "UK", "Region fetch should return correct result"); + Assert.equal( + subject, + Region.home, + "Notification should be sent with the correct region" + ); + + await cleanup(srv); +}); + +add_task(async function test_invalid_url() { + histogram.clear(); + Services.prefs.setIntPref("browser.region.retry-timeout", 0); + Services.prefs.setCharPref( + RegionTestUtils.REGION_URL_PREF, + "http://localhost:0" + ); + let result = await Region._fetchRegion(); + Assert.ok(!result, "Should return no result"); + await checkTelemetry(Region.TELEMETRY.NO_RESULT); +}); + +add_task(async function test_invalid_json() { + histogram.clear(); + Services.prefs.setCharPref( + RegionTestUtils.REGION_URL_PREF, + 'data:application/json,{"country_code"' + ); + let result = await Region._fetchRegion(); + Assert.ok(!result, "Should return no result"); + await checkTelemetry(Region.TELEMETRY.NO_RESULT); +}); + +add_task(async function test_timeout() { + histogram.clear(); + Services.prefs.setIntPref("browser.region.retry-timeout", 0); + Services.prefs.setIntPref("browser.region.timeout", RESPONSE_TIMEOUT); + let srv = useHttpServer(RegionTestUtils.REGION_URL_PREF); + srv.registerPathHandler("/", (req, res) => { + res.processAsync(); + do_timeout(RESPONSE_DELAY, () => { + send(res, { country_code: "UK" }); + res.finish(); + }); + }); + + let result = await Region._fetchRegion(); + Assert.equal(result, null, "Region fetch should return null"); + + await checkTelemetry(Region.TELEMETRY.TIMEOUT); + await cleanup(srv); +}); + +add_task(async function test_mismatched_probe() { + let probeDetails = await getExpectedHistogramDetails(); + let probeHistogram; + if (probeDetails) { + probeHistogram = Services.telemetry.getHistogramById(probeDetails.probeId); + probeHistogram.clear(); + } + histogram.clear(); + Region._home = null; + + RegionTestUtils.setNetworkRegion("AU"); + await Region._fetchRegion(); + Assert.equal(Region.home, "AU", "Should have correct region"); + await checkTelemetry(Region.TELEMETRY.SUCCESS); + + // We dont store probes for linux and on treeherder + + // Mac there is no plaform countryCode so in these cases + // skip the rest of the checks. + if (!probeDetails) { + return; + } + let snapshot = probeHistogram.snapshot(); + deepEqual(snapshot.values, probeDetails.expectedResult); + + await cleanup(); +}); + +add_task(async function test_location() { + let location = { location: { lat: -1, lng: 1 }, accuracy: 100 }; + let srv = useHttpServer("geo.provider.network.url"); + srv.registerPathHandler("/", (req, res) => { + res.setStatusLine("1.1", 200, "OK"); + send(res, location); + }); + + let result = await Region._getLocation(); + Assert.ok(true, "Region fetch should succeed"); + Assert.deepEqual(result, location, "Location is returned"); + + await cleanup(srv); +}); + +add_task(async function test_update() { + Region._home = null; + RegionTestUtils.setNetworkRegion("FR"); + await Region._fetchRegion(); + Assert.equal(Region.home, "FR", "Should have correct region"); + RegionTestUtils.setNetworkRegion("DE"); + await Region._fetchRegion(); + Assert.equal(Region.home, "FR", "Shouldnt have changed yet"); + // Thie first fetchRegion will set the prefs to determine when + // to update the home region, we need to do 2 fetchRegions to test + // it isnt updating when it shouldnt. + await Region._fetchRegion(); + Assert.equal(Region.home, "FR", "Shouldnt have changed yet again"); + Services.prefs.setIntPref(INTERVAL_PREF, 1); + /* eslint-disable mozilla/no-arbitrary-setTimeout */ + await new Promise(resolve => setTimeout(resolve, 1100)); + await Region._fetchRegion(); + Assert.equal(Region.home, "DE", "Should have updated now"); + + await cleanup(); +}); + +add_task(async function test_max_retry() { + Region._home = null; + let requestsSeen = 0; + Services.prefs.setIntPref("browser.region.retry-timeout", RESPONSE_TIMEOUT); + Services.prefs.setIntPref("browser.region.timeout", RESPONSE_TIMEOUT); + let srv = useHttpServer(RegionTestUtils.REGION_URL_PREF); + srv.registerPathHandler("/", (req, res) => { + requestsSeen++; + res.setStatusLine("1.1", 200, "OK"); + res.processAsync(); + do_timeout(RESPONSE_DELAY, res.finish.bind(res)); + }); + + Region._fetchRegion(); + await TestUtils.waitForCondition(() => requestsSeen === 3); + /* eslint-disable mozilla/no-arbitrary-setTimeout */ + await new Promise(resolve => setTimeout(resolve, RESPONSE_DELAY)); + + Assert.equal(Region.home, null, "failed to fetch region"); + Assert.equal(requestsSeen, 3, "Retried 4 times"); + + Region._retryCount = 0; + await cleanup(srv); +}); + +add_task(async function test_retry() { + Region._home = null; + let requestsSeen = 0; + Services.prefs.setIntPref("browser.region.retry-timeout", RESPONSE_TIMEOUT); + Services.prefs.setIntPref("browser.region.timeout", RESPONSE_TIMEOUT); + let srv = useHttpServer(RegionTestUtils.REGION_URL_PREF); + srv.registerPathHandler("/", (req, res) => { + res.setStatusLine("1.1", 200, "OK"); + if (++requestsSeen == 2) { + res.setStatusLine("1.1", 200, "OK"); + send(res, { country_code: "UK" }); + } else { + res.processAsync(); + do_timeout(RESPONSE_DELAY, res.finish.bind(res)); + } + }); + + Region._fetchRegion(); + await TestUtils.waitForCondition(() => requestsSeen === 2); + /* eslint-disable mozilla/no-arbitrary-setTimeout */ + await new Promise(resolve => setTimeout(resolve, RESPONSE_DELAY)); + + Assert.equal(Region.home, "UK", "failed to fetch region"); + Assert.equal(requestsSeen, 2, "Retried 2 times"); + + await cleanup(srv); +}); + +add_task(async function test_timerManager() { + RegionTestUtils.setNetworkRegion("FR"); + + // Ensure the home region updates immediately, but the update + // check will only happen once per second. + Services.prefs.setIntPref("browser.region.update.interval", 0); + Services.prefs.setIntPref("browser.region.update.debounce", 1); + + let region = Region.newInstance(); + await region.init(); + Assert.equal(region.home, "FR", "Should have correct initial region"); + + // Updates are being debounced, these should be ignored. + RegionTestUtils.setNetworkRegion("DE"); + await region._updateTimer(); + await region._updateTimer(); + Assert.equal(region.home, "FR", "Ignored updates to region"); + + // Set the debounce interval to 0 so these updates are used. + Services.prefs.setIntPref("browser.region.update.debounce", 0); + RegionTestUtils.setNetworkRegion("AU"); + await region._updateTimer(); + await region._updateTimer(); + Assert.equal(region.home, "AU", "region has been updated"); + await cleanup(); +}); + +function useHttpServer(pref) { + let server = new HttpServer(); + server.start(-1); + Services.prefs.setCharPref( + pref, + `http://localhost:${server.identity.primaryPort}/` + ); + return server; +} + +function send(res, json) { + res.setStatusLine("1.1", 200, "OK"); + res.setHeader("content-type", "application/json", true); + res.write(JSON.stringify(json)); +} + +async function cleanup(srv = null) { + Services.prefs.clearUserPref("browser.search.region"); + if (srv) { + await new Promise(r => srv.stop(r)); + } +} + +async function checkTelemetry(aExpectedValue) { + // Wait until there is 1 result. + await TestUtils.waitForCondition(() => { + let snapshot = histogram.snapshot(); + return Object.values(snapshot.values).reduce((a, b) => a + b) == 1; + }); + let snapshot = histogram.snapshot(); + Assert.equal(snapshot.values[aExpectedValue], 1); +} + +// Define some checks for our platform-specific telemetry. +// We can't influence what they return (as we can't +// influence the countryCode the platform thinks we +// are in), but we can check the values are +// correct given reality. +async function getExpectedHistogramDetails() { + let probeUSMismatched, probeNonUSMismatched; + switch (AppConstants.platform) { + case "macosx": + probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_OSX"; + probeNonUSMismatched = + "SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_OSX"; + break; + case "win": + probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_WIN"; + probeNonUSMismatched = + "SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_WIN"; + break; + default: + break; + } + + if (probeUSMismatched && probeNonUSMismatched) { + let countryCode = await Services.sysinfo.countryCode; + print("Platform says the country-code is", countryCode); + if (!countryCode) { + // On treeherder for Mac the countryCode is null, so the probes won't be + // recorded. + // We still let the test run for Mac, as a developer would likely + // eventually pick up on the issue. + info("No country code set on this machine, skipping rest of test"); + return false; + } + + if (countryCode == "US") { + // boolean probe so 3 buckets, expect 1 result for |1|. + return { + probeId: probeUSMismatched, + expectedResult: { 0: 0, 1: 1, 2: 0 }, + }; + } + // We are expecting probeNonUSMismatched with false if the platform + // says AU (not a mismatch) and true otherwise. + return { + probeId: probeNonUSMismatched, + expectedResult: + countryCode == "AU" ? { 0: 1, 1: 0 } : { 0: 0, 1: 1, 2: 0 }, + }; + } + return false; +} diff --git a/toolkit/modules/tests/xpcshell/test_Region_geocoding.js b/toolkit/modules/tests/xpcshell/test_Region_geocoding.js new file mode 100644 index 0000000000..5ecd9d361d --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_Region_geocoding.js @@ -0,0 +1,108 @@ +"use strict"; + +const { Region } = ChromeUtils.importESModule( + "resource://gre/modules/Region.sys.mjs" +); +const { sinon } = ChromeUtils.importESModule( + "resource://testing-common/Sinon.sys.mjs" +); +const { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(this, { + RegionTestUtils: "resource://testing-common/RegionTestUtils.sys.mjs", +}); + +function setLocation(location) { + Services.prefs.setCharPref( + "geo.provider.network.url", + `data:application/json,${JSON.stringify({ location })}` + ); +} + +async function stubMap(obj, path, fun) { + let map = await IOUtils.readUTF8(do_get_file(path).path); + sinon.stub(obj, fun).resolves(JSON.parse(map)); +} + +async function stubMaps(obj) { + await stubMap(obj, "regions/world.geojson", "_getPlainMap"); + await stubMap(obj, "regions/world-buffered.geojson", "_getBufferedMap"); +} + +add_task(async function test_setup() { + Services.prefs.setBoolPref("browser.region.log", true); + Services.prefs.setBoolPref("browser.region.local-geocoding", true); + await stubMaps(Region); +}); + +const LOCATIONS = [ + { lat: 55.867005, lng: -4.271078, expectedRegion: "GB" }, + // Small cove in Italy surrounded by another region. + { lat: 45.6523148, lng: 13.7486427, expectedRegion: "IT" }, + // In Bosnia and Herzegovina but within a lot of borders. + { lat: 42.557079, lng: 18.4370373, expectedRegion: "HR" }, + // In the sea bordering Croatia and a few other regions. + { lat: 45.608696, lng: 13.4667903, expectedRegion: "HR" }, + // In the middle of the Atlantic. + { lat: 35.4411368, lng: -41.5372973, expectedRegion: null }, + // Tanzania. + { lat: -5.066019, lng: 39.1026251, expectedRegion: "TZ" }, +]; + +add_task(async function test_local_basic() { + for (const { lat, lng, expectedRegion } of LOCATIONS) { + setLocation({ lat, lng }); + let region = await Region._getRegionLocally(); + Assert.equal( + region, + expectedRegion, + `Got the expected region at ${lat},${lng}` + ); + } +}); + +add_task(async function test_mls_results() { + let data = await IOUtils.readUTF8( + do_get_file("regions/mls-lookup-results.csv").path + ); + for (const row of data.split("\n")) { + let [lat, lng, expectedRegion] = row.split(","); + setLocation({ lng: parseFloat(lng), lat: parseFloat(lat) }); + let region = await Region._getRegionLocally(); + Assert.equal( + region, + expectedRegion, + `Expected ${expectedRegion} at ${lat},${lng} got ${region}` + ); + } +}); + +add_task(async function test_geolocation_update() { + RegionTestUtils.setNetworkRegion("AU"); + + // Enable immediate region updates. + Services.prefs.setBoolPref("browser.region.update.enabled", true); + Services.prefs.setIntPref("browser.region.update.interval", 0); + Services.prefs.setIntPref("browser.region.update.debounce", 0); + + let region = Region.newInstance(); + await stubMaps(region); + await region.init(); + Assert.equal(region.home, "AU", "Correct initial region"); + + Services.obs.notifyObservers( + { coords: { latitude: -5.066019, longitude: 39.1026251 } }, + "geolocation-position-events" + ); + Assert.equal(region.home, "AU", "First update will mark new region as seen"); + + let regionUpdate = TestUtils.topicObserved("browser-region-updated"); + Services.obs.notifyObservers( + { coords: { latitude: -5.066019, longitude: 39.1026251 } }, + "geolocation-position-events" + ); + await regionUpdate; + Assert.equal(region.home, "TZ", "Second update will change location"); +}); diff --git a/toolkit/modules/tests/xpcshell/test_Services.js b/toolkit/modules/tests/xpcshell/test_Services.js new file mode 100644 index 0000000000..55c762fdad --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_Services.js @@ -0,0 +1,66 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * This file tests the Services global variable. + */ + +// Globals + +function checkService(service, interfaceObj) { + info("Checking that Services." + service + " is an " + interfaceObj); + Assert.ok(service in Services); + Assert.ok(Services[service] instanceof interfaceObj); +} + +// Tests + +function run_test() { + do_get_profile(); + + checkService("appShell", Ci.nsIAppShellService); + checkService("appinfo", Ci.nsIXULRuntime); + checkService("cache2", Ci.nsICacheStorageService); + checkService("clipboard", Ci.nsIClipboard); + checkService("console", Ci.nsIConsoleService); + checkService("cookies", Ci.nsICookieManager); + checkService("dirsvc", Ci.nsIDirectoryService); + checkService("dirsvc", Ci.nsIProperties); + checkService("DOMRequest", Ci.nsIDOMRequestService); + checkService("domStorageManager", Ci.nsIDOMStorageManager); + checkService("droppedLinkHandler", Ci.nsIDroppedLinkHandler); + checkService("eTLD", Ci.nsIEffectiveTLDService); + checkService("focus", Ci.nsIFocusManager); + checkService("io", Ci.nsIIOService); + checkService("intl", Ci.mozIMozIntl); + checkService("locale", Ci.mozILocaleService); + checkService("logins", Ci.nsILoginManager); + checkService("obs", Ci.nsIObserverService); + checkService("perms", Ci.nsIPermissionManager); + checkService("prefs", Ci.nsIPrefBranch); + checkService("prefs", Ci.nsIPrefService); + checkService("prompt", Ci.nsIPromptService); + checkService("scriptSecurityManager", Ci.nsIScriptSecurityManager); + checkService("scriptloader", Ci.mozIJSSubScriptLoader); + checkService("startup", Ci.nsIAppStartup); + checkService("storage", Ci.mozIStorageService); + checkService("strings", Ci.nsIStringBundleService); + checkService("sysinfo", Ci.nsIPropertyBag2); + checkService("telemetry", Ci.nsITelemetry); + checkService("tm", Ci.nsIThreadManager); + checkService("urlFormatter", Ci.nsIURLFormatter); + checkService("vc", Ci.nsIVersionComparator); + checkService("wm", Ci.nsIWindowMediator); + checkService("ww", Ci.nsIWindowWatcher); + if ("nsISearchService" in Ci) { + checkService("search", Ci.nsISearchService); + } + if ("nsIAndroidBridge" in Ci) { + checkService("androidBridge", Ci.nsIAndroidBridge); + } + if ("@mozilla.org/enterprisepolicies;1" in Cc) { + checkService("policies", Ci.nsIEnterprisePolicies); + } +} diff --git a/toolkit/modules/tests/xpcshell/test_UpdateUtils_updatechannel.js b/toolkit/modules/tests/xpcshell/test_UpdateUtils_updatechannel.js new file mode 100644 index 0000000000..81db867ba4 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_UpdateUtils_updatechannel.js @@ -0,0 +1,44 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +const { Preferences } = ChromeUtils.importESModule( + "resource://gre/modules/Preferences.sys.mjs" +); +const { UpdateUtils } = ChromeUtils.importESModule( + "resource://gre/modules/UpdateUtils.sys.mjs" +); + +const PREF_APP_UPDATE_CHANNEL = "app.update.channel"; +const TEST_CHANNEL = "TestChannel"; +const PREF_PARTNER_A = "app.partner.test_partner_a"; +const TEST_PARTNER_A = "TestPartnerA"; +const PREF_PARTNER_B = "app.partner.test_partner_b"; +const TEST_PARTNER_B = "TestPartnerB"; + +add_task(async function test_updatechannel() { + let defaultPrefs = new Preferences({ defaultBranch: true }); + let currentChannel = defaultPrefs.get(PREF_APP_UPDATE_CHANNEL); + + Assert.equal(UpdateUtils.UpdateChannel, currentChannel); + Assert.equal(UpdateUtils.getUpdateChannel(true), currentChannel); + Assert.equal(UpdateUtils.getUpdateChannel(false), currentChannel); + + defaultPrefs.set(PREF_APP_UPDATE_CHANNEL, TEST_CHANNEL); + Assert.equal(UpdateUtils.UpdateChannel, TEST_CHANNEL); + Assert.equal(UpdateUtils.getUpdateChannel(true), TEST_CHANNEL); + Assert.equal(UpdateUtils.getUpdateChannel(false), TEST_CHANNEL); + + defaultPrefs.set(PREF_PARTNER_A, TEST_PARTNER_A); + defaultPrefs.set(PREF_PARTNER_B, TEST_PARTNER_B); + Assert.equal( + UpdateUtils.UpdateChannel, + TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B + ); + Assert.equal( + UpdateUtils.getUpdateChannel(true), + TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B + ); + Assert.equal(UpdateUtils.getUpdateChannel(false), TEST_CHANNEL); +}); diff --git a/toolkit/modules/tests/xpcshell/test_UpdateUtils_url.js b/toolkit/modules/tests/xpcshell/test_UpdateUtils_url.js new file mode 100644 index 0000000000..00176a5f3a --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_UpdateUtils_url.js @@ -0,0 +1,417 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +const { UpdateUtils } = ChromeUtils.importESModule( + "resource://gre/modules/UpdateUtils.sys.mjs" +); +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); +const { getAppInfo, updateAppInfo } = ChromeUtils.importESModule( + "resource://testing-common/AppInfo.sys.mjs" +); +const { ctypes } = ChromeUtils.importESModule( + "resource://gre/modules/ctypes.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(this, { + WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs", +}); + +const PREF_APP_UPDATE_CHANNEL = "app.update.channel"; +const PREF_APP_PARTNER_BRANCH = "app.partner."; +const PREF_DISTRIBUTION_ID = "distribution.id"; +const PREF_DISTRIBUTION_VERSION = "distribution.version"; + +const URL_PREFIX = "http://localhost/"; + +const MSG_SHOULD_EQUAL = " should equal the expected value"; + +updateAppInfo(); +const gAppInfo = getAppInfo(); +const gDefaultPrefBranch = Services.prefs.getDefaultBranch(null); + +function setUpdateChannel(aChannel) { + gDefaultPrefBranch.setCharPref(PREF_APP_UPDATE_CHANNEL, aChannel); +} + +function getServicePack() { + // NOTE: This function is a helper function and not a test. Thus, + // it uses throw() instead of do_throw(). Any tests that use this function + // should catch exceptions thrown in this function and deal with them + // appropriately (usually by calling do_throw). + const BYTE = ctypes.uint8_t; + const WORD = ctypes.uint16_t; + const DWORD = ctypes.uint32_t; + const WCHAR = ctypes.char16_t; + const BOOL = ctypes.int; + + // This structure is described at: + // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx + const SZCSDVERSIONLENGTH = 128; + const OSVERSIONINFOEXW = new ctypes.StructType("OSVERSIONINFOEXW", [ + { dwOSVersionInfoSize: DWORD }, + { dwMajorVersion: DWORD }, + { dwMinorVersion: DWORD }, + { dwBuildNumber: DWORD }, + { dwPlatformId: DWORD }, + { szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH) }, + { wServicePackMajor: WORD }, + { wServicePackMinor: WORD }, + { wSuiteMask: WORD }, + { wProductType: BYTE }, + { wReserved: BYTE }, + ]); + + let kernel32 = ctypes.open("kernel32"); + try { + let GetVersionEx = kernel32.declare( + "GetVersionExW", + ctypes.winapi_abi, + BOOL, + OSVERSIONINFOEXW.ptr + ); + let winVer = OSVERSIONINFOEXW(); + winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size; + + if (0 === GetVersionEx(winVer.address())) { + // Using "throw" instead of "do_throw" (see NOTE above) + throw new Error("Failure in GetVersionEx (returned 0)"); + } + + return ( + winVer.wServicePackMajor + + "." + + winVer.wServicePackMinor + + "." + + winVer.dwBuildNumber + ); + } finally { + kernel32.close(); + } +} + +function getProcArchitecture() { + // NOTE: This function is a helper function and not a test. Thus, + // it uses throw() instead of do_throw(). Any tests that use this function + // should catch exceptions thrown in this function and deal with them + // appropriately (usually by calling do_throw). + const WORD = ctypes.uint16_t; + const DWORD = ctypes.uint32_t; + + // This structure is described at: + // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx + const SYSTEM_INFO = new ctypes.StructType("SYSTEM_INFO", [ + { wProcessorArchitecture: WORD }, + { wReserved: WORD }, + { dwPageSize: DWORD }, + { lpMinimumApplicationAddress: ctypes.voidptr_t }, + { lpMaximumApplicationAddress: ctypes.voidptr_t }, + { dwActiveProcessorMask: DWORD.ptr }, + { dwNumberOfProcessors: DWORD }, + { dwProcessorType: DWORD }, + { dwAllocationGranularity: DWORD }, + { wProcessorLevel: WORD }, + { wProcessorRevision: WORD }, + ]); + + let kernel32 = ctypes.open("kernel32"); + try { + let GetNativeSystemInfo = kernel32.declare( + "GetNativeSystemInfo", + ctypes.winapi_abi, + ctypes.void_t, + SYSTEM_INFO.ptr + ); + let sysInfo = SYSTEM_INFO(); + // Default to unknown + sysInfo.wProcessorArchitecture = 0xffff; + + GetNativeSystemInfo(sysInfo.address()); + switch (sysInfo.wProcessorArchitecture) { + case 12: + return "aarch64"; + case 9: + return "x64"; + case 6: + return "IA64"; + case 0: + return "x86"; + default: + // Using "throw" instead of "do_throw" (see NOTE above) + throw new Error( + "Unknown architecture returned from GetNativeSystemInfo: " + + sysInfo.wProcessorArchitecture + ); + } + } finally { + kernel32.close(); + } +} + +// Gets the supported CPU instruction set. +function getInstructionSet() { + const CPU_EXTENSIONS = [ + "hasSSE4_2", + "hasSSE4_1", + "hasSSE4A", + "hasSSSE3", + "hasSSE3", + "hasSSE2", + "hasSSE", + "hasMMX", + "hasNEON", + "hasARMv7", + "hasARMv6", + ]; + for (let ext of CPU_EXTENSIONS) { + if (Services.sysinfo.getProperty(ext)) { + return ext.substring(3); + } + } + + return "error"; +} + +// Gets the RAM size in megabytes. This will round the value because sysinfo +// doesn't always provide RAM in multiples of 1024. +function getMemoryMB() { + let memoryMB = "unknown"; + try { + memoryMB = Services.sysinfo.getProperty("memsize"); + if (memoryMB) { + memoryMB = Math.round(memoryMB / 1024 / 1024); + } + } catch (e) { + do_throw("Error getting system info memsize property. Exception: " + e); + } + return memoryMB; +} + +// Helper function for formatting a url and getting the result we're +// interested in +async function getResult(url) { + url = await UpdateUtils.formatUpdateURL(url); + const component = url.substr(URL_PREFIX.length).split("/")[0]; + // The docs for encodeURIComponent specify that it will encode everything + // except for: + // A-Z a-z 0-9 - _ . ! ~ * ' ( ) + // We want to ensure that we are passing Update URL components into + // encodeURIComponent, so we will make sure that we don't have characters + // except for these and `%` (for the escape sequences). + const escapedCharRegex = new RegExp("^[A-Za-z0-9_.!~*'()%-]*$"); + Assert.ok( + escapedCharRegex.test(component), + `URL component (${component}) should not have unescaped characters` + ); + return decodeURIComponent(component); +} + +// url constructed with %PRODUCT% +add_task(async function test_product() { + let url = URL_PREFIX + "%PRODUCT%/"; + Assert.equal( + await getResult(url), + gAppInfo.name, + "the url param for %PRODUCT%" + MSG_SHOULD_EQUAL + ); +}); + +// url constructed with %VERSION% +add_task(async function test_version() { + let url = URL_PREFIX + "%VERSION%/"; + Assert.equal( + await getResult(url), + gAppInfo.version, + "the url param for %VERSION%" + MSG_SHOULD_EQUAL + ); +}); + +// url constructed with %BUILD_ID% +add_task(async function test_build_id() { + let url = URL_PREFIX + "%BUILD_ID%/"; + Assert.equal( + await getResult(url), + gAppInfo.appBuildID, + "the url param for %BUILD_ID%" + MSG_SHOULD_EQUAL + ); +}); + +// url constructed with %BUILD_TARGET% +// XXX TODO - it might be nice if we tested the actual ABI +add_task(async function test_build_target() { + let url = URL_PREFIX + "%BUILD_TARGET%/"; + + let abi; + try { + abi = gAppInfo.XPCOMABI; + } catch (e) { + do_throw("nsIXULAppInfo:XPCOMABI not defined\n"); + } + + if (AppConstants.platform == "win") { + // Windows build should report the CPU architecture that it's running on. + abi += "-" + getProcArchitecture(); + } + + if (AppConstants.ASAN) { + // Allow ASan builds to receive their own updates + abi += "-asan"; + } + + Assert.equal( + await getResult(url), + gAppInfo.OS + "_" + abi, + "the url param for %BUILD_TARGET%" + MSG_SHOULD_EQUAL + ); +}); + +// url constructed with %LOCALE% +// Bug 488936 added the update.locale file that stores the update locale +add_task(async function test_locale() { + // The code that gets the locale accesses the profile which is only available + // after calling do_get_profile in xpcshell tests. This prevents an error from + // being logged. + do_get_profile(); + + let url = URL_PREFIX + "%LOCALE%/"; + Assert.equal( + await getResult(url), + "en-US", + "Assuming we are built with en-US, the url param for %LOCALE%" + + MSG_SHOULD_EQUAL + ); +}); + +// url constructed with %CHANNEL% +add_task(async function test_channel() { + let url = URL_PREFIX + "%CHANNEL%/"; + setUpdateChannel("test_channel"); + Assert.equal( + await getResult(url), + "test_channel", + "the url param for %CHANNEL%" + MSG_SHOULD_EQUAL + ); +}); + +// url constructed with %CHANNEL% with distribution partners +add_task(async function test_channel_distribution() { + let url = URL_PREFIX + "%CHANNEL%/"; + gDefaultPrefBranch.setCharPref( + PREF_APP_PARTNER_BRANCH + "test_partner1", + "test_partner1" + ); + gDefaultPrefBranch.setCharPref( + PREF_APP_PARTNER_BRANCH + "test_partner2", + "test_partner2" + ); + Assert.equal( + await getResult(url), + "test_channel-cck-test_partner1-test_partner2", + "the url param for %CHANNEL%" + MSG_SHOULD_EQUAL + ); +}); + +// url constructed with %PLATFORM_VERSION% +add_task(async function test_platform_version() { + let url = URL_PREFIX + "%PLATFORM_VERSION%/"; + Assert.equal( + await getResult(url), + gAppInfo.platformVersion, + "the url param for %PLATFORM_VERSION%" + MSG_SHOULD_EQUAL + ); +}); + +// url constructed with %OS_VERSION% +add_task(async function test_os_version() { + let url = URL_PREFIX + "%OS_VERSION%/"; + let osVersion = + Services.sysinfo.getProperty("name") + + " " + + Services.sysinfo.getProperty("version"); + + if (AppConstants.platform == "win") { + try { + let servicePack = getServicePack(); + osVersion += "." + servicePack; + } catch (e) { + do_throw("Failure obtaining service pack: " + e); + } + + if ( + Services.vc.compare(Services.sysinfo.getProperty("version"), "10") >= 0 + ) { + const WINDOWS_UBR_KEY_PATH = + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"; + let ubr = WindowsRegistry.readRegKey( + Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, + WINDOWS_UBR_KEY_PATH, + "UBR", + Ci.nsIWindowsRegKey.WOW64_64 + ); + osVersion += ubr !== undefined ? "." + ubr : ".unknown"; + } + + try { + osVersion += " (" + getProcArchitecture() + ")"; + } catch (e) { + do_throw("Failed to obtain processor architecture: " + e); + } + } + + if (osVersion) { + try { + osVersion += + " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")"; + } catch (e) { + // Not all platforms have a secondary widget library, so an error is + // nothing to worry about. + } + osVersion = encodeURIComponent(osVersion); + } + + Assert.equal( + await getResult(url), + osVersion, + "the url param for %OS_VERSION%" + MSG_SHOULD_EQUAL + ); +}); + +// url constructed with %DISTRIBUTION% +add_task(async function test_distribution() { + let url = URL_PREFIX + "%DISTRIBUTION%/"; + gDefaultPrefBranch.setCharPref(PREF_DISTRIBUTION_ID, "test_distro"); + Assert.equal( + await getResult(url), + "test_distro", + "the url param for %DISTRIBUTION%" + MSG_SHOULD_EQUAL + ); +}); + +// url constructed with %DISTRIBUTION_VERSION% +add_task(async function test_distribution_version() { + let url = URL_PREFIX + "%DISTRIBUTION_VERSION%/"; + gDefaultPrefBranch.setCharPref( + PREF_DISTRIBUTION_VERSION, + "test_distro_version" + ); + Assert.equal( + await getResult(url), + "test_distro_version", + "the url param for %DISTRIBUTION_VERSION%" + MSG_SHOULD_EQUAL + ); +}); + +// url constructed with %SYSTEM_CAPABILITIES% +add_task(async function test_systemCapabilities() { + let url = URL_PREFIX + "%SYSTEM_CAPABILITIES%/"; + let systemCapabilities = + "ISET:" + getInstructionSet() + ",MEM:" + getMemoryMB(); + Assert.equal( + await getResult(url), + systemCapabilities, + "the url param for %SYSTEM_CAPABILITIES%" + MSG_SHOULD_EQUAL + ); +}); diff --git a/toolkit/modules/tests/xpcshell/test_firstStartup.js b/toolkit/modules/tests/xpcshell/test_firstStartup.js new file mode 100644 index 0000000000..02f126d66f --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_firstStartup.js @@ -0,0 +1,100 @@ +"use strict"; + +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); +const { FirstStartup } = ChromeUtils.importESModule( + "resource://gre/modules/FirstStartup.sys.mjs" +); +const { updateAppInfo } = ChromeUtils.importESModule( + "resource://testing-common/AppInfo.sys.mjs" +); + +const PREF_TIMEOUT = "first-startup.timeout"; + +add_setup(function test_setup() { + // FOG needs a profile directory to put its data in. + do_get_profile(); + + // FOG needs to be initialized in order for data to flow. + Services.fog.initializeFOG(); +}); + +add_task(async function test_success() { + updateAppInfo(); + + let submissionPromise; + + if (AppConstants.MOZ_NORMANDY || AppConstants.MOZ_UPDATE_AGENT) { + submissionPromise = new Promise(resolve => { + GleanPings.firstStartup.testBeforeNextSubmit(() => { + Assert.equal(FirstStartup.state, FirstStartup.SUCCESS); + Assert.equal( + Glean.firstStartup.statusCode.testGetValue(), + FirstStartup.SUCCESS + ); + + if (AppConstants.MOZ_NORMANDY) { + Assert.ok(Glean.firstStartup.normandyInitTime.testGetValue() > 0); + } + + if (AppConstants.MOZ_UPDATE_AGENT) { + Assert.ok(Glean.firstStartup.deleteTasksTime.testGetValue() > 0); + } + + resolve(); + }); + }); + } else { + submissionPromise = new Promise(resolve => { + GleanPings.firstStartup.testBeforeNextSubmit(() => { + Assert.equal(FirstStartup.state, FirstStartup.UNSUPPORTED); + Assert.equal( + Glean.firstStartup.statusCode.testGetValue(), + FirstStartup.UNSUPPORTED + ); + resolve(); + }); + }); + } + + FirstStartup.init(); + await submissionPromise; +}); + +add_task(async function test_timeout() { + updateAppInfo(); + Services.prefs.setIntPref(PREF_TIMEOUT, 0); + + let submissionPromise; + + if (AppConstants.MOZ_NORMANDY || AppConstants.MOZ_UPDATE_AGENT) { + submissionPromise = new Promise(resolve => { + GleanPings.firstStartup.testBeforeNextSubmit(() => { + Assert.equal(FirstStartup.state, FirstStartup.TIMED_OUT); + Assert.ok(Glean.firstStartup.elapsed.testGetValue() > 0); + + if (AppConstants.MOZ_NORMANDY) { + Assert.ok(Glean.firstStartup.normandyInitTime.testGetValue() > 0); + } + + if (AppConstants.MOZ_UPDATE_AGENT) { + Assert.ok(Glean.firstStartup.deleteTasksTime.testGetValue() > 0); + } + + resolve(); + }); + }); + } else { + submissionPromise = new Promise(resolve => { + GleanPings.firstStartup.testBeforeNextSubmit(() => { + Assert.equal(FirstStartup.state, FirstStartup.UNSUPPORTED); + Assert.equal(Glean.firstStartup.elapsed.testGetValue(), 0); + resolve(); + }); + }); + } + + FirstStartup.init(); + await submissionPromise; +}); diff --git a/toolkit/modules/tests/xpcshell/test_jsesc.js b/toolkit/modules/tests/xpcshell/test_jsesc.js new file mode 100644 index 0000000000..5770650105 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_jsesc.js @@ -0,0 +1,14 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { jsesc } = ChromeUtils.importESModule( + "resource://gre/modules/third_party/jsesc/jsesc.mjs" +); + +function run_test() { + Assert.equal(jsesc("teééést", { lowercaseHex: true }), "te\\xe9\\xe9\\xe9st"); + Assert.equal( + jsesc("teééést", { lowercaseHex: false }), + "te\\xE9\\xE9\\xE9st" + ); +} diff --git a/toolkit/modules/tests/xpcshell/test_osKeyStore.js b/toolkit/modules/tests/xpcshell/test_osKeyStore.js new file mode 100644 index 0000000000..eb66a817b6 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_osKeyStore.js @@ -0,0 +1,102 @@ +/** + * Tests of OSKeyStore.sys.mjs + */ + +"use strict"; + +var { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +let OSKeyStoreTestUtils; +add_task(async function os_key_store_setup() { + ({ OSKeyStoreTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/OSKeyStoreTestUtils.sys.mjs" + )); + OSKeyStoreTestUtils.setup(); + registerCleanupFunction(async function cleanup() { + await OSKeyStoreTestUtils.cleanup(); + }); +}); + +let OSKeyStore; +add_task(async function setup() { + ({ OSKeyStore } = ChromeUtils.importESModule( + "resource://gre/modules/OSKeyStore.sys.mjs" + )); +}); + +// Ensure that the appropriate initialization has happened. +do_get_profile(); + +const testText = "test string"; +let cipherText; + +add_task(async function test_encrypt_decrypt() { + Assert.equal( + (await OSKeyStore.ensureLoggedIn()).authenticated, + true, + "Started logged in." + ); + + cipherText = await OSKeyStore.encrypt(testText); + Assert.notEqual(testText, cipherText); + + let plainText = await OSKeyStore.decrypt(cipherText); + Assert.equal(testText, plainText); +}); + +add_task(async function test_reauth() { + let canTest = OSKeyStoreTestUtils.canTestOSKeyStoreLogin(); + if (!canTest) { + todo_check_true( + canTest, + "test_reauth: Cannot test OS key store login on this build. See OSKeyStoreTestUtils.canTestOSKeyStoreLogin for details" + ); + return; + } + + let reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(false); + await new Promise(resolve => TestUtils.executeSoon(resolve)); + try { + await OSKeyStore.decrypt(cipherText, "prompt message text"); + throw new Error("Not receiving canceled OS unlock error"); + } catch (ex) { + Assert.equal(ex.message, "User canceled OS unlock entry"); + Assert.equal(ex.result, Cr.NS_ERROR_ABORT); + } + await reauthObserved; + + reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(false); + await new Promise(resolve => TestUtils.executeSoon(resolve)); + Assert.equal( + (await OSKeyStore.ensureLoggedIn("test message")).authenticated, + false, + "Reauth cancelled." + ); + await reauthObserved; + + reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + await new Promise(resolve => TestUtils.executeSoon(resolve)); + let plainText2 = await OSKeyStore.decrypt(cipherText, "prompt message text"); + await reauthObserved; + Assert.equal(testText, plainText2); + + reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + await new Promise(resolve => TestUtils.executeSoon(resolve)); + Assert.equal( + (await OSKeyStore.ensureLoggedIn("test message")).authenticated, + true, + "Reauth logged in." + ); + await reauthObserved; +}); + +add_task(async function test_decryption_failure() { + try { + await OSKeyStore.decrypt("Malformed cipher text"); + throw new Error("Not receiving decryption error"); + } catch (ex) { + Assert.notEqual(ex.result, Cr.NS_ERROR_ABORT); + } +}); diff --git a/toolkit/modules/tests/xpcshell/test_propertyListsUtils.js b/toolkit/modules/tests/xpcshell/test_propertyListsUtils.js new file mode 100644 index 0000000000..f600eadab0 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_propertyListsUtils.js @@ -0,0 +1,131 @@ +/* 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/. */ +"use strict"; + +const { PropertyListUtils } = ChromeUtils.importESModule( + "resource://gre/modules/PropertyListUtils.sys.mjs" +); + +function checkValue(aPropertyListObject, aType, aValue) { + Assert.equal(PropertyListUtils.getObjectType(aPropertyListObject), aType); + if (aValue !== undefined) { + // Perform strict equality checks until Bug 714467 is fixed. + let strictEqualityCheck = function (a, b) { + Assert.equal(typeof a, typeof b); + Assert.equal(a, b); + }; + + if (typeof aPropertyListObject == "object") { + strictEqualityCheck(aPropertyListObject.valueOf(), aValue.valueOf()); + } else { + strictEqualityCheck(aPropertyListObject, aValue); + } + } +} + +function checkLazyGetterValue(aObject, aPropertyName, aType, aValue) { + let descriptor = Object.getOwnPropertyDescriptor(aObject, aPropertyName); + Assert.equal(typeof descriptor.get, "function"); + Assert.equal(typeof descriptor.value, "undefined"); + checkValue(aObject[aPropertyName], aType, aValue); + descriptor = Object.getOwnPropertyDescriptor(aObject, aPropertyName); + Assert.equal(typeof descriptor.get, "undefined"); + Assert.notEqual(typeof descriptor.value, "undefined"); +} + +function checkMainPropertyList(aPropertyListRoot) { + const PRIMITIVE = PropertyListUtils.TYPE_PRIMITIVE; + + checkValue(aPropertyListRoot, PropertyListUtils.TYPE_DICTIONARY); + + // Check .has() + Assert.ok(aPropertyListRoot.has("Boolean")); + Assert.ok(!aPropertyListRoot.has("Nonexistent")); + + checkValue(aPropertyListRoot.get("Boolean"), PRIMITIVE, false); + + let array = aPropertyListRoot.get("Array"); + checkValue(array, PropertyListUtils.TYPE_ARRAY); + Assert.equal(array.length, 8); + + // Test both long and short values, since binary property lists store + // long values a little bit differently (see readDataLengthAndOffset). + + // Short ASCII string + checkLazyGetterValue(array, 0, PRIMITIVE, "abc"); + // Long ASCII string + checkLazyGetterValue(array, 1, PRIMITIVE, new Array(1001).join("a")); + // Short unicode string + checkLazyGetterValue(array, 2, PRIMITIVE, "\u05D0\u05D0\u05D0"); + // Long unicode string + checkLazyGetterValue(array, 3, PRIMITIVE, new Array(1001).join("\u05D0")); + // Unicode surrogate pair + checkLazyGetterValue( + array, + 4, + PRIMITIVE, + "\uD800\uDC00\uD800\uDC00\uD800\uDC00" + ); + + // Date + checkLazyGetterValue( + array, + 5, + PropertyListUtils.TYPE_DATE, + new Date("2011-12-31T11:15:23Z") + ); + + // Data + checkLazyGetterValue(array, 6, PropertyListUtils.TYPE_UINT8_ARRAY); + let dataAsString = Array.from(array[6]) + .map(b => String.fromCharCode(b)) + .join(""); + Assert.equal(dataAsString, "2011-12-31T11:15:33Z"); + + // Dict + let dict = array[7]; + checkValue(dict, PropertyListUtils.TYPE_DICTIONARY); + checkValue(dict.get("Negative Number"), PRIMITIVE, -400); + checkValue(dict.get("Real Number"), PRIMITIVE, 2.71828183); + checkValue( + dict.get("Big Int"), + PropertyListUtils.TYPE_INT64, + "9007199254740993" + ); + checkValue( + dict.get("Negative Big Int"), + PropertyListUtils.TYPE_INT64, + "-9007199254740993" + ); +} + +function readPropertyList(aFile, aCallback) { + PropertyListUtils.read(aFile, function (aPropertyListRoot) { + // Null root indicates failure to read property list. + // Note: It is important not to run do_check_n/eq directly on Dict and array + // objects, because it cases their toString to get invoked, doing away with + // all the lazy getter we'd like to test later. + Assert.ok(aPropertyListRoot !== null); + aCallback(aPropertyListRoot); + run_next_test(); + }); +} + +function run_test() { + add_test( + readPropertyList.bind( + this, + do_get_file("propertyLists/bug710259_propertyListBinary.plist", false), + checkMainPropertyList + ) + ); + add_test( + readPropertyList.bind( + this, + do_get_file("propertyLists/bug710259_propertyListXML.plist", false), + checkMainPropertyList + ) + ); + run_next_test(); +} diff --git a/toolkit/modules/tests/xpcshell/test_readCertPrefs.js b/toolkit/modules/tests/xpcshell/test_readCertPrefs.js new file mode 100644 index 0000000000..655b085cc7 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_readCertPrefs.js @@ -0,0 +1,115 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { CertUtils } = ChromeUtils.importESModule( + "resource://gre/modules/CertUtils.sys.mjs" +); + +const PREF_PREFIX = "certutils.certs."; + +function resetPrefs() { + var prefs = Services.prefs.getChildList(PREF_PREFIX); + prefs.forEach(Services.prefs.clearUserPref); +} + +function attributes_match(aCert, aExpected) { + if (Object.keys(aCert).length != Object.keys(aExpected).length) { + return false; + } + + for (var attribute in aCert) { + if (!(attribute in aExpected)) { + return false; + } + if (aCert[attribute] != aExpected[attribute]) { + return false; + } + } + + return true; +} + +function test_results(aCerts, aExpected) { + Assert.equal(aCerts.length, aExpected.length); + + for (var i = 0; i < aCerts.length; i++) { + if (!attributes_match(aCerts[i], aExpected[i])) { + dump( + "Attributes for certificate " + + (i + 1) + + " did not match expected attributes\n" + ); + dump("Saw: " + aCerts[i].toSource() + "\n"); + dump("Expected: " + aExpected[i].toSource() + "\n"); + Assert.ok(false); + } + } +} + +add_test(function test_singleCert() { + Services.prefs.setCharPref(PREF_PREFIX + "1.attribute1", "foo"); + Services.prefs.setCharPref(PREF_PREFIX + "1.attribute2", "bar"); + + var certs = CertUtils.readCertPrefs(PREF_PREFIX); + test_results(certs, [ + { + attribute1: "foo", + attribute2: "bar", + }, + ]); + + resetPrefs(); + run_next_test(); +}); + +add_test(function test_multipleCert() { + Services.prefs.setCharPref( + PREF_PREFIX + "1.md5Fingerprint", + "cf84a9a2a804e021f27cb5128fe151f4" + ); + Services.prefs.setCharPref(PREF_PREFIX + "1.nickname", "1st cert"); + Services.prefs.setCharPref( + PREF_PREFIX + "2.md5Fingerprint", + "9441051b7eb50e5ca2226095af710c1a" + ); + Services.prefs.setCharPref(PREF_PREFIX + "2.nickname", "2nd cert"); + + var certs = CertUtils.readCertPrefs(PREF_PREFIX); + test_results(certs, [ + { + md5Fingerprint: "cf84a9a2a804e021f27cb5128fe151f4", + nickname: "1st cert", + }, + { + md5Fingerprint: "9441051b7eb50e5ca2226095af710c1a", + nickname: "2nd cert", + }, + ]); + + resetPrefs(); + run_next_test(); +}); + +add_test(function test_skippedCert() { + Services.prefs.setCharPref(PREF_PREFIX + "1.issuerName", "Mozilla"); + Services.prefs.setCharPref(PREF_PREFIX + "1.nickname", "1st cert"); + Services.prefs.setCharPref(PREF_PREFIX + "2.issuerName", "Top CA"); + Services.prefs.setCharPref(PREF_PREFIX + "2.nickname", "2nd cert"); + Services.prefs.setCharPref(PREF_PREFIX + "4.issuerName", "Unknown CA"); + Services.prefs.setCharPref(PREF_PREFIX + "4.nickname", "Ignored cert"); + + var certs = CertUtils.readCertPrefs(PREF_PREFIX); + test_results(certs, [ + { + issuerName: "Mozilla", + nickname: "1st cert", + }, + { + issuerName: "Top CA", + nickname: "2nd cert", + }, + ]); + + resetPrefs(); + run_next_test(); +}); diff --git a/toolkit/modules/tests/xpcshell/test_servicerequest_xhr.js b/toolkit/modules/tests/xpcshell/test_servicerequest_xhr.js new file mode 100644 index 0000000000..8d7955e1ee --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_servicerequest_xhr.js @@ -0,0 +1,62 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { ServiceRequest } = ChromeUtils.importESModule( + "resource://gre/modules/ServiceRequest.sys.mjs" +); + +add_task(async function test_tls_conservative() { + const request = new ServiceRequest(); + request.open("GET", "http://example.com", false); + + const sr_channel = request.channel.QueryInterface(Ci.nsIHttpChannelInternal); + ok("beConservative" in sr_channel, "TLS setting is present in SR channel"); + ok( + sr_channel.beConservative, + "TLS setting in request channel is set to conservative for SR" + ); + + const xhr = new XMLHttpRequest(); + xhr.open("GET", "http://example.com", false); + + const xhr_channel = xhr.channel.QueryInterface(Ci.nsIHttpChannelInternal); + ok("beConservative" in xhr_channel, "TLS setting is present in XHR channel"); + ok( + !xhr_channel.beConservative, + "TLS setting in request channel is not set to conservative for XHR" + ); +}); + +add_task(async function test_bypassProxy_default() { + const request = new ServiceRequest(); + request.open("GET", "http://example.com", true); + const sr_channel = request.channel.QueryInterface(Ci.nsIHttpChannelInternal); + + ok(!sr_channel.bypassProxy, "bypassProxy is false on SR channel"); + ok(!request.bypassProxy, "bypassProxy is false for SR"); +}); + +add_task(async function test_bypassProxy_true() { + const request = new ServiceRequest(); + request.open("GET", "http://example.com", { bypassProxy: true }); + const sr_channel = request.channel.QueryInterface(Ci.nsIHttpChannelInternal); + + ok(sr_channel.bypassProxy, "bypassProxy is true on SR channel"); + ok(request.bypassProxy, "bypassProxy is true for SR"); +}); + +add_task(async function test_bypassProxy_disabled_by_pref() { + const request = new ServiceRequest(); + + ok(request.bypassProxyEnabled, "bypassProxyEnabled is true"); + Services.prefs.setBoolPref("network.proxy.allow_bypass", false); + ok(!request.bypassProxyEnabled, "bypassProxyEnabled is false"); + + request.open("GET", "http://example.com", { bypassProxy: true }); + const sr_channel = request.channel.QueryInterface(Ci.nsIHttpChannelInternal); + + ok(!sr_channel.bypassProxy, "bypassProxy is false on SR channel"); + ok(!request.bypassProxy, "bypassProxy is false for SR"); +}); diff --git a/toolkit/modules/tests/xpcshell/test_sqlite.js b/toolkit/modules/tests/xpcshell/test_sqlite.js new file mode 100644 index 0000000000..2c3ede46d6 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_sqlite.js @@ -0,0 +1,1402 @@ +"use strict"; + +const PROFILE_DIR = do_get_profile().path; + +const { FileUtils } = ChromeUtils.importESModule( + "resource://gre/modules/FileUtils.sys.mjs" +); +const { Sqlite } = ChromeUtils.importESModule( + "resource://gre/modules/Sqlite.sys.mjs" +); +const { TelemetryTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TelemetryTestUtils.sys.mjs" +); + +// Enable the collection (during test) for all products so even products +// that don't collect the data will be able to run the test without failure. +Services.prefs.setBoolPref( + "toolkit.telemetry.testing.overrideProductsCheck", + true +); + +function sleep(ms) { + return new Promise(resolve => { + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + + timer.initWithCallback( + { + notify() { + resolve(); + }, + }, + ms, + timer.TYPE_ONE_SHOT + ); + }); +} + +// When testing finalization, use this to tell Sqlite.sys.mjs to not throw +// an uncatchable `Promise.reject` +function failTestsOnAutoClose(enabled) { + Sqlite.failTestsOnAutoClose(enabled); +} + +function getConnection(dbName, extraOptions = {}) { + let path = dbName + ".sqlite"; + let options = { path }; + for (let [k, v] of Object.entries(extraOptions)) { + options[k] = v; + } + + return Sqlite.openConnection(options); +} + +async function getDummyDatabase(name, extraOptions = {}) { + let c = await getConnection(name, extraOptions); + c._initialStatementCount = 0; + + if (!extraOptions.readOnly) { + const TABLES = new Map([ + ["dirs", "id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT"], + [ + "files", + "id INTEGER PRIMARY KEY AUTOINCREMENT, dir_id INTEGER, path TEXT", + ], + ]); + for (let [k, v] of TABLES) { + await c.execute("CREATE TABLE " + k + "(" + v + ")"); + c._initialStatementCount++; + } + } + + return c; +} + +async function getDummyTempDatabase(name, extraOptions = {}) { + const TABLES = { + dirs: "id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT", + files: "id INTEGER PRIMARY KEY AUTOINCREMENT, dir_id INTEGER, path TEXT", + }; + + let c = await getConnection(name, extraOptions); + c._initialStatementCount = 0; + + for (let [k, v] of Object.entries(TABLES)) { + await c.execute("CREATE TEMP TABLE " + k + "(" + v + ")"); + c._initialStatementCount++; + } + + return c; +} + +add_task(async function test_setup() { + const { initTestLogging } = ChromeUtils.importESModule( + "resource://testing-common/services/common/logging.sys.mjs" + ); + initTestLogging("Trace"); +}); + +add_task(async function test_open_normal() { + let c = await Sqlite.openConnection({ path: "test_open_normal.sqlite" }); + Assert.equal(c.defaultTransactionType, "DEFERRED"); + await c.close(); +}); + +add_task(async function test_open_with_defaultTransactionType() { + let c = await getConnection("execute_transaction_types", { + defaultTransactionType: "IMMEDIATE", + }); + Assert.equal(c.defaultTransactionType, "IMMEDIATE"); + await c.close(); +}); + +add_task(async function test_open_normal_error() { + let currentDir = do_get_cwd().path; + + let src = PathUtils.join(currentDir, "corrupt.sqlite"); + Assert.ok(await IOUtils.exists(src), "Database file found"); + + // Ensure that our database doesn't already exist. + let path = PathUtils.join(PROFILE_DIR, "corrupt.sqlite"); + await Assert.rejects( + IOUtils.stat(path), + /Could not stat file\(.*\) because it does not exist/, + "Database file should not exist yet" + ); + + await IOUtils.copy(src, path); + + let openPromise = Sqlite.openConnection({ path }); + await Assert.rejects( + openPromise, + reason => { + return reason.result == Cr.NS_ERROR_FILE_CORRUPTED; + }, + "Check error status" + ); +}); + +add_task(async function test_open_unshared() { + let path = PathUtils.join(PROFILE_DIR, "test_open_unshared.sqlite"); + + let c = await Sqlite.openConnection({ path, sharedMemoryCache: false }); + await c.close(); +}); + +add_task(async function test_get_dummy_database() { + let db = await getDummyDatabase("get_dummy_database"); + + Assert.equal(typeof db, "object"); + await db.close(); +}); + +add_task(async function test_schema_version() { + let db = await getDummyDatabase("schema_version"); + + let version = await db.getSchemaVersion(); + Assert.strictEqual(version, 0); + + db.setSchemaVersion(14); + version = await db.getSchemaVersion(); + Assert.strictEqual(version, 14); + + for (let v of [0.5, "foobar", NaN]) { + let success; + try { + await db.setSchemaVersion(v); + info("Schema version " + v + " should have been rejected"); + success = false; + } catch (ex) { + if (!ex.message.startsWith("Schema version must be an integer.")) { + throw ex; + } + success = true; + } + Assert.ok(success); + + version = await db.getSchemaVersion(); + Assert.strictEqual(version, 14); + } + + await db.execute("ATTACH :memory AS attached"); + + let attachedVersion = await db.getSchemaVersion("attached"); + Assert.equal( + attachedVersion, + 0, + "Should return 0 for initial attached schema version" + ); + + await db.setSchemaVersion(3, "attached"); + attachedVersion = await db.getSchemaVersion("attached"); + Assert.equal(attachedVersion, 3, "Should set attached schema version"); + + version = await db.getSchemaVersion(); + Assert.equal( + version, + 14, + "Setting attached schema version should not change main schema version" + ); + + await db.setSchemaVersion(15); + attachedVersion = await db.getSchemaVersion("attached"); + Assert.equal( + attachedVersion, + 3, + "Setting main schema version should not change attached schema version" + ); + + await db.close(); +}); + +add_task(async function test_simple_insert() { + let c = await getDummyDatabase("simple_insert"); + + let result = await c.execute("INSERT INTO dirs VALUES (NULL, 'foo')"); + Assert.ok(Array.isArray(result)); + Assert.equal(result.length, 0); + await c.close(); +}); + +add_task(async function test_simple_bound_array() { + let c = await getDummyDatabase("simple_bound_array"); + + let result = await c.execute("INSERT INTO dirs VALUES (?, ?)", [1, "foo"]); + Assert.equal(result.length, 0); + await c.close(); +}); + +add_task(async function test_simple_bound_object() { + let c = await getDummyDatabase("simple_bound_object"); + let result = await c.execute("INSERT INTO dirs VALUES (:id, :path)", { + id: 1, + path: "foo", + }); + Assert.equal(result.length, 0); + result = await c.execute("SELECT id, path FROM dirs"); + Assert.equal(result.length, 1); + Assert.equal(result[0].getResultByName("id"), 1); + Assert.equal(result[0].getResultByName("path"), "foo"); + await c.close(); +}); + +// This is mostly a sanity test to ensure simple executions work. +add_task(async function test_simple_insert_then_select() { + let c = await getDummyDatabase("simple_insert_then_select"); + + await c.execute("INSERT INTO dirs VALUES (NULL, 'foo')"); + await c.execute("INSERT INTO dirs (path) VALUES (?)", ["bar"]); + + let result = await c.execute("SELECT * FROM dirs"); + Assert.equal(result.length, 2); + + let i = 0; + for (let row of result) { + i++; + + Assert.equal(row.numEntries, 2); + Assert.equal(row.getResultByIndex(0), i); + + let expected = { 1: "foo", 2: "bar" }[i]; + Assert.equal(row.getResultByName("path"), expected); + } + + await c.close(); +}); + +add_task(async function test_repeat_execution() { + let c = await getDummyDatabase("repeat_execution"); + + let sql = "INSERT INTO dirs (path) VALUES (:path)"; + await c.executeCached(sql, { path: "foo" }); + await c.executeCached(sql); + + let result = await c.execute("SELECT * FROM dirs"); + + Assert.equal(result.length, 2); + + await c.close(); +}); + +add_task(async function test_table_exists() { + let c = await getDummyDatabase("table_exists"); + + Assert.equal(false, await c.tableExists("does_not_exist")); + Assert.ok(await c.tableExists("dirs")); + Assert.ok(await c.tableExists("files")); + + await c.close(); +}); + +add_task(async function test_index_exists() { + let c = await getDummyDatabase("index_exists"); + + Assert.equal(false, await c.indexExists("does_not_exist")); + + await c.execute("CREATE INDEX my_index ON dirs (path)"); + Assert.ok(await c.indexExists("my_index")); + + await c.close(); +}); + +add_task(async function test_temp_table_exists() { + let c = await getDummyTempDatabase("temp_table_exists"); + + Assert.equal(false, await c.tableExists("temp_does_not_exist")); + Assert.ok(await c.tableExists("dirs")); + Assert.ok(await c.tableExists("files")); + + await c.close(); +}); + +add_task(async function test_temp_index_exists() { + let c = await getDummyTempDatabase("temp_index_exists"); + + Assert.equal(false, await c.indexExists("temp_does_not_exist")); + + await c.execute("CREATE INDEX my_index ON dirs (path)"); + Assert.ok(await c.indexExists("my_index")); + + await c.close(); +}); + +add_task(async function test_close_cached() { + let c = await getDummyDatabase("close_cached"); + + await c.executeCached("SELECT * FROM dirs"); + await c.executeCached("SELECT * FROM files"); + + await c.close(); +}); + +add_task(async function test_execute_invalid_statement() { + let c = await getDummyDatabase("invalid_statement"); + + await new Promise(resolve => { + Assert.equal(c._connectionData._anonymousStatements.size, 0); + + c.execute("SELECT invalid FROM unknown").then( + do_throw, + function onError(error) { + resolve(); + } + ); + }); + + // Ensure we don't leak the statement instance. + Assert.equal(c._connectionData._anonymousStatements.size, 0); + + await c.close(); +}); + +add_task(async function test_incorrect_like_bindings() { + let c = await getDummyDatabase("incorrect_like_bindings"); + + let sql = "select * from dirs where path LIKE 'non%'"; + Assert.throws(() => c.execute(sql), /Please enter a LIKE clause/); + Assert.throws(() => c.executeCached(sql), /Please enter a LIKE clause/); + + await c.close(); +}); +add_task(async function test_on_row_exception_ignored() { + let c = await getDummyDatabase("on_row_exception_ignored"); + + let sql = "INSERT INTO dirs (path) VALUES (?)"; + for (let i = 0; i < 10; i++) { + await c.executeCached(sql, ["dir" + i]); + } + + let i = 0; + let hasResult = await c.execute( + "SELECT * FROM DIRS", + null, + function onRow(row) { + i++; + + throw new Error("Some silly error."); + } + ); + + Assert.equal(hasResult, true); + Assert.equal(i, 10); + + await c.close(); +}); + +// Ensure StopIteration during onRow causes processing to stop. +add_task(async function test_on_row_stop_iteration() { + let c = await getDummyDatabase("on_row_stop_iteration"); + + let sql = "INSERT INTO dirs (path) VALUES (?)"; + for (let i = 0; i < 10; i++) { + await c.executeCached(sql, ["dir" + i]); + } + + let i = 0; + let hasResult = await c.execute( + "SELECT * FROM dirs", + null, + function onRow(row, cancel) { + i++; + + if (i == 5) { + cancel(); + } + } + ); + + Assert.equal(hasResult, true); + Assert.equal(i, 5); + + await c.close(); +}); + +// Ensure execute resolves to false when no rows are selected. +add_task(async function test_on_row_stop_iteration() { + let c = await getDummyDatabase("no_on_row"); + + let i = 0; + let hasResult = await c.execute( + `SELECT * FROM dirs WHERE path="nonexistent"`, + null, + function onRow(row) { + i++; + } + ); + + Assert.equal(hasResult, false); + Assert.equal(i, 0); + + await c.close(); +}); + +add_task(async function test_invalid_transaction_type() { + let c = await getDummyDatabase("invalid_transaction_type"); + + Assert.throws( + () => c.executeTransaction(function () {}, "foobar"), + /Unknown transaction type/, + "Unknown transaction type should throw" + ); + + await c.close(); +}); + +add_task(async function test_execute_transaction_success() { + let c = await getDummyDatabase("execute_transaction_success"); + + Assert.ok(!c.transactionInProgress); + + await c.executeTransaction(async function transaction(conn) { + Assert.equal(c, conn); + Assert.ok(conn.transactionInProgress); + + await conn.execute("INSERT INTO dirs (path) VALUES ('foo')"); + }); + + Assert.ok(!c.transactionInProgress); + let rows = await c.execute("SELECT * FROM dirs"); + Assert.ok(Array.isArray(rows)); + Assert.equal(rows.length, 1); + + await c.close(); +}); + +add_task(async function test_execute_transaction_rollback() { + let c = await getDummyDatabase("execute_transaction_rollback"); + + let deferred = Promise.withResolvers(); + + c.executeTransaction(async function transaction(conn) { + await conn.execute("INSERT INTO dirs (path) VALUES ('foo')"); + print("Expecting error with next statement."); + await conn.execute("INSERT INTO invalid VALUES ('foo')"); + + // We should never get here. + do_throw(); + }).then(do_throw, function onError(error) { + deferred.resolve(); + }); + + await deferred.promise; + + let rows = await c.execute("SELECT * FROM dirs"); + Assert.equal(rows.length, 0); + + await c.close(); +}); + +add_task(async function test_close_during_transaction() { + let c = await getDummyDatabase("close_during_transaction"); + + await c.execute("INSERT INTO dirs (path) VALUES ('foo')"); + + let promise = c.executeTransaction(async function transaction(conn) { + await c.execute("INSERT INTO dirs (path) VALUES ('bar')"); + }); + await c.close(); + + await Assert.rejects( + promise, + /Transaction canceled due to a closed connection/, + "closing a connection in the middle of a transaction should reject it" + ); + + let c2 = await getConnection("close_during_transaction"); + let rows = await c2.execute("SELECT * FROM dirs"); + Assert.equal(rows.length, 1); + + await c2.close(); +}); + +// Verify that we support concurrent transactions. +add_task(async function test_multiple_transactions() { + let c = await getDummyDatabase("detect_multiple_transactions"); + + for (let i = 0; i < 10; ++i) { + // We don't wait for these transactions. + c.executeTransaction(async function () { + await c.execute("INSERT INTO dirs (path) VALUES (:path)", { + path: `foo${i}`, + }); + await c.execute("SELECT * FROM dirs"); + }); + } + for (let i = 0; i < 10; ++i) { + await c.executeTransaction(async function () { + await c.execute("INSERT INTO dirs (path) VALUES (:path)", { + path: `bar${i}`, + }); + await c.execute("SELECT * FROM dirs"); + }); + } + + let rows = await c.execute("SELECT * FROM dirs"); + Assert.equal(rows.length, 20); + + await c.close(); +}); + +// Verify that wrapped transactions ignore a BEGIN TRANSACTION failure, when +// an externally opened transaction exists. +add_task(async function test_wrapped_connection_transaction() { + let file = new FileUtils.File( + PathUtils.join(PROFILE_DIR, "test_wrapStorageConnection.sqlite") + ); + let c = await new Promise((resolve, reject) => { + Services.storage.openAsyncDatabase( + file, + /* openFlags */ Ci.mozIStorageService.OPEN_DEFAULT, + /* connectionFlags */ Ci.mozIStorageService.CONNECTION_DEFAULT, + (status, db) => { + if (Components.isSuccessCode(status)) { + resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection)); + } else { + reject(new Error(status)); + } + } + ); + }); + + let wrapper = await Sqlite.wrapStorageConnection({ connection: c }); + // Start a transaction on the raw connection. + await c.executeSimpleSQLAsync("BEGIN"); + // Now use executeTransaction, it will be executed, but not in a transaction. + await wrapper.executeTransaction(async function () { + await wrapper.execute( + "CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT)" + ); + }); + // This should not fail cause the internal transaction has not been created. + await c.executeSimpleSQLAsync("COMMIT"); + + await wrapper.execute("SELECT * FROM test"); + + // Closing the wrapper should just finalize statements but not close the + // database. + await wrapper.close(); + await c.asyncClose(); +}); + +add_task(async function test_transaction_timeout() { + // Lower the transactions timeout for the test. + let defaultTimeout = Sqlite.TRANSACTIONS_TIMEOUT_MS; + Sqlite.TRANSACTIONS_TIMEOUT_MS = 500; + Services.telemetry.clearScalars(); + let myResolve = () => {}; + try { + let c = await getDummyDatabase("transaction_timeout"); + Assert.ok(!c.transactionInProgress, "Should not be in a transaction"); + let promise = c.executeTransaction(async function transaction(conn) { + // Make a change, we'll later check it is undone by ROLLBACK. + await conn.execute( + "CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT)" + ); + Assert.ok(c.transactionInProgress, "Should be in a transaction"); + // Return a never fulfilled promise. + await new Promise(resolve => { + // Keep this alive regardless GC, and clean it up in finally. + myResolve = resolve; + }); + }); + + await Assert.rejects( + promise, + /Transaction timeout, most likely caused by unresolved pending work./, + "A transaction timeout should reject it" + ); + + let rows = await c.execute("SELECT * FROM dirs"); + Assert.equal(rows.length, 0, "Changes should have been rolled back"); + await c.close(); + + let scalars = TelemetryTestUtils.getProcessScalars("parent", true, true); + TelemetryTestUtils.assertKeyedScalar( + scalars, + "mozstorage.sqlitejsm_transaction_timeout", + "test_transaction_timeout@test_sqlite.js", + 1 + ); + } finally { + Sqlite.TRANSACTIONS_TIMEOUT_MS = defaultTimeout; + myResolve(); + } +}); + +add_task(async function test_shrink_memory() { + let c = await getDummyDatabase("shrink_memory"); + + // It's just a simple sanity test. We have no way of measuring whether this + // actually does anything. + + await c.shrinkMemory(); + await c.close(); +}); + +add_task(async function test_no_shrink_on_init() { + let c = await getConnection("no_shrink_on_init", { + shrinkMemoryOnConnectionIdleMS: 200, + }); + + let count = 0; + Object.defineProperty(c._connectionData, "shrinkMemory", { + value() { + count++; + }, + }); + + // We should not shrink until a statement has been executed. + await sleep(220); + Assert.equal(count, 0); + + await c.execute("SELECT 1"); + await sleep(220); + Assert.equal(count, 1); + + await c.close(); +}); + +add_task(async function test_idle_shrink_fires() { + let c = await getDummyDatabase("idle_shrink_fires", { + shrinkMemoryOnConnectionIdleMS: 200, + }); + c._connectionData._clearIdleShrinkTimer(); + + let oldShrink = c._connectionData.shrinkMemory; + let shrinkPromises = []; + + let count = 0; + Object.defineProperty(c._connectionData, "shrinkMemory", { + value() { + count++; + let promise = oldShrink.call(c._connectionData); + shrinkPromises.push(promise); + return promise; + }, + }); + + // We reset the idle shrink timer after monkeypatching because otherwise the + // installed timer callback will reference the non-monkeypatched function. + c._connectionData._startIdleShrinkTimer(); + + await sleep(220); + Assert.equal(count, 1); + Assert.equal(shrinkPromises.length, 1); + await shrinkPromises[0]; + shrinkPromises.shift(); + + // We shouldn't shrink again unless a statement was executed. + await sleep(300); + Assert.equal(count, 1); + + await c.execute("SELECT 1"); + await sleep(300); + + Assert.equal(count, 2); + Assert.equal(shrinkPromises.length, 1); + await shrinkPromises[0]; + + await c.close(); +}); + +add_task(async function test_idle_shrink_reset_on_operation() { + const INTERVAL = 500; + let c = await getDummyDatabase("idle_shrink_reset_on_operation", { + shrinkMemoryOnConnectionIdleMS: INTERVAL, + }); + + c._connectionData._clearIdleShrinkTimer(); + + let oldShrink = c._connectionData.shrinkMemory; + let shrinkPromises = []; + let count = 0; + + Object.defineProperty(c._connectionData, "shrinkMemory", { + value() { + count++; + let promise = oldShrink.call(c._connectionData); + shrinkPromises.push(promise); + return promise; + }, + }); + + let now = new Date(); + c._connectionData._startIdleShrinkTimer(); + + let initialIdle = new Date(now.getTime() + INTERVAL); + + // Perform database operations until initial scheduled time has been passed. + let i = 0; + while (new Date() < initialIdle) { + await c.execute("INSERT INTO dirs (path) VALUES (?)", ["" + i]); + i++; + } + + Assert.ok(i > 0); + + // We should not have performed an idle while doing operations. + Assert.equal(count, 0); + + // Wait for idle timer. + await sleep(INTERVAL); + + // Ensure we fired. + Assert.equal(count, 1); + Assert.equal(shrinkPromises.length, 1); + await shrinkPromises[0]; + + await c.close(); +}); + +add_task(async function test_in_progress_counts() { + let c = await getDummyDatabase("in_progress_counts"); + Assert.equal(c._connectionData._statementCounter, c._initialStatementCount); + Assert.equal(c._connectionData._pendingStatements.size, 0); + await c.executeCached("INSERT INTO dirs (path) VALUES ('foo')"); + Assert.equal( + c._connectionData._statementCounter, + c._initialStatementCount + 1 + ); + Assert.equal(c._connectionData._pendingStatements.size, 0); + + let expectOne; + let expectTwo; + + // We want to make sure that two queries executing simultaneously + // result in `_pendingStatements.size` reaching 2, then dropping back to 0. + // + // To do so, we kick off a second statement within the row handler + // of the first, then wait for both to finish. + + let inner = Promise.withResolvers(); + await c.executeCached("SELECT * from dirs", null, function onRow() { + // In the onRow handler, we're still an outstanding query. + // Expect a single in-progress entry. + expectOne = c._connectionData._pendingStatements.size; + + // Start another query, checking that after its statement has been created + // there are two statements in progress. + c.executeCached("SELECT 10, path from dirs").then(inner.resolve); + expectTwo = c._connectionData._pendingStatements.size; + }); + + await inner.promise; + + Assert.equal(expectOne, 1); + Assert.equal(expectTwo, 2); + Assert.equal( + c._connectionData._statementCounter, + c._initialStatementCount + 3 + ); + Assert.equal(c._connectionData._pendingStatements.size, 0); + + await c.close(); +}); + +add_task(async function test_discard_while_active() { + let c = await getDummyDatabase("discard_while_active"); + + await c.executeCached("INSERT INTO dirs (path) VALUES ('foo')"); + await c.executeCached("INSERT INTO dirs (path) VALUES ('bar')"); + + let discarded = -1; + let first = true; + let sql = "SELECT * FROM dirs"; + await c.executeCached(sql, null, function onRow(row) { + if (!first) { + return; + } + first = false; + discarded = c.discardCachedStatements(); + }); + + // We discarded everything, because the SELECT had already started to run. + Assert.equal(3, discarded); + + // And again is safe. + Assert.equal(0, c.discardCachedStatements()); + + await c.close(); +}); + +add_task(async function test_discard_cached() { + let c = await getDummyDatabase("discard_cached"); + + await c.executeCached("SELECT * from dirs"); + Assert.equal(1, c._connectionData._cachedStatements.size); + + await c.executeCached("SELECT * from files"); + Assert.equal(2, c._connectionData._cachedStatements.size); + + await c.executeCached("SELECT * from dirs"); + Assert.equal(2, c._connectionData._cachedStatements.size); + + c.discardCachedStatements(); + Assert.equal(0, c._connectionData._cachedStatements.size); + + await c.close(); +}); + +add_task(async function test_array_binding_with_null() { + let c = await getDummyDatabase("array_binding_null"); + + let bindings = [null, "test"]; + + let sql = "INSERT INTO files (dir_id, path) VALUES (?, ?)"; + let result = await c.execute(sql, bindings); + Assert.equal(result.length, 0); + + let rows = await c.executeCached("SELECT * from files"); + Assert.equal(rows.length, 1); + Assert.strictEqual(rows[0].getResultByName("dir_id"), null); + Assert.equal(rows[0].getResultByName("path"), "test"); + await c.close(); +}); + +add_task(async function test_programmatic_binding() { + let c = await getDummyDatabase("programmatic_binding"); + + let bindings = [ + { id: 1, path: "foobar" }, + { id: null, path: "baznoo" }, + { id: 5, path: "toofoo" }, + ]; + + let sql = "INSERT INTO dirs VALUES (:id, :path)"; + let result = await c.execute(sql, bindings); + Assert.equal(result.length, 0); + + let rows = await c.executeCached("SELECT * from dirs"); + Assert.equal(rows.length, 3); + await c.close(); +}); + +add_task(async function test_programmatic_binding_transaction() { + let c = await getDummyDatabase("programmatic_binding_transaction"); + + let bindings = [ + { id: 1, path: "foobar" }, + { id: null, path: "baznoo" }, + { id: 5, path: "toofoo" }, + ]; + + let sql = "INSERT INTO dirs VALUES (:id, :path)"; + await c.executeTransaction(async function transaction() { + let result = await c.execute(sql, bindings); + Assert.equal(result.length, 0); + + let rows = await c.executeCached("SELECT * from dirs"); + Assert.equal(rows.length, 3); + }); + + // Transaction committed. + let rows = await c.executeCached("SELECT * from dirs"); + Assert.equal(rows.length, 3); + await c.close(); +}); + +add_task( + async function test_programmatic_binding_transaction_partial_rollback() { + let c = await getDummyDatabase( + "programmatic_binding_transaction_partial_rollback" + ); + + let bindings = [ + { id: 2, path: "foobar" }, + { id: 3, path: "toofoo" }, + ]; + + let sql = "INSERT INTO dirs VALUES (:id, :path)"; + + // Add some data in an implicit transaction before beginning the batch insert. + await c.execute(sql, { id: 1, path: "works" }); + + let secondSucceeded = false; + try { + await c.executeTransaction(async function transaction() { + // Insert one row. This won't implicitly start a transaction. + await c.execute(sql, bindings[0]); + + // Insert multiple rows. mozStorage will want to start a transaction. + // One of the inserts will fail, so the transaction should be rolled back. + await c.execute(sql, bindings); + secondSucceeded = true; + }); + } catch (ex) { + print("Caught expected exception: " + ex); + Assert.greater( + ex.result, + 0x80000000, + "The ex.result value should be forwarded." + ); + } + + // We did not get to the end of our in-transaction block. + Assert.ok(!secondSucceeded); + + // Everything that happened in *our* transaction, not mozStorage's, got + // rolled back, but the first row still exists. + let rows = await c.executeCached("SELECT * from dirs"); + Assert.equal(rows.length, 1); + Assert.equal(rows[0].getResultByName("path"), "works"); + await c.close(); + } +); + +// Just like the previous test, but relying on the implicit +// transaction established by mozStorage. +add_task(async function test_programmatic_binding_implicit_transaction() { + let c = await getDummyDatabase("programmatic_binding_implicit_transaction"); + + let bindings = [ + { id: 2, path: "foobar" }, + { id: 1, path: "toofoo" }, + ]; + + let sql = "INSERT INTO dirs VALUES (:id, :path)"; + let secondSucceeded = false; + await c.execute(sql, { id: 1, path: "works" }); + try { + await c.execute(sql, bindings); + secondSucceeded = true; + } catch (ex) { + print("Caught expected exception: " + ex); + Assert.greater( + ex.result, + 0x80000000, + "The ex.result value should be forwarded." + ); + } + + Assert.ok(!secondSucceeded); + + // The entire batch failed. + let rows = await c.executeCached("SELECT * from dirs"); + Assert.equal(rows.length, 1); + Assert.equal(rows[0].getResultByName("path"), "works"); + await c.close(); +}); + +// Test that direct binding of params and execution through mozStorage doesn't +// error when we manually create a transaction. See Bug 856925. +add_task(async function test_direct() { + let file = new FileUtils.File( + PathUtils.join(PathUtils.tempDir, "test_direct.sqlite") + ); + file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE); + print("Opening " + file.path); + + let db = Services.storage.openDatabase(file); + print("Opened " + db); + + db.executeSimpleSQL( + "CREATE TABLE types (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, UNIQUE (name))" + ); + print("Executed setup."); + + let statement = db.createAsyncStatement( + "INSERT INTO types (name) VALUES (:name)" + ); + let params = statement.newBindingParamsArray(); + let one = params.newBindingParams(); + one.bindByName("name", null); + params.addParams(one); + let two = params.newBindingParams(); + two.bindByName("name", "bar"); + params.addParams(two); + + print("Beginning transaction."); + let begin = db.createAsyncStatement("BEGIN DEFERRED TRANSACTION"); + let end = db.createAsyncStatement("COMMIT TRANSACTION"); + + let deferred = Promise.withResolvers(); + begin.executeAsync({ + handleCompletion(reason) { + deferred.resolve(); + }, + }); + await deferred.promise; + + statement.bindParameters(params); + + deferred = Promise.withResolvers(); + print("Executing async."); + statement.executeAsync({ + handleResult(resultSet) {}, + + handleError(error) { + print( + "Error when executing SQL (" + error.result + "): " + error.message + ); + print("Original error: " + error.error); + deferred.reject(); + }, + + handleCompletion(reason) { + print("Completed."); + deferred.resolve(); + }, + }); + + await deferred.promise; + + deferred = Promise.withResolvers(); + end.executeAsync({ + handleCompletion(reason) { + deferred.resolve(); + }, + }); + await deferred.promise; + + statement.finalize(); + begin.finalize(); + end.finalize(); + + deferred = Promise.withResolvers(); + db.asyncClose(function () { + deferred.resolve(); + }); + await deferred.promise; +}); + +// Test Sqlite.cloneStorageConnection. +add_task(async function test_cloneStorageConnection() { + let file = new FileUtils.File( + PathUtils.join(PROFILE_DIR, "test_cloneStorageConnection.sqlite") + ); + let c = await new Promise((resolve, reject) => { + Services.storage.openAsyncDatabase( + file, + /* openFlags */ Ci.mozIStorageService.OPEN_DEFAULT, + /* connectionFlags */ Ci.mozIStorageService.CONNECTION_DEFAULT, + (status, db) => { + if (Components.isSuccessCode(status)) { + resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection)); + } else { + reject(new Error(status)); + } + } + ); + }); + + let clone = await Sqlite.cloneStorageConnection({ + connection: c, + readOnly: true, + }); + Assert.equal(clone.defaultTransactionType, "DEFERRED"); + // Just check that it works. + await clone.execute("SELECT 1"); + + info("Set default transaction type on storage connection"); + c.defaultTransactionType = Ci.mozIStorageConnection.TRANSACTION_IMMEDIATE; + + let clone2 = await Sqlite.cloneStorageConnection({ + connection: c, + readOnly: false, + }); + Assert.equal(clone2.defaultTransactionType, "IMMEDIATE"); + // Just check that it works. + await clone2.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)"); + + // Closing order should not matter. + await c.asyncClose(); + await clone2.close(); + await clone.close(); +}); + +// Test Sqlite.cloneStorageConnection invalid argument. +add_task(async function test_cloneStorageConnection() { + try { + await Sqlite.cloneStorageConnection({ connection: null }); + do_throw(new Error("Should throw on invalid connection")); + } catch (ex) { + if (ex.name != "TypeError") { + throw ex; + } + } +}); + +// Test clone() method. +add_task(async function test_clone() { + let c = await getDummyDatabase("clone"); + + let clone = await c.clone(); + // Just check that it works. + await clone.execute("SELECT 1"); + // Closing order should not matter. + await c.close(); + await clone.close(); +}); + +// Test clone(readOnly) method. +add_task(async function test_readOnly_clone() { + let path = PathUtils.join(PROFILE_DIR, "test_readOnly_clone.sqlite"); + let c = await Sqlite.openConnection({ path, sharedMemoryCache: false }); + + let clone = await c.clone(true); + // Just check that it works. + await clone.execute("SELECT 1"); + // But should not be able to write. + + await Assert.rejects( + clone.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)"), + /readonly/ + ); + // Closing order should not matter. + await c.close(); + await clone.close(); +}); + +// Test Sqlite.wrapStorageConnection. +add_task(async function test_wrapStorageConnection() { + let file = new FileUtils.File( + PathUtils.join(PROFILE_DIR, "test_wrapStorageConnection.sqlite") + ); + let c = await new Promise((resolve, reject) => { + Services.storage.openAsyncDatabase( + file, + /* openFlags */ Ci.mozIStorageService.OPEN_DEFAULT, + /* connectionFlags */ Ci.mozIStorageService.CONNECTION_DEFAULT, + (status, db) => { + if (Components.isSuccessCode(status)) { + resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection)); + } else { + reject(new Error(status)); + } + } + ); + }); + + let wrapper = await Sqlite.wrapStorageConnection({ connection: c }); + Assert.equal(wrapper.defaultTransactionType, "DEFERRED"); + // Just check that it works. + await wrapper.execute("SELECT 1"); + await wrapper.executeCached("SELECT 1"); + + // Closing the wrapper should just finalize statements but not close the + // database. + await wrapper.close(); + + info("Set default transaction type on storage connection"); + c.defaultTransactionType = Ci.mozIStorageConnection.TRANSACTION_EXCLUSIVE; + + let wrapper2 = await Sqlite.wrapStorageConnection({ connection: c }); + Assert.equal(wrapper2.defaultTransactionType, "EXCLUSIVE"); + // Just check that it works. + await wrapper2.execute("SELECT 1"); + + await wrapper2.close(); + + await c.asyncClose(); +}); + +// Test finalization +add_task(async function test_closed_by_witness() { + failTestsOnAutoClose(false); + let c = await getDummyDatabase("closed_by_witness"); + + Services.obs.notifyObservers( + null, + "sqlite-finalization-witness", + c._connectionData._identifier + ); + // Since we triggered finalization ourselves, tell the witness to + // forget the connection so it does not trigger a finalization again + c._witness.forget(); + await c._connectionData._deferredClose.promise; + Assert.ok(!c._connectionData._open); + failTestsOnAutoClose(true); +}); + +add_task(async function test_warning_message_on_finalization() { + failTestsOnAutoClose(false); + let c = await getDummyDatabase("warning_message_on_finalization"); + let identifier = c._connectionData._identifier; + let deferred = Promise.withResolvers(); + + let listener = { + observe(msg) { + let messageText = msg.message; + // Make sure the message starts with a warning containing the + // connection identifier + if ( + messageText.includes("Warning: Sqlite connection '" + identifier + "'") + ) { + deferred.resolve(); + } + }, + }; + Services.console.registerListener(listener); + + Services.obs.notifyObservers(null, "sqlite-finalization-witness", identifier); + // Since we triggered finalization ourselves, tell the witness to + // forget the connection so it does not trigger a finalization again + c._witness.forget(); + + await deferred.promise; + Services.console.unregisterListener(listener); + failTestsOnAutoClose(true); +}); + +add_task(async function test_error_message_on_unknown_finalization() { + failTestsOnAutoClose(false); + let deferred = Promise.withResolvers(); + + let listener = { + observe(msg) { + let messageText = msg.message; + if ( + messageText.includes( + "Error: Attempt to finalize unknown Sqlite connection: foo" + ) + ) { + deferred.resolve(); + } + }, + }; + Services.console.registerListener(listener); + Services.obs.notifyObservers(null, "sqlite-finalization-witness", "foo"); + + await deferred.promise; + Services.console.unregisterListener(listener); + failTestsOnAutoClose(true); +}); + +add_task(async function test_forget_witness_on_close() { + let c = await getDummyDatabase("forget_witness_on_close"); + + let forgetCalled = false; + let oldWitness = c._witness; + c._witness = { + forget() { + forgetCalled = true; + oldWitness.forget(); + }, + }; + + await c.close(); + // After close, witness should have forgotten the connection + Assert.ok(forgetCalled); +}); + +add_task(async function test_close_database_on_gc() { + failTestsOnAutoClose(false); + let finalPromise; + + { + let collectedPromises = []; + for (let i = 0; i < 100; ++i) { + let deferred = Promise.withResolvers(); + let c = await getDummyDatabase("gc_" + i); + c._connectionData._deferredClose.promise.then(deferred.resolve); + collectedPromises.push(deferred.promise); + } + finalPromise = Promise.all(collectedPromises); + } + + // Call getDummyDatabase once more to clear any remaining + // references. This is needed at the moment, otherwise + // garbage-collection takes place after the shutdown barrier and the + // test will timeout. Once that is fixed, we can remove this line + // and be fine as long as the connections are garbage-collected. + let last = await getDummyDatabase("gc_last"); + await last.close(); + + Cu.forceGC(); + Cu.forceCC(); + Cu.forceShrinkingGC(); + + await finalPromise; + failTestsOnAutoClose(true); +}); + +// Test all supported datatypes +add_task(async function test_datatypes() { + let c = await getConnection("datatypes"); + await c.execute("DROP TABLE IF EXISTS datatypes"); + await c.execute(`CREATE TABLE datatypes ( + null_col NULL, + integer_col INTEGER NOT NULL, + text_col TEXT NOT NULL, + blob_col BLOB NOT NULL, + real_col REAL NOT NULL, + numeric_col NUMERIC NOT NULL + )`); + const bindings = [ + { + null_col: null, + integer_col: 12345, + text_col: "qwerty", + blob_col: new Uint8Array(256).map((value, index) => index % 256), + real_col: 3.14159265359, + numeric_col: true, + }, + { + null_col: null, + integer_col: -12345, + text_col: "", + blob_col: new Uint8Array(256 * 2).map((value, index) => index % 256), + real_col: Number.NEGATIVE_INFINITY, + numeric_col: false, + }, + ]; + + await c.execute( + `INSERT INTO datatypes VALUES ( + :null_col, + :integer_col, + :text_col, + :blob_col, + :real_col, + :numeric_col + )`, + bindings + ); + + let rows = await c.execute("SELECT * FROM datatypes"); + Assert.ok(Array.isArray(rows)); + Assert.equal(rows.length, bindings.length); + for (let i = 0; i < bindings.length; ++i) { + let binding = bindings[i]; + let row = rows[i]; + for (let colName in binding) { + // In Sqlite bool is stored and then retrieved as numeric. + let val = + typeof binding[colName] == "boolean" + ? +binding[colName] + : binding[colName]; + Assert.deepEqual(val, row.getResultByName(colName)); + } + } + await c.close(); +}); + +add_task(async function test_interrupt() { + // Testing the interrupt functionality is left to mozStorage unit tests, here + // we'll just test error conditions. + let c = await getDummyDatabase("interrupt"); + await c.interrupt(); + ok(true, "Sqlite.interrupt() should not throw on a writable connection"); + await c.close(); + Assert.throws( + () => c.interrupt(), + /Connection is not open/, + "Sqlite.interrupt() should throw on a closed connection" + ); +}); + +add_task(async function test_pageSize() { + // Testing the possibility to set the page size on database creation. + await Assert.rejects( + getDummyDatabase("pagesize", { pageSize: 1234 }), + /Invalid pageSize/, + "Check invalid pageSize value" + ); + let c = await getDummyDatabase("pagesize", { pageSize: 8192 }); + Assert.equal( + (await c.execute("PRAGMA page_size"))[0].getResultByIndex(0), + 8192, + "Check page size was set" + ); + await c.close(); +}); diff --git a/toolkit/modules/tests/xpcshell/test_sqlite_autoVacuum.js b/toolkit/modules/tests/xpcshell/test_sqlite_autoVacuum.js new file mode 100644 index 0000000000..f3a6dced3f --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_sqlite_autoVacuum.js @@ -0,0 +1,96 @@ +"use strict"; + +const { Sqlite } = ChromeUtils.importESModule( + "resource://gre/modules/Sqlite.sys.mjs" +); +const { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +/** + * Sends a fake idle-daily notification to the VACUUM Manager. + */ +function synthesize_idle_daily() { + Cc["@mozilla.org/storage/vacuum;1"] + .getService(Ci.nsIObserver) + .observe(null, "idle-daily", null); +} + +function unregister_vacuum_participants() { + // First unregister other participants. + for (let { data: entry } of Services.catMan.enumerateCategory( + "vacuum-participant" + )) { + Services.catMan.deleteCategoryEntry("vacuum-participant", entry, false); + } +} + +function reset_vacuum_date(dbname) { + let date = parseInt(Date.now() / 1000 - 31 * 86400); + // Set last VACUUM to a date in the past. + Services.prefs.setIntPref(`storage.vacuum.last.${dbname}`, date); + return date; +} + +function get_vacuum_date(dbname) { + return Services.prefs.getIntPref(`storage.vacuum.last.${dbname}`, 0); +} + +async function get_freelist_count(conn) { + return (await conn.execute("PRAGMA freelist_count"))[0].getResultByIndex(0); +} + +async function get_auto_vacuum(conn) { + return (await conn.execute("PRAGMA auto_vacuum"))[0].getResultByIndex(0); +} + +async function test_vacuum(options = {}) { + unregister_vacuum_participants(); + const dbName = "testVacuum.sqlite"; + const dbFile = PathUtils.join(PathUtils.profileDir, dbName); + let lastVacuumDate = reset_vacuum_date(dbName); + let conn = await Sqlite.openConnection( + Object.assign( + { + path: dbFile, + vacuumOnIdle: true, + }, + options + ) + ); + // Ensure the category manager is up-to-date. + await TestUtils.waitForTick(); + + Assert.equal( + await get_auto_vacuum(conn), + options.incrementalVacuum ? 2 : 0, + "Check auto_vacuum" + ); + + // Generate some freelist page. + await conn.execute("CREATE TABLE test (id INTEGER)"); + await conn.execute("DROP TABLE test"); + Assert.greater(await get_freelist_count(conn), 0, "Check freelist_count"); + + let promiseVacuumEnd = TestUtils.topicObserved( + "vacuum-end", + (_, d) => d == dbName + ); + synthesize_idle_daily(); + info("Await vacuum end"); + await promiseVacuumEnd; + + Assert.greater(get_vacuum_date(dbName), lastVacuumDate); + + Assert.equal(await get_freelist_count(conn), 0, "Check freelist_count"); + + await conn.close(); + await IOUtils.remove(dbFile); +} + +add_task(async function test_vacuumOnIdle() { + info("Test full vacuum"); + await test_vacuum(); + info("Test incremental vacuum"); + await test_vacuum({ incrementalVacuum: true }); +}); diff --git a/toolkit/modules/tests/xpcshell/test_sqlite_shutdown.js b/toolkit/modules/tests/xpcshell/test_sqlite_shutdown.js new file mode 100644 index 0000000000..46dcd57fd2 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_sqlite_shutdown.js @@ -0,0 +1,124 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { AsyncShutdown } = ChromeUtils.importESModule( + "resource://gre/modules/AsyncShutdown.sys.mjs" +); + +function getConnection(dbName, extraOptions = {}) { + let path = dbName + ".sqlite"; + let options = { path }; + for (let [k, v] of Object.entries(extraOptions)) { + options[k] = v; + } + + return Sqlite.openConnection(options); +} + +async function getDummyDatabase(name, extraOptions = {}) { + const TABLES = { + dirs: "id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT", + files: "id INTEGER PRIMARY KEY AUTOINCREMENT, dir_id INTEGER, path TEXT", + }; + + let c = await getConnection(name, extraOptions); + c._initialStatementCount = 0; + + for (let [k, v] of Object.entries(TABLES)) { + await c.execute("CREATE TABLE " + k + "(" + v + ")"); + c._initialStatementCount++; + } + + return c; +} + +function sleep(ms) { + return new Promise(resolve => { + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + + timer.initWithCallback( + { + notify() { + resolve(); + }, + }, + ms, + timer.TYPE_ONE_SHOT + ); + }); +} + +// +// ----------- Don't add a test after this one, as it shuts down Sqlite.sys.mjs +// +add_task(async function test_shutdown_clients() { + info("Ensuring that Sqlite.sys.mjs doesn't shutdown before its clients"); + + let assertions = []; + + let sleepStarted = false; + let sleepComplete = false; + Sqlite.shutdown.addBlocker( + "test_sqlite.js shutdown blocker (sleep)", + async function () { + sleepStarted = true; + await sleep(100); + sleepComplete = true; + } + ); + assertions.push({ name: "sleepStarted", value: () => sleepStarted }); + assertions.push({ name: "sleepComplete", value: () => sleepComplete }); + + Sqlite.shutdown.addBlocker( + "test_sqlite.js shutdown blocker (immediate)", + true + ); + + let dbOpened = false; + let dbClosed = false; + + Sqlite.shutdown.addBlocker( + "test_sqlite.js shutdown blocker (open a connection during shutdown)", + async function () { + let db = await getDummyDatabase("opened during shutdown"); + dbOpened = true; + db.close().then(() => (dbClosed = true)); // Don't wait for this task to complete, Sqlite.sys.mjs must wait automatically + } + ); + + assertions.push({ name: "dbOpened", value: () => dbOpened }); + assertions.push({ name: "dbClosed", value: () => dbClosed }); + + info("Now shutdown Sqlite.sys.mjs synchronously"); + Services.prefs.setBoolPref("toolkit.asyncshutdown.testing", true); + // Check opening a connection during shutdown fails. + let deferred = Promise.withResolvers(); + let conn = Sqlite.openConnection({ + path: PathUtils.join(PathUtils.profileDir, "test_shutdown.sqlite"), + testDelayedOpenPromise: deferred.promise, + }); + await AsyncShutdown.profileBeforeChange._trigger(); + deferred.resolve(); + await Assert.rejects( + conn, + /has been shutdown/, + "Should close the connection and not block" + ); + Services.prefs.clearUserPref("toolkit.asyncshutdown.testing"); + + for (let { name, value } of assertions) { + Assert.ok(value(), "Checking: " + name); + } + + info("Ensure that we cannot open databases anymore"); + let exn; + try { + await getDummyDatabase("opened after shutdown"); + } catch (ex) { + exn = ex; + } + Assert.ok(!!exn, `exception: ${exn.message}`); + Assert.ok(exn.message.includes("Sqlite.sys.mjs has been shutdown")); +}); diff --git a/toolkit/modules/tests/xpcshell/test_timer.js b/toolkit/modules/tests/xpcshell/test_timer.js new file mode 100644 index 0000000000..421244c769 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_timer.js @@ -0,0 +1,149 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests exports from Timer.sys.mjs + +var imported = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); + +add_task(async function test_setTimeout() { + let timeout1 = imported.setTimeout( + () => do_throw("Should not be called"), + 100 + ); + Assert.equal(typeof timeout1, "number", "setTimeout returns a number"); + Assert.greater(timeout1, 0, "setTimeout returns a positive number"); + + imported.clearTimeout(timeout1); + + await new Promise(resolve => { + let timeout2 = imported.setTimeout( + (param1, param2) => { + Assert.ok(true, "Should be called"); + Assert.equal(param1, 5, "first parameter is correct"); + Assert.equal(param2, "test", "second parameter is correct"); + resolve(); + }, + 100, + 5, + "test" + ); + + Assert.equal(typeof timeout2, "number", "setTimeout returns a number"); + Assert.greater(timeout2, 0, "setTimeout returns a positive number"); + Assert.notEqual( + timeout1, + timeout2, + "Calling setTimeout again returns a different value" + ); + }); +}); + +add_task(async function test_setInterval() { + let interval1 = imported.setInterval( + () => do_throw("Should not be called!"), + 100 + ); + Assert.equal(typeof interval1, "number", "setInterval returns a number"); + Assert.greater(interval1, 0, "setTimeout returns a positive number"); + + imported.clearInterval(interval1); + + const EXPECTED_CALLS = 5; + let calls = 0; + + await new Promise(resolve => { + let interval2 = imported.setInterval( + (param1, param2) => { + Assert.ok(true, "Should be called"); + Assert.equal(param1, 15, "first parameter is correct"); + Assert.equal(param2, "hola", "second parameter is correct"); + if (calls >= EXPECTED_CALLS) { + imported.clearInterval(interval2); + resolve(); + } + calls++; + }, + 100, + 15, + "hola" + ); + }); +}); + +add_task(async function test_setTimeoutNonFunction() { + Assert.throws(() => { + imported.setTimeout({}, 0); + }, /callback is not a function in setTimeout/); +}); + +add_task(async function test_setIntervalNonFunction() { + Assert.throws(() => { + imported.setInterval({}, 0); + }, /callback is not a function in setInterval/); +}); + +add_task(async function test_setTimeoutWithTargetNonFunction() { + Assert.throws(() => { + imported.setTimeoutWithTarget({}, 0); + }, /callback is not a function in setTimeout/); +}); + +add_task(async function test_setIntervalWithTargetNonFunction() { + Assert.throws(() => { + imported.setIntervalWithTarget({}, 0); + }, /callback is not a function in setInterval/); +}); + +add_task(async function test_requestIdleCallback() { + let request1 = imported.requestIdleCallback(() => + do_throw("Should not be called") + ); + Assert.equal( + typeof request1, + "number", + "requestIdleCallback returns a number" + ); + Assert.greater(request1, 0, "setTimeout returns a positive number"); + + imported.cancelIdleCallback(request1); + + await new Promise(resolve => { + let request2 = imported.requestIdleCallback( + deadline => { + Assert.ok(true, "Should be called"); + Assert.equal( + typeof deadline.didTimeout, + "boolean", + "deadline parameter has .didTimeout property" + ); + Assert.equal( + typeof deadline.timeRemaining(), + "number", + "deadline parameter has .timeRemaining() function" + ); + resolve(); + }, + { timeout: 100 } + ); + + Assert.equal( + typeof request2, + "number", + "requestIdleCallback returns a number" + ); + Assert.greater( + request2, + 0, + "requestIdleCallback returns a positive number" + ); + Assert.notEqual( + request1, + request2, + "Calling requestIdleCallback again returns a different value" + ); + }); +}); diff --git a/toolkit/modules/tests/xpcshell/test_web_channel.js b/toolkit/modules/tests/xpcshell/test_web_channel.js new file mode 100644 index 0000000000..6d62762e97 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_web_channel.js @@ -0,0 +1,178 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { WebChannel } = ChromeUtils.importESModule( + "resource://gre/modules/WebChannel.sys.mjs" +); +const { PermissionTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PermissionTestUtils.sys.mjs" +); + +const ERROR_ID_ORIGIN_REQUIRED = + "WebChannel id and originOrPermission are required."; +const VALID_WEB_CHANNEL_ID = "id"; +const URL_STRING = "http://example.com"; +const VALID_WEB_CHANNEL_ORIGIN = Services.io.newURI(URL_STRING); +const TEST_PERMISSION_NAME = "test-webchannel-permissions"; + +var MockWebChannelBroker = { + _channelMap: new Map(), + registerChannel(channel) { + if (!this._channelMap.has(channel)) { + this._channelMap.set(channel); + } + }, + unregisterChannel(channelToRemove) { + this._channelMap.delete(channelToRemove); + }, +}; + +/** + * Web channel tests + */ + +/** + * Test channel listening with originOrPermission being an nsIURI. + */ +add_task(function test_web_channel_listen() { + return new Promise((resolve, reject) => { + let channel = new WebChannel( + VALID_WEB_CHANNEL_ID, + VALID_WEB_CHANNEL_ORIGIN, + { + broker: MockWebChannelBroker, + } + ); + let delivered = 0; + Assert.equal(channel.id, VALID_WEB_CHANNEL_ID); + Assert.equal( + channel._originOrPermission.spec, + VALID_WEB_CHANNEL_ORIGIN.spec + ); + Assert.equal(channel._deliverCallback, null); + + channel.listen(function (id, message, target) { + Assert.equal(id, VALID_WEB_CHANNEL_ID); + Assert.ok(message); + Assert.ok(message.command); + Assert.ok(target.sender); + delivered++; + // 2 messages should be delivered + if (delivered === 2) { + channel.stopListening(); + Assert.equal(channel._deliverCallback, null); + resolve(); + } + }); + + // send two messages + channel.deliver( + { + id: VALID_WEB_CHANNEL_ID, + message: { + command: "one", + }, + }, + { sender: true } + ); + + channel.deliver( + { + id: VALID_WEB_CHANNEL_ID, + message: { + command: "two", + }, + }, + { sender: true } + ); + }); +}); + +/** + * Test channel listening with originOrPermission being a permission string. + */ +add_task(function test_web_channel_listen_permission() { + return new Promise((resolve, reject) => { + // add a new permission + PermissionTestUtils.add( + VALID_WEB_CHANNEL_ORIGIN, + TEST_PERMISSION_NAME, + Services.perms.ALLOW_ACTION + ); + registerCleanupFunction(() => + PermissionTestUtils.remove(VALID_WEB_CHANNEL_ORIGIN, TEST_PERMISSION_NAME) + ); + let channel = new WebChannel(VALID_WEB_CHANNEL_ID, TEST_PERMISSION_NAME, { + broker: MockWebChannelBroker, + }); + let delivered = 0; + Assert.equal(channel.id, VALID_WEB_CHANNEL_ID); + Assert.equal(channel._originOrPermission, TEST_PERMISSION_NAME); + Assert.equal(channel._deliverCallback, null); + + channel.listen(function (id, message, target) { + Assert.equal(id, VALID_WEB_CHANNEL_ID); + Assert.ok(message); + Assert.ok(message.command); + Assert.ok(target.sender); + delivered++; + // 2 messages should be delivered + if (delivered === 2) { + channel.stopListening(); + Assert.equal(channel._deliverCallback, null); + resolve(); + } + }); + + // send two messages + channel.deliver( + { + id: VALID_WEB_CHANNEL_ID, + message: { + command: "one", + }, + }, + { sender: true } + ); + + channel.deliver( + { + id: VALID_WEB_CHANNEL_ID, + message: { + command: "two", + }, + }, + { sender: true } + ); + }); +}); + +/** + * Test constructor + */ +add_test(function test_web_channel_constructor() { + Assert.equal(constructorTester(), ERROR_ID_ORIGIN_REQUIRED); + Assert.equal(constructorTester(undefined), ERROR_ID_ORIGIN_REQUIRED); + Assert.equal( + constructorTester(undefined, VALID_WEB_CHANNEL_ORIGIN), + ERROR_ID_ORIGIN_REQUIRED + ); + Assert.equal( + constructorTester(VALID_WEB_CHANNEL_ID, undefined), + ERROR_ID_ORIGIN_REQUIRED + ); + Assert.ok(!constructorTester(VALID_WEB_CHANNEL_ID, VALID_WEB_CHANNEL_ORIGIN)); + + run_next_test(); +}); + +function constructorTester(id, origin) { + try { + new WebChannel(id, origin); + } catch (e) { + return e.message; + } + return false; +} diff --git a/toolkit/modules/tests/xpcshell/test_web_channel_broker.js b/toolkit/modules/tests/xpcshell/test_web_channel_broker.js new file mode 100644 index 0000000000..232e02e935 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_web_channel_broker.js @@ -0,0 +1,78 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { WebChannelBroker } = ChromeUtils.importESModule( + "resource://gre/modules/WebChannel.sys.mjs" +); + +const VALID_WEB_CHANNEL_ID = "id"; +const URL_STRING = "http://example.com"; +const VALID_WEB_CHANNEL_ORIGIN = Services.io.newURI(URL_STRING); + +/** + * Test WebChannelBroker channel map + */ +add_test(function test_web_channel_broker_channel_map() { + let channel = {}; + let channel2 = {}; + + Assert.equal(WebChannelBroker._channelMap.size, 0); + + // make sure _channelMap works correctly + WebChannelBroker.registerChannel(channel); + Assert.equal(WebChannelBroker._channelMap.size, 1); + + WebChannelBroker.registerChannel(channel2); + Assert.equal(WebChannelBroker._channelMap.size, 2); + + WebChannelBroker.unregisterChannel(channel); + Assert.equal(WebChannelBroker._channelMap.size, 1); + + // make sure the correct channel is unregistered + Assert.ok(!WebChannelBroker._channelMap.has(channel)); + Assert.ok(WebChannelBroker._channelMap.has(channel2)); + + WebChannelBroker.unregisterChannel(channel2); + Assert.equal(WebChannelBroker._channelMap.size, 0); + + run_next_test(); +}); + +/** + * Test WebChannelBroker _listener test + */ +add_task(function test_web_channel_broker_listener() { + return new Promise((resolve, reject) => { + var channel = { + id: VALID_WEB_CHANNEL_ID, + _originCheckCallback: requestPrincipal => { + return VALID_WEB_CHANNEL_ORIGIN.prePath === requestPrincipal.origin; + }, + deliver(data, sender) { + Assert.equal(data.id, VALID_WEB_CHANNEL_ID); + Assert.equal(data.message.command, "hello"); + Assert.notEqual(sender, undefined); + WebChannelBroker.unregisterChannel(channel); + resolve(); + }, + }; + + WebChannelBroker.registerChannel(channel); + + var mockEvent = { + id: VALID_WEB_CHANNEL_ID, + message: { + command: "hello", + }, + }; + + WebChannelBroker.tryToDeliver(mockEvent, { + browsingContext: {}, + principal: { + origin: URL_STRING, + }, + }); + }); +}); diff --git a/toolkit/modules/tests/xpcshell/xpcshell.toml b/toolkit/modules/tests/xpcshell/xpcshell.toml new file mode 100644 index 0000000000..52328fc24e --- /dev/null +++ b/toolkit/modules/tests/xpcshell/xpcshell.toml @@ -0,0 +1,131 @@ +[DEFAULT] +head = "head.js" +firefox-appdir = "browser" +support-files = [ + "propertyLists/bug710259_propertyListBinary.plist", + "propertyLists/bug710259_propertyListXML.plist", + "chromeappsstore.sqlite", + "corrupt.sqlite", + "zips/dummy_gmp.zip", + "zips/zen.zip", + "regions/mls-lookup-results.csv", + "regions/world.geojson", + "regions/world-buffered.geojson", +] + +["test_AllowedAppSources.js"] +run-if = ["os == 'win'"] # Test of a Windows-specific feature + +["test_BinarySearch.js"] + +["test_BrowserUtils.js"] + +["test_BrowserUtils_urlFormatting.js"] + +["test_CanonicalJSON.js"] + +["test_Color.js"] + +["test_CreditCard.js"] + +["test_DeferredTask.js"] +skip-if = [ + "os == 'android'", + "os == 'mac'", # osx: Bug 1550141; +] + +["test_E10SUtils_getRemoteTypeForURIObject.js"] + +["test_EventEmitter.js"] + +["test_FileUtils.js"] +skip-if = ["os == 'android'"] + +["test_FinderIterator.js"] + +["test_GMPInstallManager.js"] +skip-if = ["os == 'android'"] +support-files = [ + "../../../mozapps/extensions/test/xpcshell/data/productaddons/content_signing_aus_ee.pem", + "../../../mozapps/extensions/test/xpcshell/data/productaddons/content_signing_int.pem", + "../../../mozapps/extensions/test/xpcshell/data/productaddons/bad.xml", + "../../../mozapps/extensions/test/xpcshell/data/productaddons/good.xml", +] + +["test_IgnoreList.js"] +tags = "remote-settings" + +["test_Integration.js"] + +["test_JSONFile.js"] + +["test_JsonSchema.js"] + +["test_Log.js"] + +["test_Log_double_ext.js"] + +["test_Log_nsIStackFrame.js"] + +["test_Log_stackTrace.js"] + +["test_MatchURLFilters.js"] + +["test_NewTabUtils.js"] +skip-if = ["os == 'android'"] + +["test_ObjectUtils.js"] + +["test_ObjectUtils_strict.js"] + +["test_PermissionsUtils.js"] + +["test_Preferences.js"] + +["test_PrivacyLevel.js"] + +["test_ProfileAge.js"] + +["test_Region.js"] + +["test_Region_geocoding.js"] +run-sequentially = "very high failure rate in parallel" + +["test_Services.js"] + +["test_UpdateUtils_updatechannel.js"] + +["test_UpdateUtils_url.js"] +skip-if = ["!updater"] +reason = "LOCALE is not defined without MOZ_UPDATER" + +["test_firstStartup.js"] +skip-if = [ + "os == 'android'", +] + +["test_jsesc.js"] + +["test_osKeyStore.js"] +skip-if = ["apple_silicon"] # bug 1729538 + +["test_propertyListsUtils.js"] +run-if = ["os == 'mac'"] + +["test_readCertPrefs.js"] + +["test_servicerequest_xhr.js"] + +["test_sqlite.js"] +skip-if = ["os == 'android'"] + +["test_sqlite_autoVacuum.js"] +skip-if = ["os == 'android'"] + +["test_sqlite_shutdown.js"] + +["test_timer.js"] + +["test_web_channel.js"] + +["test_web_channel_broker.js"] diff --git a/toolkit/modules/tests/xpcshell/zips/dummy_gmp.zip b/toolkit/modules/tests/xpcshell/zips/dummy_gmp.zip Binary files differnew file mode 100644 index 0000000000..5ee9a49e2b --- /dev/null +++ b/toolkit/modules/tests/xpcshell/zips/dummy_gmp.zip diff --git a/toolkit/modules/tests/xpcshell/zips/zen.zip b/toolkit/modules/tests/xpcshell/zips/zen.zip Binary files differnew file mode 100644 index 0000000000..8f84999743 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/zips/zen.zip diff --git a/toolkit/modules/third_party/fathom/LICENSE b/toolkit/modules/third_party/fathom/LICENSE new file mode 100644 index 0000000000..14e2f777f6 --- /dev/null +++ b/toolkit/modules/third_party/fathom/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + 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/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/toolkit/modules/third_party/fathom/README b/toolkit/modules/third_party/fathom/README new file mode 100644 index 0000000000..5f7ba3b4cb --- /dev/null +++ b/toolkit/modules/third_party/fathom/README @@ -0,0 +1,17 @@ +This code comes from an externally managed library, available at +<https://github.com/mozilla/fathom>. Bugs should be reported directly +upstream and integrated back here. + +In order to regenerate this file, do the following: + +* Run: + + $ git clone git@github.com:mozilla/fathom.git && cd fathom/fathom + +* Ensure that [this pull request](https://github.com/mozilla/fathom/pull/311) + is either landed or been applied to the latest code you want to bundle. +* Then run: + + $ make bundleESModule + $ export MOZ_FATHOM="../../mozilla-central/toolkit/modules/third_party/fathom" + $ cat $MOZ_FATHOM/fx-header dist/fathom.js > $MOZ_FATHOM/fathom.jsm diff --git a/toolkit/modules/third_party/fathom/fathom.mjs b/toolkit/modules/third_party/fathom/fathom.mjs new file mode 100644 index 0000000000..c1d984a9e3 --- /dev/null +++ b/toolkit/modules/third_party/fathom/fathom.mjs @@ -0,0 +1,2765 @@ +/* +DO NOT TOUCH fathom.jsm DIRECTLY. See the README for instructions. +*/ + +/* 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/. */ + +/** + * A :func:`rule` depends on another rule which itself depends on the first + * rule again, either directly or indirectly. + */ +class CycleError extends Error { +} + +/** + * An examined element was not contained in a browser ``window`` object, but + * something needed it to be. + */ +class NoWindowError extends Error { +} + +var exceptions = /*#__PURE__*/Object.freeze({ + __proto__: null, + CycleError: CycleError, + NoWindowError: NoWindowError +}); + +/** + * Return the passed-in arg. Useful as a default. + */ +function identity(x) { + return x; +} + +/*eslint-env browser*/ + +/** + * From an iterable return the best item, according to an arbitrary comparator + * function. In case of a tie, the first item wins. + * + * @arg by {function} Given an item of the iterable, return a value to compare + * @arg isBetter {function} Return whether its first arg is better than its + * second + */ +function best(iterable, by, isBetter) { + let bestSoFar, bestKeySoFar; + let isFirst = true; + forEach( + function (item) { + const key = by(item); + if (isBetter(key, bestKeySoFar) || isFirst) { + bestSoFar = item; + bestKeySoFar = key; + isFirst = false; + } + }, + iterable); + if (isFirst) { + throw new Error('Tried to call best() on empty iterable'); + } + return bestSoFar; +} + +/** + * Return the maximum item from an iterable, as defined by >. + * + * Works with any type that works with >. If multiple items are equally great, + * return the first. + * + * @arg by {function} Given an item of the iterable, returns a value to + * compare + */ +function max(iterable, by = identity) { + return best(iterable, by, (a, b) => a > b); +} + +/** + * Return an Array of maximum items from an iterable, as defined by > and ===. + * + * If an empty iterable is passed in, return []. + */ +function maxes(iterable, by = identity) { + let bests = []; + let bestKeySoFar; + let isFirst = true; + forEach( + function (item) { + const key = by(item); + if (key > bestKeySoFar || isFirst) { + bests = [item]; + bestKeySoFar = key; + isFirst = false; + } else if (key === bestKeySoFar) { + bests.push(item); + } + }, + iterable); + return bests; +} + +/** + * Return the minimum item from an iterable, as defined by <. + * + * If multiple items are equally great, return the first. + */ +function min(iterable, by = identity) { + return best(iterable, by, (a, b) => a < b); +} + +/** + * Return the sum of an iterable, as defined by the + operator. + */ +function sum(iterable) { + let total; + let isFirst = true; + forEach( + function assignOrAdd(addend) { + if (isFirst) { + total = addend; + isFirst = false; + } else { + total += addend; + } + }, + iterable); + return total; +} + +/** + * Return the number of items in an iterable, consuming it as a side effect. + */ +function length(iterable) { + let num = 0; + // eslint-disable-next-line no-unused-vars + for (let item of iterable) { + num++; + } + return num; +} + +/** + * Iterate, depth first, over a DOM node. Return the original node first. + * + * @arg shouldTraverse {function} Given a node, say whether we should + * include it and its children. Default: always true. + */ +function *walk(element, shouldTraverse = element => true) { + yield element; + for (let child of element.childNodes) { + if (shouldTraverse(child)) { + for (let w of walk(child, shouldTraverse)) { + yield w; + } + } + } +} + +const blockTags = new Set( + ['ADDRESS', 'BLOCKQUOTE', 'BODY', 'CENTER', 'DIR', 'DIV', 'DL', + 'FIELDSET', 'FORM', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HR', + 'ISINDEX', 'MENU', 'NOFRAMES', 'NOSCRIPT', 'OL', 'P', 'PRE', + 'TABLE', 'UL', 'DD', 'DT', 'FRAMESET', 'LI', 'TBODY', 'TD', + 'TFOOT', 'TH', 'THEAD', 'TR', 'HTML']); +/** + * Return whether a DOM element is a block element by default (rather than by + * styling). + */ +function isBlock(element) { + return blockTags.has(element.tagName); +} + +/** + * Yield strings of text nodes within a normalized DOM node and its children, + * without venturing into any contained block elements. + * + * @arg shouldTraverse {function} Specify additional elements to exclude by + * returning false + */ +function *inlineTexts(element, shouldTraverse = element => true) { + // TODO: Could we just use querySelectorAll() with a really long + // selector rather than walk(), for speed? + for (let child of walk(element, + element => !(isBlock(element) || + element.tagName === 'SCRIPT' && + element.tagName === 'STYLE') + && shouldTraverse(element))) { + if (child.nodeType === child.TEXT_NODE) { + // wholeText() is not implemented by jsdom, so we use + // textContent(). The result should be the same, since + // we're calling it on only text nodes, but it may be + // slower. On the positive side, it means we don't need to + // normalize the DOM tree first. + yield child.textContent; + } + } +} + +/** + * Return the total length of the inline text within an element, with + * whitespace collapsed. + * + * @arg shouldTraverse {function} Specify additional elements to exclude by + * returning false + */ +function inlineTextLength(element, shouldTraverse = element => true) { + return sum(map(text => collapseWhitespace(text).length, + inlineTexts(element, shouldTraverse))); +} + +/** + * Return a string with each run of whitespace collapsed to a single space. + */ +function collapseWhitespace(str) { + return str.replace(/\s{2,}/g, ' '); +} + +/** + * Return the ratio of the inline text length of the links in an element to the + * inline text length of the entire element. + * + * @arg inlineLength {number} Optionally, the precalculated inline + * length of the fnode. If omitted, we will calculate it ourselves. + */ +function linkDensity(fnode, inlineLength) { + if (inlineLength === undefined) { + inlineLength = inlineTextLength(fnode.element); + } + const lengthWithoutLinks = inlineTextLength(fnode.element, + element => element.tagName !== 'A'); + return (inlineLength - lengthWithoutLinks) / inlineLength; +} + +/** + * Return whether an element is a text node that consist wholly of whitespace. + */ +function isWhitespace(element) { + return (element.nodeType === element.TEXT_NODE && + element.textContent.trim().length === 0); +} + +/** + * Get a key of a map, first setting it to a default value if it's missing. + */ +function setDefault(map, key, defaultMaker) { + if (map.has(key)) { + return map.get(key); + } + const defaultValue = defaultMaker(); + map.set(key, defaultValue); + return defaultValue; +} + +/** + * Get a key of a map or, if it's missing, a default value. + */ +function getDefault(map, key, defaultMaker) { + if (map.has(key)) { + return map.get(key); + } + return defaultMaker(); +} + +/** + * Return an Array, the reverse topological sort of the given nodes. + * + * @arg nodes An iterable of arbitrary things + * @arg nodesThatNeed {function} Take a node and returns an Array of nodes + * that depend on it + */ +function toposort(nodes, nodesThatNeed) { + const ret = []; + const todo = new Set(nodes); + const inProgress = new Set(); + + function visit(node) { + if (inProgress.has(node)) { + throw new CycleError('The graph has a cycle.'); + } + if (todo.has(node)) { + inProgress.add(node); + for (let needer of nodesThatNeed(node)) { + visit(needer); + } + inProgress.delete(node); + todo.delete(node); + ret.push(node); + } + } + + while (todo.size > 0) { + visit(first(todo)); + } + return ret; +} + +/** + * A Set with the additional methods it ought to have had + */ +class NiceSet extends Set { + /** + * Remove and return an arbitrary item. Throw an Error if I am empty. + */ + pop() { + for (let v of this.values()) { + this.delete(v); + return v; + } + throw new Error('Tried to pop from an empty NiceSet.'); + } + + /** + * Union another set or other iterable into myself. + * + * @return myself, for chaining + */ + extend(otherSet) { + for (let item of otherSet) { + this.add(item); + } + return this; + } + + /** + * Subtract another set from a copy of me. + * + * @return a copy of myself excluding the elements in ``otherSet``. + */ + minus(otherSet) { + const ret = new NiceSet(this); + for (const item of otherSet) { + ret.delete(item); + } + return ret; + } + + /** + * Actually show the items in me. + */ + toString() { + return '{' + Array.from(this).join(', ') + '}'; + } +} + +/** + * Return the first item of an iterable. + */ +function first(iterable) { + for (let i of iterable) { + return i; + } +} + +/** + * Given any node in a DOM tree, return the root element of the tree, generally + * an HTML element. + */ +function rootElement(element) { + return element.ownerDocument.documentElement; +} + +/** + * Return the number of times a regex occurs within the string `haystack`. + * + * Caller must make sure `regex` has the 'g' option set. + */ +function numberOfMatches(regex, haystack) { + return (haystack.match(regex) || []).length; +} + +/** + * Wrap a scoring callback, and set its element to the page root iff a score is + * returned. + * + * This is used to build rulesets which classify entire pages rather than + * picking out specific elements. + * + * For example, these rules might classify a page as a "login page", influenced + * by whether they have login buttons or username fields: + * + * ``rule(type('loginPage'), score(page(pageContainsLoginButton))),`` + * ``rule(type('loginPage'), score(page(pageContainsUsernameField)))`` + */ +function page(scoringFunction) { + function wrapper(fnode) { + const scoreAndTypeAndNote = scoringFunction(fnode); + if (scoreAndTypeAndNote.score !== undefined) { + scoreAndTypeAndNote.element = rootElement(fnode.element); + } + return scoreAndTypeAndNote; + } + return wrapper; +} + +/** + * Sort the elements by their position in the DOM. + * + * @arg fnodes {iterable} fnodes to sort + * @return {Array} sorted fnodes + */ +function domSort(fnodes) { + function compare(a, b) { + const element = a.element; + const position = element.compareDocumentPosition(b.element); + if (position & element.DOCUMENT_POSITION_FOLLOWING) { + return -1; + } else if (position & element.DOCUMENT_POSITION_PRECEDING) { + return 1; + } else { + return 0; + } + } + return Array.from(fnodes).sort(compare); +} + +/* istanbul ignore next */ +/** + * Return the DOM element contained in a passed-in fnode. Return passed-in DOM + * elements verbatim. + * + * @arg fnodeOrElement {Node|Fnode} + */ +function toDomElement(fnodeOrElement) { + return isDomElement(fnodeOrElement) ? fnodeOrElement : fnodeOrElement.element; +} + +/** + * Checks whether any of the element's attribute values satisfy some condition. + * + * Example:: + * + * rule(type('foo'), + * score(attributesMatch(element, + * attr => attr.includes('good'), + * ['id', 'alt']) ? 2 : 1)) + * + * @arg element {Node} Element whose attributes you want to search + * @arg predicate {function} A condition to check. Take a string and + * return a boolean. If an attribute has multiple values (e.g. the class + * attribute), attributesMatch will check each one. + * @arg attrs {string[]} An Array of attributes you want to search. If none are + * provided, search all. + * @return Whether any of the attribute values satisfy the predicate function + */ +function attributesMatch(element, predicate, attrs = []) { + const attributes = attrs.length === 0 ? Array.from(element.attributes).map(a => a.name) : attrs; + for (let i = 0; i < attributes.length; i++) { + const attr = element.getAttribute(attributes[i]); + // If the attribute is an array, apply the scoring function to each element + if (attr && ((Array.isArray(attr) && attr.some(predicate)) || predicate(attr))) { + return true; + } + } + return false; +} + +/* istanbul ignore next */ +/** + * Yield an element and each of its ancestors. + */ +function *ancestors(element) { + yield element; + let parent; + while ((parent = element.parentNode) !== null && parent.nodeType === parent.ELEMENT_NODE) { + yield parent; + element = parent; + } +} + +/** + * Return the sigmoid of the argument: 1 / (1 + exp(-x)). This is useful for + * crunching a feature value that may have a wide range into the range (0, 1) + * without a hard ceiling: the sigmoid of even a very large number will be a + * little larger than that of a slightly smaller one. + * + * @arg x {Number} a number to be compressed into the range (0, 1) + */ +function sigmoid(x) { + return 1 / (1 + Math.exp(-x)); +} + +/* istanbul ignore next */ +/** + * Return whether an element is practically visible, considering things like 0 + * size or opacity, ``visibility: hidden`` and ``overflow: hidden``. + * + * Merely being scrolled off the page in either horizontally or vertically + * doesn't count as invisible; the result of this function is meant to be + * independent of viewport size. + * + * @throws {NoWindowError} The element (or perhaps one of its ancestors) is not + * in a window, so we can't find the `getComputedStyle()` routine to call. + * That routine is the source of most of the information we use, so you + * should pick a different strategy for non-window contexts. + */ +function isVisible(fnodeOrElement) { + // This could be 5x more efficient if https://github.com/w3c/csswg-drafts/issues/4122 happens. + const element = toDomElement(fnodeOrElement); + const elementWindow = windowForElement(element); + const elementRect = element.getBoundingClientRect(); + const elementStyle = elementWindow.getComputedStyle(element); + // Alternative to reading ``display: none`` due to Bug 1381071. + if (elementRect.width === 0 && elementRect.height === 0 && elementStyle.overflow !== 'hidden') { + return false; + } + if (elementStyle.visibility === 'hidden') { + return false; + } + // Check if the element is irrevocably off-screen: + if (elementRect.x + elementRect.width < 0 || + elementRect.y + elementRect.height < 0 + ) { + return false; + } + for (const ancestor of ancestors(element)) { + const isElement = ancestor === element; + const style = isElement ? elementStyle : elementWindow.getComputedStyle(ancestor); + if (style.opacity === '0') { + return false; + } + if (style.display === 'contents') { + // ``display: contents`` elements have no box themselves, but children are + // still rendered. + continue; + } + const rect = isElement ? elementRect : ancestor.getBoundingClientRect(); + if ((rect.width === 0 || rect.height === 0) && elementStyle.overflow === 'hidden') { + // Zero-sized ancestors don’t make descendants hidden unless the descendant + // has ``overflow: hidden``. + return false; + } + } + return true; +} + +/** + * Return the extracted [r, g, b, a] values from a string like "rgba(0, 5, 255, 0.8)", + * and scale them to 0..1. If no alpha is specified, return undefined for it. + */ +function rgbaFromString(str) { + const m = str.match(/^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)$/i); + if (m) { + return [m[1] / 255, m[2] / 255, m[3] / 255, m[4] === undefined ? undefined : parseFloat(m[4])]; + } else { + throw new Error('Color ' + str + ' did not match pattern rgb[a](r, g, b[, a]).'); + } +} + +/** + * Return the saturation 0..1 of a color defined by RGB values 0..1. + */ +function saturation(r, g, b) { + const cMax = Math.max(r, g, b); + const cMin = Math.min(r, g, b); + const delta = cMax - cMin; + const lightness = (cMax + cMin) / 2; + const denom = (1 - (Math.abs(2 * lightness - 1))); + // Return 0 if it's black (R, G, and B all 0). + return (denom === 0) ? 0 : delta / denom; +} + +/** + * Scale a number to the range [0, 1] using a linear slope. + * + * For a rising line, the result is 0 until the input reaches zeroAt, then + * increases linearly until oneAt, at which it becomes 1. To make a falling + * line, where the result is 1 to the left and 0 to the right, use a zeroAt + * greater than oneAt. + */ +function linearScale(number, zeroAt, oneAt) { + const isRising = zeroAt < oneAt; + if (isRising) { + if (number <= zeroAt) { + return 0; + } else if (number >= oneAt) { + return 1; + } + } else { + if (number >= zeroAt) { + return 0; + } else if (number <= oneAt) { + return 1; + } + } + const slope = 1 / (oneAt - zeroAt); + return slope * (number - zeroAt); +} + +// -------- Routines below this point are private to the framework. -------- + +/** + * Flatten out an iterable of iterables into a single iterable of non- + * iterables. Does not consider strings to be iterable. + */ +function *flatten(iterable) { + for (const i of iterable) { + if (typeof i !== 'string' && isIterable(i)) { + yield *(flatten(i)); + } else { + yield i; + } + } +} + +/** + * A lazy, top-level ``Array.map()`` workalike that works on anything iterable + */ +function *map(fn, iterable) { + for (const i of iterable) { + yield fn(i); + } +} + +/** + * A lazy, top-level ``Array.forEach()`` workalike that works on anything + * iterable + */ +function forEach(fn, iterable) { + for (const i of iterable) { + fn(i); + } +} + +/* istanbul ignore next */ +/** + * @return whether a thing appears to be a DOM element. + */ +function isDomElement(thing) { + return thing.nodeName !== undefined; +} + +function isIterable(thing) { + return thing && typeof thing[Symbol.iterator] === 'function'; +} + +/** + * Return an backward iterator over an Array without reversing it in place. + */ +function *reversed(array) { + for (let i = array.length - 1; i >= 0; i--) { + yield array[i]; + } +} + +/* istanbul ignore next */ +/* + * Return the window an element is in. + * + * @throws {NoWindowError} There isn't such a window. + */ +function windowForElement(element) { + let doc = element.ownerDocument; + if (doc === null) { + // The element itself was a document. + doc = element; + } + const win = doc.defaultView; + if (win === null) { + throw new NoWindowError(); + } + return win; +} + +var utilsForFrontend = /*#__PURE__*/Object.freeze({ + __proto__: null, + identity: identity, + best: best, + max: max, + maxes: maxes, + min: min, + sum: sum, + length: length, + walk: walk, + isBlock: isBlock, + inlineTexts: inlineTexts, + inlineTextLength: inlineTextLength, + collapseWhitespace: collapseWhitespace, + linkDensity: linkDensity, + isWhitespace: isWhitespace, + setDefault: setDefault, + getDefault: getDefault, + toposort: toposort, + NiceSet: NiceSet, + first: first, + rootElement: rootElement, + numberOfMatches: numberOfMatches, + page: page, + domSort: domSort, + toDomElement: toDomElement, + attributesMatch: attributesMatch, + ancestors: ancestors, + sigmoid: sigmoid, + isVisible: isVisible, + rgbaFromString: rgbaFromString, + saturation: saturation, + linearScale: linearScale, + flatten: flatten, + map: map, + forEach: forEach, + isDomElement: isDomElement, + reversed: reversed, + windowForElement: windowForElement +}); + +/** + * Return the number of stride nodes between 2 DOM nodes *at the same + * level of the tree*, without going up or down the tree. + * + * ``left`` xor ``right`` may also be undefined. + */ +function numStrides(left, right) { + let num = 0; + + // Walk right from left node until we hit the right node or run out: + let sibling = left; + let shouldContinue = sibling && sibling !== right; + while (shouldContinue) { + sibling = sibling.nextSibling; + if ((shouldContinue = sibling && sibling !== right) && + !isWhitespace(sibling)) { + num += 1; + } + } + if (sibling !== right) { // Don't double-punish if left and right are siblings. + // Walk left from right node: + sibling = right; + while (sibling) { + sibling = sibling.previousSibling; + if (sibling && !isWhitespace(sibling)) { + num += 1; + } + } + } + return num; +} + +/** + * Return a topological distance between 2 DOM nodes or :term:`fnodes<fnode>` + * weighted according to the similarity of their ancestry in the DOM. For + * instance, if one node is situated inside ``<div><span><b><theNode>`` and the + * other node is at ``<differentDiv><span><b><otherNode>``, they are considered + * close to each other for clustering purposes. This is useful for picking out + * nodes which have similar purposes. + * + * Return ``Number.MAX_VALUE`` if one of the nodes contains the other. + * + * This is largely an implementation detail of :func:`clusters`, but you can + * call it yourself if you wish to implement your own clustering. Takes O(n log + * n) time. + * + * Note that the default costs may change; pass them in explicitly if they are + * important to you. + * + * @arg fnodeA {Node|Fnode} + * @arg fnodeB {Node|Fnode} + * @arg differentDepthCost {number} Cost for each level deeper one node is than + * the other below their common ancestor + * @arg differentTagCost {number} Cost for a level below the common ancestor + * where tagNames differ + * @arg sameTagCost {number} Cost for a level below the common ancestor where + * tagNames are the same + * @arg strideCost {number} Cost for each stride node between A and B. Stride + * nodes are siblings or siblings-of-ancestors that lie between the 2 + * nodes. These interposed nodes make it less likely that the 2 nodes + * should be together in a cluster. + * @arg additionalCost {function} Return an additional cost, given 2 fnodes or + * nodes. + * + */ +function distance(fnodeA, + fnodeB, + {differentDepthCost = 2, + differentTagCost = 2, + sameTagCost = 1, + strideCost = 1, + additionalCost = (fnodeA, fnodeB) => 0} = {}) { + // I was thinking of something that adds little cost for siblings. Up + // should probably be more expensive than down (see middle example in the + // Nokia paper). + + // TODO: Test and tune default costs. They're off the cuff at the moment. + + if (fnodeA === fnodeB) { + return 0; + } + + const elementA = isDomElement(fnodeA) ? fnodeA : fnodeA.element; + const elementB = isDomElement(fnodeB) ? fnodeB : fnodeB.element; + + // Stacks that go from the common ancestor all the way to A and B: + const aAncestors = [elementA]; + const bAncestors = [elementB]; + + let aAncestor = elementA; + let bAncestor = elementB; + + // Ascend to common parent, stacking them up for later reference: + while (!aAncestor.contains(elementB)) { // Note: an element does contain() itself. + aAncestor = aAncestor.parentNode; + aAncestors.push(aAncestor); //aAncestors = [a, b]. aAncestor = b // if a is outer: no loop here; aAncestors = [a]. aAncestor = a. + } + + // In compareDocumentPosition()'s opinion, inside implies after. Basically, + // before and after pertain to opening tags. + const comparison = elementA.compareDocumentPosition(elementB); + + // If either contains the other, abort. We'd either return a misleading + // number or else walk upward right out of the document while trying to + // make the ancestor stack. + if (comparison & (elementA.DOCUMENT_POSITION_CONTAINS | elementA.DOCUMENT_POSITION_CONTAINED_BY)) { + return Number.MAX_VALUE; + } + // Make an ancestor stack for the right node too so we can walk + // efficiently down to it: + do { + bAncestor = bAncestor.parentNode; // Assumes we've early-returned above if A === B. This walks upward from the outer node and up out of the tree. It STARTS OUT with aAncestor === bAncestor! + bAncestors.push(bAncestor); + } while (bAncestor !== aAncestor); + + // Figure out which node is left and which is right, so we can follow + // sibling links in the appropriate directions when looking for stride + // nodes: + let left = aAncestors; + let right = bAncestors; + let cost = 0; + if (comparison & elementA.DOCUMENT_POSITION_FOLLOWING) { + // A is before, so it could contain the other node. What did I mean to do if one contained the other? + left = aAncestors; + right = bAncestors; + } else if (comparison & elementA.DOCUMENT_POSITION_PRECEDING) { + // A is after, so it might be contained by the other node. + left = bAncestors; + right = aAncestors; + } + + // Descend to both nodes in parallel, discounting the traversal + // cost iff the nodes we hit look similar, implying the nodes dwell + // within similar structures. + while (left.length || right.length) { + const l = left.pop(); + const r = right.pop(); + if (l === undefined || r === undefined) { + // Punishment for being at different depths: same as ordinary + // dissimilarity punishment for now + cost += differentDepthCost; + } else { + // TODO: Consider similarity of classList. + cost += l.tagName === r.tagName ? sameTagCost : differentTagCost; + } + // Optimization: strides might be a good dimension to eliminate. + if (strideCost !== 0) { + cost += numStrides(l, r) * strideCost; + } + } + + return cost + additionalCost(fnodeA, fnodeB); +} + +/** + * Return the spatial distance between 2 fnodes or elements, assuming a + * rendered page. + * + * Specifically, return the distance in pixels between the centers of + * ``fnodeA.element.getBoundingClientRect()`` and + * ``fnodeB.element.getBoundingClientRect()``. + */ +function euclidean(fnodeA, fnodeB) { + /** + * Return the horizontal distance from the left edge of the viewport to the + * center of an element, given a DOMRect object for it. It doesn't matter + * that the distance is affected by the page's scroll offset, since the 2 + * elements have the same offset. + */ + function xCenter(domRect) { + return domRect.left + domRect.width / 2; + } + function yCenter(domRect) { + return domRect.top + domRect.height / 2; + } + + const elementA = toDomElement(fnodeA); + const elementB = toDomElement(fnodeB); + const aRect = elementA.getBoundingClientRect(); + const bRect = elementB.getBoundingClientRect(); + return Math.sqrt((xCenter(aRect) - xCenter(bRect)) ** 2 + + (yCenter(aRect) - yCenter(bRect)) ** 2); +} + +/** A lower-triangular matrix of inter-cluster distances */ +class DistanceMatrix { + /** + * @arg distance {function} Some notion of distance between 2 given nodes + */ + constructor(elements, distance) { + // A sparse adjacency matrix: + // {A => {}, + // B => {A => 4}, + // C => {A => 4, B => 4}, + // D => {A => 4, B => 4, C => 4} + // E => {A => 4, B => 4, C => 4, D => 4}} + // + // A, B, etc. are arrays of [arrays of arrays of...] nodes, each + // array being a cluster. In this way, they not only accumulate a + // cluster but retain the steps along the way. + // + // This is an efficient data structure in terms of CPU and memory, in + // that we don't have to slide a lot of memory around when we delete a + // row or column from the middle of the matrix while merging. Of + // course, we lose some practical efficiency by using hash tables, and + // maps in particular are slow in their early implementations. + this._matrix = new Map(); + + // Convert elements to clusters: + const clusters = elements.map(el => [el]); + + // Init matrix: + for (let outerCluster of clusters) { + const innerMap = new Map(); + for (let innerCluster of this._matrix.keys()) { + innerMap.set(innerCluster, distance(outerCluster[0], + innerCluster[0])); + } + this._matrix.set(outerCluster, innerMap); + } + this._numClusters = clusters.length; + } + + // Return (distance, a: clusterA, b: clusterB) of closest-together clusters. + // Replace this to change linkage criterion. + closest() { + const self = this; + + if (this._numClusters < 2) { + throw new Error('There must be at least 2 clusters in order to return the closest() ones.'); + } + + // Return the distances between every pair of clusters. + function clustersAndDistances() { + const ret = []; + for (let [outerKey, row] of self._matrix.entries()) { + for (let [innerKey, storedDistance] of row.entries()) { + ret.push({a: outerKey, b: innerKey, distance: storedDistance}); + } + } + return ret; + } + // Optimizing this by inlining the loop and writing it less + // functionally doesn't help: + return min(clustersAndDistances(), x => x.distance); + } + + // Look up the distance between 2 clusters in me. Try the lookup in the + // other direction if the first one falls in the nonexistent half of the + // triangle. + _cachedDistance(clusterA, clusterB) { + let ret = this._matrix.get(clusterA).get(clusterB); + if (ret === undefined) { + ret = this._matrix.get(clusterB).get(clusterA); + } + return ret; + } + + // Merge two clusters. + merge(clusterA, clusterB) { + // An example showing how rows merge: + // A: {} + // B: {A: 1} + // C: {A: 4, B: 4}, + // D: {A: 4, B: 4, C: 4} + // E: {A: 4, B: 4, C: 2, D: 4}} + // + // Step 2: + // C: {} + // D: {C: 4} + // E: {C: 2, D: 4}} + // AB: {C: 4, D: 4, E: 4} + // + // Step 3: + // D: {} + // AB: {D: 4} + // CE: {D: 4, AB: 4} + + // Construct new row, finding min distances from either subcluster of + // the new cluster to old clusters. + // + // There will be no repetition in the matrix because, after all, + // nothing pointed to this new cluster before it existed. + const newRow = new Map(); + for (let outerKey of this._matrix.keys()) { + if (outerKey !== clusterA && outerKey !== clusterB) { + newRow.set(outerKey, Math.min(this._cachedDistance(clusterA, outerKey), + this._cachedDistance(clusterB, outerKey))); + } + } + + // Delete the rows of the clusters we're merging. + this._matrix.delete(clusterA); + this._matrix.delete(clusterB); + + // Remove inner refs to the clusters we're merging. + for (let inner of this._matrix.values()) { + inner.delete(clusterA); + inner.delete(clusterB); + } + + // Attach new row. + this._matrix.set([clusterA, clusterB], newRow); + + // There is a net decrease of 1 cluster: + this._numClusters -= 1; + } + + numClusters() { + return this._numClusters; + } + + // Return an Array of nodes for each cluster in me. + clusters() { + // TODO: Can't get map to work here. Don't know why. + return Array.from(this._matrix.keys()).map(e => Array.from(flatten(e))); + } +} + +/** + * Partition the given nodes into one or more clusters by position in the DOM + * tree. + * + * This implements an agglomerative clustering. It uses single linkage, since + * we're talking about adjacency here more than Euclidean proximity: the + * clusters we're talking about in the DOM will tend to be adjacent, not + * overlapping. We haven't tried other linkage criteria yet. + * + * In a later release, we may consider score or notes. + * + * @arg {Fnode[]|Node[]} fnodes :term:`fnodes<fnode>` or DOM nodes to group + * into clusters + * @arg {number} splittingDistance The closest-nodes :func:`distance` beyond + * which we will not attempt to unify 2 clusters. Make this larger to make + * larger clusters. + * @arg getDistance {function} A function that returns some notion of numerical + * distance between 2 nodes. Default: :func:`distance` + * @return {Array} An Array of Arrays, with each Array containing all the + * nodes in one cluster. Note that neither the clusters nor the nodes are + * in any particular order. You may find :func:`domSort` helpful to remedy + * the latter. + */ +function clusters(fnodes, splittingDistance, getDistance = distance) { + const matrix = new DistanceMatrix(fnodes, getDistance); + let closest; + + while (matrix.numClusters() > 1 && (closest = matrix.closest()).distance < splittingDistance) { + matrix.merge(closest.a, closest.b); + } + + return matrix.clusters(); +} + +var clusters$1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + distance: distance, + euclidean: euclidean, + clusters: clusters +}); + +// The left-hand side of a rule + + +/** + * Take nodes that match a given DOM selector. Example: + * ``dom('meta[property="og:title"]')`` + * + * Every ruleset has at least one ``dom`` or :func:`element` rule, as that is + * where nodes begin to flow into the system. If run against a subtree of a + * document, the root of the subtree is not considered as a possible match. + */ +function dom(selector) { + return new DomLhs(selector); +} + +/** + * Take a single given node if it matches a given DOM selector, without looking + * through its descendents or ancestors. Otherwise, take no nodes. Example: + * ``element('input')`` + * + * This is useful for applications in which you want Fathom to classify an + * element the user has selected, rather than scanning the whole page for + * candidates. + */ +function element(selector) { + return new ElementLhs(selector); +} + +/** + * Rules and the LHSs and RHSs that comprise them have no mutable state. This + * lets us make BoundRulesets from Rulesets without duplicating the rules. It + * also lets us share a common cache among rules: multiple ones might care + * about a cached type(), for instance; there isn't a one-to-one relationship + * of storing with caring. There would also, because of the interdependencies + * of rules in a ruleset, be little use in segmenting the caches: if you do + * something that causes one to need to be cleared, you'll need to clear many + * more as well. + * + * Lhses are responsible for maintaining ruleset.maxCache. + * + * Lhs and its subclasses are private to the Fathom framework. + */ +class Lhs { + constructor() { + this._predicate = () => true; + } + + /** Return a new Lhs of the appropriate kind, given its first call. */ + static fromFirstCall(firstCall) { + // firstCall is never 'dom', because dom() directly returns a DomLhs. + if (firstCall.method === 'type') { + return new TypeLhs(...firstCall.args); + } else if (firstCall.method === 'and') { + return new AndLhs(firstCall.args); + } else if (firstCall.method === 'nearest') { + return new NearestLhs(firstCall.args); + } else { + throw new Error('The left-hand side of a rule() must start with dom(), type(), and(), or nearest().'); + } + } + + /** + * Prune nodes from consideration early in run execution, before scoring is + * done. + * + * Reserve this for where you are sure it is always correct or when + * performance demands it. It is generally preferable to use :func:`score` + * and let the :doc:`trainer<training>` determine the relative significance + * of each rule. Human intuition as to what is important is often wrong: + * for example, one might assume that a music player website would include + * the word "play", but this does not hold once you include sites in other + * languages. + * + * Can be chained after :func:`type` or :func:`dom`. + * + * Example: ``dom('p').when(isVisible)`` + * + * @arg {function} predicate Accepts a fnode and returns a boolean + */ + when(predicate) { + let lhs = this.clone(); + lhs._predicate = predicate; + return lhs; + } + + /** + * Of all the dom nodes selected by type() or dom(), return only + * the fnodes that satisfy all the predicates imposed by calls to + * when() + */ + fnodesSatisfyingWhen(fnodes) { + return Array.from(fnodes).filter(this._predicate); + } + + /** + * Return an iterable of output fnodes selected by this left-hand-side + * expression. + * + * Pre: The rules I depend on have already been run, and their results are + * in ruleset.typeCache. + * + * @arg ruleset {BoundRuleset} + */ + // fnodes (ruleset) {} + + /** + * Check that a RHS-emitted fact is legal for this kind of LHS, and throw + * an error if it isn't. + */ + checkFact(fact) {} + + /** + * Return the single type the output of the LHS is guaranteed to have. + * Return undefined if there is no such single type we can ascertain. + */ + guaranteedType() {} + + /** + * Return the type I aggregate if I am an aggregate LHS; return undefined + * otherwise. + */ + aggregatedType() {} + + /** + * Return each combination of types my selected nodes could be locally (that + * is, by this rule only) constrained to have. + * + * For example, type(A) would return [A]. and(A, or(B, C)) would return + * [AB, AC, ABC]. More examples: + * + * or(A, B) → typeIn(A, B, C) # Finalizes A, B. combos A, B, AB: finalizes AB. Optimization: there's no point in returning the last combo in ors. Compilation into 2 rules with identical RHSs will inherently implement this optimization. + * or(A, B) → typeIn(A, B) # Finalizes A, B + * or(A, B) → A # Finalizes B + * and(A) -> A # Finalizes nothing + * and(A, B) -> A # Finalizes nothing. AB: Ø + * and(A) -> typeIn(A, B) # Finalizes A. A + * and(A, B) -> typeIn(A, B) # Finalizes nothing. AB + * and(A, B) -> typeIn(A, B, C) # Finalizes A, B. AB + * and(A, or(B, C)) -> D # Finalizes A, B, C. AB, AC, ABC: ABC + * and(A, or(B, C)) -> B # Finalizes A, C. AB, AC, ABC: AC + * type(A).not(and(A, B)) -> + * + * @return {NiceSet[]} + */ + // possibleTypeCombinations() {} + + /** + * Types mentioned in this LHS. + * + * In other words, the types I need to know the assignment status of before + * I can make my selections + * + * @return NiceSet of strings + */ + // typesMentioned() {} +} + +class DomLhs extends Lhs { + constructor(selector) { + super(); + if (selector === undefined) { + throw new Error('A querySelector()-style selector is required as the argument to ' + this._callName() + '().'); + } + this.selector = selector; + } + + /** + * Return the name of this kind of LHS, for use in error messages. + */ + _callName() { + return 'dom'; + } + + clone() { + return new this.constructor(this.selector); + } + + fnodes(ruleset) { + return this._domNodesToFilteredFnodes( + ruleset, + ruleset.doc.querySelectorAll(this.selector)); + } + + /** + * Turn a NodeList of DOM nodes into an array of fnodes, and filter out + * those that don't match the :func:`when()` clause. + */ + _domNodesToFilteredFnodes(ruleset, domNodes) { + let ret = []; + for (let i = 0; i < domNodes.length; i++) { + ret.push(ruleset.fnodeForElement(domNodes[i])); + } + return this.fnodesSatisfyingWhen(ret); + } + + checkFact(fact) { + if (fact.type === undefined) { + throw new Error(`The right-hand side of a ${this._callName()}() rule failed to specify a type. This means there is no way for its output to be used by later rules. All it specified was ${fact}.`); + } + } + + asLhs() { + return this; + } + + possibleTypeCombinations() { + return []; + } + + typesMentioned() { + return new NiceSet(); + } +} + +class ElementLhs extends DomLhs { + _callName() { + return 'element'; + } + + fnodes(ruleset) { + return this._domNodesToFilteredFnodes( + ruleset, + ruleset.doc.matches(this.selector) ? [ruleset.doc] : []); + } +} + +/** Internal representation of a LHS constrained by type but not by max() */ +class TypeLhs extends Lhs { + constructor(type) { + super(); + if (type === undefined) { + throw new Error('A type name is required when calling type().'); + } + this._type = type; // the input type + } + + clone() { + return new this.constructor(this._type); + } + + fnodes(ruleset) { + const cached = getDefault(ruleset.typeCache, this._type, () => []); + return this.fnodesSatisfyingWhen(cached); + } + + /** Override the type previously specified by this constraint. */ + type(inputType) { + // Preserve the class in case this is a TypeMaxLhs. + return new this.constructor(inputType); + } + + /** + * Of the nodes selected by a ``type`` call to the left, constrain the LHS + * to return only the max-scoring one. If there is a tie, more than 1 node + * will be returned. Example: ``type('titley').max()`` + */ + max() { + return new TypeMaxLhs(this._type); + } + + /** + * Take the nodes selected by a ``type`` call to the left, group them into + * clusters, and return the nodes in the cluster that has the highest total + * score (on the relevant type). + * + * Nodes come out in arbitrary order, so, if you plan to emit them, + * consider using ``.out('whatever').allThrough(domSort)``. See + * :func:`domSort`. + * + * If multiple clusters have equally high scores, return an arbitrary one, + * because Fathom has no way to represent arrays of arrays in rulesets. + * + * @arg options {Object} The same depth costs taken by :func:`distance`, + * plus ``splittingDistance``, which is the distance beyond which 2 + * clusters will be considered separate. ``splittingDistance``, if + * omitted, defaults to 3. + */ + bestCluster(options) { + return new BestClusterLhs(this._type, options); + } + + // Other clustering calls could be called biggestCluster() (having the most + // nodes) and bestAverageCluster() (having the highest average score). + + guaranteedType() { + return this._type; + } + + possibleTypeCombinations() { + return [this.typesMentioned()]; + } + + typesMentioned() { + return new NiceSet([this._type]); + } +} + +/** + * Abstract LHS that is an aggregate function taken across all fnodes of a type + * + * The main point here is that any aggregate function over a (typed) set of + * nodes depends on first computing all the rules that could emit those nodes + * (nodes of that type). + */ +class AggregateTypeLhs extends TypeLhs { + aggregatedType() { + return this._type; + } +} + +/** + * Internal representation of a LHS that has both type and max([NUMBER]) + * constraints. max(NUMBER != 1) support is not yet implemented. + */ +class TypeMaxLhs extends AggregateTypeLhs { + /** + * Return the max-scoring node (or nodes if there is a tie) of the given + * type. + */ + fnodes(ruleset) { + // TODO: Optimize better. Walk the dependency tree, and run only the + // rules that could possibly lead to a max result. As part of this, + // make RHSs expose their max potential scores. + const self = this; + // Work around V8 bug: + // https://stackoverflow.com/questions/32943776/using-super-within-an- + // arrow-function-within-an-arrow-function-within-a-method + const getSuperFnodes = () => super.fnodes(ruleset); + return setDefault( + ruleset.maxCache, + this._type, + function maxFnodesOfType() { + return maxes(getSuperFnodes(), fnode => ruleset.weightedScore(fnode.scoresSoFarFor(self._type))); + }); + } +} + +class BestClusterLhs extends AggregateTypeLhs { + constructor(type, options) { + super(type); + this._options = options || {splittingDistance: 3}; + } + + /** + * Group the nodes of my type into clusters, and return the cluster with + * the highest total score for that type. + */ + fnodes(ruleset) { + // Get the nodes of the type: + const fnodesOfType = Array.from(super.fnodes(ruleset)); + if (fnodesOfType.length === 0) { + return []; + } + // Cluster them: + const clusts = clusters( + fnodesOfType, + this._options.splittingDistance, + (a, b) => distance(a, b, this._options)); + // Tag each cluster with the total of its nodes' scores: + const clustsAndSums = clusts.map( + clust => [clust, + sum(clust.map(fnode => fnode.scoreFor(this._type)))]); + // Return the highest-scoring cluster: + return max(clustsAndSums, clustAndSum => clustAndSum[1])[0]; + } +} + +class AndLhs extends Lhs { + constructor(lhss) { + super(); + + // For the moment, we accept only type()s as args. TODO: Generalize to + // type().max() and such later. + this._args = lhss.map(sideToTypeLhs); + } + + *fnodes(ruleset) { + // Take an arbitrary one for starters. Optimization: we could always + // choose the pickiest one to start with. + const fnodes = this._args[0].fnodes(ruleset); + // Then keep only the fnodes that have the type of every other arg: + fnodeLoop: for (let fnode of fnodes) { + for (let otherLhs of this._args.slice(1)) { + // Optimization: could use a .hasTypeSoFar() below + if (!fnode.hasType(otherLhs.guaranteedType())) { + // TODO: This is n^2. Why is there no set intersection in JS?! + continue fnodeLoop; + } + } + yield fnode; + } + } + + possibleTypeCombinations() { + return [this.typesMentioned()]; + } + + typesMentioned() { + return new NiceSet(this._args.map(arg => arg.guaranteedType())); + } +} + +function sideToTypeLhs(side) { + const lhs = side.asLhs(); + if (!(lhs.constructor === TypeLhs)) { + throw new Error('and() and nearest() support only simple type() calls as arguments for now.'); + // TODO: Though we could solve this with a compilation step: and(type(A), type(B).max()) is equivalent to type(B).max() -> type(Bmax); and(type(A), type(Bmax)). + // In fact, we should be able to compile most (any?) arbitrary and()s, including nested ands and and(type(...).max(), ...) constructions into several and(type(A), type(B), ...) rules. + } + return lhs; +} + +class NearestLhs extends Lhs { + constructor([a, b, distance]) { + super(); + this._a = sideToTypeLhs(a); + this._b = sideToTypeLhs(b); + this._distance = distance; + } + + /** + * Return an iterable of {fnodes, transformer} pairs. + */ + *fnodes(ruleset) { + // Go through all the left arg's nodes. For each one, find the closest + // right-arg's node. O(a * b). Once a right-arg's node is used, we + // don't eliminate it from consideration, because then order of left- + // args' nodes would matter. + + // TODO: Still not sure how to get the distance to factor into the + // score unless we hard-code nearest() to do that. It's a + // matter of not being able to bind on the RHS to the output of the + // distance function on the LHS. Perhaps we could at least make + // distance part of the note and read it in a props() callback. + + // We're assuming here that simple type() calls return just plain + // fnodes, not {fnode, rhsTransformer} pairs: + const as_ = this._a.fnodes(ruleset); + const bs = Array.from(this._b.fnodes(ruleset)); + if (bs.length > 0) { + // If bs is empty, there can be no nearest nodes, so don't emit any. + for (const a of as_) { + const nearest = min(bs, b => this._distance(a, b)); + yield {fnode: a, + rhsTransformer: function setNoteIfEmpty(fact) { + // If note is explicitly set by the RHS, let it take + // precedence, even though that makes this entire LHS + // pointless. + if (fact.note === undefined) { + fact.note = nearest; // TODO: Wrap this in an object to make room to return distance later. + } + return fact; + }}; + } + } + } + + checkFact(fact) { + // Barf if the fact doesn't set a type at least. It should be a *new* type or at least one that doesn't result in cycles, but we can't deduce that. + } + + possibleTypeCombinations() { + return [new NiceSet([this._a.guaranteedType()])]; + } + + typesMentioned() { + return new NiceSet([this._a.guaranteedType(), + this._b.guaranteedType()]); + } + + guaranteedType() { + return this._a.guaranteedType(); + } +} + +// The right-hand side of a rule + + +const TYPE = 1; +const NOTE = 2; +const SCORE = 4; +const ELEMENT = 8; +const SUBFACTS = { + type: TYPE, + note: NOTE, + score: SCORE, + element: ELEMENT +}; + +/** + * Expose the output of this rule's LHS as a "final result" to the surrounding + * program. It will be available by calling :func:`~BoundRuleset.get` on the + * ruleset and passing the key. You can run each node through a callback + * function first by adding :func:`through()`, or you can run the entire set of + * nodes through a callback function by adding :func:`allThrough()`. + */ +function out(key) { + return new OutwardRhs(key); +} + +class InwardRhs { + constructor(calls = [], max = Infinity, types) { + this._calls = calls.slice(); + this._max = max; // max score + this._types = new NiceSet(types); // empty set if unconstrained + } + + /** + * Declare that the maximum returned subscore is such and such, + * which helps the optimizer plan efficiently. This doesn't force it to be + * true; it merely throws an error at runtime if it isn't. To lift an + * ``atMost`` constraint, call ``atMost()`` (with no args). The reason + * ``atMost`` and ``typeIn`` apply until explicitly cleared is so that, if + * someone used them for safety reasons on a lexically distant rule you are + * extending, you won't stomp on their constraint and break their + * invariants accidentally. + */ + atMost(score) { + return new this.constructor(this._calls, score, this._types); + } + + _checkAtMost(fact) { + if (fact.score !== undefined && fact.score > this._max) { + throw new Error(`Score of ${fact.score} exceeds the declared atMost(${this._max}).`); + } + } + + /** + * Determine any of type, note, score, and element using a callback. This + * overrides any previous call to `props` and, depending on what + * properties of the callback's return value are filled out, may override + * the effects of other previous calls as well. + * + * The callback should return... + * + * * An optional :term:`subscore` + * * A type (required on ``dom(...)`` rules, defaulting to the input one on + * ``type(...)`` rules) + * * Optional notes + * * An element, defaulting to the input one. Overriding the default + * enables a callback to walk around the tree and say things about nodes + * other than the input one. + */ + props(callback) { + function getSubfacts(fnode) { + const subfacts = callback(fnode); + // Filter the raw result down to okayed properties so callbacks + // can't insert arbitrary keys (like conserveScore, which might + // mess up the optimizer). + for (let subfact in subfacts) { + if (!SUBFACTS.hasOwnProperty(subfact) || !(SUBFACTS[subfact] & getSubfacts.possibleSubfacts)) { + // The ES5.1 spec says in 12.6.4 that it's fine to delete + // as we iterate. + delete subfacts[subfact]; + } + } + return subfacts; + } + // Thse are the subfacts this call could affect: + getSubfacts.possibleSubfacts = TYPE | NOTE | SCORE | ELEMENT; + getSubfacts.kind = 'props'; + return new this.constructor(this._calls.concat(getSubfacts), + this._max, + this._types); + } + + /** + * Set the type applied to fnodes processed by this RHS. + */ + type(theType) { + // In the future, we might also support providing a callback that receives + // the fnode and returns a type. We couldn't reason based on these, but the + // use would be rather a consise way to to override part of what a previous + // .props() call provides. + + // Actually emit a given type. + function getSubfacts() { + return {type: theType}; + } + getSubfacts.possibleSubfacts = TYPE; + getSubfacts.type = theType; + getSubfacts.kind = 'type'; + return new this.constructor(this._calls.concat(getSubfacts), + this._max, + this._types); + } + + /** + * Constrain this rule to emit 1 of a set of given types. Pass no args to lift + * a previous ``typeIn`` constraint, as you might do when basing a LHS on a + * common value to factor out repetition. + * + * ``typeIn`` is mostly a hint for the query planner when you're emitting types + * dynamically from ``props`` calls—in fact, an error will be raised if + * ``props`` is used without a ``typeIn`` or ``type`` to constrain it—but it + * also checks conformance at runtime to ensure validity. + */ + typeIn(...types) { + // Rationale: If we used the spelling "type('a', 'b', ...)" instead of + // this, one might expect type('a', 'b').type(fn) to have the latter + // call override, while expecting type(fn).type('a', 'b') to keep both + // in effect. Then different calls to type() don't consistently + // override each other, and the rules get complicated. Plus you can't + // inherit a type constraint and then sub in another type-returning + // function that still gets the constraint applied. + return new this.constructor(this._calls, + this._max, + types); + } + + /** + * Check a fact for conformance with any typeIn() call. + * + * @arg leftType the type of the LHS, which becomes my emitted type if the + * fact doesn't specify one + */ + _checkTypeIn(result, leftType) { + if (this._types.size > 0) { + if (result.type === undefined) { + if (!this._types.has(leftType)) { + throw new Error(`A right-hand side claimed, via typeIn(...) to emit one of the types ${this._types} but actually inherited ${leftType} from the left-hand side.`); + } + } else if (!this._types.has(result.type)) { + throw new Error(`A right-hand side claimed, via typeIn(...) to emit one of the types ${this._types} but actually emitted ${result.type}.`); + } + } + } + + /** + * Whatever the callback returns (even ``undefined``) becomes the note of + * the fact. This overrides any previous call to ``note``. + */ + note(callback) { + function getSubfacts(fnode) { + return {note: callback(fnode)}; + } + getSubfacts.possibleSubfacts = NOTE; + getSubfacts.kind = 'note'; + return new this.constructor(this._calls.concat(getSubfacts), + this._max, + this._types); + } + + /** + * Affect the confidence with which the input node should be considered a + * member of a type. + * + * The parameter is generally between 0 and 1 (inclusive), with 0 meaning + * the node does not have the "smell" this rule checks for and 1 meaning it + * does. The range between 0 and 1 is available to represent "fuzzy" + * confidences. If you have an unbounded range to compress down to [0, 1], + * consider using :func:`sigmoid` or a scaling thereof. + * + * Since every node can have multiple, independent scores (one for each + * type), this applies to the type explicitly set by the RHS or, if none, + * to the type named by the ``type`` call on the LHS. If the LHS has none + * because it's a ``dom(...)`` LHS, an error is raised. + * + * @arg {number|function} scoreOrCallback Can either be a static number, + * generally 0 to 1 inclusive, or else a callback which takes the fnode + * and returns such a number. If the callback returns a boolean, it is + * cast to a number. + */ + score(scoreOrCallback) { + let getSubfacts; + + function getSubfactsFromNumber(fnode) { + return {score: scoreOrCallback}; + } + + function getSubfactsFromFunction(fnode) { + let result = scoreOrCallback(fnode); + if (typeof result === 'boolean') { + // Case bools to numbers for convenience. Boolean features are + // common. Don't cast other things, as it frustrates ruleset + // debugging. + result = Number(result); + } + return {score: result}; + } + + if (typeof scoreOrCallback === 'number') { + getSubfacts = getSubfactsFromNumber; + } else { + getSubfacts = getSubfactsFromFunction; + } + getSubfacts.possibleSubfacts = SCORE; + getSubfacts.kind = 'score'; + + return new this.constructor(this._calls.concat(getSubfacts), + this._max, + this._types); + } + + // Future: why not have an .element() method for completeness? + + // -------- Methods below this point are private to the framework. -------- + + /** + * Run all my props().type().note().score() stuff across a given fnode, + * enforce my max() stuff, and return a fact ({element, type, score, + * notes}) for incorporation into that fnode (or a different one, if + * element is specified). Any of the 4 fact properties can be missing; + * filling in defaults is a job for the caller. + * + * @arg leftType The type the LHS takes in + */ + fact(fnode, leftType) { + const doneKinds = new Set(); + const result = {}; + let haveSubfacts = 0; + for (let call of reversed(this._calls)) { + // If we've already called a call of this kind, then forget it. + if (!doneKinds.has(call.kind)) { + doneKinds.add(call.kind); + + if (~haveSubfacts & call.possibleSubfacts) { + // This call might provide a subfact we are missing. + const newSubfacts = call(fnode); + + // We start with an empty object, so we're okay here. + // eslint-disable-next-line guard-for-in + for (let subfact in newSubfacts) { + // A props() callback could insert arbitrary keys into + // the result, but it shouldn't matter, because nothing + // pays any attention to them. + if (!result.hasOwnProperty(subfact)) { + result[subfact] = newSubfacts[subfact]; + } + haveSubfacts |= SUBFACTS[subfact]; + } + } + } + } + this._checkAtMost(result); + this._checkTypeIn(result, leftType); + return result; + } + + /** + * Return a record describing the types I might emit (which means either to + * add a type to a fnode or to output a fnode that already has that type). + * {couldChangeType: whether I might add a type to the fnode, + * possibleTypes: If couldChangeType, the types I might emit; empty set if + * we cannot infer it. If not couldChangeType, undefined.} + */ + possibleEmissions() { + // If there is a typeIn() constraint or there is a type() call to the + // right of all props() calls, we have a constraint. We hunt for the + // tightest constraint we can find, favoring a type() call because it + // gives us a single type but then falling back to a typeIn(). + let couldChangeType = false; + for (let call of reversed(this._calls)) { + if (call.kind === 'props') { + couldChangeType = true; + break; + } else if (call.kind === 'type') { + return {couldChangeType: true, + possibleTypes: new Set([call.type])}; + } + } + return {couldChangeType, + possibleTypes: this._types}; + } +} + +class OutwardRhs { + constructor(key, through = x => x, allThrough = x => x) { + this.key = key; + this.callback = through; + this.allCallback = allThrough; + } + + /** + * Append ``.through`` to :func:`out` to run each :term:`fnode` emitted + * from the LHS through an arbitrary function before returning it to the + * containing program. Example:: + * + * out('titleLengths').through(fnode => fnode.noteFor('title').length) + */ + through(callback) { + return new this.constructor(this.key, callback, this.allCallback); + } + + /** + * Append ``.allThrough`` to :func:`out` to run the entire iterable of + * emitted :term:`fnodes<fnode>` through an arbitrary function before + * returning them to the containing program. Example:: + * + * out('sortedTitles').allThrough(domSort) + */ + allThrough(callback) { + return new this.constructor(this.key, this.callback, callback); + } + + asRhs() { + return this; + } +} + +function props(callback) { + return new Side({method: 'props', args: [callback]}); +} + +/** Constrain to an input type on the LHS, or apply a type on the RHS. */ +function type(theType) { + return new Side({method: 'type', args: [theType]}); +} + +function note(callback) { + return new Side({method: 'note', args: [callback]}); +} + +function score(scoreOrCallback) { + return new Side({method: 'score', args: [scoreOrCallback]}); +} + +function atMost(score) { + return new Side({method: 'atMost', args: [score]}); +} + +function typeIn(...types) { + return new Side({method: 'typeIn', args: types}); +} + +/** + * Pull nodes that conform to multiple conditions at once. + * + * For example: ``and(type('title'), type('english'))`` + * + * Caveats: ``and`` supports only simple ``type`` calls as arguments for now, + * and it may fire off more rules as prerequisites than strictly necessary. + * ``not`` and ``or`` don't exist yet, but you can express ``or`` the long way + * around by having 2 rules with identical RHSs. + */ +function and(...lhss) { + return new Side({method: 'and', args: lhss}); +} + +/** + * Experimental. For each :term:`fnode` from ``typeCallA``, find the closest + * node from ``typeCallB``, and attach it as a note. The note is attached to + * the type specified by the RHS, defaulting to the type of ``typeCallA``. If + * no nodes are emitted from ``typeCallB``, do nothing. + * + * For example... :: + * + * nearest(type('image'), type('price')) + * + * The score of the ``typeCallA`` can be added to the new type's score by using + * :func:`conserveScore` (though this routine has since been removed):: + * + * rule(nearest(type('image'), type('price')), + * type('imageWithPrice').score(2).conserveScore()) + * + * Caveats: ``nearest`` supports only simple ``type`` calls as arguments ``a`` + * and ``b`` for now. + * + * @arg distance {function} A function that takes 2 fnodes and returns a + * numerical distance between them. Included options are :func:`distance`, + * which is a weighted topological distance, and :func:`euclidean`, which + * is a spatial distance. + */ +function nearest(typeCallA, typeCallB, distance = euclidean) { + return new Side({method: 'nearest', args: [typeCallA, typeCallB, distance]}); +} + +/** + * A chain of calls that can be compiled into a Rhs or Lhs, depending on its + * position in a Rule. This lets us use type() as a leading call for both RHSs + * and LHSs. I would prefer to do this dynamically, but that wouldn't compile + * down to old versions of ES. + */ +class Side { + constructor(...calls) { + // A "call" is like {method: 'dom', args: ['p.smoo']}. + this._calls = calls; + } + + max() { + return this._and('max'); + } + + bestCluster(options) { + return this._and('bestCluster', options); + } + + props(callback) { + return this._and('props', callback); + } + + type(...types) { + return this._and('type', ...types); + } + + note(callback) { + return this._and('note', callback); + } + + score(scoreOrCallback) { + return this._and('score', scoreOrCallback); + } + + atMost(score) { + return this._and('atMost', score); + } + + typeIn(...types) { + return this._and('typeIn', ...types); + } + + and(...lhss) { + return this._and('and', lhss); + } + + _and(method, ...args) { + return new this.constructor(...this._calls.concat({method, args})); + } + + asLhs() { + return this._asSide(Lhs.fromFirstCall(this._calls[0]), this._calls.slice(1)); + } + + asRhs() { + return this._asSide(new InwardRhs(), this._calls); + } + + _asSide(side, calls) { + for (let call of calls) { + side = side[call.method](...call.args); + } + return side; + } + + when(pred) { + return this._and('when', pred); + } +} + +/** + * A wrapper around a DOM node, storing :term:`types<type>`, + * :term:`scores<score>`, and :term:`notes<note>` that apply to it + */ +class Fnode { + /** + * @arg element The DOM element described by the fnode. + * @arg ruleset The ruleset which created the fnode. + */ + constructor(element, ruleset) { + if (element === undefined) { + throw new Error("Someone tried to make a fnode without specifying the element they're talking about."); + } + /** + * The raw DOM element this fnode describes + */ + this.element = element; + this._ruleset = ruleset; + + // A map of type => {score: number, note: anything}. `score` is always + // present and defaults to 1. A note is set iff `note` is present and + // not undefined. + this._types = new Map(); + + // Note: conserveScore() is temporarily absent in 3.0. + // + // By default, a fnode has an independent score for each of its types. + // However, a RHS can opt to conserve the score of an upstream type, + // carrying it forward into another type. To avoid runaway scores in + // the case that multiple rules choose to do this, we limit the + // contribution of an upstream type's score to being multiplied in a + // single time. In this set, we keep track of which upstream types' + // scores have already been multiplied into each type. LHS fnode => Set + // of types whose score for that node have been multiplied into this + // node's score. + this._conservedScores = new Map(); + } + + /** + * Return whether the given type is one of the ones attached to the wrapped + * HTML node. + */ + hasType(type) { + // Run type(theType) against the ruleset to make sure this doesn't + // return false just because we haven't lazily run certain rules yet. + this._computeType(type); + return this._types.has(type); + } + + /** + * Return the confidence, in the range (0, 1), that the fnode belongs to the + * given type, 0 by default. + */ + scoreFor(type) { + this._computeType(type); + return sigmoid(this._ruleset.weightedScore(this.scoresSoFarFor(type)) + + getDefault(this._ruleset.biases, type, () => 0)); + } + + /** + * Return the fnode's note for the given type, ``undefined`` if none. + */ + noteFor(type) { + this._computeType(type); + return this._noteSoFarFor(type); + } + + /** + * Return whether this fnode has a note for the given type. + * + * ``undefined`` is not considered a note and may be overwritten with + * impunity. + */ + hasNoteFor(type) { + this._computeType(type); + return this._hasNoteSoFarFor(type); + } + + // -------- Methods below this point are private to the framework. -------- + + /** + * Return an iterable of the types tagged onto me by rules that have + * already executed. + */ + typesSoFar() { + return this._types.keys(); + } + + _noteSoFarFor(type) { + return this._typeRecordForGetting(type).note; + } + + _hasNoteSoFarFor(type) { + return this._noteSoFarFor(type) !== undefined; + } + + /** + * Return the score thus far computed on me for a certain type. Doesn't + * implicitly run any rules. If no score has yet been determined for the + * given type, return undefined. + */ + scoresSoFarFor(type) { + return this._typeRecordForGetting(type).score; + } + + /** + * Add a given number to one of our per-type scores. Implicitly assign us + * the given type. Keep track of which rule it resulted from so we can + * later mess with the coeffs. + */ + addScoreFor(type, score, ruleName) { + this._typeRecordForSetting(type).score.set(ruleName, score); + } + + /** + * Set the note attached to one of our types. Implicitly assign us that + * type if we don't have it already. + */ + setNoteFor(type, note) { + if (this._hasNoteSoFarFor(type)) { + if (note !== undefined) { + throw new Error(`Someone (likely the right-hand side of a rule) tried to add a note of type ${type} to an element, but one of that type already exists. Overwriting notes is not allowed, since it would make the order of rules matter.`); + } + // else the incoming note is undefined and we already have the + // type, so it's a no-op + } else { + // Apply either a type and note or just a type (which means a note + // that is undefined): + this._typeRecordForSetting(type).note = note; + } + } + + /** + * Return a score/note record for a type, creating it if it doesn't exist. + */ + _typeRecordForSetting(type) { + return setDefault(this._types, type, () => ({score: new Map()})); + } + + /** + * Manifest a temporary type record for reading, working around the lack of + * a .? operator in JS. + */ + _typeRecordForGetting(type) { + return getDefault(this._types, type, () => ({score: new Map()})); + } + + /** + * Make sure any scores, notes, and type-tagging for the given type are + * computed for my element. + */ + _computeType(theType) { + if (!this._types.has(theType)) { // Prevent infinite recursion when an A->A rule looks at A's note in a callback. + this._ruleset.get(type(theType)); + } + } +} + +/** + * Construct and return the proper type of rule class based on the + * inwardness/outwardness of the RHS. + * + * @arg lhs {Lhs} The left-hand side of the rule + * @arg rhs {Rhs} The right-hand side of the rule + * @arg options {object} Other, optional information about the rule. + * Currently, the only recognized option is ``name``, which points to a + * string that uniquely identifies this rule in a ruleset. The name + * correlates this rule with one of the coefficients passed into + * :func:`ruleset`. If no name is given, an identifier is assigned based on + * the index of this rule in the ruleset, but that is, of course, brittle. + */ +function rule(lhs, rhs, options) { + // Since out() is a valid call only on the RHS (unlike type()), we can take + // a shortcut here: any outward RHS will already be an OutwardRhs; we don't + // need to sidetrack it through being a Side. And OutwardRhs has an asRhs() + // that just returns itself. + if (typeof rhs === 'string') { + rhs = out(rhs); + } + return new ((rhs instanceof OutwardRhs) ? OutwardRule : InwardRule)(lhs, rhs, options); +} + +let nextRuleNumber = 0; +function newInternalRuleName() { + return '_' + nextRuleNumber++; +} + +/** + * We place the in/out distinction in Rules because it determines whether the + * RHS result is cached, and Rules are responsible for maintaining the rulewise + * cache ruleset.ruleCache. + */ +class Rule { // abstract + constructor(lhs, rhs, options) { + this.lhs = lhs.asLhs(); + this.rhs = rhs.asRhs(); + // TODO: Make auto-generated rule names be based on the out types of + // the rules, e.g. _priceish_4. That way, adding rules for one type + // won't make the coeffs misalign for another. + this.name = (options ? options.name : undefined) || newInternalRuleName(); + } + + /** + * Return a NiceSet of the rules that this one shallowly depends on in the + * given ruleset. In a BoundRuleset, this may include rules that have + * already been executed. + * + * Depend on emitters of any LHS type this rule finalizes. (See + * _typesFinalized for a definition.) Depend on adders of any other LHS + * types (because, after all, we need to know what nodes have that type in + * order to find the set of LHS nodes). This works for simple rules and + * complex ones like and(). + * + * Specific examples (where A is a type): + * * A.max->* depends on anything emitting A. + * * Even A.max->A depends on A emitters, because we have to have all the + * scores factored in first. For example, what if we did + * max(A)->score(.5)? + * * A->A depends on anything adding A. + * * A->(something other than A) depends on anything emitting A. (For + * example, we need the A score finalized before we could transfer it to + * B using conserveScore().) + * * A->out() also depends on anything emitting A. Fnode methods aren't + * smart enough to lazily run emitter rules as needed. We could make them + * so if it was shown to be an advantage. + */ + prerequisites(ruleset) { + // Optimization: we could cache the result of this when in a compiled (immutable) ruleset. + + // Extend prereqs with rules derived from each of the given types. If + // no rules are found, raise an exception, as that indicates a + // malformed ruleset. + function extendOrThrow(prereqs, types, ruleGetter, verb) { + for (let type of types) { + const rules = ruleGetter(type); + if (rules.length > 0) { + prereqs.extend(rules); + } else { + throw new Error(`No rule ${verb} the "${type}" type, but another rule needs it as input.`); + } + } + } + + const prereqs = new NiceSet(); + + // Add finalized types: + extendOrThrow(prereqs, this._typesFinalized(), type => ruleset.inwardRulesThatCouldEmit(type), 'emits'); + + // Add mentioned types: + // We could say this.lhs.typesMentioned().minus(typesFinalized) as an + // optimization. But since types mentioned are a superset of types + // finalized and rules adding are a subset of rules emitting, we get + // the same result without. + extendOrThrow(prereqs, this.lhs.typesMentioned(), type => ruleset.inwardRulesThatCouldAdd(type), 'adds'); + + return prereqs; + } + + /** + * Return the types that this rule finalizes. + * + * To "finalize" a type means to make sure we're finished running all + * possible rules that might change a node's score or notes w.r.t. a given + * type. This is generally done because we're about to use those data for + * something, like computing a new type's score or or an aggregate + * function. Exhaustively, we're about to... + * * change the type of the nodes or + * * aggregate all nodes of a type + * + * This base-class implementation just returns what aggregate functions + * need, since that need spans inward and outward rules. + * + * @return Set of types + */ + _typesFinalized() { + // Get the types that are fed to aggregate functions. Aggregate + // functions are more demanding than a simple type() LHS. A type() LHS + // itself does not finalize its nodes because the things it could do to + // them without changing their type (adding notes, adding to score) + // are immutable or commutative (respectively). Thus, we require a RHS + // type change in order to require finalization of a simple type() + // mention. A max(B), OTOH, is not commutative with other B->B rules + // (imagine type(B).max()->score(.5)), so it must depend on B emitters + // and thus finalize B. (This will have to be relaxed or rethought when + // we do the max()/atMost() optimization. Perhaps we can delegate to + // aggregate functions up in Rule.prerequisites() to ask what their + // prereqs are. If they implement such an optimization, they can reply. + // Otherwise, we can assume they are all the nodes of their type.) + // + // TODO: Could arbitrary predicates (once we implement those) matter + // too? Maybe it's not just aggregations. + const type = this.lhs.aggregatedType(); + return (type === undefined) ? new NiceSet() : new NiceSet([type]); + } +} + +/** + * A normal rule, whose results head back into the Fathom knowledgebase, to be + * operated on by further rules. + */ +class InwardRule extends Rule { + // TODO: On construct, complain about useless rules, like a dom() rule that + // doesn't assign a type. + + /** + * Return an iterable of the fnodes emitted by the RHS of this rule. + * Side effect: update ruleset's store of fnodes, its accounting of which + * rules are done executing, and its cache of results per type. + */ + results(ruleset) { + if (ruleset.doneRules.has(this)) { // shouldn't happen + throw new Error('A bug in Fathom caused results() to be called on an inward rule twice. That could cause redundant score contributions, etc.'); + } + const self = this; + // For now, we consider most of what a LHS computes to be cheap, aside + // from type() and type().max(), which are cached by their specialized + // LHS subclasses. + const leftResults = this.lhs.fnodes(ruleset); + // Avoid returning a single fnode more than once. LHSs uniquify + // themselves, but the RHS can change the element it's talking + // about and thus end up with dupes. + const returnedFnodes = new Set(); + + // Merge facts into fnodes: + forEach( + // leftResult can be either a fnode or a {fnode, rhsTransformer} pair. + function updateFnode(leftResult) { + const leftType = self.lhs.guaranteedType(); + // Get a fnode and a RHS transformer, whether a plain fnode is + // returned or a {fnode, rhsTransformer} pair: + const {fnode: leftFnode = leftResult, rhsTransformer = identity} = leftResult; + // Grab the fact from the RHS, and run the LHS's optional + // transformer over it to pick up anything special it wants to + // do: + const fact = rhsTransformer(self.rhs.fact(leftFnode, leftType)); + self.lhs.checkFact(fact); + const rightFnode = ruleset.fnodeForElement(fact.element || leftFnode.element); + // If the RHS doesn't specify a type, default to the + // type of the LHS, if any: + const rightType = fact.type || self.lhs.guaranteedType(); + if (fact.score !== undefined) { + if (rightType !== undefined) { + rightFnode.addScoreFor(rightType, fact.score, self.name); + } else { + throw new Error(`The right-hand side of a rule specified a score (${fact.score}) with neither an explicit type nor one we could infer from the left-hand side.`); + } + } + if (fact.type !== undefined || fact.note !== undefined) { + // There's a reason to call setNoteFor. + if (rightType === undefined) { + throw new Error(`The right-hand side of a rule specified a note (${fact.note}) with neither an explicit type nor one we could infer from the left-hand side. Notes are per-type, per-node, so that's a problem.`); + } else { + rightFnode.setNoteFor(rightType, fact.note); + } + } + returnedFnodes.add(rightFnode); + }, + leftResults); + + // Update ruleset lookup tables. + // First, mark this rule as done: + ruleset.doneRules.add(this); + // Then, stick each fnode in typeCache under all applicable types. + // Optimization: we really only need to loop over the types + // this rule can possibly add. + for (let fnode of returnedFnodes) { + for (let type of fnode.typesSoFar()) { + setDefault(ruleset.typeCache, type, () => new Set()).add(fnode); + } + } + return returnedFnodes.values(); + } + + /** + * Return a Set of the types that could be emitted back into the system. + * To emit a type means to either to add it to a fnode emitted from the RHS + * or to leave it on such a fnode where it already exists. + */ + typesItCouldEmit() { + const rhs = this.rhs.possibleEmissions(); + if (!rhs.couldChangeType && this.lhs.guaranteedType() !== undefined) { + // It's a b -> b rule. + return new Set([this.lhs.guaranteedType()]); + } else if (rhs.possibleTypes.size > 0) { + // We can prove the type emission from the RHS alone. + return rhs.possibleTypes; + } else { + throw new Error('Could not determine the emitted type of a rule because its right-hand side calls props() without calling typeIn().'); + } + } + + /** + * Return a Set of types I could add to fnodes I output (where the fnodes + * did not already have them). + */ + typesItCouldAdd() { + const ret = new Set(this.typesItCouldEmit()); + ret.delete(this.lhs.guaranteedType()); + return ret; + } + + /** + * Add the types we could change to the superclass's result. + */ + _typesFinalized() { + const self = this; + function typesThatCouldChange() { + const ret = new NiceSet(); + + // Get types that could change: + const emissions = self.rhs.possibleEmissions(); + if (emissions.couldChangeType) { + // Get the possible guaranteed combinations of types on the LHS + // (taking just this LHS into account). For each combo, if the RHS + // adds a type that's not in the combo, the types in the combo get + // unioned into ret. + for (let combo of self.lhs.possibleTypeCombinations()) { + for (let rhsType of emissions.possibleTypes) { + if (!combo.has(rhsType)) { + ret.extend(combo); + break; + } + } + } + } + // Optimization: the possible combos could be later expanded to be + // informed by earlier rules which add the types mentioned in the LHS. + // If the only way for something to get B is to have Q first, then we + // can add Q to each combo and end up with fewer types finalized. Would + // this imply the existence of a Q->B->Q cycle and thus be impossible? + // Think about it. If we do this, we can centralize that logic here, + // rather than repeating it in all the Lhs subclasses). + return ret; + } + + return typesThatCouldChange().extend(super._typesFinalized()); + } +} + +/** + * A rule whose RHS is an out(). This represents a final goal of a ruleset. + * Its results go out into the world, not inward back into the Fathom + * knowledgebase. + */ +class OutwardRule extends Rule { + /** + * Compute the whole thing, including any .through() and .allThrough(). + * Do not mark me done in ruleset.doneRules; out rules are never marked as + * done so they can be requested many times without having to cache their + * (potentially big, since they aren't necessarily fnodes?) results. (We + * can add caching later if it proves beneficial.) + */ + results(ruleset) { + /** + * From a LHS's ``{fnode, rhsTransform}`` object or plain fnode, pick off just + * the fnode and return it. + */ + function justFnode(fnodeOrStruct) { + return (fnodeOrStruct instanceof Fnode) ? fnodeOrStruct : fnodeOrStruct.fnode; + } + + return this.rhs.allCallback(map(this.rhs.callback, map(justFnode, this.lhs.fnodes(ruleset)))); + } + + /** + * @return the key under which the output of this rule will be available + */ + key() { + return this.rhs.key; + } + + /** + * OutwardRules finalize all types mentioned. + */ + _typesFinalized() { + return this.lhs.typesMentioned().extend(super._typesFinalized()); + } +} + +/** + * A shortcut for creating a new :class:`Ruleset`, for symmetry with + * :func:`rule` + */ +function ruleset(rules, coeffs = [], biases = []) { + return new Ruleset(rules, coeffs, biases); +} + +/** + * An unbound ruleset. When you bind it by calling :func:`~Ruleset.against()`, + * the resulting :class:`BoundRuleset` will be immutable. + */ +class Ruleset { + /** + * @arg rules {Array} Rules returned from :func:`rule` + * @arg coeffs {Map} A map of rule names to numerical weights, typically + * returned by the :doc:`trainer<training>`. Example: + * ``[['someRuleName', 5.04], ...]``. If not given, coefficients + * default to 1. + * @arg biases {object} A map of type names to neural-net biases. These + * enable accurate confidence estimates. Example: ``[['someType', + * -2.08], ...]``. If absent, biases default to 0. + */ + constructor(rules, coeffs = [], biases = []) { + this._inRules = []; + this._outRules = new Map(); // key -> rule + this._rulesThatCouldEmit = new Map(); // type -> [rules] + this._rulesThatCouldAdd = new Map(); // type -> [rules] + // Private to the framework: + this._coeffs = new Map(coeffs); // rule name => coefficient + this.biases = new Map(biases); // type name => bias + + // Separate rules into out ones and in ones, and sock them away. We do + // this here so mistakes raise errors early. + for (let rule of rules) { + if (rule instanceof InwardRule) { + this._inRules.push(rule); + + // Keep track of what inward rules can emit or add: + // TODO: Combine these hashes for space efficiency: + const emittedTypes = rule.typesItCouldEmit(); + for (let type of emittedTypes) { + setDefault(this._rulesThatCouldEmit, type, () => []).push(rule); + } + for (let type of rule.typesItCouldAdd()) { + setDefault(this._rulesThatCouldAdd, type, () => []).push(rule); + } + } else if (rule instanceof OutwardRule) { + this._outRules.set(rule.key(), rule); + } else { + throw new Error(`This element of ruleset()'s first param wasn't a rule: ${rule}`); + } + } + } + + /** + * Commit this ruleset to running against a specific DOM tree or subtree. + * + * When run against a subtree, the root of the subtree is not considered as + * a possible match. + * + * This doesn't actually modify the Ruleset but rather returns a fresh + * :class:`BoundRuleset`, which contains caches and other stateful, per-DOM + * bric-a-brac. + */ + against(doc) { + return new BoundRuleset(doc, + this._inRules, + this._outRules, + this._rulesThatCouldEmit, + this._rulesThatCouldAdd, + this._coeffs, + this.biases); + } + + /** + * Return all the rules (both inward and outward) that make up this ruleset. + * + * From this, you can construct another ruleset like this one but with your + * own rules added. + */ + rules() { + return Array.from([...this._inRules, ...this._outRules.values()]); + } +} + +/** + * A ruleset that is earmarked to analyze a certain DOM + * + * Carries a cache of rule results on that DOM. Typically comes from + * :meth:`~Ruleset.against`. + */ +class BoundRuleset { + /** + * @arg inRules {Array} Non-out() rules + * @arg outRules {Map} Output key -> out() rule + */ + constructor(doc, inRules, outRules, rulesThatCouldEmit, rulesThatCouldAdd, coeffs, biases) { + this.doc = doc; + this._inRules = inRules; + this._outRules = outRules; + this._rulesThatCouldEmit = rulesThatCouldEmit; + this._rulesThatCouldAdd = rulesThatCouldAdd; + this._coeffs = coeffs; + + // Private, for the use of only helper classes: + this.biases = biases; + this._clearCaches(); + this.elementCache = new WeakMap(); // DOM element => fnode about it + this.doneRules = new Set(); // InwardRules that have been executed. OutwardRules can be executed more than once because they don't change any fnodes and are thus idempotent. + } + + /** + * Change my coefficients and biases after construction. + * + * @arg coeffs See the :class:`Ruleset` constructor. + * @arg biases See the :class:`Ruleset` constructor. + */ + setCoeffsAndBiases(coeffs, biases = []) { + // Destructuring assignment doesn't make it through rollup properly + // (https://github.com/rollup/rollup-plugin-commonjs/issues/358): + this._coeffs = new Map(coeffs); + this.biases = new Map(biases); + this._clearCaches(); + } + + /** + * Clear the typeCache and maxCache, usually in the wake of changing + * ``this._coeffs``, because both of thise depend on weighted scores. + */ + _clearCaches() { + this.maxCache = new Map(); // type => Array of max fnode (or fnodes, if tied) of this type + this.typeCache = new Map(); // type => Set of all fnodes of this type found so far. (The dependency resolution during execution ensures that individual types will be comprehensive just in time.) + } + + /** + * Return an array of zero or more fnodes. + * @arg thing {string|Lhs|Node} Can be + * + * (1) A string which matches up with an "out" rule in the ruleset. + * If the out rule uses through(), the results of through's + * callback (which might not be fnodes) will be returned. + * (2) An arbitrary LHS which we calculate and return the results of. + * (3) A DOM node, for which we will return the corresponding fnode. + * + * Results are cached for cases (1) and (3). + */ + get(thing) { + if (typeof thing === 'string') { + if (this._outRules.has(thing)) { + return Array.from(this._execute(this._outRules.get(thing))); + } else { + throw new Error(`There is no out() rule with key "${thing}".`); + } + } else if (isDomElement(thing)) { + // Return the fnode and let it run type(foo) on demand, as people + // ask it things like scoreFor(foo). + return this.fnodeForElement(thing); + } else if (thing.asLhs !== undefined) { + // Make a temporary out rule, and run it. This may add things to + // the ruleset's cache, but that's fine: it doesn't change any + // future results; it just might make them faster. For example, if + // you ask for .get(type('smoo')) twice, the second time will be a + // cache hit. + const outRule = rule(thing, out(Symbol('outKey'))); + return Array.from(this._execute(outRule)); + } else { + throw new Error('ruleset.get() expects a string, an expression like on the left-hand side of a rule, or a DOM node.'); + } + } + + /** + * Return the weighted sum of the per-rule, per-type scores from a fnode. + * + * @arg mapOfScores a Map of rule name to the [0, 1] score it computed for + * the type in question + */ + weightedScore(mapOfScores) { + let total = 0; + for (const [name, score] of mapOfScores) { + total += score * getDefault(this._coeffs, name, () => 1); + } + return total; + } + + // Provide an opaque context object to be made available to all ranker + // functions. + // context (object) { + // self.context = object; + // } + + // -------- Methods below this point are private to the framework. -------- + + /** + * Return all the thus-far-unexecuted rules that will have to run to run + * the requested rule, in the form of Map(prereq: [rulesItIsNeededBy]). + */ + _prerequisitesTo(rule, undonePrereqs = new Map()) { + for (let prereq of rule.prerequisites(this)) { + if (!this.doneRules.has(prereq)) { + // prereq is not already run. (If it were, we wouldn't care + // about adding it to the graph.) + const alreadyAdded = undonePrereqs.has(prereq); + setDefault(undonePrereqs, prereq, () => []).push(rule); + + // alreadyAdded means we've already computed the prereqs of + // this prereq and added them to undonePrereqs. So, now + // that we've hooked up the rule to this prereq in the + // graph, we can stop. But, if we haven't, then... + if (!alreadyAdded) { + this._prerequisitesTo(prereq, undonePrereqs); + } + } + } + return undonePrereqs; + } + + /** + * Run the given rule (and its dependencies, in the proper order), and + * return its results. + * + * The caller is responsible for ensuring that _execute() is not called + * more than once for a given InwardRule, lest non-idempotent + * transformations, like score contributions, be applied to fnodes more + * than once. + * + * The basic idea is to sort rules in topological order (according to input + * and output types) and then run them. On top of that, we do some + * optimizations. We keep a cache of results by type (whether partial or + * comprehensive--either way, the topology ensures that any + * non-comprehensive typeCache entry is made comprehensive before another + * rule needs it). And we prune our search for prerequisite rules at the + * first encountered already-executed rule. + */ + _execute(rule) { + const prereqs = this._prerequisitesTo(rule); + let sorted; + try { + sorted = [rule].concat(toposort(prereqs.keys(), + prereq => prereqs.get(prereq))); + } catch (exc) { + if (exc instanceof CycleError) { + throw new CycleError('There is a cyclic dependency in the ruleset.'); + } else { + throw exc; + } + } + let fnodes; + for (let eachRule of reversed(sorted)) { + // Sock each set of results away in this.typeCache: + fnodes = eachRule.results(this); + } + return Array.from(fnodes); + } + + /** @return {Rule[]} */ + inwardRulesThatCouldEmit(type) { + return getDefault(this._rulesThatCouldEmit, type, () => []); + } + + /** @return {Rule[]} */ + inwardRulesThatCouldAdd(type) { + return getDefault(this._rulesThatCouldAdd, type, () => []); + } + + /** + * @return the Fathom node that describes the given DOM element. This does + * not trigger any execution, so the result may be incomplete. + */ + fnodeForElement(element) { + return setDefault(this.elementCache, + element, + () => new Fnode(element, this)); + } +} + +/* 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/. */ + +const version = '3.7.3'; + +export { and, atMost, clusters$1 as clusters, dom, element, exceptions, nearest, note, out, props, rule, ruleset, score, type, typeIn, utilsForFrontend as utils, version }; diff --git a/toolkit/modules/third_party/fathom/fx-header b/toolkit/modules/third_party/fathom/fx-header new file mode 100644 index 0000000000..c159bf4806 --- /dev/null +++ b/toolkit/modules/third_party/fathom/fx-header @@ -0,0 +1,8 @@ +/* +DO NOT TOUCH fathom.mjs DIRECTLY. See the README for instructions. +*/ + +/* 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/. */ + diff --git a/toolkit/modules/third_party/fathom/moz.yaml b/toolkit/modules/third_party/fathom/moz.yaml new file mode 100644 index 0000000000..606b6fd9c0 --- /dev/null +++ b/toolkit/modules/third_party/fathom/moz.yaml @@ -0,0 +1,29 @@ +# Version of this schema +schema: 1 + +bugzilla: + # Bugzilla product and component for this directory and subdirectories + product: Toolkit + component: General + +# Document the source of externally hosted code +origin: + + # Short name of the package/library + name: Fathom + + description: Machine-learning system for DOM element recognition + + # Full URL for the package's homepage/etc + # Usually different from repository url + url: https://mozilla.github.io/fathom/ + + # Human-readable identifier for this version/release + # Generally "version NNN", "tag SSS", "bookmark SSS" + release: version 3.7.3 + + # The package's license, where possible using the mnemonic from + # https://spdx.org/licenses/ + # Multiple licenses can be specified (as a YAML list) + # A "LICENSE" file must exist containing the full license text + license: MPL-2.0 diff --git a/toolkit/modules/third_party/jsesc/README b/toolkit/modules/third_party/jsesc/README new file mode 100644 index 0000000000..fd74412f5f --- /dev/null +++ b/toolkit/modules/third_party/jsesc/README @@ -0,0 +1,12 @@ +This code comes from an externally managed library, available at +<https://github.com/mathiasbynens/jsesc>. Bugs should be reported directly +upstream and integrated back here. + +In order to regenerate this file, you need to do the following: + + $ git clone git@github.com:mathiasbynens/jsesc.git && cd jsesc + $ grunt template + $ export MOZ_JSESC="../mozilla-central/toolkit/modules/third_party/jsesc" + $ cat $MOZ_JSESC/fx-header jsesc.js $MOZ_JSESC/fx-footer > $MOZ_JSESC/jsesc.mjs + +You will then need to change the penulitmate line from `}(this));` to `}(global));` diff --git a/toolkit/modules/third_party/jsesc/fx-footer b/toolkit/modules/third_party/jsesc/fx-footer new file mode 100644 index 0000000000..3edd0d0bf3 --- /dev/null +++ b/toolkit/modules/third_party/jsesc/fx-footer @@ -0,0 +1,2 @@ + +export var jsesc = global.jsesc; diff --git a/toolkit/modules/third_party/jsesc/fx-header b/toolkit/modules/third_party/jsesc/fx-header new file mode 100644 index 0000000000..560fa37b3e --- /dev/null +++ b/toolkit/modules/third_party/jsesc/fx-header @@ -0,0 +1,26 @@ +/* +DO NOT TOUCH THIS FILE DIRECTLY. See the README for instructions. + +Copyright Mathias Bynens <https://mathiasbynens.be/> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +var global = {}; diff --git a/toolkit/modules/third_party/jsesc/jsesc.mjs b/toolkit/modules/third_party/jsesc/jsesc.mjs new file mode 100644 index 0000000000..c4e832be99 --- /dev/null +++ b/toolkit/modules/third_party/jsesc/jsesc.mjs @@ -0,0 +1,302 @@ +/* +DO NOT TOUCH THIS FILE DIRECTLY. See the README for instructions. + +Copyright Mathias Bynens <https://mathiasbynens.be/> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +var global = {}; + +/*! https://mths.be/jsesc v1.0.0 by @mathias */ +;(function(root) { + + // Detect free variables `exports` + var freeExports = typeof exports == 'object' && exports; + + // Detect free variable `module` + var freeModule = typeof module == 'object' && module && + module.exports == freeExports && module; + + // Detect free variable `global`, from Node.js or Browserified code, + // and use it as `root` + var freeGlobal = typeof global == 'object' && global; + if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { + root = freeGlobal; + } + + /*--------------------------------------------------------------------------*/ + + var object = {}; + var hasOwnProperty = object.hasOwnProperty; + var forOwn = function(object, callback) { + var key; + for (key in object) { + if (hasOwnProperty.call(object, key)) { + callback(key, object[key]); + } + } + }; + + var extend = function(destination, source) { + if (!source) { + return destination; + } + forOwn(source, function(key, value) { + destination[key] = value; + }); + return destination; + }; + + var forEach = function(array, callback) { + var length = array.length; + var index = -1; + while (++index < length) { + callback(array[index]); + } + }; + + var toString = object.toString; + var isArray = function(value) { + return toString.call(value) == '[object Array]'; + }; + var isObject = function(value) { + // This is a very simple check, but it’s good enough for what we need. + return toString.call(value) == '[object Object]'; + }; + var isString = function(value) { + return typeof value == 'string' || + toString.call(value) == '[object String]'; + }; + var isFunction = function(value) { + // In a perfect world, the `typeof` check would be sufficient. However, + // in Chrome 1–12, `typeof /x/ == 'object'`, and in IE 6–8 + // `typeof alert == 'object'` and similar for other host objects. + return typeof value == 'function' || + toString.call(value) == '[object Function]'; + }; + + /*--------------------------------------------------------------------------*/ + + // https://mathiasbynens.be/notes/javascript-escapes#single + var singleEscapes = { + '"': '\\"', + '\'': '\\\'', + '\\': '\\\\', + '\b': '\\b', + '\f': '\\f', + '\n': '\\n', + '\r': '\\r', + '\t': '\\t' + // `\v` is omitted intentionally, because in IE < 9, '\v' == 'v'. + // '\v': '\\x0B' + }; + var regexSingleEscape = /["'\\\b\f\n\r\t]/; + + var regexDigit = /[0-9]/; + var regexWhitelist = /[ !#-&\(-\[\]-~]/; + + var jsesc = function(argument, options) { + // Handle options + var defaults = { + 'escapeEverything': false, + 'quotes': 'single', + 'wrap': false, + 'es6': false, + 'json': false, + 'compact': true, + 'lowercaseHex': false, + 'indent': '\t', + '__indent__': '' + }; + var json = options && options.json; + if (json) { + defaults.quotes = 'double'; + defaults.wrap = true; + } + options = extend(defaults, options); + if (options.quotes != 'single' && options.quotes != 'double') { + options.quotes = 'single'; + } + var quote = options.quotes == 'double' ? '"' : '\''; + var compact = options.compact; + var indent = options.indent; + var oldIndent; + var newLine = compact ? '' : '\n'; + var result; + var isEmpty = true; + + if (json && argument && isFunction(argument.toJSON)) { + argument = argument.toJSON(); + } + + if (!isString(argument)) { + if (isArray(argument)) { + result = []; + options.wrap = true; + oldIndent = options.__indent__; + indent += oldIndent; + options.__indent__ = indent; + forEach(argument, function(value) { + isEmpty = false; + result.push( + (compact ? '' : indent) + + jsesc(value, options) + ); + }); + if (isEmpty) { + return '[]'; + } + return '[' + newLine + result.join(',' + newLine) + newLine + + (compact ? '' : oldIndent) + ']'; + } else if (!isObject(argument)) { + if (json) { + // For some values (e.g. `undefined`, `function` objects), + // `JSON.stringify(value)` returns `undefined` (which isn’t valid + // JSON) instead of `'null'`. + return JSON.stringify(argument) || 'null'; + } + return String(argument); + } else { // it’s an object + result = []; + options.wrap = true; + oldIndent = options.__indent__; + indent += oldIndent; + options.__indent__ = indent; + forOwn(argument, function(key, value) { + isEmpty = false; + result.push( + (compact ? '' : indent) + + jsesc(key, options) + ':' + + (compact ? '' : ' ') + + jsesc(value, options) + ); + }); + if (isEmpty) { + return '{}'; + } + return '{' + newLine + result.join(',' + newLine) + newLine + + (compact ? '' : oldIndent) + '}'; + } + } + + var string = argument; + // Loop over each code unit in the string and escape it + var index = -1; + var length = string.length; + var first; + var second; + var codePoint; + result = ''; + while (++index < length) { + var character = string.charAt(index); + if (options.es6) { + first = string.charCodeAt(index); + if ( // check if it’s the start of a surrogate pair + first >= 0xD800 && first <= 0xDBFF && // high surrogate + length > index + 1 // there is a next code unit + ) { + second = string.charCodeAt(index + 1); + if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate + // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae + codePoint = (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; + var hexadecimal = codePoint.toString(16); + if (!options.lowercaseHex) { + hexadecimal = hexadecimal.toUpperCase(); + } + result += '\\u{' + hexadecimal + '}'; + index++; + continue; + } + } + } + if (!options.escapeEverything) { + if (regexWhitelist.test(character)) { + // It’s a printable ASCII character that is not `"`, `'` or `\`, + // so don’t escape it. + result += character; + continue; + } + if (character == '"') { + result += quote == character ? '\\"' : character; + continue; + } + if (character == '\'') { + result += quote == character ? '\\\'' : character; + continue; + } + } + if ( + character == '\0' && + !json && + !regexDigit.test(string.charAt(index + 1)) + ) { + result += '\\0'; + continue; + } + if (regexSingleEscape.test(character)) { + // no need for a `hasOwnProperty` check here + result += singleEscapes[character]; + continue; + } + var charCode = character.charCodeAt(0); + var hexadecimal = charCode.toString(16); + if (!options.lowercaseHex) { + hexadecimal = hexadecimal.toUpperCase(); + } + var longhand = hexadecimal.length > 2 || json; + var escaped = '\\' + (longhand ? 'u' : 'x') + + ('0000' + hexadecimal).slice(longhand ? -4 : -2); + result += escaped; + continue; + } + if (options.wrap) { + result = quote + result + quote; + } + return result; + }; + + jsesc.version = '1.0.0'; + + /*--------------------------------------------------------------------------*/ + + // Some AMD build optimizers, like r.js, check for specific condition patterns + // like the following: + if ( + typeof define == 'function' && + typeof define.amd == 'object' && + define.amd + ) { + define(function() { + return jsesc; + }); + } else if (freeExports && !freeExports.nodeType) { + if (freeModule) { // in Node.js or RingoJS v0.8.0+ + freeModule.exports = jsesc; + } else { // in Narwhal or RingoJS v0.7.0- + freeExports.jsesc = jsesc; + } + } else { // in Rhino or a web browser + root.jsesc = jsesc; + } + +}(global)); + +export var jsesc = global.jsesc; diff --git a/toolkit/modules/win.xhtml b/toolkit/modules/win.xhtml new file mode 100644 index 0000000000..e3b8e41aba --- /dev/null +++ b/toolkit/modules/win.xhtml @@ -0,0 +1,6 @@ +<?xml version="1.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/. --> + +<window id="win" /> |