Repository: marcokreeft87/formulaone-card Branch: main Commit: f99299f5b575 Files: 89 Total size: 809.4 KB Directory structure: gitextract_77tvhghf/ ├── .devcontainer/ │ └── devcontainer.json ├── .eslintrc.js ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── config.yml │ │ └── feature_request.md │ └── workflows/ │ ├── pr.yml │ └── push.yml ├── .gitignore ├── .nvmrc ├── LICENSE ├── README.md ├── formulaone-card.js.LICENSE.txt ├── hacs.json ├── jest.config.js ├── package.json ├── src/ │ ├── api/ │ │ ├── client-base.ts │ │ ├── ergast-client.ts │ │ ├── f1-models.ts │ │ ├── f1sensor-client.ts │ │ ├── image-client.ts │ │ ├── restcountry-client.ts │ │ ├── vc-weather-client.ts │ │ └── weather-models.ts │ ├── cards/ │ │ ├── base-card.ts │ │ ├── constructor-standings.ts │ │ ├── countdown.ts │ │ ├── driver-standings.ts │ │ ├── last-result.ts │ │ ├── next-race.ts │ │ ├── results.ts │ │ └── schedule.ts │ ├── consts.ts │ ├── directives/ │ │ └── action-handler-directive.ts │ ├── editor.ts │ ├── fonts.ts │ ├── index.ts │ ├── lib/ │ │ ├── constants.ts │ │ ├── format_date.ts │ │ ├── format_date_time.ts │ │ ├── format_time.ts │ │ └── use_am_pm.ts │ ├── styles.ts │ ├── types/ │ │ ├── formulaone-card-types.ts │ │ └── rest-country-types.ts │ └── utils.ts ├── test/ │ ├── configuration.yaml │ └── lovelace.yaml ├── tests/ │ ├── api/ │ │ ├── ergast-client.test.ts │ │ ├── image-client.test.ts │ │ ├── restcountry-client.test.ts │ │ └── weather-client.test.ts │ ├── cards/ │ │ ├── base-card.test.ts │ │ ├── constructor-standings.test.ts │ │ ├── countdown.test.ts │ │ ├── driver-standings.test.ts │ │ ├── last-result.test.ts │ │ ├── next-race.test.ts │ │ ├── results.test.ts │ │ └── schedule.test.ts │ ├── config.ts │ ├── index.test.ts │ ├── lib/ │ │ ├── formate_date.test.ts │ │ ├── formate_date_time.test.ts │ │ └── formate_time.test.ts │ ├── testdata/ │ │ ├── constructorStandings.json │ │ ├── countries.json │ │ ├── driverStandings.json │ │ ├── localStorageMock.ts │ │ ├── qualifying.json │ │ ├── results.json │ │ ├── schedule.json │ │ ├── seasons.json │ │ └── sprint.json │ ├── utils/ │ │ ├── calculateWindDirection.test.ts │ │ ├── checkConfig.test.ts │ │ ├── getCircuitName.test.ts │ │ ├── getCountryFlagUrl.test.ts │ │ ├── getDriverName.test.ts │ │ ├── getRefreshTime.test.ts │ │ ├── getTeamImageUrl.test.ts │ │ ├── hasConfigOrEntitiesChanged.test.ts │ │ ├── reduceArray.test.ts │ │ ├── renderHeader.test.ts │ │ ├── renderLastYearsResults.test.ts │ │ ├── renderRaceInfo.test.ts │ │ └── renderWeatherInfo.test.ts │ └── utils.ts ├── tsconfig.json └── webpack.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .devcontainer/devcontainer.json ================================================ { "name": "formulaone-card Dev", "image": "marcokreeft/hass-custom-devcontainer", "postCreateCommand": "sudo -E container setup-dev && npm add && sudo hass -c /config -v", "containerEnv": { "DEVCONTAINER": "1" }, "appPort": "8124:8123", "forwardPorts": [8123], "mounts": [ "source=${localWorkspaceFolder},target=/config/www/workspace,type=bind", "source=${localWorkspaceFolder}/test,target=/config/test,type=bind", "source=${localWorkspaceFolder}/test/configuration.yaml,target=/config/configuration.yaml,type=bind" ], "runArgs": ["--env-file", "${localWorkspaceFolder}/test/.env"], "extensions": [ "github.vscode-pull-request-github", "esbenp.prettier-vscode", "spmeesseman.vscode-taskexplorer" ], "settings": { "files.eol": "\n", "editor.tabSize": 2, "editor.formatOnPaste": false, "editor.formatOnSave": true, "editor.formatOnType": true, "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "files.trimTrailingWhitespace": true } } // { // "image": "thomasloven/hass-custom-devcontainer", // "postCreateCommand": "container setup && npm add", // "forwardPorts": [8123], // "mounts": [ // "source=${localWorkspaceFolder},target=/config/www/workspace,type=bind", // "source=${localWorkspaceFolder}/test,target=/config/test,type=bind", // "source=${localWorkspaceFolder}/test/configuration.yaml,target=/config/configuration.yaml,type=bind" // ], // "runArgs": ["--env-file", "${localWorkspaceFolder}/test/.env"] // } ================================================ FILE: .eslintrc.js ================================================ module.exports = { "env": { "browser": true, "commonjs": true, "es2021": true }, "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended" ], "overrides": [ ], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": "latest" }, "plugins": [ "@typescript-eslint" ], "rules": { } } ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] **Smartphone (please complete the following information):** - Device: [e.g. iPhone6] - OS: [e.g. iOS8.1] - Browser [e.g. stock browser, safari] - Version [e.g. 22] **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: enhancement assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Have you tried if it is already a feature? Have you checked the wiki?** A lot of feature requests are closed because the functionality is already existing **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/workflows/pr.yml ================================================ name: Tests # Controls when the workflow will run on: # Triggers the workflow on push or pull request events but only for the "master" branch pull_request: branches: [ "main" ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: hacs: permissions: write-all runs-on: "ubuntu-latest" name: Test and validate steps: - name: Check out the repository uses: actions/checkout@v3 - name: Set Timezone uses: szenius/set-timezone@v1.0 with: timezoneLinux: "Europe/Amsterdam" timezoneMacos: "Europe/Amsterdam" timezoneWindows: "Central European Time" - name: Install dependencies run: npm install - name: Build project run: npm run build ================================================ FILE: .github/workflows/push.yml ================================================ name: Tests on: push: branches: - main paths: - '**' - '!README.md' workflow_dispatch: jobs: hacs: runs-on: "ubuntu-latest" name: Test, Build, and Release steps: - name: Check out the repository uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set Timezone uses: szenius/set-timezone@v1.0 with: timezoneLinux: "Europe/Amsterdam" timezoneMacos: "Europe/Amsterdam" timezoneWindows: "Central European Time" - name: HACS validation uses: hacs/action@main with: category: "plugin" ignore: brands - name: Set up Node.js uses: actions/setup-node@v3 with: node-version: '20' - name: Install dependencies run: npm ci - name: Build project run: npm run build - name: Get version from package.json id: package_version run: | VERSION=$(node -p "require('./package.json').version") echo "version=$VERSION" >> $GITHUB_OUTPUT - name: Create Git tag from package.json version run: | git config user.name "github-actions" git config user.email "github-actions@github.com" git tag v${{ steps.package_version.outputs.version }} git push origin v${{ steps.package_version.outputs.version }} - name: Get commit messages since last tag id: changelog run: | LAST_TAG=$(git describe --tags --abbrev=0) LOG=$(git log ${LAST_TAG}..HEAD --pretty=format:"- %s") echo "log<> $GITHUB_ENV echo "$LOG" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV - name: Get latest commit message id: get_commit run: echo "message=$(git log -1 --pretty=%B)" >> "$GITHUB_OUTPUT" - name: Create GitHub Release id: create_release uses: softprops/action-gh-release@v2 with: body: ${{ env.log }} tag_name: v${{ steps.package_version.outputs.version }} name: v${{ steps.package_version.outputs.version }} files: | formulaone-card.js formulaone-card.js.gz env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Print release ID run: echo "Release created with ID ${{ steps.create_release.outputs.id }}" ================================================ FILE: .gitignore ================================================ node_modules/* coverage/* formulaone-card.js formulaone-card.js.gz ================================================ FILE: .nvmrc ================================================ v18 ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2022 Marco Kreeft 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. ================================================ FILE: README.md ================================================ # FormulaOne Card [![GH-release](https://img.shields.io/github/v/release/marcokreeft87/formulaone-card.svg?style=flat-square)](https://github.com/marcokreeft87/formulaone-card/releases) [![GH-last-commit](https://img.shields.io/github/last-commit/marcokreeft87/formulaone-card.svg?style=flat-square)](https://github.com/marcokreeft87/formulaone-card/commits/master) [![GH-code-size](https://img.shields.io/github/languages/code-size/marcokreeft87/formulaone-card.svg?color=red&style=flat-square)](https://github.com/marcokreeft87/formulaone-card) [![hacs_badge](https://img.shields.io/badge/HACS-Default-41BDF5.svg?style=flat-square)](https://github.com/hacs/default) [![Codecov Coverage](https://img.shields.io/codecov/c/github/marcokreeft87/formulaone-card/main.svg?style=flat-square)](https://codecov.io/gh/marcokreeft87/formulaone-card/) [![CodeFactor](https://www.codefactor.io/repository/github/marcokreeft87/formulaone-card/badge?style=flat-square)](https://www.codefactor.io/repository/github/marcokreeft87/formulaone-card) # This card just displays the data. Data related bugs will probably be closed immediately Present the data of [Formula One](https://ergast.com/mrd/) in a pretty way Watch a demo of the card by BeardedTinker! [![Demo of BeardedTinker](https://img.youtube.com/vi/z7blY6D-Qmk/0.jpg)](https://www.youtube.com/watch?v=z7blY6D-Qmk) ## Installation ### HACS (recommended) Make sure you have [HACS](https://hacs.xyz/) (Home Assistant Community Store) installed.
_HACS is a third party community store and is not included in Home Assistant out of the box._ Just click here to directly go to the repository in HACS and click "Download": [![Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?owner=marcokreeft87&repository=formulaone-card&category=plugin) Or: - Open HACS - Go to "Frontend" section - Click button with "+" icon - Search for "formulaone-card" - Click "Download" button and install repository in HACS In both situations: - _If u are using YAML mode then add this to your_ [Lovelace resources](https://my.home-assistant.io/redirect/lovelace_resources/) ```yaml url: /hacsfiles/formulaone-card/formulaone-card.js type: module ``` - Refresh your browser ### Manual install Manually download [formulaone-card.js](https://raw.githubusercontent.com/marcokreeft87/formulaone-card/master/formulaone-card.js) and add it to your `/www/` folder and add the following to the `configuration.yaml` file: ```yaml lovelace: resources: - url: /local/formulaone-card.js type: module ``` The above configuration can be managed directly in the Configuration -> Lovelace Dashboards -> Resources panel when not using YAML mode, or added by clicking the "Add to lovelace" button on the HACS dashboard after installing the plugin. > [!TIP] > If you don't want to use the data from the Jolpi API directy but want to use a Home Assistant integration instead. Use [F1 sensor](https://github.com/Nicxe/f1_sensor) ## Configuration | Name | Type | Default | Description | | ----------------- | ------------- | ----------------------------------- | ------------------------------------------------ | | type | string | **Required** | `custom:formulaonecard` | | source | string | 'jolpi' | The source you want to use for the card (jolpi/f1sensor). You will have to set the entity | | entity | string | | Only required when using source: f1sensor. Set it to the entity that contains the data needed for the card. So for example when using driver_standings use entity: sensor.f1_driver_standings | | card_type | string | **Required** | The type of card you want to display (driver_standings,constructor_standings,next_race,schedule,last_result,results,countdown) | | title | string | | The header of the card ( hidden when null or empty) | | date_locale | string | | Override the locale used for the date and time formatting. [Available options listed here](https://www.w3.org/International/O-charset-lang.html)| | image_clickable | boolean | `false` | Click on image leads to wikipedia, or not | | show_carnumber | boolean | `false` | Show the number of the car | | show_raceinfo | boolean | `false` | Show the info of the race in the countdown and next_race card | | hide_tracklayout | boolean | `false` | Hide the track layout image in the card | | hide_racedatetimes | boolean | `false` | Hide the race information (dates and times of the qualifications/race/sprint) in the card | | f1_font | boolean | `false` | Use the official F1 font for headers | | location_clickable| boolean | `false` | Click on the location leads to wikipedia | | previous_race | enum | | Hide/strikethrough or make the past races italic options are (hide, strikethrough or italic) | | standings | object | | Configuration for the driver standings card | | translations | dictionary | _[translations](#translations)_ | Dictionary to override the default translation | | actions | object | _[Actions](#actions)_ | The tap, double tap or hold actions set on the image of the countdown, last_result, results, qualifying_results and next-race cards | | row_limit | number | | Limit the schedule, results, last_result, driver_standings and constructor_standings to this amount of row | | countdown_type | string or array | 'race' | Set the event to countdown to (race,qualifying,practice1,practice2,practice3,sprint,sprint_qualifying) | | show_weather | boolean | `false` | Show the _[weather forecast](#Forecast)_ of the upcoming race | | next_race_delay | number | | Delay (in hours) before the card switches to the next race | | show_lastyears_result | boolean | `false` | Show the winner of last year (next_race, countdown) | | only_show_date | boolean | `false` | Show the date of the next race (next_race) | | tabs_order | array |'results', 'qualifying', 'sprint' | Determine the order of the tabs (result) | | show_refresh | boolean |`false` | Show the refresh button (top right) | | next_race_display | enum |`date` | Show the date, time or both for the next race (date,time,datetime) | | show_event_details | boolean |`false` | Show the date of the next event (countdown) | | countdown_format | string | d h m s | Determine which parts of the countdown is displayed, d (days), h (hours), m (minutes) or s (seconds) | ### Actions This card supports all the default HA actions, except from more-info and toggle. See [Lovelace Actions](https://www.home-assistant.io/lovelace/actions/) for more detailed descriptions and examples. | Name | Type | Default | Description | | --------------- | ----------- | ------------ | ------------------------------------------------------------------------------------------ | | action | string | **Required** | `call-service`, `url`, `navigate`, `fire-dom-event`, `none` | | service | string | | Service to call when `action` is `call-service` | | service_data | object | | Optional data to include when `action` is `call-service` | | url_path | string | | URL to open when `action` is `url` | | navigation_path | string | | Path to navigate to when `action` is `navigate` | | confirmation | bool/object | `false` | Enable confirmation dialog | | haptic | string | `none` | Haptic feedback (`success`, `warning`, `failure`, `light`, `medium`, `heavy`, `selection`) | Actions example: ```yaml type: custom:formulaone-card card_type: next_race show_raceinfo: true actions: tap_action: action: navigate navigation_path: /lovelace/overview ``` ## Example configurations ### Next race ```yaml type: custom:formulaone-card card_type: next_race title: Next Race date_locale: nl image_clickable: false ``` ![image](https://user-images.githubusercontent.com/10223677/194120592-3df715bc-888d-460b-8743-ec1ab6017b96.png) ### Constructor standings ```yaml type: custom:formulaone-card card_type: constructor_standings title: Constructor Standings ``` ![image](https://user-images.githubusercontent.com/10223677/194120698-b981aac2-8678-4f35-afc9-ca6bb8514566.png) ```yaml type: custom:formulaone-card card_type: constructor_standings title: Constructor Standings standings: show_teamlogo: true ``` ![image](https://user-images.githubusercontent.com/10223677/213992061-91ade5f2-68bb-4572-84a1-5d5cf38e0645.png) ### Driver standings ```yaml type: custom:formulaone-card card_type: driver_standings title: Driver Standings ``` ![image](https://user-images.githubusercontent.com/10223677/194120796-28532a9d-a62d-44bb-8cb8-403bfa434a8b.png) This card can also show the flags and team names of the driver: ```yaml type: custom:formulaone-card card_type: driver_standings title: Driver Standings standings: show_flag: true show_team: true show_teamlogo: true ``` ### Schedule ```yaml type: custom:formulaone-card card_type: schedule title: Schedule date_locale: nl ``` ![image](https://user-images.githubusercontent.com/10223677/194120864-be0db0e9-dd0b-42aa-8829-d094c23ef0a5.png) This card can also show the flags of the countries of the tracks: ```yaml type: custom:formulaone-card card_type: schedule standings: show_flag: true ``` ### Last results ```yaml type: custom:formulaone-card card_type: last_result title: Last Result ``` ![image](https://user-images.githubusercontent.com/10223677/194120925-5fc6c1a7-8b2a-4c58-b89c-d0316d70efe9.png) ### Results ```yaml type: custom:formulaone-card card_type: results title: Results ``` ![image](https://user-images.githubusercontent.com/10223677/216916869-4d2dc991-3429-45f8-b286-0b08d538031f.png) This card can also show the flags and team names of the driver, alongside the logo of the teams: ```yaml type: custom:formulaone-card card_type: results title: Results standings: show_flag: true show_team: true show_teamlogo: true ``` ### Countdown ```yaml type: custom:formulaone-card card_type: countdown ``` ![image](https://user-images.githubusercontent.com/10223677/213435405-fdb2ff7c-3364-43d5-80b0-0f253d9b60c8.png) ```yaml type: custom:formulaone-card card_type: countdown f1_font: true ``` ![image](https://user-images.githubusercontent.com/10223677/215340692-898a03ef-2f66-46fd-92da-6e842d413500.png) ```yaml type: custom:formulaone-card card_type: countdown f1_font: true show_raceinfo: false countdown_type: - practice1 - practice2 - practice3 - qualifying - sprint - race ``` ![Screenshot 2025-04-29 095323](https://github.com/user-attachments/assets/600c239d-a20a-4cb7-997d-99a103b237e8) ## Icons The following icons can be altered. | Card type(s) | Key | Default value | | ----------------------------------- | ------------- | ----------------------------------- | | results | results | mdi:trophy | | results | qualifying | mdi:timer-outline | | results | sprint | mdi:flag-checkered | ## Standings The display options for the standings can be altered | Name | Type | Default | Description | | ------------------ | ------------- | ----------------------------------- | ------------------------------------------------ | | show_team | boolean | `true` | Hide or show the team name | | show_flag | boolean | `true` | Hide or show the country flag | | show_teamlogo | boolean | `true` | Hide or show the team logo | | hide_season_selector | boolean | `false` | Hide or show the season selector | ## Translations The following texts can be translated or altered. | Card type(s) | Key | Default value | | ----------------------------------- | ------------- | ----------------------------------- | | next_race, schedule | date | 'Date' | | next_race, countdown | practice1 | 'Practice 1' | | next_race, countdown | practice2 | 'Practice 2' | | next_race, countdown | practice3 | 'Practice 3' | | next_race, countdown | race | 'Race' | | schedule | round | 'Race' | | next_race, countdown | racename | 'Race name' | | next_race, countdown | circuitname | 'Circuit name' | | next_race, countdown, schedule | location' | 'Location' | | next_race, countdown | city | 'City' | | next_race, countdown | sprint | 'Sprint' | | next_race, countdown | qualifying | 'Qualifying' | | next_race, countdown | sprint_qualifying : 'Sprint Qualifying' | | next_race, countdown, schedule | endofseason | 'Season is over. See you next year!' | | constructor_standings | constructor | 'Constructor' | | constructor_standings, driver_standings, last_result | points | 'Pts' | | constructor_standings, driver_standings | wins | 'Wins' | | driver_standings, results | team | 'Team' | | driver_standings, last_result, results | driver | 'Driver' | | last_result | grid | 'Grid' | | last_result | status | 'Status' | | schedule | time | 'Time' | | results | raceheader | 'Race' | | results | seasonheader | 'Season' | | results, driver_standings, constructor_standings | selectseason | 'Select season' | | results | selectrace | 'Select race' | | results | noresults | 'Please select a race thats already been run' | | countdown | days | 'd' | | countdown | hours | 'h' | | countdown | minutes | 'm' | | countdown | seconds | 's' | | countdown | until | 'Until' | | constructor_standings, driver_standings | no_standings | 'No standings available yet' | Example: ```yaml type: custom:formulaone-card card_type: next_race title: Next Race date_locale: nl image_clickable: true translations: 'date' : 'Date' 'practice1' : 'Practice 1' 'practice2' : 'Practice 2' 'practice3' : 'Practice 3' 'race' : 'Race' 'racename' : 'Race name' 'circuitname' : 'Circuit name' 'location' : 'Location' 'racetime' : 'Race' 'sprint' : 'Sprint' 'qualifying' : 'Qualifying' 'endofseason' : 'Season is over. See you next year!!' ``` ## Result card status translation You can translate the status of the result and last_result card. But only the status column. It works the same way as the other translations. The possible values for the status column are: Here are all possible values for the status property with their default translation: ```yaml 'Finished' : 'Finished', '+1 Lap' : '+1 Lap', 'Engine' : 'Engine', '+2 Laps' : '+2 Laps', 'Accident' : 'Accident', 'Collision' : 'Collision', 'Gearbox' : 'Gearbox', 'Spun off' : 'Spun off', '+3 Laps' : '+3 Laps', 'Suspension' : 'Suspension', '+4 Laps' : '+4 Laps', 'Transmission' : 'Transmission', 'Electrical' : 'Electrical', 'Brakes' : 'Brakes', 'Withdrew' : 'Withdrew', '+5 Laps' : '+5 Laps', 'Clutch' : 'Clutch', 'Lapped' : 'Lapped', 'Retired' : 'Retired', 'Not classified' : 'Not classified', 'Fuel system' : 'Fuel system', '+6 Laps' : '+6 Laps', 'Disqualified' : 'Disqualified', 'Turbo' : 'Turbo', 'Hydraulics' : 'Hydraulics', 'Overheating' : 'Overheating', 'Ignition' : 'Ignition', 'Oil leak' : 'Oil leak', 'Throttle' : 'Throttle', 'Out of fuel' : 'Out of fuel' ``` ## Weather forecast For this feature to work you have to get an API key [here](https://www.visualcrossing.com/sign-up) or use [F1 sensor](https://github.com/Nicxe/f1_sensor) ```yaml show_weather: true weather_options: source: visualcrossing or f1sensor unit: metric api_key: [YOUR API KEY HERE] entity: [f1sensor entity] ``` ================================================ FILE: formulaone-card.js.LICENSE.txt ================================================ /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ /** * @license * Copyright 2019 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ /** * @license * Copyright 2020 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ /** * @license * Copyright 2021 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ /** * @license * Copyright 2022 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ ================================================ FILE: hacs.json ================================================ { "name": "Formula One Card", "filename": "formulaone-card.js", "render_readme": true, "content_in_root": true } ================================================ FILE: jest.config.js ================================================ module.exports = { transform: { '^.+\\.ts?$': ['ts-jest', { "compiler": "ttypescript" } ], '^.+\\.(js|jsx)$': [ 'babel-jest', { 'presets': ['@babel/preset-env'], "plugins": [ ["@babel/plugin-transform-runtime"] ] }] }, testEnvironment: 'jsdom', testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx)$', moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], setupFiles: [ "/tests/config.ts" ], collectCoverageFrom: ["**/src/**/*.{js,jsx,ts}", "!**/node_modules/**", "!**/vendor/**","!**/src/styles.ts","!**/src/fonts.ts", "!**/src/editor.ts"], transformIgnorePatterns: ["node_modules\/(?!(lit|lit-element|lit-html|@lit)\/)"] }; ================================================ FILE: package.json ================================================ { "name": "formulaone-card", "version": "1.14.6", "description": "Frontend card for Home Assistant to display Formula One data", "main": "index.js", "scripts": { "lint": "eslint src/**/*.ts", "dev": "webpack -c webpack.config.js", "build": "yarn lint && webpack -c webpack.config.js", "test": "jest", "coverage": "jest --coverage", "workflow": "jest --coverage --json --outputFile=/home/runner/work/formulaone-card/formulaone-card/jest.results.json" }, "repository": { "type": "git", "url": "git+https://github.com/marcokreeft87/formulaone-card.git" }, "keywords": [], "author": "", "license": "ISC", "bugs": { "url": "https://github.com/marcokreeft87/formulaone-card/issues" }, "homepage": "https://github.com/marcokreeft87/formulaone-card#readme", "devDependencies": { "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^5.59.8", "@typescript-eslint/parser": "^5.62.0", "codecov": "^3.8.3", "eslint": "^8.52.0", "home-assistant-js-websocket": "^9.1.0", "lit": "^3.0.2", "lit-element": "^3.3.3", "minify-html-literals-loader": "^1.1.1", "typescript": "^4.9.5", "webpack": "^5.89.0", "webpack-cli": "^5.1.4" }, "dependencies": { "@babel/plugin-transform-runtime": "^7.22.5", "@babel/preset-env": "^7.23.8", "@lit-labs/scoped-registry-mixin": "^1.0.1", "@marcokreeft/ha-editor-formbuilder": "^2024.9.1", "babel-jest": "^29.7.0", "compression-webpack-plugin": "^10.0.0", "custom-card-helpers": "^1.9.0", "isomorphic-fetch": "^3.0.0", "jest-environment-jsdom": "^29.6.2", "jest-fetch-mock": "^3.0.3", "jest-ts-auto-mock": "^2.1.0", "minify-html-literals-loader": "^1.1.1", "ts-auto-mock": "^3.6.4", "ts-jest": "^29.1.1", "ts-loader": "^9.5.1", "ttypescript": "^1.5.15", "yarn": "^1.22.21" } } ================================================ FILE: src/api/client-base.ts ================================================ import { LocalStorageItem } from "../types/formulaone-card-types"; import { ConstructorStanding, DriverStanding, Race } from "./f1-models"; export interface IClient { GetConstructorStandings(): Promise; GetConstructorStandingsForSeason(season: number | undefined) : Promise; GetSchedule(season: number) : Promise; GetLastResult() : Promise; GetDriverStandings() : Promise; GetDriverStandingsForSeason(season: number | undefined) : Promise; RefreshCache(): void; } export abstract class ClientBase { abstract baseUrl: string; async GetData(endpoint: string, cacheResult: boolean, hoursBeforeInvalid: number): Promise { const localStorageData = localStorage.getItem(endpoint); if (localStorageData && cacheResult) { const item: LocalStorageItem = JSON.parse(localStorageData); const checkDate = new Date(); checkDate.setHours(checkDate.getHours() - hoursBeforeInvalid); if (new Date(item.created) > checkDate) { return JSON.parse(item.data); } } const response = await fetch(`${this.baseUrl}/${endpoint}`, { headers: { Accept: 'application/json', } }); if (!response || !response.ok) { return Promise.reject(response); } const data = await response.json(); const item: LocalStorageItem = { data: JSON.stringify(data), created: new Date() } if (cacheResult) { localStorage.setItem(endpoint, JSON.stringify(item)); } return data; } } ================================================ FILE: src/api/ergast-client.ts ================================================ import { getRefreshTime } from '../utils'; import { ClientBase, IClient } from './client-base'; import { ConstructorStanding, DriverStanding, Race, RaceTable, Root, Season } from './f1-models'; export default class ErgastClient extends ClientBase implements IClient { baseUrl = 'https://api.jolpi.ca/ergast/f1'; async GetSchedule(season: number) : Promise { const data = await this.GetData(`${season}.json`, true, 72); return data.MRData.RaceTable.Races; } async GetLastResult() : Promise { const refreshCacheHours = getRefreshTime('current/last/results.json'); const data = await this.GetData('current/last/results.json', true, refreshCacheHours); return data.MRData.RaceTable.Races[0]; } async GetDriverStandings() : Promise { const refreshCacheHours = getRefreshTime('current/driverStandings.json'); const data = await this.GetData('current/driverStandings.json', true, refreshCacheHours); const standingsLists = data.MRData.StandingsTable.StandingsLists; return standingsLists && standingsLists.length > 0 ? standingsLists[0].DriverStandings : []; } async GetDriverStandingsForSeason(selectedSeason: number | undefined) { if (!selectedSeason || selectedSeason === 0) { return this.GetDriverStandings(); } const refreshCacheHours = getRefreshTime(`${selectedSeason}/driverStandings.json`); const data = await this.GetData(`${selectedSeason}/driverStandings.json`, true, refreshCacheHours); const standingsLists = data.MRData.StandingsTable.StandingsLists; return standingsLists && standingsLists.length > 0 ? standingsLists[0].DriverStandings : []; } async GetConstructorStandings() : Promise { const refreshCacheHours = getRefreshTime('current/constructorStandings.json'); const data = await this.GetData('current/constructorStandings.json', true, refreshCacheHours); const standingsLists = data.MRData.StandingsTable.StandingsLists; return standingsLists && standingsLists.length > 0 ? standingsLists[0].ConstructorStandings : []; } async GetConstructorStandingsForSeason(selectedSeason: number | undefined) { if (!selectedSeason || selectedSeason === 0) { return this.GetConstructorStandings(); } const refreshCacheHours = getRefreshTime(`${selectedSeason}/constructorStandings.json`); const data = await this.GetData(`${selectedSeason}/constructorStandings.json`, true, refreshCacheHours); const standingsLists = data.MRData.StandingsTable.StandingsLists; return standingsLists && standingsLists.length > 0 ? standingsLists[0].ConstructorStandings : []; } async GetSprintResults(season: number, round: number) : Promise { const data = await this.GetData(`${season}/${round}/sprint.json`, false, 0); return data.MRData.RaceTable; } async GetQualifyingResults(season: number, round: number) : Promise { const data = await this.GetData(`${season}/${round}/qualifying.json`, false, 0); return data.MRData.RaceTable; } async GetResults(season: number, round: number) : Promise { const data = await this.GetData(`${season}/${round}/results.json`, false, 0); return data.MRData.RaceTable; } async GetSeasons() : Promise { const data = await this.GetData('seasons.json?limit=200', true, 72); return data.MRData.SeasonTable.Seasons; } async GetSeasonRaces(season: number) : Promise { const data = await this.GetData(`${season}.json`, true, 72); return data.MRData.RaceTable.Races; } async GetLastYearsResults(circuitName: string) : Promise { // Get schedule of last year const lastYear = new Date().getFullYear() - 1; const data = await this.GetData(`${lastYear}.json`, true, 72); // Get the index of the circuit on the schedule of last year const raceRound = data.MRData.RaceTable.Races.findIndex((race: Race) => { return race.Circuit.circuitName === circuitName; }) + 1; // Get the results of the last year race const results = await this.GetData(`${lastYear}/${raceRound}/results.json`, false, 0); return results.MRData.RaceTable.Races[0]; } async RefreshCache() { await this.GetData('current.json', true, 0); await this.GetData('current/last/results.json', true, 0); await this.GetData('current/driverStandings.json', true, 0); await this.GetData('current/constructorStandings.json', true, 0); } } ================================================ FILE: src/api/f1-models.ts ================================================ export interface Root { MRData: Mrdata } export interface Mrdata { xmlns: string series: string url: string limit: string offset: string total: string RaceTable?: RaceTable SeasonTable?: SeasonTable StandingsTable?: StandingsTable } export interface StandingsTable { season: string StandingsLists: StandingsList[] } export interface StandingsList { season: string round: string ConstructorStandings: ConstructorStanding[] DriverStandings: DriverStanding[] } export interface DriverStanding { position: string positionText: string points: string wins: string Driver: Driver Constructors: Constructor[] } export interface ConstructorStanding { position: string positionText: string points: string wins: string Constructor: Constructor } export interface RaceTable { season: string round?: string Races?: Race[] } export interface Race { season: string round: string url: string raceName: string Circuit: Circuit date: string time: string Results?: Result[] QualifyingResults?: QualifyingResult[] SprintResults?: Result[] FirstPractice: FirstPractice SecondPractice: SecondPractice ThirdPractice?: ThirdPractice Qualifying?: Qualifying Sprint?: Sprint SprintQualifying?: Qualifying } export interface Circuit { circuitId: string url: string circuitName: string Location: Location } export interface Location { lat: string long: string locality: string country: string } export interface Result { number: string position: string positionText: string points: string Driver: Driver Constructor: Constructor grid: string laps: string status: string Time?: Time FastestLap?: FastestLap | undefined } export interface QualifyingResult { number: string; position: string; Driver: Driver; Constructor: Constructor; Q1: string; Q2: string; Q3: string; } export interface Driver { driverId: string permanentNumber: string code: string url: string givenName: string familyName: string dateOfBirth: string nationality: string } export interface Constructor { constructorId: string url: string name: string nationality: string } export interface Time { millis: string time: string } export interface FastestLap { rank: string lap: string Time: Time2 AverageSpeed: AverageSpeed } export interface Time2 { time: string } export interface AverageSpeed { units: string speed: string } export interface SeasonTable { Seasons: Season[] } export interface Season { season: string url: string } export interface FirstPractice { date: string time: string } export interface SecondPractice { date: string time: string } export interface ThirdPractice { date: string time: string } export interface Qualifying { date: string time: string } export interface Sprint { date: string time: string } ================================================ FILE: src/api/f1sensor-client.ts ================================================ import { HomeAssistant } from 'custom-card-helpers'; import { ClientBase, IClient } from './client-base'; import { ConstructorStanding, DriverStanding, Race, RaceTable, Season } from './f1-models'; import { IWeatherClient, WeatherData } from './weather-models'; import { WeatherOptions } from '../types/formulaone-card-types'; export default class F1SensorClient extends ClientBase implements IClient, IWeatherClient { baseUrl: string; hass: HomeAssistant; entity: string; constructor(hass: HomeAssistant, entity: string) { super(); this.hass = hass; this.entity = entity; } // eslint-disable-next-line @typescript-eslint/no-unused-vars async getRaceWeatherData(options: WeatherOptions, race: Race) : Promise { const attributes = this.hass.states[options.entity]?.attributes; if (!attributes) { throw new Error('Weather data not found for the specified entity.'); } return attributes as WeatherData; } async GetConstructorStandings() : Promise { return this.hass.states[this.entity]?.attributes?.constructor_standings ?? []; } async GetConstructorStandingsForSeason(season: number | undefined) : Promise { const data = this.hass.states[this.entity]?.attributes; if (season && season !== parseFloat(data?.season)) throw new Error('This entity is only valid for the current season. Please use source: jolpi for other seasons.'); return data?.constructor_standings ?? []; } async GetDriverStandings(): Promise { return this.hass.states[this.entity]?.attributes?.driver_standings ?? []; } async GetDriverStandingsForSeason(season: number | undefined): Promise { const data = this.hass.states[this.entity]?.attributes; if (season && season !== parseFloat(data?.season)) throw new Error('This entity is only valid for the current season. Please use source: jolpi for other seasons.'); return data?.driver_standings ?? []; } async GetSchedule(season: number): Promise { const data = this.hass.states[this.entity]?.attributes; if (season !== parseFloat(data?.season)) throw new Error('This entity is only valid for the current season. Please use source: jolpi for other seasons.'); return data?.races ?? []; } GetLastResult(): Promise { throw new Error('Method not implemented.'); } // eslint-disable-next-line @typescript-eslint/no-unused-vars GetSprintResults(season: number, round: number): Promise { throw new Error('Method not implemented.'); } // eslint-disable-next-line @typescript-eslint/no-unused-vars GetQualifyingResults(season: number, round: number): Promise { throw new Error('Method not implemented.'); } // eslint-disable-next-line @typescript-eslint/no-unused-vars GetResults(season: number, round: number): Promise { throw new Error('Method not implemented.'); } GetSeasons(): Promise { throw new Error('Method not implemented.'); } // eslint-disable-next-line @typescript-eslint/no-unused-vars GetSeasonRaces(season: number): Promise { throw new Error('Method not implemented.'); } RefreshCache(): void { throw new Error('Method not implemented.'); } } ================================================ FILE: src/api/image-client.ts ================================================ import { ImageConstants } from "../lib/constants"; import { LocalStorageItem } from "../types/formulaone-card-types"; import { getCircuitName, getCircuitNameLegacy } from "../utils"; import { Race } from "./f1-models"; export default class ImageClient { // Get image by url with fetch and save to local storage for 24 hours base64 encoded GetImage(url: string): string { // Check local storage for image const localStorageData = localStorage.getItem(url); if (localStorageData) { const item: LocalStorageItem = JSON.parse(localStorageData); const checkDate = new Date(); checkDate.setHours(checkDate.getHours() - (24 * 7 * 4)); if (new Date(item.created) > checkDate) { return item.data; } } fetch(url) .then(response => response.blob()) .then(imageBlob => { const reader = new FileReader(); reader.readAsDataURL(imageBlob); // istanbul ignore next reader.onloadend = function() { const base64data = reader.result; const item: LocalStorageItem = { data: base64data as string, created: new Date() } localStorage.setItem(url, JSON.stringify(item)); return item.data; } }); return url; } GetTeamLogoImage(teamName: string, selectedSeason: number): string { teamName = teamName.toLocaleLowerCase().replace('_', '-'); if (selectedSeason < 2026) { const exceptions = [{ teamName: 'red-bull', corrected: 'red-bull-racing'}, { teamName: 'alfa', corrected: 'alfa-romeo'}, { teamName: 'haas', corrected: 'haas-f1-team'}, { teamName: 'sauber', corrected: 'kick-sauber'}]; const exception = exceptions.filter(exception => exception.teamName == teamName); if(exception.length > 0) { teamName = exception[0].corrected; } return this.GetImage(`${ImageConstants.TeamLogoCDNLegacy}/2024/${teamName.toLowerCase()}-logo.png.transform/2col-retina/image.png`); } const exceptions = [{ teamName: 'red-bull', corrected: 'redbullracing'}, { teamName: 'rb', corrected: 'racingbulls'}, { teamName: 'haas', corrected: 'haasf1team'}, { teamName: 'aston-martin', corrected: 'astonmartin'}]; const exception = exceptions.filter(exception => exception.teamName == teamName); if(exception.length > 0) { teamName = exception[0].corrected; } return this.GetImage(`${ImageConstants.TeamLogoCDN}2026/${teamName.toLowerCase()}/2026${teamName.toLowerCase()}logo.webp`); } GetTrackLayoutImage(race: Race): string { const circuitName = getCircuitNameLegacy(race.Circuit.Location); const year = parseInt(race.season); if (year < 2026) { return this.GetImage(`${ImageConstants.F1CDNLegacy}/${circuitName}_Circuit`); } return this.GetImage(`${ImageConstants.F1CDN}${getCircuitName(race).toLowerCase()}detailed.webp`); } } ================================================ FILE: src/api/restcountry-client.ts ================================================ import { LocalStorageItem } from "../types/formulaone-card-types"; import { Country } from "../types/rest-country-types"; import { ClientBase } from "./client-base"; export default class RestCountryClient extends ClientBase { baseUrl = 'https://restcountries.com/v2'; allEndpoint = 'all?fields=name,flag,flags,nativeName,demonym,population,altSpellings'; async GetAll() : Promise { return await this.GetData(this.allEndpoint, true, 730); } GetCountriesFromLocalStorage() : Country[] { const localStorageData = localStorage.getItem(this.allEndpoint); if(localStorageData) { const item: LocalStorageItem = JSON.parse(localStorageData); return JSON.parse(item.data); } return []; } } ================================================ FILE: src/api/vc-weather-client.ts ================================================ import { WeatherOptions, WeatherUnit } from '../types/formulaone-card-types'; import { ClientBase } from './client-base'; import { Race } from './f1-models'; import { IWeatherClient, WeatherData, WeatherResponse } from './weather-models'; export default class VCWeatherClient extends ClientBase implements IWeatherClient { private readonly apiKey: string; private readonly unitGroup: string = 'metric'; baseUrl = 'https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline'; constructor(apiKey: string, unitGroup?: string) { super(); this.apiKey = apiKey; this.unitGroup = unitGroup ?? this.unitGroup; } async getRaceWeatherData(options: WeatherOptions, race: Race): Promise { const endpoint = `${race.Circuit.Location.lat}, ${ race.Circuit.Location.long}/${race.date}T${race.time}`; const contentType = 'json'; const url = `${endpoint}?unitGroup=${this.unitGroup}&key=${this.apiKey}&contentType=${contentType}`; const data = await this.GetData(url, true, 1); const day = data?.days[0] return { race_temperature: day.temp, race_temperature_unit: options.unit === WeatherUnit.Metric ? 'celsius' : 'fahrenheit', race_humidity: day.humidity, race_humidity_unit: '%', race_cloud_cover: day.cloudcover, race_cloud_cover_unit: '%', race_precipitation: day.precip, race_precipitation_unit : 'mm', race_wind_speed : day.windspeed, race_wind_speed_unit : 'm/s', race_wind_direction: this.calculateWindDirection(day.winddir), race_wind_from_direction_degrees: day.winddir, race_wind_from_direction_unit: 'degrees', race_feelslike: day.feelslike, race_feelslike_unit: options.unit === WeatherUnit.Metric ? 'celsius' : 'fahrenheit', race_precipitation_prob: day.precipprob, icon: '', friendly_name: 'visualcrossing', }; } private calculateWindDirection = (windDirection: number) => { const directions = [ { label: 'N', range: [0, 11.25] }, { label: 'NNE', range: [11.25, 33.75] }, { label: 'NE', range: [33.75, 56.25] }, { label: 'ENE', range: [56.25, 78.75] }, { label: 'E', range: [78.75, 101.25] }, { label: 'ESE', range: [101.25, 123.75] }, { label: 'SE', range: [123.75, 146.25] }, { label: 'SSE', range: [146.25, 168.75] }, { label: 'S', range: [168.75, 191.25] }, { label: 'SSW', range: [191.25, 213.75] }, { label: 'SW', range: [213.75, 236.25] }, { label: 'WSW', range: [236.25, 258.75] }, { label: 'W', range: [258.75, 281.25] }, { label: 'WNW', range: [281.25, 303.75] }, { label: 'NW', range: [303.75, 326.25] }, { label: 'NNW', range: [326.25, 348.75] }, { label: 'N', range: [348.75, 360] }, ]; for (const { label, range } of directions) { if (windDirection >= range[0] && windDirection <= range[1]) { return label; } } }; } ================================================ FILE: src/api/weather-models.ts ================================================ import { WeatherOptions } from "../types/formulaone-card-types"; import { Race } from "./f1-models"; export interface IWeatherClient { getRaceWeatherData(options: WeatherOptions, race: Race) : Promise; } export interface WeatherData { race_temperature: number; race_temperature_unit: string; race_feelslike: number; race_feelslike_unit: string; race_humidity: number; race_humidity_unit: string; race_cloud_cover: number; race_cloud_cover_unit: string; race_precipitation: number; race_precipitation_prob: number; race_precipitation_unit: string; race_wind_speed: number; race_wind_speed_unit: string; race_wind_direction: string; race_wind_from_direction_degrees: number; race_wind_from_direction_unit: string; icon: string; friendly_name: string; } export interface Hour { datetime: string; datetimeEpoch: number; temp: number; feelslike: number; humidity: number; dew: number; precip: number; precipprob: number; snow: number; snowdepth: number; preciptype: string[]; windgust: number; windspeed: number; winddir: number; pressure: number; visibility: number; cloudcover: number; solarradiation: number; solarenergy?: number; uvindex: number; severerisk: number; conditions: string; icon: string; stations: string[]; source: string; sunrise: string; sunriseEpoch?: number; sunset: string; sunsetEpoch?: number; moonphase?: number; } export interface Day { datetime: string; datetimeEpoch: number; tempmax: number; tempmin: number; temp: number; feelslikemax: number; feelslikemin: number; feelslike: number; dew: number; humidity: number; precip: number; precipprob: number; precipcover: number; preciptype: string[]; snow: number; snowdepth: number; windgust: number; windspeed: number; winddir: number; pressure: number; cloudcover: number; visibility: number; solarradiation: number; solarenergy: number; uvindex: number; severerisk: number; sunrise: string; sunriseEpoch: number; sunset: string; sunsetEpoch: number; moonphase: number; conditions: string; description: string; icon: string; stations: string[]; source: string; hours: Hour[]; } export interface CurrentConditions { datetime: string; datetimeEpoch: number; temp: number; feelslike: number; humidity: number; dew: number; precip: number; precipprob: number; snow: number; snowdepth: number; //preciptype?: any; windgust: number; windspeed: number; winddir: number; pressure: number; visibility: number; cloudcover: number; solarradiation: number; //solarenergy?: any; uvindex: number; severerisk: number; conditions: string; icon: string; //stations: any[]; source: string; sunrise: string; sunriseEpoch: number; sunset: string; sunsetEpoch: number; moonphase: number; } export interface WeatherResponse { queryCost: number; latitude: number; longitude: number; resolvedAddress: string; address: string; timezone: string; tzoffset: number; description: string; days: Day[]; //alerts: any[]; currentConditions: CurrentConditions; } ================================================ FILE: src/cards/base-card.ts ================================================ import { HomeAssistant } from "custom-card-helpers"; import { HTMLTemplateResult } from "lit-html"; import FormulaOneCard from ".."; import JolpiClient from "../api/ergast-client"; import { Race } from "../api/f1-models"; import ImageClient from "../api/image-client"; import VCWeatherClient from "../api/vc-weather-client"; import { CardProperties, F1DataSource, FormulaOneCardConfig, Translation } from "../types/formulaone-card-types"; import { IClient } from "../api/client-base"; import F1SensorClient from "../api/f1sensor-client"; import { IWeatherClient } from "../api/weather-models"; export abstract class BaseCard { parent: FormulaOneCard; config: FormulaOneCardConfig; client: IClient; resultsClient: JolpiClient; hass: HomeAssistant; weatherClient: IWeatherClient; imageClient: ImageClient; constructor(parent: FormulaOneCard) { this.config = parent.config; this.hass = parent._hass; this.client = this.config.source === F1DataSource.F1Sensor ? new F1SensorClient(this.hass, this.config.entity) : new JolpiClient(); this.resultsClient = new JolpiClient(); this.parent = parent; this.weatherClient = this.config.weather_options?.source ? new F1SensorClient(this.hass, this.config.entity) : new VCWeatherClient(this.config.weather_options?.api_key ?? ''); this.imageClient = new ImageClient(); } translation(key: string) : string { if(!this.config.translations || Object.keys(this.config.translations).indexOf(key) < 0) { return this.defaultTranslations[key]; } return this.config.translations[key]; } abstract render() : HTMLTemplateResult; abstract cardSize() : number; abstract defaultTranslations: Translation; protected getProperties() { const cardProperties = this.parent.properties?.get('cardValues') as CardProperties; const races = cardProperties?.races as Race[]; const selectedRace = cardProperties?.selectedRace as Race; const selectedSeason = cardProperties?.selectedSeason as number ?? new Date().getFullYear(); const selectedTabIndex = cardProperties?.selectedTabIndex as number ?? 0; return { races, selectedRace, selectedSeason, selectedTabIndex }; } protected getParentCardValues() { const cardValues = this.parent.properties ?? new Map(); const properties = cardValues.get('cardValues') as CardProperties ?? {} as CardProperties; properties.selectedSeason = properties.selectedSeason ?? new Date().getFullYear(); return { properties, cardValues }; } } ================================================ FILE: src/cards/constructor-standings.ts ================================================ import { html, HTMLTemplateResult } from "lit-html"; import { until } from 'lit-html/directives/until.js'; import FormulaOneCard from ".."; import { ConstructorStanding } from "../api/f1-models"; import { getApiErrorMessage, getApiLoadingMessage, getEndOfSeasonMessage, getTeamImage, reduceArray } from "../utils"; import { BaseCard } from "./base-card"; export default class ConstructorStandings extends BaseCard { defaultTranslations = { 'constructor' : 'Constructor', 'points' : 'Pts', 'wins' : 'Wins', 'no_standings' : 'No standings available yet', 'selectseason' : 'Select season' }; constructor(parent: FormulaOneCard) { super(parent); } cardSize(): number { return 11; } renderStandingRow(standing: ConstructorStanding, selectedSeason: number): HTMLTemplateResult { return html` ${standing.position} ${(this.config.standings?.show_teamlogo ? html` ` : '')} ${standing.Constructor.name} ${standing.points} ${standing.wins} `; } render() : HTMLTemplateResult { const { selectedSeason } = this.getProperties ? this.getProperties() : { selectedSeason: new Date().getFullYear() }; const selectedSeasonChanged = (ev: any): void => { this.setSeason(ev); }; return html` ${this.config.standings?.hide_season_selector ? '' : html`
Season
${until( this.resultsClient.GetSeasons().then((response: any[]) => { const seasons = response.reverse(); return html``; }).catch(() => html`${getApiErrorMessage('seasons')}`), html`${getApiLoadingMessage()}` )}
`} ${until( this.resultsClient.GetConstructorStandingsForSeason(selectedSeason).then((response: ConstructorStanding[]) => response.length === 0 ? html`${getEndOfSeasonMessage(this.translation('no_standings'))}` : html` ${reduceArray(response, this.config.row_limit).map(standing => this.renderStandingRow(standing, selectedSeason))}
  ${this.translation('constructor')} ${this.translation('points')} ${this.translation('wins')}
` ).catch(() => html`${getApiErrorMessage('standings')}`), html`${getApiLoadingMessage()}` )} `; } setSeason(ev: any) { const season = ev.target.value; const { properties, cardValues } = this.getParentCardValues(); properties.selectedSeason = season; cardValues.set('cardValues', properties); this.parent.properties = cardValues; } } ================================================ FILE: src/cards/countdown.ts ================================================ import { ActionHandlerEvent, formatDateTime, hasAction, HomeAssistant } from "custom-card-helpers"; import { html, HTMLTemplateResult } from "lit-html"; import { until } from 'lit-html/directives/until.js'; import { asyncReplace } from 'lit/directives/async-replace.js'; import FormulaOneCard from ".."; import { Race } from "../api/f1-models"; import { actionHandler } from "../directives/action-handler-directive"; import { CountdownType } from "../types/formulaone-card-types"; import { clickHandler, getApiErrorMessage, getApiLoadingMessage, getCountryFlagByName, getEndOfSeasonMessage, renderHeader, renderRaceInfo } from "../utils"; import { BaseCard } from "./base-card"; export default class Countdown extends BaseCard { hass: HomeAssistant; defaultTranslations = { 'days' : 'd', 'hours' : 'h', 'minutes' : 'm', 'seconds' : 's', 'endofseason' : 'Season is over. See you next year!', 'racenow' : 'We are racing!', 'date' : 'Date', 'practice1' : 'Practice 1', 'practice2' : 'Practice 2', 'practice3' : 'Practice 3', 'race' : 'Race', 'round' : 'Round', 'racename' : 'Race name', 'circuitname' : 'Circuit name', 'location' : 'Location', 'city': 'City', 'racetime' : 'Race', 'sprint' : 'Sprint', 'qualifying' : 'Qualifying', 'sprint_qualifying' : 'Sprint Qualifying', 'until' : 'Until' }; constructor(parent: FormulaOneCard) { super(parent); this.config.countdown_type = this.config.countdown_type ?? CountdownType.Race; } cardSize(): number { return this.config.show_raceinfo ? 12 : 6; } renderHeader(race: Race): HTMLTemplateResult { return this.config.show_raceinfo ? html` ${renderRaceInfo(this, race)}
${renderHeader(this, race)}
` : null; } async *countDownTillDate(raceDateTime: Date) { while (raceDateTime > new Date()) { const now = new Date().getTime(); const distance = raceDateTime.getTime() - now; const days = Math.floor(distance / (1000 * 60 * 60 * 24)); const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((distance % (1000 * 60)) / 1000); const countdown_format = this.config.countdown_format ?? 'd h m s'; let displayValue = ''; if (countdown_format.includes('d')) { displayValue += `${days}${this.translation('days')} `; } if (countdown_format.includes('h')) { displayValue += `${hours}${this.translation('hours')} `; } if (countdown_format.includes('m')) { displayValue += `${minutes}${this.translation('minutes')} `; } if (countdown_format.includes('s')) { displayValue += `${seconds}${this.translation('seconds')} `; } yield displayValue; /* istanbul ignore next */ await new Promise((r) => setTimeout(r, 1000)); } yield this.translation('racenow'); } render() : HTMLTemplateResult { const _handleAction = (ev: ActionHandlerEvent): void => { if (this.hass && this.config.actions && ev.detail.action) { clickHandler(this.parent, this.config, this.hass, ev); } }; return html`${until( this.client.GetSchedule(new Date().getFullYear()).then(response => { const { nextRace, raceDateTime, countdownType } = this.getNextEvent(response); if(!nextRace) { return getEndOfSeasonMessage(this.translation('endofseason')); } const timer = this.countDownTillDate(raceDateTime); const hasConfigAction = this.config.actions !== undefined; return html` ${( Array.isArray(this.config.countdown_type) && this.config.countdown_type.length > 1 ? html`` : null )}

   ${nextRace.round} : ${nextRace.raceName}

${asyncReplace(timer)}

${this.translation('until')} ${this.translation(countdownType.toLowerCase())}

${this.config.show_event_details ? formatDateTime(raceDateTime, this.hass.locale) : ''}

${this.renderHeader(nextRace)}`; }).catch(() => html`${getApiErrorMessage('next race')}`), html`${getApiLoadingMessage()}` )}`; } getNextEvent(response: Race[]) { const nextRace = response.filter(race => { const raceDateTime = new Date(race.date + 'T' + race.time); raceDateTime.setHours(raceDateTime.getHours() + 3); return raceDateTime >= new Date(); })[0]; let raceDateTime = null; let countdownType = this.config.countdown_type as CountdownType; if(nextRace) { const countdownTypes = this.config.countdown_type as CountdownType[]; const raceEvents = [ { Date: nextRace.FirstPractice ? new Date(nextRace.FirstPractice.date + 'T' + nextRace.FirstPractice.time) : null, Type: CountdownType.Practice1 }, { Date: nextRace.SecondPractice ? new Date(nextRace.SecondPractice.date + 'T' + nextRace.SecondPractice.time) : null, Type: CountdownType.Practice2 }, { Date: nextRace.ThirdPractice ? new Date(nextRace.ThirdPractice.date + 'T' + nextRace.ThirdPractice.time) : null, Type: CountdownType.Practice3 }, { Date: nextRace.Sprint ? new Date(nextRace.Sprint.date + 'T' + nextRace.Sprint.time) : null, Type: CountdownType.Sprint }, { Date: nextRace.SprintQualifying ? new Date(nextRace.SprintQualifying.date + 'T' + nextRace.SprintQualifying.time) : null, Type: CountdownType.SprintQualifying }, { Date: nextRace.Qualifying ? new Date(nextRace.Qualifying.date + 'T' + nextRace.Qualifying.time) : null, Type: CountdownType.Qualifying }, { Date: new Date(nextRace.date + 'T' + nextRace.time), Type: CountdownType.Race } ].filter(x => x.Date).filter(x => x.Date > new Date()).sort((a, b) => a.Date.getTime() - b.Date.getTime()); // Get the first countdown type that occurs in race events and get the date and time for that event const nextEvent = raceEvents.filter(x => countdownTypes?.includes(x.Type))[0]; raceDateTime = nextEvent?.Date; countdownType = nextEvent?.Type ?? countdownType; } return { nextRace, raceDateTime, countdownType }; } } ================================================ FILE: src/cards/driver-standings.ts ================================================ import { html, HTMLTemplateResult } from "lit-html"; import { until } from 'lit-html/directives/until.js'; import FormulaOneCard from ".."; import { DriverStanding } from "../api/f1-models"; import { getApiErrorMessage, getApiLoadingMessage, getCountryFlagByNationality, getDriverName, getEndOfSeasonMessage, reduceArray, renderConstructorColumn } from "../utils"; import { BaseCard } from "./base-card"; export default class DriverStandings extends BaseCard { defaultTranslations = { 'driver' : 'Driver', 'team' : 'Team', 'points' : 'Pts', 'wins' : 'Wins', 'no_standings' : 'No standings available yet', 'selectseason' : 'Select season' }; constructor(parent: FormulaOneCard) { super(parent); } cardSize(): number { return 12; } renderStandingRow(standing: DriverStanding, selectedSeason: number): HTMLTemplateResult { return html` ${standing.position} ${(this.config.standings?.show_flag ? html` ` : '')}${standing.Driver.code} ${getDriverName(standing.Driver, this.config)} ${(this.config.standings?.show_team ? html`${renderConstructorColumn(this, standing.Constructors[standing.Constructors.length - 1], selectedSeason)}` : '')} ${standing.points} ${standing.wins} `; } render() : HTMLTemplateResult { const { selectedSeason } = this.getProperties ? this.getProperties() : { selectedSeason: new Date().getFullYear() }; const selectedSeasonChanged = (ev: any): void => { this.setSeason(ev); }; return html` ${this.config.standings?.hide_season_selector ? '' : html`
Season
${until( this.resultsClient.GetSeasons().then((response: any[]) => { const seasons = response.reverse(); return this.config.standings?.hide_season_selector ? '' : html``; }).catch(() => html`${getApiErrorMessage('seasons')}`), html`${getApiLoadingMessage()}` )}
`} ${until( this.resultsClient.GetDriverStandingsForSeason(selectedSeason).then((response: DriverStanding[]) => response.length === 0 ? html`${getEndOfSeasonMessage(this.translation('no_standings'))}` : html` ${(this.config.standings?.show_team ? html`` : '')} ${reduceArray(response, this.config.row_limit).map(standing => this.renderStandingRow(standing, selectedSeason))}
  ${this.translation('driver')}${this.translation('team')}${this.translation('points')} ${this.translation('wins')}
` ).catch(() => html`${getApiErrorMessage('standings')}`), html`${getApiLoadingMessage()}` )} `; } setSeason(ev: any) { const season = ev.target.value; const { properties, cardValues } = this.getParentCardValues(); properties.selectedSeason = season; cardValues.set('cardValues', properties); this.parent.properties = cardValues; } } ================================================ FILE: src/cards/last-result.ts ================================================ import { html, HTMLTemplateResult } from "lit-html"; import { until } from 'lit-html/directives/until.js'; import FormulaOneCard from ".."; import { Result } from "../api/f1-models"; import { getApiErrorMessage, getApiLoadingMessage, getDriverName, getEndOfSeasonMessage, reduceArray, renderHeader, translateStatus } from "../utils"; import { BaseCard } from "./base-card"; export default class LastResult extends BaseCard { defaultTranslations = { 'driver' : 'Driver', 'grid' : 'Grid', 'points' : 'Points', 'status' : 'Status', 'no_result' : 'No result available yet', }; constructor(parent: FormulaOneCard) { super(parent); } cardSize(): number { return 11; } renderResultRow(result: Result): HTMLTemplateResult { return html` ${result.position} ${getDriverName(result.Driver, this.config)} ${result.grid} ${result.points} ${translateStatus(result.status, this.config)} `; } render() : HTMLTemplateResult { return html`${until( this.client.GetLastResult().then(response => !response ? html`${getEndOfSeasonMessage(this.translation('no_result'))}` : html`
${renderHeader(this, response)}
${reduceArray(response.Results, this.config.row_limit).map(result => this.renderResultRow(result))}
  ${this.translation('driver')} ${this.translation('grid')} ${this.translation('points')} ${this.translation('status')}
`) .catch(() => html`${getApiErrorMessage('last result')}`), html`${getApiLoadingMessage()}`, )}`; } } ================================================ FILE: src/cards/next-race.ts ================================================ import { HomeAssistant } from "custom-card-helpers"; import { html, HTMLTemplateResult } from "lit-html"; import { until } from 'lit-html/directives/until.js'; import { getApiErrorMessage, getApiLoadingMessage, getEndOfSeasonMessage, renderHeader, renderRaceInfo } from "../utils"; import { BaseCard } from "./base-card"; import { formatDateNumeric } from "../lib/format_date"; import { NextRaceDisplay } from "../types/formulaone-card-types"; import { Race } from "../api/f1-models"; export default class NextRace extends BaseCard { hass: HomeAssistant; defaultTranslations = { 'date' : 'Date', 'practice1' : 'Practice 1', 'practice2' : 'Practice 2', 'practice3' : 'Practice 3', 'race' : 'Race', 'round' : 'Round', 'racename' : 'Race name', 'circuitname' : 'Circuit name', 'location' : 'Location', 'city': 'City', 'racetime' : 'Race', 'sprint' : 'Sprint', 'qualifying' : 'Qualifying', 'sprint_qualifying' : 'Sprint Qualifying', 'endofseason' : 'Season is over. See you next year!', }; cardSize(): number { return 8; } render() : HTMLTemplateResult { return html`${until( this.client.GetSchedule(new Date().getFullYear()).then(response => { const delay = this.config.next_race_delay || 0; const nextRace = response.filter(race => { const nextRaceDate = new Date(race.date + 'T' + race.time); // Add the delay to the hours of the next race nextRaceDate.setHours(nextRaceDate.getHours() + delay); return nextRaceDate >= new Date(); })[0]; if(!nextRace) { return getEndOfSeasonMessage(this.translation('endofseason')); } return html` ${this.config.show_raceinfo ? renderRaceInfo(this, nextRace) : this.config.only_show_date ? html`` : null }
${renderHeader(this, nextRace)}

${this.renderDateTime(nextRace)}

` }).catch(() => { return html`${getApiErrorMessage('next race')}`; }), html`${getApiLoadingMessage()}` )}`; } private renderDateTime(nextRace: Race) { switch(this.config.next_race_display) { case NextRaceDisplay.DateOnly: return formatDateNumeric(new Date(nextRace.date + 'T' + nextRace.time), this.hass.locale, this.config.date_locale); case NextRaceDisplay.TimeOnly: return new Date(nextRace.date + 'T' + nextRace.time).toLocaleTimeString(this.hass.locale.language, { hour: '2-digit', minute: '2-digit' }); case NextRaceDisplay.DateAndTime: return formatDateNumeric(new Date(nextRace.date + 'T' + nextRace.time), this.hass.locale, this.config.date_locale) + ' ' + new Date(nextRace.date + 'T' + nextRace.time).toLocaleTimeString(this.hass.locale.language, { hour: '2-digit', minute: '2-digit' }); default: return formatDateNumeric(new Date(nextRace.date + 'T' + nextRace.time), this.hass.locale, this.config.date_locale); } return null; } } ================================================ FILE: src/cards/results.ts ================================================ import { html, HTMLTemplateResult } from "lit-html"; import { until } from 'lit-html/directives/until.js'; import FormulaOneCard from ".."; import { QualifyingResult, Race, Result, Season } from "../api/f1-models"; import { CustomIcons, F1DataSource, FormulaOneCardTab, mwcTabBarEvent, SelectChangeEvent } from "../types/formulaone-card-types"; import { getApiErrorMessage, getApiLoadingMessage, getCountryFlagByNationality, getDriverName, reduceArray, renderConstructorColumn, renderHeader, translateStatus } from "../utils"; import { BaseCard } from "./base-card"; export default class Results extends BaseCard { defaultTranslations = { 'driver' : 'Driver', 'grid' : 'Grid', 'team' : 'Team', 'points' : 'Points', 'status' : 'Status', 'raceheader' : 'Race', 'seasonheader' : 'Season', 'selectseason' : 'Select season', 'selectrace' : 'Select race', 'noresults' : 'Please select a race thats already been run.', 'q1' : 'Q1', 'q2' : 'Q2', 'q3' : 'Q3', 'finished' : 'Finished', 'retired' : 'Retired', 'disqualified' : 'Disqualified', 'notclassified' : 'Not classified' }; icons: CustomIcons = { 'sprint' : 'mdi:flag-checkered', 'qualifying' : 'mdi:timer-outline', 'results' : 'mdi:trophy', } constructor(parent: FormulaOneCard) { super(parent); if (this.config.source === F1DataSource.F1Sensor) throw new Error('F1Sensor is not supported for this card type. Please use source: jolpi.'); } cardSize(): number { return 12; } renderTabs(selectedRace: Race) : FormulaOneCardTab[] { const tabs: FormulaOneCardTab[] = [{ title: 'Results', icon: this.icon('results'), content: this.renderResults(selectedRace), order: this.tabOrder('results') }, { title: 'Qualifying', icon: this.icon('qualifying'), content: this.renderQualifying(selectedRace), order: this.tabOrder('qualifying') }, { title: 'Sprint', icon: this.icon('sprint'), content: this.renderSprint(selectedRace), hide: !selectedRace?.SprintResults, order: this.tabOrder('sprint') }]; return tabs.sort((a, b) => a.order - b.order); } renderSprint(selectedRace: Race) : HTMLTemplateResult { return selectedRace?.SprintResults ? html` ${(this.config.standings?.show_team ? html`` : '')} ${reduceArray(selectedRace.SprintResults, this.config.row_limit).map(result => this.renderResultRow(result, false, selectedRace.season))}
  ${this.translation('driver')}${this.translation('team')}${this.translation('grid')} ${this.translation('points')} ${this.translation('status')}
` : null; } renderQualifying(selectedRace: Race): HTMLTemplateResult { return selectedRace?.QualifyingResults ? html` ${(this.config.standings?.show_team ? html`` : '')} ${reduceArray(selectedRace.QualifyingResults, this.config.row_limit).map(result => this.renderQualifyingResultRow(result, selectedRace.season))}
  ${this.translation('driver')}${this.translation('team')}${this.translation('q1')} ${this.translation('q2')} ${this.translation('q3')}
` : null; } renderResults(selectedRace: Race): HTMLTemplateResult { const fastest = selectedRace?.Results?.filter((result) => result.FastestLap?.rank === '1')[0]; return selectedRace?.Results ? html` ${(this.config.standings?.show_team ? html`` : '')} ${reduceArray(selectedRace.Results, this.config.row_limit).map(result => this.renderResultRow(result, result.position === fastest?.position, selectedRace.season))} ${fastest ? html` ` : ''}
  ${this.translation('driver')}${this.translation('team')}${this.translation('grid')} ${this.translation('points')} ${this.translation('status')}
* Fastest lap: ${fastest.FastestLap.Time.time}
` : null; } renderResultRow(result: Result, fastest: boolean, selectedSeason: string): HTMLTemplateResult { return html` ${result.position} ${(this.config.standings?.show_flag ? html` ` : '')}${getDriverName(result.Driver, this.config)}${fastest ? ' *' : ''} ${(this.config.standings?.show_team ? html`${renderConstructorColumn(this, result.Constructor, parseInt(selectedSeason))}` : '')} ${result.grid} ${result.points} ${translateStatus(result.status, this.config)} `; } renderQualifyingResultRow(result: QualifyingResult, selectedSeason: string): HTMLTemplateResult { return html` ${result.position} ${(this.config.standings?.show_flag ? html` ` : '')}${getDriverName(result.Driver, this.config)} ${(this.config.standings?.show_team ? html`${renderConstructorColumn(this, result.Constructor, parseInt(selectedSeason))}` : '')} ${result.Q1} ${result.Q2} ${result.Q3} `; } renderHeader(race?: Race): HTMLTemplateResult { if(race === null || race === undefined || parseInt(race.season) < 2018) { return null; } return renderHeader(this, race); } render() : HTMLTemplateResult { const { races, selectedRace, selectedSeason, selectedTabIndex } = this.getProperties(); if(selectedSeason === new Date().getFullYear() && !selectedRace) { this.getLastResult(); } const selectedSeasonChanged = (ev: SelectChangeEvent): void => { this.setRaces(ev); } const selectedRaceChanged = (ev: SelectChangeEvent): void => { this.setSelectedRace(ev); } const tabs = this.renderTabs(selectedRace); return html`
${this.translation('seasonheader')}
${until( this.resultsClient.GetSeasons().then((response: Season[]) => { const seasons = response.reverse(); return html``; }).catch(() => { return html`${getApiErrorMessage('seasons')}`; }), html`${getApiLoadingMessage()}`, )}
${this.translation('raceheader')}
${this.renderTabsHtml(tabs, selectedTabIndex, selectedRace)}`; } renderTabsHtml = (tabs: FormulaOneCardTab[], selectedTabIndex: number, selectedRace?: Race): HTMLTemplateResult => { return selectedRace ? html` ${tabs.filter(tab => tab.content).length > 0 ? html`` : html``}
${this.renderHeader(selectedRace)}
(this.setSelectedTabIndex(ev.detail.index))} > ${tabs.filter(tab => !tab.hide).map( (tab) => html` `, )}
${tabs.filter(tab => !tab.hide).find((_, index) => index == selectedTabIndex).content}
${this.translation('noresults')}
` : html``; } setSelectedRace(ev: SelectChangeEvent) { const round = parseInt(ev.target.value); const { properties, cardValues } = this.getParentCardValues(); properties.selectedRound = round; const selectedSeason = properties.selectedSeason as number; Promise.all([this.resultsClient.GetResults(selectedSeason, round), this.resultsClient.GetQualifyingResults(selectedSeason, round), this.resultsClient.GetSprintResults(selectedSeason, round), this.resultsClient.GetSchedule(selectedSeason)]) .then(([results, qualifyingResults, sprintResults, schedule]) => { let race = results.Races[0]; /* istanbul ignore next */ if(race) { race.QualifyingResults = qualifyingResults.Races[0].QualifyingResults; /* istanbul ignore next */ race.SprintResults = sprintResults?.Races[0]?.SprintResults properties.selectedSeason = race.season; /* istanbul ignore next */ } else { /* istanbul ignore next */ race = schedule.filter((item: Race) => parseInt(item.round) == round)[0]; } properties.selectedRace = race; cardValues.set('cardValues', properties); this.parent.properties = cardValues; }); } private setRaces(ev: SelectChangeEvent) { const selectedSeason = ev.target.value; const { properties, cardValues } = this.getParentCardValues(); this.resultsClient.GetSeasonRaces(parseInt(selectedSeason)).then((response: Race[]) => { properties.selectedSeason = selectedSeason; properties.selectedRace = undefined; properties.races = response; cardValues.set('cardValues', properties); this.parent.properties = cardValues; }); } private getUpcomingRace(now: Date, races: Race[]) : Race { const nextRaces = races.filter(race => { const raceDateTime = new Date(race.date + 'T' + race.time); const qualifyingDateTime = new Date(race.Qualifying.date + 'T' + race.Qualifying.time); const sprintDateTime = race.Sprint ? new Date(race.Sprint.date + 'T' + race.Sprint.time) : null; if(raceDateTime >= now && (qualifyingDateTime < now && (sprintDateTime === null || sprintDateTime < now))) { return true; } return false; }); return nextRaces.length > 0 ? nextRaces[0] : null; } private getLastResult() { const now = new Date(); console.log('Getting last result, schedule and season'); Promise.all([this.client.GetSchedule(now.getFullYear()), this.client.GetLastResult()]) .then(([schedule, lastResult]) => { const upcomingRace = this.getUpcomingRace(now, schedule); let season : number = new Date().getFullYear(); let round : number = upcomingRace !== null ? parseInt(upcomingRace.round) : 0; let race = { } as Race; if(upcomingRace !== null) { race = upcomingRace; round = parseInt(race.round); season = parseInt(race.season); } else if(lastResult !== null) { race = lastResult; round = parseInt(lastResult.round); season = parseInt(lastResult.season); } Promise.all([this.resultsClient.GetQualifyingResults(season, round), this.resultsClient.GetSprintResults(season, round), this.resultsClient.GetSeasonRaces(season)]) .then(([qualifyingResults, sprintResults, seasonRaces]) => { const { properties, cardValues } = this.getParentCardValues(); race.QualifyingResults = qualifyingResults.Races[0].QualifyingResults; race.SprintResults = sprintResults.Races[0]?.SprintResults; properties.races = seasonRaces; properties.selectedRace = race; properties.selectedSeason = season.toString(); console.log('Selected race: ' + race.raceName); cardValues.set('cardValues', properties); this.parent.properties = cardValues; }); }); } setSelectedTabIndex(index: number) { const { properties, cardValues } = this.getParentCardValues(); properties.selectedTabIndex = index; cardValues.set('cardValues', properties); this.parent.properties = cardValues; } icon(key: string) : string { if(!this.config.icons || Object.keys(this.config.icons).indexOf(key) < 0) { return this.icons[key]; } return this.config.icons[key]; } tabOrder(tab: string) : number { const tabsOrder = this.config.tabs_order?.map(tab => tab.toLowerCase()) ?? ['results', 'qualifying', 'sprint']; return tabsOrder.indexOf(tab); } } ================================================ FILE: src/cards/schedule.ts ================================================ import { formatTime, HomeAssistant } from "custom-card-helpers"; import { html, HTMLTemplateResult } from "lit-html"; import { until } from 'lit-html/directives/until.js'; import FormulaOneCard from ".."; import { Circuit, Race } from "../api/f1-models"; import { formatDate } from "../lib/format_date"; import { PreviousRaceDisplay } from "../types/formulaone-card-types"; import { getApiErrorMessage, getApiLoadingMessage, getCountryFlagByName, getEndOfSeasonMessage, reduceArray } from "../utils"; import { BaseCard } from "./base-card"; export default class Schedule extends BaseCard { hass: HomeAssistant; defaultTranslations = { 'date' : 'Date', 'round' : 'Race', 'time' : 'Time', 'location' : 'Location', 'endofseason' : 'Season is over. See you next year!' }; constructor(parent: FormulaOneCard) { super(parent); } cardSize(): number { return 12; } renderLocation(circuit: Circuit) { const locationConcatted = html`${(this.config.standings?.show_flag ? html` ` : '')}${circuit.Location.locality}, ${circuit.Location.country}`; return this.config.location_clickable ? html`${locationConcatted}` : locationConcatted; } renderScheduleRow(race: Race): HTMLTemplateResult { const raceDate = new Date(race.date + 'T' + race.time); const renderClass = this.config.previous_race && raceDate < new Date() ? this.config.previous_race : ''; return html` ${race.round} ${race.Circuit.circuitName} ${this.renderLocation(race.Circuit)} ${formatDate(raceDate, this.hass.locale, this.config.date_locale)} ${formatTime(raceDate, this.hass.locale)} `; } render() : HTMLTemplateResult { return html`${until( this.client.GetSchedule(new Date().getFullYear()).then(response => { const schedule = this.config.previous_race === PreviousRaceDisplay.Hide ? response.filter(race => { return new Date(race.date + 'T' + race.time) >= new Date(); }) : response; const next_race = schedule.filter(race => { return new Date(race.date + 'T' + race.time) >= new Date(); })[0]; if(!next_race) { return getEndOfSeasonMessage(this.translation('endofseason')); } return html` ${reduceArray(schedule, this.config.row_limit).map(race => this.renderScheduleRow(race))}
  ${this.translation('round')} ${this.translation('location')} ${this.translation('date')} ${this.translation('time')}
`; }).catch(() => html`${getApiErrorMessage('schedule')}`), html`${getApiLoadingMessage()}` )}`; } } ================================================ FILE: src/consts.ts ================================================ export const CARD_NAME = 'formulaone-card'; export const CARD_EDITOR_NAME = `${CARD_NAME}-editor`; ================================================ FILE: src/directives/action-handler-directive.ts ================================================ /* istanbul ignore file */ import { AttributePart, directive, Directive, DirectiveParameters } from 'lit/directive.js'; import { ActionHandlerDetail, ActionHandlerOptions } from 'custom-card-helpers/dist/types'; import { fireEvent } from 'custom-card-helpers'; import { ActionHandlerElement } from '../types/formulaone-card-types'; import { noChange } from 'lit'; const isTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.maxTouchPoints > 0; declare global { interface HASSDomEvents { action: ActionHandlerDetail; } } class ActionHandler extends HTMLElement implements ActionHandler { public holdTime = 500; // eslint-disable-next-line @typescript-eslint/no-explicit-any public ripple: any; protected timer?: number; protected held = false; private dblClickTimeout?: number; constructor() { super(); this.ripple = document.createElement('mwc-ripple'); } public connectedCallback(): void { Object.assign(this.style, { position: 'absolute', width: isTouch ? '100px' : '50px', height: isTouch ? '100px' : '50px', transform: 'translate(-50%, -50%)', pointerEvents: 'none', zIndex: '999', }); this.appendChild(this.ripple); this.ripple.primary = true; ['touchcancel', 'mouseout', 'mouseup', 'touchmove', 'mousewheel', 'wheel', 'scroll'].forEach((ev) => { document.addEventListener( ev, () => { clearTimeout(this.timer); this.stopAnimation(); this.timer = undefined; }, { passive: true }, ); }); } public bind(element: ActionHandlerElement, options: ActionHandlerOptions): void { if (element.actionHandler) { return; } element.actionHandler = true; element.addEventListener('contextmenu', (ev: Event) => { const e = ev || window.event; if (e.preventDefault) { e.preventDefault(); } if (e.stopPropagation) { e.stopPropagation(); } e.cancelBubble = true; e.returnValue = false; return false; }); const start = (ev: Event): void => { this.held = false; let x: number; let y: number; if ((ev as TouchEvent).touches) { x = (ev as TouchEvent).touches[0].pageX; y = (ev as TouchEvent).touches[0].pageY; } else { x = (ev as MouseEvent).pageX; y = (ev as MouseEvent).pageY; } this.timer = window.setTimeout(() => { this.startAnimation(x, y); this.held = true; }, this.holdTime); }; const end = (ev: Event): void => { // Prevent mouse event if touch event ev.preventDefault(); if (['touchend', 'touchcancel'].includes(ev.type) && this.timer === undefined) { return; } clearTimeout(this.timer); this.stopAnimation(); this.timer = undefined; if (this.held) { fireEvent(element, 'action', { action: 'hold' }); } else if (options.hasDoubleClick) { if ((ev.type === 'click' && (ev as MouseEvent).detail < 2) || !this.dblClickTimeout) { this.dblClickTimeout = window.setTimeout(() => { this.dblClickTimeout = undefined; fireEvent(element, 'action', { action: 'tap' }); }, 250); } else { clearTimeout(this.dblClickTimeout); this.dblClickTimeout = undefined; fireEvent(element, 'action', { action: 'double_tap' }); } } else { fireEvent(element, 'action', { action: 'tap' }); } }; const handleEnter = (ev: KeyboardEvent): void => { if (ev.keyCode !== 13) { return; } end(ev); }; element.addEventListener('touchstart', start, { passive: true }); element.addEventListener('touchend', end); element.addEventListener('touchcancel', end); element.addEventListener('mousedown', start, { passive: true }); element.addEventListener('click', end); element.addEventListener('keyup', handleEnter); } private startAnimation(x: number, y: number): void { Object.assign(this.style, { left: `${x}px`, top: `${y}px`, display: null, }); this.ripple.disabled = false; this.ripple.active = true; this.ripple.unbounded = true; } private stopAnimation(): void { this.ripple.active = false; this.ripple.disabled = true; this.style.display = 'none'; } } // TODO You need to replace all instances of "action-handler-boilerplate" with "action-handler-" customElements.define('action-handler-formulaonecard', ActionHandler); const getActionHandler = (): ActionHandler => { const body = document.body; if (body.querySelector('action-handler-formulaonecard')) { return body.querySelector('action-handler-formulaonecard') as ActionHandler; } const actionhandler = document.createElement('action-handler-formulaonecard'); body.appendChild(actionhandler); return actionhandler as ActionHandler; }; export const actionHandlerBind = (element: ActionHandlerElement, options?: ActionHandlerOptions): void => { const actionhandler: ActionHandler = getActionHandler(); if (!actionhandler) { return; } actionhandler.bind(element, options); }; export const actionHandler = directive( class extends Directive { update(part: AttributePart, [options]: DirectiveParameters) { actionHandlerBind(part.element as ActionHandlerElement, options); return noChange; } // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars render(_options?: ActionHandlerOptions) {} }, ); ================================================ FILE: src/editor.ts ================================================ import EditorForm from '@marcokreeft/ha-editor-formbuilder'; import { FormControlType } from "@marcokreeft/ha-editor-formbuilder/dist/interfaces"; import { getDropdownOptionsFromEnum } from "@marcokreeft/ha-editor-formbuilder/dist/utils/entities"; import { css, CSSResult } from "lit"; import { html, TemplateResult } from "lit-html"; import { customElement } from 'lit/decorators.js'; import { CARD_EDITOR_NAME } from "./consts"; import { CountdownType, FormulaOneCardType, PreviousRaceDisplay, WeatherUnit } from "./types/formulaone-card-types"; @customElement(CARD_EDITOR_NAME) export class FormulaOneCardEditor extends EditorForm { protected render(): TemplateResult { if (!this._hass || !this._config) { return html``; } return this.renderForm([ { controls: [{ label: "Card Type (Required)", configValue: "card_type", type: FormControlType.Dropdown, items: getDropdownOptionsFromEnum(FormulaOneCardType) }] }, { controls: [{ label: "Title", configValue: "title", type: FormControlType.Textbox }] }, { label: "Basic configuration", cssClass: 'side-by-side', controls: [ { label: "Use F1 font", configValue: "f1_font", type: FormControlType.Switch }, { label: "Image clickable", configValue: "image_clickable", type: FormControlType.Switch }, { label: "Show carnumber", configValue: "show_carnumber", type: FormControlType.Switch }, { label: "Location clickable", configValue: "location_clickable", type: FormControlType.Switch }, { label: "Show race information", configValue: "show_raceinfo", type: FormControlType.Switch }, { label: "Hide track layout", configValue: "hide_tracklayout", type: FormControlType.Switch }, { label: "Hide race dates and times", configValue: "hide_racedatetimes", type: FormControlType.Switch }, { label: "Show last years result", configValue: "show_lastyears_result", type: FormControlType.Switch }, { label: "Only show date", configValue: "only_show_date", type: FormControlType.Switch }, { type: FormControlType.Filler }, { label: "Row limit", configValue: "row_limit", type: FormControlType.Textbox }, { label: "Date locale", configValue: "date_locale", type: FormControlType.Textbox } ] }, { label: "Countdown Type", hidden: this._config.card_type !== FormulaOneCardType.Countdown, cssClass: 'side-by-side', controls: [{ configValue: "countdown_type", type: FormControlType.Checkboxes, items: getDropdownOptionsFromEnum(CountdownType) }] }, { hidden: this._config.card_type !== FormulaOneCardType.NextRace, controls: [ { label: "Next race delay", configValue: "next_race_delay", type: FormControlType.Textbox }, ] }, { hidden: this._config.card_type !== FormulaOneCardType.Schedule, controls: [{ label: "Previous race", configValue: "previous_race", type: FormControlType.Dropdown, items: getDropdownOptionsFromEnum(PreviousRaceDisplay) }] }, { label: "Standings", hidden: this._config.card_type !== FormulaOneCardType.ConstructorStandings && this._config.card_type !== FormulaOneCardType.DriverStandings, cssClass: 'side-by-side', controls: [ { label: "Show team", configValue: "standings.show_team", type: FormControlType.Switch }, { label: "Show flag", configValue: "standings.show_flag", type: FormControlType.Switch }, { label: "Show teamlogo", configValue: "standings.show_teamlogo", type: FormControlType.Switch } ] }, { label: "Weather", hidden: this._config.card_type !== FormulaOneCardType.NextRace && this._config.card_type !== FormulaOneCardType.Countdown, controls: [ { label: "Show weather", configValue: "show_weather", type: FormControlType.Switch } ] }, { cssClass: 'side-by-side', hidden: (this._config.card_type !== FormulaOneCardType.NextRace && this._config.card_type !== FormulaOneCardType.Countdown) || !this._config.show_weather, controls: [ { label: "API key", configValue: "weather_options.api_key", type: FormControlType.Textbox }, { label: "Unit", configValue: "weather_options.unit", type: FormControlType.Dropdown, items: getDropdownOptionsFromEnum(WeatherUnit) }, { label: "Show icon", configValue: "weather_options.show_icon", type: FormControlType.Switch }, { label: "Show precipitation", configValue: "weather_options.show_precipitation", type: FormControlType.Switch }, { label: "Show wind", configValue: "weather_options.show_wind", type: FormControlType.Switch }, { label: "Show temperature", configValue: "weather_options.show_temperature", type: FormControlType.Switch }, { label: "Show cloud coverage", configValue: "weather_options.show_cloud_cover", type: FormControlType.Switch }, { label: "Show visibility", configValue: "weather_options.show_visibility", type: FormControlType.Switch } ] }, { label: "Tabs", hidden: this._config.card_type !== FormulaOneCardType.Results, controls: [ { label: "Tabs order", configValue: "tabs_order", type: FormControlType.Textbox } ] }, ]); } static get styles() : CSSResult { return css` .form-row { margin-bottom: 10px; } .form-control { display: flex; align-items: center; } ha-switch { padding: 16px 6px; } .side-by-side { display: flex; flex-flow: row wrap; } .side-by-side > label { width: 100%; } .side-by-side > .form-control { width: 49%; padding: 2px; } ha-textfield { width: 100%; } .hidden { display: none; } @media (max-width: 600px) { .side-by-side > .form-control { width: 48%; } } `; } } ================================================ FILE: src/fonts.ts ================================================ export const loadCustomFonts = () => { if(window && document.fonts) { // Load the F1 font using the CSS Font Loading API const font = new FontFace("F1Bold", "url(https://www.formula1.com/etc/designs/fom-website/fonts/F1Bold/Formula1-Bold.woff)"); document.fonts.add(font); font.load(); } } ================================================ FILE: src/index.ts ================================================ import * as packageJson from '../package.json'; import { property, customElement } from 'lit/decorators.js'; import { HomeAssistant, LovelaceCardEditor } from 'custom-card-helpers'; import { FormulaOneCardConfig, FormulaOneCardType } from './types/formulaone-card-types'; import { CSSResult, html, HTMLTemplateResult, LitElement, PropertyValues } from 'lit'; import { checkConfig, hasConfigOrCardValuesChanged } from './utils'; import { loadCustomFonts } from './fonts'; import { styles } from './styles'; import ConstructorStandings from './cards/constructor-standings'; import DriverStandings from './cards/driver-standings'; import Schedule from './cards/schedule'; import NextRace from './cards/next-race'; import LastResult from './cards/last-result'; import { BaseCard } from './cards/base-card'; import Countdown from './cards/countdown'; import Results from './cards/results'; import RestCountryClient from './api/restcountry-client'; import { CARD_EDITOR_NAME, CARD_NAME } from './consts'; console.info( `%c ${CARD_NAME.toUpperCase()} %c ${packageJson.version}`, 'color: cyan; background: black; font-weight: bold;', 'color: darkblue; background: white; font-weight: bold;' ); /* eslint-disable @typescript-eslint/no-explicit-any */ (window as any).customCards = (window as any).customCards || []; (window as any).customCards.push({ type: 'formulaone-card', name: 'FormulaOne card', preview: false, description: 'Present the data of Formula One in a pretty way', }); /* eslint-enable @typescript-eslint/no-explicit-any */ @customElement(CARD_NAME) export default class FormulaOneCard extends LitElement { @property() _hass?: HomeAssistant; @property() config?: FormulaOneCardConfig; @property() card: BaseCard; @property() warning: string; @property() set properties(values: Map) { this._cardValues = values; this.update(values); } get properties() { return this._cardValues; } constructor() { super(); this.setCountryCache(); } private _cardValues?: Map; /* istanbul ignore next */ public static async getConfigElement(): Promise { await import("./editor"); return document.createElement(CARD_EDITOR_NAME) as LovelaceCardEditor; } setConfig(config: FormulaOneCardConfig) { checkConfig(config); this.config = { ...config }; } setCountryCache() { new RestCountryClient().GetAll().catch(() => { this.warning = 'Country API is down, so flags are not available at the moment!'; this.update(this._cardValues); }); } protected shouldUpdate(changedProps: PropertyValues): boolean { return hasConfigOrCardValuesChanged(this, changedProps); } set hass(hass: HomeAssistant) { this._hass = hass; this.config.hass = hass; switch(this.config.card_type) { case FormulaOneCardType.ConstructorStandings: this.card = new ConstructorStandings(this); break; case FormulaOneCardType.DriverStandings: this.card = new DriverStandings(this); break; case FormulaOneCardType.Schedule: this.card = new Schedule(this); break; case FormulaOneCardType.NextRace: this.card = new NextRace(this); break; case FormulaOneCardType.LastResult: this.card = new LastResult(this); break; case FormulaOneCardType.Countdown: this.card = new Countdown(this); break; case FormulaOneCardType.Results: this.card = new Results(this); break; } } static get styles(): CSSResult { loadCustomFonts(); return styles; } render() : HTMLTemplateResult { if (!this._hass || !this.config) return html``; try { return html` ${this.renderRefreshButton()} ${this.warning ? html`${this.warning}` : ''} ${this.config.title ? html`

${this.config.title}

` : ''} ${this.card.render()}
`; } catch (error) { return html`${error.toString()}`; } } getCardSize() { return this.card.cardSize(); } /* istanbul ignore next */ renderRefreshButton() { return this.config.show_refresh ? html`
this.refreshCache(e)}>
` : null; } // eslint-disable-next-line @typescript-eslint/no-unused-vars refreshCache(event: Event) { console.log('Refreshing cache...'); this.card.client.RefreshCache(); } } ================================================ FILE: src/lib/constants.ts ================================================ export const ImageConstants = { FlagCDN : 'https://flagcdn.com/w320/', TeamLogoCDNLegacy : 'https://www.formula1.com/content/dam/fom-website/teams/', TeamLogoCDN : 'https://media.formula1.com/image/upload/c_lfill,w_48/q_auto/v1740000000/common/f1/', F1CDNLegacy : 'https://media.formula1.com/image/upload/f_auto,c_limit,q_auto,w_1320/content/dam/fom-website/2018-redesign-assets/Circuit%20maps%2016x9', F1CDN: 'https://media.formula1.com/image/upload/c_fit,h_704/q_auto/v1740000001/common/f1/2026/track/2026track' }; export const TIMESTAMP_FORMATS = ['relative', 'total', 'date', 'time', 'datetime']; export const SECONDARY_INFO_VALUES = [ 'entity-id', 'last-changed', 'last-updated', 'last-triggered', 'position', 'tilt-position', 'brightness', ]; export const NumberFormat = { language: 'language', system: 'system', comma_decimal: 'comma_decimal', decimal_comma: 'decimal_comma', space_comma: 'space_comma', none: 'none', }; export const TimeFormat = { language: 'language', system: 'system', am_pm: '12', twenty_four: '24', }; ================================================ FILE: src/lib/format_date.ts ================================================ // Source: https://github.com/home-assistant/frontend/blob/dev/src/common/datetime/format_date.ts import { FrontendLocaleData } from 'custom-card-helpers'; export const formatDate = (dateObj: Date, locale: FrontendLocaleData, overrideLanguage?: string) => new Intl.DateTimeFormat(overrideLanguage ?? locale.language, { month: '2-digit', day: '2-digit', }).format(dateObj); export const formatDateNumeric = (dateObj: Date, locale: FrontendLocaleData, overrideLanguage?: string) => new Intl.DateTimeFormat(overrideLanguage ?? locale.language, { year: "2-digit", month: "2-digit", day: "2-digit", }).format(dateObj); ================================================ FILE: src/lib/format_date_time.ts ================================================ // Source: https://github.com/home-assistant/frontend/blob/dev/src/common/datetime/format_date_time.ts import { FrontendLocaleData } from 'custom-card-helpers'; import { useAmPm } from './use_am_pm'; export const formatDateTime = (dateObj: Date, locale: FrontendLocaleData) => new Intl.DateTimeFormat(locale.language, { year: 'numeric', month: 'long', day: 'numeric', hour: useAmPm(locale) ? 'numeric' : '2-digit', minute: '2-digit', hour12: useAmPm(locale), }).format(dateObj); export const formatDateTimeRaceInfo = (dateObj: Date, locale: FrontendLocaleData) => new Intl.DateTimeFormat(locale.language, { weekday: 'short', hour: '2-digit', minute: '2-digit', hour12: useAmPm(locale), }).format(dateObj); ================================================ FILE: src/lib/format_time.ts ================================================ // Source: https://github.com/home-assistant/frontend/blob/dev/src/common/datetime/format_time.ts import { FrontendLocaleData } from 'custom-card-helpers'; export const formatTime = (dateObj: Date, locale: FrontendLocaleData) => new Intl.DateTimeFormat(locale.language, { hour: '2-digit', minute: '2-digit', hour12: false, }).format(dateObj); ================================================ FILE: src/lib/use_am_pm.ts ================================================ // Source: https://github.com/home-assistant/frontend/blob/dev/src/common/datetime/use_am_pm.ts import { FrontendLocaleData } from 'custom-card-helpers'; import { TimeFormat } from './constants'; export const useAmPm = (locale: FrontendLocaleData) => { if (locale.time_format === TimeFormat.language || locale.time_format === TimeFormat.system) { const testLanguage = locale.time_format === TimeFormat.language ? locale.language : undefined; const test = new Date().toLocaleString(testLanguage); return test.includes('AM') || test.includes('PM'); } return locale.time_format === TimeFormat.am_pm; }; ================================================ FILE: src/styles.ts ================================================ import { css } from 'lit'; export const styles = css` table { width: 100%; border-spacing: 0; border-collapse: separate; padding: 0px 16px 16px; } table.nopadding { padding: 0px; width: 100%; border-spacing: 0; border-collapse: separate; } th { background-color: var(--table-row-alternative-background-color, #eee); } th, td { padding: 5px; text-align: left; } tr { color: var(--secondary-text-color); } tr:nth-child(even) { background-color: var(--table-row-alternative-background-color, #eee); } .text-center { text-align: center; } .width-40 { width: 40px; } .width-50 { width: 50px; } .width-60 { width: 60px; } .hide { display: none; } .strikethrough { text-decoration: line-through; } .italic { font-style: italic; } a { text-decoration: none; color: var(--secondary-text-color); } .constructor-logo { width: 20px; margin: auto; display: block; float: left; background-color: white; border-radius: 50%; margin-right: 3px; } .clickable { cursor: pointer; } .formulaone-font { font-family: 'F1Bold'; } ha-icon { color: var(--secondary-text-color); } .transparent { background-color: transparent !important; } .weather-info { padding: 10px; } .weather-info td { width: 33%; } .refresh-cache { position: absolute; right: 10px; top: 10px; } `; ================================================ FILE: src/types/formulaone-card-types.ts ================================================ import { ActionConfig, ActionHandlerOptions, HomeAssistant, LovelaceCardConfig } from 'custom-card-helpers'; import { HTMLTemplateResult } from 'lit'; export interface FormulaOneCardConfig extends LovelaceCardConfig { source: F1DataSource; entity?: string; show_icon?: boolean; title?: string; name?: string; hass?: HomeAssistant; card_type?: FormulaOneCardType; date_locale?: string; image_clickable?: boolean; show_carnumber?: boolean; location_clickable?: boolean; previous_race?: PreviousRaceDisplay; standings?: StandingDisplayOptions; translations?: Translation; show_raceinfo?: boolean; hide_tracklayout?: boolean; hide_racedatetimes?: boolean; actions?: ActionOptions; f1_font?: boolean; row_limit?: number; icons?: CustomIcons; countdown_type?: CountdownType | CountdownType[] | undefined; show_event_details?: boolean; show_weather?: boolean; weather_options?: WeatherOptions; next_race_delay?: number; show_lastyears_result?: boolean; only_show_date?: boolean; tabs_order?: string[]; show_refresh?: boolean; next_race_display?: NextRaceDisplay | undefined; countdown_format?: string; } export enum F1DataSource { Jolpi = 'jolpi', F1Sensor = 'f1sensor' } export interface ValueChangedEvent { detail: { value: { itemValue: string; parentElement: { configValue: string; }; } }; target: { value: string; configValue: string; checked?: boolean; }; } export interface WeatherOptions { source: WeatherSource; entity?: string; api_key?: string; unit?: WeatherUnit; show_icon?: boolean; show_precipitation?: boolean; show_wind?: boolean; show_temperature?: boolean; show_cloud_cover?: boolean; show_visibility?: boolean; } export enum WeatherSource { VisualCrossing = 'visualcrossing', F1Sensor = 'f1sensor', } export enum NextRaceDisplay { DateOnly = 'date', TimeOnly = 'time', DateAndTime = 'datetime' } export enum WeatherUnit { Metric = 'metric', MilesCelsius = 'uk', MilesFahrenheit = 'us' } export enum CountdownType { Race = "race", Qualifying = "qualifying", Practice1 = "practice1", Practice2 = "practice2", Practice3 = "practice3", Sprint = "sprint", SprintQualifying = "sprint_qualifying" } export interface ActionOptions { tap_action?: ActionConfig; hold_action?: ActionConfig; double_tap_action?: ActionConfig; } export interface Translation { [key: string]: string; } export interface CustomIcons { [key: string]: string; } export interface StandingDisplayOptions { show_team?: boolean; show_flag?: boolean; show_teamlogo?: boolean; hide_season_selector?: boolean; } export enum PreviousRaceDisplay { Strikethrough = 'strikethrough', Italic = 'italic', Hide = 'hide' } export enum FormulaOneCardType { DriverStandings = 'driver_standings', ConstructorStandings = 'constructor_standings', NextRace = 'next_race', Schedule = 'schedule', LastResult = 'last_result', Results = 'results', Countdown = 'countdown' } export interface LocalStorageItem { data: string, created: Date } export interface CardProperties { [key: string]: unknown; } export interface ActionHandler extends HTMLElement { holdTime: number; bind(element: Element, options: ActionHandlerOptions): void; } export interface ActionHandlerElement extends HTMLElement { actionHandler?: boolean; } export interface FormulaOneCardTab { title: string icon: string content: HTMLTemplateResult, hide?: boolean, order?: number } export interface SelectChangeEvent { target: { value: string; } } export interface mwcTabBarEvent extends Event { detail: { index: number; }; } ================================================ FILE: src/types/rest-country-types.ts ================================================ export interface Flags { svg: string; png: string; } export interface Currency { code: string; name: string; symbol: string; } export interface Language { iso639_1: string; iso639_2: string; name: string; nativeName: string; } export interface Translations { br: string; pt: string; nl: string; hr: string; fa: string; de: string; es: string; fr: string; ja: string; it: string; hu: string; } export interface RegionalBloc { acronym: string; name: string; otherNames: string[]; otherAcronyms: string[]; } export interface Country { name: string; topLevelDomain: string[]; alpha2Code: string; alpha3Code: string; callingCodes: string[]; capital: string; altSpellings: string[]; subregion: string; region: string; population: number; latlng: number[]; demonym: string; area: number; timezones: string[]; borders: string[]; nativeName: string; numericCode: string; flags: Flags; currencies: Currency[]; languages: Language[]; translations: Translations; flag: string; regionalBlocs: RegionalBloc[]; cioc: string; independent: boolean; gini?: number; } ================================================ FILE: src/utils.ts ================================================ import { ActionHandlerEvent, handleAction, hasAction, HomeAssistant } from "custom-card-helpers"; import { html, HTMLTemplateResult, LitElement, PropertyValues } from "lit"; import { until } from 'lit-html/directives/until.js'; import FormulaOneCard from "."; import { Constructor, Driver, Location, Race, Root } from "./api/f1-models"; import RestCountryClient from "./api/restcountry-client"; import { WeatherData } from "./api/weather-models"; import { BaseCard } from "./cards/base-card"; import { actionHandler } from './directives/action-handler-directive'; import { ImageConstants } from "./lib/constants"; import { formatDateNumeric } from "./lib/format_date"; import { formatDateTimeRaceInfo } from "./lib/format_date_time"; import { FormulaOneCardConfig, FormulaOneCardType, LocalStorageItem, Translation } from "./types/formulaone-card-types"; export const hasConfigOrCardValuesChanged = (node: FormulaOneCard, changedProps: PropertyValues) => { if (changedProps.has('config')) { return true; } const card = changedProps.get('card') as BaseCard; if (card && card.parent) { return card.parent.properties !== node.properties; } const cardValues = changedProps.get('cardValues') as Map; if(cardValues) { return cardValues != node.properties; } return false; }; export const getCountries = () => { const countryClient = new RestCountryClient(); return countryClient.GetCountriesFromLocalStorage(); } export const getCountryFlagByNationality = (card: BaseCard, nationality: string) => { const countries = getCountries(); nationality = nationality.trim(); const exceptions = [{ demonym: 'Argentinian', corrected: 'Argentinean'}, { demonym: 'Argentine', corrected: 'Argentinean'}]; const exception = exceptions.filter(exception => exception.demonym == nationality); if(exception.length > 0) { nationality = exception[0].corrected; } const country = countries.filter(x => x.demonym == nationality); if(country.length > 1) { return card.imageClient.GetImage(country.sort((a, b) => (a.population > b.population) ? -1 : 1)[0].flags.png); } return card.imageClient.GetImage(country[0].flags.png); } export const getCountryFlagByName = (card: BaseCard, countryName: string) => { const countries = getCountries(); const country = countries.filter(x => x.name == countryName || x.nativeName == countryName || x.altSpellings?.includes(countryName))[0]; return card.imageClient.GetImage(country.flags.png); } export const checkConfig = (config: FormulaOneCardConfig) => { if (config.card_type === undefined) { throw new Error('Please define FormulaOne card type (card_type).'); } }; export const getTeamImage = (card: BaseCard, teamName: string, selectedSeason: number) => { return card.imageClient.GetTeamLogoImage(teamName, selectedSeason); } export const getCircuitName = (race: Race) => { const exceptions = [{ countryDashed: 'Spain', name: 'Catalunya'}, { countryDashed: 'Belgium', name: 'SpaFrancorchamps'}, { countryDashed: 'Hungary', name: 'Hungaroring'}, { countryDashed: 'Brazil', name: 'Interlagos'}, { countryDashed: 'USA', name: 'LasVegas'}, { countryDashed: 'USA', name: 'Miami'}, { countryDashed: 'UAE', name: 'YasMarina'}, { countryDashed: 'Singapore', name: 'singapore'}]; const exception = exceptions.filter(exception => exception.countryDashed == race.Circuit.Location.country); if(exception.length > 0) { if (exception.length > 1) { const circuitException = exception.filter(exception => exception.name.toLowerCase() == race.Circuit.Location.locality.toLowerCase()); if(circuitException.length > 0) { return circuitException[0].name; } } return exception[0].name; } return race.Circuit.Location.locality.replace(" ",""); } export const getCircuitNameLegacy = (location: Location) => { let circuitName = location.country.replace(" ","-") const exceptions = [{ countryDashed: 'UAE', name: 'Abu_Dhabi'}, { countryDashed: 'UK', name: 'Great_Britain'}, { countryDashed: 'Azerbaijan', name: 'Baku'}, { countryDashed: 'Saudi-Arabia', name: 'Saudi_Arabia'}]; const exception = exceptions.filter(exception => exception.countryDashed == circuitName); if(exception.length > 0) { circuitName = exception[0].name; } if((location.country == 'USA' || location.country == 'United States') && location.locality != 'Austin') { circuitName = location.locality.replace(" ","_"); } if(location.country == 'Italy' && location.locality == 'Imola') { circuitName = "Emilia_Romagna"; } return circuitName; } export const getDriverName = (driver: Driver, config: FormulaOneCardConfig) => { const permanentNumber = driver.code == 'VER' ? 1 : driver.permanentNumber; return `${driver.givenName} ${driver.familyName}${(config.show_carnumber ? ` #${permanentNumber}` : '')}`; } export const getApiErrorMessage = (dataType: string) => { return html`
Error getting ${dataType}
` } export const getApiLoadingMessage = () => { return html`
Loading...
` } export const getEndOfSeasonMessage = (message: string) => { return html`
${message}
`; } export const clickHandler = (node: LitElement, config: FormulaOneCardConfig, hass: HomeAssistant, ev: ActionHandlerEvent) => { handleAction(node, hass, config.actions, ev.detail.action); } export const renderHeader = (card: BaseCard, race: Race): HTMLTemplateResult => { const _handleAction = (ev: ActionHandlerEvent): void => { if (card.hass && card.config.actions && ev.detail.action && card.config.image_clickable) { clickHandler(card.parent, card.config, card.hass, ev); } } const hasConfigAction = card.config.image_clickable || card.config.actions !== undefined; const circuitUrl = race.Circuit.url; if(card.config.image_clickable && !card.config.actions) { card.config.actions = { tap_action: { action: 'url', url_path: circuitUrl } }; } const imageHtml = html``; const raceName = html`

  ${race.round} : ${race.raceName}

`; return html`${(card.config.card_type == FormulaOneCardType.Countdown ? html`` : raceName)} ${(card.config.hide_tracklayout ? html`` : imageHtml)}
`; } export const renderRaceInfo = (card: BaseCard, race: Race) => { const config = card.config; const hass = card.hass; const weatherPromise = config.show_weather ? card.weatherClient.getRaceWeatherData(card.config.weather_options, race) : Promise.resolve(null); const lastYearPromise = config.show_lastyears_result ? card.resultsClient.GetLastYearsResults(race.Circuit.circuitName) : Promise.resolve(null); const promises = Promise.all([weatherPromise, lastYearPromise]); return html`${until(promises.then(([weather, lastYearData]) => { const weatherInfo = renderWeatherInfo(weather); const lastYearsResult = renderLastYearsResults(config, lastYearData) if (config.hide_racedatetimes && (config.show_weather || config.show_lastyears_result)) return html`${weatherInfo}${lastYearsResult}`; const raceDate = new Date(race.date + 'T' + race.time); const freePractice1Datetime = race.FirstPractice !== undefined ? new Date(race.FirstPractice.date + 'T' + race.FirstPractice.time) : null; const freePractice2Datetime = race.SecondPractice !== undefined ? new Date(race.SecondPractice.date + 'T' + race.SecondPractice.time) : null; const freePractice3Datetime = race.ThirdPractice !== undefined ? new Date(race.ThirdPractice.date + 'T' + race.ThirdPractice.time) : null; const qualifyingDatetime = race.Qualifying !== undefined ? new Date(race.Qualifying.date + 'T' + race.Qualifying.time) : null; const sprintQualifyingDatetime = race.SprintQualifying !== undefined ? new Date(race.SprintQualifying.date + 'T' + race.SprintQualifying.time) : null; const sprintDatetime = race.Sprint !== undefined ? new Date(race.Sprint.date + 'T' + race.Sprint.time) : null; const freePractice1 = race.FirstPractice !== undefined ? formatDateTimeRaceInfo(freePractice1Datetime, hass.locale) : '-'; const freePractice2 = race.SecondPractice !== undefined ? formatDateTimeRaceInfo(freePractice2Datetime, hass.locale) : '-'; const freePractice3 = race.ThirdPractice !== undefined ? formatDateTimeRaceInfo(freePractice3Datetime, hass.locale) : '-'; const raceDateFormatted = formatDateTimeRaceInfo(raceDate, hass.locale); const qualifyingDate = formatDateTimeRaceInfo(qualifyingDatetime, hass.locale); const sprintDate = race.Sprint !== undefined ? formatDateTimeRaceInfo(sprintDatetime, hass.locale) : '-'; const sprintQualifyingDate = race.SprintQualifying !== undefined ? formatDateTimeRaceInfo(sprintQualifyingDatetime, hass.locale) : '-'; const events: { date: Date, name: string, value: string }[] = []; events.push({ date: freePractice1Datetime, name: card.translation('practice1'), value: freePractice1 }); events.push({ date: freePractice2Datetime, name: card.translation('practice2'), value: freePractice2 }); events.push({ date: freePractice3Datetime, name: card.translation('practice3'), value: freePractice3 }); events.push({ date: qualifyingDatetime, name: card.translation('qualifying'), value: qualifyingDate }); events.push({ date: sprintQualifyingDatetime, name: card.translation('sprint_qualifying'), value: sprintQualifyingDate }); events.push({ date: sprintDatetime, name: card.translation('sprint'), value: sprintDate }); events.push({ date: raceDate, name: card.translation('racetime'), value: raceDateFormatted }); const filteredEvents = events.filter(event => event.date !== null).sort((a, b) => a.date.getTime() - b.date.getTime()); return html`${lastYearsResult}${weatherInfo}${card.translation('date')}${formatDateNumeric(raceDate, hass.locale, config.date_locale)} ${renderEventColumn(0, 'name', filteredEvents)}${renderEventColumn(0, 'value', filteredEvents)} ${card.translation('round')}${race.round} ${renderEventColumn(1, 'name', filteredEvents)}${renderEventColumn(1, 'value', filteredEvents)} ${card.translation('racename')}${race.raceName} ${renderEventColumn(2, 'name', filteredEvents)}${renderEventColumn(2, 'value', filteredEvents)} ${card.translation('circuitname')}${race.Circuit.circuitName} ${renderEventColumn(3, 'name', filteredEvents)}${renderEventColumn(3, 'value', filteredEvents)} ${card.translation('location')}${race.Circuit.Location.country} ${renderEventColumn(4, 'name', filteredEvents)}${renderEventColumn(4, 'value', filteredEvents)} ${card.translation('city')}${race.Circuit.Location.locality} ${renderEventColumn(5, 'name', filteredEvents)}${renderEventColumn(5, 'value', filteredEvents)}`; }))}`; } export const renderEventColumn = (index: number, lookupKey: string, events: { date: Date, name: string, value: string }[]) => { if (events.length > index) { if(lookupKey === 'name') return events[index].name; if(lookupKey === 'value') return events[index].value; } return '-'; }; export const renderLastYearsResults = (config: FormulaOneCardConfig, raceData: Race) => { if(!raceData) { return html``; } const result = raceData.Results ? raceData.Results[0] : null; const fastest = raceData.Results?.filter((result) => result.FastestLap?.rank === '1')[0]; return html`

${new Date(raceData.date).getFullYear()}

${result?.Driver.givenName} ${result?.Driver.familyName} (${result?.Constructor.name})

${fastest?.Driver.givenName} ${fastest?.Driver.familyName} (${fastest?.FastestLap?.Time.time})

 `; } export const renderWeatherInfo = (weatherData: WeatherData) => { if(!weatherData) { return html``; } const tempUnit = weatherData.race_temperature_unit === 'fahrenheit' ? '°F' : '°C'; return html`
 ${weatherData.race_cloud_cover} ${weatherData.race_cloud_cover_unit}  ${weatherData.race_temperature} ${tempUnit}  ${weatherData.race_humidity} ${weatherData.race_humidity_unit}
 ${weatherData.race_wind_direction} ${weatherData.race_wind_speed} ${weatherData.race_wind_speed_unit}  ${weatherData.race_precipitation} ${weatherData.race_precipitation_unit} ${(weatherData.race_precipitation_prob ? html` ${weatherData.race_precipitation_prob} %` : html``)}
 `; } export const getRefreshTime = (endpoint: string) => { let refreshCacheHours = 24; const now = new Date(); const scheduleLocalStorage = localStorage.getItem(`${now.getFullYear()}.json`); if(scheduleLocalStorage) { const item: LocalStorageItem = JSON.parse(scheduleLocalStorage); const schedule = JSON.parse(item.data); const filteredRaces = schedule.MRData.RaceTable.Races.filter(race => new Date(race.date).toLocaleDateString == now.toLocaleDateString); if(filteredRaces.length > 0) { const todaysRace = filteredRaces[0]; const raceTime = new Date(todaysRace.date + 'T' + todaysRace.time); const lastResultLocalStorage = localStorage.getItem(endpoint); if(lastResultLocalStorage) { const resultItem: LocalStorageItem = JSON.parse(lastResultLocalStorage); if(new Date(resultItem.created) < raceTime) { refreshCacheHours = 1; } } } } return refreshCacheHours; } export const reduceArray = (array?: T[], number?: number) => { if(array === undefined) { return []; } return number ? array.slice(0, number) : array; } export const renderConstructorColumn = (card: BaseCard, constructor: Constructor, selectedSeason: number): HTMLTemplateResult => { return html`${(card.config.standings.show_teamlogo ? html` ` : '')}${constructor.name}`; } export const translateStatus = (status: string, config: FormulaOneCardConfig) => { const defaultTranslations: Translation = { 'Finished' : 'Finished', '+1 Lap' : '+1 Lap', 'Engine' : 'Engine', '+2 Laps' : '+2 Laps', 'Accident' : 'Accident', 'Collision' : 'Collision', 'Gearbox' : 'Gearbox', 'Spun off' : 'Spun off', '+3 Laps' : '+3 Laps', 'Suspension' : 'Suspension', '+4 Laps' : '+4 Laps', 'Transmission' : 'Transmission', 'Electrical' : 'Electrical', 'Brakes' : 'Brakes', 'Withdrew' : 'Withdrew', '+5 Laps' : '+5 Laps', 'Clutch' : 'Clutch', 'Lapped' : 'Lapped', 'Retired' : 'Retired', 'Not classified' : 'Not classified', 'Fuel system' : 'Fuel system', '+6 Laps' : '+6 Laps', 'Disqualified' : 'Disqualified', 'Turbo' : 'Turbo', 'Hydraulics' : 'Hydraulics', 'Overheating' : 'Overheating', 'Ignition' : 'Ignition', 'Oil leak' : 'Oil leak', 'Throttle' : 'Throttle', 'Out of fuel' : 'Out of fuel' }; if(!config.translations || Object.keys(config.translations).indexOf(status) < 0) { return defaultTranslations[status]; } return config.translations[status] } ================================================ FILE: test/configuration.yaml ================================================ homeassistant: time_zone: Europe/Amsterdam temperature_unit: C unit_system: metric default_config: lovelace: mode: storage dashboards: lovelace-yaml: mode: yaml title: yaml filename: test/lovelace.yaml ================================================ FILE: test/lovelace.yaml ================================================ views: - theme: Backend-selected title: f1 path: f1 icon: mdi:car-sports subview: false badges: [] cards: - type: custom:formulaone-card card_type: countdown show_raceinfo: true f1_font: true date_locale: en-US show_weather: true weather_options: api_key: R69TG493HYLZWLP3UKKJDTPB2 title: null show_lastyears_result: true hide_tracklayout: false show_refresh: false - type: custom:formulaone-card card_type: results date_locale: nl location_clickable: true f1_font: true - type: custom:formulaone-card card_type: schedule date_locale: nl previous_race: hide location_clickable: true - type: custom:formulaone-card card_type: constructor_standings standings: show_teamlogo: true - type: custom:formulaone-card card_type: driver_standings standings: show_flag: true show_team: true show_teamlogo: true ================================================ FILE: tests/api/ergast-client.test.ts ================================================ import ErgastClient from "../../src/api/ergast-client"; import LocalStorageMock from "../testdata/localStorageMock"; import fetchMock from "jest-fetch-mock"; // Models import { Mrdata } from "../../src/api/f1-models"; import { LocalStorageItem } from '../../src/types/formulaone-card-types'; // Importing test data import { MRData as scheduleData } from '../testdata/schedule.json' import { MRData as resultData } from '../testdata/results.json' import { MRData as driverStandingsData } from '../testdata/driverStandings.json' import { MRData as constructorStandingsData } from '../testdata/constructorStandings.json' import { MRData as seasonData } from '../testdata/seasons.json' import { MRData as qualifyingData } from '../testdata/qualifying.json' describe('Testing ergast client file', () => { const client = new ErgastClient(); const localStorageMock = new LocalStorageMock(); Object.defineProperty(window, 'localStorage', { value: localStorageMock }); beforeEach(() => { localStorageMock.clear(); }); test('Passing number to GetSchedule should return correct data', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const result = await client.GetSchedule(2022); // Assert expect(JSON.stringify(result)).toMatch(JSON.stringify(scheduleData.RaceTable.Races)); }), test('Calling GetLastResult should return correct data', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify({ MRData : resultData })); // Act const result = await client.GetLastResult(); // Assert expect(JSON.stringify(result)).toMatch(JSON.stringify(resultData.RaceTable.Races[0])); }), test('Calling GetDriverStandings should return correct data', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify({ MRData : driverStandingsData })); // Act const result = await client.GetDriverStandings(); // Assert expect(JSON.stringify(result)).toMatch(JSON.stringify(driverStandingsData.StandingsTable.StandingsLists[0].DriverStandings)); }), test('Calling GetConstructorStandings should return correct data', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify({ MRData : constructorStandingsData })); // Act const result = await client.GetConstructorStandings(); // Assert expect(JSON.stringify(result)).toMatch(JSON.stringify(constructorStandingsData.StandingsTable.StandingsLists[0].ConstructorStandings)); }), test('Calling GetResults should return correct data', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify({ MRData : resultData })); // Act const result = await client.GetResults(2022, 2); // Assert expect(JSON.stringify(result)).toMatch(JSON.stringify(resultData.RaceTable)); }), test('Calling GetSeasons should return correct data', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify({ MRData : seasonData })); // Act const result = await client.GetSeasons(); // Assert expect(JSON.stringify(result)).toMatch(JSON.stringify(seasonData.SeasonTable.Seasons)); }), test('Calling GetSeasonRaces should return correct data', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const result = await client.GetSeasonRaces(2022); // Assert expect(JSON.stringify(result)).toMatch(JSON.stringify(scheduleData.RaceTable.Races)); }), test('Calling GetQualifyingResults should return correct data', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify({ MRData : qualifyingData })); // Act const result = await client.GetQualifyingResults(2022, 2); // Assert expect(JSON.stringify(result)).toMatch(JSON.stringify(qualifyingData.RaceTable)); }), test('Calling GetData with data in localstorage and cacheResult true should return correct data', async () => { // Arrange localStorageMock.setItem('2022.json', JSON.stringify({ data: JSON.stringify({ MRData: scheduleData }), created: new Date() })); // Act const result = await client.GetData('2022.json', true, 24); // Assert expect(JSON.stringify(result)).toMatch(JSON.stringify(scheduleData)); }), test('Calling GetData without data in localstorage and cacheResult true should return correct data', async () => { // Arrange const endpoint = '2022.json'; fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const result = await client.GetData(endpoint, true, 24); const localStorageItem = JSON.parse(localStorageMock.getItem(endpoint)); // Assert expect(JSON.stringify(result)).toMatch(JSON.stringify(scheduleData)); expect(localStorageItem.data).toMatch(JSON.stringify(scheduleData)); }), test('Calling GetLastYearsResults without data in localstorage and cacheResult true should return correct data', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : resultData })); // Act const result = await client.GetLastYearsResults('Hungaroring'); // Assert expect(JSON.stringify(result)).toMatch(JSON.stringify(resultData.RaceTable.Races[0])); }) }); ================================================ FILE: tests/api/image-client.test.ts ================================================ // Mocks import fetchMock from "jest-fetch-mock"; import LocalStorageMock from "../testdata/localStorageMock"; // Models import ImageClient from "../../src/api/image-client"; const localStorageMock = new LocalStorageMock(); Object.defineProperty(window, 'localStorage', { value: localStorageMock }); beforeEach(() => { jest.spyOn(FileReader.prototype, 'readAsDataURL').mockImplementation(jest.fn()); }); describe('Testing image client file', () => { const client = new ImageClient(); test('Calling GetImage should return correct data', () => { // Arrange fetchMock.mockResponseOnce("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAACgCAMAAABKfUWuAAAAQlBMVEUBIWn5wcoyS4bM0uHsRGMAIGlUapr////kACtDW5DBydtmeaWzvdL7ztaAkLQQLnLw8vYgPHugrMeQnr3f4+zxc4uUeOcyAAAGhklEQVR42u2dXZejIAyGMzKYAVzEj/3/f3UvrK1trQJhbOzmvZgz03OqzlMCCSQpfH/hg5oKklQjIuLPgxARsU67UtUgIv75vl3l+wu46+eHirAUwDV8iCcASEVYBuA6vpMApCEsAfAVvtMApCCkA3yN70QA8xFSAW7hOwHAr28qQhrAbXwnAIhIRUgBuIfvFACpCPMB7uM7AUBjqQhzAcbgUwbg4xHmAfwYfHSEOQA/Ch8VYTrAj8NHQ5gK8CPxURCmAfxYfPkIUwC+HV/X8kMYD5DB6HOO3yiMBcjBeHXT8DPkOIA85r4WsWU3F8YAfDc+PV1bO8SLDRs+CPcBMhh99Th0ANAgNgDQDWPNZxTuAWQx9+kGUbkOEbFzasLIBeE2QC5+XxuWdwxHzISxCLcA8nGbdb28Zw0HKQaheg1QcYo6msWnziw6eQmQVdBW3W5blQS0KzeNo7/fV/39Ewtw+12j3797sTnQL+/sy82BWEivRiBVhWZAF+4vG5wATLFe+3xhWwnA+Dik9n6wV4rWDt7XRgAm+NF6+b8uXxOACeslIoaAiEWX/h/mKhkPI6Jzpd3o/wigR3SgHaIXgFnq0YEG7bAXgFmyblo8nBWAeXMg6ImgzIGZzgwU9V/+P4C/IwEoAAWgABSAAlAACkABKAAFoGyolpATgDSl5X0IwOeNaycAafuujQDMyl1w3WTBlyzW1kWdHMemdiy1lrCxD/D5Xcodl9oRdWbS1NPZnQOo+0IpSM+H+neJRn9SRuDzOy2rihCFGIYeEe0wFsohjME3vgY4ngthhYVzCGPwKbOVYGnsqRAuM7iag/DtpfieC6EqlwQci28/yfxECDtVKvUzHl9MmcPvI9R1gRmrHe6esW8PwRdXaPPrCHt6tsFjDmZ2EmYavthSr19GGELxKSvTjlPxxRcb/ibCuoTXURnj5me03pgKjsCXUu76CwhdX03xKw4AAFVPyH3W+ubINJCTv5CDL63gujxChcq1MCKO0LoRFXEUWkTsEdHCUfhSS/6NKouwQkS01x/UzHGFOIDL2cTIxZfedCIGYcLsfV//Qd+g76eq2PYofDltT8oiXFyMasDQYQ8AWg9pedQUfHmNd0oiLFnCZWZ3sq+Pwpfb+qkYwuWFyKV3N8OtjsKX33ysCMK2x1LxV/asSf0n8tvfxSBMDL8KFsFlAcwxI0oDxn2E2/ysfbiAsta9D2DeLERrAbqHcC90AIBufme3ePENAHPXQWoT2m2EKctwBW8Q3Y2gt0HeQhgRwhpE7PvSNXBpACmObIlG3K8RxsUiPUBfuIQrBSAplCrUCv4Vwoi3DlPtVv8ugDR85b6MYB1hzA7K5QDN2ncApOIr+XUYawhjtvPb2akGESn+yohCqkr4kdTLqKUN3hBaoUDQrZ+gKHnweT/YgBjs4L0Mw3QtmhkFGYVZBKsLwVCBFhx5S8iBzRg/EuA0AgVgrg3XiON4YDvLj9OAQ9sO79mD+AyAFQBANQgJkUgk4qGuEwYUz0p7CStpUkoYkCz4kh4gypRsUFItuECW6H9uwWLDRAsWG6aoOfrLOs7u9tX+zl7XNig7/+HbbZokhY27/eUmgMtXGrT6eJ0Hf4WIob+kR+jmvli+6gNikDVlU1Od0OjamwVPNty5qeOELCn7nt807GpwM0AHdVOqdujjdasTCtcmJyrgW9OWz2jELyTnNQlGvFYyL3SSjPhJYsA0IxYDphmxGDDRiMWAo9Wv8ZPs41h1dn0EWoniYtRuOIKSOrsvN2450qOkju0sH3aviayVpWTDeoeYPrwDJzvmVJmknxsWrItTEvwwnMh6OdrxyMe7994/tXxYjUes9Z5LUMcxc6I1xnnfPHJrvHfGsJn+3FTDe82cMIymFb3su7fcSGB0uKO7MPpuzpzo/MjuqOZpPeaWue0Q0U4H/5bjUc3TimJZPyG7p4M5HgmzazOyi9gXThfDs9b5yaq5gg/ZPaK7AeR31jrvCF47wzPcDfSct8rN9ZO9JngYZk94t+kxGpafrruEePzMpHv2Uzt+AN3sFzpmZrK6ZRlYbVbeuVYTQV6eQmuMu2JkFSNNUncjTsPA7UxOa627W/ITuwS4pyOknqEfYxBRKX7rG8ztlx8IcnNWtUNUbas4uoFm5QizZ/c5ewwdQBcYuoFd9IvvHIFNmCrUg6TB56m5BEeVAMwbgdXjLyKRSCQSiUQikUgkEolEIpFIlKJ/+z5WV3uERkEAAAAASUVORK5CYII="); // Act const image = client.GetImage('fakeurl'); // Assert expect(JSON.stringify(image)).toMatch(JSON.stringify(`fakeurl`)); }), test('Calling GetImage should return correct data with localstorage', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify('fakeurl')); localStorageMock.setItem(`fakeurl`, JSON.stringify({ data: 'fakedata', created: new Date() })); // Act const image = client.GetImage('fakeurl'); // Assert expect(JSON.stringify(image)).toMatch(JSON.stringify('fakedata')); }) }); ================================================ FILE: tests/api/restcountry-client.test.ts ================================================ // Mocks import fetchMock from "jest-fetch-mock"; import LocalStorageMock from "../testdata/localStorageMock"; // Models import RestCountryClient from "../../src/api/restcountry-client"; import { LocalStorageItem } from "../../src/types/formulaone-card-types"; // Importing test data import * as countryData from '../testdata/countries.json' describe('Testing restcountry client file', () => { const client = new RestCountryClient(); const localStorageMock = new LocalStorageMock(); Object.defineProperty(window, 'localStorage', { value: localStorageMock }); test('Calling GetCountriesFromLocalStorage should return correct data', () => { // Arrange localStorageMock.clear(); localStorageMock.setItem('all', JSON.stringify({ data: JSON.stringify(countryData), created: new Date() } as LocalStorageItem)); // Act const countries = client.GetCountriesFromLocalStorage(); // Assert expect(JSON.stringify(countries)).toMatch(JSON.stringify(countryData)); }), test('Calling GetCountriesFromLocalStorage should return correct data without localstorage', () => { // Arrange localStorageMock.clear(); // Act const countries = client.GetCountriesFromLocalStorage(); // Assert expect(JSON.stringify(countries)).toMatch(JSON.stringify([])); }), test('Calling GetAll should return correct data', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify(countryData)); // Act const result = await client.GetAll(); // Assert expect(JSON.stringify(result)).toMatch(JSON.stringify(countryData)); }) }) ================================================ FILE: tests/api/weather-client.test.ts ================================================ // Mocks import { createMock } from "ts-auto-mock"; import fetchMock from "jest-fetch-mock"; // Models import WeatherClient from "../../src/api/weather-client"; import { WeatherResponse } from "../../src/api/weather-models"; describe('Testing weather client file', () => { const client = new WeatherClient('fakekey', 'metric'); const weatherData = createMock(); test('Calling GetWeatherFromLocalStorage should return correct data without localstorage', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify(weatherData)); // Act const weather = await client.getWeatherData("1", "2", "2023-01-01"); // Assert expect(JSON.stringify(weather)).toMatch(JSON.stringify(weatherData)); }) }); ================================================ FILE: tests/cards/base-card.test.ts ================================================ // Mocks import { createMock } from "ts-auto-mock"; import FormulaOneCard from "../../src"; // Models import WeatherClient from "../../src/api/weather-client"; import { FormulaOneCardConfig } from "../../src/types/formulaone-card-types"; // Importing test data import ConstructorStandings from "../../src/cards/constructor-standings"; describe('Testing base-card file', () => { const parent = createMock({ config: createMock() }); test.each` key | expected ${'constructor'}, ${'Constructor'} ${'points'}, ${'Punten'} `('Calling translation should return correct translation', ({ key, expected }) => { // Arrange parent.config.translations = { "points" : "Punten" }; // Act const card = new ConstructorStandings(parent); // Assert expect(card.translation(key)).toBe(expected); }), test('Given config without weater_options when weatherOptions is called then default weatherOptions are returned', () => { // Arrange parent.config.weather_options = {}; // Act const card = new ConstructorStandings(parent); // Assert expect(card.weatherClient).toMatchObject(new WeatherClient('')); }), test('Given config without weater_options when weatherOptions is called then default weatherOptions are returned', () => { // Arrange parent.config.weather_options = { api_key: 'undefined' }; // Act const card = new ConstructorStandings(parent); // Assert expect(card.weatherClient).toMatchObject(new WeatherClient('undefined')); }) }); ================================================ FILE: tests/cards/constructor-standings.test.ts ================================================ // Mocks import { createMock } from 'ts-auto-mock'; import fetchMock from "jest-fetch-mock"; import LocalStorageMock from '../testdata/localStorageMock'; // Models import ConstructorStandings from '../../src/cards/constructor-standings'; import { getRenderString, getRenderStringAsync } from '../utils'; import { FormulaOneCardConfig } from '../../src/types/formulaone-card-types'; import { Mrdata } from '../../src/api/f1-models'; import { getApiErrorMessage } from '../../src/utils'; import FormulaOneCard from '../../src'; import ImageClient from '../../src/api/image-client'; // Importing test data import { MRData } from '../testdata/constructorStandings.json' describe('Testing constructor-standings file', () => { const parent = createMock({ config: createMock() }); const card = new ConstructorStandings(parent); const localStorageMock = new LocalStorageMock(); Object.defineProperty(window, 'localStorage', { value: localStorageMock }); beforeEach(() => { localStorageMock.clear(); fetchMock.resetMocks(); jest.spyOn(FileReader.prototype, 'readAsDataURL').mockImplementation(() => null); jest.spyOn(ImageClient.prototype, 'GetImage').mockImplementation((url: string) => { return url; }); }); test('Calling render with api returning data', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
  Constructor Pts Wins
1 Red Bull 576 13
2 Ferrari 439 4
3 Mercedes 373 0
4 McLaren 129 0
5 Alpine F1 Team 125 0
6 Alfa Romeo 52 0
7 Aston Martin 37 0
8 Haas F1 Team 34 0
9 AlphaTauri 34 0
10 Williams 6 0
'); }), test('Calling render with api and show_teamlogo returning data', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); card.config.standings = { show_teamlogo: true } // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
  Constructor Pts Wins
1  Red Bull 576 13
2  Ferrari 439 4
3  Mercedes 373 0
4  McLaren 129 0
5  Alpine F1 Team 125 0
6  Alfa Romeo 52 0
7  Aston Martin 37 0
8  Haas F1 Team 34 0
9  AlphaTauri 34 0
10  Williams 6 0
'); }), test('Calling render with api not returning data', async () => { // Arrange fetchMock.mockRejectOnce(new Error('Error')); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); const expectedResult = getRenderString(getApiErrorMessage('standings')); expect(htmlResult).toMatch(expectedResult); }), test('Calling cardSize with hass and sensor', () => { expect(card.cardSize()).toBe(11); }) }); ================================================ FILE: tests/cards/countdown.test.ts ================================================ // Mocks import { createMock } from "ts-auto-mock"; import fetchMock from "jest-fetch-mock"; import LocalStorageMock from '../testdata/localStorageMock'; // Models import Countdown from "../../src/cards/countdown"; import { CountdownType, FormulaOneCardConfig } from "../../src/types/formulaone-card-types"; import { getRenderStringAsync } from "../utils"; import { Mrdata, Race } from "../../src/api/f1-models"; import { HTMLTemplateResult } from "lit"; import { HomeAssistant, NumberFormat, TimeFormat } from "custom-card-helpers"; import FormulaOneCard from "../../src"; import * as customCardHelper from "custom-card-helpers"; import RestCountryClient from "../../src/api/restcountry-client"; import { Country } from "../../src/types/rest-country-types"; import ImageClient from "../../src/api/image-client"; // Importing test data import * as countries from '../testdata/countries.json' import { MRData as scheduleData } from '../testdata/schedule.json' describe('Testing countdown file', () => { const parent = createMock({ config: createMock(), }); const hass = createMock(); hass.locale = { language: 'NL', number_format: NumberFormat.comma_decimal, time_format: TimeFormat.language } parent._hass = hass; const config = createMock(); const card = new Countdown(parent); const race: Race = { season: '2022', round: '22', url: 'https://en.wikipedia.org/wiki/2022_Formula_One_World_Championship', raceName: 'Season is over. See you next year!', Circuit: { circuitId: 'bahrain', url: 'https://en.wikipedia.org/wiki/Bahrain_International_Circuit', circuitName: 'Bahrain International Circuit', Location: { lat: '26.0325', long: '50.5106', locality: 'Sakhir', country: 'Bahrain' } }, date: '2022-12-30', time: '13:00:00Z', FirstPractice: { date: '2022-12-30', time: '10:00:00Z' }, SecondPractice: { date: '2022-12-30', time: '14:00:00Z' }, ThirdPractice: { date: '2022-12-31', time: '11:00:00Z' }, Qualifying: { date: '2022-12-31', time: '14:00:00Z' } }; const localStorageMock = new LocalStorageMock(); Object.defineProperty(window, 'localStorage', { value: localStorageMock }); beforeEach(() => { localStorageMock.clear(); fetchMock.resetMocks(); jest.useFakeTimers(); jest.spyOn(FileReader.prototype, 'readAsDataURL').mockImplementation(() => null); jest.spyOn(ImageClient.prototype, 'GetImage').mockImplementation((url: string) => { return url; }); }); beforeAll(() => { jest.spyOn(RestCountryClient.prototype, 'GetCountriesFromLocalStorage').mockImplementation(() => { return countries as Country[]; }); }); afterEach (() => { jest.useRealTimers(); }); test('Calling render with date in future should render countdown', async () => { // Arrange card.config.countdown_type = CountdownType.Race; jest.setSystemTime(new Date(2022, 2, 1)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const { htmlResult, date } = await getHtmlResultAndDate(card); // Assert expect(htmlResult).toMatch('

   1 : Bahrain Grand Prix

'); expect(date.value).toMatch('19d 16h 0m 0s'); }), test('Calling render with date equal to race start should render we are racing', async () => { // Arrange jest.setSystemTime(new Date(2022, 2, 20, 16)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const { htmlResult, date } = await getHtmlResultAndDate(card); // Assert expect(htmlResult).toMatch('

   1 : Bahrain Grand Prix

'); expect(date.value).toMatch('We are racing!'); }), test('Calling render with date an hour past race start render we are racing', async () => { // Arrange jest.setSystemTime(new Date(2022, 2, 20, 17)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const { htmlResult, date } = await getHtmlResultAndDate(card); // Assert expect(htmlResult).toMatch('

   1 : Bahrain Grand Prix

'); expect(date.value).toMatch('We are racing!'); }), test('Calling render with date an day past race start render countdown till next race', async () => { // Arrange card.config.countdown_type = CountdownType.Race; jest.setSystemTime(new Date(2022, 2, 21)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const { htmlResult, date } = await getHtmlResultAndDate(card); // Assert expect(htmlResult).toMatch('

   2 : Saudi Arabian Grand Prix

'); expect(date.value).toMatch('6d 18h 0m 0s'); }), test('Calling render with date end of season', async () => { // Arrange jest.setSystemTime(new Date(2022, 11, 30)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
Season is over. See you next year!
Loading...
'); }), test('Calling render with api not returning data', async () => { // Arrange jest.setSystemTime(new Date(2022, 11, 30)); // Weird bug in jest setting this to the last of the month fetchMock.mockRejectOnce(new Error('API not available')); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
Error getting next race
'); }), test('Calling renderheader with date end of season and show_raceinfo = true', async () => { // Arrange config.show_raceinfo = true; card.config = config; // Act const result = card.renderHeader(race, new Date(2022, 11, 30)); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('

  22 : Season is over. See you next year!

'); }), test('Calling renderheader with date end of season and show_raceinfo undefined', async () => { // Arrange config.show_raceinfo = undefined; card.config = config; // Act const result = card.renderHeader(race, new Date(2022, 11, 30)); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toBe(''); }), test.each` show_raceinfo | expected ${undefined}, ${6} ${true}, ${12} ${false}, ${6} `('Calling getCardSize with type should return card size', ({ show_raceinfo, expected }) => { config.show_raceinfo = show_raceinfo; card.config = config; expect(card.cardSize()).toBe(expected); }), test('Calling render with actions', async () => { // Arrange const spy = jest.spyOn(customCardHelper, 'handleAction'); card.config.f1_font = true; card.config.countdown_type = undefined; card.config.actions = { tap_action: { action: 'navigate', navigation_path: '/lovelace/0', }, hold_action: { action: 'navigate', navigation_path: '/lovelace/1', }, double_tap_action: { action: 'navigate', navigation_path: '/lovelace/2', } } jest.setSystemTime(new Date(2022, 2, 1)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const { htmlResult, date, handleAction } = await getHtmlResultAndDate(card); // Assert expect(htmlResult).toMatch('

   1 : Bahrain Grand Prix

'); expect(date.value).toMatch('We are racing!'); // eslint-disable-next-line @typescript-eslint/ban-types handleAction({ detail: { action: 'tap' } }); handleAction({ detail: { action: 'double_tap' } }); handleAction({ detail: { action: 'hold' } }); expect(customCardHelper.handleAction).toBeCalledTimes(3); spy.mockClear(); }), test.each` countdown_type | current_date | expected ${CountdownType.Practice1}, ${new Date(2022, 3, 19)}, ${new Date("2022-04-22T11:30:00.000Z")} ${CountdownType.Practice2}, ${new Date(2022, 3, 19)}, ${new Date("2022-04-23T10:30:00.000Z")} ${CountdownType.Practice3}, ${new Date(2022, 2, 1)}, ${new Date("2022-03-19T12:00:00.000Z")} ${CountdownType.Qualifying}, ${new Date(2022, 3, 19)}, ${new Date("2022-04-22T15:00:00.000Z")} ${CountdownType.Race}, ${new Date(2022, 3, 19)}, ${new Date("2022-04-24T13:00:00.000Z")} ${CountdownType.Sprint}, ${new Date(2022, 3, 19, 11, 0)}, ${new Date("2022-04-23T14:30:00.000Z")} `(`Calling render with countdown_type $countdown_type`, async ({ countdown_type, current_date, expected }) => { // Arrange config.countdown_type = countdown_type; card.config = config; jest.setSystemTime(current_date); // Weird bug in jest setting this to the last of the month // Act const result = card.getNextEvent(scheduleData.RaceTable.Races); // Assert expect(result.raceDateTime).toMatchObject(expected); }), test.each` current_date | expected | hasSprint ${new Date(2022, 3, 22, 13, 0)}, ${new Date("2022-04-22T11:30:00.000Z")}, ${true} // Practice 1 ${new Date(2022, 3, 22, 16, 30)}, ${new Date("2022-04-22T15:00:00.000Z")}, ${true} // Qualifying ${new Date(2022, 3, 23, 12, 0)}, ${new Date("2022-04-23T10:30:00.000Z")}, ${true} // Practice 2 ${new Date(2022, 3, 23, 14, 20)}, ${new Date("2022-04-23T14:30:00.000Z")}, ${true} // Sprint ${new Date(2022, 3, 24, 10)}, ${new Date("2022-04-24T13:00:00.000Z")}, ${true} // Race ${new Date(2022, 3, 23, 14, 20)}, ${new Date("2022-04-24T13:00:00.000Z")}, ${false} // Race `(`Calling render with race, events in the future $current_date`, async ({ current_date, expected, hasSprint }) => { // Arrange jest.setSystemTime(current_date); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); config.countdown_type = [ CountdownType.Practice1, CountdownType.Practice2, CountdownType.Practice3, CountdownType.Qualifying, CountdownType.Sprint, CountdownType.Race ]; card.config = config; const races = [{ season: '2022', round: '1', url: 'https://en.wikipedia.org/wiki/2022_Bahrain_Grand_Prix', raceName: 'Bahrain Grand Prix', Circuit: { circuitId: 'bahrain', url: 'http://en.wikipedia.org/wiki/Bahrain_International_Circuit', circuitName: 'Bahrain International Circuit', Location: { lat: '26.0325', long: '50.5106', locality: 'Sakhir', country: 'Bahrain' } }, date: '2022-04-24', time: '13:00:00Z', FirstPractice: { date: '2022-04-22', time: '11:30:00Z' }, SecondPractice: { date: '2022-04-23', time: '10:30:00Z' }, Qualifying: { date: '2022-04-22', time: '15:00:00Z' }, Sprint: hasSprint ? { date: '2022-04-23', time: '14:30:00Z' } : undefined } as Race]; // Act const result = card.getNextEvent(races); // Assert expect(result.raceDateTime).toMatchObject(expected); }), test.each` current_date | expected | withFont ${new Date(2022, 3, 22, 13, 0)}, ${"0d 0h 30m 0s"}, ${true} // Practice 1 ${new Date(2022, 3, 22, 16, 30)}, ${"0d 0h 30m 0s"}, ${true} // Qualifying ${new Date(2022, 3, 23, 12, 0)}, ${"0d 0h 30m 0s"}, ${true} // Practice 2 ${new Date(2022, 3, 23, 14, 20)}, ${"0d 2h 10m 0s"}, ${true} // Sprint ${new Date(2022, 3, 24, 10)}, ${"0d 5h 0m 0s"}, ${true} // Race ${new Date(2022, 3, 23, 14, 20)}, ${"0d 2h 10m 0s"},${false}// Race `(`Calling render with race, events in the future $current_date`, async ({ current_date, expected, withFont }) => { // Arrange jest.setSystemTime(current_date); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); config.countdown_type = [ CountdownType.Practice1, CountdownType.Practice2, CountdownType.Practice3, CountdownType.Qualifying, CountdownType.Sprint, CountdownType.Race ]; config.f1_font = withFont; card.config = config; // Act const { htmlResult, date } = await getHtmlResultAndDate(card); // Assert expect(htmlResult).toMatch(`

   4 : Emilia Romagna Grand Prix

`); expect(date.value).toMatch(expected); }), test('Calling constructor without countdown_type should set countdown_type', () => { // Arrange const config = createMock(); config.countdown_type = undefined; const parent = createMock(); parent.config = config; // Act const card = new Countdown(parent); // Assert expect(card.config.countdown_type).toBe(CountdownType.Race); }), test('Calling constructor with countdown_type should not change countdown_type', () => { // Arrange const config = createMock(); config.countdown_type = CountdownType.Practice1; const parent = createMock(); parent.config = config; // Act const card = new Countdown(parent); // Assert expect(card.config.countdown_type).toBe(CountdownType.Practice1); }); }); async function getHtmlResultAndDate(card: Countdown) { const result = card.render(); const promise = (result.values[0] as HTMLTemplateResult).values[0] as Promise; const promiseResult = await promise; const iterator = (promiseResult.values[8] as HTMLTemplateResult).values[0] as AsyncIterableIterator; // eslint-disable-next-line @typescript-eslint/ban-types const handleAction = promiseResult.values[0] as Function; const date = await iterator.next(); const htmlResult = await getRenderStringAsync(promiseResult); return { htmlResult, date, handleAction }; } ================================================ FILE: tests/cards/driver-standings.test.ts ================================================ // Mocks import { createMock } from "ts-auto-mock"; import fetchMock from "jest-fetch-mock"; import LocalStorageMock from '../testdata/localStorageMock'; // Models import DriverStandings from '../../src/cards/driver-standings'; import { getRenderString, getRenderStringAsync } from '../utils'; import { MRData } from '../testdata/driverStandings.json' import { FormulaOneCardConfig } from '../../src/types/formulaone-card-types'; import { Mrdata } from '../../src/api/f1-models'; import { getApiErrorMessage } from '../../src/utils'; import FormulaOneCard from '../../src'; import RestCountryClient from '../../src/api/restcountry-client'; import { Country } from '../../src/types/rest-country-types'; // Importing test data import * as countries from '../testdata/countries.json' import ImageClient from "../../src/api/image-client"; beforeEach(() => { jest.spyOn(FileReader.prototype, 'readAsDataURL').mockImplementation(() => null); jest.spyOn(ImageClient.prototype, 'GetImage').mockImplementation((url: string) => { return url; }); }); describe('Testing driver-standings file', () => { const parent = createMock({ config: createMock() }); const card = new DriverStandings(parent); const localStorageMock = new LocalStorageMock(); Object.defineProperty(window, 'localStorage', { value: localStorageMock }); beforeEach(() => { localStorageMock.clear(); fetchMock.resetMocks(); }); beforeAll(() => { jest.spyOn(RestCountryClient.prototype, 'GetCountriesFromLocalStorage').mockImplementation(() => { return countries as Country[]; }); }); test('Calling render with api returning data', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
  Driver Pts Wins
1 VER Max Verstappen 341 11
2 LEC Charles Leclerc 237 3
3 PER Sergio Pérez 235 2
4 RUS George Russell 203 0
5 SAI Carlos Sainz 202 1
6 HAM Lewis Hamilton 170 0
7 NOR Lando Norris 100 0
8 OCO Esteban Ocon 66 0
9 ALO Fernando Alonso 59 0
10 BOT Valtteri Bottas 46 0
11 RIC Daniel Ricciardo 29 0
12 VET Sebastian Vettel 24 0
13 GAS Pierre Gasly 23 0
14 MAG Kevin Magnussen 22 0
15 STR Lance Stroll 13 0
16 MSC Mick Schumacher 12 0
17 TSU Yuki Tsunoda 11 0
18 ZHO Guanyu Zhou 6 0
19 ALB Alexander Albon 4 0
20 DEV Nyck de Vries 2 0
21 LAT Nicholas Latifi 0 0
22 HUL Nico Hülkenberg 0 0
'); }), test('Calling render with api returning data and showing flag and team', async () => { // Arrange const flagCard = new DriverStandings(parent); flagCard.config.standings = { show_flag: true, show_team: true } fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = flagCard.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
  Driver Team Pts Wins
1  VER Max Verstappen Red Bull 341 11
2  LEC Charles Leclerc Ferrari 237 3
3  PER Sergio Pérez Red Bull 235 2
4  RUS George Russell Mercedes 203 0
5  SAI Carlos Sainz Ferrari 202 1
6  HAM Lewis Hamilton Mercedes 170 0
7  NOR Lando Norris McLaren 100 0
8  OCO Esteban Ocon Alpine F1 Team 66 0
9  ALO Fernando Alonso Alpine F1 Team 59 0
10  BOT Valtteri Bottas Alfa Romeo 46 0
11  RIC Daniel Ricciardo McLaren 29 0
12  VET Sebastian Vettel Aston Martin 24 0
13  GAS Pierre Gasly AlphaTauri 23 0
14  MAG Kevin Magnussen Haas F1 Team 22 0
15  STR Lance Stroll Aston Martin 13 0
16  MSC Mick Schumacher Haas F1 Team 12 0
17  TSU Yuki Tsunoda AlphaTauri 11 0
18  ZHO Guanyu Zhou Alfa Romeo 6 0
19  ALB Alexander Albon Williams 4 0
20  DEV Nyck de Vries Williams 2 0
21  LAT Nicholas Latifi Williams 0 0
22  HUL Nico Hülkenberg Aston Martin 0 0
'); }), test('Calling render with api returning data and showing flag, team and teamlogo', async () => { // Arrange const flagCard = new DriverStandings(parent); flagCard.config.standings = { show_flag: true, show_team: true, show_teamlogo: true } fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = flagCard.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
  Driver Team Pts Wins
1  VER Max Verstappen  Red Bull 341 11
2  LEC Charles Leclerc  Ferrari 237 3
3  PER Sergio Pérez  Red Bull 235 2
4  RUS George Russell  Mercedes 203 0
5  SAI Carlos Sainz  Ferrari 202 1
6  HAM Lewis Hamilton  Mercedes 170 0
7  NOR Lando Norris  McLaren 100 0
8  OCO Esteban Ocon  Alpine F1 Team 66 0
9  ALO Fernando Alonso  Alpine F1 Team 59 0
10  BOT Valtteri Bottas  Alfa Romeo 46 0
11  RIC Daniel Ricciardo  McLaren 29 0
12  VET Sebastian Vettel  Aston Martin 24 0
13  GAS Pierre Gasly  AlphaTauri 23 0
14  MAG Kevin Magnussen  Haas F1 Team 22 0
15  STR Lance Stroll  Aston Martin 13 0
16  MSC Mick Schumacher  Haas F1 Team 12 0
17  TSU Yuki Tsunoda  AlphaTauri 11 0
18  ZHO Guanyu Zhou  Alfa Romeo 6 0
19  ALB Alexander Albon  Williams 4 0
20  DEV Nyck de Vries  Williams 2 0
21  LAT Nicholas Latifi  Williams 0 0
22  HUL Nico Hülkenberg  Aston Martin 0 0
'); }), test('Calling render with api not returning data', async () => { // Arrange fetchMock.mockRejectOnce(new Error('API is down')); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); const expectedResult = getRenderString(getApiErrorMessage('standings')); expect(htmlResult).toMatch(expectedResult); }), test('Calling cardSize with hass and sensor', () => { expect(card.cardSize()).toBe(12); }) }); ================================================ FILE: tests/cards/last-result.test.ts ================================================ // Mocks import { createMock } from 'ts-auto-mock'; import fetchMock from "jest-fetch-mock"; import LocalStorageMock from '../testdata/localStorageMock'; // Models import LastResult from '../../src/cards/last-result'; import { getRenderString, getRenderStringAsync } from '../utils'; import { MRData } from '../testdata/results.json' import { FormulaOneCardConfig } from '../../src/types/formulaone-card-types'; import { Mrdata } from '../../src/api/f1-models'; import { getApiErrorMessage } from '../../src/utils'; import FormulaOneCard from '../../src'; import RestCountryClient from '../../src/api/restcountry-client'; import { Country } from '../../src/types/rest-country-types'; import ImageClient from '../../src/api/image-client'; // Importing test data import * as countries from '../testdata/countries.json' beforeEach(() => { jest.spyOn(FileReader.prototype, 'readAsDataURL').mockImplementation(() => null); jest.spyOn(ImageClient.prototype, 'GetImage').mockImplementation((url: string) => { return url; }); }); describe('Testing last-result file', () => { const parent = createMock({ config: createMock(), }); const localStorageMock = new LocalStorageMock(); Object.defineProperty(window, 'localStorage', { value: localStorageMock }); beforeEach(() => { localStorageMock.clear(); fetchMock.resetMocks(); }); beforeAll(() => { jest.spyOn(RestCountryClient.prototype, 'GetCountriesFromLocalStorage').mockImplementation(() => { return countries as Country[]; }); }); test('Calling render with api returning data', async () => { // Arrange fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); const card = new LastResult(parent); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('

  17 : Singapore Grand Prix


  Driver Grid Points Status
1 Sergio Pérez 2 25 Finished
2 Charles Leclerc 1 18 Finished
3 Carlos Sainz 4 15 Finished
4 Lando Norris 6 12 Finished
5 Daniel Ricciardo 16 10 Finished
6 Lance Stroll 11 8 Finished
7 Max Verstappen 8 6 Finished
8 Sebastian Vettel 13 4 Finished
9 Lewis Hamilton 3 2 Finished
10 Pierre Gasly 7 1 Finished
11 Valtteri Bottas 15 0 Finished
12 Kevin Magnussen 9 0 Finished
13 Mick Schumacher 12 0 +1 Lap
14 George Russell 0 0 +2 Laps
15 Yuki Tsunoda 10 0 Accident
16 Esteban Ocon 17 0 Engine
17 Alexander Albon 18 0 Collision damage
18 Fernando Alonso 5 0 Engine
19 Nicholas Latifi 19 0 Collision damage
20 Guanyu Zhou 14 0 Collision
'); }), test('Calling render with api not returning data', async () => { // Arrange fetchMock.mockRejectOnce(new Error('API is down')); const card = new LastResult(parent); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); const expectedResult = getRenderString(getApiErrorMessage('last result')); expect(htmlResult).toMatch(expectedResult); }), test('Calling cardSize with hass and sensor', () => { const card = new LastResult(parent); expect(card.cardSize()).toBe(11); }) }); ================================================ FILE: tests/cards/next-race.test.ts ================================================ // Mocks import { createMock } from "ts-auto-mock"; import fetchMock from "jest-fetch-mock"; import LocalStorageMock from '../testdata/localStorageMock'; // Models import NextRace from '../../src/cards/next-race'; import { HomeAssistant, NumberFormat, TimeFormat } from 'custom-card-helpers'; import { getRenderString, getRenderStringAsync, getRenderStringAsyncIndex } from '../utils'; import { FormulaOneCardConfig, NextRaceDisplay } from '../../src/types/formulaone-card-types'; import { Mrdata } from '../../src/api/f1-models'; import { getApiErrorMessage, getEndOfSeasonMessage } from '../../src/utils'; import FormulaOneCard from '../../src'; import RestCountryClient from '../../src/api/restcountry-client'; import { Country } from '../../src/types/rest-country-types'; import ImageClient from "../../src/api/image-client"; // Importing test data import * as countries from '../testdata/countries.json' import { MRData } from '../testdata/schedule.json' describe('Testing next-race file', () => { const parent = createMock({ config: createMock(), }); const hass = createMock(); hass.locale = { language: 'NL', number_format: NumberFormat.comma_decimal, time_format: TimeFormat.language } parent._hass = hass; const localStorageMock = new LocalStorageMock(); Object.defineProperty(window, 'localStorage', { value: localStorageMock }); beforeEach(() => { localStorageMock.clear(); fetchMock.resetMocks(); jest.useFakeTimers(); jest.spyOn(FileReader.prototype, 'readAsDataURL').mockImplementation(() => null); jest.spyOn(ImageClient.prototype, 'GetImage').mockImplementation((url: string) => { return url; }); }); beforeAll(() => { jest.spyOn(RestCountryClient.prototype, 'GetCountriesFromLocalStorage').mockImplementation(() => { return countries as Country[]; }); }); afterEach(() => { jest.useRealTimers(); }); test('Calling render with api returning data', async () => { // Arrange const card = new NextRace(parent); jest.setSystemTime(new Date(2022, 2, 1)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toMatch('

  1 : Bahrain Grand Prix

  1 : Bahrain Grand Prix

  1 : Bahrain Grand Prix

  1 : Bahrain Grand Prix


  1 : Bahrain Grand Prix

  1 : Bahrain Grand Prix

  1 : Bahrain Grand Prix

  1 : Bahrain Grand Prix


'); }), test('Calling render with api not returning data', async () => { // Arrange const card = new NextRace(parent); fetchMock.mockResponseOnce('{}', { status: 500 }); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); const expectedResult = getRenderString(getApiErrorMessage('next race')); expect(htmlResult).toMatch(expectedResult); }), test('Calling render with api returning data', async () => { // Arrange const card = new NextRace(parent); jest.setSystemTime(new Date(2022, 11, 30)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); const expectedResult = getRenderString(getEndOfSeasonMessage('Season is over. See you next year!')); expect(htmlResult).toMatch(expectedResult); }), test('Calling render with api returning data sprint race', async () => { // Arrange const card = new NextRace(parent); jest.setSystemTime(new Date(2022, 3, 20)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toMatch('

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


'); }), test('Calling render with only_show_date true and api returning data sprint race', async () => { // Arrange const card = new NextRace(parent); card.config.only_show_date = true; jest.setSystemTime(new Date(2022, 3, 20)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toMatch('

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


24-04-22

24-04-22

'); }), test('Calling render with only_show_date true and f1 font and api returning data sprint race', async () => { // Arrange const card = new NextRace(parent); card.config.only_show_date = true; card.config.f1_font = true; jest.setSystemTime(new Date(2022, 3, 20)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toMatch('

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


24-04-22

24-04-22

'); }), test('Calling render with show_raceinfo true and f1 font and api returning data sprint race', async () => { // Arrange const card = new NextRace(parent); card.config.show_raceinfo = true; card.config.f1_font = true; jest.setSystemTime(new Date(2022, 3, 20)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toMatch('

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


Date24-04-22 Practice 1vr 13:30
Race4 Practice 2za 12:30
Race nameEmilia Romagna Grand Prix Practice 3-
Circuit nameAutodromo Enzo e Dino Ferrari Qualifyingvr 17:00
LocationItaly Sprintza 16:30
CityImola Racezo 15:00
'); }), test('Calling cardSize with hass and sensor without data', () => { const card = new NextRace(parent); expect(card.cardSize()).toBe(8); }), test('Calling render with next_race_display DateAndTime', async () => { // Arrange const card = new NextRace(parent); card.config.next_race_display = NextRaceDisplay.DateAndTime; card.config.show_raceinfo = true; card.config.f1_font = true; jest.setSystemTime(new Date(2022, 3, 20)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsyncIndex(result); console.log(htmlResult); expect(htmlResult).toMatch('

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


Date24-04-22 Practice 1vr 13:30
Race4 Practice 2za 12:30
Race nameEmilia Romagna Grand Prix Practice 3-
Circuit nameAutodromo Enzo e Dino Ferrari Qualifyingvr 17:00
LocationItaly Sprintza 16:30
CityImola Racezo 15:00
'); }), test('Calling render with next_race_display DateOnly', async () => { // Arrange const card = new NextRace(parent); card.config.next_race_display = NextRaceDisplay.DateOnly; card.config.show_raceinfo = true; card.config.f1_font = true; jest.setSystemTime(new Date(2022, 3, 20)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsyncIndex(result); console.log(htmlResult); expect(htmlResult).toMatch('

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


Date24-04-22 Practice 1vr 13:30
Race4 Practice 2za 12:30
Race nameEmilia Romagna Grand Prix Practice 3-
Circuit nameAutodromo Enzo e Dino Ferrari Qualifyingvr 17:00
LocationItaly Sprintza 16:30
CityImola Racezo 15:00
'); }), test('Calling render with next_race_display TimeOnly', async () => { // Arrange const card = new NextRace(parent); card.config.next_race_display = NextRaceDisplay.TimeOnly; card.config.show_raceinfo = true; card.config.f1_font = true; jest.setSystemTime(new Date(2022, 3, 20)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsyncIndex(result); console.log(htmlResult); expect(htmlResult).toMatch('

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


Date24-04-22 Practice 1vr 13:30
Race4 Practice 2za 12:30
Race nameEmilia Romagna Grand Prix Practice 3-
Circuit nameAutodromo Enzo e Dino Ferrari Qualifyingvr 17:00
LocationItaly Sprintza 16:30
CityImola Racezo 15:00
'); }), test('Calling render with next_race_display null', async () => { // Arrange const card = new NextRace(parent); card.config.next_race_display = undefined; card.config.show_raceinfo = true; card.config.f1_font = true; jest.setSystemTime(new Date(2022, 3, 20)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsyncIndex(result); console.log(htmlResult); expect(htmlResult).toMatch('

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix

  4 : Emilia Romagna Grand Prix


Date24-04-22 Practice 1vr 13:30
Race4 Practice 2za 12:30
Race nameEmilia Romagna Grand Prix Practice 3-
Circuit nameAutodromo Enzo e Dino Ferrari Qualifyingvr 17:00
LocationItaly Sprintza 16:30
CityImola Racezo 15:00
'); }); }); ================================================ FILE: tests/cards/results.test.ts ================================================ // Mocks import { createMock } from "ts-auto-mock"; import fetchMock from "jest-fetch-mock"; // Models import FormulaOneCard from "../../src"; import { FastestLap, Mrdata, QualifyingResult, Race, Result } from "../../src/api/f1-models"; import Results from "../../src/cards/results"; import { CardProperties, FormulaOneCardConfig, FormulaOneCardTab, mwcTabBarEvent } from "../../src/types/formulaone-card-types"; import { getRenderString, getRenderStringAsync, getRenderStringAsyncIndex } from "../utils"; import { HTMLTemplateResult, html } from "lit"; import RestCountryClient from "../../src/api/restcountry-client"; import { Country } from "../../src/types/rest-country-types"; import ImageClient from "../../src/api/image-client"; import LocalStorageMock from "../testdata/localStorageMock"; // Importing test data import { MRData as resultData } from '../testdata/results.json' import { MRData as seasonData } from '../testdata/seasons.json' import { MRData as scheduleData } from '../testdata/schedule.json' import { MRData as qualifyingData } from '../testdata/qualifying.json' import { MRData as sprintData } from '../testdata/sprint.json' import * as countries from '../testdata/countries.json' describe('Testing results file', () => { const parent = createMock({ config: createMock(), }); const lastRace = resultData.RaceTable.Races[0]; const localStorageMock = new LocalStorageMock(); Object.defineProperty(window, 'localStorage', { value: localStorageMock }); beforeEach(() => { localStorageMock.clear(); fetchMock.resetMocks(); jest.useFakeTimers(); jest.spyOn(FileReader.prototype, 'readAsDataURL').mockImplementation(() => null); jest.spyOn(ImageClient.prototype, 'GetImage').mockImplementation((url: string) => { return url; }); }); beforeAll(() => { jest.spyOn(RestCountryClient.prototype, 'GetCountriesFromLocalStorage').mockImplementation(() => { return countries as Country[]; }); }); parent.properties = new Map(); test('Calling render without selecting season', async () => { // Arrange const card = new Results(parent); setFetchMock(); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
Season
Loading...
Race
'); }), test('Calling render with selected season', async () => { // Arrange parent.properties.set('cardValues', { selectedSeason: '1979', races: scheduleData.RaceTable.Races, selectedRace: undefined }); const card = new Results(parent); fetchMock.mockResponseOnce(JSON.stringify({ MRData : seasonData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toMatch('
Season
Race
'); }), test('Calling render with selected season and selected race', async () => { // Arrange const race = resultData.RaceTable.Races[0]; race.round = "1"; parent.properties.set('cardValues', { selectedSeason: '1979', selectedRace: race, races: scheduleData.RaceTable.Races, selectedTabIndex: 0 }); const card = new Results(parent); fetchMock.mockResponseOnce(JSON.stringify({ MRData : seasonData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderString(result); expect(htmlResult).toMatch('
Season
Race

  1 : Singapore Grand Prix


  Driver Grid Points Status
1 Sergio Pérez 2 25 Finished
2 Charles Leclerc 1 18 Finished
3 Carlos Sainz 4 15 Finished
4 Lando Norris 6 12 Finished
5 Daniel Ricciardo 16 10 Finished
6 Lance Stroll 11 8 Finished
7 Max Verstappen 8 6 Finished
8 Sebastian Vettel 13 4 Finished
9 Lewis Hamilton 3 2 Finished
10 Pierre Gasly 7 1 Finished
11 Valtteri Bottas 15 0 Finished
12 Kevin Magnussen 9 0 Finished
13 Mick Schumacher 12 0 +1 Lap
14 George Russell * 0 0 +2 Laps
15 Yuki Tsunoda 10 0 Accident
16 Esteban Ocon 17 0 Engine
17 Alexander Albon 18 0 Collision damage
18 Fernando Alonso 5 0 Engine
19 Nicholas Latifi 19 0 Collision damage
20 Guanyu Zhou 14 0 Collision
* Fastest lap: 1:46.458
'); }), test('Calling render with selected season and selected race', async () => { // Arrange const race = resultData.RaceTable.Races[0]; race.round = "1"; parent.properties.set('cardValues', { selectedSeason: '1979', selectedRace: race, races: scheduleData.RaceTable.Races, results: race.Results, selectedTabIndex: 0 }); const card = new Results(parent); // Act const result = card.render(); // Assert // eslint-disable-next-line @typescript-eslint/ban-types const func = ((result.values[7] as HTMLTemplateResult).values[1] as HTMLTemplateResult).values[0] as Function; func({ detail: { index: 23 } } as mwcTabBarEvent); const cardValues = card.parent.properties.get('cardValues') as CardProperties; expect(cardValues.selectedTabIndex).toBe(23); }), test('Calling render with selected season and selected race with standings', async () => { // Arrange const race = resultData.RaceTable.Races[0] as Race; race.round = "1"; parent.properties.set('cardValues', { selectedSeason: '1979', selectedRace: race, races: scheduleData.RaceTable.Races, results: race.Results, selectedTabIndex: 0 }); if(parent?.config) parent.config.standings = { show_flag: true, show_team: true, show_teamlogo: true }; const card = new Results(parent); fetchMock.mockResponseOnce(JSON.stringify({ MRData : seasonData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderString(result); expect(htmlResult).toMatch('
Season
Race

  1 : Singapore Grand Prix


  Driver Team Grid Points Status
1  Sergio Pérez  Red Bull 2 25 Finished
2  Charles Leclerc  Ferrari 1 18 Finished
3  Carlos Sainz  Ferrari 4 15 Finished
4  Lando Norris  McLaren 6 12 Finished
5  Daniel Ricciardo  McLaren 16 10 Finished
6  Lance Stroll  Aston Martin 11 8 Finished
7  Max Verstappen  Red Bull 8 6 Finished
8  Sebastian Vettel  Aston Martin 13 4 Finished
9  Lewis Hamilton  Mercedes 3 2 Finished
10  Pierre Gasly  AlphaTauri 7 1 Finished
11  Valtteri Bottas  Alfa Romeo 15 0 Finished
12  Kevin Magnussen  Haas F1 Team 9 0 Finished
13  Mick Schumacher  Haas F1 Team 12 0 +1 Lap
14  George Russell *  Mercedes 0 0 +2 Laps
15  Yuki Tsunoda  AlphaTauri 10 0 Accident
16  Esteban Ocon  Alpine F1 Team 17 0 Engine
17  Alexander Albon  Williams 18 0 Collision damage
18  Fernando Alonso  Alpine F1 Team 5 0 Engine
19  Nicholas Latifi  Williams 19 0 Collision damage
20  Guanyu Zhou  Alfa Romeo 14 0 Collision
* Fastest lap: 1:46.458
'); }), test('Calling render with selected season and selected race with standings', async () => { // Arrange const race = resultData.RaceTable.Races[0] as Race; race.QualifyingResults = qualifyingData.RaceTable.Races[0].QualifyingResults as QualifyingResult[]; race.SprintResults = sprintData.RaceTable.Races[0].SprintResults as Result[]; race.round = "1"; parent.properties.set('cardValues', { selectedSeason: '1979', selectedRace: race, races: scheduleData.RaceTable.Races, selectedTabIndex: 1 }); if(parent?.config) parent.config.standings = undefined const card = new Results(parent); fetchMock.mockResponseOnce(JSON.stringify({ MRData : seasonData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderString(result); expect(htmlResult).toMatch('
Season
Race

  1 : Singapore Grand Prix


  Driver Q1 Q2 Q3
1 Max Verstappen 1:19.222 1:18.566 1:17.775
2 George Russell 1:19.583 1:18.565 1:18.079
3 Lewis Hamilton 1:19.169 1:18.552 1:18.084
4 Sergio Pérez 1:19.706 1:18.615 1:18.128
5 Carlos Sainz 1:19.566 1:18.560 1:18.351
6 Valtteri Bottas 1:19.523 1:18.762 1:18.401
7 Charles Leclerc 1:19.505 1:19.109 1:18.555
8 Lando Norris 1:19.857 1:19.119 1:18.721
9 Fernando Alonso 1:20.006 1:19.272 1:18.939
10 Esteban Ocon 1:19.945 1:19.081 1:19.010
11 Daniel Ricciardo 1:20.279 1:19.325
12 Guanyu Zhou 1:20.283 1:19.476
13 Yuki Tsunoda 1:19.907 1:19.589
14 Pierre Gasly 1:20.256 1:19.672
15 Kevin Magnussen 1:20.293 1:19.833
16 Mick Schumacher 1:20.419
17 Sebastian Vettel 1:20.419
18 Lance Stroll 1:20.520
19 Alexander Albon 1:20.859
20 Nicholas Latifi 1:21.167
'); }), test('Calling render with selected season and selected race with standings and qualifying results with sprint', async () => { // Arrange const race = resultData.RaceTable.Races[0] as Race; race.QualifyingResults = qualifyingData.RaceTable.Races[0].QualifyingResults as QualifyingResult[]; race.SprintResults = sprintData.RaceTable.Races[0].SprintResults as Result[]; race.round = "1"; parent.properties.set('cardValues', { selectedSeason: '1979', selectedRace: race, races: scheduleData.RaceTable.Races, selectedTabIndex: 0 }); if(parent?.config) parent.config.standings = { show_flag: true, show_team: true, show_teamlogo: true }; const card = new Results(parent); fetchMock.mockResponseOnce(JSON.stringify({ MRData : seasonData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderString(result); expect(htmlResult).toMatch('
Season
Race

  1 : Singapore Grand Prix


  Driver Team Grid Points Status
1  Sergio Pérez  Red Bull 2 25 Finished
2  Charles Leclerc  Ferrari 1 18 Finished
3  Carlos Sainz  Ferrari 4 15 Finished
4  Lando Norris  McLaren 6 12 Finished
5  Daniel Ricciardo  McLaren 16 10 Finished
6  Lance Stroll  Aston Martin 11 8 Finished
7  Max Verstappen  Red Bull 8 6 Finished
8  Sebastian Vettel  Aston Martin 13 4 Finished
9  Lewis Hamilton  Mercedes 3 2 Finished
10  Pierre Gasly  AlphaTauri 7 1 Finished
11  Valtteri Bottas  Alfa Romeo 15 0 Finished
12  Kevin Magnussen  Haas F1 Team 9 0 Finished
13  Mick Schumacher  Haas F1 Team 12 0 +1 Lap
14  George Russell *  Mercedes 0 0 +2 Laps
15  Yuki Tsunoda  AlphaTauri 10 0 Accident
16  Esteban Ocon  Alpine F1 Team 17 0 Engine
17  Alexander Albon  Williams 18 0 Collision damage
18  Fernando Alonso  Alpine F1 Team 5 0 Engine
19  Nicholas Latifi  Williams 19 0 Collision damage
20  Guanyu Zhou  Alfa Romeo 14 0 Collision
* Fastest lap: 1:46.458
'); }), test('Calling render with selected season and selected race with standings and qualifying results with sprint', async () => { // Arrange const race = resultData.RaceTable.Races[0] as Race; race.QualifyingResults = qualifyingData.RaceTable.Races[0].QualifyingResults as QualifyingResult[]; race.SprintResults = sprintData.RaceTable.Races[0].SprintResults as Result[]; race.round = "1"; parent.properties.set('cardValues', { selectedSeason: '1979', selectedRace: race, races: scheduleData.RaceTable.Races, selectedTabIndex: 2 }); if(parent?.config) parent.config.standings = { show_flag: true, show_team: true, show_teamlogo: true }; const card = new Results(parent); fetchMock.mockResponseOnce(JSON.stringify({ MRData : seasonData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderString(result); expect(htmlResult).toMatch('
Season
Race

  1 : Singapore Grand Prix


  Driver Team Grid Points Status
1  Max Verstappen  Red Bull 1 8 Finished
2  Charles Leclerc  Ferrari 2 7 Finished
3  Sergio Pérez  Red Bull 7 6 Finished
4  Carlos Sainz  Ferrari 10 5 Finished
5  Lando Norris  McLaren 3 4 Finished
6  Daniel Ricciardo  McLaren 6 3 Finished
7  Valtteri Bottas  Alfa Romeo 8 2 Finished
8  Kevin Magnussen  Haas F1 Team 4 1 Finished
9  Fernando Alonso  Alpine F1 Team 5 0 Finished
10  Mick Schumacher  Haas F1 Team 12 0 Finished
11  George Russell  Mercedes 11 0 Finished
12  Yuki Tsunoda  AlphaTauri 16 0 Finished
13  Sebastian Vettel  Aston Martin 9 0 Finished
14  Lewis Hamilton  Mercedes 13 0 Finished
15  Lance Stroll  Aston Martin 15 0 Finished
16  Esteban Ocon  Alpine F1 Team 19 0 Finished
17  Pierre Gasly  AlphaTauri 17 0 Finished
18  Alexander Albon  Williams 20 0 Finished
19  Nicholas Latifi  Williams 18 0 Finished
20  Guanyu Zhou  Alfa Romeo 0 0 Retired
'); }), test('Calling render with selected season and selected race with standings and qualifying results with sprint failing service', async () => { // Arrange const race = resultData.RaceTable.Races[0] as Race; race.QualifyingResults = qualifyingData.RaceTable.Races[0].QualifyingResults as QualifyingResult[]; race.SprintResults = sprintData.RaceTable.Races[0].SprintResults as Result[]; race.round = "1"; parent.properties.set('cardValues', { selectedSeason: '1979', selectedRace: race, races: scheduleData.RaceTable.Races, selectedTabIndex: 2 }); if(parent?.config) parent.config.standings = { show_flag: true, show_team: true, show_teamlogo: true }; const card = new Results(parent); fetchMock.mockResponseOnce(JSON.stringify({ MRData : seasonData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderString(result); expect(htmlResult).toMatch('
Season
Race

  1 : Singapore Grand Prix


  Driver Team Grid Points Status
1  Max Verstappen  Red Bull 1 8 Finished
2  Charles Leclerc  Ferrari 2 7 Finished
3  Sergio Pérez  Red Bull 7 6 Finished
4  Carlos Sainz  Ferrari 10 5 Finished
5  Lando Norris  McLaren 3 4 Finished
6  Daniel Ricciardo  McLaren 6 3 Finished
7  Valtteri Bottas  Alfa Romeo 8 2 Finished
8  Kevin Magnussen  Haas F1 Team 4 1 Finished
9  Fernando Alonso  Alpine F1 Team 5 0 Finished
10  Mick Schumacher  Haas F1 Team 12 0 Finished
11  George Russell  Mercedes 11 0 Finished
12  Yuki Tsunoda  AlphaTauri 16 0 Finished
13  Sebastian Vettel  Aston Martin 9 0 Finished
14  Lewis Hamilton  Mercedes 13 0 Finished
15  Lance Stroll  Aston Martin 15 0 Finished
16  Esteban Ocon  Alpine F1 Team 19 0 Finished
17  Pierre Gasly  AlphaTauri 17 0 Finished
18  Alexander Albon  Williams 20 0 Finished
19  Nicholas Latifi  Williams 18 0 Finished
20  Guanyu Zhou  Alfa Romeo 0 0 Retired
'); }), test('Calling selectedSeasonChanged should change parent properties', async () => { // Arrange parent.properties.set('cardValues', { selectedSeason: '1979' }); const card = new Results(parent); fetchMock.mockResponseOnce(JSON.stringify({ MRData : seasonData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const result = card.render(); // Assert const directive = result.values[1] as HTMLTemplateResult; const promise = directive.values[0] as Promise; const promiseResult = await promise; // eslint-disable-next-line @typescript-eslint/ban-types const selectedSeasonChangedFn = (promiseResult as HTMLTemplateResult).values[0] as Function; selectedSeasonChangedFn({ target: { value: '2022' } }); const properties = card.parent.properties.get('cardValues') as CardProperties; expect(properties).toMatchObject({ selectedSeason: '1979' } as CardProperties); }), test('Calling selectedRaceChanged should set parent properties', async () => { // Arrange const parentNew = createMock({ config: createMock(), }); const card = new Results(parentNew); setFetchMock(); // Act const result = card.render(); // Assert // eslint-disable-next-line @typescript-eslint/ban-types const selectedRaceChangedFn = result.values[3] as Function; selectedRaceChangedFn({ target: { value: '17' } }); }), test('Calling renderHeader with image not clickable', async () => { // Arrange const card = new Results(parent); card.config.image_clickable = undefined; // Act const result = card.renderHeader(lastRace); // Assert const htmlResult = getRenderString(result); expect(htmlResult).toMatch('

  1 : Singapore Grand Prix


'); }), test('Calling renderHeader with clickable image ', () => { // Arrange const card = new Results(parent); card.config.image_clickable = true; // Act const result = card.renderHeader(lastRace); // Assert const htmlResult = getRenderString(result); expect(htmlResult).toMatch('

  1 : Singapore Grand Prix


'); }), test('Calling renderHeader with undefined race', async () => { // Arrange const card = new Results(parent); card.config.image_clickable = undefined; // Act const result = card.renderHeader(undefined); // Assert const htmlResult = getRenderString(result); expect(htmlResult).toBe(''); }), test('Calling cardSize with hass and sensor', () => { const card = new Results(parent); expect(card.cardSize()).toBe(12); }), test('Calling icon', () => { const card = new Results(parent); card.config.icons = { 'sprint': 'mdi:car-sports', } expect(card.icon('sprint')).toBe('mdi:car-sports'); expect(card.icon('qualifying')).toBe('mdi:timer-outline'); }), test('Calling render with api not returning data', async () => { // Arrange parent.properties.set('cardValues', { selectedSeason: '1979', selectedRace: undefined }); const card = new Results(parent); fetchMock.mockRejectOnce(new Error('Error getting data')); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
Season
Error getting seasons
Loading...
Race
'); }), test('Calling render with selectedSeason undefined', async () => { // Arrange jest.useFakeTimers(); jest.setSystemTime(new Date(2022, 2, 27, 15, 10, 0)); // Weird bug in jest setting this to the last of the month parent.properties.set('cardValues', { selectedSeason: undefined, selectedRace: undefined }); const card = new Results(parent); setFetchMock(); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
Season
Loading...
Race
'); }), test('Calling render with selectedSeason undefined and sprint in past', async () => { // Arrange jest.useFakeTimers(); jest.setSystemTime(new Date(2022, 3, 24, 10)); // Weird bug in jest setting this to the last of the month parent.properties.set('cardValues', { selectedSeason: undefined, selectedRace: undefined }); const card = new Results(parent); setFetchMock(); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
Season
Loading...
Race
'); }), test('Calling render with selectedSeason undefined and no sprintdata', async () => { // Arrange jest.useFakeTimers(); jest.setSystemTime(new Date(2022, 2, 27, 15, 10, 0)); // Weird bug in jest setting this to the last of the month parent.properties.set('cardValues', { selectedSeason: undefined, selectedRace: undefined }); const card = new Results(parent); const sprintDataCopy = sprintData; sprintDataCopy.RaceTable.Races = []; fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : resultData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : seasonData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : qualifyingData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : sprintDataCopy })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : resultData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : resultData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : resultData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
Season
Loading...
Race
'); }), test('Calling render with selectedSeason undefined no results', async () => { // Arrange jest.setSystemTime(new Date(2022, 2, 27, 15, 10, 0)); // Weird bug in jest setting this to the last of the month parent.properties.set('cardValues', { selectedSeason: undefined, selectedRace: undefined }); const card = new Results(parent); setFetchMock(); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
Season
Loading...
Race
'); }), test('Calling setSelectedRace with selectedSeason undefined no results', async () => { // Arrange parent.properties.set('cardValues', { selectedSeason: '2022', selectedRace: undefined }); const card = new Results(parent); setFetchMock(); // Act card.setSelectedRace({ target: { value: '6' } }); // Assert expect(fetchMock).toHaveBeenCalledWith("https://api.jolpi.ca/ergast/f1/2022/6/results.json", {"headers": {"Accept": "application/json"}}); expect(fetchMock).toHaveBeenCalledWith("https://api.jolpi.ca/ergast/f1/2022/6/qualifying.json", {"headers": {"Accept": "application/json"}}); expect(fetchMock).toHaveBeenCalledWith("https://api.jolpi.ca/ergast/f1/2022/6/sprint.json", {"headers": {"Accept": "application/json"}}); }), test('Calling setSelectedRace with selectedSeason undefined no results', async () => { // Arrange parent.properties.set('cardValues', { selectedSeason: '2022', selectedRace: undefined }); const card = new Results(parent); setFetchMock(); // Act card.setSelectedRace({ target: { value: '5' } }); // Assert expect(fetchMock).toHaveBeenCalledWith("https://api.jolpi.ca/ergast/f1/2022/5/results.json", {"headers": {"Accept": "application/json"}}); expect(fetchMock).toHaveBeenCalledWith("https://api.jolpi.ca/ergast/f1/2022/5/qualifying.json", {"headers": {"Accept": "application/json"}}); expect(fetchMock).toHaveBeenCalledWith("https://api.jolpi.ca/ergast/f1/2022/5/sprint.json", {"headers": {"Accept": "application/json"}}); }), test('Calling setSelectedRace with selectedSeason undefined no results', async () => { // Arrange parent.properties.set('cardValues', { selectedSeason: '2022', selectedRace: undefined }); const card = new Results(parent); setFetchMock(); // Act card.setSelectedRace({ target: { value: '5' } }); // Assert expect(fetchMock).toHaveBeenCalledWith("https://api.jolpi.ca/ergast/f1/2022/5/results.json", {"headers": {"Accept": "application/json"}}); expect(fetchMock).toHaveBeenCalledWith("https://api.jolpi.ca/ergast/f1/2022/5/qualifying.json", {"headers": {"Accept": "application/json"}}); expect(fetchMock).toHaveBeenCalledWith("https://api.jolpi.ca/ergast/f1/2022/5/sprint.json", {"headers": {"Accept": "application/json"}}); }), test('Calling render without tab_order should arrange tabs in default order', async () => { // Arrange const card = new Results(parent); // Act const tabs = card.renderTabs(lastRace); // Assert expect(tabs[0].title).toBe('Results'); expect(tabs[1].title).toBe('Qualifying'); expect(tabs[2].title).toBe('Sprint'); }), test('Calling render with selected season and selected race without fastest lap', async () => { // Arrange const race = resultData.RaceTable.Races[0]; race.Results.forEach(result => { result.FastestLap = undefined as unknown as FastestLap; }); race.round = "1"; parent.properties.set('cardValues', { selectedSeason: '1979', selectedRace: race, races: scheduleData.RaceTable.Races, selectedTabIndex: 0 }); const card = new Results(parent); fetchMock.mockResponseOnce(JSON.stringify({ MRData : seasonData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderString(result); expect(htmlResult).toMatch('
Season
Race

  1 : Singapore Grand Prix


  Driver Team Grid Points Status
1  Sergio Pérez  Red Bull 2 25 Finished
2  Charles Leclerc  Ferrari 1 18 Finished
3  Carlos Sainz  Ferrari 4 15 Finished
4  Lando Norris  McLaren 6 12 Finished
5  Daniel Ricciardo  McLaren 16 10 Finished
6  Lance Stroll  Aston Martin 11 8 Finished
7  Max Verstappen  Red Bull 8 6 Finished
8  Sebastian Vettel  Aston Martin 13 4 Finished
9  Lewis Hamilton  Mercedes 3 2 Finished
10  Pierre Gasly  AlphaTauri 7 1 Finished
11  Valtteri Bottas  Alfa Romeo 15 0 Finished
12  Kevin Magnussen  Haas F1 Team 9 0 Finished
13  Mick Schumacher  Haas F1 Team 12 0 +1 Lap
14  George Russell  Mercedes 0 0 +2 Laps
15  Yuki Tsunoda  AlphaTauri 10 0 Accident
16  Esteban Ocon  Alpine F1 Team 17 0 Engine
17  Alexander Albon  Williams 18 0 Collision damage
18  Fernando Alonso  Alpine F1 Team 5 0 Engine
19  Nicholas Latifi  Williams 19 0 Collision damage
20  Guanyu Zhou  Alfa Romeo 14 0 Collision
'); }), test('Calling render without tab_order should arrange tabs in given order', async () => { // Arrange const card = new Results(parent); card.config.tabs_order = ['Qualifying', 'Sprint', 'Results']; // Act const tabs = card.renderTabs(lastRace); // Assert expect(tabs[0].title).toBe('Qualifying'); expect(tabs[1].title).toBe('Sprint'); expect(tabs[2].title).toBe('Results'); }), test('Calling renderTabsHtml with race, tabs and selected index should return expected html', async () => { // Arrange const race = resultData.RaceTable.Races[0] as Race; const card = new Results(parent); const tabs: FormulaOneCardTab[] = [ { title: 'Qualifying', content: html`Qualifying`, icon: 'speed' }, { title: 'Sprint', content: html`Sprint`, icon: 'speed' }, { title: 'Results', content: html`Results`, icon: 'speed' } ]; // Act const result = card.renderTabsHtml(tabs, 1, race); const htmlResult = await getRenderString(result); // Assert expect(htmlResult).toBe('

  1 : Singapore Grand Prix


Sprint
'); }), test('Calling renderTabsHtml with race, tabs and selected index should return expected html', async () => { // Arrange const race = resultData.RaceTable.Races[0] as Race; const card = new Results(parent); const tabs: FormulaOneCardTab[] = [ { title: 'Qualifying', content: null as unknown as HTMLTemplateResult, icon: 'speed' }, { title: 'Sprint', content: null as unknown as HTMLTemplateResult, icon: 'speed' }, { title: 'Results', content: null as unknown as HTMLTemplateResult, icon: 'speed' } ]; // Act const result = card.renderTabsHtml(tabs, 1, race); const htmlResult = await getRenderString(result); // Assert expect(htmlResult).toBe('

  1 : Singapore Grand Prix


Please select a race thats already been run.
'); }), test('Calling renderTabsHtml with race undefined, tabs and selected index should return expected html', async () => { // Arrange const card = new Results(parent); const tabs: FormulaOneCardTab[] = [ { title: 'Qualifying', content: html`Qualifying`, icon: 'speed' }, { title: 'Sprint', content: html`Sprint`, icon: 'speed' }, { title: 'Results', content: html`Results`, icon: 'speed' } ]; // Act const result = card.renderTabsHtml(tabs, 1, undefined); const htmlResult = await getRenderString(result); // Assert expect(htmlResult).toBe(''); }) function setFetchMock() { fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : resultData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : seasonData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : qualifyingData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : sprintData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : resultData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : resultData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : resultData })); } }); ================================================ FILE: tests/cards/schedule.test.ts ================================================ // Mocks import { createMock } from "ts-auto-mock"; import fetchMock from "jest-fetch-mock"; import LocalStorageMock from '../testdata/localStorageMock'; // Models import Schedule from '../../src/cards/schedule'; import { HomeAssistant, NumberFormat, TimeFormat } from 'custom-card-helpers'; import { getRenderString, getRenderStringAsync } from '../utils'; import { FormulaOneCardConfig, PreviousRaceDisplay } from '../../src/types/formulaone-card-types'; import { Mrdata } from '../../src/api/f1-models'; import { getApiErrorMessage, getEndOfSeasonMessage } from '../../src/utils'; import FormulaOneCard from '../../src'; import RestCountryClient from '../../src/api/restcountry-client'; import { Country } from '../../src/types/rest-country-types'; import ImageClient from "../../src/api/image-client"; // Importing test data import * as countries from '../testdata/countries.json' import { MRData } from '../testdata/schedule.json' describe('Testing schedule file', () => { const parent = createMock({ config: createMock(), }); const hass = createMock(); hass.locale = { language: 'NL', number_format: NumberFormat.comma_decimal, time_format: TimeFormat.language } parent._hass = hass; const localStorageMock = new LocalStorageMock(); Object.defineProperty(window, 'localStorage', { value: localStorageMock }); beforeEach(() => { localStorageMock.clear(); fetchMock.resetMocks(); jest.useFakeTimers(); jest.spyOn(FileReader.prototype, 'readAsDataURL').mockImplementation(() => null); jest.spyOn(ImageClient.prototype, 'GetImage').mockImplementation((url: string) => { return url; }); }); beforeAll(() => { jest.spyOn(RestCountryClient.prototype, 'GetCountriesFromLocalStorage').mockImplementation(() => { return countries as Country[]; }); }); afterEach (() => { jest.useRealTimers(); }); test('Calling render with api returning data', async () => { // Arrange const card = new Schedule(parent); jest.setSystemTime(new Date(2022, 2, 1)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
  Race Location Date Time
1 Bahrain International Circuit Sakhir, Bahrain 20-03 16:00
2 Jeddah Corniche Circuit Jeddah, Saudi Arabia 27-03 19:00
3 Albert Park Grand Prix Circuit Melbourne, Australia 10-04 7:00
4 Autodromo Enzo e Dino Ferrari Imola, Italy 24-04 15:00
5 Miami International Autodrome Miami, USA 08-05 21:30
6 Circuit de Barcelona-Catalunya Montmeló, Spain 22-05 15:00
7 Circuit de Monaco Monte-Carlo, Monaco 29-05 15:00
8 Baku City Circuit Baku, Azerbaijan 12-06 13:00
9 Circuit Gilles Villeneuve Montreal, Canada 19-06 20:00
10 Silverstone Circuit Silverstone, UK 03-07 16:00
11 Red Bull Ring Spielberg, Austria 10-07 15:00
12 Circuit Paul Ricard Le Castellet, France 24-07 15:00
13 Hungaroring Budapest, Hungary 31-07 15:00
14 Circuit de Spa-Francorchamps Spa, Belgium 28-08 15:00
15 Circuit Park Zandvoort Zandvoort, Netherlands 04-09 15:00
16 Autodromo Nazionale di Monza Monza, Italy 11-09 15:00
17 Marina Bay Street Circuit Marina Bay, Singapore 02-10 14:00
18 Suzuka Circuit Suzuka, Japan 09-10 7:00
19 Circuit of the Americas Austin, USA 23-10 21:00
20 Autódromo Hermanos Rodríguez Mexico City, Mexico 30-10 21:00
21 Autódromo José Carlos Pace São Paulo, Brazil 13-11 19:00
22 Yas Marina Circuit Abu Dhabi, UAE 20-11 14:00
'); }), test('Calling render with api returning data and standings config', async () => { // Arrange const card = new Schedule(parent); card.config.standings = { show_flag: true }; jest.setSystemTime(new Date(2022, 2, 1)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
  Race Location Date Time
1 Bahrain International Circuit  Sakhir, Bahrain 20-03 16:00
2 Jeddah Corniche Circuit  Jeddah, Saudi Arabia 27-03 19:00
3 Albert Park Grand Prix Circuit  Melbourne, Australia 10-04 7:00
4 Autodromo Enzo e Dino Ferrari  Imola, Italy 24-04 15:00
5 Miami International Autodrome  Miami, USA 08-05 21:30
6 Circuit de Barcelona-Catalunya  Montmeló, Spain 22-05 15:00
7 Circuit de Monaco  Monte-Carlo, Monaco 29-05 15:00
8 Baku City Circuit  Baku, Azerbaijan 12-06 13:00
9 Circuit Gilles Villeneuve  Montreal, Canada 19-06 20:00
10 Silverstone Circuit  Silverstone, UK 03-07 16:00
11 Red Bull Ring  Spielberg, Austria 10-07 15:00
12 Circuit Paul Ricard  Le Castellet, France 24-07 15:00
13 Hungaroring  Budapest, Hungary 31-07 15:00
14 Circuit de Spa-Francorchamps  Spa, Belgium 28-08 15:00
15 Circuit Park Zandvoort  Zandvoort, Netherlands 04-09 15:00
16 Autodromo Nazionale di Monza  Monza, Italy 11-09 15:00
17 Marina Bay Street Circuit  Marina Bay, Singapore 02-10 14:00
18 Suzuka Circuit  Suzuka, Japan 09-10 7:00
19 Circuit of the Americas  Austin, USA 23-10 21:00
20 Autódromo Hermanos Rodríguez  Mexico City, Mexico 30-10 21:00
21 Autódromo José Carlos Pace  São Paulo, Brazil 13-11 19:00
22 Yas Marina Circuit  Abu Dhabi, UAE 20-11 14:00
'); }), test('Calling render with api not returning data', async () => { // Arrange const card = new Schedule(parent); fetchMock.mockResponseOnce('{}', { status: 500 }); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); const expectedResult = getRenderString(getApiErrorMessage('schedule')); expect(htmlResult).toMatch(expectedResult); }), test('Calling render with api returning data at end of season', async () => { const card = new Schedule(parent); // Arrange jest.setSystemTime(new Date(2022, 11, 30)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); const expectedResult = getRenderString(getEndOfSeasonMessage('Season is over. See you next year!')); expect(htmlResult).toMatch(expectedResult); }), test('Calling render with api returning data and location clickable', async () => { // Arrange const card = new Schedule(parent); card.config.location_clickable = true; jest.setSystemTime(new Date(2022, 2, 1)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch('
  Race Location Date Time
1 Bahrain International Circuit  Sakhir, Bahrain 20-03 16:00
2 Jeddah Corniche Circuit  Jeddah, Saudi Arabia 27-03 19:00
3 Albert Park Grand Prix Circuit  Melbourne, Australia 10-04 7:00
4 Autodromo Enzo e Dino Ferrari  Imola, Italy 24-04 15:00
5 Miami International Autodrome  Miami, USA 08-05 21:30
6 Circuit de Barcelona-Catalunya  Montmeló, Spain 22-05 15:00
7 Circuit de Monaco  Monte-Carlo, Monaco 29-05 15:00
8 Baku City Circuit  Baku, Azerbaijan 12-06 13:00
9 Circuit Gilles Villeneuve  Montreal, Canada 19-06 20:00
10 Silverstone Circuit  Silverstone, UK 03-07 16:00
11 Red Bull Ring  Spielberg, Austria 10-07 15:00
12 Circuit Paul Ricard  Le Castellet, France 24-07 15:00
13 Hungaroring  Budapest, Hungary 31-07 15:00
14 Circuit de Spa-Francorchamps  Spa, Belgium 28-08 15:00
15 Circuit Park Zandvoort  Zandvoort, Netherlands 04-09 15:00
16 Autodromo Nazionale di Monza  Monza, Italy 11-09 15:00
17 Marina Bay Street Circuit  Marina Bay, Singapore 02-10 14:00
18 Suzuka Circuit  Suzuka, Japan 09-10 7:00
19 Circuit of the Americas  Austin, USA 23-10 21:00
20 Autódromo Hermanos Rodríguez  Mexico City, Mexico 30-10 21:00
21 Autódromo José Carlos Pace  São Paulo, Brazil 13-11 19:00
22 Yas Marina Circuit  Abu Dhabi, UAE 20-11 14:00
'); }), test.each` previous_race ${PreviousRaceDisplay.Strikethrough} ${PreviousRaceDisplay.Italic} `('Calling render with api returning data and previous race', async ({ previous_race }) => { // Arrange const card = new Schedule(parent); card.config.previous_race = previous_race; jest.setSystemTime(new Date(2022, 3, 1)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch(`
  Race Location Date Time
1 Bahrain International Circuit  Sakhir, Bahrain 20-03 16:00
2 Jeddah Corniche Circuit  Jeddah, Saudi Arabia 27-03 19:00
3 Albert Park Grand Prix Circuit  Melbourne, Australia 10-04 7:00
4 Autodromo Enzo e Dino Ferrari  Imola, Italy 24-04 15:00
5 Miami International Autodrome  Miami, USA 08-05 21:30
6 Circuit de Barcelona-Catalunya  Montmeló, Spain 22-05 15:00
7 Circuit de Monaco  Monte-Carlo, Monaco 29-05 15:00
8 Baku City Circuit  Baku, Azerbaijan 12-06 13:00
9 Circuit Gilles Villeneuve  Montreal, Canada 19-06 20:00
10 Silverstone Circuit  Silverstone, UK 03-07 16:00
11 Red Bull Ring  Spielberg, Austria 10-07 15:00
12 Circuit Paul Ricard  Le Castellet, France 24-07 15:00
13 Hungaroring  Budapest, Hungary 31-07 15:00
14 Circuit de Spa-Francorchamps  Spa, Belgium 28-08 15:00
15 Circuit Park Zandvoort  Zandvoort, Netherlands 04-09 15:00
16 Autodromo Nazionale di Monza  Monza, Italy 11-09 15:00
17 Marina Bay Street Circuit  Marina Bay, Singapore 02-10 14:00
18 Suzuka Circuit  Suzuka, Japan 09-10 7:00
19 Circuit of the Americas  Austin, USA 23-10 21:00
20 Autódromo Hermanos Rodríguez  Mexico City, Mexico 30-10 21:00
21 Autódromo José Carlos Pace  São Paulo, Brazil 13-11 19:00
22 Yas Marina Circuit  Abu Dhabi, UAE 20-11 14:00
`); }), test.each` previous_race | expected ${PreviousRaceDisplay.Hide}, ${'
  Race Location Date Time
3 Albert Park Grand Prix Circuit  Melbourne, Australia 10-04 7:00
4 Autodromo Enzo e Dino Ferrari  Imola, Italy 24-04 15:00
Loading...
'} ${PreviousRaceDisplay.Strikethrough}, ${'
  Race Location Date Time
1 Bahrain International Circuit  Sakhir, Bahrain 20-03 16:00
2 Jeddah Corniche Circuit  Jeddah, Saudi Arabia 27-03 19:00
Loading...
'} ${PreviousRaceDisplay.Italic}, ${'
  Race Location Date Time
1 Bahrain International Circuit  Sakhir, Bahrain 20-03 16:00
2 Jeddah Corniche Circuit  Jeddah, Saudi Arabia 27-03 19:00
Loading...
'} `('Calling render with api returning data and previous race and row_limit', async ({ previous_race, expected }) => { // Arrange const card = new Schedule(parent); card.config.previous_race = previous_race; card.config.row_limit = 2; jest.setSystemTime(new Date(2022, 3, 1)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : MRData })); // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsync(result); expect(htmlResult).toMatch(expected); }), test('Calling cardSize', () => { const card = new Schedule(parent); expect(card.cardSize()).toBe(12); }) }); ================================================ FILE: tests/config.ts ================================================ import 'jest-ts-auto-mock'; import fetchMock from "jest-fetch-mock"; fetchMock.enableMocks(); ================================================ FILE: tests/index.test.ts ================================================ // Mocks import { createMock } from "ts-auto-mock"; import fetchMock from "jest-fetch-mock"; import LocalStorageMock from './testdata/localStorageMock'; // Models import { HomeAssistant, NumberFormat, TimeFormat } from 'custom-card-helpers'; import FormulaOneCard from '../src/index'; import { FormulaOneCardConfig, FormulaOneCardType } from '../src/types/formulaone-card-types'; import { getRenderString, getRenderStringAsyncIndex } from './utils'; import { LitElement, PropertyValues } from 'lit'; import { Mrdata, Root } from '../src/api/f1-models'; import ErgastClient from '../src/api/ergast-client'; import RestCountryClient from '../src/api/restcountry-client'; import { Country } from '../src/types/rest-country-types'; import ImageClient from "../src/api/image-client"; // Importing test data import { MRData as scheduleData } from './testdata/schedule.json' import { MRData as constructorStandingsData } from './testdata/constructorStandings.json' import { MRData as driverStandingsData } from './testdata/driverStandings.json' import { MRData as resultData } from './testdata/results.json' import { MRData as seasonData } from './testdata/seasons.json' import * as countries from './testdata/countries.json' beforeEach(() => { jest.spyOn(FileReader.prototype, 'readAsDataURL').mockImplementation(() => null); jest.spyOn(ImageClient.prototype, 'GetImage').mockImplementation((url: string) => { return url; }); }); describe('Testing index file function setConfig', () => { jest.spyOn(RestCountryClient.prototype, 'GetData').mockImplementation(() => { return new Promise((resolve) => { resolve(countries as Country[]); }); }); const card = new FormulaOneCard(); const hass = createMock(); hass.locale = { language: 'NL', number_format: NumberFormat.comma_decimal, time_format: TimeFormat.language } const localStorageMock = new LocalStorageMock(); Object.defineProperty(window, 'localStorage', { value: localStorageMock }); beforeEach(() => { localStorageMock.clear(); fetchMock.resetMocks(); jest.useFakeTimers(); }); beforeAll(() => { jest.spyOn(RestCountryClient.prototype, 'GetCountriesFromLocalStorage').mockImplementation(() => { return countries as Country[]; }); }); afterEach (() => { jest.useRealTimers(); }); test('Calling render without hass and config should return empty html', () => { // Act const result = card.render(); // Assert const htmlResult = getRenderString(result); expect(htmlResult).toMatch(''); }), test('Passing empty config should throw error', () => { // Arrange const config: FormulaOneCardConfig = { type: '' } // Act & Assert expect(() => card.setConfig(config)).toThrowError('Please define FormulaOne card type (card_type).'); }), test('Calling render ConstructorStandings should return expected html', async () => { // Arrange const config: FormulaOneCardConfig = { type: '', title: 'Test', card_type: FormulaOneCardType.ConstructorStandings } fetchMock.mockResponseOnce(JSON.stringify({ MRData : constructorStandingsData })); card.setConfig(config); card.hass = hass; // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toMatch('

Test

Test

  Constructor Pts Wins
1 Red Bull 576 13
2 Ferrari 439 4
3 Mercedes 373 0
4 McLaren 129 0
5 Alpine F1 Team 125 0
6 Alfa Romeo 52 0
7 Aston Martin 37 0
8 Haas F1 Team 34 0
9 AlphaTauri 34 0
10 Williams 6 0
'); }), test('Calling render with hass and config DriverStanding should return expected html', async () => { // Arrange const config: FormulaOneCardConfig = { type: '', card_type: FormulaOneCardType.DriverStandings } fetchMock.mockResponseOnce(JSON.stringify({ MRData : driverStandingsData })); card.setConfig(config); card.hass = hass; // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toMatch('
  Driver Pts Wins
1 VER Max Verstappen 341 11
2 LEC Charles Leclerc 237 3
3 PER Sergio Pérez 235 2
4 RUS George Russell 203 0
5 SAI Carlos Sainz 202 1
6 HAM Lewis Hamilton 170 0
7 NOR Lando Norris 100 0
8 OCO Esteban Ocon 66 0
9 ALO Fernando Alonso 59 0
10 BOT Valtteri Bottas 46 0
11 RIC Daniel Ricciardo 29 0
12 VET Sebastian Vettel 24 0
13 GAS Pierre Gasly 23 0
14 MAG Kevin Magnussen 22 0
15 STR Lance Stroll 13 0
16 MSC Mick Schumacher 12 0
17 TSU Yuki Tsunoda 11 0
18 ZHO Guanyu Zhou 6 0
19 ALB Alexander Albon 4 0
20 DEV Nyck de Vries 2 0
21 LAT Nicholas Latifi 0 0
22 HUL Nico Hülkenberg 0 0
'); }), test('Calling render with hass and config LastResult should return expected html', async () => { // Arrange const config: FormulaOneCardConfig = { type: '', title: 'Test', card_type: FormulaOneCardType.LastResult } fetchMock.mockResponseOnce(JSON.stringify({ MRData : resultData })); card.setConfig(config); card.hass = hass; // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toMatch('

Test

Test

  17 : Singapore Grand Prix

  17 : Singapore Grand Prix

  17 : Singapore Grand Prix

  17 : Singapore Grand Prix


  17 : Singapore Grand Prix

  17 : Singapore Grand Prix

  17 : Singapore Grand Prix

  17 : Singapore Grand Prix


  Driver Grid Points Status
1 Sergio Pérez 2 25 Finished
2 Charles Leclerc 1 18 Finished
3 Carlos Sainz 4 15 Finished
4 Lando Norris 6 12 Finished
5 Daniel Ricciardo 16 10 Finished
6 Lance Stroll 11 8 Finished
7 Max Verstappen 8 6 Finished
8 Sebastian Vettel 13 4 Finished
9 Lewis Hamilton 3 2 Finished
10 Pierre Gasly 7 1 Finished
11 Valtteri Bottas 15 0 Finished
12 Kevin Magnussen 9 0 Finished
13 Mick Schumacher 12 0 +1 Lap
14 George Russell 0 0 +2 Laps
15 Yuki Tsunoda 10 0 Accident
16 Esteban Ocon 17 0 Engine
17 Alexander Albon 18 0 Collision damage
18 Fernando Alonso 5 0 Engine
19 Nicholas Latifi 19 0 Collision damage
20 Guanyu Zhou 14 0 Collision
'); }), test('Calling render with hass and config Results should return expected html', async () => { // Arrange const config: FormulaOneCardConfig = { type: '', title: 'Test', card_type: FormulaOneCardType.Results } // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-explicit-any jest.spyOn(LitElement.prototype as any, 'update').mockImplementationOnce(() => { }); jest.spyOn(ErgastClient.prototype, 'GetData').mockImplementation((_endpoint) => { if(_endpoint === '2022/2/results.json`') { return new Promise((resolve) => { resolve({ MRData : resultData }); }); } if(_endpoint === 'seasons.json?limit=200') { return new Promise((resolve) => { resolve({ MRData : seasonData }); }); } return new Promise((resolve) => { resolve({ MRData : scheduleData }); }); }); card.setConfig(config); card.hass = hass; // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toMatch('

Test

Test

Season
Race
Season
Race
Season
Race
Season
Race
Season
Race
Season
Race
Season
Race
Season
Race
'); }), test('Calling render with hass and config NextRace should return expected html', async () => { // Arrange const config: FormulaOneCardConfig = { type: '', title: 'Test', card_type: FormulaOneCardType.NextRace, f1_font: true } fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); jest.setSystemTime(new Date(2022, 2, 1)); // Weird bug in jest setting this to the last of the month card.setConfig(config); card.hass = hass; // Act const result = card.render(); // Assert const htmlResult = await getRenderString(result); expect(htmlResult).toMatch('

Test

'); }), test('Calling render with hass and config Schedule should return expected html', async () => { // Arrange const config: FormulaOneCardConfig = { type: '', title: 'Test', card_type: FormulaOneCardType.Schedule } fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); jest.setSystemTime(new Date(2022, 2, 1)); // Weird bug in jest setting this to the last of the month card.setConfig(config); card.hass = hass; // Act const result = card.render(); // Assert const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toMatch('

Test

Test

  Race Location Date Time
1 Bahrain International Circuit Sakhir, BahrainSakhir, BahrainSakhir, Bahrain 20-03 16:00
2 Jeddah Corniche Circuit Jeddah, Saudi ArabiaJeddah, Saudi ArabiaJeddah, Saudi Arabia 27-03 19:00
3 Albert Park Grand Prix Circuit Melbourne, AustraliaMelbourne, AustraliaMelbourne, Australia 10-04 7:00
4 Autodromo Enzo e Dino Ferrari Imola, ItalyImola, ItalyImola, Italy 24-04 15:00
5 Miami International Autodrome Miami, USAMiami, USAMiami, USA 08-05 21:30
6 Circuit de Barcelona-Catalunya Montmeló, SpainMontmeló, SpainMontmeló, Spain 22-05 15:00
7 Circuit de Monaco Monte-Carlo, MonacoMonte-Carlo, MonacoMonte-Carlo, Monaco 29-05 15:00
8 Baku City Circuit Baku, AzerbaijanBaku, AzerbaijanBaku, Azerbaijan 12-06 13:00
9 Circuit Gilles Villeneuve Montreal, CanadaMontreal, CanadaMontreal, Canada 19-06 20:00
10 Silverstone Circuit Silverstone, UKSilverstone, UKSilverstone, UK 03-07 16:00
11 Red Bull Ring Spielberg, AustriaSpielberg, AustriaSpielberg, Austria 10-07 15:00
12 Circuit Paul Ricard Le Castellet, FranceLe Castellet, FranceLe Castellet, France 24-07 15:00
13 Hungaroring Budapest, HungaryBudapest, HungaryBudapest, Hungary 31-07 15:00
14 Circuit de Spa-Francorchamps Spa, BelgiumSpa, BelgiumSpa, Belgium 28-08 15:00
15 Circuit Park Zandvoort Zandvoort, NetherlandsZandvoort, NetherlandsZandvoort, Netherlands 04-09 15:00
16 Autodromo Nazionale di Monza Monza, ItalyMonza, ItalyMonza, Italy 11-09 15:00
17 Marina Bay Street Circuit Marina Bay, SingaporeMarina Bay, SingaporeMarina Bay, Singapore 02-10 14:00
18 Suzuka Circuit Suzuka, JapanSuzuka, JapanSuzuka, Japan 09-10 7:00
19 Circuit of the Americas Austin, USAAustin, USAAustin, USA 23-10 21:00
20 Autódromo Hermanos Rodríguez Mexico City, MexicoMexico City, MexicoMexico City, Mexico 30-10 21:00
21 Autódromo José Carlos Pace São Paulo, BrazilSão Paulo, BrazilSão Paulo, Brazil 13-11 19:00
22 Yas Marina Circuit Abu Dhabi, UAEAbu Dhabi, UAEAbu Dhabi, UAE 20-11 14:00
'); }), test('Calling shouldUpdate with config should return true', () => { // Arrange const config: FormulaOneCardConfig = { type: '', title: 'Test', card_type: FormulaOneCardType.Schedule } const props : PropertyValues = new Map([['config', config]]); // Act const result = card['shouldUpdate'](props); // Assert expect(result).toBeTruthy(); }), test.each` type | expected ${FormulaOneCardType.ConstructorStandings}, ${11} ${FormulaOneCardType.DriverStandings}, ${12} ${FormulaOneCardType.LastResult}, ${11} ${FormulaOneCardType.NextRace}, ${8} ${FormulaOneCardType.Schedule}, ${12} ${FormulaOneCardType.Results}, ${12} ${FormulaOneCardType.Countdown}, ${6} `('Calling getCardSize with type should return card size', ({ type, expected }) => { // Arrange const config: FormulaOneCardConfig = { type: '', title: 'Test', card_type: type.toString() } // Act card.setConfig(config); card.hass = hass; // Assert expect(card.getCardSize()).toBe(expected); }), test('Passing empty config should throw warning', () => { // Arrange card.card.render = jest.fn().mockImplementationOnce(() => { throw new Error('Error for warning'); }); // Act const result = card.render(); // Assert const htmlResult = getRenderString(result); expect(htmlResult).toMatch('Error: Error for warning'); }), test('Setting cardValues should trigger update and values should be set', () => { // Arrange // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-explicit-any const updateSpy = jest.spyOn(LitElement.prototype as any, 'update').mockImplementationOnce(() => { }); // Act card.properties = new Map([['races', []]]); // Assert expect(updateSpy).toHaveBeenCalled(); expect(card.properties).toEqual(new Map([['races', []]])); }), test('Calling constructor should set warning', async () => { // Arrange fetchMock.mockReject(new Error('Error for warning')); // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-explicit-any const updateSpy = jest.spyOn(LitElement.prototype as any, 'update').mockImplementationOnce(() => { }); jest.spyOn(RestCountryClient.prototype, 'GetAll').mockImplementationOnce(() => { return Promise.reject(new Error()); }); // Act const formulaOneCard = new FormulaOneCard(); formulaOneCard.setCountryCache(); // Assert expect(updateSpy).toHaveBeenCalled(); }), test('Calling render with warning should show warning', async () => { // Arrange card.warning = 'Error for warning'; // Act const result = card.render(); // Assert const htmlResult = getRenderString(result); expect(htmlResult).toMatch(' Error for warning

Test

'); }), test('Calling refresh cache should set cache', async () => { // Arrange const event = new Event('refresh-cache'); const config: FormulaOneCardConfig = { type: '', title: 'Test', card_type: FormulaOneCardType.Countdown, show_refresh: true } card.setConfig(config); // Act card.refreshCache(event); // Assert }) }) ================================================ FILE: tests/lib/formate_date.test.ts ================================================ import { FrontendLocaleData, NumberFormat, TimeFormat } from "custom-card-helpers"; import { formatDate, formatDateNumeric } from "../../src/lib/format_date"; describe('Testing formate_date file', () => { const locale: FrontendLocaleData = { language: 'NL', number_format: NumberFormat.comma_decimal, time_format: TimeFormat.language } test('Passing date, locale and en locale', () => { const date = new Date('2022-01-01') expect(formatDate(date, locale, 'en')).toBe('01/01'); }), test('Passing date and locale', () => { const date = new Date('2022-01-01') expect(formatDate(date, locale)).toBe('01-01'); }), test('Passing date, locale and en locale', () => { const date = new Date('2022-01-01') expect(formatDateNumeric(date, locale, 'en')).toBe('01/01/22'); }), test('Passing date and locale', () => { const date = new Date('2022-01-01') expect(formatDateNumeric(date, locale)).toBe('01-01-22'); }) }) ================================================ FILE: tests/lib/formate_date_time.test.ts ================================================ import { FrontendLocaleData, NumberFormat, TimeFormat } from "custom-card-helpers"; import { formatDateTime, formatDateTimeRaceInfo } from "../../src/lib/format_date_time"; describe('Testing formate_date_time file', () => { const locale: FrontendLocaleData = { language: 'NL', number_format: NumberFormat.comma_decimal, time_format: TimeFormat.language } test('Passing date, locale formatDateTime', () => { const date = new Date('2022-01-01 10:00:00') const result = formatDateTime(date, locale).split(' '); '1 januari 2022 10:00'.split(' ').forEach((x: string) => expect(result).toContain(x)); }), test('Passing date and locale formatDateTimeRaceInfo', () => { const date = new Date('2022-01-01 10:00:00') const result = formatDateTimeRaceInfo(date, locale).split(' '); 'za 10:00'.split(' ').forEach((x: string) => expect(result).toContain(x)); }), test('Passing date, locale and en locale formatDateTime', () => { const date = new Date('2022-01-01 10:00:00') locale.time_format = TimeFormat.am_pm; const result = formatDateTime(date, locale).split(' '); '1 januari 2022 10:00 a.m.'.split(' ').forEach((x: string) => expect(result).toContain(x)); }), test('Passing date and locale en locale formatDateTimeRaceInfo', () => { const date = new Date('2022-01-01 10:00:00') locale.time_format = TimeFormat.am_pm; const result = formatDateTimeRaceInfo(date, locale).split(' '); 'za 10:00 a.m.'.split(' ').forEach((x: string) => expect(result).toContain(x)); }), test('Passing date, locale and timeformat system formatDateTime', () => { const date = new Date('2022-01-01 10:00:00') locale.time_format = TimeFormat.system; const result = formatDateTime(date, locale).split(' '); '1 januari 2022 10:00'.split(' ').forEach((x: string) => expect(result).toContain(x)); }), test('Passing date and locale timeformat system formatDateTimeRaceInfo', () => { const date = new Date('2022-01-01 10:00:00') locale.time_format = TimeFormat.system; const result = formatDateTimeRaceInfo(date, locale).split(' '); 'za 10:00'.split(' ').forEach((x: string) => expect(result).toContain(x)); }) }) ================================================ FILE: tests/lib/formate_time.test.ts ================================================ import { FrontendLocaleData, NumberFormat, TimeFormat } from "custom-card-helpers"; import { formatTime } from "../../src/lib/format_time"; describe('Testing formate_time file', () => { const locale: FrontendLocaleData = { language: 'NL', number_format: NumberFormat.comma_decimal, time_format: TimeFormat.language } test('Passing date, locale', () => { const date = new Date('2022-01-01 10:00:00') expect(formatTime(date, locale)).toBe('10:00'); }) }) ================================================ FILE: tests/testdata/constructorStandings.json ================================================ {"MRData":{"xmlns":"http:\/\/ergast.com\/mrd\/1.5","series":"f1","url":"http://ergast.com/api/f1/2022/constructorstandings.json","limit":"30","offset":"0","total":"10","StandingsTable":{"season":"2022","StandingsLists":[{"season":"2022","round":"17","ConstructorStandings":[{"position":"1","positionText":"1","points":"576","wins":"13","Constructor":{"constructorId":"red_bull","url":"http://en.wikipedia.org/wiki/Red_Bull_Racing","name":"Red Bull","nationality":"Austrian"}},{"position":"2","positionText":"2","points":"439","wins":"4","Constructor":{"constructorId":"ferrari","url":"http://en.wikipedia.org/wiki/Scuderia_Ferrari","name":"Ferrari","nationality":"Italian"}},{"position":"3","positionText":"3","points":"373","wins":"0","Constructor":{"constructorId":"mercedes","url":"http://en.wikipedia.org/wiki/Mercedes-Benz_in_Formula_One","name":"Mercedes","nationality":"German"}},{"position":"4","positionText":"4","points":"129","wins":"0","Constructor":{"constructorId":"mclaren","url":"http://en.wikipedia.org/wiki/McLaren","name":"McLaren","nationality":"British"}},{"position":"5","positionText":"5","points":"125","wins":"0","Constructor":{"constructorId":"alpine","url":"http://en.wikipedia.org/wiki/Alpine_F1_Team","name":"Alpine F1 Team","nationality":"French"}},{"position":"6","positionText":"6","points":"52","wins":"0","Constructor":{"constructorId":"alfa","url":"http://en.wikipedia.org/wiki/Alfa_Romeo_in_Formula_One","name":"Alfa Romeo","nationality":"Swiss"}},{"position":"7","positionText":"7","points":"37","wins":"0","Constructor":{"constructorId":"aston_martin","url":"http://en.wikipedia.org/wiki/Aston_Martin_in_Formula_One","name":"Aston Martin","nationality":"British"}},{"position":"8","positionText":"8","points":"34","wins":"0","Constructor":{"constructorId":"haas","url":"http://en.wikipedia.org/wiki/Haas_F1_Team","name":"Haas F1 Team","nationality":"American"}},{"position":"9","positionText":"9","points":"34","wins":"0","Constructor":{"constructorId":"alphatauri","url":"http://en.wikipedia.org/wiki/Scuderia_AlphaTauri","name":"AlphaTauri","nationality":"Italian"}},{"position":"10","positionText":"10","points":"6","wins":"0","Constructor":{"constructorId":"williams","url":"http://en.wikipedia.org/wiki/Williams_Grand_Prix_Engineering","name":"Williams","nationality":"British"}}]}]}}} ================================================ FILE: tests/testdata/countries.json ================================================ [{"name":"Afghanistan","topLevelDomain":[".af"],"alpha2Code":"AF","alpha3Code":"AFG","callingCodes":["93"],"capital":"Kabul","altSpellings":["AF","Afġānistān"],"subregion":"Southern Asia","region":"Asia","population":40218234,"latlng":[33.0,65.0],"demonym":"Afghan","area":652230.0,"timezones":["UTC+04:30"],"borders":["IRN","PAK","TKM","UZB","TJK","CHN"],"nativeName":"افغانستان","numericCode":"004","flags":{"svg":"https://upload.wikimedia.org/wikipedia/commons/5/5c/Flag_of_the_Taliban.svg","png":"https://upload.wikimedia.org/wikipedia/commons/thumb/5/5c/Flag_of_the_Taliban.svg/320px-Flag_of_the_Taliban.svg.png"},"currencies":[{"code":"AFN","name":"Afghan afghani","symbol":"؋"}],"languages":[{"iso639_1":"ps","iso639_2":"pus","name":"Pashto","nativeName":"پښتو"},{"iso639_1":"uz","iso639_2":"uzb","name":"Uzbek","nativeName":"Oʻzbek"},{"iso639_1":"tk","iso639_2":"tuk","name":"Turkmen","nativeName":"Türkmen"}],"translations":{"br":"Afghanistan","pt":"Afeganistão","nl":"Afghanistan","hr":"Afganistan","fa":"افغانستان","de":"Afghanistan","es":"Afganistán","fr":"Afghanistan","ja":"アフガニスタン","it":"Afghanistan","hu":"Afganisztán"},"flag":"https://upload.wikimedia.org/wikipedia/commons/5/5c/Flag_of_the_Taliban.svg","regionalBlocs":[{"acronym":"SAARC","name":"South Asian Association for Regional Cooperation"}],"cioc":"AFG","independent":true},{"name":"Åland Islands","topLevelDomain":[".ax"],"alpha2Code":"AX","alpha3Code":"ALA","callingCodes":["358"],"capital":"Mariehamn","altSpellings":["AX","Aaland","Aland","Ahvenanmaa"],"subregion":"Northern Europe","region":"Europe","population":28875,"latlng":[60.116667,19.9],"demonym":"Ålandish","area":1580.0,"timezones":["UTC+02:00"],"nativeName":"Åland","numericCode":"248","flags":{"svg":"https://flagcdn.com/ax.svg","png":"https://flagcdn.com/w320/ax.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"sv","iso639_2":"swe","name":"Swedish","nativeName":"svenska"}],"translations":{"br":"Åland","pt":"Ilhas de Aland","nl":"Ålandeilanden","hr":"Ålandski otoci","fa":"جزایر الند","de":"Åland","es":"Alandia","fr":"Åland","ja":"オーランド諸島","it":"Isole Aland","hu":"Åland-szigetek"},"flag":"https://flagcdn.com/ax.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"independent":false},{"name":"Albania","topLevelDomain":[".al"],"alpha2Code":"AL","alpha3Code":"ALB","callingCodes":["355"],"capital":"Tirana","altSpellings":["AL","Shqipëri","Shqipëria","Shqipnia"],"subregion":"Southern Europe","region":"Europe","population":2837743,"latlng":[41.0,20.0],"demonym":"Albanian","area":28748.0,"gini":33.2,"timezones":["UTC+01:00"],"borders":["MNE","GRC","MKD","UNK"],"nativeName":"Shqipëria","numericCode":"008","flags":{"svg":"https://flagcdn.com/al.svg","png":"https://flagcdn.com/w320/al.png"},"currencies":[{"code":"ALL","name":"Albanian lek","symbol":"L"}],"languages":[{"iso639_1":"sq","iso639_2":"sqi","name":"Albanian","nativeName":"Shqip"}],"translations":{"br":"Albania","pt":"Albânia","nl":"Albanië","hr":"Albanija","fa":"آلبانی","de":"Albanien","es":"Albania","fr":"Albanie","ja":"アルバニア","it":"Albania","hu":"Albánia"},"flag":"https://flagcdn.com/al.svg","regionalBlocs":[{"acronym":"CEFTA","name":"Central European Free Trade Agreement"}],"cioc":"ALB","independent":true},{"name":"Algeria","topLevelDomain":[".dz"],"alpha2Code":"DZ","alpha3Code":"DZA","callingCodes":["213"],"capital":"Algiers","altSpellings":["DZ","Dzayer","Algérie"],"subregion":"Northern Africa","region":"Africa","population":44700000,"latlng":[28.0,3.0],"demonym":"Algerian","area":2381741.0,"gini":27.6,"timezones":["UTC+01:00"],"borders":["TUN","LBY","NER","ESH","MRT","MLI","MAR"],"nativeName":"الجزائر","numericCode":"012","flags":{"svg":"https://flagcdn.com/dz.svg","png":"https://flagcdn.com/w320/dz.png"},"currencies":[{"code":"DZD","name":"Algerian dinar","symbol":"د.ج"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Aljeria","pt":"Argélia","nl":"Algerije","hr":"Alžir","fa":"الجزایر","de":"Algerien","es":"Argelia","fr":"Algérie","ja":"アルジェリア","it":"Algeria","hu":"Algéria"},"flag":"https://flagcdn.com/dz.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]},{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"ALG","independent":true},{"name":"American Samoa","topLevelDomain":[".as"],"alpha2Code":"AS","alpha3Code":"ASM","callingCodes":["1"],"capital":"Pago Pago","altSpellings":["AS","Amerika Sāmoa","Amelika Sāmoa","Sāmoa Amelika"],"subregion":"Polynesia","region":"Oceania","population":55197,"latlng":[-14.33333333,-170.0],"demonym":"American Samoan","area":199.0,"timezones":["UTC-11:00"],"nativeName":"American Samoa","numericCode":"016","flags":{"svg":"https://flagcdn.com/as.svg","png":"https://flagcdn.com/w320/as.png"},"currencies":[{"code":"USD","name":"United States Dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"sm","iso639_2":"smo","name":"Samoan","nativeName":"gagana fa'a Samoa"}],"translations":{"br":"Samoa Amerikan","pt":"Samoa Americana","nl":"Amerikaans Samoa","hr":"Američka Samoa","fa":"ساموآی آمریکا","de":"Amerikanisch-Samoa","es":"Samoa Americana","fr":"Samoa américaines","ja":"アメリカ領サモア","it":"Samoa Americane","hu":"Amerikai Szamoa"},"flag":"https://flagcdn.com/as.svg","cioc":"ASA","independent":false},{"name":"Andorra","topLevelDomain":[".ad"],"alpha2Code":"AD","alpha3Code":"AND","callingCodes":["376"],"capital":"Andorra la Vella","altSpellings":["AD","Principality of Andorra","Principat d'Andorra"],"subregion":"Southern Europe","region":"Europe","population":77265,"latlng":[42.5,1.5],"demonym":"Andorran","area":468.0,"timezones":["UTC+01:00"],"borders":["FRA","ESP"],"nativeName":"Andorra","numericCode":"020","flags":{"svg":"https://flagcdn.com/ad.svg","png":"https://flagcdn.com/w320/ad.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"ca","iso639_2":"cat","name":"Catalan","nativeName":"català"}],"translations":{"br":"Andorra","pt":"Andorra","nl":"Andorra","hr":"Andora","fa":"آندورا","de":"Andorra","es":"Andorra","fr":"Andorre","ja":"アンドラ","it":"Andorra","hu":"Andorra"},"flag":"https://flagcdn.com/ad.svg","cioc":"AND","independent":true},{"name":"Angola","topLevelDomain":[".ao"],"alpha2Code":"AO","alpha3Code":"AGO","callingCodes":["244"],"capital":"Luanda","altSpellings":["AO","República de Angola","ʁɛpublika de an'ɡɔla"],"subregion":"Middle Africa","region":"Africa","population":32866268,"latlng":[-12.5,18.5],"demonym":"Angolan","area":1246700.0,"gini":51.3,"timezones":["UTC+01:00"],"borders":["COG","COD","ZMB","NAM"],"nativeName":"Angola","numericCode":"024","flags":{"svg":"https://flagcdn.com/ao.svg","png":"https://flagcdn.com/w320/ao.png"},"currencies":[{"code":"AOA","name":"Angolan kwanza","symbol":"Kz"}],"languages":[{"iso639_1":"pt","iso639_2":"por","name":"Portuguese","nativeName":"Português"}],"translations":{"br":"Angola","pt":"Angola","nl":"Angola","hr":"Angola","fa":"آنگولا","de":"Angola","es":"Angola","fr":"Angola","ja":"アンゴラ","it":"Angola","hu":"Angola"},"flag":"https://flagcdn.com/ao.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"ANG","independent":true},{"name":"Anguilla","topLevelDomain":[".ai"],"alpha2Code":"AI","alpha3Code":"AIA","callingCodes":["1"],"capital":"The Valley","altSpellings":["AI"],"subregion":"Caribbean","region":"Americas","population":13452,"latlng":[18.25,-63.16666666],"demonym":"Anguillian","area":91.0,"timezones":["UTC-04:00"],"nativeName":"Anguilla","numericCode":"660","flags":{"svg":"https://flagcdn.com/ai.svg","png":"https://flagcdn.com/w320/ai.png"},"currencies":[{"code":"XCD","name":"East Caribbean dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Anguilla","pt":"Anguila","nl":"Anguilla","hr":"Angvila","fa":"آنگویلا","de":"Anguilla","es":"Anguilla","fr":"Anguilla","ja":"アンギラ","it":"Anguilla","hu":"Anguilla"},"flag":"https://flagcdn.com/ai.svg","independent":false},{"name":"Antarctica","topLevelDomain":[".aq"],"alpha2Code":"AQ","alpha3Code":"ATA","callingCodes":["672"],"subregion":"Antarctica","region":"Polar","population":1000,"latlng":[-74.65,4.48],"demonym":"Antarctic","area":1.4E7,"timezones":["UTC-03:00","UTC+03:00","UTC+05:00","UTC+06:00","UTC+07:00","UTC+08:00","UTC+10:00","UTC+12:00"],"nativeName":"Antarctica","numericCode":"010","flags":{"svg":"https://flagcdn.com/aq.svg","png":"https://flagcdn.com/w320/aq.png"},"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"ru","iso639_2":"rus","name":"Russian","nativeName":"Русский"}],"translations":{"br":"Antarktika","pt":"Antárctida","nl":"Antarctica","hr":"Antarktika","fa":"جنوبگان","de":"Antarktika","es":"Antártida","fr":"Antarctique","ja":"南極大陸","it":"Antartide","hu":"Antarktisz"},"flag":"https://flagcdn.com/aq.svg","independent":false},{"name":"Antigua and Barbuda","topLevelDomain":[".ag"],"alpha2Code":"AG","alpha3Code":"ATG","callingCodes":["1"],"capital":"Saint John's","altSpellings":["AG"],"subregion":"Caribbean","region":"Americas","population":97928,"latlng":[17.05,-61.8],"demonym":"Antiguan, Barbudan","area":442.0,"timezones":["UTC-04:00"],"nativeName":"Antigua and Barbuda","numericCode":"028","flags":{"svg":"https://flagcdn.com/ag.svg","png":"https://flagcdn.com/w320/ag.png"},"currencies":[{"code":"XCD","name":"East Caribbean dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Antigua ha Barbuda","pt":"Antígua e Barbuda","nl":"Antigua en Barbuda","hr":"Antigva i Barbuda","fa":"آنتیگوا و باربودا","de":"Antigua und Barbuda","es":"Antigua y Barbuda","fr":"Antigua-et-Barbuda","ja":"アンティグア・バーブーダ","it":"Antigua e Barbuda","hu":"Antigua és Barbuda"},"flag":"https://flagcdn.com/ag.svg","regionalBlocs":[{"acronym":"CARICOM","name":"Caribbean Community","otherNames":["Comunidad del Caribe","Communauté Caribéenne","Caribische Gemeenschap"]}],"cioc":"ANT","independent":true},{"name":"Argentina","topLevelDomain":[".ar"],"alpha2Code":"AR","alpha3Code":"ARG","callingCodes":["54"],"capital":"Buenos Aires","altSpellings":["AR","Argentine Republic","República Argentina"],"subregion":"South America","region":"Americas","population":45376763,"latlng":[-34.0,-64.0],"demonym":"Argentinean","area":2780400.0,"gini":42.9,"timezones":["UTC-03:00"],"borders":["BOL","BRA","CHL","PRY","URY"],"nativeName":"Argentina","numericCode":"032","flags":{"svg":"https://flagcdn.com/ar.svg","png":"https://flagcdn.com/w320/ar.png"},"currencies":[{"code":"ARS","name":"Argentine peso","symbol":"$"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"},{"iso639_1":"gn","iso639_2":"grn","name":"Guaraní","nativeName":"Avañe'ẽ"}],"translations":{"br":"Arc'hantina","pt":"Argentina","nl":"Argentinië","hr":"Argentina","fa":"آرژانتین","de":"Argentinien","es":"Argentina","fr":"Argentine","ja":"アルゼンチン","it":"Argentina","hu":"Argentína"},"flag":"https://flagcdn.com/ar.svg","regionalBlocs":[{"acronym":"USAN","name":"Union of South American Nations","otherAcronyms":["UNASUR","UNASUL","UZAN"],"otherNames":["Unión de Naciones Suramericanas","União de Nações Sul-Americanas","Unie van Zuid-Amerikaanse Naties","South American Union"]}],"cioc":"ARG","independent":true},{"name":"Armenia","topLevelDomain":[".am"],"alpha2Code":"AM","alpha3Code":"ARM","callingCodes":["374"],"capital":"Yerevan","altSpellings":["AM","Hayastan","Republic of Armenia","Հայաստանի Հանրապետություն"],"subregion":"Western Asia","region":"Asia","population":2963234,"latlng":[40.0,45.0],"demonym":"Armenian","area":29743.0,"gini":29.9,"timezones":["UTC+04:00"],"borders":["AZE","GEO","IRN","TUR"],"nativeName":"Հայաստան","numericCode":"051","flags":{"svg":"https://flagcdn.com/am.svg","png":"https://flagcdn.com/w320/am.png"},"currencies":[{"code":"AMD","name":"Armenian dram","symbol":"֏"}],"languages":[{"iso639_1":"hy","iso639_2":"hye","name":"Armenian","nativeName":"Հայերեն"}],"translations":{"br":"Armenia","pt":"Arménia","nl":"Armenië","hr":"Armenija","fa":"ارمنستان","de":"Armenien","es":"Armenia","fr":"Arménie","ja":"アルメニア","it":"Armenia","hu":"Örményország"},"flag":"https://flagcdn.com/am.svg","regionalBlocs":[{"acronym":"EEU","name":"Eurasian Economic Union","otherAcronyms":["EAEU"]}],"cioc":"ARM","independent":true},{"name":"Aruba","topLevelDomain":[".aw"],"alpha2Code":"AW","alpha3Code":"ABW","callingCodes":["297"],"capital":"Oranjestad","altSpellings":["AW"],"subregion":"Caribbean","region":"Americas","population":106766,"latlng":[12.5,-69.96666666],"demonym":"Aruban","area":180.0,"timezones":["UTC-04:00"],"nativeName":"Aruba","numericCode":"533","flags":{"svg":"https://flagcdn.com/aw.svg","png":"https://flagcdn.com/w320/aw.png"},"currencies":[{"code":"AWG","name":"Aruban florin","symbol":"ƒ"}],"languages":[{"iso639_1":"nl","iso639_2":"nld","name":"Dutch","nativeName":"Nederlands"},{"iso639_1":"pa","iso639_2":"pan","name":"(Eastern) Punjabi","nativeName":"ਪੰਜਾਬੀ"}],"translations":{"br":"Aruba","pt":"Aruba","nl":"Aruba","hr":"Aruba","fa":"آروبا","de":"Aruba","es":"Aruba","fr":"Aruba","ja":"アルバ","it":"Aruba","hu":"Aruba"},"flag":"https://flagcdn.com/aw.svg","cioc":"ARU","independent":true},{"name":"Australia","topLevelDomain":[".au"],"alpha2Code":"AU","alpha3Code":"AUS","callingCodes":["61"],"capital":"Canberra","altSpellings":["AU"],"subregion":"Australia and New Zealand","region":"Oceania","population":25687041,"latlng":[-27.0,133.0],"demonym":"Australian","area":7692024.0,"gini":34.4,"timezones":["UTC+05:00","UTC+06:30","UTC+07:00","UTC+08:00","UTC+09:30","UTC+10:00","UTC+10:30","UTC+11:30"],"nativeName":"Australia","numericCode":"036","flags":{"svg":"https://flagcdn.com/au.svg","png":"https://flagcdn.com/w320/au.png"},"currencies":[{"code":"AUD","name":"Australian dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Aostralia","pt":"Austrália","nl":"Australië","hr":"Australija","fa":"استرالیا","de":"Australien","es":"Australia","fr":"Australie","ja":"オーストラリア","it":"Australia","hu":"Ausztrália"},"flag":"https://flagcdn.com/au.svg","cioc":"AUS","independent":true},{"name":"Austria","topLevelDomain":[".at"],"alpha2Code":"AT","alpha3Code":"AUT","callingCodes":["43"],"capital":"Vienna","altSpellings":["AT","Österreich","Osterreich","Oesterreich"],"subregion":"Central Europe","region":"Europe","population":8917205,"latlng":[47.33333333,13.33333333],"demonym":"Austrian","area":83871.0,"gini":30.8,"timezones":["UTC+01:00"],"borders":["CZE","DEU","HUN","ITA","LIE","SVK","SVN","CHE"],"nativeName":"Österreich","numericCode":"040","flags":{"svg":"https://flagcdn.com/at.svg","png":"https://flagcdn.com/w320/at.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"de","iso639_2":"deu","name":"German","nativeName":"Deutsch"}],"translations":{"br":"Aostria","pt":"Áustria","nl":"Oostenrijk","hr":"Austrija","fa":"اتریش","de":"Österreich","es":"Austria","fr":"Autriche","ja":"オーストリア","it":"Austria","hu":"Ausztria"},"flag":"https://flagcdn.com/at.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"AUT","independent":true},{"name":"Azerbaijan","topLevelDomain":[".az"],"alpha2Code":"AZ","alpha3Code":"AZE","callingCodes":["994"],"capital":"Baku","altSpellings":["AZ","Republic of Azerbaijan","Azərbaycan Respublikası"],"subregion":"Western Asia","region":"Asia","population":10110116,"latlng":[40.5,47.5],"demonym":"Azerbaijani","area":86600.0,"gini":26.6,"timezones":["UTC+04:00"],"borders":["ARM","GEO","IRN","RUS","TUR"],"nativeName":"Azərbaycan","numericCode":"031","flags":{"svg":"https://flagcdn.com/az.svg","png":"https://flagcdn.com/w320/az.png"},"currencies":[{"code":"AZN","name":"Azerbaijani manat","symbol":"₼"}],"languages":[{"iso639_1":"az","iso639_2":"aze","name":"Azerbaijani","nativeName":"azərbaycan dili"}],"translations":{"br":"Azerbaidjan","pt":"Azerbaijão","nl":"Azerbeidzjan","hr":"Azerbajdžan","fa":"آذربایجان","de":"Aserbaidschan","es":"Azerbaiyán","fr":"Azerbaïdjan","ja":"アゼルバイジャン","it":"Azerbaijan","hu":"Azerbajdzsán"},"flag":"https://flagcdn.com/az.svg","cioc":"AZE","independent":true},{"name":"Bahamas","topLevelDomain":[".bs"],"alpha2Code":"BS","alpha3Code":"BHS","callingCodes":["1"],"capital":"Nassau","altSpellings":["BS","Commonwealth of the Bahamas"],"subregion":"Caribbean","region":"Americas","population":393248,"latlng":[24.25,-76.0],"demonym":"Bahamian","area":13943.0,"timezones":["UTC-05:00"],"nativeName":"Bahamas","numericCode":"044","flags":{"svg":"https://flagcdn.com/bs.svg","png":"https://flagcdn.com/w320/bs.png"},"currencies":[{"code":"BSD","name":"Bahamian dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Bahamas","pt":"Baamas","nl":"Bahama’s","hr":"Bahami","fa":"باهاما","de":"Bahamas","es":"Bahamas","fr":"Bahamas","ja":"バハマ","it":"Bahamas","hu":"Bahama-szigetek"},"flag":"https://flagcdn.com/bs.svg","regionalBlocs":[{"acronym":"CARICOM","name":"Caribbean Community","otherNames":["Comunidad del Caribe","Communauté Caribéenne","Caribische Gemeenschap"]}],"cioc":"BAH","independent":true},{"name":"Bahrain","topLevelDomain":[".bh"],"alpha2Code":"BH","alpha3Code":"BHR","callingCodes":["973"],"capital":"Manama","altSpellings":["BH","Kingdom of Bahrain","Mamlakat al-Baḥrayn"],"subregion":"Western Asia","region":"Asia","population":1701583,"latlng":[26.0,50.55],"demonym":"Bahraini","area":765.0,"timezones":["UTC+03:00"],"nativeName":"‏البحرين","numericCode":"048","flags":{"svg":"https://flagcdn.com/bh.svg","png":"https://flagcdn.com/w320/bh.png"},"currencies":[{"code":"BHD","name":"Bahraini dinar","symbol":".د.ب"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Bahrein","pt":"Barém","nl":"Bahrein","hr":"Bahrein","fa":"بحرین","de":"Bahrain","es":"Bahrein","fr":"Bahreïn","ja":"バーレーン","it":"Bahrein","hu":"Bahrein"},"flag":"https://flagcdn.com/bh.svg","regionalBlocs":[{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"BRN","independent":true},{"name":"Bangladesh","topLevelDomain":[".bd"],"alpha2Code":"BD","alpha3Code":"BGD","callingCodes":["880"],"capital":"Dhaka","altSpellings":["BD","People's Republic of Bangladesh","Gônôprôjatôntri Bangladesh"],"subregion":"Southern Asia","region":"Asia","population":164689383,"latlng":[24.0,90.0],"demonym":"Bangladeshi","area":147570.0,"gini":32.4,"timezones":["UTC+06:00"],"borders":["MMR","IND"],"nativeName":"Bangladesh","numericCode":"050","flags":{"svg":"https://flagcdn.com/bd.svg","png":"https://flagcdn.com/w320/bd.png"},"currencies":[{"code":"BDT","name":"Bangladeshi taka","symbol":"৳"}],"languages":[{"iso639_1":"bn","iso639_2":"ben","name":"Bengali","nativeName":"বাংলা"}],"translations":{"br":"Bangladesh","pt":"Bangladeche","nl":"Bangladesh","hr":"Bangladeš","fa":"بنگلادش","de":"Bangladesch","es":"Bangladesh","fr":"Bangladesh","ja":"バングラデシュ","it":"Bangladesh","hu":"Banglades"},"flag":"https://flagcdn.com/bd.svg","regionalBlocs":[{"acronym":"SAARC","name":"South Asian Association for Regional Cooperation"}],"cioc":"BAN","independent":true},{"name":"Barbados","topLevelDomain":[".bb"],"alpha2Code":"BB","alpha3Code":"BRB","callingCodes":["1"],"capital":"Bridgetown","altSpellings":["BB"],"subregion":"Caribbean","region":"Americas","population":287371,"latlng":[13.16666666,-59.53333333],"demonym":"Barbadian","area":430.0,"timezones":["UTC-04:00"],"nativeName":"Barbados","numericCode":"052","flags":{"svg":"https://flagcdn.com/bb.svg","png":"https://flagcdn.com/w320/bb.png"},"currencies":[{"code":"BBD","name":"Barbadian dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Barbados","pt":"Barbados","nl":"Barbados","hr":"Barbados","fa":"باربادوس","de":"Barbados","es":"Barbados","fr":"Barbade","ja":"バルバドス","it":"Barbados","hu":"Barbados"},"flag":"https://flagcdn.com/bb.svg","regionalBlocs":[{"acronym":"CARICOM","name":"Caribbean Community","otherNames":["Comunidad del Caribe","Communauté Caribéenne","Caribische Gemeenschap"]}],"cioc":"BAR","independent":true},{"name":"Belarus","topLevelDomain":[".by"],"alpha2Code":"BY","alpha3Code":"BLR","callingCodes":["375"],"capital":"Minsk","altSpellings":["BY","Bielaruś","Republic of Belarus","Белоруссия","Республика Беларусь","Belorussiya","Respublika Belarus’"],"subregion":"Eastern Europe","region":"Europe","population":9398861,"latlng":[53.0,28.0],"demonym":"Belarusian","area":207600.0,"gini":25.3,"timezones":["UTC+03:00"],"borders":["LVA","LTU","POL","RUS","UKR"],"nativeName":"Белару́сь","numericCode":"112","flags":{"svg":"https://flagcdn.com/by.svg","png":"https://flagcdn.com/w320/by.png"},"currencies":[{"code":"BYN","name":"New Belarusian ruble","symbol":"Br"},{"code":"BYR","name":"Old Belarusian ruble","symbol":"Br"}],"languages":[{"iso639_1":"be","iso639_2":"bel","name":"Belarusian","nativeName":"беларуская мова"},{"iso639_1":"ru","iso639_2":"rus","name":"Russian","nativeName":"Русский"}],"translations":{"br":"Belarus","pt":"Bielorrússia","nl":"Wit-Rusland","hr":"Bjelorusija","fa":"بلاروس","de":"Weißrussland","es":"Bielorrusia","fr":"Biélorussie","ja":"ベラルーシ","it":"Bielorussia","hu":"Fehéroroszország"},"flag":"https://flagcdn.com/by.svg","regionalBlocs":[{"acronym":"EEU","name":"Eurasian Economic Union","otherAcronyms":["EAEU"]}],"cioc":"BLR","independent":true},{"name":"Belgium","topLevelDomain":[".be"],"alpha2Code":"BE","alpha3Code":"BEL","callingCodes":["32"],"capital":"Brussels","altSpellings":["BE","België","Belgie","Belgien","Belgique","Kingdom of Belgium","Koninkrijk België","Royaume de Belgique","Königreich Belgien"],"subregion":"Western Europe","region":"Europe","population":11555997,"latlng":[50.83333333,4.0],"demonym":"Belgian","area":30528.0,"gini":27.2,"timezones":["UTC+01:00"],"borders":["FRA","DEU","LUX","NLD"],"nativeName":"België","numericCode":"056","flags":{"svg":"https://flagcdn.com/be.svg","png":"https://flagcdn.com/w320/be.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"nl","iso639_2":"nld","name":"Dutch","nativeName":"Nederlands"},{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"de","iso639_2":"deu","name":"German","nativeName":"Deutsch"}],"translations":{"br":"Belgia","pt":"Bélgica","nl":"België","hr":"Belgija","fa":"بلژیک","de":"Belgien","es":"Bélgica","fr":"Belgique","ja":"ベルギー","it":"Belgio","hu":"Belgium"},"flag":"https://flagcdn.com/be.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"BEL","independent":true},{"name":"Belize","topLevelDomain":[".bz"],"alpha2Code":"BZ","alpha3Code":"BLZ","callingCodes":["501"],"capital":"Belmopan","altSpellings":["BZ"],"subregion":"Central America","region":"Americas","population":397621,"latlng":[17.25,-88.75],"demonym":"Belizean","area":22966.0,"gini":53.3,"timezones":["UTC-06:00"],"borders":["GTM","MEX"],"nativeName":"Belize","numericCode":"084","flags":{"svg":"https://flagcdn.com/bz.svg","png":"https://flagcdn.com/w320/bz.png"},"currencies":[{"code":"BZD","name":"Belize dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Belize","pt":"Belize","nl":"Belize","hr":"Belize","fa":"بلیز","de":"Belize","es":"Belice","fr":"Belize","ja":"ベリーズ","it":"Belize","hu":"Belize"},"flag":"https://flagcdn.com/bz.svg","regionalBlocs":[{"acronym":"CARICOM","name":"Caribbean Community","otherNames":["Comunidad del Caribe","Communauté Caribéenne","Caribische Gemeenschap"]},{"acronym":"CAIS","name":"Central American Integration System","otherAcronyms":["SICA"],"otherNames":["Sistema de la Integración Centroamericana,"]}],"cioc":"BIZ","independent":true},{"name":"Benin","topLevelDomain":[".bj"],"alpha2Code":"BJ","alpha3Code":"BEN","callingCodes":["229"],"capital":"Porto-Novo","altSpellings":["BJ","Republic of Benin","République du Bénin"],"subregion":"Western Africa","region":"Africa","population":12123198,"latlng":[9.5,2.25],"demonym":"Beninese","area":112622.0,"gini":47.8,"timezones":["UTC+01:00"],"borders":["BFA","NER","NGA","TGO"],"nativeName":"Bénin","numericCode":"204","flags":{"svg":"https://flagcdn.com/bj.svg","png":"https://flagcdn.com/w320/bj.png"},"currencies":[{"code":"XOF","name":"West African CFA franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Benin","pt":"Benim","nl":"Benin","hr":"Benin","fa":"بنین","de":"Benin","es":"Benín","fr":"Bénin","ja":"ベナン","it":"Benin","hu":"Benin"},"flag":"https://flagcdn.com/bj.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"BEN","independent":true},{"name":"Bermuda","topLevelDomain":[".bm"],"alpha2Code":"BM","alpha3Code":"BMU","callingCodes":["1"],"capital":"Hamilton","altSpellings":["BM","The Islands of Bermuda","The Bermudas","Somers Isles"],"subregion":"Northern America","region":"Americas","population":63903,"latlng":[32.33333333,-64.75],"demonym":"Bermudian","area":54.0,"timezones":["UTC-04:00"],"nativeName":"Bermuda","numericCode":"060","flags":{"svg":"https://flagcdn.com/bm.svg","png":"https://flagcdn.com/w320/bm.png"},"currencies":[{"code":"BMD","name":"Bermudian dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Bermuda","pt":"Bermudas","nl":"Bermuda","hr":"Bermudi","fa":"برمودا","de":"Bermuda","es":"Bermudas","fr":"Bermudes","ja":"バミューダ","it":"Bermuda","hu":"Bermuda"},"flag":"https://flagcdn.com/bm.svg","cioc":"BER","independent":false},{"name":"Bhutan","topLevelDomain":[".bt"],"alpha2Code":"BT","alpha3Code":"BTN","callingCodes":["975"],"capital":"Thimphu","altSpellings":["BT","Kingdom of Bhutan"],"subregion":"Southern Asia","region":"Asia","population":771612,"latlng":[27.5,90.5],"demonym":"Bhutanese","area":38394.0,"gini":37.4,"timezones":["UTC+06:00"],"borders":["CHN","IND"],"nativeName":"ʼbrug-yul","numericCode":"064","flags":{"svg":"https://flagcdn.com/bt.svg","png":"https://flagcdn.com/w320/bt.png"},"currencies":[{"code":"BTN","name":"Bhutanese ngultrum","symbol":"Nu."},{"code":"INR","name":"Indian rupee","symbol":"₹"}],"languages":[{"iso639_1":"dz","iso639_2":"dzo","name":"Dzongkha","nativeName":"རྫོང་ཁ"}],"translations":{"br":"Bhoutan","pt":"Butão","nl":"Bhutan","hr":"Butan","fa":"بوتان","de":"Bhutan","es":"Bután","fr":"Bhoutan","ja":"ブータン","it":"Bhutan","hu":"Bhután"},"flag":"https://flagcdn.com/bt.svg","regionalBlocs":[{"acronym":"SAARC","name":"South Asian Association for Regional Cooperation"}],"cioc":"BHU","independent":true},{"name":"Bolivia (Plurinational State of)","topLevelDomain":[".bo"],"alpha2Code":"BO","alpha3Code":"BOL","callingCodes":["591"],"capital":"Sucre","altSpellings":["BO","Buliwya","Wuliwya","Plurinational State of Bolivia","Estado Plurinacional de Bolivia","Buliwya Mamallaqta","Wuliwya Suyu","Tetã Volívia"],"subregion":"South America","region":"Americas","population":11673029,"latlng":[-17.0,-65.0],"demonym":"Bolivian","area":1098581.0,"gini":41.6,"timezones":["UTC-04:00"],"borders":["ARG","BRA","CHL","PRY","PER"],"nativeName":"Bolivia","numericCode":"068","flags":{"svg":"https://flagcdn.com/bo.svg","png":"https://flagcdn.com/w320/bo.png"},"currencies":[{"code":"BOB","name":"Bolivian boliviano","symbol":"Bs."}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"},{"iso639_1":"ay","iso639_2":"aym","name":"Aymara","nativeName":"aymar aru"},{"iso639_1":"qu","iso639_2":"que","name":"Quechua","nativeName":"Runa Simi"}],"translations":{"br":"Bolivia","pt":"Bolívia","nl":"Bolivia","hr":"Bolivija","fa":"بولیوی","de":"Bolivien","es":"Bolivia","fr":"Bolivie","ja":"ボリビア多民族国","it":"Bolivia","hu":"Bolívia"},"flag":"https://flagcdn.com/bo.svg","regionalBlocs":[{"acronym":"USAN","name":"Union of South American Nations","otherAcronyms":["UNASUR","UNASUL","UZAN"],"otherNames":["Unión de Naciones Suramericanas","União de Nações Sul-Americanas","Unie van Zuid-Amerikaanse Naties","South American Union"]}],"cioc":"BOL","independent":true},{"name":"Bonaire, Sint Eustatius and Saba","topLevelDomain":[".an",".nl"],"alpha2Code":"BQ","alpha3Code":"BES","callingCodes":["599"],"capital":"Kralendijk","altSpellings":["BQ","Boneiru"],"subregion":"Caribbean","region":"Americas","population":17408,"latlng":[12.15,-68.266667],"demonym":"Dutch","area":294.0,"timezones":["UTC-04:00"],"nativeName":"Bonaire","numericCode":"535","flags":{"svg":"https://flagcdn.com/bq.svg","png":"https://flagcdn.com/w320/bq.png"},"currencies":[{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"nl","iso639_2":"nld","name":"Dutch","nativeName":"Nederlands"}],"translations":{"br":"Bonaire, Sint Eustatius ha Saba","pt":"Bonaire","nl":"Caribisch Nederland","hr":"Bonaire, Sint Eustatius and Saba","fa":"بونیر","de":"Bonaire, Sint Eustatius und Saba","es":"Bonaire, Sint Eustatius and Saba","fr":"Bonaire, Saint-Eustache et Saba","ja":"Bonaire, Sint Eustatius and Saba","it":"Bonaire, Saint-Eustache e Saba","hu":"Bonaire"},"flag":"https://flagcdn.com/bq.svg","independent":true},{"name":"Bosnia and Herzegovina","topLevelDomain":[".ba"],"alpha2Code":"BA","alpha3Code":"BIH","callingCodes":["387"],"capital":"Sarajevo","altSpellings":["BA","Bosnia-Herzegovina","Босна и Херцеговина"],"subregion":"Southern Europe","region":"Europe","population":3280815,"latlng":[44.0,18.0],"demonym":"Bosnian, Herzegovinian","area":51209.0,"gini":33.0,"timezones":["UTC+01:00"],"borders":["HRV","MNE","SRB"],"nativeName":"Bosna i Hercegovina","numericCode":"070","flags":{"svg":"https://flagcdn.com/ba.svg","png":"https://flagcdn.com/w320/ba.png"},"currencies":[{"code":"BAM","name":"Bosnia and Herzegovina convertible mark","symbol":"KM"}],"languages":[{"iso639_1":"bs","iso639_2":"bos","name":"Bosnian","nativeName":"bosanski jezik"},{"iso639_1":"hr","iso639_2":"hrv","name":"Croatian","nativeName":"hrvatski jezik"},{"iso639_1":"sr","iso639_2":"srp","name":"Serbian","nativeName":"српски језик"}],"translations":{"br":"Bosnia-ha-Herzegovina","pt":"Bósnia e Herzegovina","nl":"Bosnië en Herzegovina","hr":"Bosna i Hercegovina","fa":"بوسنی و هرزگوین","de":"Bosnien und Herzegowina","es":"Bosnia y Herzegovina","fr":"Bosnie-Herzégovine","ja":"ボスニア・ヘルツェゴビナ","it":"Bosnia ed Erzegovina","hu":"Bosznia-Hercegovina"},"flag":"https://flagcdn.com/ba.svg","regionalBlocs":[{"acronym":"CEFTA","name":"Central European Free Trade Agreement"}],"cioc":"BIH","independent":true},{"name":"Botswana","topLevelDomain":[".bw"],"alpha2Code":"BW","alpha3Code":"BWA","callingCodes":["267"],"capital":"Gaborone","altSpellings":["BW","Republic of Botswana","Lefatshe la Botswana"],"subregion":"Southern Africa","region":"Africa","population":2351625,"latlng":[-22.0,24.0],"demonym":"Motswana","area":582000.0,"gini":53.3,"timezones":["UTC+02:00"],"borders":["NAM","ZAF","ZMB","ZWE"],"nativeName":"Botswana","numericCode":"072","flags":{"svg":"https://flagcdn.com/bw.svg","png":"https://flagcdn.com/w320/bw.png"},"currencies":[{"code":"BWP","name":"Botswana pula","symbol":"P"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"tn","iso639_2":"tsn","name":"Tswana","nativeName":"Setswana"}],"translations":{"br":"Botswana","pt":"Botsuana","nl":"Botswana","hr":"Bocvana","fa":"بوتسوانا","de":"Botswana","es":"Botswana","fr":"Botswana","ja":"ボツワナ","it":"Botswana","hu":"Botswana"},"flag":"https://flagcdn.com/bw.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"BOT","independent":true},{"name":"Bouvet Island","topLevelDomain":[".bv"],"alpha2Code":"BV","alpha3Code":"BVT","callingCodes":["47"],"altSpellings":["BV","Bouvetøya","Bouvet-øya"],"subregion":"South Antarctic Ocean","region":"Antarctic Ocean","population":0,"latlng":[-54.43333333,3.4],"demonym":"Norwegian","area":49.0,"timezones":["UTC+01:00"],"nativeName":"Bouvetøya","numericCode":"074","flags":{"svg":"https://flagcdn.com/bv.svg","png":"https://flagcdn.com/w320/bv.png"},"currencies":[{"code":"NOK","name":"Norwegian krone","symbol":"kr"}],"languages":[{"iso639_1":"no","iso639_2":"nor","name":"Norwegian","nativeName":"Norsk"},{"iso639_1":"nb","iso639_2":"nob","name":"Norwegian Bokmål","nativeName":"Norsk bokmål"},{"iso639_1":"nn","iso639_2":"nno","name":"Norwegian Nynorsk","nativeName":"Norsk nynorsk"}],"translations":{"br":"Enez Bouvet","pt":"Ilha Bouvet","nl":"Bouveteiland","hr":"Otok Bouvet","fa":"جزیره بووه","de":"Bouvetinsel","es":"Isla Bouvet","fr":"Île Bouvet","ja":"ブーベ島","it":"Isola Bouvet","hu":"Bouvet-sziget"},"flag":"https://flagcdn.com/bv.svg","independent":false},{"name":"Brazil","topLevelDomain":[".br"],"alpha2Code":"BR","alpha3Code":"BRA","callingCodes":["55"],"capital":"Brasília","altSpellings":["BR","Brasil","Federative Republic of Brazil","República Federativa do Brasil"],"subregion":"South America","region":"Americas","population":212559409,"latlng":[-10.0,-55.0],"demonym":"Brazilian","area":8515767.0,"gini":53.4,"timezones":["UTC-05:00","UTC-04:00","UTC-03:00","UTC-02:00"],"borders":["ARG","BOL","COL","FRA","GUF","GUY","PRY","PER","SUR","URY","VEN"],"nativeName":"Brasil","numericCode":"076","flags":{"svg":"https://flagcdn.com/br.svg","png":"https://flagcdn.com/w320/br.png"},"currencies":[{"code":"BRL","name":"Brazilian real","symbol":"R$"}],"languages":[{"iso639_1":"pt","iso639_2":"por","name":"Portuguese","nativeName":"Português"}],"translations":{"br":"Brazil","pt":"Brasil","nl":"Brazilië","hr":"Brazil","fa":"برزیل","de":"Brasilien","es":"Brasil","fr":"Brésil","ja":"ブラジル","it":"Brasile","hu":"Brazília"},"flag":"https://flagcdn.com/br.svg","regionalBlocs":[{"acronym":"USAN","name":"Union of South American Nations","otherAcronyms":["UNASUR","UNASUL","UZAN"],"otherNames":["Unión de Naciones Suramericanas","União de Nações Sul-Americanas","Unie van Zuid-Amerikaanse Naties","South American Union"]}],"cioc":"BRA","independent":true},{"name":"British Indian Ocean Territory","topLevelDomain":[".io"],"alpha2Code":"IO","alpha3Code":"IOT","callingCodes":["246"],"capital":"Diego Garcia","altSpellings":["IO"],"subregion":"Eastern Africa","region":"Africa","population":3000,"latlng":[-6.0,71.5],"demonym":"Indian","area":60.0,"timezones":["UTC+06:00"],"nativeName":"British Indian Ocean Territory","numericCode":"086","flags":{"svg":"https://flagcdn.com/io.svg","png":"https://flagcdn.com/w320/io.png"},"currencies":[{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Tiriad breizhveurat Meurvor Indez","pt":"Território Britânico do Oceano Índico","nl":"Britse Gebieden in de Indische Oceaan","hr":"Britanski Indijskooceanski teritorij","fa":"قلمرو بریتانیا در اقیانوس هند","de":"Britisches Territorium im Indischen Ozean","es":"Territorio Británico del Océano Índico","fr":"Territoire britannique de l'océan Indien","ja":"イギリス領インド洋地域","it":"Territorio britannico dell'oceano indiano","hu":"Brit Indiai-óceáni Terület"},"flag":"https://flagcdn.com/io.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"independent":true},{"name":"United States Minor Outlying Islands","topLevelDomain":[".us"],"alpha2Code":"UM","alpha3Code":"UMI","callingCodes":["246"],"altSpellings":["UM"],"subregion":"Northern America","region":"Americas","population":300,"demonym":"American","timezones":["UTC-11:00","UTC-10:00","UTC+12:00"],"nativeName":"United States Minor Outlying Islands","numericCode":"581","flags":{"svg":"https://flagcdn.com/um.svg","png":"https://flagcdn.com/w320/um.png"},"currencies":[{"code":"GBP","name":"British pound","symbol":"£"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Inizi Minor A-bell Stadoù-Unanet","pt":"Ilhas Menores Distantes dos Estados Unidos","nl":"Kleine afgelegen eilanden van de Verenigde Staten","hr":"Mali udaljeni otoci SAD-a","fa":"جزایر کوچک حاشیه‌ای ایالات متحده آمریکا","de":"Kleinere Inselbesitzungen der Vereinigten Staaten","es":"Islas Ultramarinas Menores de Estados Unidos","fr":"Îles mineures éloignées des États-Unis","ja":"合衆国領有小離島","it":"Isole minori esterne degli Stati Uniti d'America","hu":"Amerikai Egyesült Államok lakatlan külbirtokai"},"flag":"https://flagcdn.com/um.svg","independent":false},{"name":"Virgin Islands (British)","topLevelDomain":[".vg"],"alpha2Code":"VG","alpha3Code":"VGB","callingCodes":["1"],"capital":"Road Town","altSpellings":["VG"],"subregion":"Caribbean","region":"Americas","population":30237,"latlng":[18.431383,-64.62305],"demonym":"Virgin Islander","area":151.0,"timezones":["UTC-04:00"],"nativeName":"British Virgin Islands","numericCode":"092","flags":{"svg":"https://flagcdn.com/vg.svg","png":"https://flagcdn.com/w320/vg.png"},"currencies":[{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Inizi Gwerc'h Breizhveurat","pt":"Ilhas Virgens Britânicas","nl":"Britse Maagdeneilanden","hr":"Britanski Djevičanski Otoci","fa":"جزایر ویرجین بریتانیا","de":"Britische Jungferninseln","es":"Islas Vírgenes del Reino Unido","fr":"Îles Vierges britanniques","ja":"イギリス領ヴァージン諸島","it":"Isole Vergini Britanniche","hu":"Brit Virgin-szigetek"},"flag":"https://flagcdn.com/vg.svg","cioc":"IVB","independent":false},{"name":"Virgin Islands (U.S.)","topLevelDomain":[".vi"],"alpha2Code":"VI","alpha3Code":"VIR","callingCodes":["1 340"],"capital":"Charlotte Amalie","altSpellings":["VI","USVI","American Virgin Islands","U.S. Virgin Islands"],"subregion":"Caribbean","region":"Americas","population":106290,"latlng":[18.34,-64.93],"demonym":"Virgin Islander","area":346.36,"timezones":["UTC-04:00"],"nativeName":"Virgin Islands of the United States","numericCode":"850","flags":{"svg":"https://flagcdn.com/vi.svg","png":"https://flagcdn.com/w320/vi.png"},"currencies":[{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Inizi Gwerc'h ar Stadoù-Unanet","pt":"Ilhas Virgens Americanas","nl":"Verenigde Staten Maagdeneilanden","hr":"Virgin Islands (U.S.)","fa":"جزایر ویرجین آمریکا","de":"Amerikanische Jungferninseln","es":"Islas Vírgenes de los Estados Unidos","fr":"Îles Vierges des États-Unis","ja":"アメリカ領ヴァージン諸島","it":"Isole Vergini americane","hu":"Amerikai Virgin-szigetek"},"flag":"https://flagcdn.com/vi.svg","cioc":"ISV","independent":false},{"name":"Brunei Darussalam","topLevelDomain":[".bn"],"alpha2Code":"BN","alpha3Code":"BRN","callingCodes":["673"],"capital":"Bandar Seri Begawan","altSpellings":["BN","Nation of Brunei"," the Abode of Peace"],"subregion":"South-Eastern Asia","region":"Asia","population":437483,"latlng":[4.5,114.66666666],"demonym":"Bruneian","area":5765.0,"timezones":["UTC+08:00"],"borders":["MYS"],"nativeName":"Negara Brunei Darussalam","numericCode":"096","flags":{"svg":"https://flagcdn.com/bn.svg","png":"https://flagcdn.com/w320/bn.png"},"currencies":[{"code":"BND","name":"Brunei dollar","symbol":"$"},{"code":"SGD","name":"Singapore dollar","symbol":"$"}],"languages":[{"iso639_1":"ms","iso639_2":"msa","name":"Malay","nativeName":"bahasa Melayu"}],"translations":{"br":"Brunei","pt":"Brunei","nl":"Brunei","hr":"Brunej","fa":"برونئی","de":"Brunei","es":"Brunei","fr":"Brunei","ja":"ブルネイ・ダルサラーム","it":"Brunei","hu":"Brunei"},"flag":"https://flagcdn.com/bn.svg","regionalBlocs":[{"acronym":"ASEAN","name":"Association of Southeast Asian Nations"}],"cioc":"BRU","independent":true},{"name":"Bulgaria","topLevelDomain":[".bg"],"alpha2Code":"BG","alpha3Code":"BGR","callingCodes":["359"],"capital":"Sofia","altSpellings":["BG","Republic of Bulgaria","Република България"],"subregion":"Eastern Europe","region":"Europe","population":6927288,"latlng":[43.0,25.0],"demonym":"Bulgarian","area":110879.0,"gini":41.3,"timezones":["UTC+02:00"],"borders":["GRC","MKD","ROU","SRB","TUR"],"nativeName":"България","numericCode":"100","flags":{"svg":"https://flagcdn.com/bg.svg","png":"https://flagcdn.com/w320/bg.png"},"currencies":[{"code":"BGN","name":"Bulgarian lev","symbol":"лв"}],"languages":[{"iso639_1":"bg","iso639_2":"bul","name":"Bulgarian","nativeName":"български език"}],"translations":{"br":"Bulgaria","pt":"Bulgária","nl":"Bulgarije","hr":"Bugarska","fa":"بلغارستان","de":"Bulgarien","es":"Bulgaria","fr":"Bulgarie","ja":"ブルガリア","it":"Bulgaria","hu":"Bulgária"},"flag":"https://flagcdn.com/bg.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"BUL","independent":true},{"name":"Burkina Faso","topLevelDomain":[".bf"],"alpha2Code":"BF","alpha3Code":"BFA","callingCodes":["226"],"capital":"Ouagadougou","altSpellings":["BF"],"subregion":"Western Africa","region":"Africa","population":20903278,"latlng":[13.0,-2.0],"demonym":"Burkinabe","area":272967.0,"gini":35.3,"timezones":["UTC"],"borders":["BEN","CIV","GHA","MLI","NER","TGO"],"nativeName":"Burkina Faso","numericCode":"854","flags":{"svg":"https://flagcdn.com/bf.svg","png":"https://flagcdn.com/w320/bf.png"},"currencies":[{"code":"XOF","name":"West African CFA franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"ff","iso639_2":"ful","name":"Fula","nativeName":"Fulfulde"}],"translations":{"br":"Burkina Faso","pt":"Burquina Faso","nl":"Burkina Faso","hr":"Burkina Faso","fa":"بورکینافاسو","de":"Burkina Faso","es":"Burkina Faso","fr":"Burkina Faso","ja":"ブルキナファソ","it":"Burkina Faso","hu":"Burkina Faso"},"flag":"https://flagcdn.com/bf.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"BUR","independent":true},{"name":"Burundi","topLevelDomain":[".bi"],"alpha2Code":"BI","alpha3Code":"BDI","callingCodes":["257"],"capital":"Gitega","altSpellings":["BI","Republic of Burundi","Republika y'Uburundi","République du Burundi"],"subregion":"Eastern Africa","region":"Africa","population":11890781,"latlng":[-3.5,30.0],"demonym":"Burundian","area":27834.0,"gini":38.6,"timezones":["UTC+02:00"],"borders":["COD","RWA","TZA"],"nativeName":"Burundi","numericCode":"108","flags":{"svg":"https://flagcdn.com/bi.svg","png":"https://flagcdn.com/w320/bi.png"},"currencies":[{"code":"BIF","name":"Burundian franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"rn","iso639_2":"run","name":"Kirundi","nativeName":"Ikirundi"}],"translations":{"br":"Burundi","pt":"Burúndi","nl":"Burundi","hr":"Burundi","fa":"بوروندی","de":"Burundi","es":"Burundi","fr":"Burundi","ja":"ブルンジ","it":"Burundi","hu":"Burundi"},"flag":"https://flagcdn.com/bi.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"BDI","independent":true},{"name":"Cambodia","topLevelDomain":[".kh"],"alpha2Code":"KH","alpha3Code":"KHM","callingCodes":["855"],"capital":"Phnom Penh","altSpellings":["KH","Kingdom of Cambodia"],"subregion":"South-Eastern Asia","region":"Asia","population":16718971,"latlng":[13.0,105.0],"demonym":"Cambodian","area":181035.0,"timezones":["UTC+07:00"],"borders":["LAO","THA","VNM"],"nativeName":"Kâmpŭchéa","numericCode":"116","flags":{"svg":"https://flagcdn.com/kh.svg","png":"https://flagcdn.com/w320/kh.png"},"currencies":[{"code":"KHR","name":"Cambodian riel","symbol":"៛"},{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"km","iso639_2":"khm","name":"Khmer","nativeName":"ខ្មែរ"}],"translations":{"br":"Kambodja","pt":"Camboja","nl":"Cambodja","hr":"Kambodža","fa":"کامبوج","de":"Kambodscha","es":"Camboya","fr":"Cambodge","ja":"カンボジア","it":"Cambogia","hu":"Kambodzsa"},"flag":"https://flagcdn.com/kh.svg","regionalBlocs":[{"acronym":"ASEAN","name":"Association of Southeast Asian Nations"}],"cioc":"CAM","independent":true},{"name":"Cameroon","topLevelDomain":[".cm"],"alpha2Code":"CM","alpha3Code":"CMR","callingCodes":["237"],"capital":"Yaoundé","altSpellings":["CM","Republic of Cameroon","République du Cameroun"],"subregion":"Middle Africa","region":"Africa","population":26545864,"latlng":[6.0,12.0],"demonym":"Cameroonian","area":475442.0,"gini":46.6,"timezones":["UTC+01:00"],"borders":["CAF","TCD","COG","GNQ","GAB","NGA"],"nativeName":"Cameroon","numericCode":"120","flags":{"svg":"https://flagcdn.com/cm.svg","png":"https://flagcdn.com/w320/cm.png"},"currencies":[{"code":"XAF","name":"Central African CFA franc","symbol":"Fr"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Kameroun","pt":"Camarões","nl":"Kameroen","hr":"Kamerun","fa":"کامرون","de":"Kamerun","es":"Camerún","fr":"Cameroun","ja":"カメルーン","it":"Camerun","hu":"Kamerun"},"flag":"https://flagcdn.com/cm.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"CMR","independent":true},{"name":"Canada","topLevelDomain":[".ca"],"alpha2Code":"CA","alpha3Code":"CAN","callingCodes":["1"],"capital":"Ottawa","altSpellings":["CA"],"subregion":"Northern America","region":"Americas","population":38005238,"latlng":[60.0,-95.0],"demonym":"Canadian","area":9984670.0,"gini":33.3,"timezones":["UTC-08:00","UTC-07:00","UTC-06:00","UTC-05:00","UTC-04:00","UTC-03:30"],"borders":["USA"],"nativeName":"Canada","numericCode":"124","flags":{"svg":"https://flagcdn.com/ca.svg","png":"https://flagcdn.com/w320/ca.png"},"currencies":[{"code":"CAD","name":"Canadian dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Kanada","pt":"Canadá","nl":"Canada","hr":"Kanada","fa":"کانادا","de":"Kanada","es":"Canadá","fr":"Canada","ja":"カナダ","it":"Canada","hu":"Kanada"},"flag":"https://flagcdn.com/ca.svg","regionalBlocs":[{"acronym":"NAFTA","name":"North American Free Trade Agreement","otherNames":["Tratado de Libre Comercio de América del Norte","Accord de Libre-échange Nord-Américain"]}],"cioc":"CAN","independent":true},{"name":"Cabo Verde","topLevelDomain":[".cv"],"alpha2Code":"CV","alpha3Code":"CPV","callingCodes":["238"],"capital":"Praia","altSpellings":["CV","Republic of Cabo Verde","República de Cabo Verde"],"subregion":"Western Africa","region":"Africa","population":555988,"latlng":[16.0,-24.0],"demonym":"Cape Verdian","area":4033.0,"gini":42.4,"timezones":["UTC-01:00"],"nativeName":"Cabo Verde","numericCode":"132","flags":{"svg":"https://flagcdn.com/cv.svg","png":"https://flagcdn.com/w320/cv.png"},"currencies":[{"code":"CVE","name":"Cape Verdean escudo","symbol":"Esc"}],"languages":[{"iso639_1":"pt","iso639_2":"por","name":"Portuguese","nativeName":"Português"}],"translations":{"br":"Kab Glas","pt":"Cabo Verde","nl":"Kaapverdië","hr":"Zelenortska Republika","fa":"کیپ ورد","de":"Kap Verde","es":"Cabo Verde","fr":"Cap Vert","ja":"カーボベルデ","it":"Capo Verde","hu":"Zöld-foki Köztársaság"},"flag":"https://flagcdn.com/cv.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"CPV","independent":true},{"name":"Cayman Islands","topLevelDomain":[".ky"],"alpha2Code":"KY","alpha3Code":"CYM","callingCodes":["1"],"capital":"George Town","altSpellings":["KY"],"subregion":"Caribbean","region":"Americas","population":65720,"latlng":[19.5,-80.5],"demonym":"Caymanian","area":264.0,"timezones":["UTC-05:00"],"nativeName":"Cayman Islands","numericCode":"136","flags":{"svg":"https://flagcdn.com/ky.svg","png":"https://flagcdn.com/w320/ky.png"},"currencies":[{"code":"KYD","name":"Cayman Islands dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Inizi Cayman","pt":"Ilhas Caimão","nl":"Caymaneilanden","hr":"Kajmanski otoci","fa":"جزایر کیمن","de":"Kaimaninseln","es":"Islas Caimán","fr":"Îles Caïmans","ja":"ケイマン諸島","it":"Isole Cayman","hu":"Kajmán-szigetek"},"flag":"https://flagcdn.com/ky.svg","cioc":"CAY","independent":false},{"name":"Central African Republic","topLevelDomain":[".cf"],"alpha2Code":"CF","alpha3Code":"CAF","callingCodes":["236"],"capital":"Bangui","altSpellings":["CF","Central African Republic","République centrafricaine"],"subregion":"Middle Africa","region":"Africa","population":4829764,"latlng":[7.0,21.0],"demonym":"Central African","area":622984.0,"gini":56.2,"timezones":["UTC+01:00"],"borders":["CMR","TCD","COD","COG","SSD","SDN"],"nativeName":"Ködörösêse tî Bêafrîka","numericCode":"140","flags":{"svg":"https://flagcdn.com/cf.svg","png":"https://flagcdn.com/w320/cf.png"},"currencies":[{"code":"XAF","name":"Central African CFA franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"sg","iso639_2":"sag","name":"Sango","nativeName":"yângâ tî sängö"}],"translations":{"br":"Republik Kreizafrikan","pt":"República Centro-Africana","nl":"Centraal-Afrikaanse Republiek","hr":"Srednjoafrička Republika","fa":"جمهوری آفریقای مرکزی","de":"Zentralafrikanische Republik","es":"República Centroafricana","fr":"République centrafricaine","ja":"中央アフリカ共和国","it":"Repubblica Centrafricana","hu":"Közép-afrikai Köztársaság"},"flag":"https://flagcdn.com/cf.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"CAF","independent":true},{"name":"Chad","topLevelDomain":[".td"],"alpha2Code":"TD","alpha3Code":"TCD","callingCodes":["235"],"capital":"N'Djamena","altSpellings":["TD","Tchad","Republic of Chad","République du Tchad"],"subregion":"Middle Africa","region":"Africa","population":16425859,"latlng":[15.0,19.0],"demonym":"Chadian","area":1284000.0,"gini":43.3,"timezones":["UTC+01:00"],"borders":["CMR","CAF","LBY","NER","NGA","SDN"],"nativeName":"Tchad","numericCode":"148","flags":{"svg":"https://flagcdn.com/td.svg","png":"https://flagcdn.com/w320/td.png"},"currencies":[{"code":"XAF","name":"Central African CFA franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Tchad","pt":"Chade","nl":"Tsjaad","hr":"Čad","fa":"چاد","de":"Tschad","es":"Chad","fr":"Tchad","ja":"チャド","it":"Ciad","hu":"Csád"},"flag":"https://flagcdn.com/td.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"CHA","independent":true},{"name":"Chile","topLevelDomain":[".cl"],"alpha2Code":"CL","alpha3Code":"CHL","callingCodes":["56"],"capital":"Santiago","altSpellings":["CL","Republic of Chile","República de Chile"],"subregion":"South America","region":"Americas","population":19116209,"latlng":[-30.0,-71.0],"demonym":"Chilean","area":756102.0,"gini":44.4,"timezones":["UTC-06:00","UTC-04:00"],"borders":["ARG","BOL","PER"],"nativeName":"Chile","numericCode":"152","flags":{"svg":"https://flagcdn.com/cl.svg","png":"https://flagcdn.com/w320/cl.png"},"currencies":[{"code":"CLP","name":"Chilean peso","symbol":"$"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Chile","pt":"Chile","nl":"Chili","hr":"Čile","fa":"شیلی","de":"Chile","es":"Chile","fr":"Chili","ja":"チリ","it":"Cile","hu":"Chile"},"flag":"https://flagcdn.com/cl.svg","regionalBlocs":[{"acronym":"PA","name":"Pacific Alliance","otherNames":["Alianza del Pacífico"]},{"acronym":"USAN","name":"Union of South American Nations","otherAcronyms":["UNASUR","UNASUL","UZAN"],"otherNames":["Unión de Naciones Suramericanas","União de Nações Sul-Americanas","Unie van Zuid-Amerikaanse Naties","South American Union"]}],"cioc":"CHI","independent":true},{"name":"China","topLevelDomain":[".cn"],"alpha2Code":"CN","alpha3Code":"CHN","callingCodes":["86"],"capital":"Beijing","altSpellings":["CN","Zhōngguó","Zhongguo","Zhonghua","People's Republic of China","中华人民共和国","Zhōnghuá Rénmín Gònghéguó"],"subregion":"Eastern Asia","region":"Asia","population":1402112000,"latlng":[35.0,105.0],"demonym":"Chinese","area":9640011.0,"gini":38.5,"timezones":["UTC+08:00"],"borders":["AFG","BTN","MMR","HKG","IND","KAZ","PRK","KGZ","LAO","MAC","MNG","PAK","RUS","TJK","VNM","NPL"],"nativeName":"中国","numericCode":"156","flags":{"svg":"https://flagcdn.com/cn.svg","png":"https://flagcdn.com/w320/cn.png"},"currencies":[{"code":"CNY","name":"Chinese yuan","symbol":"¥"}],"languages":[{"iso639_1":"zh","iso639_2":"zho","name":"Chinese","nativeName":"中文 (Zhōngwén)"}],"translations":{"br":"Sina","pt":"China","nl":"China","hr":"Kina","fa":"چین","de":"China","es":"China","fr":"Chine","ja":"中国","it":"Cina","hu":"Kína"},"flag":"https://flagcdn.com/cn.svg","cioc":"CHN","independent":true},{"name":"Christmas Island","topLevelDomain":[".cx"],"alpha2Code":"CX","alpha3Code":"CXR","callingCodes":["61"],"capital":"Flying Fish Cove","altSpellings":["CX","Territory of Christmas Island"],"subregion":"Australia and New Zealand","region":"Oceania","population":2072,"latlng":[-10.5,105.66666666],"demonym":"Christmas Island","area":135.0,"timezones":["UTC+07:00"],"nativeName":"Christmas Island","numericCode":"162","flags":{"svg":"https://flagcdn.com/cx.svg","png":"https://flagcdn.com/w320/cx.png"},"currencies":[{"code":"AUD","name":"Australian dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Enez Christmas","pt":"Ilha do Natal","nl":"Christmaseiland","hr":"Božićni otok","fa":"جزیره کریسمس","de":"Weihnachtsinsel","es":"Isla de Navidad","fr":"Île Christmas","ja":"クリスマス島","it":"Isola di Natale","hu":"Karácsony-sziget"},"flag":"https://flagcdn.com/cx.svg","independent":false},{"name":"Cocos (Keeling) Islands","topLevelDomain":[".cc"],"alpha2Code":"CC","alpha3Code":"CCK","callingCodes":["61"],"capital":"West Island","altSpellings":["CC","Territory of the Cocos (Keeling) Islands","Keeling Islands"],"subregion":"Australia and New Zealand","region":"Oceania","population":550,"latlng":[-12.5,96.83333333],"demonym":"Cocos Islander","area":14.0,"timezones":["UTC+06:30"],"nativeName":"Cocos (Keeling) Islands","numericCode":"166","flags":{"svg":"https://flagcdn.com/cc.svg","png":"https://flagcdn.com/w320/cc.png"},"currencies":[{"code":"AUD","name":"Australian dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Inizi Cocos (Keeling)","pt":"Ilhas dos Cocos","nl":"Cocoseilanden","hr":"Kokosovi Otoci","fa":"جزایر کوکوس","de":"Kokosinseln","es":"Islas Cocos o Islas Keeling","fr":"Îles Cocos","ja":"ココス(キーリング)諸島","it":"Isole Cocos e Keeling","hu":"Kókusz-szigetek"},"flag":"https://flagcdn.com/cc.svg","independent":false},{"name":"Colombia","topLevelDomain":[".co"],"alpha2Code":"CO","alpha3Code":"COL","callingCodes":["57"],"capital":"Bogotá","altSpellings":["CO","Republic of Colombia","República de Colombia"],"subregion":"South America","region":"Americas","population":50882884,"latlng":[4.0,-72.0],"demonym":"Colombian","area":1141748.0,"gini":51.3,"timezones":["UTC-05:00"],"borders":["BRA","ECU","PAN","PER","VEN"],"nativeName":"Colombia","numericCode":"170","flags":{"svg":"https://flagcdn.com/co.svg","png":"https://flagcdn.com/w320/co.png"},"currencies":[{"code":"COP","name":"Colombian peso","symbol":"$"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Kolombia","pt":"Colômbia","nl":"Colombia","hr":"Kolumbija","fa":"کلمبیا","de":"Kolumbien","es":"Colombia","fr":"Colombie","ja":"コロンビア","it":"Colombia","hu":"Kolumbia"},"flag":"https://flagcdn.com/co.svg","regionalBlocs":[{"acronym":"PA","name":"Pacific Alliance","otherNames":["Alianza del Pacífico"]},{"acronym":"USAN","name":"Union of South American Nations","otherAcronyms":["UNASUR","UNASUL","UZAN"],"otherNames":["Unión de Naciones Suramericanas","União de Nações Sul-Americanas","Unie van Zuid-Amerikaanse Naties","South American Union"]}],"cioc":"COL","independent":true},{"name":"Comoros","topLevelDomain":[".km"],"alpha2Code":"KM","alpha3Code":"COM","callingCodes":["269"],"capital":"Moroni","altSpellings":["KM","Union of the Comoros","Union des Comores","Udzima wa Komori","al-Ittiḥād al-Qumurī"],"subregion":"Eastern Africa","region":"Africa","population":869595,"latlng":[-12.16666666,44.25],"demonym":"Comoran","area":1862.0,"gini":45.3,"timezones":["UTC+03:00"],"nativeName":"Komori","numericCode":"174","flags":{"svg":"https://flagcdn.com/km.svg","png":"https://flagcdn.com/w320/km.png"},"currencies":[{"code":"KMF","name":"Comorian franc","symbol":"Fr"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"},{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Komorez","pt":"Comores","nl":"Comoren","hr":"Komori","fa":"کومور","de":"Union der Komoren","es":"Comoras","fr":"Comores","ja":"コモロ","it":"Comore","hu":"Comore-szigetek"},"flag":"https://flagcdn.com/km.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]},{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"COM","independent":true},{"name":"Congo","topLevelDomain":[".cg"],"alpha2Code":"CG","alpha3Code":"COG","callingCodes":["242"],"capital":"Brazzaville","altSpellings":["CG","Congo-Brazzaville"],"subregion":"Middle Africa","region":"Africa","population":5518092,"latlng":[-1.0,15.0],"demonym":"Congolese","area":342000.0,"gini":48.9,"timezones":["UTC+01:00"],"borders":["AGO","CMR","CAF","COD","GAB"],"nativeName":"République du Congo","numericCode":"178","flags":{"svg":"https://flagcdn.com/cg.svg","png":"https://flagcdn.com/w320/cg.png"},"currencies":[{"code":"XAF","name":"Central African CFA franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"ln","iso639_2":"lin","name":"Lingala","nativeName":"Lingála"}],"translations":{"br":"Kongo-Brazzaville","pt":"Congo","nl":"Congo [Republiek]","hr":"Kongo","fa":"کنگو","de":"Kongo","es":"Congo","fr":"Congo","ja":"コンゴ共和国","it":"Congo","hu":"Kongói Köztársaság"},"flag":"https://flagcdn.com/cg.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"CGO","independent":true},{"name":"Congo (Democratic Republic of the)","topLevelDomain":[".cd"],"alpha2Code":"CD","alpha3Code":"COD","callingCodes":["243"],"capital":"Kinshasa","altSpellings":["CD","DR Congo","Congo-Kinshasa","DRC"],"subregion":"Middle Africa","region":"Africa","population":89561404,"latlng":[0.0,25.0],"demonym":"Congolese","area":2344858.0,"gini":42.1,"timezones":["UTC+01:00","UTC+02:00"],"borders":["AGO","BDI","CAF","COG","RWA","SSD","TZA","UGA","ZMB"],"nativeName":"République démocratique du Congo","numericCode":"180","flags":{"svg":"https://flagcdn.com/cd.svg","png":"https://flagcdn.com/w320/cd.png"},"currencies":[{"code":"CDF","name":"Congolese franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"ln","iso639_2":"lin","name":"Lingala","nativeName":"Lingála"},{"iso639_1":"kg","iso639_2":"kon","name":"Kongo","nativeName":"Kikongo"},{"iso639_1":"sw","iso639_2":"swa","name":"Swahili","nativeName":"Kiswahili"},{"iso639_1":"lu","iso639_2":"lub","name":"Luba-Katanga","nativeName":"Tshiluba"}],"translations":{"br":"Kongo-Kinshasa","pt":"RD Congo","nl":"Congo [DRC]","hr":"Kongo, Demokratska Republika","fa":"جمهوری کنگو","de":"Kongo (Dem. Rep.)","es":"Congo (Rep. Dem.)","fr":"Congo (Rép. dém.)","ja":"コンゴ民主共和国","it":"Congo (Rep. Dem.)","hu":"Kongói Demokratikus Köztársaság"},"flag":"https://flagcdn.com/cd.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"COD","independent":true},{"name":"Cook Islands","topLevelDomain":[".ck"],"alpha2Code":"CK","alpha3Code":"COK","callingCodes":["682"],"capital":"Avarua","altSpellings":["CK","Kūki 'Āirani"],"subregion":"Polynesia","region":"Oceania","population":18100,"latlng":[-21.23333333,-159.76666666],"demonym":"Cook Islander","area":236.0,"timezones":["UTC-10:00"],"nativeName":"Cook Islands","numericCode":"184","flags":{"svg":"https://flagcdn.com/ck.svg","png":"https://flagcdn.com/w320/ck.png"},"currencies":[{"code":"NZD","name":"New Zealand dollar","symbol":"$"},{"code":"CKD","name":"Cook Islands dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_2":"rar","name":"Cook Islands Māori","nativeName":"Māori"}],"translations":{"br":"Inizi Cook","pt":"Ilhas Cook","nl":"Cookeilanden","hr":"Cookovo Otočje","fa":"جزایر کوک","de":"Cookinseln","es":"Islas Cook","fr":"Îles Cook","ja":"クック諸島","it":"Isole Cook","hu":"Cook-szigetek"},"flag":"https://flagcdn.com/ck.svg","cioc":"COK","independent":true},{"name":"Costa Rica","topLevelDomain":[".cr"],"alpha2Code":"CR","alpha3Code":"CRI","callingCodes":["506"],"capital":"San José","altSpellings":["CR","Republic of Costa Rica","República de Costa Rica"],"subregion":"Central America","region":"Americas","population":5094114,"latlng":[10.0,-84.0],"demonym":"Costa Rican","area":51100.0,"gini":48.2,"timezones":["UTC-06:00"],"borders":["NIC","PAN"],"nativeName":"Costa Rica","numericCode":"188","flags":{"svg":"https://flagcdn.com/cr.svg","png":"https://flagcdn.com/w320/cr.png"},"currencies":[{"code":"CRC","name":"Costa Rican colón","symbol":"₡"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Costa Rica","pt":"Costa Rica","nl":"Costa Rica","hr":"Kostarika","fa":"کاستاریکا","de":"Costa Rica","es":"Costa Rica","fr":"Costa Rica","ja":"コスタリカ","it":"Costa Rica","hu":"Costa Rica"},"flag":"https://flagcdn.com/cr.svg","regionalBlocs":[{"acronym":"CAIS","name":"Central American Integration System","otherAcronyms":["SICA"],"otherNames":["Sistema de la Integración Centroamericana,"]}],"cioc":"CRC","independent":true},{"name":"Croatia","topLevelDomain":[".hr"],"alpha2Code":"HR","alpha3Code":"HRV","callingCodes":["385"],"capital":"Zagreb","altSpellings":["HR","Hrvatska","Republic of Croatia","Republika Hrvatska"],"subregion":"Southern Europe","region":"Europe","population":4047200,"latlng":[45.16666666,15.5],"demonym":"Croatian","area":56594.0,"gini":29.7,"timezones":["UTC+01:00"],"borders":["BIH","HUN","MNE","SRB","SVN"],"nativeName":"Hrvatska","numericCode":"191","flags":{"svg":"https://flagcdn.com/hr.svg","png":"https://flagcdn.com/w320/hr.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"hr","iso639_2":"hrv","name":"Croatian","nativeName":"hrvatski jezik"}],"translations":{"br":"Kroatia","pt":"Croácia","nl":"Kroatië","hr":"Hrvatska","fa":"کرواسی","de":"Kroatien","es":"Croacia","fr":"Croatie","ja":"クロアチア","it":"Croazia","hu":"Horvátország"},"flag":"https://flagcdn.com/hr.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"CRO","independent":true},{"name":"Cuba","topLevelDomain":[".cu"],"alpha2Code":"CU","alpha3Code":"CUB","callingCodes":["53"],"capital":"Havana","altSpellings":["CU","Republic of Cuba","República de Cuba"],"subregion":"Caribbean","region":"Americas","population":11326616,"latlng":[21.5,-80.0],"demonym":"Cuban","area":109884.0,"timezones":["UTC-05:00"],"nativeName":"Cuba","numericCode":"192","flags":{"svg":"https://flagcdn.com/cu.svg","png":"https://flagcdn.com/w320/cu.png"},"currencies":[{"code":"CUC","name":"Cuban convertible peso","symbol":"$"},{"code":"CUP","name":"Cuban peso","symbol":"$"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Kuba","pt":"Cuba","nl":"Cuba","hr":"Kuba","fa":"کوبا","de":"Kuba","es":"Cuba","fr":"Cuba","ja":"キューバ","it":"Cuba","hu":"Kuba"},"flag":"https://flagcdn.com/cu.svg","cioc":"CUB","independent":true},{"name":"Curaçao","topLevelDomain":[".cw"],"alpha2Code":"CW","alpha3Code":"CUW","callingCodes":["599"],"capital":"Willemstad","altSpellings":["CW","Curacao","Kòrsou","Country of Curaçao","Land Curaçao","Pais Kòrsou"],"subregion":"Caribbean","region":"Americas","population":155014,"latlng":[12.116667,-68.933333],"demonym":"Dutch","area":444.0,"timezones":["UTC-04:00"],"nativeName":"Curaçao","numericCode":"531","flags":{"svg":"https://flagcdn.com/cw.svg","png":"https://flagcdn.com/w320/cw.png"},"currencies":[{"code":"ANG","name":"Netherlands Antillean guilder","symbol":"ƒ"}],"languages":[{"iso639_1":"nl","iso639_2":"nld","name":"Dutch","nativeName":"Nederlands"},{"iso639_1":"pa","iso639_2":"pan","name":"(Eastern) Punjabi","nativeName":"ਪੰਜਾਬੀ"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Curaçao","pt":"Curaçao","nl":"Curaçao","hr":"Curaçao","fa":"کوراسائو","de":"Curaçao","es":"Curaçao","fr":"Curaçao","ja":"Curaçao","it":"Curaçao","hu":"Curaçao"},"flag":"https://flagcdn.com/cw.svg","independent":false},{"name":"Cyprus","topLevelDomain":[".cy"],"alpha2Code":"CY","alpha3Code":"CYP","callingCodes":["357"],"capital":"Nicosia","altSpellings":["CY","Kýpros","Kıbrıs","Republic of Cyprus","Κυπριακή Δημοκρατία","Kıbrıs Cumhuriyeti"],"subregion":"Southern Europe","region":"Europe","population":1207361,"latlng":[35.0,33.0],"demonym":"Cypriot","area":9251.0,"gini":32.7,"timezones":["UTC+02:00"],"borders":["GBR"],"nativeName":"Κύπρος","numericCode":"196","flags":{"svg":"https://flagcdn.com/cy.svg","png":"https://flagcdn.com/w320/cy.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"el","iso639_2":"ell","name":"Greek (modern)","nativeName":"ελληνικά"},{"iso639_1":"tr","iso639_2":"tur","name":"Turkish","nativeName":"Türkçe"},{"iso639_1":"hy","iso639_2":"hye","name":"Armenian","nativeName":"Հայերեն"}],"translations":{"br":"Kiprenez","pt":"Chipre","nl":"Cyprus","hr":"Cipar","fa":"قبرس","de":"Zypern","es":"Chipre","fr":"Chypre","ja":"キプロス","it":"Cipro","hu":"Ciprus"},"flag":"https://flagcdn.com/cy.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"CYP","independent":true},{"name":"Czech Republic","topLevelDomain":[".cz"],"alpha2Code":"CZ","alpha3Code":"CZE","callingCodes":["420"],"capital":"Prague","altSpellings":["CZ","Česká republika","Česko"],"subregion":"Central Europe","region":"Europe","population":10698896,"latlng":[49.75,15.5],"demonym":"Czech","area":78865.0,"gini":25.0,"timezones":["UTC+01:00"],"nativeName":"Česká republika","numericCode":"203","flags":{"svg":"https://flagcdn.com/cz.svg","png":"https://flagcdn.com/w320/cz.png"},"currencies":[{"code":"CZK","name":"Czech koruna","symbol":"Kč"}],"languages":[{"iso639_1":"cs","iso639_2":"ces","name":"Czech","nativeName":"čeština"},{"iso639_1":"sk","iso639_2":"slk","name":"Slovak","nativeName":"slovenčina"}],"translations":{"br":"Tchekia","pt":"República Checa","nl":"Tsjechië","hr":"Češka","fa":"جمهوری چک","de":"Tschechische Republik","es":"República Checa","fr":"République tchèque","ja":"チェコ","it":"Repubblica Ceca","hu":"Csehország"},"flag":"https://flagcdn.com/cz.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"CZE","independent":true},{"name":"Denmark","topLevelDomain":[".dk"],"alpha2Code":"DK","alpha3Code":"DNK","callingCodes":["45"],"capital":"Copenhagen","altSpellings":["DK","Danmark","Kingdom of Denmark","Kongeriget Danmark"],"subregion":"Northern Europe","region":"Europe","population":5831404,"latlng":[56.0,10.0],"demonym":"Danish","area":43094.0,"gini":28.2,"timezones":["UTC-04:00","UTC-03:00","UTC-01:00","UTC","UTC+01:00"],"borders":["DEU"],"nativeName":"Danmark","numericCode":"208","flags":{"svg":"https://flagcdn.com/dk.svg","png":"https://flagcdn.com/w320/dk.png"},"currencies":[{"code":"DKK","name":"Danish krone","symbol":"kr"}],"languages":[{"iso639_1":"da","iso639_2":"dan","name":"Danish","nativeName":"dansk"}],"translations":{"br":"Danmark","pt":"Dinamarca","nl":"Denemarken","hr":"Danska","fa":"دانمارک","de":"Dänemark","es":"Dinamarca","fr":"Danemark","ja":"デンマーク","it":"Danimarca","hu":"Dánia"},"flag":"https://flagcdn.com/dk.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"DEN","independent":true},{"name":"Djibouti","topLevelDomain":[".dj"],"alpha2Code":"DJ","alpha3Code":"DJI","callingCodes":["253"],"capital":"Djibouti","altSpellings":["DJ","Jabuuti","Gabuuti","Republic of Djibouti","République de Djibouti","Gabuutih Ummuuno","Jamhuuriyadda Jabuuti"],"subregion":"Eastern Africa","region":"Africa","population":988002,"latlng":[11.5,43.0],"demonym":"Djibouti","area":23200.0,"gini":41.6,"timezones":["UTC+03:00"],"borders":["ERI","ETH","SOM"],"nativeName":"Djibouti","numericCode":"262","flags":{"svg":"https://flagcdn.com/dj.svg","png":"https://flagcdn.com/w320/dj.png"},"currencies":[{"code":"DJF","name":"Djiboutian franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Djibouti","pt":"Djibuti","nl":"Djibouti","hr":"Džibuti","fa":"جیبوتی","de":"Dschibuti","es":"Yibuti","fr":"Djibouti","ja":"ジブチ","it":"Gibuti","hu":"Dzsibuti"},"flag":"https://flagcdn.com/dj.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]},{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"DJI","independent":true},{"name":"Dominica","topLevelDomain":[".dm"],"alpha2Code":"DM","alpha3Code":"DMA","callingCodes":["1"],"capital":"Roseau","altSpellings":["DM","Dominique","Wai‘tu kubuli","Commonwealth of Dominica"],"subregion":"Caribbean","region":"Americas","population":71991,"latlng":[15.41666666,-61.33333333],"demonym":"Dominican","area":751.0,"timezones":["UTC-04:00"],"nativeName":"Dominica","numericCode":"212","flags":{"svg":"https://flagcdn.com/dm.svg","png":"https://flagcdn.com/w320/dm.png"},"currencies":[{"code":"XCD","name":"East Caribbean dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Dominika","pt":"Dominica","nl":"Dominica","hr":"Dominika","fa":"دومینیکا","de":"Dominica","es":"Dominica","fr":"Dominique","ja":"ドミニカ国","it":"Dominica","hu":"Dominikai Közösség"},"flag":"https://flagcdn.com/dm.svg","cioc":"DMA","independent":true},{"name":"Dominican Republic","topLevelDomain":[".do"],"alpha2Code":"DO","alpha3Code":"DOM","callingCodes":["1"],"capital":"Santo Domingo","altSpellings":["DO"],"subregion":"Caribbean","region":"Americas","population":10847904,"latlng":[19.0,-70.66666666],"demonym":"Dominican","area":48671.0,"gini":41.9,"timezones":["UTC-04:00"],"borders":["HTI"],"nativeName":"República Dominicana","numericCode":"214","flags":{"svg":"https://flagcdn.com/do.svg","png":"https://flagcdn.com/w320/do.png"},"currencies":[{"code":"DOP","name":"Dominican peso","symbol":"$"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Republik Dominikan","pt":"República Dominicana","nl":"Dominicaanse Republiek","hr":"Dominikanska Republika","fa":"جمهوری دومینیکن","de":"Dominikanische Republik","es":"República Dominicana","fr":"République dominicaine","ja":"ドミニカ共和国","it":"Repubblica Dominicana","hu":"Dominikai Köztársaság"},"flag":"https://flagcdn.com/do.svg","regionalBlocs":[{"acronym":"CARICOM","name":"Caribbean Community","otherNames":["Comunidad del Caribe","Communauté Caribéenne","Caribische Gemeenschap"]},{"acronym":"CAIS","name":"Central American Integration System","otherAcronyms":["SICA"],"otherNames":["Sistema de la Integración Centroamericana,"]}],"cioc":"DOM","independent":true},{"name":"Ecuador","topLevelDomain":[".ec"],"alpha2Code":"EC","alpha3Code":"ECU","callingCodes":["593"],"capital":"Quito","altSpellings":["EC","Republic of Ecuador","República del Ecuador"],"subregion":"South America","region":"Americas","population":17643060,"latlng":[-2.0,-77.5],"demonym":"Ecuadorean","area":276841.0,"gini":45.7,"timezones":["UTC-06:00","UTC-05:00"],"borders":["COL","PER"],"nativeName":"Ecuador","numericCode":"218","flags":{"svg":"https://flagcdn.com/ec.svg","png":"https://flagcdn.com/w320/ec.png"},"currencies":[{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Ecuador","pt":"Equador","nl":"Ecuador","hr":"Ekvador","fa":"اکوادور","de":"Ecuador","es":"Ecuador","fr":"Équateur","ja":"エクアドル","it":"Ecuador","hu":"Ecuador"},"flag":"https://flagcdn.com/ec.svg","regionalBlocs":[{"acronym":"USAN","name":"Union of South American Nations","otherAcronyms":["UNASUR","UNASUL","UZAN"],"otherNames":["Unión de Naciones Suramericanas","União de Nações Sul-Americanas","Unie van Zuid-Amerikaanse Naties","South American Union"]}],"cioc":"ECU","independent":true},{"name":"Egypt","topLevelDomain":[".eg"],"alpha2Code":"EG","alpha3Code":"EGY","callingCodes":["20"],"capital":"Cairo","altSpellings":["EG","Arab Republic of Egypt"],"subregion":"Northern Africa","region":"Africa","population":102334403,"latlng":[27.0,30.0],"demonym":"Egyptian","area":1002450.0,"gini":31.5,"timezones":["UTC+02:00"],"borders":["ISR","LBY","SDN"],"nativeName":"مصر‎","numericCode":"818","flags":{"svg":"https://flagcdn.com/eg.svg","png":"https://flagcdn.com/w320/eg.png"},"currencies":[{"code":"EGP","name":"Egyptian pound","symbol":"£"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Egipt","pt":"Egipto","nl":"Egypte","hr":"Egipat","fa":"مصر","de":"Ägypten","es":"Egipto","fr":"Égypte","ja":"エジプト","it":"Egitto","hu":"Egyiptom"},"flag":"https://flagcdn.com/eg.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]},{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"EGY","independent":true},{"name":"El Salvador","topLevelDomain":[".sv"],"alpha2Code":"SV","alpha3Code":"SLV","callingCodes":["503"],"capital":"San Salvador","altSpellings":["SV","Republic of El Salvador","República de El Salvador"],"subregion":"Central America","region":"Americas","population":6486201,"latlng":[13.83333333,-88.91666666],"demonym":"Salvadoran","area":21041.0,"gini":38.8,"timezones":["UTC-06:00"],"borders":["GTM","HND"],"nativeName":"El Salvador","numericCode":"222","flags":{"svg":"https://flagcdn.com/sv.svg","png":"https://flagcdn.com/w320/sv.png"},"currencies":[{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"El Salvador","pt":"El Salvador","nl":"El Salvador","hr":"Salvador","fa":"السالوادور","de":"El Salvador","es":"El Salvador","fr":"Salvador","ja":"エルサルバドル","it":"El Salvador","hu":"Salvador"},"flag":"https://flagcdn.com/sv.svg","regionalBlocs":[{"acronym":"CAIS","name":"Central American Integration System","otherAcronyms":["SICA"],"otherNames":["Sistema de la Integración Centroamericana,"]}],"cioc":"ESA","independent":true},{"name":"Equatorial Guinea","topLevelDomain":[".gq"],"alpha2Code":"GQ","alpha3Code":"GNQ","callingCodes":["240"],"capital":"Malabo","altSpellings":["GQ","Republic of Equatorial Guinea","República de Guinea Ecuatorial","République de Guinée équatoriale","República da Guiné Equatorial"],"subregion":"Middle Africa","region":"Africa","population":1402985,"latlng":[2.0,10.0],"demonym":"Equatorial Guinean","area":28051.0,"timezones":["UTC+01:00"],"borders":["CMR","GAB"],"nativeName":"Guinea Ecuatorial","numericCode":"226","flags":{"svg":"https://flagcdn.com/gq.svg","png":"https://flagcdn.com/w320/gq.png"},"currencies":[{"code":"XAF","name":"Central African CFA franc","symbol":"Fr"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"},{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"pt","iso639_2":"por","name":"Portuguese","nativeName":"Português"},{"iso639_2":"fan","name":"Fang","nativeName":"Fang"}],"translations":{"br":"Ginea ar C'heheder","pt":"Guiné Equatorial","nl":"Equatoriaal-Guinea","hr":"Ekvatorijalna Gvineja","fa":"گینه استوایی","de":"Äquatorial-Guinea","es":"Guinea Ecuatorial","fr":"Guinée-Équatoriale","ja":"赤道ギニア","it":"Guinea Equatoriale","hu":"Egyenlítői-Guinea"},"flag":"https://flagcdn.com/gq.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"GEQ","independent":true},{"name":"Eritrea","topLevelDomain":[".er"],"alpha2Code":"ER","alpha3Code":"ERI","callingCodes":["291"],"capital":"Asmara","altSpellings":["ER","State of Eritrea","ሃገረ ኤርትራ","Dawlat Iritriyá","ʾErtrā","Iritriyā",""],"subregion":"Eastern Africa","region":"Africa","population":5352000,"latlng":[15.0,39.0],"demonym":"Eritrean","area":117600.0,"timezones":["UTC+03:00"],"borders":["DJI","ETH","SDN"],"nativeName":"ኤርትራ","numericCode":"232","flags":{"svg":"https://flagcdn.com/er.svg","png":"https://flagcdn.com/w320/er.png"},"currencies":[{"code":"ERN","name":"Eritrean nakfa","symbol":"Nfk"}],"languages":[{"iso639_1":"ti","iso639_2":"tir","name":"Tigrinya","nativeName":"ትግርኛ"},{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_2":"tig","name":"Tigre","nativeName":"ትግረ"},{"iso639_2":"kun","name":"Kunama","nativeName":"Kunama"},{"iso639_2":"ssy","name":"Saho","nativeName":"Saho"},{"iso639_2":"byn","name":"Bilen","nativeName":"ብሊና"},{"iso639_2":"nrb","name":"Nara","nativeName":"Nara"},{"iso639_1":"aa","iso639_2":"aar","name":"Afar","nativeName":"Afar"}],"translations":{"br":"Eritrea","pt":"Eritreia","nl":"Eritrea","hr":"Eritreja","fa":"اریتره","de":"Eritrea","es":"Eritrea","fr":"Érythrée","ja":"エリトリア","it":"Eritrea","hu":"Eritrea"},"flag":"https://flagcdn.com/er.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"ERI","independent":true},{"name":"Estonia","topLevelDomain":[".ee"],"alpha2Code":"EE","alpha3Code":"EST","callingCodes":["372"],"capital":"Tallinn","altSpellings":["EE","Eesti","Republic of Estonia","Eesti Vabariik"],"subregion":"Northern Europe","region":"Europe","population":1331057,"latlng":[59.0,26.0],"demonym":"Estonian","area":45227.0,"gini":30.3,"timezones":["UTC+02:00"],"borders":["LVA","RUS"],"nativeName":"Eesti","numericCode":"233","flags":{"svg":"https://flagcdn.com/ee.svg","png":"https://flagcdn.com/w320/ee.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"et","iso639_2":"est","name":"Estonian","nativeName":"eesti"}],"translations":{"br":"Estonia","pt":"Estónia","nl":"Estland","hr":"Estonija","fa":"استونی","de":"Estland","es":"Estonia","fr":"Estonie","ja":"エストニア","it":"Estonia","hu":"Észtország"},"flag":"https://flagcdn.com/ee.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"EST","independent":true},{"name":"Ethiopia","topLevelDomain":[".et"],"alpha2Code":"ET","alpha3Code":"ETH","callingCodes":["251"],"capital":"Addis Ababa","altSpellings":["ET","ʾĪtyōṗṗyā","Federal Democratic Republic of Ethiopia","የኢትዮጵያ ፌዴራላዊ ዲሞክራሲያዊ ሪፐብሊክ"],"subregion":"Eastern Africa","region":"Africa","population":114963583,"latlng":[8.0,38.0],"demonym":"Ethiopian","area":1104300.0,"gini":35.0,"timezones":["UTC+03:00"],"borders":["DJI","ERI","KEN","SOM","SSD","SDN"],"nativeName":"ኢትዮጵያ","numericCode":"231","flags":{"svg":"https://flagcdn.com/et.svg","png":"https://flagcdn.com/w320/et.png"},"currencies":[{"code":"ETB","name":"Ethiopian birr","symbol":"Br"}],"languages":[{"iso639_1":"am","iso639_2":"amh","name":"Amharic","nativeName":"አማርኛ"}],"translations":{"br":"Etiopia","pt":"Etiópia","nl":"Ethiopië","hr":"Etiopija","fa":"اتیوپی","de":"Äthiopien","es":"Etiopía","fr":"Éthiopie","ja":"エチオピア","it":"Etiopia","hu":"Etiópia"},"flag":"https://flagcdn.com/et.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"ETH","independent":true},{"name":"Falkland Islands (Malvinas)","topLevelDomain":[".fk"],"alpha2Code":"FK","alpha3Code":"FLK","callingCodes":["500"],"capital":"Stanley","altSpellings":["FK","Islas Malvinas"],"subregion":"South America","region":"Americas","population":2563,"latlng":[-51.75,-59.0],"demonym":"Falkland Islander","area":12173.0,"timezones":["UTC-04:00"],"nativeName":"Falkland Islands","numericCode":"238","flags":{"svg":"https://flagcdn.com/fk.svg","png":"https://flagcdn.com/w320/fk.png"},"currencies":[{"code":"FKP","name":"Falkland Islands pound","symbol":"£"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Inizi Maloù","pt":"Ilhas Falkland","nl":"Falklandeilanden [Islas Malvinas]","hr":"Falklandski Otoci","fa":"جزایر فالکلند","de":"Falklandinseln","es":"Islas Malvinas","fr":"Îles Malouines","ja":"フォークランド(マルビナス)諸島","it":"Isole Falkland o Isole Malvine","hu":"Falkland-szigetek"},"flag":"https://flagcdn.com/fk.svg","regionalBlocs":[{"acronym":"USAN","name":"Union of South American Nations","otherAcronyms":["UNASUR","UNASUL","UZAN"],"otherNames":["Unión de Naciones Suramericanas","União de Nações Sul-Americanas","Unie van Zuid-Amerikaanse Naties","South American Union"]}],"independent":false},{"name":"Faroe Islands","topLevelDomain":[".fo"],"alpha2Code":"FO","alpha3Code":"FRO","callingCodes":["298"],"capital":"Tórshavn","altSpellings":["FO","Føroyar","Færøerne"],"subregion":"Northern Europe","region":"Europe","population":48865,"latlng":[62.0,-7.0],"demonym":"Faroese","area":1393.0,"timezones":["UTC+00:00"],"nativeName":"Føroyar","numericCode":"234","flags":{"svg":"https://flagcdn.com/fo.svg","png":"https://flagcdn.com/w320/fo.png"},"currencies":[{"code":"DKK","name":"Danish krone","symbol":"kr"},{"code":"FOK","name":"Faroese króna","symbol":"kr"}],"languages":[{"iso639_1":"fo","iso639_2":"fao","name":"Faroese","nativeName":"føroyskt"}],"translations":{"br":"Inizi Faero","pt":"Ilhas Faroé","nl":"Faeröer","hr":"Farski Otoci","fa":"جزایر فارو","de":"Färöer-Inseln","es":"Islas Faroe","fr":"Îles Féroé","ja":"フェロー諸島","it":"Isole Far Oer","hu":"Feröer"},"flag":"https://flagcdn.com/fo.svg","independent":false},{"name":"Fiji","topLevelDomain":[".fj"],"alpha2Code":"FJ","alpha3Code":"FJI","callingCodes":["679"],"capital":"Suva","altSpellings":["FJ","Viti","Republic of Fiji","Matanitu ko Viti","Fijī Gaṇarājya"],"subregion":"Melanesia","region":"Oceania","population":896444,"latlng":[-18.0,175.0],"demonym":"Fijian","area":18272.0,"gini":36.7,"timezones":["UTC+12:00"],"nativeName":"Fiji","numericCode":"242","flags":{"svg":"https://flagcdn.com/fj.svg","png":"https://flagcdn.com/w320/fj.png"},"currencies":[{"code":"FJD","name":"Fijian dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"fj","iso639_2":"fij","name":"Fijian","nativeName":"vosa Vakaviti"},{"iso639_2":"hif","name":"Fiji Hindi","nativeName":"फ़िजी बात"},{"iso639_2":"rtm","name":"Rotuman","nativeName":"Fäeag Rotuma"}],"translations":{"br":"Fidji","pt":"Fiji","nl":"Fiji","hr":"Fiđi","fa":"فیجی","de":"Fidschi","es":"Fiyi","fr":"Fidji","ja":"フィジー","it":"Figi","hu":"Fidzsi-szigetek"},"flag":"https://flagcdn.com/fj.svg","cioc":"FIJ","independent":true},{"name":"Finland","topLevelDomain":[".fi"],"alpha2Code":"FI","alpha3Code":"FIN","callingCodes":["358"],"capital":"Helsinki","altSpellings":["FI","Suomi","Republic of Finland","Suomen tasavalta","Republiken Finland"],"subregion":"Northern Europe","region":"Europe","population":5530719,"latlng":[64.0,26.0],"demonym":"Finnish","area":338424.0,"gini":27.3,"timezones":["UTC+02:00"],"borders":["NOR","SWE","RUS"],"nativeName":"Suomi","numericCode":"246","flags":{"svg":"https://flagcdn.com/fi.svg","png":"https://flagcdn.com/w320/fi.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"fi","iso639_2":"fin","name":"Finnish","nativeName":"suomi"},{"iso639_1":"sv","iso639_2":"swe","name":"Swedish","nativeName":"svenska"}],"translations":{"br":"Finland","pt":"Finlândia","nl":"Finland","hr":"Finska","fa":"فنلاند","de":"Finnland","es":"Finlandia","fr":"Finlande","ja":"フィンランド","it":"Finlandia","hu":"Finnország"},"flag":"https://flagcdn.com/fi.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"FIN","independent":true},{"name":"France","topLevelDomain":[".fr"],"alpha2Code":"FR","alpha3Code":"FRA","callingCodes":["33"],"capital":"Paris","altSpellings":["FR","French Republic","République française"],"subregion":"Western Europe","region":"Europe","population":67391582,"latlng":[46.0,2.0],"demonym":"French","area":640679.0,"gini":32.4,"timezones":["UTC-10:00","UTC-09:30","UTC-09:00","UTC-08:00","UTC-04:00","UTC-03:00","UTC+01:00","UTC+02:00","UTC+03:00","UTC+04:00","UTC+05:00","UTC+10:00","UTC+11:00","UTC+12:00"],"borders":["AND","BEL","DEU","ITA","LUX","MCO","ESP","CHE"],"nativeName":"France","numericCode":"250","flags":{"svg":"https://flagcdn.com/fr.svg","png":"https://flagcdn.com/w320/fr.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Frañs","pt":"França","nl":"Frankrijk","hr":"Francuska","fa":"فرانسه","de":"Frankreich","es":"Francia","fr":"France","ja":"フランス","it":"Francia","hu":"Franciaország"},"flag":"https://flagcdn.com/fr.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"FRA","independent":true},{"name":"French Guiana","topLevelDomain":[".gf"],"alpha2Code":"GF","alpha3Code":"GUF","callingCodes":["594"],"capital":"Cayenne","altSpellings":["GF","Guiana","Guyane"],"subregion":"South America","region":"Americas","population":254541,"latlng":[4.0,-53.0],"demonym":"French Guianan","timezones":["UTC-03:00"],"borders":["BRA","SUR"],"nativeName":"Guyane française","numericCode":"254","flags":{"svg":"https://flagcdn.com/gf.svg","png":"https://flagcdn.com/w320/gf.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Gwiana C'hall","pt":"Guiana Francesa","nl":"Frans-Guyana","hr":"Francuska Gvajana","fa":"گویان فرانسه","de":"Französisch Guyana","es":"Guayana Francesa","fr":"Guayane","ja":"フランス領ギアナ","it":"Guyana francese","hu":"Francia Guyana"},"flag":"https://flagcdn.com/gf.svg","regionalBlocs":[{"acronym":"USAN","name":"Union of South American Nations","otherAcronyms":["UNASUR","UNASUL","UZAN"],"otherNames":["Unión de Naciones Suramericanas","União de Nações Sul-Americanas","Unie van Zuid-Amerikaanse Naties","South American Union"]},{"acronym":"EU","name":"European Union"}],"independent":false},{"name":"French Polynesia","topLevelDomain":[".pf"],"alpha2Code":"PF","alpha3Code":"PYF","callingCodes":["689"],"capital":"Papeetē","altSpellings":["PF","Polynésie française","French Polynesia","Pōrīnetia Farāni"],"subregion":"Polynesia","region":"Oceania","population":280904,"latlng":[-15.0,-140.0],"demonym":"French Polynesian","area":4167.0,"timezones":["UTC-10:00","UTC-09:30","UTC-09:00"],"nativeName":"Polynésie française","numericCode":"258","flags":{"svg":"https://flagcdn.com/pf.svg","png":"https://flagcdn.com/w320/pf.png"},"currencies":[{"code":"XPF","name":"CFP franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Polinezia C'hall","pt":"Polinésia Francesa","nl":"Frans-Polynesië","hr":"Francuska Polinezija","fa":"پلی‌نزی فرانسه","de":"Französisch-Polynesien","es":"Polinesia Francesa","fr":"Polynésie française","ja":"フランス領ポリネシア","it":"Polinesia Francese","hu":"Francia Polinézia"},"flag":"https://flagcdn.com/pf.svg","independent":false},{"name":"French Southern Territories","topLevelDomain":[".tf"],"alpha2Code":"TF","alpha3Code":"ATF","callingCodes":["262"],"capital":"Port-aux-Français","altSpellings":["TF"],"subregion":"Southern Africa","region":"Africa","population":140,"latlng":[-49.25,69.167],"demonym":"French","area":7747.0,"timezones":["UTC+05:00"],"nativeName":"Territoire des Terres australes et antarctiques françaises","numericCode":"260","flags":{"svg":"https://flagcdn.com/tf.svg","png":"https://flagcdn.com/w320/tf.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Douaroù Aostral hag Antarktikel Frañs","pt":"Terras Austrais e Antárticas Francesas","nl":"Franse Gebieden in de zuidelijke Indische Oceaan","hr":"Francuski južni i antarktički teritoriji","fa":"سرزمین‌های جنوبی و جنوبگانی فرانسه","de":"Französische Süd- und Antarktisgebiete","es":"Tierras Australes y Antárticas Francesas","fr":"Terres australes et antarctiques françaises","ja":"フランス領南方・南極地域","it":"Territori Francesi del Sud","hu":"Francia déli és antarktiszi területek"},"flag":"https://flagcdn.com/tf.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"independent":false},{"name":"Gabon","topLevelDomain":[".ga"],"alpha2Code":"GA","alpha3Code":"GAB","callingCodes":["241"],"capital":"Libreville","altSpellings":["GA","Gabonese Republic","République Gabonaise"],"subregion":"Middle Africa","region":"Africa","population":2225728,"latlng":[-1.0,11.75],"demonym":"Gabonese","area":267668.0,"gini":38.0,"timezones":["UTC+01:00"],"borders":["CMR","COG","GNQ"],"nativeName":"Gabon","numericCode":"266","flags":{"svg":"https://flagcdn.com/ga.svg","png":"https://flagcdn.com/w320/ga.png"},"currencies":[{"code":"XAF","name":"Central African CFA franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Gabon","pt":"Gabão","nl":"Gabon","hr":"Gabon","fa":"گابن","de":"Gabun","es":"Gabón","fr":"Gabon","ja":"ガボン","it":"Gabon","hu":"Gabon"},"flag":"https://flagcdn.com/ga.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"GAB","independent":true},{"name":"Gambia","topLevelDomain":[".gm"],"alpha2Code":"GM","alpha3Code":"GMB","callingCodes":["220"],"capital":"Banjul","altSpellings":["GM","Republic of the Gambia"],"subregion":"Western Africa","region":"Africa","population":2416664,"latlng":[13.46666666,-16.56666666],"demonym":"Gambian","area":11295.0,"gini":35.9,"timezones":["UTC+00:00"],"borders":["SEN"],"nativeName":"Gambia","numericCode":"270","flags":{"svg":"https://flagcdn.com/gm.svg","png":"https://flagcdn.com/w320/gm.png"},"currencies":[{"code":"GMD","name":"Gambian dalasi","symbol":"D"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Gambia","pt":"Gâmbia","nl":"Gambia","hr":"Gambija","fa":"گامبیا","de":"Gambia","es":"Gambia","fr":"Gambie","ja":"ガンビア","it":"Gambia","hu":"Gambia"},"flag":"https://flagcdn.com/gm.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"GAM","independent":true},{"name":"Georgia","topLevelDomain":[".ge"],"alpha2Code":"GE","alpha3Code":"GEO","callingCodes":["995"],"capital":"Tbilisi","altSpellings":["GE","Sakartvelo"],"subregion":"Western Asia","region":"Asia","population":3714000,"latlng":[42.0,43.5],"demonym":"Georgian","area":69700.0,"gini":35.9,"timezones":["UTC-04:00"],"borders":["ARM","AZE","RUS","TUR"],"nativeName":"საქართველო","numericCode":"268","flags":{"svg":"https://flagcdn.com/ge.svg","png":"https://flagcdn.com/w320/ge.png"},"currencies":[{"code":"GEL","name":"Georgian Lari","symbol":"ლ"}],"languages":[{"iso639_1":"ka","iso639_2":"kat","name":"Georgian","nativeName":"ქართული"}],"translations":{"br":"Jorjia","pt":"Geórgia","nl":"Georgië","hr":"Németország","fa":"گرجستان","de":"Georgien","es":"Georgia","fr":"Géorgie","ja":"グルジア","it":"Georgia","hu":"Grúzia"},"flag":"https://flagcdn.com/ge.svg","cioc":"GEO","independent":true},{"name":"Germany","topLevelDomain":[".de"],"alpha2Code":"DE","alpha3Code":"DEU","callingCodes":["49"],"capital":"Berlin","altSpellings":["DE","Federal Republic of Germany","Bundesrepublik Deutschland"],"subregion":"Central Europe","region":"Europe","population":83240525,"latlng":[51.0,9.0],"demonym":"German","area":357114.0,"gini":31.9,"timezones":["UTC+01:00"],"borders":["AUT","BEL","CZE","DNK","FRA","LUX","NLD","POL","CHE"],"nativeName":"Deutschland","numericCode":"276","flags":{"svg":"https://flagcdn.com/de.svg","png":"https://flagcdn.com/w320/de.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"de","iso639_2":"deu","name":"German","nativeName":"Deutsch"}],"translations":{"br":"Alamagn","pt":"Alemanha","nl":"Duitsland","hr":"Njemačka","fa":"آلمان","de":"Deutschland","es":"Alemania","fr":"Allemagne","ja":"ドイツ","it":"Germania","hu":"Grúzia"},"flag":"https://flagcdn.com/de.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"GER","independent":true},{"name":"Ghana","topLevelDomain":[".gh"],"alpha2Code":"GH","alpha3Code":"GHA","callingCodes":["233"],"capital":"Accra","altSpellings":["GH"],"subregion":"Western Africa","region":"Africa","population":31072945,"latlng":[8.0,-2.0],"demonym":"Ghanaian","area":238533.0,"gini":43.5,"timezones":["UTC"],"borders":["BFA","CIV","TGO"],"nativeName":"Ghana","numericCode":"288","flags":{"svg":"https://flagcdn.com/gh.svg","png":"https://flagcdn.com/w320/gh.png"},"currencies":[{"code":"GHS","name":"Ghanaian cedi","symbol":"₵"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Ghana","pt":"Gana","nl":"Ghana","hr":"Gana","fa":"غنا","de":"Ghana","es":"Ghana","fr":"Ghana","ja":"ガーナ","it":"Ghana","hu":"Ghána"},"flag":"https://flagcdn.com/gh.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"GHA","independent":true},{"name":"Gibraltar","topLevelDomain":[".gi"],"alpha2Code":"GI","alpha3Code":"GIB","callingCodes":["350"],"capital":"Gibraltar","altSpellings":["GI"],"subregion":"Southern Europe","region":"Europe","population":33691,"latlng":[36.13333333,-5.35],"demonym":"Gibraltar","area":6.0,"timezones":["UTC+01:00"],"borders":["ESP"],"nativeName":"Gibraltar","numericCode":"292","flags":{"svg":"https://flagcdn.com/gi.svg","png":"https://flagcdn.com/w320/gi.png"},"currencies":[{"code":"GIP","name":"Gibraltar pound","symbol":"£"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Jibraltar","pt":"Gibraltar","nl":"Gibraltar","hr":"Gibraltar","fa":"جبل‌طارق","de":"Gibraltar","es":"Gibraltar","fr":"Gibraltar","ja":"ジブラルタル","it":"Gibilterra","hu":"Gibraltár"},"flag":"https://flagcdn.com/gi.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"independent":false},{"name":"Greece","topLevelDomain":[".gr"],"alpha2Code":"GR","alpha3Code":"GRC","callingCodes":["30"],"capital":"Athens","altSpellings":["GR","Elláda","Hellenic Republic","Ελληνική Δημοκρατία"],"subregion":"Southern Europe","region":"Europe","population":10715549,"latlng":[39.0,22.0],"demonym":"Greek","area":131990.0,"gini":32.9,"timezones":["UTC+02:00"],"borders":["ALB","BGR","TUR","MKD"],"nativeName":"Ελλάδα","numericCode":"300","flags":{"svg":"https://flagcdn.com/gr.svg","png":"https://flagcdn.com/w320/gr.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"el","iso639_2":"ell","name":"Greek (modern)","nativeName":"ελληνικά"}],"translations":{"br":"Gres","pt":"Grécia","nl":"Griekenland","hr":"Grčka","fa":"یونان","de":"Griechenland","es":"Grecia","fr":"Grèce","ja":"ギリシャ","it":"Grecia","hu":"Görögország"},"flag":"https://flagcdn.com/gr.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"GRE","independent":true},{"name":"Greenland","topLevelDomain":[".gl"],"alpha2Code":"GL","alpha3Code":"GRL","callingCodes":["299"],"capital":"Nuuk","altSpellings":["GL","Grønland"],"subregion":"Northern America","region":"Americas","population":56367,"latlng":[72.0,-40.0],"demonym":"Greenlandic","area":2166086.0,"timezones":["UTC-04:00","UTC-03:00","UTC-01:00","UTC+00:00"],"nativeName":"Kalaallit Nunaat","numericCode":"304","flags":{"svg":"https://flagcdn.com/gl.svg","png":"https://flagcdn.com/w320/gl.png"},"currencies":[{"code":"DKK","name":"Danish krone","symbol":"kr"}],"languages":[{"iso639_1":"kl","iso639_2":"kal","name":"Greenlandic","nativeName":"kalaallisut"}],"translations":{"br":"Greunland","pt":"Gronelândia","nl":"Groenland","hr":"Grenland","fa":"گرینلند","de":"Grönland","es":"Groenlandia","fr":"Groenland","ja":"グリーンランド","it":"Groenlandia","hu":"Grönland"},"flag":"https://flagcdn.com/gl.svg","independent":false},{"name":"Grenada","topLevelDomain":[".gd"],"alpha2Code":"GD","alpha3Code":"GRD","callingCodes":["1"],"capital":"St. George's","altSpellings":["GD"],"subregion":"Caribbean","region":"Americas","population":112519,"latlng":[12.11666666,-61.66666666],"demonym":"Grenadian","area":344.0,"timezones":["UTC-04:00"],"nativeName":"Grenada","numericCode":"308","flags":{"svg":"https://flagcdn.com/gd.svg","png":"https://flagcdn.com/w320/gd.png"},"currencies":[{"code":"XCD","name":"East Caribbean dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Grenada","pt":"Granada","nl":"Grenada","hr":"Grenada","fa":"گرنادا","de":"Grenada","es":"Grenada","fr":"Grenade","ja":"グレナダ","it":"Grenada","hu":"Grenada"},"flag":"https://flagcdn.com/gd.svg","regionalBlocs":[{"acronym":"CARICOM","name":"Caribbean Community","otherNames":["Comunidad del Caribe","Communauté Caribéenne","Caribische Gemeenschap"]}],"cioc":"GRN","independent":true},{"name":"Guadeloupe","topLevelDomain":[".gp"],"alpha2Code":"GP","alpha3Code":"GLP","callingCodes":["590"],"capital":"Basse-Terre","altSpellings":["GP","Gwadloup"],"subregion":"Caribbean","region":"Americas","population":400132,"latlng":[16.25,-61.583333],"demonym":"Guadeloupian","timezones":["UTC-04:00"],"nativeName":"Guadeloupe","numericCode":"312","flags":{"svg":"https://flagcdn.com/gp.svg","png":"https://flagcdn.com/w320/gp.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Gwadeloup","pt":"Guadalupe","nl":"Guadeloupe","hr":"Gvadalupa","fa":"جزیره گوادلوپ","de":"Guadeloupe","es":"Guadalupe","fr":"Guadeloupe","ja":"グアドループ","it":"Guadeloupa","hu":"Guadeloupe"},"flag":"https://flagcdn.com/gp.svg","independent":false},{"name":"Guam","topLevelDomain":[".gu"],"alpha2Code":"GU","alpha3Code":"GUM","callingCodes":["1"],"capital":"Hagåtña","altSpellings":["GU","Guåhån"],"subregion":"Micronesia","region":"Oceania","population":168783,"latlng":[13.46666666,144.78333333],"demonym":"Guamanian","area":549.0,"timezones":["UTC+10:00"],"nativeName":"Guam","numericCode":"316","flags":{"svg":"https://flagcdn.com/gu.svg","png":"https://flagcdn.com/w320/gu.png"},"currencies":[{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"ch","iso639_2":"cha","name":"Chamorro","nativeName":"Chamoru"},{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Guam","pt":"Guame","nl":"Guam","hr":"Guam","fa":"گوام","de":"Guam","es":"Guam","fr":"Guam","ja":"グアム","it":"Guam","hu":"Guam"},"flag":"https://flagcdn.com/gu.svg","cioc":"GUM","independent":false},{"name":"Guatemala","topLevelDomain":[".gt"],"alpha2Code":"GT","alpha3Code":"GTM","callingCodes":["502"],"capital":"Guatemala City","altSpellings":["GT"],"subregion":"Central America","region":"Americas","population":16858333,"latlng":[15.5,-90.25],"demonym":"Guatemalan","area":108889.0,"gini":48.3,"timezones":["UTC-06:00"],"borders":["BLZ","SLV","HND","MEX"],"nativeName":"Guatemala","numericCode":"320","flags":{"svg":"https://flagcdn.com/gt.svg","png":"https://flagcdn.com/w320/gt.png"},"currencies":[{"code":"GTQ","name":"Guatemalan quetzal","symbol":"Q"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Guatemala","pt":"Guatemala","nl":"Guatemala","hr":"Gvatemala","fa":"گواتمالا","de":"Guatemala","es":"Guatemala","fr":"Guatemala","ja":"グアテマラ","it":"Guatemala","hu":"Guatemala"},"flag":"https://flagcdn.com/gt.svg","regionalBlocs":[{"acronym":"CAIS","name":"Central American Integration System","otherAcronyms":["SICA"],"otherNames":["Sistema de la Integración Centroamericana,"]}],"cioc":"GUA","independent":true},{"name":"Guernsey","topLevelDomain":[".gg"],"alpha2Code":"GG","alpha3Code":"GGY","callingCodes":["44"],"capital":"St. Peter Port","altSpellings":["GG","Bailiwick of Guernsey","Bailliage de Guernesey"],"subregion":"Northern Europe","region":"Europe","population":62999,"latlng":[49.46666666,-2.58333333],"demonym":"Channel Islander","area":78.0,"timezones":["UTC+00:00"],"nativeName":"Guernsey","numericCode":"831","flags":{"svg":"https://flagcdn.com/gg.svg","png":"https://flagcdn.com/w320/gg.png"},"currencies":[{"code":"GBP","name":"British pound","symbol":"£"},{"code":"GGP","name":"Guernsey pound","symbol":"£"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Gwernenez","pt":"Guernsey","nl":"Guernsey","hr":"Guernsey","fa":"گرنزی","de":"Guernsey","es":"Guernsey","fr":"Guernesey","ja":"ガーンジー","it":"Guernsey","hu":"Guernsey"},"flag":"https://flagcdn.com/gg.svg","independent":false},{"name":"Guinea","topLevelDomain":[".gn"],"alpha2Code":"GN","alpha3Code":"GIN","callingCodes":["224"],"capital":"Conakry","altSpellings":["GN","Republic of Guinea","République de Guinée"],"subregion":"Western Africa","region":"Africa","population":13132792,"latlng":[11.0,-10.0],"demonym":"Guinean","area":245857.0,"gini":33.7,"timezones":["UTC"],"borders":["CIV","GNB","LBR","MLI","SEN","SLE"],"nativeName":"Guinée","numericCode":"324","flags":{"svg":"https://flagcdn.com/gn.svg","png":"https://flagcdn.com/w320/gn.png"},"currencies":[{"code":"GNF","name":"Guinean franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"ff","iso639_2":"ful","name":"Fula","nativeName":"Fulfulde"}],"translations":{"br":"Ginea","pt":"Guiné","nl":"Guinee","hr":"Gvineja","fa":"گینه","de":"Guinea","es":"Guinea","fr":"Guinée","ja":"ギニア","it":"Guinea","hu":"Guinea"},"flag":"https://flagcdn.com/gn.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"GUI","independent":true},{"name":"Guinea-Bissau","topLevelDomain":[".gw"],"alpha2Code":"GW","alpha3Code":"GNB","callingCodes":["245"],"capital":"Bissau","altSpellings":["GW","Republic of Guinea-Bissau","República da Guiné-Bissau"],"subregion":"Western Africa","region":"Africa","population":1967998,"latlng":[12.0,-15.0],"demonym":"Guinea-Bissauan","area":36125.0,"gini":50.7,"timezones":["UTC"],"borders":["GIN","SEN"],"nativeName":"Guiné-Bissau","numericCode":"624","flags":{"svg":"https://flagcdn.com/gw.svg","png":"https://flagcdn.com/w320/gw.png"},"currencies":[{"code":"XOF","name":"West African CFA franc","symbol":"Fr"}],"languages":[{"iso639_1":"pt","iso639_2":"por","name":"Portuguese","nativeName":"Português"}],"translations":{"br":"Ginea-Bissau","pt":"Guiné-Bissau","nl":"Guinee-Bissau","hr":"Gvineja Bisau","fa":"گینه بیسائو","de":"Guinea-Bissau","es":"Guinea-Bisáu","fr":"Guinée-Bissau","ja":"ギニアビサウ","it":"Guinea-Bissau","hu":"Bissau-Guinea"},"flag":"https://flagcdn.com/gw.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"GBS","independent":true},{"name":"Guyana","topLevelDomain":[".gy"],"alpha2Code":"GY","alpha3Code":"GUY","callingCodes":["592"],"capital":"Georgetown","altSpellings":["GY","Co-operative Republic of Guyana"],"subregion":"South America","region":"Americas","population":786559,"latlng":[5.0,-59.0],"demonym":"Guyanese","area":214969.0,"gini":45.1,"timezones":["UTC-04:00"],"borders":["BRA","SUR","VEN"],"nativeName":"Guyana","numericCode":"328","flags":{"svg":"https://flagcdn.com/gy.svg","png":"https://flagcdn.com/w320/gy.png"},"currencies":[{"code":"GYD","name":"Guyanese dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Guyana","pt":"Guiana","nl":"Guyana","hr":"Gvajana","fa":"گویان","de":"Guyana","es":"Guyana","fr":"Guyane","ja":"ガイアナ","it":"Guyana","hu":"Guyana"},"flag":"https://flagcdn.com/gy.svg","regionalBlocs":[{"acronym":"CARICOM","name":"Caribbean Community","otherNames":["Comunidad del Caribe","Communauté Caribéenne","Caribische Gemeenschap"]},{"acronym":"USAN","name":"Union of South American Nations","otherAcronyms":["UNASUR","UNASUL","UZAN"],"otherNames":["Unión de Naciones Suramericanas","União de Nações Sul-Americanas","Unie van Zuid-Amerikaanse Naties","South American Union"]}],"cioc":"GUY","independent":true},{"name":"Haiti","topLevelDomain":[".ht"],"alpha2Code":"HT","alpha3Code":"HTI","callingCodes":["509"],"capital":"Port-au-Prince","altSpellings":["HT","Republic of Haiti","République d'Haïti","Repiblik Ayiti"],"subregion":"Caribbean","region":"Americas","population":11402533,"latlng":[19.0,-72.41666666],"demonym":"Haitian","area":27750.0,"gini":41.1,"timezones":["UTC-05:00"],"borders":["DOM"],"nativeName":"Haïti","numericCode":"332","flags":{"svg":"https://flagcdn.com/ht.svg","png":"https://flagcdn.com/w320/ht.png"},"currencies":[{"code":"HTG","name":"Haitian gourde","symbol":"G"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"ht","iso639_2":"hat","name":"Haitian","nativeName":"Kreyòl ayisyen"}],"translations":{"br":"Haiti","pt":"Haiti","nl":"Haïti","hr":"Haiti","fa":"هائیتی","de":"Haiti","es":"Haiti","fr":"Haïti","ja":"ハイチ","it":"Haiti","hu":"Haiti"},"flag":"https://flagcdn.com/ht.svg","regionalBlocs":[{"acronym":"CARICOM","name":"Caribbean Community","otherNames":["Comunidad del Caribe","Communauté Caribéenne","Caribische Gemeenschap"]}],"cioc":"HAI","independent":true},{"name":"Heard Island and McDonald Islands","topLevelDomain":[".hm",".aq"],"alpha2Code":"HM","alpha3Code":"HMD","callingCodes":["672"],"altSpellings":["HM"],"subregion":"Antarctic","region":"Antarctic","population":0,"latlng":[-53.1,72.51666666],"demonym":"Heard and McDonald Islander","area":412.0,"timezones":["UTC+05:00"],"nativeName":"Heard Island and McDonald Islands","numericCode":"334","flags":{"svg":"https://flagcdn.com/hm.svg","png":"https://flagcdn.com/w320/hm.png"},"currencies":[{"code":"AUD","name":"Australian dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Inizi Heard ha McDonald","pt":"Ilha Heard e Ilhas McDonald","nl":"Heard- en McDonaldeilanden","hr":"Otok Heard i otočje McDonald","fa":"جزیره هرد و جزایر مک‌دونالد","de":"Heard und die McDonaldinseln","es":"Islas Heard y McDonald","fr":"Îles Heard-et-MacDonald","ja":"ハード島とマクドナルド諸島","it":"Isole Heard e McDonald","hu":"Heard-sziget és McDonald-szigetek"},"flag":"https://flagcdn.com/hm.svg","independent":false},{"name":"Vatican City","topLevelDomain":[".va"],"alpha2Code":"VA","alpha3Code":"VAT","callingCodes":["379"],"capital":"Vatican City","altSpellings":["Vatican","The Vatican"],"subregion":"Southern Europe","region":"Europe","population":451,"latlng":[41.9,12.45],"demonym":"Vatican","area":0.44,"timezones":["UTC+01:00"],"borders":["ITA"],"nativeName":"Status Civitatis Vaticanae","numericCode":"336","flags":{"svg":"https://flagcdn.com/va.svg","png":"https://flagcdn.com/w320/va.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"la","iso639_2":"lat","name":"Latin","nativeName":"latine"},{"iso639_1":"it","iso639_2":"ita","name":"Italian","nativeName":"Italiano"},{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"Français"},{"iso639_1":"de","iso639_2":"deu","name":"German","nativeName":"Deutsch"}],"translations":{"br":"Vatikan","pt":"Vaticano","nl":"Heilige Stoel","hr":"Sveta Stolica","fa":"سریر مقدس","de":"Heiliger Stuhl","es":"Santa Sede","fr":"Saint-Siège","ja":"聖座","it":"Santa Sede","hu":"Vatikán"},"flag":"https://flagcdn.com/va.svg","independent":true},{"name":"Honduras","topLevelDomain":[".hn"],"alpha2Code":"HN","alpha3Code":"HND","callingCodes":["504"],"capital":"Tegucigalpa","altSpellings":["HN","Republic of Honduras","República de Honduras"],"subregion":"Central America","region":"Americas","population":9904608,"latlng":[15.0,-86.5],"demonym":"Honduran","area":112492.0,"gini":48.2,"timezones":["UTC-06:00"],"borders":["GTM","SLV","NIC"],"nativeName":"Honduras","numericCode":"340","flags":{"svg":"https://flagcdn.com/hn.svg","png":"https://flagcdn.com/w320/hn.png"},"currencies":[{"code":"HNL","name":"Honduran lempira","symbol":"L"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Honduras","pt":"Honduras","nl":"Honduras","hr":"Honduras","fa":"هندوراس","de":"Honduras","es":"Honduras","fr":"Honduras","ja":"ホンジュラス","it":"Honduras","hu":"Honduras"},"flag":"https://flagcdn.com/hn.svg","regionalBlocs":[{"acronym":"CAIS","name":"Central American Integration System","otherAcronyms":["SICA"],"otherNames":["Sistema de la Integración Centroamericana,"]}],"cioc":"HON","independent":true},{"name":"Hungary","topLevelDomain":[".hu"],"alpha2Code":"HU","alpha3Code":"HUN","callingCodes":["36"],"capital":"Budapest","altSpellings":["HU"],"subregion":"Central Europe","region":"Europe","population":9749763,"latlng":[47.0,20.0],"demonym":"Hungarian","area":93028.0,"gini":29.6,"timezones":["UTC+01:00"],"borders":["AUT","HRV","ROU","SRB","SVK","SVN","UKR"],"nativeName":"Magyarország","numericCode":"348","flags":{"svg":"https://flagcdn.com/hu.svg","png":"https://flagcdn.com/w320/hu.png"},"currencies":[{"code":"HUF","name":"Hungarian forint","symbol":"Ft"}],"languages":[{"iso639_1":"hu","iso639_2":"hun","name":"Hungarian","nativeName":"magyar"}],"translations":{"br":"Hungaria","pt":"Hungria","nl":"Hongarije","hr":"Mađarska","fa":"مجارستان","de":"Ungarn","es":"Hungría","fr":"Hongrie","ja":"ハンガリー","it":"Ungheria","hu":"Magyarország"},"flag":"https://flagcdn.com/hu.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"HUN","independent":true},{"name":"Hong Kong","topLevelDomain":[".hk"],"alpha2Code":"HK","alpha3Code":"HKG","callingCodes":["852"],"capital":"City of Victoria","altSpellings":["HK","香港"],"subregion":"Eastern Asia","region":"Asia","population":7481800,"latlng":[22.25,114.16666666],"demonym":"Chinese","area":1104.0,"timezones":["UTC+08:00"],"borders":["CHN"],"nativeName":"香港","numericCode":"344","flags":{"svg":"https://flagcdn.com/hk.svg","png":"https://flagcdn.com/w320/hk.png"},"currencies":[{"code":"HKD","name":"Hong Kong dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"zh","iso639_2":"zho","name":"Chinese","nativeName":"中文 (Zhōngwén)"}],"translations":{"br":"Hong Kong","pt":"Hong Kong","nl":"Hongkong","hr":"Hong Kong","fa":"هنگ‌کنگ","de":"Hong Kong","es":"Hong Kong","fr":"Hong Kong","ja":"香港","it":"Hong Kong","hu":"Hong Kong"},"flag":"https://flagcdn.com/hk.svg","cioc":"HKG","independent":false},{"name":"Iceland","topLevelDomain":[".is"],"alpha2Code":"IS","alpha3Code":"ISL","callingCodes":["354"],"capital":"Reykjavík","altSpellings":["IS","Island","Republic of Iceland","Lýðveldið Ísland"],"subregion":"Northern Europe","region":"Europe","population":366425,"latlng":[65.0,-18.0],"demonym":"Icelander","area":103000.0,"gini":26.1,"timezones":["UTC"],"nativeName":"Ísland","numericCode":"352","flags":{"svg":"https://flagcdn.com/is.svg","png":"https://flagcdn.com/w320/is.png"},"currencies":[{"code":"ISK","name":"Icelandic króna","symbol":"kr"}],"languages":[{"iso639_1":"is","iso639_2":"isl","name":"Icelandic","nativeName":"Íslenska"}],"translations":{"br":"Island","pt":"Islândia","nl":"IJsland","hr":"Island","fa":"ایسلند","de":"Island","es":"Islandia","fr":"Islande","ja":"アイスランド","it":"Islanda","hu":"Izland"},"flag":"https://flagcdn.com/is.svg","regionalBlocs":[{"acronym":"EFTA","name":"European Free Trade Association"}],"cioc":"ISL","independent":true},{"name":"India","topLevelDomain":[".in"],"alpha2Code":"IN","alpha3Code":"IND","callingCodes":["91"],"capital":"New Delhi","altSpellings":["IN","Bhārat","Republic of India","Bharat Ganrajya"],"subregion":"Southern Asia","region":"Asia","population":1380004385,"latlng":[20.0,77.0],"demonym":"Indian","area":3287590.0,"gini":35.7,"timezones":["UTC+05:30"],"borders":["AFG","BGD","BTN","MMR","CHN","NPL","PAK","LKA"],"nativeName":"भारत","numericCode":"356","flags":{"svg":"https://flagcdn.com/in.svg","png":"https://flagcdn.com/w320/in.png"},"currencies":[{"code":"INR","name":"Indian rupee","symbol":"₹"}],"languages":[{"iso639_1":"hi","iso639_2":"hin","name":"Hindi","nativeName":"हिन्दी"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"India","pt":"Índia","nl":"India","hr":"Indija","fa":"هند","de":"Indien","es":"India","fr":"Inde","ja":"インド","it":"India","hu":"India"},"flag":"https://flagcdn.com/in.svg","regionalBlocs":[{"acronym":"SAARC","name":"South Asian Association for Regional Cooperation"}],"cioc":"IND","independent":true},{"name":"Indonesia","topLevelDomain":[".id"],"alpha2Code":"ID","alpha3Code":"IDN","callingCodes":["62"],"capital":"Jakarta","altSpellings":["ID","Republic of Indonesia","Republik Indonesia"],"subregion":"South-Eastern Asia","region":"Asia","population":273523621,"latlng":[-5.0,120.0],"demonym":"Indonesian","area":1904569.0,"gini":38.2,"timezones":["UTC+07:00","UTC+08:00","UTC+09:00"],"borders":["TLS","MYS","PNG"],"nativeName":"Indonesia","numericCode":"360","flags":{"svg":"https://flagcdn.com/id.svg","png":"https://flagcdn.com/w320/id.png"},"currencies":[{"code":"IDR","name":"Indonesian rupiah","symbol":"Rp"}],"languages":[{"iso639_1":"id","iso639_2":"ind","name":"Indonesian","nativeName":"Bahasa Indonesia"}],"translations":{"br":"Indonezia","pt":"Indonésia","nl":"Indonesië","hr":"Indonezija","fa":"اندونزی","de":"Indonesien","es":"Indonesia","fr":"Indonésie","ja":"インドネシア","it":"Indonesia","hu":"Indonézia"},"flag":"https://flagcdn.com/id.svg","regionalBlocs":[{"acronym":"ASEAN","name":"Association of Southeast Asian Nations"}],"cioc":"INA","independent":true},{"name":"Ivory Coast","topLevelDomain":[".ci"],"alpha2Code":"CI","alpha3Code":"CIV","callingCodes":["225"],"capital":"Yamoussoukro","altSpellings":["CI","Ivory Coast","Republic of Côte d'Ivoire","République de Côte d'Ivoire"],"subregion":"Western Africa","region":"Africa","population":26378275,"latlng":[8.0,-5.0],"demonym":"Ivorian","area":322463.0,"gini":41.5,"timezones":["UTC"],"borders":["BFA","GHA","GIN","LBR","MLI"],"nativeName":"Côte d'Ivoire","numericCode":"384","flags":{"svg":"https://flagcdn.com/ci.svg","png":"https://flagcdn.com/w320/ci.png"},"currencies":[{"code":"XOF","name":"West African CFA franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Aod an Olifant","pt":"Costa do Marfim","nl":"Ivoorkust","hr":"Obala Bjelokosti","fa":"ساحل عاج","de":"Elfenbeinküste","es":"Costa de Marfil","fr":"Côte d'Ivoire","ja":"コートジボワール","it":"Costa D'Avorio","hu":"Elefántcsontpart"},"flag":"https://flagcdn.com/ci.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"CIV","independent":true},{"name":"Iran (Islamic Republic of)","topLevelDomain":[".ir"],"alpha2Code":"IR","alpha3Code":"IRN","callingCodes":["98"],"capital":"Tehran","altSpellings":["IR","Islamic Republic of Iran","Jomhuri-ye Eslāmi-ye Irān"],"subregion":"Southern Asia","region":"Asia","population":83992953,"latlng":[32.0,53.0],"demonym":"Iranian","area":1648195.0,"gini":42.0,"timezones":["UTC+03:30"],"borders":["AFG","ARM","AZE","IRQ","PAK","TUR","TKM"],"nativeName":"ایران","numericCode":"364","flags":{"svg":"https://flagcdn.com/ir.svg","png":"https://flagcdn.com/w320/ir.png"},"currencies":[{"code":"IRR","name":"Iranian rial","symbol":"﷼"}],"languages":[{"iso639_1":"fa","iso639_2":"fas","name":"Persian (Farsi)","nativeName":"فارسی"}],"translations":{"br":"Iran","pt":"Irão","nl":"Iran","hr":"Iran","fa":"ایران","de":"Iran","es":"Iran","fr":"Iran","ja":"イラン・イスラム共和国","it":"Iran (Islamic Republic of)","hu":"Irán"},"flag":"https://flagcdn.com/ir.svg","cioc":"IRI","independent":true},{"name":"Iraq","topLevelDomain":[".iq"],"alpha2Code":"IQ","alpha3Code":"IRQ","callingCodes":["964"],"capital":"Baghdad","altSpellings":["IQ","Republic of Iraq","Jumhūriyyat al-‘Irāq"],"subregion":"Western Asia","region":"Asia","population":40222503,"latlng":[33.0,44.0],"demonym":"Iraqi","area":438317.0,"gini":29.5,"timezones":["UTC+03:00"],"borders":["IRN","JOR","KWT","SAU","SYR","TUR"],"nativeName":"العراق","numericCode":"368","flags":{"svg":"https://flagcdn.com/iq.svg","png":"https://flagcdn.com/w320/iq.png"},"currencies":[{"code":"IQD","name":"Iraqi dinar","symbol":"ع.د"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"},{"iso639_1":"ku","iso639_2":"kur","name":"Kurdish","nativeName":"Kurdî"}],"translations":{"br":"Irak","pt":"Iraque","nl":"Irak","hr":"Irak","fa":"عراق","de":"Irak","es":"Irak","fr":"Irak","ja":"イラク","it":"Iraq","hu":"Irak"},"flag":"https://flagcdn.com/iq.svg","regionalBlocs":[{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"IRQ","independent":true},{"name":"Ireland","topLevelDomain":[".ie"],"alpha2Code":"IE","alpha3Code":"IRL","callingCodes":["353"],"capital":"Dublin","altSpellings":["IE","Éire","Republic of Ireland","Poblacht na hÉireann"],"subregion":"Northern Europe","region":"Europe","population":4994724,"latlng":[53.0,-8.0],"demonym":"Irish","area":70273.0,"gini":31.4,"timezones":["UTC"],"borders":["GBR"],"nativeName":"Éire","numericCode":"372","flags":{"svg":"https://flagcdn.com/ie.svg","png":"https://flagcdn.com/w320/ie.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"ga","iso639_2":"gle","name":"Irish","nativeName":"Gaeilge"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Iwerzhon","pt":"Irlanda","nl":"Ierland","hr":"Irska","fa":"ایرلند","de":"Irland","es":"Irlanda","fr":"Irlande","ja":"アイルランド","it":"Irlanda","hu":"Írország"},"flag":"https://flagcdn.com/ie.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"IRL","independent":true},{"name":"Isle of Man","topLevelDomain":[".im"],"alpha2Code":"IM","alpha3Code":"IMN","callingCodes":["44"],"capital":"Douglas","altSpellings":["IM","Ellan Vannin","Mann","Mannin"],"subregion":"Northern Europe","region":"Europe","population":85032,"latlng":[54.25,-4.5],"demonym":"Manx","area":572.0,"timezones":["UTC+00:00"],"nativeName":"Isle of Man","numericCode":"833","flags":{"svg":"https://flagcdn.com/im.svg","png":"https://flagcdn.com/w320/im.png"},"currencies":[{"code":"GBP","name":"British pound","symbol":"£"},{"code":"IMP[G]","name":"Manx pound","symbol":"£"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"gv","iso639_2":"glv","name":"Manx","nativeName":"Gaelg"}],"translations":{"br":"Enez Vanav","pt":"Ilha de Man","nl":"Isle of Man","hr":"Otok Man","fa":"جزیره من","de":"Insel Man","es":"Isla de Man","fr":"Île de Man","ja":"マン島","it":"Isola di Man","hu":"Man"},"flag":"https://flagcdn.com/im.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"independent":false},{"name":"Israel","topLevelDomain":[".il"],"alpha2Code":"IL","alpha3Code":"ISR","callingCodes":["972"],"capital":"Jerusalem","altSpellings":["IL","State of Israel","Medīnat Yisrā'el"],"subregion":"Western Asia","region":"Asia","population":9216900,"latlng":[31.5,34.75],"demonym":"Israeli","area":20770.0,"gini":39.0,"timezones":["UTC+02:00"],"borders":["EGY","JOR","LBN","SYR"],"nativeName":"יִשְׂרָאֵל","numericCode":"376","flags":{"svg":"https://flagcdn.com/il.svg","png":"https://flagcdn.com/w320/il.png"},"currencies":[{"code":"ILS","name":"Israeli new shekel","symbol":"₪"}],"languages":[{"iso639_1":"he","iso639_2":"heb","name":"Hebrew (modern)","nativeName":"עברית"},{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Israel","pt":"Israel","nl":"Israël","hr":"Izrael","fa":"اسرائیل","de":"Israel","es":"Israel","fr":"Israël","ja":"イスラエル","it":"Israele","hu":"Izrael"},"flag":"https://flagcdn.com/il.svg","cioc":"ISR","independent":true},{"name":"Italy","topLevelDomain":[".it"],"alpha2Code":"IT","alpha3Code":"ITA","callingCodes":["39"],"capital":"Rome","altSpellings":["IT","Italian Republic","Repubblica italiana"],"subregion":"Southern Europe","region":"Europe","population":59554023,"latlng":[42.83333333,12.83333333],"demonym":"Italian","area":301336.0,"gini":35.9,"timezones":["UTC+01:00"],"borders":["AUT","FRA","SMR","SVN","CHE","VAT"],"nativeName":"Italia","numericCode":"380","flags":{"svg":"https://flagcdn.com/it.svg","png":"https://flagcdn.com/w320/it.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"it","iso639_2":"ita","name":"Italian","nativeName":"Italiano"}],"translations":{"br":"Italia","pt":"Itália","nl":"Italië","hr":"Italija","fa":"ایتالیا","de":"Italien","es":"Italia","fr":"Italie","ja":"イタリア","it":"Italia","hu":"Olaszország"},"flag":"https://flagcdn.com/it.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"ITA","independent":true},{"name":"Jamaica","topLevelDomain":[".jm"],"alpha2Code":"JM","alpha3Code":"JAM","callingCodes":["1"],"capital":"Kingston","altSpellings":["JM"],"subregion":"Caribbean","region":"Americas","population":2961161,"latlng":[18.25,-77.5],"demonym":"Jamaican","area":10991.0,"gini":45.5,"timezones":["UTC-05:00"],"nativeName":"Jamaica","numericCode":"388","flags":{"svg":"https://flagcdn.com/jm.svg","png":"https://flagcdn.com/w320/jm.png"},"currencies":[{"code":"JMD","name":"Jamaican dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Jamaika","pt":"Jamaica","nl":"Jamaica","hr":"Jamajka","fa":"جامائیکا","de":"Jamaika","es":"Jamaica","fr":"Jamaïque","ja":"ジャマイカ","it":"Giamaica","hu":"Jamaica"},"flag":"https://flagcdn.com/jm.svg","regionalBlocs":[{"acronym":"CARICOM","name":"Caribbean Community","otherNames":["Comunidad del Caribe","Communauté Caribéenne","Caribische Gemeenschap"]}],"cioc":"JAM","independent":true},{"name":"Japan","topLevelDomain":[".jp"],"alpha2Code":"JP","alpha3Code":"JPN","callingCodes":["81"],"capital":"Tokyo","altSpellings":["JP","Nippon","Nihon"],"subregion":"Eastern Asia","region":"Asia","population":125836021,"latlng":[36.0,138.0],"demonym":"Japanese","area":377930.0,"gini":32.9,"timezones":["UTC+09:00"],"nativeName":"日本","numericCode":"392","flags":{"svg":"https://flagcdn.com/jp.svg","png":"https://flagcdn.com/w320/jp.png"},"currencies":[{"code":"JPY","name":"Japanese yen","symbol":"¥"}],"languages":[{"iso639_1":"ja","iso639_2":"jpn","name":"Japanese","nativeName":"日本語 (にほんご)"}],"translations":{"br":"Japan","pt":"Japão","nl":"Japan","hr":"Japan","fa":"ژاپن","de":"Japan","es":"Japón","fr":"Japon","ja":"日本","it":"Giappone","hu":"Japán"},"flag":"https://flagcdn.com/jp.svg","cioc":"JPN","independent":true},{"name":"Jersey","topLevelDomain":[".je"],"alpha2Code":"JE","alpha3Code":"JEY","callingCodes":["44"],"capital":"Saint Helier","altSpellings":["JE","Bailiwick of Jersey","Bailliage de Jersey","Bailliage dé Jèrri"],"subregion":"Northern Europe","region":"Europe","population":100800,"latlng":[49.25,-2.16666666],"demonym":"Channel Islander","area":116.0,"timezones":["UTC+01:00"],"nativeName":"Jersey","numericCode":"832","flags":{"svg":"https://flagcdn.com/je.svg","png":"https://flagcdn.com/w320/je.png"},"currencies":[{"code":"GBP","name":"British pound","symbol":"£"},{"code":"JEP[G]","name":"Jersey pound","symbol":"£"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Jerzenez","pt":"Jersey","nl":"Jersey","hr":"Jersey","fa":"جرزی","de":"Jersey","es":"Jersey","fr":"Jersey","ja":"ジャージー","it":"Isola di Jersey","hu":"Jersey"},"flag":"https://flagcdn.com/je.svg","independent":false},{"name":"Jordan","topLevelDomain":[".jo"],"alpha2Code":"JO","alpha3Code":"JOR","callingCodes":["962"],"capital":"Amman","altSpellings":["JO","Hashemite Kingdom of Jordan","al-Mamlakah al-Urdunīyah al-Hāshimīyah"],"subregion":"Western Asia","region":"Asia","population":10203140,"latlng":[31.0,36.0],"demonym":"Jordanian","area":89342.0,"gini":33.7,"timezones":["UTC+03:00"],"borders":["IRQ","ISR","SAU","SYR"],"nativeName":"الأردن","numericCode":"400","flags":{"svg":"https://flagcdn.com/jo.svg","png":"https://flagcdn.com/w320/jo.png"},"currencies":[{"code":"JOD","name":"Jordanian dinar","symbol":"د.ا"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Jordania","pt":"Jordânia","nl":"Jordanië","hr":"Jordan","fa":"اردن","de":"Jordanien","es":"Jordania","fr":"Jordanie","ja":"ヨルダン","it":"Giordania","hu":"Jordánia"},"flag":"https://flagcdn.com/jo.svg","regionalBlocs":[{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"JOR","independent":true},{"name":"Kazakhstan","topLevelDomain":[".kz",".қаз"],"alpha2Code":"KZ","alpha3Code":"KAZ","callingCodes":["76","77"],"capital":"Nur-Sultan","altSpellings":["KZ","Qazaqstan","Казахстан","Republic of Kazakhstan","Қазақстан Республикасы","Qazaqstan Respublïkası","Республика Казахстан","Respublika Kazakhstan"],"subregion":"Central Asia","region":"Asia","population":18754440,"latlng":[48.0,68.0],"demonym":"Kazakhstani","area":2724900.0,"gini":27.8,"timezones":["UTC+05:00","UTC+06:00"],"borders":["CHN","KGZ","RUS","TKM","UZB"],"nativeName":"Қазақстан","numericCode":"398","flags":{"svg":"https://flagcdn.com/kz.svg","png":"https://flagcdn.com/w320/kz.png"},"currencies":[{"code":"KZT","name":"Kazakhstani tenge","symbol":"₸"}],"languages":[{"iso639_1":"kk","iso639_2":"kaz","name":"Kazakh","nativeName":"қазақ тілі"},{"iso639_1":"ru","iso639_2":"rus","name":"Russian","nativeName":"Русский"}],"translations":{"br":"Kazakstan","pt":"Cazaquistão","nl":"Kazachstan","hr":"Kazahstan","fa":"قزاقستان","de":"Kasachstan","es":"Kazajistán","fr":"Kazakhstan","ja":"カザフスタン","it":"Kazakistan","hu":"Kazahsztán"},"flag":"https://flagcdn.com/kz.svg","regionalBlocs":[{"acronym":"EEU","name":"Eurasian Economic Union","otherAcronyms":["EAEU"]}],"cioc":"KAZ","independent":true},{"name":"Kenya","topLevelDomain":[".ke"],"alpha2Code":"KE","alpha3Code":"KEN","callingCodes":["254"],"capital":"Nairobi","altSpellings":["KE","Republic of Kenya","Jamhuri ya Kenya"],"subregion":"Eastern Africa","region":"Africa","population":53771300,"latlng":[1.0,38.0],"demonym":"Kenyan","area":580367.0,"gini":40.8,"timezones":["UTC+03:00"],"borders":["ETH","SOM","SSD","TZA","UGA"],"nativeName":"Kenya","numericCode":"404","flags":{"svg":"https://flagcdn.com/ke.svg","png":"https://flagcdn.com/w320/ke.png"},"currencies":[{"code":"KES","name":"Kenyan shilling","symbol":"Sh"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"sw","iso639_2":"swa","name":"Swahili","nativeName":"Kiswahili"}],"translations":{"br":"Kenya","pt":"Quénia","nl":"Kenia","hr":"Kenija","fa":"کنیا","de":"Kenia","es":"Kenia","fr":"Kenya","ja":"ケニア","it":"Kenya","hu":"Kenya"},"flag":"https://flagcdn.com/ke.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"KEN","independent":true},{"name":"Kiribati","topLevelDomain":[".ki"],"alpha2Code":"KI","alpha3Code":"KIR","callingCodes":["686"],"capital":"South Tarawa","altSpellings":["KI","Republic of Kiribati","Ribaberiki Kiribati"],"subregion":"Micronesia","region":"Oceania","population":119446,"latlng":[1.41666666,173.0],"demonym":"I-Kiribati","area":811.0,"gini":37.0,"timezones":["UTC+12:00","UTC+13:00","UTC+14:00"],"nativeName":"Kiribati","numericCode":"296","flags":{"svg":"https://flagcdn.com/ki.svg","png":"https://flagcdn.com/w320/ki.png"},"currencies":[{"code":"AUD","name":"Australian dollar","symbol":"$"},{"code":"KID","name":"Kiribati dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Kiribati","pt":"Quiribáti","nl":"Kiribati","hr":"Kiribati","fa":"کیریباتی","de":"Kiribati","es":"Kiribati","fr":"Kiribati","ja":"キリバス","it":"Kiribati","hu":"Kiribati"},"flag":"https://flagcdn.com/ki.svg","cioc":"KIR","independent":true},{"name":"Kuwait","topLevelDomain":[".kw"],"alpha2Code":"KW","alpha3Code":"KWT","callingCodes":["965"],"capital":"Kuwait City","altSpellings":["KW","State of Kuwait","Dawlat al-Kuwait"],"subregion":"Western Asia","region":"Asia","population":4270563,"latlng":[29.5,45.75],"demonym":"Kuwaiti","area":17818.0,"timezones":["UTC+03:00"],"borders":["IRQ","SAU"],"nativeName":"الكويت","numericCode":"414","flags":{"svg":"https://flagcdn.com/kw.svg","png":"https://flagcdn.com/w320/kw.png"},"currencies":[{"code":"KWD","name":"Kuwaiti dinar","symbol":"د.ك"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Koweit","pt":"Kuwait","nl":"Koeweit","hr":"Kuvajt","fa":"کویت","de":"Kuwait","es":"Kuwait","fr":"Koweït","ja":"クウェート","it":"Kuwait","hu":"Kuvait"},"flag":"https://flagcdn.com/kw.svg","regionalBlocs":[{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"KUW","independent":true},{"name":"Kyrgyzstan","topLevelDomain":[".kg"],"alpha2Code":"KG","alpha3Code":"KGZ","callingCodes":["996"],"capital":"Bishkek","altSpellings":["KG","Киргизия","Kyrgyz Republic","Кыргыз Республикасы","Kyrgyz Respublikasy"],"subregion":"Central Asia","region":"Asia","population":6591600,"latlng":[41.0,75.0],"demonym":"Kirghiz","area":199951.0,"gini":29.7,"timezones":["UTC+06:00"],"borders":["CHN","KAZ","TJK","UZB"],"nativeName":"Кыргызстан","numericCode":"417","flags":{"svg":"https://flagcdn.com/kg.svg","png":"https://flagcdn.com/w320/kg.png"},"currencies":[{"code":"KGS","name":"Kyrgyzstani som","symbol":"с"}],"languages":[{"iso639_1":"ky","iso639_2":"kir","name":"Kyrgyz","nativeName":"Кыргызча"},{"iso639_1":"ru","iso639_2":"rus","name":"Russian","nativeName":"Русский"}],"translations":{"br":"Kirgizstan","pt":"Quirguizistão","nl":"Kirgizië","hr":"Kirgistan","fa":"قرقیزستان","de":"Kirgisistan","es":"Kirguizistán","fr":"Kirghizistan","ja":"キルギス","it":"Kirghizistan","hu":"Kirgizisztán"},"flag":"https://flagcdn.com/kg.svg","regionalBlocs":[{"acronym":"EEU","name":"Eurasian Economic Union","otherAcronyms":["EAEU"]}],"cioc":"KGZ","independent":true},{"name":"Lao People's Democratic Republic","topLevelDomain":[".la"],"alpha2Code":"LA","alpha3Code":"LAO","callingCodes":["856"],"capital":"Vientiane","altSpellings":["LA","Lao","Laos","Lao People's Democratic Republic","Sathalanalat Paxathipatai Paxaxon Lao"],"subregion":"South-Eastern Asia","region":"Asia","population":7275556,"latlng":[18.0,105.0],"demonym":"Laotian","area":236800.0,"gini":38.8,"timezones":["UTC+07:00"],"borders":["MMR","KHM","CHN","THA","VNM"],"nativeName":"ສາທາລະນະລັດ ປະຊາທິປະໄຕ ປະຊາຊົນລາວ","numericCode":"418","flags":{"svg":"https://flagcdn.com/la.svg","png":"https://flagcdn.com/w320/la.png"},"currencies":[{"code":"LAK","name":"Lao kip","symbol":"₭"}],"languages":[{"iso639_1":"lo","iso639_2":"lao","name":"Lao","nativeName":"ພາສາລາວ"}],"translations":{"br":"Laos","pt":"Laos","nl":"Laos","hr":"Laos","fa":"لائوس","de":"Laos","es":"Laos","fr":"Laos","ja":"ラオス人民民主共和国","it":"Laos","hu":"Laosz"},"flag":"https://flagcdn.com/la.svg","regionalBlocs":[{"acronym":"ASEAN","name":"Association of Southeast Asian Nations"}],"cioc":"LAO","independent":true},{"name":"Latvia","topLevelDomain":[".lv"],"alpha2Code":"LV","alpha3Code":"LVA","callingCodes":["371"],"capital":"Riga","altSpellings":["LV","Republic of Latvia","Latvijas Republika"],"subregion":"Northern Europe","region":"Europe","population":1901548,"latlng":[57.0,25.0],"demonym":"Latvian","area":64559.0,"gini":35.1,"timezones":["UTC+02:00"],"borders":["BLR","EST","LTU","RUS"],"nativeName":"Latvija","numericCode":"428","flags":{"svg":"https://flagcdn.com/lv.svg","png":"https://flagcdn.com/w320/lv.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"lv","iso639_2":"lav","name":"Latvian","nativeName":"latviešu valoda"}],"translations":{"br":"Latvia","pt":"Letónia","nl":"Letland","hr":"Latvija","fa":"لتونی","de":"Lettland","es":"Letonia","fr":"Lettonie","ja":"ラトビア","it":"Lettonia","hu":"Lettország"},"flag":"https://flagcdn.com/lv.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"LAT","independent":true},{"name":"Lebanon","topLevelDomain":[".lb"],"alpha2Code":"LB","alpha3Code":"LBN","callingCodes":["961"],"capital":"Beirut","altSpellings":["LB","Lebanese Republic","Al-Jumhūrīyah Al-Libnānīyah"],"subregion":"Western Asia","region":"Asia","population":6825442,"latlng":[33.83333333,35.83333333],"demonym":"Lebanese","area":10452.0,"gini":31.8,"timezones":["UTC+02:00"],"borders":["ISR","SYR"],"nativeName":"لبنان","numericCode":"422","flags":{"svg":"https://flagcdn.com/lb.svg","png":"https://flagcdn.com/w320/lb.png"},"currencies":[{"code":"LBP","name":"Lebanese pound","symbol":"ل.ل"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"},{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Liban","pt":"Líbano","nl":"Libanon","hr":"Libanon","fa":"لبنان","de":"Libanon","es":"Líbano","fr":"Liban","ja":"レバノン","it":"Libano","hu":"Libanon"},"flag":"https://flagcdn.com/lb.svg","regionalBlocs":[{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"LIB","independent":true},{"name":"Lesotho","topLevelDomain":[".ls"],"alpha2Code":"LS","alpha3Code":"LSO","callingCodes":["266"],"capital":"Maseru","altSpellings":["LS","Kingdom of Lesotho","Muso oa Lesotho"],"subregion":"Southern Africa","region":"Africa","population":2142252,"latlng":[-29.5,28.5],"demonym":"Mosotho","area":30355.0,"gini":44.9,"timezones":["UTC+02:00"],"borders":["ZAF"],"nativeName":"Lesotho","numericCode":"426","flags":{"svg":"https://flagcdn.com/ls.svg","png":"https://flagcdn.com/w320/ls.png"},"currencies":[{"code":"LSL","name":"Lesotho loti","symbol":"L"},{"code":"ZAR","name":"South African rand","symbol":"R"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"st","iso639_2":"sot","name":"Southern Sotho","nativeName":"Sesotho"}],"translations":{"br":"Lesotho","pt":"Lesoto","nl":"Lesotho","hr":"Lesoto","fa":"لسوتو","de":"Lesotho","es":"Lesotho","fr":"Lesotho","ja":"レソト","it":"Lesotho","hu":"Lesotho"},"flag":"https://flagcdn.com/ls.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"LES","independent":true},{"name":"Liberia","topLevelDomain":[".lr"],"alpha2Code":"LR","alpha3Code":"LBR","callingCodes":["231"],"capital":"Monrovia","altSpellings":["LR","Republic of Liberia"],"subregion":"Western Africa","region":"Africa","population":5057677,"latlng":[6.5,-9.5],"demonym":"Liberian","area":111369.0,"gini":35.3,"timezones":["UTC"],"borders":["GIN","CIV","SLE"],"nativeName":"Liberia","numericCode":"430","flags":{"svg":"https://flagcdn.com/lr.svg","png":"https://flagcdn.com/w320/lr.png"},"currencies":[{"code":"LRD","name":"Liberian dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Liberia","pt":"Libéria","nl":"Liberia","hr":"Liberija","fa":"لیبریا","de":"Liberia","es":"Liberia","fr":"Liberia","ja":"リベリア","it":"Liberia","hu":"Libéria"},"flag":"https://flagcdn.com/lr.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"LBR","independent":true},{"name":"Libya","topLevelDomain":[".ly"],"alpha2Code":"LY","alpha3Code":"LBY","callingCodes":["218"],"capital":"Tripoli","altSpellings":["LY","State of Libya","Dawlat Libya"],"subregion":"Northern Africa","region":"Africa","population":6871287,"latlng":[25.0,17.0],"demonym":"Libyan","area":1759540.0,"timezones":["UTC+01:00"],"borders":["DZA","TCD","EGY","NER","SDN","TUN"],"nativeName":"‏ليبيا","numericCode":"434","flags":{"svg":"https://flagcdn.com/ly.svg","png":"https://flagcdn.com/w320/ly.png"},"currencies":[{"code":"LYD","name":"Libyan dinar","symbol":"ل.د"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Libia","pt":"Líbia","nl":"Libië","hr":"Libija","fa":"لیبی","de":"Libyen","es":"Libia","fr":"Libye","ja":"リビア","it":"Libia","hu":"Líbia"},"flag":"https://flagcdn.com/ly.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]},{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"LBA","independent":true},{"name":"Liechtenstein","topLevelDomain":[".li"],"alpha2Code":"LI","alpha3Code":"LIE","callingCodes":["423"],"capital":"Vaduz","altSpellings":["LI","Principality of Liechtenstein","Fürstentum Liechtenstein"],"subregion":"Central Europe","region":"Europe","population":38137,"latlng":[47.26666666,9.53333333],"demonym":"Liechtensteiner","area":160.0,"timezones":["UTC+01:00"],"borders":["AUT","CHE"],"nativeName":"Liechtenstein","numericCode":"438","flags":{"svg":"https://flagcdn.com/li.svg","png":"https://flagcdn.com/w320/li.png"},"currencies":[{"code":"CHF","name":"Swiss franc","symbol":"Fr"}],"languages":[{"iso639_1":"de","iso639_2":"deu","name":"German","nativeName":"Deutsch"}],"translations":{"br":"Liechtenstein","pt":"Listenstaine","nl":"Liechtenstein","hr":"Lihtenštajn","fa":"لیختن‌اشتاین","de":"Liechtenstein","es":"Liechtenstein","fr":"Liechtenstein","ja":"リヒテンシュタイン","it":"Liechtenstein","hu":"Liechtenstein"},"flag":"https://flagcdn.com/li.svg","regionalBlocs":[{"acronym":"EFTA","name":"European Free Trade Association"}],"cioc":"LIE","independent":true},{"name":"Lithuania","topLevelDomain":[".lt"],"alpha2Code":"LT","alpha3Code":"LTU","callingCodes":["370"],"capital":"Vilnius","altSpellings":["LT","Republic of Lithuania","Lietuvos Respublika"],"subregion":"Northern Europe","region":"Europe","population":2794700,"latlng":[56.0,24.0],"demonym":"Lithuanian","area":65300.0,"gini":35.7,"timezones":["UTC+02:00"],"borders":["BLR","LVA","POL","RUS"],"nativeName":"Lietuva","numericCode":"440","flags":{"svg":"https://flagcdn.com/lt.svg","png":"https://flagcdn.com/w320/lt.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"lt","iso639_2":"lit","name":"Lithuanian","nativeName":"lietuvių kalba"}],"translations":{"br":"Lituania","pt":"Lituânia","nl":"Litouwen","hr":"Litva","fa":"لیتوانی","de":"Litauen","es":"Lituania","fr":"Lituanie","ja":"リトアニア","it":"Lituania","hu":"Litvánia"},"flag":"https://flagcdn.com/lt.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"LTU","independent":true},{"name":"Luxembourg","topLevelDomain":[".lu"],"alpha2Code":"LU","alpha3Code":"LUX","callingCodes":["352"],"capital":"Luxembourg","altSpellings":["LU","Grand Duchy of Luxembourg","Grand-Duché de Luxembourg","Großherzogtum Luxemburg","Groussherzogtum Lëtzebuerg"],"subregion":"Western Europe","region":"Europe","population":632275,"latlng":[49.75,6.16666666],"demonym":"Luxembourger","area":2586.0,"gini":35.4,"timezones":["UTC+01:00"],"borders":["BEL","FRA","DEU"],"nativeName":"Lëtzebuerg","numericCode":"442","flags":{"svg":"https://flagcdn.com/lu.svg","png":"https://flagcdn.com/w320/lu.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"de","iso639_2":"deu","name":"German","nativeName":"Deutsch"},{"iso639_1":"lb","iso639_2":"ltz","name":"Luxembourgish","nativeName":"Lëtzebuergesch"}],"translations":{"br":"Luksembourg","pt":"Luxemburgo","nl":"Luxemburg","hr":"Luksemburg","fa":"لوکزامبورگ","de":"Luxemburg","es":"Luxemburgo","fr":"Luxembourg","ja":"ルクセンブルク","it":"Lussemburgo","hu":"Luxemburg"},"flag":"https://flagcdn.com/lu.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"LUX","independent":true},{"name":"Macao","topLevelDomain":[".mo"],"alpha2Code":"MO","alpha3Code":"MAC","callingCodes":["853"],"altSpellings":["MO","澳门","Macao Special Administrative Region of the People's Republic of China","中華人民共和國澳門特別行政區","Região Administrativa Especial de Macau da República Popular da China"],"subregion":"Eastern Asia","region":"Asia","population":649342,"latlng":[22.16666666,113.55],"demonym":"Chinese","area":30.0,"timezones":["UTC+08:00"],"borders":["CHN"],"nativeName":"澳門","numericCode":"446","flags":{"svg":"https://flagcdn.com/mo.svg","png":"https://flagcdn.com/w320/mo.png"},"currencies":[{"code":"MOP","name":"Macanese pataca","symbol":"P"}],"languages":[{"iso639_1":"zh","iso639_2":"zho","name":"Chinese","nativeName":"中文 (Zhōngwén)"},{"iso639_1":"pt","iso639_2":"por","name":"Portuguese","nativeName":"Português"}],"translations":{"br":"Makao","pt":"Macau","nl":"Macao","hr":"Makao","fa":"مکائو","de":"Macao","es":"Macao","fr":"Macao","ja":"マカオ","it":"Macao","hu":"Makaó"},"flag":"https://flagcdn.com/mo.svg","independent":false},{"name":"North Macedonia","topLevelDomain":[".mk"],"alpha2Code":"MK","alpha3Code":"MKD","callingCodes":["389"],"capital":"Skopje","altSpellings":["MK","Republic of Macedonia","Република Македонија"],"subregion":"Southern Europe","region":"Europe","population":2083380,"latlng":[41.83333333,22.0],"demonym":"Macedonian","area":25713.0,"gini":33.0,"timezones":["UTC+01:00"],"borders":["ALB","BGR","GRC","UNK","SRB"],"nativeName":"Македонија","numericCode":"807","flags":{"svg":"https://flagcdn.com/mk.svg","png":"https://flagcdn.com/w320/mk.png"},"currencies":[{"code":"MKD","name":"Macedonian denar","symbol":"ден"}],"languages":[{"iso639_1":"mk","iso639_2":"mkd","name":"Macedonian","nativeName":"македонски јазик"}],"translations":{"br":"Makedonia an Norzh","pt":"Macedónia","nl":"Macedonië","hr":"Makedonija","de":"Mazedonien","es":"Macedonia","fr":"Macédoine","ja":"マケドニア旧ユーゴスラビア共和国","it":"Macedonia","hu":"Macedónia"},"flag":"https://flagcdn.com/mk.svg","regionalBlocs":[{"acronym":"CEFTA","name":"Central European Free Trade Agreement"}],"cioc":"MKD","independent":true},{"name":"Madagascar","topLevelDomain":[".mg"],"alpha2Code":"MG","alpha3Code":"MDG","callingCodes":["261"],"capital":"Antananarivo","altSpellings":["MG","Republic of Madagascar","Repoblikan'i Madagasikara","République de Madagascar"],"subregion":"Eastern Africa","region":"Africa","population":27691019,"latlng":[-20.0,47.0],"demonym":"Malagasy","area":587041.0,"gini":42.6,"timezones":["UTC+03:00"],"nativeName":"Madagasikara","numericCode":"450","flags":{"svg":"https://flagcdn.com/mg.svg","png":"https://flagcdn.com/w320/mg.png"},"currencies":[{"code":"MGA","name":"Malagasy ariary","symbol":"Ar"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"mg","iso639_2":"mlg","name":"Malagasy","nativeName":"fiteny malagasy"}],"translations":{"br":"Madagaskar","pt":"Madagáscar","nl":"Madagaskar","hr":"Madagaskar","fa":"ماداگاسکار","de":"Madagaskar","es":"Madagascar","fr":"Madagascar","ja":"マダガスカル","it":"Madagascar","hu":"Madagaszkár"},"flag":"https://flagcdn.com/mg.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"MAD","independent":true},{"name":"Malawi","topLevelDomain":[".mw"],"alpha2Code":"MW","alpha3Code":"MWI","callingCodes":["265"],"capital":"Lilongwe","altSpellings":["MW","Republic of Malawi"],"subregion":"Eastern Africa","region":"Africa","population":19129955,"latlng":[-13.5,34.0],"demonym":"Malawian","area":118484.0,"gini":44.7,"timezones":["UTC+02:00"],"borders":["MOZ","TZA","ZMB"],"nativeName":"Malawi","numericCode":"454","flags":{"svg":"https://flagcdn.com/mw.svg","png":"https://flagcdn.com/w320/mw.png"},"currencies":[{"code":"MWK","name":"Malawian kwacha","symbol":"MK"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"ny","iso639_2":"nya","name":"Chichewa","nativeName":"chiCheŵa"}],"translations":{"br":"Malawi","pt":"Malávi","nl":"Malawi","hr":"Malavi","fa":"مالاوی","de":"Malawi","es":"Malawi","fr":"Malawi","ja":"マラウイ","it":"Malawi","hu":"Malawi"},"flag":"https://flagcdn.com/mw.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"MAW","independent":true},{"name":"Malaysia","topLevelDomain":[".my"],"alpha2Code":"MY","alpha3Code":"MYS","callingCodes":["60"],"capital":"Kuala Lumpur","altSpellings":["MY"],"subregion":"South-Eastern Asia","region":"Asia","population":32365998,"latlng":[2.5,112.5],"demonym":"Malaysian","area":330803.0,"gini":41.1,"timezones":["UTC+08:00"],"borders":["BRN","IDN","THA"],"nativeName":"Malaysia","numericCode":"458","flags":{"svg":"https://flagcdn.com/my.svg","png":"https://flagcdn.com/w320/my.png"},"currencies":[{"code":"MYR","name":"Malaysian ringgit","symbol":"RM"}],"languages":[{"iso639_1":"ms","iso639_2":"zsm","name":"Malaysian","nativeName":"بهاس مليسيا"}],"translations":{"br":"Malaysia","pt":"Malásia","nl":"Maleisië","hr":"Malezija","fa":"مالزی","de":"Malaysia","es":"Malasia","fr":"Malaisie","ja":"マレーシア","it":"Malesia","hu":"Malajzia"},"flag":"https://flagcdn.com/my.svg","regionalBlocs":[{"acronym":"ASEAN","name":"Association of Southeast Asian Nations"}],"cioc":"MAS","independent":true},{"name":"Maldives","topLevelDomain":[".mv"],"alpha2Code":"MV","alpha3Code":"MDV","callingCodes":["960"],"capital":"Malé","altSpellings":["MV","Maldive Islands","Republic of the Maldives","Dhivehi Raajjeyge Jumhooriyya"],"subregion":"Southern Asia","region":"Asia","population":540542,"latlng":[3.25,73.0],"demonym":"Maldivan","area":300.0,"gini":31.3,"timezones":["UTC+05:00"],"nativeName":"Maldives","numericCode":"462","flags":{"svg":"https://flagcdn.com/mv.svg","png":"https://flagcdn.com/w320/mv.png"},"currencies":[{"code":"MVR","name":"Maldivian rufiyaa","symbol":".ރ"}],"languages":[{"iso639_1":"dv","iso639_2":"div","name":"Divehi","nativeName":"ދިވެހި"}],"translations":{"br":"Maldivez","pt":"Maldivas","nl":"Maldiven","hr":"Maldivi","fa":"مالدیو","de":"Malediven","es":"Maldivas","fr":"Maldives","ja":"モルディブ","it":"Maldive","hu":"Maldív-szigetek"},"flag":"https://flagcdn.com/mv.svg","regionalBlocs":[{"acronym":"SAARC","name":"South Asian Association for Regional Cooperation"}],"cioc":"MDV","independent":true},{"name":"Mali","topLevelDomain":[".ml"],"alpha2Code":"ML","alpha3Code":"MLI","callingCodes":["223"],"capital":"Bamako","altSpellings":["ML","Republic of Mali","République du Mali"],"subregion":"Western Africa","region":"Africa","population":20250834,"latlng":[17.0,-4.0],"demonym":"Malian","area":1240192.0,"gini":33.0,"timezones":["UTC"],"borders":["DZA","BFA","GIN","CIV","MRT","NER","SEN"],"nativeName":"Mali","numericCode":"466","flags":{"svg":"https://flagcdn.com/ml.svg","png":"https://flagcdn.com/w320/ml.png"},"currencies":[{"code":"XOF","name":"West African CFA franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Mali","pt":"Mali","nl":"Mali","hr":"Mali","fa":"مالی","de":"Mali","es":"Mali","fr":"Mali","ja":"マリ","it":"Mali","hu":"Mali"},"flag":"https://flagcdn.com/ml.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"MLI","independent":true},{"name":"Malta","topLevelDomain":[".mt"],"alpha2Code":"MT","alpha3Code":"MLT","callingCodes":["356"],"capital":"Valletta","altSpellings":["MT","Republic of Malta","Repubblika ta' Malta"],"subregion":"Southern Europe","region":"Europe","population":525285,"latlng":[35.83333333,14.58333333],"demonym":"Maltese","area":316.0,"gini":28.7,"timezones":["UTC+01:00"],"nativeName":"Malta","numericCode":"470","flags":{"svg":"https://flagcdn.com/mt.svg","png":"https://flagcdn.com/w320/mt.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"mt","iso639_2":"mlt","name":"Maltese","nativeName":"Malti"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Malta","pt":"Malta","nl":"Malta","hr":"Malta","fa":"مالت","de":"Malta","es":"Malta","fr":"Malte","ja":"マルタ","it":"Malta","hu":"Málta"},"flag":"https://flagcdn.com/mt.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"MLT","independent":true},{"name":"Marshall Islands","topLevelDomain":[".mh"],"alpha2Code":"MH","alpha3Code":"MHL","callingCodes":["692"],"capital":"Majuro","altSpellings":["MH","Republic of the Marshall Islands","Aolepān Aorōkin M̧ajeļ"],"subregion":"Micronesia","region":"Oceania","population":59194,"latlng":[9.0,168.0],"demonym":"Marshallese","area":181.0,"timezones":["UTC+12:00"],"nativeName":"M̧ajeļ","numericCode":"584","flags":{"svg":"https://flagcdn.com/mh.svg","png":"https://flagcdn.com/w320/mh.png"},"currencies":[{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"mh","iso639_2":"mah","name":"Marshallese","nativeName":"Kajin M̧ajeļ"}],"translations":{"br":"Inizi Marshall","pt":"Ilhas Marshall","nl":"Marshalleilanden","hr":"Maršalovi Otoci","fa":"جزایر مارشال","de":"Marshallinseln","es":"Islas Marshall","fr":"Îles Marshall","ja":"マーシャル諸島","it":"Isole Marshall","hu":"Marshall-szigetek"},"flag":"https://flagcdn.com/mh.svg","cioc":"MHL","independent":true},{"name":"Martinique","topLevelDomain":[".mq"],"alpha2Code":"MQ","alpha3Code":"MTQ","callingCodes":["596"],"capital":"Fort-de-France","altSpellings":["MQ"],"subregion":"Caribbean","region":"Americas","population":378243,"latlng":[14.666667,-61.0],"demonym":"French","timezones":["UTC-04:00"],"nativeName":"Martinique","numericCode":"474","flags":{"svg":"https://flagcdn.com/mq.svg","png":"https://flagcdn.com/w320/mq.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Martinik","pt":"Martinica","nl":"Martinique","hr":"Martinique","fa":"مونتسرات","de":"Martinique","es":"Martinica","fr":"Martinique","ja":"マルティニーク","it":"Martinica","hu":"Martinique"},"flag":"https://flagcdn.com/mq.svg","independent":false},{"name":"Mauritania","topLevelDomain":[".mr"],"alpha2Code":"MR","alpha3Code":"MRT","callingCodes":["222"],"capital":"Nouakchott","altSpellings":["MR","Islamic Republic of Mauritania","al-Jumhūriyyah al-ʾIslāmiyyah al-Mūrītāniyyah"],"subregion":"Western Africa","region":"Africa","population":4649660,"latlng":[20.0,-12.0],"demonym":"Mauritanian","area":1030700.0,"gini":32.6,"timezones":["UTC"],"borders":["DZA","MLI","SEN","ESH"],"nativeName":"موريتانيا","numericCode":"478","flags":{"svg":"https://flagcdn.com/mr.svg","png":"https://flagcdn.com/w320/mr.png"},"currencies":[{"code":"MRO","name":"Mauritanian ouguiya","symbol":"UM"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Maouritania","pt":"Mauritânia","nl":"Mauritanië","hr":"Mauritanija","fa":"موریتانی","de":"Mauretanien","es":"Mauritania","fr":"Mauritanie","ja":"モーリタニア","it":"Mauritania","hu":"Mauritánia"},"flag":"https://flagcdn.com/mr.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]},{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"MTN","independent":true},{"name":"Mauritius","topLevelDomain":[".mu"],"alpha2Code":"MU","alpha3Code":"MUS","callingCodes":["230"],"capital":"Port Louis","altSpellings":["MU","Republic of Mauritius","République de Maurice"],"subregion":"Eastern Africa","region":"Africa","population":1265740,"latlng":[-20.28333333,57.55],"demonym":"Mauritian","area":2040.0,"gini":36.8,"timezones":["UTC+04:00"],"nativeName":"Maurice","numericCode":"480","flags":{"svg":"https://flagcdn.com/mu.svg","png":"https://flagcdn.com/w320/mu.png"},"currencies":[{"code":"MUR","name":"Mauritian rupee","symbol":"₨"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Moris","pt":"Maurícia","nl":"Mauritius","hr":"Mauricijus","fa":"موریس","de":"Mauritius","es":"Mauricio","fr":"Île Maurice","ja":"モーリシャス","it":"Mauritius","hu":"Mauritius"},"flag":"https://flagcdn.com/mu.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"MRI","independent":true},{"name":"Mayotte","topLevelDomain":[".yt"],"alpha2Code":"YT","alpha3Code":"MYT","callingCodes":["262"],"capital":"Mamoudzou","altSpellings":["YT","Department of Mayotte","Département de Mayotte"],"subregion":"Eastern Africa","region":"Africa","population":226915,"latlng":[-12.83333333,45.16666666],"demonym":"French","timezones":["UTC+03:00"],"nativeName":"Mayotte","numericCode":"175","flags":{"svg":"https://flagcdn.com/yt.svg","png":"https://flagcdn.com/w320/yt.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Mayotte","pt":"Mayotte","nl":"Mayotte","hr":"Mayotte","fa":"مایوت","de":"Mayotte","es":"Mayotte","fr":"Mayotte","ja":"マヨット","it":"Mayotte","hu":"Mayotte"},"flag":"https://flagcdn.com/yt.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"independent":false},{"name":"Mexico","topLevelDomain":[".mx"],"alpha2Code":"MX","alpha3Code":"MEX","callingCodes":["52"],"capital":"Mexico City","altSpellings":["MX","Mexicanos","United Mexican States","Estados Unidos Mexicanos"],"subregion":"North America","region":"Americas","population":128932753,"latlng":[23.0,-102.0],"demonym":"Mexican","area":1964375.0,"gini":45.4,"timezones":["UTC-08:00","UTC-07:00","UTC-06:00"],"borders":["BLZ","GTM","USA"],"nativeName":"México","numericCode":"484","flags":{"svg":"https://flagcdn.com/mx.svg","png":"https://flagcdn.com/w320/mx.png"},"currencies":[{"code":"MXN","name":"Mexican peso","symbol":"$"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Mec'hiko","pt":"México","nl":"Mexico","hr":"Meksiko","fa":"مکزیک","de":"Mexiko","es":"México","fr":"Mexique","ja":"メキシコ","it":"Messico","hu":"Mexikó"},"flag":"https://flagcdn.com/mx.svg","regionalBlocs":[{"acronym":"PA","name":"Pacific Alliance","otherNames":["Alianza del Pacífico"]},{"acronym":"NAFTA","name":"North American Free Trade Agreement","otherNames":["Tratado de Libre Comercio de América del Norte","Accord de Libre-échange Nord-Américain"]}],"cioc":"MEX","independent":true},{"name":"Micronesia (Federated States of)","topLevelDomain":[".fm"],"alpha2Code":"FM","alpha3Code":"FSM","callingCodes":["691"],"capital":"Palikir","altSpellings":["FM","Federated States of Micronesia"],"subregion":"Micronesia","region":"Oceania","population":115021,"latlng":[6.91666666,158.25],"demonym":"Micronesian","area":702.0,"gini":40.1,"timezones":["UTC+10:00","UTC+11:00"],"nativeName":"Micronesia","numericCode":"583","flags":{"svg":"https://flagcdn.com/fm.svg","png":"https://flagcdn.com/w320/fm.png"},"currencies":[{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Mikronezia","pt":"Micronésia","nl":"Micronesië","hr":"Mikronezija","fa":"ایالات فدرال میکرونزی","de":"Mikronesien","es":"Micronesia","fr":"Micronésie","ja":"ミクロネシア連邦","it":"Micronesia","hu":"Mikronézia"},"flag":"https://flagcdn.com/fm.svg","cioc":"FSM","independent":true},{"name":"Moldova (Republic of)","topLevelDomain":[".md"],"alpha2Code":"MD","alpha3Code":"MDA","callingCodes":["373"],"capital":"Chișinău","altSpellings":["MD","Republic of Moldova","Republica Moldova"],"subregion":"Eastern Europe","region":"Europe","population":2617820,"latlng":[47.0,29.0],"demonym":"Moldovan","area":33846.0,"gini":25.7,"timezones":["UTC+02:00"],"borders":["ROU","UKR"],"nativeName":"Moldova","numericCode":"498","flags":{"svg":"https://flagcdn.com/md.svg","png":"https://flagcdn.com/w320/md.png"},"currencies":[{"code":"MDL","name":"Moldovan leu","symbol":"L"}],"languages":[{"iso639_1":"ro","iso639_2":"ron","name":"Romanian","nativeName":"Română"}],"translations":{"br":"Moldova","pt":"Moldávia","nl":"Moldavië","hr":"Moldova","fa":"مولداوی","de":"Moldawie","es":"Moldavia","fr":"Moldavie","ja":"モルドバ共和国","it":"Moldavia","hu":"Moldova"},"flag":"https://flagcdn.com/md.svg","regionalBlocs":[{"acronym":"CEFTA","name":"Central European Free Trade Agreement"}],"cioc":"MDA","independent":true},{"name":"Monaco","topLevelDomain":[".mc"],"alpha2Code":"MC","alpha3Code":"MCO","callingCodes":["377"],"capital":"Monaco","altSpellings":["MC","Principality of Monaco","Principauté de Monaco"],"subregion":"Western Europe","region":"Europe","population":39244,"latlng":[43.73333333,7.4],"demonym":"Monegasque","area":2.02,"timezones":["UTC+01:00"],"borders":["FRA"],"nativeName":"Monaco","numericCode":"492","flags":{"svg":"https://flagcdn.com/mc.svg","png":"https://flagcdn.com/w320/mc.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Monako","pt":"Mónaco","nl":"Monaco","hr":"Monako","fa":"موناکو","de":"Monaco","es":"Mónaco","fr":"Monaco","ja":"モナコ","it":"Principato di Monaco","hu":"Monaco"},"flag":"https://flagcdn.com/mc.svg","cioc":"MON","independent":true},{"name":"Mongolia","topLevelDomain":[".mn"],"alpha2Code":"MN","alpha3Code":"MNG","callingCodes":["976"],"capital":"Ulan Bator","altSpellings":["MN"],"subregion":"Eastern Asia","region":"Asia","population":3278292,"latlng":[46.0,105.0],"demonym":"Mongolian","area":1564110.0,"gini":32.7,"timezones":["UTC+07:00","UTC+08:00"],"borders":["CHN","RUS"],"nativeName":"Монгол улс","numericCode":"496","flags":{"svg":"https://flagcdn.com/mn.svg","png":"https://flagcdn.com/w320/mn.png"},"currencies":[{"code":"MNT","name":"Mongolian tögrög","symbol":"₮"}],"languages":[{"iso639_1":"mn","iso639_2":"mon","name":"Mongolian","nativeName":"Монгол хэл"}],"translations":{"br":"Mongolia","pt":"Mongólia","nl":"Mongolië","hr":"Mongolija","fa":"مغولستان","de":"Mongolei","es":"Mongolia","fr":"Mongolie","ja":"モンゴル","it":"Mongolia","hu":"Mongólia"},"flag":"https://flagcdn.com/mn.svg","cioc":"MGL","independent":true},{"name":"Montenegro","topLevelDomain":[".me"],"alpha2Code":"ME","alpha3Code":"MNE","callingCodes":["382"],"capital":"Podgorica","altSpellings":["ME","Crna Gora"],"subregion":"Southern Europe","region":"Europe","population":621718,"latlng":[42.5,19.3],"demonym":"Montenegrin","area":13812.0,"gini":38.5,"timezones":["UTC+01:00"],"borders":["ALB","BIH","HRV","UNK","SRB"],"nativeName":"Црна Гора","numericCode":"499","flags":{"svg":"https://flagcdn.com/me.svg","png":"https://flagcdn.com/w320/me.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"sr","iso639_2":"srp","name":"Serbian","nativeName":"српски језик"},{"iso639_1":"bs","iso639_2":"bos","name":"Bosnian","nativeName":"bosanski jezik"},{"iso639_1":"sq","iso639_2":"sqi","name":"Albanian","nativeName":"Shqip"},{"iso639_1":"hr","iso639_2":"hrv","name":"Croatian","nativeName":"hrvatski jezik"}],"translations":{"br":"Montenegro","pt":"Montenegro","nl":"Montenegro","hr":"Crna Gora","fa":"مونته‌نگرو","de":"Montenegro","es":"Montenegro","fr":"Monténégro","ja":"モンテネグロ","it":"Montenegro","hu":"Montenegró"},"flag":"https://flagcdn.com/me.svg","regionalBlocs":[{"acronym":"CEFTA","name":"Central European Free Trade Agreement"}],"cioc":"MNE","independent":true},{"name":"Montserrat","topLevelDomain":[".ms"],"alpha2Code":"MS","alpha3Code":"MSR","callingCodes":["1"],"capital":"Plymouth","altSpellings":["MS"],"subregion":"Caribbean","region":"Americas","population":4922,"latlng":[16.75,-62.2],"demonym":"Montserratian","area":102.0,"timezones":["UTC-04:00"],"nativeName":"Montserrat","numericCode":"500","flags":{"svg":"https://flagcdn.com/ms.svg","png":"https://flagcdn.com/w320/ms.png"},"currencies":[{"code":"XCD","name":"East Caribbean dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Montserrat","pt":"Monserrate","nl":"Montserrat","hr":"Montserrat","fa":"مایوت","de":"Montserrat","es":"Montserrat","fr":"Montserrat","ja":"モントセラト","it":"Montserrat","hu":"Montserrat"},"flag":"https://flagcdn.com/ms.svg","regionalBlocs":[{"acronym":"CARICOM","name":"Caribbean Community","otherNames":["Comunidad del Caribe","Communauté Caribéenne","Caribische Gemeenschap"]}],"independent":false},{"name":"Morocco","topLevelDomain":[".ma"],"alpha2Code":"MA","alpha3Code":"MAR","callingCodes":["212"],"capital":"Rabat","altSpellings":["MA","Kingdom of Morocco","Al-Mamlakah al-Maġribiyah"],"subregion":"Northern Africa","region":"Africa","population":36910558,"latlng":[32.0,-5.0],"demonym":"Moroccan","area":446550.0,"gini":39.5,"timezones":["UTC"],"borders":["DZA","ESH","ESP"],"nativeName":"المغرب","numericCode":"504","flags":{"svg":"https://flagcdn.com/ma.svg","png":"https://flagcdn.com/w320/ma.png"},"currencies":[{"code":"MAD","name":"Moroccan dirham","symbol":"د.م."}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Maroko","pt":"Marrocos","nl":"Marokko","hr":"Maroko","fa":"المغرب","de":"Marokko","es":"Marruecos","fr":"Maroc","ja":"モロッコ","it":"Marocco","hu":"Marokkó"},"flag":"https://flagcdn.com/ma.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]},{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"MAR","independent":true},{"name":"Mozambique","topLevelDomain":[".mz"],"alpha2Code":"MZ","alpha3Code":"MOZ","callingCodes":["258"],"capital":"Maputo","altSpellings":["MZ","Republic of Mozambique","República de Moçambique"],"subregion":"Eastern Africa","region":"Africa","population":31255435,"latlng":[-18.25,35.0],"demonym":"Mozambican","area":801590.0,"gini":54.0,"timezones":["UTC+02:00"],"borders":["MWI","ZAF","SWZ","TZA","ZMB","ZWE"],"nativeName":"Moçambique","numericCode":"508","flags":{"svg":"https://flagcdn.com/mz.svg","png":"https://flagcdn.com/w320/mz.png"},"currencies":[{"code":"MZN","name":"Mozambican metical","symbol":"MT"}],"languages":[{"iso639_1":"pt","iso639_2":"por","name":"Portuguese","nativeName":"Português"}],"translations":{"br":"Mozambik","pt":"Moçambique","nl":"Mozambique","hr":"Mozambik","fa":"موزامبیک","de":"Mosambik","es":"Mozambique","fr":"Mozambique","ja":"モザンビーク","it":"Mozambico","hu":"Mozambik"},"flag":"https://flagcdn.com/mz.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"MOZ","independent":true},{"name":"Myanmar","topLevelDomain":[".mm"],"alpha2Code":"MM","alpha3Code":"MMR","callingCodes":["95"],"capital":"Naypyidaw","altSpellings":["MM","Burma","Republic of the Union of Myanmar","Pyidaunzu Thanmăda Myăma Nainngandaw"],"subregion":"South-Eastern Asia","region":"Asia","population":54409794,"latlng":[22.0,98.0],"demonym":"Burmese","area":676578.0,"gini":30.7,"timezones":["UTC+06:30"],"borders":["BGD","CHN","IND","LAO","THA"],"nativeName":"Myanma","numericCode":"104","flags":{"svg":"https://flagcdn.com/mm.svg","png":"https://flagcdn.com/w320/mm.png"},"currencies":[{"code":"MMK","name":"Burmese kyat","symbol":"Ks"}],"languages":[{"iso639_1":"my","iso639_2":"mya","name":"Burmese","nativeName":"ဗမာစာ"}],"translations":{"br":"Myanmar","pt":"Myanmar","nl":"Myanmar","hr":"Mijanmar","fa":"میانمار","de":"Myanmar","es":"Myanmar","fr":"Myanmar","ja":"ミャンマー","it":"Birmania","hu":"Mianmar"},"flag":"https://flagcdn.com/mm.svg","regionalBlocs":[{"acronym":"ASEAN","name":"Association of Southeast Asian Nations"}],"cioc":"MYA","independent":true},{"name":"Namibia","topLevelDomain":[".na"],"alpha2Code":"NA","alpha3Code":"NAM","callingCodes":["264"],"capital":"Windhoek","altSpellings":["NA","Namibië","Republic of Namibia"],"subregion":"Southern Africa","region":"Africa","population":2540916,"latlng":[-22.0,17.0],"demonym":"Namibian","area":825615.0,"gini":59.1,"timezones":["UTC+01:00"],"borders":["AGO","BWA","ZAF","ZMB"],"nativeName":"Namibia","numericCode":"516","flags":{"svg":"https://flagcdn.com/na.svg","png":"https://flagcdn.com/w320/na.png"},"currencies":[{"code":"NAD","name":"Namibian dollar","symbol":"$"},{"code":"ZAR","name":"South African rand","symbol":"R"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"af","iso639_2":"afr","name":"Afrikaans","nativeName":"Afrikaans"}],"translations":{"br":"Namibia","pt":"Namíbia","nl":"Namibië","hr":"Namibija","fa":"نامیبیا","de":"Namibia","es":"Namibia","fr":"Namibie","ja":"ナミビア","it":"Namibia","hu":"Namíbia"},"flag":"https://flagcdn.com/na.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"NAM","independent":true},{"name":"Nauru","topLevelDomain":[".nr"],"alpha2Code":"NR","alpha3Code":"NRU","callingCodes":["674"],"capital":"Yaren","altSpellings":["NR","Naoero","Pleasant Island","Republic of Nauru","Ripublik Naoero"],"subregion":"Micronesia","region":"Oceania","population":10834,"latlng":[-0.53333333,166.91666666],"demonym":"Nauruan","area":21.0,"gini":34.8,"timezones":["UTC+12:00"],"nativeName":"Nauru","numericCode":"520","flags":{"svg":"https://flagcdn.com/nr.svg","png":"https://flagcdn.com/w320/nr.png"},"currencies":[{"code":"AUD","name":"Australian dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"na","iso639_2":"nau","name":"Nauruan","nativeName":"Dorerin Naoero"}],"translations":{"br":"Nauru","pt":"Nauru","nl":"Nauru","hr":"Nauru","fa":"نائورو","de":"Nauru","es":"Nauru","fr":"Nauru","ja":"ナウル","it":"Nauru","hu":"Nauru"},"flag":"https://flagcdn.com/nr.svg","cioc":"NRU","independent":true},{"name":"Nepal","topLevelDomain":[".np"],"alpha2Code":"NP","alpha3Code":"NPL","callingCodes":["977"],"capital":"Kathmandu","altSpellings":["NP","Federal Democratic Republic of Nepal","Loktāntrik Ganatantra Nepāl"],"subregion":"Southern Asia","region":"Asia","population":29136808,"latlng":[28.0,84.0],"demonym":"Nepalese","area":147181.0,"gini":32.8,"timezones":["UTC+05:45"],"borders":["CHN","IND"],"nativeName":"नेपाल","numericCode":"524","flags":{"svg":"https://flagcdn.com/np.svg","png":"https://flagcdn.com/w320/np.png"},"currencies":[{"code":"NPR","name":"Nepalese rupee","symbol":"₨"}],"languages":[{"iso639_1":"ne","iso639_2":"nep","name":"Nepali","nativeName":"नेपाली"}],"translations":{"br":"Nepal","pt":"Nepal","nl":"Nepal","hr":"Nepal","fa":"نپال","de":"Népal","es":"Nepal","fr":"Népal","ja":"ネパール","it":"Nepal","hu":"Nepál"},"flag":"https://flagcdn.com/np.svg","regionalBlocs":[{"acronym":"SAARC","name":"South Asian Association for Regional Cooperation"}],"cioc":"NEP","independent":true},{"name":"Netherlands","topLevelDomain":[".nl"],"alpha2Code":"NL","alpha3Code":"NLD","callingCodes":["31"],"capital":"Amsterdam","altSpellings":["NL","Holland","Nederland"],"subregion":"Western Europe","region":"Europe","population":17441139,"latlng":[52.5,5.75],"demonym":"Dutch","area":41850.0,"gini":28.1,"timezones":["UTC-04:00","UTC+01:00"],"borders":["BEL","DEU"],"nativeName":"Nederland","numericCode":"528","flags":{"svg":"https://flagcdn.com/nl.svg","png":"https://flagcdn.com/w320/nl.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"nl","iso639_2":"nld","name":"Dutch","nativeName":"Nederlands"}],"translations":{"br":"Izelvroioù","pt":"Países Baixos","nl":"Nederland","hr":"Nizozemska","fa":"پادشاهی هلند","de":"Niederlande","es":"Países Bajos","fr":"Pays-Bas","ja":"オランダ","it":"Paesi Bassi","hu":"Hollandia"},"flag":"https://flagcdn.com/nl.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"NED","independent":true},{"name":"New Caledonia","topLevelDomain":[".nc"],"alpha2Code":"NC","alpha3Code":"NCL","callingCodes":["687"],"capital":"Nouméa","altSpellings":["NC"],"subregion":"Melanesia","region":"Oceania","population":271960,"latlng":[-21.5,165.5],"demonym":"New Caledonian","area":18575.0,"timezones":["UTC+11:00"],"nativeName":"Nouvelle-Calédonie","numericCode":"540","flags":{"svg":"https://flagcdn.com/nc.svg","png":"https://flagcdn.com/w320/nc.png"},"currencies":[{"code":"XPF","name":"CFP franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Kaledonia-Nevez","pt":"Nova Caledónia","nl":"Nieuw-Caledonië","hr":"Nova Kaledonija","fa":"کالدونیای جدید","de":"Neukaledonien","es":"Nueva Caledonia","fr":"Nouvelle-Calédonie","ja":"ニューカレドニア","it":"Nuova Caledonia","hu":"Új-Kaledónia"},"flag":"https://flagcdn.com/nc.svg","independent":false},{"name":"New Zealand","topLevelDomain":[".nz"],"alpha2Code":"NZ","alpha3Code":"NZL","callingCodes":["64"],"capital":"Wellington","altSpellings":["NZ","Aotearoa"],"subregion":"Australia and New Zealand","region":"Oceania","population":5084300,"latlng":[-41.0,174.0],"demonym":"New Zealander","area":270467.0,"timezones":["UTC-11:00","UTC-10:00","UTC+12:00","UTC+12:45","UTC+13:00"],"nativeName":"New Zealand","numericCode":"554","flags":{"svg":"https://flagcdn.com/nz.svg","png":"https://flagcdn.com/w320/nz.png"},"currencies":[{"code":"NZD","name":"New Zealand dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"mi","iso639_2":"mri","name":"Māori","nativeName":"te reo Māori"}],"translations":{"br":"Zeland-Nevez","pt":"Nova Zelândia","nl":"Nieuw-Zeeland","hr":"Novi Zeland","fa":"نیوزیلند","de":"Neuseeland","es":"Nueva Zelanda","fr":"Nouvelle-Zélande","ja":"ニュージーランド","it":"Nuova Zelanda","hu":"Új-Zéland"},"flag":"https://flagcdn.com/nz.svg","cioc":"NZL","independent":true},{"name":"Nicaragua","topLevelDomain":[".ni"],"alpha2Code":"NI","alpha3Code":"NIC","callingCodes":["505"],"capital":"Managua","altSpellings":["NI","Republic of Nicaragua","República de Nicaragua"],"subregion":"Central America","region":"Americas","population":6624554,"latlng":[13.0,-85.0],"demonym":"Nicaraguan","area":130373.0,"gini":46.2,"timezones":["UTC-06:00"],"borders":["CRI","HND"],"nativeName":"Nicaragua","numericCode":"558","flags":{"svg":"https://flagcdn.com/ni.svg","png":"https://flagcdn.com/w320/ni.png"},"currencies":[{"code":"NIO","name":"Nicaraguan córdoba","symbol":"C$"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Nicaragua","pt":"Nicarágua","nl":"Nicaragua","hr":"Nikaragva","fa":"نیکاراگوئه","de":"Nicaragua","es":"Nicaragua","fr":"Nicaragua","ja":"ニカラグア","it":"Nicaragua","hu":"Nicaragua"},"flag":"https://flagcdn.com/ni.svg","regionalBlocs":[{"acronym":"CAIS","name":"Central American Integration System","otherAcronyms":["SICA"],"otherNames":["Sistema de la Integración Centroamericana,"]}],"cioc":"NCA","independent":true},{"name":"Niger","topLevelDomain":[".ne"],"alpha2Code":"NE","alpha3Code":"NER","callingCodes":["227"],"capital":"Niamey","altSpellings":["NE","Nijar","Republic of Niger","République du Niger"],"subregion":"Western Africa","region":"Africa","population":24206636,"latlng":[16.0,8.0],"demonym":"Nigerien","area":1267000.0,"gini":34.3,"timezones":["UTC+01:00"],"borders":["DZA","BEN","BFA","TCD","LBY","MLI","NGA"],"nativeName":"Niger","numericCode":"562","flags":{"svg":"https://flagcdn.com/ne.svg","png":"https://flagcdn.com/w320/ne.png"},"currencies":[{"code":"XOF","name":"West African CFA franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Niger","pt":"Níger","nl":"Niger","hr":"Niger","fa":"نیجر","de":"Niger","es":"Níger","fr":"Niger","ja":"ニジェール","it":"Niger","hu":"Niger"},"flag":"https://flagcdn.com/ne.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"NIG","independent":true},{"name":"Nigeria","topLevelDomain":[".ng"],"alpha2Code":"NG","alpha3Code":"NGA","callingCodes":["234"],"capital":"Abuja","altSpellings":["NG","Nijeriya","Naíjíríà","Federal Republic of Nigeria"],"subregion":"Western Africa","region":"Africa","population":206139587,"latlng":[10.0,8.0],"demonym":"Nigerian","area":923768.0,"gini":35.1,"timezones":["UTC+01:00"],"borders":["BEN","CMR","TCD","NER"],"nativeName":"Nigeria","numericCode":"566","flags":{"svg":"https://flagcdn.com/ng.svg","png":"https://flagcdn.com/w320/ng.png"},"currencies":[{"code":"NGN","name":"Nigerian naira","symbol":"₦"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Nigeria","pt":"Nigéria","nl":"Nigeria","hr":"Nigerija","fa":"نیجریه","de":"Nigeria","es":"Nigeria","fr":"Nigéria","ja":"ナイジェリア","it":"Nigeria","hu":"Nigéria"},"flag":"https://flagcdn.com/ng.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"NGR","independent":true},{"name":"Niue","topLevelDomain":[".nu"],"alpha2Code":"NU","alpha3Code":"NIU","callingCodes":["683"],"capital":"Alofi","altSpellings":["NU"],"subregion":"Polynesia","region":"Oceania","population":1470,"latlng":[-19.03333333,-169.86666666],"demonym":"Niuean","area":260.0,"timezones":["UTC-11:00"],"nativeName":"Niuē","numericCode":"570","flags":{"svg":"https://flagcdn.com/nu.svg","png":"https://flagcdn.com/w320/nu.png"},"currencies":[{"code":"NZD","name":"New Zealand dollar","symbol":"$"},{"code":"NZD","name":"Niue dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Niue","pt":"Niue","nl":"Niue","hr":"Niue","fa":"نیووی","de":"Niue","es":"Niue","fr":"Niue","ja":"ニウエ","it":"Niue","hu":"Niue"},"flag":"https://flagcdn.com/nu.svg","independent":true},{"name":"Norfolk Island","topLevelDomain":[".nf"],"alpha2Code":"NF","alpha3Code":"NFK","callingCodes":["672"],"capital":"Kingston","altSpellings":["NF","Territory of Norfolk Island","Teratri of Norf'k Ailen"],"subregion":"Australia and New Zealand","region":"Oceania","population":2302,"latlng":[-29.03333333,167.95],"demonym":"Norfolk Islander","area":36.0,"timezones":["UTC+11:30"],"nativeName":"Norfolk Island","numericCode":"574","flags":{"svg":"https://flagcdn.com/nf.svg","png":"https://flagcdn.com/w320/nf.png"},"currencies":[{"code":"AUD","name":"Australian dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Enez Norfolk","pt":"Ilha Norfolk","nl":"Norfolkeiland","hr":"Otok Norfolk","fa":"جزیره نورفک","de":"Norfolkinsel","es":"Isla de Norfolk","fr":"Île de Norfolk","ja":"ノーフォーク島","it":"Isola Norfolk","hu":"Norfolk-sziget"},"flag":"https://flagcdn.com/nf.svg","independent":false},{"name":"Korea (Democratic People's Republic of)","topLevelDomain":[".kp"],"alpha2Code":"KP","alpha3Code":"PRK","callingCodes":["850"],"capital":"Pyongyang","altSpellings":["KP","Democratic People's Republic of Korea","조선민주주의인민공화국","Chosŏn Minjujuŭi Inmin Konghwaguk"],"subregion":"Eastern Asia","region":"Asia","population":25778815,"latlng":[40.0,127.0],"demonym":"North Korean","area":120538.0,"timezones":["UTC+09:00"],"borders":["CHN","KOR","RUS"],"nativeName":"북한","numericCode":"408","flags":{"svg":"https://flagcdn.com/kp.svg","png":"https://flagcdn.com/w320/kp.png"},"currencies":[{"code":"KPW","name":"North Korean won","symbol":"₩"}],"languages":[{"iso639_1":"ko","iso639_2":"kor","name":"Korean","nativeName":"한국어"}],"translations":{"br":"Korea an Norzh","pt":"Coreia do Norte","nl":"Noord-Korea","hr":"Sjeverna Koreja","fa":"کره جنوبی","de":"Nordkorea","es":"Corea del Norte","fr":"Corée du Nord","ja":"朝鮮民主主義人民共和国","it":"Corea del Nord","hu":"Észak-Korea"},"flag":"https://flagcdn.com/kp.svg","cioc":"PRK","independent":true},{"name":"Northern Mariana Islands","topLevelDomain":[".mp"],"alpha2Code":"MP","alpha3Code":"MNP","callingCodes":["1"],"capital":"Saipan","altSpellings":["MP","Commonwealth of the Northern Mariana Islands","Sankattan Siha Na Islas Mariånas"],"subregion":"Micronesia","region":"Oceania","population":57557,"latlng":[15.2,145.75],"demonym":"American","area":464.0,"timezones":["UTC+10:00"],"nativeName":"Northern Mariana Islands","numericCode":"580","flags":{"svg":"https://flagcdn.com/mp.svg","png":"https://flagcdn.com/w320/mp.png"},"currencies":[{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"ch","iso639_2":"cha","name":"Chamorro","nativeName":"Chamoru"}],"translations":{"br":"Inizi Mariana an Norzh","pt":"Ilhas Marianas","nl":"Noordelijke Marianeneilanden","hr":"Sjevernomarijanski otoci","fa":"جزایر ماریانای شمالی","de":"Nördliche Marianen","es":"Islas Marianas del Norte","fr":"Îles Mariannes du Nord","ja":"北マリアナ諸島","it":"Isole Marianne Settentrionali","hu":"Északi-Mariana-szigetek"},"flag":"https://flagcdn.com/mp.svg","independent":false},{"name":"Norway","topLevelDomain":[".no"],"alpha2Code":"NO","alpha3Code":"NOR","callingCodes":["47"],"capital":"Oslo","altSpellings":["NO","Norge","Noreg","Kingdom of Norway","Kongeriket Norge","Kongeriket Noreg"],"subregion":"Northern Europe","region":"Europe","population":5379475,"latlng":[62.0,10.0],"demonym":"Norwegian","area":323802.0,"gini":27.6,"timezones":["UTC+01:00"],"borders":["FIN","SWE","RUS"],"nativeName":"Norge","numericCode":"578","flags":{"svg":"https://flagcdn.com/no.svg","png":"https://flagcdn.com/w320/no.png"},"currencies":[{"code":"NOK","name":"Norwegian krone","symbol":"kr"}],"languages":[{"iso639_1":"no","iso639_2":"nor","name":"Norwegian","nativeName":"Norsk"},{"iso639_1":"nb","iso639_2":"nob","name":"Norwegian Bokmål","nativeName":"Norsk bokmål"},{"iso639_1":"nn","iso639_2":"nno","name":"Norwegian Nynorsk","nativeName":"Norsk nynorsk"}],"translations":{"br":"Norvegia","pt":"Noruega","nl":"Noorwegen","hr":"Norveška","fa":"نروژ","de":"Norwegen","es":"Noruega","fr":"Norvège","ja":"ノルウェー","it":"Norvegia","hu":"Norvégia"},"flag":"https://flagcdn.com/no.svg","regionalBlocs":[{"acronym":"EFTA","name":"European Free Trade Association"}],"cioc":"NOR","independent":true},{"name":"Oman","topLevelDomain":[".om"],"alpha2Code":"OM","alpha3Code":"OMN","callingCodes":["968"],"capital":"Muscat","altSpellings":["OM","Sultanate of Oman","Salṭanat ʻUmān"],"subregion":"Western Asia","region":"Asia","population":5106622,"latlng":[21.0,57.0],"demonym":"Omani","area":309500.0,"timezones":["UTC+04:00"],"borders":["SAU","ARE","YEM"],"nativeName":"عمان","numericCode":"512","flags":{"svg":"https://flagcdn.com/om.svg","png":"https://flagcdn.com/w320/om.png"},"currencies":[{"code":"OMR","name":"Omani rial","symbol":"ر.ع."}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Oman","pt":"Omã","nl":"Oman","hr":"Oman","fa":"عمان","de":"Oman","es":"Omán","fr":"Oman","ja":"オマーン","it":"oman","hu":"Omán"},"flag":"https://flagcdn.com/om.svg","regionalBlocs":[{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"OMA","independent":true},{"name":"Pakistan","topLevelDomain":[".pk"],"alpha2Code":"PK","alpha3Code":"PAK","callingCodes":["92"],"capital":"Islamabad","altSpellings":["PK","Pākistān","Islamic Republic of Pakistan","Islāmī Jumhūriya'eh Pākistān"],"subregion":"Southern Asia","region":"Asia","population":220892331,"latlng":[30.0,70.0],"demonym":"Pakistani","area":881912.0,"gini":31.6,"timezones":["UTC+05:00"],"borders":["AFG","CHN","IND","IRN"],"nativeName":"Pakistan","numericCode":"586","flags":{"svg":"https://flagcdn.com/pk.svg","png":"https://flagcdn.com/w320/pk.png"},"currencies":[{"code":"PKR","name":"Pakistani rupee","symbol":"₨"}],"languages":[{"iso639_1":"ur","iso639_2":"urd","name":"Urdu","nativeName":"اردو"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Pakistan","pt":"Paquistão","nl":"Pakistan","hr":"Pakistan","fa":"پاکستان","de":"Pakistan","es":"Pakistán","fr":"Pakistan","ja":"パキスタン","it":"Pakistan","hu":"Pakisztán"},"flag":"https://flagcdn.com/pk.svg","regionalBlocs":[{"acronym":"SAARC","name":"South Asian Association for Regional Cooperation"}],"cioc":"PAK","independent":true},{"name":"Palau","topLevelDomain":[".pw"],"alpha2Code":"PW","alpha3Code":"PLW","callingCodes":["680"],"capital":"Ngerulmud","altSpellings":["PW","Republic of Palau","Beluu er a Belau"],"subregion":"Micronesia","region":"Oceania","population":18092,"latlng":[7.5,134.5],"demonym":"Palauan","area":459.0,"timezones":["UTC+09:00"],"nativeName":"Palau","numericCode":"585","flags":{"svg":"https://flagcdn.com/pw.svg","png":"https://flagcdn.com/w320/pw.png"},"currencies":[{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Palau","pt":"Palau","nl":"Palau","hr":"Palau","fa":"پالائو","de":"Palau","es":"Palau","fr":"Palaos","ja":"パラオ","it":"Palau","hu":"Palau"},"flag":"https://flagcdn.com/pw.svg","cioc":"PLW","independent":true},{"name":"Palestine, State of","topLevelDomain":[".ps"],"alpha2Code":"PS","alpha3Code":"PSE","callingCodes":["970"],"capital":"Ramallah","altSpellings":["PS","State of Palestine","Dawlat Filasṭin"],"subregion":"Western Asia","region":"Asia","population":4803269,"latlng":[31.9,35.2],"demonym":"Palestinian","gini":33.7,"timezones":["UTC+02:00"],"borders":["ISR","EGY","JOR"],"nativeName":"فلسطين","numericCode":"275","flags":{"svg":"https://flagcdn.com/ps.svg","png":"https://flagcdn.com/w320/ps.png"},"currencies":[{"code":"EGP","name":"Egyptian pound","symbol":"E£"},{"code":"ILS","name":"Israeli new shekel","symbol":"₪"},{"code":"JOD","name":"Jordanian dinar","symbol":"د.أ"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Palestina","pt":"Palestina","nl":"Palestijnse gebieden","hr":"Palestina","fa":"فلسطین","de":"Palästina","es":"Palestina","fr":"Palestine","ja":"パレスチナ","it":"Palestina","hu":"Palesztina"},"flag":"https://flagcdn.com/ps.svg","regionalBlocs":[{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"PLE","independent":true},{"name":"Panama","topLevelDomain":[".pa"],"alpha2Code":"PA","alpha3Code":"PAN","callingCodes":["507"],"capital":"Panama City","altSpellings":["PA","Republic of Panama","República de Panamá"],"subregion":"Central America","region":"Americas","population":4314768,"latlng":[9.0,-80.0],"demonym":"Panamanian","area":75417.0,"gini":49.8,"timezones":["UTC-05:00"],"borders":["COL","CRI"],"nativeName":"Panamá","numericCode":"591","flags":{"svg":"https://flagcdn.com/pa.svg","png":"https://flagcdn.com/w320/pa.png"},"currencies":[{"code":"PAB","name":"Panamanian balboa","symbol":"B/."},{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Panama","pt":"Panamá","nl":"Panama","hr":"Panama","fa":"پاناما","de":"Panama","es":"Panamá","fr":"Panama","ja":"パナマ","it":"Panama","hu":"Panama"},"flag":"https://flagcdn.com/pa.svg","regionalBlocs":[{"acronym":"CAIS","name":"Central American Integration System","otherAcronyms":["SICA"],"otherNames":["Sistema de la Integración Centroamericana,"]}],"cioc":"PAN","independent":true},{"name":"Papua New Guinea","topLevelDomain":[".pg"],"alpha2Code":"PG","alpha3Code":"PNG","callingCodes":["675"],"capital":"Port Moresby","altSpellings":["PG","Independent State of Papua New Guinea","Independen Stet bilong Papua Niugini"],"subregion":"Melanesia","region":"Oceania","population":8947027,"latlng":[-6.0,147.0],"demonym":"Papua New Guinean","area":462840.0,"gini":41.9,"timezones":["UTC+10:00"],"borders":["IDN"],"nativeName":"Papua Niugini","numericCode":"598","flags":{"svg":"https://flagcdn.com/pg.svg","png":"https://flagcdn.com/w320/pg.png"},"currencies":[{"code":"PGK","name":"Papua New Guinean kina","symbol":"K"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Papoua-Ginea Nevez","pt":"Papua Nova Guiné","nl":"Papoea-Nieuw-Guinea","hr":"Papua Nova Gvineja","fa":"پاپوآ گینه نو","de":"Papua-Neuguinea","es":"Papúa Nueva Guinea","fr":"Papouasie-Nouvelle-Guinée","ja":"パプアニューギニア","it":"Papua Nuova Guinea","hu":"Pápua Új-Guinea"},"flag":"https://flagcdn.com/pg.svg","cioc":"PNG","independent":true},{"name":"Paraguay","topLevelDomain":[".py"],"alpha2Code":"PY","alpha3Code":"PRY","callingCodes":["595"],"capital":"Asunción","altSpellings":["PY","Republic of Paraguay","República del Paraguay","Tetã Paraguái"],"subregion":"South America","region":"Americas","population":7132530,"latlng":[-23.0,-58.0],"demonym":"Paraguayan","area":406752.0,"gini":45.7,"timezones":["UTC-04:00"],"borders":["ARG","BOL","BRA"],"nativeName":"Paraguay","numericCode":"600","flags":{"svg":"https://flagcdn.com/py.svg","png":"https://flagcdn.com/w320/py.png"},"currencies":[{"code":"PYG","name":"Paraguayan guaraní","symbol":"₲"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"},{"iso639_1":"gn","iso639_2":"grn","name":"Guaraní","nativeName":"Avañe'ẽ"}],"translations":{"br":"Paraguay","pt":"Paraguai","nl":"Paraguay","hr":"Paragvaj","fa":"پاراگوئه","de":"Paraguay","es":"Paraguay","fr":"Paraguay","ja":"パラグアイ","it":"Paraguay","hu":"Paraguay"},"flag":"https://flagcdn.com/py.svg","regionalBlocs":[{"acronym":"USAN","name":"Union of South American Nations","otherAcronyms":["UNASUR","UNASUL","UZAN"],"otherNames":["Unión de Naciones Suramericanas","União de Nações Sul-Americanas","Unie van Zuid-Amerikaanse Naties","South American Union"]}],"cioc":"PAR","independent":true},{"name":"Peru","topLevelDomain":[".pe"],"alpha2Code":"PE","alpha3Code":"PER","callingCodes":["51"],"capital":"Lima","altSpellings":["PE","Republic of Peru"," República del Perú"],"subregion":"South America","region":"Americas","population":32971846,"latlng":[-10.0,-76.0],"demonym":"Peruvian","area":1285216.0,"gini":41.5,"timezones":["UTC-05:00"],"borders":["BOL","BRA","CHL","COL","ECU"],"nativeName":"Perú","numericCode":"604","flags":{"svg":"https://flagcdn.com/pe.svg","png":"https://flagcdn.com/w320/pe.png"},"currencies":[{"code":"PEN","name":"Peruvian sol","symbol":"S/."}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Perou","pt":"Peru","nl":"Peru","hr":"Peru","fa":"پرو","de":"Peru","es":"Perú","fr":"Pérou","ja":"ペルー","it":"Perù","hu":"Peru"},"flag":"https://flagcdn.com/pe.svg","regionalBlocs":[{"acronym":"PA","name":"Pacific Alliance","otherNames":["Alianza del Pacífico"]},{"acronym":"USAN","name":"Union of South American Nations","otherAcronyms":["UNASUR","UNASUL","UZAN"],"otherNames":["Unión de Naciones Suramericanas","União de Nações Sul-Americanas","Unie van Zuid-Amerikaanse Naties","South American Union"]}],"cioc":"PER","independent":true},{"name":"Philippines","topLevelDomain":[".ph"],"alpha2Code":"PH","alpha3Code":"PHL","callingCodes":["63"],"capital":"Manila","altSpellings":["PH","Republic of the Philippines","Repúblika ng Pilipinas"],"subregion":"South-Eastern Asia","region":"Asia","population":109581085,"latlng":[13.0,122.0],"demonym":"Filipino","area":342353.0,"gini":42.3,"timezones":["UTC+08:00"],"nativeName":"Pilipinas","numericCode":"608","flags":{"svg":"https://flagcdn.com/ph.svg","png":"https://flagcdn.com/w320/ph.png"},"currencies":[{"code":"PHP","name":"Philippine peso","symbol":"₱"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Filipinez","pt":"Filipinas","nl":"Filipijnen","hr":"Filipini","fa":"جزایر الندفیلیپین","de":"Philippinen","es":"Filipinas","fr":"Philippines","ja":"フィリピン","it":"Filippine","hu":"Fülöp-szigetek"},"flag":"https://flagcdn.com/ph.svg","regionalBlocs":[{"acronym":"ASEAN","name":"Association of Southeast Asian Nations"}],"cioc":"PHI","independent":true},{"name":"Pitcairn","topLevelDomain":[".pn"],"alpha2Code":"PN","alpha3Code":"PCN","callingCodes":["64"],"capital":"Adamstown","altSpellings":["PN","Pitcairn Henderson Ducie and Oeno Islands"],"subregion":"Polynesia","region":"Oceania","population":56,"latlng":[-25.06666666,-130.1],"demonym":"Pitcairn Islander","area":47.0,"timezones":["UTC-08:00"],"nativeName":"Pitcairn Islands","numericCode":"612","flags":{"svg":"https://flagcdn.com/pn.svg","png":"https://flagcdn.com/w320/pn.png"},"currencies":[{"code":"NZD","name":"New Zealand dollar","symbol":"$"},{"code":"PND","name":"Pitcairn Islands dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Inizi Pitcairn","pt":"Ilhas Picárnia","nl":"Pitcairneilanden","hr":"Pitcairnovo otočje","fa":"پیتکرن","de":"Pitcairn","es":"Islas Pitcairn","fr":"Îles Pitcairn","ja":"ピトケアン","it":"Isole Pitcairn","hu":"Pitcairn-szigetek"},"flag":"https://flagcdn.com/pn.svg","independent":true},{"name":"Poland","topLevelDomain":[".pl"],"alpha2Code":"PL","alpha3Code":"POL","callingCodes":["48"],"capital":"Warsaw","altSpellings":["PL","Republic of Poland","Rzeczpospolita Polska"],"subregion":"Central Europe","region":"Europe","population":37950802,"latlng":[52.0,20.0],"demonym":"Polish","area":312679.0,"gini":30.2,"timezones":["UTC+01:00"],"borders":["BLR","CZE","DEU","LTU","RUS","SVK","UKR"],"nativeName":"Polska","numericCode":"616","flags":{"svg":"https://flagcdn.com/pl.svg","png":"https://flagcdn.com/w320/pl.png"},"currencies":[{"code":"PLN","name":"Polish złoty","symbol":"zł"}],"languages":[{"iso639_1":"pl","iso639_2":"pol","name":"Polish","nativeName":"język polski"}],"translations":{"br":"Polonia","pt":"Polónia","nl":"Polen","hr":"Poljska","fa":"لهستان","de":"Polen","es":"Polonia","fr":"Pologne","ja":"ポーランド","it":"Polonia","hu":"Lengyelország"},"flag":"https://flagcdn.com/pl.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"POL","independent":true},{"name":"Portugal","topLevelDomain":[".pt"],"alpha2Code":"PT","alpha3Code":"PRT","callingCodes":["351"],"capital":"Lisbon","altSpellings":["PT","Portuguesa","Portuguese Republic","República Portuguesa"],"subregion":"Southern Europe","region":"Europe","population":10305564,"latlng":[39.5,-8.0],"demonym":"Portuguese","area":92090.0,"gini":33.5,"timezones":["UTC-01:00","UTC"],"borders":["ESP"],"nativeName":"Portugal","numericCode":"620","flags":{"svg":"https://flagcdn.com/pt.svg","png":"https://flagcdn.com/w320/pt.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"pt","iso639_2":"por","name":"Portuguese","nativeName":"Português"}],"translations":{"br":"Portugal","pt":"Portugal","nl":"Portugal","hr":"Portugal","fa":"پرتغال","de":"Portugal","es":"Portugal","fr":"Portugal","ja":"ポルトガル","it":"Portogallo","hu":"Portugália"},"flag":"https://flagcdn.com/pt.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"POR","independent":true},{"name":"Puerto Rico","topLevelDomain":[".pr"],"alpha2Code":"PR","alpha3Code":"PRI","callingCodes":["1"],"capital":"San Juan","altSpellings":["PR","Commonwealth of Puerto Rico","Estado Libre Asociado de Puerto Rico"],"subregion":"Caribbean","region":"Americas","population":3194034,"latlng":[18.25,-66.5],"demonym":"Puerto Rican","area":8870.0,"timezones":["UTC-04:00"],"nativeName":"Puerto Rico","numericCode":"630","flags":{"svg":"https://flagcdn.com/pr.svg","png":"https://flagcdn.com/w320/pr.png"},"currencies":[{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Puerto Rico","pt":"Porto Rico","nl":"Puerto Rico","hr":"Portoriko","fa":"پورتو ریکو","de":"Puerto Rico","es":"Puerto Rico","fr":"Porto Rico","ja":"プエルトリコ","it":"Porto Rico","hu":"Puerto Rico"},"flag":"https://flagcdn.com/pr.svg","cioc":"PUR","independent":false},{"name":"Qatar","topLevelDomain":[".qa"],"alpha2Code":"QA","alpha3Code":"QAT","callingCodes":["974"],"capital":"Doha","altSpellings":["QA","State of Qatar","Dawlat Qaṭar"],"subregion":"Western Asia","region":"Asia","population":2881060,"latlng":[25.5,51.25],"demonym":"Qatari","area":11586.0,"timezones":["UTC+03:00"],"borders":["SAU"],"nativeName":"قطر","numericCode":"634","flags":{"svg":"https://flagcdn.com/qa.svg","png":"https://flagcdn.com/w320/qa.png"},"currencies":[{"code":"QAR","name":"Qatari riyal","symbol":"ر.ق"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Katar","pt":"Catar","nl":"Qatar","hr":"Katar","fa":"قطر","de":"Katar","es":"Catar","fr":"Qatar","ja":"カタール","it":"Qatar","hu":"Katar"},"flag":"https://flagcdn.com/qa.svg","regionalBlocs":[{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"QAT","independent":true},{"name":"Republic of Kosovo","topLevelDomain":[""],"alpha2Code":"XK","alpha3Code":"UNK","callingCodes":["383"],"capital":"Pristina","altSpellings":["XK","Република Косово"],"subregion":"Eastern Europe","region":"Europe","population":1775378,"latlng":[42.666667,21.166667],"demonym":"Kosovar","area":10908.0,"gini":29.0,"timezones":["UTC+01:00"],"borders":["ALB","MKD","MNE","SRB"],"nativeName":"Republika e Kosovës","numericCode":"926","flags":{"svg":"https://flagcdn.com/xk.svg","png":"https://flagcdn.com/w320/xk.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"sq","iso639_2":"sqi","name":"Albanian","nativeName":"Shqip"},{"iso639_1":"sr","iso639_2":"srp","name":"Serbian","nativeName":"српски језик"}],"translations":{"br":"Kosovo","pt":"Kosovo","nl":"Republiek van Kosovo","hr":"Kosovo","fa":"کوزوو","de":"Republic of Kosovo","es":"Kosovo","fr":"Kosovo","ja":"Republic of Kosovo","it":"Republic of Kosovo","hu":"Koszovó"},"flag":"https://flagcdn.com/xk.svg","regionalBlocs":[{"acronym":"CEFTA","name":"Central European Free Trade Agreement"}],"independent":true},{"name":"Réunion","topLevelDomain":[".re"],"alpha2Code":"RE","alpha3Code":"REU","callingCodes":["262"],"capital":"Saint-Denis","altSpellings":["RE","Reunion"],"subregion":"Eastern Africa","region":"Africa","population":840974,"latlng":[-21.15,55.5],"demonym":"French","timezones":["UTC+04:00"],"nativeName":"La Réunion","numericCode":"638","flags":{"svg":"https://flagcdn.com/re.svg","png":"https://flagcdn.com/w320/re.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Reünion","pt":"Reunião","nl":"Réunion","hr":"Réunion","fa":"رئونیون","de":"Réunion","es":"Reunión","fr":"Réunion","ja":"レユニオン","it":"Riunione","hu":"Réunion"},"flag":"https://flagcdn.com/re.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"independent":false},{"name":"Romania","topLevelDomain":[".ro"],"alpha2Code":"RO","alpha3Code":"ROU","callingCodes":["40"],"capital":"Bucharest","altSpellings":["RO","Rumania","Roumania","România"],"subregion":"Eastern Europe","region":"Europe","population":19286123,"latlng":[46.0,25.0],"demonym":"Romanian","area":238391.0,"gini":35.8,"timezones":["UTC+02:00"],"borders":["BGR","HUN","MDA","SRB","UKR"],"nativeName":"România","numericCode":"642","flags":{"svg":"https://flagcdn.com/ro.svg","png":"https://flagcdn.com/w320/ro.png"},"currencies":[{"code":"RON","name":"Romanian leu","symbol":"lei"}],"languages":[{"iso639_1":"ro","iso639_2":"ron","name":"Romanian","nativeName":"Română"}],"translations":{"br":"Roumania","pt":"Roménia","nl":"Roemenië","hr":"Rumunjska","fa":"رومانی","de":"Rumänien","es":"Rumania","fr":"Roumanie","ja":"ルーマニア","it":"Romania","hu":"Románia"},"flag":"https://flagcdn.com/ro.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"ROU","independent":true},{"name":"Russian Federation","topLevelDomain":[".ru"],"alpha2Code":"RU","alpha3Code":"RUS","callingCodes":["7"],"capital":"Moscow","altSpellings":["RU","Rossiya","Russian Federation","Российская Федерация","Rossiyskaya Federatsiya"],"subregion":"Eastern Europe","region":"Europe","population":144104080,"latlng":[60.0,100.0],"demonym":"Russian","area":1.7124442E7,"gini":37.5,"timezones":["UTC+03:00","UTC+04:00","UTC+06:00","UTC+07:00","UTC+08:00","UTC+09:00","UTC+10:00","UTC+11:00","UTC+12:00"],"borders":["AZE","BLR","CHN","EST","FIN","GEO","KAZ","PRK","LVA","LTU","MNG","NOR","POL","UKR"],"nativeName":"Россия","numericCode":"643","flags":{"svg":"https://flagcdn.com/ru.svg","png":"https://flagcdn.com/w320/ru.png"},"currencies":[{"code":"RUB","name":"Russian ruble","symbol":"₽"}],"languages":[{"iso639_1":"ru","iso639_2":"rus","name":"Russian","nativeName":"Русский"}],"translations":{"br":"Rusia","pt":"Rússia","nl":"Rusland","hr":"Rusija","fa":"روسیه","de":"Russland","es":"Rusia","fr":"Russie","ja":"ロシア連邦","it":"Russia","hu":"Oroszország"},"flag":"https://flagcdn.com/ru.svg","regionalBlocs":[{"acronym":"EEU","name":"Eurasian Economic Union","otherAcronyms":["EAEU"]}],"cioc":"RUS","independent":true},{"name":"Rwanda","topLevelDomain":[".rw"],"alpha2Code":"RW","alpha3Code":"RWA","callingCodes":["250"],"capital":"Kigali","altSpellings":["RW","Republic of Rwanda","Repubulika y'u Rwanda","République du Rwanda"],"subregion":"Eastern Africa","region":"Africa","population":12952209,"latlng":[-2.0,30.0],"demonym":"Rwandan","area":26338.0,"gini":43.7,"timezones":["UTC+02:00"],"borders":["BDI","COD","TZA","UGA"],"nativeName":"Rwanda","numericCode":"646","flags":{"svg":"https://flagcdn.com/rw.svg","png":"https://flagcdn.com/w320/rw.png"},"currencies":[{"code":"RWF","name":"Rwandan franc","symbol":"Fr"}],"languages":[{"iso639_1":"rw","iso639_2":"kin","name":"Kinyarwanda","nativeName":"Ikinyarwanda"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Rwanda","pt":"Ruanda","nl":"Rwanda","hr":"Ruanda","fa":"رواندا","de":"Ruanda","es":"Ruanda","fr":"Rwanda","ja":"ルワンダ","it":"Ruanda","hu":"Ruanda"},"flag":"https://flagcdn.com/rw.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"RWA","independent":true},{"name":"Saint Barthélemy","topLevelDomain":[".bl"],"alpha2Code":"BL","alpha3Code":"BLM","callingCodes":["590"],"capital":"Gustavia","altSpellings":["BL","St. Barthelemy","Collectivity of Saint Barthélemy","Collectivité de Saint-Barthélemy"],"subregion":"Caribbean","region":"Americas","population":9417,"latlng":[18.5,-63.41666666],"demonym":"Saint Barthélemy Islander","area":21.0,"timezones":["UTC-04:00"],"nativeName":"Saint-Barthélemy","numericCode":"652","flags":{"svg":"https://flagcdn.com/bl.svg","png":"https://flagcdn.com/w320/bl.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Saint-Barthélemy","pt":"São Bartolomeu","nl":"Saint Barthélemy","hr":"Saint Barthélemy","fa":"سن-بارتلمی","de":"Saint-Barthélemy","es":"San Bartolomé","fr":"Saint-Barthélemy","ja":"サン・バルテルミー","it":"Antille Francesi","hu":"Saint-Barthélemy"},"flag":"https://flagcdn.com/bl.svg","independent":false},{"name":"Saint Helena, Ascension and Tristan da Cunha","topLevelDomain":[".sh"],"alpha2Code":"SH","alpha3Code":"SHN","callingCodes":["290"],"capital":"Jamestown","altSpellings":["SH"],"subregion":"Western Africa","region":"Africa","population":4255,"latlng":[-15.95,-5.7],"demonym":"Saint Helenian","timezones":["UTC+00:00"],"nativeName":"Saint Helena","numericCode":"654","flags":{"svg":"https://flagcdn.com/sh.svg","png":"https://flagcdn.com/w320/sh.png"},"currencies":[{"code":"SHP","name":"Saint Helena pound","symbol":"£"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Saint Helena, Ascension ha Tristan da Cunha","pt":"Santa Helena","nl":"Sint-Helena","hr":"Sveta Helena","fa":"سنت هلنا، اسنشن و تریستان دا کونا","de":"Sankt Helena","es":"Santa Helena","fr":"Sainte-Hélène","ja":"セントヘレナ・アセンションおよびトリスタンダクーニャ","it":"Sant'Elena","hu":"Szent Ilona"},"flag":"https://flagcdn.com/sh.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"independent":false},{"name":"Saint Kitts and Nevis","topLevelDomain":[".kn"],"alpha2Code":"KN","alpha3Code":"KNA","callingCodes":["1"],"capital":"Basseterre","altSpellings":["KN","Federation of Saint Christopher and Nevis"],"subregion":"Caribbean","region":"Americas","population":53192,"latlng":[17.33333333,-62.75],"demonym":"Kittian and Nevisian","area":261.0,"timezones":["UTC-04:00"],"nativeName":"Saint Kitts and Nevis","numericCode":"659","flags":{"svg":"https://flagcdn.com/kn.svg","png":"https://flagcdn.com/w320/kn.png"},"currencies":[{"code":"XCD","name":"East Caribbean dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Saint Kitts ha Nevis","pt":"São Cristóvão e Neves","nl":"Saint Kitts en Nevis","hr":"Sveti Kristof i Nevis","fa":"سنت کیتس و نویس","de":"St. Kitts und Nevis","es":"San Cristóbal y Nieves","fr":"Saint-Christophe-et-Niévès","ja":"セントクリストファー・ネイビス","it":"Saint Kitts e Nevis","hu":"Saint Kitts és Nevis"},"flag":"https://flagcdn.com/kn.svg","regionalBlocs":[{"acronym":"CARICOM","name":"Caribbean Community","otherNames":["Comunidad del Caribe","Communauté Caribéenne","Caribische Gemeenschap"]}],"cioc":"SKN","independent":true},{"name":"Saint Lucia","topLevelDomain":[".lc"],"alpha2Code":"LC","alpha3Code":"LCA","callingCodes":["1"],"capital":"Castries","altSpellings":["LC"],"subregion":"Caribbean","region":"Americas","population":183629,"latlng":[13.88333333,-60.96666666],"demonym":"Saint Lucian","area":616.0,"gini":51.2,"timezones":["UTC-04:00"],"nativeName":"Saint Lucia","numericCode":"662","flags":{"svg":"https://flagcdn.com/lc.svg","png":"https://flagcdn.com/w320/lc.png"},"currencies":[{"code":"XCD","name":"East Caribbean dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Santez-Lusia","pt":"Santa Lúcia","nl":"Saint Lucia","hr":"Sveta Lucija","fa":"سنت لوسیا","de":"Saint Lucia","es":"Santa Lucía","fr":"Saint-Lucie","ja":"セントルシア","it":"Santa Lucia","hu":"Saint Lucia"},"flag":"https://flagcdn.com/lc.svg","regionalBlocs":[{"acronym":"CARICOM","name":"Caribbean Community","otherNames":["Comunidad del Caribe","Communauté Caribéenne","Caribische Gemeenschap"]}],"cioc":"LCA","independent":true},{"name":"Saint Martin (French part)","topLevelDomain":[".mf",".fr",".gp"],"alpha2Code":"MF","alpha3Code":"MAF","callingCodes":["590"],"capital":"Marigot","altSpellings":["MF","Collectivity of Saint Martin","Collectivité de Saint-Martin"],"subregion":"Caribbean","region":"Americas","population":38659,"latlng":[18.08333333,-63.95],"demonym":"Saint Martin Islander","area":53.0,"timezones":["UTC-04:00"],"borders":["SXM","NLD"],"nativeName":"Saint-Martin","numericCode":"663","flags":{"svg":"https://flagcdn.com/mf.svg","png":"https://flagcdn.com/w320/mf.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"nl","iso639_2":"nld","name":"Dutch","nativeName":"Nederlands"}],"translations":{"br":"Saint-Martin","pt":"Ilha São Martinho","nl":"Saint-Martin","hr":"Sveti Martin","fa":"سینت مارتن","de":"Saint Martin","es":"Saint Martin","fr":"Saint-Martin","ja":"サン・マルタン(フランス領)","it":"Saint Martin","hu":"Saint-Martin"},"flag":"https://flagcdn.com/mf.svg","independent":false},{"name":"Saint Pierre and Miquelon","topLevelDomain":[".pm"],"alpha2Code":"PM","alpha3Code":"SPM","callingCodes":["508"],"capital":"Saint-Pierre","altSpellings":["PM","Collectivité territoriale de Saint-Pierre-et-Miquelon"],"subregion":"Northern America","region":"Americas","population":6069,"latlng":[46.83333333,-56.33333333],"demonym":"French","area":242.0,"timezones":["UTC-03:00"],"nativeName":"Saint-Pierre-et-Miquelon","numericCode":"666","flags":{"svg":"https://flagcdn.com/pm.svg","png":"https://flagcdn.com/w320/pm.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Sant-Pêr-ha-Mikelon","pt":"São Pedro e Miquelon","nl":"Saint Pierre en Miquelon","hr":"Sveti Petar i Mikelon","fa":"سن پیر و میکلن","de":"Saint-Pierre und Miquelon","es":"San Pedro y Miquelón","fr":"Saint-Pierre-et-Miquelon","ja":"サンピエール島・ミクロン島","it":"Saint-Pierre e Miquelon","hu":"Saint-Pierre és Miquelon"},"flag":"https://flagcdn.com/pm.svg","independent":false},{"name":"Saint Vincent and the Grenadines","topLevelDomain":[".vc"],"alpha2Code":"VC","alpha3Code":"VCT","callingCodes":["1"],"capital":"Kingstown","altSpellings":["VC"],"subregion":"Caribbean","region":"Americas","population":110947,"latlng":[13.25,-61.2],"demonym":"Saint Vincentian","area":389.0,"timezones":["UTC-04:00"],"nativeName":"Saint Vincent and the Grenadines","numericCode":"670","flags":{"svg":"https://flagcdn.com/vc.svg","png":"https://flagcdn.com/w320/vc.png"},"currencies":[{"code":"XCD","name":"East Caribbean dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Sant-Visant hag ar Grenadinez","pt":"São Vicente e Granadinas","nl":"Saint Vincent en de Grenadines","hr":"Sveti Vincent i Grenadini","fa":"سنت وینسنت و گرنادین‌ها","de":"Saint Vincent und die Grenadinen","es":"San Vicente y Granadinas","fr":"Saint-Vincent-et-les-Grenadines","ja":"セントビンセントおよびグレナディーン諸島","it":"Saint Vincent e Grenadine","hu":"Saint Vincent és a Grenadine-szigetek"},"flag":"https://flagcdn.com/vc.svg","regionalBlocs":[{"acronym":"CARICOM","name":"Caribbean Community","otherNames":["Comunidad del Caribe","Communauté Caribéenne","Caribische Gemeenschap"]}],"cioc":"VIN","independent":true},{"name":"Samoa","topLevelDomain":[".ws"],"alpha2Code":"WS","alpha3Code":"WSM","callingCodes":["685"],"capital":"Apia","altSpellings":["WS","Independent State of Samoa","Malo Saʻoloto Tutoʻatasi o Sāmoa"],"subregion":"Polynesia","region":"Oceania","population":198410,"latlng":[-13.58333333,-172.33333333],"demonym":"Samoan","area":2842.0,"gini":38.7,"timezones":["UTC+13:00"],"nativeName":"Samoa","numericCode":"882","flags":{"svg":"https://flagcdn.com/ws.svg","png":"https://flagcdn.com/w320/ws.png"},"currencies":[{"code":"WST","name":"Samoan tālā","symbol":"T"}],"languages":[{"iso639_1":"sm","iso639_2":"smo","name":"Samoan","nativeName":"gagana fa'a Samoa"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Samoa","pt":"Samoa","nl":"Samoa","hr":"Samoa","fa":"ساموآ","de":"Samoa","es":"Samoa","fr":"Samoa","ja":"サモア","it":"Samoa","hu":"Szamoa"},"flag":"https://flagcdn.com/ws.svg","cioc":"SAM","independent":true},{"name":"San Marino","topLevelDomain":[".sm"],"alpha2Code":"SM","alpha3Code":"SMR","callingCodes":["378"],"capital":"City of San Marino","altSpellings":["SM","Republic of San Marino","Repubblica di San Marino"],"subregion":"Southern Europe","region":"Europe","population":33938,"latlng":[43.76666666,12.41666666],"demonym":"Sammarinese","area":61.0,"timezones":["UTC+01:00"],"borders":["ITA"],"nativeName":"San Marino","numericCode":"674","flags":{"svg":"https://flagcdn.com/sm.svg","png":"https://flagcdn.com/w320/sm.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"it","iso639_2":"ita","name":"Italian","nativeName":"Italiano"}],"translations":{"br":"San Marino","pt":"São Marinho","nl":"San Marino","hr":"San Marino","fa":"سان مارینو","de":"San Marino","es":"San Marino","fr":"Saint-Marin","ja":"サンマリノ","it":"San Marino","hu":"San Marino"},"flag":"https://flagcdn.com/sm.svg","cioc":"SMR","independent":true},{"name":"Sao Tome and Principe","topLevelDomain":[".st"],"alpha2Code":"ST","alpha3Code":"STP","callingCodes":["239"],"capital":"São Tomé","altSpellings":["ST","Democratic Republic of São Tomé and Príncipe","República Democrática de São Tomé e Príncipe"],"subregion":"Middle Africa","region":"Africa","population":219161,"latlng":[1.0,7.0],"demonym":"Sao Tomean","area":964.0,"gini":56.3,"timezones":["UTC"],"nativeName":"São Tomé e Príncipe","numericCode":"678","flags":{"svg":"https://flagcdn.com/st.svg","png":"https://flagcdn.com/w320/st.png"},"currencies":[{"code":"STD","name":"São Tomé and Príncipe dobra","symbol":"Db"}],"languages":[{"iso639_1":"pt","iso639_2":"por","name":"Portuguese","nativeName":"Português"}],"translations":{"br":"São Tomé ha Príncipe","pt":"São Tomé e Príncipe","nl":"Sao Tomé en Principe","hr":"Sveti Toma i Princip","fa":"کواترو دو فرویرو","de":"São Tomé und Príncipe","es":"Santo Tomé y Príncipe","fr":"Sao Tomé-et-Principe","ja":"サントメ・プリンシペ","it":"São Tomé e Príncipe","hu":"São Tomé és Príncipe"},"flag":"https://flagcdn.com/st.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"STP","independent":true},{"name":"Saudi Arabia","topLevelDomain":[".sa"],"alpha2Code":"SA","alpha3Code":"SAU","callingCodes":["966"],"capital":"Riyadh","altSpellings":["SA","Kingdom of Saudi Arabia","Al-Mamlakah al-‘Arabiyyah as-Su‘ūdiyyah"],"subregion":"Western Asia","region":"Asia","population":34813867,"latlng":[25.0,45.0],"demonym":"Saudi Arabian","area":2149690.0,"timezones":["UTC+03:00"],"borders":["IRQ","JOR","KWT","OMN","QAT","ARE","YEM"],"nativeName":"العربية السعودية","numericCode":"682","flags":{"svg":"https://flagcdn.com/sa.svg","png":"https://flagcdn.com/w320/sa.png"},"currencies":[{"code":"SAR","name":"Saudi riyal","symbol":"ر.س"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Arabia Saoudat","pt":"Arábia Saudita","nl":"Saoedi-Arabië","hr":"Saudijska Arabija","fa":"عربستان سعودی","de":"Saudi-Arabien","es":"Arabia Saudí","fr":"Arabie Saoudite","ja":"サウジアラビア","it":"Arabia Saudita","hu":"Szaúd-Arábia"},"flag":"https://flagcdn.com/sa.svg","regionalBlocs":[{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"KSA","independent":true},{"name":"Senegal","topLevelDomain":[".sn"],"alpha2Code":"SN","alpha3Code":"SEN","callingCodes":["221"],"capital":"Dakar","altSpellings":["SN","Republic of Senegal","République du Sénégal"],"subregion":"Western Africa","region":"Africa","population":16743930,"latlng":[14.0,-14.0],"demonym":"Senegalese","area":196722.0,"gini":40.3,"timezones":["UTC"],"borders":["GMB","GIN","GNB","MLI","MRT"],"nativeName":"Sénégal","numericCode":"686","flags":{"svg":"https://flagcdn.com/sn.svg","png":"https://flagcdn.com/w320/sn.png"},"currencies":[{"code":"XOF","name":"West African CFA franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Senegal","pt":"Senegal","nl":"Senegal","hr":"Senegal","fa":"سنگال","de":"Senegal","es":"Senegal","fr":"Sénégal","ja":"セネガル","it":"Senegal","hu":"Szenegál"},"flag":"https://flagcdn.com/sn.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"SEN","independent":true},{"name":"Serbia","topLevelDomain":[".rs"],"alpha2Code":"RS","alpha3Code":"SRB","callingCodes":["381"],"capital":"Belgrade","altSpellings":["RS","Srbija","Republic of Serbia","Република Србија","Republika Srbija"],"subregion":"Southern Europe","region":"Europe","population":6908224,"latlng":[44.0,21.0],"demonym":"Serbian","area":88361.0,"gini":36.2,"timezones":["UTC+01:00"],"borders":["BIH","BGR","HRV","HUN","UNK","MKD","MNE","ROU"],"nativeName":"Србија","numericCode":"688","flags":{"svg":"https://flagcdn.com/rs.svg","png":"https://flagcdn.com/w320/rs.png"},"currencies":[{"code":"RSD","name":"Serbian dinar","symbol":"дин."}],"languages":[{"iso639_1":"sr","iso639_2":"srp","name":"Serbian","nativeName":"српски језик"}],"translations":{"br":"Serbia","pt":"Sérvia","nl":"Servië","hr":"Srbija","fa":"صربستان","de":"Serbien","es":"Serbia","fr":"Serbie","ja":"セルビア","it":"Serbia","hu":"Szerbia"},"flag":"https://flagcdn.com/rs.svg","regionalBlocs":[{"acronym":"CEFTA","name":"Central European Free Trade Agreement"}],"cioc":"SRB","independent":true},{"name":"Seychelles","topLevelDomain":[".sc"],"alpha2Code":"SC","alpha3Code":"SYC","callingCodes":["248"],"capital":"Victoria","altSpellings":["SC","Republic of Seychelles","Repiblik Sesel","République des Seychelles"],"subregion":"Eastern Africa","region":"Africa","population":98462,"latlng":[-4.58333333,55.66666666],"demonym":"Seychellois","area":452.0,"gini":32.1,"timezones":["UTC+04:00"],"nativeName":"Seychelles","numericCode":"690","flags":{"svg":"https://flagcdn.com/sc.svg","png":"https://flagcdn.com/w320/sc.png"},"currencies":[{"code":"SCR","name":"Seychellois rupee","symbol":"₨"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Sechelez","pt":"Seicheles","nl":"Seychellen","hr":"Sejšeli","fa":"سیشل","de":"Seychellen","es":"Seychelles","fr":"Seychelles","ja":"セーシェル","it":"Seychelles","hu":"Seychelle-szigetek"},"flag":"https://flagcdn.com/sc.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"SEY","independent":true},{"name":"Sierra Leone","topLevelDomain":[".sl"],"alpha2Code":"SL","alpha3Code":"SLE","callingCodes":["232"],"capital":"Freetown","altSpellings":["SL","Republic of Sierra Leone"],"subregion":"Western Africa","region":"Africa","population":7976985,"latlng":[8.5,-11.5],"demonym":"Sierra Leonean","area":71740.0,"gini":35.7,"timezones":["UTC"],"borders":["GIN","LBR"],"nativeName":"Sierra Leone","numericCode":"694","flags":{"svg":"https://flagcdn.com/sl.svg","png":"https://flagcdn.com/w320/sl.png"},"currencies":[{"code":"SLL","name":"Sierra Leonean leone","symbol":"Le"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Sierra Leone","pt":"Serra Leoa","nl":"Sierra Leone","hr":"Sijera Leone","fa":"سیرالئون","de":"Sierra Leone","es":"Sierra Leone","fr":"Sierra Leone","ja":"シエラレオネ","it":"Sierra Leone","hu":"Sierra Leone"},"flag":"https://flagcdn.com/sl.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"SLE","independent":true},{"name":"Singapore","topLevelDomain":[".sg"],"alpha2Code":"SG","alpha3Code":"SGP","callingCodes":["65"],"capital":"Singapore","altSpellings":["SG","Singapura","Republik Singapura","新加坡共和国"],"subregion":"South-Eastern Asia","region":"Asia","population":5685807,"latlng":[1.36666666,103.8],"demonym":"Singaporean","area":710.0,"timezones":["UTC+08:00"],"nativeName":"Singapore","numericCode":"702","flags":{"svg":"https://flagcdn.com/sg.svg","png":"https://flagcdn.com/w320/sg.png"},"currencies":[{"code":"SGD","name":"Singapore dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"ms","iso639_2":"msa","name":"Malay","nativeName":"bahasa Melayu"},{"iso639_1":"ta","iso639_2":"tam","name":"Tamil","nativeName":"தமிழ்"},{"iso639_1":"zh","iso639_2":"zho","name":"Chinese","nativeName":"中文 (Zhōngwén)"}],"translations":{"br":"Singapour","pt":"Singapura","nl":"Singapore","hr":"Singapur","fa":"سنگاپور","de":"Singapur","es":"Singapur","fr":"Singapour","ja":"シンガポール","it":"Singapore","hu":"Szingapúr"},"flag":"https://flagcdn.com/sg.svg","regionalBlocs":[{"acronym":"ASEAN","name":"Association of Southeast Asian Nations"}],"cioc":"SIN","independent":true},{"name":"Sint Maarten (Dutch part)","topLevelDomain":[".sx"],"alpha2Code":"SX","alpha3Code":"SXM","callingCodes":["1"],"capital":"Philipsburg","altSpellings":["SX"],"subregion":"Caribbean","region":"Americas","population":40812,"latlng":[18.033333,-63.05],"demonym":"Dutch","area":34.0,"timezones":["UTC-04:00"],"borders":["MAF"],"nativeName":"Sint Maarten","numericCode":"534","flags":{"svg":"https://flagcdn.com/sx.svg","png":"https://flagcdn.com/w320/sx.png"},"currencies":[{"code":"ANG","name":"Netherlands Antillean guilder","symbol":"ƒ"}],"languages":[{"iso639_1":"nl","iso639_2":"nld","name":"Dutch","nativeName":"Nederlands"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Sint Maarten","pt":"São Martinho","nl":"Sint Maarten","hr":"Sint Maarten (Dutch part)","fa":"سینت مارتن","de":"Sint Maarten (niederl. Teil)","es":"Sint Maarten (Dutch part)","fr":"Saint Martin (partie néerlandaise)","ja":"Sint Maarten (Dutch part)","it":"Saint Martin (parte olandese)","hu":"Sint Maarten"},"flag":"https://flagcdn.com/sx.svg","independent":false},{"name":"Slovakia","topLevelDomain":[".sk"],"alpha2Code":"SK","alpha3Code":"SVK","callingCodes":["421"],"capital":"Bratislava","altSpellings":["SK","Slovak Republic","Slovenská republika"],"subregion":"Central Europe","region":"Europe","population":5458827,"latlng":[48.66666666,19.5],"demonym":"Slovak","area":49037.0,"gini":25.0,"timezones":["UTC+01:00"],"borders":["AUT","CZE","HUN","POL","UKR"],"nativeName":"Slovensko","numericCode":"703","flags":{"svg":"https://flagcdn.com/sk.svg","png":"https://flagcdn.com/w320/sk.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"sk","iso639_2":"slk","name":"Slovak","nativeName":"slovenčina"}],"translations":{"br":"Slovakia","pt":"Eslováquia","nl":"Slowakije","hr":"Slovačka","fa":"اسلواکی","de":"Slowakei","es":"República Eslovaca","fr":"Slovaquie","ja":"スロバキア","it":"Slovacchia","hu":"Szlovákia"},"flag":"https://flagcdn.com/sk.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"SVK","independent":true},{"name":"Slovenia","topLevelDomain":[".si"],"alpha2Code":"SI","alpha3Code":"SVN","callingCodes":["386"],"capital":"Ljubljana","altSpellings":["SI","Republic of Slovenia","Republika Slovenija"],"subregion":"Southern Europe","region":"Europe","population":2100126,"latlng":[46.11666666,14.81666666],"demonym":"Slovene","area":20273.0,"gini":24.6,"timezones":["UTC+01:00"],"borders":["AUT","HRV","ITA","HUN"],"nativeName":"Slovenija","numericCode":"705","flags":{"svg":"https://flagcdn.com/si.svg","png":"https://flagcdn.com/w320/si.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"sl","iso639_2":"slv","name":"Slovene","nativeName":"slovenski jezik"}],"translations":{"br":"Slovenia","pt":"Eslovénia","nl":"Slovenië","hr":"Slovenija","fa":"اسلوونی","de":"Slowenien","es":"Eslovenia","fr":"Slovénie","ja":"スロベニア","it":"Slovenia","hu":"Szlovénia"},"flag":"https://flagcdn.com/si.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"SLO","independent":true},{"name":"Solomon Islands","topLevelDomain":[".sb"],"alpha2Code":"SB","alpha3Code":"SLB","callingCodes":["677"],"capital":"Honiara","altSpellings":["SB"],"subregion":"Melanesia","region":"Oceania","population":686878,"latlng":[-8.0,159.0],"demonym":"Solomon Islander","area":28896.0,"gini":37.1,"timezones":["UTC+11:00"],"nativeName":"Solomon Islands","numericCode":"090","flags":{"svg":"https://flagcdn.com/sb.svg","png":"https://flagcdn.com/w320/sb.png"},"currencies":[{"code":"SBD","name":"Solomon Islands dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Inizi Salomon","pt":"Ilhas Salomão","nl":"Salomonseilanden","hr":"Solomonski Otoci","fa":"جزایر سلیمان","de":"Salomonen","es":"Islas Salomón","fr":"Îles Salomon","ja":"ソロモン諸島","it":"Isole Salomone","hu":"Salamon-szigetek"},"flag":"https://flagcdn.com/sb.svg","cioc":"SOL","independent":true},{"name":"Somalia","topLevelDomain":[".so"],"alpha2Code":"SO","alpha3Code":"SOM","callingCodes":["252"],"capital":"Mogadishu","altSpellings":["SO","aṣ-Ṣūmāl","Federal Republic of Somalia","Jamhuuriyadda Federaalka Soomaaliya","Jumhūriyyat aṣ-Ṣūmāl al-Fiderāliyya"],"subregion":"Eastern Africa","region":"Africa","population":15893219,"latlng":[10.0,49.0],"demonym":"Somali","area":637657.0,"gini":36.8,"timezones":["UTC+03:00"],"borders":["DJI","ETH","KEN"],"nativeName":"Soomaaliya","numericCode":"706","flags":{"svg":"https://flagcdn.com/so.svg","png":"https://flagcdn.com/w320/so.png"},"currencies":[{"code":"SOS","name":"Somali shilling","symbol":"Sh"}],"languages":[{"iso639_1":"so","iso639_2":"som","name":"Somali","nativeName":"Soomaaliga"},{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Somalia","pt":"Somália","nl":"Somalië","hr":"Somalija","fa":"سومالی","de":"Somalia","es":"Somalia","fr":"Somalie","ja":"ソマリア","it":"Somalia","hu":"Szomália"},"flag":"https://flagcdn.com/so.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]},{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"SOM","independent":true},{"name":"South Africa","topLevelDomain":[".za"],"alpha2Code":"ZA","alpha3Code":"ZAF","callingCodes":["27"],"capital":"Pretoria","altSpellings":["ZA","RSA","Suid-Afrika","Republic of South Africa"],"subregion":"Southern Africa","region":"Africa","population":59308690,"latlng":[-29.0,24.0],"demonym":"South African","area":1221037.0,"gini":63.0,"timezones":["UTC+02:00"],"borders":["BWA","LSO","MOZ","NAM","SWZ","ZWE"],"nativeName":"South Africa","numericCode":"710","flags":{"svg":"https://flagcdn.com/za.svg","png":"https://flagcdn.com/w320/za.png"},"currencies":[{"code":"ZAR","name":"South African rand","symbol":"R"}],"languages":[{"iso639_1":"af","iso639_2":"afr","name":"Afrikaans","nativeName":"Afrikaans"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"nr","iso639_2":"nbl","name":"Southern Ndebele","nativeName":"isiNdebele"},{"iso639_1":"st","iso639_2":"sot","name":"Southern Sotho","nativeName":"Sesotho"},{"iso639_1":"ss","iso639_2":"ssw","name":"Swati","nativeName":"SiSwati"},{"iso639_1":"tn","iso639_2":"tsn","name":"Tswana","nativeName":"Setswana"},{"iso639_1":"ts","iso639_2":"tso","name":"Tsonga","nativeName":"Xitsonga"},{"iso639_1":"ve","iso639_2":"ven","name":"Venda","nativeName":"Tshivenḓa"},{"iso639_1":"xh","iso639_2":"xho","name":"Xhosa","nativeName":"isiXhosa"},{"iso639_1":"zu","iso639_2":"zul","name":"Zulu","nativeName":"isiZulu"}],"translations":{"br":"Suafrika","pt":"República Sul-Africana","nl":"Zuid-Afrika","hr":"Južnoafrička Republika","fa":"آفریقای جنوبی","de":"Republik Südafrika","es":"República de Sudáfrica","fr":"Afrique du Sud","ja":"南アフリカ","it":"Sud Africa","hu":"Dél-afrikai Köztársaság"},"flag":"https://flagcdn.com/za.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"RSA","independent":true},{"name":"South Georgia and the South Sandwich Islands","topLevelDomain":[".gs"],"alpha2Code":"GS","alpha3Code":"SGS","callingCodes":["500"],"capital":"King Edward Point","altSpellings":["GS","South Georgia and the South Sandwich Islands"],"subregion":"South America","region":"Americas","population":30,"latlng":[-54.5,-37.0],"demonym":"South Georgia and the South Sandwich Islander","timezones":["UTC-02:00"],"nativeName":"South Georgia","numericCode":"239","flags":{"svg":"https://flagcdn.com/gs.svg","png":"https://flagcdn.com/w320/gs.png"},"currencies":[{"code":"FKP","name":"Falkland Islands Pound","symbol":"£"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Georgia ar Su hag Inizi Sandwich ar Su","pt":"Ilhas Geórgia do Sul e Sanduíche do Sul","nl":"Zuid-Georgia en Zuidelijke Sandwicheilanden","hr":"Južna Georgija i otočje Južni Sandwich","fa":"جزایر جورجیای جنوبی و ساندویچ جنوبی","de":"Südgeorgien und die Südlichen Sandwichinseln","es":"Islas Georgias del Sur y Sandwich del Sur","fr":"Géorgie du Sud-et-les Îles Sandwich du Sud","ja":"サウスジョージア・サウスサンドウィッチ諸島","it":"Georgia del Sud e Isole Sandwich Meridionali","hu":"Déli-Georgia és Déli-Sandwich-szigetek"},"flag":"https://flagcdn.com/gs.svg","regionalBlocs":[{"acronym":"USAN","name":"Union of South American Nations","otherAcronyms":["UNASUR","UNASUL","UZAN"],"otherNames":["Unión de Naciones Suramericanas","União de Nações Sul-Americanas","Unie van Zuid-Amerikaanse Naties","South American Union"]}],"independent":false},{"name":"Korea (Republic of)","topLevelDomain":[".kr"],"alpha2Code":"KR","alpha3Code":"KOR","callingCodes":["82"],"capital":"Seoul","altSpellings":["KR","Republic of Korea"],"subregion":"Eastern Asia","region":"Asia","population":51780579,"latlng":[37.0,127.5],"demonym":"South Korean","area":100210.0,"gini":31.4,"timezones":["UTC+09:00"],"borders":["PRK"],"nativeName":"대한민국","numericCode":"410","flags":{"svg":"https://flagcdn.com/kr.svg","png":"https://flagcdn.com/w320/kr.png"},"currencies":[{"code":"KRW","name":"South Korean won","symbol":"₩"}],"languages":[{"iso639_1":"ko","iso639_2":"kor","name":"Korean","nativeName":"한국어"}],"translations":{"br":"Korea ar Su","pt":"Coreia do Sul","nl":"Zuid-Korea","hr":"Južna Koreja","fa":"کره شمالی","de":"Südkorea","es":"Corea del Sur","fr":"Corée du Sud","ja":"大韓民国","it":"Corea del Sud","hu":"Dél-Korea"},"flag":"https://flagcdn.com/kr.svg","cioc":"KOR","independent":true},{"name":"Spain","topLevelDomain":[".es"],"alpha2Code":"ES","alpha3Code":"ESP","callingCodes":["34"],"capital":"Madrid","altSpellings":["ES","Kingdom of Spain","Reino de España"],"subregion":"Southern Europe","region":"Europe","population":47351567,"latlng":[40.0,-4.0],"demonym":"Spanish","area":505992.0,"gini":34.7,"timezones":["UTC","UTC+01:00"],"borders":["AND","FRA","GIB","PRT","MAR"],"nativeName":"España","numericCode":"724","flags":{"svg":"https://flagcdn.com/es.svg","png":"https://flagcdn.com/w320/es.png"},"currencies":[{"code":"EUR","name":"Euro","symbol":"€"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Spagn","pt":"Espanha","nl":"Spanje","hr":"Španjolska","fa":"اسپانیا","de":"Spanien","es":"España","fr":"Espagne","ja":"スペイン","it":"Spagna","hu":"Spanyolország"},"flag":"https://flagcdn.com/es.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"ESP","independent":true},{"name":"Sri Lanka","topLevelDomain":[".lk"],"alpha2Code":"LK","alpha3Code":"LKA","callingCodes":["94"],"capital":"Sri Jayawardenepura Kotte","altSpellings":["LK","ilaṅkai","Democratic Socialist Republic of Sri Lanka"],"subregion":"Southern Asia","region":"Asia","population":21919000,"latlng":[7.0,81.0],"demonym":"Sri Lankan","area":65610.0,"gini":39.3,"timezones":["UTC+05:30"],"borders":["IND"],"nativeName":"śrī laṃkāva","numericCode":"144","flags":{"svg":"https://flagcdn.com/lk.svg","png":"https://flagcdn.com/w320/lk.png"},"currencies":[{"code":"LKR","name":"Sri Lankan rupee","symbol":"Rs"}],"languages":[{"iso639_1":"si","iso639_2":"sin","name":"Sinhalese","nativeName":"සිංහල"},{"iso639_1":"ta","iso639_2":"tam","name":"Tamil","nativeName":"தமிழ்"}],"translations":{"br":"Sri Lanka","pt":"Sri Lanka","nl":"Sri Lanka","hr":"Šri Lanka","fa":"سری‌لانکا","de":"Sri Lanka","es":"Sri Lanka","fr":"Sri Lanka","ja":"スリランカ","it":"Sri Lanka","hu":"Srí Lanka"},"flag":"https://flagcdn.com/lk.svg","regionalBlocs":[{"acronym":"SAARC","name":"South Asian Association for Regional Cooperation"}],"cioc":"SRI","independent":true},{"name":"Sudan","topLevelDomain":[".sd"],"alpha2Code":"SD","alpha3Code":"SDN","callingCodes":["249"],"capital":"Khartoum","altSpellings":["SD","Republic of the Sudan","Jumhūrīyat as-Sūdān"],"subregion":"Northern Africa","region":"Africa","population":43849269,"latlng":[15.0,30.0],"demonym":"Sudanese","area":1886068.0,"gini":34.2,"timezones":["UTC+03:00"],"borders":["CAF","TCD","EGY","ERI","ETH","LBY","SSD"],"nativeName":"السودان","numericCode":"729","flags":{"svg":"https://flagcdn.com/sd.svg","png":"https://flagcdn.com/w320/sd.png"},"currencies":[{"code":"SDG","name":"Sudanese pound","symbol":"ج.س."}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Soudan","pt":"Sudão","nl":"Soedan","hr":"Sudan","fa":"سودان","de":"Sudan","es":"Sudán","fr":"Soudan","ja":"スーダン","it":"Sudan","hu":"Szudán"},"flag":"https://flagcdn.com/sd.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]},{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"SUD","independent":true},{"name":"South Sudan","topLevelDomain":[".ss"],"alpha2Code":"SS","alpha3Code":"SSD","callingCodes":["211"],"capital":"Juba","altSpellings":["SS"],"subregion":"Middle Africa","region":"Africa","population":11193729,"latlng":[7.0,30.0],"demonym":"South Sudanese","area":619745.0,"gini":44.1,"timezones":["UTC+03:00"],"borders":["CAF","COD","ETH","KEN","SDN","UGA"],"nativeName":"South Sudan","numericCode":"728","flags":{"svg":"https://flagcdn.com/ss.svg","png":"https://flagcdn.com/w320/ss.png"},"currencies":[{"code":"SSP","name":"South Sudanese pound","symbol":"£"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Soudan ar Su","pt":"Sudão do Sul","nl":"Zuid-Soedan","hr":"Južni Sudan","fa":"سودان جنوبی","de":"Südsudan","es":"Sudán del Sur","fr":"Soudan du Sud","ja":"南スーダン","it":"Sudan del sud","hu":"Dél-Szudán"},"flag":"https://flagcdn.com/ss.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"SSD","independent":true},{"name":"Suriname","topLevelDomain":[".sr"],"alpha2Code":"SR","alpha3Code":"SUR","callingCodes":["597"],"capital":"Paramaribo","altSpellings":["SR","Sarnam","Sranangron","Republic of Suriname","Republiek Suriname"],"subregion":"South America","region":"Americas","population":586634,"latlng":[4.0,-56.0],"demonym":"Surinamer","area":163820.0,"gini":57.9,"timezones":["UTC-03:00"],"borders":["BRA","FRA","GUF","GUY"],"nativeName":"Suriname","numericCode":"740","flags":{"svg":"https://flagcdn.com/sr.svg","png":"https://flagcdn.com/w320/sr.png"},"currencies":[{"code":"SRD","name":"Surinamese dollar","symbol":"$"}],"languages":[{"iso639_1":"nl","iso639_2":"nld","name":"Dutch","nativeName":"Nederlands"}],"translations":{"br":"Surinam","pt":"Suriname","nl":"Suriname","hr":"Surinam","fa":"سورینام","de":"Suriname","es":"Surinam","fr":"Surinam","ja":"スリナム","it":"Suriname","hu":"Suriname"},"flag":"https://flagcdn.com/sr.svg","regionalBlocs":[{"acronym":"CARICOM","name":"Caribbean Community","otherNames":["Comunidad del Caribe","Communauté Caribéenne","Caribische Gemeenschap"]},{"acronym":"USAN","name":"Union of South American Nations","otherAcronyms":["UNASUR","UNASUL","UZAN"],"otherNames":["Unión de Naciones Suramericanas","União de Nações Sul-Americanas","Unie van Zuid-Amerikaanse Naties","South American Union"]}],"cioc":"SUR","independent":true},{"name":"Svalbard and Jan Mayen","topLevelDomain":[".sj"],"alpha2Code":"SJ","alpha3Code":"SJM","callingCodes":["47"],"capital":"Longyearbyen","altSpellings":["SJ","Svalbard and Jan Mayen Islands"],"subregion":"Northern Europe","region":"Europe","population":2562,"latlng":[78.0,20.0],"demonym":"Norwegian","timezones":["UTC+01:00"],"nativeName":"Svalbard og Jan Mayen","numericCode":"744","flags":{"svg":"https://flagcdn.com/sj.svg","png":"https://flagcdn.com/w320/sj.png"},"currencies":[{"code":"NOK","name":"Norwegian krone","symbol":"kr"}],"languages":[{"iso639_1":"no","iso639_2":"nor","name":"Norwegian","nativeName":"Norsk"}],"translations":{"br":"Svalbard ha Jan Mayen","pt":"Svalbard","nl":"Svalbard en Jan Mayen","hr":"Svalbard i Jan Mayen","fa":"سوالبارد و یان ماین","de":"Svalbard und Jan Mayen","es":"Islas Svalbard y Jan Mayen","fr":"Svalbard et Jan Mayen","ja":"スヴァールバル諸島およびヤンマイエン島","it":"Svalbard e Jan Mayen","hu":"Spitzbergák és Jan Mayen-szigetek"},"flag":"https://flagcdn.com/sj.svg","independent":false},{"name":"Swaziland","topLevelDomain":[".sz"],"alpha2Code":"SZ","alpha3Code":"SWZ","callingCodes":["268"],"capital":"Mbabane","altSpellings":["SZ","weSwatini","Swatini","Ngwane","Kingdom of Swaziland","Umbuso waseSwatini"],"subregion":"Southern Africa","region":"Africa","population":1160164,"latlng":[-26.5,31.5],"demonym":"Swazi","area":17364.0,"gini":54.6,"timezones":["UTC+02:00"],"borders":["MOZ","ZAF"],"nativeName":"Swaziland","numericCode":"748","flags":{"svg":"https://flagcdn.com/sz.svg","png":"https://flagcdn.com/w320/sz.png"},"currencies":[{"code":"SZL","name":"Swazi lilangeni","symbol":"L"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"ss","iso639_2":"ssw","name":"Swati","nativeName":"SiSwati"}],"translations":{"br":"Eswatini","pt":"Suazilândia","nl":"Swaziland","hr":"Svazi","fa":"سوازیلند","de":"Swasiland","es":"Suazilandia","fr":"Swaziland","ja":"スワジランド","it":"Swaziland","hu":"Szváziföld"},"flag":"https://flagcdn.com/sz.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"SWZ","independent":true},{"name":"Sweden","topLevelDomain":[".se"],"alpha2Code":"SE","alpha3Code":"SWE","callingCodes":["46"],"capital":"Stockholm","altSpellings":["SE","Kingdom of Sweden","Konungariket Sverige"],"subregion":"Northern Europe","region":"Europe","population":10353442,"latlng":[62.0,15.0],"demonym":"Swedish","area":450295.0,"gini":30.0,"timezones":["UTC+01:00"],"borders":["FIN","NOR"],"nativeName":"Sverige","numericCode":"752","flags":{"svg":"https://flagcdn.com/se.svg","png":"https://flagcdn.com/w320/se.png"},"currencies":[{"code":"SEK","name":"Swedish krona","symbol":"kr"}],"languages":[{"iso639_1":"sv","iso639_2":"swe","name":"Swedish","nativeName":"svenska"}],"translations":{"br":"Sveden","pt":"Suécia","nl":"Zweden","hr":"Švedska","fa":"سوئد","de":"Schweden","es":"Suecia","fr":"Suède","ja":"スウェーデン","it":"Svezia","hu":"Svédország"},"flag":"https://flagcdn.com/se.svg","regionalBlocs":[{"acronym":"EU","name":"European Union"}],"cioc":"SWE","independent":true},{"name":"Switzerland","topLevelDomain":[".ch"],"alpha2Code":"CH","alpha3Code":"CHE","callingCodes":["41"],"capital":"Bern","altSpellings":["CH","Swiss Confederation","Schweiz","Suisse","Svizzera","Svizra"],"subregion":"Central Europe","region":"Europe","population":8636896,"latlng":[47.0,8.0],"demonym":"Swiss","area":41284.0,"gini":33.1,"timezones":["UTC+01:00"],"borders":["AUT","FRA","ITA","LIE","DEU"],"nativeName":"Schweiz","numericCode":"756","flags":{"svg":"https://flagcdn.com/ch.svg","png":"https://flagcdn.com/w320/ch.png"},"currencies":[{"code":"CHF","name":"Swiss franc","symbol":"Fr"}],"languages":[{"iso639_1":"de","iso639_2":"deu","name":"German","nativeName":"Deutsch"},{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"},{"iso639_1":"it","iso639_2":"ita","name":"Italian","nativeName":"Italiano"},{"iso639_2":"roh","name":"Romansh"}],"translations":{"br":"Suis","pt":"Suíça","nl":"Zwitserland","hr":"Švicarska","fa":"سوئیس","de":"Schweiz","es":"Suiza","fr":"Suisse","ja":"スイス","it":"Svizzera","hu":"Svájc"},"flag":"https://flagcdn.com/ch.svg","regionalBlocs":[{"acronym":"EFTA","name":"European Free Trade Association"}],"cioc":"SUI","independent":true},{"name":"Syrian Arab Republic","topLevelDomain":[".sy"],"alpha2Code":"SY","alpha3Code":"SYR","callingCodes":["963"],"capital":"Damascus","altSpellings":["SY","Syrian Arab Republic","Al-Jumhūrīyah Al-ʻArabīyah As-Sūrīyah"],"subregion":"Western Asia","region":"Asia","population":17500657,"latlng":[35.0,38.0],"demonym":"Syrian","area":185180.0,"gini":37.5,"timezones":["UTC+02:00"],"borders":["IRQ","ISR","JOR","LBN","TUR"],"nativeName":"سوريا","numericCode":"760","flags":{"svg":"https://flagcdn.com/sy.svg","png":"https://flagcdn.com/w320/sy.png"},"currencies":[{"code":"SYP","name":"Syrian pound","symbol":"£"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Siria","pt":"Síria","nl":"Syrië","hr":"Sirija","fa":"سوریه","de":"Syrien","es":"Siria","fr":"Syrie","ja":"シリア・アラブ共和国","it":"Siria","hu":"Szíria"},"flag":"https://flagcdn.com/sy.svg","regionalBlocs":[{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"SYR","independent":true},{"name":"Taiwan","topLevelDomain":[".tw"],"alpha2Code":"TW","alpha3Code":"TWN","callingCodes":["886"],"capital":"Taipei","altSpellings":["TW","Táiwān","Republic of China","中華民國","Zhōnghuá Mínguó"],"subregion":"Eastern Asia","region":"Asia","population":23503349,"latlng":[23.5,121.0],"demonym":"Taiwanese","area":36193.0,"timezones":["UTC+08:00"],"nativeName":"臺灣","numericCode":"158","flags":{"svg":"https://flagcdn.com/tw.svg","png":"https://flagcdn.com/w320/tw.png"},"currencies":[{"code":"TWD","name":"New Taiwan dollar","symbol":"$"}],"languages":[{"iso639_1":"zh","iso639_2":"zho","name":"Chinese","nativeName":"中文 (Zhōngwén)"}],"translations":{"br":"Taiwan","pt":"Taiwan","nl":"Taiwan","hr":"Tajvan","fa":"تایوان","de":"Taiwan","es":"Taiwán","fr":"Taïwan","ja":"台湾(中華民国)","it":"Taiwan","hu":"Tajvan"},"flag":"https://flagcdn.com/tw.svg","cioc":"TPE","independent":true},{"name":"Tajikistan","topLevelDomain":[".tj"],"alpha2Code":"TJ","alpha3Code":"TJK","callingCodes":["992"],"capital":"Dushanbe","altSpellings":["TJ","Toçikiston","Republic of Tajikistan","Ҷумҳурии Тоҷикистон","Çumhuriyi Toçikiston"],"subregion":"Central Asia","region":"Asia","population":9537642,"latlng":[39.0,71.0],"demonym":"Tadzhik","area":143100.0,"gini":34.0,"timezones":["UTC+05:00"],"borders":["AFG","CHN","KGZ","UZB"],"nativeName":"Тоҷикистон","numericCode":"762","flags":{"svg":"https://flagcdn.com/tj.svg","png":"https://flagcdn.com/w320/tj.png"},"currencies":[{"code":"TJS","name":"Tajikistani somoni","symbol":"ЅМ"}],"languages":[{"iso639_1":"tg","iso639_2":"tgk","name":"Tajik","nativeName":"тоҷикӣ"},{"iso639_1":"ru","iso639_2":"rus","name":"Russian","nativeName":"Русский"}],"translations":{"br":"Tadjikistan","pt":"Tajiquistão","nl":"Tadzjikistan","hr":"Tađikistan","fa":"تاجیکستان","de":"Tadschikistan","es":"Tayikistán","fr":"Tadjikistan","ja":"タジキスタン","it":"Tagikistan","hu":"Tádzsikisztán"},"flag":"https://flagcdn.com/tj.svg","cioc":"TJK","independent":true},{"name":"Tanzania, United Republic of","topLevelDomain":[".tz"],"alpha2Code":"TZ","alpha3Code":"TZA","callingCodes":["255"],"capital":"Dodoma","altSpellings":["TZ","United Republic of Tanzania","Jamhuri ya Muungano wa Tanzania"],"subregion":"Eastern Africa","region":"Africa","population":59734213,"latlng":[-6.0,35.0],"demonym":"Tanzanian","area":945087.0,"gini":40.5,"timezones":["UTC+03:00"],"borders":["BDI","COD","KEN","MWI","MOZ","RWA","UGA","ZMB"],"nativeName":"Tanzania","numericCode":"834","flags":{"svg":"https://flagcdn.com/tz.svg","png":"https://flagcdn.com/w320/tz.png"},"currencies":[{"code":"TZS","name":"Tanzanian shilling","symbol":"Sh"}],"languages":[{"iso639_1":"sw","iso639_2":"swa","name":"Swahili","nativeName":"Kiswahili"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Tanzania","pt":"Tanzânia","nl":"Tanzania","hr":"Tanzanija","fa":"تانزانیا","de":"Tansania","es":"Tanzania","fr":"Tanzanie","ja":"タンザニア","it":"Tanzania","hu":"Tanzánia"},"flag":"https://flagcdn.com/tz.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"TAN","independent":true},{"name":"Thailand","topLevelDomain":[".th"],"alpha2Code":"TH","alpha3Code":"THA","callingCodes":["66"],"capital":"Bangkok","altSpellings":["TH","Prathet","Thai","Kingdom of Thailand","ราชอาณาจักรไทย","Ratcha Anachak Thai"],"subregion":"South-Eastern Asia","region":"Asia","population":69799978,"latlng":[15.0,100.0],"demonym":"Thai","area":513120.0,"gini":34.9,"timezones":["UTC+07:00"],"borders":["MMR","KHM","LAO","MYS"],"nativeName":"ประเทศไทย","numericCode":"764","flags":{"svg":"https://flagcdn.com/th.svg","png":"https://flagcdn.com/w320/th.png"},"currencies":[{"code":"THB","name":"Thai baht","symbol":"฿"}],"languages":[{"iso639_1":"th","iso639_2":"tha","name":"Thai","nativeName":"ไทย"}],"translations":{"br":"Thailand","pt":"Tailândia","nl":"Thailand","hr":"Tajland","fa":"تایلند","de":"Thailand","es":"Tailandia","fr":"Thaïlande","ja":"タイ","it":"Tailandia","hu":"Thaiföld"},"flag":"https://flagcdn.com/th.svg","regionalBlocs":[{"acronym":"ASEAN","name":"Association of Southeast Asian Nations"}],"cioc":"THA","independent":true},{"name":"Timor-Leste","topLevelDomain":[".tl"],"alpha2Code":"TL","alpha3Code":"TLS","callingCodes":["670"],"capital":"Dili","altSpellings":["TL","East Timor","Democratic Republic of Timor-Leste","República Democrática de Timor-Leste","Repúblika Demokrátika Timór-Leste"],"subregion":"South-Eastern Asia","region":"Asia","population":1318442,"latlng":[-8.83333333,125.91666666],"demonym":"East Timorese","area":14874.0,"gini":28.7,"timezones":["UTC+09:00"],"borders":["IDN"],"nativeName":"Timor-Leste","numericCode":"626","flags":{"svg":"https://flagcdn.com/tl.svg","png":"https://flagcdn.com/w320/tl.png"},"currencies":[{"code":"USD","name":"United States Dollar","symbol":"$"}],"languages":[{"iso639_1":"pt","iso639_2":"por","name":"Portuguese","nativeName":"Português"}],"translations":{"br":"Timor ar Reter","pt":"Timor Leste","nl":"Oost-Timor","hr":"Istočni Timor","fa":"تیمور شرقی","de":"Timor-Leste","es":"Timor Oriental","fr":"Timor oriental","ja":"東ティモール","it":"Timor Est","hu":"Kelet-Timor"},"flag":"https://flagcdn.com/tl.svg","cioc":"TLS","independent":true},{"name":"Togo","topLevelDomain":[".tg"],"alpha2Code":"TG","alpha3Code":"TGO","callingCodes":["228"],"capital":"Lomé","altSpellings":["TG","Togolese","Togolese Republic","République Togolaise"],"subregion":"Western Africa","region":"Africa","population":8278737,"latlng":[8.0,1.16666666],"demonym":"Togolese","area":56785.0,"gini":43.1,"timezones":["UTC"],"borders":["BEN","BFA","GHA"],"nativeName":"Togo","numericCode":"768","flags":{"svg":"https://flagcdn.com/tg.svg","png":"https://flagcdn.com/w320/tg.png"},"currencies":[{"code":"XOF","name":"West African CFA franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Togo","pt":"Togo","nl":"Togo","hr":"Togo","fa":"توگو","de":"Togo","es":"Togo","fr":"Togo","ja":"トーゴ","it":"Togo","hu":"Togo"},"flag":"https://flagcdn.com/tg.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"TOG","independent":true},{"name":"Tokelau","topLevelDomain":[".tk"],"alpha2Code":"TK","alpha3Code":"TKL","callingCodes":["690"],"capital":"Fakaofo","altSpellings":["TK"],"subregion":"Polynesia","region":"Oceania","population":1411,"latlng":[-9.0,-172.0],"demonym":"Tokelauan","area":12.0,"timezones":["UTC+13:00"],"nativeName":"Tokelau","numericCode":"772","flags":{"svg":"https://flagcdn.com/tk.svg","png":"https://flagcdn.com/w320/tk.png"},"currencies":[{"code":"NZD","name":"New Zealand dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Tokelau","pt":"Toquelau","nl":"Tokelau","hr":"Tokelau","fa":"توکلائو","de":"Tokelau","es":"Islas Tokelau","fr":"Tokelau","ja":"トケラウ","it":"Isole Tokelau","hu":"Tokelau-szigetek"},"flag":"https://flagcdn.com/tk.svg","independent":false},{"name":"Tonga","topLevelDomain":[".to"],"alpha2Code":"TO","alpha3Code":"TON","callingCodes":["676"],"capital":"Nuku'alofa","altSpellings":["TO"],"subregion":"Polynesia","region":"Oceania","population":105697,"latlng":[-20.0,-175.0],"demonym":"Tongan","area":747.0,"gini":37.6,"timezones":["UTC+13:00"],"nativeName":"Tonga","numericCode":"776","flags":{"svg":"https://flagcdn.com/to.svg","png":"https://flagcdn.com/w320/to.png"},"currencies":[{"code":"TOP","name":"Tongan paʻanga","symbol":"T$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"to","iso639_2":"ton","name":"Tonga (Tonga Islands)","nativeName":"faka Tonga"}],"translations":{"br":"Tonga","pt":"Tonga","nl":"Tonga","hr":"Tonga","fa":"تونگا","de":"Tonga","es":"Tonga","fr":"Tonga","ja":"トンガ","it":"Tonga","hu":"Tonga"},"flag":"https://flagcdn.com/to.svg","cioc":"TGA","independent":true},{"name":"Trinidad and Tobago","topLevelDomain":[".tt"],"alpha2Code":"TT","alpha3Code":"TTO","callingCodes":["1"],"capital":"Port of Spain","altSpellings":["TT","Republic of Trinidad and Tobago"],"subregion":"Caribbean","region":"Americas","population":1399491,"latlng":[11.0,-61.0],"demonym":"Trinidadian","area":5130.0,"gini":40.3,"timezones":["UTC-04:00"],"nativeName":"Trinidad and Tobago","numericCode":"780","flags":{"svg":"https://flagcdn.com/tt.svg","png":"https://flagcdn.com/w320/tt.png"},"currencies":[{"code":"TTD","name":"Trinidad and Tobago dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Trinidad ha Tobago","pt":"Trindade e Tobago","nl":"Trinidad en Tobago","hr":"Trinidad i Tobago","fa":"ترینیداد و توباگو","de":"Trinidad und Tobago","es":"Trinidad y Tobago","fr":"Trinité et Tobago","ja":"トリニダード・トバゴ","it":"Trinidad e Tobago","hu":"Trinidad és Tobago"},"flag":"https://flagcdn.com/tt.svg","regionalBlocs":[{"acronym":"CARICOM","name":"Caribbean Community","otherNames":["Comunidad del Caribe","Communauté Caribéenne","Caribische Gemeenschap"]}],"cioc":"TTO","independent":true},{"name":"Tunisia","topLevelDomain":[".tn"],"alpha2Code":"TN","alpha3Code":"TUN","callingCodes":["216"],"capital":"Tunis","altSpellings":["TN","Republic of Tunisia","al-Jumhūriyyah at-Tūnisiyyah"],"subregion":"Northern Africa","region":"Africa","population":11818618,"latlng":[34.0,9.0],"demonym":"Tunisian","area":163610.0,"gini":32.8,"timezones":["UTC+01:00"],"borders":["DZA","LBY"],"nativeName":"تونس","numericCode":"788","flags":{"svg":"https://flagcdn.com/tn.svg","png":"https://flagcdn.com/w320/tn.png"},"currencies":[{"code":"TND","name":"Tunisian dinar","symbol":"د.ت"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Tunizia","pt":"Tunísia","nl":"Tunesië","hr":"Tunis","fa":"تونس","de":"Tunesien","es":"Túnez","fr":"Tunisie","ja":"チュニジア","it":"Tunisia","hu":"Tunézia"},"flag":"https://flagcdn.com/tn.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]},{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"TUN","independent":true},{"name":"Turkey","topLevelDomain":[".tr"],"alpha2Code":"TR","alpha3Code":"TUR","callingCodes":["90"],"capital":"Ankara","altSpellings":["TR","Turkiye","Republic of Turkey","Türkiye Cumhuriyeti"],"subregion":"Western Asia","region":"Asia","population":84339067,"latlng":[39.0,35.0],"demonym":"Turkish","area":783562.0,"gini":41.9,"timezones":["UTC+03:00"],"borders":["ARM","AZE","BGR","GEO","GRC","IRN","IRQ","SYR"],"nativeName":"Türkiye","numericCode":"792","flags":{"svg":"https://flagcdn.com/tr.svg","png":"https://flagcdn.com/w320/tr.png"},"currencies":[{"code":"TRY","name":"Turkish lira","symbol":"₺"}],"languages":[{"iso639_1":"tr","iso639_2":"tur","name":"Turkish","nativeName":"Türkçe"}],"translations":{"br":"Turkia","pt":"Turquia","nl":"Turkije","hr":"Turska","fa":"ترکیه","de":"Türkei","es":"Turquía","fr":"Turquie","ja":"トルコ","it":"Turchia","hu":"Törökország"},"flag":"https://flagcdn.com/tr.svg","cioc":"TUR","independent":true},{"name":"Turkmenistan","topLevelDomain":[".tm"],"alpha2Code":"TM","alpha3Code":"TKM","callingCodes":["993"],"capital":"Ashgabat","altSpellings":["TM"],"subregion":"Central Asia","region":"Asia","population":6031187,"latlng":[40.0,60.0],"demonym":"Turkmen","area":488100.0,"gini":40.8,"timezones":["UTC+05:00"],"borders":["AFG","IRN","KAZ","UZB"],"nativeName":"Türkmenistan","numericCode":"795","flags":{"svg":"https://flagcdn.com/tm.svg","png":"https://flagcdn.com/w320/tm.png"},"currencies":[{"code":"TMT","name":"Turkmenistan manat","symbol":"m"}],"languages":[{"iso639_1":"tk","iso639_2":"tuk","name":"Turkmen","nativeName":"Türkmen"},{"iso639_1":"ru","iso639_2":"rus","name":"Russian","nativeName":"Русский"}],"translations":{"br":"Turkmenistan","pt":"Turquemenistão","nl":"Turkmenistan","hr":"Turkmenistan","fa":"ترکمنستان","de":"Turkmenistan","es":"Turkmenistán","fr":"Turkménistan","ja":"トルクメニスタン","it":"Turkmenistan","hu":"Türkmenisztán"},"flag":"https://flagcdn.com/tm.svg","cioc":"TKM","independent":true},{"name":"Turks and Caicos Islands","topLevelDomain":[".tc"],"alpha2Code":"TC","alpha3Code":"TCA","callingCodes":["1"],"capital":"Cockburn Town","altSpellings":["TC"],"subregion":"Caribbean","region":"Americas","population":38718,"latlng":[21.75,-71.58333333],"demonym":"Turks and Caicos Islander","area":948.0,"timezones":["UTC-04:00"],"nativeName":"Turks and Caicos Islands","numericCode":"796","flags":{"svg":"https://flagcdn.com/tc.svg","png":"https://flagcdn.com/w320/tc.png"},"currencies":[{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Inizi Turks ha Caicos","pt":"Ilhas Turcas e Caicos","nl":"Turks- en Caicoseilanden","hr":"Otoci Turks i Caicos","fa":"جزایر تورکس و کایکوس","de":"Turks- und Caicosinseln","es":"Islas Turks y Caicos","fr":"Îles Turques-et-Caïques","ja":"タークス・カイコス諸島","it":"Isole Turks e Caicos","hu":"Turks- és Caicos-szigetek"},"flag":"https://flagcdn.com/tc.svg","independent":false},{"name":"Tuvalu","topLevelDomain":[".tv"],"alpha2Code":"TV","alpha3Code":"TUV","callingCodes":["688"],"capital":"Funafuti","altSpellings":["TV"],"subregion":"Polynesia","region":"Oceania","population":11792,"latlng":[-8.0,178.0],"demonym":"Tuvaluan","area":26.0,"gini":39.1,"timezones":["UTC+12:00"],"nativeName":"Tuvalu","numericCode":"798","flags":{"svg":"https://flagcdn.com/tv.svg","png":"https://flagcdn.com/w320/tv.png"},"currencies":[{"code":"AUD","name":"Australian dollar","symbol":"$"},{"code":"TVD[G]","name":"Tuvaluan dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Tuvalu","pt":"Tuvalu","nl":"Tuvalu","hr":"Tuvalu","fa":"تووالو","de":"Tuvalu","es":"Tuvalu","fr":"Tuvalu","ja":"ツバル","it":"Tuvalu","hu":"Tuvalu"},"flag":"https://flagcdn.com/tv.svg","cioc":"TUV","independent":true},{"name":"Uganda","topLevelDomain":[".ug"],"alpha2Code":"UG","alpha3Code":"UGA","callingCodes":["256"],"capital":"Kampala","altSpellings":["UG","Republic of Uganda","Jamhuri ya Uganda"],"subregion":"Eastern Africa","region":"Africa","population":45741000,"latlng":[1.0,32.0],"demonym":"Ugandan","area":241550.0,"gini":42.8,"timezones":["UTC+03:00"],"borders":["COD","KEN","RWA","SSD","TZA"],"nativeName":"Uganda","numericCode":"800","flags":{"svg":"https://flagcdn.com/ug.svg","png":"https://flagcdn.com/w320/ug.png"},"currencies":[{"code":"UGX","name":"Ugandan shilling","symbol":"Sh"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"sw","iso639_2":"swa","name":"Swahili","nativeName":"Kiswahili"}],"translations":{"br":"Ouganda","pt":"Uganda","nl":"Oeganda","hr":"Uganda","fa":"اوگاندا","de":"Uganda","es":"Uganda","fr":"Uganda","ja":"ウガンダ","it":"Uganda","hu":"Uganda"},"flag":"https://flagcdn.com/ug.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"UGA","independent":true},{"name":"Ukraine","topLevelDomain":[".ua"],"alpha2Code":"UA","alpha3Code":"UKR","callingCodes":["380"],"capital":"Kyiv","altSpellings":["UA","Ukrayina"],"subregion":"Eastern Europe","region":"Europe","population":44134693,"latlng":[49.0,32.0],"demonym":"Ukrainian","area":603700.0,"gini":26.6,"timezones":["UTC+02:00"],"borders":["BLR","HUN","MDA","POL","ROU","RUS","SVK"],"nativeName":"Україна","numericCode":"804","flags":{"svg":"https://flagcdn.com/ua.svg","png":"https://flagcdn.com/w320/ua.png"},"currencies":[{"code":"UAH","name":"Ukrainian hryvnia","symbol":"₴"}],"languages":[{"iso639_1":"uk","iso639_2":"ukr","name":"Ukrainian","nativeName":"Українська"}],"translations":{"br":"Ukraina","pt":"Ucrânia","nl":"Oekraïne","hr":"Ukrajina","fa":"وکراین","de":"Ukraine","es":"Ucrania","fr":"Ukraine","ja":"ウクライナ","it":"Ucraina","hu":"Ukrajna"},"flag":"https://flagcdn.com/ua.svg","cioc":"UKR","independent":true},{"name":"United Arab Emirates","topLevelDomain":[".ae"],"alpha2Code":"AE","alpha3Code":"ARE","callingCodes":["971"],"capital":"Abu Dhabi","altSpellings":["AE","UAE"],"subregion":"Western Asia","region":"Asia","population":9890400,"latlng":[24.0,54.0],"demonym":"Emirati","area":83600.0,"gini":26.0,"timezones":["UTC+04:00"],"borders":["OMN","SAU"],"nativeName":"دولة الإمارات العربية المتحدة","numericCode":"784","flags":{"svg":"https://flagcdn.com/ae.svg","png":"https://flagcdn.com/w320/ae.png"},"currencies":[{"code":"AED","name":"United Arab Emirates dirham","symbol":"د.إ"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Emirelezhioù Arab Unanet","pt":"Emirados árabes Unidos","nl":"Verenigde Arabische Emiraten","hr":"Ujedinjeni Arapski Emirati","fa":"امارات متحده عربی","de":"Vereinigte Arabische Emirate","es":"Emiratos Árabes Unidos","fr":"Émirats arabes unis","ja":"アラブ首長国連邦","it":"Emirati Arabi Uniti","hu":"Egyesült Arab Emírségek"},"flag":"https://flagcdn.com/ae.svg","regionalBlocs":[{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"UAE","independent":true},{"name":"United Kingdom of Great Britain and Northern Ireland","topLevelDomain":[".uk"],"alpha2Code":"GB","alpha3Code":"GBR","callingCodes":["44"],"capital":"London","altSpellings":["GB","UK","Great Britain"],"subregion":"Northern Europe","region":"Europe","population":67215293,"latlng":[54.0,-2.0],"demonym":"British","area":242900.0,"gini":35.1,"timezones":["UTC-08:00","UTC-05:00","UTC-04:00","UTC-03:00","UTC-02:00","UTC","UTC+01:00","UTC+02:00","UTC+06:00"],"borders":["IRL"],"nativeName":"United Kingdom","numericCode":"826","flags":{"svg":"https://flagcdn.com/gb.svg","png":"https://flagcdn.com/w320/gb.png"},"currencies":[{"code":"GBP","name":"British pound","symbol":"£"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Rouantelezh-Unanet","pt":"Reino Unido","nl":"Verenigd Koninkrijk","hr":"Ujedinjeno Kraljevstvo","fa":"بریتانیای کبیر و ایرلند شمالی","de":"Vereinigtes Königreich","es":"Reino Unido","fr":"Royaume-Uni","ja":"イギリス","it":"Regno Unito","hu":"Nagy-Britannia"},"flag":"https://flagcdn.com/gb.svg","cioc":"GBR","independent":true},{"name":"United States of America","topLevelDomain":[".us"],"alpha2Code":"US","alpha3Code":"USA","callingCodes":["1"],"capital":"Washington, D.C.","altSpellings":["US","USA","United States of America"],"subregion":"Northern America","region":"Americas","population":329484123,"latlng":[38.0,-97.0],"demonym":"American","area":9629091.0,"gini":41.4,"timezones":["UTC-12:00","UTC-11:00","UTC-10:00","UTC-09:00","UTC-08:00","UTC-07:00","UTC-06:00","UTC-05:00","UTC-04:00","UTC+10:00","UTC+12:00"],"borders":["CAN","MEX"],"nativeName":"United States","numericCode":"840","flags":{"svg":"https://flagcdn.com/us.svg","png":"https://flagcdn.com/w320/us.png"},"currencies":[{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Stadoù-Unanet","pt":"Estados Unidos","nl":"Verenigde Staten","hr":"Sjedinjene Američke Države","fa":"ایالات متحده آمریکا","de":"Vereinigte Staaten von Amerika","es":"Estados Unidos","fr":"États-Unis","ja":"アメリカ合衆国","it":"Stati Uniti D'America","hu":"Amerikai Egyesült Államok"},"flag":"https://flagcdn.com/us.svg","regionalBlocs":[{"acronym":"NAFTA","name":"North American Free Trade Agreement","otherNames":["Tratado de Libre Comercio de América del Norte","Accord de Libre-échange Nord-Américain"]}],"cioc":"USA","independent":true},{"name":"Uruguay","topLevelDomain":[".uy"],"alpha2Code":"UY","alpha3Code":"URY","callingCodes":["598"],"capital":"Montevideo","altSpellings":["UY","Oriental Republic of Uruguay","República Oriental del Uruguay"],"subregion":"South America","region":"Americas","population":3473727,"latlng":[-33.0,-56.0],"demonym":"Uruguayan","area":181034.0,"gini":39.7,"timezones":["UTC-03:00"],"borders":["ARG","BRA"],"nativeName":"Uruguay","numericCode":"858","flags":{"svg":"https://flagcdn.com/uy.svg","png":"https://flagcdn.com/w320/uy.png"},"currencies":[{"code":"UYU","name":"Uruguayan peso","symbol":"$"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Uruguay","pt":"Uruguai","nl":"Uruguay","hr":"Urugvaj","fa":"اروگوئه","de":"Uruguay","es":"Uruguay","fr":"Uruguay","ja":"ウルグアイ","it":"Uruguay","hu":"Uruguay"},"flag":"https://flagcdn.com/uy.svg","regionalBlocs":[{"acronym":"USAN","name":"Union of South American Nations","otherAcronyms":["UNASUR","UNASUL","UZAN"],"otherNames":["Unión de Naciones Suramericanas","União de Nações Sul-Americanas","Unie van Zuid-Amerikaanse Naties","South American Union"]}],"cioc":"URU","independent":true},{"name":"Uzbekistan","topLevelDomain":[".uz"],"alpha2Code":"UZ","alpha3Code":"UZB","callingCodes":["998"],"capital":"Tashkent","altSpellings":["UZ","Republic of Uzbekistan","O‘zbekiston Respublikasi","Ўзбекистон Республикаси"],"subregion":"Central Asia","region":"Asia","population":34232050,"latlng":[41.0,64.0],"demonym":"Uzbekistani","area":447400.0,"gini":35.3,"timezones":["UTC+05:00"],"borders":["AFG","KAZ","KGZ","TJK","TKM"],"nativeName":"O‘zbekiston","numericCode":"860","flags":{"svg":"https://flagcdn.com/uz.svg","png":"https://flagcdn.com/w320/uz.png"},"currencies":[{"code":"UZS","name":"Uzbekistani so'm","symbol":"so'm"}],"languages":[{"iso639_1":"uz","iso639_2":"uzb","name":"Uzbek","nativeName":"Oʻzbek"},{"iso639_1":"ru","iso639_2":"rus","name":"Russian","nativeName":"Русский"}],"translations":{"br":"Ouzbekistan","pt":"Usbequistão","nl":"Oezbekistan","hr":"Uzbekistan","fa":"ازبکستان","de":"Usbekistan","es":"Uzbekistán","fr":"Ouzbékistan","ja":"ウズベキスタン","it":"Uzbekistan","hu":"Üzbegisztán"},"flag":"https://flagcdn.com/uz.svg","cioc":"UZB","independent":false},{"name":"Vanuatu","topLevelDomain":[".vu"],"alpha2Code":"VU","alpha3Code":"VUT","callingCodes":["678"],"capital":"Port Vila","altSpellings":["VU","Republic of Vanuatu","Ripablik blong Vanuatu","République de Vanuatu"],"subregion":"Melanesia","region":"Oceania","population":307150,"latlng":[-16.0,167.0],"demonym":"Ni-Vanuatu","area":12189.0,"gini":37.6,"timezones":["UTC+11:00"],"nativeName":"Vanuatu","numericCode":"548","flags":{"svg":"https://flagcdn.com/vu.svg","png":"https://flagcdn.com/w320/vu.png"},"currencies":[{"code":"VUV","name":"Vanuatu vatu","symbol":"Vt"}],"languages":[{"iso639_1":"bi","iso639_2":"bis","name":"Bislama","nativeName":"Bislama"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Vanuatu","pt":"Vanuatu","nl":"Vanuatu","hr":"Vanuatu","fa":"وانواتو","de":"Vanuatu","es":"Vanuatu","fr":"Vanuatu","ja":"バヌアツ","it":"Vanuatu","hu":"Vanuatu"},"flag":"https://flagcdn.com/vu.svg","cioc":"VAN","independent":true},{"name":"Venezuela (Bolivarian Republic of)","topLevelDomain":[".ve"],"alpha2Code":"VE","alpha3Code":"VEN","callingCodes":["58"],"capital":"Caracas","altSpellings":["VE","Bolivarian Republic of Venezuela","República Bolivariana de Venezuela"],"subregion":"South America","region":"Americas","population":28435943,"latlng":[8.0,-66.0],"demonym":"Venezuelan","area":916445.0,"gini":44.8,"timezones":["UTC-04:00"],"borders":["BRA","COL","GUY"],"nativeName":"Venezuela","numericCode":"862","flags":{"svg":"https://flagcdn.com/ve.svg","png":"https://flagcdn.com/w320/ve.png"},"currencies":[{"code":"VEF","name":"Venezuelan bolívar","symbol":"Bs S"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Venezuela","pt":"Venezuela","nl":"Venezuela","hr":"Venezuela","fa":"ونزوئلا","de":"Venezuela","es":"Venezuela","fr":"Venezuela","ja":"ベネズエラ・ボリバル共和国","it":"Venezuela","hu":"Venezuela"},"flag":"https://flagcdn.com/ve.svg","regionalBlocs":[{"acronym":"USAN","name":"Union of South American Nations","otherAcronyms":["UNASUR","UNASUL","UZAN"],"otherNames":["Unión de Naciones Suramericanas","União de Nações Sul-Americanas","Unie van Zuid-Amerikaanse Naties","South American Union"]}],"cioc":"VEN","independent":true},{"name":"Vietnam","topLevelDomain":[".vn"],"alpha2Code":"VN","alpha3Code":"VNM","callingCodes":["84"],"capital":"Hanoi","altSpellings":["VN","Socialist Republic of Vietnam","Cộng hòa Xã hội chủ nghĩa Việt Nam"],"subregion":"South-Eastern Asia","region":"Asia","population":97338583,"latlng":[16.16666666,107.83333333],"demonym":"Vietnamese","area":331212.0,"gini":35.7,"timezones":["UTC+07:00"],"borders":["KHM","CHN","LAO"],"nativeName":"Việt Nam","numericCode":"704","flags":{"svg":"https://flagcdn.com/vn.svg","png":"https://flagcdn.com/w320/vn.png"},"currencies":[{"code":"VND","name":"Vietnamese đồng","symbol":"₫"}],"languages":[{"iso639_1":"vi","iso639_2":"vie","name":"Vietnamese","nativeName":"Tiếng Việt"}],"translations":{"br":"Viêt Nam","pt":"Vietname","nl":"Vietnam","hr":"Vijetnam","fa":"ویتنام","de":"Vietnam","es":"Vietnam","fr":"Viêt Nam","ja":"ベトナム","it":"Vietnam","hu":"Vietnám"},"flag":"https://flagcdn.com/vn.svg","regionalBlocs":[{"acronym":"ASEAN","name":"Association of Southeast Asian Nations"}],"cioc":"VIE","independent":true},{"name":"Wallis and Futuna","topLevelDomain":[".wf"],"alpha2Code":"WF","alpha3Code":"WLF","callingCodes":["681"],"capital":"Mata-Utu","altSpellings":["WF","Territory of the Wallis and Futuna Islands","Territoire des îles Wallis et Futuna"],"subregion":"Polynesia","region":"Oceania","population":11750,"latlng":[-13.3,-176.2],"demonym":"Wallis and Futuna Islander","area":142.0,"timezones":["UTC+12:00"],"nativeName":"Wallis et Futuna","numericCode":"876","flags":{"svg":"https://flagcdn.com/wf.svg","png":"https://flagcdn.com/w320/wf.png"},"currencies":[{"code":"XPF","name":"CFP franc","symbol":"Fr"}],"languages":[{"iso639_1":"fr","iso639_2":"fra","name":"French","nativeName":"français"}],"translations":{"br":"Wallis ha Futuna","pt":"Wallis e Futuna","nl":"Wallis en Futuna","hr":"Wallis i Fortuna","fa":"والیس و فوتونا","de":"Wallis und Futuna","es":"Wallis y Futuna","fr":"Wallis-et-Futuna","ja":"ウォリス・フツナ","it":"Wallis e Futuna","hu":"Wallis és Futuna"},"flag":"https://flagcdn.com/wf.svg","independent":false},{"name":"Western Sahara","topLevelDomain":[".eh"],"alpha2Code":"EH","alpha3Code":"ESH","callingCodes":["212"],"capital":"El Aaiún","altSpellings":["EH","Taneẓroft Tutrimt"],"subregion":"Northern Africa","region":"Africa","population":510713,"latlng":[24.5,-13.0],"demonym":"Sahrawi","area":266000.0,"timezones":["UTC+00:00"],"borders":["DZA","MRT","MAR"],"nativeName":"الصحراء الغربية","numericCode":"732","flags":{"svg":"https://flagcdn.com/eh.svg","png":"https://flagcdn.com/w320/eh.png"},"currencies":[{"code":"MAD","name":"Moroccan dirham","symbol":"د.م."},{"code":"DZD","name":"Algerian dinar","symbol":"د.ج"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"br":"Sahara ar C'hornôg","pt":"Saara Ocidental","nl":"Westelijke Sahara","hr":"Zapadna Sahara","fa":"جمهوری دموکراتیک عربی صحرا","de":"Westsahara","es":"Sahara Occidental","fr":"Sahara Occidental","ja":"西サハラ","it":"Sahara Occidentale","hu":"Nyugat-Szahara"},"flag":"https://flagcdn.com/eh.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"independent":false},{"name":"Yemen","topLevelDomain":[".ye"],"alpha2Code":"YE","alpha3Code":"YEM","callingCodes":["967"],"capital":"Sana'a","altSpellings":["YE","Yemeni Republic","al-Jumhūriyyah al-Yamaniyyah"],"subregion":"Western Asia","region":"Asia","population":29825968,"latlng":[15.0,48.0],"demonym":"Yemeni","area":527968.0,"gini":36.7,"timezones":["UTC+03:00"],"borders":["OMN","SAU"],"nativeName":"اليَمَن","numericCode":"887","flags":{"svg":"https://flagcdn.com/ye.svg","png":"https://flagcdn.com/w320/ye.png"},"currencies":[{"code":"YER","name":"Yemeni rial","symbol":"﷼"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"br":"Yemen","pt":"Iémen","nl":"Jemen","hr":"Jemen","fa":"یمن","de":"Jemen","es":"Yemen","fr":"Yémen","ja":"イエメン","it":"Yemen","hu":"Jemen"},"flag":"https://flagcdn.com/ye.svg","regionalBlocs":[{"acronym":"AL","name":"Arab League","otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"YEM","independent":true},{"name":"Zambia","topLevelDomain":[".zm"],"alpha2Code":"ZM","alpha3Code":"ZMB","callingCodes":["260"],"capital":"Lusaka","altSpellings":["ZM","Republic of Zambia"],"subregion":"Eastern Africa","region":"Africa","population":18383956,"latlng":[-15.0,30.0],"demonym":"Zambian","area":752618.0,"gini":57.1,"timezones":["UTC+02:00"],"borders":["AGO","BWA","COD","MWI","MOZ","NAM","TZA","ZWE"],"nativeName":"Zambia","numericCode":"894","flags":{"svg":"https://flagcdn.com/zm.svg","png":"https://flagcdn.com/w320/zm.png"},"currencies":[{"code":"ZMW","name":"Zambian kwacha","symbol":"ZK"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"br":"Zambia","pt":"Zâmbia","nl":"Zambia","hr":"Zambija","fa":"زامبیا","de":"Sambia","es":"Zambia","fr":"Zambie","ja":"ザンビア","it":"Zambia","hu":"Zambia"},"flag":"https://flagcdn.com/zm.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"ZAM","independent":true},{"name":"Zimbabwe","topLevelDomain":[".zw"],"alpha2Code":"ZW","alpha3Code":"ZWE","callingCodes":["263"],"capital":"Harare","altSpellings":["ZW","Republic of Zimbabwe"],"subregion":"Southern Africa","region":"Africa","population":14862927,"latlng":[-20.0,30.0],"demonym":"Zimbabwean","area":390757.0,"gini":50.3,"timezones":["UTC+02:00"],"borders":["BWA","MOZ","ZAF","ZMB"],"nativeName":"Zimbabwe","numericCode":"716","flags":{"svg":"https://flagcdn.com/zw.svg","png":"https://flagcdn.com/w320/zw.png"},"currencies":[{"code":"ZMW","name":"Zambian kwacha","symbol":"K"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"},{"iso639_1":"sn","iso639_2":"sna","name":"Shona","nativeName":"chiShona"},{"iso639_1":"nd","iso639_2":"nde","name":"Northern Ndebele","nativeName":"isiNdebele"}],"translations":{"br":"Zimbabwe","pt":"Zimbabué","nl":"Zimbabwe","hr":"Zimbabve","fa":"زیمباوه","de":"Simbabwe","es":"Zimbabue","fr":"Zimbabwe","ja":"ジンバブエ","it":"Zimbabwe","hu":"Zimbabwe"},"flag":"https://flagcdn.com/zw.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"ZIM","independent":true}] ================================================ FILE: tests/testdata/driverStandings.json ================================================ {"MRData":{"xmlns":"http:\/\/ergast.com\/mrd\/1.5","series":"f1","url":"http://ergast.com/api/f1/2022/driverstandings.json","limit":"30","offset":"0","total":"22","StandingsTable":{"season":"2022","StandingsLists":[{"season":"2022","round":"17","DriverStandings":[{"position":"1","positionText":"1","points":"341","wins":"11","Driver":{"driverId":"max_verstappen","permanentNumber":"33","code":"VER","url":"http:\/\/en.wikipedia.org\/wiki\/Max_Verstappen","givenName":"Max","familyName":"Verstappen","dateOfBirth":"1997-09-30","nationality":"Dutch"}, "Constructors":[{"constructorId":"red_bull","url":"http:\/\/en.wikipedia.org\/wiki\/Red_Bull_Racing","name":"Red Bull","nationality":"Austrian"}]},{"position":"2","positionText":"2","points":"237","wins":"3","Driver":{"driverId":"leclerc","permanentNumber":"16","code":"LEC","url":"http:\/\/en.wikipedia.org\/wiki\/Charles_Leclerc","givenName":"Charles","familyName":"Leclerc","dateOfBirth":"1997-10-16","nationality":"Monegasque"}, "Constructors":[{"constructorId":"ferrari","url":"http:\/\/en.wikipedia.org\/wiki\/Scuderia_Ferrari","name":"Ferrari","nationality":"Italian"}]},{"position":"3","positionText":"3","points":"235","wins":"2","Driver":{"driverId":"perez","permanentNumber":"11","code":"PER","url":"http:\/\/en.wikipedia.org\/wiki\/Sergio_P%C3%A9rez","givenName":"Sergio","familyName":"Pérez","dateOfBirth":"1990-01-26","nationality":"Mexican"}, "Constructors":[{"constructorId":"red_bull","url":"http:\/\/en.wikipedia.org\/wiki\/Red_Bull_Racing","name":"Red Bull","nationality":"Austrian"}]},{"position":"4","positionText":"4","points":"203","wins":"0","Driver":{"driverId":"russell","permanentNumber":"63","code":"RUS","url":"http:\/\/en.wikipedia.org\/wiki\/George_Russell_%28racing_driver%29","givenName":"George","familyName":"Russell","dateOfBirth":"1998-02-15","nationality":"British"}, "Constructors":[{"constructorId":"mercedes","url":"http:\/\/en.wikipedia.org\/wiki\/Mercedes-Benz_in_Formula_One","name":"Mercedes","nationality":"German"}]},{"position":"5","positionText":"5","points":"202","wins":"1","Driver":{"driverId":"sainz","permanentNumber":"55","code":"SAI","url":"http:\/\/en.wikipedia.org\/wiki\/Carlos_Sainz_Jr.","givenName":"Carlos","familyName":"Sainz","dateOfBirth":"1994-09-01","nationality":"Spanish"}, "Constructors":[{"constructorId":"ferrari","url":"http:\/\/en.wikipedia.org\/wiki\/Scuderia_Ferrari","name":"Ferrari","nationality":"Italian"}]},{"position":"6","positionText":"6","points":"170","wins":"0","Driver":{"driverId":"hamilton","permanentNumber":"44","code":"HAM","url":"http:\/\/en.wikipedia.org\/wiki\/Lewis_Hamilton","givenName":"Lewis","familyName":"Hamilton","dateOfBirth":"1985-01-07","nationality":"British"}, "Constructors":[{"constructorId":"mercedes","url":"http:\/\/en.wikipedia.org\/wiki\/Mercedes-Benz_in_Formula_One","name":"Mercedes","nationality":"German"}]},{"position":"7","positionText":"7","points":"100","wins":"0","Driver":{"driverId":"norris","permanentNumber":"4","code":"NOR","url":"http:\/\/en.wikipedia.org\/wiki\/Lando_Norris","givenName":"Lando","familyName":"Norris","dateOfBirth":"1999-11-13","nationality":"British"}, "Constructors":[{"constructorId":"mclaren","url":"http:\/\/en.wikipedia.org\/wiki\/McLaren","name":"McLaren","nationality":"British"}]},{"position":"8","positionText":"8","points":"66","wins":"0","Driver":{"driverId":"ocon","permanentNumber":"31","code":"OCO","url":"http:\/\/en.wikipedia.org\/wiki\/Esteban_Ocon","givenName":"Esteban","familyName":"Ocon","dateOfBirth":"1996-09-17","nationality":"French"}, "Constructors":[{"constructorId":"alpine","url":"http:\/\/en.wikipedia.org\/wiki\/Alpine_F1_Team","name":"Alpine F1 Team","nationality":"French"}]},{"position":"9","positionText":"9","points":"59","wins":"0","Driver":{"driverId":"alonso","permanentNumber":"14","code":"ALO","url":"http:\/\/en.wikipedia.org\/wiki\/Fernando_Alonso","givenName":"Fernando","familyName":"Alonso","dateOfBirth":"1981-07-29","nationality":"Spanish"}, "Constructors":[{"constructorId":"alpine","url":"http:\/\/en.wikipedia.org\/wiki\/Alpine_F1_Team","name":"Alpine F1 Team","nationality":"French"}]},{"position":"10","positionText":"10","points":"46","wins":"0","Driver":{"driverId":"bottas","permanentNumber":"77","code":"BOT","url":"http:\/\/en.wikipedia.org\/wiki\/Valtteri_Bottas","givenName":"Valtteri","familyName":"Bottas","dateOfBirth":"1989-08-28","nationality":"Finnish"}, "Constructors":[{"constructorId":"alfa","url":"http:\/\/en.wikipedia.org\/wiki\/Alfa_Romeo_in_Formula_One","name":"Alfa Romeo","nationality":"Swiss"}]},{"position":"11","positionText":"11","points":"29","wins":"0","Driver":{"driverId":"ricciardo","permanentNumber":"3","code":"RIC","url":"http:\/\/en.wikipedia.org\/wiki\/Daniel_Ricciardo","givenName":"Daniel","familyName":"Ricciardo","dateOfBirth":"1989-07-01","nationality":"Australian"}, "Constructors":[{"constructorId":"mclaren","url":"http:\/\/en.wikipedia.org\/wiki\/McLaren","name":"McLaren","nationality":"British"}]},{"position":"12","positionText":"12","points":"24","wins":"0","Driver":{"driverId":"vettel","permanentNumber":"5","code":"VET","url":"http:\/\/en.wikipedia.org\/wiki\/Sebastian_Vettel","givenName":"Sebastian","familyName":"Vettel","dateOfBirth":"1987-07-03","nationality":"German"}, "Constructors":[{"constructorId":"aston_martin","url":"http:\/\/en.wikipedia.org\/wiki\/Aston_Martin_in_Formula_One","name":"Aston Martin","nationality":"British"}]},{"position":"13","positionText":"13","points":"23","wins":"0","Driver":{"driverId":"gasly","permanentNumber":"10","code":"GAS","url":"http:\/\/en.wikipedia.org\/wiki\/Pierre_Gasly","givenName":"Pierre","familyName":"Gasly","dateOfBirth":"1996-02-07","nationality":"French"}, "Constructors":[{"constructorId":"alphatauri","url":"http:\/\/en.wikipedia.org\/wiki\/Scuderia_AlphaTauri","name":"AlphaTauri","nationality":"Italian"}]},{"position":"14","positionText":"14","points":"22","wins":"0","Driver":{"driverId":"kevin_magnussen","permanentNumber":"20","code":"MAG","url":"http:\/\/en.wikipedia.org\/wiki\/Kevin_Magnussen","givenName":"Kevin","familyName":"Magnussen","dateOfBirth":"1992-10-05","nationality":"Danish"}, "Constructors":[{"constructorId":"haas","url":"http:\/\/en.wikipedia.org\/wiki\/Haas_F1_Team","name":"Haas F1 Team","nationality":"American"}]},{"position":"15","positionText":"15","points":"13","wins":"0","Driver":{"driverId":"stroll","permanentNumber":"18","code":"STR","url":"http:\/\/en.wikipedia.org\/wiki\/Lance_Stroll","givenName":"Lance","familyName":"Stroll","dateOfBirth":"1998-10-29","nationality":"Canadian"}, "Constructors":[{"constructorId":"aston_martin","url":"http:\/\/en.wikipedia.org\/wiki\/Aston_Martin_in_Formula_One","name":"Aston Martin","nationality":"British"}]},{"position":"16","positionText":"16","points":"12","wins":"0","Driver":{"driverId":"mick_schumacher","permanentNumber":"47","code":"MSC","url":"http:\/\/en.wikipedia.org\/wiki\/Mick_Schumacher","givenName":"Mick","familyName":"Schumacher","dateOfBirth":"1999-03-22","nationality":"German"}, "Constructors":[{"constructorId":"haas","url":"http:\/\/en.wikipedia.org\/wiki\/Haas_F1_Team","name":"Haas F1 Team","nationality":"American"}]},{"position":"17","positionText":"17","points":"11","wins":"0","Driver":{"driverId":"tsunoda","permanentNumber":"22","code":"TSU","url":"http:\/\/en.wikipedia.org\/wiki\/Yuki_Tsunoda","givenName":"Yuki","familyName":"Tsunoda","dateOfBirth":"2000-05-11","nationality":"Japanese"}, "Constructors":[{"constructorId":"alphatauri","url":"http:\/\/en.wikipedia.org\/wiki\/Scuderia_AlphaTauri","name":"AlphaTauri","nationality":"Italian"}]},{"position":"18","positionText":"18","points":"6","wins":"0","Driver":{"driverId":"zhou","permanentNumber":"24","code":"ZHO","url":"http:\/\/en.wikipedia.org\/wiki\/Guanyu_Zhou","givenName":"Guanyu","familyName":"Zhou","dateOfBirth":"1999-05-30","nationality":"Chinese"}, "Constructors":[{"constructorId":"alfa","url":"http:\/\/en.wikipedia.org\/wiki\/Alfa_Romeo_in_Formula_One","name":"Alfa Romeo","nationality":"Swiss"}]},{"position":"19","positionText":"19","points":"4","wins":"0","Driver":{"driverId":"albon","permanentNumber":"23","code":"ALB","url":"http:\/\/en.wikipedia.org\/wiki\/Alexander_Albon","givenName":"Alexander","familyName":"Albon","dateOfBirth":"1996-03-23","nationality":"Thai"}, "Constructors":[{"constructorId":"williams","url":"http:\/\/en.wikipedia.org\/wiki\/Williams_Grand_Prix_Engineering","name":"Williams","nationality":"British"}]},{"position":"20","positionText":"20","points":"2","wins":"0","Driver":{"driverId":"de_vries","permanentNumber":"45","code":"DEV","url":"http:\/\/en.wikipedia.org\/wiki\/Nyck_de_Vries","givenName":"Nyck","familyName":"de Vries","dateOfBirth":"1995-02-06","nationality":"Dutch"}, "Constructors":[{"constructorId":"williams","url":"http:\/\/en.wikipedia.org\/wiki\/Williams_Grand_Prix_Engineering","name":"Williams","nationality":"British"}]},{"position":"21","positionText":"21","points":"0","wins":"0","Driver":{"driverId":"latifi","permanentNumber":"6","code":"LAT","url":"http:\/\/en.wikipedia.org\/wiki\/Nicholas_Latifi","givenName":"Nicholas","familyName":"Latifi","dateOfBirth":"1995-06-29","nationality":"Canadian"}, "Constructors":[{"constructorId":"williams","url":"http:\/\/en.wikipedia.org\/wiki\/Williams_Grand_Prix_Engineering","name":"Williams","nationality":"British"}]},{"position":"22","positionText":"22","points":"0","wins":"0","Driver":{"driverId":"hulkenberg","permanentNumber":"27","code":"HUL","url":"http:\/\/en.wikipedia.org\/wiki\/Nico_H%C3%BClkenberg","givenName":"Nico","familyName":"Hülkenberg","dateOfBirth":"1987-08-19","nationality":"German"}, "Constructors":[{"constructorId":"aston_martin","url":"http:\/\/en.wikipedia.org\/wiki\/Aston_Martin_in_Formula_One","name":"Aston Martin","nationality":"British"}]}]}]}}} ================================================ FILE: tests/testdata/localStorageMock.ts ================================================ export default class LocalStorageMock { store: { [key: string]: string }; constructor() { this.store = {}; } clear() { this.store = {}; } getItem(key: string): string { return this.store[key]; } setItem(key: string, value: string) { this.store[key] = value; } removeItem(key: string) { delete this.store[key]; } } ================================================ FILE: tests/testdata/qualifying.json ================================================ {"MRData":{"xmlns":"http:\/\/ergast.com\/mrd\/1.5","series":"f1","url":"http://ergast.com/api/f1/2022/20/qualifying.json","limit":"30","offset":"0","total":"20","RaceTable":{"season":"2022","round":"20","Races":[{"season":"2022","round":"20","url":"http://en.wikipedia.org/wiki/2022_Mexican_Grand_Prix","raceName":"Mexico City Grand Prix","Circuit":{"circuitId":"rodriguez","url":"http://en.wikipedia.org/wiki/Aut%C3%B3dromo_Hermanos_Rodr%C3%ADguez","circuitName":"Autódromo Hermanos Rodríguez","Location":{"lat":"19.4042","long":"-99.0907","locality":"Mexico City","country":"Mexico"}},"date":"2022-10-30","time":"20:00:00Z","QualifyingResults":[{"number":"1","position":"1","Driver":{"driverId":"max_verstappen","permanentNumber":"33","code":"VER","url":"http://en.wikipedia.org/wiki/Max_Verstappen","givenName":"Max","familyName":"Verstappen","dateOfBirth":"1997-09-30","nationality":"Dutch"},"Constructor":{"constructorId":"red_bull","url":"http://en.wikipedia.org/wiki/Red_Bull_Racing","name":"Red Bull","nationality":"Austrian"},"Q1":"1:19.222","Q2":"1:18.566","Q3":"1:17.775"},{"number":"63","position":"2","Driver":{"driverId":"russell","permanentNumber":"63","code":"RUS","url":"http://en.wikipedia.org/wiki/George_Russell_%28racing_driver%29","givenName":"George","familyName":"Russell","dateOfBirth":"1998-02-15","nationality":"British"},"Constructor":{"constructorId":"mercedes","url":"http://en.wikipedia.org/wiki/Mercedes-Benz_in_Formula_One","name":"Mercedes","nationality":"German"},"Q1":"1:19.583","Q2":"1:18.565","Q3":"1:18.079"},{"number":"44","position":"3","Driver":{"driverId":"hamilton","permanentNumber":"44","code":"HAM","url":"http://en.wikipedia.org/wiki/Lewis_Hamilton","givenName":"Lewis","familyName":"Hamilton","dateOfBirth":"1985-01-07","nationality":"British"},"Constructor":{"constructorId":"mercedes","url":"http://en.wikipedia.org/wiki/Mercedes-Benz_in_Formula_One","name":"Mercedes","nationality":"German"},"Q1":"1:19.169","Q2":"1:18.552","Q3":"1:18.084"},{"number":"11","position":"4","Driver":{"driverId":"perez","permanentNumber":"11","code":"PER","url":"http://en.wikipedia.org/wiki/Sergio_P%C3%A9rez","givenName":"Sergio","familyName":"Pérez","dateOfBirth":"1990-01-26","nationality":"Mexican"},"Constructor":{"constructorId":"red_bull","url":"http://en.wikipedia.org/wiki/Red_Bull_Racing","name":"Red Bull","nationality":"Austrian"},"Q1":"1:19.706","Q2":"1:18.615","Q3":"1:18.128"},{"number":"55","position":"5","Driver":{"driverId":"sainz","permanentNumber":"55","code":"SAI","url":"http://en.wikipedia.org/wiki/Carlos_Sainz_Jr.","givenName":"Carlos","familyName":"Sainz","dateOfBirth":"1994-09-01","nationality":"Spanish"},"Constructor":{"constructorId":"ferrari","url":"http://en.wikipedia.org/wiki/Scuderia_Ferrari","name":"Ferrari","nationality":"Italian"},"Q1":"1:19.566","Q2":"1:18.560","Q3":"1:18.351"},{"number":"77","position":"6","Driver":{"driverId":"bottas","permanentNumber":"77","code":"BOT","url":"http://en.wikipedia.org/wiki/Valtteri_Bottas","givenName":"Valtteri","familyName":"Bottas","dateOfBirth":"1989-08-28","nationality":"Finnish"},"Constructor":{"constructorId":"alfa","url":"http://en.wikipedia.org/wiki/Alfa_Romeo_in_Formula_One","name":"Alfa Romeo","nationality":"Swiss"},"Q1":"1:19.523","Q2":"1:18.762","Q3":"1:18.401"},{"number":"16","position":"7","Driver":{"driverId":"leclerc","permanentNumber":"16","code":"LEC","url":"http://en.wikipedia.org/wiki/Charles_Leclerc","givenName":"Charles","familyName":"Leclerc","dateOfBirth":"1997-10-16","nationality":"Monegasque"},"Constructor":{"constructorId":"ferrari","url":"http://en.wikipedia.org/wiki/Scuderia_Ferrari","name":"Ferrari","nationality":"Italian"},"Q1":"1:19.505","Q2":"1:19.109","Q3":"1:18.555"},{"number":"4","position":"8","Driver":{"driverId":"norris","permanentNumber":"4","code":"NOR","url":"http://en.wikipedia.org/wiki/Lando_Norris","givenName":"Lando","familyName":"Norris","dateOfBirth":"1999-11-13","nationality":"British"},"Constructor":{"constructorId":"mclaren","url":"http://en.wikipedia.org/wiki/McLaren","name":"McLaren","nationality":"British"},"Q1":"1:19.857","Q2":"1:19.119","Q3":"1:18.721"},{"number":"14","position":"9","Driver":{"driverId":"alonso","permanentNumber":"14","code":"ALO","url":"http://en.wikipedia.org/wiki/Fernando_Alonso","givenName":"Fernando","familyName":"Alonso","dateOfBirth":"1981-07-29","nationality":"Spanish"},"Constructor":{"constructorId":"alpine","url":"http://en.wikipedia.org/wiki/Alpine_F1_Team","name":"Alpine F1 Team","nationality":"French"},"Q1":"1:20.006","Q2":"1:19.272","Q3":"1:18.939"},{"number":"31","position":"10","Driver":{"driverId":"ocon","permanentNumber":"31","code":"OCO","url":"http://en.wikipedia.org/wiki/Esteban_Ocon","givenName":"Esteban","familyName":"Ocon","dateOfBirth":"1996-09-17","nationality":"French"},"Constructor":{"constructorId":"alpine","url":"http://en.wikipedia.org/wiki/Alpine_F1_Team","name":"Alpine F1 Team","nationality":"French"},"Q1":"1:19.945","Q2":"1:19.081","Q3":"1:19.010"},{"number":"3","position":"11","Driver":{"driverId":"ricciardo","permanentNumber":"3","code":"RIC","url":"http://en.wikipedia.org/wiki/Daniel_Ricciardo","givenName":"Daniel","familyName":"Ricciardo","dateOfBirth":"1989-07-01","nationality":"Australian"},"Constructor":{"constructorId":"mclaren","url":"http://en.wikipedia.org/wiki/McLaren","name":"McLaren","nationality":"British"},"Q1":"1:20.279","Q2":"1:19.325"},{"number":"24","position":"12","Driver":{"driverId":"zhou","permanentNumber":"24","code":"ZHO","url":"http://en.wikipedia.org/wiki/Guanyu_Zhou","givenName":"Guanyu","familyName":"Zhou","dateOfBirth":"1999-05-30","nationality":"Chinese"},"Constructor":{"constructorId":"alfa","url":"http://en.wikipedia.org/wiki/Alfa_Romeo_in_Formula_One","name":"Alfa Romeo","nationality":"Swiss"},"Q1":"1:20.283","Q2":"1:19.476"},{"number":"22","position":"13","Driver":{"driverId":"tsunoda","permanentNumber":"22","code":"TSU","url":"http://en.wikipedia.org/wiki/Yuki_Tsunoda","givenName":"Yuki","familyName":"Tsunoda","dateOfBirth":"2000-05-11","nationality":"Japanese"},"Constructor":{"constructorId":"alphatauri","url":"http://en.wikipedia.org/wiki/Scuderia_AlphaTauri","name":"AlphaTauri","nationality":"Italian"},"Q1":"1:19.907","Q2":"1:19.589"},{"number":"10","position":"14","Driver":{"driverId":"gasly","permanentNumber":"10","code":"GAS","url":"http://en.wikipedia.org/wiki/Pierre_Gasly","givenName":"Pierre","familyName":"Gasly","dateOfBirth":"1996-02-07","nationality":"French"},"Constructor":{"constructorId":"alphatauri","url":"http://en.wikipedia.org/wiki/Scuderia_AlphaTauri","name":"AlphaTauri","nationality":"Italian"},"Q1":"1:20.256","Q2":"1:19.672"},{"number":"20","position":"15","Driver":{"driverId":"kevin_magnussen","permanentNumber":"20","code":"MAG","url":"http://en.wikipedia.org/wiki/Kevin_Magnussen","givenName":"Kevin","familyName":"Magnussen","dateOfBirth":"1992-10-05","nationality":"Danish"},"Constructor":{"constructorId":"haas","url":"http://en.wikipedia.org/wiki/Haas_F1_Team","name":"Haas F1 Team","nationality":"American"},"Q1":"1:20.293","Q2":"1:19.833"},{"number":"47","position":"16","Driver":{"driverId":"mick_schumacher","permanentNumber":"47","code":"MSC","url":"http://en.wikipedia.org/wiki/Mick_Schumacher","givenName":"Mick","familyName":"Schumacher","dateOfBirth":"1999-03-22","nationality":"German"},"Constructor":{"constructorId":"haas","url":"http://en.wikipedia.org/wiki/Haas_F1_Team","name":"Haas F1 Team","nationality":"American"},"Q1":"1:20.419"},{"number":"5","position":"17","Driver":{"driverId":"vettel","permanentNumber":"5","code":"VET","url":"http://en.wikipedia.org/wiki/Sebastian_Vettel","givenName":"Sebastian","familyName":"Vettel","dateOfBirth":"1987-07-03","nationality":"German"},"Constructor":{"constructorId":"aston_martin","url":"http://en.wikipedia.org/wiki/Aston_Martin_in_Formula_One","name":"Aston Martin","nationality":"British"},"Q1":"1:20.419"},{"number":"18","position":"18","Driver":{"driverId":"stroll","permanentNumber":"18","code":"STR","url":"http://en.wikipedia.org/wiki/Lance_Stroll","givenName":"Lance","familyName":"Stroll","dateOfBirth":"1998-10-29","nationality":"Canadian"},"Constructor":{"constructorId":"aston_martin","url":"http://en.wikipedia.org/wiki/Aston_Martin_in_Formula_One","name":"Aston Martin","nationality":"British"},"Q1":"1:20.520"},{"number":"23","position":"19","Driver":{"driverId":"albon","permanentNumber":"23","code":"ALB","url":"http://en.wikipedia.org/wiki/Alexander_Albon","givenName":"Alexander","familyName":"Albon","dateOfBirth":"1996-03-23","nationality":"Thai"},"Constructor":{"constructorId":"williams","url":"http://en.wikipedia.org/wiki/Williams_Grand_Prix_Engineering","name":"Williams","nationality":"British"},"Q1":"1:20.859"},{"number":"6","position":"20","Driver":{"driverId":"latifi","permanentNumber":"6","code":"LAT","url":"http://en.wikipedia.org/wiki/Nicholas_Latifi","givenName":"Nicholas","familyName":"Latifi","dateOfBirth":"1995-06-29","nationality":"Canadian"},"Constructor":{"constructorId":"williams","url":"http://en.wikipedia.org/wiki/Williams_Grand_Prix_Engineering","name":"Williams","nationality":"British"},"Q1":"1:21.167"}]}]}}} ================================================ FILE: tests/testdata/results.json ================================================ {"MRData":{"xmlns":"http:\/\/ergast.com\/mrd\/1.5","series":"f1","url":"http://ergast.com/api/f1/current/last/results.json","limit":"30","offset":"0","total":"20","RaceTable":{"season":"2022","round":"17","Races":[{"season":"2022","round":"17","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Singapore_Grand_Prix","raceName":"Singapore Grand Prix","Circuit":{"circuitId":"marina_bay","url":"http://en.wikipedia.org/wiki/Marina_Bay_Street_Circuit","circuitName":"Marina Bay Street Circuit","Location":{"lat":"1.2914","long":"103.864","locality":"Marina Bay","country":"Singapore"}},"date":"2022-10-02","time":"12:00:00Z","Results":[{"number":"11","position":"1","positionText":"1","points":"25","Driver":{"driverId":"perez","permanentNumber":"11","code":"PER","url":"http:\/\/en.wikipedia.org\/wiki\/Sergio_P%C3%A9rez","givenName":"Sergio","familyName":"Pérez","dateOfBirth":"1990-01-26","nationality":"Mexican"},"Constructor":{"constructorId":"red_bull","url":"http:\/\/en.wikipedia.org\/wiki\/Red_Bull_Racing","name":"Red Bull","nationality":"Austrian"},"grid":"2","laps":"59","status":"Finished","Time":{"millis":"7340238","time":"2:02:20.238"},"FastestLap":{"rank":"2","lap":"57","Time":{"time":"1:48.165"},"AverageSpeed":{"units":"kph","speed":"168.509"}}},{"number":"16","position":"2","positionText":"2","points":"18","Driver":{"driverId":"leclerc","permanentNumber":"16","code":"LEC","url":"http:\/\/en.wikipedia.org\/wiki\/Charles_Leclerc","givenName":"Charles","familyName":"Leclerc","dateOfBirth":"1997-10-16","nationality":"Monegasque"},"Constructor":{"constructorId":"ferrari","url":"http:\/\/en.wikipedia.org\/wiki\/Scuderia_Ferrari","name":"Ferrari","nationality":"Italian"},"grid":"1","laps":"59","status":"Finished","Time":{"millis":"7342833","time":"+2.595"},"FastestLap":{"rank":"4","lap":"54","Time":{"time":"1:48.753"},"AverageSpeed":{"units":"kph","speed":"167.598"}}},{"number":"55","position":"3","positionText":"3","points":"15","Driver":{"driverId":"sainz","permanentNumber":"55","code":"SAI","url":"http:\/\/en.wikipedia.org\/wiki\/Carlos_Sainz_Jr.","givenName":"Carlos","familyName":"Sainz","dateOfBirth":"1994-09-01","nationality":"Spanish"},"Constructor":{"constructorId":"ferrari","url":"http:\/\/en.wikipedia.org\/wiki\/Scuderia_Ferrari","name":"Ferrari","nationality":"Italian"},"grid":"4","laps":"59","status":"Finished","Time":{"millis":"7350543","time":"+10.305"},"FastestLap":{"rank":"3","lap":"59","Time":{"time":"1:48.414"},"AverageSpeed":{"units":"kph","speed":"168.122"}}},{"number":"4","position":"4","positionText":"4","points":"12","Driver":{"driverId":"norris","permanentNumber":"4","code":"NOR","url":"http:\/\/en.wikipedia.org\/wiki\/Lando_Norris","givenName":"Lando","familyName":"Norris","dateOfBirth":"1999-11-13","nationality":"British"},"Constructor":{"constructorId":"mclaren","url":"http:\/\/en.wikipedia.org\/wiki\/McLaren","name":"McLaren","nationality":"British"},"grid":"6","laps":"59","status":"Finished","Time":{"millis":"7361371","time":"+21.133"},"FastestLap":{"rank":"6","lap":"56","Time":{"time":"1:49.212"},"AverageSpeed":{"units":"kph","speed":"166.893"}}},{"number":"3","position":"5","positionText":"5","points":"10","Driver":{"driverId":"ricciardo","permanentNumber":"3","code":"RIC","url":"http:\/\/en.wikipedia.org\/wiki\/Daniel_Ricciardo","givenName":"Daniel","familyName":"Ricciardo","dateOfBirth":"1989-07-01","nationality":"Australian"},"Constructor":{"constructorId":"mclaren","url":"http:\/\/en.wikipedia.org\/wiki\/McLaren","name":"McLaren","nationality":"British"},"grid":"16","laps":"59","status":"Finished","Time":{"millis":"7393520","time":"+53.282"},"FastestLap":{"rank":"12","lap":"57","Time":{"time":"1:51.006"},"AverageSpeed":{"units":"kph","speed":"164.196"}}},{"number":"18","position":"6","positionText":"6","points":"8","Driver":{"driverId":"stroll","permanentNumber":"18","code":"STR","url":"http:\/\/en.wikipedia.org\/wiki\/Lance_Stroll","givenName":"Lance","familyName":"Stroll","dateOfBirth":"1998-10-29","nationality":"Canadian"},"Constructor":{"constructorId":"aston_martin","url":"http:\/\/en.wikipedia.org\/wiki\/Aston_Martin_in_Formula_One","name":"Aston Martin","nationality":"British"},"grid":"11","laps":"59","status":"Finished","Time":{"millis":"7396568","time":"+56.330"},"FastestLap":{"rank":"7","lap":"58","Time":{"time":"1:50.283"},"AverageSpeed":{"units":"kph","speed":"165.272"}}},{"number":"1","position":"7","positionText":"7","points":"6","Driver":{"driverId":"max_verstappen","permanentNumber":"33","code":"VER","url":"http:\/\/en.wikipedia.org\/wiki\/Max_Verstappen","givenName":"Max","familyName":"Verstappen","dateOfBirth":"1997-09-30","nationality":"Dutch"},"Constructor":{"constructorId":"red_bull","url":"http:\/\/en.wikipedia.org\/wiki\/Red_Bull_Racing","name":"Red Bull","nationality":"Austrian"},"grid":"8","laps":"59","status":"Finished","Time":{"millis":"7399063","time":"+58.825"},"FastestLap":{"rank":"5","lap":"52","Time":{"time":"1:49.142"},"AverageSpeed":{"units":"kph","speed":"167.000"}}},{"number":"5","position":"8","positionText":"8","points":"4","Driver":{"driverId":"vettel","permanentNumber":"5","code":"VET","url":"http:\/\/en.wikipedia.org\/wiki\/Sebastian_Vettel","givenName":"Sebastian","familyName":"Vettel","dateOfBirth":"1987-07-03","nationality":"German"},"Constructor":{"constructorId":"aston_martin","url":"http:\/\/en.wikipedia.org\/wiki\/Aston_Martin_in_Formula_One","name":"Aston Martin","nationality":"British"},"grid":"13","laps":"59","status":"Finished","Time":{"millis":"7400270","time":"+1:00.032"},"FastestLap":{"rank":"11","lap":"58","Time":{"time":"1:50.669"},"AverageSpeed":{"units":"kph","speed":"164.696"}}},{"number":"44","position":"9","positionText":"9","points":"2","Driver":{"driverId":"hamilton","permanentNumber":"44","code":"HAM","url":"http:\/\/en.wikipedia.org\/wiki\/Lewis_Hamilton","givenName":"Lewis","familyName":"Hamilton","dateOfBirth":"1985-01-07","nationality":"British"},"Constructor":{"constructorId":"mercedes","url":"http:\/\/en.wikipedia.org\/wiki\/Mercedes-Benz_in_Formula_One","name":"Mercedes","nationality":"German"},"grid":"3","laps":"59","status":"Finished","Time":{"millis":"7401753","time":"+1:01.515"},"FastestLap":{"rank":"10","lap":"58","Time":{"time":"1:50.622"},"AverageSpeed":{"units":"kph","speed":"164.766"}}},{"number":"10","position":"10","positionText":"10","points":"1","Driver":{"driverId":"gasly","permanentNumber":"10","code":"GAS","url":"http:\/\/en.wikipedia.org\/wiki\/Pierre_Gasly","givenName":"Pierre","familyName":"Gasly","dateOfBirth":"1996-02-07","nationality":"French"},"Constructor":{"constructorId":"alphatauri","url":"http:\/\/en.wikipedia.org\/wiki\/Scuderia_AlphaTauri","name":"AlphaTauri","nationality":"Italian"},"grid":"7","laps":"59","status":"Finished","Time":{"millis":"7409814","time":"+1:09.576"},"FastestLap":{"rank":"9","lap":"56","Time":{"time":"1:50.569"},"AverageSpeed":{"units":"kph","speed":"164.845"}}},{"number":"77","position":"11","positionText":"11","points":"0","Driver":{"driverId":"bottas","permanentNumber":"77","code":"BOT","url":"http:\/\/en.wikipedia.org\/wiki\/Valtteri_Bottas","givenName":"Valtteri","familyName":"Bottas","dateOfBirth":"1989-08-28","nationality":"Finnish"},"Constructor":{"constructorId":"alfa","url":"http:\/\/en.wikipedia.org\/wiki\/Alfa_Romeo_in_Formula_One","name":"Alfa Romeo","nationality":"Swiss"},"grid":"15","laps":"59","status":"Finished","Time":{"millis":"7429082","time":"+1:28.844"},"FastestLap":{"rank":"13","lap":"52","Time":{"time":"1:51.864"},"AverageSpeed":{"units":"kph","speed":"162.937"}}},{"number":"20","position":"12","positionText":"12","points":"0","Driver":{"driverId":"kevin_magnussen","permanentNumber":"20","code":"MAG","url":"http:\/\/en.wikipedia.org\/wiki\/Kevin_Magnussen","givenName":"Kevin","familyName":"Magnussen","dateOfBirth":"1992-10-05","nationality":"Danish"},"Constructor":{"constructorId":"haas","url":"http:\/\/en.wikipedia.org\/wiki\/Haas_F1_Team","name":"Haas F1 Team","nationality":"American"},"grid":"9","laps":"59","status":"Finished","Time":{"millis":"7432848","time":"+1:32.610"},"FastestLap":{"rank":"14","lap":"59","Time":{"time":"1:52.067"},"AverageSpeed":{"units":"kph","speed":"162.641"}}},{"number":"47","position":"13","positionText":"13","points":"0","Driver":{"driverId":"mick_schumacher","permanentNumber":"47","code":"MSC","url":"http:\/\/en.wikipedia.org\/wiki\/Mick_Schumacher","givenName":"Mick","familyName":"Schumacher","dateOfBirth":"1999-03-22","nationality":"German"},"Constructor":{"constructorId":"haas","url":"http:\/\/en.wikipedia.org\/wiki\/Haas_F1_Team","name":"Haas F1 Team","nationality":"American"},"grid":"12","laps":"58","status":"+1 Lap","FastestLap":{"rank":"8","lap":"58","Time":{"time":"1:50.290"},"AverageSpeed":{"units":"kph","speed":"165.262"}}},{"number":"63","position":"14","positionText":"14","points":"0","Driver":{"driverId":"russell","permanentNumber":"63","code":"RUS","url":"http:\/\/en.wikipedia.org\/wiki\/George_Russell_%28racing_driver%29","givenName":"George","familyName":"Russell","dateOfBirth":"1998-02-15","nationality":"British"},"Constructor":{"constructorId":"mercedes","url":"http:\/\/en.wikipedia.org\/wiki\/Mercedes-Benz_in_Formula_One","name":"Mercedes","nationality":"German"},"grid":"0","laps":"57","status":"+2 Laps","FastestLap":{"rank":"1","lap":"54","Time":{"time":"1:46.458"},"AverageSpeed":{"units":"kph","speed":"171.211"}}},{"number":"22","position":"15","positionText":"R","points":"0","Driver":{"driverId":"tsunoda","permanentNumber":"22","code":"TSU","url":"http:\/\/en.wikipedia.org\/wiki\/Yuki_Tsunoda","givenName":"Yuki","familyName":"Tsunoda","dateOfBirth":"2000-05-11","nationality":"Japanese"},"Constructor":{"constructorId":"alphatauri","url":"http:\/\/en.wikipedia.org\/wiki\/Scuderia_AlphaTauri","name":"AlphaTauri","nationality":"Italian"},"grid":"10","laps":"34","status":"Accident","FastestLap":{"rank":"15","lap":"32","Time":{"time":"1:58.716"},"AverageSpeed":{"units":"kph","speed":"153.532"}}},{"number":"31","position":"16","positionText":"R","points":"0","Driver":{"driverId":"ocon","permanentNumber":"31","code":"OCO","url":"http:\/\/en.wikipedia.org\/wiki\/Esteban_Ocon","givenName":"Esteban","familyName":"Ocon","dateOfBirth":"1996-09-17","nationality":"French"},"Constructor":{"constructorId":"alpine","url":"http:\/\/en.wikipedia.org\/wiki\/Alpine_F1_Team","name":"Alpine F1 Team","nationality":"French"},"grid":"17","laps":"26","status":"Engine","FastestLap":{"rank":"17","lap":"24","Time":{"time":"2:01.105"},"AverageSpeed":{"units":"kph","speed":"150.504"}}},{"number":"23","position":"17","positionText":"R","points":"0","Driver":{"driverId":"albon","permanentNumber":"23","code":"ALB","url":"http:\/\/en.wikipedia.org\/wiki\/Alexander_Albon","givenName":"Alexander","familyName":"Albon","dateOfBirth":"1996-03-23","nationality":"Thai"},"Constructor":{"constructorId":"williams","url":"http:\/\/en.wikipedia.org\/wiki\/Williams_Grand_Prix_Engineering","name":"Williams","nationality":"British"},"grid":"18","laps":"25","status":"Collision damage","FastestLap":{"rank":"18","lap":"24","Time":{"time":"2:02.121"},"AverageSpeed":{"units":"kph","speed":"149.251"}}},{"number":"14","position":"18","positionText":"R","points":"0","Driver":{"driverId":"alonso","permanentNumber":"14","code":"ALO","url":"http:\/\/en.wikipedia.org\/wiki\/Fernando_Alonso","givenName":"Fernando","familyName":"Alonso","dateOfBirth":"1981-07-29","nationality":"Spanish"},"Constructor":{"constructorId":"alpine","url":"http:\/\/en.wikipedia.org\/wiki\/Alpine_F1_Team","name":"Alpine F1 Team","nationality":"French"},"grid":"5","laps":"20","status":"Engine","FastestLap":{"rank":"16","lap":"19","Time":{"time":"2:00.463"},"AverageSpeed":{"units":"kph","speed":"151.306"}}},{"number":"6","position":"19","positionText":"R","points":"0","Driver":{"driverId":"latifi","permanentNumber":"6","code":"LAT","url":"http:\/\/en.wikipedia.org\/wiki\/Nicholas_Latifi","givenName":"Nicholas","familyName":"Latifi","dateOfBirth":"1995-06-29","nationality":"Canadian"},"Constructor":{"constructorId":"williams","url":"http:\/\/en.wikipedia.org\/wiki\/Williams_Grand_Prix_Engineering","name":"Williams","nationality":"British"},"grid":"19","laps":"7","status":"Collision damage","FastestLap":{"rank":"20","lap":"5","Time":{"time":"2:05.585"},"AverageSpeed":{"units":"kph","speed":"145.135"}}},{"number":"24","position":"20","positionText":"R","points":"0","Driver":{"driverId":"zhou","permanentNumber":"24","code":"ZHO","url":"http:\/\/en.wikipedia.org\/wiki\/Guanyu_Zhou","givenName":"Guanyu","familyName":"Zhou","dateOfBirth":"1999-05-30","nationality":"Chinese"},"Constructor":{"constructorId":"alfa","url":"http:\/\/en.wikipedia.org\/wiki\/Alfa_Romeo_in_Formula_One","name":"Alfa Romeo","nationality":"Swiss"},"grid":"14","laps":"6","status":"Collision","FastestLap":{"rank":"19","lap":"5","Time":{"time":"2:05.556"},"AverageSpeed":{"units":"kph","speed":"145.168"}}}]}]}}} ================================================ FILE: tests/testdata/schedule.json ================================================ {"MRData":{"xmlns":"http:\/\/ergast.com\/mrd\/1.5","series":"f1","url":"http://ergast.com/api/f1/2022.json","limit":"30","offset":"0","total":"22","RaceTable":{"season":"2022","Races":[{"season":"2022","round":"1","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Bahrain_Grand_Prix","raceName":"Bahrain Grand Prix","Circuit":{"circuitId":"bahrain","url":"http:\/\/en.wikipedia.org\/wiki\/Bahrain_International_Circuit","circuitName":"Bahrain International Circuit","Location":{"lat":"26.0325","long":"50.5106","locality":"Sakhir","country":"Bahrain"}},"date":"2022-03-20","time":"15:00:00Z","FirstPractice":{"date":"2022-03-18","time":"12:00:00Z"},"SecondPractice":{"date":"2022-03-18","time":"15:00:00Z"},"ThirdPractice":{"date":"2022-03-19","time":"12:00:00Z"},"Qualifying":{"date":"2022-03-19","time":"15:00:00Z"}},{"season":"2022","round":"2","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Saudi_Arabian_Grand_Prix","raceName":"Saudi Arabian Grand Prix","Circuit":{"circuitId":"jeddah","url":"http:\/\/en.wikipedia.org\/wiki\/Jeddah_Street_Circuit","circuitName":"Jeddah Corniche Circuit","Location":{"lat":"21.6319","long":"39.1044","locality":"Jeddah","country":"Saudi Arabia"}},"date":"2022-03-27","time":"17:00:00Z","FirstPractice":{"date":"2022-03-25","time":"14:00:00Z"},"SecondPractice":{"date":"2022-03-25","time":"17:00:00Z"},"ThirdPractice":{"date":"2022-03-26","time":"14:00:00Z"},"Qualifying":{"date":"2022-03-26","time":"17:00:00Z"}},{"season":"2022","round":"3","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Australian_Grand_Prix","raceName":"Australian Grand Prix","Circuit":{"circuitId":"albert_park","url":"http:\/\/en.wikipedia.org\/wiki\/Melbourne_Grand_Prix_Circuit","circuitName":"Albert Park Grand Prix Circuit","Location":{"lat":"-37.8497","long":"144.968","locality":"Melbourne","country":"Australia"}},"date":"2022-04-10","time":"05:00:00Z","FirstPractice":{"date":"2022-04-08","time":"03:00:00Z"},"SecondPractice":{"date":"2022-04-08","time":"06:00:00Z"},"ThirdPractice":{"date":"2022-04-09","time":"03:00:00Z"},"Qualifying":{"date":"2022-04-09","time":"06:00:00Z"}},{"season":"2022","round":"4","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Emilia_Romagna_Grand_Prix","raceName":"Emilia Romagna Grand Prix","Circuit":{"circuitId":"imola","url":"http:\/\/en.wikipedia.org\/wiki\/Autodromo_Enzo_e_Dino_Ferrari","circuitName":"Autodromo Enzo e Dino Ferrari","Location":{"lat":"44.3439","long":"11.7167","locality":"Imola","country":"Italy"}},"date":"2022-04-24","time":"13:00:00Z","FirstPractice":{"date":"2022-04-22","time":"11:30:00Z"},"Qualifying":{"date":"2022-04-22","time":"15:00:00Z"},"SecondPractice":{"date":"2022-04-23","time":"10:30:00Z"},"Sprint":{"date":"2022-04-23","time":"14:30:00Z"}},{"season":"2022","round":"5","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Miami_Grand_Prix","raceName":"Miami Grand Prix","Circuit":{"circuitId":"miami","url":"http:\/\/en.wikipedia.org\/wiki\/Miami_International_Autodrome","circuitName":"Miami International Autodrome","Location":{"lat":"25.9581","long":"-80.2389","locality":"Miami","country":"USA"}},"date":"2022-05-08","time":"19:30:00Z","FirstPractice":{"date":"2022-05-06","time":"18:30:00Z"},"SecondPractice":{"date":"2022-05-06","time":"21:30:00Z"},"ThirdPractice":{"date":"2022-05-07","time":"17:00:00Z"},"Qualifying":{"date":"2022-05-07","time":"20:00:00Z"}},{"season":"2022","round":"6","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Spanish_Grand_Prix","raceName":"Spanish Grand Prix","Circuit":{"circuitId":"catalunya","url":"http:\/\/en.wikipedia.org\/wiki\/Circuit_de_Barcelona-Catalunya","circuitName":"Circuit de Barcelona-Catalunya","Location":{"lat":"41.57","long":"2.26111","locality":"Montmeló","country":"Spain"}},"date":"2022-05-22","time":"13:00:00Z","FirstPractice":{"date":"2022-05-20","time":"12:00:00Z"},"SecondPractice":{"date":"2022-05-20","time":"15:00:00Z"},"ThirdPractice":{"date":"2022-05-21","time":"11:00:00Z"},"Qualifying":{"date":"2022-05-21","time":"14:00:00Z"}},{"season":"2022","round":"7","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Monaco_Grand_Prix","raceName":"Monaco Grand Prix","Circuit":{"circuitId":"monaco","url":"http:\/\/en.wikipedia.org\/wiki\/Circuit_de_Monaco","circuitName":"Circuit de Monaco","Location":{"lat":"43.7347","long":"7.42056","locality":"Monte-Carlo","country":"Monaco"}},"date":"2022-05-29","time":"13:00:00Z","FirstPractice":{"date":"2022-05-27","time":"12:00:00Z"},"SecondPractice":{"date":"2022-05-27","time":"15:00:00Z"},"ThirdPractice":{"date":"2022-05-28","time":"11:00:00Z"},"Qualifying":{"date":"2022-05-28","time":"14:00:00Z"}},{"season":"2022","round":"8","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Azerbaijan_Grand_Prix","raceName":"Azerbaijan Grand Prix","Circuit":{"circuitId":"baku","url":"http:\/\/en.wikipedia.org\/wiki\/Baku_City_Circuit","circuitName":"Baku City Circuit","Location":{"lat":"40.3725","long":"49.8533","locality":"Baku","country":"Azerbaijan"}},"date":"2022-06-12","time":"11:00:00Z","FirstPractice":{"date":"2022-06-10","time":"11:00:00Z"},"SecondPractice":{"date":"2022-06-10","time":"14:00:00Z"},"ThirdPractice":{"date":"2022-06-11","time":"11:00:00Z"},"Qualifying":{"date":"2022-06-11","time":"14:00:00Z"}},{"season":"2022","round":"9","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Canadian_Grand_Prix","raceName":"Canadian Grand Prix","Circuit":{"circuitId":"villeneuve","url":"http:\/\/en.wikipedia.org\/wiki\/Circuit_Gilles_Villeneuve","circuitName":"Circuit Gilles Villeneuve","Location":{"lat":"45.5","long":"-73.5228","locality":"Montreal","country":"Canada"}},"date":"2022-06-19","time":"18:00:00Z","FirstPractice":{"date":"2022-06-17","time":"18:00:00Z"},"SecondPractice":{"date":"2022-06-17","time":"21:00:00Z"},"ThirdPractice":{"date":"2022-06-18","time":"17:00:00Z"},"Qualifying":{"date":"2022-06-18","time":"20:00:00Z"}},{"season":"2022","round":"10","url":"http:\/\/en.wikipedia.org\/wiki\/2022_British_Grand_Prix","raceName":"British Grand Prix","Circuit":{"circuitId":"silverstone","url":"http:\/\/en.wikipedia.org\/wiki\/Silverstone_Circuit","circuitName":"Silverstone Circuit","Location":{"lat":"52.0786","long":"-1.01694","locality":"Silverstone","country":"UK"}},"date":"2022-07-03","time":"14:00:00Z","FirstPractice":{"date":"2022-07-01","time":"12:00:00Z"},"SecondPractice":{"date":"2022-07-01","time":"15:00:00Z"},"ThirdPractice":{"date":"2022-07-02","time":"11:00:00Z"},"Qualifying":{"date":"2022-07-02","time":"14:00:00Z"}},{"season":"2022","round":"11","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Austrian_Grand_Prix","raceName":"Austrian Grand Prix","Circuit":{"circuitId":"red_bull_ring","url":"http:\/\/en.wikipedia.org\/wiki\/Red_Bull_Ring","circuitName":"Red Bull Ring","Location":{"lat":"47.2197","long":"14.7647","locality":"Spielberg","country":"Austria"}},"date":"2022-07-10","time":"13:00:00Z","FirstPractice":{"date":"2022-07-08","time":"11:30:00Z"},"Qualifying":{"date":"2022-07-08","time":"15:00:00Z"},"SecondPractice":{"date":"2022-07-09","time":"10:30:00Z"},"Sprint":{"date":"2022-07-09","time":"14:30:00Z"}},{"season":"2022","round":"12","url":"http:\/\/en.wikipedia.org\/wiki\/2022_French_Grand_Prix","raceName":"French Grand Prix","Circuit":{"circuitId":"ricard","url":"http:\/\/en.wikipedia.org\/wiki\/Paul_Ricard_Circuit","circuitName":"Circuit Paul Ricard","Location":{"lat":"43.2506","long":"5.79167","locality":"Le Castellet","country":"France"}},"date":"2022-07-24","time":"13:00:00Z","FirstPractice":{"date":"2022-07-22","time":"12:00:00Z"},"SecondPractice":{"date":"2022-07-22","time":"15:00:00Z"},"ThirdPractice":{"date":"2022-07-23","time":"11:00:00Z"},"Qualifying":{"date":"2022-07-23","time":"14:00:00Z"}},{"season":"2022","round":"13","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Hungarian_Grand_Prix","raceName":"Hungarian Grand Prix","Circuit":{"circuitId":"hungaroring","url":"http:\/\/en.wikipedia.org\/wiki\/Hungaroring","circuitName":"Hungaroring","Location":{"lat":"47.5789","long":"19.2486","locality":"Budapest","country":"Hungary"}},"date":"2022-07-31","time":"13:00:00Z","FirstPractice":{"date":"2022-07-29","time":"12:00:00Z"},"SecondPractice":{"date":"2022-07-29","time":"15:00:00Z"},"ThirdPractice":{"date":"2022-07-30","time":"11:00:00Z"},"Qualifying":{"date":"2022-07-30","time":"14:00:00Z"}},{"season":"2022","round":"14","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Belgian_Grand_Prix","raceName":"Belgian Grand Prix","Circuit":{"circuitId":"spa","url":"http:\/\/en.wikipedia.org\/wiki\/Circuit_de_Spa-Francorchamps","circuitName":"Circuit de Spa-Francorchamps","Location":{"lat":"50.4372","long":"5.97139","locality":"Spa","country":"Belgium"}},"date":"2022-08-28","time":"13:00:00Z","FirstPractice":{"date":"2022-08-26","time":"12:00:00Z"},"SecondPractice":{"date":"2022-08-26","time":"15:00:00Z"},"ThirdPractice":{"date":"2022-08-27","time":"11:00:00Z"},"Qualifying":{"date":"2022-08-27","time":"14:00:00Z"}},{"season":"2022","round":"15","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Dutch_Grand_Prix","raceName":"Dutch Grand Prix","Circuit":{"circuitId":"zandvoort","url":"http:\/\/en.wikipedia.org\/wiki\/Circuit_Zandvoort","circuitName":"Circuit Park Zandvoort","Location":{"lat":"52.3888","long":"4.54092","locality":"Zandvoort","country":"Netherlands"}},"date":"2022-09-04","time":"13:00:00Z","FirstPractice":{"date":"2022-09-02","time":"10:30:00Z"},"SecondPractice":{"date":"2022-09-02","time":"14:00:00Z"},"ThirdPractice":{"date":"2022-09-03","time":"10:00:00Z"},"Qualifying":{"date":"2022-09-03","time":"13:00:00Z"}},{"season":"2022","round":"16","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Italian_Grand_Prix","raceName":"Italian Grand Prix","Circuit":{"circuitId":"monza","url":"http:\/\/en.wikipedia.org\/wiki\/Autodromo_Nazionale_Monza","circuitName":"Autodromo Nazionale di Monza","Location":{"lat":"45.6156","long":"9.28111","locality":"Monza","country":"Italy"}},"date":"2022-09-11","time":"13:00:00Z","FirstPractice":{"date":"2022-09-09","time":"12:00:00Z"},"SecondPractice":{"date":"2022-09-09","time":"15:00:00Z"},"ThirdPractice":{"date":"2022-09-10","time":"11:00:00Z"},"Qualifying":{"date":"2022-09-10","time":"14:00:00Z"}},{"season":"2022","round":"17","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Singapore_Grand_Prix","raceName":"Singapore Grand Prix","Circuit":{"circuitId":"marina_bay","url":"http:\/\/en.wikipedia.org\/wiki\/Marina_Bay_Street_Circuit","circuitName":"Marina Bay Street Circuit","Location":{"lat":"1.2914","long":"103.864","locality":"Marina Bay","country":"Singapore"}},"date":"2022-10-02","time":"12:00:00Z","FirstPractice":{"date":"2022-09-30","time":"10:00:00Z"},"SecondPractice":{"date":"2022-09-30","time":"13:00:00Z"},"ThirdPractice":{"date":"2022-10-01","time":"10:00:00Z"},"Qualifying":{"date":"2022-10-01","time":"13:00:00Z"}},{"season":"2022","round":"18","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Japanese_Grand_Prix","raceName":"Japanese Grand Prix","Circuit":{"circuitId":"suzuka","url":"http:\/\/en.wikipedia.org\/wiki\/Suzuka_Circuit","circuitName":"Suzuka Circuit","Location":{"lat":"34.8431","long":"136.541","locality":"Suzuka","country":"Japan"}},"date":"2022-10-09","time":"05:00:00Z","FirstPractice":{"date":"2022-10-07","time":"03:00:00Z"},"SecondPractice":{"date":"2022-10-07","time":"06:00:00Z"},"ThirdPractice":{"date":"2022-10-08","time":"03:00:00Z"},"Qualifying":{"date":"2022-10-08","time":"06:00:00Z"}},{"season":"2022","round":"19","url":"http:\/\/en.wikipedia.org\/wiki\/2022_United_States_Grand_Prix","raceName":"United States Grand Prix","Circuit":{"circuitId":"americas","url":"http:\/\/en.wikipedia.org\/wiki\/Circuit_of_the_Americas","circuitName":"Circuit of the Americas","Location":{"lat":"30.1328","long":"-97.6411","locality":"Austin","country":"USA"}},"date":"2022-10-23","time":"19:00:00Z","FirstPractice":{"date":"2022-10-21","time":"19:00:00Z"},"SecondPractice":{"date":"2022-10-21","time":"22:00:00Z"},"ThirdPractice":{"date":"2022-10-22","time":"19:00:00Z"},"Qualifying":{"date":"2022-10-22","time":"22:00:00Z"}},{"season":"2022","round":"20","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Mexican_Grand_Prix","raceName":"Mexico City Grand Prix","Circuit":{"circuitId":"rodriguez","url":"http:\/\/en.wikipedia.org\/wiki\/Aut%C3%B3dromo_Hermanos_Rodr%C3%ADguez","circuitName":"Autódromo Hermanos Rodríguez","Location":{"lat":"19.4042","long":"-99.0907","locality":"Mexico City","country":"Mexico"}},"date":"2022-10-30","time":"20:00:00Z","FirstPractice":{"date":"2022-10-28","time":"18:00:00Z"},"SecondPractice":{"date":"2022-10-28","time":"21:00:00Z"},"ThirdPractice":{"date":"2022-10-29","time":"17:00:00Z"},"Qualifying":{"date":"2022-10-29","time":"20:00:00Z"}},{"season":"2022","round":"21","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Brazilian_Grand_Prix","raceName":"Brazilian Grand Prix","Circuit":{"circuitId":"interlagos","url":"http:\/\/en.wikipedia.org\/wiki\/Aut%C3%B3dromo_Jos%C3%A9_Carlos_Pace","circuitName":"Autódromo José Carlos Pace","Location":{"lat":"-23.7036","long":"-46.6997","locality":"São Paulo","country":"Brazil"}},"date":"2022-11-13","time":"18:00:00Z","FirstPractice":{"date":"2022-11-11","time":"15:30:00Z"},"Qualifying":{"date":"2022-11-11","time":"19:00:00Z"},"SecondPractice":{"date":"2022-11-12","time":"15:30:00Z"},"Sprint":{"date":"2022-11-12","time":"19:30:00Z"}},{"season":"2022","round":"22","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Abu_Dhabi_Grand_Prix","raceName":"Abu Dhabi Grand Prix","Circuit":{"circuitId":"yas_marina","url":"http:\/\/en.wikipedia.org\/wiki\/Yas_Marina_Circuit","circuitName":"Yas Marina Circuit","Location":{"lat":"24.4672","long":"54.6031","locality":"Abu Dhabi","country":"UAE"}},"date":"2022-11-20","time":"13:00:00Z","FirstPractice":{"date":"2022-11-18","time":"10:00:00Z"},"SecondPractice":{"date":"2022-11-18","time":"13:00:00Z"},"ThirdPractice":{"date":"2022-11-19","time":"11:00:00Z"},"Qualifying":{"date":"2022-11-19","time":"14:00:00Z"}}]}}} ================================================ FILE: tests/testdata/seasons.json ================================================ {"MRData":{"xmlns":"http:\/\/ergast.com\/mrd\/1.5","series":"f1","url":"http://ergast.com/api/f1/seasons.json","limit":"30","offset":"0","total":"74","SeasonTable":{"Seasons":[{"season":"1950","url":"http:\/\/en.wikipedia.org\/wiki\/1950_Formula_One_season"},{"season":"1951","url":"http:\/\/en.wikipedia.org\/wiki\/1951_Formula_One_season"},{"season":"1952","url":"http:\/\/en.wikipedia.org\/wiki\/1952_Formula_One_season"},{"season":"1953","url":"http:\/\/en.wikipedia.org\/wiki\/1953_Formula_One_season"},{"season":"1954","url":"http:\/\/en.wikipedia.org\/wiki\/1954_Formula_One_season"},{"season":"1955","url":"http:\/\/en.wikipedia.org\/wiki\/1955_Formula_One_season"},{"season":"1956","url":"http:\/\/en.wikipedia.org\/wiki\/1956_Formula_One_season"},{"season":"1957","url":"http:\/\/en.wikipedia.org\/wiki\/1957_Formula_One_season"},{"season":"1958","url":"http:\/\/en.wikipedia.org\/wiki\/1958_Formula_One_season"},{"season":"1959","url":"http:\/\/en.wikipedia.org\/wiki\/1959_Formula_One_season"},{"season":"1960","url":"http:\/\/en.wikipedia.org\/wiki\/1960_Formula_One_season"},{"season":"1961","url":"http:\/\/en.wikipedia.org\/wiki\/1961_Formula_One_season"},{"season":"1962","url":"http:\/\/en.wikipedia.org\/wiki\/1962_Formula_One_season"},{"season":"1963","url":"http:\/\/en.wikipedia.org\/wiki\/1963_Formula_One_season"},{"season":"1964","url":"http:\/\/en.wikipedia.org\/wiki\/1964_Formula_One_season"},{"season":"1965","url":"http:\/\/en.wikipedia.org\/wiki\/1965_Formula_One_season"},{"season":"1966","url":"http:\/\/en.wikipedia.org\/wiki\/1966_Formula_One_season"},{"season":"1967","url":"http:\/\/en.wikipedia.org\/wiki\/1967_Formula_One_season"},{"season":"1968","url":"http:\/\/en.wikipedia.org\/wiki\/1968_Formula_One_season"},{"season":"1969","url":"http:\/\/en.wikipedia.org\/wiki\/1969_Formula_One_season"},{"season":"1970","url":"http:\/\/en.wikipedia.org\/wiki\/1970_Formula_One_season"},{"season":"1971","url":"http:\/\/en.wikipedia.org\/wiki\/1971_Formula_One_season"},{"season":"1972","url":"http:\/\/en.wikipedia.org\/wiki\/1972_Formula_One_season"},{"season":"1973","url":"http:\/\/en.wikipedia.org\/wiki\/1973_Formula_One_season"},{"season":"1974","url":"http:\/\/en.wikipedia.org\/wiki\/1974_Formula_One_season"},{"season":"1975","url":"http:\/\/en.wikipedia.org\/wiki\/1975_Formula_One_season"},{"season":"1976","url":"http:\/\/en.wikipedia.org\/wiki\/1976_Formula_One_season"},{"season":"1977","url":"http:\/\/en.wikipedia.org\/wiki\/1977_Formula_One_season"},{"season":"1978","url":"http:\/\/en.wikipedia.org\/wiki\/1978_Formula_One_season"},{"season":"1979","url":"http:\/\/en.wikipedia.org\/wiki\/1979_Formula_One_season"}]}}} ================================================ FILE: tests/testdata/sprint.json ================================================ {"MRData":{"xmlns":"http:\/\/ergast.com\/mrd\/1.5","series":"f1","url":"http://ergast.com/api/f1/2022/4/sprint.json","limit":"30","offset":"0","total":"20","RaceTable":{"season":"2022","round":"4","Races":[{"season":"2022","round":"4","url":"http:\/\/en.wikipedia.org\/wiki\/2022_Emilia_Romagna_Grand_Prix","raceName":"Emilia Romagna Grand Prix","Circuit":{"circuitId":"imola","url":"http://en.wikipedia.org/wiki/Autodromo_Enzo_e_Dino_Ferrari","circuitName":"Autodromo Enzo e Dino Ferrari","Location":{"lat":"44.3439","long":"11.7167","locality":"Imola","country":"Italy"}},"date":"2022-04-24","time":"13:00:00Z","SprintResults":[{"number":"1","position":"1","positionText":"1","points":"8","Driver":{"driverId":"max_verstappen","permanentNumber":"33","code":"VER","url":"http:\/\/en.wikipedia.org\/wiki\/Max_Verstappen","givenName":"Max","familyName":"Verstappen","dateOfBirth":"1997-09-30","nationality":"Dutch"},"Constructor":{"constructorId":"red_bull","url":"http:\/\/en.wikipedia.org\/wiki\/Red_Bull_Racing","name":"Red Bull","nationality":"Austrian"},"grid":"1","laps":"21","status":"Finished","Time":{"millis":"1839567","time":"30:39.567"},"FastestLap":{"lap":"14","Time":{"time":"1:19.154"}}},{"number":"16","position":"2","positionText":"2","points":"7","Driver":{"driverId":"leclerc","permanentNumber":"16","code":"LEC","url":"http:\/\/en.wikipedia.org\/wiki\/Charles_Leclerc","givenName":"Charles","familyName":"Leclerc","dateOfBirth":"1997-10-16","nationality":"Monegasque"},"Constructor":{"constructorId":"ferrari","url":"http:\/\/en.wikipedia.org\/wiki\/Scuderia_Ferrari","name":"Ferrari","nationality":"Italian"},"grid":"2","laps":"21","status":"Finished","Time":{"millis":"1842542","time":"+2.975"},"FastestLap":{"lap":"11","Time":{"time":"1:19.044"}}},{"number":"11","position":"3","positionText":"3","points":"6","Driver":{"driverId":"perez","permanentNumber":"11","code":"PER","url":"http:\/\/en.wikipedia.org\/wiki\/Sergio_P%C3%A9rez","givenName":"Sergio","familyName":"Pérez","dateOfBirth":"1990-01-26","nationality":"Mexican"},"Constructor":{"constructorId":"red_bull","url":"http:\/\/en.wikipedia.org\/wiki\/Red_Bull_Racing","name":"Red Bull","nationality":"Austrian"},"grid":"7","laps":"21","status":"Finished","Time":{"millis":"1844288","time":"+4.721"},"FastestLap":{"lap":"14","Time":{"time":"1:19.012"}}},{"number":"55","position":"4","positionText":"4","points":"5","Driver":{"driverId":"sainz","permanentNumber":"55","code":"SAI","url":"http:\/\/en.wikipedia.org\/wiki\/Carlos_Sainz_Jr.","givenName":"Carlos","familyName":"Sainz","dateOfBirth":"1994-09-01","nationality":"Spanish"},"Constructor":{"constructorId":"ferrari","url":"http:\/\/en.wikipedia.org\/wiki\/Scuderia_Ferrari","name":"Ferrari","nationality":"Italian"},"grid":"10","laps":"21","status":"Finished","Time":{"millis":"1857145","time":"+17.578"},"FastestLap":{"lap":"14","Time":{"time":"1:19.251"}}},{"number":"4","position":"5","positionText":"5","points":"4","Driver":{"driverId":"norris","permanentNumber":"4","code":"NOR","url":"http:\/\/en.wikipedia.org\/wiki\/Lando_Norris","givenName":"Lando","familyName":"Norris","dateOfBirth":"1999-11-13","nationality":"British"},"Constructor":{"constructorId":"mclaren","url":"http:\/\/en.wikipedia.org\/wiki\/McLaren","name":"McLaren","nationality":"British"},"grid":"3","laps":"21","status":"Finished","Time":{"millis":"1864128","time":"+24.561"},"FastestLap":{"lap":"9","Time":{"time":"1:20.030"}}},{"number":"3","position":"6","positionText":"6","points":"3","Driver":{"driverId":"ricciardo","permanentNumber":"3","code":"RIC","url":"http:\/\/en.wikipedia.org\/wiki\/Daniel_Ricciardo","givenName":"Daniel","familyName":"Ricciardo","dateOfBirth":"1989-07-01","nationality":"Australian"},"Constructor":{"constructorId":"mclaren","url":"http:\/\/en.wikipedia.org\/wiki\/McLaren","name":"McLaren","nationality":"British"},"grid":"6","laps":"21","status":"Finished","Time":{"millis":"1867307","time":"+27.740"},"FastestLap":{"lap":"12","Time":{"time":"1:20.328"}}},{"number":"77","position":"7","positionText":"7","points":"2","Driver":{"driverId":"bottas","permanentNumber":"77","code":"BOT","url":"http:\/\/en.wikipedia.org\/wiki\/Valtteri_Bottas","givenName":"Valtteri","familyName":"Bottas","dateOfBirth":"1989-08-28","nationality":"Finnish"},"Constructor":{"constructorId":"alfa","url":"http:\/\/en.wikipedia.org\/wiki\/Alfa_Romeo_in_Formula_One","name":"Alfa Romeo","nationality":"Swiss"},"grid":"8","laps":"21","status":"Finished","Time":{"millis":"1867700","time":"+28.133"},"FastestLap":{"lap":"9","Time":{"time":"1:20.219"}}},{"number":"20","position":"8","positionText":"8","points":"1","Driver":{"driverId":"kevin_magnussen","permanentNumber":"20","code":"MAG","url":"http:\/\/en.wikipedia.org\/wiki\/Kevin_Magnussen","givenName":"Kevin","familyName":"Magnussen","dateOfBirth":"1992-10-05","nationality":"Danish"},"Constructor":{"constructorId":"haas","url":"http:\/\/en.wikipedia.org\/wiki\/Haas_F1_Team","name":"Haas F1 Team","nationality":"American"},"grid":"4","laps":"21","status":"Finished","Time":{"millis":"1870279","time":"+30.712"},"FastestLap":{"lap":"6","Time":{"time":"1:20.557"}}},{"number":"14","position":"9","positionText":"9","points":"0","Driver":{"driverId":"alonso","permanentNumber":"14","code":"ALO","url":"http:\/\/en.wikipedia.org\/wiki\/Fernando_Alonso","givenName":"Fernando","familyName":"Alonso","dateOfBirth":"1981-07-29","nationality":"Spanish"},"Constructor":{"constructorId":"alpine","url":"http:\/\/en.wikipedia.org\/wiki\/Alpine_F1_Team","name":"Alpine F1 Team","nationality":"French"},"grid":"5","laps":"21","status":"Finished","Time":{"millis":"1871845","time":"+32.278"},"FastestLap":{"lap":"10","Time":{"time":"1:20.639"}}},{"number":"47","position":"10","positionText":"10","points":"0","Driver":{"driverId":"mick_schumacher","permanentNumber":"47","code":"MSC","url":"http:\/\/en.wikipedia.org\/wiki\/Mick_Schumacher","givenName":"Mick","familyName":"Schumacher","dateOfBirth":"1999-03-22","nationality":"German"},"Constructor":{"constructorId":"haas","url":"http:\/\/en.wikipedia.org\/wiki\/Haas_F1_Team","name":"Haas F1 Team","nationality":"American"},"grid":"12","laps":"21","status":"Finished","Time":{"millis":"1873340","time":"+33.773"},"FastestLap":{"lap":"15","Time":{"time":"1:20.567"}}},{"number":"63","position":"11","positionText":"11","points":"0","Driver":{"driverId":"russell","permanentNumber":"63","code":"RUS","url":"http:\/\/en.wikipedia.org\/wiki\/George_Russell_%28racing_driver%29","givenName":"George","familyName":"Russell","dateOfBirth":"1998-02-15","nationality":"British"},"Constructor":{"constructorId":"mercedes","url":"http:\/\/en.wikipedia.org\/wiki\/Mercedes-Benz_in_Formula_One","name":"Mercedes","nationality":"German"},"grid":"11","laps":"21","status":"Finished","Time":{"millis":"1875851","time":"+36.284"},"FastestLap":{"lap":"19","Time":{"time":"1:20.756"}}},{"number":"22","position":"12","positionText":"12","points":"0","Driver":{"driverId":"tsunoda","permanentNumber":"22","code":"TSU","url":"http:\/\/en.wikipedia.org\/wiki\/Yuki_Tsunoda","givenName":"Yuki","familyName":"Tsunoda","dateOfBirth":"2000-05-11","nationality":"Japanese"},"Constructor":{"constructorId":"alphatauri","url":"http:\/\/en.wikipedia.org\/wiki\/Scuderia_AlphaTauri","name":"AlphaTauri","nationality":"Italian"},"grid":"16","laps":"21","status":"Finished","Time":{"millis":"1877865","time":"+38.298"},"FastestLap":{"lap":"20","Time":{"time":"1:20.909"}}},{"number":"5","position":"13","positionText":"13","points":"0","Driver":{"driverId":"vettel","permanentNumber":"5","code":"VET","url":"http:\/\/en.wikipedia.org\/wiki\/Sebastian_Vettel","givenName":"Sebastian","familyName":"Vettel","dateOfBirth":"1987-07-03","nationality":"German"},"Constructor":{"constructorId":"aston_martin","url":"http:\/\/en.wikipedia.org\/wiki\/Aston_Martin_in_Formula_One","name":"Aston Martin","nationality":"British"},"grid":"9","laps":"21","status":"Finished","Time":{"millis":"1879744","time":"+40.177"},"FastestLap":{"lap":"8","Time":{"time":"1:21.044"}}},{"number":"44","position":"14","positionText":"14","points":"0","Driver":{"driverId":"hamilton","permanentNumber":"44","code":"HAM","url":"http:\/\/en.wikipedia.org\/wiki\/Lewis_Hamilton","givenName":"Lewis","familyName":"Hamilton","dateOfBirth":"1985-01-07","nationality":"British"},"Constructor":{"constructorId":"mercedes","url":"http:\/\/en.wikipedia.org\/wiki\/Mercedes-Benz_in_Formula_One","name":"Mercedes","nationality":"German"},"grid":"13","laps":"21","status":"Finished","Time":{"millis":"1881026","time":"+41.459"},"FastestLap":{"lap":"9","Time":{"time":"1:20.663"}}},{"number":"18","position":"15","positionText":"15","points":"0","Driver":{"driverId":"stroll","permanentNumber":"18","code":"STR","url":"http:\/\/en.wikipedia.org\/wiki\/Lance_Stroll","givenName":"Lance","familyName":"Stroll","dateOfBirth":"1998-10-29","nationality":"Canadian"},"Constructor":{"constructorId":"aston_martin","url":"http:\/\/en.wikipedia.org\/wiki\/Aston_Martin_in_Formula_One","name":"Aston Martin","nationality":"British"},"grid":"15","laps":"21","status":"Finished","Time":{"millis":"1882477","time":"+42.910"},"FastestLap":{"lap":"14","Time":{"time":"1:20.948"}}},{"number":"31","position":"16","positionText":"16","points":"0","Driver":{"driverId":"ocon","permanentNumber":"31","code":"OCO","url":"http:\/\/en.wikipedia.org\/wiki\/Esteban_Ocon","givenName":"Esteban","familyName":"Ocon","dateOfBirth":"1996-09-17","nationality":"French"},"Constructor":{"constructorId":"alpine","url":"http:\/\/en.wikipedia.org\/wiki\/Alpine_F1_Team","name":"Alpine F1 Team","nationality":"French"},"grid":"19","laps":"21","status":"Finished","Time":{"millis":"1883084","time":"+43.517"},"FastestLap":{"lap":"14","Time":{"time":"1:20.995"}}},{"number":"10","position":"17","positionText":"17","points":"0","Driver":{"driverId":"gasly","permanentNumber":"10","code":"GAS","url":"http:\/\/en.wikipedia.org\/wiki\/Pierre_Gasly","givenName":"Pierre","familyName":"Gasly","dateOfBirth":"1996-02-07","nationality":"French"},"Constructor":{"constructorId":"alphatauri","url":"http:\/\/en.wikipedia.org\/wiki\/Scuderia_AlphaTauri","name":"AlphaTauri","nationality":"Italian"},"grid":"17","laps":"21","status":"Finished","Time":{"millis":"1883361","time":"+43.794"},"FastestLap":{"lap":"18","Time":{"time":"1:20.599"}}},{"number":"23","position":"18","positionText":"18","points":"0","Driver":{"driverId":"albon","permanentNumber":"23","code":"ALB","url":"http:\/\/en.wikipedia.org\/wiki\/Alexander_Albon","givenName":"Alexander","familyName":"Albon","dateOfBirth":"1996-03-23","nationality":"Thai"},"Constructor":{"constructorId":"williams","url":"http:\/\/en.wikipedia.org\/wiki\/Williams_Grand_Prix_Engineering","name":"Williams","nationality":"British"},"grid":"20","laps":"21","status":"Finished","Time":{"millis":"1888438","time":"+48.871"},"FastestLap":{"lap":"14","Time":{"time":"1:21.020"}}},{"number":"6","position":"19","positionText":"19","points":"0","Driver":{"driverId":"latifi","permanentNumber":"6","code":"LAT","url":"http:\/\/en.wikipedia.org\/wiki\/Nicholas_Latifi","givenName":"Nicholas","familyName":"Latifi","dateOfBirth":"1995-06-29","nationality":"Canadian"},"Constructor":{"constructorId":"williams","url":"http:\/\/en.wikipedia.org\/wiki\/Williams_Grand_Prix_Engineering","name":"Williams","nationality":"British"},"grid":"18","laps":"21","status":"Finished","Time":{"millis":"1891584","time":"+52.017"},"FastestLap":{"lap":"16","Time":{"time":"1:21.437"}}},{"number":"24","position":"20","positionText":"R","points":"0","Driver":{"driverId":"zhou","permanentNumber":"24","code":"ZHO","url":"http:\/\/en.wikipedia.org\/wiki\/Guanyu_Zhou","givenName":"Guanyu","familyName":"Zhou","dateOfBirth":"1999-05-30","nationality":"Chinese"},"Constructor":{"constructorId":"alfa","url":"http:\/\/en.wikipedia.org\/wiki\/Alfa_Romeo_in_Formula_One","name":"Alfa Romeo","nationality":"Swiss"},"grid":"0","laps":"0","status":"Retired"}]}]}}} ================================================ FILE: tests/utils/calculateWindDirection.test.ts ================================================ import { calculateWindDirection } from "../../src/utils"; describe('Testing util file function calculateWindDirection ', () => { test.each` windDirection | expected ${0} | ${'N'} ${22.5} | ${'NNE'} ${45} | ${'NE'} ${67.5} | ${'ENE'} ${90} | ${'E'} ${112.5} | ${'ESE'} ${135} | ${'SE'} ${157.5} | ${'SSE'} ${180} | ${'S'} ${202.5} | ${'SSW'} ${225} | ${'SW'} ${247.5} | ${'WSW'} ${270} | ${'W'} ${292.5} | ${'WNW'} ${315} | ${'NW'} ${337.5} | ${'NNW'} ${360} | ${'N'} `( 'Passing $windDirection should return $expected', ({ windDirection, expected }) => { expect(calculateWindDirection(windDirection)).toBe(expected) }); }); ================================================ FILE: tests/utils/checkConfig.test.ts ================================================ import { checkConfig } from '../../src/utils'; import { FormulaOneCardConfig } from '../../src/types/formulaone-card-types'; describe('Testing util file function checkConfig', () => { test('Passing empty config should throw error', () => { const config: FormulaOneCardConfig = { type: '' } expect(() => checkConfig(config)).toThrowError('Please define FormulaOne card type (card_type).'); }) }) ================================================ FILE: tests/utils/getCircuitName.test.ts ================================================ import { getCircuitName } from '../../src/utils'; describe('Testing util file function getCircuitName', () => { test('Passing Japan should return expected circuit name', () => { expect(getCircuitName({ country: 'Japan', lat: '', long: '', locality: '' })).toBe('Japan') }), test('Passing UAE should return expected circuit name', () => { expect(getCircuitName({ country: 'UAE', lat: '', long: '', locality: '' })).toBe('Abu_Dhabi') }) }) ================================================ FILE: tests/utils/getCountryFlagUrl.test.ts ================================================ // Mocks import { createMock } from 'ts-auto-mock'; import { getCountryFlagByName } from '../../src/utils'; import * as countries from '../testdata/countries.json'; import RestCountryClient from '../../src/api/restcountry-client'; import { Country } from '../../src/types/rest-country-types'; import ImageClient from '../../src/api/image-client'; import { BaseCard } from '../../src/cards/base-card'; describe('Testing util file function getCountryFlagUrl', () => { const card = createMock(); beforeAll(() => { jest.spyOn(RestCountryClient.prototype, 'GetCountriesFromLocalStorage').mockImplementation(() => { return countries as Country[]; }); jest.spyOn(FileReader.prototype, 'readAsDataURL').mockImplementation(() => null); jest.spyOn(ImageClient.prototype, 'GetImage').mockImplementation((url: string) => { return url; }); }); test('Passing USA should return expected flag url', () => { expect(getCountryFlagByName(card, 'USA')).toBe('') }), test('Passing UAE should return expected flag url', () => { expect(getCountryFlagByName(card, 'UAE')).toBe('') }) }) ================================================ FILE: tests/utils/getDriverName.test.ts ================================================ import { createMock } from 'ts-auto-mock'; import { Driver } from '../../src/api/f1-models'; import { FormulaOneCardConfig } from '../../src/types/formulaone-card-types'; import { getDriverName } from '../../src/utils'; describe('Testing util file function getDriverName', () => { const config = createMock(); const driver: Driver = { familyName: 'Verstappen', givenName: 'Max', permanentNumber: '1', driverId: '', code: 'VER', url: '', dateOfBirth: '', nationality: '' }; test('Passing driver and config with show_carnumber should return expected driver name', () => { expect(getDriverName(driver, config)).toBe('Max Verstappen') }), test('Passing driver and config with show_carnumber should return expected driver name', () => { config.show_carnumber = true; expect(getDriverName(driver, config)).toBe('Max Verstappen #1') }) }) ================================================ FILE: tests/utils/getRefreshTime.test.ts ================================================ import { getRefreshTime } from '../../src/utils'; import { MRData as scheduleData } from '../testdata/schedule.json' import LocalStorageMock from '../testdata/localStorageMock'; describe('Testing util file function getRefreshTime', () => { const localStorageMock = new LocalStorageMock(); beforeAll(() => { jest.useFakeTimers(); jest.setSystemTime(new Date(2022, 2, 1)); // Weird bug in jest setting this to the last of the month const now = new Date(); const currentYear = now.getFullYear(); localStorageMock.setItem(`${currentYear}.json`, JSON.stringify({ data: JSON.stringify({ MRData: scheduleData }), created: new Date() })); Object.defineProperty(window, 'localStorage', { value: localStorageMock }); }); afterAll(() => { jest.useRealTimers(); }) test('Calling getRefreshTime without data in localstorage should return 24', () => { const result = getRefreshTime(""); expect(result).toBe(24); }), test('Calling getRefreshTime with data in localstorage should return 24', () => { localStorageMock.setItem('', JSON.stringify({ data: [], created: new Date(2022, 3, 1)})) const result = getRefreshTime(""); expect(result).toBe(24); }), test('Calling getRefreshTime with data in localstorage should return 1', () => { localStorageMock.setItem('', JSON.stringify({ data: [], created: new Date(2022, 2, 20, 10)})) const result = getRefreshTime(""); expect(result).toBe(1); }) }) ================================================ FILE: tests/utils/getTeamImageUrl.test.ts ================================================ import { getTeamImage } from '../../src/utils'; import { MRData } from '../testdata/constructorStandings.json' import 'isomorphic-fetch'; import { createMock } from 'ts-auto-mock'; import { BaseCard } from '../../src/cards/base-card'; describe('Testing util file function renderHeader', () => { const card = createMock(); MRData.StandingsTable.StandingsLists[0].ConstructorStandings.forEach(constructor => { const constructorName = constructor.Constructor.constructorId; test(`Calling getTeamImageUrl with ${constructorName} should return valid image`, async () => { const imageUrl = getTeamImage(card, constructorName); const result = await fetch(imageUrl); expect(result.status).toBe(200); }); }) }); ================================================ FILE: tests/utils/hasConfigOrEntitiesChanged.test.ts ================================================ import { PropertyValues } from "lit"; import { createMock } from "ts-auto-mock"; import FormulaOneCard from "../../src"; import { BaseCard } from "../../src/cards/base-card"; import { FormulaOneCardConfig } from "../../src/types/formulaone-card-types"; import { hasConfigOrCardValuesChanged } from '../../src/utils'; describe('Testing util file function hasConfigOrEntitiesChanged', () => { const config : FormulaOneCardConfig = { type: '' }; const card = createMock(); const baseCard = createMock(); test('Passing PropertyValues with config should return true', () => { const props : PropertyValues = new Map([['config', config]]); expect(hasConfigOrCardValuesChanged(card, props)).toBe(true); }), test('Passing PropertyValues empty should return false', () => { const props : PropertyValues = new Map(); expect(hasConfigOrCardValuesChanged(card, props)).toBe(false); }), test('Passing PropertyValues config and card should return true', () => { card.properties = new Map([['test', 'test']]); const props : PropertyValues = new Map([['card', baseCard]]); expect(hasConfigOrCardValuesChanged(card, props)).toBe(true); }), test('Passing PropertyValues config and cardValues should return true', () => { const props : PropertyValues = new Map([['cardValues', new Map([['cardValues', 'test']])]]); expect(hasConfigOrCardValuesChanged(card, props)).toBe(true); }) }) ================================================ FILE: tests/utils/reduceArray.test.ts ================================================ import { reduceArray } from "../../src/utils"; describe('reduceArray', () => { it('should reduce the array to the size of the limit', () => { const array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l']; const limit = 10; const result = reduceArray(array, limit); expect(result.length).toBe(limit); }); it('should not reduce the array if the limit is greater than the array size', () => { const array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l']; const limit = 20; const result = reduceArray(array, limit); expect(result.length).toBe(array.length); }); it('should return an empty array if the array is empty', () => { const array = [] as string[]; const limit = 20; const result = reduceArray(array, limit); expect(result.length).toBe(0); }); it('should return an empty array if the array is undefined', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const limit = 20; const result = reduceArray(undefined, limit); expect(result.length).toBe(0); }); // eslint-disable-next-line @typescript-eslint/no-explicit-any it('should return an empty array if the limit is undefined', () => { const array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l']; // eslint-disable-next-line @typescript-eslint/no-explicit-any const result = reduceArray(array, undefined); expect(result.length).toBe(12); }); }); ================================================ FILE: tests/utils/renderHeader.test.ts ================================================ import { HomeAssistant } from "custom-card-helpers"; import { HTMLTemplateResult } from "lit"; import { createMock } from "ts-auto-mock"; import FormulaOneCard from "../../src"; import { Race } from "../../src/api/f1-models"; import { BaseCard } from "../../src/cards/base-card"; import { renderHeader } from "../../src/utils"; import { MRData } from '../testdata/results.json' import { getRenderString } from "../utils"; import * as customCardHelper from "custom-card-helpers"; import { FormulaOneCardType } from "../../src/types/formulaone-card-types"; import RestCountryClient from "../../src/api/restcountry-client"; import { Country } from "../../src/types/rest-country-types"; import * as countries from '../testdata/countries.json' describe('Testing util file function renderHeader', () => { const card = createMock(); card.hass = createMock(); card.parent = createMock(); const lastRace = MRData.RaceTable.Races[0]; beforeAll(() => { jest.spyOn(RestCountryClient.prototype, 'GetCountriesFromLocalStorage').mockImplementation(() => { return countries as Country[]; }); }); test('Calling renderHeader with image not clickable', async () => { card.config.image_clickable = undefined; const result = renderHeader(card, lastRace); const htmlResult = getRenderString(result); expect(htmlResult).toMatch(`

  17 : Singapore Grand Prix


`); }), test('Calling renderHeader with clickable image ', () => { card.config.image_clickable = true; card.config.f1_font = true; const result = renderHeader(card, lastRace); const htmlResult = getRenderString(result); expect(card.config.actions).toMatchObject({ tap_action: { action: 'url', url_path: 'http://en.wikipedia.org/wiki/Marina_Bay_Street_Circuit' } }); expect(htmlResult).toMatch(`

  17 : Singapore Grand Prix


`); }), test('Calling renderHeader with image not clickable and card countdown', async () => { card.config.image_clickable = undefined; card.config.card_type = FormulaOneCardType.Countdown; const result = renderHeader(card, lastRace); const htmlResult = getRenderString(result); expect(htmlResult).toMatch(`
`); }), test('Calling renderHeader with actions', () => { // handleAction const spy = jest.spyOn(customCardHelper, 'handleAction'); card.config.actions = { tap_action: { action: 'navigate', navigation_path: '/lovelace/0', }, hold_action: { action: 'navigate', navigation_path: '/lovelace/1', }, double_tap_action: { action: 'navigate', navigation_path: '/lovelace/2', } } const result = renderHeader(card, lastRace); // eslint-disable-next-line @typescript-eslint/ban-types const actionHandler = (result.values[1] as HTMLTemplateResult).values[1] as Function; actionHandler({ detail: { action: 'tap' } }); actionHandler({ detail: { action: 'double_tap' } }); actionHandler({ detail: { action: 'hold' } }); expect(customCardHelper.handleAction).toBeCalledTimes(3); spy.mockClear(); }), test('Calling renderHeader with actions', () => { // handleAction const spy = jest.spyOn(customCardHelper, 'handleAction'); card.config.actions = { tap_action: { action: 'navigate', navigation_path: '/lovelace/0', }, hold_action: { action: 'navigate', navigation_path: '/lovelace/1', }, double_tap_action: { action: 'navigate', navigation_path: '/lovelace/2', } } const result = renderHeader(card, lastRace, true); // eslint-disable-next-line @typescript-eslint/ban-types const actionHandler = (result.values[1] as HTMLTemplateResult).values[1] as Function; actionHandler({ detail: { action: 'tap' } }); actionHandler({ detail: { action: 'double_tap' } }); actionHandler({ detail: { action: 'hold' } }); expect(customCardHelper.handleAction).toBeCalledTimes(0); spy.mockClear(); }), test('Calling renderHeader with config hide_tracklayout true', () => { card.config.hide_tracklayout = true; card.config.card_type = FormulaOneCardType.Results; const result = renderHeader(card, lastRace); const htmlResult = getRenderString(result); expect(htmlResult).toMatch(`

  17 : Singapore Grand Prix

`); }), test('Calling renderHeader with Miami with image not clickable', async () => { card.config.image_clickable = undefined; card.config.hide_tracklayout = false; lastRace.Circuit.Location.country = "USA"; lastRace.Circuit.Location.locality = "Miami"; const result = renderHeader(card, lastRace); const htmlResult = getRenderString(result); expect(htmlResult).toMatch(`

  17 : Singapore Grand Prix


`); }), test('Calling renderHeader with Imola with image not clickable', async () => { card.config.image_clickable = undefined; card.config.hide_tracklayout = false; lastRace.Circuit.Location.country = "Italy"; lastRace.Circuit.Location.locality = "Imola"; const result = renderHeader(card, lastRace); const htmlResult = getRenderString(result); expect(htmlResult).toMatch(`

  17 : Singapore Grand Prix


`); }) }); ================================================ FILE: tests/utils/renderLastYearsResults.test.ts ================================================ import { HomeAssistant, NumberFormat, TimeFormat } from "custom-card-helpers"; import { createMock } from "ts-auto-mock"; import { BaseCard } from "../../src/cards/base-card"; import { renderLastYearsResults } from "../../src/utils"; import { getRenderStringAsyncIndex } from "../utils"; import { Race } from "../../src/api/f1-models"; import { MRData as resultData } from '../testdata/results.json' describe('Testing util file function renderLastYearsResults', () => { const card = createMock(); const hass = createMock(); hass.locale = { language: 'NL', number_format: NumberFormat.comma_decimal, time_format: TimeFormat.language }; test('Given config with hide_raceinfo = false when raceinfo is rendered then raceinfo is rendered', async () => { card.config.f1_font = false; const result = renderLastYearsResults(card.config, resultData.RaceTable.Races[0] as Race); const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toBe('

Sergio Pérez (Red Bull)

George Russell (1:46.458)

 '); }), test('Given config with hide_raceinfo = false when raceinfo is rendered then raceinfo is rendered', async () => { card.config.f1_font = true; const result = renderLastYearsResults(card.config, resultData.RaceTable.Races[0] as Race); const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toBe('

Sergio Pérez (Red Bull)

George Russell (1:46.458)

 '); }), test('Given config and data without fastest lap with hide_raceinfo = false when raceinfo is rendered then raceinfo is rendered', async () => { card.config.f1_font = true; const raceData = resultData.RaceTable.Races[0] as Race; raceData.Results?.forEach(element => { element.FastestLap = undefined; }); const result = renderLastYearsResults(card.config, raceData); const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toBe('

Sergio Pérez (Red Bull)

()

 '); }) }); ================================================ FILE: tests/utils/renderRaceInfo.test.ts ================================================ import { HomeAssistant, NumberFormat, TimeFormat } from 'custom-card-helpers'; import { createMock } from 'ts-auto-mock'; import fetchMock from "jest-fetch-mock"; import FormulaOneCard from '../../src'; import { Mrdata, Race } from '../../src/api/f1-models'; import { BaseCard } from '../../src/cards/base-card'; import { WeatherUnit } from '../../src/types/formulaone-card-types'; import { renderRaceInfo } from '../../src/utils'; import { getRenderString, getRenderStringAsyncIndex } from '../utils'; import { MRData as scheduleData } from '../testdata/schedule.json' import { MRData as resultData } from '../testdata/results.json' describe('Testing util file function renderRaceInfo', () => { const card = createMock(); const hass = createMock(); hass.locale = { language: 'NL', number_format: NumberFormat.comma_decimal, time_format: TimeFormat.language } card.hass = hass; card.parent = createMock(); const race : Race = { season: '2021', round: '1', url: 'https://en.wikipedia.org/wiki/2021_Abu_Dhabi_Grand_Prix', Sprint: { date: '2021-12-11', time: '15:00:00Z', }, Qualifying: { date: '2021-12-11', time: '18:00:00Z', }, FirstPractice: { date: '2021-12-09', time: '11:00:00Z', }, SecondPractice: { date: '2021-12-10', time: '12:00:00Z', }, time: '18:00:00Z', date: '2021-12-12', raceName: 'Abu Dhabi Grand Prix', Circuit: { circuitId: 'yas_marina', url: 'http://en.wikipedia.org/wiki/Yas_Marina_Circuit', circuitName: 'Yas Marina Circuit', Location: { lat: '24.4672', long: '54.6031', locality: 'Abu Dhabi', country: 'United Arab Emirates', } }, }; test('Given config with hide_raceinfo = false when raceinfo is rendered then raceinfo is rendered', async () => { card.config.hide_racedatetimes = false; const result = renderRaceInfo(card, race); const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toBe('12-12-21 do 12:00 1 vr 13:00 Abu Dhabi Grand Prix - Yas Marina Circuit za 19:00 United Arab Emirates za 16:00 Abu Dhabi zo 19:00'); }), test('Given config with hide_raceinfo = true when raceinfo is rendered then raceinfo is not rendered', () => { card.config.hide_racedatetimes = true; const result = renderRaceInfo(card, race); const htmlResult = getRenderString(result); expect(htmlResult).toBe(''); }), test('Given config with hide_raceinfo = undefined when raceinfo is rendered then raceinfo is rendered', async () => { card.config.hide_racedatetimes = undefined; const result = renderRaceInfo(card, race); const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toBe('12-12-21 do 12:00 1 vr 13:00 Abu Dhabi Grand Prix - Yas Marina Circuit za 19:00 United Arab Emirates za 16:00 Abu Dhabi zo 19:00'); }), test('Given config with show_weather = true and no weather options when raceinfo is rendered then weather is rendered', async () => { card.config.hide_racedatetimes = false; card.config.show_weather = true; const result = renderRaceInfo(card, race); const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toBe('12-12-21 do 12:00 1 vr 13:00 Abu Dhabi Grand Prix - Yas Marina Circuit za 19:00 United Arab Emirates za 16:00 Abu Dhabi zo 19:00'); }), test('Given config with show_weather = true and weather options without api key when raceinfo is rendered then weather is rendered', async () => { card.config.hide_racedatetimes = false; card.config.show_weather = true; card.config.weather_options = { api_key: undefined, }; const result = renderRaceInfo(card, race); const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toBe('12-12-21 do 12:00 1 vr 13:00 Abu Dhabi Grand Prix - Yas Marina Circuit za 19:00 United Arab Emirates za 16:00 Abu Dhabi zo 19:00'); }), test('Given config with show_weather = true and weather options unit metric when raceinfo is rendered then weather is rendered', async () => { card.config.hide_racedatetimes = false; card.config.show_weather = true; card.config.weather_options = { api_key: 'fakekey', unit: WeatherUnit.Metric, }; const result = renderRaceInfo(card, race); const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toBe('12-12-21 do 12:00 1 vr 13:00 Abu Dhabi Grand Prix - Yas Marina Circuit za 19:00 United Arab Emirates za 16:00 Abu Dhabi zo 19:00'); }), test('Given config with show_weather = true and weather options unit MilesFahrenheit when raceinfo is rendered then weather is rendered', async () => { card.config.hide_racedatetimes = false; card.config.show_weather = true; card.config.weather_options = { api_key: 'fakekey', unit: WeatherUnit.MilesFahrenheit, }; const result = renderRaceInfo(card, race); const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toBe('12-12-21 do 12:00 1 vr 13:00 Abu Dhabi Grand Prix - Yas Marina Circuit za 19:00 United Arab Emirates za 16:00 Abu Dhabi zo 19:00'); }), test('Given config with show_lastyears_result = true when raceinfo is rendered then last years result is rendered', async () => { card.config.hide_racedatetimes = false; card.config.show_lastyears_result = true; jest.useFakeTimers(); jest.setSystemTime(new Date(2022, 3, 20)); // Weird bug in jest setting this to the last of the month fetchMock.mockResponseOnce(JSON.stringify({ MRData : scheduleData })); fetchMock.mockResponseOnce(JSON.stringify({ MRData : resultData })); const result = renderRaceInfo(card, race); const htmlResult = await getRenderStringAsyncIndex(result); expect(htmlResult).toBe('

()

()

 

()

()

 

()

()

 

()

()

 

()

()

 

()

()

 

()

()

 

()

()

 

()

()

 

()

()

 12-12-21 do 12:00 1 vr 13:00 Abu Dhabi Grand Prix - Yas Marina Circuit za 19:00 United Arab Emirates za 16:00 Abu Dhabi zo 19:00'); }) }) ================================================ FILE: tests/utils/renderWeatherInfo.test.ts ================================================ import { createMock } from "ts-auto-mock"; import { Day, Hour } from "../../src/api/weather-models"; import { FormulaOneCardConfig, WeatherUnit } from "../../src/types/formulaone-card-types"; import { renderWeatherInfo } from "../../src/utils"; import { getRenderString } from '../utils'; describe('Testing util file function renderWeatherInfo ', () => { const config = createMock(); const weather = createMock(); weather.hours = []; const hour = createMock(); hour.winddir = 270; hour.windspeed = 10; hour.temp = 20; hour.precip = 2; hour.precipprob = 10; weather.winddir = 270; weather.windspeed = 10; weather.temp = 20; weather.precip = 2; weather.precipprob = 10; weather.hours.push(hour); test('Given config with hide_weatherinfo = false when weatherinfo is rendered then weatherinfo is rendered', () => { config.hide_weatherinfo = false; const result = renderWeatherInfo(weather, config, new Date(2021, 1, 1, 0, 0, 0)); const htmlResult = getRenderString(result); expect(htmlResult).toBe('
W mph mm %
% °C °C
 '); }), test('Given weatherData, config and raceDate when weatherinfo is rendered then weatherinfo is rendered', () => { config.hide_weatherinfo = false; const result = renderWeatherInfo(weather, config, new Date(2021, 1, 1, 0, 0, 0)); const htmlResult = getRenderString(result); expect(htmlResult).toBe('
W mph mm %
% °C °C
 '); }), test('Given weatherData, config with unit metric unit and raceDate when weatherinfo is rendered then weatherinfo is rendered', () => { config.hide_weatherinfo = false; config.weather_options = { unit: WeatherUnit.Metric } const result = renderWeatherInfo(weather, config, new Date(2021, 1, 1, 0, 0, 0)); const htmlResult = getRenderString(result); expect(htmlResult).toBe('
W km/h mm %
% °C °C
 '); }), test('Given weatherData, config with unit metric unit and raceDate when weatherinfo is rendered then weatherinfo is rendered', () => { config.hide_weatherinfo = false; config.weather_options = { unit: WeatherUnit.MilesFahrenheit } const result = renderWeatherInfo(weather, config, new Date(2021, 1, 1, 0, 0, 0)); const htmlResult = getRenderString(result); expect(htmlResult).toBe('
W mph mm %
% °F °F
 '); }), test('Given weatherData, config and raceDate when weatherinfo hours is undefined is rendered then weatherinfo is rendered', () => { config.hide_weatherinfo = false; config.weather_options = { unit: WeatherUnit.Metric } const weatherHoursUndefined = weather; weatherHoursUndefined.hours = undefined as unknown as Hour[]; const result = renderWeatherInfo(weatherHoursUndefined, config, new Date(2021, 1, 1, 0, 0, 0)); const htmlResult = getRenderString(result); expect(htmlResult).toBe('
W km/h mm %
% °C °C
 '); }) }); ================================================ FILE: tests/utils.ts ================================================ import { HTMLTemplateResult } from "lit"; export const getRenderString = (data: HTMLTemplateResult) : string => { let returnHtml = ''; if(!data) { return returnHtml; } const {strings, values} = data; if(strings === undefined) { return returnHtml; } for(let i = 0; i < strings.length; i++) { returnHtml += strings[i]; if(typeof values[i] === 'string') { returnHtml += values[i]; } if(typeof values[i] === 'function') { // eslint-disable-next-line @typescript-eslint/ban-types returnHtml += (values[i] as Function).name; } else if(typeof values[i] === 'object') { const templates = values[i] as HTMLTemplateResult[]; if(templates !== undefined && templates !== null) { for(let i = 0; i < templates.length; i++) { returnHtml += getRenderString(templates[i] as HTMLTemplateResult); } } const template = values[i] as HTMLTemplateResult; if(template !== undefined && templates !== null) { returnHtml += getRenderString(template); } } } return returnHtml.replace(/\s\s+/g, ' '); } export const getRenderStringAsync = async (data: HTMLTemplateResult) : Promise => { let returnHtml = ''; if(!data) { return returnHtml; } const {strings, values} = data; if(strings === undefined) { return returnHtml; } for(let i = 0; i < strings.length; i++) { returnHtml += strings[i]; if(typeof values[i] === 'string') { returnHtml += values[i]; } if(typeof values[i] === 'function') { // eslint-disable-next-line @typescript-eslint/ban-types returnHtml += (values[i] as Function).name; } else if(typeof values[i] === 'object') { const templateResult = values[i]; if(templateResult) { for(let i = 0; i < templateResult.values.length; i++) { if(templateResult.values[i] instanceof Promise) { const promise = >templateResult.values[i]; const promiseResult = await promise; returnHtml += getRenderString(promiseResult) } else { const templates = templateResult.values[i] as HTMLTemplateResult[]; if(templates !== undefined && templates !== null) { for(let i = 0; i < templates.length; i++) { returnHtml += getRenderString(templates[i])//, iteration + 1); } } const template = templateResult.values[i] as HTMLTemplateResult; if(template !== undefined && template !== null) { returnHtml += getRenderString(template)//, iteration + 1); } } } } } } return returnHtml.replace(/\s\s+/g, ' '); } export const getRenderStringAsyncIndex = async (data: HTMLTemplateResult) : Promise => { // loop through the strings and values and build the html string iteratively // if the value is a string, add it to the html string // if the value is an object, check if it is a template result or an array of template results // if it is a template result, call this function recursively // if it is an array of template results, loop through the array and call this function recursively for each item // if the value is a promise, await the promise and call this function recursively // if the value is a function, add the function name to the html string // if the value is undefined, add an empty string to the html string // if the value is null, add an empty string to the html string // if the value is a number, add the number to the html string // if the value is a boolean, add the boolean to the html string // let returnHtml = ''; if(!data) { return returnHtml; } const {strings, values} = data; if(strings === undefined) { return returnHtml; } for(let i = 0; i < strings.length; i++) { returnHtml += strings[i]; if(i >= values.length) { continue; } if(typeof values[i] === 'string') { returnHtml += values[i]; } else if(typeof values[i] === 'object') { const templates = values[i]; if(templates !== undefined) { for(let j = 0; j < templates?.length; j++) { returnHtml += await getRenderStringAsyncIndex(templates[j]); } } const templateResult = values[i]; for(let j = 0; j < templateResult?.values.length; j++) { if(templateResult.values[j] instanceof Promise) { const promise = >templateResult.values[j]; const promiseResult = await promise; returnHtml += await getRenderStringAsyncIndex(promiseResult); } else { returnHtml += await getRenderStringAsyncIndex(templateResult); } } } } return returnHtml.replace(/\s\s+/g, ' '); } ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "experimentalDecorators": true, "module": "CommonJS", "noImplicitAny": true, "removeComments": true, "resolveJsonModule": true, "preserveConstEnums": true, "sourceMap": true, "moduleResolution": "Node", "target": "es6", "plugins": [ { "transform": "ts-auto-mock/transformer", "cacheBetweenTests": false } ], "paths": { "jest-ts-auto-mock": ["../../index"] } }, "include": ["src/**/*"], "exclude": ["node_modules", "**/*.spec.ts"] } ================================================ FILE: webpack.config.js ================================================ const webpack = require('webpack'); const path = require('path'); const compressionPlugin = require('compression-webpack-plugin'); module.exports = { mode: 'production', entry: path.resolve(__dirname, 'src', 'index.ts'), output: { filename: 'formulaone-card.js', path: path.resolve(__dirname), }, optimization: { minimize: true }, module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'], }, }, }, { test: /\.(js|jsx|ts|tsx)$/, use: [ { loader: 'minify-html-literals-loader' } ], }, { test: /\.tsx?$/, loader: "ts-loader" } ], }, plugins: [ new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production'), }), new compressionPlugin({ test: /\.js(\?.*)?$/i, }), ], resolve: { extensions: ['.js', '.jsx', '.ts', '.tsx'] } };