[
  {
    "path": ".eslintignore",
    "content": "**/demo/**\n**/grunt/**\n**/build/**\n**/third_party/**\n**/tmp/**\nGruntfile.js\n**/intl-tel-input/utils.js\nsite/static/js/highlight.min.js\nsite/static/js/silktide-consent-manager.js\nsite/src/examples/js/*_display_code*\nplaywright-report/\n"
  },
  {
    "path": ".eslintrc.js",
    "content": "module.exports = {\n  root: true,\n  env: {\n    browser: true,\n    es2021: true,\n    node: true,\n    \"jest/globals\": true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"plugin:@typescript-eslint/recommended\",\n    \"plugin:react/recommended\",\n    \"plugin:react-hooks/recommended\",\n  ],\n  parser: \"@typescript-eslint/parser\",\n  parserOptions: {\n    \"ecmaVersion\": \"latest\",\n    \"sourceType\": \"module\",\n  },\n  plugins: [\n    \"@typescript-eslint\",\n    \"react\",\n    \"jest\",\n  ],\n  globals: {\n    goog: true,\n    i18n: true,\n    require: true,\n  },\n  rules: {\n    semi: [\"error\", \"always\"],\n    \"comma-dangle\": [\"error\", \"always-multiline\"],\n    quotes: [\"error\", \"double\"],\n    \"no-unused-vars\": \"off\",\n    \"no-prototype-builtins\": \"off\",\n    \"class-methods-use-this\": \"error\",\n    \"@typescript-eslint/explicit-function-return-type\": \"off\",\n    \"@typescript-eslint/no-unused-vars\": [\"error\", {\n      \"argsIgnorePattern\": \"^_\",\n      \"varsIgnorePattern\": \"^_\",\n      \"caughtErrorsIgnorePattern\": \"^_\",\n      \"ignoreRestSiblings\": true,\n    }],\n    \"@typescript-eslint/no-var-requires\": \"off\",\n    \"@typescript-eslint/no-explicit-any\": \"off\",\n    \"@typescript-eslint/no-require-imports\": \"off\",\n  },\n  overrides: [{\n    files: [\".eslintrc.{js,cjs}\"],\n    env: { \"node\": true },\n    parserOptions: { \"sourceType\": \"script\" },\n  }],\n  settings: {\n    \"import/resolver\": {\n      \"typescript\": {},\n    },\n    \"react\": {\n      \"version\": \"detect\",\n    },\n  },\n};\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributing\n\nI'm very open to contributions, big and small! For general instructions on submitting a pull request on GitHub, see these guides: [Fork A Repo](https://help.github.com/articles/fork-a-repo) and [Creating a pull request from a fork](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).\n\n## Table of Contents\n- [Changes to the plugin](#changes-to-the-plugin)\n- [Updating the flag images](#updating-the-flag-images)\n- [Adding a new translation](#adding-a-new-translation)\n\n## Changes to the plugin\n\n### Setup\n\nOnce you have [forked the repository](https://help.github.com/articles/fork-a-repo) and checked out your fork on your local machine, you need to initialise the submodules with `git submodule update --init --recursive`, then run `npm install`, and then `npm run build`. You should now be able to open the included demo.html in your browser and have a working plugin!\n\n### Making changes\n\nAny time you make changes, you’ll need to rebuild the plugin. You can run `npm run watch` to do this automatically. Else you can manually run one of the build commands below:\n\n- `npm run build` to build everything (slow)\n  - Builds flag images, translations, CSS, and all of the JS (see below)\n- `npm run build:js` to build all of the JS (slow)\n  - Builds utils script, main plugin module, TS type declaration files, react/vue/angular/svelte components and demo bundles\n- `npm run build:jsfast` to just build the JS needed for the demo/tests (fast)\n- `npm run build:css` to just build the CSS (fast)\n- And lots more - see [package.json \"scripts\" section](https://github.com/jackocnr/intl-tel-input/blob/master/package.json#L7-L29) for full list\n\n### Tests\n\nAfter building all the assets (`npm run build`) you can run `npm test` to run all the tests (Jest + Playwright). For Playwright, you may also need to install the browsers first with `npx playwright install`.\n\n## Updating the flag images\n\nWe get our flags from the [flag-icons](https://github.com/lipis/flag-icons) project. If there is a problem with the flags, you'll need to raise it with them. When there is an update in that project that you want to pull into this project, you can update the npm package with `npm install flag-icons@VERSION --save-dev`, and then rebuild the flag sprite images with `npm run build:img`. Once you've checked everything looks ok (by opening the included demo.html in your browser), you can then create a pull request on GitHub. _NOTE: since we removed the build files from the repo, the only changes you will be committing are in package.json and package-lock.json._\n\n## Adding a new translation\n\nNOTE: that country names are now translated automatically using the native `Intl.DisplayNames` (see `countryNameLocale` option), so there's no need to provide translations for these anymore.\n\nThe [provided translations](https://github.com/jackocnr/intl-tel-input/tree/master/src/js/intl-tel-input/i18n) are now just for the user interface strings (e.g. the country search placeholder). If we don't yet support a language you need, it's easy to contribute this yourself - you only need to provide a handful of strings (for example, see the [English translations](https://github.com/jackocnr/intl-tel-input/blob/master/src/js/intl-tel-input/i18n/en/index.ts)).\n\nThe translation files are located in src/js/intl-tel-input/i18n/. There is a directory for each language we support (e.g. \"en\" for English). Inside each of these directories, there is an index.ts, which contains the translations. All you need to do to add a new translation is create a new language directory, create the index.ts file and populate it with your translation strings, following the same pattern as the other languages.\n\nIf you haven't already, you will need to run `npm install` to install the project dependencies, and then you can run `npm run build:translations` to automatically add your new language to the root index.ts file. Once you have tested and confirmed that the new translations are working, you can create a pull request on GitHub.\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: jackocnr\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1_bug_report.yml",
    "content": "name: Bug Report\ndescription: Report a bug or issue with intl-tel-input\nlabels: [\"bug\"]\nbody:\n  - type: checkboxes\n    attributes:\n      label: Prerequisites\n      description: Take a couple of minutes to help our maintainers work faster.\n      options:\n        - label: I am using the latest version (v26.8.1) of both intl-tel-input and utils.js.\n          required: true\n        - label: This plugin uses libphonenumber for formatting, validation, and placeholder numbers. If my issue relates to one of these things, I confirm I have used their [test site](https://libphonenumber.appspot.com) to ensure the issue is not with them, and will provide a link to the test results page showing this. Else, I have [reported the issue](https://github.com/google/libphonenumber/blob/master/CONTRIBUTING.md) to them instead of here.\n          required: true\n        - label: I have searched the existing issues (including closed issues) to ensure this has not already been discussed.\n          required: true\n  - type: textarea\n    id: current\n    attributes:\n      label: Current behaviour\n      description: A concise description of what you're experiencing.\n    validations:\n      required: true\n  - type: textarea\n    id: expected\n    attributes:\n      label: Expected behaviour\n      description: A concise description of what you expected to happen.\n    validations:\n      required: true\n  - type: textarea\n    id: steps\n    attributes:\n      label: Steps to reproduce\n      description: How can we reproduce the issue? (e.g. provide a [Playground](https://intl-tel-input.com/playground) link with the right configuration)\n      value: |\n        1.\n        2.\n        3.\n    validations:\n      required: true\n  - type: textarea\n    id: comments\n    attributes:\n      label: Anything else?\n      description: Additional comments, screenshots/videos, initialisation options, browser/device info, etc.\n    validations:\n      required: false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/2_feature_request.yml",
    "content": "name: Feature Request\ndescription: Suggest a new feature or improvement\nlabels: [\"enhancement\"]\nbody:\n  - type: textarea\n    id: description\n    attributes:\n      label: Description\n      description: Describe the feature or improvement you'd like to see.\n    validations:\n      required: true\n  - type: textarea\n    id: use-case\n    attributes:\n      label: Use case\n      description: Why do you need this feature? What problem does it solve?\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Ask the community\n    url: https://github.com/jackocnr/intl-tel-input/discussions\n    about: Ask questions and get help from the community\n"
  },
  {
    "path": ".github/copilot-instructions.md",
    "content": "At the beginning of a chat, remind me to run `npm run watch`, so any changes get automatically built, meaning I can manually test them in the browser, and also when you run the tests, they will be running the latest code. Do not offer to run the build command yourself. Do not ever make changes to files in the build/ directories, e.g. the root ./build/ directory, or the component build/ directories (angular/build/, react/build/, svelte/build/ and vue/build/). These files are auto-generated, so do not update them.\n\nWhen making changes to the root src/ directory, do not touch any of the files in the component directories (angular/, react/, svelte/ and vue/). A lot of the files in those directories are just symlinks to the root src/ directory.\n\nWhen making changes to the root src/ code, make sure to also update the tests (in the root tests/ directory) if necessary, and run the tests to make sure they are passing.\n\nWhen making changes to the root src/ code, make sure to also update the website documentation and example pages if necessary. The documentation is located in the site/src/docs/ directory, and the examples are located in the site/src/examples/ directory. If you are not sure how to update the documentation or examples, just ask me.\n\nWhen writing code, prioritise clarity and brevity. Try to follow the existing code style as much as possible.\n\nWhen making changes in the root site/ directory, remember there are no tests for this, so don't bother running the tests. This also applies to the component directories (angular/, react/, svelte/ and vue/), as they also don't have tests.\n\nWhen making changes in the component directories (angular/, react/, svelte/ and vue/), note that the \"withUtils\" files are auto-generated, so do not update them. For example, when making changes to react/src/intl-tel-input/react.tsx, do not update react/src/intl-tel-input/react-withUtils.tsx, as it is auto-generated. The same applies to the other component directories."
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches: [ \"**\" ]\n  pull_request:\n    branches: [ \"**\" ]\n\npermissions:\n  contents: read\n\njobs:\n  build-and-test:\n    name: Build and Test (Node ${{ matrix.node-version }})\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        node-version: [20]\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n        with:\n          submodules: recursive\n          fetch-depth: 0\n\n      - name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n          cache: npm\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Build\n        run: npm run build\n\n      - name: Run JS tests\n        run: npm run test:js -- --runInBand\n\n      - name: Run e2e tests\n        run: npm run test:e2e:linux\n\n      - name: Upload Playwright artifacts\n        if: failure()\n        uses: actions/upload-artifact@v4\n        with:\n          name: playwright\n          path: |\n            playwright-report/\n            test-results/\n"
  },
  {
    "path": ".gitignore",
    "content": "/node_modules/\n/lib/*\n!/lib/libphonenumber\n/.sass-cache/\n/.grunt/\n/tmp/\n/vendor/\n/.idea/\n*.iml\n.DS_Store\n/build/\n/react/build/\n/svelte/build/\n/vue/build/\n/angular/build/\n/*/demo/*/*-bundle.js\n/test-results/\n/playwright-report/\n/.claude/"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"third_party/libphonenumber\"]\n\tpath = third_party/libphonenumber\n\turl = https://github.com/google/libphonenumber\n"
  },
  {
    "path": ".node-version",
    "content": "v20.12.0\n"
  },
  {
    "path": ".npmrc",
    "content": "# Jest uses the `vm` module, which does not have production ready module support\n# yet. This option lets us use dynamic imports.\nnode-options='--experimental-vm-modules'\n"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\n    \"rvest.vs-code-prettier-eslint\",\n    \"davidanson.vscode-markdownlint\",\n    \"aaron-bond.better-comments\"\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"editor.defaultFormatter\": \"rvest.vs-code-prettier-eslint\",\n    \"editor.formatOnPaste\": false, //* required\n    \"editor.formatOnType\": false, //* required\n    \"editor.formatOnSaveMode\": \"file\", //* required to format on save\n    \"vs-code-prettier-eslint.prettierLast\": false, //* set as \"true\" to run 'prettier' last not first\n    \"typescript.tsdk\": \"node_modules/typescript/lib\",\n    \"files.associations\": {\n      \"LICENSE\": \"text\",\n    },\n    \"markdownlint.config\": {\n      \"blanks-around-headings\": false,\n      \"blanks-around-lists\": false,\n      \"ul-style\": false,\n      \"blanks-around-fences\": false,\n      \"ol-prefix\": false,\n      \"no-inline-html\": false,\n    },\n    \"cSpell.language\": \"en-GB\",\n    \"cSpell.enabledLanguageIds\": [\n      \"asciidoc\",\n      \"c\",\n      \"cpp\",\n      \"csharp\",\n      \"css\",\n      \"dotenv\",\n      \"go\",\n      \"handlebars\",\n      \"html\",\n      \"ignore\",\n      \"jade\",\n      \"java\",\n      \"javascript\",\n      \"javascriptreact\",\n      \"json\",\n      \"jsonc\",\n      \"latex\",\n      \"less\",\n      \"markdown\",\n      \"php\",\n      \"plaintext\",\n      \"pug\",\n      \"python\",\n      \"restructuredtext\",\n      \"rust\",\n      \"scala\",\n      \"scss\",\n      \"text\",\n      \"typescript\",\n      \"typescriptreact\",\n      \"yaml\",\n      \"yml\",\n      \"COBOL\",\n      \"ACUCOBOL\"\n    ],\n    \"files.trimTrailingWhitespace\": true,\n    \"[markdown]\": {\n      \"files.trimTrailingWhitespace\": false\n    }\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "See the Github Releases page for changelog: https://github.com/jackocnr/intl-tel-input/releases\n\nOr to view a specific version, e.g. v20.0.0, update the URL accordingly, e.g. https://github.com/jackocnr/intl-tel-input/releases/tag/v20.0.0\n\n## Breaking changes\n\n- v26.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v26.0.0\n- v25.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v25.0.0\n- v24.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v24.0.0\n- v23.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v23.0.0\n- v22.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v22.0.0\n- v21.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v21.0.0\n- v20.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v20.0.0\n- v19.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v19.0.0\n- v18.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v18.0.0\n- v17.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v17.0.0\n- v16.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v16.0.0\n- v15.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v15.0.0\n- v14.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v14.0.0\n- v13.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v13.0.0\n- v12.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v12.0.0\n- v11.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v11.0.0\n- v10.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v10.0.0\n- v9.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v9.0.0\n- v8.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v8.0.0\n- v7.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v7.0.0\n- v6.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v6.0.0\n- v5.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v5.0.0\n- v4.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v4.0.0\n- v3.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v3.0.0\n- v2.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v2.0.0\n- v1.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v1.0.0\n"
  },
  {
    "path": "Gruntfile.js",
    "content": "module.exports = function(grunt) {\n\n  // load all tasks from package.json\n  require('load-grunt-config')(grunt);\n  require('time-grunt')(grunt);\n  require('google-closure-compiler').grunt(grunt, {\n    platfrom: 'native'\n  });\n\n  /**\n   * BUILD TASKS\n   */\n  // build everything ready for a commit\n  grunt.registerTask('build', [\n    'clean:allBuild',\n    'build:img',\n    'translations',\n    'build:js',\n  ]);\n\n  // build translations\n  grunt.registerTask('build:translations', [\n    'clean:buildJs',\n    'clean:tmpIntermediates',\n    'translations',\n    'build:js',\n  ]);\n\n  // build utils\n  grunt.registerTask('build:utils', [\n    'clean:utils',\n    'closure-compiler:utils',\n    'shell:checkLpnMetadata',\n  ]);\n\n  // just CSS\n  grunt.registerTask('build:css', [\n    'clean:buildCss',\n    'sass',\n    'cssmin',\n  ]);\n\n  // just images (and CSS)\n  grunt.registerTask('build:img', [\n    'clean:buildImg',\n    'generate-sprite',\n    'build:css',\n  ]);\n\n  // just javascript\n  grunt.registerTask('build:js', [\n    'clean:buildJs',\n    'clean:tmpIntermediates',\n    'shell:eslint',\n    'closure-compiler:utils',\n    'shell:genTsDeclaration',\n    'shell:buildJs',\n    'build:components',\n  ]);\n\n  // just 4 components\n  grunt.registerTask('build:components', [\n    'clean:reactBuild',\n    'clean:vueBuild',\n    'clean:angularBuild',\n    'clean:svelteBuild',\n    'build:react',\n    'build:vue',\n    'build:angular',\n    'build:svelte',\n  ]);\n\n  // Ensure build/js/utils.js exists (src/js/intl-tel-input/utils.js is a symlink to it).\n  grunt.registerTask('ensure:utils', 'Build utils if missing', function() {\n    if (!grunt.file.exists('build/js/utils.js')) {\n      grunt.task.run('closure-compiler:utils');\n    }\n  });\n\n  // fast version which only builds the main plugin JS files (see root build.js file for details)\n  grunt.registerTask('build:jsfast', [\n    'clean:buildJsKeepUtils',\n    'clean:tmpIntermediates',\n    'ensure:utils',\n    'shell:buildJs',\n  ]);\n\n  // just react\n  grunt.registerTask('build:react', [\n    'clean:reactBuild',\n    'replace:reactWithUtils',\n    'shell:genReactTsDeclaration',\n    'shell:buildReact',\n  ]);\n\n  // just vue\n  grunt.registerTask('build:vue', [\n    'clean:vueBuild',\n    'replace:vueWithUtils',\n    'shell:buildVue',\n  ]);\n\n  // just angular\n  grunt.registerTask('build:angular', [\n    'clean:angularBuild',\n    'replace:angularWithUtils',\n    'shell:genAngularTsDeclarationAndJs',\n    'shell:buildAngular',\n  ]);\n\n  // just svelte\n  grunt.registerTask('build:svelte', [\n    'clean:svelteBuild',\n    'replace:svelteWithUtils',\n    'shell:buildSvelte',\n  ]);\n\n  /**\n   * VERSIONING TASKS\n  */\n  // (1) build for tests, and run tests before allowing a version bump\n  // (2) bump version number in package.json etc\n  // (3) rebuild js to update version numbers in those files, as well as readme etc\n  // (4) commit, tag and push\n  grunt.registerTask('version', [\n    'build:jsfast',\n    'shell:test',\n    'bump-only',\n    'versionNumbers',\n    'bump-commit',\n  ]);\n\n  grunt.registerTask('version:minor', [\n    'build:jsfast',\n    'shell:test',\n    'bump-only:minor',\n    'versionNumbers',\n    'bump-commit'\n  ]);\n\n  grunt.registerTask('version:major', [\n    'build:jsfast',\n    'shell:test',\n    'bump-only:major',\n    'versionNumbers',\n    'bump-commit'\n  ]);\n\n  // update version numbers in docs etc\n  grunt.registerTask('versionNumbers', [\n    'replace:siteDocs',\n    'replace:issueTemplate',\n    'replace:packageLockInner',\n  ]);\n\n};\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014-2016 Jack O'Connor\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# International Telephone Input\n[![CI](https://github.com/jackocnr/intl-tel-input/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/jackocnr/intl-tel-input/actions/workflows/ci.yml) <img src=\"https://img.shields.io/github/package-json/v/jackocnr/intl-tel-input.svg\" alt=\"version\"/> <img src=\"https://img.shields.io/npm/dm/intl-tel-input.svg\"  alt=\"downloads\"/> [![NerdyData.com logo](https://badges.nerdydata.com/719de9d2-d0e7-4988-b02f-9f9d52687076)](https://www.nerdydata.com/reports/international-telephone-input/719de9d2-d0e7-4988-b02f-9f9d52687076)\n\nA JavaScript plugin for entering, formatting and validating international telephone numbers. Includes TypeScript definitions, plus React, Vue, Angular and Svelte components.\n\n[Explore docs »](https://intl-tel-input.com/docs/choose-integration)\n\n<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.github.com/jackocnr/intl-tel-input/master/screenshots/vanilla-dark.png\">\n  <source media=\"(prefers-color-scheme: light)\" srcset=\"https://raw.github.com/jackocnr/intl-tel-input/master/screenshots/vanilla-light.png\">\n  <img width=\"263\" height=\"269\" alt=\"Plugin screenshot showing country dropdown open\" src=\"https://raw.github.com/jackocnr/intl-tel-input/master/screenshots/vanilla-light.png\">\n</picture>\n\n## Sponsored by\n<img src=\"https://raw.github.com/jackocnr/intl-tel-input/master/screenshots/twilio.webp\" height=\"100\" alt=\"Twilio\"/>\n\nUse [Twilio's API to build phone verification, SMS 2FA, appointment reminders, marketing notifications and so much more](https://www.twilio.com/blog/international-telephone-input-twilio?utm_source=github&utm_medium=referral&utm_campaign=intl_tel_input). We can't wait to see what you build.\n\n## React, Vue, Angular and Svelte Components\nWe provide React, Vue, Angular and Svelte (beta) components alongside the regular JavaScript plugin. This readme is for the JavaScript plugin. View the [React Component](https://intl-tel-input.com/docs/react-component), the [Vue Component](https://intl-tel-input.com/docs/vue-component) the [Angular Component](https://intl-tel-input.com/docs/angular-component), or the [Svelte component](https://intl-tel-input.com/docs/svelte-component).\n\n## Docs and Examples\nWe have a newly updated website, where you can find [a full set of docs](https://intl-tel-input.com/docs/getting-started.html), a [live playground](https://intl-tel-input.com/playground/) where you can try out all of the options, as well as plenty of [examples](https://intl-tel-input.com/examples/validation-practical.html) of different setups.\n\n## Features\n* Automatically select the user's current country using an IP lookup\n* Automatically set the input placeholder to an example number for the selected country\n* Navigate the country dropdown by typing a country's name, or using the up/down keys\n* Automatically format the number as the user types\n* Optionally, only allow numeric characters and cap the number at the maximum valid length\n* The user types their national number, and the plugin gives you the full standardised international number\n* Number validation, including specific error types\n* High-resolution flag images\n* Accessibility provided via ARIA tags\n* Typescript type definitions included\n* Easily customise styles by overriding CSS variables, e.g. support dark mode\n* React, Vue, Angular and Svelte components also included\n* Translations provided in over 40 languages, as well as support for RTL layout and alternative numeral sets\n* Lots of initialisation options for customisation, as well as instance methods/events for interaction\n\n## Contributing\nSee the [contributing guide](https://github.com/jackocnr/intl-tel-input/blob/master/.github/CONTRIBUTING.md) for instructions on setting up the project and making changes, and also on how to update the flag images, or how to add a new translation.\n\n## Attributions\n* Flag images from [flag-icons](https://github.com/lipis/flag-icons)\n* Original country data from mledoze's [World countries in JSON, CSV and XML](https://github.com/mledoze/countries)\n* Formatting/validation/example number code from [libphonenumber](https://github.com/googlei18n/libphonenumber)\n\nUser testing powered by [BrowserStack Open-Source Program](https://www.browserstack.com/open-source)  \n\nBrowser testing via <a href=\"https://www.lambdatest.com/\" target=\"_blank\"><img src=\"https://raw.githubusercontent.com/jackocnr/intl-tel-input/refs/heads/master/screenshots/lambda_test.svg\" style=\"vertical-align:middle;margin-left:5px\" width=\"147\" height=\"26\" /></a>\n"
  },
  {
    "path": "angular/README.md",
    "content": "# IntlTelInput Angular Component\n\nAn Angular component for the [intl-tel-input](https://github.com/jackocnr/intl-tel-input) JavaScript plugin. View the [source code](https://github.com/jackocnr/intl-tel-input/blob/master/angular/src/intl-tel-input/angular.ts).\n\n[Explore docs »](https://intl-tel-input.com/docs/angular-component)\n"
  },
  {
    "path": "angular/build.js",
    "content": "const { build } = require(\"esbuild\");\nconst fs = require(\"fs\");\nconst packageJson = require(\"../package.json\");\n\nconst mainShared = {\n  bundle: true,\n  external: [\"@angular/core\", \"@angular/forms\"],\n  logLevel: \"info\",\n  minify: false,\n  define: { \"process.env.VERSION\": `\"${packageJson.version}\"` },\n};\n\nasync function buildMain() {\n  //* Angular Component - Default (ES Modules)\n  await build({\n    ...mainShared,\n    entryPoints: [\"angular/build/temp/intl-tel-input/angular.js\"],\n    format: \"esm\",\n    outfile: \"angular/build/IntlTelInput.js\",\n  });\n\n  //* Angular Component With Utils - Default (ES Modules)\n  await build({\n    ...mainShared,\n    entryPoints: [\"angular/build/temp/intl-tel-input/angularWithUtils.js\"],\n    format: \"esm\",\n    outfile: \"angular/build/IntlTelInputWithUtils.js\",\n  });\n\n  // remove temp folder after builds are complete\n  fs.rmSync(\"angular/build/temp\", { recursive: true, force: true });\n}\n\nbuildMain().catch(console.error);\n\nconst demoShared = {\n  bundle: true,\n  define: { \"process.env.VERSION\": `\"${packageJson.version}\"` },\n  format: \"iife\",\n};\n\nbuild({\n  ...demoShared,\n  entryPoints: [\"angular/demo/simple/main.ts\"],\n  outfile: \"angular/demo/simple/simple-bundle.js\",\n});\n\nbuild({\n  ...demoShared,\n  entryPoints: [\"angular/demo/validation/main.ts\"],\n  outfile: \"angular/demo/validation/validation-bundle.js\",\n});\n\nbuild({\n  ...demoShared,\n  entryPoints: [\"angular/demo/set-number/main.ts\"],\n  outfile: \"angular/demo/set-number/set-number-bundle.js\",\n});\n\nbuild({\n  ...demoShared,\n  entryPoints: [\"angular/demo/toggle-disabled/main.ts\"],\n  outfile: \"angular/demo/toggle-disabled/toggle-disabled-bundle.js\",\n});\n\nbuild({\n  ...demoShared,\n  entryPoints: [\"angular/demo/form/main.ts\"],\n  outfile: \"angular/demo/form/form-bundle.js\",\n});"
  },
  {
    "path": "angular/demo/form/form.component.ts",
    "content": "import { Component, OnInit, ViewChild } from \"@angular/core\";\nimport {\n  FormControl,\n  FormGroup,\n  ReactiveFormsModule,\n  Validators,\n} from \"@angular/forms\";\nimport { IntlTelInputComponent } from \"../../src/intl-tel-input/angularWithUtils\";\n\n@Component({\n  selector: \"app-root\",\n  template: `\n    <form [formGroup]=\"fg\" (ngSubmit)=\"handleSubmit()\">\n      <intl-tel-input\n        #telInput\n        formControlName=\"phone\"\n        name=\"phone\"\n        [initOptions]=\"{\n          initialCountry: 'us',\n        }\"\n      />\n      <button class=\"button\" type=\"submit\" [disabled]=\"!fg.valid\">\n        Validate\n      </button>\n      <div class=\"notice\">\n        @if (phone?.errors?.[\"required\"] && phone?.touched) {\n          Phone number is required.\n        } @else if (phone?.errors?.[\"invalidPhone\"] && phone?.touched) {\n          {{ phone?.errors?.[\"invalidPhone\"].errorMessage }}\n        } @else if (isSubmitted && fg.valid) {\n          Valid number: {{ telInput.getInstance()?.getNumber() }}\n        }\n      </div>\n    </form>\n  `,\n  standalone: true,\n  imports: [IntlTelInputComponent, ReactiveFormsModule],\n})\nexport class AppComponent implements OnInit {\n  @ViewChild(\"telInput\") telInput!: IntlTelInputComponent;\n\n  fg: FormGroup = new FormGroup({\n    phone: new FormControl<string>(\"\", [Validators.required]),\n  });\n\n  isSubmitted = false;\n\n  get phone() {\n    return this.fg.get(\"phone\");\n  }\n\n  ngOnInit(): void {\n    this.phone?.valueChanges.subscribe(() => {\n      this.isSubmitted = false;\n    });\n  }\n\n  handleSubmit(): void {\n    this.phone?.markAsTouched();\n    if (this.fg.valid) {\n      this.isSubmitted = true;\n    }\n  }\n}\n"
  },
  {
    "path": "angular/demo/form/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <link rel=\"stylesheet\" href=\"../../../build/css/intlTelInput.css\" />\n  <link rel=\"stylesheet\" href=\"../../../build/css/demo.css\" />\n  <title>Angular App - Form Validation Demo</title>\n</head>\n\n<body>\n  <h1>Angular Form Validation Demo</h1>\n  <p>A simple Angular app using reactive forms with the IntlTelInput component for phone number entry and validation.</p>\n  <p>Enter a phone number and click \"Validate\" to see form validation in action.</p>\n  <app-root></app-root>\n  <script src=\"./form-bundle.js\"></script>\n</body>\n\n</html>\n\n"
  },
  {
    "path": "angular/demo/form/main.ts",
    "content": "import 'zone.js';\nimport \"@angular/compiler\";\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport { AppComponent } from './form.component';\n\nbootstrapApplication(AppComponent)\n  .catch((err) => console.error(err));"
  },
  {
    "path": "angular/demo/set-number/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <link rel=\"stylesheet\" href=\"../../../build/css/intlTelInput.css\" />\n  <link rel=\"stylesheet\" href=\"../../../build/css/demo.css\" />\n  <title>Angular App - Set Number Demo</title>\n</head>\n\n<body>\n  <h1>Angular App - Set Number Demo</h1>\n  <p>A simple Angular app, using the IntlTelInput component to handle phone number entry, calling setNumber and validation.</p>\n  <p>Click \"Set Number\" then click \"Validate\" to check that the angular internals have updated correctly.</p>\n  <app-root></app-root>\n  <script src=\"./set-number-bundle.js\"></script>\n</body>\n\n</html>\n\n"
  },
  {
    "path": "angular/demo/set-number/main.ts",
    "content": "import 'zone.js';\nimport \"@angular/compiler\";\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport { AppComponent } from './set-number.component';\n\nbootstrapApplication(AppComponent)\n  .catch((err) => console.error(err));"
  },
  {
    "path": "angular/demo/set-number/set-number.component.ts",
    "content": "import { Component, ViewChild } from '@angular/core';\nimport { IntlTelInputComponent, PHONE_ERROR_MESSAGES } from '../../src/intl-tel-input/angularWithUtils';\n\n@Component({\n  selector: \"app-root\",\n  template: `\n    <div>\n      <intl-tel-input\n        #telInput\n        (numberChange)=\"handleNumberChange($event)\"\n        (validityChange)=\"handleValidityChange($event)\"\n        (errorCodeChange)=\"handleErrorCodeChange($event)\"\n        [initOptions]=\"{\n          initialCountry: 'us',\n        }\"\n      />\n      <button class=\"button\" type=\"button\" (click)=\"handleSetNumber()\">\n        Set Number\n      </button>\n      <button class=\"button\" type=\"button\" (click)=\"handleSubmit()\">\n        Validate\n      </button>\n      @if (notice) {\n        <div class=\"notice\">{{ notice }}</div>\n      }\n    </div>\n  `,\n  standalone: true,\n  imports: [IntlTelInputComponent]\n})\nexport class AppComponent {\n  @ViewChild('telInput') telInput!: IntlTelInputComponent;\n\n  isValid: boolean | null = null;\n  number: string | null = null;\n  errorCode: number | null = null;\n  notice: string | null = null;\n\n  handleNumberChange(value: string): void {\n    this.number = value;\n  }\n\n  handleValidityChange(value: boolean): void {\n    this.isValid = value;\n  }\n\n  handleErrorCodeChange(value: number | null): void {\n    this.errorCode = value;\n  }\n\n  handleSetNumber(): void {\n    const instance = this.telInput?.getInstance();\n    if (instance) {\n      instance.setNumber('+14155552671');\n    }\n  }\n\n  handleSubmit(): void {\n    if (this.isValid) {\n      this.notice = `Valid number: ${this.number}`;\n    } else {\n      const errorMessage = PHONE_ERROR_MESSAGES[this.errorCode || 0] || \"Invalid number\";\n      this.notice = `Error: ${errorMessage}`;\n    }\n  }\n}"
  },
  {
    "path": "angular/demo/simple/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <link rel=\"stylesheet\" href=\"../../../build/css/intlTelInput.css\" />\n  <link rel=\"stylesheet\" href=\"../../../build/css/demo.css\" />\n  <title>Angular App - Simple Demo</title>\n</head>\n\n<body>\n  <h1>Angular App - Simple Demo</h1>\n  <p>A simple Angular app, using the IntlTelInput component to handle phone number entry.</p>\n  <app-root></app-root>\n  <script src=\"./simple-bundle.js\"></script>\n</body>\n\n</html>\n\n"
  },
  {
    "path": "angular/demo/simple/main.ts",
    "content": "import 'zone.js';\nimport \"@angular/compiler\";\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport { AppComponent } from './simple.component';\n\nbootstrapApplication(AppComponent)\n  .catch((err) => console.error(err));"
  },
  {
    "path": "angular/demo/simple/simple.component.ts",
    "content": "import { Component } from '@angular/core';\nimport { IntlTelInputComponent } from '../../src/intl-tel-input/angularWithUtils';\n\n@Component({\n  selector: \"app-root\",\n  template: `\n    <intl-tel-input\n      [initOptions]=\"{\n        initialCountry: 'us',\n      }\"\n    />\n  `,\n  standalone: true,\n  imports: [IntlTelInputComponent]\n})\nexport class AppComponent {}"
  },
  {
    "path": "angular/demo/toggle-disabled/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <link rel=\"stylesheet\" href=\"../../../build/css/intlTelInput.css\" />\n  <link rel=\"stylesheet\" href=\"../../../build/css/demo.css\" />\n  <title>Angular App - Toggle disabled prop Demo</title>\n</head>\n\n<body>\n  <h1>Angular App - Toggle disabled prop Demo</h1>\n  <p>A simple Angular app, using the IntlTelInput component to show it toggle.</p>\n  <p>Click the button to enable/disable component.</p>\n  <app-root></app-root>\n  <script src=\"./toggle-disabled-bundle.js\"></script>\n</body>\n\n</html>\n\n"
  },
  {
    "path": "angular/demo/toggle-disabled/main.ts",
    "content": "import 'zone.js';\nimport \"@angular/compiler\";\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport { AppComponent } from './toggle-disabled.component';\n\nbootstrapApplication(AppComponent)\n  .catch((err) => console.error(err));"
  },
  {
    "path": "angular/demo/toggle-disabled/toggle-disabled.component.ts",
    "content": "import { Component } from '@angular/core';\nimport { IntlTelInputComponent } from '../../src/intl-tel-input/angularWithUtils';\n\n@Component({\n  selector: \"app-root\",\n  template: `\n    <div>\n      <intl-tel-input\n        [disabled]=\"isDisabled\"\n      />\n      <button class=\"button\" type=\"button\" (click)=\"toggleDisabled()\">Toggle</button>\n    </div>\n  `,\n  standalone: true,\n  imports: [IntlTelInputComponent]\n})\nexport class AppComponent {\n  isDisabled: boolean = false;\n\n  toggleDisabled(): void {\n    this.isDisabled = !this.isDisabled;\n  }\n}"
  },
  {
    "path": "angular/demo/validation/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <link rel=\"stylesheet\" href=\"../../../build/css/intlTelInput.css\" />\n  <link rel=\"stylesheet\" href=\"../../../build/css/demo.css\" />\n  <title>Angular App - Validation Demo</title>\n</head>\n\n<body>\n  <h1>Angular App - Validation Demo</h1>\n  <p>A simple Angular app, using the IntlTelInput component to handle phone number entry and validation.</p>\n  <p>Enter a phone number below and click \"Validate\".</p>\n  <app-root></app-root>\n  <script src=\"./validation-bundle.js\"></script>\n</body>\n\n</html>\n\n"
  },
  {
    "path": "angular/demo/validation/main.ts",
    "content": "import 'zone.js';\nimport \"@angular/compiler\";\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport { AppComponent } from './validation.component';\n\nbootstrapApplication(AppComponent)\n  .catch((err) => console.error(err));"
  },
  {
    "path": "angular/demo/validation/validation.component.ts",
    "content": "import { Component } from '@angular/core';\nimport { IntlTelInputComponent, PHONE_ERROR_MESSAGES } from '../../src/intl-tel-input/angularWithUtils';\n\n@Component({\n  selector: \"app-root\",\n  template: `\n    <div>\n      <intl-tel-input\n        (numberChange)=\"handleNumberChange($event)\"\n        (validityChange)=\"handleValidityChange($event)\"\n        (errorCodeChange)=\"handleErrorCodeChange($event)\"\n        [initOptions]=\"{\n          initialCountry: 'us',\n        }\"\n      />\n      <button class=\"button\" type=\"button\" (click)=\"handleSubmit()\">\n        Validate\n      </button>\n      @if (notice) {\n        <div class=\"notice\">{{ notice }}</div>\n      }\n    </div>\n  `,\n  standalone: true,\n  imports: [IntlTelInputComponent]\n})\nexport class AppComponent {\n  isValid: boolean | null = null;\n  number: string | null = null;\n  errorCode: number | null = null;\n  notice: string | null = null;\n\n  handleNumberChange(value: string): void {\n    this.number = value;\n  }\n\n  handleValidityChange(value: boolean): void {\n    this.isValid = value;\n  }\n\n  handleErrorCodeChange(value: number | null): void {\n    this.errorCode = value;\n  }\n\n  handleSubmit(): void {\n    if (this.isValid) {\n      this.notice = `Valid number: ${this.number}`;\n    } else {\n      const errorMessage = PHONE_ERROR_MESSAGES[this.errorCode || 0] || \"Invalid number\";\n      this.notice = `Error: ${errorMessage}`;\n    }\n  }\n}"
  },
  {
    "path": "angular/src/intl-tel-input/angular.ts",
    "content": "import intlTelInput from \"../intl-tel-input\";\n//* Keep the TS imports separate, as the above line gets substituted in the angularWithUtils build process.\nimport { Iti } from \"../intl-tel-input\";\nimport {\n  Component,\n  Input,\n  OnDestroy,\n  ViewChild,\n  ElementRef,\n  Output,\n  EventEmitter,\n  forwardRef,\n  AfterViewInit,\n  OnChanges,\n  SimpleChanges,\n} from \"@angular/core\";\nimport {\n  ControlValueAccessor,\n  NG_VALUE_ACCESSOR,\n  NG_VALIDATORS,\n  Validator,\n  AbstractControl,\n  ValidationErrors,\n} from \"@angular/forms\";\nimport { SomeOptions } from \"../modules/types/public-api\";\n\nexport { intlTelInput };\n\nexport const PHONE_ERROR_MESSAGES: string[] = [\n  \"invalid\",\n  \"invalid-country-code\",\n  \"too-short\",\n  \"too-long\",\n  \"invalid-format\",\n];\n\n@Component({\n  selector: \"intl-tel-input\",\n  standalone: true,\n  template: `\n    <input\n      type=\"tel\"\n      #inputRef\n      (input)=\"handleInput()\"\n      (blur)=\"handleBlur($event)\"\n      (focus)=\"handleFocus($event)\"\n      (keydown)=\"handleKeyDown($event)\"\n      (keyup)=\"handleKeyUp($event)\"\n      (paste)=\"handlePaste($event)\"\n      (click)=\"handleClick($event)\"\n    />\n  `,\n  providers: [\n    {\n      provide: NG_VALUE_ACCESSOR,\n      useExisting: forwardRef(() => IntlTelInputComponent),\n      multi: true,\n    },\n    {\n      provide: NG_VALIDATORS,\n      useExisting: forwardRef(() => IntlTelInputComponent),\n      multi: true,\n    },\n  ],\n})\nexport class IntlTelInputComponent\n  implements\n    AfterViewInit,\n    OnDestroy,\n    OnChanges,\n    ControlValueAccessor,\n    Validator\n{\n  @ViewChild(\"inputRef\", { static: true })\n  inputRef!: ElementRef<HTMLInputElement>;\n\n  @Input() initialValue?: string;\n  @Input() usePreciseValidation: boolean = false;\n  @Input() inputProps: Record<string, string> = {};\n  @Input() disabled: boolean = false;\n  @Input() initOptions?: SomeOptions;\n\n  @Output() numberChange = new EventEmitter<string>();\n  @Output() countryChange = new EventEmitter<string>();\n  @Output() validityChange = new EventEmitter<boolean>();\n  @Output() errorCodeChange = new EventEmitter<number | null>();\n  @Output() blur = new EventEmitter<FocusEvent>();\n  @Output() focus = new EventEmitter<FocusEvent>();\n  @Output() keydown = new EventEmitter<KeyboardEvent>();\n  @Output() keyup = new EventEmitter<KeyboardEvent>();\n  @Output() paste = new EventEmitter<ClipboardEvent>();\n  @Output() click = new EventEmitter<MouseEvent>();\n\n  private iti?: Iti;\n  private appliedInputPropKeys = new Set<string>();\n\n  private lastEmittedNumber?: string;\n  private lastEmittedCountry?: string;\n  private lastEmittedValidity?: boolean;\n  private lastEmittedErrorCode?: number | null;\n\n  private countryChangeHandler = () => this.handleInput();\n  // eslint-disable-next-line class-methods-use-this\n  private onChange: (value: string) => void = () => {};\n  // eslint-disable-next-line class-methods-use-this\n  private onTouched: () => void = () => {};\n  // eslint-disable-next-line class-methods-use-this\n  private onValidatorChange: () => void = () => {};\n\n  ngAfterViewInit() {\n    if (this.inputRef.nativeElement) {\n      this.iti = intlTelInput(this.inputRef.nativeElement, this.initOptions);\n    }\n\n    this.inputRef.nativeElement.addEventListener(\n      \"countrychange\",\n      this.countryChangeHandler,\n    );\n\n    this.applyInputProps();\n\n    if (this.initialValue) {\n      this.iti?.setNumber(this.initialValue);\n    }\n\n    if (this.disabled) {\n      this.iti?.setDisabled(this.disabled);\n    }\n  }\n\n  ngOnChanges(changes: SimpleChanges) {\n    if (changes[\"disabled\"]) {\n      this.iti?.setDisabled(this.disabled);\n    }\n\n    if (changes[\"inputProps\"]) {\n      this.applyInputProps();\n    }\n  }\n\n  handleInput() {\n    if (!this.iti) return;\n\n    const num = this.iti.getNumber() || \"\";\n    const countryIso = this.iti.getSelectedCountryData().iso2 || \"\";\n\n    let hasChanged = false;\n    if (num !== this.lastEmittedNumber) {\n      this.lastEmittedNumber = num;\n      this.numberChange.emit(num);\n      this.onChange(num);\n      hasChanged = true;\n    }\n\n    if (countryIso !== this.lastEmittedCountry) {\n      this.lastEmittedCountry = countryIso;\n      this.countryChange.emit(countryIso);\n      hasChanged = true;\n    }\n\n    const isValid = this.usePreciseValidation\n      ? this.iti.isValidNumberPrecise()\n      : this.iti.isValidNumber();\n\n    const errorCode = isValid ? null : this.iti.getValidationError();\n\n    if (isValid !== this.lastEmittedValidity) {\n      this.lastEmittedValidity = isValid;\n      this.validityChange.emit(isValid);\n      hasChanged = true;\n    }\n\n    if (errorCode !== this.lastEmittedErrorCode) {\n      this.lastEmittedErrorCode = errorCode;\n      this.errorCodeChange.emit(errorCode);\n      hasChanged = true;\n    }\n\n    if (hasChanged) {\n      this.onValidatorChange();\n    }\n  }\n\n  handleBlur(event: FocusEvent) {\n    this.onTouched();\n    this.blur.emit(event);\n  }\n\n  handleFocus(event: FocusEvent) {\n    this.focus.emit(event);\n  }\n\n  handleKeyDown(event: KeyboardEvent) {\n    this.keydown.emit(event);\n  }\n\n  handleKeyUp(event: KeyboardEvent) {\n    this.keyup.emit(event);\n  }\n\n  handlePaste(event: ClipboardEvent) {\n    this.paste.emit(event);\n  }\n\n  handleClick(event: MouseEvent) {\n    this.click.emit(event);\n  }\n\n  /**\n   * This method must be called in `ngAfterViewInit` or later lifecycle hooks,\n   * not in `ngOnInit` or the `constructor`, as the component needs to be fully initialized.\n   */\n  getInstance(): Iti | null {\n    return this.iti;\n  }\n\n  /**\n   * This method must be called in `ngAfterViewInit` or later lifecycle hooks,\n   * not in `ngOnInit` or the `constructor`, as the component needs to be fully initialized.\n   */\n  getInput(): HTMLInputElement | null {\n    return this.inputRef.nativeElement;\n  }\n\n  ngOnDestroy() {\n    this.iti?.destroy();\n\n    this.inputRef.nativeElement.removeEventListener(\n      \"countrychange\",\n      this.countryChangeHandler,\n    );\n  }\n\n  private applyInputProps(): void {\n    const currentKeys = new Set<string>();\n    Object.entries(this.inputProps).forEach(([key, value]) => {\n      currentKeys.add(key);\n      this.inputRef.nativeElement.setAttribute(key, value);\n    });\n    this.appliedInputPropKeys.forEach((key) => {\n      if (!currentKeys.has(key)) {\n        this.inputRef.nativeElement.removeAttribute(key);\n      }\n    });\n    this.appliedInputPropKeys = currentKeys;\n  }\n\n  // ============ ControlValueAccessor Implementation ============\n\n  writeValue(value: string | null): void {\n    if (this.iti) {\n      this.iti.setNumber(value || \"\");\n    }\n  }\n\n  registerOnChange(fn: any): void {\n    this.onChange = fn;\n  }\n\n  registerOnTouched(fn: any): void {\n    this.onTouched = fn;\n  }\n\n  setDisabledState(isDisabled: boolean): void {\n    this.disabled = isDisabled;\n    this.iti?.setDisabled(isDisabled);\n  }\n\n  // ============ Validator Implementation ============\n\n  validate(control: AbstractControl): ValidationErrors | null {\n    if (!control.value || !this.iti) {\n      return null;\n    }\n\n    const isValid = this.usePreciseValidation\n      ? this.iti.isValidNumberPrecise()\n      : this.iti.isValidNumber();\n\n    if (isValid) {\n      return null;\n    }\n\n    const errorCode = this.iti.getValidationError();\n    return {\n      invalidPhone: {\n        errorCode,\n        errorMessage: PHONE_ERROR_MESSAGES[errorCode] ?? \"unknown\",\n      },\n    };\n  }\n\n  registerOnValidatorChange(fn: () => void): void {\n    this.onValidatorChange = fn;\n  }\n}\n"
  },
  {
    "path": "angular/src/intl-tel-input/angularWithUtils.ts",
    "content": "//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\nimport intlTelInput from \"./intlTelInputWithUtils\";\n//* Keep the TS imports separate, as the above line gets substituted in the angularWithUtils build process.\nimport { Iti } from \"../intl-tel-input\";\nimport {\n  Component,\n  Input,\n  OnDestroy,\n  ViewChild,\n  ElementRef,\n  Output,\n  EventEmitter,\n  forwardRef,\n  AfterViewInit,\n  OnChanges,\n  SimpleChanges,\n} from \"@angular/core\";\nimport {\n  ControlValueAccessor,\n  NG_VALUE_ACCESSOR,\n  NG_VALIDATORS,\n  Validator,\n  AbstractControl,\n  ValidationErrors,\n} from \"@angular/forms\";\nimport { SomeOptions } from \"../modules/types/public-api\";\n\nexport { intlTelInput };\n\nexport const PHONE_ERROR_MESSAGES: string[] = [\n  \"invalid\",\n  \"invalid-country-code\",\n  \"too-short\",\n  \"too-long\",\n  \"invalid-format\",\n];\n\n@Component({\n  selector: \"intl-tel-input\",\n  standalone: true,\n  template: `\n    <input\n      type=\"tel\"\n      #inputRef\n      (input)=\"handleInput()\"\n      (blur)=\"handleBlur($event)\"\n      (focus)=\"handleFocus($event)\"\n      (keydown)=\"handleKeyDown($event)\"\n      (keyup)=\"handleKeyUp($event)\"\n      (paste)=\"handlePaste($event)\"\n      (click)=\"handleClick($event)\"\n    />\n  `,\n  providers: [\n    {\n      provide: NG_VALUE_ACCESSOR,\n      useExisting: forwardRef(() => IntlTelInputComponent),\n      multi: true,\n    },\n    {\n      provide: NG_VALIDATORS,\n      useExisting: forwardRef(() => IntlTelInputComponent),\n      multi: true,\n    },\n  ],\n})\nexport class IntlTelInputComponent\n  implements\n    AfterViewInit,\n    OnDestroy,\n    OnChanges,\n    ControlValueAccessor,\n    Validator\n{\n  @ViewChild(\"inputRef\", { static: true })\n  inputRef!: ElementRef<HTMLInputElement>;\n\n  @Input() initialValue?: string;\n  @Input() usePreciseValidation: boolean = false;\n  @Input() inputProps: Record<string, string> = {};\n  @Input() disabled: boolean = false;\n  @Input() initOptions?: SomeOptions;\n\n  @Output() numberChange = new EventEmitter<string>();\n  @Output() countryChange = new EventEmitter<string>();\n  @Output() validityChange = new EventEmitter<boolean>();\n  @Output() errorCodeChange = new EventEmitter<number | null>();\n  @Output() blur = new EventEmitter<FocusEvent>();\n  @Output() focus = new EventEmitter<FocusEvent>();\n  @Output() keydown = new EventEmitter<KeyboardEvent>();\n  @Output() keyup = new EventEmitter<KeyboardEvent>();\n  @Output() paste = new EventEmitter<ClipboardEvent>();\n  @Output() click = new EventEmitter<MouseEvent>();\n\n  private iti?: Iti;\n  private appliedInputPropKeys = new Set<string>();\n\n  private lastEmittedNumber?: string;\n  private lastEmittedCountry?: string;\n  private lastEmittedValidity?: boolean;\n  private lastEmittedErrorCode?: number | null;\n\n  private countryChangeHandler = () => this.handleInput();\n  // eslint-disable-next-line class-methods-use-this\n  private onChange: (value: string) => void = () => {};\n  // eslint-disable-next-line class-methods-use-this\n  private onTouched: () => void = () => {};\n  // eslint-disable-next-line class-methods-use-this\n  private onValidatorChange: () => void = () => {};\n\n  ngAfterViewInit() {\n    if (this.inputRef.nativeElement) {\n      this.iti = intlTelInput(this.inputRef.nativeElement, this.initOptions);\n    }\n\n    this.inputRef.nativeElement.addEventListener(\n      \"countrychange\",\n      this.countryChangeHandler,\n    );\n\n    this.applyInputProps();\n\n    if (this.initialValue) {\n      this.iti?.setNumber(this.initialValue);\n    }\n\n    if (this.disabled) {\n      this.iti?.setDisabled(this.disabled);\n    }\n  }\n\n  ngOnChanges(changes: SimpleChanges) {\n    if (changes[\"disabled\"]) {\n      this.iti?.setDisabled(this.disabled);\n    }\n\n    if (changes[\"inputProps\"]) {\n      this.applyInputProps();\n    }\n  }\n\n  handleInput() {\n    if (!this.iti) return;\n\n    const num = this.iti.getNumber() || \"\";\n    const countryIso = this.iti.getSelectedCountryData().iso2 || \"\";\n\n    let hasChanged = false;\n    if (num !== this.lastEmittedNumber) {\n      this.lastEmittedNumber = num;\n      this.numberChange.emit(num);\n      this.onChange(num);\n      hasChanged = true;\n    }\n\n    if (countryIso !== this.lastEmittedCountry) {\n      this.lastEmittedCountry = countryIso;\n      this.countryChange.emit(countryIso);\n      hasChanged = true;\n    }\n\n    const isValid = this.usePreciseValidation\n      ? this.iti.isValidNumberPrecise()\n      : this.iti.isValidNumber();\n\n    const errorCode = isValid ? null : this.iti.getValidationError();\n\n    if (isValid !== this.lastEmittedValidity) {\n      this.lastEmittedValidity = isValid;\n      this.validityChange.emit(isValid);\n      hasChanged = true;\n    }\n\n    if (errorCode !== this.lastEmittedErrorCode) {\n      this.lastEmittedErrorCode = errorCode;\n      this.errorCodeChange.emit(errorCode);\n      hasChanged = true;\n    }\n\n    if (hasChanged) {\n      this.onValidatorChange();\n    }\n  }\n\n  handleBlur(event: FocusEvent) {\n    this.onTouched();\n    this.blur.emit(event);\n  }\n\n  handleFocus(event: FocusEvent) {\n    this.focus.emit(event);\n  }\n\n  handleKeyDown(event: KeyboardEvent) {\n    this.keydown.emit(event);\n  }\n\n  handleKeyUp(event: KeyboardEvent) {\n    this.keyup.emit(event);\n  }\n\n  handlePaste(event: ClipboardEvent) {\n    this.paste.emit(event);\n  }\n\n  handleClick(event: MouseEvent) {\n    this.click.emit(event);\n  }\n\n  /**\n   * This method must be called in `ngAfterViewInit` or later lifecycle hooks,\n   * not in `ngOnInit` or the `constructor`, as the component needs to be fully initialized.\n   */\n  getInstance(): Iti | null {\n    return this.iti;\n  }\n\n  /**\n   * This method must be called in `ngAfterViewInit` or later lifecycle hooks,\n   * not in `ngOnInit` or the `constructor`, as the component needs to be fully initialized.\n   */\n  getInput(): HTMLInputElement | null {\n    return this.inputRef.nativeElement;\n  }\n\n  ngOnDestroy() {\n    this.iti?.destroy();\n\n    this.inputRef.nativeElement.removeEventListener(\n      \"countrychange\",\n      this.countryChangeHandler,\n    );\n  }\n\n  private applyInputProps(): void {\n    const currentKeys = new Set<string>();\n    Object.entries(this.inputProps).forEach(([key, value]) => {\n      currentKeys.add(key);\n      this.inputRef.nativeElement.setAttribute(key, value);\n    });\n    this.appliedInputPropKeys.forEach((key) => {\n      if (!currentKeys.has(key)) {\n        this.inputRef.nativeElement.removeAttribute(key);\n      }\n    });\n    this.appliedInputPropKeys = currentKeys;\n  }\n\n  // ============ ControlValueAccessor Implementation ============\n\n  writeValue(value: string | null): void {\n    if (this.iti) {\n      this.iti.setNumber(value || \"\");\n    }\n  }\n\n  registerOnChange(fn: any): void {\n    this.onChange = fn;\n  }\n\n  registerOnTouched(fn: any): void {\n    this.onTouched = fn;\n  }\n\n  setDisabledState(isDisabled: boolean): void {\n    this.disabled = isDisabled;\n    this.iti?.setDisabled(isDisabled);\n  }\n\n  // ============ Validator Implementation ============\n\n  validate(control: AbstractControl): ValidationErrors | null {\n    if (!control.value || !this.iti) {\n      return null;\n    }\n\n    const isValid = this.usePreciseValidation\n      ? this.iti.isValidNumberPrecise()\n      : this.iti.isValidNumber();\n\n    if (isValid) {\n      return null;\n    }\n\n    const errorCode = this.iti.getValidationError();\n    return {\n      invalidPhone: {\n        errorCode,\n        errorMessage: PHONE_ERROR_MESSAGES[errorCode] ?? \"unknown\",\n      },\n    };\n  }\n\n  registerOnValidatorChange(fn: () => void): void {\n    this.onValidatorChange = fn;\n  }\n}\n"
  },
  {
    "path": "angular/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es6\",\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"esModuleInterop\": true, //* Required for react (etc) default imports.\n    \"experimentalDecorators\": true, //* Required for angular decorators.\n    \"emitDecoratorMetadata\": true, //* Required for angular decorators.\n    \"declaration\": true, //* Generate .d.ts files.\n    \"outDir\": \"build/temp\",\n    \"declarationDir\": \"build/types\",\n    \"rootDir\": \"./src\",\n    \"allowJs\": true, // allow importing .mjs files e.g. i18n files\n  },\n  \"angularCompilerOptions\": {\n    \"strictTemplates\": true,\n  },\n  \"include\": [\n    \"src/intl-tel-input/angular.ts\",\n    \"src/intl-tel-input/angularWithUtils.ts\",\n    \"src/intl-tel-input/i18n/index.ts\",\n  ],\n}"
  },
  {
    "path": "build.js",
    "content": "/* eslint-disable no-undef */\n/* eslint-disable @typescript-eslint/no-var-requires */\nconst { build } = require(\"esbuild\");\nconst packageJson = require(\"./package.json\");\n\nconst getBanner = (moduleName) =>\n  \"/*\\n\" +\n  ` * International Telephone Input v${packageJson.version}\\n` +\n  ` * ${packageJson.repository.url}\\n` +\n  \" * Licensed under the MIT license\\n\" +\n  \" */\\n\\n\" +\n  // we can remove this UMD hack once it is supported by esbuild: https://github.com/evanw/esbuild/issues/507\n  \"// UMD\\n\" +\n  \"(function(factory) {\\n\" +\n  \"  if (typeof module === 'object' && module.exports) {\\n\" +\n  \"    module.exports = factory();\\n\" +\n  \"  } else {\\n\" +\n  `    window.${moduleName} = factory();\\n` +\n  \"  }\\n\" +\n  \"}(() => {\\n\";\n\nconst footer =\n  \"\\n// UMD\\n\" +\n  \"  return factoryOutput.default;\\n\" +\n  \"}));\";\n\nconst shared = {\n  bundle: true,\n  logLevel: \"info\",\n  format: \"iife\",\n  globalName: \"factoryOutput\",\n  footer: {\n    js: footer,\n  },\n  define: {\n    \"process.env.VERSION\": `\"${packageJson.version}\"`,\n  },\n};\n\n//* build/js/intlTelInput.js\nbuild({\n  ...shared,\n  banner: {\n    js: getBanner(\"intlTelInput\"),\n  },\n  entryPoints: [\"src/js/intl-tel-input.ts\"],\n  minify: false,\n  outfile: \"build/js/intlTelInput.js\",\n});\n\n//* build/js/intlTelInput.min.js\nbuild({\n  ...shared,\n  banner: {\n    js: getBanner(\"intlTelInput\"),\n  },\n  entryPoints: [\"src/js/intl-tel-input.ts\"],\n  minify: true,\n  outfile: \"build/js/intlTelInput.min.js\",\n});\n\n//* build/js/data.js\nbuild({\n  ...shared,\n  banner: {\n    js: getBanner(\"allCountries\"),\n  },\n  entryPoints: [\"src/js/intl-tel-input/data.ts\"],\n  minify: false,\n  outfile: \"build/js/data.js\",\n});\n\n//* build/js/data.min.js\nbuild({\n  ...shared,\n  banner: {\n    js: getBanner(\"allCountries\"),\n  },\n  entryPoints: [\"src/js/intl-tel-input/data.ts\"],\n  minify: true,\n  outfile: \"build/js/data.min.js\",\n});\n\n//* build/js/intlTelInputWithUtils.js\nbuild({\n  ...shared,\n  banner: {\n    js: getBanner(\"intlTelInput\"),\n  },\n  entryPoints: [\"src/js/intl-tel-input/intlTelInputWithUtils.ts\"],\n  minify: false,\n  outfile: \"build/js/intlTelInputWithUtils.js\",\n});\n\n//* build/js/intlTelInputWithUtils.min.js\nbuild({\n  ...shared,\n  banner: {\n    js: getBanner(\"intlTelInput\"),\n  },\n  entryPoints: [\"src/js/intl-tel-input/intlTelInputWithUtils.ts\"],\n  minify: true,\n  outfile: \"build/js/intlTelInputWithUtils.min.js\",\n});\n\n//* build/js/i18n\nbuild({\n  charset: \"utf8\",\n  entryPoints: [\"src/js/intl-tel-input/i18n/**/*.ts\"],\n  outdir: \"build/js/i18n\",\n});"
  },
  {
    "path": "composer.json",
    "content": "{\n  \"name\": \"jackocnr/intl-tel-input\",\n  \"version\": \"26.8.1\",\n  \"description\": \"A JavaScript plugin for entering and validating international telephone numbers\",\n  \"keywords\": [\n    \"international\",\n    \"i18n\",\n    \"country\",\n    \"dial\",\n    \"code\",\n    \"telephone\",\n    \"tel\",\n    \"number\",\n    \"mobile\",\n    \"input\",\n    \"flag\"\n  ],\n  \"homepage\": \"https://github.com/jackocnr/intl-tel-input\",\n  \"type\": \"library\",\n  \"license\": \"MIT\",\n  \"authors\": [\n    {\n      \"name\": \"Jack O'Connor\",\n      \"homepage\": \"http://jackocnr.com\"\n    }\n  ],\n  \"minimum-stability\": \"stable\",\n  \"prefer-stable\": true\n}\n"
  },
  {
    "path": "cspell.json",
    "content": "{\n  \"$schema\": \"https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json\",\n  \"version\": \"0.2\",\n  \"allowCompoundWords\": true,\n  \"maxNumberOfProblems\": 1000,\n  // \"checkLimit\": 5000, //! Setting that is in the vscode eextension not in the cli\n  // \"diagnosticLevel\": \"Warning\", //! Setting that is in the vscode eextension not in the cli\n  \"language\": \"en_GB\",\n  \"useGitignore\": true,\n  \"minWordLength\": 4,\n  \"dictionaries\": [\n    \"css\",\n    \"en_GB\",\n    \"html\",\n    \"ignore\",\n    \"javascript\",\n    \"javascriptreact\",\n    \"json\",\n    \"jsonc\",\n    \"markdown\",\n    \"plaintext\",\n    \"scss\",\n    \"text\",\n    \"typescript\",\n    \"typescriptreact\",\n    \"COBOL\",\n    \"ACUCOBOL\"\n  ],\n  \"dictionaryDefinitions\": [],\n  \"import\": [],\n  \"ignorePaths\": [\n    //* Directories\n    \"**/.git/**\",\n    \"**/build/**\",\n    \"**/node_modules/**\",\n    \"**/vscode-extension/**\",\n    \"**/.vscode/**\",\n    \"**/.scraps/**\",\n    \"**/third_party/**\",\n    //* Files\n    \"**/package-lock.json\",\n    \"**/yarn.lock\",\n    \"**/Gemfile\",\n    \"**/cspell.json\",\n    \"**/.env\",\n    //* File Extensions\n    \"**/*.svg\",\n    //* Specific files and folders\n    \"src/js/intl-tel-input/data.ts\",\n    \"react/demo/validation-bundle.js\",\n    \"react/demo/simple-bundle.js\",\n    \"react/demo/toggle-disabled.js\"\n  ],\n  \"ignoreWords\": [],\n  \"flagWords\": [],\n  \"words\": [\n    \"Åland\",\n    \"AYTF\",\n    \"dropup\",\n    \"evenizer\",\n    \"FAYT\",\n    \"geoip\",\n    \"iife\",\n    \"ipapi\",\n    \"jackocnr\",\n    \"jsfast\",\n    \"librsvg\",\n    \"mledoze's\",\n    \"NANP\",\n    \"normalised\",\n    \"optipng\",\n    \"sass\",\n    \"tmpl\",\n    \"vars\",\n    \"Veaudry\"\n  ]\n}\n"
  },
  {
    "path": "demo.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <title>International Telephone Input</title>\n    <link rel=\"stylesheet\" href=\"build/css/intlTelInput.css\" />\n    <link rel=\"stylesheet\" href=\"build/css/demo.css\" />\n  </head>\n\n  <body>\n    <h1>International Telephone Input</h1>\n    <form>\n      <input id=\"phone\" name=\"phone\" type=\"tel\" value=\"\" />\n      <button class=\"button\" id=\"btn\" type=\"button\">Validate</button>\n      <span id=\"valid-msg\" class=\"hide\"></span>\n      <span id=\"error-msg\" class=\"hide\"></span>\n    </form>\n\n    <script src=\"build/js/intlTelInputWithUtils.js\"></script>\n    <script>\n      const input = document.querySelector(\"#phone\");\n      const iti = window.intlTelInput(input, {\n        // allowDropdown: false,\n        // allowedNumberTypes: [\"TOLL_FREE\"],\n        // allowNumberExtensions: true,\n        // allowPhonewords: true,\n        // autoPlaceholder: \"off\",\n        // containerClass: \"test\",\n        // countryNameLocale: \"ru\",\n        // countryOrder: [\"jp\", \"kr\"],\n        // countrySearch: false,\n        // customPlaceholder: function(selectedCountryPlaceholder, selectedCountryData) {\n        //   return \"e.g. \" + selectedCountryPlaceholder;\n        // },\n        // dropdownContainer: document.querySelector('#custom-container'),\n        // excludeCountries: [\"us\"],\n        // fixDropdownWidth: false,\n        // formatAsYouType: false,\n        // formatOnDisplay: false,\n        // geoIpLookup: function(success, failure) {\n        //   fetch(\"https://ipapi.co/json\")\n        //     .then((res) => res.json())\n        //     .then((data) => success(data.country_code))\n        //     .catch(() => failure());\n        // },\n        // hiddenInput: () => ({ phone: \"phone_full\", country: \"country_code\" }),\n        // i18n: { searchPlaceholder: \"Custom search\" },\n        // initialCountry: \"us\",\n        // loadUtils: () => import(\"/build/js/utils.js\"), // leading slash (and http-server) required for this to work in chrome\n        // nationalMode: false,\n        // onlyCountries: ['us', 'gb', 'ch', 'ca', 'do'],\n        // placeholderNumberType: \"MOBILE\",\n        // showFlags: false,\n        // separateDialCode: true,\n        // strictMode: true,\n        // useFullscreenPopup: true,\n      });\n      window.iti = iti; // useful for testing\n\n      const button = document.querySelector(\"#btn\");\n      const errorMsg = document.querySelector(\"#error-msg\");\n      const validMsg = document.querySelector(\"#valid-msg\");\n      const errorMap = [\"Invalid number\", \"Invalid country code\", \"Too short\", \"Too long\", \"Invalid number\"];\n\n      const reset = () => {\n        input.classList.remove(\"error\");\n        errorMsg.innerHTML = \"\";\n        validMsg.innerHTML = \"\";\n        errorMsg.classList.add(\"hide\");\n        validMsg.classList.add(\"hide\");\n      };\n\n      const showError = (msg) => {\n        input.classList.add(\"error\");\n        errorMsg.innerHTML = msg;\n        errorMsg.classList.remove(\"hide\");\n      };\n\n      // on click button: validate\n      button.addEventListener('click', () => {\n        reset();\n        if (!input.value.trim()) {\n          showError(\"Required\");\n        } else if (iti.isValidNumber()) {\n          validMsg.innerHTML = \"Valid number: \" + iti.getNumber();\n          validMsg.classList.remove(\"hide\");\n        } else {\n          const errorCode = iti.getValidationError();\n          const msg = errorMap[errorCode] || \"Invalid number\";\n          showError(msg);\n        }\n      });\n\n      // on keyup / change flag: reset\n      input.addEventListener('change', reset);\n      input.addEventListener('keyup', reset);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "functions/_middleware.js",
    "content": "/**\n * Cloudflare Workers middleware to inject a script tag with the user's geographical information (window.__IS_EUROPE)\n */\n\nconst EUROPE = new Set([\n  // EU members\n  \"AT\",\"BE\",\"BG\",\"HR\",\"CY\",\"CZ\",\"DK\",\"EE\",\"FI\",\"FR\",\n  \"DE\",\"GR\",\"HU\",\"IE\",\"IT\",\"LV\",\"LT\",\"LU\",\"MT\",\"NL\",\n  \"PL\",\"PT\",\"RO\",\"SK\",\"SI\",\"ES\",\"SE\",\n  // EEA + other European countries\n  \"GB\",\"NO\",\"IS\",\"LI\",\"CH\",\"AL\",\"AD\",\"BA\",\"BY\",\"GE\",\n  \"MD\",\"ME\",\"MK\",\"MC\",\"RS\",\"SM\",\"TR\",\"UA\",\"VA\",\n]);\n\nclass GeoInjector {\n  constructor(isEurope) {\n    this.isEurope = isEurope;\n  }\n\n  element(element) {\n    element.prepend(\n      `<script>window.__IS_EUROPE=${this.isEurope};</script>`,\n      { html: true },\n    );\n  }\n}\n\nexport async function onRequest(context) {\n  const country = context.request.cf?.country || \"\";\n  const isEurope = EUROPE.has(country);\n  const response = await context.next();\n\n  const contentType = response.headers.get(\"content-type\") || \"\";\n  if (!contentType.includes(\"text/html\")) {\n    return response;\n  }\n\n  // eslint-disable-next-line no-undef\n  return new HTMLRewriter()\n    .on(\"head\", new GeoInjector(isEurope))\n    .transform(response);\n}"
  },
  {
    "path": "grunt/bump.js",
    "content": "module.exports = function(grunt) {\n  return {\n    options: {\n      files: ['package.json', 'package-lock.json', 'composer.json'],\n      updateConfigs: ['package'],\n      commitFiles: ['-a'],\n      pushTo: 'origin'\n    }\n  };\n};\n"
  },
  {
    "path": "grunt/clean.js",
    "content": "module.exports = function(grunt) {\n  return {\n    buildCss: ['build/css/*'],\n    buildImg: ['build/img/*'],\n    buildJs: ['build/js/*'],\n\n    // Used by build:jsfast/watch. Preserves build/js/utils.js because\n    // src/js/intl-tel-input/utils.js is a symlink to it.\n    buildJsKeepUtils: ['build/js/*', '!build/js/utils.js'],\n\n    reactBuild: ['react/build/*'],\n    vueBuild: ['vue/build/*'],\n    angularBuild: ['angular/build/*'],\n    svelteBuild: ['svelte/build/*'],\n\n    // Intermediate artifacts used by the build/minify/replace steps.\n    tmpIntermediates: ['tmp/built.min.js', 'tmp/one.min.js'],\n\n    // build:utils output\n    utils: ['build/js/utils.js'],\n\n    // Convenience target for top-level build.\n    allBuild: [\n      'build/css/*',\n      'build/img/*',\n      'build/js/*',\n      'react/build/*',\n      'vue/build/*',\n      'angular/build/*',\n      'svelte/build/*',\n      'tmp/built.min.js',\n      'tmp/one.min.js',\n    ],\n  };\n};\n"
  },
  {
    "path": "grunt/closure-compiler.js",
    "content": "module.exports = function (grunt) {\n  return {\n    utils: {\n      files: {\n        \"build/js/utils.js\": \"src/js/utils.js\",\n      },\n      options: {\n        js: [\n          \"node_modules/google-closure-library/**.js\",\n          \"third_party/libphonenumber/javascript/i18n/phonenumbers/**.js\",\n          \"!third_party/libphonenumber/javascript/i18n/phonenumbers/demo-compiled.js\",\n          \"!third_party/libphonenumber/javascript/i18n/phonenumbers/metadatafortesting.js\",\n          \"!third_party/libphonenumber/javascript/i18n/phonenumbers/metadatalite.js\",\n          \"!third_party/libphonenumber/javascript/i18n/phonenumbers/regioncodefortesting.js\",\n          \"!third_party/libphonenumber/javascript/i18n/phonenumbers/**_test.js\",\n        ],\n        entry_point: \"goog:i18n.phonenumbers.demo\",\n        compilation_level: \"ADVANCED_OPTIMIZATIONS\",\n        output_wrapper: \"(function () {%output%})();\\nconst globalContext = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this;\\nconst utils = globalContext.intlTelInputUtilsTemp;\\ndelete globalContext.intlTelInputUtilsTemp;\\nexport default utils;\",\n      },\n    },\n  };\n};\n"
  },
  {
    "path": "grunt/connect.js",
    "content": "module.exports = function(grunt) {\n  return {\n    test: {\n      port: 8000\n    }\n  };\n};\n"
  },
  {
    "path": "grunt/cssmin.js",
    "content": "module.exports = function(grunt) {\n  return {\n    target: {\n      files: {\n        'build/css/intlTelInput.min.css': 'build/css/intlTelInput.css',\n        'build/css/intlTelInput-no-assets.min.css': 'build/css/intlTelInput-no-assets.css'\n      }\n    }\n  };\n};\n"
  },
  {
    "path": "grunt/generate-sprite.js",
    "content": "const fs = require('fs');\nconst path = require('path');\n// ts-node allows us to require TypeScript files\nrequire(\"ts-node\").register();\nconst supportedCountries = require('../src/js/intl-tel-input/data.ts').default;\n\nmodule.exports = function(grunt) {\n  grunt.registerTask('generate-sprite', async function() {\n    const done = this.async();\n    // Require \"sharp\" on demand, else Travis was breaking with \"Error: Could not load the \"sharp\" module using the linux-x64 runtime\" when Travis doesn't even use this task\n    const sharp = require('sharp');\n    // ensure /build/img/ dir exists before trying to write to it\n    const buildImgDir = path.join(__dirname, '..', 'build', 'img');\n    if (!fs.existsSync(buildImgDir)) {\n      fs.mkdirSync(buildImgDir, { recursive: true });\n    }\n\n    const supportedCountryFilenames = supportedCountries.map(country => `${country.iso2}.svg`).sort();\n\n    // customise this number to change the size of the flags (NOTE: flags are 4x3 ratio)\n    // must be a multiple of 3\n    const TARGET_HEIGHT = 12;\n\n    const TARGET_WIDTH = (TARGET_HEIGHT / 3) * 4;\n    const FLAG_MARGIN = 0;\n\n    const specialCases = {\n      'ac.svg': 'sh-ac.svg', // Ascension Island\n      // Add more special cases here if needed\n    };\n\n    const handleSpecialCases = (filename) => specialCases[filename] || filename;\n\n    const generateFlagMetadataAndSprite = async () => {\n      try {\n        const fileWarning = \"//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\";\n        const flagsPath = 'node_modules/flag-icons/flags/4x3';\n        const outputFile = 'src/css/_metadata.scss';\n        const spriteFile1xWebP = \"build/img/flags.webp\";\n        const spriteFile2xWebP = \"build/img/flags@2x.webp\";\n        const spriteFile1xPNG = \"build/img/flags.png\";\n        const spriteFile2xPNG = \"build/img/flags@2x.png\";\n        let outputFileContent = '';\n\n        let totalWidth = supportedCountryFilenames.length * (TARGET_WIDTH + FLAG_MARGIN) - FLAG_MARGIN;\n        const maxHeight = TARGET_HEIGHT;\n\n        let flagsMetadata = \"$flags: (\\n\";\n        let currentOffset = 0;\n\n        const scaledImages1x = [];\n        const scaledImages2x = [];\n\n        for (const filename of supportedCountryFilenames) {\n          const countryCode = filename.split('.')[0];\n          const processedFilename = handleSpecialCases(filename);\n          const imagePath = path.join(flagsPath, processedFilename);\n          const imagePathExists = fs.existsSync(imagePath);\n\n          if (!imagePathExists) {\n            console.log(`WARNING: Missing flag image: ${imagePath} - skipping this flag.`);\n            break;\n          }\n\n          const svgBuffer = fs.readFileSync(imagePath);\n\n          const pngBuffer1x = await sharp(svgBuffer)\n            .resize({\n              width: TARGET_WIDTH,\n              height: TARGET_HEIGHT,\n              fit: sharp.fit.fill,\n              position: sharp.strategy.centre\n            })\n            .ensureAlpha()\n            .png({ compressionLevel: 9, adaptiveFiltering: true, force: true })\n            .toBuffer();\n\n          const pngBuffer2x = await sharp(svgBuffer)\n            .resize({\n              width: TARGET_WIDTH * 2,\n              height: TARGET_HEIGHT * 2,\n              fit: sharp.fit.fill,\n              position: sharp.strategy.centre\n            })\n            .ensureAlpha()\n            .png({ compressionLevel: 9, adaptiveFiltering: true, force: true })\n            .toBuffer();\n\n          scaledImages1x.push({\n            buffer: pngBuffer1x,\n            offset: currentOffset\n          });\n\n          scaledImages2x.push({\n            buffer: pngBuffer2x,\n            offset: currentOffset * 2\n          });\n\n          flagsMetadata += `  ${countryCode}: (\\n`;\n          flagsMetadata += `    offset: ${-currentOffset}px,\\n`;\n          flagsMetadata += \"  ),\\n\";\n\n          currentOffset += TARGET_WIDTH + FLAG_MARGIN;\n        }\n        flagsMetadata += \");\";\n\n        // Create 1x sprites\n        await createSprite(scaledImages1x, totalWidth, maxHeight, spriteFile1xWebP, 'webp');\n        await createSprite(scaledImages1x, totalWidth, maxHeight, spriteFile1xPNG, 'png');\n        console.log(`1x combined images saved as ${spriteFile1xWebP} and ${spriteFile1xPNG}`);\n\n        // Create 2x sprites\n        await createSprite(scaledImages2x, totalWidth * 2, maxHeight * 2, spriteFile2xWebP, 'webp');\n        await createSprite(scaledImages2x, totalWidth * 2, maxHeight * 2, spriteFile2xPNG, 'png');\n        console.log(`2x combined images saved as ${spriteFile2xWebP} and ${spriteFile2xPNG}`);\n\n        // Generate SCSS content\n        outputFileContent += fileWarning + \"\\n\\n\";\n\n        outputFileContent += `$flags-sprite-1x: (\\n`;\n        outputFileContent += `  height: ${maxHeight}px,\\n`;\n        outputFileContent += `  width: ${totalWidth}px,\\n`;\n        outputFileContent += \");\\n\\n\";\n\n        outputFileContent += `$flag-width: ${TARGET_WIDTH}px;\\n\\n`;\n        outputFileContent += `$flag-height: ${TARGET_HEIGHT}px;\\n\\n`;\n\n        outputFileContent += flagsMetadata + \"\\n\\n\";\n        outputFileContent += fileWarning + \"\\n\";\n\n        fs.writeFileSync(outputFile, outputFileContent);\n        console.log('SCSS file generated successfully.');\n        done();\n      } catch (error) {\n        console.error('Error:', error);\n        done(error);\n      }\n    };\n\n  const createSprite = async (images, width, height, outputFile, format) => {\n      const combinedImage = sharp({\n        create: {\n          width: width,\n          height: height,\n          channels: 4,\n          background: { r: 0, g: 0, b: 0, alpha: 0 }\n        }\n      });\n\n      const compositeOperations = images.map((img) => ({\n        input: img.buffer,\n        left: img.offset,\n        top: 0\n      }));\n\n      let processedImage = combinedImage.composite(compositeOperations);\n\n      if (format === 'webp') {\n        processedImage = processedImage.webp({\n          quality: 100,\n          lossless: true,\n          effort: 6\n        });\n      } else if (format === 'png') {\n        processedImage = processedImage.png({\n          compressionLevel: 9,\n          adaptiveFiltering: true,\n          force: true\n        });\n      }\n\n      await processedImage.toFile(outputFile);\n    };\n\n    generateFlagMetadataAndSprite();\n  });\n};"
  },
  {
    "path": "grunt/replace.js",
    "content": "module.exports = function(grunt) {\n  return {\n\n    /**************\n     * Update version numbers\n     **************/\n    siteDocs: {\n      options: {\n        patterns: [\n          {\n            match: /intl-tel-input@([0-9.]+)\\/build/g,\n            replacement: 'intl-tel-input@<%= package.version %>/build'\n          }\n        ]\n      },\n      files: {\n        'site/src/docs/markdown/options.md': 'site/src/docs/markdown/options.md',\n        'site/src/docs/markdown/getting_started.md': 'site/src/docs/markdown/getting_started.md',\n      }\n    },\n    issueTemplate: {\n      options: {\n        patterns: [\n          {\n            match: /the latest version \\(v[0-9]+\\.[0-9]+\\.[0-9]+\\)/,\n            replacement: 'the latest version (v<%= package.version %>)'\n          }\n        ]\n      },\n      files: {\n        '.github/ISSUE_TEMPLATE/1_bug_report.yml': '.github/ISSUE_TEMPLATE/1_bug_report.yml'\n      }\n    },\n    // grunt bump already updates the version number at the beginning of package-lock, but not the \"inner\" one (aprx line 9), so do that here\n    packageLockInner: {\n      options: {\n        patterns: [\n          {\n            match: /\"name\": \"intl-tel-input\",\\n      \"version\": \"[0-9]+\\.[0-9]+\\.[0-9]+\"/,\n            replacement: '\"name\": \"intl-tel-input\",\\n      \"version\": \"<%= package.version %>\"'\n          }\n        ]\n      },\n      files: {\n        'package-lock.json': 'package-lock.json'\n      }\n    },\n\n    /**************\n     * Generate reactWithUtils.tsx\n     **************/\n    reactWithUtils: {\n      options: {\n        patterns: [\n          {\n            match: /import intlTelInput from \\\"\\.\\.\\/intl\\-tel\\-input\\\"\\;/,\n            replacement: '//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\\nimport intlTelInput from \"./intlTelInputWithUtils\";'\n          }\n        ]\n      },\n      files: {\n        'react/src/intl-tel-input/reactWithUtils.tsx': 'react/src/intl-tel-input/react.tsx',\n      }\n    },\n\n    /**************\n     * Generate vue/src/IntlTelInputWithUtils.vue\n     **************/\n    vueWithUtils: {\n      options: {\n        patterns: [\n          {\n            match: /\\<script setup lang=\\\"ts\\\"\\>\\simport intlTelInput from \\\"\\.\\/intl\\-tel\\-input\\\"\\;/,\n            replacement: '<!-- THIS FILE IS AUTO-GENERATED. DO NOT EDIT. -->\\n<script setup lang=\"ts\">\\nimport intlTelInput from \"./intl-tel-input/intlTelInputWithUtils\";'\n          }\n        ]\n      },\n      files: {\n        'vue/src/IntlTelInputWithUtils.vue': 'vue/src/IntlTelInput.vue',\n      }\n    },\n\n    /**************\n     * Generate angular/src/angularWithUtils.ts\n     **************/\n    angularWithUtils: {\n      options: {\n        patterns: [\n          {\n            match: /import intlTelInput from \\\"\\.\\.\\/intl\\-tel\\-input\\\"\\;/,\n            replacement: '//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\\nimport intlTelInput from \"./intlTelInputWithUtils\";'\n          }\n        ]\n      },\n      files: {\n        'angular/src/intl-tel-input/angularWithUtils.ts': 'angular/src/intl-tel-input/angular.ts',\n      }\n    },\n\n    /**************\n     * Generate svelte/src/IntlTelInputWithUtils.svelte\n     **************/\n    svelteWithUtils: {\n      options: {\n        patterns: [\n          {\n            match: /\\<script lang=\"ts\"\\>\\s*import intlTelInput from \"\\.\\.\\/intl\\-tel\\-input\"\\;/,\n            replacement: '<!-- THIS FILE IS AUTO-GENERATED. DO NOT EDIT. -->\\n<script lang=\"ts\">\\nimport intlTelInput from \"./intlTelInputWithUtils\";'\n          }\n        ]\n      },\n      files: {\n        'svelte/src/intl-tel-input/IntlTelInputWithUtils.svelte': 'svelte/src/intl-tel-input/IntlTelInput.svelte',\n      }\n    },\n  };\n};"
  },
  {
    "path": "grunt/sass.js",
    "content": "const sass = require('sass');\n\nmodule.exports = function(grunt) {\n  return {\n    main: {\n      options: {\n        implementation: sass,\n        sourcemap: \"none\",\n        style: \"compressed\"\n      },\n      files: {\n        'build/css/intlTelInput-no-assets.css': 'src/css/intlTelInput.scss',\n        'build/css/intlTelInput.css': 'src/css/intlTelInputWithAssets.scss'\n      }\n    },\n    demo: {\n      options: {\n        implementation: sass,\n        sourcemap: \"none\"\n      },\n      files: {\n        'build/css/demo.css': 'src/css/demo.scss'\n      }\n    }\n  };\n};\n"
  },
  {
    "path": "grunt/shell.js",
    "content": "const os = require('os');\n// On MacOS, sed requires the empty quotes in order to avoid creating a backup file, whereas on Linux (etc) it errors out with the empty quotes.\nconst sedArg = os.platform() === 'darwin' ? '-i \"\"' : '-i';\n\nmodule.exports = function(grunt) {\n  return {\n    buildReact: {\n      command: 'node react/build.js'\n    },\n    buildVue: {\n      command: 'vite build --config vue/vite.config.mts'\n    },\n    buildAngular: {\n      command: 'node angular/build.js'\n    },\n    buildSvelte: {\n      command: 'vite build --config svelte/viteConfig.mjs && vite build --config svelte/viteConfigWithUtils.mjs'\n    },\n    buildJs: {\n      command: 'node build.js'\n    },\n    genTsDeclaration: {\n      //* Clean up the module names by removing the /index suffix as this is how they will be used.\n      command: `tsc --p tsconfig.json && sed ${sedArg} -e \"s/\\\\/index\\\\\"/\\\\\"/g\" build/js/intlTelInput.d.ts`\n    },\n    genReactTsDeclaration: {\n      //* Clean up the module names by removing the /index suffix as this is how they will be used.\n      command: `tsc --p react/tsconfig.json && sed ${sedArg} -e \"s/\\\\/index\\\\\"/\\\\\"/g\" react/build/IntlTelInput.d.ts`\n    },\n    genAngularTsDeclarationAndJs: {\n      command: 'ngc --p angular/tsconfig.json'\n    },\n    eslint: {\n      command: 'eslint .'\n    },\n    test: {\n      command: 'npm run test'\n    },\n    checkLpnMetadata: {\n      command: 'node scripts/check-lpn-metadata.cjs'\n    },\n  };\n};\n"
  },
  {
    "path": "grunt/translations.js",
    "content": "const fs = require('fs');\nconst path = require('path');\n// ts-node allows us to require TypeScript files\nrequire(\"ts-node\").register();\n\nmodule.exports = function(grunt) {\n  grunt.registerTask('translations', 'Generate country translations', function() {\n    const supportedLocalesDirectory = \"src/js/intl-tel-input/i18n\";\n    const rootIndexFilePath = path.join(supportedLocalesDirectory, 'index.ts');\n    let rootIndexFileContent = \"//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\\n\";\n\n    const localeToExportName = (locale) => {\n      // Convert locale folder names (e.g. \"zh-hk\") into valid JS identifiers (e.g. \"zhHk\").\n      // Keep the first segment as-is, then capitalize the first letter of subsequent segments.\n      const parts = String(locale)\n        .trim()\n        .split(/[^a-zA-Z0-9]+/)\n        .filter(Boolean);\n\n      if (parts.length === 0) return '';\n\n      const [first, ...rest] = parts;\n      return first + rest.map(part => part.charAt(0).toUpperCase() + part.slice(1)).join('');\n    };\n\n    //* Get list of translation locales that exist in this project.\n    const supportedLocales = fs.readdirSync(supportedLocalesDirectory, { withFileTypes: true })\n      .filter(dir => dir.isDirectory())\n      .map(dir => dir.name);\n\n    grunt.log.writeln(`Supported locales: ${supportedLocales.join(\", \")}.\\n`);\n\n    //* For each supported locale: update the root index.ts file.\n    supportedLocales.forEach(locale => {\n      const translationFilePath = path.join(supportedLocalesDirectory, locale, 'index.ts');\n      const translationExists = fs.existsSync(translationFilePath);\n\n      //* If the interface file does not exist, skip the iteration.\n      if (!translationExists) {\n        grunt.log.writeln(`WARNING: Missing interface file: ${translationFilePath} - skipping this locale.\\n`);\n        return;\n      }\n\n      //* Add the locale export to the content of the root index.ts file\n      const exportName = localeToExportName(locale);\n      if (!exportName) {\n        grunt.log.writeln(`WARNING: Could not generate export name for locale: ${locale} - skipping this locale.\\n`);\n        return;\n      }\n      rootIndexFileContent += `export { default as ${exportName} } from \"./${locale}\";\\n`;\n    });\n    fs.writeFileSync(rootIndexFilePath, rootIndexFileContent);\n  });\n};\n"
  },
  {
    "path": "grunt/watch.js",
    "content": "module.exports = function(grunt) {\n  return {\n    js: {\n      // only TS files (excludes utils.js, which is watched separately below)\n      files: \"src/js/**/*.ts\",\n      tasks: \"build:jsfast\"\n    },\n    utils: {\n      files: \"src/js/utils.js\",\n      tasks: [\"build:utils\", \"build:jsfast\"]\n    },\n    translations: {\n      files: \"src/i18n/**/*\",\n      tasks: \"build:translations\",\n    },\n    react: {\n      files: [\n        \"react/src/intl-tel-input/react.tsx\",\n        \"react/demo/set-number/SetNumberApp.tsx\",\n        \"react/demo/simple/SimpleApp.tsx\",\n        \"react/demo/toggle-disabled/ToggleDisabledApp.tsx\",\n        \"react/demo/validation/ValidationApp.tsx\",\n      ],\n      tasks: \"build:react\"\n    },\n    vue: {\n      files: [\"vue/src/IntlTelInput.vue\"],\n      tasks: \"build:vue\"\n    },\n    svelte: {\n      files: [\"svelte/src/intl-tel-input/IntlTelInput.svelte\"],\n      tasks: \"build:svelte\"\n    },\n    angular: {\n      files: [\n        \"angular/src/intl-tel-input/angular.ts\",\n        \"angular/demo/form/form.component.ts\",\n        \"angular/demo/set-number/set-number.component.ts\",\n        \"angular/demo/simple/simple.component.ts\",\n        \"angular/demo/toggle-disabled/toggle-disabled.component.ts\",\n        \"angular/demo/validation/validation.component.ts\",\n      ],\n      tasks: \"build:angular\"\n    },\n    css: {\n      files: \"src/css/**/*\",\n      tasks: \"build:css\"\n    }\n  };\n};\n"
  },
  {
    "path": "index.js",
    "content": "module.exports = require(\"./build/js/intlTelInput\");\n"
  },
  {
    "path": "jest.config.js",
    "content": "/** @type {import('jest').Config} */\nmodule.exports = {\n  roots: [\n    \"<rootDir>/tests\",\n  ],\n  moduleDirectories: [\n    \"node_modules\",\n    \"build/js\",\n  ],\n  transform: {\n    // Support TypeScript source files directly in tests (lightweight)\n    \"^.+\\\\.(ts|tsx)$\": [\n      \"babel-jest\",\n      {\n        presets: [\n          [\"@babel/preset-typescript\", { allowDeclareFields: true }],\n        ],\n        plugins: [\n          \"@babel/plugin-transform-modules-commonjs\",\n          \"babel-plugin-add-module-exports\",\n        ],\n      },\n    ],\n    // Special handling for the utils ESM file\n    \"utils.js$\": [\n      \"babel-jest\",\n      {\n        plugins: [\n          \"@babel/plugin-transform-modules-commonjs\",\n          \"babel-plugin-add-module-exports\",\n        ],\n      },\n    ],\n  },\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"intl-tel-input\",\n  \"version\": \"26.8.1\",\n  \"description\": \"A JavaScript plugin for entering and validating international telephone numbers\",\n  \"license\": \"MIT\",\n  \"author\": \"Jack O'Connor (http://jackocnr.com)\",\n  \"scripts\": {\n    \"test\": \"npm run test:js && npm run test:e2e\",\n    \"test:js\": \"jest\",\n    \"test:e2e\": \"playwright test\",\n    \"test:e2e:ui\": \"playwright test --ui\",\n    \"test:e2e:linux\": \"./scripts/playwright-linux-docker.sh\",\n    \"test:e2e:linux:update\": \"./scripts/playwright-linux-docker.sh --update-snapshots\",\n    \"lint:js\": \"eslint .\",\n    \"lint:spelling\": \"cspell --dot --gitignore --no-progress '**'\",\n    \"watch\": \"grunt watch\",\n    \"build\": \"grunt build\",\n    \"build:js\": \"grunt build:js\",\n    \"build:jsfast\": \"grunt build:jsfast\",\n    \"build:translations\": \"grunt build:translations\",\n    \"build:utils\": \"grunt build:utils\",\n    \"build:css\": \"grunt build:css\",\n    \"build:img\": \"grunt build:img\",\n    \"build:react\": \"grunt build:react\",\n    \"build:vue\": \"grunt build:vue\",\n    \"build:angular\": \"grunt build:angular\",\n    \"build:svelte\": \"grunt build:svelte\",\n    \"prepublishOnly\": \"grunt build\",\n    \"vue:demo\": \"vite --config vue/demo/validation/vite.config.js\",\n    \"svelte:demo\": \"vite --config svelte/demo/validation/vite.config.mjs\"\n  },\n  \"devDependencies\": {\n    \"@angular/compiler\": \"^19.2.14\",\n    \"@angular/compiler-cli\": \"^19.2.14\",\n    \"@angular/core\": \"^19.1.4\",\n    \"@angular/forms\": \"^19.1.4\",\n    \"@angular/platform-browser\": \"^19.1.4\",\n    \"@babel/plugin-transform-modules-commonjs\": \"^7.25.7\",\n    \"@babel/preset-typescript\": \"^7.27.1\",\n    \"@playwright/test\": \"^1.58.0\",\n    \"@sveltejs/vite-plugin-svelte\": \"^5.0.3\",\n    \"@testing-library/jest-dom\": \"^6.4.6\",\n    \"@testing-library/user-event\": \"^14.5.2\",\n    \"@types/node\": \"^22.10.5\",\n    \"@types/react\": \"^18.2.74\",\n    \"@types/react-dom\": \"^18.2.24\",\n    \"@typescript-eslint/eslint-plugin\": \"^8.1.0\",\n    \"@typescript-eslint/parser\": \"^8.1.0\",\n    \"@vitejs/plugin-vue\": \"^5.2.1\",\n    \"@vue/tsconfig\": \"^0.7.0\",\n    \"babel-plugin-add-module-exports\": \"^1.0.4\",\n    \"cspell\": \"^8.6.1\",\n    \"esbuild\": \"^0.25.0\",\n    \"eslint\": \"^8.57.0\",\n    \"eslint-import-resolver-typescript\": \"^3.6.1\",\n    \"eslint-plugin-import\": \"^2.29.1\",\n    \"eslint-plugin-jest\": \"^28.5.0\",\n    \"eslint-plugin-react\": \"^7.34.1\",\n    \"eslint-plugin-react-hooks\": \"^4.6.2\",\n    \"fast-xml-parser\": \"^5.2.5\",\n    \"flag-icons\": \"^7.2.3\",\n    \"google-closure-compiler\": \"^20240317.0.0\",\n    \"google-closure-library\": \"^20230802.0.0\",\n    \"grunt\": \"^1.6.1\",\n    \"grunt-bump\": \"^0.8.0\",\n    \"grunt-cli\": \"^1.2.0\",\n    \"grunt-contrib-clean\": \"^2.0.1\",\n    \"grunt-contrib-connect\": \"^5.0.0\",\n    \"grunt-contrib-cssmin\": \"^5.0.0\",\n    \"grunt-contrib-watch\": \"^1.1.0\",\n    \"grunt-replace\": \"^2.0.2\",\n    \"grunt-sass\": \"^3.0.0\",\n    \"grunt-shell\": \"^4.0.0\",\n    \"http-server\": \"^14.1.1\",\n    \"jest\": \"^29.7.0\",\n    \"jest-environment-jsdom\": \"^29.7.0\",\n    \"jquery\": \"^3.1.1\",\n    \"load-grunt-config\": \"^4.0.1\",\n    \"playwright\": \"^1.58.0\",\n    \"prettier\": \"^3.2.5\",\n    \"prettier-eslint\": \"^16.4.2\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\",\n    \"sass\": \"^1.83.1\",\n    \"sharp\": \"^0.33.5\",\n    \"svelte\": \"^5.46.4\",\n    \"time-grunt\": \"^2.0.0\",\n    \"ts-node\": \"^10.9.2\",\n    \"typescript\": \"^5.5.3\",\n    \"vite\": \"^6.1.0\",\n    \"vite-plugin-dts\": \"^4.4.0\",\n    \"vue\": \"^3.5.13\",\n    \"vue-tsc\": \"^2.2.0\",\n    \"zone.js\": \"^0.15.0\"\n  },\n  \"files\": [\n    \"build/*\",\n    \"react/build/*\",\n    \"vue/build/*\",\n    \"angular/build/*\",\n    \"svelte/build/*\",\n    \"CHANGELOG.md\",\n    \"LICENSE\",\n    \"package.json\",\n    \"package-lock.json\",\n    \"README.md\",\n    \"index.js\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/jackocnr/intl-tel-input.git\"\n  },\n  \"homepage\": \"https://intl-tel-input.com\",\n  \"style\": \"build/css/intlTelInput.css\",\n  \"main\": \"./build/js/intlTelInput.js\",\n  \"types\": \"./build/js/intlTelInput.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./build/js/intlTelInput.d.ts\",\n      \"import\": \"./build/js/intlTelInput.js\",\n      \"default\": \"./build/js/intlTelInput.js\"\n    },\n    \"./intlTelInputWithUtils\": {\n      \"types\": \"./build/js/intlTelInput.d.ts\",\n      \"import\": \"./build/js/intlTelInputWithUtils.js\",\n      \"default\": \"./build/js/intlTelInputWithUtils.js\"\n    },\n    \"./data\": \"./build/js/data.js\",\n    \"./utils\": \"./build/js/utils.js\",\n    \"./react\": {\n      \"types\": \"./react/build/IntlTelInput.d.ts\",\n      \"require\": \"./react/build/IntlTelInput.cjs\",\n      \"import\": \"./react/build/IntlTelInput.js\",\n      \"default\": \"./react/build/IntlTelInput.js\"\n    },\n    \"./reactWithUtils\": {\n      \"types\": \"./react/build/IntlTelInput.d.ts\",\n      \"require\": \"./react/build/IntlTelInputWithUtils.cjs\",\n      \"import\": \"./react/build/IntlTelInputWithUtils.js\",\n      \"default\": \"./react/build/IntlTelInputWithUtils.js\"\n    },\n    \"./vue\": {\n      \"types\": \"./vue/build/exports/IntlTelInput.d.ts\",\n      \"import\": \"./vue/build/exports/IntlTelInput.mjs\"\n    },\n    \"./vueWithUtils\": {\n      \"types\": \"./vue/build/exports/IntlTelInputWithUtils.d.ts\",\n      \"import\": \"./vue/build/exports/IntlTelInputWithUtils.mjs\"\n    },\n    \"./angular\": {\n      \"types\": \"./angular/build/types/intl-tel-input/angular.d.ts\",\n      \"import\": \"./angular/build/IntlTelInput.js\",\n      \"default\": \"./angular/build/IntlTelInput.js\"\n    },\n    \"./angularWithUtils\": {\n      \"types\": \"./angular/build/types/intl-tel-input/angularWithUtils.d.ts\",\n      \"import\": \"./angular/build/IntlTelInputWithUtils.js\",\n      \"default\": \"./angular/build/IntlTelInputWithUtils.js\"\n    },\n    \"./svelte\": {\n      \"import\": \"./svelte/build/IntlTelInput.mjs\"\n    },\n    \"./svelteWithUtils\": {\n      \"import\": \"./svelte/build/IntlTelInputWithUtils.mjs\"\n    },\n    \"./i18n\": {\n      \"types\": \"./build/js/intlTelInput.d.ts\",\n      \"import\": \"./build/js/i18n/index.js\",\n      \"default\": \"./build/js/i18n/index.js\"\n    },\n    \"./i18n/*\": {\n      \"types\": \"./build/js/intlTelInput.d.ts\",\n      \"import\": \"./build/js/i18n/*/index.js\",\n      \"default\": \"./build/js/i18n/*/index.js\"\n    },\n    \"./styles\": \"./build/css/intlTelInput.css\",\n    \"./*\": \"./*\"\n  },\n  \"typesVersions\": {\n    \"*\": {\n      \".\": [\n        \"build/js/intlTelInput.d.ts\"\n      ],\n      \"react\": [\n        \"react/build/IntlTelInput.d.ts\"\n      ],\n      \"angular\": [\n        \"angular/build/types/intl-tel-input/angular.d.ts\"\n      ]\n    }\n  },\n  \"keywords\": [\n    \"international\",\n    \"country\",\n    \"dial code\",\n    \"telephone\",\n    \"phone\",\n    \"mobile\",\n    \"input\",\n    \"flag\",\n    \"dropdown\",\n    \"javascript\",\n    \"plugin\",\n    \"css\",\n    \"html\",\n    \"validation\",\n    \"formatting\",\n    \"react\",\n    \"vue\",\n    \"angular\",\n    \"svelte\",\n    \"component\",\n    \"typescript\"\n  ],\n  \"workspaces\": [\n    \"site\"\n  ]\n}\n"
  },
  {
    "path": "playwright.config.ts",
    "content": "import { defineConfig } from \"@playwright/test\";\n\nexport default defineConfig({\n  testDir: \"tests-e2e\",\n  timeout: 30_000,\n  retries: 0,\n  snapshotPathTemplate: \"{testDir}/{testFilePath}-snapshots/{arg}{-projectName}{ext}\",\n  reporter: [[\"list\"], [\"html\", { open: \"never\" }]],\n  expect: {\n    toHaveScreenshot: {\n      // Reduce flakiness from caret blink / CSS animations.\n      animations: \"disabled\",\n      caret: \"hide\",\n      scale: \"css\",\n    },\n  },\n  use: {\n    baseURL: \"http://localhost:4173\",\n    trace: \"on-first-retry\",\n    viewport: { width: 900, height: 600 },\n    deviceScaleFactor: 1,\n  },\n  projects: [\n    {\n      name: \"chromium\",\n      use: {\n        browserName: \"chromium\",\n      },\n    },\n  ],\n  webServer: [\n    {\n      command: \"./node_modules/.bin/http-server -p 4173 -c-1 .\",\n      url: \"http://localhost:4173/tests-e2e/fixtures/vanilla.html\",\n      reuseExistingServer: true,\n      timeout: 120_000,\n    },\n    {\n      command:\n        \"./node_modules/.bin/vite --config vue/demo/simple/vite.config.js --port 4174 --strictPort\",\n      url: \"http://localhost:4174/\",\n      reuseExistingServer: true,\n      timeout: 120_000,\n    },\n    {\n      command:\n        \"./node_modules/.bin/vite --config svelte/demo/simple/vite.config.mjs --port 4175 --strictPort\",\n      url: \"http://localhost:4175/\",\n      reuseExistingServer: true,\n      timeout: 120_000,\n    },\n  ],\n});"
  },
  {
    "path": "react/README.md",
    "content": "# IntlTelInput React Component\n\nA React component for the [intl-tel-input](https://github.com/jackocnr/intl-tel-input) JavaScript plugin. View the [source code](https://github.com/jackocnr/intl-tel-input/blob/master/react/src/intl-tel-input/react.tsx).\n\n[Explore docs »](https://intl-tel-input.com/docs/react-component)\n"
  },
  {
    "path": "react/build.js",
    "content": "/* eslint-disable no-undef */\n/* eslint-disable @typescript-eslint/no-var-requires */\nconst { build } = require(\"esbuild\");\nconst packageJson = require(\"../package.json\");\n\nconst mainShared = {\n  bundle: true,\n  external: [\"react\", \"react-dom\", \"prop-types\"],\n  logLevel: \"info\",\n  minify: false, //* Don't minify as (1) esbuild minify removes comments that we need to keep e.g. webpack import fix, (2) these files will be imported into other projects that will have their own minification process\n  define: { \"process.env.VERSION\": `\"${packageJson.version}\"` },\n};\n\n//* React Component - CommonJS\nbuild({\n  ...mainShared,\n  entryPoints: [\"react/src/intl-tel-input/react.tsx\"],\n  format: \"cjs\",\n  outfile: \"react/build/IntlTelInput.cjs\",\n});\n\n//* React Component - Default (ES Modules)\nbuild({\n  ...mainShared,\n  entryPoints: [\"react/src/intl-tel-input/react.tsx\"],\n  format: \"esm\",\n  outfile: \"react/build/IntlTelInput.js\",\n});\n\n//* React Component With Utils - CommonJS\nbuild({\n  ...mainShared,\n  entryPoints: [\"react/src/intl-tel-input/reactWithUtils.tsx\"],\n  format: \"cjs\",\n  outfile: \"react/build/IntlTelInputWithUtils.cjs\",\n});\n\n//* React Component With Utils - Default (ES Modules)\nbuild({\n  ...mainShared,\n  entryPoints: [\"react/src/intl-tel-input/reactWithUtils.tsx\"],\n  format: \"esm\",\n  outfile: \"react/build/IntlTelInputWithUtils.js\",\n});\n\n//* Demo shared config\nconst demoShared = {\n  bundle: true,\n  define: { \"process.env.VERSION\": `\"${packageJson.version}\"` },\n  format: \"iife\",\n};\n\n//* Simple demo app\nbuild({\n  ...demoShared,\n  entryPoints: [\"react/demo/simple/SimpleApp.tsx\"],\n  outfile: \"react/demo/simple/simple-bundle.js\",\n});\n\n//* Validation demo app\nbuild({\n  ...demoShared,\n  entryPoints: [\"react/demo/validation/ValidationApp.tsx\"],\n  outfile: \"react/demo/validation/validation-bundle.js\",\n});\n\n//* Set Number demo app\nbuild({\n  ...demoShared,\n  entryPoints: [\"react/demo/set-number/SetNumberApp.tsx\"],\n  outfile: \"react/demo/set-number/set-number-bundle.js\",\n});\n\n//* Toggle Disabled demo app\nbuild({\n  ...demoShared,\n  entryPoints: [\"react/demo/toggle-disabled/ToggleDisabledApp.tsx\"],\n  outfile: \"react/demo/toggle-disabled/toggle-disabled-bundle.js\",\n});\n"
  },
  {
    "path": "react/demo/set-number/SetNumberApp.tsx",
    "content": "import React, { useState, ReactElement, useRef } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport IntlTelInput from \"../../src/intl-tel-input/reactWithUtils\";\n\nconst errorMap = [\n  \"Invalid number\",\n  \"Invalid country code\",\n  \"Too short\",\n  \"Too long\",\n  \"Invalid number\",\n];\n\nconst App = (): ReactElement => {\n  const ref = useRef(null);\n  const [isValid, setIsValid] = useState<boolean | null>(null);\n  const [number, setNumber] = useState<string | null>(null);\n  const [errorCode, setErrorCode] = useState<number | null>(null);\n  const [notice, setNotice] = useState<string | null>(null);\n  \n  const handleSetNumber = (): void => {\n    ref.current?.getInstance().setNumber(\"+14155552671\");\n  };\n  \n  const handleSubmit = (): void => {\n    if (isValid) {\n      setNotice(`Valid number: ${number}`);\n    } else {\n      const errorMessage = errorMap[errorCode || 0] || \"Invalid number\";\n      setNotice(`Error: ${errorMessage}`);\n    }\n  };\n  \n  return (\n    <form>\n      <IntlTelInput\n        ref={ref}\n        onChangeNumber={setNumber}\n        onChangeValidity={setIsValid}\n        onChangeErrorCode={setErrorCode}\n        initOptions={{\n          initialCountry: \"us\",\n        }}\n      />\n      <button className=\"button\" type=\"button\" onClick={handleSetNumber}>Set Number</button>\n      <button className=\"button\" type=\"button\" onClick={handleSubmit}>Validate</button>\n      {notice && <div className=\"notice\">{notice}</div>}\n    </form>\n  );\n};\n\nconst container = document.getElementById(\"app\");\nif (container) {\n  const root = createRoot(container);\n  root.render(<App />);\n}"
  },
  {
    "path": "react/demo/set-number/set-number.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <title>React App - Validation Demo</title>\n  <link rel=\"stylesheet\" href=\"../../../build/css/intlTelInput.css\" />\n  <link rel=\"stylesheet\" href=\"../../../build/css/demo.css\" />\n</head>\n\n<body>\n  <h1>React App - Set Number Demo</h1>\n  <p>A simple react app, using the IntlTelInput component to handle phone number entry, calling setNumber and validation.</p>\n  <p>Click \"Set Number\" then click \"Validate\" to check that the react internals have updated correctly.</p>\n  <div id=\"app\"></div>\n  <script src=\"./set-number-bundle.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "react/demo/simple/SimpleApp.tsx",
    "content": "import React, { ReactElement } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport IntlTelInput from \"../../src/intl-tel-input/reactWithUtils\";\n\nconst App = (): ReactElement => (\n  <IntlTelInput\n    initOptions={{\n      initialCountry: \"us\",\n    }}\n  />\n);\n\nconst container = document.getElementById(\"app\");\nif (container) {\n  const root = createRoot(container);\n  root.render(<App />);\n}"
  },
  {
    "path": "react/demo/simple/simple.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <title>React App - Simple Demo</title>\n  <link rel=\"stylesheet\" href=\"../../../build/css/intlTelInput.css\" />\n  <link rel=\"stylesheet\" href=\"../../../build/css/demo.css\" />\n</head>\n\n<body>\n  <h1>React App - Simple Demo</h1>\n  <p>A simple react app, using the IntlTelInput component to handle phone number entry.</p>\n  <div id=\"app\"></div>\n  <script src=\"./simple-bundle.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "react/demo/toggle-disabled/ToggleDisabledApp.tsx",
    "content": "import React, { useState, ReactElement } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport IntlTelInput from \"../../src/intl-tel-input/reactWithUtils\";\n\nconst App = (): ReactElement => {\n  const [isDisabled, setIsDisabled] = useState(true);\n\n  const toggleDisabled = () => setIsDisabled(!isDisabled);\n\n  return (\n    <form>\n      <IntlTelInput\n        disabled={isDisabled}\n      />\n      <button className=\"button\" type=\"button\" onClick={toggleDisabled}>Toggle</button>\n    </form>\n  );\n};\n\nconst container = document.getElementById(\"app\");\nif (container) {\n  const root = createRoot(container);\n  root.render(<App />);\n}"
  },
  {
    "path": "react/demo/toggle-disabled/toggle-disabled.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <title>React App - Validation Demo</title>\n  <link rel=\"stylesheet\" href=\"../../../build/css/intlTelInput.css\" />\n  <link rel=\"stylesheet\" href=\"../../../build/css/demo.css\" />\n</head>\n\n<body>\n  <h1>React App - Toggle disabled prop Demo</h1>\n  <p>A simple react app, using the IntlTelInput component to show it toggle.</p>\n  <p>Click the button to enable/disable component.</p>\n  <div id=\"app\"></div>\n  <script src=\"./toggle-disabled-bundle.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "react/demo/validation/ValidationApp.tsx",
    "content": "import React, { useState, ReactElement } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport IntlTelInput from \"../../src/intl-tel-input/reactWithUtils\";\n\nconst errorMap = [\n  \"Invalid number\",\n  \"Invalid country code\",\n  \"Too short\",\n  \"Too long\",\n  \"Invalid number\",\n];\n\nconst App = (): ReactElement => {\n  const [isValid, setIsValid] = useState<boolean | null>(null);\n  const [number, setNumber] = useState<string | null>(null);\n  const [errorCode, setErrorCode] = useState<number | null>(null);\n  const [notice, setNotice] = useState<string | null>(null);\n  \n  const handleSubmit = (): void => {\n    if (isValid) {\n      setNotice(`Valid number: ${number}`);\n    } else {\n      const errorMessage = errorMap[errorCode || 0] || \"Invalid number\";\n      setNotice(`Error: ${errorMessage}`);\n    }\n  };\n  \n  return (\n    <form>\n      <IntlTelInput\n        onChangeNumber={setNumber}\n        onChangeValidity={setIsValid}\n        onChangeErrorCode={setErrorCode}\n        initOptions={{\n          initialCountry: \"us\",\n        }}\n      />\n      <button className=\"button\" type=\"button\" onClick={handleSubmit}>Validate</button>\n      {notice && <div className=\"notice\">{notice}</div>}\n    </form>\n  );\n};\n\nconst container = document.getElementById(\"app\");\nif (container) {\n  const root = createRoot(container);\n  root.render(<App />);\n}"
  },
  {
    "path": "react/demo/validation/validation.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <title>React App - Validation Demo</title>\n  <link rel=\"stylesheet\" href=\"../../../build/css/intlTelInput.css\" />\n  <link rel=\"stylesheet\" href=\"../../../build/css/demo.css\" />\n</head>\n\n<body>\n  <h1>React App - Validation Demo</h1>\n  <p>A simple react app, using the IntlTelInput component to handle phone number entry and validation.</p>\n  <p>Enter a phone number below and click \"Validate\".</p>\n  <div id=\"app\"></div>\n  <script src=\"./validation-bundle.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "react/src/intl-tel-input/react.tsx",
    "content": "import intlTelInput from \"../intl-tel-input\";\n//* Keep the TS imports separate, as the above line gets substituted in the reactWithUtils build process.\nimport { Iti } from \"../intl-tel-input\";\nimport React, {\n  useRef,\n  useEffect,\n  forwardRef,\n  useImperativeHandle,\n  useCallback,\n} from \"react\";\nimport { SomeOptions } from \"../modules/types/public-api\";\n\n// make this available as a named export, so react users can access globals like intlTelInput.utils\nexport { intlTelInput };\n\ntype InputProps = Omit<React.ComponentPropsWithoutRef<\"input\">, \"onInput\">;\n\ntype ItiProps = {\n  initialValue?: string;\n  onChangeNumber?: (number: string) => void;\n  onChangeCountry?: (country: string) => void;\n  onChangeValidity?: (valid: boolean) => void;\n  onChangeErrorCode?: (errorCode: number | null) => void;\n  usePreciseValidation?: boolean;\n  initOptions?: SomeOptions;\n  inputProps?: InputProps;\n  disabled?: boolean | undefined;\n};\n\nexport type IntlTelInputRef = {\n  getInstance: () => Iti | null;\n  getInput: () => HTMLInputElement | null;\n};\n\nconst IntlTelInput = forwardRef(function IntlTelInput(\n  {\n    initialValue = \"\",\n    onChangeNumber = () => {},\n    onChangeCountry = () => {},\n    onChangeValidity = () => {},\n    onChangeErrorCode = () => {},\n    usePreciseValidation = false,\n    initOptions = {},\n    inputProps = {},\n    disabled = undefined,\n  }: ItiProps,\n  ref: React.ForwardedRef<IntlTelInputRef>,\n) {\n  const inputRef = useRef<HTMLInputElement | null>(null);\n  const itiRef = useRef<Iti | null>(null);\n  const lastEmittedNumberRef = useRef<string>();\n  const lastEmittedCountryRef = useRef<string>();\n  const lastEmittedValidityRef = useRef<boolean>();\n  const lastEmittedErrorCodeRef = useRef<number | null>();\n\n  // expose the instance and input ref to the parent component\n  useImperativeHandle(ref, () => ({\n    getInstance: () => itiRef.current,\n    getInput: () => inputRef.current,\n  }));\n\n  const update = useCallback((): void => {\n    // if the instance is not valid (e.g. has been destroyed/unmounted), do not attempt to call any methods on it\n    if (!itiRef.current?.isActive()) {\n      return;\n    }\n    const num = itiRef.current?.getNumber() || \"\";\n    const countryIso = itiRef.current?.getSelectedCountryData().iso2 || \"\";\n    // note: this number will be in standard E164 format, but any container component can use\n    // intlTelInput.utils.formatNumber() to convert this to another format\n    // as well as intlTelInput.utils.getNumberType() etc. if need be\n    if (num !== lastEmittedNumberRef.current) {\n      lastEmittedNumberRef.current = num;\n      onChangeNumber(num);\n    }\n    if (countryIso !== lastEmittedCountryRef.current) {\n      lastEmittedCountryRef.current = countryIso;\n      onChangeCountry(countryIso);\n    }\n\n    if (itiRef.current) {\n      const isValid = usePreciseValidation\n        ? itiRef.current.isValidNumberPrecise()\n        : itiRef.current.isValidNumber();\n      const errorCode = isValid ? null : itiRef.current.getValidationError();\n\n      if (isValid !== lastEmittedValidityRef.current) {\n        lastEmittedValidityRef.current = isValid;\n        onChangeValidity(isValid);\n      }\n      if (errorCode !== lastEmittedErrorCodeRef.current) {\n        lastEmittedErrorCodeRef.current = errorCode;\n        onChangeErrorCode(errorCode);\n      }\n    }\n  }, [\n    onChangeCountry,\n    onChangeErrorCode,\n    onChangeNumber,\n    onChangeValidity,\n    usePreciseValidation,\n  ]);\n\n  useEffect(() => {\n    if (inputRef.current) {\n      itiRef.current = intlTelInput(inputRef.current, initOptions);\n    }\n    return (): void => {\n      itiRef.current?.destroy();\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  useEffect(() => {\n    // store a reference to the current input ref, which otherwise is already lost in the cleanup function\n    const inputRefCurrent = inputRef.current;\n    if (inputRefCurrent) {\n      inputRefCurrent.addEventListener(\"countrychange\", update);\n      // when plugin initialisation has finished (e.g. loaded utils script), update all the state values\n      itiRef.current.promise.then(update);\n    }\n    return (): void => {\n      if (inputRefCurrent) {\n        inputRefCurrent.removeEventListener(\"countrychange\", update);\n      }\n    };\n  }, [update]);\n\n  useEffect(() => {\n    if (itiRef.current && disabled !== undefined) {\n      itiRef.current.setDisabled(disabled);\n    }\n  }, [disabled]);\n\n  // ignore keys that would break functionality\n  const {\n    value: _value,\n    // disabled: _disabled,\n    ...sanitizedInputProps\n  } = inputProps as unknown as Record<string, unknown>;\n\n  return (\n    <input\n      {...(sanitizedInputProps as InputProps)}\n      type=\"tel\"\n      ref={inputRef}\n      onInput={update}\n      defaultValue={initialValue}\n    />\n  );\n});\n\nexport default IntlTelInput;\n"
  },
  {
    "path": "react/src/intl-tel-input/reactWithUtils.tsx",
    "content": "//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\nimport intlTelInput from \"./intlTelInputWithUtils\";\n//* Keep the TS imports separate, as the above line gets substituted in the reactWithUtils build process.\nimport { Iti } from \"../intl-tel-input\";\nimport React, {\n  useRef,\n  useEffect,\n  forwardRef,\n  useImperativeHandle,\n  useCallback,\n} from \"react\";\nimport { SomeOptions } from \"../modules/types/public-api\";\n\n// make this available as a named export, so react users can access globals like intlTelInput.utils\nexport { intlTelInput };\n\ntype InputProps = Omit<React.ComponentPropsWithoutRef<\"input\">, \"onInput\">;\n\ntype ItiProps = {\n  initialValue?: string;\n  onChangeNumber?: (number: string) => void;\n  onChangeCountry?: (country: string) => void;\n  onChangeValidity?: (valid: boolean) => void;\n  onChangeErrorCode?: (errorCode: number | null) => void;\n  usePreciseValidation?: boolean;\n  initOptions?: SomeOptions;\n  inputProps?: InputProps;\n  disabled?: boolean | undefined;\n};\n\nexport type IntlTelInputRef = {\n  getInstance: () => Iti | null;\n  getInput: () => HTMLInputElement | null;\n};\n\nconst IntlTelInput = forwardRef(function IntlTelInput(\n  {\n    initialValue = \"\",\n    onChangeNumber = () => {},\n    onChangeCountry = () => {},\n    onChangeValidity = () => {},\n    onChangeErrorCode = () => {},\n    usePreciseValidation = false,\n    initOptions = {},\n    inputProps = {},\n    disabled = undefined,\n  }: ItiProps,\n  ref: React.ForwardedRef<IntlTelInputRef>,\n) {\n  const inputRef = useRef<HTMLInputElement | null>(null);\n  const itiRef = useRef<Iti | null>(null);\n  const lastEmittedNumberRef = useRef<string>();\n  const lastEmittedCountryRef = useRef<string>();\n  const lastEmittedValidityRef = useRef<boolean>();\n  const lastEmittedErrorCodeRef = useRef<number | null>();\n\n  // expose the instance and input ref to the parent component\n  useImperativeHandle(ref, () => ({\n    getInstance: () => itiRef.current,\n    getInput: () => inputRef.current,\n  }));\n\n  const update = useCallback((): void => {\n    // if the instance is not valid (e.g. has been destroyed/unmounted), do not attempt to call any methods on it\n    if (!itiRef.current?.isActive()) {\n      return;\n    }\n    const num = itiRef.current?.getNumber() || \"\";\n    const countryIso = itiRef.current?.getSelectedCountryData().iso2 || \"\";\n    // note: this number will be in standard E164 format, but any container component can use\n    // intlTelInput.utils.formatNumber() to convert this to another format\n    // as well as intlTelInput.utils.getNumberType() etc. if need be\n    if (num !== lastEmittedNumberRef.current) {\n      lastEmittedNumberRef.current = num;\n      onChangeNumber(num);\n    }\n    if (countryIso !== lastEmittedCountryRef.current) {\n      lastEmittedCountryRef.current = countryIso;\n      onChangeCountry(countryIso);\n    }\n\n    if (itiRef.current) {\n      const isValid = usePreciseValidation\n        ? itiRef.current.isValidNumberPrecise()\n        : itiRef.current.isValidNumber();\n      const errorCode = isValid ? null : itiRef.current.getValidationError();\n\n      if (isValid !== lastEmittedValidityRef.current) {\n        lastEmittedValidityRef.current = isValid;\n        onChangeValidity(isValid);\n      }\n      if (errorCode !== lastEmittedErrorCodeRef.current) {\n        lastEmittedErrorCodeRef.current = errorCode;\n        onChangeErrorCode(errorCode);\n      }\n    }\n  }, [\n    onChangeCountry,\n    onChangeErrorCode,\n    onChangeNumber,\n    onChangeValidity,\n    usePreciseValidation,\n  ]);\n\n  useEffect(() => {\n    if (inputRef.current) {\n      itiRef.current = intlTelInput(inputRef.current, initOptions);\n    }\n    return (): void => {\n      itiRef.current?.destroy();\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  useEffect(() => {\n    // store a reference to the current input ref, which otherwise is already lost in the cleanup function\n    const inputRefCurrent = inputRef.current;\n    if (inputRefCurrent) {\n      inputRefCurrent.addEventListener(\"countrychange\", update);\n      // when plugin initialisation has finished (e.g. loaded utils script), update all the state values\n      itiRef.current.promise.then(update);\n    }\n    return (): void => {\n      if (inputRefCurrent) {\n        inputRefCurrent.removeEventListener(\"countrychange\", update);\n      }\n    };\n  }, [update]);\n\n  useEffect(() => {\n    if (itiRef.current && disabled !== undefined) {\n      itiRef.current.setDisabled(disabled);\n    }\n  }, [disabled]);\n\n  // ignore keys that would break functionality\n  const {\n    value: _value,\n    // disabled: _disabled,\n    ...sanitizedInputProps\n  } = inputProps as unknown as Record<string, unknown>;\n\n  return (\n    <input\n      {...(sanitizedInputProps as InputProps)}\n      type=\"tel\"\n      ref={inputRef}\n      onInput={update}\n      defaultValue={initialValue}\n    />\n  );\n});\n\nexport default IntlTelInput;\n"
  },
  {
    "path": "react/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es6\",\n    \"jsx\": \"react\",\n    \"module\": \"nodenext\",\n    \"moduleResolution\": \"nodenext\",\n    \"esModuleInterop\": true, //* Required for react (etc) default imports.\n    \"declaration\": true, //* Generate .d.ts files.\n    \"emitDeclarationOnly\": true, //* ONLY generate .d.ts files (we use esbuild for the actual bundling).\n    \"outFile\": \"build/IntlTelInput.d.ts\",\n    \"rootDir\": \"./src\",\n    \"allowJs\": true, // allow importing .mjs files e.g. i18n files\n  },\n  \"include\": [\n    \"src/intl-tel-input/react.tsx\",\n    \"src/intl-tel-input/reactWithUtils.tsx\",\n    \"src/intl-tel-input/i18n/index.ts\",\n  ],\n}\n"
  },
  {
    "path": "scripts/check-lpn-metadata.cjs",
    "content": "/**\n * This script loads the existing country data from data.ts,\n * then parses libphonenumber's PhoneNumberMetadata.xml to look for any updates.\n *\n * I decided to keep the hand-made data.ts data instead of just having it all auto-generated from the LPN metadata because LPN only stores the precise ranges that are known to be in use, whereas (1) we know that there are other ranges that have been assigned to a territory and so would make more sense belonging to that one rather than the main country, and (2) because of the high level of precision, the resulting output is massive from LPN e.g. outputting 20x 6-digit numbers for IM (isle of man), when it is clearly documented on the wiki page that they have been assigned the 5 short strings that I have included in data.ts\n */\n\n\n/* eslint-disable no-console */\nconst fs = require(\"fs\");\nconst path = require(\"path\");\nconst vm = require(\"vm\");\n\nconst REPO_ROOT = path.resolve(__dirname, \"..\", \"\");\nconst XML_PATH = path.resolve(\n  REPO_ROOT,\n  \"third_party/libphonenumber/resources/PhoneNumberMetadata.xml\",\n);\nconst OUT_TS = path.resolve(REPO_ROOT, \"tmp/generated-rawCountryData.ts\");\nconst CURATED_DATA_TS_PATH = path.resolve(REPO_ROOT, \"src/js/intl-tel-input/data.ts\");\n\n// Extract a compact leading digits prefix representative for a region.\n// Strategy:\n// - Prefer a short literal digit prefix found in the first national number pattern for fixed-line or mobile\n// - Fallback to a header-level leadingDigits string when present, trimming to a short digit-only prefix\n// (legacy single-prefix extractor removed in favor of extractLeadingPrefixes)\n\nfunction sortDigitStringsNumeric(arr) {\n  // Lexicographic digit-by-digit ordering (e.g., 74576 < 7524)\n  return arr.slice().sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));\n}\n\n// Shared simple prefix parser used for XML mode\nfunction splitTopLevelAlts(inner) {\n    const parts = [];\n    let cur = \"\";\n    let depth = 0;\n    for (let i = 0; i < inner.length; i++) {\n      const ch = inner[i];\n      if (ch === \"(\") depth++;\n      else if (ch === \")\") depth = Math.max(0, depth - 1);\n      if (ch === \"|\" && depth === 0) { parts.push(cur); cur = \"\"; continue; }\n      cur += ch;\n    }\n    parts.push(cur);\n    return parts;\n}\n\nfunction expandCharClass(cls) {\n    const res = new Set();\n    for (let i = 0; i < cls.length; i++) {\n      const c = cls[i];\n      if (/\\d/.test(c)) {\n        if (i + 2 < cls.length && cls[i + 1] === \"-\" && /\\d/.test(cls[i + 2])) {\n          const start = Number(c), end = Number(cls[i + 2]);\n          for (let d = start; d <= end; d++) res.add(String(d));\n          i += 2;\n        } else {\n          res.add(c);\n        }\n      }\n    }\n    return Array.from(res);\n}\n\nfunction expandSimple(prefixPart) {\n    if (/^\\d{2,6}$/.test(prefixPart)) return [prefixPart];\n    const m = prefixPart.match(/^(\\d*)\\[([0-9-]+)\\](\\d*)$/);\n    if (m) {\n      const lead = m[1] || \"\";\n      const cls = m[2];\n      const tail = m[3] || \"\";\n      return expandCharClass(cls).map((d) => lead + d + tail).filter((s) => /^\\d{2,6}$/.test(s));\n    }\n    return [];\n}\n\nfunction simplePrefixesFromPattern(pat) {\n    // Normalize: collapse all whitespace to simplify parsing of multi-line XML patterns\n    pat = String(pat).replace(/\\s+/g, \"\");\n    // Handle a leading literal digit sequence followed by a non-capturing group, e.g., '7(?:781|839)\\\\d|911[17])\\\\d{5}'\n    let mLeadGroup = pat.match(/^(\\d{1,3})\\(\\?:/);\n    if (mLeadGroup) {\n      const lead = mLeadGroup[1];\n      // Extract the first non-capturing group's inner content after the leading digits\n      let i = lead.length + 3; // position after '<lead>(?:'\n      let depth = 1;\n      let inner = \"\";\n      while (i < pat.length) {\n        const ch = pat[i];\n        if (ch === \"\\\\\") { i += 2; continue; }\n        if (ch === \"(\") depth++;\n        else if (ch === \")\") { depth--; if (depth === 0) { i++; break; } }\n        inner += ch; i++;\n      }\n      const alts = splitTopLevelAlts(inner);\n      const set = new Set();\n      for (const a of alts) {\n        const arr = simplePrefixesFromPattern(a);\n        for (const v of arr) set.add(lead + v);\n      }\n      return Array.from(set).filter((s) => /^\\d{2,6}$/.test(s));\n    }\n    // If the pattern starts with a top-level non-capturing group, extract its inner content\n    if (pat.startsWith(\"(?:\")) {\n      let i = 3; // after (?:\n      let depth = 1;\n      let inner = \"\";\n      while (i < pat.length) {\n        const ch = pat[i];\n        if (ch === \"\\\\\") { // skip escaped char\n          i += 2;\n          continue;\n        }\n        if (ch === \"(\") depth++;\n        else if (ch === \")\") {\n          depth--;\n          if (depth === 0) { i++; break; }\n        }\n        inner += ch;\n        i++;\n      }\n      // Now 'inner' holds the content of the outer non-capturing group; ignore any suffix like \\d{n}\n      const alts = splitTopLevelAlts(inner);\n      if (alts.length > 1) {\n        const set = new Set();\n        for (const a of alts) {\n          const arr = simplePrefixesFromPattern(a);\n          for (const v of arr) set.add(v);\n        }\n        return Array.from(set);\n      }\n      // Single alternative inside, recurse into it directly\n      return simplePrefixesFromPattern(inner);\n    }\n    // Handle top-level alternations like \"658|876\" or \"8001|8[024]9\"\n    const top = splitTopLevelAlts(pat);\n    if (top.length > 1) {\n      const set = new Set();\n      for (const part of top) {\n        const arr = simplePrefixesFromPattern(part);\n        for (const v of arr) set.add(v);\n      }\n      return Array.from(set);\n    }\n    // Handle bare character class at start, e.g., \"[347]\" or \"[3-7]24\"\n    let m = pat.match(/^\\[([0-9-]+)\\](\\d*)$/);\n    if (m) {\n      const cls = m[1];\n      const tail = m[2] || \"\";\n      return expandCharClass(cls).map((d) => d + tail).filter((s) => /^\\d{1,6}$/.test(s));\n    }\n    m = pat.match(/^(\\d+)\\[([0-9-]+)\\](\\d*)/);\n    if (m) {\n      const lead = m[1];\n      const cls = m[2];\n      const tail = m[3] || \"\";\n      return expandCharClass(cls).map((d) => lead + d + tail).filter((s) => /^\\d{2,6}$/.test(s));\n    }\n  m = pat.match(/^(\\d{1,6})/);\n    if (m) return [m[1]];\n    return [];\n}\n\n// Collapse exhaustive ranges (prefix-aware): if for a parent prefix p we see all 10 next digits\n// across any longer codes (e.g., p0*, p1*, ..., p9*), then replace all those children with p.\nfunction collapseExhaustiveRanges(codes) {\n  if (!Array.isArray(codes) || !codes.length) return codes || [];\n  const set = new Set(codes);\n  let changed = true;\n  while (changed) {\n    changed = false;\n    // Build parent -> set(nextDigits)\n    const parents = new Map();\n    for (const s of set) {\n      if (typeof s !== \"string\" || s.length < 2) continue;\n      for (let k = 1; k < s.length; k++) {\n        const parent = s.slice(0, k);\n        const next = s[k];\n        if (!/\\d/.test(next)) continue;\n        let bag = parents.get(parent);\n        if (!bag) { bag = new Set(); parents.set(parent, bag); }\n        bag.add(next);\n      }\n    }\n    // For any parent with all 10 digits seen, collapse all children starting with parent+digit\n    for (const [parent, bag] of parents) {\n      if (bag.size === 10) {\n        let mutated = false;\n        const toDelete = [];\n        for (const s of set) {\n          if (s.length > parent.length && s.startsWith(parent)) {\n            toDelete.push(s);\n          }\n        }\n        for (const s of toDelete) { set.delete(s); mutated = true; }\n        if (!set.has(parent)) { set.add(parent); mutated = true; }\n        if (mutated) changed = true;\n      }\n    }\n  }\n  return sortDigitStringsNumeric(Array.from(set));\n}\n\n// Attempt to extract 3-digit NANP NPAs from a complex pattern like CA's,\n// which uses structure: (?: 2(?:..|..|..) | 3(?:..|..) | ... )[2-9]\\d{6}\nfunction extractNanpNpasFromPattern(pat) {\n  if (typeof pat !== \"string\" || !pat) return [];\n  const s = pat.replace(/\\s+/g, \"\");\n  // Find the top-level non-capturing group at the start, if present\n  let inner = s;\n  if (s.startsWith(\"(?:\")) {\n    let i = 3, depth = 1; inner = \"\";\n    while (i < s.length) {\n      const ch = s[i];\n      if (ch === \"\\\\\") { i += 2; continue; }\n      if (ch === \"(\") depth++;\n      else if (ch === \")\") { depth--; if (depth === 0) { i++; break; } }\n      inner += ch; i++;\n    }\n  }\n  const alts = splitTopLevelAlts(inner);\n  const out = new Set();\n  for (const a of alts) {\n    // Expect a like: 2(?:04|[23]6|...)\n    const m = a.match(/^([2-9])\\(\\?:(.+)\\)$/);\n    if (!m) continue;\n    const d = m[1];\n    const rest = m[2];\n    const parts = splitTopLevelAlts(rest);\n    for (const p of parts) {\n      // Expand simple pieces like 04, [23]6, 5[07], 63\n      const expansions = expandSimple(d + p);\n      for (const e of expansions) { if (/^\\d{3}$/.test(e)) out.add(e); }\n      // Also handle pure two-digit literals (e.g., '63') after the 1st digit\n      if (/^\\d{2}$/.test(p)) out.add(d + p);\n    }\n  }\n  return sortDigitStringsNumeric(Array.from(out));\n}\n\nfunction deriveNanpNpasFromPatterns(fixedList, mobileList) {\n  const set = new Set();\n  const addFrom = (list) => {\n    if (!Array.isArray(list)) return;\n    for (const pat of list) {\n      // 1) General extraction using simplePrefixesFromPattern\n      const simple = simplePrefixesFromPattern(pat);\n      for (const s of simple) {\n        if (/^[2-9]\\d{2}$/.test(s)) set.add(s);\n        else if (/^[2-9]\\d{3,6}$/.test(s)) set.add(s.slice(0, 3));\n      }\n      // 2) CA-style nested non-capturing groups as a supplement\n      const caLike = extractNanpNpasFromPattern(pat);\n      for (const v of caLike) set.add(v);\n      if (set.size > 400) break; // safety guardrail\n    }\n  };\n  addFrom(fixedList);\n  addFrom(mobileList);\n  return sortDigitStringsNumeric(Array.from(set));\n}\n\n// XML-mode generic extractor: only use territory.leadingDigits + fixedLine/mobile patterns\n// Apply safe expansions and filter out obvious service/example-like prefixes\nfunction extractGenericPrefixesXml(src) {\n  const out = new Set();\n\n  function isDisallowedPrefix(s) {\n    // Leading zero prefixes are often trunk/formatting hints\n    if (/^0/.test(s)) return true;\n    // Non-geo/service families and known service groups\n    if (/^(?:800|80\\d|900|90\\d|13|1300|1800|190\\d)\\d*$/.test(s)) return true;\n    // Example-like tails\n    if (/(?:0123|1234)$/.test(s)) return true;\n    // Common synthetic sequences sometimes found in examples\n    if (/701234$/.test(s)) return true;\n    return false;\n  }\n\n  function addFrom(list) {\n    if (!Array.isArray(list)) return;\n    for (const p of list) {\n      if (typeof p !== \"string\" || !p) continue;\n      const arr = simplePrefixesFromPattern(p);\n      for (const s of arr) out.add(s);\n      if (out.size > 40) break;\n    }\n  }\n\n  // Use only fixedLine and mobile patterns (ignore leadingDigits entirely)\n  addFrom(src.fixed);\n  addFrom(src.mobile);\n\n  // Perform prefix-aware collapse before filtering so that short parents (e.g., '4') can be retained\n  const rawList = sortDigitStringsNumeric(Array.from(out));\n  const collapsed = collapseExhaustiveRanges(rawList);\n  const rawSet = new Set(rawList);\n\n  // Filter out obvious service/example-like prefixes\n  const filtered = Array.from(collapsed).filter((s) => {\n    if (isDisallowedPrefix(s)) return false;\n    // Drop 709x branch (special allocations seen in 262/590 groups) to match curated area codes\n    if (src && (src.dial === \"262\" || src.dial === \"590\") && /^709/.test(s)) return false;\n    // Do not treat the full dial code itself as an area-code discriminator,\n    // except for Kazakhstan (kz) which legitimately needs '7' to disambiguate from RU.\n    if (src && s === src.dial && src.iso2 !== \"kz\") return false;\n    // Enforce minimum length with exceptions:\n    //  - allow 1-digit for dial 599 (BQ group)\n    //  - allow 1-digit for KZ (iso2 === 'kz')\n    const minLen = (src && (src.dial === \"599\" || src.iso2 === \"kz\")) ? 1 : 3;\n    // If this value is a collapsed parent (i.e., not present in raw expansions), allow even if short\n    const isCollapsedParent = !rawSet.has(s);\n    if (s.length < minLen && !isCollapsedParent) return false;\n    return true;\n  });\n\n  return sortDigitStringsNumeric(filtered);\n}\n\n// XML-only: extend a base prefix to at least minLen using only safe expansions from XML patterns\nfunction extendPrefixXml(iso2, base, minLen, xmlSources) {\n  const src = xmlSources[iso2];\n  if (!src || !base) return base;\n  const candidates = new Set();\n  // Reuse filtering from extractor\n  function isDisallowedPrefix(s) {\n    if (/^0/.test(s)) return true;\n    if (/^(?:800|80\\d|900|90\\d|13|1300|1800|190\\d)\\d*$/.test(s)) return true;\n    if (/(?:0123|1234)$/.test(s)) return true;\n    if (/701234$/.test(s)) return true;\n    return false;\n  }\n  const collect = (list) => {\n    if (!Array.isArray(list)) return;\n    for (const pat of list) {\n      if (typeof pat !== \"string\") continue;\n      const arr = simplePrefixesFromPattern(pat);\n      for (const p of arr) {\n        if (p.startsWith(base) && p.length >= minLen && !isDisallowedPrefix(p)) candidates.add(p);\n      }\n    }\n  };\n  collect(src.fixed);\n  collect(src.mobile);\n  if (!candidates.size) return base;\n  // choose the shortest candidate\n  return Array.from(candidates).sort((a, b) => a.length - b.length || (a < b ? -1 : a > b ? 1 : 0))[0];\n}\n\n\n\n// No iso2-codes.json; we derive the allowlist from curated data.ts\n\nfunction main() {\n  let countryToMetadata;\n  const dialCodeToRegions = {};\n  const xmlNationalPrefixByIso2 = {};\n  // Regions where we intentionally do NOT derive areaCodes as there is no way to distinguish numbers from main region\n  const AREA_CODES_EXCLUDE = new Set([\"bl\", \"mf\", \"cc\", \"cx\"]);\n\n  // For XML mode, we also keep a structured source set per region\n  const xmlSourcesByIso2 = {};\n  console.log(\"Loading libphonenumber XML from:\", XML_PATH);\n  // Lazy-require to keep default path lightweight\n  let XMLParser;\n  try {\n    ({ XMLParser } = require(\"fast-xml-parser\"));\n  } catch {\n    throw new Error(\n      \"fast-xml-parser is required. Install with: npm i -D fast-xml-parser\",\n    );\n  }\n  const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: \"\" });\n  const xmlRaw = fs.readFileSync(XML_PATH, \"utf8\");\n  const xmlDoc = parser.parse(xmlRaw);\n  const territoriesRoot = xmlDoc && xmlDoc.phoneNumberMetadata && xmlDoc.phoneNumberMetadata.territories;\n  if (!territoriesRoot) throw new Error(\"Invalid PhoneNumberMetadata.xml structure: missing territories\");\n  let territories = territoriesRoot.territory || [];\n  if (!Array.isArray(territories)) territories = [territories];\n\n  // Build minimal structures compatible with the rest of the pipeline\n  // countryToMetadataXml: REG -> array of arrays of strings (patterns) so extendPrefix can scan\n  const countryToMetadataXml = {};\n\n  territories.forEach((t) => {\n    const reg = (t.id || \"\").toString();\n    const iso2 = reg.toLowerCase();\n    if (!reg) return;\n    const dial = (t.countryCode || \"\").toString();\n    if (!dial) return;\n    const natPref = t.nationalPrefix ? String(t.nationalPrefix) : null;\n    if (natPref) xmlNationalPrefixByIso2[iso2] = natPref;\n\n    // Collect safe patterns from territory-level leadingDigits, fixedLine, mobile\n    const patterns = [];\n    const leading = [];\n    const fixedPats = [];\n    const mobilePats = [];\n    // Some schemas put territory-level leadingDigits as child elements or arrays\n    const ld = t.leadingDigits;\n    if (typeof ld === \"string\") { patterns.push(ld); leading.push(ld); }\n    else if (Array.isArray(ld)) ld.forEach((s) => { if (typeof s === \"string\") { patterns.push(s); leading.push(s); } });\n\n    const fixed = t.fixedLine && t.fixedLine.nationalNumberPattern;\n    if (typeof fixed === \"string\") { patterns.push(fixed); fixedPats.push(fixed); }\n    const mobile = t.mobile && t.mobile.nationalNumberPattern;\n    if (typeof mobile === \"string\") { patterns.push(mobile); mobilePats.push(mobile); }\n\n    // Build dialCodeToRegions honoring mainCountryForCode where available\n    if (!dialCodeToRegions[dial]) dialCodeToRegions[dial] = [];\n    const isMain = String(t.mainCountryForCode || \"\") === \"true\";\n    if (isMain) {\n      // Ensure main region is first\n      dialCodeToRegions[dial].unshift(iso2);\n    } else {\n      dialCodeToRegions[dial].push(iso2);\n    }\n\n    // Provide a scanning surface for extendPrefix: one inner array with collected patterns\n    countryToMetadataXml[reg] = [patterns];\n    xmlSourcesByIso2[iso2] = { iso2, leading, fixed: fixedPats, mobile: mobilePats, dial, main: isMain };\n  });\n\n  countryToMetadata = countryToMetadataXml;\n\n  // Load curated area codes from src/js/intl-tel-input/data.ts and seed from those first.\n  // We only add newly-derived codes if they are not already covered by curated ones.\n  // Also capture curated priority per iso2 to preserve priority values in generated output (we don't compute priorities ourselves).\n  const curatedPriorityByIso2 = new Map();\n  const curatedIso2Set = new Set();\n  function loadCuratedAreaCodes() {\n    const map = new Map();\n    if (!fs.existsSync(CURATED_DATA_TS_PATH)) return map;\n    try {\n      const ts = fs.readFileSync(CURATED_DATA_TS_PATH, \"utf8\");\n      const anchor = ts.indexOf(\"export const rawCountryData\");\n      if (anchor === -1) return map;\n      // Find first '[' after anchor and then parse matching brackets until the corresponding ']'\n      let i = ts.indexOf(\"[\", anchor);\n      if (i === -1) return map;\n      let depth = 0;\n      let start = i;\n      for (; i < ts.length; i++) {\n        const ch = ts[i];\n        if (ch === \"[\") depth++;\n        else if (ch === \"]\") { depth--; if (depth === 0) { i++; break; } }\n      }\n      if (depth !== 0) return map;\n      const arrSrc = ts.slice(start, i);\n      // Evaluate the array literal. Comments are allowed in JS arrays, so this should work.\n      // Wrap in parentheses to make it a valid expression.\n      // eslint-disable-next-line no-new-func\n      const curated = Function(\"return (\" + arrSrc + \")\")();\n      if (Array.isArray(curated)) {\n        for (const row of curated) {\n          if (!Array.isArray(row) || row.length < 2) continue;\n          const iso2 = row[0];\n          const priority = row[2];\n          const area = row[3];\n          if (typeof iso2 === \"string\") curatedIso2Set.add(iso2.toLowerCase());\n          if (typeof iso2 === \"string\" && Array.isArray(area) && area.length) {\n            map.set(iso2.toLowerCase(), area.slice());\n          }\n          if (typeof iso2 === \"string\" && (typeof priority === \"number\" || typeof priority === \"string\")) {\n            const p = Number(priority);\n            if (!Number.isNaN(p)) curatedPriorityByIso2.set(iso2.toLowerCase(), p);\n          }\n        }\n      }\n    } catch (e) {\n      console.warn(\"Warning: failed to parse curated data.ts for area codes:\", e && e.message ? e.message : e);\n    }\n    return map;\n  }\n  const curatedAreaCodesByIso2 = loadCuratedAreaCodes();\n  // Build allowlist from curated data.ts iso2s\n  console.log(\"Building iso2 allowlist from curated data.ts\");\n  const allowIso2 = curatedIso2Set;\n\n  const tuples = [];\n\n  // Precompute candidate leading prefixes per region\n  const candidatePrefixesByIso2 = {};\n  const collapsedParentsByIso2 = {};\n  // NANP behavior: derive 3-digit NPAs from fixed/mobile patterns\n  Object.keys(countryToMetadata).forEach((REG) => {\n    const iso2 = REG.toLowerCase();\n    if (!allowIso2.has(iso2)) return;\n    // find dial code for region\n    let dial = null;\n    // In XML mode we already built dialCodeToRegions, so find the dial by reverse lookup\n    for (const [dc, regs] of Object.entries(dialCodeToRegions)) {\n      if (regs.includes(iso2)) { dial = dc; break; }\n    }\n    if (!dial) return;\n  const siblings = dialCodeToRegions[dial] || [];\n  if (siblings.length <= 1) return; // unique dial code -> no areaCodes\n  // Use explicit main flag from XML to decide skipping area code derivation\n  const srcForRegion = xmlSourcesByIso2[iso2];\n  if (srcForRegion && srcForRegion.main) return; // main region -> skip areaCodes\n    // Skip area-code derivation for excluded regions\n    if (AREA_CODES_EXCLUDE.has(iso2)) return;\n    let cands = [];\n    if (dial === \"1\") {\n      // NANP: for non-main regions, extract 3-digit NPAs from fixed/mobile patterns\n      const src = srcForRegion;\n      if (src) {\n        let npas = deriveNanpNpasFromPatterns(src.fixed, src.mobile);\n        // Fallback: take a 3-digit slice from the first simple prefix if needed\n        if (!npas.length) {\n          const simple = [];\n          (src.fixed || []).forEach((p) => simple.push(...simplePrefixesFromPattern(p)));\n          (src.mobile || []).forEach((p) => simple.push(...simplePrefixesFromPattern(p)));\n          const first = simple.find((s) => /^[2-9]\\d{2,6}$/.test(s));\n          if (first) npas = [first.slice(0, 3)];\n        }\n        if (npas.length) cands = npas;\n      }\n    } else {\n      // XML mode: use filtered IM-style extraction from leading/fixed/mobile only\n      const src = srcForRegion;\n      if (src) cands = extractGenericPrefixesXml(src);\n    }\n    if (cands && cands.length) {\n      const uniq = Array.from(new Set(cands));\n      const collapsed = collapseExhaustiveRanges(uniq);\n      candidatePrefixesByIso2[iso2] = collapsed;\n      // Build raw expansion set from fixed/mobile patterns to detect which entries are collapsed parents\n      const rawSet = new Set();\n      const src = srcForRegion;\n      if (src) {\n        const collect = (list) => {\n          if (!Array.isArray(list)) return;\n          for (const p of list) {\n            if (typeof p !== \"string\" || !p) continue;\n            for (const s of simplePrefixesFromPattern(p)) rawSet.add(s);\n          }\n        };\n        collect(src.fixed);\n        collect(src.mobile);\n      }\n      const parents = new Set(collapsed.filter((s) => !rawSet.has(s)));\n      if (parents.size) collapsedParentsByIso2[iso2] = parents;\n    }\n  });\n\n  // For each dial code group, shrink prefixes to minimal unique values among non-main regions\n  // Allow per-dial-code minimum lengths to ensure meaningful disambiguation (e.g., 44 → 4 digits)\n  // const minUniqueLenByDial = {\n  //   1: 3,\n  //   7: 1,\n  //   44: 4,\n  //   262: 3,\n  //   212: 4,\n  //   599: 1,\n  // };\n  const uniquePrefixByIso2 = {};\n  Object.keys(dialCodeToRegions).forEach((dial) => {\n    const regions = dialCodeToRegions[dial];\n    if (!regions || regions.length <= 1) return;\n    const rest = regions.slice(1);\n    const entries = rest\n      .map((r) => ({ iso2: r, full: candidatePrefixesByIso2[r] }))\n      .filter((e) => Array.isArray(e.full) && e.full.length);\n    if (!entries.length) return;\n    // determine minimal unique truncation length between 2 and full length\n    const allLens = entries.flatMap((e) => e.full.map((s) => s.length));\n    const maxLen = Math.min(6, Math.max(...allLens));\n    // const minLenDefault = 2;\n    const minLen = 1;//Math.max(minLenDefault, Number(minUniqueLenByDial[dial]) || minLenDefault);\n    // Choose the longest possible unique length first (descending), then collapse if possible\n    for (let len = maxLen; len >= minLen; len--) {\n      const seen = new Map();\n      let collision = false;\n      // Expand all candidates per region, extending to len when needed\n      const expanded = entries.map((e) => {\n        const arr = [];\n        for (const full of e.full) {\n          let val = full;\n          const parents = collapsedParentsByIso2[e.iso2];\n          const isCollapsed = parents && parents.has(full);\n          if (val.length < len && !isCollapsed) {\n            val = extendPrefixXml(e.iso2, val, len, xmlSourcesByIso2);\n          }\n          arr.push(val.slice(0, Math.min(len, val.length)));\n        }\n        return { iso2: e.iso2, arr: Array.from(new Set(arr)) };\n      });\n      // Check collisions across all regions\n      for (const e of expanded) {\n        for (const k of e.arr) {\n          if (seen.has(k)) { collision = true; break; }\n          seen.set(k, e.iso2);\n        }\n        if (collision) break;\n      }\n      if (!collision) {\n        // Try to collapse ranges per region without breaking uniqueness\n        const collapsedPerIso2 = new Map();\n        let collapseCollision = false;\n        const seenCollapsed = new Map();\n        for (const e of expanded) {\n          const collapsedArr = collapseExhaustiveRanges(e.arr);\n          collapsedPerIso2.set(e.iso2, collapsedArr);\n        }\n        // Validate uniqueness after collapse\n        for (const e of expanded) {\n          const arrToUse = collapsedPerIso2.get(e.iso2) || e.arr;\n          for (const k of arrToUse) {\n            if (seenCollapsed.has(k)) { collapseCollision = true; break; }\n            seenCollapsed.set(k, e.iso2);\n          }\n          if (collapseCollision) break;\n        }\n        if (!collapseCollision) {\n          for (const e of expanded) uniquePrefixByIso2[e.iso2] = collapsedPerIso2.get(e.iso2) || e.arr;\n        } else {\n          for (const e of expanded) uniquePrefixByIso2[e.iso2] = e.arr;\n        }\n        return;\n      }\n    }\n    // If we reach here, use full prefixes\n    for (const e of entries) uniquePrefixByIso2[e.iso2] = e.full;\n  });\n\n  function getNationalPrefix(iso2) {\n    const np = xmlNationalPrefixByIso2[iso2];\n    return typeof np === \"string\" && np.length ? np : null;\n  }\n\n  const dialCodes = Object.keys(dialCodeToRegions);\n  console.log(\"Found\", dialCodes.length, \"country calling codes\");\n  dialCodes\n    .sort((a, b) => Number(a) - Number(b))\n    .forEach((dialCode) => {\n  const regions = dialCodeToRegions[dialCode];\n      regions.forEach((iso2) => {\n        if (!allowIso2.has(iso2)) return;\n        // Priority is ignored; always 0\n        // Compute area codes starting from curated data.ts, then add uncovered generated codes\n        let areaCodes = null;\n        const baseline = curatedAreaCodesByIso2.get(iso2) || null;\n        const area = uniquePrefixByIso2[iso2];\n        const generated = Array.isArray(area) && area.length ? area.slice() : [];\n        const usedCurated = Array.isArray(baseline) && baseline.length > 0;\n        if (usedCurated) {\n          const base = baseline.slice();\n          // Determine which generated codes are not already covered by baseline\n          const isCoveredByBaseline = (code) => base.some((b) => typeof b === \"string\" && code.startsWith(b));\n          const isBroaderThanBaseline = (code) => base.some((b) => typeof b === \"string\" && b.startsWith(code));\n          for (const g of generated) {\n            if (!isCoveredByBaseline(g) && !isBroaderThanBaseline(g)) base.push(g);\n          }\n          areaCodes = base;\n        } else {\n          areaCodes = generated.length ? generated : null;\n        }\n        // Sort generated area codes numerically for deterministic, readable output\n        if (Array.isArray(areaCodes)) {\n          areaCodes = sortDigitStringsNumeric(areaCodes);\n        }\n        // Generic collapse: avoid collapsing when using curated baseline to preserve existing sets\n        if (Array.isArray(areaCodes)) {\n          const usedCurated = Array.isArray(baseline) && baseline.length > 0;\n          if (!usedCurated) areaCodes = collapseExhaustiveRanges(areaCodes);\n        }\n        const nationalPrefix = getNationalPrefix(iso2);\n        const curatedPriority = curatedPriorityByIso2.get(iso2) || 0;\n        const tuple = [iso2, dialCode, curatedPriority, null, null];\n        if (areaCodes) tuple[3] = areaCodes;\n        if (nationalPrefix) tuple[4] = nationalPrefix;\n        tuples.push(tuple);\n      });\n    });\n\n  tuples.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));\n  const ts = `export const rawCountryData = ${JSON.stringify(tuples, null, 2)} as const;\\n`;\n  fs.mkdirSync(path.dirname(OUT_TS), { recursive: true });\n  fs.writeFileSync(OUT_TS, ts, \"utf8\");\n  console.log(`Wrote ${tuples.length} entries as TS to ${path.relative(process.cwd(), OUT_TS)}`);\n\n  // Always run diff against curated data.ts after generation\n  function loadArray(tsPath, isDataTs = false) {\n    let s = fs.readFileSync(tsPath, \"utf8\");\n    if (isDataTs) {\n      // strip line comments\n      s = s.replace(/\\/\\/.*$/mg, \"\");\n      // drop trailing TS type exports if present\n      s = s.replace(/export type[\\s\\S]*/, \"\");\n    }\n    // normalize export for eval\n    s = s.replace(/export const rawCountryData\\s*=\\s*/, \"var rawCountryData = \");\n    s = s.replace(/\\] as const;?\\s*$/, \"];\\n\");\n    const ctx = {};\n    vm.createContext(ctx);\n    vm.runInContext(s, ctx, { filename: tsPath });\n    return ctx.rawCountryData;\n  }\n\n  function toMap(arr) {\n    const m = new Map();\n    for (const t of arr) {\n      const [iso2, dialCode, priority = 0, areaCodes = null, nationalPrefix = null] = t;\n      m.set(iso2, { iso2, dialCode, priority, areaCodes: areaCodes ?? null, nationalPrefix: nationalPrefix ?? null });\n    }\n    return m;\n  }\n\n  function eqArr(a, b) {\n    if (a === b) return true;\n    if (!a && !b) return true;\n    if (!a || !b) return false;\n    if (a.length !== b.length) return false;\n    for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;\n    return true;\n  }\n\n  function summarizeArrayChange(name, oldArr, newArr) {\n    const oa = Array.isArray(oldArr) ? oldArr : [];\n    const na = Array.isArray(newArr) ? newArr : [];\n    const oSet = new Set(oa);\n    const nSet = new Set(na);\n    const added = na.filter((x) => !oSet.has(x));\n    const removed = oa.filter((x) => !nSet.has(x));\n    const header = `${name}: ${oldArr ? oa.length : \"null\"} items -> ${newArr ? na.length : \"null\"} items`;\n    const limit = 12;\n    const fmt = (arr) => {\n      if (!arr.length) return \"[]\";\n      if (arr.length <= limit) return `[${arr.map((s)=>JSON.stringify(s)).join(\",\")}]`;\n      const head = arr.slice(0, limit - 2).map((s)=>JSON.stringify(s)).join(\",\");\n      const tail = arr.slice(-2).map((s)=>JSON.stringify(s)).join(\",\");\n      const more = arr.length - (limit);\n      return `[${head},…${more} more… ,${tail}]`;\n    };\n    if (added.length === 0 && removed.length === 0 && Array.isArray(oldArr) && Array.isArray(newArr)) {\n      // Order-only change\n      return `${name}: reordered (${oa.length} items)`;\n    }\n    // For small diffs, show full arrays for readability\n    if ((oa.length + na.length) <= 40 && (added.length + removed.length) <= 20) {\n      return `${name}: ${JSON.stringify(oldArr)} -> ${JSON.stringify(newArr)}`;\n    }\n    const parts = [];\n    if (added.length) parts.push(`New item(s): ${fmt(added)}`);\n    if (removed.length) parts.push(`Removed item(s): ${fmt(removed)}`);\n    return `${header}${parts.length ? \", \" + parts.join(\", \") : \"\"}`;\n  }\n\n  function runDiff() {\n    const GEN_PATH = OUT_TS;\n    const ORIG_PATH = CURATED_DATA_TS_PATH;\n    if (!fs.existsSync(GEN_PATH)) {\n      console.error(\"Missing generated file:\", GEN_PATH);\n      return;\n    }\n    if (!fs.existsSync(ORIG_PATH)) {\n      console.error(\"Missing original file:\", ORIG_PATH);\n      return;\n    }\n    const gen = loadArray(GEN_PATH, false);\n    const orig = loadArray(ORIG_PATH, true);\n    const g = toMap(gen);\n    const o = toMap(orig);\n    const allIso2 = [...new Set([...g.keys(), ...o.keys()])].sort();\n    const lines = [];\n    let added = 0, removed = 0, changed = 0;\n    for (const iso of allIso2) {\n      const G = g.get(iso);\n      const O = o.get(iso);\n      if (!G) {\n        removed++;\n        lines.push(`- ${iso}: dial=${O.dialCode}, priority=${O.priority}, areaCodes=${JSON.stringify(O.areaCodes)}, nationalPrefix=${JSON.stringify(O.nationalPrefix)}`);\n        continue;\n      }\n      if (!O) {\n        added++;\n        lines.push(`+ ${iso}: dial=${G.dialCode}, priority=${G.priority}, areaCodes=${JSON.stringify(G.areaCodes)}, nationalPrefix=${JSON.stringify(G.nationalPrefix)}`);\n        continue;\n      }\n      const parts = [];\n      if (G.dialCode !== O.dialCode) parts.push(`dial: ${O.dialCode} -> ${G.dialCode}`);\n      if ((G.priority || 0) !== (O.priority || 0)) parts.push(`priority: ${O.priority || 0} -> ${G.priority || 0}`);\n  if (!eqArr(G.areaCodes, O.areaCodes)) parts.push(summarizeArrayChange(\"areaCodes\", O.areaCodes, G.areaCodes));\n      if ((G.nationalPrefix || null) !== (O.nationalPrefix || null)) parts.push(`nationalPrefix: ${JSON.stringify(O.nationalPrefix || null)} -> ${JSON.stringify(G.nationalPrefix || null)}`);\n      if (parts.length) {\n        changed++;\n        lines.push(`~ ${iso}: ${parts.join(\", \")}`);\n      }\n    }\n    const out = [`# Summary: +${added} added, ~${changed} changed, -${removed} removed`, ...(lines.length ? lines : [])].join(\"\\n\");\n    console.log(out);\n    // Write to tmp file\n    const outDir = path.resolve(REPO_ROOT, \"tmp\");\n    try { fs.mkdirSync(outDir, { recursive: true }); } catch { /* ignore */ }\n    fs.writeFileSync(path.join(outDir, \"rawCountryData.diff.txt\"), out + \"\\n\", \"utf8\");\n    if (added || removed || changed) {\n      // Exit with non-zero to signal differences (suitable for CI failure)\n      process.exit(1);\n    }\n  }\n\n  runDiff();\n\n  // iso2 list is sourced from iso2-codes.js; we never write it from this script\n}\n\ntry {\n  main();\n} catch (e) {\n  console.error(\"Generation failed:\", e && e.stack ? e.stack : e);\n  process.exit(1);\n}\n"
  },
  {
    "path": "scripts/playwright-linux-docker.sh",
    "content": "#!/usr/bin/env bash\nset -euo pipefail\n\nPLAYWRIGHT_VERSION_DEFAULT=\"1.58.0\"\nPLAYWRIGHT_VERSION=\"$PLAYWRIGHT_VERSION_DEFAULT\"\n\n# Try to keep the Docker image version in sync with the repo's Playwright version.\nif command -v node >/dev/null 2>&1; then\n  DETECTED_VERSION=\"$(node -e \"\n    try {\n      const pkg = require('./package.json');\n      const spec = (pkg.devDependencies && (pkg.devDependencies['@playwright/test'] || pkg.devDependencies.playwright)) || '';\n      const v = String(spec).replace(/^[^0-9]*/, '');\n      if (v) process.stdout.write(v);\n    } catch (e) {\n      // ignore\n    }\n  \" 2>/dev/null || true)\"\n\n  if [[ -n \"$DETECTED_VERSION\" ]]; then\n    PLAYWRIGHT_VERSION=\"$DETECTED_VERSION\"\n  fi\nfi\n\nIMAGE_DEFAULT=\"mcr.microsoft.com/playwright:v${PLAYWRIGHT_VERSION}-jammy\"\nIMAGE=\"${PLAYWRIGHT_DOCKER_IMAGE:-$IMAGE_DEFAULT}\"\n\nif ! command -v docker >/dev/null 2>&1; then\n  echo \"Docker is required to run Playwright in Linux.\" >&2\n  echo \"Install Docker Desktop, then re-run this command.\" >&2\n  exit 1\nfi\n\nWORKDIR=\"/work\"\n\nHOST_UID=\"\"\nHOST_GID=\"\"\nif command -v id >/dev/null 2>&1; then\n  HOST_UID=\"$(id -u)\"\n  HOST_GID=\"$(id -g)\"\nfi\n\nDOCKER_ARGS=(\n  --rm\n  --shm-size=1g\n  -e HOME=/tmp\n  -e npm_config_cache=/tmp/.npm\n  -e npm_config_prefix=/tmp/.npm-global\n  -e npm_config_update_notifier=false\n  -e npm_config_fund=false\n  -e npm_config_audit=false\n  -e HOST_UID\n  -e HOST_GID\n  -v \"$(pwd):$WORKDIR\"\n  -w \"$WORKDIR\"\n  # Keep node_modules inside the container (avoid host permission issues and platform-specific deps).\n  -v \"$WORKDIR/node_modules\"\n)\n\n# Useful on Apple Silicon if the image/platform doesn't match.\nif [[ -n \"${PLAYWRIGHT_DOCKER_PLATFORM:-}\" ]]; then\n  DOCKER_ARGS+=( --platform \"$PLAYWRIGHT_DOCKER_PLATFORM\" )\nfi\n\n# Run the full workflow inside the container:\n# - install deps\n# - build\n# - run Playwright tests (pass through any extra args)\nexec docker run \"${DOCKER_ARGS[@]}\" \"$IMAGE\" bash -lc \\\n  '\n    set -euo pipefail\n\n    mkdir -p \"$npm_config_cache\" \"$npm_config_prefix\"\n\n    npm ci\n\n    # Closure Compiler (grunt-google-closure-compiler) requires Java.\n    apt-get update\n    apt-get install -y --no-install-recommends openjdk-17-jre-headless\n    rm -rf /var/lib/apt/lists/*\n\n    npm run build\n\n    npm run test:e2e -- \"$@\"\n\n    # If we ran as root, ensure generated files are owned by the host user.\n    if [[ -n \"${HOST_UID:-}\" && -n \"${HOST_GID:-}\" ]]; then\n      chown -R \"${HOST_UID}:${HOST_GID}\" \\\n        \"$WORKDIR/build\" \\\n        \"$WORKDIR/tmp\" \\\n        \"$WORKDIR/playwright-report\" \\\n        \"$WORKDIR/test-results\" \\\n        \"$WORKDIR/tests-e2e\" \\\n        2>/dev/null || true\n    fi\n  ' -- \"$@\"\n"
  },
  {
    "path": "site/.gitignore",
    "content": "node_modules/\ntmp/\nbuild/\n"
  },
  {
    "path": "site/Gruntfile.js",
    "content": "module.exports = function (grunt) {\n  // load all tasks from package.json\n  require(\"load-grunt-config\")(grunt);\n\n  // build css\n  grunt.registerTask(\"build:css\", [\n    \"sass\",\n    \"template:website_css\",\n    \"template:large_flags_overrides_css\",\n    \"cssmin\",\n  ]);\n\n  // build esbuild\n  grunt.registerTask(\"build:esbuild\", [\n    \"template:lookup_country_js\", // needs to go through esbuild for IPAPI_TOKEN injection\n    \"template:right_to_left_js\",\n    \"template:angular_component_js\",\n    \"template:react_component_js\",\n    \"template:playground_js\",\n    \"shell:esbuild\",\n  ]);\n\n  // build vue component\n  grunt.registerTask(\"build:vue_component\", [\n    \"template:vue_component_js\",\n    \"shell:vite\",\n  ]);\n\n  // build svelte component\n  grunt.registerTask(\"build:svelte_component\", [\n    \"template:svelte_component_js\",\n    \"shell:viteSvelte\",\n  ]);\n\n  // build all\n  grunt.registerTask(\"build\", [\n    \"shell:clearBuild\",\n    \"shell:fetchStats\",\n    \"copy\",\n    \"build:css\",\n    \"build:esbuild\",\n    \"build:vue_component\",\n    \"build:svelte_component\",\n    \"template\",\n    \"replace:validationPrecise\",\n    \"strip-html-comments\",\n  ]);\n\n  grunt.registerTask(\"strip-html-comments\", () => {\n    const htmlFiles = grunt.file.expand({ dot: true }, [\"build/**/*.html\"]);\n    let updatedCount = 0;\n\n    htmlFiles.forEach((filePath) => {\n      const input = grunt.file.read(filePath);\n      const output = input.replace(/<!--[\\s\\S]*?-->/g, \"\");\n      if (output !== input) {\n        grunt.file.write(filePath, output);\n        updatedCount += 1;\n      }\n    });\n\n    grunt.log.writeln(\n      `strip-html-comments: processed ${htmlFiles.length} files, updated ${updatedCount}`\n    );\n  });\n};\n"
  },
  {
    "path": "site/README.md",
    "content": "# Website for intl-tel-input\n\nLive here: https://intl-tel-input.com\n\nThere are 4 page types:\n- Homepage\n- Docs page\n- Playground\n- Examples page\n\n## Contributing\n\nTo build and run the site locally:\n- Install the dependencies: run `npm install` in the root of the intl-tel-input project\n- Build the website: cd into the site/ directory and run `npm run build`\n- Run the website: cd into the build/ directory and start a web server, e.g. by running `http-server` (you may have to run `npm install -g http-server` first), and it will give you an address to open in your browser, and you should see the site running locally.\n\nMaking changes:  \nIn the site/ directory, run `npm run watch` to automatically re-build when you edit the source files.\n\nSee src/ directory for HTML templates/partials, JS/CSS and the docs markdown files.\n\nSee grunt/template.js for where everything is threaded together."
  },
  {
    "path": "site/esbuild/build.mjs",
    "content": "import { build } from \"esbuild\";\nimport externalUtilsPlugin from \"./externalUtilsPlugin.mjs\";\n\nconst sharedOptions = {\n  bundle: true,\n  plugins: [externalUtilsPlugin],\n  minify: true,\n  define: {\n    // This replaces the string \"process.env.IPAPI_TOKEN\" with the actual value from Cloudflare's environment\n    \"process.env.IPAPI_TOKEN\": JSON.stringify(process.env.IPAPI_TOKEN || \"\"),\n  },\n};\n\n// lookup country example\nbuild({\n  ...sharedOptions,\n  entryPoints: [\"tmp/examples/js/lookup_country.js\"],\n  outfile: \"build/examples/js/lookup_country.js\",\n});\n\n// right to left example\nbuild({\n  ...sharedOptions,\n  entryPoints: [\"tmp/examples/js/right_to_left.js\"],\n  outfile: \"build/examples/js/right_to_left_bundle.js\",\n});\n\n// react component example\nbuild({\n  ...sharedOptions,\n  loader: { \".js\": \"jsx\" },\n  entryPoints: [\"tmp/examples/js/react_component.js\"],\n  outfile: \"build/examples/js/react_component_bundle.js\",\n});\n\n// angular component example\nbuild({\n  ...sharedOptions,\n  loader: { \".ts\": \"ts\" },\n  tsconfig: \"../angular/tsconfig.json\",\n  entryPoints: [\"tmp/examples/js/angular_component.ts\"],\n  outfile: \"build/examples/js/angular_component_bundle.js\",\n});\n\n// playground\nbuild({\n  ...sharedOptions,\n  entryPoints: [\"src/playground/js/playground.js\"],\n  outfile: \"build/js/playground.js\",\n});\n\n// all JS files in /src/js\nbuild({\n  ...sharedOptions,\n  entryPoints: [\"src/js/**/*.js\"],\n  outdir: \"build/js\",\n});"
  },
  {
    "path": "site/esbuild/externalUtilsPlugin.mjs",
    "content": "const externalUtilsPlugin = {\n  name: \"external-utils-plugin\",\n  setup({ onResolve }) {\n    onResolve({ filter: /utils/ }, args => {\n      return { path: args.path, external: true };\n    });\n  },\n};\n\nexport default externalUtilsPlugin;"
  },
  {
    "path": "site/grunt/copy.js",
    "content": "module.exports = function (grunt) {\n  return {\n    plugin: {\n      cwd: \"../build\", // set working folder / root to copy\n      src: \"**/*\", // copy all files and subfolders\n      dest: \"build/intl-tel-input\", // destination folder\n      expand: true, // required when using cwd\n    },\n    react: {\n      cwd: \"../react/build\", // set working folder / root to copy\n      src: \"**/*\", // copy all files and subfolders\n      dest: \"build/intl-tel-input/react\", // destination folder\n      expand: true, // required when using cwd\n    },\n    vue: {\n      cwd: \"../vue/build\", // set working folder / root to copy\n      src: \"**/*\", // copy all files and subfolders\n      dest: \"build/intl-tel-input/vue\", // destination folder\n      expand: true, // required when using cwd\n    },\n    angular: {\n      cwd: \"../angular/build\", // set working folder / root to copy\n      src: \"**/*\", // copy all files and subfolders\n      dest: \"build/intl-tel-input/angular\", // destination folder\n      expand: true, // required when using cwd\n    },\n    svelte: {\n      cwd: \"../svelte/build\", // set working folder / root to copy\n      src: \"**/*\", // copy all files and subfolders\n      dest: \"build/intl-tel-input/svelte\", // destination folder\n      expand: true, // required when using cwd\n    },\n    static: {\n      cwd: \"static\", // set working folder / root to copy\n      src: \"**/*\", // copy all files and subfolders\n      dest: \"build\", // destination folder\n      expand: true, // required when using cwd\n    },\n    htaccess: {\n      cwd: \"static\", // set working folder / root to copy\n      src: \".htaccess\", // copy the .htaccess dotfile\n      dest: \"build\", // destination folder\n      expand: true, // required when using cwd\n    },\n    examples_css: {\n      cwd: \"src/examples/css\", // set working folder / root to copy\n      src: \"*\",\n      dest: \"build/examples/css\", // destination folder\n      expand: true, // required when using cwd\n    }\n  };\n};\n"
  },
  {
    "path": "site/grunt/cssmin.js",
    "content": "module.exports = function(grunt) {\n  return {\n    target: {\n      files: {\n        // NOTE: don't minify large_flags_overrides.css because we link to it as an example from the large_flags example page\n        'build/css/website.css': 'build/css/website.css',\n        'build/css/playground.css': 'build/css/playground.css',\n        'build/css/homepage.css': 'build/css/homepage.css',\n        'build/css/docs.css': 'build/css/docs.css'\n      }\n    }\n  };\n};\n"
  },
  {
    "path": "site/grunt/fetchStats.js",
    "content": "const https = require(\"https\");\nconst fs = require(\"fs\");\nconst path = require(\"path\");\n\nfunction httpsGet(url) {\n  return new Promise((resolve, reject) => {\n    https.get(url, { headers: { \"User-Agent\": \"intl-tel-input-build\" } }, (res) => {\n      let data = \"\";\n      res.on(\"data\", (chunk) => { data += chunk; });\n      res.on(\"end\", () => {\n        if (res.statusCode !== 200) {\n          reject(new Error(`HTTP ${res.statusCode} for ${url}`));\n        } else {\n          resolve(data);\n        }\n      });\n    }).on(\"error\", reject);\n  });\n}\n\nfunction formatNumber(n) {\n  if (n >= 1_000_000) {\n    const val = n / 1_000_000;\n    return `${val % 1 === 0 ? val.toFixed(0) : val.toFixed(1)}M`;\n  }\n  if (n >= 1_000) {\n    const val = n / 1_000;\n    return `${val % 1 === 0 ? val.toFixed(0) : val.toFixed(1)}k`;\n  }\n  return `${n}`;\n}\n\nfunction roundToDisplay(n) {\n  if (n >= 1_000_000) {\n    return Math.round(n / 100_000) * 100_000;\n  }\n  if (n >= 100_000) {\n    return Math.round(n / 1_000) * 1_000;\n  }\n  if (n >= 1_000) {\n    return Math.round(n / 100) * 100;\n  }\n  return n;\n}\n\nasync function fetchStats() {\n  const statsPath = path.join(__dirname, \"..\", \"tmp\", \"stats.json\");\n\n  // Default/fallback values\n  const defaults = { websites: \"120k\", downloads: \"3.2M\", stars: \"8.2k\" };\n\n  const isProd = process.argv.includes(\"--env=prod\");\n  if (!isProd) {\n    console.log(\"Dev build — using default stats\");\n    fs.mkdirSync(path.dirname(statsPath), { recursive: true });\n    fs.writeFileSync(statsPath, JSON.stringify(defaults, null, 2));\n    return;\n  }\n\n  let stats = { ...defaults };\n\n  try {\n    const ghData = JSON.parse(\n      await httpsGet(\"https://api.github.com/repos/jackocnr/intl-tel-input\")\n    );\n    stats.stars = formatNumber(roundToDisplay(ghData.stargazers_count));\n  } catch (e) {\n    console.warn(\"Failed to fetch GitHub stars, using fallback:\", e.message);\n  }\n\n  try {\n    const npmData = JSON.parse(\n      await httpsGet(\"https://api.npmjs.org/downloads/point/last-month/intl-tel-input\")\n    );\n    stats.downloads = formatNumber(roundToDisplay(npmData.downloads));\n  } catch (e) {\n    console.warn(\"Failed to fetch npm downloads, using fallback:\", e.message);\n  }\n\n  try {\n    const nerdyHtml = await httpsGet(\n      \"https://www.nerdydata.com/reports/international-telephone-input/719de9d2-d0e7-4988-b02f-9f9d52687076\"\n    );\n    const match = nerdyHtml.match(/\"answerCount\"\\s*:\\s*(\\d+)/);\n    if (match) {\n      stats.websites = formatNumber(roundToDisplay(Number(match[1])));\n    }\n  } catch (e) {\n    console.warn(\"Failed to fetch NerdyData count, using fallback:\", e.message);\n  }\n\n  fs.mkdirSync(path.dirname(statsPath), { recursive: true });\n  fs.writeFileSync(statsPath, JSON.stringify(stats, null, 2));\n  console.log(\"Stats fetched:\", stats);\n}\n\nfetchStats();\n"
  },
  {
    "path": "site/grunt/replace.js",
    "content": "module.exports = function(grunt) {\n  return {\n    validationPrecise: {\n      options: {\n        patterns: [\n          {\n            match: /\\biti\\.isValidNumber\\(\\)/g,\n            replacement: \"iti.isValidNumberPrecise()\",\n          },\n        ],\n      },\n      files: {\n        \"build/examples/js/validation_precise.js\": \"build/examples/js/validation_precise.js\",\n      },\n    }\n  };\n};"
  },
  {
    "path": "site/grunt/sass.js",
    "content": "const sass = require('sass');\n\nmodule.exports = function(grunt) {\n  return {\n    main: {\n      options: {\n        implementation: sass,\n        sourcemap: \"none\",\n      },\n      files: {\n        'build/css/website.css': 'src/css/website.scss',\n        'build/css/large_flags_overrides.css': 'src/css/large_flags_overrides.scss',\n        'build/css/highlightjs_overrides.css': 'src/css/highlightjs_overrides.scss',\n        'build/css/playground.css': 'src/css/playground.scss',\n        'build/css/docs.css': 'src/css/docs.scss',\n        'build/css/homepage.css': 'src/css/homepage.scss',\n      }\n    },\n  };\n};\n"
  },
  {
    "path": "site/grunt/shell.js",
    "content": "const os = require('os');\n\nmodule.exports = function(grunt) {\n  return {\n    clearBuild: {\n      command: \"rm -rf build tmp\",\n    },\n    fetchStats: {\n      command: `node grunt/fetchStats.js --env=${grunt.option(\"env\") || \"dev\"}`,\n    },\n    esbuild: {\n      command: \"node esbuild/build.mjs\",\n    },\n    vite: {\n      command: \"vite build --config src/examples/js/viteVueDemo.config.js\",\n    },\n    viteSvelte: {\n      command: \"vite build --config src/examples/js/viteSvelteDemo.config.mjs\",\n    },\n  };\n};\n"
  },
  {
    "path": "site/grunt/template.js",
    "content": "module.exports = function (grunt) {\n  const path = require(\"path\");\n  const {\n    cacheBust,\n    getDirHash,\n    getI18nLanguages,\n    createMarkdownRenderer,\n    buildOpenGraphMetaTags,\n  } = require(\"./templateUtils\");\n\n  const env = grunt.option(\"env\");\n  const isDevBuild = env === \"dev\" || env === \"development\";\n  const showRightSidebarAd = !isDevBuild;\n\n  const {\n    makeTemplateTask,\n    makeLayoutTask,\n    readCommonPagePartials,\n    readCommonBodyEndScript,\n    readItiLiveResultsScript,\n    readItiScript,\n  } = require(\"./templateGruntHelpers\");\n\n  // Helper: create a cache-bust template task for a built asset path\n  const makeCacheBustTask = (assetPath) => ({\n    src: assetPath,\n    dest: assetPath,\n    options: { data: () => ({ cacheBust }) },\n  });\n\n  const {\n    docsDropdownPages,\n    examplesDropdownPages,\n  } = require(\"./templateNav\");\n\n  const md = createMarkdownRenderer();\n\n  const toBcp47LanguageTag = (code) => {\n    const raw = String(code || \"\").trim();\n    if (!raw) return \"\";\n    const parts = raw.split(\"-\");\n    if (parts.length === 1) return parts[0].toLowerCase();\n    const [lang, region, ...rest] = parts;\n    const normLang = String(lang).toLowerCase();\n    const normRegion = region && region.length === 2 ? String(region).toUpperCase() : String(region || \"\");\n    return [normLang, normRegion, ...rest].filter(Boolean).join(\"-\");\n  };\n\n  const escapeHtml = (value) =>\n    String(value ?? \"\")\n      .replace(/&/g, \"&amp;\")\n      .replace(/</g, \"&lt;\")\n      .replace(/>/g, \"&gt;\")\n      .replace(/\\\"/g, \"&quot;\")\n      .replace(/'/g, \"&#39;\");\n\n  const createI18nLanguageListText = (languageCodes) => {\n    const codes = Array.isArray(languageCodes) ? languageCodes.filter(Boolean) : [];\n    if (!codes.length) {\n      return \"_No language modules found._\";\n    }\n\n    let displayNames = null;\n    try {\n      if (typeof Intl !== \"undefined\" && Intl.DisplayNames) {\n        displayNames = new Intl.DisplayNames([\"en\"], { type: \"language\" });\n      }\n    } catch {\n      displayNames = null;\n    }\n\n    const items = codes\n      .map((code) => {\n        const tag = toBcp47LanguageTag(code);\n        let label = null;\n        try {\n          label = displayNames && tag ? displayNames.of(tag) : null;\n        } catch {\n          label = null;\n        }\n\n        return {\n          code: String(code),\n          label: label ? String(label) : \"\",\n        };\n      });\n\n    items.sort((a, b) => {\n      const aKey = a.label || a.code;\n      const bKey = b.label || b.code;\n      return aKey.localeCompare(bKey, \"en\", { sensitivity: \"base\" });\n    });\n\n    return items\n      .map(({ code, label }) => {\n        const text = label ? `${label} (${code})` : code;\n        return escapeHtml(text);\n      })\n      .join(\", \");\n  };\n\n  const homepageTitle = \"International Telephone Input\";\n  const homepageMetaDesc = \"A JavaScript plugin for entering, formatting and validating international telephone numbers. Includes React, Vue, Angular and Svelte components.\";\n  const homepageCanonicalUrl = \"https://intl-tel-input.com\";\n\n  const playgroundTitle = \"Playground - International Telephone Input\";\n  const playgroundMetaDesc = \"Try different initialisation options and see the plugin update live.\";\n  const playgroundCanonicalUrl = \"https://intl-tel-input.com/playground\";\n\n  const notFoundTitle = \"404 - Page not found | intl-tel-input\";\n  const notFoundMetaDesc = \"Page not found.\";\n  const notFoundCanonicalUrl = \"https://intl-tel-input.com/404\";\n\n  const config = {\n    // cache bust common assets\n    iti_script: {\n      src: \"src/shared/iti_script.html.ejs\",\n      dest: \"tmp/shared/iti_script.html\",\n      options: {\n        data: () => ({ cacheBust, isDevBuild }),\n      },\n    },\n    website_css: makeCacheBustTask(\"build/css/website.css\"),\n    homepage_css: makeCacheBustTask(\"build/css/homepage.css\"),\n    docs_css: makeCacheBustTask(\"build/css/docs.css\"),\n    playground_css: makeCacheBustTask(\"build/css/playground.css\"),\n    large_flags_overrides_css: makeCacheBustTask(\"build/css/large_flags_overrides.css\"),\n\n    // homepage\n    homepage_layout: {\n      src: \"src/layout_template.html.ejs\",\n      dest: \"tmp/homepage/homepage_layout.html\",\n      options: {\n        data: () => {\n          const stats = JSON.parse(grunt.file.read(\"tmp/stats.json\"));\n          const content = grunt.file.read(\"src/homepage/homepage_content.html\")\n            .replace(\"{{STAT_WEBSITES}}\", stats.websites)\n            .replace(\"{{STAT_DOWNLOADS}}\", stats.downloads)\n            .replace(\"{{STAT_STARS}}\", stats.stars);\n          return {\n            layoutClass: \"iti-layout-no-sidebars\",\n            showLeftSidebar: false,\n            content,\n            name: \"home\",\n            pageType: \"home\",\n            docsDropdownPages,\n            examplesDropdownPages,\n          };\n        },\n      },\n    },\n    homepage_page: {\n      src: \"src/homepage/homepage_page_template.html.ejs\",\n      dest: \"build/index.html\",\n      options: {\n        data: () => ({\n          homepageTitle,\n          homepageMetaDesc,\n          homepageCanonicalUrl,\n          cacheBust,\n          isDevBuild,\n          ...readCommonPagePartials(grunt, {\n            cacheBust,\n            isDevBuild,\n            iti_styles: \"homepage\",\n          }),\n          og_meta_tags: buildOpenGraphMetaTags({\n            title: homepageTitle,\n            description: homepageMetaDesc,\n            url: homepageCanonicalUrl,\n          }),\n          layout: grunt.file.read(\"tmp/homepage/homepage_layout.html\"),\n          common_body_end: readCommonBodyEndScript(grunt),\n          iti_live_results_script: readItiLiveResultsScript(grunt, { cacheBust }),\n          iti_script: readItiScript(grunt),\n        }),\n      },\n    },\n\n    // playground\n    playground_js: {\n      src: \"src/playground/js/templates/playgroundConstants.js.ejs\",\n      dest: \"tmp/playground/playgroundConstants.js\",\n      options: {\n        data: () => ({\n          cacheBust,\n          getDirHash,\n          i18nLanguages: getI18nLanguages(),\n        }),\n      },\n    },\n    playground_layout: {\n      src: \"src/layout_template.html.ejs\",\n      dest: \"tmp/playground/playground_layout.html\",\n      options: {\n        data: () => ({\n          showLeftSidebar: false,\n          layoutClass: \"iti-layout-no-sidebars iti-layout--playground\",\n          content: grunt.file.read(\"src/playground/playground_content.html\"),\n          pageType: \"playground\",\n          name: \"playground\",\n          docsDropdownPages,\n          examplesDropdownPages,\n        }),\n      },\n    },\n    playground_page: {\n      src: \"src/playground/playground_page_template.html.ejs\",\n      dest: \"build/playground.html\",\n      options: {\n        data: () => ({\n          playgroundTitle,\n          playgroundMetaDesc,\n          playgroundCanonicalUrl,\n          cacheBust,\n          ...readCommonPagePartials(grunt, {\n            cacheBust,\n            isDevBuild,\n            iti_styles: \"normal\",\n            highlightjs_styles: true,\n          }),\n          og_meta_tags: buildOpenGraphMetaTags({\n            title: playgroundTitle,\n            description: playgroundMetaDesc,\n            url: playgroundCanonicalUrl,\n          }),\n          layout: grunt.file.read(\"tmp/playground/playground_layout.html\"),\n          common_body_end: readCommonBodyEndScript(grunt),\n          iti_live_results_script: readItiLiveResultsScript(grunt, { cacheBust }),\n          iti_script: readItiScript(grunt),\n        }),\n      },\n    },\n\n    // 404\n    not_found_layout: {\n      src: \"src/layout_template.html.ejs\",\n      dest: \"tmp/404/not_found_layout.html\",\n      options: {\n        data: () => ({\n          showLeftSidebar: false,\n          layoutClass: \"iti-layout-no-sidebars\",\n          content: grunt.file.read(\"src/404/404_content.html\"),\n          pageType: \"home\",\n          name: \"404\",\n          docsDropdownPages,\n          examplesDropdownPages,\n        }),\n      },\n    },\n    not_found_page: {\n      src: \"src/404/404_page_template.html.ejs\",\n      dest: \"build/404.html\",\n      options: {\n        data: () => ({\n          cacheBust,\n          head_title: notFoundTitle,\n          canonical_url: notFoundCanonicalUrl,\n          meta_desc: notFoundMetaDesc,\n          og_meta_tags: buildOpenGraphMetaTags({\n            title: notFoundTitle,\n            description: notFoundMetaDesc,\n            url: notFoundCanonicalUrl,\n          }),\n          ...readCommonPagePartials(grunt, {\n            cacheBust,\n            isDevBuild,\n            iti_styles: \"none\",\n          }),\n          layout: grunt.file.read(\"tmp/404/not_found_layout.html\"),\n          common_body_end: readCommonBodyEndScript(grunt),\n        }),\n      },\n    },\n  };\n\n  const registerExample = ({\n    key,\n    title,\n    metaDesc,\n    js = {},\n    extraJsTasks = [],\n    content = {},\n    layoutExtra = {},\n    pageExtra = {},\n  }) => {\n    const slug = key.replace(/_/g, \"-\");\n    const jsSrc = js.src || `src/examples/js/${key}.js`;\n    // some examples build to tmp first\n    const jsDest = js.dest || `${js.destDir || \"build\"}/examples/js/${key}.js`;\n    const displayCode = content.displayCode || jsDest;\n    const scriptName = js.script || `${key}.js`;\n\n    const contentDest = content.dest || `tmp/examples/${key}_content.html`;\n    const layoutDest = content.layoutDest || `tmp/examples/${key}_layout.html`;\n    const pageDest = content.pageDest || `build/examples/${slug}.html`;\n\n    // the HTML to actually use for the demo\n    const markupName = content.markupName || key;\n    const markupPath = `src/examples/html/${markupName}.html`;\n    // the (cleaner) HTML we want to display in the \"Html\" section\n    const displayMarkupCandidate = `src/examples/html/${markupName}_display_code.html`;\n    const displayMarkupPath = grunt.file.exists(displayMarkupCandidate) ? displayMarkupCandidate : markupPath;\n\n    const fullTitle = `${title} example - International Telephone Input`;\n    const canonicalUrl = `https://intl-tel-input.com/examples/${slug}`;\n\n    const templateData = {\n      cacheBust,\n      ...(js.data || {}),\n    };\n\n    config[`${key}_js`] = makeTemplateTask(jsSrc, jsDest, () => templateData);\n\n    extraJsTasks.forEach((t) => {\n      config[t.key] = makeTemplateTask(t.src, t.dest, () => ({ cacheBust }));\n    });\n\n    config[`${key}_content`] = makeTemplateTask(\n      \"src/examples/examples_content_template.html.ejs\",\n      contentDest,\n      () => ({\n        cacheBust,\n        content_title: title,\n        desc: grunt.file.read(`src/examples/copy/${key}_desc.html`),\n        markup: grunt.file.read(markupPath),\n        display_markup: grunt.file.read(displayMarkupPath),\n        display_code: (() => {\n          let displayCodeContent = grunt.file.read(displayCode);\n          // hack so that the validation_precise example page shows the right validation method in the displayed code\n          if (key === \"validation_precise\") {\n            displayCodeContent = displayCodeContent.replace(\n              /\\biti\\.isValidNumber\\(\\)/g,\n              \"iti.isValidNumberPrecise()\"\n            );\n          }\n          return grunt.template.process(displayCodeContent, { data: templateData });\n        })(),\n        script: scriptName,\n        ...(content.demo_note ? { demo_note: content.demo_note } : {}),\n        ...(content.hideMarkupSection\n          ? { hideMarkupSection: true }\n          : {}),\n        ...(content.isRtl ? { isRtl: true } : {}),\n        ...(content.extraData ? content.extraData() : {}),\n        common_body_end: readCommonBodyEndScript(grunt),\n        ...(content.includeItiScript\n          ? { iti_script: readItiScript(grunt) }\n          : {}),\n      })\n    );\n\n    config[`${key}_layout`] = makeLayoutTask(grunt, {\n      dest: layoutDest,\n      showLeftSidebar: true,\n      layoutClass: \"iti-layout-both-sidebars\",\n      navPath: \"src/examples/examples_nav_template.html.ejs\",\n      contentPath: contentDest,\n      name: key,\n      pageType: \"examples\",\n      docsDropdownPages,\n      examplesDropdownPages,\n      extra: {\n        ...layoutExtra,\n        show_right_sidebar_ad: showRightSidebarAd,\n      },\n    });\n\n    config[`${key}_page`] = makeTemplateTask(\n      \"src/examples/examples_page_template.html.ejs\",\n      pageDest,\n      () => ({\n        cacheBust,\n        head_title: fullTitle,\n        canonical_url: canonicalUrl,\n        meta_desc: metaDesc,\n        og_meta_tags: buildOpenGraphMetaTags({\n          title: fullTitle,\n          description: metaDesc,\n          url: canonicalUrl,\n        }),\n        ...readCommonPagePartials(grunt, {\n          cacheBust,\n          isDevBuild,\n          iti_styles: pageExtra.iti_styles || \"normal\",\n          highlightjs_styles: true,\n        }),\n        content: grunt.file.read(layoutDest),\n        ...pageExtra,\n      })\n    );\n  };\n\n  const docsDefinitions = [{\n    key: \"choose_integration\",\n    title: \"Choose integration\",\n    metaDesc: \"Which integration of intl-tel-input is right for you? Pure JavaScript, React, Vue, Angular or Svelte component?\",\n  }, {\n    key: \"getting_started\",\n    title: \"Getting started\",\n    metaDesc: \"How to quickly get up and running with intl-tel-input.\",\n  }, {\n    key: \"options\",\n    title: \"Initialisation options\",\n    metaDesc: \"All the different options you can use when initialising intl-tel-input.\",\n  }, {\n    key: \"localisation\",\n    title: \"Localisation\",\n    metaDesc: \"How to localise country names and user interface strings, including RTL support.\",\n  }, {\n    key: \"accessibility\",\n    title: \"Accessibility\",\n    metaDesc: \"Accessibility guidance for intl-tel-input, including keyboard and screen reader support.\",\n  }, {\n    key: \"methods\",\n    title: \"Methods\",\n    metaDesc: \"All the different methods you can call on an intl-tel-input instance.\",\n  }, {\n    key: \"events\",\n    title: \"Events\",\n    metaDesc: \"All the different events that an intl-tel-input instance can emit.\",\n  }, {\n    key: \"utils\",\n    title: \"Utilities script\",\n    metaDesc: \"Learn about the utils script, what it's for and how to load it.\",\n  }, {\n    key: \"theming\",\n    title: \"Theming / dark mode\",\n    metaDesc: \"How to theme the plugin, including how to set it up for dark mode.\",\n  }, {\n    key: \"troubleshooting\",\n    title: \"Troubleshooting\",\n    metaDesc: \"Solutions to common problems and FAQs about intl-tel-input.\",\n  }, {\n    key: \"faq\",\n    title: \"FAQ\",\n    metaDesc: \"Frequently asked questions about intl-tel-input, including common setup and localisation topics.\",\n  }, {\n    key: \"react_component\",\n    title: \"React component\",\n    metaDesc: \"How to use the intl-tel-input React component.\",\n  }, {\n    key: \"vue_component\",\n    title: \"Vue component\",\n    metaDesc: \"How to use the intl-tel-input Vue component.\",\n  }, {\n    key: \"angular_component\",\n    title: \"Angular component\",\n    metaDesc: \"How to use the intl-tel-input Angular component.\",\n  }, {\n    key: \"svelte_component\",\n    title: \"Svelte component\",\n    metaDesc: \"How to use the intl-tel-input Svelte component.\",\n  }];\n\n  const exampleDefinitions = [{\n    key: \"lookup_country\",\n    title: \"Lookup user's country\",\n    metaDesc: \"Automatically set the country based on the user's IP address.\",\n    js: {\n      // evaluate the template into tmp, then use esbuild for IPAPI_TOKEN injection\n      destDir: \"tmp\",\n    },\n    content: {\n      markupName: \"simple_input\",\n      includeItiScript: true,\n      displayCode: \"src/examples/js/lookup_country_display_code.js\",\n    },\n  }, {\n    key: \"right_to_left\",\n    title: \"Right to left\",\n    metaDesc: \"Support for right-to-left languages.\",\n    js: {\n      destDir: \"tmp\",\n      script: \"right_to_left_bundle.js\",\n    },\n    content: {\n      markupName: \"simple_input\",\n      isRtl: true,\n      displayCode: \"src/examples/js/right_to_left_display_code.js\",\n    },\n    layoutExtra: { isRtl: true },\n  }, {\n    key: \"single_country\",\n    title: \"Single country\",\n    metaDesc: \"When you only need to handle numbers from a single country.\",\n    content: {\n      demo_note: \"<p>Enter a US number:</p>\",\n      markupName: \"validation\",\n      includeItiScript: true,\n      displayCode: \"src/examples/js/single_country_display_code.js\",\n    },\n    pageExtra: { stylesheet_after_website_css: \"/examples/css/validation.css\" },\n  }, {\n    key: \"validation_practical\",\n    title: \"Validation\",\n    metaDesc:\n      \"Validate the user's phone number and if there's an error, display a relevant message.\",\n    js: {\n      src: \"src/examples/js/validation.js\",\n    },\n    content: {\n      markupName: \"validation\",\n      includeItiScript: true,\n      displayCode: \"src/examples/js/validation_display_code.js\",\n      extraData: () => ({\n        demo_note: `<p>NOTE: by default, <code>isValidNumber</code> only returns <code>true</code> for <i>mobile</i> and <i>fixed line</i> numbers. See <code>allowedNumberTypes</code> option for more information.</p>`,\n      }),\n    },\n    pageExtra: {\n      stylesheet_after_website_css: \"/examples/css/validation.css\",\n    },\n  }, {\n    key: \"validation_precise\",\n    title: \"Precise validation (advanced)\",\n    metaDesc:\n      \"Validate the user's phone number using the more precise method, and if there's an error, display a relevant message.\",\n    js: {\n      src: \"src/examples/js/validation.js\",\n    },\n    content: {\n      markupName: \"validation\",\n      includeItiScript: true,\n      displayCode: \"src/examples/js/validation_display_code.js\",\n      extraData: () => ({\n        demo_note: `<p>NOTE: by default, <code>isValidNumberPrecise</code> only returns <code>true</code> for <i>mobile</i> and <i>fixed line</i> numbers. See <code>allowedNumberTypes</code> option for more information.</p>`,\n      }),\n    },\n    pageExtra: {\n      stylesheet_after_website_css: \"/examples/css/validation.css\",\n    },\n  }, {\n    key: \"hidden_input\",\n    title: \"Hidden input\",\n    metaDesc:\n      \"Automatically populate a hidden input with the full international number, so it gets submitted to your backend.\",\n    content: {\n      includeItiScript: true,\n      markupName: \"validation\",\n      displayCode: \"src/examples/js/hidden_input_display_code.js\",\n    },\n  }, {\n    key: \"multiple_instances\",\n    title: \"Multiple instances\",\n    metaDesc: \"Use multiple instances of the plugin with different configurations on the same page.\",\n    content: {\n      includeItiScript: true,\n      displayCode: \"src/examples/js/multiple_instances_display_code.js\",\n    },\n    pageExtra: {\n      stylesheet_after_website_css: \"/examples/css/multiple_instances.css\",\n    },\n  }, {\n    key: \"display_number\",\n    title: \"Display existing number\",\n    metaDesc: \"Automatically format an existing number during initialisation.\",\n    js: {\n      src: \"src/examples/js/simple_init_plugin.js\",\n    },\n    content: {\n      includeItiScript: true,\n      displayCode: \"src/examples/js/simple_init_plugin_display_code.js\",\n    },\n  }, {\n    key: \"large_flags\",\n    title: \"Large flags\",\n    metaDesc: \"How to display extra large flag images.\",\n    js: {\n      src: \"src/examples/js/simple_init_plugin.js\",\n    },\n    content: {\n      markupName: \"simple_input\",\n      includeItiScript: true,\n      displayCode: \"src/examples/js/simple_init_plugin_display_code.js\",\n    },\n    pageExtra: {\n      iti_styles: \"largeFlags\",\n    },\n  }, {\n    key: \"angular_component\",\n    title: \"Angular component\",\n    metaDesc: \"How to use intl-tel-input with Angular.\",\n    js: {\n      src: \"src/examples/js/angular_component.ts\",\n      dest: \"tmp/examples/js/angular_component.ts\",\n      script: \"angular_component_bundle.js\",\n    },\n    content: {\n      markupName: \"component\",\n      hideMarkupSection: true,\n      displayCode: \"src/examples/js/angular_component_display_code.js\",\n      script: \"angular_component_bundle.js\",\n    },\n  }, {\n    key: \"react_component\",\n    title: \"React component\",\n    metaDesc: \"How to use intl-tel-input with React.\",\n    js: {\n      dest: \"tmp/examples/js/react_component.js\",\n      script: \"react_component_bundle.js\",\n    },\n    content: {\n      markupName: \"component\",\n      hideMarkupSection: true,\n      displayCode: \"src/examples/js/react_component_display_code.js\",\n      script: \"react_component_bundle.js\",\n    },\n  }, {\n    key: \"vue_component\",\n    title: \"Vue component\",\n    metaDesc: \"How to use intl-tel-input with Vue.\",\n    js: {\n      // need to specify the source because of the alternative .vue extension\n      src: \"src/examples/js/vue_component.vue\",\n      dest: \"tmp/examples/js/vue_component.vue\",\n      script: \"vue_component_bundle.js\",\n    },\n    content: {\n      markupName: \"component\",\n      hideMarkupSection: true,\n      displayCode: \"src/examples/js/vue_component_display_code.vue\",\n      script: \"vue_component_bundle.js\",\n    },\n  }, {\n    key: \"svelte_component\",\n    title: \"Svelte component\",\n    metaDesc: \"How to use intl-tel-input with Svelte.\",\n    js: {\n      // need to specify the source because of the alternative .svelte extension\n      src: \"src/examples/js/svelte_component.svelte\",\n      dest: \"tmp/examples/js/svelte_component.svelte\",\n      script: \"svelte_component_bundle.js\",\n    },\n    content: {\n      markupName: \"component\",\n      hideMarkupSection: true,\n      displayCode: \"src/examples/js/svelte_component_display_code.svelte\",\n      script: \"svelte_component_bundle.js\",\n    },\n  }];\n\n  exampleDefinitions.forEach((definition) => registerExample(definition));\n\n  docsDefinitions.forEach(({ key, title, metaDesc }) => {\n    const mdPath = path.join(\"src\", \"docs\", \"markdown\", `${key}.md`);\n    const urlSlug = key.replace(/_/g, \"-\");\n    const destPath = `build/docs/${urlSlug}.html`;\n    const canonicalUrl = `https://intl-tel-input.com/docs/${urlSlug}`;\n    const fullTitle = `${title} docs - International Telephone Input`;\n\n    config[`docs_content_${key}`] = {\n      src: \"src/docs/docs_content_template.html.ejs\",\n      dest: `tmp/docs/${key}_content.html`,\n      options: {\n        data: () => ({\n          docKey: key,\n          html: (() => {\n            let source = grunt.file.read(mdPath);\n            if (key === \"localisation\") {\n              const languageList = createI18nLanguageListText(getI18nLanguages());\n              source = source.replace(\"<!-- I18N_LANGUAGE_LIST -->\", `\\n${languageList}\\n`);\n            }\n            return md.render(source, { docKey: key });\n          })(),\n        }),\n      },\n    };\n\n    config[`docs_layout_${key}`] = {\n      src: \"src/layout_template.html.ejs\",\n      dest: `tmp/docs/${key}_layout.html`,\n      options: {\n        data: () => ({\n          showLeftSidebar: true,\n          layoutClass: \"iti-layout-both-sidebars\",\n          nav: grunt.file.read(\"src/docs/docs_nav_template.html.ejs\"),\n          content: grunt.file.read(`tmp/docs/${key}_content.html`),\n          name: key,\n          pageType: \"docs\",\n          docsDropdownPages,\n          examplesDropdownPages,\n          show_right_sidebar_ad: showRightSidebarAd,\n        }),\n      },\n    };\n\n    config[`docs_page_${key}`] = {\n      src: \"src/docs/docs_page_template.html.ejs\",\n      dest: destPath,\n      options: {\n        data: () => ({\n          cacheBust,\n          head_title: fullTitle,\n          canonical_url: canonicalUrl,\n          meta_desc: metaDesc,\n          og_meta_tags: buildOpenGraphMetaTags({\n            title: fullTitle,\n            description: metaDesc,\n            url: canonicalUrl,\n          }),\n          ...readCommonPagePartials(grunt, {\n            cacheBust,\n            isDevBuild,\n            highlightjs_styles: true,\n            iti_styles: \"none\",\n          }),\n          layout: grunt.file.read(`tmp/docs/${key}_layout.html`),\n          common_body_end: readCommonBodyEndScript(grunt),\n        }),\n      },\n    };\n  });\n\n  return config;\n};\n"
  },
  {
    "path": "site/grunt/templateGruntHelpers.js",
    "content": "const makeTemplateTask = (src, dest, data) => ({\n  src,\n  dest,\n  options: {\n    data,\n  },\n});\n\nconst readCommonPagePartials = (grunt, data) => ({\n  common_meta_tags: grunt.file.read(\"src/shared/common_meta_tags.html\"),\n  common_styles: grunt.template.process(grunt.file.read(\"src/shared/common_styles.html.ejs\"), {\n    data,\n  }),\n  common_head_end_prod: data && data.isDevBuild\n    ? \"\"\n    : grunt.file.read(\"src/shared/common_head_end_prod.html\"),\n});\n\nconst readCommonBodyEndScript = (grunt) => grunt.file.read(\"src/shared/common_body_end.html\");\n\nconst readItiLiveResultsScript = (grunt, data) =>\n  grunt.template.process(grunt.file.read(\"src/shared/iti_live_results_script.html.ejs\"), {\n    data,\n  });\n\nconst readItiScript = (grunt) => grunt.file.read(\"tmp/shared/iti_script.html\");\n\nconst makeLayoutTask = (\n  grunt,\n  {\n    dest,\n    showLeftSidebar,\n    layoutClass,\n    navPath,\n    contentPath,\n    name,\n    pageType,\n    docsDropdownPages,\n    examplesDropdownPages,\n    extra = {},\n  }\n) =>\n  makeTemplateTask(\"src/layout_template.html.ejs\", dest, () => ({\n    showLeftSidebar,\n    layoutClass,\n    ...(navPath ? { nav: grunt.file.read(navPath) } : {}),\n    content: grunt.file.read(contentPath),\n    name,\n    pageType,\n    docsDropdownPages,\n    examplesDropdownPages,\n    ...extra,\n  }));\n\nmodule.exports = {\n  makeTemplateTask,\n  makeLayoutTask,\n  readCommonPagePartials,\n  readCommonBodyEndScript,\n  readItiLiveResultsScript,\n  readItiScript,\n};\n"
  },
  {
    "path": "site/grunt/templateNav.js",
    "content": "const docsDropdownPages = [\n  { name: \"choose_integration\", href: \"/docs/choose-integration\", label: \"Choose integration\" },\n  { name: \"getting_started\", href: \"/docs/getting-started\", label: \"Getting started\" },\n  { name: \"options\", href: \"/docs/options\", label: \"Initialisation options\" },\n  { name: \"localisation\", href: \"/docs/localisation\", label: \"Localisation\" },\n  { name: \"accessibility\", href: \"/docs/accessibility\", label: \"Accessibility\" },\n  { name: \"methods\", href: \"/docs/methods\", label: \"Methods\" },\n  { name: \"events\", href: \"/docs/events\", label: \"Events\" },\n  { name: \"utils\", href: \"/docs/utils\", label: \"Utilities script\" },\n  { name: \"theming\", href: \"/docs/theming\", label: \"Theming / dark mode\" },\n  { name: \"troubleshooting\", href: \"/docs/troubleshooting\", label: \"Troubleshooting\" },\n  { name: \"faq\", href: \"/docs/faq\", label: \"FAQ\" },\n  { name: \"react_component\", href: \"/docs/react-component\", label: \"React component\" },\n  { name: \"vue_component\", href: \"/docs/vue-component\", label: \"Vue component\" },\n  { name: \"angular_component\", href: \"/docs/angular-component\", label: \"Angular component\" },\n  { name: \"svelte_component\", href: \"/docs/svelte-component\", label: \"Svelte component\" },\n];\n\nconst examplesDropdownPages = [\n  { name: \"validation_practical\", href: \"/examples/validation-practical\", label: \"Validation\" },\n  { name: \"lookup_country\", href: \"/examples/lookup-country\", label: \"Lookup user's country\" },\n  { name: \"single_country\", href: \"/examples/single-country\", label: \"Single country\" },\n  { name: \"right_to_left\", href: \"/examples/right-to-left\", label: \"Right to left\" },\n  { name: \"hidden_input\", href: \"/examples/hidden-input\", label: \"Hidden input\" },\n  { name: \"display_number\", href: \"/examples/display-number\", label: \"Display existing number\" },\n  { name: \"multiple_instances\", href: \"/examples/multiple-instances\", label: \"Multiple instances\" },\n  { name: \"validation_precise\", href: \"/examples/validation-precise\", label: \"Precise validation (advanced)\" },\n  { name: \"large_flags\", href: \"/examples/large-flags\", label: \"Large flags\" },\n  { name: \"react_component\", href: \"/examples/react-component\", label: \"React component\" },\n  { name: \"vue_component\", href: \"/examples/vue-component\", label: \"Vue component\" },\n  { name: \"angular_component\", href: \"/examples/angular-component\", label: \"Angular component\" },\n  { name: \"svelte_component\", href: \"/examples/svelte-component\", label: \"Svelte component\" },\n];\n\nmodule.exports = {\n  docsDropdownPages,\n  examplesDropdownPages,\n};\n"
  },
  {
    "path": "site/grunt/templateUtils.js",
    "content": "const path = require(\"path\");\nconst fs = require(\"fs\");\nconst crypto = require(\"crypto\");\nconst MarkdownIt = require(\"markdown-it\");\nconst markdownItAnchor = require(\"markdown-it-anchor\");\n\nconst BUILD_DIR = \"build\";\nconst HASH_LENGTH = 12;\n\nconst hashCacheByPath = new Map();\n\nconst toPosixPath = (p) => String(p || \"\").replace(/\\\\/g, \"/\");\n\nconst defaultSlugifyHeading = (value) =>\n  String(value)\n    .trim()\n    .toLowerCase()\n    // remove apostrophes\n    .replace(/['’]/g, \"\")\n    // replace non-alphanumeric with hyphens\n    .replace(/[^a-z0-9]+/g, \"-\")\n    // collapse repeats\n    .replace(/-+/g, \"-\")\n    // trim hyphens\n    .replace(/^-|-$/g, \"\");\n\n// This plugin (AI-generated) looks for the \"options\" doc (etc), and injects table layout markup for display purposes. It relies on each h6 option being immediately followed by a paragraph containing the Type/Default info, and then any remaining content for that option (e.g. description, examples) coming after that in the same section (until the next heading).\nconst addDocOptionsLayoutPlugin = (md) => {\n  md.core.ruler.after(\"inline\", \"iti_doc_options_layout\", (state) => {\n    const env = state.env || {};\n    const applyToDocKeys = [\"options\", \"react_component\", \"vue_component\", \"angular_component\", \"svelte_component\"];\n    if (!applyToDocKeys.includes(env.docKey)) return;\n\n    const tokens = state.tokens;\n    const isHeadingOpen = (token, tag) => token && token.type === \"heading_open\" && token.tag === tag;\n    const isAnyHeadingOpen = (token) => token && token.type === \"heading_open\" && /^h[1-6]$/.test(token.tag);\n\n    const makeHtmlBlock = (content) => {\n      const token = new state.Token(\"html_block\", \"\", 0);\n      token.content = content;\n      return token;\n    };\n\n    const startRowAndKeyCellMarkup = () =>\n      makeHtmlBlock(\n        '<div class=\"iti-doc-options__row\">\\n' +\n          '  <div class=\"iti-doc-options__cell iti-doc-options__cell--key\">\\n',\n      );\n\n    const endKeyCellAndStartValueCellMarkup = () =>\n      makeHtmlBlock(\n        \"  </div>\\n\" +\n          '  <div class=\"iti-doc-options__cell iti-doc-options__cell--value\">\\n',\n      );\n\n    const endValueCellAndRowMarkup = () => makeHtmlBlock(\"  </div>\\n</div>\\n\");\n\n    const nextTokens = [];\n    let i = 0;\n\n    const consumeThroughHeadingClose = (tagName) => {\n      while (i < tokens.length) {\n        const t = tokens[i];\n        nextTokens.push(t);\n        i += 1;\n        if (t.type === \"heading_close\" && t.tag === tagName) return;\n      }\n    };\n\n    const findHeadingCloseIndex = (startIndex, tagName) => {\n      for (let k = startIndex; k < tokens.length; k += 1) {\n        const t = tokens[k];\n        if (t.type === \"heading_close\" && t.tag === tagName) return k;\n      }\n      return -1;\n    };\n\n    // ad blocks are divs with class=\"article-ad\", but MarkdownIt treats divs and their contents as a single raw \"html_block\" token\n    const isAdBlockOpen = (token) => token.type === \"html_block\" && token.content.includes('class=\"article-ad\"');\n\n    const consumeUntilNextHeadingOrAdBlock = () => {\n      while (i < tokens.length) {\n        const t = tokens[i];\n        if (isAnyHeadingOpen(t) || isAdBlockOpen(t)) return;\n        nextTokens.push(t);\n        i += 1;\n      }\n    };\n\n    const consumeOptionBlock = () => {\n      nextTokens.push(startRowAndKeyCellMarkup());\n\n      // H6 heading (option name) lives in the key cell.\n      nextTokens.push(tokens[i]);\n      i += 1;\n      consumeThroughHeadingClose(\"h6\");\n\n      // The Type/Default paragraph also lives in the key cell.\n      // Consume paragraph_open, inline, paragraph_close.\n      while (i < tokens.length && tokens[i].type !== \"paragraph_close\") {\n        nextTokens.push(tokens[i]);\n        i += 1;\n      }\n      if (i < tokens.length && tokens[i].type === \"paragraph_close\") {\n        nextTokens.push(tokens[i]);\n        i += 1;\n      }\n\n      // Switch to the value cell for the rest of the option content.\n      nextTokens.push(endKeyCellAndStartValueCellMarkup());\n\n      consumeUntilNextHeadingOrAdBlock();\n      nextTokens.push(endValueCellAndRowMarkup());\n    };\n\n    while (i < tokens.length) {\n      const token = tokens[i];\n      if (isHeadingOpen(token, \"h6\")) {\n        // Only treat this heading as an \"option\" row when the very next block\n        // after the heading close is a paragraph containing the Type/Default meta info\n        const closeIndex = findHeadingCloseIndex(i, \"h6\");\n        const afterClose = closeIndex >= 0 ? closeIndex + 1 : -1;\n        if (afterClose >= 0 && tokens[afterClose] && tokens[afterClose].type === \"paragraph_open\") {\n          const inlineToken = tokens[afterClose + 1];\n          const inlineContent = inlineToken && inlineToken.type === \"inline\" ? inlineToken.content : \"\";\n          if (inlineContent.includes(\"Type:\") && inlineContent.includes(\"Default:\")) {\n            consumeOptionBlock();\n            continue;\n          }\n        }\n\n        // Not an options entry: emit the heading tokens unchanged.\n        nextTokens.push(token);\n        i += 1;\n        continue;\n      }\n\n      nextTokens.push(token);\n      i += 1;\n    }\n\n    state.tokens = nextTokens;\n  });\n};\n\nconst createMarkdownRenderer = ({ slugifyHeading = defaultSlugifyHeading } = {}) => {\n  const md = new MarkdownIt({\n    html: true,\n    linkify: true,\n    typographer: true,\n  }).use(markdownItAnchor, {\n    slugify: slugifyHeading,\n    permalink: markdownItAnchor.permalink.headerLink({ safariReaderFix: true }),\n  });\n\n  addDocOptionsLayoutPlugin(md);\n  return md;\n};\n\nconst resolveBuildPathFromUrl = (urlPath) => {\n  const clean = toPosixPath(String(urlPath || \"\").split(\"?\")[0]);\n  const withoutLeadingSlash = clean.replace(/^\\//, \"\");\n  return path.join(BUILD_DIR, withoutLeadingSlash);\n};\n\nconst hashFile = (filePath) => {\n  const resolved = path.resolve(filePath);\n  if (hashCacheByPath.has(resolved)) return hashCacheByPath.get(resolved);\n\n  try {\n    const content = fs.readFileSync(resolved);\n    const hash = crypto\n      .createHash(\"sha256\")\n      .update(content)\n      .digest(\"hex\")\n      .slice(0, HASH_LENGTH);\n    hashCacheByPath.set(resolved, hash);\n    return hash;\n  } catch {\n    const fallback = \"missing\";\n    hashCacheByPath.set(resolved, fallback);\n    return fallback;\n  }\n};\n\nconst hashDirRecursive = (dirPath) => {\n  const resolved = path.resolve(dirPath);\n  const cacheKey = `${resolved}:dir`;\n  if (hashCacheByPath.has(cacheKey)) return hashCacheByPath.get(cacheKey);\n\n  try {\n    const files = [];\n    const walk = (currentDir) => {\n      fs.readdirSync(currentDir, { withFileTypes: true }).forEach((entry) => {\n        const abs = path.join(currentDir, entry.name);\n        if (entry.isDirectory()) {\n          walk(abs);\n        } else if (entry.isFile()) {\n          files.push(abs);\n        }\n      });\n    };\n\n    walk(resolved);\n    files.sort((a, b) => a.localeCompare(b));\n\n    const hasher = crypto.createHash(\"sha256\");\n    files.forEach((abs) => {\n      const rel = path.relative(resolved, abs);\n      hasher.update(rel);\n      hasher.update(\"\\0\");\n      hasher.update(fs.readFileSync(abs));\n      hasher.update(\"\\0\");\n    });\n\n    const hash = hasher.digest(\"hex\").slice(0, HASH_LENGTH);\n    hashCacheByPath.set(cacheKey, hash);\n    return hash;\n  } catch {\n    const fallback = \"missing\";\n    hashCacheByPath.set(cacheKey, fallback);\n    return fallback;\n  }\n};\n\n// Take a URL path (e.g. \"/intl-tel-input/js/utils.js\"), resolve it to a file in the build directory, hash that file, and return the URL with a cache-busting query param (e.g. \"/intl-tel-input/js/utils.js?v=abc123\").\nconst cacheBust = (urlPath) => {\n  const resolvedPath = resolveBuildPathFromUrl(urlPath);\n  return `${urlPath}?v=${hashFile(resolvedPath)}`;\n};\n\n// Take a URL path to a directory (e.g. \"/intl-tel-input/js/i18n\"), resolve it to a directory in the build directory, hash all files within that directory recursively, and return the hash to use as a cache-busting query param (e.g. \"abc123\").\nconst getDirHash = (urlDirPath) => {\n  const resolvedPath = resolveBuildPathFromUrl(urlDirPath);\n  return hashDirRecursive(resolvedPath);\n};\n\nconst getI18nLanguages = () => {\n  try {\n    const i18nDir = path.join(BUILD_DIR, \"intl-tel-input\", \"js\", \"i18n\");\n    return fs\n      .readdirSync(i18nDir, { withFileTypes: true })\n      .filter((d) => d.isDirectory())\n      .map((d) => d.name)\n      .filter(Boolean)\n      .sort((a, b) => a.localeCompare(b));\n  } catch {\n    return [];\n  }\n};\n\nconst escapeHtmlAttr = (value) =>\n  String(value ?? \"\")\n    .replace(/&/g, \"&amp;\")\n    .replace(/</g, \"&lt;\")\n    .replace(/>/g, \"&gt;\")\n    .replace(/\\\"/g, \"&quot;\");\n\nconst buildOpenGraphMetaTags = ({\n  title,\n  description,\n  url,\n}) => {\n  return [\n    `<meta property=\"og:site_name\" content=\"International Telephone Input\" />`,\n    `<meta property=\"og:type\" content=\"website\" />`,\n    `<meta property=\"og:title\" content=\"${escapeHtmlAttr(title)}\" />`,\n    `<meta property=\"og:description\" content=\"${escapeHtmlAttr(description)}\" />`,\n    `<meta property=\"og:url\" content=\"${escapeHtmlAttr(url)}\" />`,\n    `<meta property=\"og:image\" content=\"https://intl-tel-input.com/img/logo-green.png\" />`,\n  ].join(\"\\n\");\n};\n\nmodule.exports = {\n  createMarkdownRenderer,\n  cacheBust,\n  getDirHash,\n  getI18nLanguages,\n  buildOpenGraphMetaTags,\n};\n"
  },
  {
    "path": "site/grunt/watch.js",
    "content": "module.exports = function(grunt) {\n  return {\n    js: {\n      files: [\"src/**/*\", \"static/**/*\", \"grunt/**/*\", \"../build/**/*\"],\n      tasks: \"build\"\n    }\n  };\n};\n"
  },
  {
    "path": "site/package.json",
    "content": "{\n  \"name\": \"iti-website\",\n  \"version\": \"1.0.0\",\n  \"description\": \"The website for intl-tel-input\",\n  \"dependencies\": {\n    \"intl-tel-input\": \"*\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\"\n  },\n  \"devDependencies\": {\n    \"@angular/compiler\": \"^19.2.18\",\n    \"@angular/core\": \"^19.2.18\",\n    \"@angular/forms\": \"^19.2.18\",\n    \"@angular/platform-browser\": \"^19.2.18\",\n    \"@vitejs/plugin-vue\": \"^5.1.3\",\n    \"esbuild\": \"^0.25.0\",\n    \"eslint\": \"^8.38.0\",\n    \"grunt\": \"^1.6.1\",\n    \"grunt-contrib-copy\": \"^1.0.0\",\n    \"grunt-contrib-cssmin\": \"^5.0.0\",\n    \"grunt-contrib-watch\": \"^1.1.0\",\n    \"grunt-replace\": \"^2.0.2\",\n    \"grunt-sass\": \"^3.0.0\",\n    \"grunt-shell\": \"^4.0.0\",\n    \"grunt-template\": \"^1.0.0\",\n    \"load-grunt-config\": \"^4.0.1\",\n    \"markdown-it\": \"^14.1.0\",\n    \"markdown-it-anchor\": \"^9.2.0\",\n    \"prettier\": \"^2.8.7\",\n    \"prop-types\": \"^15.8.1\",\n    \"zone.js\": \"^0.15.1\"\n  },\n  \"scripts\": {\n    \"watch\": \"grunt watch --env=dev\",\n    \"build\": \"grunt build --env=dev\",\n    \"build:prod\": \"grunt build --env=prod\"\n  }\n}\n"
  },
  {
    "path": "site/src/404/404_content.html",
    "content": "<section class=\"py-5\">\n  <div class=\"row justify-content-center\">\n    <div class=\"col-12 col-md-10 col-lg-7 text-center\">\n      <h1 class=\"display-3 fw-semibold mb-3\">404</h1>\n      <p class=\"lead mb-4\">That page doesn’t exist.</p>\n\n      <div class=\"d-grid gap-2 d-sm-flex justify-content-sm-center\">\n        <a class=\"btn btn-primary\" href=\"/\">Go to homepage</a>\n      </div>\n    </div>\n  </div>\n</section>\n"
  },
  {
    "path": "site/src/404/404_page_template.html.ejs",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <%= common_meta_tags %>\n    <title><%= head_title %></title>\n    <link rel=\"canonical\" href=\"<%= canonical_url %>\" />\n    <meta name=\"description\" content=\"<%= meta_desc %>\">\n\n    <%= og_meta_tags %>\n    <%= common_styles %>\n\n    <%= common_head_end_prod %>\n  </head>\n\n  <body class=\"iti-page iti-page--404\">\n    <%= layout %>\n    <%= common_body_end %>\n  </body>\n</html>\n"
  },
  {
    "path": "site/src/css/_base.scss",
    "content": "html {\n  /* fix: links with # scroll values in (e.g. docs/options#separatedialcode) scroll until subtitle is below header, as it has position:sticky */\n  scroll-padding-top: 70px;\n}\n\nbody {\n  min-height: 100vh;\n  background-repeat: no-repeat;\n  background-size: cover;\n}\n\n* {\n  box-sizing: border-box;\n}\n\n/* vue and react logos in news section on homepage */\np img {\n  vertical-align: baseline;\n}\n\nh2,\nh3 {\n  margin-top: 20px;\n  margin-bottom: 10px;\n}\nh1 {\n  margin-bottom: 20px;\n}\n\n/* bootstrap override */\n@media (min-width: 1200px) {\n  .h2,\n  h2 {\n    font-size: 1.7rem;\n  }\n}\n\n.section {\n  margin-bottom: 30px;\n}\n\n/** Cookie / Google consent repositioning (desktop) **/\n@media (min-width: 800px) {\n  body .fc-consent-root .fc-dialog-container {\n    position: absolute !important;\n    bottom: 30px !important;\n    right: 30px !important;\n  }\n}\n\n.iti-live-results {\n  margin: 10px 0 20px;\n  border: 1px solid rgb(var(--header-bg-color-rgb));\n  padding: 10px;\n  border-radius: 3px;\n  background-color: rgb(var(--header-bg-color-rgb) / 0.3);\n  font-style: italic;\n  text-align: left;\n}\n\n/* Page breadcrumb (Docs/Examples) */\n.iti-breadcrumb {\n  margin-bottom: 0.25rem;\n  font-size: 0.875rem;\n  color: var(--bs-secondary-color);\n\n  &__list {\n    list-style: none;\n    padding: 0;\n    margin: 0;\n    display: flex;\n    flex-wrap: wrap;\n    align-items: center;\n  }\n  &__item + &__item::before {\n    content: \"/\";\n    opacity: 0.6;\n    padding: 0 0.4rem;\n  }\n  a {\n    color: inherit;\n    text-decoration: none;\n    &:hover {\n      text-decoration: underline;\n    }\n  }\n}\n\n// prevent layout shift when initialise components / show validation errors in demos\n#app,\n.validation-demo {\n  min-height: 71px;\n}\n\n// in-article ads should have some margin\n.article-ad {\n  margin: 20px 0;\n}\n\n// Anchor link styles for clickable section titles (used on Docs and Playground pages)\n.header-anchor {\n  // reset title styles, now that they're wrapped in a link\n  color: inherit;\n  &:not(:hover) {\n    text-decoration: none;\n  }\n  // on non-touch screens\n  @media (hover: hover) and (pointer: fine) {\n    &::before {\n      position: absolute;\n      content: \"#\";\n      margin-left: -0.9em; // space from title\n      padding-right: 0.9em; // allow moving cursor from title to icon without losing hover state\n      margin-top: 0.3em;\n      font-size: 0.75em;\n      color: var(--bs-secondary-color);\n    }\n  }\n  &:not(:hover,:focus)::before {\n    visibility: hidden;\n  }\n}"
  },
  {
    "path": "site/src/css/_forms.scss",
    "content": "// example page demos\n.demo input[type=\"tel\"] {\n  width: 250px;\n}\n// override bootstrap default placeholder color which is too high contrast for our design\ninput::placeholder,\n.demo input::placeholder,\ninput.form-control::placeholder,\ntextarea.form-control::placeholder {\n  color: #bbb;\n\n  @media (prefers-color-scheme: dark) {\n    color: #5a5a5a;\n  }\n}\n// match bootstrap input border radius\n.iti__selected-country-primary {\n  border-top-left-radius: var(--bs-border-radius);\n  border-bottom-left-radius: var(--bs-border-radius);\n}\n\n:root {\n  /* plugin overrides (NOTE: the bootstrap vars handle dark mode automatically) */\n  --iti-border-color: var(--bs-border-color);\n  --iti-dropdown-bg: var(--bs-body-bg);\n  --iti-icon-color: var(--bs-body-color);\n  --iti-hover-color: #f8f9fa;\n\n  @media (prefers-color-scheme: dark) {\n    --iti-hover-color: #30363d;\n  }\n}\n\n.iti__dropdown-content,\n.iti__search-input {\n  border-radius: var(--bs-border-radius);\n}"
  },
  {
    "path": "site/src/css/_layout.scss",
    "content": "/* LAYOUT */\n.iti-gutter {\n  --bs-gutter-x: 3rem;\n}\n/* never show the right sidebar on mobile (or on some desktop pages) */\n/* NOTE: can't include left-sidebar here, because it is always used on mobile */\n.iti-right-sidebar {\n  display: none;\n}\n\n@media (min-width: 992px) {\n  .iti-layout-both-sidebars {\n    display: grid;\n    grid-template-areas: \"nav main ads\";\n    /* this minmax ensures main block doesn't encroach on sidebars */\n    grid-template-columns: minmax(0, 1fr) minmax(0, 4fr) minmax(0, 1fr);\n    gap: 1.5rem;\n  }\n}\n\n.iti-layout-both-sidebars {\n  .iti-left-sidebar {\n    grid-area: nav;\n    min-width: 0;\n  }\n  .iti-right-sidebar {\n    grid-area: ads;\n    min-width: 0;\n  }\n}\n\n@media (min-width: 992px) {\n  .iti-layout-both-sidebars {\n    .iti-left-sidebar,\n    .iti-right-sidebar {\n      position: sticky;\n      top: 5rem;\n      display: block !important;\n      height: calc(100vh - 6rem);\n      padding-left: 0.25rem;\n      margin-left: -0.25rem;\n      overflow-y: auto;\n    }\n  }\n}\n.iti-main {\n  grid-area: main;\n  min-width: 0;\n  padding-bottom: 30px;\n}\n\n/* Sticky-footer layout: ensure short pages still fill the viewport so any\n * elements injected at end of <body> (e.g. AdSense) get pushed down. */\n.iti-page {\n  min-height: 100vh;\n  display: flex;\n  flex-direction: column;\n}\n.iti-page__main {\n  flex: 1 0 auto;\n}\n"
  },
  {
    "path": "site/src/css/_navbar.scss",
    "content": "/* TOP NAVBAR */\n.iti-navbar {\n  padding: 0.75rem 0;\n  background-image: linear-gradient(rgb(var(--header-bg-color-rgb) / 1), rgb(var(--header-bg-color-rgb) / 0.95));\n  box-shadow: 0 .5rem 1rem #00000026;\n\n  .navbar-toggler {\n    padding: 0;\n    border: 0;\n  }\n\n  /* Header dropdown toggle: replace Bootstrap caret triangle with CSS chevron */\n  .dropdown-toggle::after {\n    display: inline-block;\n    width: 7px;\n    height: 7px;\n    margin: 0px 0 3px 4px;\n    content: \"\";\n    border: solid currentColor;\n    border-width: 0 2px 2px 0;\n    transform: rotate(45deg);\n  }\n\n  // cute spinny logo animation on hover\n  @media (hover: hover) and (pointer: fine) {\n    .iti-logo {\n      transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);\n    }\n    .navbar-brand:hover .iti-logo {\n      transform: rotate(360deg) scale(1.1);\n    }\n  }\n}\n\n@media (min-width: 992px) {\n  .iti-navbar-container {\n    position: relative;\n  }\n\n  // Center search on the page (not within remaining flex space)\n  .iti-navbar-search {\n    position: absolute;\n    left: 50%;\n    top: 50%;\n    transform: translate(-50%, -50%);\n  }\n}\n\n.DocSearch-Container {\n  /* above Bootstrap navbar (z-1020) but below mobile sidebar (z-1055) */\n  z-index: 1051;\n}\n.DocSearch-Button {\n  width: 260px;\n  --docsearch-search-button-background: rgba(0, 0, 0, .1);\n  --docsearch-search-button-text-color: rgba(255, 255, 255, .65);\n  border: 1px solid rgba(255, 255, 255, .4);\n\n  &:hover {\n    --docsearch-search-button-text-color: rgba(255, 255, 255, 1);\n    border-color: rgba(255, 255, 255, 1);\n  }\n}\n.DocSearch-Button-Keys {\n    min-width: 0;\n    padding: .125rem .25rem;\n    background: rgba(0, 0, 0, .25);\n    border-radius: .25rem\n}\n.DocSearch-Button-Key {\n    color: rgba(255, 255, 255, .65);\n    width: auto;\n    height: 1.25rem;\n    padding-right: .125rem;\n    padding-left: .125rem;\n    background: none;\n    border: none;\n    margin: 0 !important;\n}\n// disable DocSearch's \"pressed\" styles\n.DocSearch-Button-Key--pressed {\n  box-shadow: none !important;\n  transform: none !important;\n}\n\n// mobile search icon button in header\n.iti-navbar-icon-btn {\n  padding: 0.25rem;\n  border: 0;\n  background: transparent;\n  line-height: 1;\n\n  svg {\n    width: 20px;\n  }\n}\n\n.iti-navbar-search {\n  min-width: 0;\n}\n\n// both in header and mobile sidebar\n.iti-logo {\n  margin-right: 10px;\n}\n\n#bdNavbar {\n  .nav-link {\n    color: white;\n    // fix: unselected playground link not looking centered (to make space for bold active state)\n    text-align: center;\n\n    /* Prevent nav items shifting when toggling font-weight by reserving space\n       for the bold version of the label (used on the active state). */\n    &.iti-navlink-no-shift > span {\n      display: inline-block;\n    }\n    &.iti-navlink-no-shift > span::after {\n      content: attr(data-text);\n      display: block;\n      height: 0;\n      overflow: hidden;\n      visibility: hidden;\n    }\n    &.iti-navlink-no-shift > span::after,\n    &.active {\n      font-weight: 700;\n    }\n  }\n\n  .nav-link--github {\n    height: 100%; /* required for vertical alignment */\n  }\n\n  @media (min-width: 992px) {\n    .navbar-nav {\n      .nav-link {\n        opacity: 0.85;\n      }\n      .nav-link:hover,\n      .nav-link:focus,\n      .nav-link.active,\n      .show > .nav-link {\n        opacity: 1;\n      }\n    }\n  }\n  /* Header docs dropdown: active item uses header green */\n  .dropdown-item.active,\n  .dropdown-item:active {\n    background-color: rgb(var(--header-bg-color-rgb) / 0.5);\n    color: white;\n  }\n\n  /* Header nav dropdown: open on hover (desktop) + keyboard focus */\n  @media (hover: hover) and (pointer: fine) {\n    .dropdown:hover > .dropdown-menu {\n      display: block;\n      /* Match Bootstrap's positioning styles that normally apply when JS adds data-bs-popper */\n      top: 100%;\n      left: 0;\n    }\n  }\n}\n/* override Bootstrap's margin which causes a gap between the dropdown and toggle */\n.dropdown-menu[data-bs-popper] {\n  margin-top: 0 !important;\n}\n.offcanvas-title a {\n  color: var(--bs-body-color);\n  text-decoration: none;\n}\n\n\n/* Mobile nav: remove hover styles (prevents iOS/Android sticky :hover states) */\n.iti-mobile-sidebar {\n  .nav-pills .nav-link:not(.active):hover {\n    background-color: transparent;\n  }\n}\n\n\n.iti-left-sidebar .nav-link,\n.iti-mobile-sidebar .nav-link {\n  color: var(--bs-body-color);\n}\n.nav--primary-links > .nav-item > .nav-link {\n  font-weight: 700;\n  font-size: 1.05rem;\n  padding-top: 0.65rem;\n  padding-bottom: 0.65rem;\n}\n\n.iti-mobile-sidebar {\n  .iti-mobile-submenu .nav-link {\n    font-size: 0.95rem;\n    font-weight: 500;\n    opacity: 0.95;\n  }\n  /* Mobile sidebar icon + label spacing: slightly wider than Bootstrap gap-2 */\n  .d-flex.align-items-center.gap-2 {\n    gap: calc(0.5rem + 2px) !important;\n  }\n}\n/* Mobile sidebar icon + label spacing: slightly wider than Bootstrap gap-2 */\n.iti-mobile-nav-toggle {\n  border: 0;\n  background: none;\n\n  &__chevron {\n    margin: 0px 0 3px 6px;\n    width: 8px;\n    height: 8px;\n    display: inline-block;\n    border-right: 2px solid currentColor;\n    border-bottom: 2px solid currentColor;\n    transform: rotate(45deg);\n    opacity: 0.85;\n  }\n\n  &[aria-expanded=\"true\"] &__chevron {\n    transform: rotate(225deg);\n    margin-top: 8px;\n  }\n}\n.nav-pills {\n  .nav-link.active {\n    background-color: rgb(var(--header-bg-color-rgb) / 0.5);\n  }\n  .nav-link:not(.active):hover {\n    background-color: rgb(var(--header-bg-color-rgb) / 0.5);\n  }\n}"
  },
  {
    "path": "site/src/css/_variables.scss",
    "content": ":root {\n  /* Header background color as space-separated RGB for easy alpha usage via: rgb(var(--header-bg-color-rgb) / <alpha>) */\n  --header-bg-color-rgb: 16 135 75;\n\n  /* override flag paths, to include cache busting */\n  --iti-path-flags-1x: url(\"<%= cacheBust('/intl-tel-input/img/flags.webp') %>\");\n  --iti-path-flags-2x: url(\"<%= cacheBust('/intl-tel-input/img/flags@2x.webp') %>\");\n  --iti-icon-color: var(--bs-body-color);\n}\n"
  },
  {
    "path": "site/src/css/docs.scss",
    "content": "/* Docs options page layout: keep code blocks within the content width */\n.iti-doc-options {\n  &__row {\n    border: var(--bs-border-width, 1px) solid var(--bs-border-color, #dee2e6);\n    display: flex;\n\n    p:last-child,\n    pre:last-child {\n      margin-bottom: 0;\n    }\n\n    & + & {\n      border-top: none;\n    }\n  }\n\n  &__cell {\n    vertical-align: top;\n    box-sizing: border-box;\n    padding: var(--bs-table-cell-padding-y, 0.5rem)\n      var(--bs-table-cell-padding-x, 0.5rem);\n    min-width: 0;\n  }\n  &__cell--key {\n    flex: 0 0 210px; /* this is wide enough for all of the option names */\n    border-right: var(--bs-border-width, 1px) solid\n      var(--bs-border-color, #dee2e6);\n    h6 {\n      font-weight: 700;\n    }\n  }\n  &__cell--value {\n    flex: 1 1 auto;\n    min-width: 0;\n  }\n  /* Docs options layout: stack key/value on small screens */\n  @media (max-width: 600px) {\n    &__row {\n      flex-direction: column;\n    }\n    &__cell--key {\n      flex: 0 0 auto;\n      border-right: none;\n    }\n  }\n}\n\n"
  },
  {
    "path": "site/src/css/highlightjs_overrides.scss",
    "content": "/* Highlight.js overrides */\n.hljs {\n  @media (prefers-color-scheme: light), (prefers-color-scheme: no-preference) {\n    background-color: #f6f8fa; /* match GitHub */\n  }\n  @media (prefers-color-scheme: dark) {\n    background-color: #151b23; /* match GitHub */\n  }\n}\n"
  },
  {
    "path": "site/src/css/homepage.scss",
    "content": "@use 'variables';\n\n/**************\n * HOMEPAGE\n **************/\n body.iti-page--homepage {\n  background-image: linear-gradient(\n      182deg,\n      rgba(var(--bs-body-bg-rgb), 0.012),\n      rgba(var(--bs-body-bg-rgb), 1) 87%\n    ),\n    radial-gradient(\n      ellipse at 12% 18%,\n      rgba(65, 133, 234, 0.32),\n      transparent 55%\n    ),\n    radial-gradient(\n      ellipse at 88% 14%,\n      rgba(111, 167, 104, 0.45),\n      transparent 55%\n    ),\n    radial-gradient(\n      ellipse at 90% 56%,\n      rgba(128, 63, 240, 0.42),\n      transparent 52%\n    ),\n    radial-gradient(\n      ellipse at 10% 60%,\n      rgba(224, 76, 135, 0.42),\n      transparent 52%\n    );\n}\n.homepage-content {\n  margin-top: 60px;\n  margin-left: auto;\n  margin-right: auto;\n\n  .section--homepage-demo,\n  .section--twilio {\n    max-width: 580px;\n  }\n\n  h1 {\n    font-size: 41px;\n    font-weight: 600;\n  }\n\n  /* allow this part of title to wrap on small screens, but keep together on larger screens */\n  @media (min-width: 420px) {\n    h1 .keep-together {\n      white-space: nowrap;\n    }\n  }\n\n  .text-muted a {\n    color: inherit;\n    /* keep the same muted hue, but remove Bootstrap's alpha transparency on interaction */\n    &:hover {\n      color: rgb(var(--bs-secondary-color-rgb));\n    }\n  }\n\n  .section {\n    margin-bottom: 60px;\n  }\n\n  /* remove margin on last section */\n  .section:last-child {\n    margin-bottom: 0;\n  }\n\n  @media (min-width: 992px) {\n    margin-top: 100px;\n\n    .section--homepage-demo,\n    .section--twilio {\n      max-width: 700px;\n    }\n\n    h1 {\n      font-size: 58px;\n    }\n\n    p.subtitle {\n      font-size: 23px;\n    }\n  }\n\n  .section--homepage-demo {\n    margin-top: 10px;\n\n    /* homepage: make the demo input width match the live results box */\n    .iti,\n    input[type=\"tel\"] {\n      width: 100%;\n      text-align: left; /* required because we have text-align:center on this section */\n    }\n\n    /* to match the other sections, which have a <p> at the bottom with this margin */\n    .btn-primary {\n      margin-bottom: 16px;\n    }\n  }\n\n  .section--twilio {\n    h2 {\n      margin-bottom: 0;\n    }\n  }\n\n  .section--social-proof {\n    .social-proof-logos {\n      margin-top: 24px;\n      max-width: 900px;\n      display: flex;\n      flex-wrap: wrap;\n      justify-content: center;\n      align-items: center;\n      gap: 24px 32px;\n\n      .logo {\n        height: 40px;\n        width: auto;\n        opacity: 0.55;\n        filter: grayscale(100%);\n        transition: opacity 0.2s;\n\n        &:hover {\n          opacity: 0.8;\n        }\n      }\n\n      // hide last 3 logos on mobile\n      .logo:nth-last-child(-n+3) {\n        display: none;\n\n        @media (min-width: 768px) {\n          display: inline-flex;\n        }\n      }\n\n      .logo-pair {\n        display: inline-flex;\n        align-items: center;\n\n        img {\n          height: 100%;\n          width: auto;\n        }\n      }\n\n      /* The following AI generated code attempts to approximate var(--bs-body-color) (#212529 light / #dee2e6 dark) via filters */\n\n      // nike, unicef, forbes, ubuntu_text, and quickbooks_text logos are black on transparent\n      img[src*=\"nike.png\"],\n      img[src*=\"unicef.png\"],\n      img[src*=\"forbes\"],\n      img[src*=\"ubuntu_text\"],\n      img[src*=\"quickbooks_text\"] {\n        filter: invert(0.13);\n\n        @media (prefers-color-scheme: dark) {\n          filter: invert(0.87);\n        }\n      }\n\n      // dell logo is blue on transparent, so use brightness(0) to make it black before inverting\n      img[src*=\"dell\"] {\n        filter: brightness(0) invert(0.13);\n\n        @media (prefers-color-scheme: dark) {\n          filter: brightness(0) invert(0.87);\n        }\n      }\n\n      // universal music logo is white on transparent\n      img[src*=\"universal-music\"] {\n        filter: invert(0.87);\n\n        @media (prefers-color-scheme: dark) {\n          filter: invert(0.13);\n        }\n      }\n    }\n  }\n\n  @media (min-width: 992px) {\n    .section--social-proof .social-proof-logos .logo {\n      height: 48px;\n    }\n  }\n\n  .section--stats {\n    .stats-row {\n      display: flex;\n      flex-wrap: wrap;\n      justify-content: center;\n      gap: 24px 48px;\n    }\n\n    .stat {\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n    }\n\n    .stat-number {\n      font-size: 36px;\n      font-weight: 700;\n      color: rgb(var(--bs-link-color-rgb));\n    }\n\n    .stat-label {\n      font-size: 14px;\n      color: var(--bs-secondary-color);\n      margin-top: 4px;\n    }\n  }\n\n  @media (min-width: 992px) {\n    .section--stats {\n      .stat-number {\n        font-size: 48px;\n      }\n\n      .stat-label {\n        font-size: 16px;\n      }\n    }\n  }\n\n  /* icons that should match link colour */\n  .section--features {\n    .link-icon {\n      color: rgb(var(--bs-link-color-rgb)) !important;\n    }\n  }\n  /* there is a br in the middle of the playground preset links: ignore it on mobile, show it on larger screens to break the links into two rows */\n  @media (min-width: 550px) {\n    .playground-presets {\n      max-width: 550px;\n    }\n  }\n  /* Make the playground preset outline buttons use the link colour */\n  .playground-presets .btn-outline-primary {\n    color: rgb(var(--bs-link-color-rgb));\n    border-color: rgb(var(--bs-link-color-rgb));\n  }\n  .playground-presets .btn-outline-primary:hover,\n  .playground-presets .btn-outline-primary:focus {\n    background-color: rgba(var(--bs-link-color-rgb), 0.1);\n    border-color: rgb(var(--bs-link-color-rgb));\n  }\n}"
  },
  {
    "path": "site/src/css/large_flags_overrides.scss",
    "content": ":root {\n  --iti-spacer-horizontal: 12px;\n  --iti-globe-height: 22px;\n  --iti-search-clear-icon-height: 16px;\n  --iti-arrow-height: 5px;\n  --iti-arrow-padding: 9px;\n  --iti-path-flags-1x: url(\"<%= cacheBust('/img/largeFlags.webp') %>\");\n  --iti-path-flags-2x: url(\"<%= cacheBust('/img/largeFlags@2x.webp') %>\");\n}\n.iti {\n  font-size: 20px;\n}\n.iti,\n.section.demo input {\n  width: 350px !important;\n}\n.section.demo input,\n.iti__search-input {\n  font-size: inherit !important; // bootstrap override\n}\n.iti__search-icon {\n  left: 13px;\n}\n.iti__search-clear {\n  right: 8px;\n}\ninput.iti__search-input {\n  padding-left: 44px;\n  padding-right: 40px;\n}\n.iti--inline-dropdown .iti__country-list {\n  max-height: 210px;\n}"
  },
  {
    "path": "site/src/css/playground.scss",
    "content": "@use 'variables';\n\n/**************\n * PLAYGROUND\n **************/\n\n// When navigating to a Playground heading via hash, leave space above it.\n.iti-playground h2[id] {\n  scroll-margin-top: 30px;\n}\n\n// Make enable span behave like a label (default cursor)\n.form-check-enable-span {\n  cursor: default;\n}\n// force these buttons to have the same colour as text-muted, which has a high enough contrast ratio to pass a11y guidelines.\n.playground-action-btn {\n  color: var(--bs-secondary-color) !important;\n}\n.playground-action-btn:hover {\n  background-color: #e3e1e1;\n  @media (prefers-color-scheme: dark) {\n    background-color: #363535;\n  }\n}\n/* multidropdown toggle: disable hover styling when closed */\n.iti-playground-multidropdown-toggle {\n  cursor: default;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 0.5rem;\n  --bs-btn-bg: var(--bs-body-bg);\n  --bs-btn-color: var(--bs-body-color);\n  --bs-btn-border-color: var(--bs-border-color, #ced4da);\n  --bs-btn-hover-bg: var(--bs-btn-bg);\n  --bs-btn-hover-color: var(--bs-btn-color);\n  --bs-btn-hover-border-color: var(--bs-btn-border-color);\n  --bs-btn-active-bg: var(--bs-btn-bg);\n  --bs-btn-active-color: var(--bs-btn-color);\n  --bs-btn-active-border-color: var(--bs-btn-border-color);\n\n  /* multidropdown toggle: use CSS chevron for down arrow */\n  &.dropdown-toggle::after {\n    opacity: 0.85;\n    margin: 0 3px 2px auto;\n    display: inline-block;\n    width: 8px;\n    height: 8px;\n    content: \"\";\n    border: solid currentColor;\n    border-width: 0 2px 2px 0;\n    transform: rotate(45deg);\n  }\n  /* multidropdown toggle: flip down arrow when open */\n  &.dropdown-toggle.show::after,\n  .dropdown.show > &.dropdown-toggle::after {\n    margin-top: 6px;\n    transform: rotate(225deg);\n  }\n}\n\n// mobile: make the presets select smaller so it doesn't distract from the page title\n@media (max-width: 576px) {\n  .playground-presets-select {\n    width: 7em;\n  }\n}\n\n.iti-playground-layout {\n  display: flex;\n  flex-direction: column;\n  gap: 1.5rem; /* bootstrap g-4 */\n\n  /* On mobile, flatten the column wrappers so we can control ordering. */\n  &__col {\n    display: contents;\n  }\n  &__demo {\n    order: 1;\n  }\n  &__options {\n    order: 2;\n  }\n  &__attrs {\n    order: 3;\n  }\n  &__code {\n    order: 4;\n  }\n\n  &__demo,\n  &__options,\n  &__attrs,\n  &__code {\n    min-width: 0;\n  }\n\n  @media (min-width: 992px) {\n    display: grid;\n    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n    column-gap: 1.5rem; /* bootstrap g-4 */\n    align-items: start;\n\n    &__col {\n      display: flex;\n      flex-direction: column;\n      gap: 1.5rem; /* bootstrap g-4 */\n      min-width: 0;\n    }\n\n    &__col--left {\n      grid-column: 1;\n    }\n\n    &__col--right {\n      grid-column: 2;\n      position: sticky;\n      top: calc(70px + 1rem);\n      max-height: calc(100vh - 70px - 2rem);\n      overflow: auto;\n    }\n  }\n\n  &__demo {\n    .iti,\n    input[type=\"tel\"],\n    .iti-live-results {\n      width: 100%;\n      max-width: 350px;\n    }\n  }\n}\n\n.iti-playground--keep-dropdown-open {\n  .iti__dropdown-content.iti__hide {\n    display: block !important;\n  }\n  .playground-dropdown-checkbox {\n    margin-top: 240px !important;\n  }\n}\n\n/* playground: option info tooltip */\n.iti-playground-info {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  width: 16px;\n  height: 16px;\n  margin-left: 0;\n  color: var(--bs-secondary-color, #6c757d);\n  user-select: none;\n  cursor: help;\n\n  > svg {\n    display: block;\n    width: 16px;\n    height: 16px;\n  }\n\n  &:focus-visible {\n    outline: 2px solid rgb(var(--header-bg-color-rgb));\n    outline-offset: 2px;\n  }\n}\n\n.iti-playground-labelgroup {\n  padding-top: 3px;\n  display: inline-flex;\n  align-items: center;\n  gap: 0.4rem;\n\n  > .form-label {\n    margin-bottom: 0;\n  }\n}\n\n.iti-playground-example-toggle {\n  margin-top: 6px;\n}\n\n/* preserve bootstrap-like spacing for stacked (mobile) label + control */\n.iti-playground-control {\n  &:not(.iti-playground-control--check) > .iti-playground-labelgroup {\n    margin-bottom: var(--bs-form-label-margin-bottom, 0.5rem);\n  }\n\n  /* playground: on wider screens, align label + control horizontally to save space */\n  @media (min-width: 600px) {\n    /* playground: loadUtils has an example snippet + a checkbox, so keep the example aligned with the label, and put the checkbox beneath. */\n    &--example-code {\n      align-items: start;\n    }\n    &--example-code > .iti-playground-example-toggle {\n      grid-column: 2;\n      margin-top: 0;\n    }\n    /* Shared grid layout for controls on wider screens */\n    & {\n      display: grid;\n      grid-template-columns: minmax(160px, 220px) 1fr;\n      grid-template-areas: \"label control\";\n      column-gap: 12px;\n\n      > .iti-playground-labelgroup {\n        grid-area: label;\n        margin-bottom: 0;\n      }\n    }\n\n    /* Non-checkbox controls: slightly more vertical spacing and form-specific tweaks */\n    &:not(.iti-playground-control--check) {\n      row-gap: 6px;\n      align-items: start;\n\n      > .form-control,\n      > .form-select,\n      > .dropdown {\n        min-width: 0;\n      }\n      > .form-text {\n        grid-column: 2;\n      }\n    }\n\n    /* On mobile, checkboxes appear on LHS. Here, we move them to the RHS. */\n    &.iti-playground-control--check {\n      row-gap: 0;\n      align-items: center;\n      padding-left: 0; /* override bootstrap .form-check */\n\n      > .form-check-input {\n        grid-area: control;\n        margin-left: 0;\n        float: none;\n        justify-self: start;\n      }\n    }\n  }\n}\n/* contextual hint (shown after toggling certain options) */\n.iti-playground-hint {\n  display: block;\n  margin-left: -1.5em;\n  animation: iti-hint-fade-in 0.2s ease-out;\n\n  @media (min-width: 600px) {\n    grid-column: 2;\n    margin-left: 0;\n  }\n}\n@keyframes iti-hint-fade-in {\n  from { opacity: 0; }\n  to { opacity: 1; }\n}\n\n.iti-playground pre {\n  display: block;\n}\n"
  },
  {
    "path": "site/src/css/website.scss",
    "content": "\n/* Split into partials for maintainability. Partial files live alongside this file.\n   Keep this file as the single entrypoint imported by the build system. */\n\n@use 'variables';\n@use 'base';\n@use 'navbar';\n@use 'layout';\n@use 'forms';\n\n\n/* Any small remaining overrides or page-specific rules can go here. */\n\n"
  },
  {
    "path": "site/src/docs/docs_content_template.html.ejs",
    "content": "<nav class=\"iti-breadcrumb\" aria-label=\"Breadcrumb\">\n  <ol class=\"iti-breadcrumb__list\">\n    <li class=\"iti-breadcrumb__item\" aria-current=\"page\">Docs »</li>\n  </ol>\n</nav>\n\n<div class=\"iti-docs\">\n  <%= html %>\n</div>\n\n<div class=\"d-flex justify-content-start mt-4\">\n  <a\n    class=\"d-inline-flex align-items-center gap-2\"\n    href=\"https://github.com/jackocnr/intl-tel-input/edit/master/site/src/docs/markdown/<%= docKey %>.md\"\n    rel=\"noopener\"\n  >\n    <i class=\"bi bi-pencil-square\" aria-hidden=\"true\"></i>\n    <span>Edit this page</span>\n  </a>\n</div>\n"
  },
  {
    "path": "site/src/docs/docs_nav_template.html.ejs",
    "content": "<h6 class=\"mt-4 mb-2 text-uppercase iti-sidebar-heading\">Docs</h6>\n<ul class=\"flex-column nav nav-pills gap-1 my-3\">\n  <% docsDropdownPages.forEach((p) => { %>\n    <li>\n      <a class=\"nav-link py-1 <% if (name === p.name) { %>active<% } %>\" href=\"<%= p.href %>\"><%= p.label %></a>\n    </li>\n  <% }); %>\n</ul>\n"
  },
  {
    "path": "site/src/docs/docs_page_template.html.ejs",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <%= common_meta_tags %>\n    <title><%= head_title %></title>\n    <link rel=\"canonical\" href=\"<%= canonical_url %>\" />\n    <meta name=\"description\" content=\"<%= meta_desc %>\">\n\n    <%= og_meta_tags %>\n    <%= common_styles %>\n    <link rel=\"stylesheet\" href=\"<%= cacheBust('/css/docs.css') %>\" />\n\n    <%= common_head_end_prod %>\n  </head>\n\n  <body class=\"iti-page iti-page--docs\">\n    <%= layout %>\n    <%= common_body_end %>\n  </body>\n</html>\n"
  },
  {
    "path": "site/src/docs/markdown/accessibility.md",
    "content": "# Accessibility\n\nintl-tel-input aims to be accessible out of the box, but good accessibility also depends on how you integrate it into your form.\n\nThis page covers:\n\n- How the country dropdown and search are exposed to assistive tech\n- Keyboard interaction\n- What you should do to ensure the phone input has an accessible name and helpful errors\n- How to translate the plugin’s accessibility strings\n\n\n## Contents\n\n- [Accessible naming](#accessible-naming)\n- [Keyboard support](#keyboard-support)\n- [Screen reader support](#screen-reader-support)\n- [Translating accessibility strings](#translating-accessibility-strings)\n- [Form validation and errors](#form-validation-and-errors)\n\n\n## Accessible naming\n\nMake sure the telephone input has an accessible name.\n\nRecommended:\n\n- Use a visible `<label>` connected via `for`/`id` (best for most forms)\n- Or use `aria-label` / `aria-labelledby` if you can’t use a visible label\n\nExample:\n\n```html\n<label for=\"phone\">Phone number</label>\n<input id=\"phone\" type=\"tel\" autocomplete=\"tel\" />\n```\n\nNote: the plugin injects its own country UI next to the input, but it does not replace the need for your input to have an accessible name.\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>\n\n## Keyboard support\n\nWhen the dropdown is enabled, the country selector can be operated with the keyboard:\n\n- From the country button, press Up/Down, Space, or Enter to open the dropdown\n- Press Esc to close the dropdown (focus returns to the country button)\n- Use Up/Down to move through countries\n- Press Enter to select the highlighted country\n- If country search is disabled, typing letters will jump to matching countries (like a native `<select>`)\n\nIf the country search input is enabled, users can type to filter the list.\n\n\n## Screen reader support\n\nThe plugin’s dropdown/search UI includes ARIA attributes to expose state and relationships (e.g. expanded state, controls relationships, listbox semantics), and it provides screen reader text for things like:\n\n- The selected country button label (including the “no country selected” state)\n- Country list label\n- Search placeholder/label\n- Clear-search button label\n- A live summary of the number of matching results while searching\n\n\n## Translating accessibility strings\n\nIf you localise the UI using the [`i18n`](/docs/options#i18n) option, that also covers the accessibility strings listed above (placeholders and ARIA labels).\n\nSee the [Localisation docs](/docs/localisation) for the full language list and how to provide your own.\n\n\n## Form validation and errors\n\nintl-tel-input can help you validate numbers (see the [Initialisation options](/docs/options) and the [Validation examples](/examples/validation-practical)), but you are responsible for presenting validation errors accessibly.\n\nGeneral guidance:\n\n- Use a clear, specific error message text\n- Ensure the error is programmatically associated with the input (e.g. `aria-describedby`)\n- Don’t rely on color alone to convey an error state\n"
  },
  {
    "path": "site/src/docs/markdown/angular_component.md",
    "content": "# Angular component\n\nAn Angular component for the intl-tel-input JavaScript plugin. View the [source code](https://github.com/jackocnr/intl-tel-input/blob/master/angular/src/intl-tel-input/angular.ts).\n\n## Contents\n\n- [Demo](#demo)\n- [Getting started](#getting-started)\n- [Props](#props)\n- [Events](#events)\n- [Accessing instance methods](#accessing-instance-methods)\n- [Accessing static methods](#accessing-static-methods)\n\n## Demo\n\nYou can see a live demo and example code on the [Angular component](/examples/angular-component) example page.\n\nOr try it yourself by running the demos locally:\n\n```bash\n# Clone the repository\ngit clone https://github.com/jackocnr/intl-tel-input.git\ncd intl-tel-input\n\n# Initialise submodules (required for build)\ngit submodule update --init --recursive\n\n# Install dependencies and build\nnpm install\nnpm run build\n\n# Serve from project root\npython3 -m http.server\n```\n\n> Then open [http://localhost:8000/angular/demo/simple/](http://localhost:8000/angular/demo/simple/) or other demos from the [angular demo folder](https://github.com/jackocnr/intl-tel-input/blob/master/angular/demo/)\n\n> [!NOTE]\n> Make sure to serve from the project root, not from the demo folders.\n\n## Getting started\n\nFirst, install the package: \n\n```sh\nnpm install intl-tel-input\n```\n\nThen, add something like this to your code:\n\n```js\nimport { IntlTelInputComponent } from \"intl-tel-input/angularWithUtils\";\nimport \"intl-tel-input/styles\";\n\n<intl-tel-input\n  #telInput\n  (numberChange)=\"handleNumberChange($event)\"\n  (validityChange)=\"handleValidityChange($event)\"\n  [initOptions]=\"{\n    initialCountry: 'us',\n  }\"\n/>\n```\n\n> [!NOTE]\n> You can also import the CSS in your Angular workspace configuration (`angular.json`). See Angular's [Styles and scripts configuration](https://angular.dev/reference/configs/workspace-config#styles-and-scripts-configuration) documentation.\n\nSee the [validation demo](https://github.com/jackocnr/intl-tel-input/blob/master/angular/demo/validation/validation.component.ts) for a more fleshed-out example of how to handle validation, or check out the [form demo](https://github.com/jackocnr/intl-tel-input/blob/master/angular/demo/form/form.component.ts) for an alternative approach using `ReactiveFormsModule`.\n\nA note on the utils script (~260KB): if you're lazy loading the IntlTelInput chunk (and so less worried about filesize), then you can just `import { IntlTelInputComponent } from \"intl-tel-input/angularWithUtils\"`, to include the utils script. Alternatively, if you use the main `\"intl-tel-input/angular\"` import, then you should couple this with the `loadUtils` initialisation option - you will need to host the utils.js file, and then set the `loadUtils` option to that URL, or alternatively just point it to a CDN-hosted version, e.g. `\"https://cdn.jsdelivr.net/npm/intl-tel-input@26.5.0/build/js/utils.js\"`.\n\n## Props\n\nHere's a list of all of the current props you can pass to the IntlTelInput Angular component.\n\n###### disabled\nType: `Boolean`  \nDefault: `false`  \n\nSets the disabled attribute of both the telephone input and the selected country button. _Note: We recommend using this instead of `inputProps.disabled`._\n\n###### initialValue\nType: `String`  \nDefault: `\"\"`  \n\nThe initial value to put in the input. This will get auto-formatted on init (according to `formatOnDisplay` initialisation option). IntlTelInput is an uncontrolled input, and so will ignore any changes to this value.\n\n###### initOptions\nType: `Object`  \nDefault: `{}`  \n\nAn object containing the [initialisation options](https://github.com/jackocnr/intl-tel-input?tab=readme-ov-file#initialisation-options) to pass to the plugin. You can use these exactly the same way as with the main JavaScript plugin.\n\n###### inputProps\nType: `Object`  \nDefault: `{}`  \n\nThe attributes to pass to the input element, e.g. `class`, `placeholder`, `required`, etc. _Note: We recommend using the separate `disabled` prop instead of `inputProps.disabled`._\n\n###### usePreciseValidation\nType: `Boolean`  \nDefault: `false`  \n\nBy default, we use `isValidNumber` for validation, but if you'd rather use `isValidNumberPrecise`, you can set this to `true`.\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>\n\n## Events\n\nHere's a list of all of the current events you can listen to on the IntlTelInput angular component.\n\n###### countryChange\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the selected country changes. It will be passed the new country iso2 code, e.g. \"gb\" for the UK.\n\n###### errorCodeChange\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the number validation error changes. It will be passed the new error code (or `null`).\n\n###### numberChange\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the number changes. It will be passed the new number.\n\n###### validityChange\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the number validity changes, e.g. to true/false. It will be passed the new isValid boolean.\n\n### Native input events\n\nThe component exposes several commonly used native DOM events that you can bind to using Angular's standard event binding syntax `(eventName)=\"handlerMethod($event)\"`. For other native events not listed below, you can access the input element directly (see [Other native events](#other-native-events) section).\n\n#### Supported events\n\nThe following native input events are directly supported:\n- `blur` - Fired when the input loses focus (receives `FocusEvent`)\n- `focus` - Fired when the input receives focus (receives `FocusEvent`)\n- `keydown` - Fired when a key is pressed down (receives `KeyboardEvent`)\n- `keyup` - Fired when a key is released (receives `KeyboardEvent`)\n- `paste` - Fired when content is pasted (receives `ClipboardEvent`)\n- `click` - Fired when the input is clicked (receives `MouseEvent`)\n\nExample usage:\n```html\n<intl-tel-input\n  (blur)=\"handleBlur($event)\"\n  (focus)=\"handleFocus($event)\"\n  (keydown)=\"handleKeyDown($event)\"\n  (paste)=\"handlePaste($event)\"\n/>\n```\n\n#### Other native events\n\nFor any other native DOM events not listed above, you can access the input element directly using a `ViewChild` reference and add event listeners manually:\n\n```typescript\nexport class MyComponent implements AfterViewInit, OnDestroy {\n  @ViewChild('telInput') telInput!: IntlTelInputComponent;\n\n  ngAfterViewInit() {\n    const input = this.telInput.getInput();\n    input?.addEventListener('mouseenter', this.handleMouseEnter);\n  }\n\n  ngOnDestroy() {\n    const input = this.telInput.getInput();\n    input?.removeEventListener('mouseenter', this.handleMouseEnter);\n  }\n\n  handleMouseEnter = (event: MouseEvent) => {\n    console.log('Mouse entered input:', event);\n  }\n}\n```\n\n## Accessing instance methods\n\nYou can access all of the plugin's [instance methods](/docs/methods#instance-methods) (`setNumber`, `setCountry`, `setPlaceholderNumberType`, etc.) by using a ViewChild reference into the IntlTelInput component (using the `#ref` prop), and then calling `this.ref.getInstance()`, e.g. `this.ref.getInstance().setNumber(...);`. See the [Set Number demo](https://github.com/jackocnr/intl-tel-input/blob/master/angular/demo/set-number/set-number.component.ts) for a full example. You can also access the input DOM element in a similar way: `this.ref.getInput()`.\n\n> [!IMPORTANT]\n> You must use `ngAfterViewInit` (not `ngOnInit` or `constructor`) to access instance or input methods, as the component needs to be fully initialised first.\n\n## Accessing static methods\n\nYou can access all of the plugin's [static methods](/docs/methods#static-methods) by importing `intlTelInput` from the same file as the angular component, e.g. `import { intlTelInput } from \"intl-tel-input/angular\"` (note the lower case \"i\" in \"intlTelInput\"). You can then use this as you would with the main plugin, e.g. `intlTelInput.getCountryData()` or `intlTelInput.utils.numberType` etc.\n"
  },
  {
    "path": "site/src/docs/markdown/choose_integration.md",
    "content": "# Choosing your integration\n\n`intl-tel-input` is available in two primary flavors: a standalone **JavaScript plugin** and a set of native **framework components**. While both offer the same core features (country-picker, formatting and validation), the best choice depends on your project’s architecture.\n\n## 1. JavaScript plugin\nThe original version of `intl-tel-input`. Use this if you are not using a modern frontend framework, or if you prefer to manage the DOM manually. It is lightweight, has zero dependencies, and works in any environment.\n\n[Getting Started with the JS Plugin](/docs/getting-started)\n\n## 2. Framework components\nWe provide native wrappers for the four most popular frontend frameworks. These components handle the lifecycle of the plugin for you (initialisation and cleanup) and allow you to sync the input value directly with your app’s state.\n\n* [React component docs](/docs/react-component) – For React 16.8+ (Hooks-based)\n* [Vue component docs](/docs/vue-component) – Supports Vue 3\n* [Angular component docs](/docs/angular-component) – For modern Angular versions\n* [Svelte Component Docs](/docs/svelte-component) – Lightweight and reactive\n\n---\n\n## FAQ\n\n**Can I use the JS Plugin inside a React/Vue app?**  \nYes, but it is not recommended. You would need to manually handle `useEffect` or `onMounted` hooks to prevent memory leaks and ensure the plugin re-initialises correctly when the component updates. Our native components do this for you out of the box.\n\n**Do the components include all the plugin features?**  \nYes. All [initialisation options](/docs/initialisation-options) and [methods](/docs/methods) are available through the component props and refs.\n\n**Which version is the most \"up to date\"?**  \nAll versions are maintained simultaneously. When the core JavaScript logic is updated, those changes are immediately available to the React, Vue, Angular, and Svelte components."
  },
  {
    "path": "site/src/docs/markdown/events.md",
    "content": "# Events\n\nYou can listen for the following events triggered on the input element.\n\n## countrychange\n\nThis is triggered when the selected country is updated, e.g. if the user selects a country from the dropdown, or they type a different dial code into the input, or you call [`setCountry`](/docs/methods#setcountry) etc.\n```js\ninput.addEventListener(\"countrychange\", () => {\n  // do something, e.g. get the new country data with getSelectedCountryData\n});\n```\n\n## open:countrydropdown\n\nThis is triggered when the user opens the dropdown.  \n\n## close:countrydropdown\n\nThis is triggered when the user closes the dropdown.  \n"
  },
  {
    "path": "site/src/docs/markdown/faq.md",
    "content": "# FAQ\n\n## Contents\n\n- [Do I need the utils script?](#do-i-need-the-utils-script)\n- [What format should I store phone numbers in?](#what-format-should-i-store-phone-numbers-in)\n- [How do I submit the full international number in a normal HTML form?](#how-do-i-submit-the-full-international-number-in-a-normal-html-form)\n- [How do I set the initial country?](#how-do-i-set-the-initial-country)\n- [Can I translate the UI and country names?](#can-i-translate-the-ui-and-country-names)\n- [Can I disable formatting as you type?](#can-i-disable-formatting-as-you-type)\n- [How do I prevent users typing invalid characters?](#how-do-i-prevent-users-typing-invalid-characters)\n- [How do I validate a number?](#how-do-i-validate-a-number)\n- [How do I show the dial code next to the selected flag?](#how-do-i-show-the-dial-code-next-to-the-selected-flag)\n- [Something looks broken — where should I look first?](#something-looks-broken--where-should-i-look-first)\n\n\n## Do I need the utils script?\n\nIt depends on which features you want.\n\nThe utilities script enables things like:\n\n- Formatting and validation helpers\n- Automatic country-specific placeholders\n\nSee [Utilities script](/docs/utils) for how to load it and what it’s used for.\n\n\n## What format should I store phone numbers in?\n\nIn most cases you should store phone numbers in standardised international format ([E.164](https://en.wikipedia.org/wiki/E.164)), e.g. `+447740123456`. That way, you can just store a single string that contains all the information you need (the phone number and the country it belongs to).\n\nYou can always get the E.164 number from the plugin using `getNumber`, even if the user has entered their number in national format, or `separateDialCode` is enabled. For displaying numbers back to the user (e.g. on a user profile page), simply initialise the plugin on an input containing an E.164 number, and it will automatically select the right flag and format the number according to your settings (national format by default).\n\n\n## How do I submit the full international number in a normal HTML form?\n\nUse the [`hiddenInput`](/docs/options#hiddeninput) option.\n\nThat option listens for the form submit event and injects hidden input(s) containing the full international number (and optionally the country code), so they’re included in the form post.\n\nSee the [Hidden input example](/examples/hidden-input).\n\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>\n\n## How do I set the initial country?\n\nUse the [`initialCountry`](/docs/options#initialcountry) option.\n\n- If you already know the user's country, set it to the relevant ISO2 code, e.g. `\"us\"`.\n- Or set it to `\"auto\"` and provide [`geoIpLookup`](/docs/options#geoiplookup) to detect the user’s country.\n\n\n## Can I translate the UI and country names?\n\nYes.\n\n- Country names can be localised using [`countryNameLocale`](/docs/options#countrynamelocale) (uses `Intl.DisplayNames`).\n- UI strings (including accessibility strings) can be translated using [`i18n`](/docs/options#i18n).\n\nSee [Localisation](/docs/localisation) for more information.\n\n\n## How do I prevent users typing invalid characters?\n\nEnable [`strictMode`](/docs/options#strictmode).\n\nWhen `strictMode` is enabled, the input will reject characters that aren’t valid phone number characters. This is useful if you want to keep the value clean (e.g. only digits, and a leading `+` when appropriate), and avoid users pasting in extra text. This also caps the number at the maximum valid length.\n\n\n## How do I validate a number?\n\nIf you’re using the utilities script, you can validate numbers and show useful error messages.\n\n- See the [Validation example](/examples/validation-practical)\n- See the [Utilities script](/docs/utils)\n\n\n## How do I show the dial code next to the selected flag?\n\nUse the [`separateDialCode`](/docs/options#separatedialcode) option.\n\nWhen enabled, the selected country's dial code will be displayed next to the flag, and treated as if it was part of the typed number. This is useful if you want the user to enter their number in international format, and want to prevent them from being able to delete the dial code.\n\nTry it out in the [Playground](/playground?initialCountry=gb&separateDialCode=true#user-interface-options).\n\n\n## Something looks broken — where should I look first?\n\nA lot of “broken” behaviour is caused by layout/CSS or initialising the plugin before the input is in the DOM. Check devtools: is the plugin CSS loading? Have you overridden the flag paths correctly, and are the flag images loading?\n\nAlso see:\n\n- [Getting started](/docs/getting-started)\n- [Troubleshooting](/docs/troubleshooting)\n- [Initialisation options](/docs/options)\n"
  },
  {
    "path": "site/src/docs/markdown/getting_started.md",
    "content": "# Getting started with the JavaScript plugin\n\nThis page is for getting started with the JavaScript plugin. For the framework components (React, Vue, Angular and Svelte), see [Choosing your integration](/docs/choose-integration).\n\n## Contents\n- [Using a CDN](#using-a-cdn)\n- [Using a bundler, e.g. Vite](#using-a-bundler-e-g-vite)\n- [Not using a bundler](#not-using-a-bundler)\n- [Recommended usage](#recommended-usage)\n\n### Using a CDN\n\n1. Add the CSS\n  ```html\n  <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/intl-tel-input@26.8.1/build/css/intlTelInput.css\">\n  ```\n\n2. Add the plugin script and initialise it on your input element\n  ```html\n  <script src=\"https://cdn.jsdelivr.net/npm/intl-tel-input@26.8.1/build/js/intlTelInput.min.js\"></script>\n  <script>\n    const input = document.querySelector(\"#phone\");\n    window.intlTelInput(input, {\n      loadUtils: () => import(\"https://cdn.jsdelivr.net/npm/intl-tel-input@26.8.1/build/js/utils.js\"),\n    });\n  </script>\n  ```\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>\n\n### Using a bundler, e.g. Vite\n\n1. Install with npm\n\n```bash\nnpm install intl-tel-input\n```\n\n2. Import the CSS\n\n```js\nimport \"intl-tel-input/styles\";\n```\n\n3. Set the path to flags.webp in your CSS, by overriding the CSS variables\n\n```css\n.iti {\n  --iti-path-flags-1x: url(\"path/to/flags.webp\");\n  --iti-path-flags-2x: url(\"path/to/flags@2x.webp\");\n}\n```\n\n4. Import the JS and initialise the plugin on your input element\n\n```js\nimport intlTelInput from \"intl-tel-input\";\n\nconst input = document.querySelector(\"#phone\");\nintlTelInput(input, {\n  loadUtils: () => import(\"intl-tel-input/utils\"),\n});\n```\n\nMost bundlers (such as Vite, Turbopack or Parcel) will see this and place the [utilities script](/docs/utils) in a separate bundle and load it asynchronously, only when needed. If this doesn’t work with your bundler or you want to load the utils module from some other location (such as a CDN or your own hosted version), you can do that as well - see other examples.\n\n### Not using a bundler\n\n1. Download the [latest release](https://github.com/jackocnr/intl-tel-input/releases/latest), or better yet install it with [npm](https://www.npmjs.com/package/intl-tel-input)\n\n2. Add the stylesheet\n  ```html\n  <link rel=\"stylesheet\" href=\"path/to/intlTelInput.css\">\n  ```\n\n3. Set the path to flags.webp in your CSS, by overriding the CSS variables\n  ```css\n  .iti {\n    --iti-path-flags-1x: url('path/to/flags.webp');\n    --iti-path-flags-2x: url('path/to/flags@2x.webp');\n  }\n  ```\n\n4. Add the plugin script and initialise it on your input element\n  ```html\n  <script src=\"path/to/intlTelInput.js\"></script>\n  <script>\n    const input = document.querySelector(\"#phone\");\n    window.intlTelInput(input, {\n      loadUtils: () => import(\"https://your-domain.com/path/to/utils.js\"),\n    });\n  </script>\n  ```\n\n## Recommended usage\n\nWe highly recommend you [load the included utils.js](/docs/utils#loading-the-utilities-script), which enables formatting and validation, etc. Then the plugin is built to always deal with numbers in the full international format (e.g. \"+17024181234\") and convert them accordingly - even when [`nationalMode`](/docs/options#nationalmode) or [`separateDialCode`](/docs/options#separatedialcode) is enabled. We recommend you get, store, and set numbers exclusively in this format for simplicity - then you don't have to deal with handling the country code separately, as full international numbers include the country code information*.\n\nYou can always get the full international number (including country code) using [`getNumber`](/docs/methods#getnumber), then you only have to store that one string in your database (you don't have to store the country separately), and then the next time you initialise the plugin with that number in the input, it will automatically set the country* and format it according to the options you specify (e.g. when using [`nationalMode`](/docs/options#nationalmode) it will automatically display the number in national format, removing the international dial code).\n\nIf you know the user's country, you can set it with [`initialCountry`](/docs/options#initialcountry) (e.g. `\"us\"` for the United States). If you don't, we recommend setting `initialCountry` to `\"auto\"` (along with the [`geoIpLookup`](/docs/options#geoiplookup) option) to determine the user's country based on their IP address - [see example](/examples/lookup-country).\n\nIf you know the user's language, there is a built in way to translate the country names and user interface strings - [see example](/playground?countryNameLocale=ru&i18n=ru#translation-options).\n\n_*Except for some small satellite territories, which share number ranges with the main country (search data.ts for \"shared\" for examples). When displaying numbers from those shared ranges, we default to selecting the main country._\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>\n"
  },
  {
    "path": "site/src/docs/markdown/localisation.md",
    "content": "# Localisation\n\nintl-tel-input supports localisation in a few different areas:\n\n- Country names in the dropdown\n- User interface strings (search placeholder, empty state, aria-labels, etc.)\n- Right-to-left (RTL) layout\n\nIf you want to see this in action first, you can experiment with the different language options in the [Playground](/playground?countryNameLocale=ru&i18n=ru#translation-options) and view the [Right to left example](/examples/right-to-left).\n\n## Contents\n\n- [Localising country names](#localising-country-names)\n- [Localising user interface strings](#localising-user-interface-strings)\n- [Right-to-left (RTL) languages](#right-to-left-rtl-languages)\n- [Alternative numerals](#alternative-numerals)\n- [Browser support / fallbacks](#browser-support--fallbacks)\n\n\n## Localising country names\n\nCountry names are generated using the native `Intl.DisplayNames` API.\n\nUse the [`countryNameLocale`](/docs/options#countrynamelocale) option to specify the locale (a BCP 47 language tag):\n\n```js\nintlTelInput(input, {\n  countryNameLocale: \"fr\", // French\n});\n```\n\nYou can also override individual country names via the [`i18n`](/docs/options#i18n) option by providing iso2 keys (e.g. `\"gb\"`, `\"us\"`):\n\n```js\nintlTelInput(input, {\n  countryNameLocale: \"fr\",\n  i18n: {\n    gb: \"Royaume-Uni\",\n  },\n});\n```\n\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>\n\n## Localising user interface strings\n\nUse the [`i18n`](/docs/options#i18n) option to translate the plugin’s user interface strings (search placeholder, empty state, aria-labels, etc.).\n\nWe provide translations for many languages in the `intl-tel-input/i18n` entrypoint, so you can import the one you need and pass it in:\n\n```js\nimport { fr } from \"intl-tel-input/i18n\";\n\nintlTelInput(input, {\n  i18n: fr,\n});\n```\n\nYou can override one or more keys by spreading the provided translations:\n\n```js\nimport { fr } from \"intl-tel-input/i18n\";\n\nintlTelInput(input, {\n  i18n: {\n    ...fr,\n    searchPlaceholder: \"Rechercher un pays\",\n  },\n});\n```\n\n### Supported languages\n\n<!-- I18N_LANGUAGE_LIST -->\n\nDon't see your language? You can provide your own translations by passing a custom object to the [`i18n`](/docs/options#i18n) option, and it's also easy to contribute a new language module — see [Adding a new translation](https://github.com/jackocnr/intl-tel-input/blob/master/.github/CONTRIBUTING.md#adding-a-new-translation).\n\nFor the full list of supported keys (and how to provide your own translations), see the [`i18n` option docs](/docs/options#i18n).\n\n\n## Right-to-left (RTL) languages\n\nFor RTL languages, add `dir=\"rtl\"` to the plugin container (or another appropriate ancestor) so the layout flows right-to-left.\n\nPhone numbers and dial codes are still displayed left-to-right, as that’s the standard way to write telephone numbers.\n\nSee the [Right to left example](/examples/right-to-left).\n\n\n## Alternative numerals\n\nintl-tel-input supports alternative numerals for phone number input.\n\n- Arabic-Indic digits (U+0660–U+0669)\n- Persian / Extended Arabic-Indic digits (U+06F0–U+06F9)\n\nThese are normalised internally to ASCII `0-9` for parsing/validation, and when the plugin updates the input value it preserves the user’s numeral set.\n\nNote: format-as-you-type is only applied when the user is typing ASCII digits (caret positioning gets unreliable with RTL numeral sets).\n"
  },
  {
    "path": "site/src/docs/markdown/methods.md",
    "content": "# Methods\n\nThis page lists the plugin's public API methods.\n\n## Contents\n\n- [Instance methods](#instance-methods)\n- [Static methods](#static-methods)\n\n## Instance Methods\n\nFor all of these examples, we will use the instance variable `iti` returned from the initialisation call, e.g. `const iti = intlTelInput(input, options)`\n\n### destroy\n\nRemove the plugin from the input, and unbind any event listeners.  \n```js\niti.destroy();\n```\n\n### getExtension\n\nGet the extension from the current number. Requires the [utils script to be loaded](/docs/utils#loading-the-utilities-script).\n```js\nconst extension = iti.getExtension();\n```\nReturns a string, e.g. if the input value was `\"(702) 555-5555 ext. 1234\"`, this would return `\"1234\"`\n\n### getNumber\n\nGet the current number in the given format (defaults to [E.164 standard](https://en.wikipedia.org/wiki/E.164)). The different formats are available in the enum [`intlTelInput.utils.numberFormat`](https://github.com/jackocnr/intl-tel-input/blob/master/src/js/utils.js#L198). Requires the [utils script to be loaded](/docs/utils#loading-the-utilities-script). _Note that even if [`nationalMode`](/docs/options#nationalmode) is enabled, this can still return a full international number. Also note that this method expects a valid number, and so should only be used after validation._  \n```js\nconst number = iti.getNumber();\n// or\nconst number = iti.getNumber(intlTelInput.utils.numberFormat.E164);\n```\nReturns a string e.g. `\"+17024181234\"`\n\n### getNumberType\n\nGet the type (fixed-line/mobile/toll-free, etc) of the current number. Requires the [utils script to be loaded](/docs/utils#loading-the-utilities-script).  \n```js\nconst numberType = iti.getNumberType();\n```\nReturns an integer, which you can match against the [various options](https://github.com/jackocnr/intl-tel-input/blob/master/src/js/utils.js#L207) in the enum `intlTelInput.utils.numberType`, e.g.  \n```js\nif (numberType === intlTelInput.utils.numberType.MOBILE) {\n    // is a mobile number\n}\n```\n_Note that in the US, there's no way to differentiate between fixed-line and mobile numbers, so instead it will return `FIXED_LINE_OR_MOBILE`._\n\n### getSelectedCountryData\n\nGet the country data for the currently selected country.  \n```js\nconst countryData = iti.getSelectedCountryData();\n```\nReturns something like this:\n```js\n{\n  name: \"Afghanistan\",\n  iso2: \"af\",\n  dialCode: \"93\"\n}\n```\n\n### getValidationError\n\nGet more information about a validation error. Requires the [utils script to be loaded](/docs/utils#loading-the-utilities-script).  \n```js\nconst error = iti.getValidationError();\n```\nReturns an integer, which you can match against the [various options](https://github.com/jackocnr/intl-tel-input/blob/master/src/js/utils.js#L223) in the enum `intlTelInput.utils.validationError`, e.g.  \n```js\nif (error === intlTelInput.utils.validationError.TOO_SHORT) {\n    // the number is too short\n}\n```\n\n### isValidNumber\n\n(Note: only returns `true` for valid <ins>mobile numbers</ins> by default - see [`allowedNumberTypes`](/docs/options#allowednumbertypes))  \nCheck if the current number is valid based on its length - [see example](/examples/validation-practical), which should be sufficient for most use cases. See [`isValidNumberPrecise`](/docs/methods#isvalidnumberprecise) (advanced) for more precise validation, but the advantage of [`isValidNumber`](/docs/methods#isvalidnumber) is that it is much more future-proof, as while countries around the world regularly update their number rules, they rarely change their number lengths. If this method returns `false`, you can use [`getValidationError`](/docs/methods#getvalidationerror) to get more information. Requires the [utils script to be loaded](/docs/utils#loading-the-utilities-script). _Note: previously named `isPossibleNumber`._  \n```js\nconst isValid = iti.isValidNumber();\n```\nReturns: `true`/`false`\n\n### isValidNumberPrecise\n\n⚠️ **ADVANCED**  \n(Note: only returns `true` for valid <ins>mobile numbers</ins> by default - see [`allowedNumberTypes`](/docs/options#allowednumbertypes))  \nCheck if the current number is valid using precise matching rules for each country/area code, etc - [see example](/examples/validation-precise). Note that these rules change each month for various countries around the world, so you need to constantly keep the plugin up-to-date (e.g. via an automated script) else <ins>you will start rejecting valid numbers</ins>. For a simpler and more future-proof form of validation, see [`isValidNumber`](/docs/methods#isvalidnumber) above. If validation fails, you can use [`getValidationError`](/docs/methods#getvalidationerror) to get more information. Requires the [utils script to be loaded](/docs/utils#loading-the-utilities-script).  \n```js\nconst isValid = iti.isValidNumberPrecise();\n```\nReturns: `true`/`false`\n\n### setCountry\n\nChange the selected country. It should be rare, if ever, that you need to do this, as the selected country gets updated automatically when calling [`setNumber`](/docs/methods#setnumber) and passing a number including an international dial code, which is the recommended usage. Note, you can omit the country code argument to set the country to the default empty (globe) state. _Note that if [`formatOnDisplay`](/docs/options#formatondisplay) is enabled, this will attempt to format the number to either national or international format according to the [`nationalMode`](/docs/options#nationalmode) option._  \n```js\niti.setCountry(\"gb\");\n```\n\n### setDisabled\n\nUpdates the disabled attribute of both the telephone input and the selected country button. Accepts a boolean value. _Note: we recommend using this instead of updating the disabled attribute of the input directly._\n```js\niti.setDisabled(true);\n```\n\n### setNumber\n\nInsert a number, and update the selected country accordingly. _Note that if [`formatOnDisplay`](/docs/options#formatondisplay) is enabled, this will attempt to format the number to either national or international format according to the [`nationalMode`](/docs/options#nationalmode) option._  \n```js\niti.setNumber(\"+447733123456\");\n```\n\n### setPlaceholderNumberType\n\nChange the [`placeholderNumberType`](/docs/options#placeholdernumbertype) option.\n```js\niti.setPlaceholderNumberType(\"FIXED_LINE\");\n```\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>\n\n## Static Methods\n\n### getCountryData\n\nRetrieve the plugin's country data - either to re-use elsewhere (e.g. to generate your own country dropdown), or alternatively, you could use it to modify the country data. Note that any modifications must be done before initialising the plugin.  \n```js\nconst countryData = intlTelInput.getCountryData();\n```\nReturns an array of country objects:\n```js\n[{\n  name: \"Afghanistan\",\n  iso2: \"af\",\n  dialCode: \"93\"\n}, ...]\n```\n\n### getInstance\n\nAfter initialising the plugin, you can always access the instance again using this method, by just passing in the relevant input element.\n```js\nconst input = document.querySelector('#phone');\nconst iti = intlTelInput.getInstance(input);\niti.isValidNumber(); // etc\n```\n\n### attachUtils\n\nAn alternative to the [`loadUtils`](/docs/options#loadutils) option, this method lets you manually load the utils.js script on demand, to enable formatting/validation etc. See [Loading The Utilities Script](/docs/utils#loading-the-utilities-script) for more information. This method should only be called once per page. A [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) object is returned so you can use `attachUtils().then(...)` to know when it's finished.\n```js\nconst loadUtils = () => import(\"/build/js/utils.js\");\nintlTelInput.attachUtils(loadUtils);\n```\n"
  },
  {
    "path": "site/src/docs/markdown/options.md",
    "content": "# Initialisation options\n\nWhen you initialise the plugin, the first argument is the input element, and the second is an object containing any initialisation options you want, which are detailed below. Country codes should always be [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) codes. Try out different options in the [Playground](/playground).\n\n## Contents\n\n- [Country options](#country-options)\n- [User interface options](#user-interface-options)\n- [Placeholder options](#placeholder-options)\n- [Formatting options](#formatting-options)\n- [Validation options](#validation-options)\n- [Translation options](#translation-options)\n- [Miscellaneous options](#miscellaneous-options)\n\n\n## Country options\n\nChoose which countries are available, the order they're displayed in, and how the initial country is determined.\n\n###### countryOrder\nType: `String[]`  \nDefault: `null`  \n\nAn array of iso2 country codes that is used to order the country dropdown. Any omitted countries will appear after those specified, in alphabetical order, e.g. setting `countryOrder` to `[\"jp\", \"kr\"]` will result in the list: Japan, South Korea, Afghanistan, Albania, Algeria, etc. See what this looks like in the [Playground](/playground?countryOrder=%5B\"jp\"%2C\"kr\"%5D#country-options). _Note: this replaces the legacy `preferredCountries` option (now removed)._ \n\n###### excludeCountries\nType: `String[]`  \nDefault: `[]`  \n\nAn array of iso2 country codes to exclude from the country dropdown e.g. `[\"gb\", \"us\"]`. Play with this option in the [Playground](/playground). Also see: [`onlyCountries`](#onlycountries) option.\n\n###### geoIpLookup\nType: `Function`  \nDefault: `null`  \n\nWhen setting [`initialCountry`](#initialcountry) to `\"auto\"`, you must use this option to specify a custom function that calls an IP lookup service to get the user's location and then invokes the `success` callback with the relevant country code. Also note that when instantiating the plugin, a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) object is returned under the `promise` instance property, so with an instance variable `iti` you can do something like `iti.promise.then(...)` to know when initialisation requests like this have completed.\n\nHere is an example using the [ipapi](https://ipapi.co/api/?javascript#location-of-clients-ip) service:  \n```js\nintlTelInput(input, {\n  initialCountry: \"auto\",\n  geoIpLookup: (success, failure) => {\n    fetch(\"https://ipapi.co/json\")\n      .then((res) => res.json())\n      .then((data) => success(data.country_code))\n      .catch(() => failure());\n  }\n});\n```\n_Note that the `failure` callback must be called in the event of an error, hence the use of `catch()` in this example. Tip: store the result in a cookie to avoid repeat lookups!_\n\n###### initialCountry\nType: `String`  \nDefault: `\"\"`  \n\nSet the initial country selection by specifying its country code, e.g. `\"us\"` for the United States. (Be careful not to do this unless you are sure of the user's country, as it can lead to tricky issues if set incorrectly and the user auto-fills their national number and submits the form without checking - in certain cases, this can pass validation and you can end up storing a number with the wrong dial code). You can also set [`initialCountry`](#initialcountry) to `\"auto\"`, which will look up the user's country based on their IP address (requires the [`geoIpLookup`](#geoiplookup) option - [see example](/examples/lookup-country)). Note: however you use [`initialCountry`](#initialcountry), it will not update the country selection if the input already contains a number with an international dial code. View the plugin with this set to `\"de\"` (Germany) in the [Playground](/playground?initialCountry=de#country-options).\n\n###### onlyCountries\nType: `String[]`  \nDefault: `[]`  \n\nIn the dropdown, display only the countries you specify here, using their iso2 codes e.g. `[\"fr\", \"de\", \"es\"]`. Try the plugin with this option set to only European countries in the [Playground](/playground?onlyCountries=%5B\"al\"%2C\"ad\"%2C\"at\"%2C\"by\"%2C\"be\"%2C\"ba\"%2C\"bg\"%2C\"hr\"%2C\"cz\"%2C\"dk\"%2C\"ee\"%2C\"fo\"%2C\"fi\"%2C\"fr\"%2C\"de\"%2C\"gi\"%2C\"gr\"%2C\"va\"%2C\"hu\"%2C\"is\"%2C\"ie\"%2C\"it\"%2C\"lv\"%2C\"li\"%2C\"lt\"%2C\"lu\"%2C\"mk\"%2C\"mt\"%2C\"md\"%2C\"mc\"%2C\"me\"%2C\"nl\"%2C\"no\"%2C\"pl\"%2C\"pt\"%2C\"ro\"%2C\"ru\"%2C\"sm\"%2C\"rs\"%2C\"sk\"%2C\"si\"%2C\"es\"%2C\"se\"%2C\"ch\"%2C\"ua\"%2C\"gb\"%5D#country-options). Also see: [`excludeCountries`](#excludecountries) option.\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>\n\n## User interface options\n\nControl dropdown behaviour and whether certain UI elements are displayed.\n\n###### allowDropdown\nType: `Boolean`  \nDefault: `true`  \n\nWhether or not to allow the dropdown. If disabled, there is no dropdown arrow, and the selected country is not clickable. Also, if [`showFlags`](/docs/options#showflags) is enabled, we display the selected flag on the right instead, because it is just a marker of state. Note that if [`separateDialCode`](#separatedialcode) is enabled, [`allowDropdown`](/docs/options#allowdropdown) is forced to `true` as the dropdown is required when the user types \"+\" in this case. Try the plugin with [`allowDropdown`](#allowdropdown) disabled in the [Playground](/playground?allowDropdown=false#user-interface-options).\n\n###### containerClass\nType: `String`  \nDefault: `\"\"`  \n\nAdditional class(es) to add to the (injected) wrapper div element `<div class=\"iti\">`.\n\n###### countrySearch\nType: `Boolean`  \nDefault: `true`  \n\nAdd a search input to the top of the dropdown, so users can filter the displayed countries. View the plugin with this disabled in the [Playground](/playground?countrySearch=false#user-interface-options).\n\n###### dropdownContainer\nType: `Node`  \nDefault: `null`  \n\nExpects a node, e.g. `document.body`. Instead of putting the country dropdown markup next to the input, append it to the specified node, and it will then be positioned next to the input using JavaScript (using `position: fixed`). This is useful when the input is inside a container with `overflow: hidden`. Note that the positioning is broken by scrolling, so the dropdown will automatically close on the `window` scroll event. This also applies to the fullscreen popup.\n\n###### fixDropdownWidth\nType: `Boolean`  \nDefault: `true`  \n\nFix the dropdown width to the input width (rather than being as wide as the longest country name). Try the plugin with this disabled in the [Playground](/playground?fixDropdownWidth=false#user-interface-options).\n\n###### searchInputClass\nType: `String`  \nDefault: `\"\"`  \n\nAdditional class(es) to add to the country search input element (requires [`countrySearch`](#countrysearch) to be enabled).\n\n###### separateDialCode\nType: `Boolean`  \nDefault: `false`  \n\nDisplay the selected country's international dial code next to the input, so it looks like it's part of the typed number. This makes it clear to the user which dial code is currently selected and that they are entering their number in international format. In the case that the user tries to type a new dial code (as well), we automatically open the country dropdown and put the newly typed dial code in the search input instead. So if they type +54, then Argentina will be highlighted in the dropdown, and they can simply press Enter to select it, updating the displayed dial code (this feature requires [`allowDropdown`](#allowdropdown) and [`countrySearch`](#countrysearch) to be enabled). Try the plugin with this enabled in the [Playground](/playground?separateDialCode=true&initialCountry=gb#user-interface-options). _Note: previously named `showSelectedDialCode`._\n\n<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"/img/iti-separate-dark.png\">\n  <source media=\"(prefers-color-scheme: light)\" srcset=\"/img/iti-separate-light.png\">\n  <img width=\"262\" height=\"50\" alt=\"Separate dial code\" src=\"/img/iti-separate-light.png\">\n</picture>\n\n###### showFlags\nType: `Boolean`  \nDefault: `true`  \n\nSet this to false to hide the flags, e.g. for political reasons. Instead, it will show a generic globe icon. Try the plugin with this disabled in the [Playground](/playground?showFlags=false#user-interface-options).\n\n###### useFullscreenPopup\nType: `Boolean`  \nDefault: `true on mobile devices, false otherwise`  \n\nControl when the country list appears as a fullscreen popup vs an inline dropdown. By default, it will appear as a fullscreen popup on mobile devices (based on user-agent and screen width), to make better use of the limited space (similar to how a native `<select>` works), and as an inline dropdown on larger devices/screens. Play with this option in the [Playground](/playground).\n\n<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"/img/iti-mobile-dark.png\">\n  <source media=\"(prefers-color-scheme: light)\" srcset=\"/img/iti-mobile-light.png\">\n  <img width=\"350\" height=\"637\" alt=\"Separate dial code\" src=\"/img/iti-mobile-light.png\">\n</picture>\n\n## Placeholder options\n\nConfigure the automatically generated placeholder numbers.\n\n###### autoPlaceholder\nType: `String`  \nDefault: `\"polite\"`  \n\nSet the input's placeholder to an example number for the selected country, and update it if the country changes. You can specify the number type using the [`placeholderNumberType`](#placeholdernumbertype) option. By default, it is set to `\"polite\"`, which means it will only set the placeholder if the input doesn't already have one. You can also set it to `\"aggressive\"`, which will replace any existing placeholder, or `\"off\"`. Requires the [utils script to be loaded](/docs/utils#loading-the-utilities-script). Play with this option on an input that contains a placeholder in the [Playground](/playground?initialCountry=gb&placeholder=Phone#placeholder-options).\n\n###### customPlaceholder\nType: `Function`  \nDefault: `null`  \n\nChange the placeholder generated by [`autoPlaceholder`](#autoplaceholder). Must return a string.\n\n```js\nintlTelInput(input, {\n  customPlaceholder: (selectedCountryPlaceholder, selectedCountryData) => \"e.g. \" + selectedCountryPlaceholder,\n});\n```\n\n###### placeholderNumberType\nType: `String`  \nDefault: `\"MOBILE\"`  \n\nSpecify [one of the keys](https://github.com/jackocnr/intl-tel-input/blob/master/src/js/utils.js#L207) from the enum `intlTelInput.utils.numberType` (e.g. `\"FIXED_LINE\"`) to set the number type to use for the generated placeholder numbers. Play with this option in the [Playground](/playground).\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>\n\n## Formatting options\n\nHow numbers are formatted as you type and on initial display.\n\n###### formatAsYouType\nType: `Boolean`  \nDefault: `true`  \n\nAutomatically format the number as the user types. This feature will be disabled if the user types their own formatting characters. Requires the [utils script to be loaded](/docs/utils#loading-the-utilities-script). Try the plugin with this disabled in the [Playground](/playground?formatAsYouType=false#formatting-options). _Note: previously named `autoFormat`._\n\n###### formatOnDisplay\nType: `Boolean`  \nDefault: `true`  \n\nFormat the input value (according to the [`nationalMode`](#nationalmode) option) during initialisation, when a new country is selected, and when calling [`setNumber`](/docs/methods#setnumber) or [`setCountry`](/docs/methods#setcountry). Requires the [utils script to be loaded](/docs/utils#loading-the-utilities-script). Try toggling this option on/off on an input containing a number in the [Playground](/playground?formatOnDisplay=false&value=%2B447947123123#formatting-options).\n\n###### nationalMode\nType: `Boolean`  \nDefault: `true`  \n\nFormat numbers in the national format, rather than the international format. This applies to placeholder numbers and when displaying users' existing numbers. Note that it's fine for users to type their numbers in national format - as long as they have selected the right country, you can use [`getNumber`](/docs/methods#getnumber) to extract a full international number. It is recommended to leave this option enabled to encourage users to enter their numbers in national format, as this is usually more familiar to them, and so it creates a better user experience. Play with this option in the [Playground](/playground).\n\n###### strictMode\nType: `Boolean`  \nDefault: `false`  \n\nAs the user types in the input, ignore any irrelevant characters. The user can only enter numeric characters and an optional plus at the beginning. Cap the length at the maximum valid number length (this respects [`allowedNumberTypes`](#allowednumbertypes)). Requires the [utils script to be loaded](/docs/utils#loading-the-utilities-script). Try the plugin with this enabled (and initialCountry=\"us\") in the [Playground](/playground?strictMode=true&initialCountry=us#formatting-options).\n\n## Validation options\n\nAdjust what is considered a valid number.\n\n###### allowedNumberTypes\nType: `String[]`  \nDefault: `[\"MOBILE\", \"FIXED_LINE\"]`  \n\nSpecify an array of [the keys](https://github.com/jackocnr/intl-tel-input/blob/master/src/js/utils.js#L207) from the enum `intlTelInput.utils.numberType` to set the number type(s) to enforce during validation, as well as the number length to enforce with [strictMode](#strictmode). Set it to `null` to not enforce any particular type. By default, it's set to `[\"MOBILE\", \"FIXED_LINE\"]` so [`isValidNumber`](/docs/methods#isvalidnumber) (etc) will only return `true` for those kinds of numbers. Alternatively, you could set it to simply `[\"MOBILE\"]` if you only wanted to accept mobile numbers as valid. Play with this option in the [Playground](/playground). _Note: previously named `validationNumberTypes`._\n\n###### allowNumberExtensions\nType: `Boolean`  \nDefault: `false`  \n\nWhether or not the validation methods return `true` for numbers containing extensions, e.g. \"+1 702 123-1234 ext. 1234\". Try toggling this option on/off on a number with an extension in the [Playground](/playground?value=%2B447947692123+ext.+12345#validation-options).\n\n###### allowPhonewords\nType: `Boolean`  \nDefault: `false`  \n\nWhether or not the validation methods return `true` for numbers containing phonewords, e.g. \"+1 702 FLOWERS\". Play with this option in the [Playground](/playground).\n\n## Translation options\n\nLocalise country names and the plugin UI strings, e.g. the country search placeholder.\n\n###### countryNameLocale\nType: `String`  \nDefault: `\"en\"`  \n\nThe locale to pass to `Intl.DisplayNames` to generate the country names. Should adhere to the [BCP 47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) standard, e.g. `\"zh\"` (Chinese), or `\"zh-Hans\"` (Simplified Chinese). For translating the other UI strings, like the country search placeholder, see the [`i18n`](#i18n) option below. View the plugin with `countryNameLocale` set to `\"zh\"`, and `i18n` set to the provided ZH user interface translations in the [Playground](/playground?countryNameLocale=zh&i18n=zh&initialCountry=cn#translation-options).\n\n###### i18n\nType: `Object`  \nDefault: `{}`  \n\nFor translating country names, see [`countryNameLocale`](#countrynamelocale) option above. The `i18n` option is for translating the user interface strings (including the country search placeholder, search empty state, and various other strings for screen readers). We provide translations for these in <a href=\"https://github.com/jackocnr/intl-tel-input/tree/master/src/js/intl-tel-input/i18n\">over 40 languages</a> - simply import the language module you need and pass it to the plugin using the `i18n` option (see option 1 below). You can also override one or more individual keys this way. Alternatively, you can provide your own custom translations (see option 2 below). If providing your own, please see the required UI strings listed below. If we don't currently support a language you need, it's easy to [contribute this](https://github.com/jackocnr/intl-tel-input/blob/master/.github/CONTRIBUTING.md#adding-a-new-translation) yourself - you only need to provide a handful of UI translation strings. View the plugin with `countryNameLocale` set to `\"zh\"`, and `i18n` set to the provided ZH user interface translations in the [Playground](/playground?countryNameLocale=zh&i18n=zh&initialCountry=cn#translation-options). _Note: previously named `localizedCountries`._\n\nOption 1: import one of the provided translation modules\n```js\nimport { fr } from \"intl-tel-input/i18n\";\n\nintlTelInput(input, {\n  i18n: fr,\n});\n\n// or to override one or more keys, you could do something like this\nintlTelInput(input, {\n  i18n: {\n    ...fr,\n    searchPlaceholder: \"Recherche de pays\",\n    // or override a specific country name\n    us: \"United States\",\n  },\n});\n```\n\nOption 2: define your own custom translations\n```js\nintlTelInput(input, {\n  i18n: {\n    // Aria label for the selected country element, when there is a country selected\n    selectedCountryAriaLabel: \"Change country, selected ${countryName} (${dialCode})\",\n    // Aria label and title text for the selected country element, when no country is selected\n    noCountrySelected: \"Select country\",\n    // Aria label for the country list element\n    countryListAriaLabel: \"List of countries\",\n    // Placeholder for the search input in the dropdown\n    searchPlaceholder: \"Search\",\n    // Aria label for the clear search button\n    clearSearchAriaLabel: \"Clear search\",\n    // Visible text for when the search produces no results\n    searchEmptyState: \"No results found\",\n    // Screen reader summary of search results\n    searchSummaryAria(count) {\n      if (count === 0) return \"No results found\";\n      if (count === 1) return \"1 result found\";\n      return `${count} results found`;\n    }\n  }\n});\n```\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>\n\n## Miscellaneous options\n\nExtra features like hidden inputs and loading the utilities module.\n\n###### hiddenInput\nType: `Function`  \nDefault: `null`  \n\nAllows the creation of hidden input fields within a form, which, on submit, get populated with (1) the full international telephone number and (2) the selected country code. It accepts a function that receives the name of the main telephone input as an argument. This function should return an object with `phone` and (optionally) `country` properties to specify the names of the hidden inputs for the phone number and country code, respectively. This is useful for old-fashioned, page-load form submissions to ensure the full international number and country code are captured, especially when [`nationalMode`](#nationalmode) is enabled. [See example](/examples/hidden-input).\n\n***Note**: This feature requires the input to be inside a `<form>` element, as it listens for the `submit` event on the closest form element. Also note that since this uses [`getNumber`](/docs/methods#getnumber) internally, firstly it requires the [utils script to be loaded](/docs/utils#loading-the-utilities-script), and secondly, it expects a valid number and so will only work correctly if you have used [`isValidNumber`](/docs/methods#isvalidnumber) to validate the number before allowing the form submit to go through.\n\n```js\nintlTelInput(input, {\n  hiddenInput: (telInputName) => ({\n    phone: \"phone_full\",\n    country: \"country_code\"\n  }),\n});\n```\n\nThis will generate the following (hidden) elements, which will be automatically populated on submit:\n\n```html\n<input type=\"hidden\" name=\"phone_full\">\n<input type=\"hidden\" name=\"country_code\">\n```\n\n###### loadUtils\nType: `() => Promise<module>`  \nDefault: `null`  \n\nThis is one way to lazy load the included utils.js (to enable formatting/validation, etc) - see [Loading The Utilities Script](/docs/utils#loading-the-utilities-script) for more options.\n\nThe [`loadUtils`](#loadutils) option takes a function that returns a Promise resolving to the utils module. You can `import` the utils module in different ways (examples below): (A) from a CDN, (B) from your own hosted version of utils.js, or (C) if you use a bundler like Webpack, Vite or Parcel, you can import it directly from the package. Play with this option in the [Playground](/playground). _Note: this replaces the `utilsScript` option (now removed)._ \n\n```js\n// (A) import utils module from a CDN\nintlTelInput(htmlInputElement, {\n  loadUtils: () => import(\"https://cdn.jsdelivr.net/npm/intl-tel-input@26.8.1/build/js/utils.js\"),\n});\n\n// (B) import utils module from your own hosted version of utils.js\nintlTelInput(htmlInputElement, {\n  loadUtils: () => import(\"/path/to/utils.js\"),\n});\n\n// (C) with a bundler, you can import the utils module directly from the package\nintlTelInput(htmlInputElement, {\n  loadUtils: () => import(\"intl-tel-input/utils\"),\n});\n```\n\nThe module is only loaded when you initialise the plugin, and additionally, only when the page has finished loading (on the window load event) to prevent blocking (the script is ~260KB). When instantiating the plugin, a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) object is returned under the `promise` instance property, so with an instance variable `iti` you can do something like `iti.promise.then(callback)` to know when initialisation requests like this have finished. See [Utilities Script](/docs/utils) for more information.\n\nIf you want more control over when this file is lazy-loaded, you can manually invoke the [`attachUtils`](/docs/methods#attachutils) static method whenever you like, instead of using the [`loadUtils`](/docs/options#loadutils) initialisation option.\n"
  },
  {
    "path": "site/src/docs/markdown/react_component.md",
    "content": "# React component\n\nA React component for the intl-tel-input JavaScript plugin. View the [source code](https://github.com/jackocnr/intl-tel-input/blob/master/react/src/intl-tel-input/react.tsx).\n\n## Contents\n\n- [Demo](#demo)\n- [Getting started](#getting-started)\n- [Props](#props)\n- [Accessing instance methods](#accessing-instance-methods)\n- [Accessing static methods](#accessing-static-methods)\n- [Troubleshooting](#troubleshooting)\n\n## Demo\n\nYou can see a live demo and example code on the [React component](/examples/react-component) example page.\n\nAlternatively, download and build the project yourself in 3 simple steps. You just need to initialise the submodules with `git submodule update --init --recursive`, then run `npm install`, and then `npm run build`. You should now be able to open the validation demo page /react/demo/validation/validation.html in your browser and give it a try. View other [available demos](https://github.com/jackocnr/intl-tel-input/tree/master/react/demo).\n\n## Getting started\n\nFirst, install the package: \n\n```sh\nnpm install intl-tel-input\n```\n\nThen, add something like this to your code:\n\n```js\nimport IntlTelInput from \"intl-tel-input/reactWithUtils\";\nimport \"intl-tel-input/styles\";\n\n<IntlTelInput\n    onChangeNumber={setNumber}\n    onChangeValidity={setIsValid}\n    initOptions={{\n        initialCountry: \"us\",\n    }}\n/>\n```\n\nSee the [Validation demo](https://github.com/jackocnr/intl-tel-input/blob/master/react/demo/validation/ValidationApp.tsx) for a more fleshed-out example of how to handle validation.\n\nA note on the utils script (~260KB): if you're lazy loading the IntlTelInput chunk (and so less worried about filesize), then you can just import IntlTelInput from `\"intl-tel-input/reactWithUtils\"`, to include the utils script. Alternatively, if you use the main `\"intl-tel-input/react\"` import, then you should couple this with the `loadUtils` initialisation option - you will need to host the utils.js file, and then set the `loadUtils` option to that URL, or just point it to a CDN-hosted version, e.g. `\"https://cdn.jsdelivr.net/npm/intl-tel-input@26.5.0/build/js/utils.js\"`.\n\n## Props\n\nHere's a list of all of the current props you can pass to the IntlTelInput React component.\n\n###### disabled\nType: `Boolean`  \nDefault: `false`  \n\nSets the disabled attribute of both the telephone input and the selected country button. _Note: We recommend using this instead of `inputProps.disabled`._\n\n###### initialValue\nType: `String`  \nDefault: `\"\"`  \n\nThe initial value to put in the input. This will get auto-formatted on init (according to `formatOnDisplay` initialisation option). IntlTelInput is an uncontrolled input and so will ignore any changes to this value.\n\n###### initOptions\nType: `Object`  \nDefault: `{}`  \n\nAn object containing the [initialisation options](/docs/options) to pass to the plugin. These can be used exactly the same way as with the main JavaScript plugin.\n\n###### inputProps\nType: `Object`  \nDefault: `{}`  \n\nThe props to pass to the input element, e.g. `id`, `className`, `placeholder`, `required`, `onBlur`, etc.\n\nNote: some keys are reserved for the component/plugin integration and will be ignored if provided via `inputProps`: `type`, `ref`, `onInput`, `defaultValue`, `value`, `disabled`. Use the component props (`initialValue`, `disabled`) and the callback props instead.\n\n###### onChangeCountry\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the selected country changes. It will receive the new country iso2 code, e.g. \"gb\" for the UK.\n\n###### onChangeErrorCode\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the number validation error changes. It will receive the new error code (or `null`). Requires the utils script to be loaded (see above).\n\n###### onChangeNumber\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the number changes. For valid numbers (see `onChangeValidity`), it will receive the new number in the standard E.164 format. Requires the utils script to be loaded (see above).\n\n###### onChangeValidity\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the number validity changes, e.g. to true/false. It will receive the new isValid boolean. Requires the utils script to be loaded (see above).\n\n###### usePreciseValidation\nType: `Boolean`\nDefault: `false`  \n\nBy default, we use `isValidNumber` for validation, but if you'd rather use `isValidNumberPrecise`, you can set this to `true`.\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>\n\n## Accessing instance methods\n\nYou can access all of the plugin's [instance methods](/docs/methods#instance-methods) (`setNumber`, `setCountry`, `setPlaceholderNumberType`, etc) by passing a ref into the IntlTelInput component (using the `ref` prop), and then calling `ref.current.getInstance()`, e.g. `ref.current.getInstance().setNumber(...);`. See the [Set Number demo](https://github.com/jackocnr/intl-tel-input/blob/master/react/demo/set-number/SetNumberApp.tsx) for a full example. You can also access the input DOM element in a similar way: `ref.current.getInput()`.\n\n## Accessing static methods\n\nYou can access all of the plugin's [static methods](/docs/methods#static-methods) by importing `intlTelInput` from the same file as the React component, e.g. `import { intlTelInput } from \"intl-tel-input/react\"` (note the lower case \"i\" in \"intlTelInput\"). You can then use this as you would with the main plugin, e.g. `intlTelInput.getCountryData()` or `intlTelInput.utils.numberType` etc.\n\n## Troubleshooting\n\n###### Error when toggling presence of IntlTelInput component\n\nError message: `Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.`\n\nSolution: wrap the component in a div, e.g.\n\n```js\n{showIntlTelInput && (\n    <div>\n        <IntlTelInput />\n    </div>\n)}\n```\n"
  },
  {
    "path": "site/src/docs/markdown/svelte_component.md",
    "content": "# Svelte component\n\nA Svelte 5 component for the intl-tel-input JavaScript plugin. View the [source code](https://github.com/jackocnr/intl-tel-input/blob/master/svelte/src/intl-tel-input/IntlTelInput.svelte).\n\n## Contents\n\n- [Demo](#demo)\n- [Getting started](#getting-started)\n- [Props](#props)\n- [Accessing instance methods](#accessing-instance-methods)\n- [Accessing static methods](#accessing-static-methods)\n\n## Demo\n\nYou can see a live demo and example code on the [Svelte component](/examples/svelte-component) example page.\n\nTry it for yourself by downloading and building the project yourself in 3 simple steps. You just need to initialise the submodules with `git submodule update --init --recursive`, then run `npm install`, and then `npm run build`. You can then run `npm run svelte:demo` and copy the given URL into your browser. By default, this is set up to show the validation demo - you can change this by locating the `\"svelte:demo\"` task in the scripts section of package.json, and updating the demo path accordingly. View a list of [available demos](https://github.com/jackocnr/intl-tel-input/tree/master/svelte/demo).\n\n## Getting started\n\nFirst, install the package: \n\n```sh\nnpm install intl-tel-input\n```\n\nThen, add something like this to your code:\n\n```html\n<script>\n  import IntlTelInput from \"intl-tel-input/svelteWithUtils\";\n  import \"intl-tel-input/styles\";\n</script>\n\n<IntlTelInput\n  options={{\n    initialCountry: 'us',\n  }}\n/>\n```\n\nSee the [Validation demo](https://github.com/jackocnr/intl-tel-input/blob/master/svelte/demo/validation/App.svelte) for a more fleshed-out example of how to handle validation.\n\nA note on the utils script (~260KB): if you're lazy loading the IntlTelInput chunk (and so less worried about filesize) then you can just import IntlTelInput from `\"intl-tel-input/svelteWithUtils\"`, to include the utils script. Alternatively, if you use the main `\"intl-tel-input/svelte\"` import, then you should couple this with the `loadUtils` initialisation option - you will need to host the utils.js file, and then set the `loadUtils` option to that URL, or just point it to a CDN-hosted version, e.g. `\"https://cdn.jsdelivr.net/npm/intl-tel-input@26.5.0/build/js/utils.js\"`.\n\n## Props\n\nHere's a list of all of the current props you can pass to the IntlTelInput Svelte component.\n\n###### disabled\nType: `Boolean`  \nDefault: `false`  \n\nSets the disabled attribute of both the telephone input and the selected country button. *Note: We recommend using this instead of `inputProps.disabled`.*\n\n###### inputProps\nType: `Object`  \nDefault: `{}`  \n\nThe props to pass to the input element, e.g. `id`, `class`, `placeholder`, `required`, `onblur`, etc.\n\nNote: some keys are reserved for the component/plugin integration and will be ignored if provided via `inputProps`: `type`, `oninput`, `value`, `disabled`. Use the component props (`value`, `disabled`) and the `onChange...` callback props instead.\n\n###### options\nType: `Object`  \nDefault: `{}`  \n\nAn object containing the [initialisation options](/docs/options) to pass to the plugin. You can use these exactly the same way as with the main JavaScript plugin.\n\n###### value\nType: `String`  \nDefault: `\"\"`  \n\nThe initial value to put in the input. This will get auto-formatted on init (according to `formatOnDisplay` initialisation option). IntlTelInput is an uncontrolled input, and so will ignore any changes to this value.\n\n###### onChangeCountry\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the selected country changes. It will be passed the new country iso2 code, e.g. \"gb\" for the UK.\n\n###### onChangeErrorCode\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the number validation error changes. It will be passed the new error code (or `null`).\n\n###### onChangeNumber\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the number changes. It will be passed the new number.\n\n###### onChangeValidity\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the number validity changes, e.g. to true/false. It will be passed the new isValid boolean.\n\n###### usePreciseValidation\nType: `Boolean`\nDefault: `false`  \n\nBy default, we use `isValidNumber` for validation, but if you'd rather use `isValidNumberPrecise`, you can set this to `true`.\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>\n\n## Accessing instance methods\n\nYou can access all of the plugin's [instance methods](/docs/methods#instance-methods) (`setNumber`, `setCountry`, `setPlaceholderNumberType`, etc.) by passing a ref into the IntlTelInput component (using `bind:this`), and then calling the `getInstance()` method, e.g. `ref.getInstance().setNumber(...);`. See the [Set Number demo](https://github.com/jackocnr/intl-tel-input/blob/master/svelte/demo/set-number/App.svelte) for a full example. You can also access the input DOM element via: `ref.getInput()`.\n\n## Accessing static methods\n\nYou can access all of the plugin's [static methods](/docs/methods#static-methods) by importing `intlTelInput` from the core package, e.g. `import intlTelInput from \"intl-tel-input\"` (note the lower case \"i\" in \"intlTelInput\"). You can then use this as you would with the main plugin, e.g. `intlTelInput.getCountryData()` or `intlTelInput.utils.numberType` etc.\n"
  },
  {
    "path": "site/src/docs/markdown/theming.md",
    "content": "# Theming / dark mode\n\nThere are lots of CSS variables available for theming. See [intlTelInput.scss](https://github.com/jackocnr/intl-tel-input/blob/master/src/css/intlTelInput.scss) for the full list.\n\nDark mode example (with screenshot below):\n```css\n@media (prefers-color-scheme: dark) {\n  .iti {\n    --iti-border-color: #5b5b5b;\n    --iti-dropdown-bg: #0d1117;\n    --iti-icon-color: #aaaaaa;\n    --iti-hover-color: #30363d;\n  }\n}\n```\n\nNOTE: this assumes you already have your own dark mode styling in place for general body/input styling, e.g. something like this:\n\n```css\n@media (prefers-color-scheme: dark) {\n  body, input {\n    color: white;\n    background-color: #0d1117;\n  }\n  input {\n    border-color: #5b5b5b;\n  }\n  input::placeholder {\n    color: #8d96a0;\n  }\n}\n```\n\nExample:  \n<img src=\"/img/iti-vanilla-dark.png\" alt=\"Screenshot\" width=\"265\" height=\"280\" />\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>"
  },
  {
    "path": "site/src/docs/markdown/troubleshooting.md",
    "content": "# Troubleshooting\n\n## Full-width input\n\nIf you want your input to be full-width, you need to set the container to be the same, i.e.\n\n```css\n.iti { width: 100%; }\n```\n\n## dropdownContainer: dropdown not closing on scroll\n\nIf you are using the [`dropdownContainer`](/docs/options#dropdowncontainer) option and you have a scrolling container other than `window` which is causing problems by not closing the dropdown on scroll, simply listen for the scroll event on that element, and trigger a scroll event on `window`, which in turn will close the dropdown, e.g.\n\n```js\nscrollingElement.addEventListener(\"scroll\", () => {\n  const e = document.createEvent('Event');\n  e.initEvent(\"scroll\", true, true);\n  window.dispatchEvent(e);\n});\n```\n\n## Input margin\n\nFor the sake of alignment, the default CSS forces the input's vertical margin to `0px`. If you want a vertical margin, you should add it to the container (with class `iti`).\n\n## Displaying error messages\n\nIf your error handling code inserts an error message before the `<input>`, it will break the layout. Instead, you must insert it before the container (with class `iti`).\n\n## Dropdown position\n\nThe dropdown should automatically appear above/below the input depending on the available space. For this to work properly, you must only initialise the plugin after the `<input>` has been added to the DOM.\n\n## Placeholders\n\nTo get the automatic country-specific placeholder numbers, simply omit the placeholder attribute on the `<input>`, or set [`autoPlaceholder`](/docs/options#autoplaceholder) to `\"aggressive\"` to override any existing placeholder. Note: this requires the utils script to be loaded.\n\n## Bootstrap input groups\n\nA couple of CSS fixes are required to get the plugin to play nice with Bootstrap [input groups](https://getbootstrap.com/docs/3.3/components/#input-groups). [Codepen](https://codepen.io/jackocnr/pen/EyPXed).  \n_Note: there is currently [a bug](https://bugs.webkit.org/show_bug.cgi?id=141822) in Mobile Safari which causes a crash when you click the dropdown arrow (a CSS triangle) inside an input group. The simplest workaround is to remove the CSS triangle with this line:_\n\n```css\n.iti__arrow { border: none; }\n```\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>"
  },
  {
    "path": "site/src/docs/markdown/utils.md",
    "content": "# Utilities script\n\nThe utilities script (src/js/utils.js) is a custom build of Google's [libphonenumber](https://github.com/googlei18n/libphonenumber) which enables the following features:\n\n* Formatting upon initialisation, as well as with [`getNumber`](/docs/methods#getnumber) and [`setNumber`](/docs/methods#setnumber)\n* Validation with [`isValidNumber`](/docs/methods#isvalidnumber), [`getNumberType`](/docs/methods#getnumbertype) and [`getValidationError`](/docs/methods#getvalidationerror) methods\n* Placeholder set to an example number for the selected country - even specify the type of number (e.g. mobile) using the [`placeholderNumberType`](/docs/options#placeholdernumbertype) option\n* Extract the standardised (E.164) international number with [`getNumber`](/docs/methods#getnumber) even when using the [`nationalMode`](/docs/options#nationalmode) option\n\nInternational number formatting/validation is hard (it varies by country/district, and we currently support ~230 countries). The only comprehensive solution we have found is libphonenumber, from which we have precompiled the relevant parts into a single JavaScript file, included in the build directory. Unfortunately, even after modification, it is still ~260KB. See the section below on the best way to load it.\n\n## Loading the utilities script\n\nThe utils script provides lots of great functionality (see the above section), but comes at the cost of increased filesize (~260KB). There are two main ways to load the utils script, depending on whether you're concerned about filesize or not.\n\n**Option 1: intlTelInputWithUtils**  \nIf you're not concerned about filesize (e.g. you're lazy loading the main plugin script), the easiest thing to do is to use the all-in-one bundle (`/build/js/intlTelInputWithUtils.js`), which comes with the utils script included. This script can be used exactly like the main intlTelInput.js - so it can either be loaded directly onto the page (which defines `window.intlTelInput` like usual), or it can be imported like so:\n\n```js\nimport intlTelInput from \"intl-tel-input/intlTelInputWithUtils\"\n```\n\n**Option 2: loadUtils**  \nIf you *are* concerned about filesize, you can lazy load the utils module when the plugin initialises, using the [`loadUtils`](/docs/options#loadutils) initialisation option.\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>\n"
  },
  {
    "path": "site/src/docs/markdown/vue_component.md",
    "content": "# Vue component\n\nA Vue component for the intl-tel-input JavaScript plugin. View the [source code](https://github.com/jackocnr/intl-tel-input/blob/master/vue/src/intl-tel-input/IntlTelInput.vue).\n\n## Contents\n\n- [Demo](#demo)\n- [Getting started](#getting-started)\n- [Props](#props)\n- [Events](#events)\n- [Accessing instance methods](#accessing-instance-methods)\n- [Accessing static methods](#accessing-static-methods)\n\n## Demo\n\nYou can see a live demo and example code on the [Vue component](/examples/vue-component) example page.\n\nAlternatively, download and build the project yourself in 3 simple steps. You just need to initialise the submodules with `git submodule update --init --recursive`, then run `npm install`, and then `npm run build`. You can then run `npm run vue:demo` and copy the given URL into your browser. By default, this is set up to show the validation demo - you can change this by locating the `\"vue:demo\"` task in the scripts section of package.json, and updating the demo path accordingly. View a list of [available demos](https://github.com/jackocnr/intl-tel-input/tree/master/vue/demo).\n\n## Getting started\n\nFirst, install the package: \n\n```sh\nnpm install intl-tel-input\n```\n\nThen, add something like this to your code:\n\n```html\n<script setup>\n  import IntlTelInput from \"intl-tel-input/vueWithUtils\";\n  import \"intl-tel-input/styles\";\n</script>\n\n<template>\n  <IntlTelInput\n    :options=\"{\n      initialCountry: 'us',\n    }\"\n  />\n</template>\n```\n\nSee the [Validation demo](https://github.com/jackocnr/intl-tel-input/blob/master/vue/demo/validation/App.vue) for a more fleshed-out example of how to handle validation. See the instructions above for how to run this demo (and others) yourself.\n\nA note on the utils script (~260KB): if you're lazy loading the IntlTelInput chunk (and so less worried about filesize), then you can just import IntlTelInput from `\"intl-tel-input/vueWithUtils\"`, to include the utils script. Alternatively, if you use the main `\"intl-tel-input/vue\"` import, then you should couple this with the `loadUtils` initialisation option - you will need to host the utils.js file, and then set the `loadUtils` option to that URL, or alternatively just point it to a CDN-hosted version, e.g. `\"https://cdn.jsdelivr.net/npm/intl-tel-input@26.5.0/build/js/utils.js\"`.\n\n## Props\n\nHere's a list of all of the current props you can pass to the IntlTelInput Vue component.\n\n###### disabled\nType: `Boolean`  \nDefault: `false`  \n\nSets the disabled attribute of both the telephone input and the selected country button. *Note: we recommend using this instead of `inputProps.disabled`.*\n\n###### inputProps\nType: `Object`  \nDefault: `{}`  \n\nThe props to pass to the input element, e.g. `id`, `class`, `placeholder`, `required`, `onBlur`.\n\nNote: some keys are reserved for the component/plugin integration and will be ignored if provided via `inputProps`: `type`, `ref`, `@input`/`onInput`, `@countrychange`/`onCountrychange`, `value`, `disabled`. Use the component props (`value`, `disabled`) and component events (`changeNumber`, `changeCountry`, etc.) instead.\n\n###### options\nType: `Object`  \nDefault: `{}`  \n\nAn object containing the [initialisation options](/docs/options) to pass to the plugin. You can use these exactly the same way as with the main JavaScript plugin.\n\n###### usePreciseValidation\nType: `Boolean`\nDefault: `false`  \n\nBy default, the component uses the plugin's `isValidNumber` method for validation, but if you'd rather use `isValidNumberPrecise`, set this to `true`.\n\n###### value\nType: `String`  \nDefault: `\"\"`  \n\nThe initial value to put in the input. This will get auto-formatted on init (according to `formatOnDisplay` initialisation option). IntlTelInput is an uncontrolled input, and so will ignore any changes to this value.\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>\n\n## Events\nHere's a list of all of the current events you can listen to on the IntlTelInput Vue component.\n\n###### changeCountry\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the selected country changes. It will be passed the new country iso2 code, e.g. \"gb\" for the UK.\n\n###### changeErrorCode\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the number validation error changes. It will be passed the new error code (or `null`).\n\n###### changeNumber\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the number changes. It will be passed the new number.\n\n###### changeValidity\nType: `Function`  \nDefault: `null`  \n\nA handler to be called when the number validity changes, e.g. to true/false. It will be passed the new isValid boolean.\n\n## Accessing instance methods\n\nYou can access all of the plugin's [instance methods](/docs/methods#instance-methods) (`setNumber`, `setCountry`, `setPlaceholderNumberType`, etc.) by passing a ref into the IntlTelInput component (using the `ref` prop), and then accessing `ref.value.instance`, e.g. `ref.value?.instance?.setNumber(...);`. See the [Set Number demo](https://github.com/jackocnr/intl-tel-input/blob/master/vue/demo/set-number/App.vue) for a full example. You can also access the input DOM element in a similar way: `ref.value?.input`.\n\n## Accessing static methods\n\nYou can access all of the plugin's [static methods](/docs/methods#static-methods) by importing `intlTelInput` from the same file as the Vue component, e.g. `import { intlTelInput } from \"intl-tel-input/vue\"` (note the lower case \"i\" in \"intlTelInput\"). You can then use this as you would with the main plugin, e.g. `intlTelInput.getCountryData()` or `intlTelInput.utils.numberType` etc.\n"
  },
  {
    "path": "site/src/examples/copy/angular_component_desc.html",
    "content": "<p>This project includes an official Angular component alongside the original JavaScript plugin. It supports all of the same features, including the country picker, formatting, and validation, and comes with TypeScript type definitions included. View all of the supported props in the <a href=\"/docs/angular-component\">Angular component</a> docs.</p>\n\n<p>Below is a live demo and example code demonstrating <code>ReactiveFormsModule</code> integration, validation (on blur/submit), and error messaging. For more patterns and use cases, explore the <a href=\"https://github.com/jackocnr/intl-tel-input/tree/master/angular/demo\">Angular demos</a>.</p>\n"
  },
  {
    "path": "site/src/examples/copy/display_number_desc.html",
    "content": "<p>When you want to display a user's number back to them, in your HTML you can just set the input's value to the full international number before you initialise International Telephone Input. The plugin will automatically select the relevant flag, and re-format the number to national format (unless you set <a href=\"/docs/options#nationalmode\"><code>nationalMode</code></a> to false). We recommend that internally, you always deal with numbers in full international format, and then through the plugin, the user can always deal with numbers in the familiar national format. Note that reformating the number in this way requires the utils script to be loaded via the <a href=\"/docs/options#loadutils\"><code>loadUtils</code></a> option. The utils script is a custom build of Google's libphonenumber library in JavaScript, which we use for all formatting, validation and generating placeholder numbers. Play with the various plugin options on an input containing a number in the <a href=\"/playground?value=%2B447947123123#input-attributes\">Playground</a>.</p>\n"
  },
  {
    "path": "site/src/examples/copy/hidden_input_desc.html",
    "content": "<p>If you're handling your form submissions using good old-fashioned page requests (instead of modern JavaScript fetch requests) then you need to make sure your input values contain all of the information you need before the page request is made. Assuming you want to save your user's phone numbers in full standardised international format, which we <a rel=\"noopener\" href=\"/docs/getting-started#recommended-usage\">strongly recommend</a>, you will encounter a problem when a user enters their phone number in national format. For this situation, International Telephone Input provides the <a href=\"/docs/options#hiddeninput\"><code>hiddenInput</code></a> option, which listens for the form submit event, and injects a hidden input containing the full international number (and another containing the country code), which will then be submitted along with the rest of your form data.</p>\n\n<p>You can see this in action using the demo below. Just select your country, enter a valid phone number in national format (no international dial code), and submit the form. You will see that the hidden input value \"full_phone\" (containing the full international number) is sent along with the other input value (\"phone\") in the URL parameters. In this example, the form submits to the current URL, so it just reloads the current page with those URL parameters. Then, for demonstration purposes, when we detect the \"full_phone\" parameter in the URL, we display it below so you can see it working.</p>\n"
  },
  {
    "path": "site/src/examples/copy/large_flags_desc.html",
    "content": "<p>While you can easily override a lot of the plugin's styles in your own CSS, you cannot change the flag image dimensions, which are pre-built to a specific size. By default, this is 16x12px, or 32x24px for the retina images. In order to show larger flags, you will need to re-build the images yourself. This is very simple to do.</p>\n\n<p>In order to rebuild larger flag images, first follow the <a rel=\"noopener\" href=\"https://github.com/jackocnr/intl-tel-input/blob/master/.github/CONTRIBUTING.md#changes-to-the-plugin\">Contributing</a> guidelines to download the project and get it building (<code>npm run build</code>) without any errors. Now, let's say you want to display the flag images at 24x18px (retina image is 48x36px), as per the demo below. In grunt/generate-sprite.js, update the <code>TARGET_HEIGHT</code> value to 18 (this will automatically be doubled for the retina sprite). Note that whatever height you choose, it must be a multiple of 3. Then run <code>npm run build:img</code> to generate the new flag images and the updated CSS needed to use them. At this point you should be able to open the provided demo.html to see the larger flag images. You may then want to override some of the CSS variables/styles from <code>src/css/intlTelInput.scss</code> in your own CSS in order to accommodate the larger flag images, e.g. see <a href=\"/css/large_flags_overrides.css\">the overrides</a> used in this demo. When you're happy, you can copy the built flag images and CSS file into your own project, and then just load the standard plugin script as usual.</p>\n"
  },
  {
    "path": "site/src/examples/copy/lookup_country_desc.html",
    "content": "<p>For the best user experience, International Telephone Input supports setting the initial selected country to the user's current country, using an IP lookup service. This means that when the user first sees the phone input, they already see the correct flag displayed, and they see a familiar placeholder number that is relevant to their country.</p>\n\n<p>To achieve this, you must enable two initialisation options. First, you must set the <a href=\"/docs/options#initialcountry\"><code>initialCountry</code></a> option to <code>\"auto\"</code>. This tells the plugin to use the IP lookup service to determine the user's country. Second, you must set the <a href=\"/docs/options#geoiplookup\"><code>geoIpLookup</code></a> option to a function that will make the IP lookup request and return the country code. The plugin will then use this country code to set the initial country correctly. In the example below, we make a request to a service called <a rel=\"noopener\" href=\"https://ipapi.co\">ipapi</a> which allows a certain amount of lookups for free. We use JavaScript's <code>fetch</code> method to make the request, which is supported by all modern browsers. Note that within the <a href=\"/docs/options#geoiplookup\"><code>geoIpLookup</code></a> function, the <code>callback</code> must always be called, even in the event of an error, hence the use of <code>catch</code> in the example code.</p>\n"
  },
  {
    "path": "site/src/examples/copy/multiple_instances_desc.html",
    "content": "<p>If you have multiple telephone inputs on the same page, it is safe to initialise International Telephone Input on each of them individually in your JavaScript. You can use different initialisation options in each case e.g. in the demo below, we specify different <a href=\"/docs/options#placeholdernumbertype\"><code>placeholderNumberType</code></a> values for each of the first two instances. We also specify the <a href=\"/docs/options#onlycountries\"><code>onlyCountries</code></a> option in the third instance, so that country dropdown only includes three countries, but this does not affect the first two instances. Play with these options in the <a href=\"/playground\">Playground</a>. Note that if you use <a href=\"/docs/options#loadutils\"><code>loadUtils</code></a> or <a href=\"/docs/options#geoiplookup\"><code>geoIpLookup</code></a> options across multiple instances, those requests will only be fired once, and the result will be shared.</p>\n"
  },
  {
    "path": "site/src/examples/copy/react_component_desc.html",
    "content": "<p>This project includes an official React component alongside the original JavaScript plugin. It supports all of the same features, including the country picker, formatting, and validation, and comes with TypeScript type definitions included. View all of the supported props in the <a href=\"/docs/react-component\">React component</a> docs.</p>\n\n<p>Below is a live demo and example code demonstrating form integration, validation (on blur/submit), and error messaging. For more patterns and use cases, explore the <a href=\"https://github.com/jackocnr/intl-tel-input/tree/master/react/demo\">React demos</a>.</p>\n\n<p>Note: the red/green validation styling and warning/success icons in this example come from <a rel=\"noopener\" href=\"https://getbootstrap.com/docs/5.3/forms/validation/\">Bootstrap form validation</a>.</p>\n"
  },
  {
    "path": "site/src/examples/copy/right_to_left_desc.html",
    "content": "<p>In this demo, we add <code>dir=\"rtl\"</code> to the plugin container so the layout flows right-to-left. Phone numbers and dial codes are still displayed left-to-right, as that’s the standard way to write telephone numbers. We also import the Arabic locale and pass it via the <a href=\"/docs/options#i18n\"><code>i18n</code></a> option, set <a href=\"/docs/options#countrynamelocale\"><code>countryNameLocale</code></a> to <code>\"ar\"</code> (Arabic), and initialise the plugin with <a href=\"/docs/options#initialcountry\"><code>initialCountry</code></a> set to Egypt.</p>\n"
  },
  {
    "path": "site/src/examples/copy/single_country_desc.html",
    "content": "<p>Even if you only need to handle numbers from a single country, the plugin can still be helpful. It can provide an example number for the placeholder, formatting as-you-type, as well as validation. The user can type their number in national format and then you can use <a href=\"/docs/methods#getnumber\"><code>getNumber</code></a> to get the number in standardised international format to store in your database. In this example, we've set <a href=\"/docs/options#onlycountries\"><code>onlyCountries</code></a> to a single country: United States, and we've disabled <a href=\"/docs/options#allowdropdown\"><code>allowDropdown</code></a> as well as <a href=\"/docs/options#showflags\"><code>showFlags</code></a>. Play with these options in the <a href=\"/playground?allowDropdown=false&onlyCountries=%5B%22us%22%5D&showFlags=false\">Playground</a>.</p>\n"
  },
  {
    "path": "site/src/examples/copy/svelte_component_desc.html",
    "content": "<p>This project includes an official Svelte component alongside the original JavaScript plugin. It supports all of the same features, including the country picker, formatting, and validation. View all of the supported props in the <a href=\"/docs/svelte-component\">Svelte component</a> docs.</p>\n\n<p>Below is a live demo and example code demonstrating form integration, validation, and error messaging. For more patterns and use cases, explore the <a href=\"https://github.com/jackocnr/intl-tel-input/tree/master/svelte/demo\">Svelte demos</a>.</p>\n\n<p>Note: the red/green validation styling and warning/success icons in this example come from <a rel=\"noopener\" href=\"https://getbootstrap.com/docs/5.3/forms/validation/\">Bootstrap form validation</a>.</p>\n"
  },
  {
    "path": "site/src/examples/copy/validation_practical_desc.html",
    "content": "<p>It is important to validate any user input before saving it to your backend. The <a href=\"/docs/methods#isvalidnumber\"><code>isValidNumber</code></a> method provides a simple form of validation that only checks the number length, which should be sufficient for most use cases. The advantage of this simple approach is that it is much more future-proof as while countries around the world regularly update their number rules, they very rarely change their number lengths. For a more precise (and much less stable) form of validation, see <a href=\"/examples/validation-precise\">Precise Validation (advanced)</a>.</p>\n\n<p><a href=\"/docs/methods#isvalidnumber\"><code>isValidNumber</code></a> respects the <a href=\"/docs/options#allowednumbertypes\"><code>allowedNumberTypes</code></a> option, which is set to <code>[\"MOBILE\", \"FIXED_LINE\"]</code> by default, meaning it will only return <code>true</code> for those types of numbers.</p>\n\n<p>If <a href=\"/docs/methods#isvalidnumber\"><code>isValidNumber</code></a> returns <code>false</code> and you'd like more detail, you can then use the <a href=\"/docs/methods#getvalidationerror\"><code>getValidationError</code></a> method to get more information. This method returns an <a rel=\"noopener\" href=\"https://github.com/jackocnr/intl-tel-input/blob/master/src/js/utils.js#L223\">error code</a> (integer), which you can then map to your own custom error message, as in the example below.</p>\n\n<p>All validation methods require the utils script to be loaded (see the readme for instructions). The utils script contains a custom build of Google's <a rel=\"noopener\" href=\"https://github.com/google/libphonenumber\">libphonenumber</a> library in JavaScript, which provides validation tools as well as formatting and placeholder number generation.</p>\n\n<p>Note: the red/green validation styling and warning/success icons in this example come from <a rel=\"noopener\" href=\"https://getbootstrap.com/docs/5.3/forms/validation/\">Bootstrap form validation</a>.</p>\n"
  },
  {
    "path": "site/src/examples/copy/validation_precise_desc.html",
    "content": "<p>WARNING: Various countries around the world update their number rules every month, so <a href=\"/docs/methods#isvalidnumberprecise\"><code>isValidNumberPrecise</code></a> will start rejecting valid numbers unless you constantly keep it up-to-date e.g. via an automated script. For this reason, we strongly recommend sticking to <a href=\"/examples/validation-practical\">the standard validation method</a> which is much more stable as it only checks number length rules, which rarely change.</p>\n\n<p><a href=\"/docs/methods#isvalidnumberprecise\"><code>isValidNumberPrecise</code></a> respects the <a href=\"/docs/options#allowednumbertypes\"><code>allowedNumberTypes</code></a> option, which is set to <code>[\"MOBILE\", \"FIXED_LINE\"]</code> by default, meaning it will only return <code>true</code> for those types of numbers.</p>\n\n<p>If <a href=\"/docs/methods#isvalidnumberprecise\"><code>isValidNumberPrecise</code></a> returns <code>false</code>, and you'd like more detail, you can then use the <a href=\"/docs/methods#getvalidationerror\"><code>getValidationError</code></a> method to get more information. This method returns an <a rel=\"noopener\" href=\"https://github.com/jackocnr/intl-tel-input/blob/master/src/js/utils.js#L223\">error code</a> (integer), which you can then map to your own custom error message, as in the example below.</p>\n\n<p>All validation methods require the utils script to be loaded (see the readme for instructions). The utils script contains a custom build of Google's <a rel=\"noopener\" href=\"https://github.com/google/libphonenumber\">libphonenumber</a> library in JavaScript, which provides validation tools as well as formatting and placeholder number generation.</p>\n\n<p>Note: the red/green validation styling and warning/success icons in this example come from <a rel=\"noopener\" href=\"https://getbootstrap.com/docs/5.3/forms/validation/\">Bootstrap form validation</a>.</p>\n"
  },
  {
    "path": "site/src/examples/copy/vue_component_desc.html",
    "content": "<p>This project includes an official Vue component alongside the original JavaScript plugin. It supports all of the same features, including the country picker, formatting, and validation, and comes with TypeScript type definitions included. View all of the supported props in the <a href=\"/docs/vue-component\">Vue component</a> docs.</p>\n\n<p>Below is a live demo and example code demonstrating form integration, validation, and error messaging. For more patterns and use cases, explore the <a href=\"https://github.com/jackocnr/intl-tel-input/tree/master/vue/demo\">Vue demos</a>.</p>\n\n<p>Note: the red/green validation styling and warning/success icons in this example come from <a rel=\"noopener\" href=\"https://getbootstrap.com/docs/5.3/forms/validation/\">Bootstrap form validation</a>.</p>\n"
  },
  {
    "path": "site/src/examples/css/multiple_instances.css",
    "content": "td {\n  padding: 7px 10px 7px 0;\n}\n"
  },
  {
    "path": "site/src/examples/css/validation.css",
    "content": "#error-msg {\n  color: red;\n}\n#valid-msg {\n  color: #00c900;\n}\ninput.error {\n  border: 1px solid #ff7c7c;\n}\n.hide {\n  display: none;\n}\n"
  },
  {
    "path": "site/src/examples/examples_content_template.html.ejs",
    "content": "<nav class=\"iti-breadcrumb\" aria-label=\"Breadcrumb\">\n  <ol class=\"iti-breadcrumb__list\">\n    <li class=\"iti-breadcrumb__item\" aria-current=\"page\">Examples »</li>\n  </ol>\n</nav>\n\n<h1><%= content_title %></h1>\n\n<div class=\"section\">\n  <% if (desc.charAt(0) === '<') { %>\n    <%= desc %>\n  <% } else { %>\n    <p><%= desc %></p>\n  <% } %>\n</div>\n\n<div class=\"section demo\" <% if (typeof isRtl !== 'undefined') { %>dir=\"rtl\"<% } %>>\n  <h2>Demo</h2>\n  <% if (typeof demo_note !== 'undefined') { %>\n    <%= demo_note %>\n  <% } %>\n  <%= markup %>\n</div>\n\n<% if (typeof hideMarkupSection === 'undefined') { %>\n<div class=\"section\">\n  <h2>Html</h2>\n  <pre><code class=\"language-html\"><%- display_markup %></code></pre>\n</div>\n<% } %>\n\n<div class=\"section\">\n  <h2>JavaScript</h2>\n  <pre><code class=\"language-javascript\"><%- display_code %></code></pre>\n</div>\n\n<%= common_body_end %>\n<% if (typeof iti_script !== 'undefined') { %>\n  <%= iti_script %>\n<% } %>\n<script src=\"<%= cacheBust('/examples/js/' + script) %>\"></script>\n\n<div class=\"article-ad\">\n  <ins class=\"adsbygoogle\"\n    style=\"display:block; text-align:center;\"\n    data-ad-layout=\"in-article\"\n    data-ad-format=\"fluid\"\n    data-ad-client=\"ca-pub-1090343328224651\"\n    data-ad-slot=\"6972377388\"></ins>\n  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n</div>\n"
  },
  {
    "path": "site/src/examples/examples_nav_template.html.ejs",
    "content": "<h6 class=\"mt-4 mb-2 text-uppercase iti-sidebar-heading\">Examples</h6>\n<ul class=\"flex-column nav nav-pills gap-1 my-3\">\n  <% examplesDropdownPages.forEach((p) => { %>\n    <li>\n      <a class=\"nav-link py-1 <% if (name === p.name) { %>active<% } %>\" href=\"<%= p.href %>\"><%= p.label %></a>\n    </li>\n  <% }); %>\n</ul>"
  },
  {
    "path": "site/src/examples/examples_page_template.html.ejs",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <%= common_meta_tags %>\n    <title><%= head_title %></title>\n    <link rel=\"canonical\" href=\"<%= canonical_url %>\" />\n    <meta name=\"description\" content=\"<%= meta_desc %>\">\n\n    <%= og_meta_tags %>\n    <%= common_styles %>\n    <% if (typeof stylesheet_after_website_css !== 'undefined') { %>\n      <link rel=\"stylesheet\" href=\"<%= cacheBust(stylesheet_after_website_css) %>\">\n    <% } %>\n\n    <%= common_head_end_prod %>\n  </head>\n\n  <body class=\"iti-page iti-page--examples\">\n    <%= content %>\n  </body>\n</html>\n"
  },
  {
    "path": "site/src/examples/html/component.html",
    "content": "<div id=\"app\"></div>"
  },
  {
    "path": "site/src/examples/html/display_number.html",
    "content": "<input id=\"phone\" type=\"tel\" class=\"form-control\" value=\"+447733312345\" title=\"Enter your phone number\">\n"
  },
  {
    "path": "site/src/examples/html/display_number_display_code.html",
    "content": "<input id=\"phone\" type=\"tel\" value=\"+447733312345\">\n"
  },
  {
    "path": "site/src/examples/html/multiple_instances.html",
    "content": "<form>\n  <div class=\"row mb-3 align-items-center\">\n    <label for=\"home\" class=\"col-sm-1 col-form-label\">Home</label>\n    <div class=\"col-sm-10\">\n      <input id=\"home\" type=\"tel\" class=\"form-control\" />\n    </div>\n  </div>\n  <div class=\"row mb-3 align-items-center\">\n    <label for=\"mobile\" class=\"col-sm-1 col-form-label\">Mobile</label>\n    <div class=\"col-sm-10\">\n      <input id=\"mobile\" type=\"tel\" class=\"form-control\" />\n    </div>\n  </div>\n  <div class=\"row mb-3 align-items-center\">\n    <label for=\"vacation\" class=\"col-sm-1 col-form-label\">Vacation</label>\n    <div class=\"col-sm-10\">\n      <input id=\"vacation\" type=\"tel\" class=\"form-control\" />\n    </div>\n  </div>\n</form>\n"
  },
  {
    "path": "site/src/examples/html/multiple_instances_display_code.html",
    "content": "<input id=\"home\" type=\"tel\">\n<input id=\"mobile\" type=\"tel\">\n<input id=\"vacation\" type=\"tel\">\n"
  },
  {
    "path": "site/src/examples/html/simple_input.html",
    "content": "<input id=\"phone\" type=\"tel\" class=\"form-control\" title=\"Enter your phone number\" />"
  },
  {
    "path": "site/src/examples/html/simple_input_display_code.html",
    "content": "<input id=\"phone\" type=\"tel\">\n"
  },
  {
    "path": "site/src/examples/html/validation.html",
    "content": "<form id=\"form\" class=\"row g-2 validation-demo\" noValidate>\n  <div class=\"col-auto\">\n    <input\n      id=\"phone\"\n      type=\"tel\"\n      class=\"form-control\"\n      name=\"phone\"\n      title=\"Enter your phone number\"\n      required\n    />\n    <div id=\"error-msg\" class=\"invalid-feedback d-block\"></div>\n    <div id=\"valid-msg\" class=\"valid-feedback d-block\"></div>\n  </div>\n  <div class=\"col-auto\">\n    <button class=\"btn btn-primary\" id=\"btn\" type=\"submit\">Submit</button>\n  </div>\n</form>"
  },
  {
    "path": "site/src/examples/html/validation_display_code.html",
    "content": "<form id=\"form\">\n  <input id=\"phone\" type=\"tel\">\n  <button type=\"submit\">Submit</button>\n</form>\n<span id=\"error-msg\"></span>\n"
  },
  {
    "path": "site/src/examples/js/angular_component.ts",
    "content": "import \"zone.js\";\nimport \"@angular/compiler\";\nimport { bootstrapApplication } from \"@angular/platform-browser\";\nimport { Component, ViewChild } from \"@angular/core\";\nimport { FormControl, FormGroup, ReactiveFormsModule } from \"@angular/forms\";\nimport { IntlTelInputComponent } from \"../../../build/intl-tel-input/angular/IntlTelInput.js\";\nimport type { IntlTelInputComponent as IntlTelInputComponentType } from \"../../../build/intl-tel-input/angular/types/intl-tel-input/angular\";\n\nconst errorMap = [\n  \"Invalid number\",\n  \"Invalid country code\",\n  \"Too short\",\n  \"Too long\",\n  \"Invalid number\",\n];\n\n@Component({\n  selector: \"#app\",\n  template: `\n    <form [formGroup]=\"fg\" (ngSubmit)=\"handleSubmit()\" class=\"row g-2\" novalidate>\n      <div class=\"col-auto\">\n        <intl-tel-input\n          #telInput\n          formControlName=\"phone\"\n          (numberChange)=\"handleNumberChange($event)\"\n          (validityChange)=\"isValid = $event\"\n          (errorCodeChange)=\"errorCode = $event ?? 0\"\n          (blur)=\"enableValidation()\"\n          [initOptions]=\"initOptions\"\n          [inputProps]=\"inputProps\"\n        />\n        @if (invalidMsg) {\n          <div class=\"invalid-feedback d-block\">{{ invalidMsg }}</div>\n        }\n        @if (validMsg) {\n          <div class=\"valid-feedback d-block\">{{ validMsg }}</div>\n        }\n      </div>\n      <div class=\"col-auto\">\n        <button class=\"btn btn-primary\" type=\"submit\">Submit</button>\n      </div>\n    </form>\n  `,\n  standalone: true,\n  imports: [IntlTelInputComponent, ReactiveFormsModule],\n})\nexport class AppComponent {\n  @ViewChild(\"telInput\") telInput?: IntlTelInputComponentType;\n\n  number = \"\";\n  isValid = false;\n  errorCode = 0;\n  showValidation = false;\n  submitted = false;\n\n  initOptions = {\n    initialCountry: \"us\",\n    // @ts-expect-error Vite/ESM dynamic import is using an EJS-templated URL string.\n    loadUtils: () => import(\"<%= cacheBust('/intl-tel-input/js/utils.js') %>\"),\n    searchInputClass: \"form-control\",\n  };\n\n  fg: FormGroup = new FormGroup({\n    phone: new FormControl<string>(\"\"),\n  });\n\n  get inputValidityClass(): string {\n    if (!this.showValidation) return \"\";\n    return this.number && this.isValid ? \"is-valid\" : \"is-invalid\";\n  }\n\n  get invalidMsg(): string | null {\n    if (!this.showValidation || this.isValid) return null;\n    return this.number\n      ? errorMap[this.errorCode || 0] || \"Invalid number\"\n      : \"Please enter a number\";\n  }\n\n  get validMsg(): string | null {\n    const showValid =\n      this.showValidation && this.number && this.isValid && this.submitted;\n    return showValid ? `Full number: ${this.number}` : null;\n  }\n\n  get inputProps(): Record<string, unknown> {\n    return {\n      name: \"phone\",\n      title: \"Enter your phone number\",\n      required: true,\n      class: `form-control ${this.inputValidityClass}`,\n    };\n  }\n\n  enableValidation(): void {\n    this.showValidation = true;\n  }\n\n  handleNumberChange(newNumber: string): void {\n    this.submitted = false;\n    this.number = newNumber;\n  }\n\n  handleSubmit(): void {\n    this.showValidation = true;\n    this.submitted = true;\n  }\n}\n\nbootstrapApplication(AppComponent)\n  .catch((err) => console.error(err));\n"
  },
  {
    "path": "site/src/examples/js/angular_component_display_code.js",
    "content": "import { Component, ViewChild } from \"@angular/core\";\nimport { FormControl, FormGroup, ReactiveFormsModule, Validators } from \"@angular/forms\";\nimport { IntlTelInputComponent } from \"intl-tel-input/angular\";\nimport \"intl-tel-input/styles\";\n\n@Component({\n  selector: \"#app\",\n  template: `\n    <form [formGroup]=\"fg\" (ngSubmit)=\"handleSubmit()\">\n      <intl-tel-input\n        #telInput\n        formControlName=\"phone\"\n        name=\"phone\"\n        [initOptions]=\"initOptions\"\n      />\n      <button type=\"submit\">Validate</button>\n      <div class=\"notice\">{{ noticeText }}</div>\n    </form>\n  `,\n  standalone: true,\n  imports: [IntlTelInputComponent, ReactiveFormsModule],\n})\nexport class AppComponent {\n  @ViewChild(\"telInput\") telInput;\n  hasValidated = false;\n\n  initOptions = {\n    initialCountry: \"us\",\n    loadUtils: () => import(\"intl-tel-input/utils\"),\n  };\n\n  fg = new FormGroup({\n    phone: new FormControl<string>(\"\", [Validators.required]),\n  });\n\n  get phone() {\n    return this.fg.get(\"phone\");\n  }\n\n  get noticeText() {\n    // Determine the notice message based on the current state\n  }\n\n  handleSubmit() {\n    this.phone?.markAsTouched();\n    this.hasValidated = true;\n  }\n}\n"
  },
  {
    "path": "site/src/examples/js/hidden_input.js",
    "content": "const form = document.querySelector(\"#form\");\nconst input = document.querySelector(\"#phone\");\nconst errorMsg = document.querySelector(\"#error-msg\");\nconst validMsg = document.querySelector(\"#valid-msg\");\n\n// here, the index maps to the error code returned from getValidationError - see readme\nconst errorMap = [\n  \"Invalid number\",\n  \"Invalid country code\",\n  \"Too short\",\n  \"Too long\",\n  \"Invalid number\",\n];\n\n// initialise plugin\nconst iti = window.intlTelInput(input, {\n  initialCountry: \"us\",\n  hiddenInput: () => ({ phone: \"full_phone\", country: \"country_code\" }),\n  loadUtils: () => import(\"<%= cacheBust('/intl-tel-input/js/utils.js') %>\"),\n  searchInputClass: \"form-control\",\n});\n\nlet showValidation = false;\n\nconst setText = (el, text) => el.textContent = text;\n\nconst updateUI = () => {\n  if (!showValidation) {\n    return;\n  }\n\n  // once showValidation is true, we always show the validity state (via the input class and message below), so keep it up-to-date here\n  const value = input.value.trim();\n  const isValid = Boolean(value) && iti.isValidNumber();\n\n  input.classList.toggle(\"is-valid\", isValid);\n  input.classList.toggle(\"is-invalid\", !isValid);\n\n  let invalidMsg = \"\";\n  if (!isValid) {\n    const errorCode = iti.getValidationError();\n    invalidMsg = value\n      ? errorMap[errorCode || 0] || \"Invalid number\"\n      : \"Please enter a number\";\n  }\n  setText(errorMsg, invalidMsg);\n  setText(validMsg, \"\");\n\n  return isValid;\n};\n\n// on submit: validate\nform.addEventListener(\"submit\", (e) => {\n  showValidation = true;\n  const isValid = updateUI();\n  if (!isValid) {\n    e.preventDefault();\n  }\n});\n\n// on blur: enable validation UI\ninput.addEventListener(\"blur\", () => {\n  showValidation = true;\n  updateUI();\n});\n\n// while typing / pasting / changing country: update validity state\ninput.addEventListener(\"input\", updateUI);\ninput.addEventListener(\"countrychange\", updateUI);\n\n// if the form was submitted and the page reloaded with the full phone number in the query string, show it here\nconst urlParams = new URLSearchParams(window.location.search);\nconst fullPhone = urlParams.get(\"full_phone\");\nif (fullPhone) {\n  setText(validMsg, `Submitted value: ${fullPhone}`);\n}\n"
  },
  {
    "path": "site/src/examples/js/hidden_input_display_code.js",
    "content": "import intlTelInput from \"intl-tel-input\";\n\nconst form = document.querySelector(\"#form\");\nconst input = document.querySelector(\"#phone\");\nconst errorMsg = document.querySelector(\"#error-msg\");\nconst validMsg = document.querySelector(\"#valid-msg\");\n\n// initialise plugin\nconst iti = intlTelInput(input, {\n  initialCountry: \"us\",\n  hiddenInput: () => ({ phone: \"full_phone\", country: \"country_code\" }),\n  loadUtils: () => import(\"intl-tel-input/utils\"),\n});\n\n// validation code\nlet showValidation = false;\n\nconst updateUI = () => {\n  if (!showValidation) return;\n\n  let invalidMsg = \"\";\n  const isValid = iti.isValidNumber();\n  if (!isValid) {\n    const errorCode = iti.getValidationError();\n    invalidMsg = yourCodeToDeriveErrorMessage(input.value(), errorCode);\n  }\n  errorMsg.textContent = invalidMsg;\n  return isValid;\n};\n\n// on submit: enable validation UI\nform.addEventListener(\"submit\", (e) => {\n  showValidation = true;\n  const isValid = updateUI();\n  if (!isValid) {\n    e.preventDefault();\n  }\n});\n\n// on blur: enable validation UI\ninput.addEventListener(\"blur\", () => {\n  showValidation = true;\n  updateUI();\n});\n\n// while typing / pasting / changing country: update validity state\ninput.addEventListener(\"input\", updateUI);\ninput.addEventListener(\"countrychange\", updateUI);\n\n// if the form was submitted and the page reloaded with the full phone number in the query string, show it here\nconst urlParams = new URLSearchParams(window.location.search);\nconst fullPhone = urlParams.get(\"full_phone\");\nif (fullPhone) {\n  validMsg.textContent = `Submitted value: ${fullPhone}`;\n}\n"
  },
  {
    "path": "site/src/examples/js/lookup_country.js",
    "content": "const input = document.querySelector(\"#phone\");\nwindow.intlTelInput(input, {\n  initialCountry: \"auto\",\n  geoIpLookup: (success, failure) => {\n    fetch(`https://ipapi.co/json?token=${process.env.IPAPI_TOKEN}`)\n      .then(res => res.json())\n      .then(data => success(data.country_code))\n      .catch(() => failure());\n  },\n  loadUtils: () => import(\"<%= cacheBust('/intl-tel-input/js/utils.js') %>\"),\n  searchInputClass: \"form-control\",\n});\n"
  },
  {
    "path": "site/src/examples/js/lookup_country_display_code.js",
    "content": "import intlTelInput from \"intl-tel-input\";\n\nconst input = document.querySelector(\"#phone\");\nintlTelInput(input, {\n  initialCountry: \"auto\",\n  geoIpLookup: (success, failure) => {\n    fetch(\"https://ipapi.co/json\")\n      .then(res => res.json())\n      .then(data => success(data.country_code))\n      .catch(() => failure());\n  },\n  loadUtils: () => import(\"/intl-tel-input/js/utils.js\"),\n});\n"
  },
  {
    "path": "site/src/examples/js/multiple_instances.js",
    "content": "const inputHome = document.querySelector(\"#home\");\nconst inputMobile = document.querySelector(\"#mobile\");\nconst inputVacation = document.querySelector(\"#vacation\");\n\nwindow.intlTelInput(inputHome, {\n  initialCountry: \"gb\",\n  placeholderNumberType: \"FIXED_LINE\",\n  loadUtils: () => import(\"<%= cacheBust('/intl-tel-input/js/utils.js') %>\"),\n  searchInputClass: \"form-control\",\n});\nwindow.intlTelInput(inputMobile, {\n  initialCountry: \"gb\",\n  placeholderNumberType: \"MOBILE\",\n  loadUtils: () => import(\"<%= cacheBust('/intl-tel-input/js/utils.js') %>\"),\n  searchInputClass: \"form-control\",\n});\nwindow.intlTelInput(inputVacation, {\n  initialCountry: \"es\",\n  onlyCountries: [\"es\", \"fr\", \"it\"],\n  loadUtils: () => import(\"<%= cacheBust('/intl-tel-input/js/utils.js') %>\"),\n  searchInputClass: \"form-control\",\n});\n"
  },
  {
    "path": "site/src/examples/js/multiple_instances_display_code.js",
    "content": "import intlTelInput from \"intl-tel-input\";\n\nconst inputHome = document.querySelector(\"#home\");\nconst inputMobile = document.querySelector(\"#mobile\");\nconst inputVacation = document.querySelector(\"#vacation\");\n\nintlTelInput(inputHome, {\n  initialCountry: \"gb\",\n  placeholderNumberType: \"FIXED_LINE\",\n  loadUtils: () => import(\"intl-tel-input/utils\"),\n});\nintlTelInput(inputMobile, {\n  initialCountry: \"gb\",\n  placeholderNumberType: \"MOBILE\",\n  loadUtils: () => import(\"intl-tel-input/utils\"),\n});\nintlTelInput(inputVacation, {\n  initialCountry: \"es\",\n  onlyCountries: [\"es\", \"fr\", \"it\"],\n  loadUtils: () => import(\"intl-tel-input/utils\"),\n});\n"
  },
  {
    "path": "site/src/examples/js/react_component.js",
    "content": "import React, { useState } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport IntlTelInput from \"../../../build/intl-tel-input/react/IntlTelInput.js\";\n\nconst errorMap = [\n  \"Invalid number\",\n  \"Invalid country code\",\n  \"Too short\",\n  \"Too long\",\n  \"Invalid number\",\n];\n\nconst App = () => {\n  const [number, setNumber] = useState(\"\");\n  const [isValid, setIsValid] = useState(false);\n  const [errorCode, setErrorCode] = useState(0);\n  const [showValidation, setShowValidation] = useState(false);\n  const [submitted, setSubmitted] = useState(false);\n\n  let inputValidityClass = \"\";\n  if (showValidation) {\n    inputValidityClass = number && isValid ? \"is-valid\" : \"is-invalid\";\n  }\n\n  let invalidMsg = null;\n  if (showValidation && !isValid) {\n    invalidMsg = number\n      ? errorMap[errorCode || 0] || \"Invalid number\"\n      : \"Please enter a number\";\n  }\n\n  const showValid = showValidation && number && isValid && submitted;\n  const validMsg = showValid ? `Full number: ${number}` : null;\n\n  const handleChangeNumber = (newNumber) => {\n    setSubmitted(false);\n    setNumber(newNumber);\n  };\n\n  const handleSubmit = (e) => {\n    e.preventDefault();\n    setShowValidation(true);\n    setSubmitted(true);\n  };\n\n  return (\n    <form onSubmit={handleSubmit} className=\"row g-2\" noValidate>\n      <div className=\"col-auto\">\n        <IntlTelInput\n          onChangeNumber={handleChangeNumber}\n          onChangeValidity={setIsValid}\n          onChangeErrorCode={setErrorCode}\n          initOptions={{\n            initialCountry: \"us\",\n            loadUtils: () => import(\"<%= cacheBust('/intl-tel-input/js/utils.js') %>\"),\n            searchInputClass: \"form-control\",\n          }}\n          inputProps={{\n            name: \"phone\",\n            title: \"Enter your phone number\",\n            required: true,\n            onBlur: () => setShowValidation(true),\n            className: `form-control ${inputValidityClass}`,\n          }}\n        />\n        {invalidMsg && (\n          <div className=\"invalid-feedback d-block\">{invalidMsg}</div>\n        )}\n        {validMsg && (\n          <div className=\"valid-feedback d-block\">{validMsg}</div>\n        )}\n      </div>\n      <div className=\"col-auto\">\n        <button className=\"btn btn-primary\" type=\"submit\">Submit</button>\n      </div>\n    </form>\n  );\n};\n\nconst container = document.getElementById(\"app\");\nconst root = createRoot(container);\nroot.render(<App />);"
  },
  {
    "path": "site/src/examples/js/react_component_display_code.js",
    "content": "import React, { useState } from \"react\";\nimport IntlTelInput from \"intl-tel-input/react\";\nimport \"intl-tel-input/styles\";\n\nconst App = () => {\n  const [number, setNumber] = useState(\"\");\n  const [isValid, setIsValid] = useState(false);\n  const [errorCode, setErrorCode] = useState(0);\n  const [showValidation, setShowValidation] = useState(false);\n\n  let invalidMsg = null;\n  if (showValidation && !isValid) {\n    // your logic to derive the invalid message\n    invalidMsg = deriveInvalidMsg(number, errorCode);\n  }\n\n  const handleSubmit = (e) => {\n    e.preventDefault();\n    setShowValidation(true);\n  };\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <IntlTelInput\n        onChangeNumber={setNumber}\n        onChangeValidity={setIsValid}\n        onChangeErrorCode={setErrorCode}\n        initOptions={{\n          initialCountry: \"us\",\n          loadUtils: () => import(\"intl-tel-input/utils\"),\n        }}\n        inputProps={{\n          onBlur: () => setShowValidation(true),\n        }}\n      />\n      <button type=\"submit\">Submit</button>\n      {invalidMsg && (\n        <div className=\"invalid\">{invalidMsg}</div>\n      )}\n    </form>\n  );\n};\nexport default App;"
  },
  {
    "path": "site/src/examples/js/right_to_left.js",
    "content": "import intlTelInput from \"../../../build/intl-tel-input/js/intlTelInput\";\nimport ar from \"../../../build/intl-tel-input/js/i18n/ar\"; // arabic\n\nconst input = document.querySelector(\"#phone\");\n\nintlTelInput(input, {\n  i18n: ar,\n  countryNameLocale: \"ar\",\n  initialCountry: \"eg\",\n  loadUtils: () => import(\"<%= cacheBust('/intl-tel-input/js/utils.js') %>\"),\n  searchInputClass: \"form-control\",\n});\n"
  },
  {
    "path": "site/src/examples/js/right_to_left_display_code.js",
    "content": "import intlTelInput from \"intl-tel-input\";\n// Arabic\nimport { ar } from \"intl-tel-input/i18n\";\n\nconst input = document.querySelector(\"#phone\");\n\nintlTelInput(input, {\n  i18n: ar,\n  // Arabic\n  countryNameLocale: \"ar\",\n  // Egypt\n  initialCountry: \"eg\",\n  loadUtils: () => import(\"intl-tel-input/utils\"),\n});\n"
  },
  {
    "path": "site/src/examples/js/simple_init_plugin.js",
    "content": "const input = document.querySelector(\"#phone\");\nwindow.intlTelInput(input, {\n  initialCountry: \"us\",\n  loadUtils: () => import(\"<%= cacheBust('/intl-tel-input/js/utils.js') %>\"),\n  searchInputClass: \"form-control\",\n});\n"
  },
  {
    "path": "site/src/examples/js/simple_init_plugin_display_code.js",
    "content": "import intlTelInput from \"intl-tel-input\";\n\nconst input = document.querySelector(\"#phone\");\nintlTelInput(input, {\n  initialCountry: \"us\",\n  loadUtils: () => import(\"intl-tel-input/utils\"),\n});\n"
  },
  {
    "path": "site/src/examples/js/single_country.js",
    "content": "const form = document.querySelector(\"#form\");\nconst input = document.querySelector(\"#phone\");\nconst errorMsg = document.querySelector(\"#error-msg\");\nconst validMsg = document.querySelector(\"#valid-msg\");\n\n// here, the index maps to the error code returned from getValidationError - see readme\nconst errorMap = [\n  \"Invalid number\",\n  \"Invalid country code\",\n  \"Too short\",\n  \"Too long\",\n  \"Invalid number\",\n];\n\n// initialise plugin\nconst iti = window.intlTelInput(input, {\n  onlyCountries: [\"us\"],\n  allowDropdown: false,\n  showFlags: false,\n  loadUtils: () => import(\"<%= cacheBust('/intl-tel-input/js/utils.js') %>\"),\n});\n\nlet showValidation = false;\nlet submitted = false;\n\nconst setText = (el, text) => el.textContent = text;\n\nconst updateUI = () => {\n  if (!showValidation) {\n    return;\n  }\n\n  // once showValidation is true, we always show the validity state (via the input class and message below), so keep it up-to-date here\n  const value = input.value.trim();\n  const isValid = Boolean(value) && iti.isValidNumber();\n\n  input.classList.toggle(\"is-valid\", isValid);\n  input.classList.toggle(\"is-invalid\", !isValid);\n\n  let invalidMsg = \"\";\n  if (!isValid) {\n    const errorCode = iti.getValidationError();\n    invalidMsg = value\n      ? errorMap[errorCode || 0] || \"Invalid number\"\n      : \"Please enter a number\";\n  }\n  setText(errorMsg, invalidMsg);\n\n  const showValid = Boolean(value) && isValid && submitted;\n  setText(validMsg, showValid ? `Full number: ${iti.getNumber()}` : \"\");\n};\n\n// on submit: validate (and show \"Full number\" if valid)\nform.addEventListener(\"submit\", (e) => {\n  e.preventDefault();\n  showValidation = true;\n  submitted = true;\n  updateUI();\n});\n\n// on blur: enable validation UI\ninput.addEventListener(\"blur\", () => {\n  showValidation = true;\n  updateUI();\n});\n\n// while typing / pasting / changing country: remove any submitted state and update validity state\nconst handleUserInput = () => {\n  submitted = false;\n  updateUI();\n};\n\ninput.addEventListener(\"input\", handleUserInput);\ninput.addEventListener(\"countrychange\", handleUserInput);"
  },
  {
    "path": "site/src/examples/js/single_country_display_code.js",
    "content": "import intlTelInput from \"intl-tel-input\";\n\nconst form = document.querySelector(\"#form\");\nconst input = document.querySelector(\"#phone\");\nconst errorMsg = document.querySelector(\"#error-msg\");\n\n// initialise plugin\nconst iti = intlTelInput(input, {\n  onlyCountries: [\"us\"],\n  allowDropdown: false,\n  showFlags: false,\n  loadUtils: () => import(\"intl-tel-input/utils\"),\n});\n\nlet showValidation = false;\n\nconst updateUI = () => {\n  if (!showValidation) return;\n\n  let invalidMsg = \"\";\n  if (!iti.isValidNumber()) {\n    const errorCode = iti.getValidationError();\n    invalidMsg = yourCodeToDeriveErrorMessage(input.value(), errorCode);\n  }\n  errorMsg.textContent = invalidMsg;\n};\n\n// on submit: enable validation UI\nform.addEventListener(\"submit\", (e) => {\n  e.preventDefault();\n  showValidation = true;\n  updateUI();\n});\n\n// on blur: enable validation UI\ninput.addEventListener(\"blur\", () => {\n  showValidation = true;\n  updateUI();\n});\n\n// while typing / pasting / changing country: update validity state\ninput.addEventListener(\"input\", updateUI);\ninput.addEventListener(\"countrychange\", updateUI);\n"
  },
  {
    "path": "site/src/examples/js/svelte_component.svelte",
    "content": "<script>\n  import IntlTelInput from \"../../../../svelte/src/intl-tel-input/IntlTelInput.svelte\";\n\n  const errorMap = [\n    \"Invalid number\",\n    \"Invalid country code\",\n    \"Too short\",\n    \"Too long\",\n    \"Invalid number\",\n  ];\n\n  let number = $state(\"\");\n  let isValid = $state(false);\n  let errorCode = $state(0);\n  let showValidation = $state(false);\n  let submitted = $state(false);\n\n  const inputValidityClass = $derived.by(() => {\n    if (!showValidation) return \"\";\n    return number && isValid ? \"is-valid\" : \"is-invalid\";\n  });\n\n  const invalidMsg = $derived.by(() => {\n    if (!showValidation || isValid) return null;\n    return number\n      ? errorMap[errorCode || 0] || \"Invalid number\"\n      : \"Please enter a number\";\n  });\n\n  const validMsg = $derived.by(() => {\n    const showValid = showValidation && number && isValid && submitted;\n    return showValid ? `Full number: ${number}` : null;\n  });\n\n  const handleChangeNumber = (newNumber) => {\n    submitted = false;\n    number = newNumber;\n  };\n\n  const handleSubmit = (event) => {\n    event.preventDefault();\n    showValidation = true;\n    submitted = true;\n  };\n</script>\n\n<form onsubmit={handleSubmit} class=\"row g-2\" novalidate>\n  <div class=\"col-auto\">\n    <IntlTelInput\n      onChangeNumber={handleChangeNumber}\n      onChangeValidity={(v) => (isValid = v)}\n      onChangeErrorCode={(e) => (errorCode = e)}\n      options={{\n        initialCountry: \"us\",\n        loadUtils: () => import(\"<%= cacheBust('/intl-tel-input/js/utils.js') %>\"),\n        searchInputClass: \"form-control\",\n      }}\n      inputProps={{\n        name: \"phone\",\n        title: \"Enter your phone number\",\n        required: true,\n        onblur: () => (showValidation = true),\n        class: `form-control ${inputValidityClass}`,\n      }}\n    />\n    {#if invalidMsg}\n      <div class=\"invalid-feedback d-block\">{invalidMsg}</div>\n    {/if}\n    {#if validMsg}\n      <div class=\"valid-feedback d-block\">{validMsg}</div>\n    {/if}\n  </div>\n  <div class=\"col-auto\">\n    <button class=\"btn btn-primary\" type=\"submit\">Submit</button>\n  </div>\n</form>\n"
  },
  {
    "path": "site/src/examples/js/svelte_component_display_code.svelte",
    "content": "<script>\n  import IntlTelInput from \"intl-tel-input/svelteWithUtils\";\n  import \"intl-tel-input/styles\";\n\n  let number = $state(\"\");\n  let isValid = $state(false);\n  let errorCode = $state(0);\n  let showValidation = $state(false);\n\n  const invalidMsg = $derived.by(() => {\n    // your logic to derive invalid message\n  });\n\n  const handleSubmit = (event) => {\n    event.preventDefault();\n    showValidation = true;\n  };\n</script>\n\n<form onsubmit={handleSubmit}>\n  <IntlTelInput\n    onChangeNumber={(n) => (number = n)}\n    onChangeValidity={(v) => (isValid = v)}\n    onChangeErrorCode={(e) => (errorCode = e)}\n    options={{\n      initialCountry: \"us\",\n      loadUtils: () => import(\"intl-tel-input/utils\"),\n    }}\n    inputProps={{\n      onblur: () => (showValidation = true),\n    }}\n  />\n  <button type=\"submit\">Submit</button>\n  {#if invalidMsg}\n    <div class=\"invalid\">{invalidMsg}</div>\n  {/if}\n</form>\n"
  },
  {
    "path": "site/src/examples/js/svelte_main.js",
    "content": "import App from \"../../../tmp/examples/js/svelte_component.svelte\";\n\nimport { mount } from \"svelte\";\n\nmount(App, { target: document.getElementById(\"app\") });\n"
  },
  {
    "path": "site/src/examples/js/validation.js",
    "content": "const form = document.querySelector(\"#form\");\nconst input = document.querySelector(\"#phone\");\nconst errorMsg = document.querySelector(\"#error-msg\");\nconst validMsg = document.querySelector(\"#valid-msg\");\n\n// here, the index maps to the error code returned from getValidationError - see readme\nconst errorMap = [\n  \"Invalid number\",\n  \"Invalid country code\",\n  \"Too short\",\n  \"Too long\",\n  \"Invalid number\",\n];\n\n// initialise plugin\nconst iti = window.intlTelInput(input, {\n  initialCountry: \"us\",\n  loadUtils: () => import(\"<%= cacheBust('/intl-tel-input/js/utils.js') %>\"),\n  searchInputClass: \"form-control\",\n});\n\nlet showValidation = false;\nlet submitted = false;\n\nconst setText = (el, text) => el.textContent = text;\n\nconst updateUI = () => {\n  if (!showValidation) {\n    return;\n  }\n\n  // once showValidation is true, we always show the validity state (via the input class and message below), so keep it up-to-date here\n  const value = input.value.trim();\n  const isValid = Boolean(value) && iti.isValidNumber();\n\n  input.classList.toggle(\"is-valid\", isValid);\n  input.classList.toggle(\"is-invalid\", !isValid);\n\n  let invalidMsg = \"\";\n  if (!isValid) {\n    const errorCode = iti.getValidationError();\n    invalidMsg = value\n      ? errorMap[errorCode || 0] || \"Invalid number\"\n      : \"Please enter a number\";\n  }\n  setText(errorMsg, invalidMsg);\n\n  const showValid = Boolean(value) && isValid && submitted;\n  setText(validMsg, showValid ? `Full number: ${iti.getNumber()}` : \"\");\n};\n\n// on submit: validate (and show \"Full number\" if valid)\nform.addEventListener(\"submit\", (e) => {\n  e.preventDefault();\n  showValidation = true;\n  submitted = true;\n  updateUI();\n});\n\n// on blur: enable validation UI\ninput.addEventListener(\"blur\", () => {\n  showValidation = true;\n  updateUI();\n});\n\n// while typing / pasting / changing country: remove any submitted state and update validity state\nconst handleUserInput = () => {\n  submitted = false;\n  updateUI();\n};\n\ninput.addEventListener(\"input\", handleUserInput);\ninput.addEventListener(\"countrychange\", handleUserInput);\n"
  },
  {
    "path": "site/src/examples/js/validation_display_code.js",
    "content": "import intlTelInput from \"intl-tel-input\";\n\nconst form = document.querySelector(\"#form\");\nconst input = document.querySelector(\"#phone\");\nconst errorMsg = document.querySelector(\"#error-msg\");\n\n// initialise plugin\nconst iti = intlTelInput(input, {\n  initialCountry: \"us\",\n  loadUtils: () => import(\"intl-tel-input/utils\"),\n});\n\nlet showValidation = false;\n\nconst updateUI = () => {\n  if (!showValidation) return;\n\n  let invalidMsg = \"\";\n  if (!iti.isValidNumber()) {\n    const errorCode = iti.getValidationError();\n    invalidMsg = yourCodeToDeriveErrorMessage(input.value(), errorCode);\n  }\n  errorMsg.textContent = invalidMsg;\n};\n\n// on submit: enable validation UI\nform.addEventListener(\"submit\", (e) => {\n  e.preventDefault();\n  showValidation = true;\n  updateUI();\n});\n\n// on blur: enable validation UI\ninput.addEventListener(\"blur\", () => {\n  showValidation = true;\n  updateUI();\n});\n\n// while typing / pasting / changing country: update validity state\ninput.addEventListener(\"input\", updateUI);\ninput.addEventListener(\"countrychange\", updateUI);\n"
  },
  {
    "path": "site/src/examples/js/viteSvelteDemo.config.mjs",
    "content": "// needed for svelte component demo page\n\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { defineConfig } from \"vite\";\nimport { svelte } from \"@sveltejs/vite-plugin-svelte\";\n\nconst projectRoot = fileURLToPath(new URL(\"../../..\", import.meta.url));\n\nexport default defineConfig({\n  define: {\n    \"process.env.VERSION\": \"window.IGNORE_ME\", // just to stop runtime errors\n    \"process.env.NODE_ENV\": \"'production'\", // required for vue files\n  },\n  build: {\n    outDir: path.resolve(projectRoot, \"build/examples/js\"),\n    emptyOutDir: false,\n    copyPublicDir: false,\n    rollupOptions: {\n      // stop vite/rollup from complaining about the dynamic import of utils.js\n      external: (id) => id.startsWith(\"/intl-tel-input/js/utils.js\"),\n    },\n    lib: {\n      entry: path.resolve(projectRoot, \"src/examples/js/svelte_main.js\"),\n      formats: [\"cjs\"],\n      fileName: \"svelte_component_bundle\",\n    },\n  },\n  plugins: [svelte()],\n});\n"
  },
  {
    "path": "site/src/examples/js/viteVueDemo.config.js",
    "content": "// needed for vue component demo page\n\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { defineConfig } from \"vite\";\nimport vue from \"@vitejs/plugin-vue\";\n\nconst projectRoot = fileURLToPath(new URL(\"../../..\", import.meta.url));\n\nexport default defineConfig({\n  define: {\n    \"process.env.VERSION\": \"window.IGNORE_ME\", // just to stop runtime errors\n    \"process.env.NODE_ENV\": \"'production'\", // required for vue files\n  },\n  build: {\n    outDir: path.resolve(projectRoot, \"build/examples/js\"),\n    emptyOutDir: false,\n    copyPublicDir: false,\n    lib: {\n      entry: path.resolve(projectRoot, \"src/examples/js/vue_main.js\"),\n      formats: [\"cjs\"],\n      fileName: \"vue_component_bundle\",\n    },\n    rollupOptions: {\n      external: [/utils\\.js/],\n    },\n  },\n  plugins: [vue()],\n});\n"
  },
  {
    "path": "site/src/examples/js/vue_component.vue",
    "content": "<script setup>\n  import { computed, ref } from \"vue\";\n  import IntlTelInput from \"../../../build/intl-tel-input/vue/exports/IntlTelInput.mjs\";\n\n  const errorMap = [\n    \"Invalid number\",\n    \"Invalid country code\",\n    \"Too short\",\n    \"Too long\",\n    \"Invalid number\",\n  ];\n\n  const number = ref(\"\");\n  const isValid = ref(false);\n  const errorCode = ref(0);\n  const showValidation = ref(false);\n  const submitted = ref(false);\n\n  const inputValidityClass = computed(() => {\n    if (!showValidation.value) return \"\";\n    return number.value && isValid.value ? \"is-valid\" : \"is-invalid\";\n  });\n\n  const invalidMsg = computed(() => {\n    if (!showValidation.value || isValid.value) return null;\n    return number.value\n      ? errorMap[errorCode.value || 0] || \"Invalid number\"\n      : \"Please enter a number\";\n  });\n\n  const validMsg = computed(() => {\n    const showValid =\n      showValidation.value && number.value && isValid.value && submitted.value;\n    return showValid ? `Full number: ${number.value}` : null;\n  });\n\n  const handleChangeNumber = (newNumber) => {\n    submitted.value = false;\n    number.value = newNumber;\n  };\n\n  const handleSubmit = () => {\n    showValidation.value = true;\n    submitted.value = true;\n  };\n</script>\n\n<template>\n  <form @submit.prevent=\"handleSubmit\" class=\"row g-2\" novalidate>\n    <div class=\"col-auto\">\n      <div>\n        <IntlTelInput\n          @changeNumber=\"handleChangeNumber\"\n          @changeValidity=\"isValid = $event\"\n          @changeErrorCode=\"errorCode = $event\"\n          :options='{\n            initialCountry: \"us\",\n            loadUtils: () => import(\"<%= cacheBust(`/intl-tel-input/js/utils.js`) %>\"),\n            searchInputClass: \"form-control\",\n          }'\n          :inputProps=\"{\n            name: 'phone',\n            title: 'Enter your phone number',\n            required: true,\n            onBlur: () => (showValidation.value = true),\n            class: `form-control ${inputValidityClass}`,\n          }\"\n        />\n        <div v-if=\"invalidMsg\" class=\"invalid-feedback d-block\">{{ invalidMsg }}</div>\n        <div v-if=\"validMsg\" class=\"valid-feedback d-block\">{{ validMsg }}</div>\n      </div>\n    </div>\n    <div class=\"col-auto\">\n      <button class=\"btn btn-primary\" type=\"submit\">Submit</button>\n    </div>\n  </form>\n</template>\n"
  },
  {
    "path": "site/src/examples/js/vue_component_display_code.vue",
    "content": "<script setup>\n  import { computed, ref } from \"vue\";\n  import IntlTelInput from \"intl-tel-input/vue\";\n  import \"intl-tel-input/styles\";\n\n  const number = ref(\"\");\n  const isValid = ref(false);\n  const errorCode = ref(0);\n  const showValidation = ref(false);\n\n  const invalidMsg = computed(() => {\n    // your logic to derive the invalid message\n  });\n\n  const enableValidation = () => {\n    showValidation.value = true;\n  };\n</script>\n\n<template>\n  <form @submit.prevent=\"enableValidation\">\n    <IntlTelInput\n      @changeNumber=\"handleChangeNumber\"\n      @changeValidity=\"isValid = $event\"\n      @changeErrorCode=\"errorCode = $event\"\n      :options=\"{\n        initialCountry: 'us',\n        loadUtils: () => import('intl-tel-input/utils'),\n      }\"\n      :inputProps=\"{\n        onBlur: enableValidation,\n      }\"\n    />\n    <button type=\"submit\">Submit</button>\n    <div v-if=\"invalidMsg\" class=\"invalid\">{{ invalidMsg }}</div>\n  </form>\n</template>"
  },
  {
    "path": "site/src/examples/js/vue_main.js",
    "content": "import { createApp } from \"vue\";\nimport App from \"../../../tmp/examples/js/vue_component.vue\";\n\ncreateApp(App).mount(\"#app\");\n"
  },
  {
    "path": "site/src/homepage/homepage_content.html",
    "content": "<div class=\"d-flex flex-column align-items-center text-center homepage-content\">\n  <div class=\"section section--homepage-demo demo d-flex flex-column align-items-center\">\n    <h1>International<br><span class=\"keep-together\">Telephone Input</span></h1>\n    <p class=\"subtitle text-muted\">A JavaScript plugin for entering, formatting and validating international telephone numbers. Includes TypeScript definitions, plus <a href=\"/docs/react-component\">React</a>, <a href=\"/docs/vue-component\">Vue</a>, <a href=\"/docs/angular-component\">Angular</a> and <a href=\"/docs/svelte-component\">Svelte</a> components.</p>\n    <div>\n      <input type=\"tel\" id=\"phone\" class=\"form-control\" title=\"Enter your phone number\" />\n      <div class=\"iti-live-results\">Type a number above to see the output here</div>\n    </div>\n    <a href=\"/docs/choose-integration\" class=\"btn btn-primary btn-lg d-inline-flex align-items-center\">\n      <svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        width=\"16\"\n        height=\"16\"\n        fill=\"currentColor\"\n        class=\"bi bi-book-half me-2\"\n        viewBox=\"0 0 16 16\"\n        aria-hidden=\"true\"\n        focusable=\"false\"\n      >\n        <path d=\"M8.5 2.687c.654-.689 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783\" />\n      </svg>\n      Get started\n    </a>\n  </div>\n\n  <div class=\"section section--twilio\">\n    <h2>Sponsored by</h2>\n    <img src=\"/img/twilio.png\" height=\"100\" alt=\"Twilio logo\" />\n    <p>\n      Use\n      <a\n        href=\"https://www.twilio.com/blog/international-telephone-input-twilio?utm_source=intl-tel-input.com&utm_medium=referral&utm_campaign=intl_tel_input\"\n        >Twilio's API to build phone verification, SMS 2FA, appointment reminders,\n        marketing notifications and so much more</a\n      >. We can't wait to see what you build.\n    </p>\n  </div>\n\n  <div class=\"section section--social-proof\">\n    <h2>Trusted by teams worldwide</h2>\n    <div class=\"social-proof-logos\">\n      <img class=\"logo\" src=\"/img/logos/nike.png\" alt=\"Nike\" />\n      <img class=\"logo\" src=\"/img/logos/dell.svg\" alt=\"Dell\" />\n      <img class=\"logo\" src=\"/img/logos/universal-music.png\" alt=\"Universal Music\" />\n      <span class=\"logo logo-pair\">\n        <img src=\"/img/logos/ubuntu_logo.svg\" alt=\"Ubuntu logo\" />\n        <img src=\"/img/logos/ubuntu_text.svg\" alt=\"Ubuntu text\" />\n      </span>\n      <img class=\"logo\" src=\"/img/logos/forbes.svg\" alt=\"Forbes\" />\n      <span class=\"logo logo-pair\">\n        <img src=\"/img/logos/quickbooks_logo.svg\" alt=\"Intuit QuickBooks logo\" />\n        <img src=\"/img/logos/quickbooks_text.svg\" alt=\"Intuit QuickBooks text\" />\n      </span>\n      <img class=\"logo\" src=\"/img/logos/unicef.png\" alt=\"UNICEF\" />\n      <img class=\"logo\" src=\"/img/logos/ikea.svg\" alt=\"IKEA\" />\n    </div>\n  </div>\n\n  <div class=\"section section--stats\">\n    <div class=\"stats-row\">\n      <div class=\"stat\">\n        <span class=\"stat-number\">{{STAT_WEBSITES}}</span>\n        <span class=\"stat-label\">websites</span>\n      </div>\n      <div class=\"stat\">\n        <span class=\"stat-number\">{{STAT_DOWNLOADS}}</span>\n        <span class=\"stat-label\">npm downloads/month</span>\n      </div>\n      <div class=\"stat\">\n        <span class=\"stat-number\">{{STAT_STARS}}</span>\n        <span class=\"stat-label\">GitHub stars</span>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"section section--features\">\n    <h2>Features</h2>\n\n    <div class=\"row g-4 text-start mt-2\">\n      <div class=\"col-12 col-md-6 col-lg-4\">\n        <div class=\"card bg-transparent h-100\">\n          <div class=\"card-body\">\n            <div class=\"d-flex align-items-start gap-3\">\n              <i class=\"bi bi-search fs-5 lh-1 flex-shrink-0 link-icon\" aria-hidden=\"true\"></i>\n              <div>\n                <h3 class=\"h5 mb-2\">Fast country picking</h3>\n                <ul class=\"mb-0\">\n                  <li>Search by country name or dial code</li>\n                  <li>Full keyboard navigation</li>\n                </ul>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"col-12 col-md-6 col-lg-4\">\n        <div class=\"card bg-transparent h-100\">\n          <div class=\"card-body\">\n            <div class=\"d-flex align-items-start gap-3\">\n              <i class=\"bi bi-stars fs-5 lh-1 flex-shrink-0 link-icon\" aria-hidden=\"true\"></i>\n              <div>\n                <h3 class=\"h5 mb-2\">Smart defaults</h3>\n                <ul class=\"mb-0\">\n                  <li>Optionally auto-detect the user's country</li>\n                  <li>Example placeholders per country</li>\n                </ul>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"col-12 col-md-6 col-lg-4\">\n        <div class=\"card bg-transparent h-100\">\n          <div class=\"card-body\">\n            <div class=\"d-flex align-items-start gap-3\">\n              <i class=\"bi bi-telephone fs-5 lh-1 flex-shrink-0 link-icon\" aria-hidden=\"true\"></i>\n              <div>\n                <h3 class=\"h5 mb-2\">Formatting & output</h3>\n                <ul class=\"mb-0\">\n                  <li>Formats the number as the user types</li>\n                  <li>Extract standard E.164 numbers to store</li>\n                </ul>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"col-12 col-md-6 col-lg-4\">\n        <div class=\"card bg-transparent h-100\">\n          <div class=\"card-body\">\n            <div class=\"d-flex align-items-start gap-3\">\n              <i class=\"bi bi-shield-check fs-5 lh-1 flex-shrink-0 link-icon\" aria-hidden=\"true\"></i>\n              <div>\n                <h3 class=\"h5 mb-2\">Validation</h3>\n                <ul class=\"mb-0\">\n                  <li>Validate numbers with specific error types</li>\n                  <li>Strict mode: only allow users to type valid digits and enforce max length</li>\n                </ul>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"col-12 col-md-6 col-lg-4\">\n        <div class=\"card bg-transparent h-100\">\n          <div class=\"card-body\">\n            <div class=\"d-flex align-items-start gap-3\">\n              <i class=\"bi bi-globe fs-5 lh-1 flex-shrink-0 link-icon\" aria-hidden=\"true\"></i>\n              <div>\n                <h3 class=\"h5 mb-2\">International &amp; accessible</h3>\n                <ul class=\"mb-0\">\n                  <li>Translated into 40+ languages, with support for RTL and alternative numerals</li>\n                  <li>Screen reader-friendly ARIA markup</li>\n                </ul>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"col-12 col-md-6 col-lg-4\">\n        <div class=\"card bg-transparent h-100\">\n          <div class=\"card-body\">\n            <div class=\"d-flex align-items-start gap-3\">\n              <i class=\"bi bi-sliders fs-5 lh-1 flex-shrink-0 link-icon\" aria-hidden=\"true\"></i>\n              <div>\n                <h3 class=\"h5 mb-2\">Customisable</h3>\n                <ul class=\"mb-0\">\n                  <li>Override CSS variables (e.g. dark mode)</li>\n                  <li>Optionally display the dial code next to the number</li>\n                  <li>Extensive options, methods, and events</li>\n                </ul>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"section section--playground-presets\">\n    <h2>Playground presets</h2>\n    <p class=\"text-muted mb-3\">\n      Jump straight into the <a href=\"/playground\">Playground</a> with some common configurations.\n    </p>\n\n    <div class=\"d-flex flex-wrap justify-content-center gap-2 playground-presets\">\n      <a class=\"btn btn-outline-primary btn-sm\" href=\"/playground?initialCountry=auto&geoIpLookup=true#country-options\">Auto country lookup</a>\n      <a class=\"btn btn-outline-primary btn-sm\" href=\"/playground?strictMode=true&initialCountry=us#formatting-options\">Strict numeric mode</a>\n      <a class=\"btn btn-outline-primary btn-sm\" href=\"/playground?separateDialCode=true&initialCountry=gb#user-interface-options\">Separate dial code</a>\n      <a class=\"btn btn-outline-primary btn-sm\" href=\"/playground?keepDropdownOpen=false&allowDropdown=false&initialCountry=us#user-interface-options\">No dropdown</a>\n      <a class=\"btn btn-outline-primary btn-sm\" href=\"/playground?initialCountry=fr&onlyCountries=%5B%22al%22%2C%22ad%22%2C%22at%22%2C%22by%22%2C%22be%22%2C%22ba%22%2C%22bg%22%2C%22hr%22%2C%22cz%22%2C%22dk%22%2C%22ee%22%2C%22fo%22%2C%22fi%22%2C%22fr%22%2C%22de%22%2C%22gi%22%2C%22gr%22%2C%22va%22%2C%22hu%22%2C%22is%22%2C%22ie%22%2C%22it%22%2C%22lv%22%2C%22li%22%2C%22lt%22%2C%22lu%22%2C%22mk%22%2C%22mt%22%2C%22md%22%2C%22mc%22%2C%22me%22%2C%22nl%22%2C%22no%22%2C%22pl%22%2C%22pt%22%2C%22ro%22%2C%22ru%22%2C%22sm%22%2C%22rs%22%2C%22sk%22%2C%22si%22%2C%22es%22%2C%22se%22%2C%22ch%22%2C%22ua%22%2C%22gb%22%5D#country-options\">Only European countries</a>\n      <a class=\"btn btn-outline-primary btn-sm\" href=\"/playground?countryNameLocale=zh&i18n=zh&initialCountry=cn#translation-options\">Chinese UI</a>\n    </div>\n\n    <p class=\"text-muted mt-3 mb-0\">\n      Or browse the <a href=\"/docs/choose-integration\">docs</a> and <a href=\"/examples/validation-practical\">examples</a>.\n    </p>\n  </div>\n\n  <div class=\"section\">\n    <h2>Testing</h2>\n    <p>\n      Browser testing via <a href=\"https://www.lambdatest.com/\"><img src=\"/img/lambda-test-blue-logo.svg\" style=\"vertical-align:middle;margin-left:5px\" width=\"147\" height=\"26\" class=\"lambda-logo\" alt=\"LambdaTest logo\" /></a>\n    </p>\n  </div>\n</div>\n"
  },
  {
    "path": "site/src/homepage/homepage_page_template.html.ejs",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <%= common_meta_tags %>\n    <title><%= homepageTitle %></title>\n    <link rel=\"canonical\" href=\"<%= homepageCanonicalUrl %>\" />\n    <meta name=\"description\" content=\"<%= homepageMetaDesc %>\" />\n    <%= og_meta_tags %>\n    <%= common_styles %>\n    <link rel=\"stylesheet\" href=\"<%= cacheBust('/css/homepage.css') %>\" />\n\n    <%= common_head_end_prod %>\n  </head>\n\n  <body class=\"iti-page iti-page--homepage\">\n    <%= layout %>\n    <%= common_body_end %>\n    <%= iti_script %>\n    <script src=\"<%= cacheBust('/js/homepage.js') %>\"></script>\n    <%= iti_live_results_script %>\n  </body>\n</html>\n"
  },
  {
    "path": "site/src/js/homepage.js",
    "content": "const input = document.querySelector(\"#phone\");\nwindow.intlTelInput(input, {\n  searchInputClass: \"form-control\",\n  initialCountry: \"auto\",\n  geoIpLookup: (success, failure) => {\n    fetch(`https://ipapi.co/json?token=${process.env.IPAPI_TOKEN}`)\n      .then(res => res.json())\n      .then(data => success(data.country_code))\n      .catch(() => failure());\n  },\n  loadUtils: () => import(\"/intl-tel-input/js/utils.js\"),\n});\n"
  },
  {
    "path": "site/src/js/iti-live-results.js",
    "content": "(() => {\n  const errorMap = [\"\", \"Bad country code\", \"Too short\", \"Too long\"];\n  const getItiInstance = () => Object.values(window.intlTelInput?.instances ?? {})[0];\n\n  const init = () => {\n    const liveResults = document.querySelector(\".iti-live-results\");\n    if (!liveResults || !window.intlTelInput || !getItiInstance()) return;\n\n    // Fix live results box width and height to prevent layout shift\n    const liveResultsStyle = getComputedStyle(liveResults);\n    if (liveResultsStyle.width) liveResults.style.width = liveResultsStyle.width;\n    if (liveResultsStyle.height) liveResults.style.height = liveResultsStyle.height;\n\n    const setupLiveResults = () => {\n      const itiInput = document.querySelector(\".iti__tel-input\");\n      if (!itiInput) return;\n\n      const emptyMessage = liveResults.textContent || \"\";\n\n      const updateResults = () => {\n        const hasValue = itiInput.value.trim().length > 0;\n        if (!hasValue) {\n          liveResults.textContent = emptyMessage;\n          return;\n        }\n\n        const iti = getItiInstance();\n        if (iti.isValidNumber()) {\n          const number = iti.getNumber();\n          liveResults.textContent = `Valid number: ${number}`;\n          return;\n        }\n\n        const errorCode = iti.getValidationError();\n        const errorMessage = errorMap[errorCode] || \"\";\n        const errorSuffix = errorMessage ? ` (${errorMessage})` : \"\";\n        liveResults.textContent = `Invalid number${errorSuffix}`;\n      };\n\n      itiInput.addEventListener(\"input\", updateResults);\n      itiInput.addEventListener(\"countrychange\", updateResults);\n      updateResults();\n    };\n\n    const iti = getItiInstance();\n    if (iti.promise && typeof iti.promise.then === \"function\") {\n      iti.promise.then(setupLiveResults);\n    } else {\n      setupLiveResults();\n    }\n  };\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\"DOMContentLoaded\", init, { once: true });\n  } else {\n    init();\n  }\n})();\n"
  },
  {
    "path": "site/src/layout_template.html.ejs",
    "content": "<!-- HEADER -->\n<header class=\"navbar navbar-expand-lg navbar-dark iti-navbar sticky-top\">\n  <nav class=\"container-xxl iti-gutter flex-wrap flex-lg-nowrap iti-navbar-container\">\n\n    <!-- HEADER MENU TOGGLE BUTTON (MOBILE: VISIBLE, DESKTOP: HIDDEN) -->\n    <div class=\"iti-navbar-toggle\">\n      <button\n        class=\"navbar-toggler\"\n        aria-label=\"Open navigation menu\"\n        type=\"button\"\n        data-bs-toggle=\"offcanvas\"\n        data-bs-target=\"#itiMobileSidebar\"\n      >\n        <svg\n          xmlns=\"http://www.w3.org/2000/svg\"\n          width=\"24\"\n          height=\"24\"\n          class=\"bi\"\n          fill=\"white\"\n          viewBox=\"0 0 16 16\"\n        >\n          <path\n            fill-rule=\"evenodd\"\n            d=\"M2.5 11.5A.5.5 0 0 1 3 11h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4A.5.5 0 0 1 3 3h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z\"\n          ></path>\n        </svg>\n      </button>\n    </div>\n\n    <!-- HEADER BRAND (MOBILE: CENTERED, DESKTOP: LHS) -->\n    <a class=\"navbar-brand me-0 d-flex align-items-center\" href=\"/\">\n      <img src=\"/img/logo-white.png\" alt=\"intl-tel-input logo\" width=\"25\" height=\"25\" class=\"iti-logo\">\n      intl-tel-input\n    </a>\n\n    <!-- HEADER SEARCH TRIGGER (MOBILE: RHS ICON, DESKTOP: HIDDEN) -->\n    <div class=\"d-flex align-items-center\">\n      <button\n        type=\"button\"\n        id=\"itiSearchButtonMobile\"\n        class=\"iti-navbar-icon-btn d-lg-none\"\n        aria-label=\"Search\"\n        data-iti-search-trigger=\"mobile\"\n      >\n        <svg\n          xmlns=\"http://www.w3.org/2000/svg\"\n          width=\"24\"\n          height=\"24\"\n          class=\"bi\"\n          fill=\"white\"\n          viewBox=\"0 0 16 16\"\n          aria-hidden=\"true\"\n          focusable=\"false\"\n        >\n          <path d=\"M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0\" />\n        </svg>\n      </button>\n    </div>\n\n    <!-- HEADER RHS NAV (MOBILE: HIDDEN, DESKTOP: VISIBLE) -->\n    <div class=\"offcanvas-lg offcanvas-end flex-grow-1\" tabindex=\"-1\" id=\"bdNavbar\">\n      <div class=\"offcanvas-body p-4 pt-0 p-lg-0 d-lg-flex align-items-lg-center\">\n\n        <ul class=\"navbar-nav flex-row flex-wrap bd-navbar-nav\">\n        </ul>\n\n        <!-- HEADER SEARCH TRIGGER (DESKTOP: CENTER \"INPUT\", MOBILE: HIDDEN) -->\n        <div class=\"iti-navbar-search d-none d-lg-block\">\n          <div id=\"docsearch\"></div>\n        </div>\n\n        <ul class=\"navbar-nav flex-row flex-wrap align-items-center ms-md-auto gap-2\">\n          <li class=\"nav-item dropdown col-6 col-lg-auto\">\n            <button\n              type=\"button\"\n              class=\"nav-link dropdown-toggle d-inline-flex align-items-center gap-1 py-2 px-0 px-lg-2 bg-transparent border-0 iti-navlink-no-shift <% if (pageType === 'docs') { %>active<% } %>\"\n              data-bs-toggle=\"dropdown\"\n              aria-expanded=\"false\"\n            >\n              <span data-text=\"Docs\">Docs</span>\n            </button>\n            <ul class=\"dropdown-menu\">\n              <% docsDropdownPages.forEach((p) => { %>\n                <li>\n                  <a class=\"dropdown-item <% if (pageType === 'docs' && name === p.name) { %>active<% } %>\" href=\"<%= p.href %>\"><%= p.label %></a>\n                </li>\n              <% }); %>\n            </ul>\n          </li>\n          <li class=\"nav-item col-6 col-lg-auto\">\n            <a\n              class=\"nav-link d-inline-flex align-items-center py-2 px-0 px-lg-2 iti-navlink-no-shift <% if (pageType === 'playground') { %>active<% } %>\"\n              href=\"/playground\"\n            ><span data-text=\"Playground\">Playground</span></a>\n          </li>\n          <li class=\"nav-item dropdown col-6 col-lg-auto\">\n            <button\n              type=\"button\"\n              class=\"nav-link dropdown-toggle d-inline-flex align-items-center gap-1 py-2 px-0 px-lg-2 bg-transparent border-0 iti-navlink-no-shift <% if (pageType === 'examples') { %>active<% } %>\"\n              data-bs-toggle=\"dropdown\"\n              aria-expanded=\"false\"\n            >\n              <span data-text=\"Examples\">Examples</span>\n            </button>\n            <ul class=\"dropdown-menu\">\n              <% examplesDropdownPages.forEach((p) => { %>\n                <li>\n                  <a class=\"dropdown-item <% if (pageType === 'examples' && name === p.name) { %>active<% } %>\" href=\"<%= p.href %>\"><%= p.label %></a>\n                </li>\n              <% }); %>\n            </ul>\n          </li>\n          <li class=\"nav-item col-6 col-lg-auto\">\n            <a\n              class=\"nav-link px-lg-2 d-inline-flex align-items-center nav-link--github\"\n              href=\"https://github.com/jackocnr/intl-tel-input\"\n              rel=\"noopener\"\n            >\n              <svg\n                xmlns=\"http://www.w3.org/2000/svg\"\n                width=\"16\"\n                height=\"16\"\n                class=\"navbar-nav-svg\"\n                viewBox=\"0 0 512 499.36\"\n                role=\"img\"\n              >\n                <title>GitHub</title>\n                <path\n                  fill=\"white\"\n                  fill-rule=\"evenodd\"\n                  d=\"M256 0C114.64 0 0 114.61 0 256c0 113.09 73.34 209 175.08 242.9 12.8 2.35 17.47-5.56 17.47-12.34 0-6.08-.22-22.18-.35-43.54-71.2 15.49-86.2-34.34-86.2-34.34-11.64-29.57-28.42-37.45-28.42-37.45-23.27-15.84 1.73-15.55 1.73-15.55 25.69 1.81 39.21 26.38 39.21 26.38 22.84 39.12 59.92 27.82 74.5 21.27 2.33-16.54 8.94-27.82 16.25-34.22-56.84-6.43-116.6-28.43-116.6-126.49 0-27.95 10-50.8 26.35-68.69-2.63-6.48-11.42-32.5 2.51-67.75 0 0 21.49-6.88 70.4 26.24a242.65 242.65 0 0 1 128.18 0c48.87-33.13 70.33-26.24 70.33-26.24 14 35.25 5.18 61.27 2.55 67.75 16.41 17.9 26.31 40.75 26.31 68.69 0 98.35-59.85 120-116.88 126.32 9.19 7.9 17.38 23.53 17.38 47.41 0 34.22-.31 61.83-.31 70.23 0 6.85 4.61 14.81 17.6 12.31C438.72 464.97 512 369.08 512 256.02 512 114.62 397.37 0 256 0z\"\n                ></path>\n              </svg>\n            </a>\n          </li>\n        </ul>\n      </div>\n    </div>\n  </nav>\n</header>\n\n<!-- MOBILE NAV SIDEBAR (MOBILE: TOGGLEABLE, DESKTOP: HIDDEN) -->\n<aside class=\"iti-mobile-sidebar d-lg-none\">\n  <div class=\"offcanvas offcanvas-start\" tabindex=\"-1\" id=\"itiMobileSidebar\" aria-labelledby=\"itiMobileSidebarLabel\">\n    <div class=\"offcanvas-header border-bottom\">\n      <h5 class=\"offcanvas-title\" id=\"itiMobileSidebarLabel\">\n        <a class=\"d-flex align-items-center\" href=\"/\">\n          <img src=\"/img/logo-white.png\" alt=\"intl-tel-input logo\" width=\"25\" height=\"25\" class=\"iti-logo\">\n          intl-tel-input\n        </a>\n      </h5>\n      <button\n        type=\"button\"\n        class=\"btn-close btn-close-white\"\n        data-bs-dismiss=\"offcanvas\"\n        data-bs-target=\"#itiMobileSidebar\"\n        aria-label=\"Close navigation menu\"\n      ></button>\n    </div>\n    <div class=\"offcanvas-body flex-column\">\n\n      <ul class=\"flex-column flex-nowrap nav nav-pills nav--primary-links\" id=\"itiMobilePrimaryNav\">\n        <li class=\"nav-item\">\n          <button\n            type=\"button\"\n            class=\"nav-link iti-mobile-nav-toggle text-start w-100 d-flex align-items-center\"\n            data-bs-toggle=\"collapse\"\n            data-bs-target=\"#itiMobileDocsMenu\"\n            aria-controls=\"itiMobileDocsMenu\"\n            aria-expanded=\"<%= pageType === 'docs' ? 'true' : 'false' %>\"\n          >\n            <span class=\"d-flex align-items-center gap-2\">\n              <svg\n                xmlns=\"http://www.w3.org/2000/svg\"\n                width=\"16\"\n                height=\"16\"\n                fill=\"currentColor\"\n                class=\"bi bi-book-half\"\n                style=\"color: rgb(53 217 184)\"\n                viewBox=\"0 0 16 16\"\n                aria-hidden=\"true\"\n                focusable=\"false\"\n              >\n                <path d=\"M8.5 2.687c.654-.689 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783\" />\n              </svg>\n              <span class=\"d-inline-flex align-items-center gap-1\">\n                Docs\n                <span class=\"iti-mobile-nav-toggle__chevron\" aria-hidden=\"true\"></span>\n              </span>\n            </span>\n          </button>\n          <ul\n            class=\"iti-mobile-submenu collapse nav flex-column flex-nowrap nav-pills ms-4 mb-2 <% if (pageType === 'docs') { %>show<% } %>\"\n            id=\"itiMobileDocsMenu\"\n            data-bs-parent=\"#itiMobilePrimaryNav\"\n          >\n            <% docsDropdownPages.forEach((p) => { %>\n              <li class=\"nav-item\">\n                <a class=\"nav-link py-2 <% if (pageType === 'docs' && name === p.name) { %>active<% } %>\" href=\"<%= p.href %>\"><%= p.label %></a>\n              </li>\n            <% }); %>\n          </ul>\n        </li>\n\n        <li class=\"nav-item\">\n          <a class=\"nav-link d-flex align-items-center gap-2\" href=\"/playground\">\n            <svg\n              xmlns=\"http://www.w3.org/2000/svg\"\n              width=\"16\"\n              height=\"16\"\n              fill=\"currentColor\"\n              class=\"bi bi-joystick\"\n              style=\"color: rgb(230 68 134)\"\n              viewBox=\"0 0 16 16\"\n              aria-hidden=\"true\"\n              focusable=\"false\"\n            >\n              <path d=\"M10 2a2 2 0 0 1-1.5 1.937v5.087c.863.083 1.5.377 1.5.726 0 .414-.895.75-2 .75s-2-.336-2-.75c0-.35.637-.643 1.5-.726V3.937A2 2 0 1 1 10 2\" />\n              <path d=\"M0 9.665v1.717a1 1 0 0 0 .553.894l6.553 3.277a2 2 0 0 0 1.788 0l6.553-3.277a1 1 0 0 0 .553-.894V9.665c0-.1-.06-.19-.152-.23L9.5 6.715v.993l5.227 2.178a.125.125 0 0 1 .001.23l-5.94 2.546a2 2 0 0 1-1.576 0l-5.94-2.546a.125.125 0 0 1 .001-.23L6.5 7.708l-.013-.988L.152 9.435a.25.25 0 0 0-.152.23\" />\n            </svg>\n            Playground\n          </a>\n        </li>\n\n        <li class=\"nav-item\">\n          <button\n            type=\"button\"\n            class=\"nav-link iti-mobile-nav-toggle text-start w-100 d-flex align-items-center\"\n            data-bs-toggle=\"collapse\"\n            data-bs-target=\"#itiMobileExamplesMenu\"\n            aria-controls=\"itiMobileExamplesMenu\"\n            aria-expanded=\"<%= pageType === 'examples' ? 'true' : 'false' %>\"\n          >\n            <span class=\"d-flex align-items-center gap-2\">\n              <svg\n                xmlns=\"http://www.w3.org/2000/svg\"\n                width=\"16\"\n                height=\"16\"\n                fill=\"currentColor\"\n                class=\"bi bi-code-square\"\n                style=\"color: rgb(222 159 74)\"\n                viewBox=\"0 0 16 16\"\n                aria-hidden=\"true\"\n                focusable=\"false\"\n              >\n                <path d=\"M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z\" />\n                <path d=\"M6.854 4.646a.5.5 0 0 1 0 .708L4.207 8l2.647 2.646a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 0 1 .708 0m2.292 0a.5.5 0 0 0 0 .708L11.793 8l-2.647 2.646a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 0 0-.708 0\" />\n              </svg>\n              <span class=\"d-inline-flex align-items-center gap-1\">\n                Examples\n                <span class=\"iti-mobile-nav-toggle__chevron\" aria-hidden=\"true\"></span>\n              </span>\n            </span>\n          </button>\n          <ul\n            class=\"iti-mobile-submenu collapse nav flex-column flex-nowrap nav-pills ms-4 mb-2 <% if (pageType === 'examples') { %>show<% } %>\"\n            id=\"itiMobileExamplesMenu\"\n            data-bs-parent=\"#itiMobilePrimaryNav\"\n          >\n            <% examplesDropdownPages.forEach((p) => { %>\n              <li class=\"nav-item\">\n                <a class=\"nav-link py-2 <% if (pageType === 'examples' && name === p.name) { %>active<% } %>\" href=\"<%= p.href %>\"><%= p.label %></a>\n              </li>\n            <% }); %>\n          </ul>\n        </li>\n      </ul>\n\n      <hr class=\"border-top my-3\" />\n\n      <ul class=\"flex-column flex-nowrap nav nav-pills nav--primary-links\">\n        <li class=\"nav-item\">\n          <a\n            class=\"nav-link d-flex align-items-center gap-2\"\n            href=\"https://github.com/jackocnr/intl-tel-input\"\n            rel=\"noopener\"\n          >\n            <svg\n              xmlns=\"http://www.w3.org/2000/svg\"\n              width=\"16\"\n              height=\"16\"\n              viewBox=\"0 0 512 499.36\"\n              role=\"img\"\n              aria-hidden=\"true\"\n              focusable=\"false\"\n            >\n              <path\n                fill=\"currentColor\"\n                fill-rule=\"evenodd\"\n                d=\"M256 0C114.64 0 0 114.61 0 256c0 113.09 73.34 209 175.08 242.9 12.8 2.35 17.47-5.56 17.47-12.34 0-6.08-.22-22.18-.35-43.54-71.2 15.49-86.2-34.34-86.2-34.34-11.64-29.57-28.42-37.45-28.42-37.45-23.27-15.84 1.73-15.55 1.73-15.55 25.69 1.81 39.21 26.38 39.21 26.38 22.84 39.12 59.92 27.82 74.5 21.27 2.33-16.54 8.94-27.82 16.25-34.22-56.84-6.43-116.6-28.43-116.6-126.49 0-27.95 10-50.8 26.35-68.69-2.63-6.48-11.42-32.5 2.51-67.75 0 0 21.49-6.88 70.4 26.24a242.65 242.65 0 0 1 128.18 0c48.87-33.13 70.33-26.24 70.33-26.24 14 35.25 5.18 61.27 2.55 67.75 16.41 17.9 26.31 40.75 26.31 68.69 0 98.35-59.85 120-116.88 126.32 9.19 7.9 17.38 23.53 17.38 47.41 0 34.22-.31 61.83-.31 70.23 0 6.85 4.61 14.81 17.6 12.31C438.72 464.97 512 369.08 512 256.02 512 114.62 397.37 0 256 0z\"\n              ></path>\n            </svg>\n            Github\n          </a>\n        </li>\n      </ul>\n    </div>\n  </div>\n</aside>\n\n<!-- MAIN PAGE LAYOUT -->\n<div class=\"container-xxl iti-gutter iti-page__main mt-3 my-md-4 <%= layoutClass %>\">\n\n  <!-- LEFT SIDEBAR (DESKTOP: PAGE-SPECIFIC NAV, MOBILE: HIDDEN) -->\n  <aside class=\"iti-left-sidebar d-none <% if (showLeftSidebar) { %>d-lg-block<% } %>\">\n    <% if (typeof nav !== 'undefined') { %>\n      <%= nav %>\n    <% } %>\n  </aside>\n\n  <!-- MAIN PAGE CONTENT -->\n  <main class=\"iti-main order-1\" <% if (typeof isRtl !== 'undefined') { %>style=\"text-align:right;\"<% } %>>\n    <div class=\"iti-content\">\n      <%= content %>\n\n      <%\n        const pagesForPrevNext = pageType === 'docs'\n          ? docsDropdownPages\n          : (pageType === 'examples' ? examplesDropdownPages : null);\n        const activeIndex = pagesForPrevNext\n          ? pagesForPrevNext.findIndex((p) => p.name === name)\n          : -1;\n        const prevPage = (pagesForPrevNext && activeIndex > 0)\n          ? pagesForPrevNext[activeIndex - 1]\n          : null;\n        const nextPage = (pagesForPrevNext && activeIndex >= 0 && activeIndex < pagesForPrevNext.length - 1)\n          ? pagesForPrevNext[activeIndex + 1]\n          : null;\n      %>\n      <% if ((prevPage || nextPage) && pagesForPrevNext) { %>\n        <nav class=\"d-flex justify-content-between mt-5 pt-4 border-top\" aria-label=\"Page navigation\">\n          <div>\n            <% if (prevPage) { %>\n              <a href=\"<%= prevPage.href %>\">&larr; <%= prevPage.label %></a>\n            <% } %>\n          </div>\n          <div class=\"text-end\">\n            <% if (nextPage) { %>\n              <a href=\"<%= nextPage.href %>\"><%= nextPage.label %> &rarr;</a>\n            <% } %>\n          </div>\n        </nav>\n      <% } %>\n    </div>\n  </main>\n\n  <!-- RIGHT SIDEBAR (MOBILE: HIDDEN, DESKTOP: DEPENDS ON layoutClass) -->\n  <aside class=\"iti-right-sidebar\">\n    <% if (typeof show_right_sidebar_ad !== 'undefined' && show_right_sidebar_ad) { %>\n      <ins class=\"adsbygoogle\"\n        style=\"display:block\"\n        data-ad-client=\"ca-pub-1090343328224651\"\n        data-ad-slot=\"1699358650\"\n        data-ad-format=\"auto\"\n        data-full-width-responsive=\"true\"></ins>\n      <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>\n    <% } %>\n  </aside>\n</div>\n"
  },
  {
    "path": "site/src/playground/js/modules/clipboard.js",
    "content": "function copyTextToClipboard(text) {\n  const value = String(text || \"\");\n  if (!value) return Promise.resolve(false);\n  if (!navigator.clipboard || !window.isSecureContext) return Promise.resolve(false);\n  return navigator.clipboard.writeText(value).then(\n    () => true,\n    () => false,\n  );\n}\n\nexport function bindCopyCodeButton(buttonEl, codeEl) {\n  if (!buttonEl || !codeEl) return;\n\n  let copiedResetTimer = null;\n  const labelEl = buttonEl.querySelector(\"[data-role=\\\"label\\\"]\") || buttonEl;\n\n  buttonEl.addEventListener(\"click\", () => {\n    const originalLabel = labelEl.textContent;\n    copyTextToClipboard(codeEl.textContent).then((ok) => {\n      if (!ok) return;\n      labelEl.textContent = \"Copied code!\";\n      if (copiedResetTimer) window.clearTimeout(copiedResetTimer);\n      copiedResetTimer = window.setTimeout(() => {\n        labelEl.textContent = originalLabel;\n        copiedResetTimer = null;\n      }, 2000);\n    });\n  });\n}\n"
  },
  {
    "path": "site/src/playground/js/modules/forms.js",
    "content": "import { deepClone, parseJsonParam, safeStringify } from \"./stateUtils.js\";\n// Add a single event listener for all enable spans\n// Listener toggles the adjacent checkbox\ndocument.addEventListener(\"click\", function (e) {\n  if (e.target.classList && e.target.classList.contains(\"form-check-enable-span\")) {\n    const checkbox = e.target.previousElementSibling;\n    if (checkbox && checkbox.type === \"checkbox\") {\n      checkbox.checked = !checkbox.checked;\n      // Optionally trigger change event if needed\n      checkbox.dispatchEvent(new Event(\"change\", { bubbles: true }));\n    }\n  }\n});\n\nfunction initTooltips(container) {\n  if (!container) return;\n  if (!window.bootstrap || !window.bootstrap.Tooltip) return;\n\n  container.querySelectorAll(\"[data-bs-toggle='tooltip']\").forEach((el) => {\n    const existing = window.bootstrap.Tooltip.getInstance(el);\n    if (existing) existing.dispose();\n    new window.bootstrap.Tooltip(el);\n  });\n}\n\nfunction cloneInfoIconSvg(infoIconTemplate) {\n  if (!infoIconTemplate || !infoIconTemplate.content) return null;\n  const svg = infoIconTemplate.content.querySelector(\"svg\");\n  return svg ? svg.cloneNode(true) : null;\n}\n\nfunction createInfoIcon(meta, infoIconTemplate) {\n  const text = String(meta && meta.tooltip ? meta.tooltip : \"\").trim();\n  if (!text) return null;\n  const icon = document.createElement(\"span\");\n  icon.className = \"iti-playground-info\";\n\n  const svg = cloneInfoIconSvg(infoIconTemplate);\n  if (svg) icon.appendChild(svg);\n\n  icon.tabIndex = 0;\n  icon.setAttribute(\"role\", \"button\");\n  icon.setAttribute(\"aria-label\", text);\n  icon.setAttribute(\"data-bs-toggle\", \"tooltip\");\n  icon.setAttribute(\"data-bs-placement\", \"top\");\n  icon.setAttribute(\"title\", text);\n  return icon;\n}\n\nfunction buildLabelGroup(meta, { labelText, htmlFor, labelClassName, infoIconTemplate }) {\n  const group = document.createElement(\"div\");\n  group.className = \"iti-playground-labelgroup\";\n\n  const label = document.createElement(\"label\");\n  label.className = labelClassName;\n  label.htmlFor = htmlFor;\n  label.textContent = labelText;\n\n  group.appendChild(label);\n\n  const infoIcon = createInfoIcon(meta, infoIconTemplate);\n  if (infoIcon) group.appendChild(infoIcon);\n\n  return group;\n}\n\nfunction buildBooleanExampleControl(key, meta, { idPrefix, dataAttr, exampleText, infoIconTemplate }) {\n  // Custom layout: show the code example to the right of the label (desktop), then the checkbox below.\n  const wrapper = document.createElement(\"div\");\n  wrapper.className = \"iti-playground-control iti-playground-control--example-code\";\n\n  const checkboxRow = document.createElement(\"div\");\n  checkboxRow.className = \"form-check iti-playground-example-toggle\";\n\n  const checkbox = document.createElement(\"input\");\n  checkbox.type = \"checkbox\";\n  checkbox.className = \"form-check-input\";\n  checkbox.id = `${idPrefix}_${key}`;\n  checkbox.setAttribute(dataAttr, key);\n\n  // this must be a span as we already have a label for this checkbox\n  const enableSpan = document.createElement(\"span\");\n  enableSpan.className = \"form-check-enable-span\";\n  enableSpan.textContent = \"Enable example code\";\n\n  checkboxRow.appendChild(checkbox);\n  checkboxRow.appendChild(enableSpan);\n\n  wrapper.appendChild(\n    buildLabelGroup(meta, {\n      labelText: meta.label || key,\n      htmlFor: checkbox.id,\n      labelClassName: \"form-label\",\n      infoIconTemplate,\n    }),\n  );\n\n  const example = document.createElement(\"pre\");\n  example.className = \"mb-0 language-javascript iti-playground-loadutils-example\";\n  const code = document.createElement(\"code\");\n  code.className = \"language-javascript\";\n  code.textContent = exampleText;\n  example.appendChild(code);\n  wrapper.appendChild(example);\n\n  wrapper.appendChild(checkboxRow);\n\n  return wrapper;\n}\n\nfunction formatMultiDropdownSelection(values) {\n  const items = Array.isArray(values) ? values : [];\n  if (items.length === 0) return \"None\";\n  if (items.length <= 2) return items.join(\", \");\n  return `${items.length} selected`;\n}\n\nfunction buildControlRow(key, meta, { idPrefix, dataAttr, infoIconTemplate }) {\n  const wrapper = document.createElement(\"div\");\n\n  if (meta.type === \"boolean\") {\n    if (key === \"loadUtils\") {\n      return buildBooleanExampleControl(key, meta, {\n        idPrefix,\n        dataAttr,\n        infoIconTemplate,\n        exampleText: \"() => import(\\\"/intl-tel-input/js/utils.js\\\")\",\n      });\n    }\n\n    if (key === \"customPlaceholder\") {\n      return buildBooleanExampleControl(key, meta, {\n        idPrefix,\n        dataAttr,\n        infoIconTemplate,\n        exampleText: \"(exampleNumber) => exampleNumber ? `e.g. ${exampleNumber}` : 'Phone number'\",\n      });\n    }\n\n    if (key === \"dropdownContainer\") {\n      return buildBooleanExampleControl(key, meta, {\n        idPrefix,\n        dataAttr,\n        infoIconTemplate,\n        exampleText: \"document.body\",\n      });\n    }\n\n    if (key === \"geoIpLookup\") {\n      return buildBooleanExampleControl(key, meta, {\n        idPrefix,\n        dataAttr,\n        infoIconTemplate,\n        exampleText: [\n          \"(success, failure) => {\",\n          \"  fetch(\\\"https://ipapi.co/json\\\")\",\n          \"    .then((res) => res.json())\",\n          \"    .then((data) => success(data.country_code))\",\n          \"    .catch(() => failure());\",\n          \"}\",\n        ].join(\"\\n\"),\n      });\n    }\n\n    if (key === \"hiddenInput\") {\n      return buildBooleanExampleControl(key, meta, {\n        idPrefix,\n        dataAttr,\n        infoIconTemplate,\n        exampleText: \"() => ({ phone: \\\"phone_full\\\", country: \\\"country_code\\\" })\",\n      });\n    }\n\n    wrapper.className = \"form-check iti-playground-control iti-playground-control--check\";\n\n    const checkbox = document.createElement(\"input\");\n    checkbox.type = \"checkbox\";\n    checkbox.className = \"form-check-input\";\n    checkbox.id = `${idPrefix}_${key}`;\n    checkbox.setAttribute(dataAttr, key);\n\n    wrapper.appendChild(checkbox);\n    wrapper.appendChild(\n      buildLabelGroup(meta, {\n        labelText: meta.label || key,\n        htmlFor: checkbox.id,\n        labelClassName: \"form-check-label\",\n        infoIconTemplate,\n      }),\n    );\n\n    return wrapper;\n  }\n\n  wrapper.className = \"iti-playground-control\";\n\n  wrapper.appendChild(\n    buildLabelGroup(meta, {\n      labelText: meta.label || key,\n      htmlFor: `${idPrefix}_${key}`,\n      labelClassName: \"form-label\",\n      infoIconTemplate,\n    }),\n  );\n\n  if (meta.type === \"select\") {\n    const select = document.createElement(\"select\");\n    select.className = \"form-select\";\n    select.id = `${idPrefix}_${key}`;\n    select.setAttribute(dataAttr, key);\n\n    meta.options.forEach((value) => {\n      const option = document.createElement(\"option\");\n      option.value = value;\n      option.textContent = (meta.optionLabels && meta.optionLabels[value]) || value;\n      select.appendChild(option);\n    });\n\n    wrapper.appendChild(select);\n    return wrapper;\n  }\n\n  if (meta.type === \"multidropdown\") {\n    const dropdown = document.createElement(\"div\");\n    dropdown.className = \"dropdown\";\n    dropdown.setAttribute(\"data-multidropdown\", key);\n\n    const button = document.createElement(\"button\");\n    button.type = \"button\";\n    button.className = \"btn btn-outline-secondary dropdown-toggle w-100 text-start iti-playground-multidropdown-toggle\";\n    button.id = `${idPrefix}_${key}`;\n    button.setAttribute(\"data-bs-toggle\", \"dropdown\");\n    button.setAttribute(\"data-bs-auto-close\", \"outside\");\n    button.setAttribute(\"aria-expanded\", \"false\");\n    button.textContent = \"Select\";\n\n    const menu = document.createElement(\"div\");\n    menu.className = \"dropdown-menu p-2\";\n    menu.setAttribute(\"aria-labelledby\", button.id);\n    menu.style.maxHeight = \"240px\";\n    menu.style.overflow = \"auto\";\n\n    meta.options.forEach((value) => {\n      const checkWrap = document.createElement(\"div\");\n      checkWrap.className = \"form-check\";\n\n      const checkbox = document.createElement(\"input\");\n      checkbox.type = \"checkbox\";\n      checkbox.className = \"form-check-input\";\n      checkbox.id = `${idPrefix}_${key}_${value}`;\n      checkbox.value = value;\n      const checkLabel = document.createElement(\"label\");\n      checkLabel.className = \"form-check-label\";\n      checkLabel.htmlFor = checkbox.id;\n      checkLabel.textContent = value;\n\n      checkWrap.appendChild(checkbox);\n      checkWrap.appendChild(checkLabel);\n      menu.appendChild(checkWrap);\n    });\n\n    dropdown.appendChild(button);\n    dropdown.appendChild(menu);\n    wrapper.appendChild(dropdown);\n\n    dropdown.addEventListener(\"change\", () => {\n      const selected = [...dropdown.querySelectorAll(\"input[type=\\\"checkbox\\\"]:checked\")].map((el) => el.value);\n      button.textContent = formatMultiDropdownSelection(selected);\n    });\n\n    return wrapper;\n  }\n\n  if (meta.type === \"json\") {\n    const textarea = document.createElement(\"textarea\");\n    textarea.className = \"form-control\";\n    textarea.id = `${idPrefix}_${key}`;\n    textarea.rows = 2;\n    textarea.setAttribute(dataAttr, key);\n    wrapper.appendChild(textarea);\n\n    const placeholderText = String(meta.placeholder || \"\").trim();\n    if (placeholderText) {\n      const placeholderEl = document.createElement(\"span\");\n      placeholderEl.className = \"form-text\";\n      placeholderEl.textContent = placeholderText;\n      wrapper.appendChild(placeholderEl);\n    }\n\n    return wrapper;\n  }\n\n  if (meta.type === \"number\") {\n    const numberInput = document.createElement(\"input\");\n    numberInput.type = \"number\";\n    numberInput.className = \"form-control\";\n    numberInput.id = `${idPrefix}_${key}`;\n    numberInput.setAttribute(dataAttr, key);\n    if (meta.placeholder) numberInput.placeholder = meta.placeholder;\n    wrapper.appendChild(numberInput);\n    return wrapper;\n  }\n\n  const input = document.createElement(\"input\");\n  input.type = \"text\";\n  input.className = \"form-control\";\n  input.id = `${idPrefix}_${key}`;\n  input.setAttribute(dataAttr, key);\n  if (meta.placeholder) input.placeholder = meta.placeholder;\n\n  wrapper.appendChild(input);\n\n  return wrapper;\n}\n\nfunction createControlGroup(optionGroupTemplate, title, groupId, description, { iso2ModalId = null } = {}) {\n  const slugify = (text) => String(text || \"\")\n    .trim()\n    .toLowerCase()\n    .replace(/[^a-z0-9]+/g, \"-\")\n    .replace(/^-+|-+$/g, \"\");\n\n  const root = optionGroupTemplate.content.firstElementChild;\n  const group = root.cloneNode(true);\n\n  const titleEl = group.querySelector(\"[data-role=\\\"title\\\"]\");\n  const slug = slugify(title);\n  if (titleEl) {\n    titleEl.id = slug;\n    titleEl.textContent = \"\";\n\n    const link = document.createElement(\"a\");\n    link.href = `#${slug}`;\n    link.className = \"header-anchor\";\n    link.textContent = title;\n\n    titleEl.appendChild(link);\n  }\n\n  const descEl = group.querySelector(\"[data-role=\\\"description\\\"]\");\n  descEl.textContent = description;\n\n  const modalId = String(iso2ModalId || \"\").trim();\n  if (modalId) {\n    descEl.appendChild(document.createTextNode(\" \"));\n\n    const link = document.createElement(\"a\");\n    link.href = \"#\";\n    link.textContent = \"Supported ISO2 country codes\";\n    link.setAttribute(\"data-bs-toggle\", \"modal\");\n    link.setAttribute(\"data-bs-target\", `#${modalId}`);\n    link.addEventListener(\"click\", (e) => e.preventDefault());\n\n    descEl.appendChild(link);\n    descEl.appendChild(document.createTextNode(\".\"));\n  }\n\n  const resetButton = group.querySelector(\"[data-role=\\\"reset\\\"]\");\n  resetButton.setAttribute(\"data-playground-reset-group\", String(groupId));\n\n  const stack = group.querySelector(\"[data-role=\\\"stack\\\"]\");\n\n  return { group, stack };\n}\n\nexport function renderControls(formEl, metaMap, { idPrefix, dataAttr, groups = null, templates = {} } = {}) {\n  if (!formEl) return { resetGroupKeys: null };\n  formEl.innerHTML = \"\";\n\n  const { infoIconTemplate, optionGroupTemplate } = templates;\n\n  if (!groups) {\n    Object.entries(metaMap).forEach(([key, meta]) => {\n      formEl.appendChild(buildControlRow(key, meta, { idPrefix, dataAttr, infoIconTemplate }));\n    });\n    initTooltips(formEl);\n    return { resetGroupKeys: null };\n  }\n\n  const resetGroupKeys = new Map();\n\n  const allKeys = Object.keys(metaMap);\n  const usedKeys = new Set();\n\n  groups.forEach(({ title, description, keys, iso2ModalId }, index) => {\n    const groupKeys = (Array.isArray(keys) ? keys : []).filter((k) => metaMap[k]);\n    if (groupKeys.length === 0) return;\n\n    const groupId = `group_${index}`;\n    resetGroupKeys.set(groupId, groupKeys);\n    const { group, stack } = createControlGroup(optionGroupTemplate, title, groupId, description, { iso2ModalId });\n\n    groupKeys.forEach((key) => {\n      usedKeys.add(key);\n      stack.appendChild(buildControlRow(key, metaMap[key], { idPrefix, dataAttr, infoIconTemplate }));\n    });\n\n    formEl.appendChild(group);\n  });\n\n  const remainingKeys = allKeys.filter((k) => !usedKeys.has(k));\n  if (remainingKeys.length) {\n    const groupId = \"group_other\";\n    resetGroupKeys.set(groupId, remainingKeys);\n    const { group, stack } = createControlGroup(\n      optionGroupTemplate,\n      \"Other\",\n      groupId,\n      \"Additional options that don’t fit into the groups above.\",\n    );\n    remainingKeys.forEach((key) => {\n      stack.appendChild(buildControlRow(key, metaMap[key], { idPrefix, dataAttr, infoIconTemplate }));\n    });\n    formEl.appendChild(group);\n  }\n\n  initTooltips(formEl);\n\n  return { resetGroupKeys };\n}\n\nexport function getStateFromForm(formEl, defaults, metaMap, dataAttr) {\n  const state = deepClone(defaults);\n  if (!formEl) return state;\n\n  Object.entries(metaMap).forEach(([key, meta]) => {\n    if (meta.type === \"multidropdown\") {\n      const root = formEl.querySelector(`[data-multidropdown=\"${key}\"]`);\n      if (!root) return;\n\n      const checked = [...root.querySelectorAll(\"input[type=\\\"checkbox\\\"]:checked\")].map((el) => el.value);\n\n      // Keep ordering stable by always following the dropdown option order.\n      const optionOrder = meta && Array.isArray(meta.options) ? meta.options : [];\n      const indexByValue = new Map(optionOrder.map((v, i) => [v, i]));\n      checked.sort(\n        (a, b) => (indexByValue.get(a) ?? Number.MAX_SAFE_INTEGER) - (indexByValue.get(b) ?? Number.MAX_SAFE_INTEGER),\n      );\n\n      state[key] = checked;\n      return;\n    }\n\n    const control = formEl.querySelector(`[${dataAttr}=\"${key}\"]`);\n    if (!control) return;\n\n    if (meta.type === \"boolean\") {\n      state[key] = Boolean(control.checked);\n    } else if (meta.type === \"text\") {\n      state[key] = control.value;\n    } else if (meta.type === \"number\") {\n      const raw = String(control.value || \"\").trim();\n      state[key] = raw === \"\" ? \"\" : Number(raw);\n    } else if (meta.type === \"select\") {\n      state[key] = control.value;\n    } else if (meta.type === \"json\") {\n      const value = String(control.value || \"\").trim();\n      if (value === \"\") {\n        state[key] = defaults[key];\n      } else {\n        const parsed = parseJsonParam(value, state[key]);\n        state[key] = parsed;\n      }\n    }\n  });\n\n  return state;\n}\n\nexport function setFormFromState(formEl, state, metaMap, dataAttr, { defaultState } = {}) {\n  if (!formEl) return;\n\n  Object.entries(metaMap).forEach(([key, meta]) => {\n    if (meta.type === \"multidropdown\") {\n      const values = new Set(Array.isArray(state[key]) ? state[key] : []);\n      const root = formEl.querySelector(`[data-multidropdown=\"${key}\"]`);\n      if (!root) return;\n\n      root.querySelectorAll(\"input[type=\\\"checkbox\\\"]\").forEach((el) => {\n        el.checked = values.has(el.value);\n      });\n\n      const button = root.querySelector(\"button[data-bs-toggle=\\\"dropdown\\\"]\");\n      if (button) {\n        const optionOrder = meta && Array.isArray(meta.options) ? meta.options : [];\n        const ordered = optionOrder.filter((v) => values.has(v));\n        button.textContent = formatMultiDropdownSelection(ordered);\n      }\n      return;\n    }\n\n    const control = formEl.querySelector(`[${dataAttr}=\"${key}\"]`);\n    if (!control) return;\n\n    if (meta.type === \"boolean\") {\n      control.checked = Boolean(state[key]);\n    } else if (meta.type === \"text\") {\n      control.value = state[key] ?? \"\";\n    } else if (meta.type === \"number\") {\n      control.value = state[key] === \"\" || state[key] === null || state[key] === undefined ? \"\" : String(state[key]);\n    } else if (meta.type === \"select\") {\n      control.value = state[key] ?? \"\";\n    } else if (meta.type === \"json\") {\n      const value = state[key];\n      const defaultValue = defaultState ? defaultState[key] : undefined;\n      if (Array.isArray(value) && value.length === 0 && Array.isArray(defaultValue) && defaultValue.length === 0) {\n        control.value = \"\";\n      } else if (value === null) {\n        control.value = \"\";\n      } else if (typeof value === \"string\") {\n        control.value = value;\n      } else if (value === undefined) {\n        control.value = \"\";\n      } else {\n        control.value = safeStringify(value);\n      }\n    }\n  });\n}\n"
  },
  {
    "path": "site/src/playground/js/modules/i18n.js",
    "content": "const i18nDisplayNames = (() => {\n  try {\n    if (typeof Intl === \"undefined\" || !Intl.DisplayNames) return null;\n    return new Intl.DisplayNames([\"en\"], { type: \"language\" });\n  } catch {\n    return null;\n  }\n})();\n\nfunction toBcp47LanguageTag(code) {\n  const raw = String(code || \"\").trim();\n  if (!raw) return \"\";\n  const parts = raw.split(\"-\");\n  if (parts.length === 1) return parts[0].toLowerCase();\n  if (parts.length >= 2) {\n    const [lang, region, ...rest] = parts;\n    const normLang = String(lang).toLowerCase();\n    const normRegion = region && region.length === 2 ? String(region).toUpperCase() : String(region || \"\");\n    return [normLang, normRegion, ...rest].filter(Boolean).join(\"-\");\n  }\n  return raw;\n}\n\nfunction getLanguageLabel(code) {\n  const rawCode = String(code || \"\").trim();\n  if (!rawCode) return \"\";\n  const tag = toBcp47LanguageTag(code);\n  if (!tag) return rawCode;\n  const label = i18nDisplayNames ? i18nDisplayNames.of(tag) : null;\n  return label ? `${label} (${rawCode})` : rawCode;\n}\n\nexport function createI18nOptionLabels(languageCodes) {\n  const labels = {};\n\n  (languageCodes || []).forEach((code) => {\n    labels[code] = getLanguageLabel(code);\n  });\n\n  return labels;\n}\n\nexport async function resolveI18nSelection(value, { i18nDirHash }) {\n  if (value === null || value === undefined) return null;\n\n  if (typeof value === \"object\") return value;\n\n  const code = String(value).trim();\n  if (!code) return null;\n\n  // English is the plugin's built-in default language, so avoid loading an\n  // additional i18n pack (and keep the generated init code simpler).\n  if (code.toLowerCase() === \"en\") return null;\n\n  try {\n    const mod = await import(`/intl-tel-input/js/i18n/${code}/index.js?${i18nDirHash}`);\n    return mod && mod.default ? mod.default : null;\n  } catch {\n    return null;\n  }\n}\n"
  },
  {
    "path": "site/src/playground/js/modules/initCode.js",
    "content": "import { formatJsValue, isDefaultForKey } from \"./stateUtils.js\";\n\n// This module is responsible for generating the initialization code snippet based on the current state of the playground.\nexport function renderInitCodeFromState(state, initCodeEl, { defaultInitOptions, optionMeta, defaultState, specialOptionKeys }) {\n  if (!initCodeEl) return;\n\n  const nonDefaultOptionEntries = [];\n\n  Object.keys(defaultInitOptions).forEach((key) => {\n    if (specialOptionKeys.includes(key)) return;\n    const meta = optionMeta[key];\n    if (!meta) return;\n    const value = state[key];\n    if (isDefaultForKey(key, meta, value, defaultState)) return;\n    nonDefaultOptionEntries.push([key, value]);\n  });\n\n  const optionEntriesForCode = [...nonDefaultOptionEntries];\n  if (state.loadUtils) {\n    optionEntriesForCode.push([\"loadUtils\", \"() => import(\\\"intl-tel-input/utils\\\")\"]);\n  }\n\n  if (state.customPlaceholder) {\n    optionEntriesForCode.push([\n      \"customPlaceholder\",\n      \"(exampleNumber) => exampleNumber ? `e.g. ${exampleNumber}` : 'Phone number'\",\n    ]);\n  }\n\n  if (state.dropdownContainer) {\n    optionEntriesForCode.push([\n      \"dropdownContainer\",\n      \"document.body\",\n    ]);\n  }\n\n  if (state.geoIpLookup) {\n    optionEntriesForCode.push([\n      \"geoIpLookup\",\n      [\n        \"(success, failure) => {\",\n        \"    fetch(\\\"https://ipapi.co/json\\\")\",\n        \"      .then((res) => res.json())\",\n        \"      .then((data) => success(data.country_code))\",\n        \"      .catch(() => failure());\",\n        \"  }\",\n      ].join(\"\\n\"),\n    ]);\n  }\n\n  if (state.hiddenInput) {\n    optionEntriesForCode.push([\n      \"hiddenInput\",\n      \"() => ({ phone: \\\"phone_full\\\", country: \\\"country_code\\\" })\",\n    ]);\n  }\n\n  const i18nCode = String(state.i18n ?? \"\").trim();\n  const hasI18n = Boolean(i18nCode) && i18nCode.toLowerCase() !== \"en\";\n\n  const lines = [];\n  lines.push(\"import intlTelInput from \\\"intl-tel-input\\\";\");\n\n  let i18nExportName = \"\";\n  if (hasI18n) {\n    i18nExportName = i18nCode.replace(/-([a-z])/g, (_, chr) => chr.toUpperCase());\n    lines.push(`import { ${i18nExportName} } from \"intl-tel-input/i18n\";`);\n  }\n\n  lines.push(\"\");\n  lines.push(\"const input = document.querySelector(\\\"#phone\\\");\");\n\n  if (!hasI18n && optionEntriesForCode.length === 0) {\n    lines.push(\"const iti = intlTelInput(input);\");\n  } else {\n    lines.push(\"const iti = intlTelInput(input, {\");\n\n    if (hasI18n) {\n      lines.push(`  i18n: ${i18nExportName},`);\n    }\n\n    optionEntriesForCode.forEach(([key, value]) => {\n      const isRawJsSnippet =\n        [\"loadUtils\", \"customPlaceholder\", \"dropdownContainer\", \"geoIpLookup\", \"hiddenInput\"].includes(key) &&\n        typeof value === \"string\";\n      const formatted = isRawJsSnippet ? value : formatJsValue(value);\n      lines.push(`  ${key}: ${formatted},`);\n    });\n\n    lines.push(\"});\");\n  }\n\n  const output = lines.join(\"\\n\");\n\n  initCodeEl.textContent = output;\n\n  // Highlight.js highlights on page load, but we update this block live, so we need to re-run highlighting.\n  // Highlight.js marks nodes as already-highlighted via `data-highlighted`, so clear that first.\n  // Highlight.js is loaded after this script, so guard for it not being available yet.\n  if (window.hljs && typeof window.hljs.highlightElement === \"function\") {\n    delete initCodeEl.dataset.highlighted;\n    window.hljs.highlightElement(initCodeEl);\n  }\n}\n"
  },
  {
    "path": "site/src/playground/js/modules/itiController.js",
    "content": "import { resolveI18nSelection } from \"./i18n.js\";\n\nfunction applyInputAttributes(state, telInput) {\n  if (!state || !telInput) return;\n\n  if (typeof state.value === \"string\") {\n    telInput.value = state.value;\n    // Keep the DOM \"value\" attribute (defaultValue) in sync for visibility in devtools.\n    telInput.defaultValue = state.value;\n  }\n\n  if (typeof state.placeholder === \"string\") {\n    telInput.placeholder = state.placeholder;\n  }\n\n  telInput.disabled = Boolean(state.disabled);\n  const isReadOnly = Boolean(state.readonly);\n  telInput.readOnly = isReadOnly;\n  // Keep the HTML attribute in sync for visibility in devtools.\n  if (isReadOnly) {\n    telInput.setAttribute(\"readonly\", \"\");\n  } else {\n    telInput.removeAttribute(\"readonly\");\n  }\n}\n\nfunction destroyInstance(iti) {\n  if (iti && typeof iti.destroy === \"function\") {\n    try {\n      iti.destroy();\n    } catch {\n      // ignore\n    }\n  }\n}\n\nfunction detachUtilsIfNeeded(state) {\n  if (!state || state.loadUtils) return;\n  if (!window.intlTelInput) return;\n  // If utils were previously attached, clear them so disabling loadUtils actually disables\n  // formatting/validation, and allow re-attaching if the user re-enables it.\n  window.intlTelInput.utils = null;\n  window.intlTelInput.startedLoadingUtilsScript = false;\n}\n\nfunction toInitOptions(state, { defaultInitOptions, specialOptionKeys, utilsPath }) {\n  const opts = {};\n\n  Object.keys(defaultInitOptions).forEach((key) => {\n    if (specialOptionKeys.includes(key)) return;\n    opts[key] = state[key];\n  });\n\n  if (state.loadUtils) {\n    opts.loadUtils = () => import(utilsPath);\n  }\n\n  if (state.customPlaceholder) {\n    opts.customPlaceholder = (exampleNumber) => (exampleNumber ? `e.g. ${exampleNumber}` : \"Phone number\");\n  }\n\n  if (state.dropdownContainer) {\n    opts.dropdownContainer = document.body;\n  }\n\n  if (state.geoIpLookup) {\n    opts.geoIpLookup = (success, failure) => {\n      fetch(`https://ipapi.co/json?token=${process.env.IPAPI_TOKEN}`)\n        .then((res) => res.json())\n        .then((data) => success(data.country_code))\n        .catch(() => failure());\n    };\n  }\n\n  if (state.hiddenInput) {\n    opts.hiddenInput = () => ({ phone: \"phone_full\", country: \"country_code\" });\n  }\n\n  if (typeof state.dropdownAlwaysOpen === \"boolean\") {\n    opts.dropdownAlwaysOpen = state.dropdownAlwaysOpen;\n  }\n\n  return opts;\n}\n\nexport class ItiPlaygroundController {\n  constructor({ telInput, utilsPath, i18nDirHash, defaultInitOptions, specialOptionKeys }) {\n    this.telInput = telInput;\n    this.utilsPath = utilsPath;\n    this.i18nDirHash = i18nDirHash;\n    this.defaultInitOptions = defaultInitOptions;\n    this.specialOptionKeys = specialOptionKeys;\n\n    this.iti = null;\n    this.initNonce = 0;\n  }\n\n  destroy() {\n    destroyInstance(this.iti);\n    this.iti = null;\n  }\n\n  async initWithState(state) {\n    const nonce = (this.initNonce += 1);\n\n    detachUtilsIfNeeded(state);\n\n    const initOptions = toInitOptions(state, {\n      defaultInitOptions: this.defaultInitOptions,\n      specialOptionKeys: this.specialOptionKeys,\n      utilsPath: this.utilsPath,\n    });\n\n    const i18n = await resolveI18nSelection(state.i18n, { i18nDirHash: this.i18nDirHash });\n\n    if (nonce !== this.initNonce) return;\n\n    // Apply attributes BEFORE initialising the plugin, so it can read the correct DOM state.\n    applyInputAttributes(state, this.telInput);\n\n    this.destroy();\n    initOptions.i18n = i18n;\n    // we need this bootstrap class, but don't want to bother users with this, so just add it here.\n    initOptions.searchInputClass += \" form-control\";\n    this.iti = window.intlTelInput(this.telInput, initOptions);\n\n    // Trigger live results box to update by triggering a countrychange event on the telInput\n    // (avoid triggering an input event as the plugin responds to that)\n    window.setTimeout(() => {\n      this.telInput.dispatchEvent(new Event(\"countrychange\"));\n    }, 0);\n  }\n}\n"
  },
  {
    "path": "site/src/playground/js/modules/playgroundConfig.js",
    "content": "const AUTO_PLACEHOLDER_OPTIONS = [\"polite\", \"aggressive\", \"off\"];\nconst NUMBER_TYPES = [\n  \"FIXED_LINE\",\n  \"MOBILE\",\n  \"FIXED_LINE_OR_MOBILE\",\n  \"TOLL_FREE\",\n  \"PREMIUM_RATE\",\n  \"SHARED_COST\",\n  \"VOIP\",\n  \"PERSONAL_NUMBER\",\n  \"PAGER\",\n  \"UAN\",\n  \"VOICEMAIL\",\n];\n\nexport const SPECIAL_PLAYGROUND_OPTION_KEYS = [\n  \"i18n\",\n  \"loadUtils\",\n  \"customPlaceholder\",\n  \"dropdownContainer\",\n  \"geoIpLookup\",\n  \"hiddenInput\",\n];\n\nexport const OPTION_GROUPS = [\n  {\n    title: \"Country Options\",\n    description: \"Choose which countries are available, the order they're displayed in, and how the initial country is determined.\",\n    iso2ModalId: \"itiPlaygroundIso2Modal\",\n    keys: [\"countryOrder\", \"excludeCountries\", \"geoIpLookup\", \"initialCountry\", \"onlyCountries\"],\n  },\n  {\n    title: \"User Interface Options\",\n    description: \"Control dropdown behaviour and whether certain UI elements are displayed.\",\n    keys: [\n      \"allowDropdown\",\n      \"containerClass\",\n      \"countrySearch\",\n      \"dropdownContainer\",\n      \"fixDropdownWidth\",\n      \"searchInputClass\",\n      \"separateDialCode\",\n      \"showFlags\",\n      \"useFullscreenPopup\",\n    ],\n  },\n  {\n    title: \"Placeholder Options\",\n    description: \"Configure the automatically generated placeholder numbers.\",\n    keys: [\"autoPlaceholder\", \"customPlaceholder\", \"placeholderNumberType\"],\n  },\n  {\n    title: \"Formatting Options\",\n    description: \"How numbers are formatted as you type and on initial display.\",\n    keys: [\"formatAsYouType\", \"formatOnDisplay\", \"nationalMode\", \"strictMode\"],\n  },\n  {\n    title: \"Validation Options\",\n    description: \"Adjust what is considered a valid number.\",\n    keys: [\"allowedNumberTypes\", \"allowNumberExtensions\", \"allowPhonewords\"],\n  },\n  {\n    title: \"Translation Options\",\n    description: \"Localise country names and the plugin UI strings, e.g. the country search placeholder.\",\n    keys: [\"countryNameLocale\", \"i18n\"],\n  },\n  {\n    title: \"Miscellaneous Options\",\n    description: \"Extra features like hidden inputs and loading the utilities module.\",\n    keys: [\"hiddenInput\", \"loadUtils\"],\n  },\n];\n\nexport function createPlaygroundConfig({ defaults, i18nLanguageCodes, i18nOptionLabels }) {\n  const defaultInitOptions = {\n    allowDropdown: defaults.allowDropdown,\n    allowedNumberTypes: defaults.allowedNumberTypes,\n    allowNumberExtensions: defaults.allowNumberExtensions,\n    allowPhonewords: defaults.allowPhonewords,\n    autoPlaceholder: defaults.autoPlaceholder,\n    containerClass: defaults.containerClass,\n    countryNameLocale: defaults.countryNameLocale,\n    countryOrder: defaults.countryOrder,\n    countrySearch: defaults.countrySearch,\n    customPlaceholder: false, // in playground, this is a checkbox\n    dropdownContainer: false, // in playground, this is a checkbox\n    geoIpLookup: false, // in playground, this is a checkbox\n    hiddenInput: false, // in playground, this is a checkbox\n    excludeCountries: defaults.excludeCountries,\n    fixDropdownWidth: defaults.fixDropdownWidth,\n    formatAsYouType: defaults.formatAsYouType,\n    formatOnDisplay: defaults.formatOnDisplay,\n    i18n: \"en\", // default to English in the Playground\n    initialCountry: defaults.initialCountry,\n    loadUtils: true, // in playground, this is a checkbox\n    nationalMode: defaults.nationalMode,\n    onlyCountries: defaults.onlyCountries,\n    placeholderNumberType: defaults.placeholderNumberType,\n    searchInputClass: defaults.searchInputClass,\n    separateDialCode: defaults.separateDialCode,\n    showFlags: defaults.showFlags,\n    strictMode: defaults.strictMode,\n    // show dropdown open, even on mobile, as provides a better playground experience for testing options, otherwise they have to scroll down, change option, scroll back up, open dropdown etc\n    useFullscreenPopup: false,\n  };\n\n  const defaultInputAttributes = {\n    value: \"\",\n    placeholder: \"\",\n    disabled: false,\n    readonly: false,\n  };\n\n  const optionMeta = {\n    allowDropdown: {\n      type: \"boolean\",\n      tooltip: \"Allow clicking the selected country to open the dropdown.\",\n    },\n    allowedNumberTypes: {\n      type: \"multidropdown\",\n      tooltip: \"Restrict the types of numbers that are considered valid.\",\n      options: NUMBER_TYPES,\n    },\n    allowNumberExtensions: {\n      type: \"boolean\",\n      tooltip: \"Accept number extensions as valid (e.g. x123).\",\n    },\n    allowPhonewords: {\n      type: \"boolean\",\n      tooltip: \"Accept letters in the number (phonewords) as valid.\",\n    },\n    autoPlaceholder: {\n      type: \"select\",\n      tooltip: \"Automatically set a placeholder based on the selected country and placeholderNumberType.\",\n      options: AUTO_PLACEHOLDER_OPTIONS,\n    },\n    containerClass: {\n      type: \"text\",\n      tooltip: \"Additional CSS class to add to the container element.\",\n    },\n    countryNameLocale: {\n      type: \"text\",\n      tooltip: \"Locale used when generating country names with Intl.DisplayNames (e.g. 'fr' for French).\",\n      placeholder: \"e.g. fr\",\n    },\n    countryOrder: {\n      type: \"json\",\n      tooltip: \"Custom ordering for countries, given as an array of ISO2 codes. Any countries not listed will appear at the end in default order.\",\n      placeholder: \"e.g. [\\\"us\\\", \\\"gb\\\"]\",\n    },\n    countrySearch: {\n      type: \"boolean\",\n      tooltip: \"Enable the search input inside the country dropdown.\",\n    },\n    customPlaceholder: {\n      type: \"boolean\",\n      label: \"customPlaceholder\",\n      tooltip: \"Customise the auto-generated placeholder.\",\n    },\n    dropdownContainer: {\n      type: \"boolean\",\n      label: \"dropdownContainer\",\n      tooltip: \"Append the dropdown to a specific element (useful when the input is inside a container with overflow:hidden).\",\n    },\n    geoIpLookup: {\n      type: \"boolean\",\n      label: \"geoIpLookup\",\n      tooltip: \"Auto-detect the user's country by IP address (async). Requires initialCountry='auto'.\",\n    },\n    hiddenInput: {\n      type: \"boolean\",\n      label: \"hiddenInput\",\n      tooltip: \"Add hidden inputs that get populated with the full number and country code on submit.\",\n    },\n    excludeCountries: {\n      type: \"json\",\n      tooltip: \"Exclude specific countries (array of ISO2 codes) from the dropdown.\",\n      placeholder: \"e.g. [\\\"ru\\\", \\\"cn\\\"]\",\n    },\n    fixDropdownWidth: {\n      type: \"boolean\",\n      tooltip: \"Keep the dropdown width aligned to the input width, as opposed to the width of the longest country name.\",\n    },\n    formatAsYouType: {\n      type: \"boolean\",\n      tooltip: \"Format the number as the user types.\",\n    },\n    formatOnDisplay: {\n      type: \"boolean\",\n      tooltip: \"Format any initial value when the plugin initialises.\",\n    },\n    i18n: {\n      type: \"select\",\n      tooltip: \"Translate UI strings (e.g. country search placeholder) using the provided language packs.\",\n      options: [\"\", ...(i18nLanguageCodes || [])],\n      optionLabels: i18nOptionLabels,\n    },\n    initialCountry: {\n      type: \"text\",\n      tooltip: \"Initial selected country (ISO2 code), e.g. 'gb'.\",\n      placeholder: \"e.g. gb\",\n    },\n    loadUtils: {\n      type: \"boolean\",\n      label: \"loadUtils\",\n      tooltip: \"Dynamically load utils.js, required for formatting/validation (async).\",\n    },\n    nationalMode: {\n      type: \"boolean\",\n      tooltip: \"Display numbers in national format (instead of international) where applicable.\",\n    },\n    onlyCountries: {\n      type: \"json\",\n      tooltip: \"Restrict the dropdown to only these countries (array of ISO2 codes).\",\n      placeholder: \"e.g. [\\\"us\\\", \\\"ca\\\", \\\"mx\\\"]\",\n    },\n    placeholderNumberType: {\n      type: \"select\",\n      tooltip: \"Number type used when generating placeholders (e.g. MOBILE).\",\n      options: NUMBER_TYPES,\n    },\n    searchInputClass: {\n      type: \"text\",\n      tooltip: \"Additional CSS class to add to the search input element.\",\n    },\n    separateDialCode: {\n      type: \"boolean\",\n      tooltip: \"Show the dial code separately from the number input.\",\n    },\n    showFlags: {\n      type: \"boolean\",\n      tooltip: \"Show country flags in the dropdown and selected country.\",\n    },\n    strictMode: {\n      type: \"boolean\",\n      tooltip: \"As the user types in the input, ignore irrelevant characters and cap the number at the maximum valid length.\",\n    },\n    useFullscreenPopup: {\n      type: \"boolean\",\n      tooltip: \"Use a fullscreen-style country picker instead of the dropdown. Defaults to true on small screens (except on this page, where it is helpful to have it always visible as a dropdown).\",\n    },\n  };\n\n  const attributeMeta = {\n    value: { type: \"text\" },\n    placeholder: { type: \"text\" },\n    disabled: { type: \"boolean\" },\n    readonly: { type: \"boolean\" },\n  };\n\n  return {\n    defaultInitOptions,\n    defaultInputAttributes,\n    optionMeta,\n    attributeMeta,\n    optionGroups: OPTION_GROUPS,\n    specialOptionKeys: SPECIAL_PLAYGROUND_OPTION_KEYS,\n  };\n}\n"
  },
  {
    "path": "site/src/playground/js/modules/stateUtils.js",
    "content": "export function deepClone(value) {\n  return JSON.parse(JSON.stringify(value));\n}\n\nexport function parseBooleanParam(value, fallback) {\n  if (value === null || value === undefined) return fallback;\n  const v = String(value).trim().toLowerCase();\n  if ([\"1\", \"true\", \"yes\", \"y\", \"on\"].includes(v)) return true;\n  if ([\"0\", \"false\", \"no\", \"n\", \"off\"].includes(v)) return false;\n  return fallback;\n}\n\n// The playground textareas are user-facing and often contain JS-style literals\n// (e.g. ['us', 'gb']) rather than strict JSON (e.g. [\"us\", \"gb\"]).\n// Avoid eval; instead, do a minimal, safe normalization and retry JSON.parse.\nconst cleanTextForJson = (text) => {\n  const withoutTrailingCommas = text.replace(/,\\s*([\\]}])/g, \"$1\");\n  const normalizedQuotes = withoutTrailingCommas.replace(/'((?:\\\\.|[^'\\\\])*)'/g, (_m, inner) => {\n    // Basic support for common escapes used in these inputs.\n    const unescaped = String(inner)\n      .replace(/\\\\'/g, \"'\")\n      .replace(/\\\\\\\\/g, \"\\\\\");\n    return JSON.stringify(unescaped);\n  });\n  return normalizedQuotes;\n};\n\nexport function parseJsonParam(value, fallback) {\n  if (value === null || value === undefined) return fallback;\n  const text = String(value).trim();\n  if (!text) return fallback;\n  if (text === \"null\") return null;\n  try {\n    const cleaned = cleanTextForJson(text);\n    return JSON.parse(cleaned);\n  } catch {\n    return fallback;\n  }\n}\n\nexport function parseQueryOverrides(defaults, metaMap, { aliases = {} } = {}) {\n  const params = new URLSearchParams(window.location.search);\n  const next = deepClone(defaults);\n\n  Object.keys(metaMap).forEach((key) => {\n    const paramKey = params.has(key) ? key : aliases[key];\n    if (!paramKey || !params.has(paramKey)) return;\n    const raw = params.get(paramKey);\n    const meta = metaMap[key];\n\n    if (meta.type === \"boolean\") {\n      next[key] = parseBooleanParam(raw, next[key]);\n    } else if (meta.type === \"select\" || meta.type === \"text\") {\n      next[key] = raw;\n    } else if (meta.type === \"number\") {\n      const num = Number(raw);\n      next[key] = Number.isFinite(num) ? num : next[key];\n    } else if (meta.type === \"multidropdown\") {\n      const parsed = parseJsonParam(raw, next[key]);\n      next[key] = Array.isArray(parsed) ? parsed : next[key];\n    } else if (meta.type === \"json\") {\n      next[key] = parseJsonParam(raw, next[key]);\n    }\n  });\n\n  return next;\n}\n\nexport function safeStringify(value) {\n  try {\n    return JSON.stringify(value, null, 2);\n  } catch {\n    return String(value);\n  }\n}\n\nexport function formatJsValue(value) {\n  if (value === undefined) return \"undefined\";\n  if (value === null) return \"null\";\n  if (typeof value === \"string\") return JSON.stringify(value);\n  if (typeof value === \"number\" || typeof value === \"boolean\") return String(value);\n  try {\n    return JSON.stringify(value);\n  } catch {\n    return safeStringify(value);\n  }\n}\n\nexport function encodeJsonParam(value) {\n  if (value === null) return \"\";\n  try {\n    return JSON.stringify(value);\n  } catch {\n    return String(value);\n  }\n}\n\nexport function deepEqual(a, b) {\n  if (a === b) return true;\n  if (a === null || b === null || a === undefined || b === undefined) return a === b;\n  if (typeof a !== typeof b) return false;\n\n  if (Array.isArray(a) && Array.isArray(b)) {\n    if (a.length !== b.length) return false;\n    for (let i = 0; i < a.length; i += 1) {\n      if (!deepEqual(a[i], b[i])) return false;\n    }\n    return true;\n  }\n\n  if (typeof a === \"object\" && typeof b === \"object\") {\n    const aKeys = Object.keys(a).sort();\n    const bKeys = Object.keys(b).sort();\n    if (aKeys.length !== bKeys.length) return false;\n    for (let i = 0; i < aKeys.length; i += 1) {\n      if (aKeys[i] !== bKeys[i]) return false;\n      const key = aKeys[i];\n      if (!deepEqual(a[key], b[key])) return false;\n    }\n    return true;\n  }\n\n  return false;\n}\n\nexport function normalizeStringArrayAsSet(value) {\n  if (!Array.isArray(value)) return [];\n  return value\n    .map((v) => String(v))\n    .filter(Boolean)\n    .sort();\n}\n\nexport function createDefaultState(defaultInitOptions, defaultInputAttributes) {\n  return { ...deepClone(defaultInitOptions), ...deepClone(defaultInputAttributes) };\n}\n\nexport function isDefaultForKey(key, meta, value, defaultState) {\n  const def = defaultState[key];\n\n  if (meta.type === \"multidropdown\") {\n    const a = normalizeStringArrayAsSet(value);\n    const b = normalizeStringArrayAsSet(def);\n    return deepEqual(a, b);\n  }\n\n  if (meta.type === \"text\" || meta.type === \"select\") {\n    return String(value ?? \"\") === String(def ?? \"\");\n  }\n\n  if (meta.type === \"number\") {\n    const a = value === \"\" ? \"\" : Number(value);\n    const b = def === \"\" ? \"\" : Number(def);\n    return a === b || (Number.isNaN(a) && Number.isNaN(b));\n  }\n\n  if (meta.type === \"boolean\") {\n    return Boolean(value) === Boolean(def);\n  }\n\n  if (meta.type === \"json\") {\n    return deepEqual(value, def);\n  }\n\n  return deepEqual(value, def);\n}\n"
  },
  {
    "path": "site/src/playground/js/modules/urlState.js",
    "content": "import { encodeJsonParam, isDefaultForKey } from \"./stateUtils.js\";\n\nexport function buildShareUrlFromState(\n  state,\n  { optionMeta, attributeMeta, defaultState },\n  { excludeDefaults = false } = {},\n) {\n  const url = new URL(window.location.href);\n  const allMeta = {\n    ...optionMeta,\n    ...attributeMeta,\n  };\n\n  // Keep unrelated params, but rewrite all playground-controlled params.\n  Object.keys(allMeta).forEach((key) => url.searchParams.delete(key));\n\n  Object.entries(allMeta).forEach(([key, meta]) => {\n    const value = state[key];\n\n    if (excludeDefaults && isDefaultForKey(key, meta, value, defaultState)) return;\n\n    if (meta.type === \"boolean\") {\n      url.searchParams.set(key, value ? \"true\" : \"false\");\n    } else if (meta.type === \"text\" || meta.type === \"select\") {\n      url.searchParams.set(key, String(value ?? \"\"));\n    } else if (meta.type === \"number\") {\n      url.searchParams.set(key, value === \"\" || value === null || value === undefined ? \"\" : String(value));\n    } else if (meta.type === \"multidropdown\") {\n      url.searchParams.set(key, encodeJsonParam(Array.isArray(value) ? value : []));\n    } else if (meta.type === \"json\") {\n      url.searchParams.set(key, encodeJsonParam(value));\n    }\n  });\n\n  return url;\n}\n\nexport function updateUrlFromState(state, deps) {\n  const nextUrl = buildShareUrlFromState(state, deps, { excludeDefaults: true });\n  const currentUrl = new URL(window.location.href);\n  if (currentUrl.search === nextUrl.search) return;\n  window.history.replaceState(null, \"\", nextUrl);\n}\n"
  },
  {
    "path": "site/src/playground/js/playground.js",
    "content": "import {\n  I18N_LANGUAGE_CODES,\n  UTILS_PATH,\n  I18N_DIR_HASH,\n} from \"../../../tmp/playground/playgroundConstants.js\";\n\nimport { bindCopyCodeButton } from \"./modules/clipboard.js\";\nimport { createI18nOptionLabels } from \"./modules/i18n.js\";\nimport { renderControls, getStateFromForm, setFormFromState } from \"./modules/forms.js\";\nimport { renderInitCodeFromState } from \"./modules/initCode.js\";\nimport { createPlaygroundConfig } from \"./modules/playgroundConfig.js\";\nimport { ItiPlaygroundController } from \"./modules/itiController.js\";\nimport {\n  createDefaultState,\n  deepClone,\n  parseBooleanParam,\n  parseQueryOverrides,\n} from \"./modules/stateUtils.js\";\nimport { updateUrlFromState } from \"./modules/urlState.js\";\n\nconst telInput = document.querySelector(\"#playgroundPhone\");\nconst playgroundContainer = document.querySelector(\"#itiPlayground\");\nconst keepDropdownOpenCheckbox = document.querySelector(\"#playgroundKeepDropdownOpen\");\nconst optionsForm = document.querySelector(\"#playgroundOptions\");\nconst attrsForm = document.querySelector(\"#playgroundAttributes\");\nconst resetAllButton = document.querySelector(\"#playgroundResetAll\");\nconst shareButton = document.querySelector(\"#playgroundShareBtn\");\nconst resetAttrsButton = document.querySelector(\"#playgroundResetAttrs\");\nconst initCodeEl = document.querySelector(\"#playgroundInitCode\");\nconst copyInitCodeButton = document.querySelector(\"#playgroundCopyInitCode\");\nconst infoIconTemplate = document.querySelector(\"#itiPlaygroundInfoIconTemplate\");\nconst optionGroupTemplate = document.querySelector(\"#itiPlaygroundOptionGroupTemplate\");\nconst iso2ModalEl = document.querySelector(\"#itiPlaygroundIso2Modal\");\nconst iso2ModalTableBody = document.querySelector(\"#itiPlaygroundIso2ModalTableBody\");\n\nconst KEEP_DROPDOWN_OPEN_PARAM = \"keepDropdownOpen\";\n\nconst presetsSelect = document.querySelector(\"#playgroundPresetsSelect\");\nif (presetsSelect) {\n  presetsSelect.addEventListener(\"change\", function () {\n    if (this.value) window.location.href = this.value;\n    // reset the selection, as the longer option text doesn't fit in the box\n    presetsSelect.selectedIndex = 0;\n  });\n}\n\nfunction flashActionButtonLabel(buttonEl, temporaryLabel) {\n  if (!buttonEl) return;\n\n  const labelEl = buttonEl.querySelector(\"[data-role=\\\"label\\\"]\") || buttonEl;\n  const originalLabel = labelEl.textContent;\n\n  labelEl.textContent = temporaryLabel;\n  buttonEl.disabled = true;\n\n  window.setTimeout(() => {\n    labelEl.textContent = originalLabel;\n    buttonEl.disabled = false;\n  }, 2000);\n}\n\nfunction shouldDisableKeepDropdownOpen(state) {\n  return (\n    state.useFullscreenPopup ||\n    !state.allowDropdown ||\n    state.disabled ||\n    state.readonly ||\n    state.dropdownContainer // uses pos:fixed so can't be kept open on scroll\n  );\n}\n\nfunction syncKeepDropdownOpenAvailability(state) {\n  const disabled = shouldDisableKeepDropdownOpen(state);\n  keepDropdownOpenCheckbox.disabled = disabled;\n\n  if (disabled && keepDropdownOpenCheckbox.checked) {\n    keepDropdownOpenCheckbox.checked = false;\n    syncKeepDropdownOpen();\n  }\n}\n\nfunction getFullState(state) {\n  return {\n    ...(state || {}),\n    dropdownAlwaysOpen:\n      Boolean(keepDropdownOpenCheckbox.checked) &&\n      !shouldDisableKeepDropdownOpen(state),\n  };\n}\n\nfunction getKeepDropdownOpenFromUrl() {\n  const params = new URLSearchParams(window.location.search);\n  return parseBooleanParam(params.get(KEEP_DROPDOWN_OPEN_PARAM), true);\n}\n\nfunction updateKeepDropdownOpenUrlParam(enabled) {\n  const currentUrl = new URL(window.location.href);\n  if (enabled) {\n    // Keep the URL clean: omit the param when true (default).\n    currentUrl.searchParams.delete(KEEP_DROPDOWN_OPEN_PARAM);\n  } else {\n    // Only include the param when false.\n    currentUrl.searchParams.set(KEEP_DROPDOWN_OPEN_PARAM, \"false\");\n  }\n\n  const nextUrl = currentUrl;\n  const compareUrl = new URL(window.location.href);\n  if (compareUrl.search === nextUrl.search) return;\n  window.history.replaceState(null, \"\", nextUrl);\n}\n\n// set the checkbox state from the URL param on initial load (default checked)\nkeepDropdownOpenCheckbox.checked = getKeepDropdownOpenFromUrl();\n\nconst syncKeepDropdownOpen = () => {\n  // when keeping the dropdown open, use a CSS class to update the page layout\n  playgroundContainer.classList.toggle(\n    \"iti-playground--keep-dropdown-open\",\n    keepDropdownOpenCheckbox.checked,\n  );\n  updateKeepDropdownOpenUrlParam(keepDropdownOpenCheckbox.checked);\n};\nkeepDropdownOpenCheckbox.addEventListener(\"change\", () => {\n  syncKeepDropdownOpen();\n  scheduleReinit();\n});\nsyncKeepDropdownOpen();\n\nbindCopyCodeButton(copyInitCodeButton, initCodeEl);\n\nif (shareButton) {\n  let copiedResetTimer = null;\n  const labelEl = shareButton.querySelector(\"[data-role=\\\"label\\\"]\") || shareButton;\n  const originalLabel = labelEl.textContent;\n\n  shareButton.addEventListener(\"click\", () => {\n    const shareUrl = new URL(window.location.href);\n    shareUrl.hash = \"\";\n    const url = shareUrl.toString();\n\n    if (!navigator.clipboard || !window.isSecureContext) return;\n    navigator.clipboard.writeText(url).then(\n      () => {\n        labelEl.textContent = \"Copied URL!\";\n        shareButton.disabled = true;\n\n        if (copiedResetTimer) window.clearTimeout(copiedResetTimer);\n        copiedResetTimer = window.setTimeout(() => {\n          labelEl.textContent = originalLabel;\n          shareButton.disabled = false;\n          copiedResetTimer = null;\n        }, 2000);\n      },\n      () => {\n        // Intentionally no UI feedback on failure.\n      },\n    );\n  });\n}\n\nconst { defaults } = window.intlTelInput;\nconst i18nOptionLabels = createI18nOptionLabels(I18N_LANGUAGE_CODES);\n\nconst {\n  defaultInitOptions,\n  defaultInputAttributes,\n  optionMeta,\n  attributeMeta,\n  optionGroups,\n  specialOptionKeys,\n} = createPlaygroundConfig({\n  defaults,\n  i18nLanguageCodes: I18N_LANGUAGE_CODES,\n  i18nOptionLabels,\n});\n\nconst defaultState = createDefaultState(defaultInitOptions, defaultInputAttributes);\n\nconst itiController = new ItiPlaygroundController({\n  telInput,\n  utilsPath: UTILS_PATH,\n  i18nDirHash: I18N_DIR_HASH,\n  defaultInitOptions,\n  specialOptionKeys,\n});\n\nfunction initItiWithState(state) {\n  syncKeepDropdownOpenAvailability(state);\n  return itiController.initWithState(getFullState(state)).then(() => syncKeepDropdownOpen());\n}\n\nconst { resetGroupKeys: optionResetGroupKeys } = renderControls(optionsForm, optionMeta, {\n  idPrefix: \"opt\",\n  dataAttr: \"data-option\",\n  groups: optionGroups,\n  templates: {\n    infoIconTemplate,\n    optionGroupTemplate,\n  },\n});\n\n// If the page is loaded with a hash, the browser can't scroll to it until after\n// the option groups are rendered.\nif (window.location.hash) {\n  window.requestAnimationFrame(() => {\n    const id = window.location.hash.replace(/^#/, \"\");\n    const el = id ? document.getElementById(id) : null;\n    if (el && typeof el.scrollIntoView === \"function\") {\n      el.scrollIntoView({ block: \"start\" });\n    }\n  });\n}\n\nrenderControls(attrsForm, attributeMeta, {\n  idPrefix: \"attr\",\n  dataAttr: \"data-attr\",\n  templates: {\n    infoIconTemplate,\n  },\n});\n\nfunction getCombinedStateFromControls() {\n  return {\n    ...getStateFromForm(optionsForm, defaultInitOptions, optionMeta, \"data-option\"),\n    ...getStateFromForm(attrsForm, defaultInputAttributes, attributeMeta, \"data-attr\"),\n  };\n}\n\nconst initialOptionsState = parseQueryOverrides(defaultInitOptions, optionMeta);\nconst initialAttrsState = parseQueryOverrides(defaultInputAttributes, attributeMeta);\nconst initialState = { ...initialOptionsState, ...initialAttrsState };\n\nsetFormFromState(optionsForm, initialState, optionMeta, \"data-option\", { defaultState });\nsetFormFromState(attrsForm, initialState, attributeMeta, \"data-attr\", { defaultState });\n\nrenderInitCodeFromState(initialState, initCodeEl, {\n  defaultInitOptions,\n  optionMeta,\n  defaultState,\n  specialOptionKeys,\n});\n\nvoid initItiWithState(initialState);\nupdateUrlFromState(initialState, {\n  optionMeta,\n  attributeMeta,\n  defaultState,\n});\n\n// Contextual hints: shown when toggling an option that has no visible effect\n// until the user takes an additional action (e.g. selecting a country, typing a number).\nconst HINT_CONFIGS = [\n  // Country Options\n  {\n    optionKey: \"countryOrder\",\n    message: \"Tip: in the Live Demo section, enable \\\"Keep dropdown open\\\" to see these changes in action.\",\n    shouldShow: () => !keepDropdownOpenCheckbox.checked,\n  },\n  {\n    optionKey: \"excludeCountries\",\n    message: \"Tip: in the Live Demo section, enable \\\"Keep dropdown open\\\" to see these changes in action.\",\n    shouldShow: () => !keepDropdownOpenCheckbox.checked,\n  },\n  {\n    optionKey: \"geoIpLookup\",\n    message: \"Tip: set initialCountry to \\\"auto\\\" for this to take effect.\",\n    shouldShow: () => getCombinedStateFromControls().initialCountry !== \"auto\",\n  },\n  {\n    optionKey: \"initialCountry\",\n    message: \"Tip: enable geoIpLookup to get this working.\",\n    shouldShow: () => {\n      const state = getCombinedStateFromControls();\n      return state.initialCountry === \"auto\" && !state.geoIpLookup;\n    },\n  },\n  {\n    optionKey: \"onlyCountries\",\n    message: \"Tip: in the Live Demo section, enable \\\"Keep dropdown open\\\" to see these changes in action.\",\n    shouldShow: () => !keepDropdownOpenCheckbox.checked,\n  },\n  // User Interface Options (allowDropdown not needed as always clear)\n  {\n    optionKey: \"containerClass\",\n    message: \"Tip: open devtools and inspect the Live Demo to check this is working.\",\n    shouldShow: () => true,\n  },\n  {\n    optionKey: \"countrySearch\",\n    message: \"Tip: in the Live Demo section, enable \\\"Keep dropdown open\\\" to see this change in action.\",\n    shouldShow: () => !keepDropdownOpenCheckbox.checked,\n    alsoShowOnToggleOff: true,\n  },\n  {\n    optionKey: \"dropdownContainer\",\n    message: \"Tip: open devtools and inspect the Live Demo to check this is working.\",\n    shouldShow: () => true,\n  },\n  {\n    optionKey: \"fixDropdownWidth\",\n    message: \"Tip: in the Live Demo section, enable \\\"Keep dropdown open\\\" to see this change in action.\",\n    shouldShow: () => !keepDropdownOpenCheckbox.checked,\n    alsoShowOnToggleOff: true,\n  },\n  {\n    optionKey: \"searchInputClass\",\n    message: \"Tip: open devtools and inspect the Live Demo to check this is working.\",\n    shouldShow: () => true,\n  },\n  {\n    optionKey: \"separateDialCode\",\n    message: \"Tip: try selecting a country from the dropdown to see this in action.\",\n    shouldShow: () => !itiController.iti.getSelectedCountryData().iso2,\n  },\n  {\n    optionKey: \"showFlags\",\n    message: \"Tip: set an initialCountry and/or enable \\\"Keep dropdown open\\\" to see this in action.\",\n    shouldShow: () => !keepDropdownOpenCheckbox.checked && !itiController.iti.getSelectedCountryData().iso2,\n    alsoShowOnToggleOff: true,\n  },\n  {\n    optionKey: \"useFullscreenPopup\",\n    message: \"Tip: click the selected country to open the popup.\",\n    shouldShow: () => true,\n  },\n  // Placeholder Options\n  {\n    optionKey: \"autoPlaceholder\",\n    message: \"Tip: set an initialCountry to see the placeholder.\",\n    shouldShow: () => !itiController.iti.getSelectedCountryData().iso2,\n    alsoShowOnToggleOff: true,\n  },\n  {\n    optionKey: \"customPlaceholder\",\n    message: \"Tip: set an initialCountry to see the placeholder.\",\n    shouldShow: () => !itiController.iti.getSelectedCountryData().iso2,\n    alsoShowOnToggleOff: true,\n  },\n  {\n    optionKey: \"placeholderNumberType\",\n    message: \"Tip: set an initialCountry to see the placeholder.\",\n    shouldShow: () => !itiController.iti.getSelectedCountryData().iso2,\n    alsoShowOnToggleOff: true,\n  },\n  // Formatting Options\n  {\n    optionKey: \"formatAsYouType\",\n    message: \"Tip: select a country and type a phone number to see this in action.\",\n    shouldShow: () => !telInput.value,\n  },\n  {\n    optionKey: \"formatOnDisplay\",\n    message: \"Tip: add a phone number in the Input Attributes section below in order to see this in action.\",\n    shouldShow: () => !telInput.value,\n  },\n  {\n    optionKey: \"nationalMode\",\n    message: \"Tip: set an initialCountry to see how this option formats the placeholder number differently.\",\n    shouldShow: () => !telInput.value && !telInput.placeholder,\n    alsoShowOnToggleOff: true,\n  },\n  {\n    optionKey: \"strictMode\",\n    message: \"Tip: select a country and try typing valid/invalid characters in the input to see this in action.\",\n    shouldShow: () => !telInput.value,\n  },\n  // Validation Options\n  {\n    optionKey: \"allowedNumberTypes\",\n    message: \"Tip: add a phone number in the Input Attributes section below in order to see this in action.\",\n    shouldShow: () => !telInput.value,\n    isMultidropdown: true,\n  },\n  {\n    optionKey: \"allowNumberExtensions\",\n    message: \"Tip: add a phone number in the Input Attributes section below in order to see this in action.\",\n    shouldShow: () => !telInput.value,\n    alsoShowOnToggleOff: true,\n  },\n  {\n    optionKey: \"allowPhonewords\",\n    message: \"Tip: add a phone number in the Input Attributes section below in order to see this in action.\",\n    shouldShow: () => !telInput.value,\n    alsoShowOnToggleOff: true,\n  },\n  // Translation Options\n  {\n    optionKey: \"countryNameLocale\",\n    message: \"Tip: in the Live Demo section, enable \\\"Keep dropdown open\\\" to see this change in action.\",\n    shouldShow: () => !keepDropdownOpenCheckbox.checked,\n  },\n  {\n    optionKey: \"i18n\",\n    message: \"Tip: in the Live Demo section, enable \\\"Keep dropdown open\\\" to see this change in action.\",\n    shouldShow: () => !keepDropdownOpenCheckbox.checked,\n  },\n  // Miscellaneous Options\n  {\n    optionKey: \"hiddenInput\",\n    message: \"Tip: open devtools and inspect the Live Demo to check this is working.\",\n    shouldShow: () => true,\n  },\n  {\n    optionKey: \"loadUtils\",\n    message: \"NOTE: without utils, formatting/validation (and more) is disabled.\",\n    shouldShow: () => !getCombinedStateFromControls().loadUtils,\n    alsoShowOnToggleOff: true,\n  },\n];\n\nconst hintTimers = {};\nconst pendingHintChecks = new Set();\nconst hintOptionKeys = new Set(HINT_CONFIGS.map((c) => c.optionKey));\n\nfunction findControl(optionKey, isMultidropdown) {\n  if (isMultidropdown) {\n    return optionsForm.querySelector(`[data-multidropdown='${optionKey}']`);\n  }\n  return optionsForm.querySelector(`[data-option='${optionKey}']`);\n}\n\nfunction maybeShowHint(config) {\n  const { optionKey, message, shouldShow } = config;\n  const control = findControl(optionKey, config.isMultidropdown);\n  if (!control) return;\n\n  if (!config.isMultidropdown) {\n    const isCheckbox = control.type === \"checkbox\";\n    if (isCheckbox && !control.checked && !config.alsoShowOnToggleOff) return;\n    if (!isCheckbox && !control.value.trim()) return;\n  }\n\n  if (!shouldShow()) return;\n\n  const existing = optionsForm.querySelector(`[data-hint-option='${optionKey}']`);\n  if (existing) existing.remove();\n  if (hintTimers[optionKey]) window.clearTimeout(hintTimers[optionKey]);\n\n  const hint = document.createElement(\"span\");\n  hint.className = \"iti-playground-hint form-text\";\n  hint.setAttribute(\"data-hint-option\", optionKey);\n  hint.textContent = message;\n\n  const controlWrapper = control.closest(\".iti-playground-control\");\n  if (!controlWrapper) return;\n  controlWrapper.appendChild(hint);\n\n  hintTimers[optionKey] = window.setTimeout(() => {\n    hint.remove();\n    hintTimers[optionKey] = null;\n  }, 5000);\n}\n\nlet reinitTimer = null;\nfunction scheduleReinit() {\n  if (reinitTimer) window.clearTimeout(reinitTimer);\n  reinitTimer = window.setTimeout(() => {\n    const state = getCombinedStateFromControls();\n    syncKeepDropdownOpenAvailability(state);\n    updateUrlFromState(state, {\n      optionMeta,\n      attributeMeta,\n      defaultState,\n    });\n    renderInitCodeFromState(state, initCodeEl, {\n      defaultInitOptions,\n      optionMeta,\n      defaultState,\n      specialOptionKeys,\n    });\n    initItiWithState(state).then(() => {\n      if (pendingHintChecks.size > 0) {\n        for (const config of HINT_CONFIGS) {\n          if (pendingHintChecks.has(config.optionKey)) {\n            maybeShowHint(config);\n          }\n        }\n        pendingHintChecks.clear();\n      }\n    });\n  }, 100);\n}\n\nfunction maybeQueueHintCheck(event) {\n  const target = event.target;\n  const optionKey = target && target.getAttribute(\"data-option\");\n  if (optionKey && hintOptionKeys.has(optionKey)) {\n    pendingHintChecks.add(optionKey);\n    return;\n  }\n  // For multidropdown controls, the change event fires on inner checkboxes;\n  // walk up to find the [data-multidropdown] wrapper.\n  const multidropdown = target && target.closest(\"[data-multidropdown]\");\n  if (multidropdown) {\n    const mdKey = multidropdown.getAttribute(\"data-multidropdown\");\n    if (mdKey && hintOptionKeys.has(mdKey)) {\n      pendingHintChecks.add(mdKey);\n    }\n  }\n}\n\noptionsForm.addEventListener(\"input\", (event) => {\n  maybeQueueHintCheck(event);\n  scheduleReinit();\n});\noptionsForm.addEventListener(\"change\", (event) => {\n  maybeQueueHintCheck(event);\n  scheduleReinit();\n});\n\nif (attrsForm) {\n  attrsForm.addEventListener(\"input\", scheduleReinit);\n  attrsForm.addEventListener(\"change\", scheduleReinit);\n}\n\nfunction resetOptionGroupToDefaults(groupKeys) {\n  const keys = Array.isArray(groupKeys) ? groupKeys : [];\n  if (keys.length === 0) return;\n\n  const optionsState = getStateFromForm(optionsForm, defaultInitOptions, optionMeta, \"data-option\");\n  keys.forEach((key) => {\n    optionsState[key] = deepClone(defaultInitOptions[key]);\n  });\n\n  // Gather attributes too because we pass a single combined state object downstream.\n  const attrsState = getStateFromForm(attrsForm, defaultInputAttributes, attributeMeta, \"data-attr\");\n\n  const state = { ...optionsState, ...attrsState };\n  setFormFromState(optionsForm, state, optionMeta, \"data-option\", { defaultState });\n  renderInitCodeFromState(state, initCodeEl, {\n    defaultInitOptions,\n    optionMeta,\n    defaultState,\n    specialOptionKeys,\n  });\n  void initItiWithState(state);\n  updateUrlFromState(state, {\n    optionMeta,\n    attributeMeta,\n    defaultState,\n  });\n}\n\noptionsForm.addEventListener(\"click\", (event) => {\n  if (!(event.target instanceof Element)) return;\n  const btn = event.target.closest(\"[data-playground-reset=\\\"options\\\"]\");\n  if (!btn) return;\n\n  const groupId = btn.getAttribute(\"data-playground-reset-group\");\n  const groupKeys = groupId && optionResetGroupKeys ? optionResetGroupKeys.get(groupId) : null;\n  if (!groupKeys) return;\n\n  event.preventDefault();\n  resetOptionGroupToDefaults(groupKeys);\n  flashActionButtonLabel(btn, \"Reset!\");\n});\n\nfunction resetAllToDefaults() {\n  const state = {\n    ...deepClone(defaultInitOptions),\n    ...deepClone(defaultInputAttributes),\n  };\n\n  setFormFromState(optionsForm, state, optionMeta, \"data-option\", { defaultState });\n  setFormFromState(attrsForm, state, attributeMeta, \"data-attr\", { defaultState });\n  renderInitCodeFromState(state, initCodeEl, {\n    defaultInitOptions,\n    optionMeta,\n    defaultState,\n    specialOptionKeys,\n  });\n  void initItiWithState(state);\n  updateUrlFromState(state, {\n    optionMeta,\n    attributeMeta,\n    defaultState,\n  });\n}\n\nif (resetAllButton) {\n  resetAllButton.addEventListener(\"click\", (event) => {\n    event.preventDefault();\n    resetAllToDefaults();\n    flashActionButtonLabel(resetAllButton, \"Reset!\");\n  });\n}\n\nif (resetAttrsButton) {\n  resetAttrsButton.addEventListener(\"click\", () => {\n    // We store both options and attributes in the same state object that is passed downstream,\n    // so we must gather the current option settings first.\n    const optionsState = getStateFromForm(optionsForm, defaultInitOptions, optionMeta, \"data-option\");\n    const state = { ...optionsState, ...deepClone(defaultInputAttributes) };\n    setFormFromState(attrsForm, state, attributeMeta, \"data-attr\", { defaultState });\n    renderInitCodeFromState(state, initCodeEl, {\n      defaultInitOptions,\n      optionMeta,\n      defaultState,\n      specialOptionKeys,\n    });\n    void initItiWithState(state);\n    updateUrlFromState(state, {\n      optionMeta,\n      attributeMeta,\n      defaultState,\n    });\n\n    flashActionButtonLabel(resetAttrsButton, \"Reset!\");\n  });\n}\n\nfunction getSupportedCountries() {\n  if (!window.intlTelInput || typeof window.intlTelInput.getCountryData !== \"function\") {\n    return [];\n  }\n  const data = window.intlTelInput.getCountryData();\n  return Array.isArray(data) ? data : [];\n}\n\nfunction renderSupportedCountriesTable() {\n  if (!iso2ModalTableBody) return;\n  iso2ModalTableBody.innerHTML = \"\";\n\n  const countries = getSupportedCountries();\n\n  const fragment = document.createDocumentFragment();\n  countries.forEach(({ name, iso2, dialCode }) => {\n    const row = document.createElement(\"tr\");\n\n    const countryCell = document.createElement(\"td\");\n    const flagDiv = document.createElement(\"div\");\n    flagDiv.className = `iti__flag iti__${iso2} d-inline-block`;\n    const countryNameSpan = document.createElement(\"span\");\n    countryNameSpan.className = \"ms-2\";\n    countryNameSpan.textContent = `${name} (+${dialCode})`;\n    countryCell.appendChild(flagDiv);\n    countryCell.appendChild(countryNameSpan);\n\n    const iso2Cell = document.createElement(\"td\");\n    iso2Cell.textContent = iso2;\n\n    row.appendChild(countryCell);\n    row.appendChild(iso2Cell);\n    fragment.appendChild(row);\n  });\n\n  iso2ModalTableBody.appendChild(fragment);\n}\n\nif (iso2ModalEl) {\n  iso2ModalEl.addEventListener(\"show.bs.modal\", () => {\n    renderSupportedCountriesTable();\n  });\n}\n\n// --- ISO2 array textarea validation ---\nconst ISO2_TEXTAREA_KEYS = [\"countryOrder\", \"excludeCountries\", \"onlyCountries\"];\n\nfunction getValidIso2Set() {\n  return new Set(getSupportedCountries().map((c) => c.iso2));\n}\n\nfunction isValidIso2Array(value, validIso2s) {\n  const trimmed = value.trim();\n  if (trimmed === \"\") return null; // empty = neutral, no class\n  let parsed;\n  try {\n    parsed = JSON.parse(trimmed.replace(/'/g, \"\\\"\"));\n  } catch {\n    return false;\n  }\n  if (!Array.isArray(parsed)) return false;\n  return parsed.every((item) => typeof item === \"string\" && validIso2s.has(item.toLowerCase()));\n}\n\nfunction validateIso2Textarea(textarea, { invalidOnly = false } = {}) {\n  const validIso2s = getValidIso2Set();\n  const result = isValidIso2Array(textarea.value, validIso2s);\n  textarea.classList.remove(\"is-valid\", \"is-invalid\");\n  if (result === true && !invalidOnly) {\n    textarea.classList.add(\"is-valid\");\n  } else if (result === false) {\n    textarea.classList.add(\"is-invalid\");\n  }\n}\n\nISO2_TEXTAREA_KEYS.forEach((key) => {\n  const textarea = optionsForm.querySelector(`[data-option='${key}']`);\n  if (textarea) {\n    textarea.addEventListener(\"input\", () => validateIso2Textarea(textarea));\n    // on load, only flag invalid values (don't show green for valid)\n    validateIso2Textarea(textarea, { invalidOnly: true });\n  }\n});\n\n// --- initialCountry input validation ---\nfunction validateInitialCountryInput(input, { invalidOnly = false } = {}) {\n  const trimmed = input.value.trim();\n  input.classList.remove(\"is-valid\", \"is-invalid\");\n  if (trimmed === \"\") return; // empty = neutral\n  if (trimmed.toLowerCase() === \"auto\" || getValidIso2Set().has(trimmed.toLowerCase())) {\n    if (!invalidOnly) input.classList.add(\"is-valid\");\n  } else {\n    input.classList.add(\"is-invalid\");\n  }\n}\n\nconst initialCountryInput = optionsForm.querySelector(\"[data-option='initialCountry']\");\nif (initialCountryInput) {\n  initialCountryInput.addEventListener(\"input\", () => validateInitialCountryInput(initialCountryInput));\n  validateInitialCountryInput(initialCountryInput, { invalidOnly: true });\n}\n\n// --- countryNameLocale input validation ---\nfunction isValidLocale(value) {\n  try {\n    new Intl.DisplayNames([value], { type: \"region\" });\n    return true;\n  } catch {\n    return false;\n  }\n}\n\nfunction validateCountryNameLocaleInput(input, { invalidOnly = false } = {}) {\n  const trimmed = input.value.trim();\n  input.classList.remove(\"is-valid\", \"is-invalid\");\n  if (trimmed === \"\") return; // empty = neutral\n  if (isValidLocale(trimmed)) {\n    if (!invalidOnly) input.classList.add(\"is-valid\");\n  } else {\n    input.classList.add(\"is-invalid\");\n  }\n}\n\nconst countryNameLocaleInput = optionsForm.querySelector(\"[data-option='countryNameLocale']\");\nif (countryNameLocaleInput) {\n  countryNameLocaleInput.addEventListener(\"input\", () => validateCountryNameLocaleInput(countryNameLocaleInput));\n  validateCountryNameLocaleInput(countryNameLocaleInput, { invalidOnly: true });\n}\n"
  },
  {
    "path": "site/src/playground/js/templates/playgroundConstants.js.ejs",
    "content": "// NOTE: DO NOT IMPORT THIS FILE\n// This file is built by grunt/template.js and output to the tmp dir\n\nexport const I18N_LANGUAGE_CODES = JSON.parse('<%= JSON.stringify(i18nLanguages || []) %>');\nexport const UTILS_PATH = \"<%= cacheBust('/intl-tel-input/js/utils.js') %>\";\nexport const I18N_DIR_HASH = \"<%= getDirHash('/intl-tel-input/js/i18n') %>\";\n"
  },
  {
    "path": "site/src/playground/playground_content.html",
    "content": "<div class=\"iti-playground\" id=\"itiPlayground\">\n\n  <template id=\"itiPlaygroundInfoIconTemplate\">\n    <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\n      <path d=\"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16\"/>\n      <path d=\"m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0\"/>\n    </svg>\n  </template>\n\n  <template id=\"itiPlaygroundOptionGroupTemplate\">\n    <div class=\"card\">\n      <div class=\"card-body\">\n        <div class=\"d-flex align-items-center justify-content-between gap-2\">\n          <h2 class=\"h5 mb-0\" data-role=\"title\"></h2>\n          <div class=\"d-flex gap-2\">\n            <button\n              type=\"button\"\n              class=\"btn btn-outline-secondary btn-sm playground-action-btn\"\n              data-role=\"reset\"\n              data-playground-reset=\"options\"\n            >\n              <i class=\"bi bi-arrow-counterclockwise me-1\" aria-hidden=\"true\"></i>\n              <span data-role=\"label\">Reset</span>\n            </button>\n          </div>\n        </div>\n        <p class=\"text-muted mt-2 mb-3\" data-role=\"description\"></p>\n        <div class=\"vstack gap-3\" data-role=\"stack\"></div>\n      </div>\n    </div>\n  </template>\n\n  <div class=\"mb-4\">\n    <div class=\"d-flex align-items-start justify-content-between gap-3\">\n      <h1 class=\"h3 mb-0\">Playground</h1>\n      <div class=\"ms-auto text-end\">\n        <label for=\"playgroundPresetsSelect\" class=\"form-label visually-hidden\">Playground presets</label>\n        <div class=\"d-flex gap-2\">\n          <select id=\"playgroundPresetsSelect\" class=\"form-select form-select-sm playground-presets-select\" aria-label=\"Playground presets\">\n            <option value=\"\">Presets</option>\n            <option value=\"/playground?geoIpLookup=true&initialCountry=auto#country-options\">Auto country lookup</option>\n            <option value=\"/playground?initialCountry=us&strictMode=true#formatting-options\">Strict numeric mode</option>\n            <option value=\"/playground?initialCountry=gb&separateDialCode=true#user-interface-options\">Separate dial code</option>\n            <option value=\"/playground?keepDropdownOpen=false&allowDropdown=false&initialCountry=us#user-interface-options\">No dropdown</option>\n            <option value=\"/playground?initialCountry=fr&onlyCountries=%5B%22al%22%2C%22ad%22%2C%22at%22%2C%22by%22%2C%22be%22%2C%22ba%22%2C%22bg%22%2C%22hr%22%2C%22cz%22%2C%22dk%22%2C%22ee%22%2C%22fo%22%2C%22fi%22%2C%22fr%22%2C%22de%22%2C%22gi%22%2C%22gr%22%2C%22va%22%2C%22hu%22%2C%22is%22%2C%22ie%22%2C%22it%22%2C%22lv%22%2C%22li%22%2C%22lt%22%2C%22lu%22%2C%22mk%22%2C%22mt%22%2C%22md%22%2C%22mc%22%2C%22me%22%2C%22nl%22%2C%22no%22%2C%22pl%22%2C%22pt%22%2C%22ro%22%2C%22ru%22%2C%22sm%22%2C%22rs%22%2C%22sk%22%2C%22si%22%2C%22es%22%2C%22se%22%2C%22ch%22%2C%22ua%22%2C%22gb%22%5D#country-options\">Only European countries</option>\n            <option value=\"/playground?countryNameLocale=zh&i18n=zh&initialCountry=cn#translation-options\">Chinese UI</option>\n          </select>\n        </div>\n      </div>\n    </div>\n    <p class=\"text-muted mt-3 mb-3\">\n      Try different <code>intl-tel-input</code> initialisation options and see the plugin update live.\n      <span class=\"d-none d-lg-inline\">\n        <br />\n        (The URL updates with each change, so it's easy to share your setup)\n      </span>\n    </p>\n  </div>\n\n  <div class=\"iti-playground-layout\">\n    <div class=\"iti-playground-layout__col iti-playground-layout__col--left\">\n      <div class=\"iti-playground-layout__options\">\n        <form id=\"playgroundOptions\" class=\"vstack gap-3\" aria-label=\"intl-tel-input initialisation options\">\n          <!-- JS renders group cards here -->\n        </form>\n      </div>\n\n      <div class=\"iti-playground-layout__attrs\">\n        <div class=\"card\">\n          <div class=\"card-body\">\n            <div class=\"d-flex align-items-center justify-content-between gap-2\">\n              <h2 class=\"h5 mb-0\" id=\"input-attributes\">\n                <a href=\"#input-attributes\" class=\"header-anchor\">Input Attributes</a>\n              </h2>\n              <div class=\"d-flex gap-2\">\n                <button type=\"button\" class=\"btn btn-outline-secondary btn-sm playground-action-btn\" id=\"playgroundResetAttrs\">\n                  <i class=\"bi bi-arrow-counterclockwise me-1\" aria-hidden=\"true\"></i>\n                  <span data-role=\"label\">Reset</span>\n                </button>\n              </div>\n            </div>\n            <p class=\"text-muted mt-2 mb-3\">\n              These input attributes persist as you change the options above, so you can test how different options work in different scenarios.\n            </p>\n\n            <form id=\"playgroundAttributes\" class=\"vstack gap-3\" aria-label=\"Phone input attributes\">\n              <!-- JS renders controls here -->\n            </form>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"iti-playground-layout__col iti-playground-layout__col--right\">\n      <div class=\"iti-playground-layout__demo\">\n        <div class=\"card\">\n          <div class=\"card-body\">\n            <div class=\"d-flex align-items-center justify-content-between gap-2\">\n              <h2 class=\"h5 mb-0\">Live Demo</h2>\n              <div class=\"d-flex gap-2\">\n                <button type=\"button\" class=\"btn btn-outline-secondary btn-sm playground-action-btn\" id=\"playgroundResetAll\">\n                  <i class=\"bi bi-arrow-counterclockwise me-1\" aria-hidden=\"true\"></i>\n                  <span data-role=\"label\">Reset All</span>\n                </button>\n                <button type=\"button\" class=\"btn btn-outline-secondary btn-sm playground-action-btn\" id=\"playgroundShareBtn\">\n                  <i class=\"bi bi-share me-1\" aria-hidden=\"true\"></i>\n                  <span data-role=\"label\">Share</span>\n                </button>\n              </div>\n            </div>\n            <p class=\"text-muted mt-2 mb-3\">\n              This is reset each time the options are changed. For a persistent number, see the Input Attributes section.\n            </p>\n\n            <input type=\"tel\" id=\"playgroundPhone\" class=\"form-control\" title=\"Enter your phone number\" />\n            <div class=\"form-check mt-2 playground-dropdown-checkbox\">\n              <input class=\"form-check-input\" type=\"checkbox\" id=\"playgroundKeepDropdownOpen\" checked />\n              <label class=\"form-check-label\" for=\"playgroundKeepDropdownOpen\">Keep dropdown open</label>\n            </div>\n            <div class=\"iti-live-results\">Type a number above to see the output here</div>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"iti-playground-layout__code\">\n        <div class=\"card\">\n          <div class=\"card-body\">\n            <div class=\"d-flex align-items-center justify-content-between gap-2\">\n              <h2 class=\"h5 mb-0\">Initialisation Code</h2>\n              <div class=\"d-flex gap-2\">\n                <button type=\"button\" class=\"btn btn-outline-secondary btn-sm playground-action-btn\" id=\"playgroundCopyInitCode\" aria-label=\"Copy initialisation code\">\n                  <i class=\"bi bi-clipboard me-1\" aria-hidden=\"true\"></i>\n                  <span data-role=\"label\">Copy code</span>\n                </button>\n              </div>\n            </div>\n            <p class=\"text-muted mt-2 mb-3\">\n              JavaScript snippet for initialising the plugin with your (non-default) options. By default, we add <code>loadUtils</code> for formatting/placeholders.\n            </p>\n            <pre class=\"mb-0 language-javascript\"><code id=\"playgroundInitCode\" class=\"language-javascript\"></code></pre>\n          </div>\n        </div>\n      </div>\n    </div>\n    </div>\n  </div>\n\n  <div\n    class=\"modal fade\"\n    id=\"itiPlaygroundIso2Modal\"\n    tabindex=\"-1\"\n    aria-labelledby=\"itiPlaygroundIso2ModalLabel\"\n    aria-hidden=\"true\"\n  >\n    <div class=\"modal-dialog modal-dialog-scrollable\">\n      <div class=\"modal-content\">\n        <div class=\"modal-header\">\n          <h2 class=\"modal-title h5\" id=\"itiPlaygroundIso2ModalLabel\">Supported ISO2 country codes</h2>\n          <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button>\n        </div>\n        <div class=\"modal-body\">\n          <div class=\"table-responsive\">\n            <table class=\"table table-sm mb-0\">\n              <thead>\n                <tr>\n                  <th scope=\"col\">Country</th>\n                  <th scope=\"col\">ISO2</th>\n                </tr>\n              </thead>\n              <tbody id=\"itiPlaygroundIso2ModalTableBody\"></tbody>\n            </table>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "site/src/playground/playground_page_template.html.ejs",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <%= common_meta_tags %>\n    <title><%= playgroundTitle %></title>\n    <link rel=\"canonical\" href=\"<%= playgroundCanonicalUrl %>\" />\n    <meta name=\"description\" content=\"<%= playgroundMetaDesc %>\" />\n\n    <%= og_meta_tags %>\n    <%= common_styles %>\n    <link rel=\"stylesheet\" href=\"<%= cacheBust('/css/playground.css') %>\" />\n\n    <%= common_head_end_prod %>\n  </head>\n\n  <body class=\"iti-page iti-page--playground\">\n    <%= layout %>\n    <%= common_body_end %>\n    <%= iti_script %>\n    <script src=\"<%= cacheBust('/js/playground.js') %>\"></script>\n    <%= iti_live_results_script %>\n  </body>\n</html>\n"
  },
  {
    "path": "site/src/shared/common_body_end.html",
    "content": "<script\n  src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js\"\n  integrity=\"sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI\"\n  crossorigin=\"anonymous\"\n></script>\n\n<script src=\"<%= cacheBust('/js/highlight.min.js') %>\"></script>\n<script>hljs.highlightAll();</script>\n\n<script src=\"https://cdn.jsdelivr.net/npm/@docsearch/js@4\"></script>\n<script>\n  docsearch({\n    container: \"#docsearch\",\n    appId: \"30NQZ1MMUC\",\n    indexName: \"ITI Site\",\n    apiKey: \"36bc251934d6bbefeb56f187686f4810\",\n  });\n\n  // mobile search icon trigger\n  const algoliaButton = document.querySelector('#docsearch button');\n  const mobileSearchButton = document.getElementById('itiSearchButtonMobile');\n  mobileSearchButton.addEventListener('click', () => algoliaButton.click());\n</script>"
  },
  {
    "path": "site/src/shared/common_head_end_prod.html",
    "content": "<!-- PostHog error reporting / replays -->\n<script>\n    // our window.__IS_EUROPE variable must be defined and set to false\n    const initPostHog = typeof window.__IS_EUROPE === \"boolean\" && !window.__IS_EUROPE;\n    if (initPostHog) {\n        !function(t,e){var o,n,p,r;e.__SV||(window.posthog && window.posthog.__loaded)||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(\".\");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement(\"script\")).type=\"text/javascript\",p.crossOrigin=\"anonymous\",p.async=!0,p.src=s.api_host.replace(\".i.posthog.com\",\"-assets.i.posthog.com\")+\"/static/array.js\",(r=t.getElementsByTagName(\"script\")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a=\"posthog\",u.people=u.people||[],u.toString=function(t){var e=\"posthog\";return\"posthog\"!==a&&(e+=\".\"+a),t||(e+=\" (stub)\"),e},u.people.toString=function(){return u.toString(1)+\".people (stub)\"},o=\"init ir nr qi rr ar Ze er capture calculateEventProperties dr register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload getFeatureFlagResult isFeatureEnabled reloadFeatureFlags updateFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSurveysLoaded onSessionId getSurveys getActiveMatchingSurveys renderSurvey displaySurvey cancelPendingSurvey canRenderSurvey canRenderSurveyAsync identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException startExceptionAutocapture stopExceptionAutocapture loadToolbar get_property getSessionProperty cr hr createPersonProfile setInternalOrTestUser pr Xe gr opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing get_explicit_consent_status is_capturing clear_opt_in_out_capturing lr debug At vr getPageViewId captureTraceFeedback captureTraceMetric Je\".split(\" \"),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);\n        posthog.init('phc_ousZGP4WHTH1bs6BrGhZEyj91cUq1cK8bEBSyxTWNsa', {\n            api_host: 'https://eu.i.posthog.com',\n            defaults: '2026-01-30',\n        });\n    }\n</script>\n\n<!-- Google AdSense -->\n<script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-1090343328224651\" crossorigin=\"anonymous\"></script>\n"
  },
  {
    "path": "site/src/shared/common_meta_tags.html",
    "content": "<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n<link rel=\"icon\" type=\"image/x-icon\" href=\"/img/favicon.png\">\n"
  },
  {
    "path": "site/src/shared/common_styles.html.ejs",
    "content": "<!-- Bootstrap styles -->\n<link\n  href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css\"\n  rel=\"stylesheet\"\n  integrity=\"sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB\"\n  crossorigin=\"anonymous\"\n/>\n<link\n  href=\"https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css\"\n  rel=\"stylesheet\"\n  crossorigin=\"anonymous\"\n/>\n<!-- Bootstrap v5.3 requires a data-bs-theme attribute to support dark mode -->\n<script>\n  const setTheme = () => {\n    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n    const theme = mediaQuery.matches ? 'dark' : 'light';\n    document.documentElement.setAttribute('data-bs-theme', theme);\n  };\n  setTheme();\n  window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', setTheme);\n</script>\n\n<!-- ITI styles -->\n<% if (iti_styles === \"normal\") { %>\n  <% const cssFile = isDevBuild ? 'intlTelInput.css' : 'intlTelInput.min.css'; %>\n  <link\n    rel=\"stylesheet\"\n    href=\"<%= cacheBust('/intl-tel-input/css/' + cssFile) %>\"\n  />\n<% } else if (iti_styles === \"largeFlags\") { %>\n  <link rel=\"stylesheet\" href=\"<%= cacheBust('/css/intlTelInput-largeFlags.css') %>\">\n<% } else if (iti_styles === \"homepage\") { %>\n  <!-- show regular on mobile and largeFlags version on desktop -->\n  <% const itiCss = isDevBuild ? 'intlTelInput.css' : 'intlTelInput.min.css'; %>\n  <link rel=\"stylesheet\" href=\"<%= cacheBust('/intl-tel-input/css/' + itiCss) %>\" media=\"screen and (max-width: 991px)\">\n  <link rel=\"stylesheet\" href=\"<%= cacheBust('/css/intlTelInput-largeFlags.css') %>\" media=\"screen and (min-width: 992px)\">\n<% } %>\n\n<!-- HighlightJS styles -->\n<% if (typeof highlightjs_styles !== 'undefined') { %>\n  <link rel=\"stylesheet\" href=\"<%= cacheBust('/css/highlight-github-light.min.css') %>\" media=\"(prefers-color-scheme: light), (prefers-color-scheme: no-preference)\">\n  <link rel=\"stylesheet\" href=\"<%= cacheBust('/css/highlight-github-dark.min.css') %>\" media=\"(prefers-color-scheme: dark)\">\n  <link rel=\"stylesheet\" href=\"<%= cacheBust('/css/highlightjs_overrides.css') %>\">\n<% } %>\n\n<!-- Algolia DocSearch styles -->\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@docsearch/css@4\" />\n\n<!-- Website styles: must come after ITI styles as contains dark mode overrides -->\n<link rel=\"stylesheet\" href=\"<%= cacheBust('/css/website.css') %>\" />\n\n<!-- Large flags overrides: must come after website styles -->\n<% if (iti_styles === \"largeFlags\") { %>\n  <link rel=\"stylesheet\" href=\"<%= cacheBust('/css/large_flags_overrides.css') %>\">\n<% } else if (iti_styles === \"homepage\") { %>\n  <link rel=\"stylesheet\" href=\"<%= cacheBust('/css/large_flags_overrides.css') %>\" media=\"screen and (min-width: 992px)\">\n<% } %>\n"
  },
  {
    "path": "site/src/shared/iti_live_results_script.html.ejs",
    "content": "<script src=\"<%= cacheBust('/js/iti-live-results.js') %>\"></script>\n"
  },
  {
    "path": "site/src/shared/iti_script.html.ejs",
    "content": "<% const scriptFile = isDevBuild ? 'intlTelInput.js' : 'intlTelInput.min.js'; %>\n<script src=\"<%= cacheBust('/intl-tel-input/js/' + scriptFile) %>\"></script>\n"
  },
  {
    "path": "site/static/_redirects",
    "content": "# ancient links from when example pages were in ITI project, loaded into website project through node_modules\n/node_modules/intl-tel-input/examples/gen/is-valid-number.html /examples/validation-practical 301\n/node_modules/intl-tel-input/examples/gen/only-countries-europe.html /playground?initialCountry=fr&onlyCountries=%5B%22al%22%2C%22ad%22%2C%22at%22%2C%22by%22%2C%22be%22%2C%22ba%22%2C%22bg%22%2C%22hr%22%2C%22cz%22%2C%22dk%22%2C%22ee%22%2C%22fo%22%2C%22fi%22%2C%22fr%22%2C%22de%22%2C%22gi%22%2C%22gr%22%2C%22va%22%2C%22hu%22%2C%22is%22%2C%22ie%22%2C%22it%22%2C%22lv%22%2C%22li%22%2C%22lt%22%2C%22lu%22%2C%22mk%22%2C%22mt%22%2C%22md%22%2C%22mc%22%2C%22me%22%2C%22nl%22%2C%22no%22%2C%22pl%22%2C%22pt%22%2C%22ro%22%2C%22ru%22%2C%22sm%22%2C%22rs%22%2C%22sk%22%2C%22si%22%2C%22es%22%2C%22se%22%2C%22ch%22%2C%22ua%22%2C%22gb%22%5D#country-options 301\n\n# old links when examples were served from separate build dir\n/build/examples/single-country.html /examples/single-country 301\n/build/examples/validation-practical.html /examples/validation-practical 301\n/build/examples/display-number.html /examples/display-number 301\n/build/examples/react-component.html /examples/react-component 301\n/build/examples/validation-precise.html /examples/validation-precise 301\n/build/examples/internationalisation.html /playground?countryNameLocale=zh&i18n=zh&initialCountry=cn#translation-options 301\n/build/examples/hidden-input.html /examples/hidden-input 301\n/build/examples/lookup-country.html /examples/lookup-country 301\n/build/examples/only-countries.html /playground?initialCountry=fr&onlyCountries=%5B%22al%22%2C%22ad%22%2C%22at%22%2C%22by%22%2C%22be%22%2C%22ba%22%2C%22bg%22%2C%22hr%22%2C%22cz%22%2C%22dk%22%2C%22ee%22%2C%22fo%22%2C%22fi%22%2C%22fr%22%2C%22de%22%2C%22gi%22%2C%22gr%22%2C%22va%22%2C%22hu%22%2C%22is%22%2C%22ie%22%2C%22it%22%2C%22lv%22%2C%22li%22%2C%22lt%22%2C%22lu%22%2C%22mk%22%2C%22mt%22%2C%22md%22%2C%22mc%22%2C%22me%22%2C%22nl%22%2C%22no%22%2C%22pl%22%2C%22pt%22%2C%22ro%22%2C%22ru%22%2C%22sm%22%2C%22rs%22%2C%22sk%22%2C%22si%22%2C%22es%22%2C%22se%22%2C%22ch%22%2C%22ua%22%2C%22gb%22%5D#country-options 301\n\n# previous example page URLs\n/examples/separate-dial-code.html /playground?initialCountry=gb&separateDialCode=true#user-interface-options 301\n/examples/show-selected-dial-code.html /playground?initialCountry=gb&separateDialCode=true#user-interface-options 301\n/examples/strict-mode.html /playground?initialCountry=us&strictMode=true#formatting-options\n/examples/localise-countries* /playground?countryNameLocale=ru&i18n=ru#translation-options 301\n/examples/internationalisation /playground?countryNameLocale=ru&i18n=ru#translation-options 301\n/examples/validation.html /examples/validation-precise 301\n/examples/validation /examples/validation-precise 301\n/examples/only-countries.html /playground?initialCountry=fr&onlyCountries=%5B%22al%22%2C%22ad%22%2C%22at%22%2C%22by%22%2C%22be%22%2C%22ba%22%2C%22bg%22%2C%22hr%22%2C%22cz%22%2C%22dk%22%2C%22ee%22%2C%22fo%22%2C%22fi%22%2C%22fr%22%2C%22de%22%2C%22gi%22%2C%22gr%22%2C%22va%22%2C%22hu%22%2C%22is%22%2C%22ie%22%2C%22it%22%2C%22lv%22%2C%22li%22%2C%22lt%22%2C%22lu%22%2C%22mk%22%2C%22mt%22%2C%22md%22%2C%22mc%22%2C%22me%22%2C%22nl%22%2C%22no%22%2C%22pl%22%2C%22pt%22%2C%22ro%22%2C%22ru%22%2C%22sm%22%2C%22rs%22%2C%22sk%22%2C%22si%22%2C%22es%22%2C%22se%22%2C%22ch%22%2C%22ua%22%2C%22gb%22%5D#country-options 301\n\n# other pages\n/storybook /playground 301\n/storybook/* /playground 301\n\n# root directory redirects\n/docs /docs/choose-integration 302\n/docs/ /docs/choose-integration 302\n/examples /examples/validation-practical 302\n/examples/ /examples/validation-practical 302\n"
  },
  {
    "path": "site/static/ads.txt",
    "content": "google.com, pub-1090343328224651, DIRECT, f08c47fec0942fa0"
  },
  {
    "path": "site/static/css/intlTelInput-largeFlags.css",
    "content": ":root {\n  --iti-hover-color: rgba(0, 0, 0, 0.05);\n  --iti-border-color: #ccc;\n  --iti-dropdown-bg: white;\n  --iti-icon-color: #555;\n  --iti-spacer-horizontal: 8px;\n  --iti-flag-height: 18px;\n  --iti-flag-width: 24px;\n  --iti-globe-height: 16px;\n  --iti-search-clear-icon-height: 13px;\n  --iti-border-width: 1px;\n  --iti-arrow-height: 4px;\n  --iti-arrow-width: calc((var(--iti-arrow-height) / 2) * 3);\n  --iti-triangle-border: calc(var(--iti-arrow-width) / 2);\n  --iti-arrow-padding: 6px;\n  --iti-flag-sprite-width: 5856px;\n  --iti-flag-sprite-height: 18px;\n  --iti-mobile-popup-margin: 30px;\n}\n\n.iti {\n  position: relative;\n  display: inline-block;\n}\n.iti * {\n  box-sizing: border-box;\n}\n.iti__a11y-text {\n  width: 1px;\n  height: 1px;\n  clip: rect(1px, 1px, 1px, 1px);\n  overflow: hidden;\n  position: absolute;\n}\n.iti input.iti__tel-input,\n.iti input.iti__tel-input[type=text],\n.iti input.iti__tel-input[type=tel] {\n  position: relative;\n  z-index: 0;\n  margin: 0 !important;\n}\n.iti__country-container {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  padding: var(--iti-border-width);\n}\n.iti__selected-country {\n  z-index: 1;\n  position: relative;\n  display: flex;\n  align-items: center;\n  height: 100%;\n  background: none;\n  border: 0;\n  margin: 0;\n  padding: 0;\n  font-family: inherit;\n  font-size: inherit;\n  color: inherit;\n  border-radius: 0;\n  font-weight: inherit;\n  line-height: inherit;\n  text-decoration: none;\n}\n.iti__selected-country-primary {\n  display: flex;\n  align-items: center;\n  height: 100%;\n  padding: 0 var(--iti-arrow-padding) 0 var(--iti-spacer-horizontal);\n}\n.iti__arrow {\n  margin-left: var(--iti-arrow-padding);\n  width: 0;\n  height: 0;\n  border-left: var(--iti-triangle-border) solid transparent;\n  border-right: var(--iti-triangle-border) solid transparent;\n  border-top: var(--iti-arrow-height) solid var(--iti-icon-color);\n}\n.iti__arrow--up {\n  border-top: none;\n  border-bottom: var(--iti-arrow-height) solid var(--iti-icon-color);\n}\n.iti__dropdown-content {\n  border-radius: 3px;\n  background-color: var(--iti-dropdown-bg);\n}\n.iti--inline-dropdown .iti__dropdown-content {\n  border: var(--iti-border-width) solid var(--iti-border-color);\n  box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.2);\n}\n.iti--inline-dropdown:not(.iti--container) .iti__dropdown-content {\n  position: absolute;\n  z-index: 2;\n  left: 0;\n}\n.iti__search-input {\n  width: 100%;\n  border-width: 0;\n  border-radius: 3px;\n  padding-left: 30px;\n  padding-right: 28px;\n}\n[dir=rtl] .iti__search-input {\n  padding-left: inherit;\n  padding-right: 30px;\n  background-position: right 8px center;\n}\n.iti__search-input::-webkit-search-cancel-button {\n  appearance: none;\n}\n.iti__search-input, .iti__country {\n  padding-top: 8px;\n  padding-bottom: 8px;\n}\n.iti__search-input-wrapper {\n  position: relative;\n  display: flex;\n  align-items: center;\n  border-bottom: 1px solid var(--iti-border-color);\n}\n.iti__search-icon {\n  position: absolute;\n  left: 8px;\n  display: flex;\n  pointer-events: none;\n}\n[dir=rtl] .iti__search-icon {\n  left: auto;\n  right: 8px;\n}\n.iti__search-icon-svg {\n  width: var(--iti-globe-height);\n  height: var(--iti-globe-height);\n  display: block;\n  stroke: var(--iti-icon-color);\n  fill: none;\n  stroke-width: 3;\n}\n.iti__search-clear {\n  position: absolute;\n  right: 4px;\n  background: transparent;\n  border: 0;\n  border-radius: 3px;\n  cursor: pointer;\n  padding: 5px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  transition: background-color 0.15s ease;\n}\n.iti__search-clear .iti__search-clear-x {\n  stroke-width: 2;\n}\n.iti__search-clear .iti__search-clear-bg {\n  fill: var(--iti-icon-color);\n}\n.iti__search-clear-svg {\n  width: var(--iti-search-clear-icon-height);\n  height: var(--iti-search-clear-icon-height);\n  display: block;\n}\n[dir=rtl] .iti__search-clear {\n  right: auto;\n  left: 4px;\n}\n.iti__search-clear:hover, .iti__search-clear:focus-visible {\n  background: var(--iti-hover-color);\n  outline: none;\n}\n.iti__no-results {\n  text-align: center;\n  padding: 30px 0;\n}\n.iti__country-list {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n  cursor: pointer;\n  overflow-y: scroll;\n  -webkit-overflow-scrolling: touch;\n}\n.iti--inline-dropdown .iti__country-list {\n  max-height: 185px;\n}\n.iti--flexible-dropdown-width .iti__country-list {\n  white-space: nowrap;\n}\n@media (max-width: 500px) {\n  .iti--flexible-dropdown-width .iti__country-list {\n    white-space: normal;\n  }\n}\n.iti__country {\n  display: flex;\n  align-items: center;\n  padding-left: var(--iti-spacer-horizontal);\n  padding-right: var(--iti-spacer-horizontal);\n  outline: none;\n}\n.iti__country-name {\n  flex-grow: 1;\n}\n.iti__country-check {\n  margin: 0 1px 0 var(--iti-spacer-horizontal);\n  display: flex;\n  align-items: center;\n  color: var(--iti-icon-color);\n}\n.iti__country-check-svg {\n  width: var(--iti-search-clear-icon-height);\n  height: var(--iti-search-clear-icon-height);\n  display: block;\n}\n.iti__country.iti__highlight {\n  background-color: var(--iti-hover-color);\n}\n.iti__country-list .iti__flag {\n  margin-right: var(--iti-spacer-horizontal);\n}\n[dir=rtl] .iti__country-list .iti__flag {\n  margin-right: 0;\n  margin-left: var(--iti-spacer-horizontal);\n}\n.iti__country-list .iti__flag {\n  flex-shrink: 0;\n}\n.iti--allow-dropdown .iti__country-container:has(+ input[disabled]) button.iti__selected-country,\n.iti--allow-dropdown .iti__country-container:has(+ input[readonly]) button.iti__selected-country {\n  cursor: not-allowed;\n}\n.iti--allow-dropdown .iti__country-container:has(+ input[disabled]) button.iti__selected-country .iti__arrow,\n.iti--allow-dropdown .iti__country-container:has(+ input[readonly]) button.iti__selected-country .iti__arrow {\n  visibility: hidden;\n}\n.iti--allow-dropdown .iti__country-container:not(:has(+ input[disabled])):not(:has(+ input[readonly])) .iti__selected-country-primary:hover,\n.iti--allow-dropdown .iti__country-container:not(:has(+ input[disabled])):not(:has(+ input[readonly])) .iti__selected-country:has(+ .iti__dropdown-content:hover) .iti__selected-country-primary {\n  background-color: var(--iti-hover-color);\n}\n.iti .iti__selected-dial-code {\n  margin-left: 4px;\n}\n.iti--container {\n  position: fixed;\n  top: -1000px;\n  left: -1000px;\n  z-index: 1060;\n}\n.iti--container:hover {\n  cursor: pointer;\n}\n.iti__hide {\n  display: none;\n}\n.iti__v-hide {\n  visibility: hidden;\n}\n\n.iti--fullscreen-popup.iti--container {\n  background-color: rgba(0, 0, 0, 0.5);\n  top: 0;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  position: fixed;\n  padding: var(--iti-mobile-popup-margin);\n  display: flex;\n  flex-direction: column;\n  justify-content: flex-start;\n}\n.iti--fullscreen-popup .iti__dropdown-content {\n  display: flex;\n  flex-direction: column;\n  max-height: 100%;\n  position: relative;\n}\n.iti--fullscreen-popup .iti__country,\n.iti--fullscreen-popup .iti__search-input {\n  padding-top: 10px;\n  padding-bottom: 10px;\n}\n.iti--fullscreen-popup .iti__country {\n  padding-left: 10px;\n  padding-right: 10px;\n  line-height: 1.5em;\n}\n\n.iti__flag {\n  --iti-flag-offset: 100px;\n  height: var(--iti-flag-height);\n  width: var(--iti-flag-width);\n  border-radius: 1px;\n  box-shadow: 0px 0px 1px 0px #888;\n  background-image: image-set(var(--iti-path-flags-1x) 1x, var(--iti-path-flags-2x) 2x);\n  background-repeat: no-repeat;\n  background-position: var(--iti-flag-offset) 0;\n  background-size: var(--iti-flag-sprite-width) var(--iti-flag-sprite-height);\n}\n\n.iti__loading {\n  position: relative;\n  background: none;\n  box-shadow: none;\n}\n\n.iti__loading::after {\n  content: \"\";\n  position: absolute;\n  inset: 0;\n  margin: auto;\n  width: var(--iti-flag-height);\n  height: var(--iti-flag-height);\n  box-sizing: border-box;\n  border: 2px solid var(--iti-icon-color);\n  border-right-color: transparent;\n  border-radius: 50%;\n  animation: iti-spinner 1s linear infinite;\n}\n\n@keyframes iti-spinner {\n  to {\n    transform: rotate(360deg);\n  }\n}\n.iti__ac {\n  --iti-flag-offset: 0px;\n}\n\n.iti__ad {\n  --iti-flag-offset: -24px;\n}\n\n.iti__ae {\n  --iti-flag-offset: -48px;\n}\n\n.iti__af {\n  --iti-flag-offset: -72px;\n}\n\n.iti__ag {\n  --iti-flag-offset: -96px;\n}\n\n.iti__ai {\n  --iti-flag-offset: -120px;\n}\n\n.iti__al {\n  --iti-flag-offset: -144px;\n}\n\n.iti__am {\n  --iti-flag-offset: -168px;\n}\n\n.iti__ao {\n  --iti-flag-offset: -192px;\n}\n\n.iti__ar {\n  --iti-flag-offset: -216px;\n}\n\n.iti__as {\n  --iti-flag-offset: -240px;\n}\n\n.iti__at {\n  --iti-flag-offset: -264px;\n}\n\n.iti__au {\n  --iti-flag-offset: -288px;\n}\n\n.iti__aw {\n  --iti-flag-offset: -312px;\n}\n\n.iti__ax {\n  --iti-flag-offset: -336px;\n}\n\n.iti__az {\n  --iti-flag-offset: -360px;\n}\n\n.iti__ba {\n  --iti-flag-offset: -384px;\n}\n\n.iti__bb {\n  --iti-flag-offset: -408px;\n}\n\n.iti__bd {\n  --iti-flag-offset: -432px;\n}\n\n.iti__be {\n  --iti-flag-offset: -456px;\n}\n\n.iti__bf {\n  --iti-flag-offset: -480px;\n}\n\n.iti__bg {\n  --iti-flag-offset: -504px;\n}\n\n.iti__bh {\n  --iti-flag-offset: -528px;\n}\n\n.iti__bi {\n  --iti-flag-offset: -552px;\n}\n\n.iti__bj {\n  --iti-flag-offset: -576px;\n}\n\n.iti__bl {\n  --iti-flag-offset: -600px;\n}\n\n.iti__bm {\n  --iti-flag-offset: -624px;\n}\n\n.iti__bn {\n  --iti-flag-offset: -648px;\n}\n\n.iti__bo {\n  --iti-flag-offset: -672px;\n}\n\n.iti__bq {\n  --iti-flag-offset: -696px;\n}\n\n.iti__br {\n  --iti-flag-offset: -720px;\n}\n\n.iti__bs {\n  --iti-flag-offset: -744px;\n}\n\n.iti__bt {\n  --iti-flag-offset: -768px;\n}\n\n.iti__bw {\n  --iti-flag-offset: -792px;\n}\n\n.iti__by {\n  --iti-flag-offset: -816px;\n}\n\n.iti__bz {\n  --iti-flag-offset: -840px;\n}\n\n.iti__ca {\n  --iti-flag-offset: -864px;\n}\n\n.iti__cc {\n  --iti-flag-offset: -888px;\n}\n\n.iti__cd {\n  --iti-flag-offset: -912px;\n}\n\n.iti__cf {\n  --iti-flag-offset: -936px;\n}\n\n.iti__cg {\n  --iti-flag-offset: -960px;\n}\n\n.iti__ch {\n  --iti-flag-offset: -984px;\n}\n\n.iti__ci {\n  --iti-flag-offset: -1008px;\n}\n\n.iti__ck {\n  --iti-flag-offset: -1032px;\n}\n\n.iti__cl {\n  --iti-flag-offset: -1056px;\n}\n\n.iti__cm {\n  --iti-flag-offset: -1080px;\n}\n\n.iti__cn {\n  --iti-flag-offset: -1104px;\n}\n\n.iti__co {\n  --iti-flag-offset: -1128px;\n}\n\n.iti__cr {\n  --iti-flag-offset: -1152px;\n}\n\n.iti__cu {\n  --iti-flag-offset: -1176px;\n}\n\n.iti__cv {\n  --iti-flag-offset: -1200px;\n}\n\n.iti__cw {\n  --iti-flag-offset: -1224px;\n}\n\n.iti__cx {\n  --iti-flag-offset: -1248px;\n}\n\n.iti__cy {\n  --iti-flag-offset: -1272px;\n}\n\n.iti__cz {\n  --iti-flag-offset: -1296px;\n}\n\n.iti__de {\n  --iti-flag-offset: -1320px;\n}\n\n.iti__dj {\n  --iti-flag-offset: -1344px;\n}\n\n.iti__dk {\n  --iti-flag-offset: -1368px;\n}\n\n.iti__dm {\n  --iti-flag-offset: -1392px;\n}\n\n.iti__do {\n  --iti-flag-offset: -1416px;\n}\n\n.iti__dz {\n  --iti-flag-offset: -1440px;\n}\n\n.iti__ec {\n  --iti-flag-offset: -1464px;\n}\n\n.iti__ee {\n  --iti-flag-offset: -1488px;\n}\n\n.iti__eg {\n  --iti-flag-offset: -1512px;\n}\n\n.iti__eh {\n  --iti-flag-offset: -1536px;\n}\n\n.iti__er {\n  --iti-flag-offset: -1560px;\n}\n\n.iti__es {\n  --iti-flag-offset: -1584px;\n}\n\n.iti__et {\n  --iti-flag-offset: -1608px;\n}\n\n.iti__fi {\n  --iti-flag-offset: -1632px;\n}\n\n.iti__fj {\n  --iti-flag-offset: -1656px;\n}\n\n.iti__fk {\n  --iti-flag-offset: -1680px;\n}\n\n.iti__fm {\n  --iti-flag-offset: -1704px;\n}\n\n.iti__fo {\n  --iti-flag-offset: -1728px;\n}\n\n.iti__fr {\n  --iti-flag-offset: -1752px;\n}\n\n.iti__ga {\n  --iti-flag-offset: -1776px;\n}\n\n.iti__gb {\n  --iti-flag-offset: -1800px;\n}\n\n.iti__gd {\n  --iti-flag-offset: -1824px;\n}\n\n.iti__ge {\n  --iti-flag-offset: -1848px;\n}\n\n.iti__gf {\n  --iti-flag-offset: -1872px;\n}\n\n.iti__gg {\n  --iti-flag-offset: -1896px;\n}\n\n.iti__gh {\n  --iti-flag-offset: -1920px;\n}\n\n.iti__gi {\n  --iti-flag-offset: -1944px;\n}\n\n.iti__gl {\n  --iti-flag-offset: -1968px;\n}\n\n.iti__gm {\n  --iti-flag-offset: -1992px;\n}\n\n.iti__gn {\n  --iti-flag-offset: -2016px;\n}\n\n.iti__gp {\n  --iti-flag-offset: -2040px;\n}\n\n.iti__gq {\n  --iti-flag-offset: -2064px;\n}\n\n.iti__gr {\n  --iti-flag-offset: -2088px;\n}\n\n.iti__gt {\n  --iti-flag-offset: -2112px;\n}\n\n.iti__gu {\n  --iti-flag-offset: -2136px;\n}\n\n.iti__gw {\n  --iti-flag-offset: -2160px;\n}\n\n.iti__gy {\n  --iti-flag-offset: -2184px;\n}\n\n.iti__hk {\n  --iti-flag-offset: -2208px;\n}\n\n.iti__hn {\n  --iti-flag-offset: -2232px;\n}\n\n.iti__hr {\n  --iti-flag-offset: -2256px;\n}\n\n.iti__ht {\n  --iti-flag-offset: -2280px;\n}\n\n.iti__hu {\n  --iti-flag-offset: -2304px;\n}\n\n.iti__id {\n  --iti-flag-offset: -2328px;\n}\n\n.iti__ie {\n  --iti-flag-offset: -2352px;\n}\n\n.iti__il {\n  --iti-flag-offset: -2376px;\n}\n\n.iti__im {\n  --iti-flag-offset: -2400px;\n}\n\n.iti__in {\n  --iti-flag-offset: -2424px;\n}\n\n.iti__io {\n  --iti-flag-offset: -2448px;\n}\n\n.iti__iq {\n  --iti-flag-offset: -2472px;\n}\n\n.iti__ir {\n  --iti-flag-offset: -2496px;\n}\n\n.iti__is {\n  --iti-flag-offset: -2520px;\n}\n\n.iti__it {\n  --iti-flag-offset: -2544px;\n}\n\n.iti__je {\n  --iti-flag-offset: -2568px;\n}\n\n.iti__jm {\n  --iti-flag-offset: -2592px;\n}\n\n.iti__jo {\n  --iti-flag-offset: -2616px;\n}\n\n.iti__jp {\n  --iti-flag-offset: -2640px;\n}\n\n.iti__ke {\n  --iti-flag-offset: -2664px;\n}\n\n.iti__kg {\n  --iti-flag-offset: -2688px;\n}\n\n.iti__kh {\n  --iti-flag-offset: -2712px;\n}\n\n.iti__ki {\n  --iti-flag-offset: -2736px;\n}\n\n.iti__km {\n  --iti-flag-offset: -2760px;\n}\n\n.iti__kn {\n  --iti-flag-offset: -2784px;\n}\n\n.iti__kp {\n  --iti-flag-offset: -2808px;\n}\n\n.iti__kr {\n  --iti-flag-offset: -2832px;\n}\n\n.iti__kw {\n  --iti-flag-offset: -2856px;\n}\n\n.iti__ky {\n  --iti-flag-offset: -2880px;\n}\n\n.iti__kz {\n  --iti-flag-offset: -2904px;\n}\n\n.iti__la {\n  --iti-flag-offset: -2928px;\n}\n\n.iti__lb {\n  --iti-flag-offset: -2952px;\n}\n\n.iti__lc {\n  --iti-flag-offset: -2976px;\n}\n\n.iti__li {\n  --iti-flag-offset: -3000px;\n}\n\n.iti__lk {\n  --iti-flag-offset: -3024px;\n}\n\n.iti__lr {\n  --iti-flag-offset: -3048px;\n}\n\n.iti__ls {\n  --iti-flag-offset: -3072px;\n}\n\n.iti__lt {\n  --iti-flag-offset: -3096px;\n}\n\n.iti__lu {\n  --iti-flag-offset: -3120px;\n}\n\n.iti__lv {\n  --iti-flag-offset: -3144px;\n}\n\n.iti__ly {\n  --iti-flag-offset: -3168px;\n}\n\n.iti__ma {\n  --iti-flag-offset: -3192px;\n}\n\n.iti__mc {\n  --iti-flag-offset: -3216px;\n}\n\n.iti__md {\n  --iti-flag-offset: -3240px;\n}\n\n.iti__me {\n  --iti-flag-offset: -3264px;\n}\n\n.iti__mf {\n  --iti-flag-offset: -3288px;\n}\n\n.iti__mg {\n  --iti-flag-offset: -3312px;\n}\n\n.iti__mh {\n  --iti-flag-offset: -3336px;\n}\n\n.iti__mk {\n  --iti-flag-offset: -3360px;\n}\n\n.iti__ml {\n  --iti-flag-offset: -3384px;\n}\n\n.iti__mm {\n  --iti-flag-offset: -3408px;\n}\n\n.iti__mn {\n  --iti-flag-offset: -3432px;\n}\n\n.iti__mo {\n  --iti-flag-offset: -3456px;\n}\n\n.iti__mp {\n  --iti-flag-offset: -3480px;\n}\n\n.iti__mq {\n  --iti-flag-offset: -3504px;\n}\n\n.iti__mr {\n  --iti-flag-offset: -3528px;\n}\n\n.iti__ms {\n  --iti-flag-offset: -3552px;\n}\n\n.iti__mt {\n  --iti-flag-offset: -3576px;\n}\n\n.iti__mu {\n  --iti-flag-offset: -3600px;\n}\n\n.iti__mv {\n  --iti-flag-offset: -3624px;\n}\n\n.iti__mw {\n  --iti-flag-offset: -3648px;\n}\n\n.iti__mx {\n  --iti-flag-offset: -3672px;\n}\n\n.iti__my {\n  --iti-flag-offset: -3696px;\n}\n\n.iti__mz {\n  --iti-flag-offset: -3720px;\n}\n\n.iti__na {\n  --iti-flag-offset: -3744px;\n}\n\n.iti__nc {\n  --iti-flag-offset: -3768px;\n}\n\n.iti__ne {\n  --iti-flag-offset: -3792px;\n}\n\n.iti__nf {\n  --iti-flag-offset: -3816px;\n}\n\n.iti__ng {\n  --iti-flag-offset: -3840px;\n}\n\n.iti__ni {\n  --iti-flag-offset: -3864px;\n}\n\n.iti__nl {\n  --iti-flag-offset: -3888px;\n}\n\n.iti__no {\n  --iti-flag-offset: -3912px;\n}\n\n.iti__np {\n  --iti-flag-offset: -3936px;\n}\n\n.iti__nr {\n  --iti-flag-offset: -3960px;\n}\n\n.iti__nu {\n  --iti-flag-offset: -3984px;\n}\n\n.iti__nz {\n  --iti-flag-offset: -4008px;\n}\n\n.iti__om {\n  --iti-flag-offset: -4032px;\n}\n\n.iti__pa {\n  --iti-flag-offset: -4056px;\n}\n\n.iti__pe {\n  --iti-flag-offset: -4080px;\n}\n\n.iti__pf {\n  --iti-flag-offset: -4104px;\n}\n\n.iti__pg {\n  --iti-flag-offset: -4128px;\n}\n\n.iti__ph {\n  --iti-flag-offset: -4152px;\n}\n\n.iti__pk {\n  --iti-flag-offset: -4176px;\n}\n\n.iti__pl {\n  --iti-flag-offset: -4200px;\n}\n\n.iti__pm {\n  --iti-flag-offset: -4224px;\n}\n\n.iti__pr {\n  --iti-flag-offset: -4248px;\n}\n\n.iti__ps {\n  --iti-flag-offset: -4272px;\n}\n\n.iti__pt {\n  --iti-flag-offset: -4296px;\n}\n\n.iti__pw {\n  --iti-flag-offset: -4320px;\n}\n\n.iti__py {\n  --iti-flag-offset: -4344px;\n}\n\n.iti__qa {\n  --iti-flag-offset: -4368px;\n}\n\n.iti__re {\n  --iti-flag-offset: -4392px;\n}\n\n.iti__ro {\n  --iti-flag-offset: -4416px;\n}\n\n.iti__rs {\n  --iti-flag-offset: -4440px;\n}\n\n.iti__ru {\n  --iti-flag-offset: -4464px;\n}\n\n.iti__rw {\n  --iti-flag-offset: -4488px;\n}\n\n.iti__sa {\n  --iti-flag-offset: -4512px;\n}\n\n.iti__sb {\n  --iti-flag-offset: -4536px;\n}\n\n.iti__sc {\n  --iti-flag-offset: -4560px;\n}\n\n.iti__sd {\n  --iti-flag-offset: -4584px;\n}\n\n.iti__se {\n  --iti-flag-offset: -4608px;\n}\n\n.iti__sg {\n  --iti-flag-offset: -4632px;\n}\n\n.iti__sh {\n  --iti-flag-offset: -4656px;\n}\n\n.iti__si {\n  --iti-flag-offset: -4680px;\n}\n\n.iti__sj {\n  --iti-flag-offset: -4704px;\n}\n\n.iti__sk {\n  --iti-flag-offset: -4728px;\n}\n\n.iti__sl {\n  --iti-flag-offset: -4752px;\n}\n\n.iti__sm {\n  --iti-flag-offset: -4776px;\n}\n\n.iti__sn {\n  --iti-flag-offset: -4800px;\n}\n\n.iti__so {\n  --iti-flag-offset: -4824px;\n}\n\n.iti__sr {\n  --iti-flag-offset: -4848px;\n}\n\n.iti__ss {\n  --iti-flag-offset: -4872px;\n}\n\n.iti__st {\n  --iti-flag-offset: -4896px;\n}\n\n.iti__sv {\n  --iti-flag-offset: -4920px;\n}\n\n.iti__sx {\n  --iti-flag-offset: -4944px;\n}\n\n.iti__sy {\n  --iti-flag-offset: -4968px;\n}\n\n.iti__sz {\n  --iti-flag-offset: -4992px;\n}\n\n.iti__tc {\n  --iti-flag-offset: -5016px;\n}\n\n.iti__td {\n  --iti-flag-offset: -5040px;\n}\n\n.iti__tg {\n  --iti-flag-offset: -5064px;\n}\n\n.iti__th {\n  --iti-flag-offset: -5088px;\n}\n\n.iti__tj {\n  --iti-flag-offset: -5112px;\n}\n\n.iti__tk {\n  --iti-flag-offset: -5136px;\n}\n\n.iti__tl {\n  --iti-flag-offset: -5160px;\n}\n\n.iti__tm {\n  --iti-flag-offset: -5184px;\n}\n\n.iti__tn {\n  --iti-flag-offset: -5208px;\n}\n\n.iti__to {\n  --iti-flag-offset: -5232px;\n}\n\n.iti__tr {\n  --iti-flag-offset: -5256px;\n}\n\n.iti__tt {\n  --iti-flag-offset: -5280px;\n}\n\n.iti__tv {\n  --iti-flag-offset: -5304px;\n}\n\n.iti__tw {\n  --iti-flag-offset: -5328px;\n}\n\n.iti__tz {\n  --iti-flag-offset: -5352px;\n}\n\n.iti__ua {\n  --iti-flag-offset: -5376px;\n}\n\n.iti__ug {\n  --iti-flag-offset: -5400px;\n}\n\n.iti__us {\n  --iti-flag-offset: -5424px;\n}\n\n.iti__uy {\n  --iti-flag-offset: -5448px;\n}\n\n.iti__uz {\n  --iti-flag-offset: -5472px;\n}\n\n.iti__va {\n  --iti-flag-offset: -5496px;\n}\n\n.iti__vc {\n  --iti-flag-offset: -5520px;\n}\n\n.iti__ve {\n  --iti-flag-offset: -5544px;\n}\n\n.iti__vg {\n  --iti-flag-offset: -5568px;\n}\n\n.iti__vi {\n  --iti-flag-offset: -5592px;\n}\n\n.iti__vn {\n  --iti-flag-offset: -5616px;\n}\n\n.iti__vu {\n  --iti-flag-offset: -5640px;\n}\n\n.iti__wf {\n  --iti-flag-offset: -5664px;\n}\n\n.iti__ws {\n  --iti-flag-offset: -5688px;\n}\n\n.iti__xk {\n  --iti-flag-offset: -5712px;\n}\n\n.iti__ye {\n  --iti-flag-offset: -5736px;\n}\n\n.iti__yt {\n  --iti-flag-offset: -5760px;\n}\n\n.iti__za {\n  --iti-flag-offset: -5784px;\n}\n\n.iti__zm {\n  --iti-flag-offset: -5808px;\n}\n\n.iti__zw {\n  --iti-flag-offset: -5832px;\n}\n\n.iti__globe {\n  background: none;\n  box-shadow: none;\n  height: var(--iti-globe-height);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  padding: 0;\n}\n.iti__globe .iti__globe-svg {\n  width: 100%;\n  height: 100%;\n  fill: var(--iti-icon-color);\n}\n\n/* Browsers normally add a coloured outline when you focus an input. Chrome adds a blue outline WITHIN the input. If you focus the input and then hover the selected country, it's bg color square overlaps the focus outline and looks bad. Here, @supports is used to target Chrome only, and move the default outline out by 1px so there's no overlap. */\n@supports (-webkit-appearance: none) and (not (background: -webkit-canvas(foo))) {\n  .iti__tel-input:focus {\n    outline-offset: 1px;\n  }\n}\n:root {\n  --iti-path-flags-1x: url('../img/flags.webp');\n  --iti-path-flags-2x: url('../img/flags@2x.webp');\n}"
  },
  {
    "path": "site/static/screenshotting.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"/img/favicon.png\">\n    <title>ITI Screenshotting</title>\n\n    <link\n      href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css\"\n      rel=\"stylesheet\"\n      integrity=\"sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB\"\n      crossorigin=\"anonymous\"\n    />\n    <link\n      href=\"https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css\"\n      rel=\"stylesheet\"\n      crossorigin=\"anonymous\"\n    />\n    <script>\n      const setTheme = () => {\n        const theme = window.matchMedia('(prefers-color-scheme: dark)').matches\n          ? 'dark'\n          : 'light';\n        document.documentElement.setAttribute('data-bs-theme', theme);\n      };\n\n      // Run on load\n      setTheme();\n\n      // Update dynamically if the user changes system settings while on your page\n      window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', setTheme);\n    </script>\n\n    <link rel=\"stylesheet\" href=\"/intl-tel-input/css/intlTelInput.min.css?v=df24cc64d1d7\" />\n    <link rel=\"stylesheet\" href=\"/css/website.css?v=acf9a03fbf5c\" />\n  </head>\n\n  <body style=\"margin:50px\">\n    <input id=\"phone\" type=\"tel\" style=\"width:100px\" />\n\n    <script src=\"/intl-tel-input/js/intlTelInput.min.js?v=31d1fe9a88f4\"></script>\n    <script>\n      const input = document.querySelector(\"#phone\");\n      window.intlTelInput(input, {\n        useFullscreenPopup: true,\n        loadUtils: () => import(\"/intl-tel-input/js/utils.js?v=de02f033c674\"),\n      });\n    </script>\n  </body>\n</html>"
  },
  {
    "path": "src/css/_metadata.scss",
    "content": "//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\n\n$flags-sprite-1x: (\n  height: 12px,\n  width: 3904px,\n);\n\n$flag-width: 16px;\n\n$flag-height: 12px;\n\n$flags: (\n  ac: (\n    offset: 0px,\n  ),\n  ad: (\n    offset: -16px,\n  ),\n  ae: (\n    offset: -32px,\n  ),\n  af: (\n    offset: -48px,\n  ),\n  ag: (\n    offset: -64px,\n  ),\n  ai: (\n    offset: -80px,\n  ),\n  al: (\n    offset: -96px,\n  ),\n  am: (\n    offset: -112px,\n  ),\n  ao: (\n    offset: -128px,\n  ),\n  ar: (\n    offset: -144px,\n  ),\n  as: (\n    offset: -160px,\n  ),\n  at: (\n    offset: -176px,\n  ),\n  au: (\n    offset: -192px,\n  ),\n  aw: (\n    offset: -208px,\n  ),\n  ax: (\n    offset: -224px,\n  ),\n  az: (\n    offset: -240px,\n  ),\n  ba: (\n    offset: -256px,\n  ),\n  bb: (\n    offset: -272px,\n  ),\n  bd: (\n    offset: -288px,\n  ),\n  be: (\n    offset: -304px,\n  ),\n  bf: (\n    offset: -320px,\n  ),\n  bg: (\n    offset: -336px,\n  ),\n  bh: (\n    offset: -352px,\n  ),\n  bi: (\n    offset: -368px,\n  ),\n  bj: (\n    offset: -384px,\n  ),\n  bl: (\n    offset: -400px,\n  ),\n  bm: (\n    offset: -416px,\n  ),\n  bn: (\n    offset: -432px,\n  ),\n  bo: (\n    offset: -448px,\n  ),\n  bq: (\n    offset: -464px,\n  ),\n  br: (\n    offset: -480px,\n  ),\n  bs: (\n    offset: -496px,\n  ),\n  bt: (\n    offset: -512px,\n  ),\n  bw: (\n    offset: -528px,\n  ),\n  by: (\n    offset: -544px,\n  ),\n  bz: (\n    offset: -560px,\n  ),\n  ca: (\n    offset: -576px,\n  ),\n  cc: (\n    offset: -592px,\n  ),\n  cd: (\n    offset: -608px,\n  ),\n  cf: (\n    offset: -624px,\n  ),\n  cg: (\n    offset: -640px,\n  ),\n  ch: (\n    offset: -656px,\n  ),\n  ci: (\n    offset: -672px,\n  ),\n  ck: (\n    offset: -688px,\n  ),\n  cl: (\n    offset: -704px,\n  ),\n  cm: (\n    offset: -720px,\n  ),\n  cn: (\n    offset: -736px,\n  ),\n  co: (\n    offset: -752px,\n  ),\n  cr: (\n    offset: -768px,\n  ),\n  cu: (\n    offset: -784px,\n  ),\n  cv: (\n    offset: -800px,\n  ),\n  cw: (\n    offset: -816px,\n  ),\n  cx: (\n    offset: -832px,\n  ),\n  cy: (\n    offset: -848px,\n  ),\n  cz: (\n    offset: -864px,\n  ),\n  de: (\n    offset: -880px,\n  ),\n  dj: (\n    offset: -896px,\n  ),\n  dk: (\n    offset: -912px,\n  ),\n  dm: (\n    offset: -928px,\n  ),\n  do: (\n    offset: -944px,\n  ),\n  dz: (\n    offset: -960px,\n  ),\n  ec: (\n    offset: -976px,\n  ),\n  ee: (\n    offset: -992px,\n  ),\n  eg: (\n    offset: -1008px,\n  ),\n  eh: (\n    offset: -1024px,\n  ),\n  er: (\n    offset: -1040px,\n  ),\n  es: (\n    offset: -1056px,\n  ),\n  et: (\n    offset: -1072px,\n  ),\n  fi: (\n    offset: -1088px,\n  ),\n  fj: (\n    offset: -1104px,\n  ),\n  fk: (\n    offset: -1120px,\n  ),\n  fm: (\n    offset: -1136px,\n  ),\n  fo: (\n    offset: -1152px,\n  ),\n  fr: (\n    offset: -1168px,\n  ),\n  ga: (\n    offset: -1184px,\n  ),\n  gb: (\n    offset: -1200px,\n  ),\n  gd: (\n    offset: -1216px,\n  ),\n  ge: (\n    offset: -1232px,\n  ),\n  gf: (\n    offset: -1248px,\n  ),\n  gg: (\n    offset: -1264px,\n  ),\n  gh: (\n    offset: -1280px,\n  ),\n  gi: (\n    offset: -1296px,\n  ),\n  gl: (\n    offset: -1312px,\n  ),\n  gm: (\n    offset: -1328px,\n  ),\n  gn: (\n    offset: -1344px,\n  ),\n  gp: (\n    offset: -1360px,\n  ),\n  gq: (\n    offset: -1376px,\n  ),\n  gr: (\n    offset: -1392px,\n  ),\n  gt: (\n    offset: -1408px,\n  ),\n  gu: (\n    offset: -1424px,\n  ),\n  gw: (\n    offset: -1440px,\n  ),\n  gy: (\n    offset: -1456px,\n  ),\n  hk: (\n    offset: -1472px,\n  ),\n  hn: (\n    offset: -1488px,\n  ),\n  hr: (\n    offset: -1504px,\n  ),\n  ht: (\n    offset: -1520px,\n  ),\n  hu: (\n    offset: -1536px,\n  ),\n  id: (\n    offset: -1552px,\n  ),\n  ie: (\n    offset: -1568px,\n  ),\n  il: (\n    offset: -1584px,\n  ),\n  im: (\n    offset: -1600px,\n  ),\n  in: (\n    offset: -1616px,\n  ),\n  io: (\n    offset: -1632px,\n  ),\n  iq: (\n    offset: -1648px,\n  ),\n  ir: (\n    offset: -1664px,\n  ),\n  is: (\n    offset: -1680px,\n  ),\n  it: (\n    offset: -1696px,\n  ),\n  je: (\n    offset: -1712px,\n  ),\n  jm: (\n    offset: -1728px,\n  ),\n  jo: (\n    offset: -1744px,\n  ),\n  jp: (\n    offset: -1760px,\n  ),\n  ke: (\n    offset: -1776px,\n  ),\n  kg: (\n    offset: -1792px,\n  ),\n  kh: (\n    offset: -1808px,\n  ),\n  ki: (\n    offset: -1824px,\n  ),\n  km: (\n    offset: -1840px,\n  ),\n  kn: (\n    offset: -1856px,\n  ),\n  kp: (\n    offset: -1872px,\n  ),\n  kr: (\n    offset: -1888px,\n  ),\n  kw: (\n    offset: -1904px,\n  ),\n  ky: (\n    offset: -1920px,\n  ),\n  kz: (\n    offset: -1936px,\n  ),\n  la: (\n    offset: -1952px,\n  ),\n  lb: (\n    offset: -1968px,\n  ),\n  lc: (\n    offset: -1984px,\n  ),\n  li: (\n    offset: -2000px,\n  ),\n  lk: (\n    offset: -2016px,\n  ),\n  lr: (\n    offset: -2032px,\n  ),\n  ls: (\n    offset: -2048px,\n  ),\n  lt: (\n    offset: -2064px,\n  ),\n  lu: (\n    offset: -2080px,\n  ),\n  lv: (\n    offset: -2096px,\n  ),\n  ly: (\n    offset: -2112px,\n  ),\n  ma: (\n    offset: -2128px,\n  ),\n  mc: (\n    offset: -2144px,\n  ),\n  md: (\n    offset: -2160px,\n  ),\n  me: (\n    offset: -2176px,\n  ),\n  mf: (\n    offset: -2192px,\n  ),\n  mg: (\n    offset: -2208px,\n  ),\n  mh: (\n    offset: -2224px,\n  ),\n  mk: (\n    offset: -2240px,\n  ),\n  ml: (\n    offset: -2256px,\n  ),\n  mm: (\n    offset: -2272px,\n  ),\n  mn: (\n    offset: -2288px,\n  ),\n  mo: (\n    offset: -2304px,\n  ),\n  mp: (\n    offset: -2320px,\n  ),\n  mq: (\n    offset: -2336px,\n  ),\n  mr: (\n    offset: -2352px,\n  ),\n  ms: (\n    offset: -2368px,\n  ),\n  mt: (\n    offset: -2384px,\n  ),\n  mu: (\n    offset: -2400px,\n  ),\n  mv: (\n    offset: -2416px,\n  ),\n  mw: (\n    offset: -2432px,\n  ),\n  mx: (\n    offset: -2448px,\n  ),\n  my: (\n    offset: -2464px,\n  ),\n  mz: (\n    offset: -2480px,\n  ),\n  na: (\n    offset: -2496px,\n  ),\n  nc: (\n    offset: -2512px,\n  ),\n  ne: (\n    offset: -2528px,\n  ),\n  nf: (\n    offset: -2544px,\n  ),\n  ng: (\n    offset: -2560px,\n  ),\n  ni: (\n    offset: -2576px,\n  ),\n  nl: (\n    offset: -2592px,\n  ),\n  no: (\n    offset: -2608px,\n  ),\n  np: (\n    offset: -2624px,\n  ),\n  nr: (\n    offset: -2640px,\n  ),\n  nu: (\n    offset: -2656px,\n  ),\n  nz: (\n    offset: -2672px,\n  ),\n  om: (\n    offset: -2688px,\n  ),\n  pa: (\n    offset: -2704px,\n  ),\n  pe: (\n    offset: -2720px,\n  ),\n  pf: (\n    offset: -2736px,\n  ),\n  pg: (\n    offset: -2752px,\n  ),\n  ph: (\n    offset: -2768px,\n  ),\n  pk: (\n    offset: -2784px,\n  ),\n  pl: (\n    offset: -2800px,\n  ),\n  pm: (\n    offset: -2816px,\n  ),\n  pr: (\n    offset: -2832px,\n  ),\n  ps: (\n    offset: -2848px,\n  ),\n  pt: (\n    offset: -2864px,\n  ),\n  pw: (\n    offset: -2880px,\n  ),\n  py: (\n    offset: -2896px,\n  ),\n  qa: (\n    offset: -2912px,\n  ),\n  re: (\n    offset: -2928px,\n  ),\n  ro: (\n    offset: -2944px,\n  ),\n  rs: (\n    offset: -2960px,\n  ),\n  ru: (\n    offset: -2976px,\n  ),\n  rw: (\n    offset: -2992px,\n  ),\n  sa: (\n    offset: -3008px,\n  ),\n  sb: (\n    offset: -3024px,\n  ),\n  sc: (\n    offset: -3040px,\n  ),\n  sd: (\n    offset: -3056px,\n  ),\n  se: (\n    offset: -3072px,\n  ),\n  sg: (\n    offset: -3088px,\n  ),\n  sh: (\n    offset: -3104px,\n  ),\n  si: (\n    offset: -3120px,\n  ),\n  sj: (\n    offset: -3136px,\n  ),\n  sk: (\n    offset: -3152px,\n  ),\n  sl: (\n    offset: -3168px,\n  ),\n  sm: (\n    offset: -3184px,\n  ),\n  sn: (\n    offset: -3200px,\n  ),\n  so: (\n    offset: -3216px,\n  ),\n  sr: (\n    offset: -3232px,\n  ),\n  ss: (\n    offset: -3248px,\n  ),\n  st: (\n    offset: -3264px,\n  ),\n  sv: (\n    offset: -3280px,\n  ),\n  sx: (\n    offset: -3296px,\n  ),\n  sy: (\n    offset: -3312px,\n  ),\n  sz: (\n    offset: -3328px,\n  ),\n  tc: (\n    offset: -3344px,\n  ),\n  td: (\n    offset: -3360px,\n  ),\n  tg: (\n    offset: -3376px,\n  ),\n  th: (\n    offset: -3392px,\n  ),\n  tj: (\n    offset: -3408px,\n  ),\n  tk: (\n    offset: -3424px,\n  ),\n  tl: (\n    offset: -3440px,\n  ),\n  tm: (\n    offset: -3456px,\n  ),\n  tn: (\n    offset: -3472px,\n  ),\n  to: (\n    offset: -3488px,\n  ),\n  tr: (\n    offset: -3504px,\n  ),\n  tt: (\n    offset: -3520px,\n  ),\n  tv: (\n    offset: -3536px,\n  ),\n  tw: (\n    offset: -3552px,\n  ),\n  tz: (\n    offset: -3568px,\n  ),\n  ua: (\n    offset: -3584px,\n  ),\n  ug: (\n    offset: -3600px,\n  ),\n  us: (\n    offset: -3616px,\n  ),\n  uy: (\n    offset: -3632px,\n  ),\n  uz: (\n    offset: -3648px,\n  ),\n  va: (\n    offset: -3664px,\n  ),\n  vc: (\n    offset: -3680px,\n  ),\n  ve: (\n    offset: -3696px,\n  ),\n  vg: (\n    offset: -3712px,\n  ),\n  vi: (\n    offset: -3728px,\n  ),\n  vn: (\n    offset: -3744px,\n  ),\n  vu: (\n    offset: -3760px,\n  ),\n  wf: (\n    offset: -3776px,\n  ),\n  ws: (\n    offset: -3792px,\n  ),\n  xk: (\n    offset: -3808px,\n  ),\n  ye: (\n    offset: -3824px,\n  ),\n  yt: (\n    offset: -3840px,\n  ),\n  za: (\n    offset: -3856px,\n  ),\n  zm: (\n    offset: -3872px,\n  ),\n  zw: (\n    offset: -3888px,\n  ),\n);\n\n//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\n"
  },
  {
    "path": "src/css/demo.scss",
    "content": "* {\n  box-sizing: border-box;\n}\nbody {\n  margin: 20px;\n  font-size: 14px;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  color: #555;\n}\n\ninput,\n.button {\n  margin: 0;\n  padding: 8px 12px;\n  border-radius: 2px;\n\n  //* Same font style, size and color as rest of site.\n  font-family: inherit;\n  font-size: 100%;\n  color: inherit;\n}\n\ninput {\n  border: 1px solid #ccc;\n  width: 220px;\n}\n\ninput::placeholder {\n  color: #bbb;\n}\n\n//* Borrowed from bootstrap.\n.button {\n  color: #fff;\n  background-color: #428bca;\n  border: 1px solid #357ebd;\n  margin-left: 5px;\n  &:hover {\n    background-color: #3276b1;\n    border-color: #285e8e;\n    cursor: pointer;\n  }\n  &:disabled {\n    background-color: #ccc;\n    border-color: #999;\n    color: #666;\n    cursor: not-allowed;\n}\n}\n\n//* For react demo.\n.notice {\n  margin-top: 15px;\n}\n\n// dark mode\n@media (prefers-color-scheme: dark) {\n  // general site styling\n  body, input {\n    color: white;\n    background-color: #0d1117;\n  }\n  input[disabled] {\n    background-color: #3c3c3c;\n  }\n  input {\n    border-color: #5b5b5b;\n  }\n  input::placeholder {\n    color: #8d96a0;\n  }\n  // intl-tel-input styling\n  .iti {\n    --iti-border-color: #5b5b5b;\n    --iti-dropdown-bg: #0d1117;\n    --iti-hover-color: #30363d;\n    --iti-icon-color: #aaa;\n  }\n}\n\n// validation demo\n#error-msg {\n  color: red;\n}\n#valid-msg {\n  color: #00c900;\n}\ninput.error {\n  border: 1px solid #ff7c7c;\n}\n.hide {\n  display: none;\n}\n"
  },
  {
    "path": "src/css/intlTelInput.scss",
    "content": "@use \"sass:map\";\n@use \"metadata\" as *;\n\n:root {\n  //* rgba is needed for the selected country hover state to blend in with the border-highlighting some browsers give the input on focus.\n  --iti-hover-color: rgba(0, 0, 0, 0.05); //* Used as the bg color for both the selected country and the dropdown items, on hover.\n  --iti-border-color: #ccc; //* Used for both the dropdown border, and the divider under the country search input.\n  --iti-dropdown-bg: white;\n  //* icons include: arrow icon, globe icon (selected country empty state), search magnifying glass icon, search clear icon\n  --iti-icon-color: #555;\n\n  --iti-spacer-horizontal: 8px;\n  --iti-flag-height: #{$flag-height};\n  --iti-flag-width: #{$flag-width};\n  //* Separate height for the globe icon, as it's much thinner than the flags, so nice to have it a bit taller. This is also re-used for the search icon.\n  --iti-globe-height: 16px;\n  //* Separate height for the search clear icon, which looks too big if it's the same as the others.\n  --iti-search-clear-icon-height: 13px;\n  //* This border width is used for the popup and divider, but it is also assumed to be the border width of the input, which we do not control.\n  --iti-border-width: 1px;\n  //* Arrow height must be an even number as it gets halved when calculating arrow width\n  --iti-arrow-height: 4px;\n  --iti-arrow-width: calc((var(--iti-arrow-height) / 2) * 3);\n  --iti-triangle-border: calc(var(--iti-arrow-width) / 2);\n  --iti-arrow-padding: 6px;\n\n  //* NOTE: image paths are now defined in intlTelInputWithAssets.scss\n\n  --iti-flag-sprite-width: #{map.get($flags-sprite-1x, width)};\n  --iti-flag-sprite-height: #{map.get($flags-sprite-1x, height)};\n\n  //* Enough space for them to click off to close.\n  --iti-mobile-popup-margin: 30px;\n}\n\n.iti {\n  //* We need position on the container so the selected country can be absolutely positioned over the input.\n  position: relative;\n  //* Keep the input's default inline properties.\n  display: inline-block;\n\n  //* Paul Irish (paulirish) says this is ok.\n  //* http://www.paulirish.com/2012/box-sizing-border-box-ftw/\n  * {\n    box-sizing: border-box;\n  }\n\n  &__a11y-text {\n    width: 1px;\n    height: 1px;\n    clip: rect(1px, 1px, 1px, 1px);\n    overflow: hidden;\n    position: absolute;\n  }\n\n  //* Specify types to increase specificity e.g. to override bootstrap v2.3.\n  input.iti__tel-input,\n  input.iti__tel-input[type=\"text\"],\n  input.iti__tel-input[type=\"tel\"] {\n    position: relative;\n    //* Input is bottom level, below selected country and dropdown.\n    z-index: 0;\n\n    //* Since we wrap the input in a container div, any margin here would interfere with the positioning of the selected country.\n    margin: 0 !important;\n  }\n\n  &__country-container {\n    //* Positioned over the top of the input.\n    position: absolute;\n    //* Full height.\n    top: 0;\n    bottom: 0;\n    left: 0;\n    //* Prevent the highlighted child from overlapping the input border.\n    padding: var(--iti-border-width);\n  }\n\n  //* Now a button element.\n  &__selected-country {\n    //* Render above the input.\n    z-index: 1;\n    position: relative;\n    display: flex;\n    align-items: center;\n    //* This must be full-height both for the hover highlight, and to push down the dropdown so it appears below the input.\n    height: 100%;\n\n    //* Reset button styles (can't use all:unset as lose browser default focus outline styles).\n    background: none;\n    border: 0;\n    margin: 0;\n    padding: 0;\n    font-family: inherit;\n    font-size: inherit;\n    color: inherit;\n    border-radius: 0;\n    font-weight: inherit;\n    line-height: inherit;\n    text-decoration: none;\n  }\n\n  &__selected-country-primary {\n    display: flex;\n    align-items: center;\n    //* This must be full-height for the hover highlight.\n    height: 100%;\n    padding: 0 var(--iti-arrow-padding) 0 var(--iti-spacer-horizontal);\n  }\n\n  &__arrow {\n    margin-left: var(--iti-arrow-padding);\n\n    //* CSS triangle.\n    width: 0;\n    height: 0;\n    border-left: var(--iti-triangle-border) solid transparent;\n    border-right: var(--iti-triangle-border) solid transparent;\n    border-top: var(--iti-arrow-height) solid var(--iti-icon-color);\n\n    &--up {\n      border-top: none;\n      border-bottom: var(--iti-arrow-height) solid var(--iti-icon-color);\n    }\n  }\n\n  //* The dropdown.\n  &__dropdown-content {\n    border-radius: 3px;\n    background-color: var(--iti-dropdown-bg);\n\n    .iti--inline-dropdown & {\n      border: var(--iti-border-width) solid var(--iti-border-color);\n      box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.2);\n    }\n    // Note: dont apply these styles when using dropdownContainer, as that uses pos:fixed and so this (child) doesn't need pos:absolute\n    .iti--inline-dropdown:not(.iti--container) & {\n      position: absolute;\n      //* Popup so render above everything else.\n      z-index: 2;\n      left: 0;\n    }\n  }\n  &__search-input {\n    width: 100%;\n    border-width: 0;\n    border-radius: 3px;\n    padding-left: 30px; //* Space for inline search icon.\n    padding-right: 28px; //* Space for clear button.\n\n    //* RTL: put the icon on the right instead.\n    [dir=\"rtl\"] & {\n      padding-left: inherit;\n      padding-right: 30px;\n      background-position: right 8px center;\n    }\n\n    // hide chrome/safari's native clear button for inputs with type=search\n    &::-webkit-search-cancel-button {\n      appearance: none;\n    }\n  }\n  // give the search input the same v-padding as the country items, for visual consistency\n  &__search-input,\n  &__country {\n    padding-top: 8px;\n    padding-bottom: 8px;\n  }\n  &__search-input-wrapper {\n    position: relative;\n    display: flex;\n    align-items: center;\n    border-bottom: 1px solid var(--iti-border-color);\n  }\n  &__search-icon {\n    position: absolute;\n    left: 8px;\n    display: flex;\n    pointer-events: none;\n  }\n  [dir=\"rtl\"] &__search-icon {\n    left: auto;\n    right: 8px;\n  }\n  &__search-icon-svg {\n    width: var(--iti-globe-height);\n    height: var(--iti-globe-height);\n    display: block;\n    stroke: var(--iti-icon-color);\n    fill: none;\n    stroke-width: 3;\n  }\n  &__search-clear {\n    position: absolute;\n    right: 4px;\n    background: transparent;\n    border: 0;\n    border-radius: 3px; // nice on hover state\n    cursor: pointer;\n    padding: 5px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    transition: background-color 0.15s ease;\n\n    .iti__search-clear-x {\n      stroke-width: 2;\n    }\n    .iti__search-clear-bg {\n      fill: var(--iti-icon-color);\n    }\n  }\n  &__search-clear-svg {\n    width: var(--iti-search-clear-icon-height);\n    height: var(--iti-search-clear-icon-height);\n    display: block;\n  }\n  [dir=\"rtl\"] &__search-clear {\n    right: auto;\n    left: 4px;\n  }\n  &__search-clear:hover, &__search-clear:focus-visible {\n    background: var(--iti-hover-color);\n    outline: none;\n  }\n  &__no-results {\n    text-align: center;\n    padding: 30px 0;\n  }\n  &__country-list {\n    //* Override default list styles.\n    list-style: none;\n    padding: 0;\n    margin: 0;\n    cursor: pointer;\n\n    overflow-y: scroll;\n    //* Fixes https://github.com/jackocnr/intl-tel-input/issues/765\n    //* Apple still hasn't fixed the issue where setting overflow: scroll on a div element does not use inertia scrolling\n    //* If this is not set, then the country list scroll stops moving after raising a finger, and users report that scroll is slow\n    //* Stackoverflow question about it: https://stackoverflow.com/questions/33601165/scrolling-slow-on-mobile-ios-when-using-overflowscroll\n    -webkit-overflow-scrolling: touch;\n\n    .iti--inline-dropdown & {\n      max-height: 185px;\n    }\n  }\n  &--flexible-dropdown-width &__country-list {\n    //* Don't let the contents wrap AKA the container will be as wide as the contents.\n    white-space: nowrap;\n\n    //* Except on small screens, where we force the dropdown width to match the input.\n    @media (max-width: 500px) {\n      white-space: normal;\n    }\n  }\n\n  //* Each country item in dropdown (we must have separate class to differentiate from dividers).\n  &__country {\n    //* Note: decided not to use line-height here for alignment because it causes issues e.g. large font-sizes will overlap, and also looks bad if one country overflows onto 2 lines.\n    display: flex;\n    align-items: center;\n    padding-left: var(--iti-spacer-horizontal);\n    padding-right: var(--iti-spacer-horizontal);\n    outline: none;\n  }\n  &__country-name {\n    // expand to fill available space, so the last flex item, the check mark, is pushed to the right edge\n    flex-grow: 1;\n  }\n\n  //* Check icon shown on the right for the selected country.\n  &__country-check {\n    // align with clear icon which has padding:5px and right:4px, so is 9px from right edge\n    // here, the parent list item has padding:8px, so add 1px to make it 9px from right edge\n    margin: 0 1px 0 var(--iti-spacer-horizontal);\n    display: flex;\n    align-items: center;\n    color: var(--iti-icon-color);\n  }\n  &__country-check-svg {\n    width: var(--iti-search-clear-icon-height);\n    height: var(--iti-search-clear-icon-height);\n    display: block;\n  }\n\n  &__country.iti__highlight {\n    background-color: var(--iti-hover-color);\n  }\n\n  //* Spacing between country flag and country name.\n  &__country-list .iti__flag {\n    margin-right: var(--iti-spacer-horizontal);\n\n    [dir=\"rtl\"] & {\n      margin-right: 0;\n      margin-left: var(--iti-spacer-horizontal);\n    }\n  }\n  &__country-list .iti__flag {\n    flex-shrink: 0; // stop long country names from shrinking the flag\n  }\n\n  &--allow-dropdown {\n    //* If the adjacent input is disabled/readonly, ensure the country button doesn't look clickable.\n    .iti__country-container:has(+ input[disabled]),\n    .iti__country-container:has(+ input[readonly]) {\n      button.iti__selected-country {\n        cursor: not-allowed;\n\n        .iti__arrow {\n          visibility: hidden;\n        }\n      }\n    }\n\n    //* Only show hover background colour when you hover (1) selected flag/arrow or (2) dropdown contents, but NOT when you hover the selected dial code, which would feel weird.\n    //* Only apply when the input is NOT disabled/readonly.\n    .iti__country-container:not(:has(+ input[disabled])):not(:has(+ input[readonly])) {\n      .iti__selected-country-primary:hover,\n      .iti__selected-country:has(+ .iti__dropdown-content:hover) .iti__selected-country-primary {\n        background-color: var(--iti-hover-color);\n      }\n    }\n  }\n\n  .iti__selected-dial-code {\n    margin-left: 4px;\n  }\n\n  //* If dropdownContainer option is set, increase z-index to prevent display issues.\n  &--container {\n    position: fixed;\n    top: -1000px;\n    left: -1000px;\n    //* Higher than default Bootstrap modal z-index of 1050.\n    z-index: 1060;\n    &:hover {\n      cursor: pointer;\n    }\n  }\n\n  // put these at the end, so they override other classes with display:flex etc\n  &__hide {\n    display: none;\n  }\n\n  //* Need this during init, to get the height of the dropdown.\n  &__v-hide {\n    visibility: hidden;\n  }\n}\n\n//* Overrides for mobile popup.\n.iti--fullscreen-popup {\n  &.iti--container {\n    background-color: rgba(0, 0, 0, 0.5);\n    top: 0;\n    bottom: 0;\n    left: 0;\n    right: 0;\n    position: fixed;\n    padding: var(--iti-mobile-popup-margin);\n    //* Short country lists should be vertically centred.\n    display: flex;\n    flex-direction: column;\n    //* The country search input auto-focuses, so mobile keyboard appears, so stick to top (also because when filter countries down, the height changes and the vertical centring would make it jump around).\n    justify-content: flex-start;\n  }\n  .iti__dropdown-content {\n    display: flex;\n    flex-direction: column;\n    max-height: 100%;\n    position: relative; //* Override needed in order to get full-width working properly.\n  }\n  // give the search input the same v-padding as the country items, for visual consistency\n  .iti__country,\n  .iti__search-input {\n    padding-top: 10px;\n    padding-bottom: 10px;\n  }\n  .iti__country {\n    padding-left: 10px;\n    padding-right: 10px;\n    //* Increase line height because dropdown copy is v likely to overflow on mobile and when it does it needs to be well spaced.\n    line-height: 1.5em;\n  }\n}\n\n.iti__flag {\n  //* Start with an offset to hide any flags. This is useful to show an empty state (with correct dimensions) while geoIpLookup is loading.\n  --iti-flag-offset: 100px;\n  height: var(--iti-flag-height);\n  width: var(--iti-flag-width);\n  border-radius: 1px;\n  box-shadow: 0px 0px 1px 0px #888;\n  background-image: image-set(\n    var(--iti-path-flags-1x) 1x,\n    var(--iti-path-flags-2x) 2x\n  );\n  background-repeat: no-repeat;\n  background-position: var(--iti-flag-offset) 0;\n  background-size: var(--iti-flag-sprite-width) var(--iti-flag-sprite-height);\n}\n\n//* Loading state (used while geoIpLookup is resolving auto country).\n.iti__loading {\n  position: relative;\n  background: none;\n  box-shadow: none;\n}\n\n.iti__loading::after {\n  content: \"\";\n  position: absolute;\n  inset: 0;\n  margin: auto;\n  width: var(--iti-flag-height);\n  height: var(--iti-flag-height);\n  box-sizing: border-box;\n  border: 2px solid var(--iti-icon-color);\n  border-right-color: transparent;\n  border-radius: 50%;\n  animation: iti-spinner 1s linear infinite;\n}\n\n@keyframes iti-spinner {\n  to {\n    transform: rotate(360deg);\n  }\n}\n\n@each $country-code, $values in $flags {\n  $flag-offset: map.get($values, offset);\n\n  .iti__#{$country-code} {\n    --iti-flag-offset: #{$flag-offset};\n  }\n}\n\n//* Empty state.\n// Inline globe icon (now rendered as SVG inside .iti__flag). Keep class for width/position logic and to clear box-shadow.\n.iti__globe {\n  background: none;\n  box-shadow: none;\n  height: var(--iti-globe-height);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  padding: 0; // ensure svg centers\n\n  .iti__globe-svg {\n    width: 100%;\n    height: 100%;\n    fill: var(--iti-icon-color);\n  }\n}\n\n/* Browsers normally add a coloured outline when you focus an input. Chrome adds a blue outline WITHIN the input. If you focus the input and then hover the selected country, it's bg color square overlaps the focus outline and looks bad. Here, @supports is used to target Chrome only, and move the default outline out by 1px so there's no overlap. */\n@supports (-webkit-appearance:none) and (not (background: -webkit-canvas(foo))) {\n  .iti__tel-input:focus {\n    outline-offset: 1px;\n  }\n}\n"
  },
  {
    "path": "src/css/intlTelInputWithAssets.scss",
    "content": "@use 'intlTelInput.scss';\n\n:root {\n  //* Image related variables.\n  --iti-path-flags-1x: url('../img/flags.webp');\n  --iti-path-flags-2x: url('../img/flags@2x.webp');\n}\n"
  },
  {
    "path": "src/js/intl-tel-input/data.ts",
    "content": "//* Array of country objects for the country dropdown.\n// By default, it's sorted in English alphabetical order, on country name.\n\n//* Criteria for the plugin to support a given country/territory:\n//* - It has an iso2 code: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2\n//* - It has a flag in the flag-icons project: https://github.com/lipis/flag-icons/tree/main/flags/4x3\n//* - It is supported by libphonenumber (it must be listed on this page): https://github.com/googlei18n/libphonenumber/blob/master/resources/ShortNumberMetadata.xml\n\n//* Criteria for the plugin to support area codes for a given country/territory:\n//* - The area codes cover all valid numbers for that territory (there are no valid numbers outside of those area codes)\n//* - The area codes are exclusive to that territory (i.e. they are not shared with another territory)\n\n//* Each country array has the following information:\n//* [\n//*   iso2 code,\n//*   International dial code,\n//*   Priority (if >1 country with same dial code),\n//*   Area codes (string array), [OPTIONAL]\n//*   National prefix, [OPTIONAL - only required if using area codes]\n//* ]\n\nexport const rawCountryData = [\n  [\n    \"af\", // Afghanistan\n    \"93\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ax\", // Åland Islands\n    \"358\",\n    1,\n    [\"18\", \"4\"], // (4 is a mobile range shared with FI)\n    \"0\",\n  ],\n  [\n    \"al\", // Albania\n    \"355\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"dz\", // Algeria\n    \"213\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"as\", // American Samoa\n    \"1\",\n    5,\n    [\"684\"],\n    \"1\",\n  ],\n  [\n    \"ad\", // Andorra\n    \"376\",\n  ],\n  [\n    \"ao\", // Angola\n    \"244\",\n  ],\n  [\n    \"ai\", // Anguilla\n    \"1\",\n    6,\n    [\"264\"],\n    \"1\",\n  ],\n  [\n    \"ag\", // Antigua and Barbuda\n    \"1\",\n    7,\n    [\"268\"],\n    \"1\",\n  ],\n  [\n    \"ar\", // Argentina\n    \"54\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"am\", // Armenia\n    \"374\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"aw\", // Aruba\n    \"297\",\n  ],\n  [\n    \"ac\", // Ascension Island\n    \"247\",\n  ],\n  [\n    \"au\", // Australia\n    \"61\",\n    0,\n    [\"4\"], // (mobile range shared with CX and CC)\n    \"0\",\n  ],\n  [\n    \"at\", // Austria\n    \"43\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"az\", // Azerbaijan\n    \"994\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"bs\", // Bahamas\n    \"1\",\n    8,\n    [\"242\"],\n    \"1\",\n  ],\n  [\n    \"bh\", // Bahrain\n    \"973\",\n  ],\n  [\n    \"bd\", // Bangladesh\n    \"880\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"bb\", // Barbados\n    \"1\",\n    9,\n    [\"246\"],\n    \"1\",\n  ],\n  [\n    \"by\", // Belarus\n    \"375\",\n    0,\n    null,\n    \"8\",\n  ],\n  [\n    \"be\", // Belgium\n    \"32\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"bz\", // Belize\n    \"501\",\n  ],\n  [\n    \"bj\", // Benin\n    \"229\",\n  ],\n  [\n    \"bm\", // Bermuda\n    \"1\",\n    10,\n    [\"441\"],\n    \"1\",\n  ],\n  [\n    \"bt\", // Bhutan\n    \"975\",\n  ],\n  [\n    \"bo\", // Bolivia\n    \"591\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ba\", // Bosnia and Herzegovina\n    \"387\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"bw\", // Botswana\n    \"267\",\n  ],\n  [\n    \"br\", // Brazil\n    \"55\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"io\", // British Indian Ocean Territory\n    \"246\",\n  ],\n  [\n    \"vg\", // British Virgin Islands\n    \"1\",\n    11,\n    [\"284\"],\n    \"1\",\n  ],\n  [\n    \"bn\", // Brunei\n    \"673\",\n  ],\n  [\n    \"bg\", // Bulgaria\n    \"359\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"bf\", // Burkina Faso\n    \"226\",\n  ],\n  [\n    \"bi\", // Burundi\n    \"257\",\n  ],\n  [\n    \"kh\", // Cambodia\n    \"855\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"cm\", // Cameroon\n    \"237\",\n  ],\n  [\n    \"ca\", // Canada\n    \"1\",\n    1,\n    [\"204\", \"226\", \"236\", \"249\", \"250\", \"257\", \"263\", \"289\", \"306\", \"343\", \"354\", \"365\", \"367\", \"368\", \"382\", \"403\", \"416\", \"418\", \"428\", \"431\", \"437\", \"438\", \"450\", \"468\", \"474\", \"506\", \"514\", \"519\", \"548\", \"579\", \"581\", \"584\", \"587\", \"604\", \"613\", \"639\", \"647\", \"672\", \"683\", \"705\", \"709\", \"742\", \"753\", \"778\", \"780\", \"782\", \"807\", \"819\", \"825\", \"867\", \"873\", \"879\", \"902\", \"905\", \"942\"],\n    \"1\",\n  ],\n  [\n    \"cv\", // Cape Verde\n    \"238\",\n  ],\n  [\n    \"bq\", // Caribbean Netherlands\n    \"599\",\n    1,\n    [\"3\", \"4\", \"7\"],\n  ],\n  [\n    \"ky\", // Cayman Islands\n    \"1\",\n    12,\n    [\"345\"],\n    \"1\",\n  ],\n  [\n    \"cf\", // Central African Republic\n    \"236\",\n  ],\n  [\n    \"td\", // Chad\n    \"235\",\n  ],\n  [\n    \"cl\", // Chile\n    \"56\",\n  ],\n  [\n    \"cn\", // China\n    \"86\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"cx\", // Christmas Island\n    \"61\",\n    2,\n    [\"4\", \"89164\"], // (4 is a mobile range shared with AU and CC)\n    \"0\",\n  ],\n  [\n    \"cc\", // Cocos (Keeling) Islands\n    \"61\",\n    1,\n    [\"4\", \"89162\"], // (4 is a mobile range shared with AU and CX)\n    \"0\",\n  ],\n  [\n    \"co\", // Colombia\n    \"57\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"km\", // Comoros\n    \"269\",\n  ],\n  [\n    \"cg\", // Congo (Brazzaville)\n    \"242\",\n  ],\n  [\n    \"cd\", // Congo (Kinshasa)\n    \"243\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ck\", // Cook Islands\n    \"682\",\n  ],\n  [\n    \"cr\", // Costa Rica\n    \"506\",\n  ],\n  [\n    \"ci\", // Côte d'Ivoire\n    \"225\",\n  ],\n  [\n    \"hr\", // Croatia\n    \"385\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"cu\", // Cuba\n    \"53\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"cw\", // Curaçao\n    \"599\",\n    0,\n  ],\n  [\n    \"cy\", // Cyprus\n    \"357\",\n  ],\n  [\n    \"cz\", // Czech Republic\n    \"420\",\n  ],\n  [\n    \"dk\", // Denmark\n    \"45\",\n  ],\n  [\n    \"dj\", // Djibouti\n    \"253\",\n  ],\n  [\n    \"dm\", // Dominica\n    \"1\",\n    13,\n    [\"767\"],\n    \"1\",\n  ],\n  [\n    \"do\", // Dominican Republic\n    \"1\",\n    2,\n    [\"809\", \"829\", \"849\"],\n    \"1\",\n  ],\n  [\n    \"ec\", // Ecuador\n    \"593\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"eg\", // Egypt\n    \"20\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"sv\", // El Salvador\n    \"503\",\n  ],\n  [\n    \"gq\", // Equatorial Guinea\n    \"240\",\n  ],\n  [\n    \"er\", // Eritrea\n    \"291\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ee\", // Estonia\n    \"372\",\n  ],\n  [\n    \"sz\", // Eswatini\n    \"268\",\n  ],\n  [\n    \"et\", // Ethiopia\n    \"251\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"fk\", // Falkland Islands (Malvinas)\n    \"500\",\n  ],\n  [\n    \"fo\", // Faroe Islands\n    \"298\",\n  ],\n  [\n    \"fj\", // Fiji\n    \"679\",\n  ],\n  [\n    \"fi\", // Finland\n    \"358\",\n    0,\n    [\"4\"], // (mobile range shared with AX)\n    \"0\",\n  ],\n  [\n    \"fr\", // France\n    \"33\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"gf\", // French Guiana\n    \"594\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"pf\", // French Polynesia\n    \"689\",\n  ],\n  [\n    \"ga\", // Gabon\n    \"241\",\n  ],\n  [\n    \"gm\", // Gambia\n    \"220\",\n  ],\n  [\n    \"ge\", // Georgia\n    \"995\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"de\", // Germany\n    \"49\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"gh\", // Ghana\n    \"233\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"gi\", // Gibraltar\n    \"350\",\n  ],\n  [\n    \"gr\", // Greece\n    \"30\",\n  ],\n  [\n    \"gl\", // Greenland\n    \"299\",\n  ],\n  [\n    \"gd\", // Grenada\n    \"1\",\n    14,\n    [\"473\"],\n    \"1\",\n  ],\n  [\n    \"gp\", // Guadeloupe\n    \"590\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"gu\", // Guam\n    \"1\",\n    15,\n    [\"671\"],\n    \"1\",\n  ],\n  [\n    \"gt\", // Guatemala\n    \"502\",\n  ],\n  [\n    \"gg\", // Guernsey\n    \"44\",\n    1,\n    [\"1481\", \"7781\", \"7839\", \"7911\"],\n    \"0\",\n  ],\n  [\n    \"gn\", // Guinea\n    \"224\",\n  ],\n  [\n    \"gw\", // Guinea-Bissau\n    \"245\",\n  ],\n  [\n    \"gy\", // Guyana\n    \"592\",\n  ],\n  [\n    \"ht\", // Haiti\n    \"509\",\n  ],\n  [\n    \"hn\", // Honduras\n    \"504\",\n  ],\n  [\n    \"hk\", // Hong Kong SAR China\n    \"852\",\n  ],\n  [\n    \"hu\", // Hungary\n    \"36\",\n    0,\n    null,\n    \"06\",\n  ],\n  [\n    \"is\", // Iceland\n    \"354\",\n  ],\n  [\n    \"in\", // India\n    \"91\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"id\", // Indonesia\n    \"62\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ir\", // Iran\n    \"98\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"iq\", // Iraq\n    \"964\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ie\", // Ireland\n    \"353\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"im\", // Isle of Man\n    \"44\",\n    2,\n    [\"1624\", \"74576\", \"7524\", \"7624\", \"7924\"],\n    \"0\",\n  ],\n  [\n    \"il\", // Israel\n    \"972\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"it\", // Italy\n    \"39\",\n    0,\n    [\"3\"], // (mobile range shared with VA)\n  ],\n  [\n    \"jm\", // Jamaica\n    \"1\",\n    4,\n    [\"658\", \"876\"],\n    \"1\",\n  ],\n  [\n    \"jp\", // Japan\n    \"81\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"je\", // Jersey\n    \"44\",\n    3,\n    [\"1534\", \"7509\", \"7700\", \"7797\", \"7829\", \"7937\"],\n    \"0\",\n  ],\n  [\n    \"jo\", // Jordan\n    \"962\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"kz\", // Kazakhstan\n    \"7\",\n    1,\n    [\"33\", \"7\"], // (33 is shared with RU)\n    \"8\",\n  ],\n  [\n    \"ke\", // Kenya\n    \"254\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ki\", // Kiribati\n    \"686\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"xk\", // Kosovo\n    \"383\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"kw\", // Kuwait\n    \"965\",\n  ],\n  [\n    \"kg\", // Kyrgyzstan\n    \"996\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"la\", // Laos\n    \"856\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"lv\", // Latvia\n    \"371\",\n  ],\n  [\n    \"lb\", // Lebanon\n    \"961\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ls\", // Lesotho\n    \"266\",\n  ],\n  [\n    \"lr\", // Liberia\n    \"231\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ly\", // Libya\n    \"218\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"li\", // Liechtenstein\n    \"423\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"lt\", // Lithuania\n    \"370\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"lu\", // Luxembourg\n    \"352\",\n  ],\n  [\n    \"mo\", // Macao SAR China\n    \"853\",\n  ],\n  [\n    \"mg\", // Madagascar\n    \"261\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"mw\", // Malawi\n    \"265\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"my\", // Malaysia\n    \"60\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"mv\", // Maldives\n    \"960\",\n  ],\n  [\n    \"ml\", // Mali\n    \"223\",\n  ],\n  [\n    \"mt\", // Malta\n    \"356\",\n  ],\n  [\n    \"mh\", // Marshall Islands\n    \"692\",\n    0,\n    null,\n    \"1\",\n  ],\n  [\n    \"mq\", // Martinique\n    \"596\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"mr\", // Mauritania\n    \"222\",\n  ],\n  [\n    \"mu\", // Mauritius\n    \"230\",\n  ],\n  [\n    \"yt\", // Mayotte\n    \"262\",\n    1,\n    [\"269\", \"639\"],\n    \"0\",\n  ],\n  [\n    \"mx\", // Mexico\n    \"52\",\n  ],\n  [\n    \"fm\", // Micronesia\n    \"691\",\n  ],\n  [\n    \"md\", // Moldova\n    \"373\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"mc\", // Monaco\n    \"377\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"mn\", // Mongolia\n    \"976\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"me\", // Montenegro\n    \"382\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ms\", // Montserrat\n    \"1\",\n    16,\n    [\"664\"],\n    \"1\",\n  ],\n  [\n    \"ma\", // Morocco\n    \"212\",\n    0,\n    [\"6\", \"7\"], // (mobile ranges shared with EH)\n    \"0\",\n  ],\n  [\n    \"mz\", // Mozambique\n    \"258\",\n  ],\n  [\n    \"mm\", // Myanmar (Burma)\n    \"95\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"na\", // Namibia\n    \"264\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"nr\", // Nauru\n    \"674\",\n  ],\n  [\n    \"np\", // Nepal\n    \"977\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"nl\", // Netherlands\n    \"31\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"nc\", // New Caledonia\n    \"687\",\n  ],\n  [\n    \"nz\", // New Zealand\n    \"64\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ni\", // Nicaragua\n    \"505\",\n  ],\n  [\n    \"ne\", // Niger\n    \"227\",\n  ],\n  [\n    \"ng\", // Nigeria\n    \"234\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"nu\", // Niue\n    \"683\",\n  ],\n  [\n    \"nf\", // Norfolk Island\n    \"672\",\n  ],\n  [\n    \"kp\", // North Korea\n    \"850\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"mk\", // North Macedonia\n    \"389\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"mp\", // Northern Mariana Islands\n    \"1\",\n    17,\n    [\"670\"],\n    \"1\",\n  ],\n  [\n    \"no\", // Norway\n    \"47\",\n    0,\n    [\"4\", \"9\"], // (mobile ranges shared with SJ)\n  ],\n  [\n    \"om\", // Oman\n    \"968\",\n  ],\n  [\n    \"pk\", // Pakistan\n    \"92\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"pw\", // Palau\n    \"680\",\n  ],\n  [\n    \"ps\", // Palestinian Territories\n    \"970\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"pa\", // Panama\n    \"507\",\n  ],\n  [\n    \"pg\", // Papua New Guinea\n    \"675\",\n  ],\n  [\n    \"py\", // Paraguay\n    \"595\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"pe\", // Peru\n    \"51\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ph\", // Philippines\n    \"63\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"pl\", // Poland\n    \"48\",\n  ],\n  [\n    \"pt\", // Portugal\n    \"351\",\n  ],\n  [\n    \"pr\", // Puerto Rico\n    \"1\",\n    3,\n    [\"787\", \"939\"],\n    \"1\",\n  ],\n  [\n    \"qa\", // Qatar\n    \"974\",\n  ],\n  [\n    \"re\", // Réunion\n    \"262\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ro\", // Romania\n    \"40\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ru\", // Russia\n    \"7\",\n    0,\n    [\"33\"], // (shared with KZ)\n    \"8\",\n  ],\n  [\n    \"rw\", // Rwanda\n    \"250\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ws\", // Samoa\n    \"685\",\n  ],\n  [\n    \"sm\", // San Marino\n    \"378\",\n  ],\n  [\n    \"st\", // São Tomé & Príncipe\n    \"239\",\n  ],\n  [\n    \"sa\", // Saudi Arabia\n    \"966\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"sn\", // Senegal\n    \"221\",\n  ],\n  [\n    \"rs\", // Serbia\n    \"381\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"sc\", // Seychelles\n    \"248\",\n  ],\n  [\n    \"sl\", // Sierra Leone\n    \"232\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"sg\", // Singapore\n    \"65\",\n  ],\n  [\n    \"sx\", // Sint Maarten\n    \"1\",\n    21,\n    [\"721\"],\n    \"1\",\n  ],\n  [\n    \"sk\", // Slovakia\n    \"421\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"si\", // Slovenia\n    \"386\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"sb\", // Solomon Islands\n    \"677\",\n  ],\n  [\n    \"so\", // Somalia\n    \"252\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"za\", // South Africa\n    \"27\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"kr\", // South Korea\n    \"82\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ss\", // South Sudan\n    \"211\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"es\", // Spain\n    \"34\",\n  ],\n  [\n    \"lk\", // Sri Lanka\n    \"94\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"bl\", // St. Barthélemy\n    \"590\",\n    1,\n    null,\n    \"0\",\n  ],\n  [\n    \"sh\", // St. Helena\n    \"290\",\n  ],\n  [\n    \"kn\", // St. Kitts & Nevis\n    \"1\",\n    18,\n    [\"869\"],\n    \"1\",\n  ],\n  [\n    \"lc\", // St. Lucia\n    \"1\",\n    19,\n    [\"758\"],\n    \"1\",\n  ],\n  [\n    \"mf\", // St. Martin\n    \"590\",\n    2,\n    null,\n    \"0\",\n  ],\n  [\n    \"pm\", // St. Pierre & Miquelon\n    \"508\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"vc\", // St. Vincent & Grenadines\n    \"1\",\n    20,\n    [\"784\"],\n    \"1\",\n  ],\n  [\n    \"sd\", // Sudan\n    \"249\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"sr\", // Suriname\n    \"597\",\n  ],\n  [\n    \"sj\", // Svalbard & Jan Mayen\n    \"47\",\n    1,\n    [\"4\", \"79\", \"9\"], // (4 and 9 are mobile ranges shared with NO)\n  ],\n  [\n    \"se\", // Sweden\n    \"46\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ch\", // Switzerland\n    \"41\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"sy\", // Syria\n    \"963\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"tw\", // Taiwan\n    \"886\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"tj\", // Tajikistan\n    \"992\",\n  ],\n  [\n    \"tz\", // Tanzania\n    \"255\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"th\", // Thailand\n    \"66\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"tl\", // Timor-Leste\n    \"670\",\n  ],\n  [\n    \"tg\", // Togo\n    \"228\",\n  ],\n  [\n    \"tk\", // Tokelau\n    \"690\",\n  ],\n  [\n    \"to\", // Tonga\n    \"676\",\n  ],\n  [\n    \"tt\", // Trinidad & Tobago\n    \"1\",\n    22,\n    [\"868\"],\n    \"1\",\n  ],\n  [\n    \"tn\", // Tunisia\n    \"216\",\n  ],\n  [\n    \"tr\", // Turkey\n    \"90\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"tm\", // Turkmenistan\n    \"993\",\n    0,\n    null,\n    \"8\",\n  ],\n  [\n    \"tc\", // Turks & Caicos Islands\n    \"1\",\n    23,\n    [\"649\"],\n    \"1\",\n  ],\n  [\n    \"tv\", // Tuvalu\n    \"688\",\n  ],\n  [\n    \"vi\", // U.S. Virgin Islands\n    \"1\",\n    24,\n    [\"340\"],\n    \"1\",\n  ],\n  [\n    \"ug\", // Uganda\n    \"256\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ua\", // Ukraine\n    \"380\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"ae\", // United Arab Emirates\n    \"971\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"gb\", // United Kingdom\n    \"44\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"us\", // United States\n    \"1\",\n    0,\n    null,\n    \"1\",\n  ],\n  [\n    \"uy\", // Uruguay\n    \"598\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"uz\", // Uzbekistan\n    \"998\",\n  ],\n  [\n    \"vu\", // Vanuatu\n    \"678\",\n  ],\n  [\n    \"va\", // Vatican City\n    \"39\",\n    1,\n    [\"06698\", \"3\"], // (3 is a mobile range shared with IT)\n  ],\n  [\n    \"ve\", // Venezuela\n    \"58\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"vn\", // Vietnam\n    \"84\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"wf\", // Wallis & Futuna\n    \"681\",\n  ],\n  [\n    \"eh\", // Western Sahara\n    \"212\",\n    1,\n    [\"5288\", \"5289\", \"6\", \"7\"], // (6 and 7 are mobile ranges shared with MA)\n    \"0\",\n  ],\n  [\n    \"ye\", // Yemen\n    \"967\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"zm\", // Zambia\n    \"260\",\n    0,\n    null,\n    \"0\",\n  ],\n  [\n    \"zw\", // Zimbabwe\n    \"263\",\n    0,\n    null,\n    \"0\",\n  ],\n] as const;\n\nexport type Iso2 = typeof rawCountryData[number][0];\n\nexport type Country = {\n  iso2: Iso2;\n  dialCode: string;\n  priority: number;\n  areaCodes: string[] | null;\n  nationalPrefix: string | null;\n\n  // the following fields are populated by the plugin\n  name: string;\n  // Map instance id to corresponding country dropdown <li> element\n  nodeById: { [instanceId: number]: HTMLElement };\n  // derived fields, cached for country search efficiency\n  normalisedName: string;\n  initials: string;\n  dialCodePlus: string;\n};\n\nconst allCountries: Country[] = [];\n//* Loop over all of the countries above, restructuring the data to be objects with named keys.\nfor (const c of rawCountryData) {\n  allCountries.push({\n    name: \"\", // populated in the plugin\n    iso2: c[0],\n    dialCode: c[1],\n    priority: c[2] || 0,\n    areaCodes: c[3] || null,\n    nodeById: {}, // populated by the plugin\n    nationalPrefix: c[4] || null,\n    normalisedName: \"\", // populated in the plugin\n    initials: \"\", // populated in the plugin\n    dialCodePlus: \"\", // populated in the plugin\n  });\n}\n\nexport default allCountries;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/ar/index.ts",
    "content": "//* Arabic. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"البلد المحدد\",\n  noCountrySelected: \"لم يتم تحديد أي بلد\",\n  countryListAriaLabel: \"قائمة الدول\",\n  searchPlaceholder: \"يبحث\",\n  clearSearchAriaLabel: \"مسح البحث\",\n  searchEmptyState: \"لم يتم العثور على نتائج\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"لم يتم العثور على نتائج\";\n    }\n    if (count === 1) {\n      return \"تم العثور على نتيجة واحدة\";\n    }\n\n    if (count === 2) {\n      return \"تم العثور على نتيجتين\";\n    }\n\n    // Numbers ending in 3-10\n    if (count % 100 >= 3 && count % 100 <= 10) {\n      return `تم العثور على ${count} نتائج`;\n    }\n\n    return `تم العثور على ${count} نتيجة`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/bg/index.ts",
    "content": "//* Bulgarian. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Избрана държава\",\n  noCountrySelected: \"Няма избрана държава\",\n  countryListAriaLabel: \"Списък на страните\",\n  searchPlaceholder: \"Търсене\",\n  clearSearchAriaLabel: \"Изчистване на търсенето\",\n  searchEmptyState: \"Няма намерени резултати\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Няма намерени резултати\";\n    }\n    if (count === 1) {\n      return \"Намерен е 1 резултат\";\n    }\n    return `${count} намерени резултата`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/bn/index.ts",
    "content": "//* Bengali. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"নির্বাচিত দেশ\",\n  noCountrySelected: \"কোনো দেশ নির্বাচন করা হয়নি\",\n  countryListAriaLabel: \"দেশের তালিকা\",\n  searchPlaceholder: \"অনুসন্ধান করুন\",\n  clearSearchAriaLabel: \"অনুসন্ধান পরিষ্কার করুন\",\n  searchEmptyState: \"কোন ফলাফল পাওয়া যায়নি\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"কোন ফলাফল পাওয়া যায়নি\";\n    }\n    if (count === 1) {\n      return \"1টি ফলাফল পাওয়া গেছে\";\n    }\n    return `${count} ফলাফল পাওয়া গেছে`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/bs/index.ts",
    "content": "//* Bosnian. Translated by: Harun Sabljaković (sabljak) */\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Odabrana zemlja\",\n  noCountrySelected: \"Zemlja nije odabrana\",\n  countryListAriaLabel: \"Lista zemalja\",\n  searchPlaceholder: \"Pretraži\",\n  clearSearchAriaLabel: \"Očisti pretragu\",\n  searchEmptyState: \"Nema pronađenih rezultata\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Nema pronađenih rezultata\";\n    }\n    const mod10 = count % 10;\n    const mod100 = count % 100;\n\n    // Numbers ending in 1, but not 11\n    if (mod10 === 1 && mod100 !== 11) {\n      return `Pronađen ${count} rezultat`;\n    }\n\n    // Numbers ending in 2-4, but not 12-14\n    const isFew = mod10 >= 2 && mod10 <= 4 && !(mod100 >= 12 && mod100 <= 14);\n\n    if (isFew) {\n      return `Pronađena ${count} rezultata`;\n    }\n\n    return `Pronađeno ${count} rezultata`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/ca/index.ts",
    "content": "//* Catalan. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"País seleccionat\",\n  noCountrySelected: \"No s'ha seleccionat cap país\",\n  countryListAriaLabel: \"Llista de països\",\n  searchPlaceholder: \"Cerca\",\n  clearSearchAriaLabel: \"Esborra la cerca\",\n  searchEmptyState: \"Sense resultats\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Sense resultats\";\n    }\n    if (count === 1) {\n      return \"1 resultat trobat\";\n    }\n    return `${count} resultats trobats`;\n  },\n};\n\nexport default interfaceTranslations;\n\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/cs/index.ts",
    "content": "//* Czech. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Vybraná země\",\n  noCountrySelected: \"Není vybrána žádná země\",\n  countryListAriaLabel: \"Seznam zemí\",\n  searchPlaceholder: \"Vyhledat\",\n  clearSearchAriaLabel: \"Vymazat vyhledávání\",\n  searchEmptyState: \"Nebyly nalezeny žádné výsledky\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Nebyly nalezeny žádné výsledky\";\n    }\n    if (count === 1) {\n      return \"Nalezen 1 výsledek\";\n    }\n\n    if (count >= 2 && count <= 4) {\n      return `Nalezeny ${count} výsledky`;\n    }\n\n    return `Nalezeno ${count} výsledků`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/da/index.ts",
    "content": "//* Danish. Translated by: Matthias Dilger (matthiasdilger).\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Valgt land\",\n  noCountrySelected: \"Intet land er valgt\",\n  countryListAriaLabel: \"Liste over lande\",\n  searchPlaceholder: \"Søg\",\n  clearSearchAriaLabel: \"Ryd søgning\",\n  searchEmptyState: \"Ingen resultater fundet\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Ingen resultater fundet\";\n    }\n    if (count === 1) {\n      return \"1 resultat fundet\";\n    }\n    return `${count} resultater fundet`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/de/index.ts",
    "content": "//* German. Translated by: Jack O'Connor (jackocnr).\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Ausgewähltes Land\",\n  noCountrySelected: \"Kein Land ausgewählt\",\n  countryListAriaLabel: \"Liste der Länder\",\n  searchPlaceholder: \"Suchen\",\n  clearSearchAriaLabel: \"Suche löschen\",\n  searchEmptyState: \"Keine Suchergebnisse\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Keine Suchergebnisse\";\n    }\n    if (count === 1) {\n      return \"1 Suchergebnis\";\n    }\n    return `${count} Suchergebnisse`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/el/index.ts",
    "content": "//* Greek. Translated by: Anthony Veaudry (anthony0030).\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Επιλεγμένη χώρα\",\n  noCountrySelected: \"Δεν έχει επιλεγεί χώρα\",\n  countryListAriaLabel: \"Κατάλογος χωρών\",\n  searchPlaceholder: \"Αναζήτηση\",\n  clearSearchAriaLabel: \"Εκκαθάριση αναζήτησης\",\n  searchEmptyState: \"Δεν βρέθηκαν αποτελέσματα\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Δεν βρέθηκαν αποτελέσματα\";\n    }\n    if (count === 1) {\n      return \"Βρέθηκε 1 αποτέλεσμα\";\n    }\n    return `Βρέθηκαν ${count} αποτελέσματα`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/en/index.ts",
    "content": "//* English. Translated by: Jack O'Connor (jackocnr).\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Change country, selected ${countryName} (${dialCode})\",\n  noCountrySelected: \"Select country\",\n  countryListAriaLabel: \"List of countries\",\n  searchPlaceholder: \"Search\",\n  clearSearchAriaLabel: \"Clear search\",\n  searchEmptyState: \"No results found\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"No results found\";\n    }\n    if (count === 1) {\n      return \"1 result found\";\n    }\n    return `${count} results found`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/es/index.ts",
    "content": "//* Spanish. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"País seleccionado\",\n  noCountrySelected: \"Ningún país seleccionado\",\n  countryListAriaLabel: \"Lista de países\",\n  searchPlaceholder: \"Buscar\",\n  clearSearchAriaLabel: \"Borrar búsqueda\",\n  searchEmptyState: \"No se han encontrado resultados\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"No se han encontrado resultados\";\n    }\n    if (count === 1) {\n      return \"1 resultado encontrado\";\n    }\n    return `${count} resultados encontrados`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/et/index.ts",
    "content": "//* Estonian. Translated by: Maksim Maksimov (4matic).\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Valitud riik\",\n  noCountrySelected: \"Ühtegi riiki pole valitud\",\n  countryListAriaLabel: \"Riikide nimekiri\",\n  searchPlaceholder: \"Otsi\",\n  clearSearchAriaLabel: \"Tühjenda otsing\",\n  searchEmptyState: \"Tulemusi ei leitud\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Tulemusi ei leitud\";\n    }\n    if (count === 1) {\n      return \"1 tulemus leitud\";\n    }\n    return `${count} tulemust leitud`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/fa/index.ts",
    "content": "//* Farsi/Persian. Translated by: Mahyar SBT (mahyarsbt).\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"کشور انتخاب شده\",\n  noCountrySelected: \"هیچ کشوری انتخاب نشده است\",\n  countryListAriaLabel: \"لیست کشورها\",\n  searchPlaceholder: \"جستجو\",\n  clearSearchAriaLabel: \"پاک کردن جستجو\",\n  searchEmptyState: \"هیچ نتیجه‌ای یافت نشد\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"هیچ نتیجه‌ای یافت نشد\";\n    }\n    if (count === 1) {\n      return \"1 نتیجه یافت شد\";\n    }\n    return `${count} نتیجه یافت شد`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/fi/index.ts",
    "content": "//* Finnish. Translated by: Michael Winton (mrwinton).\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Valittu maa\",\n  noCountrySelected: \"Maata ei ole valittu\",\n  countryListAriaLabel: \"Luettelo maista\",\n  searchPlaceholder: \"Haku\",\n  clearSearchAriaLabel: \"Tyhjennä haku\",\n  searchEmptyState: \"Ei tuloksia\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Ei tuloksia\";\n    }\n    if (count === 1) {\n      return \"1 tulos löytyi\";\n    }\n    return `${count} tulosta löytyi`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/fr/index.ts",
    "content": "//* French. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Pays sélectionné\",\n  noCountrySelected: \"Aucun pays sélectionné\",\n  countryListAriaLabel: \"Liste des pays\",\n  searchPlaceholder: \"Recherche\",\n  clearSearchAriaLabel: \"Effacer la recherche\",\n  searchEmptyState: \"Aucun résultat trouvé\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Aucun résultat trouvé\";\n    }\n    if (count === 1) {\n      return \"1 résultat trouvé\";\n    }\n    return `${count} résultats trouvés`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/hi/index.ts",
    "content": "//* Hindi. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"चयनित देश\",\n  noCountrySelected: \"कोई देश चयनित नहीं\",\n  countryListAriaLabel: \"देशों की सूची\",\n  searchPlaceholder: \"खोज\",\n  clearSearchAriaLabel: \"खोज साफ़ करें\",\n  searchEmptyState: \"कोई परिणाम नहीं मिला\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"कोई परिणाम नहीं मिला\";\n    }\n    if (count === 1) {\n      return \"1 परिणाम मिला\";\n    }\n    return `${count} परिणाम मिले`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/hr/index.ts",
    "content": "//* Croatian. Translated by: Harun Sabljaković (sabljak) */\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Odabrana zemlja\",\n  noCountrySelected: \"Zemlja nije odabrana\",\n  countryListAriaLabel: \"Lista zemalja\",\n  searchPlaceholder: \"Pretraži\",\n  clearSearchAriaLabel: \"Očisti pretragu\",\n  searchEmptyState: \"Nema pronađenih rezultata\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Nema pronađenih rezultata\";\n    }\n    const mod10 = count % 10;\n    const mod100 = count % 100;\n\n    // Numbers ending in 1, but not 11\n    if (mod10 === 1 && mod100 !== 11) {\n      return `Pronađen ${count} rezultat`;\n    }\n\n    // Numbers ending in 2-4, but not 12-14\n    const isFew = mod10 >= 2 && mod10 <= 4 && !(mod100 >= 12 && mod100 <= 14);\n\n    if (isFew) {\n      return `Pronađena ${count} rezultata`;\n    }\n\n    return `Pronađeno ${count} rezultata`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/hu/index.ts",
    "content": "//* Hungarian. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Kiválasztott ország\",\n  noCountrySelected: \"Nincs ország kiválasztva\",\n  countryListAriaLabel: \"Országok listája\",\n  searchPlaceholder: \"Keresés\",\n  clearSearchAriaLabel: \"Keresés törlése\",\n  searchEmptyState: \"Nincs találat\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Nincs találat\";\n    }\n    if (count === 1) {\n      return \"1 találat\";\n    }\n    return `${count} találat`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/id/index.ts",
    "content": "//* Indonesian. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Negara yang dipilih\",\n  noCountrySelected: \"Tidak ada negara yang dipilih\",\n  countryListAriaLabel: \"Daftar negara\",\n  searchPlaceholder: \"Mencari\",\n  clearSearchAriaLabel: \"Hapus pencarian\",\n  searchEmptyState: \"Tidak ada hasil yang ditemukan\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Tidak ada hasil yang ditemukan\";\n    }\n    if (count === 1) {\n      return \"1 hasil ditemukan\";\n    }\n    return `${count} hasil ditemukan`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/index.ts",
    "content": "//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\nexport { default as ar } from \"./ar\";\nexport { default as bg } from \"./bg\";\nexport { default as bn } from \"./bn\";\nexport { default as bs } from \"./bs\";\nexport { default as ca } from \"./ca\";\nexport { default as cs } from \"./cs\";\nexport { default as da } from \"./da\";\nexport { default as de } from \"./de\";\nexport { default as el } from \"./el\";\nexport { default as en } from \"./en\";\nexport { default as es } from \"./es\";\nexport { default as et } from \"./et\";\nexport { default as fa } from \"./fa\";\nexport { default as fi } from \"./fi\";\nexport { default as fr } from \"./fr\";\nexport { default as hi } from \"./hi\";\nexport { default as hr } from \"./hr\";\nexport { default as hu } from \"./hu\";\nexport { default as id } from \"./id\";\nexport { default as it } from \"./it\";\nexport { default as ja } from \"./ja\";\nexport { default as kn } from \"./kn\";\nexport { default as ko } from \"./ko\";\nexport { default as lt } from \"./lt\";\nexport { default as mr } from \"./mr\";\nexport { default as nl } from \"./nl\";\nexport { default as no } from \"./no\";\nexport { default as pl } from \"./pl\";\nexport { default as pt } from \"./pt\";\nexport { default as ro } from \"./ro\";\nexport { default as ru } from \"./ru\";\nexport { default as sk } from \"./sk\";\nexport { default as sl } from \"./sl\";\nexport { default as sq } from \"./sq\";\nexport { default as sr } from \"./sr\";\nexport { default as sv } from \"./sv\";\nexport { default as te } from \"./te\";\nexport { default as th } from \"./th\";\nexport { default as tr } from \"./tr\";\nexport { default as uk } from \"./uk\";\nexport { default as ur } from \"./ur\";\nexport { default as uz } from \"./uz\";\nexport { default as vi } from \"./vi\";\nexport { default as zh } from \"./zh\";\nexport { default as zhHk } from \"./zh-hk\";\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/it/index.ts",
    "content": "//* Italian. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Paese selezionato\",\n  noCountrySelected: \"Nessun paese selezionato\",\n  countryListAriaLabel: \"Elenco dei paesi\",\n  searchPlaceholder: \"Ricerca\",\n  clearSearchAriaLabel: \"Cancella ricerca\",\n  searchEmptyState: \"Nessun risultato trovato\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Nessun risultato trovato\";\n    }\n    if (count === 1) {\n      return \"1 risultato trovato\";\n    }\n    return `${count} risultati trovati`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/ja/index.ts",
    "content": "//* Japanese. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"選択した国\",\n  noCountrySelected: \"国が選択されていません\",\n  countryListAriaLabel: \"国のリスト\",\n  searchPlaceholder: \"検索\",\n  clearSearchAriaLabel: \"検索をクリア\",\n  searchEmptyState: \"結果が見つかりません\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"結果が見つかりません\";\n    }\n    if (count === 1) {\n      return \"1 件の結果が見つかりました\";\n    }\n    return `${count} 件の結果が見つかりました`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/kn/index.ts",
    "content": "//* Kannada (ಕನ್ನಡ) Translated by: Mohammed Rashad Qadri (https://github.com/rashadmehtab)\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"ಆಯ್ಕೆಮಾಡಿದ ದೇಶ\",\n  noCountrySelected: \"ಯಾವುದೇ ದೇಶವನ್ನು ಆಯ್ಕೆ ಮಾಡಿಲ್ಲ\",\n  countryListAriaLabel: \"ದೇಶಗಳ ಪಟ್ಟಿ\",\n  searchPlaceholder: \"ಹುಡುಕಿ\",\n  clearSearchAriaLabel: \"ಹುಡುಕಾಟ ಅಳಿಸಿ\",\n  searchEmptyState: \"ಯಾವುದೇ ಫಲಿತಾಂಶಗಳಿಲ್ಲ\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"ಯಾವುದೇ ಫಲಿತಾಂಶಗಳಿಲ್ಲ\";\n    }\n    if (count === 1) {\n      return \"1 ಫಲಿತಾಂಶ ಕಂಡುಬಂದಿದೆ\";\n    }\n    return `${count} ಫಲಿತಾಂಶಗಳು ಕಂಡುಬಂದಿವೆ`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/ko/index.ts",
    "content": "//* Korean. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"선택한 국가\",\n  noCountrySelected: \"선택한 국가가 없습니다.\",\n  countryListAriaLabel: \"국가 목록\",\n  searchPlaceholder: \"검색\",\n  clearSearchAriaLabel: \"검색 지우기\",\n  searchEmptyState: \"검색 결과가 없습니다\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"검색 결과가 없습니다\";\n    }\n    if (count === 1) {\n      return \"1개의 결과를 찾았습니다.\";\n    }\n    return `${count}개의 결과를 찾았습니다.`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/lt/index.ts",
    "content": "//* Lithuanian. Translated by: ChatGPT 5.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel:\n    \"Pakeisti šalį, pasirinkta ${countryName} (${dialCode})\",\n  noCountrySelected: \"Pasirinkite šalį\",\n  countryListAriaLabel: \"Šalių sąrašas\",\n  searchPlaceholder: \"Paieška\",\n  clearSearchAriaLabel: \"Išvalyti paiešką\",\n  searchEmptyState: \"Rezultatų nerasta\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Rezultatų nerasta\";\n    }\n    if (count === 1) {\n      return \"Rastas 1 rezultatas\";\n    }\n\n    const mod10 = count % 10;\n    const mod100 = count % 100;\n\n    // Numbers ending in 1, but not 11\n    if (mod10 === 1 && mod100 !== 11) {\n      return `Rasti ${count} rezultatas`;\n    }\n\n    // Numbers ending in 2-9, but not 11-19\n    if (mod10 >= 2 && mod10 <= 9 && !(mod100 >= 11 && mod100 <= 19)) {\n      return `Rasti ${count} rezultatai`;\n    }\n\n    return `Rasta ${count} rezultatų`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/mr/index.ts",
    "content": "//* Marathi. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"निवडलेला देश\",\n  noCountrySelected: \"कोणताही देश निवडलेला नाही\",\n  countryListAriaLabel: \"देशांची यादी\",\n  searchPlaceholder: \"शोधा\",\n  clearSearchAriaLabel: \"शोध साफ करा\",\n  searchEmptyState: \"कोणतेही परिणाम आढळले नाहीत\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"कोणतेही परिणाम आढळले नाहीत\";\n    }\n    if (count === 1) {\n      return \"1 परिणाम आढळला\";\n    }\n    return `${count} परिणाम आढळले`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/nl/index.ts",
    "content": "//* Dutch. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Geselecteerd land\",\n  noCountrySelected: \"Geen land geselecteerd\",\n  countryListAriaLabel: \"Lijst met landen\",\n  searchPlaceholder: \"Zoekopdracht\",\n  clearSearchAriaLabel: \"Zoekopdracht wissen\",\n  searchEmptyState: \"Geen resultaten gevonden\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Geen resultaten gevonden\";\n    }\n    if (count === 1) {\n      return \"1 resultaat gevonden\";\n    }\n    return `${count} resultaten gevonden`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/no/index.ts",
    "content": "//* Norwegian. Translated by: Eric Pastoor (epastoor) with help of google translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Valgt land\",\n  noCountrySelected: \"Ingen land er valgt\",\n  countryListAriaLabel: \"Liste over land\",\n  searchPlaceholder: \"Søk\",\n  clearSearchAriaLabel: \"Tøm søk\",\n  searchEmptyState: \"Ingen resultater funnet\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Ingen resultater funnet\";\n    }\n    if (count === 1) {\n      return \"1 resultat funnet\";\n    }\n    return `${count} resultater funnet`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/pl/index.ts",
    "content": "//* Polish. Translated by: Mateusz Bronis (bronisMateusz) https://github.com/bronisMateusz.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Wybrany kraj\",\n  noCountrySelected: \"Nie wybrano kraju\",\n  countryListAriaLabel: \"Lista krajów\",\n  searchPlaceholder: \"Szukaj\",\n  clearSearchAriaLabel: \"Wyczyść wyszukiwanie\",\n  searchEmptyState: \"Nie znaleziono wyników\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Nie znaleziono wyników\";\n    }\n    if (count === 1) {\n      return \"Znaleziono 1 wynik\";\n    }\n\n    // Numbers ending with 2, 3, 4 (except those ending with 12, 13, 14) use \"wyniki\". All others use \"wyników\".\n    const isFew =\n      count % 10 >= 2 &&\n      count % 10 <= 4 &&\n      !(count % 100 >= 12 && count % 100 <= 14);\n\n    if (isFew) {\n      return `Znaleziono ${count} wyniki`;\n    }\n    return `Znaleziono ${count} wyników`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/pt/index.ts",
    "content": "//* Portuguese. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"País selecionado\",\n  noCountrySelected: \"Nenhum país selecionado\",\n  countryListAriaLabel: \"Lista de países\",\n  searchPlaceholder: \"Procurar\",\n  clearSearchAriaLabel: \"Limpar pesquisa\",\n  searchEmptyState: \"Nenhum resultado encontrado\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Nenhum resultado encontrado\";\n    }\n    if (count === 1) {\n      return \"1 resultado encontrado\";\n    }\n    return `${count} resultados encontrados`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/ro/index.ts",
    "content": "//* Romanian. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Țara selectată\",\n  noCountrySelected: \"Nicio țară selectată\",\n  countryListAriaLabel: \"Lista țărilor\",\n  searchPlaceholder: \"Căutare\",\n  clearSearchAriaLabel: \"Șterge căutarea\",\n  searchEmptyState: \"Nici un rezultat gasit\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Nici un rezultat gasit\";\n    }\n    if (count === 1) {\n      return \"1 rezultat găsit\";\n    }\n\n    const isFew = count % 100 >= 1 && count % 100 <= 19;\n\n    if (isFew) {\n      return `${count} rezultate găsite`;\n    }\n    return `${count} de rezultate găsite`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/ru/index.ts",
    "content": "//* Russian. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Выбранная страна\",\n  noCountrySelected: \"Страна не выбрана\",\n  countryListAriaLabel: \"Список стран\",\n  searchPlaceholder: \"Поиск\",\n  clearSearchAriaLabel: \"Очистить поиск\",\n  searchEmptyState: \"результатов не найдено\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"результатов не найдено\";\n    }\n    const mod10 = count % 10;\n    const mod100 = count % 100;\n\n    // Numbers ending in 1, but not 11\n    if (mod10 === 1 && mod100 !== 11) {\n      return `найден ${count} результат`;\n    }\n\n    // Numbers ending in 2-4, but not 12-14\n    const isFew = mod10 >= 2 && mod10 <= 4 && !(mod100 >= 12 && mod100 <= 14);\n\n    if (isFew) {\n      return `Найдено ${count} результата`;\n    }\n    return `Найдено ${count} результатов`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/sk/index.ts",
    "content": "//* Slovak. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Vybraná krajina\",\n  noCountrySelected: \"Nie je vybratá žiadna krajina\",\n  countryListAriaLabel: \"Zoznam krajín\",\n  searchPlaceholder: \"Vyhľadať\",\n  clearSearchAriaLabel: \"Vymazať vyhľadávanie\",\n  searchEmptyState: \"Neboli nájdené žiadne výsledky\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Neboli nájdené žiadne výsledky\";\n    }\n    const mod10 = count % 10;\n    const mod100 = count % 100;\n\n    // Numbers ending in 1, but not 11\n    if (mod10 === 1 && mod100 !== 11) {\n      return `${count} výsledok nájdený`;\n    }\n\n    // Numbers ending in 2-4, but not 12-14\n    const isFew = mod10 >= 2 && mod10 <= 4 && !(mod100 >= 12 && mod100 <= 14);\n\n    if (isFew) {\n      return `${count} výsledky nájdené`;\n    }\n    return `${count} výsledkov nájdených`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/sl/index.ts",
    "content": "//* Slovenian. Translated by: ChatGPT 5.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel:\n    \"Spremeni državo, izbrano ${countryName} (${dialCode})\",\n  noCountrySelected: \"Izberi državo\",\n  countryListAriaLabel: \"Seznam držav\",\n  searchPlaceholder: \"Išči\",\n  clearSearchAriaLabel: \"Počisti iskanje\",\n  searchEmptyState: \"Ni rezultatov\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Ni rezultatov\";\n    }\n    // Slovenian pluralisation is determined by the last two digits\n    const mod100 = count % 100;\n\n    // Numbers ending in 01\n    if (mod100 === 1) {\n      return `Najden ${count} rezultat`;\n    }\n\n    // Numbers ending in 02\n    if (mod100 === 2) {\n      return `Najdena ${count} rezultata`;\n    }\n\n    // Numbers ending in 03 or 04\n    if (mod100 === 3 || mod100 === 4) {\n      return `Najdeni ${count} rezultati`;\n    }\n\n    return `Najdenih ${count} rezultatov`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/sq/index.ts",
    "content": "//* Shqip (Albanian). Translated by: ChatGPT 5.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Ndrysho vendin, i zgjedhur ${countryName} (${dialCode})\",\n  noCountrySelected: \"Zgjidh vendin\",\n  countryListAriaLabel: \"Lista e vendeve\",\n  searchPlaceholder: \"Kërko\",\n  clearSearchAriaLabel: \"Pastro kërkimin\",\n  searchEmptyState: \"Nuk u gjet asnjë rezultat\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Nuk u gjet asnjë rezultat\";\n    }\n    if (count === 1) {\n      return \"U gjet 1 rezultat\";\n    }\n    return `U gjetën ${count} rezultate`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/sr/index.ts",
    "content": "//* Serbian. Translated by: ChatGPT 5.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel:\n    \"Промени земљу, изабрано ${countryName} (${dialCode})\",\n  noCountrySelected: \"Изабери земљу\",\n  countryListAriaLabel: \"Листа земаља\",\n  searchPlaceholder: \"Претрага\",\n  clearSearchAriaLabel: \"Обриши претрагу\",\n  searchEmptyState: \"Нема резултата\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Нема резултата\";\n    }\n    const mod10 = count % 10;\n    const mod100 = count % 100;\n\n    // Numbers ending in 1, but not 11\n    if (mod10 === 1 && mod100 !== 11) {\n      return `Пронађен ${count} резултат`;\n    }\n\n    // Numbers ending in 2-4, but not 12-14\n    const isFew = mod10 >= 2 && mod10 <= 4 && !(mod100 >= 12 && mod100 <= 14);\n\n    if (isFew) {\n      return `Пронађена ${count} резултата`;\n    }\n    return `Пронађено ${count} резултата`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/sv/index.ts",
    "content": "//* Swedish. Translated by: Nhi Tran.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Valt land\",\n  noCountrySelected: \"Inget land valt\",\n  countryListAriaLabel: \"Lista över länder\",\n  searchPlaceholder: \"Sök\",\n  clearSearchAriaLabel: \"Rensa sökning\",\n  searchEmptyState: \"Inga resultat hittades\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Inga resultat hittades\";\n    }\n    if (count === 1) {\n      return \"1 resultat hittades\";\n    }\n    return `${count} resultat hittades`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/te/index.ts",
    "content": "//* Telugu. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"ఎంచుకున్న దేశం\",\n  noCountrySelected: \"ఏ దేశం ఎంచుకోబడలేదు\",\n  countryListAriaLabel: \"దేశాల జాబితా\",\n  searchPlaceholder: \"వెతకండి\",\n  clearSearchAriaLabel: \"శోధనను క్లియర్ చేయండి\",\n  searchEmptyState: \"ఎటువంటి ఫలితాలు లభించలేదు\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"ఎటువంటి ఫలితాలు లభించలేదు\";\n    }\n    if (count === 1) {\n      return \"1 ఫలితం కనుగొనబడింది\";\n    }\n    return `${count} ఫలితాలు కనుగొనబడ్డాయి`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/th/index.ts",
    "content": "//* Thai. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"ประเทศที่เลือก\",\n  noCountrySelected: \"ไม่ได้เลือกประเทศ\",\n  countryListAriaLabel: \"รายชื่อประเทศ\",\n  searchPlaceholder: \"ค้นหา\",\n  clearSearchAriaLabel: \"ล้างการค้นหา\",\n  searchEmptyState: \"ไม่พบผลลัพธ์\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"ไม่พบผลลัพธ์\";\n    }\n    if (count === 1) {\n      return \"พบผลลัพธ์ 1 รายการ\";\n    }\n    return `พบผลลัพธ์ ${count} รายการ`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/tr/index.ts",
    "content": "//* Turkish. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Seçilen ülke\",\n  noCountrySelected: \"Hiçbir ülke seçilmedi\",\n  countryListAriaLabel: \"Ülke listesi\",\n  searchPlaceholder: \"Ara\",\n  clearSearchAriaLabel: \"Aramayı temizle\",\n  searchEmptyState: \"Sonuç bulunamadı\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Sonuç bulunamadı\";\n    }\n    if (count === 1) {\n      return \"1 sonuç bulundu\";\n    }\n    return `${count} sonuç bulundu`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/types.ts",
    "content": "import type { Iso2 } from \"../data\";\n\n// Country translation keys + additional UI strings.\n// We reference Iso2 so adding/removing a country automatically updates typing.\nexport type I18n = Partial<Record<Iso2, string>> & {\n  selectedCountryAriaLabel?: string;\n  searchPlaceholder?: string;\n  clearSearchAriaLabel?: string;\n  countryListAriaLabel?: string;\n  noCountrySelected?: string;\n  searchEmptyState?: string;\n  searchSummaryAria?: (count: number) => string;\n};"
  },
  {
    "path": "src/js/intl-tel-input/i18n/uk/index.ts",
    "content": "//* Ukrainian. Translated by: Oleksandr Shyrykalov (JustSanya).\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Обрана країна\",\n  noCountrySelected: \"Країну не обрано\",\n  countryListAriaLabel: \"Список країн\",\n  searchPlaceholder: \"Шукати\",\n  clearSearchAriaLabel: \"Очистити пошук\",\n  searchEmptyState: \"Результатів не знайдено\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Результатів не знайдено\";\n    }\n    const mod10 = count % 10;\n    const mod100 = count % 100;\n\n    // Numbers ending in 1, but not 11\n    if (mod10 === 1 && mod100 !== 11) {\n      return `Знайдено ${count} результат`;\n    }\n\n    // Numbers ending in 2-4, but not 12-14\n    const isFew = mod10 >= 2 && mod10 <= 4 && !(mod100 >= 12 && mod100 <= 14);\n\n    if (isFew) {\n      return `Знайдено ${count} результати`;\n    }\n    return `Знайдено ${count} результатів`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/ur/index.ts",
    "content": "//* Urdu. Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"منتخب ملک\",\n  noCountrySelected: \"کوئی ملک منتخب نہیں کیا گیا۔\",\n  countryListAriaLabel: \"ممالک کی فہرست\",\n  searchPlaceholder: \"تلاش کریں۔\",\n  clearSearchAriaLabel: \"تلاش صاف کریں\",\n  searchEmptyState: \"کوئی نتیجہ نہیں\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"کوئی نتیجہ نہیں\";\n    }\n    if (count === 1) {\n      return \"1 نتیجہ ملا\";\n    }\n    return `${count} نتائج ملے`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/uz/index.ts",
    "content": "//* English. Translated by: Mukhammadkhojiakbar Khusanov (khusanov-m).\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Tanlangan davlat\",\n  noCountrySelected: \"Davlat tanlanmagan\",\n  countryListAriaLabel: \"Davlatlar roʻyxati\",\n  searchPlaceholder: \"Davlatni qidiring\",\n  clearSearchAriaLabel: \"Qidiruvni tozalang\",\n  searchEmptyState: \"Natija topilmadi\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Natija topilmadi\";\n    }\n    if (count === 1) {\n      return \"1-ta natija topildi\";\n    }\n    return `${count}-ta natija topildi`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/vi/index.ts",
    "content": "//* Vietnamese. Translated by: Eric Pastoor (epastoor) with help of google translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"Quốc gia đã chọn\",\n  noCountrySelected: \"Không có quốc gia nào được chọn\",\n  countryListAriaLabel: \"Danh sách các quốc gia\",\n  searchPlaceholder: \"Khám xét\",\n  clearSearchAriaLabel: \"Xóa tìm kiếm\",\n  searchEmptyState: \"Không tìm thấy kết quả nào\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"Không tìm thấy kết quả nào\";\n    }\n    if (count === 1) {\n      return \"Đã tìm thấy 1 kết quả\";\n    }\n    return `Đã tìm thấy ${count} kết quả`;\n  },\n};\n\nexport default interfaceTranslations;\n"
  },
  {
    "path": "src/js/intl-tel-input/i18n/zh/index.ts",
    "content": "//* Chinese (Simplified). Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"所选国家\",\n  noCountrySelected: \"未选择国家/地区\",\n  countryListAriaLabel: \"国家名单\",\n  searchPlaceholder: \"搜索\",\n  clearSearchAriaLabel: \"清除搜索\",\n  searchEmptyState: \"未找到结果\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"未找到结果\";\n    }\n    if (count === 1) {\n      return \"找到 1 个结果\";\n    }\n    return `找到 ${count} 个结果`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/i18n/zh-hk/index.ts",
    "content": "//* Chinese (Hong Kong). Translated by: Google Translate.\nimport type { I18n } from \"../types\";\n\nconst interfaceTranslations: I18n = {\n  selectedCountryAriaLabel: \"更改國家，選擇「${countryName}」（${dialCode}）\",\n  noCountrySelected: \"選擇國家\",\n  countryListAriaLabel: \"國家清單\",\n  searchPlaceholder: \"搜尋\",\n  clearSearchAriaLabel: \"清除搜尋\",\n  searchEmptyState: \"未找到相關項目\",\n\n  searchSummaryAria(count) {\n    if (count === 0) {\n      return \"未找到相關項目\";\n    }\n    if (count === 1) {\n      return \"找到 1 個相關項目\";\n    }\n    return `找到 ${count} 個相關項目`;\n  },\n};\n\nexport default interfaceTranslations;"
  },
  {
    "path": "src/js/intl-tel-input/intlTelInputWithUtils.ts",
    "content": "import intlTelInput from \"../intl-tel-input\";\nimport utils from \"./utils\";\nintlTelInput.utils = utils;\nexport default intlTelInput;"
  },
  {
    "path": "src/js/intl-tel-input.ts",
    "content": "import allCountries, { type Country, type Iso2 } from \"./intl-tel-input/data\";\nimport { defaults, applyOptionSideEffects, validateOptions } from \"./modules/core/options\";\nimport type {\n  UtilsLoader,\n  NumberType,\n  AllOptions,\n  SomeOptions,\n  IntlTelInputInterface,\n  SelectedCountryData,\n} from \"./modules/types/public-api\";\nimport { getNumeric } from \"./modules/utils/string\";\nimport { getIsAndroid } from \"./modules/utils/isAndroid\";\nimport { findFirstCountryStartingWith } from \"./modules/core/countrySearch\";\nimport UI from \"./modules/core/ui\";\nimport {\n  processAllCountries,\n  processDialCodes,\n  sortCountries,\n  cacheSearchTokens,\n  generateCountryNames,\n} from \"./modules/data/country-data\";\nimport { hasRegionlessDialCode } from \"./modules/data/intl-regionless\";\nimport {\n  beforeSetNumber,\n  formatNumberAsYouType,\n} from \"./modules/format/formatting\";\nimport { translateCursorPosition } from \"./modules/format/caret\";\nimport { isRegionlessNanp } from \"./modules/data/nanp-regionless\";\nimport type { ItiEventMap } from \"./modules/types/events\";\nimport {\n  EVENTS,\n  CLASSES,\n  SENTINELS,\n  KEYS,\n  REGEX,\n  TIMINGS,\n  INITIAL_COUNTRY,\n  DATA_KEYS,\n  UK,\n  INPUT_TYPES,\n  DIAL,\n  US,\n  PLACEHOLDER_MODES,\n} from \"./modules/constants\";\nimport { Numerals } from \"./modules/core/numerals\";\nimport type { ForEachInstanceArgsMap } from \"./modules/types/forEachInstanceArgsMap\";\n\ndeclare global {\n  interface HTMLInputElement {\n    iti?: Iti;\n  }\n}\n\n//* These vars persist through all instances of the plugin.\nlet id = 0;\n\n// build a Set for iso2 runtime validation (lightweight)\nconst iso2Set: Set<Iso2> = new Set(allCountries.map((c) => c.iso2));\nconst isIso2 = (val: string): val is Iso2 => iso2Set.has(val as Iso2);\n\n//* This is our plugin class that we will create an instance of\n// eslint-disable-next-line no-unused-vars\nexport class Iti {\n  //* PUBLIC FIELDS - READONLY\n  //* Can't be private as it's called from intlTelInput convenience wrapper.\n  public readonly id: number;\n  // accessed externally via iti.promise.then(...)\n  public readonly promise: Promise<[unknown, unknown]>;\n\n  //* PRIVATE FIELDS\n  readonly #ui: UI;\n  readonly #options: AllOptions;\n  readonly #isAndroid: boolean;\n  // country data\n  readonly #countries: Country[];\n  readonly #dialCodeMaxLen: number;\n  readonly #dialCodeToIso2Map: Record<string, Iso2[]>;\n  readonly #dialCodes: Set<string>;\n  readonly #countryByIso2: Map<Iso2, Country>;\n\n  #selectedCountryData: SelectedCountryData;\n  #maxCoreNumberLength: number | null;\n  #defaultCountry: Iso2;\n  #abortController: AbortController;\n  #dropdownAbortController: AbortController | null;\n  #numerals: Numerals;\n\n  #resolveAutoCountryPromise: (value?: unknown) => void;\n  #rejectAutoCountryPromise: (reason?: unknown) => void;\n  #resolveUtilsScriptPromise: (value?: unknown) => void;\n  #rejectUtilsScriptPromise: (reason?: unknown) => void;\n\n  public constructor(input: HTMLInputElement, customOptions: SomeOptions = {}) {\n    this.id = id++;\n\n    UI.validateInput(input);\n    const validatedOptions = validateOptions(customOptions);\n\n    //* Process specified options / defaults.\n    this.#options = { ...defaults, ...validatedOptions } as AllOptions;\n    applyOptionSideEffects(this.#options);\n\n    this.#ui = new UI(input, this.#options, this.id);\n    this.#isAndroid = getIsAndroid();\n    this.#numerals = new Numerals();\n    this.promise = this.#createInitPromises(this.#options);\n\n    //* Process onlyCountries or excludeCountries array if present.\n    this.#countries = processAllCountries(this.#options);\n\n    //* Generate this.dialCodes and this.dialCodeToIso2Map.\n    const { dialCodes, dialCodeMaxLen, dialCodeToIso2Map } = processDialCodes(\n      this.#countries,\n    );\n    this.#dialCodes = dialCodes;\n    this.#dialCodeMaxLen = dialCodeMaxLen;\n    this.#dialCodeToIso2Map = dialCodeToIso2Map;\n\n    //* Build fast iso2 -> country map for O(1) lookups (used by _getCountryData).\n    this.#countryByIso2 = new Map(this.#countries.map((c) => [c.iso2, c]));\n\n    this.#init();\n  }\n\n  #getTelInputValue(): string {\n    const inputValue = this.#ui.telInput.value.trim();\n    return this.#numerals.normalise(inputValue);\n  }\n\n  #setTelInputValue(asciiValue: string): void {\n    const currentValue = this.#ui.telInput.value;\n    this.#ui.telInput.value = this.#numerals.denormalise(asciiValue, currentValue);\n  }\n\n  #createInitPromises(options: AllOptions): Promise<[unknown, unknown]> {\n    const { initialCountry, geoIpLookup, loadUtils } = options;\n    const needsAutoCountryPromise = initialCountry === INITIAL_COUNTRY.AUTO && Boolean(geoIpLookup);\n    const needsUtilsScriptPromise = Boolean(loadUtils) && !intlTelInput.utils;\n\n    //* These promises get resolved when their individual requests complete.\n    //* This way the dev can do something like iti.promise.then(...) to know when all requests are complete.\n    let autoCountryPromise: Promise<unknown>;\n    if (needsAutoCountryPromise) {\n      autoCountryPromise = new Promise<unknown>((resolve, reject) => {\n        this.#resolveAutoCountryPromise = resolve;\n        this.#rejectAutoCountryPromise = reject;\n      });\n    } else {\n      autoCountryPromise = Promise.resolve(undefined);\n      this.#resolveAutoCountryPromise = (): void => {};\n      this.#rejectAutoCountryPromise = (): void => {};\n    }\n\n    let utilsScriptPromise: Promise<unknown>;\n    if (needsUtilsScriptPromise) {\n      utilsScriptPromise = new Promise<unknown>((resolve, reject) => {\n        this.#resolveUtilsScriptPromise = resolve;\n        this.#rejectUtilsScriptPromise = reject;\n      });\n    } else {\n      utilsScriptPromise = Promise.resolve(undefined);\n      this.#resolveUtilsScriptPromise = (): void => {};\n      this.#rejectUtilsScriptPromise = (): void => {};\n    }\n\n    return Promise.all([autoCountryPromise, utilsScriptPromise]);\n  }\n\n  #init(): void {\n    //* In various situations there could be no country selected initially, but we need to be able\n    //* to assume this variable exists.\n    this.#selectedCountryData = {} as SelectedCountryData;\n\n    // init event controller\n    this.#abortController = new AbortController();\n\n    //* Process the country data: country name translations, countryOrder etc.\n    this.#processCountryData();\n\n    //* generate the markup.\n    this.#ui.generateMarkup(this.#countries);\n\n    //* Set the initial state of the input value and the selected country.\n    this.#setInitialState();\n\n    //* Start all of the event listeners: input keydown, selectedCountry click.\n    this.#initListeners();\n\n    //* Utils script, and auto country.\n    this.#initRequests();\n\n    if (this.#options.dropdownAlwaysOpen) {\n      this.#openDropdown();\n    }\n  }\n\n  //********************\n  //*  PRIVATE METHODS\n  //********************\n\n  //* Prepare all of the country data, including onlyCountries, excludeCountries, countryOrder options.\n  #processCountryData(): void {\n    //* Generate country names using Intl.DisplayNames\n    generateCountryNames(this.#countries, this.#options);\n\n    //* Sort countries by countryOrder option (if present), then name.\n    sortCountries(this.#countries, this.#options);\n\n    //* Precompute and cache country search tokens to speed up filtering\n    cacheSearchTokens(this.#countries);\n  }\n\n  //* Set the initial state of the input value and the selected country by:\n  //* 1. Extracting a dial code from the given number\n  //* 2. Using explicit initialCountry\n  #setInitialState(overrideAutoCountry: boolean = false): void {\n    //* Fix firefox bug: when first load page (with input with value set to number with intl dial code)\n    //* and initialising plugin removes the dial code from the input, then refresh page,\n    //* and we try to init plugin again but this time on number without dial code so show globe icon.\n    const attributeValueRaw = this.#ui.telInput.getAttribute(\"value\");\n    const attributeValue = this.#numerals.normalise(attributeValueRaw);\n    const inputValue = this.#getTelInputValue();\n    const useAttribute =\n      attributeValue &&\n      attributeValue.startsWith(\"+\") &&\n      (!inputValue || !inputValue.startsWith(\"+\"));\n    const val = useAttribute ? attributeValue : inputValue;\n\n    const dialCode = this.#getDialCode(val);\n    const isRegionlessNanpNumber = isRegionlessNanp(val);\n    const { initialCountry, geoIpLookup } = this.#options;\n    const isAutoCountry =\n      initialCountry === INITIAL_COUNTRY.AUTO && geoIpLookup;\n    const doingAutoCountryLookup = isAutoCountry && !overrideAutoCountry;\n    const initialCountryLower = initialCountry.toLowerCase();\n    const isValidInitialCountry = isIso2(initialCountryLower);\n\n    if (dialCode) {\n      if (isRegionlessNanpNumber) {\n        //* For regionless NANP numbers, we can't tell from the number which country to select, so defer to initialCountry, else auto country lookup, else default to US.\n        if (isValidInitialCountry) {\n          this.#setCountry(initialCountryLower);\n        } else if (!doingAutoCountryLookup) {\n          this.#setCountry(US.ISO2);\n        }\n      } else {\n        this.#updateCountryFromNumber(val);\n      }\n    } else if (isValidInitialCountry) {\n      this.#setCountry(initialCountryLower);\n    } else if (!doingAutoCountryLookup) {\n      //* No valid dial code, no valid initialCountry, and no country lookup, so default to empty (globe icon).\n      this.#setCountry(\"\");\n    }\n\n    //* Format - note this wont be run after _updateDialCode as that's only called if no val.\n    if (val) {\n      this.#updateValFromNumber(val);\n    }\n  }\n\n  //* Initialise the main event listeners: input keyup, and click selected country.\n  #initListeners(): void {\n    this.#initTelInputListeners();\n    if (this.#options.allowDropdown) {\n      this.#initDropdownListeners();\n    }\n    if (\n      (this.#ui.hiddenInput || this.#ui.hiddenInputCountry) &&\n      this.#ui.telInput.form\n    ) {\n      this.#initHiddenInputListener();\n    }\n  }\n\n  //* Update hidden input on form submit.\n  #initHiddenInputListener(): void {\n    const handleHiddenInputSubmit = (): void => {\n      if (this.#ui.hiddenInput) {\n        this.#ui.hiddenInput.value = this.getNumber();\n      }\n      if (this.#ui.hiddenInputCountry) {\n        this.#ui.hiddenInputCountry.value = this.#selectedCountryData.iso2 || \"\";\n      }\n    };\n    this.#ui.telInput.form?.addEventListener(\"submit\", handleHiddenInputSubmit, {\n      signal: this.#abortController.signal,\n    });\n  }\n\n  //* initialise the dropdown listeners.\n  #initDropdownListeners(): void {\n    const signal = this.#abortController.signal;\n    //* Hack for input nested inside label (which is valid markup): clicking the selected country to\n    //* open the dropdown would then automatically trigger a 2nd click on the input which would\n    //* close it again.\n    const handleLabelClick = (e: Event): void => {\n      //* If the dropdown is closed, then focus the input, else ignore the click.\n      if (this.#ui.isDropdownClosed()) {\n        this.#ui.telInput.focus();\n      } else {\n        e.preventDefault();\n      }\n    };\n    const label = this.#ui.telInput.closest(\"label\");\n    if (label) {\n      label.addEventListener(\"click\", handleLabelClick, { signal });\n    }\n\n    //* Toggle country dropdown on click.\n    const handleClickSelectedCountry = (): void => {\n      if (\n        this.#ui.isDropdownClosed() &&\n        !this.#ui.telInput.disabled &&\n        !this.#ui.telInput.readOnly\n      ) {\n        this.#openDropdown();\n      }\n    };\n    this.#ui.selectedCountry.addEventListener(\n      \"click\",\n      handleClickSelectedCountry,\n      {\n        signal,\n      },\n    );\n\n    //* Open dropdown if selected country is focused and they press up/down/space/enter.\n    const handleCountryContainerKeydown = (e: KeyboardEvent): void => {\n      const allowedKeys = [KEYS.ARROW_UP, KEYS.ARROW_DOWN, KEYS.SPACE, KEYS.ENTER] as string[];\n\n      if (this.#ui.isDropdownClosed() && allowedKeys.includes(e.key)) {\n        //* Prevent form from being submitted if \"ENTER\" was pressed.\n        e.preventDefault();\n        //* Prevent event from being handled again by document.\n        e.stopPropagation();\n        this.#openDropdown();\n      }\n\n      //* Allow navigation from dropdown to input on TAB.\n      if (e.key === KEYS.TAB) {\n        this.#closeDropdown();\n      }\n    };\n    this.#ui.countryContainer.addEventListener(\n      \"keydown\",\n      handleCountryContainerKeydown,\n      { signal },\n    );\n  }\n\n  //* Init requests: utils script / geo ip lookup.\n  #initRequests(): void {\n    const { loadUtils, initialCountry, geoIpLookup } = this.#options;\n\n    //* If the user has specified the path to the utils script, fetch it on window.load, else resolve.\n    if (loadUtils && !intlTelInput.utils) {\n      const doAttachUtils = () => {\n        //* Catch and ignore any errors to prevent unhandled-promise failures.\n        //* The error from `attachUtils()` is also surfaced in each instance's\n        //* `promise` property, so it's not getting lost by being ignored here.\n        intlTelInput.attachUtils(loadUtils)?.catch(() => {});\n      };\n\n      //* If the plugin is being initialised after the window.load event has already been fired.\n      if (intlTelInput.documentReady()) {\n        doAttachUtils();\n      } else {\n        const handlePageLoad = (): void => {\n          doAttachUtils();\n        };\n        //* Wait until the load event so we don't block any other requests e.g. the flags image.\n        window.addEventListener(\"load\", handlePageLoad, {\n          signal: this.#abortController.signal,\n        });\n      }\n    } else {\n      this.#resolveUtilsScriptPromise();\n    }\n\n    const isAutoCountry =\n    initialCountry === INITIAL_COUNTRY.AUTO && geoIpLookup;\n    if (isAutoCountry) {\n      //* Don't bother with IP lookup if we already have a selected country.\n      if (this.#selectedCountryData.iso2) {\n        this.#resolveAutoCountryPromise();\n      } else {\n        this.#loadAutoCountry();\n      }\n    }\n  }\n\n  //* Perform the geo ip lookup.\n  #loadAutoCountry(): void {\n    //* 3 options:\n    //* 1) Already loaded (we're done)\n    //* 2) Another instance has already started loading (do nothing - just wait for loading callback to fire)\n    //* 3) Not already started loading (start)\n    if (intlTelInput.autoCountry) {\n      this.#handleAutoCountry();\n    } else {\n      this.#ui.selectedCountryInner.classList.add(CLASSES.LOADING);\n\n      if (!intlTelInput.startedLoadingAutoCountry) {\n        //* Don't do this twice!\n        intlTelInput.startedLoadingAutoCountry = true;\n\n        if (typeof this.#options.geoIpLookup === \"function\") {\n          const successCallback = (iso2 = \"\") => {\n            this.#ui.selectedCountryInner.classList.remove(CLASSES.LOADING);\n            const iso2Lower = iso2.toLowerCase();\n            if (isIso2(iso2Lower)) {\n              intlTelInput.autoCountry = iso2Lower;\n              //* Tell all instances the auto country is ready.\n              //* UPDATE: use setTimeout in case their geoIpLookup function calls this callback straight\n              //* away (e.g. if they have already done the geo ip lookup somewhere else). Using\n              //* setTimeout means that the current thread of execution will finish before executing\n              //* this, which allows the plugin to finish initialising.\n              setTimeout(() => Iti.forEachInstance(\"handleAutoCountry\"));\n            } else {\n              Iti.forEachInstance(\"handleAutoCountryFailure\");\n            }\n          };\n          const failureCallback = () => {\n            this.#ui.selectedCountryInner.classList.remove(CLASSES.LOADING);\n            Iti.forEachInstance(\"handleAutoCountryFailure\");\n          };\n          this.#options.geoIpLookup(successCallback, failureCallback);\n        }\n      }\n    }\n  }\n\n  #openDropdownWithPlus(): void {\n    this.#openDropdown();\n    this.#ui.searchInput.value = \"+\";\n    this.#ui.filterCountriesByQuery(\"\");\n  }\n\n  //* Initialize the tel input listeners.\n  #initTelInputListeners(): void {\n    this.#bindInputListener();\n    this.#maybeBindKeydownListener();\n    this.#maybeBindPasteListener();\n  }\n\n  #bindInputListener(): void {\n    const {\n      strictMode,\n      formatAsYouType,\n      separateDialCode,\n      allowDropdown,\n      countrySearch,\n    } = this.#options;\n    let userOverrideFormatting = false;\n    //* If the initial val contains any alpha chars (e.g. the extension separator \"ext.\"), then set the override, as libphonenumber's AYT-formatter cannot handle alphas.\n    if (REGEX.ALPHA_UNICODE.test(this.#getTelInputValue())) {\n      userOverrideFormatting = true;\n    }\n\n    //* On input event: (1) Update selected country, (2) Format-as-you-type.\n    //* Note that this fires AFTER the input is updated.\n    const handleInputEvent = (e: InputEvent): void => {\n      const inputValue = this.#getTelInputValue();\n      //* Android workaround for handling plus when separateDialCode enabled (as impossible to handle with keydown/keyup, for which e.key always returns \"Unidentified\", see https://stackoverflow.com/q/59584061/217866)\n      if (\n        this.#isAndroid &&\n        e?.data === \"+\" &&\n        separateDialCode &&\n        allowDropdown &&\n        countrySearch\n      ) {\n        const currentCaretPos = this.#ui.telInput.selectionStart || 0;\n        const valueBeforeCaret = inputValue.substring(0, currentCaretPos - 1);\n        const valueAfterCaret = inputValue.substring(currentCaretPos);\n        this.#setTelInputValue(valueBeforeCaret + valueAfterCaret);\n        this.#openDropdownWithPlus();\n        return;\n      }\n\n      //* Update selected country.\n      if (this.#updateCountryFromNumber(inputValue)) {\n        this.#triggerCountryChange();\n      }\n\n      //* If user types their own formatting char (not a plus or a numeric), or they paste something, then set the override.\n      const isFormattingChar = e?.data && REGEX.NON_PLUS_NUMERIC.test(e.data);\n      const isPaste =\n        e?.inputType === INPUT_TYPES.PASTE && inputValue;\n      if (isFormattingChar || (isPaste && !strictMode)) {\n        userOverrideFormatting = true;\n      }\n      //* If user removes all formatting chars, then reset the override.\n      else if (!REGEX.NON_PLUS_NUMERIC.test(inputValue)) {\n        userOverrideFormatting = false;\n      }\n\n      const isSetNumber = e?.detail && e.detail[\"isSetNumber\"];\n      // only do formatAsYouType if userNumeralSet is ascii as too complicated to maintain caret position with RTL numeral sets - when these numbers contain spaces, they're treated as words, and so they get reversed in a way that breaks our calculations\n      const isAscii = this.#numerals.isAscii();\n      //* Handle format-as-you-type, unless userOverrideFormatting, or isSetNumber.\n      if (formatAsYouType && !userOverrideFormatting && !isSetNumber && isAscii) {\n        //* Maintain caret position after reformatting.\n        const currentCaretPos = this.#ui.telInput.selectionStart || 0;\n        const valueBeforeCaret = inputValue.substring(\n          0,\n          currentCaretPos,\n        );\n        const relevantCharsBeforeCaret = valueBeforeCaret.replace(\n          REGEX.NON_PLUS_NUMERIC_GLOBAL,\n          \"\",\n        ).length;\n        const isDeleteForwards = e?.inputType === INPUT_TYPES.DELETE_FWD;\n        const fullNumber = this.#getFullNumber();\n        const formattedValue = formatNumberAsYouType(\n          fullNumber,\n          inputValue,\n          intlTelInput.utils,\n          this.#selectedCountryData,\n          separateDialCode,\n        );\n        const newCaretPos = translateCursorPosition(\n          relevantCharsBeforeCaret,\n          formattedValue,\n          currentCaretPos,\n          isDeleteForwards,\n        );\n        // Preserve user's numeral set in displayed value\n        this.#setTelInputValue(formattedValue);\n        // WARNING: calling setSelectionRange triggers a focus on iOS\n        this.#ui.telInput.setSelectionRange(newCaretPos, newCaretPos);\n      }\n\n      //* If separateDialCode AND typed dial code (e.g. from paste or autofill), remove typed dial code.\n      if (\n        separateDialCode &&\n        inputValue.startsWith(\"+\") &&\n        this.#selectedCountryData.dialCode\n      ) {\n        const cleanNumber = beforeSetNumber(\n          inputValue,\n          true,\n          separateDialCode,\n          this.#selectedCountryData,\n        );\n        this.#setTelInputValue(cleanNumber);\n      }\n    };\n    //* This handles individual key presses as well as cut/paste events\n    //* the advantage of the \"input\" event over \"keyup\" etc is that \"input\" only fires when the value changes,\n    //* whereas \"keyup\" fires even for shift key, arrow key presses etc.\n    this.#ui.telInput.addEventListener(\n      \"input\",\n      handleInputEvent as EventListener,\n      {\n        signal: this.#abortController.signal,\n      },\n    );\n  }\n\n  #maybeBindKeydownListener(): void {\n    const { strictMode, separateDialCode, allowDropdown, countrySearch } =\n      this.#options;\n    if (strictMode || separateDialCode) {\n      //* On keydown event: (1) if strictMode then prevent invalid characters, (2) if separateDialCode then handle plus key\n      //* Note that this fires BEFORE the input is updated.\n      const handleKeydownEvent = (e: KeyboardEvent): void => {\n        //* Only interested in actual character presses, rather than ctrl, alt, command, arrow keys, delete/backspace, cut/copy/paste etc.\n        if (\n          e.key &&\n          e.key.length === 1 &&\n          !e.altKey &&\n          !e.ctrlKey &&\n          !e.metaKey\n        ) {\n          //* If separateDialCode, handle the plus key differently: open dropdown and put plus in the search input instead.\n          if (\n            separateDialCode &&\n            allowDropdown &&\n            countrySearch &&\n            e.key === \"+\"\n          ) {\n            e.preventDefault();\n            this.#openDropdownWithPlus();\n            return;\n          }\n          //* If strictMode, prevent invalid characters.\n          if (strictMode) {\n            const inputValue = this.#getTelInputValue();\n            const alreadyHasPlus = inputValue.startsWith(\"+\");\n            const isInitialPlus =\n              !alreadyHasPlus &&\n              this.#ui.telInput.selectionStart === 0 &&\n              e.key === \"+\";\n            // note that we normalise numerals here so this numerics check works, but then later we continue using the original e.key value\n            const normalisedKey = this.#numerals.normalise(e.key);\n            const isNumeric = /^[0-9]$/.test(normalisedKey);\n            const isAllowedChar = separateDialCode\n              ? isNumeric\n              : isInitialPlus || isNumeric;\n\n            // insert the new character in the right place\n            const input = this.#ui.telInput;\n            const selStart = input.selectionStart;\n            const selEnd = input.selectionEnd;\n            const before = inputValue.slice(0, selStart);\n            const after = inputValue.slice(selEnd);\n            const newValue = before + e.key + after;\n            const newFullNumber = this.#getFullNumber(newValue);\n            const coreNumber = intlTelInput.utils.getCoreNumber(\n              newFullNumber,\n              this.#selectedCountryData.iso2,\n            );\n            const hasExceededMaxLength =\n              this.#maxCoreNumberLength &&\n              coreNumber.length > this.#maxCoreNumberLength;\n\n            const newCountry = this.#getNewCountryFromNumber(newFullNumber);\n            const isChangingDialCode = newCountry !== null;\n\n            // ignore the char if (1) it's not an allowed char, or (2) this new char will exceed the max length and this char will not change the selected country and it's not the initial plus (aka they're starting to type a dial code)\n            if (\n              !isAllowedChar ||\n              (hasExceededMaxLength && !isChangingDialCode && !isInitialPlus)\n            ) {\n              e.preventDefault();\n            }\n          }\n        }\n      };\n      this.#ui.telInput.addEventListener(\"keydown\", handleKeydownEvent, {\n        signal: this.#abortController.signal,\n      });\n    }\n  }\n\n  #maybeBindPasteListener(): void {\n    // Sanitise pasted values in strictMode\n    if (this.#options.strictMode) {\n      const handlePasteEvent = (e: ClipboardEvent): void => {\n        // in strict mode we always control the pasted value\n        e.preventDefault();\n\n        // shortcuts\n        const input = this.#ui.telInput;\n        const selStart = input.selectionStart;\n        const selEnd = input.selectionEnd;\n        const inputValue = this.#getTelInputValue();\n        const before = inputValue.slice(0, selStart);\n        const after = inputValue.slice(selEnd);\n        const iso2 = this.#selectedCountryData.iso2;\n\n        const pastedRaw = e.clipboardData.getData(\"text\");\n        const pasted = this.#numerals.normalise(pastedRaw);\n        // only allow a plus in the pasted content if there's not already one in the input, or the existing one is selected to be replaced by the pasted content\n        const initialCharSelected = selStart === 0 && selEnd > 0;\n        const allowLeadingPlus =\n          !inputValue.startsWith(\"+\") || initialCharSelected;\n        // just numerics and pluses\n        const allowedChars = pasted.replace(REGEX.NON_PLUS_NUMERIC_GLOBAL, \"\");\n        const hasLeadingPlus = allowedChars.startsWith(\"+\");\n        // just numerics\n        const numerics = allowedChars.replace(/\\+/g, \"\");\n        const sanitised =\n          hasLeadingPlus && allowLeadingPlus ? `+${numerics}` : numerics;\n        let newVal = before + sanitised + after;\n\n        // utils.getCoreNumber doesn't work for very short numbers, so only bother checking once we have a few chars\n        // (fixes bug where you couldn't paste the first digit of a number)\n        if (newVal.length > 5) {\n          let coreNumber = intlTelInput.utils.getCoreNumber(newVal, iso2);\n\n          // utils.getCoreNumber returns empty string for very long numbers\n          // if this is the case, keep trimming the new value until we have a valid core number (or nothing left)\n          while (coreNumber.length === 0 && newVal.length > 0) {\n            newVal = newVal.slice(0, -1);\n            coreNumber = intlTelInput.utils.getCoreNumber(newVal, iso2);\n          }\n          // if no valid core number can be found, then just ignore the paste\n          if (!coreNumber) {\n            return;\n          }\n          if (\n            this.#maxCoreNumberLength &&\n            coreNumber.length > this.#maxCoreNumberLength\n          ) {\n            if (input.selectionEnd === inputValue.length) {\n              // if they try to paste too many digits at the end, then just trim the excess\n              const trimLength = coreNumber.length - this.#maxCoreNumberLength;\n              newVal = newVal.slice(0, newVal.length - trimLength);\n            } else {\n              // if they try to paste too many digits in the middle, then just ignore the paste entirely\n              return;\n            }\n          }\n        }\n        // preserve pasted numeral set in display\n        this.#setTelInputValue(newVal);\n        const caretPos = selStart + sanitised.length;\n        input.setSelectionRange(caretPos, caretPos);\n\n        // trigger format-as-you-type and country update etc\n        input.dispatchEvent(new InputEvent(\"input\", { bubbles: true }));\n      };\n      this.#ui.telInput.addEventListener(\"paste\", handlePasteEvent, {\n        signal: this.#abortController.signal,\n      });\n    }\n  }\n\n  //* Adhere to the input's maxlength attr.\n  #cap(number: string): string {\n    const max = Number(this.#ui.telInput.getAttribute(\"maxlength\"));\n    return max && number.length > max ? number.substring(0, max) : number;\n  }\n\n  //* Trigger a custom event on the input (typed via ItiEventMap).\n  #trigger<K extends keyof ItiEventMap>(\n    name: K,\n    detailProps: ItiEventMap[K] = {} as ItiEventMap[K],\n  ): void {\n    const e = new CustomEvent(name, {\n      bubbles: true,\n      cancelable: true,\n      detail: detailProps,\n    });\n    this.#ui.telInput.dispatchEvent(e);\n  }\n\n  //* Open the dropdown.\n  #openDropdown(): void {\n    const { dropdownContainer, useFullscreenPopup } = this.#options;\n    // create a fresh AbortController for dropdown-scoped listeners\n    this.#dropdownAbortController = new AbortController();\n\n    // Delegate DOM updates to UI\n    this.#ui.openDropdown();\n\n    // If using an external dropdown container, close menu on window scroll\n    if (!useFullscreenPopup && dropdownContainer) {\n      const handleWindowScroll = (): void => this.#closeDropdown();\n      window.addEventListener(\"scroll\", handleWindowScroll, {\n        signal: this.#dropdownAbortController.signal,\n      });\n    }\n\n    // Bind all the dropdown-related listeners: mouseover, click, click-off, keydown.\n    this.#bindDropdownListeners();\n\n    this.#trigger(EVENTS.OPEN_COUNTRY_DROPDOWN);\n  }\n\n  //* We only bind dropdown listeners when the dropdown is open.\n  #bindDropdownListeners(): void {\n    const signal = this.#dropdownAbortController.signal;\n    this.#bindDropdownMouseoverListener(signal);\n    this.#bindDropdownCountryClickListener(signal);\n    if (!this.#options.dropdownAlwaysOpen) {\n      this.#bindDropdownClickOffListener(signal);\n    }\n    this.#bindDropdownKeydownListener(signal);\n    if (this.#options.countrySearch) {\n      this.#bindDropdownSearchListeners(signal);\n    }\n  }\n\n  //* When mouse over a list item, just highlight that one\n  //* we add the class \"highlight\", so if they hit \"enter\" we know which one to select.\n  #bindDropdownMouseoverListener(signal: AbortSignal): void {\n    const handleMouseoverCountryList = (e: MouseEvent): void => {\n      //* Handle event delegation, as we're listening for this event on the countryList.\n      const listItem: HTMLElement | null = (e.target as HTMLElement)?.closest(\n        `.${CLASSES.COUNTRY_ITEM}`,\n      );\n      if (listItem) {\n        this.#ui.highlightListItem(listItem, false);\n      }\n    };\n    this.#ui.countryList.addEventListener(\n      \"mouseover\",\n      handleMouseoverCountryList,\n      {\n        signal,\n      },\n    );\n  }\n\n  //* Listen for country selection.\n  #bindDropdownCountryClickListener(signal: AbortSignal): void {\n    const handleClickCountryList = (e: MouseEvent): void => {\n      const listItem: HTMLElement | null = (e.target as HTMLElement)?.closest(\n        `.${CLASSES.COUNTRY_ITEM}`,\n      );\n      if (listItem) {\n        this.#selectListItem(listItem);\n      }\n    };\n    this.#ui.countryList.addEventListener(\"click\", handleClickCountryList, {\n      signal,\n    });\n  }\n\n  //* Click off to close (except when this initial opening click is bubbling up).\n  //* We cannot just stopPropagation as it may be needed to close another instance.\n  #bindDropdownClickOffListener(signal: AbortSignal): void {\n    const handleClickOffToClose = (e: MouseEvent): void => {\n      const target = e.target as HTMLElement;\n      const clickedInsideDropdown = !!target.closest(\n        `#iti-${this.id}__dropdown-content`,\n      );\n      // only close if clicked outside (allow clicks on country search input/clear button/no results message etc)\n      if (!clickedInsideDropdown) {\n        this.#closeDropdown();\n      }\n    };\n    // Use setTimeout to allow this event listener to be bound after the current thread of execution, which is where the opening click is happening (which would otherwise immediately trigger click-off-to-close so the dropdown would never open)\n    setTimeout(() => {\n      document.documentElement.addEventListener(\n        \"click\",\n        handleClickOffToClose,\n        { signal },\n      );\n    }, 0);\n  }\n\n  //* Listen for up/down scrolling, enter to select, or escape to close.\n  //* Use keydown as keypress doesn't fire for non-char keys and we want to catch if they\n  //* just hit down and hold it to scroll down (no keyup event).\n  //* Listen on the document because that's where key events are triggered if no input has focus.\n  #bindDropdownKeydownListener(signal: AbortSignal): void {\n    let query = \"\";\n    let queryTimer: ReturnType<typeof setTimeout> | null = null;\n    const handleKeydownOnDropdown = (e: KeyboardEvent): void => {\n      //* prevent down key from scrolling the whole page, and enter key from submitting a form etc.\n      const allowedKeys = [\n        KEYS.ARROW_UP,\n        KEYS.ARROW_DOWN,\n        KEYS.ENTER,\n        KEYS.ESC,\n      ] as string[];\n      if (allowedKeys.includes(e.key)) {\n        e.preventDefault();\n        e.stopPropagation();\n\n        //* Up and down to navigate.\n        if (e.key === KEYS.ARROW_UP || e.key === KEYS.ARROW_DOWN) {\n          this.#ui.handleUpDownKey(e.key);\n        }\n        //* Enter to select.\n        else if (e.key === KEYS.ENTER) {\n          this.#handleEnterKey();\n        }\n        //* Esc to close\n        else if (e.key === KEYS.ESC) {\n          this.#closeDropdown();\n          // Accessibility: re-focus the select country button (this is how native <select> elements behave)\n          this.#ui.selectedCountry.focus();\n        }\n      }\n\n      //* When countrySearch disabled: Listen for alpha chars to perform hidden search.\n      //* Regex allows one latin alpha char or space, based on https://stackoverflow.com/a/26900132/217866.\n      if (!this.#options.countrySearch && REGEX.HIDDEN_SEARCH_CHAR.test(e.key)) {\n        e.stopPropagation();\n        //* Jump to countries that start with the query string.\n        if (queryTimer) {\n          clearTimeout(queryTimer);\n        }\n        query += e.key.toLowerCase();\n        this.#searchForCountry(query);\n        //* If the timer hits 1 second, reset the query.\n        queryTimer = setTimeout(() => {\n          query = \"\";\n        }, TIMINGS.HIDDEN_SEARCH_RESET_MS);\n      }\n    };\n    document.addEventListener(\"keydown\", handleKeydownOnDropdown, { signal });\n  }\n\n  //* Search input listeners when countrySearch enabled.\n  #bindDropdownSearchListeners(signal: AbortSignal): void {\n    // Listen for input in the search box to filter the country list.\n    this.#ui.searchInput.addEventListener(\n      \"input\",\n      () => this.#ui.handleSearchChange(),\n      { signal },\n    );\n\n    // Prevent click from closing dropdown, clear text, refocus input, and reset filter\n    this.#ui.searchClearButton.addEventListener(\n      \"click\",\n      () => this.#ui.handleSearchClear(),\n      { signal },\n    );\n  }\n\n  //* Hidden search (countrySearch disabled): Find the first list item whose name starts with the query string.\n  #searchForCountry(query: string): void {\n    // moved logic to findFirstCountryStartingWith (pure helper) for reuse & testability\n    const match = findFirstCountryStartingWith(this.#countries, query);\n    if (match) {\n      const listItem = match.nodeById[this.id];\n      //* Update highlighting and scroll.\n      this.#ui.highlightListItem(listItem, false);\n      this.#ui.scrollTo(listItem);\n    }\n  }\n\n  //* Select the currently highlighted item.\n  #handleEnterKey(): void {\n    if (this.#ui.highlightedItem) {\n      this.#selectListItem(this.#ui.highlightedItem);\n    }\n  }\n\n  //* Update the input's value to the given val (format first if possible)\n  //* NOTE: this is called from _setInitialState, handleUtils and setNumber.\n  #updateValFromNumber(fullNumber: string): void {\n    const { formatOnDisplay, nationalMode, separateDialCode } = this.#options;\n    let number = fullNumber;\n    if (\n      formatOnDisplay &&\n      intlTelInput.utils &&\n      this.#selectedCountryData\n    ) {\n      const isRegionless = hasRegionlessDialCode(fullNumber);\n      const useNational =\n        (nationalMode && !isRegionless) ||\n        (!number.startsWith(\"+\") && !separateDialCode);\n      const { NATIONAL, INTERNATIONAL } = intlTelInput.utils.numberFormat;\n      const format = useNational ? NATIONAL : INTERNATIONAL;\n      number = intlTelInput.utils.formatNumber(\n        number,\n        this.#selectedCountryData.iso2,\n        format,\n      );\n    }\n\n    number = this.#beforeSetNumber(number);\n    this.#setTelInputValue(number);\n  }\n\n  //* Check if need to select a new country based on the given number\n  //* Note: called from _setInitialState, keyup handler, setNumber.\n  #updateCountryFromNumber(fullNumber: string): boolean {\n    const iso2 = this.#getNewCountryFromNumber(fullNumber);\n    if (iso2 !== null) {\n      return this.#setCountry(iso2);\n    }\n    return false;\n  }\n\n  // if there is a selected country, and the number doesn't start with a dial code, then add it\n  #ensureHasDialCode(number: string): string {\n    const { dialCode, nationalPrefix } = this.#selectedCountryData;\n    const alreadyHasPlus = number.startsWith(\"+\");\n    if (alreadyHasPlus || !dialCode) {\n      return number;\n    }\n    //* Don't remove \"nationalPrefix\" digit if separateDialCode is enabled, as it can be part of a valid area code e.g. in Russia then have area codes starting with 8, which is also the national prefix digit.\n    const hasPrefix =\n      nationalPrefix &&\n      number.startsWith(nationalPrefix) &&\n      !this.#options.separateDialCode;\n    const cleanNumber = hasPrefix ? number.substring(1) : number;\n    return `+${dialCode}${cleanNumber}`;\n  }\n\n  //* Get the new country based on the input number, or return null if no change, or empty string if should be empty (e.g. if they type an invalid dial code).\n  #getNewCountryFromNumber(fullNumber: string): Iso2 | \"\" | null {\n    const plusIndex = fullNumber.indexOf(\"+\");\n    //* If it contains a plus, discard any chars before it e.g. accidental space char.\n    //* This keeps the selected country auto-updating correctly, which we want as\n    //* libphonenumber's validation/getNumber methods will ignore these chars anyway.\n    let number = plusIndex ? fullNumber.substring(plusIndex) : fullNumber;\n    const selectedIso2 = this.#selectedCountryData.iso2;\n    const selectedDialCode = this.#selectedCountryData.dialCode;\n\n    //* Ensure the number starts with the dial code (if there is a selected country), for getDialCode to work properly (e.g. if number is entered in national format, or with separateDialCode enabled)\n    number = this.#ensureHasDialCode(number);\n\n    //* Try and extract valid dial code (plus area code digits) from input.\n    const dialCodeMatch = this.#getDialCode(number, true);\n    const numeric = getNumeric(number);\n    if (dialCodeMatch) {\n      // we have a match, so we WILL be selecting a country (unless it's already selected)\n      const dialCodeMatchNumeric = getNumeric(dialCodeMatch);\n      const iso2Codes = this.#dialCodeToIso2Map[dialCodeMatchNumeric];\n\n      // SINGLE country found for the typed dialcode/areacode\n      if (iso2Codes.length === 1) {\n        // if it's already selected, then no change\n        if (iso2Codes[0] === selectedIso2) {\n          return null;\n        }\n        // it's not already selected, so change\n        return iso2Codes[0];\n      }\n\n      // MULTIPLE countries found for the typed dialcode/areacode\n\n      //* If they've just typed a dial code (from empty state), and it matches the last selected country (this.defaultCountry), then stick to that country e.g. if they select Aland Islands, then type it's dial code +358, we should stick to that country and not switch to Finland!\n      if (\n        !selectedIso2 &&\n        this.#defaultCountry &&\n        iso2Codes.includes(this.#defaultCountry)\n      ) {\n        return this.#defaultCountry;\n      }\n\n      // if they're typing a regionless NANP number and they already have a NANP country selected, then don't change the country\n      const isRegionlessNanpNumber =\n        selectedDialCode === DIAL.NANP && isRegionlessNanp(numeric);\n      if (isRegionlessNanpNumber) {\n        return null;\n      }\n\n      // if the currently selected country has area codes and the entered number already has a full match to one of them, then don't change the country\n      const { areaCodes, priority } = this.#selectedCountryData;\n      if (areaCodes) {\n        const dialCodeAreaCodes = areaCodes.map(\n          (areaCode) => `${selectedDialCode}${areaCode}`,\n        );\n        for (const dialCodeAreaCode of dialCodeAreaCodes) {\n          if (numeric.startsWith(dialCodeAreaCode)) {\n            // it's a full area code match, so the right country is already selected\n            return null;\n          }\n        }\n      }\n\n      // If the currently selected country has area codes (and it's not the \"main\" country, which only has partial area code coverage), and they've typed more digits than the best area code match, then that means none of the area codes matched the input number, as a full area code match would have been caught above.\n      const isMainCountry = priority === 0;\n      const hasAreaCodesButNoneMatched =\n        areaCodes &&\n        !isMainCountry &&\n        numeric.length > dialCodeMatchNumeric.length;\n      const isValidSelection =\n        selectedIso2 &&\n        iso2Codes.includes(selectedIso2) &&\n        !hasAreaCodesButNoneMatched;\n      // extra protection: don't return isoCodes[0] if it's already selected\n      const alreadySelected = selectedIso2 === iso2Codes[0];\n\n      if (!isValidSelection && !alreadySelected) {\n        return iso2Codes[0];\n      }\n    } else if (number.startsWith(\"+\") && numeric.length) {\n      //* If the user is still typing a prefix of the currently selected country's dial code, don't change yet.\n      const currentDial = this.#selectedCountryData.dialCode || \"\";\n      if (currentDial && currentDial.startsWith(numeric)) {\n        return null;\n      }\n      //* Invalid dial code (or prefix of a different country), so empty.\n      return \"\";\n    } else if ((!number || number === \"+\") && !selectedIso2 && this.#defaultCountry) {\n      //* If no selected country, and user either clears the input, or just types a plus, then show default.\n      return this.#defaultCountry;\n    }\n    return null;\n  }\n\n  //* Update the selected country, dial code (if separateDialCode), placeholder, title, and selected list item.\n  //* Note: called from _setInitialState, _updateCountryFromNumber, _selectListItem, setCountry.\n  #setCountry(iso2: Iso2 | \"\"): boolean {\n    const prevIso2 = this.#selectedCountryData.iso2 || \"\";\n\n    this.#selectedCountryData = iso2\n      ? (this.#countryByIso2.get(iso2) as Country)\n      : ({} as SelectedCountryData);\n\n    //* Update the defaultCountry - we only need the iso2 from now on, so just store that.\n    if (this.#selectedCountryData.iso2) {\n      this.#defaultCountry = this.#selectedCountryData.iso2;\n    }\n\n    this.#ui.setCountry(this.#selectedCountryData);\n\n    //* Update the input's placeholder.\n    this.#updatePlaceholder();\n\n    //* Update the maximum valid number length.\n    this.#updateMaxLength();\n\n    //* Return if the country has changed or not.\n    return prevIso2 !== iso2;\n  }\n\n  //* Update the maximum valid number length for the currently selected country.\n  #updateMaxLength(): void {\n    const { strictMode, placeholderNumberType, allowedNumberTypes } =\n      this.#options;\n    const { iso2 } = this.#selectedCountryData;\n    if (strictMode && intlTelInput.utils) {\n      if (iso2) {\n        const numberType = intlTelInput.utils.numberType[placeholderNumberType];\n        let exampleNumber = intlTelInput.utils.getExampleNumber(\n          iso2,\n          false,\n          numberType,\n          true,\n        );\n        //* See if adding more digits is still valid to get the true maximum valid length.\n        let validNumber = exampleNumber;\n        while (\n          intlTelInput.utils.isPossibleNumber(\n            exampleNumber,\n            iso2,\n            allowedNumberTypes,\n          )\n        ) {\n          validNumber = exampleNumber;\n          exampleNumber += \"0\";\n        }\n        const coreNumber = intlTelInput.utils.getCoreNumber(validNumber, iso2);\n        this.#maxCoreNumberLength = coreNumber.length;\n\n        // hack for Belarus, because for some reason, getCoreNumber(\"80294911911\"), aka the placeholder number, returns \"294911911\" (9 digits), but getCoreNumber(\"8029491191\"), aka you're typing the penultimate digit of the placeholder number, returns \"8029491191\" (10 digits) and so strictMode blocks it. so we increase the max length to 10 digits to allow this penultimate digit, and then when they type the final digit, getCoreNumber will return 9 digits again, so it will be fine.\n        if (iso2 === \"by\") {\n          this.#maxCoreNumberLength = coreNumber.length + 1;\n        }\n      } else {\n        this.#maxCoreNumberLength = null;\n      }\n    }\n  }\n\n  //* Update the input placeholder to an example number from the currently selected country.\n  #updatePlaceholder(): void {\n    const {\n      autoPlaceholder,\n      placeholderNumberType,\n      nationalMode,\n      customPlaceholder,\n    } = this.#options;\n    const shouldSetPlaceholder =\n      autoPlaceholder === PLACEHOLDER_MODES.AGGRESSIVE ||\n      (!this.#ui.hadInitialPlaceholder && autoPlaceholder === PLACEHOLDER_MODES.POLITE);\n\n    if (intlTelInput.utils && shouldSetPlaceholder) {\n      const numberType = intlTelInput.utils.numberType[placeholderNumberType];\n      //* Note: Must set placeholder to empty string if no country selected (globe icon showing).\n      let placeholder = this.#selectedCountryData.iso2\n        ? intlTelInput.utils.getExampleNumber(\n            this.#selectedCountryData.iso2,\n            nationalMode,\n            numberType,\n          )\n        : \"\";\n\n      placeholder = this.#beforeSetNumber(placeholder);\n      if (typeof customPlaceholder === \"function\") {\n        placeholder = customPlaceholder(placeholder, this.#selectedCountryData);\n      }\n      this.#ui.telInput.setAttribute(\"placeholder\", placeholder);\n    }\n  }\n\n  //* Called when the user selects a list item from the dropdown.\n  #selectListItem(listItem: HTMLElement): void {\n    //* Update selected country and active list item.\n    const iso2 = listItem.dataset[DATA_KEYS.COUNTRY_CODE] as Iso2;\n    const countryChanged = this.#setCountry(iso2);\n    this.#closeDropdown();\n\n    const dialCode = listItem.dataset[DATA_KEYS.DIAL_CODE];\n    this.#updateDialCode(dialCode);\n\n    // reformat any existing number to the new country\n    if (this.#options.formatOnDisplay) {\n      const inputValue = this.#getTelInputValue();\n      this.#updateValFromNumber(inputValue);\n    }\n\n    //* Focus the input.\n    this.#ui.telInput.focus();\n\n    if (countryChanged) {\n      this.#triggerCountryChange();\n    }\n  }\n\n  //* Close the dropdown and unbind any listeners.\n  #closeDropdown(isDestroy?: boolean): void {\n    // we call closeDropdown in places where it might not even be open e.g. in destroy()\n    if (this.#ui.isDropdownClosed() || (this.#options.dropdownAlwaysOpen && !isDestroy)) {\n      return;\n    }\n\n    // Delegate DOM-only close behaviour to UI\n    this.#ui.closeDropdown();\n\n    //* Unbind dropdown-scoped events in one go\n    this.#dropdownAbortController.abort();\n    this.#dropdownAbortController = null;\n\n    this.#trigger(EVENTS.CLOSE_COUNTRY_DROPDOWN);\n  }\n\n  //* Replace any existing dial code with the new one\n  //* Note: called from _selectListItem and setCountry\n  #updateDialCode(newDialCodeBare: string): void {\n    const inputVal = this.#getTelInputValue();\n    //* Save having to pass this every time.\n    const newDialCode = `+${newDialCodeBare}`;\n\n    let newNumber;\n    if (inputVal.startsWith(\"+\")) {\n      //* There's a plus so we're dealing with a replacement.\n      const prevDialCode = this.#getDialCode(inputVal);\n      if (prevDialCode) {\n        //* Current number contains a valid dial code, so replace it.\n        newNumber = inputVal.replace(prevDialCode, newDialCode);\n      } else {\n        //* Current number contains an invalid dial code, so ditch it\n        //* (no way to determine where the invalid dial code ends and the rest of the number begins)\n        newNumber = newDialCode;\n      }\n      this.#setTelInputValue(newNumber);\n    }\n  }\n\n  //* Try and extract a valid international dial code from a full telephone number.\n  //* Note: returns the raw string inc plus character and any whitespace/dots etc.\n  #getDialCode(number: string, includeAreaCode?: boolean): string {\n    let dialCode = \"\";\n    //* Only interested in international numbers (starting with a plus)\n    if (number.startsWith(\"+\")) {\n      let numericChars = \"\";\n      let foundBaseDialCode = false;\n      //* Iterate over chars\n      for (let i = 0; i < number.length; i++) {\n        const c = number.charAt(i);\n        //* If char is number.\n        if (/[0-9]/.test(c)) {\n          numericChars += c;\n          const hasMapEntry = Boolean(this.#dialCodeToIso2Map[numericChars]);\n          if (!hasMapEntry) {\n            // if no mapping for this prefix, stop searching\n            break;\n          }\n\n          // If we've hit a valid base dial code, record it\n          if (this.#dialCodes.has(numericChars)) {\n            dialCode = number.substring(0, i + 1);\n            foundBaseDialCode = true;\n            // if we're not considering area codes, we can stop at the first valid base dial code\n            if (!includeAreaCode) {\n              break;\n            }\n          } else if (includeAreaCode && foundBaseDialCode) {\n            // Only extend beyond a valid base dial code (avoid partial prefixes like +88 for +880)\n            dialCode = number.substring(0, i + 1);\n          }\n\n          //* Stop searching as soon as we can - in this case when we hit max len.\n          if (numericChars.length === this.#dialCodeMaxLen) {\n            break;\n          }\n        }\n      }\n    }\n    return dialCode;\n  }\n\n  //* Get the input val, adding the dial code if separateDialCode is enabled.\n  #getFullNumber(overrideVal?: string): string {\n    const val = overrideVal ? this.#numerals.normalise(overrideVal) : this.#getTelInputValue();\n    const { dialCode } = this.#selectedCountryData;\n    let prefix;\n    const numericVal = getNumeric(val);\n\n    if (\n      this.#options.separateDialCode &&\n      !val.startsWith(\"+\") &&\n      dialCode &&\n      numericVal\n    ) {\n      //* When using separateDialCode, it is visible so is effectively part of the typed number.\n      prefix = `+${dialCode}`;\n    } else {\n      prefix = \"\";\n    }\n    return prefix + val;\n  }\n\n  //* Remove the dial code if separateDialCode is enabled also cap the length if the input has a maxlength attribute\n  #beforeSetNumber(fullNumber: string): string {\n    const hasValidDialCode = Boolean(this.#getDialCode(fullNumber));\n    const number = beforeSetNumber(\n      fullNumber,\n      hasValidDialCode,\n      this.#options.separateDialCode,\n      this.#selectedCountryData,\n    );\n    return this.#cap(number);\n  }\n\n  //* Trigger the 'countrychange' event.\n  #triggerCountryChange(): void {\n    this.#trigger(EVENTS.COUNTRY_CHANGE);\n  }\n\n  //**************************\n  //*  INTERNAL METHODS\n  //**************************\n\n  //* Called when the geoip call returns.\n  #handleAutoCountry(): void {\n    // If destroyed/unmounted, abort any UI work but still resolve the init promise\n    if (!this.#ui.telInput) {\n      this.#resolveAutoCountryPromise?.();\n      return;\n    }\n\n    if (\n      this.#options.initialCountry === INITIAL_COUNTRY.AUTO &&\n      intlTelInput.autoCountry\n    ) {\n      //* We must set this even if there is an initial val in the input: in case the initial val is\n      //* invalid and they delete it - they should see their auto country.\n      this.#defaultCountry = intlTelInput.autoCountry;\n      const hasSelectedCountryOrGlobe =\n        this.#selectedCountryData.iso2 ||\n        this.#ui.selectedCountryInner.classList.contains(CLASSES.GLOBE);\n      //* If no country/globe currently selected, then update the country.\n      if (!hasSelectedCountryOrGlobe) {\n        this.setCountry(this.#defaultCountry);\n      }\n      this.#resolveAutoCountryPromise();\n    }\n  }\n\n  //* Called when the geoip call fails or times out.\n  #handleAutoCountryFailure(): void {\n    // If instance destroyed/unmounted, just reject the promise and avoid DOM/state ops\n    if (!this.#ui.telInput) {\n      this.#rejectAutoCountryPromise?.();\n      return;\n    }\n\n    //* Reset the initial state\n    this.#setInitialState(true);\n    this.#rejectAutoCountryPromise();\n  }\n\n  //* Called when the utils request completes.\n  #handleUtils(): void {\n    // If instance destroyed/unmounted, avoid touching DOM/state but still resolve promise\n    if (!this.#ui.telInput) {\n      this.#resolveUtilsScriptPromise?.();\n      return;\n    }\n\n    //* If the request was successful\n    if (intlTelInput.utils) {\n      const inputValue = this.#getTelInputValue();\n      //* If there's an initial value in the input, then format it.\n      if (inputValue) {\n        this.#updateValFromNumber(inputValue);\n      }\n      if (this.#selectedCountryData.iso2) {\n        this.#updatePlaceholder();\n        this.#updateMaxLength();\n      }\n    }\n    this.#resolveUtilsScriptPromise();\n  }\n\n  //* Called when the utils request fails or times out.\n  #handleUtilsFailure(error: unknown): void {\n    // If instance destroyed/unmounted, just reject the promise and avoid DOM/state ops\n    if (!this.#ui.telInput) {\n      this.#rejectUtilsScriptPromise?.(error);\n      return;\n    }\n\n    this.#rejectUtilsScriptPromise(error);\n  }\n\n  //********************\n  //*  PUBLIC METHODS\n  //********************\n\n  //* Remove plugin.\n  public destroy(): void {\n    // it is possible to call destroy twice, so check there is anything left to destroy\n    if (!this.#ui.telInput) {\n      return;\n    }\n    if (this.#options.allowDropdown) {\n      //* Make sure the dropdown is closed (and unbind listeners).\n      this.#closeDropdown(true);\n    }\n\n    //* Abort all listeners registered via the main controller\n    this.#abortController.abort();\n    this.#abortController = null;\n\n    //* Remove all added DOM elements and classes.\n    this.#ui.destroy();\n\n    //* Remove this instance from the global registry.\n    if (intlTelInput.instances instanceof Map) {\n      intlTelInput.instances.delete(this.id);\n    } else {\n      delete (intlTelInput.instances as any)[this.id];\n    }\n  }\n\n  // check if the instance is still valid (not destroyed/unmounted)\n  public isActive(): boolean {\n    return !!this.#ui?.telInput;\n  }\n\n  //* Get the extension from the current number.\n  public getExtension(): string {\n    if (intlTelInput.utils && this.#ui.telInput) {\n      return intlTelInput.utils.getExtension(\n        this.#getFullNumber(),\n        this.#selectedCountryData.iso2,\n      );\n    }\n    return \"\";\n  }\n\n  //* Format the number to the given format.\n  public getNumber(format?: number): string {\n    if (intlTelInput.utils && this.#ui.telInput) {\n      const { iso2 } = this.#selectedCountryData;\n      const fullNumber = this.#getFullNumber();\n      const formattedNumber = intlTelInput.utils.formatNumber(\n        fullNumber,\n        iso2,\n        format,\n      );\n      const currentVal = this.#ui.telInput.value;\n      return this.#numerals.denormalise(formattedNumber, currentVal);\n    }\n    return \"\";\n  }\n\n  //* Get the type of the entered number e.g. landline/mobile.\n  public getNumberType(): number {\n    if (intlTelInput.utils && this.#ui.telInput) {\n      return intlTelInput.utils.getNumberType(\n        this.#getFullNumber(),\n        this.#selectedCountryData.iso2,\n      );\n    }\n    return SENTINELS.UNKNOWN_NUMBER_TYPE;\n  }\n\n  //* Get the country data for the currently selected country.\n  public getSelectedCountryData(): SelectedCountryData {\n    return this.#selectedCountryData;\n  }\n\n  //* Get the validation error.\n  public getValidationError(): number {\n    if (intlTelInput.utils && this.#ui.telInput) {\n      const { iso2 } = this.#selectedCountryData;\n      return intlTelInput.utils.getValidationError(this.#getFullNumber(), iso2);\n    }\n    return SENTINELS.UNKNOWN_VALIDATION_ERROR;\n  }\n\n  //* Validate the input val using number length only\n  public isValidNumber(): boolean | null {\n    const { dialCode, iso2 } = this.#selectedCountryData;\n    if (intlTelInput.utils && this.#ui.telInput) {\n      const number = this.#getFullNumber();\n      const coreNumber = intlTelInput.utils.getCoreNumber(number, iso2);\n      if (coreNumber) {\n        // custom validation for UK mobile numbers - useful when allowedNumberTypes=[\"MOBILE\", \"FIXED_LINE\"], where UK fixed_line numbers can be much shorter than mobile numbers\n        if (dialCode === UK.DIAL_CODE) {\n          // UK mobile numbers (starting with a 7) must have a core number that is 10 digits long (excluding dial code/national prefix)\n          if (coreNumber[0] === UK.MOBILE_PREFIX && coreNumber.length !== UK.MOBILE_CORE_LENGTH) {\n            return false;\n          }\n        }\n        // Fix for NANP and similar countries: libphonenumber can auto-prepend area codes when\n        // parsing local-format numbers (e.g. \"2462501\" for Barbados gets expanded to \"2462462501\"),\n        // making incomplete numbers appear \"possible\". Detect this by comparing the digits actually\n        // typed against the national number libphonenumber derived - if the derived national number\n        // is longer, the library completed digits the user never typed, so the number is incomplete.\n        // Skip this check for phonewords (alpha chars), since getNumeric would undercount their digits.\n        const hasAlphaChar = REGEX.ALPHA_UNICODE.test(number);\n        if (!hasAlphaChar && dialCode) {\n          const nationalPortion = number.startsWith(\"+\") ? number.slice(1 + dialCode.length) : number;\n          const nationalDigitCount = getNumeric(nationalPortion).length;\n          if (coreNumber.length > nationalDigitCount) {\n            return false;\n          }\n        }\n      }\n    }\n    return this.#validateNumber(false);\n  }\n\n  //* Validate the input val with precise validation\n  public isValidNumberPrecise(): boolean | null {\n    return this.#validateNumber(true);\n  }\n\n  #utilsIsPossibleNumber(val: string): boolean | null {\n    return intlTelInput.utils\n      ? intlTelInput.utils.isPossibleNumber(\n          val,\n          this.#selectedCountryData.iso2,\n          this.#options.allowedNumberTypes,\n        )\n      : null;\n  }\n\n  //* Shared internal validation logic to handle alpha character extension rules.\n  #validateNumber(precise: boolean): boolean | null {\n    if (!intlTelInput.utils || !this.#ui.telInput) {\n      return null;\n    }\n\n    const { allowNumberExtensions, allowPhonewords } = this.#options;\n\n    const testValidity = (s: string) =>\n      precise ? this.#utilsIsValidNumber(s) : this.#utilsIsPossibleNumber(s);\n\n    const val = this.#getFullNumber();\n\n    // If there's no selected country, still allow validation for regionless intl numbers (e.g. +800, +808, +870, +881, +882, +883, +888, +979).\n    if (!this.#selectedCountryData.iso2) {\n      const isRegionlessDialCode = hasRegionlessDialCode(val);\n      // if first char is plus, and next 3 chars are a regionless dial code\n      if (!isRegionlessDialCode) {\n        return false;\n      }\n    }\n\n    if (!testValidity(val)) {\n      return false;\n    }\n\n    // At this point, we know LPN says the number is valid, but we need to run extra checks\n\n    const alphaCharPosition = val.search(REGEX.ALPHA_UNICODE);\n    const hasAlphaChar = alphaCharPosition > -1;\n    // if there is an alpha char, we need to check if it's allowed, either as an extension or a phone word\n    if (hasAlphaChar) {\n      const selectedIso2 = this.#selectedCountryData.iso2;\n      const hasExtension = Boolean(intlTelInput.utils.getExtension(val, selectedIso2));\n      if (hasExtension) {\n        return allowNumberExtensions;\n      }\n      return allowPhonewords;\n    }\n    return true;\n  }\n\n  #utilsIsValidNumber(val: string): boolean | null {\n    return intlTelInput.utils\n      ? intlTelInput.utils.isValidNumber(\n          val,\n          this.#selectedCountryData.iso2,\n          this.#options.allowedNumberTypes,\n        )\n      : null;\n  }\n\n  //* Update the selected country, and update the input val accordingly.\n  public setCountry(iso2: Iso2): void {\n    if (!this.#ui.telInput) {\n      return;\n    }\n    const iso2Lower = iso2?.toLowerCase() as Iso2;\n    // handle invalid iso2\n    if (!isIso2(iso2Lower)) {\n      throw new Error(`Invalid country code: '${iso2Lower}'`);\n    }\n\n    const currentCountry = this.#selectedCountryData.iso2;\n    //* There is a country change IF: either there is a new country and it's different to the current one, OR there is no new country (i.e. globe state) and there is a current country\n    const isCountryChange =\n      (iso2 && iso2Lower !== currentCountry) || (!iso2 && currentCountry);\n    if (isCountryChange) {\n      this.#setCountry(iso2Lower);\n      this.#updateDialCode(this.#selectedCountryData.dialCode);\n      // reformat\n      if (this.#options.formatOnDisplay) {\n        const inputValue = this.#getTelInputValue();\n        this.#updateValFromNumber(inputValue);\n      }\n      this.#triggerCountryChange();\n    }\n  }\n\n  //* Set the input value and update the country.\n  public setNumber(number: string): void {\n    if (!this.#ui.telInput) {\n      return;\n    }\n    const normalisedNumber = this.#numerals.normalise(number);\n    //* We must update the country first, which updates this.selectedCountryData, which is used for\n    //* formatting the number before displaying it.\n    const countryChanged = this.#updateCountryFromNumber(normalisedNumber);\n    this.#updateValFromNumber(normalisedNumber);\n    if (countryChanged) {\n      this.#triggerCountryChange();\n    }\n    //* This is required for the React cmp to update its state correctly.\n    this.#trigger(EVENTS.INPUT, { isSetNumber: true });\n  }\n\n  //* Set the placeholder number typ\n  public setPlaceholderNumberType(type: NumberType): void {\n    if (!this.#ui.telInput) {\n      return;\n    }\n    this.#options.placeholderNumberType = type;\n    this.#updatePlaceholder();\n  }\n\n  public setDisabled(disabled: boolean): void {\n    if (!this.#ui.telInput) {\n      return;\n    }\n    this.#ui.telInput.disabled = disabled;\n    if (disabled) {\n      this.#ui.selectedCountry.setAttribute(\"disabled\", \"true\");\n    } else {\n      this.#ui.selectedCountry.removeAttribute(\"disabled\");\n    }\n  }\n\n  //********************\n  //*  STATIC METHODS\n  //********************\n\n  // Internal instance notification used by utils/geoip loaders.\n  // Kept public so module-level helpers (e.g. attachUtils) can call it, while still allowing\n  // access to private instance methods.\n  static forEachInstance<M extends keyof ForEachInstanceArgsMap>(\n    method: M,\n    ...args: ForEachInstanceArgsMap[M]\n  ): void {\n    const instances = intlTelInput.instances;\n    const values =\n      instances instanceof Map ? Array.from(instances.values()) : Object.values(instances);\n    const arg = args[0];\n\n    values.forEach((instance) => {\n      if (!(instance instanceof Iti)) {\n        return;\n      }\n\n      switch (method) {\n        case \"handleUtils\":\n          instance.#handleUtils();\n          break;\n        case \"handleUtilsFailure\":\n          instance.#handleUtilsFailure(arg);\n          break;\n        case \"handleAutoCountry\":\n          instance.#handleAutoCountry();\n          break;\n        case \"handleAutoCountryFailure\":\n          instance.#handleAutoCountryFailure();\n          break;\n      }\n    });\n  }\n}\n\n/********************\n *  STATIC METHODS\n ********************/\n\n//* Load the utils script.\nconst attachUtils = (source: UtilsLoader): Promise<boolean> | null => {\n  //* 2 Options:\n  //* 1) Not already started loading (start)\n  //* 2) Already started loading (do nothing - just wait for the onload callback to fire, which will\n  //* trigger handleUtils on all instances, invoking their resolveUtilsScriptPromise functions)\n  if (!intlTelInput.utils && !intlTelInput.startedLoadingUtilsScript) {\n    let loadCall;\n    if (typeof source === \"function\") {\n      try {\n        loadCall = Promise.resolve(source());\n      } catch (error) {\n        return Promise.reject(error);\n      }\n    } else {\n      return Promise.reject(\n        new TypeError(\n          `The argument passed to attachUtils must be a function that returns a promise for the utilities module, not ${typeof source}`,\n        ),\n      );\n    }\n\n    //* Only do this once.\n    intlTelInput.startedLoadingUtilsScript = true;\n\n    return loadCall\n      .then((module) => {\n        const utils = module?.default;\n        if (!utils || typeof utils !== \"object\") {\n          throw new TypeError(\n            \"The loader function passed to attachUtils did not resolve to a module object with utils as its default export.\",\n          );\n        }\n\n        intlTelInput.utils = utils;\n        Iti.forEachInstance(\"handleUtils\");\n        return true;\n      })\n      .catch((error: Error) => {\n        Iti.forEachInstance(\"handleUtilsFailure\", error);\n        throw error;\n      });\n  }\n  return null;\n};\n\n//* Convenience wrapper.\n\nconst intlTelInput: IntlTelInputInterface = Object.assign(\n  (input: HTMLInputElement, options?: SomeOptions): Iti => {\n    const iti = new Iti(input, options);\n    intlTelInput.instances[iti.id] = iti;\n    input.iti = iti;\n    return iti;\n  },\n  {\n    defaults,\n    //* Using a static var like this allows us to mock it in the tests.\n    documentReady: (): boolean => document.readyState === \"complete\",\n    //* Get the country data object.\n    getCountryData: (): Country[] => allCountries,\n    //* A getter for the plugin instance.\n    getInstance: (input: HTMLInputElement): Iti | null => {\n      const id = input.dataset.intlTelInputId;\n      return id ? intlTelInput.instances[id] : null;\n    },\n    //* A map from instance ID to instance object.\n    instances: {},\n    attachUtils,\n    startedLoadingUtilsScript: false,\n    startedLoadingAutoCountry: false,\n    version: process.env.VERSION,\n  },\n);\n\nexport default intlTelInput;\n"
  },
  {
    "path": "src/js/modules/constants.ts",
    "content": "// Shared constants extracted from intl-tel-input logic to avoid magic strings/numbers\n\nimport type { Iso2 } from \"../intl-tel-input/data\";\n\nexport const EVENTS = {\n  OPEN_COUNTRY_DROPDOWN: \"open:countrydropdown\",\n  CLOSE_COUNTRY_DROPDOWN: \"close:countrydropdown\",\n  COUNTRY_CHANGE: \"countrychange\",\n  INPUT: \"input\", // used for synthetic input trigger\n} as const;\n\nexport const CLASSES = {\n  HIDE: \"iti__hide\",\n  V_HIDE: \"iti__v-hide\",\n  ARROW_UP: \"iti__arrow--up\",\n  GLOBE: \"iti__globe\",\n  FLAG: \"iti__flag\",\n  LOADING: \"iti__loading\",\n  COUNTRY_ITEM: \"iti__country\",\n  HIGHLIGHT: \"iti__highlight\",\n} as const;\n\nexport const KEYS = {\n  ARROW_UP: \"ArrowUp\",\n  ARROW_DOWN: \"ArrowDown\",\n  SPACE: \" \",\n  ENTER: \"Enter\",\n  ESC: \"Escape\",\n  TAB: \"Tab\",\n} as const;\n\nexport const INPUT_TYPES = {\n  PASTE: \"insertFromPaste\",\n  DELETE_FWD: \"deleteContentForward\",\n} as const;\n\nexport const REGEX = {\n  ALPHA_UNICODE: /\\p{L}/u, // any kind of letter from any language\n  NON_PLUS_NUMERIC: /[^+0-9]/, // chars that are NOT + or digit\n  NON_PLUS_NUMERIC_GLOBAL: /[^+0-9]/g, // chars that are NOT + or digit (global)\n  HIDDEN_SEARCH_CHAR: /^[a-zA-ZÀ-ÿа-яА-Я ]$/, // single acceptable hidden-search char\n} as const;\n\nexport const TIMINGS = {\n  SEARCH_DEBOUNCE_MS: 100,\n  HIDDEN_SEARCH_RESET_MS: 1000,\n  NEXT_TICK: 0,\n} as const;\n\nexport const SENTINELS = {\n  UNKNOWN_NUMBER_TYPE: -99,\n  UNKNOWN_VALIDATION_ERROR: -99,\n} as const;\n\n// Layout / sizing fallbacks (used when measuring elements fails e.g., hidden containers)\nexport const LAYOUT = {\n  NARROW_VIEWPORT_WIDTH: 500, // keep in sync with .iti__country-list CSS media query\n  SANE_SELECTED_WITH_DIAL_WIDTH: 78, // px width fallback when separateDialCode enabled\n  SANE_SELECTED_NO_DIAL_WIDTH: 42, // px width fallback when no separate dial code\n  INPUT_PADDING_EXTRA_LEFT: 6, // px gap between selected country container and input text\n  DROPDOWN_MARGIN: 3, // px margin between dropdown and tel input\n  SANE_DROPDOWN_HEIGHT: 200, // px height fallback for dropdown\n} as const;\n\n// Helpful grouping for dial code logic (kept lean; expand only if reused widely)\nexport const DIAL = {\n  PLUS: \"+\",\n  NANP: \"1\", // North American Numbering Plan\n} as const;\n\n// Country-specific telephone rules\nexport const UK = {\n  ISO2: \"gb\" as Iso2,\n  DIAL_CODE: \"44\", // +44 United Kingdom\n  MOBILE_PREFIX: \"7\", // UK mobile numbers start with 7 after national trunk (0) or core section\n  MOBILE_CORE_LENGTH: 10, // core number length (excluding dial code / national prefix) for mobiles\n} as const;\n\nexport const US = {\n  ISO2: \"us\" as Iso2,\n  DIAL_CODE: \"1\", // +1 United States\n};\n\n// Placeholder / initial country string values (extracted for typo safety if needed elsewhere later)\nexport const PLACEHOLDER_MODES = {\n  AGGRESSIVE: \"aggressive\",\n  POLITE: \"polite\",\n  OFF: \"off\",\n} as const;\n\nexport const INITIAL_COUNTRY = {\n  AUTO: \"auto\",\n} as const;\n\n// libphonenumber number types (string literals exposed in public options)\n// must be kept in sync with the same list in utils.js and the libphonenumber metadata\n// TODO: add a build step to extract this from i18n.phonenumbers.PhoneNumberType in third_party/libphonenumber/javascript/i18n/phonenumbers/phonenumberutil.js\nconst NUMBER_TYPES = [\n  \"FIXED_LINE\",\n  \"MOBILE\",\n  \"FIXED_LINE_OR_MOBILE\",\n  \"TOLL_FREE\",\n  \"PREMIUM_RATE\",\n  \"SHARED_COST\",\n  \"VOIP\",\n  \"PERSONAL_NUMBER\",\n  \"PAGER\",\n  \"UAN\",\n  \"VOICEMAIL\",\n  \"UNKNOWN\",\n] as const;\n\nexport const NUMBER_TYPE_SET = new Set(NUMBER_TYPES) as ReadonlySet<\n  (typeof NUMBER_TYPES)[number]\n> & {\n  has(value: string): boolean;\n};\n\n// Data-* keys used on DOM nodes\nexport const DATA_KEYS = {\n  COUNTRY_CODE: \"countryCode\",\n  DIAL_CODE: \"dialCode\",\n} as const;\n\n// ARIA attribute names (avoid typos & for potential future refactors)\nexport const ARIA = {\n  EXPANDED: \"aria-expanded\",\n  LABEL: \"aria-label\",\n  SELECTED: \"aria-selected\",\n  ACTIVE_DESCENDANT: \"aria-activedescendant\",\n  HASPOPUP: \"aria-haspopup\",\n  CONTROLS: \"aria-controls\",\n  HIDDEN: \"aria-hidden\",\n  AUTOCOMPLETE: \"aria-autocomplete\",\n  MODAL: \"aria-modal\",\n} as const;\n"
  },
  {
    "path": "src/js/modules/core/countrySearch.ts",
    "content": "// * Country search & ranking logic extracted from intl-tel-input.ts\n// * Maintains original comments/order. Pure functions for reuse & testability.\n\nimport type { Country } from \"../../intl-tel-input/data\";\nimport { normaliseString } from \"../utils/string\";\n\n/**\n * Country search: Given raw query, return ordered list of countries by priority buckets.\n * Buckets (in order):\n *  1. exact ISO2 matches\n *  2. name starts with\n *  3. name contains\n *  4. dial code exact match (bare or with plus)\n *  5. dial code contains (with plus form)\n *  6. initials match\n * Each bucket preserves country.priority ordering.\n */\nexport const getMatchedCountries = (\n  countries: Country[],\n  query: string,\n): Country[] => {\n  const normalisedQuery = normaliseString(query);\n  // search result groups, in order of priority\n  // first, exact ISO2 matches, then name starts with, then name contains, dial code match etc.\n  const iso2Matches: Country[] = [];\n  const nameStartWith: Country[] = [];\n  const nameContains: Country[] = [];\n  const dialCodeMatches: Country[] = [];\n  const dialCodeContains: Country[] = [];\n  const initialsMatches: Country[] = [];\n\n  for (const c of countries) {\n    if (c.iso2 === normalisedQuery) {\n      iso2Matches.push(c);\n    } else if (c.normalisedName.startsWith(normalisedQuery)) {\n      nameStartWith.push(c);\n    } else if (c.normalisedName.includes(normalisedQuery)) {\n      nameContains.push(c);\n    } else if (\n      normalisedQuery === c.dialCode ||\n      normalisedQuery === c.dialCodePlus\n    ) {\n      dialCodeMatches.push(c);\n    } else if (c.dialCodePlus.includes(normalisedQuery)) {\n      dialCodeContains.push(c);\n    } else if (c.initials.includes(normalisedQuery)) {\n      initialsMatches.push(c);\n    }\n  }\n\n  // Combine result groups in correct order (and respect country priority order within each group e.g. if search +44, then UK appears first above Guernsey etc)\n  const sortByPriority = (a: Country, b: Country) => a.priority - b.priority;\n  return [\n    ...iso2Matches.sort(sortByPriority),\n    ...nameStartWith.sort(sortByPriority),\n    ...nameContains.sort(sortByPriority),\n    ...dialCodeMatches.sort(sortByPriority),\n    ...dialCodeContains.sort(sortByPriority),\n    ...initialsMatches.sort(sortByPriority),\n  ];\n};\n\n/**\n * Hidden search (when countrySearch disabled): find first whose name starts with query (case-insensitive).\n */\nexport const findFirstCountryStartingWith = (\n  countries: Country[],\n  query: string,\n): Country | null => {\n  const lowerQuery = query.toLowerCase();\n  for (const c of countries) {\n    const lowerName = c.name.toLowerCase();\n    if (lowerName.startsWith(lowerQuery)) {\n      return c;\n    }\n  }\n  return null;\n};\n"
  },
  {
    "path": "src/js/modules/core/icons.ts",
    "content": "import { ARIA } from \"../constants\";\n\n/** Magnifying glass search icon */\nexport const buildSearchIcon = (): string => `\n  <svg class=\"iti__search-icon-svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" focusable=\"false\" ${ARIA.HIDDEN}=\"true\">\n    <circle cx=\"11\" cy=\"11\" r=\"7\" />\n    <line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\" />\n  </svg>`;\n\n/**\n * Clear (circle with X) icon\n * @param id Instance id used to create a unique mask id.\n */\nexport const buildClearIcon = (id: number): string => {\n  const maskId = `iti-${id}-clear-mask`;\n  return `\n    <svg class=\"iti__search-clear-svg\" width=\"12\" height=\"12\" viewBox=\"0 0 16 16\" ${ARIA.HIDDEN}=\"true\" focusable=\"false\">\n      <mask id=\"${maskId}\" maskUnits=\"userSpaceOnUse\">\n        <rect width=\"16\" height=\"16\" fill=\"white\" />\n        <path d=\"M5.2 5.2 L10.8 10.8 M10.8 5.2 L5.2 10.8\" stroke=\"black\" stroke-linecap=\"round\" class=\"iti__search-clear-x\" />\n      </mask>\n      <circle cx=\"8\" cy=\"8\" r=\"8\" class=\"iti__search-clear-bg\" mask=\"url(#${maskId})\" />\n    </svg>`;\n};\n\n/** Check icon shown next to the selected country in the dropdown. */\nexport const buildCheckIcon = (): string => `\n  <svg class=\"iti__country-check-svg\" width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"currentColor\" focusable=\"false\" ${ARIA.HIDDEN}=\"true\">\n    <path d=\"M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0m-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z\"/>\n  </svg>`;\n\n/** Globe icon used when no country is selected */\nexport const buildGlobeIcon = (): string => `\n  <svg width=\"256\" height=\"256\" viewBox=\"0 0 512 512\" class=\"iti__globe-svg\">\n    <path d=\"M508 213a240 240 0 0 0-449-87l-2 5-2 5c-8 14-13 30-17 46a65 65 0 0 1 56 4c16-10 35-19 56-27l9-3c-6 23-10 48-10 74h-16l4 6c3 4 5 8 6 13h6c0 22 3 44 8 65l2 10-25-10-4 5 12 18 9 3 6 2 8 3 9 26 1 2 16-7h1l-5-13-1-2c24 6 49 9 75 10v26l11 10 7 7v-30l1-13c22 0 44-3 65-8l10-2-21 48-1 1a317 317 0 0 1-14 23l-21 5h-2c6 16 7 33 1 50a240 240 0 0 0 211-265m-401-56-11 6c19-44 54-79 98-98-11 20-21 44-29 69-21 6-40 15-58 23m154 182v4c-29-1-57-6-81-13-7-25-12-52-13-81h94zm0-109h-94c1-29 6-56 13-81 24-7 52-12 81-13zm0-112c-22 1-44 4-65 8l-10 2 12-30 9-17 1-2a332 332 0 0 1 13-23c13-4 26-6 40-7zm187 69 6 4c4 12 6 25 6 38v1h-68c-1-26-4-51-10-74l48 20 1 1 14 8zm-14-44 10 20c-20-11-43-21-68-29-8-25-18-49-29-69 37 16 67 44 87 78M279 49h1c13 1 27 3 39 7l14 23 1 2a343 343 0 0 1 12 26l2 5 6 16c-23-6-48-9-74-10h-1zm0 87h1c29 1 56 6 81 13 7 24 12 51 12 80v1h-94zm2 207h-2v-94h95c-1 29-6 56-13 81-24 7-51 12-80 13m86 60-20 10c11-20 21-43 29-68 25-8 48-18 68-29-16 37-43 67-77 87m87-115-7 5-16 9-2 1a337 337 0 0 1-47 21c6-24 9-49 10-75h68c0 13-2 27-6 39\"/>\n    <path d=\"m261 428-2-2-22-21a40 40 0 0 0-32-11h-1a37 37 0 0 0-18 8l-1 1-4 2-2 2-5 4c-9-3-36-31-47-44s-32-45-34-55l3-2a151 151 0 0 0 11-9v-1a39 39 0 0 0 5-48l-3-3-11-19-3-4-5-7h-1l-3-3-4-3-5-2a35 35 0 0 0-16-3h-5c-4 1-14 5-24 11l-4 2-4 3-4 2c-9 8-17 17-18 27a380 380 0 0 0 212 259h3c12 0 25-10 36-21l10-12 6-11a39 39 0 0 0-8-40\"/>\n  </svg>`;\n"
  },
  {
    "path": "src/js/modules/core/numerals.ts",
    "content": "export class Numerals {\n  #userNumeralSet: \"ascii\" | \"arabic-indic\" | \"persian\" | undefined;\n\n  constructor() {\n    // intentionally left uninitialised until we see some input\n  }\n\n  // If any Arabic-Indic digits, then label it as that set. Same for Persian. Otherwise assume ASCII.\n  #updateNumeralSet(str: string): void {\n    if (/[\\u0660-\\u0669]/.test(str)) {\n      this.#userNumeralSet = \"arabic-indic\";\n    } else if (/[\\u06F0-\\u06F9]/.test(str)) {\n      this.#userNumeralSet = \"persian\";\n    } else {\n      this.#userNumeralSet = \"ascii\";\n    }\n  }\n\n  // Denormalise ASCII 0-9 to the user's numeral set, if known. If not known, return the string as-is.\n  public denormalise(str: string, currentInputValue: string): string {\n    if (!this.#userNumeralSet) {\n      this.#updateNumeralSet(currentInputValue);\n    }\n    if (this.#userNumeralSet === \"ascii\") {\n      return str;\n    }\n    const base = this.#userNumeralSet === \"arabic-indic\" ? 0x0660 : 0x06f0;\n    return str.replace(/[0-9]/g, (d) => String.fromCharCode(base + Number(d)));\n  }\n\n  // Normalize Eastern Arabic (U+0660-0669) and Persian/Extended Arabic-Indic (U+06F0-06F9) numerals to ASCII 0-9\n  public normalise(str: string): string {\n    if (!str) {\n      return \"\";\n    }\n    this.#updateNumeralSet(str);\n    if (this.#userNumeralSet === \"ascii\") {\n      return str;\n    }\n    const base = this.#userNumeralSet === \"arabic-indic\" ? 0x0660 : 0x06f0;\n    const regex = this.#userNumeralSet === \"arabic-indic\" ? /[\\u0660-\\u0669]/g : /[\\u06F0-\\u06F9]/g;\n    return str.replace(regex, (ch) => String.fromCharCode(0x30 + (ch.charCodeAt(0) - base)));\n  }\n\n  public isAscii(): boolean {\n    return this.#userNumeralSet === \"ascii\";\n  }\n}\n"
  },
  {
    "path": "src/js/modules/core/options.ts",
    "content": "import { INITIAL_COUNTRY, PLACEHOLDER_MODES, NUMBER_TYPE_SET, LAYOUT } from \"../constants\";\nimport defaultEnglishStrings from \"../../intl-tel-input/i18n/en\";\nimport allCountries, { type Iso2 } from \"../../intl-tel-input/data\";\nimport type { AllOptions, SomeOptions } from \"../types/public-api\";\n\n// Helper for media query evaluation\nconst mq = (q: string): boolean =>\n  typeof window !== \"undefined\" &&\n  typeof window.matchMedia === \"function\" &&\n  window.matchMedia(q).matches;\n\nconst isNarrowViewport = () => mq(`(max-width: ${LAYOUT.NARROW_VIEWPORT_WIDTH}px)`);\n\n//* Helper to decide whether to use fullscreen popup by default\nconst computeDefaultUseFullscreenPopup = (): boolean => {\n  if (typeof navigator !== \"undefined\" && typeof window !== \"undefined\") {\n    const isShortViewport = mq(\"(max-height: 600px)\");\n    const isCoarsePointer = mq(\"(pointer: coarse)\");\n    /* Heuristic rationale: If narrow width OR (coarse pointer with constrained height) we  prefer fullscreen for usability. Coarse pointer usually implies touch (phones/tablets, some hybrids) where larger touch targets help (and virtual keyboards may be used, which consume more vertical space). */\n    return (\n      isNarrowViewport() ||\n      (isCoarsePointer && isShortViewport)\n    );\n  }\n  return false;\n};\n\nexport const defaults: AllOptions = {\n  //* Whether or not to allow the dropdown.\n  allowDropdown: true,\n  //* The number type to enforce during validation.\n  allowedNumberTypes: [\"MOBILE\", \"FIXED_LINE\"],\n  //* Whether or not to allow extensions after the main number.\n  allowNumberExtensions: false,\n  // Allow alphanumeric \"phonewords\" (e.g. +1 800 FLOWERS) as valid numbers\n  allowPhonewords: false,\n  //* Add a placeholder in the input with an example number for the selected country.\n  autoPlaceholder: PLACEHOLDER_MODES.POLITE,\n  //* Add a custom class to the (injected) container element.\n  containerClass: \"\",\n  //* Locale for localising country names via Intl.DisplayNames.\n  countryNameLocale: \"en\",\n  //* The order of the countries in the dropdown. Defaults to alphabetical.\n  countryOrder: null,\n  //* Add a country search input at the top of the dropdown.\n  countrySearch: true,\n  //* Modify the auto placeholder.\n  customPlaceholder: null,\n  //* Always show the dropdown\n  dropdownAlwaysOpen: false,\n  //* Append menu to specified element.\n  dropdownContainer: null,\n  //* Don't display these countries.\n  excludeCountries: [],\n  //* Fix the dropdown width to the input width (rather than being as wide as the longest country name).\n  fixDropdownWidth: true,\n  //* Format the number as the user types\n  formatAsYouType: true,\n  //* Format the input value during initialisation and on setNumber.\n  formatOnDisplay: true,\n  //* geoIp lookup function.\n  geoIpLookup: null,\n  //* Inject a hidden input with the name returned from this function, and on submit, populate it with the result of getNumber.\n  hiddenInput: null,\n  //* Internationalise the plugin text e.g. search input placeholder, country names.\n  i18n: {},\n  //* Initial country.\n  initialCountry: \"\",\n  //* A function to load the utils script.\n  loadUtils: null,\n  //* National vs international formatting for numbers e.g. placeholders and displaying existing numbers.\n  nationalMode: true,\n  //* Display only these countries.\n  onlyCountries: [],\n  //* Number type to use for placeholders.\n  placeholderNumberType: \"MOBILE\",\n  //* Add custom classes to the search input element.\n  searchInputClass: \"\",\n  //* Display the international dial code next to the selected flag.\n  separateDialCode: false,\n  //* Show flags - for both the selected country, and in the country dropdown\n  showFlags: true,\n  //* Only allow certain chars e.g. a plus followed by numeric digits, and cap at max valid length.\n  strictMode: false,\n  //* Use full screen popup instead of dropdown for country list.\n  useFullscreenPopup: computeDefaultUseFullscreenPopup(),\n};\n\nconst toString = (val: unknown): string => JSON.stringify(val);\n\nconst isPlainObject = (val: unknown): val is Record<string, unknown> =>\n  Boolean(val) && typeof val === \"object\" && !Array.isArray(val);\n\nconst isFn = (val: unknown): val is (...args: unknown[]) => unknown =>\n  typeof val === \"function\";\n\nconst isElLike = (val: unknown): val is HTMLElement => {\n  if (!val || typeof val !== \"object\") return false;\n  const v = val as any;\n  return v.nodeType === 1 && typeof v.tagName === \"string\" && typeof v.appendChild === \"function\";\n};\n\nconst iso2Set: Set<Iso2> = new Set(allCountries.map((c) => c.iso2));\nconst isIso2 = (val: string): val is Iso2 => iso2Set.has(val as Iso2);\n\nconst placeholderModeSet = new Set<string>(Object.values(PLACEHOLDER_MODES));\n\nconst warn = (message: string): void => {\n  // eslint-disable-next-line no-console\n  console.warn(`[intl-tel-input] ${message}`);\n};\n\nconst warnOption = (optionName: string, expectedType: string, actualValue: unknown): void => {\n  warn(`Option '${optionName}' must be ${expectedType}; got ${toString(actualValue)}. Ignoring.`);\n};\n\nconst hasOwn = (obj: object, key: string): boolean =>\n  Object.prototype.hasOwnProperty.call(obj, key);\n\nconst validateIso2Array = (key: string, value: unknown): boolean => {\n  const expectedType = \"an array of ISO2 country code strings\";\n  if (!Array.isArray(value)) {\n    warnOption(key, expectedType, value);\n    return false;\n  }\n  for (const v of value) {\n    if (typeof v !== \"string\") {\n      warnOption(key, expectedType, value);\n      return false;\n    }\n    const lower = v.toLowerCase();\n    if (!isIso2(lower)) {\n      warn(`Invalid country code in '${key}': '${v}'. Ignoring.`);\n      return false;\n    }\n  }\n  return true;\n};\n\n/**\n * Validate runtime init options\n */\nexport const validateOptions = (customOptions: unknown): SomeOptions => {\n  if (customOptions === undefined) {\n    return {};\n  }\n\n  if (!isPlainObject(customOptions)) {\n    const error = `The second argument must be an options object; got ${toString(customOptions)}. Using defaults.`;\n    warn(error);\n    return {};\n  }\n\n  const validatedOptions: Record<string, unknown> = {};\n\n  for (const [key, value] of Object.entries(customOptions)) {\n    // Check option exists\n    if (!hasOwn(defaults, key)) {\n      warn(`Unknown option '${key}'. Ignoring.`);\n      continue;\n    }\n\n    switch (key) {\n      case \"allowDropdown\":\n      case \"allowNumberExtensions\":\n      case \"allowPhonewords\":\n      case \"countrySearch\":\n      case \"dropdownAlwaysOpen\":\n      case \"fixDropdownWidth\":\n      case \"formatAsYouType\":\n      case \"formatOnDisplay\":\n      case \"nationalMode\":\n      case \"showFlags\":\n      case \"separateDialCode\":\n      case \"strictMode\":\n      case \"useFullscreenPopup\":\n        if (typeof value !== \"boolean\") {\n          warnOption(key, \"a boolean\", value);\n          break;\n        }\n        validatedOptions[key] = value;\n        break;\n\n      case \"autoPlaceholder\":\n        if (typeof value !== \"string\" || !placeholderModeSet.has(value)) {\n          const validModes = Array.from(placeholderModeSet).join(\", \");\n          warnOption(\"autoPlaceholder\", `one of ${validModes}`, value);\n          break;\n        }\n        validatedOptions[key] = value;\n        break;\n\n      case \"containerClass\":\n      case \"searchInputClass\":\n      case \"countryNameLocale\":\n        if (typeof value !== \"string\") {\n          warnOption(key, \"a string\", value);\n          break;\n        }\n        validatedOptions[key] = value;\n        break;\n\n      case \"countryOrder\":\n        if (value === null || validateIso2Array(key, value)) {\n          validatedOptions[key] = value;\n        }\n        break;\n\n      case \"customPlaceholder\":\n      case \"geoIpLookup\":\n      case \"hiddenInput\":\n      case \"loadUtils\":\n        if (value !== null && !isFn(value)) {\n          warnOption(key, \"a function or null\", value);\n          break;\n        }\n        validatedOptions[key] = value;\n        break;\n\n      case \"dropdownContainer\":\n        if (value !== null && !isElLike(value)) {\n          warnOption(\"dropdownContainer\", \"an HTMLElement or null\", value);\n          break;\n        }\n        validatedOptions[key] = value;\n        break;\n\n      case \"excludeCountries\":\n      case \"onlyCountries\":\n        if (validateIso2Array(key, value)) {\n          validatedOptions[key] = value;\n        }\n        break;\n\n      case \"i18n\":\n        if (value && !isPlainObject(value)) {\n          warnOption(\"i18n\", \"an object\", value);\n          break;\n        }\n        // don't bother validating the shape of the object, as the standard use is to just pass in one of the provided i18n objects.\n        validatedOptions[key] = value;\n        break;\n\n      case \"initialCountry\": {\n        if (typeof value !== \"string\") {\n          warnOption(\"initialCountry\", \"a string\", value);\n          break;\n        }\n        const lower = value.toLowerCase();\n        if (lower && (lower !== INITIAL_COUNTRY.AUTO && !isIso2(lower))) {\n          warnOption(\"initialCountry\", \"a valid ISO2 country code or 'auto'\", value);\n          break;\n        }\n        validatedOptions[key] = value;\n        break;\n      }\n\n      case \"placeholderNumberType\":\n        if (typeof value !== \"string\" || !NUMBER_TYPE_SET.has(value)) {\n          const validTypes = Array.from(NUMBER_TYPE_SET).join(\", \");\n          warnOption(\"placeholderNumberType\", `one of ${validTypes}`, value);\n          break;\n        }\n        validatedOptions[key] = value;\n        break;\n\n      case \"allowedNumberTypes\":\n        if (value !== null) {\n          if (!Array.isArray(value)) {\n            warnOption(\"allowedNumberTypes\", \"an array of number types or null\", value);\n            break;\n          }\n          let allValid = true;\n          for (const v of value as unknown[]) {\n            if (typeof v !== \"string\" || !NUMBER_TYPE_SET.has(v)) {\n              const validTypes = Array.from(NUMBER_TYPE_SET).join(\", \");\n              warnOption(\"allowedNumberTypes\", `an array of valid number types (${validTypes})`, v);\n              allValid = false;\n              break;\n            }\n          }\n          if (allValid) {\n            // include it (even if empty)\n            validatedOptions[key] = value;\n          }\n        } else {\n          validatedOptions[key] = null;\n        }\n        break;\n    }\n  }\n\n  return validatedOptions as SomeOptions;\n};\n\n// Apply option side-effects (mutates the passed object)\nexport const applyOptionSideEffects = (o: AllOptions): void => {\n  if (o.dropdownAlwaysOpen) {\n    o.useFullscreenPopup = false;\n    o.allowDropdown = true;\n  }\n\n  //* If showing fullscreen popup, do not fix the width.\n  if (o.useFullscreenPopup) {\n    o.fixDropdownWidth = false;\n  } else {\n    // if fullscreen popup disabled for whatever reason, but it's still a narrow screen (so full width dropdown wont fit), then the best UX is to fix dropdown width to input width.\n    if (isNarrowViewport()) {\n      o.fixDropdownWidth = true;\n    }\n  }\n\n  //* If theres only one country, then use it!\n  if (o.onlyCountries.length === 1) {\n    o.initialCountry = o.onlyCountries[0];\n  }\n\n  //* When separateDialCode enabled, we force nationalMode to false (because the displayed dial code is supposed to be thought of as part of the typed number).\n  if (o.separateDialCode) {\n    o.nationalMode = false;\n  }\n\n  // if there is a country dropdown, but no flags and no separate dial code, then it suggests that there are multiple countries to choose from, but no way to see which one is currently selected, so we force nationalMode to false, as it doesn't make sense to show a national number placeholder if there's no way to see which country is selected\n  if (o.allowDropdown && !o.showFlags && !o.separateDialCode) {\n    o.nationalMode = false;\n  }\n\n  //* If we want a full screen dropdown, we must append it to the body.\n  if (o.useFullscreenPopup && !o.dropdownContainer) {\n    o.dropdownContainer = document.body;\n  }\n\n  //* Allow overriding the default interface strings.\n  o.i18n = { ...defaultEnglishStrings, ...o.i18n };\n};\n"
  },
  {
    "path": "src/js/modules/core/ui.ts",
    "content": "import type { Country, Iso2 } from \"../../intl-tel-input/data\";\nimport type { AllOptions, SelectedCountryData } from \"../types/public-api\";\nimport { buildClassNames, createEl } from \"../utils/dom\";\nimport {\n  buildSearchIcon,\n  buildClearIcon,\n  buildCheckIcon,\n  buildGlobeIcon,\n} from \"./icons\";\nimport { CLASSES, ARIA, LAYOUT, KEYS, TIMINGS } from \"../constants\";\nimport { getMatchedCountries } from \"./countrySearch\";\n\nexport default class UI {\n  // private\n  readonly #options: AllOptions;\n  readonly #id: number;\n  readonly #isRTL: boolean;\n  readonly #originalPaddingLeft: string;\n  #countries: Country[];\n  #searchKeyupTimer: ReturnType<typeof setTimeout> | null = null;\n  #inlineDropdownHeight: number | null = null;\n  #selectedDialCode: HTMLElement;\n  #dropdownArrow: HTMLElement;\n  #dropdownContent: HTMLElement;\n  #searchIcon: HTMLElement;\n  #searchNoResults: HTMLElement;\n  #searchResultsA11yText: HTMLElement;\n  #dropdownForContainer: HTMLElement | null = null;\n  #selectedItem: HTMLElement | null = null;\n\n  // public\n  public telInput: HTMLInputElement;\n  public countryContainer: HTMLElement;\n  public selectedCountry: HTMLElement;\n  public selectedCountryInner: HTMLElement;\n  public searchInput: HTMLInputElement;\n  public searchClearButton: HTMLButtonElement;\n  public countryList: HTMLElement;\n  public hiddenInput: HTMLInputElement;\n  public hiddenInputCountry: HTMLInputElement;\n  public highlightedItem: HTMLElement | null = null;\n  public readonly hadInitialPlaceholder: boolean;\n\n  public constructor(input: HTMLInputElement, options: AllOptions, id: number) {\n    input.dataset.intlTelInputId = id.toString();\n    this.telInput = input;\n    this.#options = options;\n    this.#id = id;\n    this.hadInitialPlaceholder = Boolean(input.getAttribute(\"placeholder\"));\n    this.#isRTL = !!this.telInput.closest(\"[dir=rtl]\");\n    //* Store original styling before we override it.\n    if (this.#options.separateDialCode) {\n      this.#originalPaddingLeft = this.telInput.style.paddingLeft;\n    }\n  }\n\n  // Validate that the provided element is an HTMLInputElement.\n  public static validateInput(input: unknown): void {\n    const tagName = (input as { tagName?: unknown } | null)?.tagName;\n    const isInputEl =\n      Boolean(input) &&\n      typeof input === \"object\" &&\n      tagName === \"INPUT\" &&\n      typeof (input as { setAttribute?: unknown }).setAttribute === \"function\";\n\n    if (!isInputEl) {\n      const type = Object.prototype.toString.call(input);\n      throw new TypeError(\n        `The first argument must be an HTMLInputElement, not ${type}`,\n      );\n    }\n  }\n\n  //* Generate all of the markup for the plugin: the selected country overlay, and the dropdown.\n  public generateMarkup(countries: Country[]): void {\n    this.#countries = countries;\n\n    this.telInput.classList.add(\"iti__tel-input\");\n    //* Modern browsers pay more attention to autocomplete and inputmode (mobile keyboard mode) than type=\"tel\"\n    if (!this.telInput.hasAttribute(\"autocomplete\")) {\n      this.telInput.setAttribute(\"autocomplete\", \"tel\");\n    }\n    if (!this.telInput.hasAttribute(\"inputmode\")) {\n      this.telInput.setAttribute(\"inputmode\", \"tel\");\n    }\n\n    const wrapper = this.#createWrapperAndInsert();\n    this.#maybeBuildCountryContainer(wrapper);\n    wrapper.appendChild(this.telInput);\n\n    this.#maybeUpdateInputPaddingAndReveal();\n    this.#maybeBuildHiddenInputs(wrapper);\n  }\n\n  #createWrapperAndInsert(): HTMLElement {\n    const {\n      allowDropdown,\n      showFlags,\n      containerClass,\n      useFullscreenPopup,\n    } = this.#options;\n\n    //* Containers (mostly for positioning).\n    const parentClasses = buildClassNames({\n      iti: true,\n      \"iti--allow-dropdown\": allowDropdown,\n      \"iti--show-flags\": showFlags,\n      \"iti--inline-dropdown\": !useFullscreenPopup,\n      [containerClass]: Boolean(containerClass),\n    });\n    const wrapper = createEl(\"div\", { class: parentClasses });\n    // if the page is RTL, then add dir=LTR to the wrapper, as numbers are still written LTR, so the input should be LTR, but we also need to display any separate dial code to the left as well (but we then make the dropdown content RTL)\n    if (this.#isRTL) {\n      wrapper.setAttribute(\"dir\", \"ltr\");\n    }\n    this.telInput.before(wrapper);\n    return wrapper;\n  }\n\n  #maybeBuildCountryContainer(wrapper: HTMLElement): void {\n    const { allowDropdown, separateDialCode, showFlags } = this.#options;\n\n    //* If we need a countryContainer\n    if (allowDropdown || showFlags || separateDialCode) {\n      this.countryContainer = createEl(\n        \"div\",\n        // visibly hidden until we measure it's width to set the input padding correctly\n        { class: `iti__country-container ${CLASSES.V_HIDE}` },\n        wrapper,\n      );\n\n      //* Selected country (displayed on left of input while allowDropdown is enabled, otherwise to right)\n      //* https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only\n      if (allowDropdown) {\n        this.selectedCountry = createEl(\n          \"button\",\n          {\n            type: \"button\",\n            class: \"iti__selected-country\",\n            [ARIA.EXPANDED]: \"false\",\n            [ARIA.LABEL]: this.#options.i18n.noCountrySelected,\n            [ARIA.HASPOPUP]: \"dialog\",\n            [ARIA.CONTROLS]: `iti-${this.#id}__dropdown-content`,\n          },\n          this.countryContainer,\n        );\n\n        if (this.telInput.disabled) {\n          this.selectedCountry.setAttribute(\"disabled\", \"true\");\n        }\n      } else {\n        this.selectedCountry = createEl(\n          \"div\",\n          { class: \"iti__selected-country\" },\n          this.countryContainer,\n        );\n      }\n\n      // The element that gets a grey background on hover (if allowDropdown enabled)\n      const selectedCountryPrimary = createEl(\n        \"div\",\n        { class: \"iti__selected-country-primary\" },\n        this.selectedCountry,\n      );\n\n      //* This is where we will add the selected flag (or globe) class later\n      this.selectedCountryInner = createEl(\n        \"div\",\n        { class: CLASSES.FLAG },\n        selectedCountryPrimary,\n      );\n\n      if (allowDropdown) {\n        this.#dropdownArrow = createEl(\n          \"div\",\n          { class: \"iti__arrow\", [ARIA.HIDDEN]: \"true\" },\n          selectedCountryPrimary,\n        );\n      }\n\n      if (separateDialCode) {\n        this.#selectedDialCode = createEl(\n          \"div\",\n          { class: \"iti__selected-dial-code\" },\n          this.selectedCountry,\n        );\n      }\n\n      if (allowDropdown) {\n        this.#buildDropdownContent();\n      }\n    }\n  }\n\n  #maybeEnsureDropdownWidthSet(): void {\n    const { fixDropdownWidth } = this.#options;\n\n    // Note: fixDropdownWidth is always false if useFullscreenPopup is true\n    // don't re-set it if it's already set\n    if (fixDropdownWidth && !this.#dropdownContent.style.width) {\n      const inputWidth = this.telInput.offsetWidth;\n      // dont fix dropdown width if input width is zero (e.g. it's hidden during init)\n      if (inputWidth > 0) {\n        this.#dropdownContent.style.width = `${inputWidth}px`;\n      }\n    }\n  }\n\n  #buildDropdownContent(): void {\n    const {\n      fixDropdownWidth,\n      useFullscreenPopup,\n      countrySearch,\n      i18n,\n      dropdownContainer,\n      containerClass,\n    } = this.#options;\n\n    const extraClasses = fixDropdownWidth ? \"\" : \"iti--flexible-dropdown-width\";\n    this.#dropdownContent = createEl(\"div\", {\n      id: `iti-${this.#id}__dropdown-content`,\n      class: `iti__dropdown-content ${CLASSES.HIDE} ${extraClasses}`,\n      role: \"dialog\",\n      [ARIA.MODAL]: \"true\",\n    });\n    if (this.#isRTL) {\n      this.#dropdownContent.setAttribute(\"dir\", \"rtl\");\n    }\n\n    if (countrySearch) {\n      this.#buildSearchUI();\n    }\n\n    this.countryList = createEl(\n      \"ul\",\n      {\n        class: \"iti__country-list\",\n        id: `iti-${this.#id}__country-listbox`,\n        role: \"listbox\",\n        [ARIA.LABEL]: i18n.countryListAriaLabel,\n      },\n      this.#dropdownContent,\n    );\n    this.#appendListItems();\n\n    if (countrySearch) {\n      this.#updateSearchResultsA11yText();\n    }\n\n    if (!useFullscreenPopup) {\n      // inline dropdown\n      this.#maybeEnsureDropdownWidthSet();\n      // capture the dropdownHeight before injecting it into the DOM, using a clever invisible technique. This is used later to decide whether to show dropdown above/below input.\n      this.#inlineDropdownHeight = this.#getHiddenInlineDropdownHeight();\n      // fix the dropdown height when using countrySearch so when dropdown is positioned above input, and you type in the search input and the country list changes, the search input doesn't jump up/down.\n      if (countrySearch) {\n        this.#dropdownContent.style.height = `${this.#inlineDropdownHeight}px`;\n      }\n    }\n\n    //* Create dropdownContainer markup.\n    if (dropdownContainer) {\n      const dropdownClasses = buildClassNames({\n        iti: true,\n        \"iti--container\": true,\n        \"iti--fullscreen-popup\": useFullscreenPopup,\n        \"iti--inline-dropdown\": !useFullscreenPopup,\n        [containerClass]: Boolean(containerClass),\n      });\n      this.#dropdownForContainer = createEl(\"div\", { class: dropdownClasses });\n      this.#dropdownForContainer.appendChild(this.#dropdownContent);\n    } else {\n      this.countryContainer.appendChild(this.#dropdownContent);\n    }\n  }\n\n  #buildSearchUI(): void {\n    const { i18n, searchInputClass } = this.#options;\n\n    // Wrapper so we can position the icons (search + clear)\n    const searchWrapper = createEl(\n      \"div\",\n      { class: \"iti__search-input-wrapper\" },\n      this.#dropdownContent,\n    );\n\n    // Search (magnifying glass) icon SVG\n    this.#searchIcon = createEl(\n      \"span\",\n      {\n        class: \"iti__search-icon\",\n        [ARIA.HIDDEN]: \"true\",\n      },\n      searchWrapper,\n    );\n\n    this.#searchIcon.innerHTML = buildSearchIcon();\n\n    this.searchInput = createEl(\n      \"input\",\n      {\n        id: `iti-${this.#id}__search-input`, // Chrome says inputs need either a name or an id\n        type: \"search\",\n        class: `iti__search-input ${searchInputClass}`,\n        placeholder: i18n.searchPlaceholder,\n        // role=combobox + aria-autocomplete=list + aria-activedescendant allows maintaining focus on the search input while allowing users to navigate search results with up/down keyboard keys\n        role: \"combobox\",\n        [ARIA.EXPANDED]: \"true\",\n        [ARIA.LABEL]: i18n.searchPlaceholder,\n        [ARIA.CONTROLS]: `iti-${this.#id}__country-listbox`,\n        [ARIA.AUTOCOMPLETE]: \"list\",\n        autocomplete: \"off\",\n      },\n      searchWrapper,\n    ) as HTMLInputElement;\n\n    this.searchClearButton = createEl(\n      \"button\",\n      {\n        type: \"button\",\n        class: `iti__search-clear ${CLASSES.HIDE}`,\n        [ARIA.LABEL]: i18n.clearSearchAriaLabel,\n        tabindex: \"-1\",\n      },\n      searchWrapper,\n    ) as HTMLButtonElement;\n\n    // Mask creates a transparent cross 'cut' through the filled circle so underlying input bg shows.\n    this.searchClearButton.innerHTML = buildClearIcon(this.#id);\n\n    this.#searchResultsA11yText = createEl(\n      \"span\",\n      { class: \"iti__a11y-text\" },\n      this.#dropdownContent,\n    );\n\n    // Visible no-results message (hidden by default)\n    this.#searchNoResults = createEl(\n      \"div\",\n      {\n        class: `iti__no-results ${CLASSES.HIDE}`,\n        [ARIA.HIDDEN]: \"true\", // all a11y messaging happens in this.#searchResultsA11yText\n      },\n      this.#dropdownContent,\n    );\n    this.#searchNoResults.textContent = i18n.searchEmptyState;\n  }\n\n  #maybeUpdateInputPaddingAndReveal(): void {\n    if (this.countryContainer) {\n      this.#updateInputPadding();\n      this.countryContainer.classList.remove(CLASSES.V_HIDE);\n    }\n  }\n\n  #maybeBuildHiddenInputs(wrapper: HTMLElement): void {\n    const { hiddenInput } = this.#options;\n    if (hiddenInput) {\n      const telInputName = this.telInput.getAttribute(\"name\") || \"\";\n      const names = hiddenInput(telInputName);\n\n      if (names.phone) {\n        const existingInput = this.telInput.form?.querySelector(\n          `input[name=\"${names.phone}\"]`,\n        );\n        if (existingInput) {\n          this.hiddenInput = existingInput as HTMLInputElement;\n        } else {\n          //* Create hidden input for the full international number.\n          this.hiddenInput = createEl(\"input\", {\n            type: \"hidden\",\n            name: names.phone,\n          }) as HTMLInputElement;\n          wrapper.appendChild(this.hiddenInput);\n        }\n      }\n\n      if (names.country) {\n        const existingInput = this.telInput.form?.querySelector(\n          `input[name=\"${names.country}\"]`,\n        );\n        if (existingInput) {\n          this.hiddenInputCountry = existingInput as HTMLInputElement;\n        } else {\n          //* Create hidden input for the selected country iso2 code.\n          this.hiddenInputCountry = createEl(\"input\", {\n            type: \"hidden\",\n            name: names.country,\n          }) as HTMLInputElement;\n          wrapper.appendChild(this.hiddenInputCountry);\n        }\n      }\n    }\n  }\n\n  //* For each country: add a country list item <li> to the countryList <ul> container.\n  #appendListItems(): void {\n    const frag = document.createDocumentFragment();\n    for (let i = 0; i < this.#countries.length; i++) {\n      const c = this.#countries[i];\n      // Compute classes (highlight first item when countrySearch disabled)\n      const liClass = buildClassNames({\n        [CLASSES.COUNTRY_ITEM]: true,\n      });\n\n      const listItem = createEl(\"li\", {\n        id: `iti-${this.#id}__item-${c.iso2}`,\n        class: liClass,\n        tabindex: \"-1\",\n        role: \"option\",\n        [ARIA.SELECTED]: \"false\",\n      });\n      listItem.dataset.dialCode = c.dialCode;\n      listItem.dataset.countryCode = c.iso2;\n\n      // Store this for later use e.g. country search filtering.\n      c.nodeById[this.#id] = listItem;\n\n      // Build contents without innerHTML for safety and clarity\n      if (this.#options.showFlags) {\n        createEl(\"div\", { class: `${CLASSES.FLAG} iti__${c.iso2}` }, listItem);\n      }\n\n      const nameEl = createEl(\"span\", { class: \"iti__country-name\" }, listItem);\n      nameEl.textContent = `${c.name} `;\n\n      // the dial code span sits inside the name span, separated by a space, which works for both LTR and RTL languages\n      // (visually it looks better separated by a standard space character, rather than a fixed margin distance, and is more flexible)\n      const dialEl = createEl(\"span\", { class: \"iti__dial-code\" }, nameEl);\n      if (this.#isRTL) {\n        dialEl.setAttribute(\"dir\", \"ltr\");\n      }\n      dialEl.textContent = `(+${c.dialCode})`;\n\n      frag.appendChild(listItem);\n    }\n    this.countryList.appendChild(frag);\n  }\n\n  //* Update the input padding to make space for the selected country/dial code.\n  #updateInputPadding(): void {\n    if (this.selectedCountry) {\n      // fallback widths differ for separateDialCode mode\n      const fallbackWidth = this.#options.separateDialCode\n        ? LAYOUT.SANE_SELECTED_WITH_DIAL_WIDTH\n        : LAYOUT.SANE_SELECTED_NO_DIAL_WIDTH;\n      //* offsetWidth is zero if input is in a hidden container during initialisation.\n      const selectedCountryWidth =\n        this.selectedCountry.offsetWidth ||\n        this.#getHiddenSelectedCountryWidth() ||\n        fallbackWidth;\n      const inputPadding =\n        selectedCountryWidth + LAYOUT.INPUT_PADDING_EXTRA_LEFT;\n      this.telInput.style.paddingLeft = `${inputPadding}px`;\n    }\n  }\n\n  static #getBody(): HTMLElement {\n    // Use window.top as a fix for same-origin iframes (that are hidden during init) where even appending it to document.body would still be hidden. window.top accesses the top-most document, which will not be hidden.\n    let body;\n    try {\n      body = window.top.document.body;\n      // eslint-disable-next-line @typescript-eslint/no-unused-vars\n    } catch (e) {\n      // fix for cross-origin iframes, where accessing window.top.document throws a security error\n      body = document.body;\n    }\n    return body;\n  }\n\n  //* When input is in a hidden container during init, we cannot calculate the selected country width.\n  //* Fix: clone the markup, make it invisible, add it to the end of the DOM, and then measure it's width.\n  //* To get the right styling to apply, all we need is a shallow clone of the container,\n  //* and then to inject a deep clone of the selectedCountry element.\n  #getHiddenSelectedCountryWidth(): number {\n    if (this.telInput.parentNode) {\n      const body = UI.#getBody();\n      const containerClone = this.telInput.parentNode.cloneNode(\n        false,\n      ) as HTMLElement;\n      containerClone.style.visibility = \"hidden\";\n      body.appendChild(containerClone);\n\n      const countryContainerClone =\n        this.countryContainer.cloneNode() as HTMLElement;\n      containerClone.appendChild(countryContainerClone);\n\n      const selectedCountryClone = this.selectedCountry.cloneNode(\n        true,\n      ) as HTMLElement;\n      countryContainerClone.appendChild(selectedCountryClone);\n\n      const width = selectedCountryClone.offsetWidth;\n      body.removeChild(containerClone);\n      return width;\n    }\n    return 0;\n  }\n\n  // this is run before we add the dropdown to the DOM\n  #getHiddenInlineDropdownHeight(): number {\n    const body = UI.#getBody();\n    this.#dropdownContent.classList.remove(CLASSES.HIDE);\n\n    // it needs these classes on the container to get the correct height\n    const tempContainer = createEl(\"div\", { class: \"iti iti--inline-dropdown\" });\n    tempContainer.appendChild(this.#dropdownContent);\n\n    tempContainer.style.visibility = \"hidden\";\n    body.appendChild(tempContainer);\n    const height = this.#dropdownContent.offsetHeight;\n    body.removeChild(tempContainer);\n    tempContainer.style.visibility = \"\";\n\n    this.#dropdownContent.classList.add(CLASSES.HIDE);\n    return height > 0 ? height : LAYOUT.SANE_DROPDOWN_HEIGHT;\n  }\n\n  //* Update search results text (for a11y).\n  #updateSearchResultsA11yText(): void {\n    const { i18n } = this.#options;\n    const count = this.countryList.childElementCount;\n    this.#searchResultsA11yText.textContent = i18n.searchSummaryAria(count);\n  }\n\n  //* Country search: Filter the countries according to the search query.\n  public filterCountriesByQuery(query: string): void {\n    let matchedCountries: Country[];\n\n    if (query === \"\") {\n      // reset - back to all countries\n      matchedCountries = this.#countries;\n    } else {\n      matchedCountries = getMatchedCountries(this.#countries, query);\n    }\n    this.#filterCountries(matchedCountries);\n  }\n\n  // Search input handlers\n  #doFilter(): void {\n    const inputQuery = this.searchInput.value.trim();\n    this.filterCountriesByQuery(inputQuery);\n    // show/hide clear button\n    if (this.searchInput.value) {\n      this.searchClearButton.classList.remove(CLASSES.HIDE);\n    } else {\n      this.searchClearButton.classList.add(CLASSES.HIDE);\n    }\n  }\n\n  public handleSearchChange(): void {\n    // Filtering country nodes is expensive (lots of DOM manipulation), so rate limit it.\n    if (this.#searchKeyupTimer) {\n      clearTimeout(this.#searchKeyupTimer);\n    }\n    this.#searchKeyupTimer = setTimeout(() => {\n      this.#doFilter();\n      this.#searchKeyupTimer = null;\n    }, TIMINGS.SEARCH_DEBOUNCE_MS);\n  }\n\n  public handleSearchClear(): void {\n    this.searchInput.value = \"\";\n    this.searchInput.focus();\n    this.#doFilter();\n  }\n\n  //* Check if an element is visible within it's container, else scroll until it is.\n  public scrollTo(element: HTMLElement): void {\n    const container = this.countryList;\n    const scrollTop = document.documentElement.scrollTop;\n    const containerHeight = container.offsetHeight;\n    const containerTop = container.getBoundingClientRect().top + scrollTop;\n    const containerBottom = containerTop + containerHeight;\n    const elementHeight = element.offsetHeight;\n    const elementTop = element.getBoundingClientRect().top + scrollTop;\n    const elementBottom = elementTop + elementHeight;\n    const newScrollTop = elementTop - containerTop + container.scrollTop;\n\n    if (elementTop < containerTop) {\n      //* Scroll up.\n      container.scrollTop = newScrollTop;\n    } else if (elementBottom > containerBottom) {\n      //* Scroll down.\n      const heightDifference = containerHeight - elementHeight;\n      container.scrollTop = newScrollTop - heightDifference;\n    }\n  }\n\n  //* Remove highlighting from the previous list item and highlight the new one.\n  public highlightListItem(\n    listItem: HTMLElement | null,\n    shouldFocus: boolean,\n  ): void {\n    const prevItem = this.highlightedItem;\n    if (prevItem) {\n      prevItem.classList.remove(CLASSES.HIGHLIGHT);\n    }\n    //* Set this, even if it's null, as it will clear the highlight.\n    this.highlightedItem = listItem;\n    if (this.highlightedItem) {\n      this.highlightedItem.classList.add(CLASSES.HIGHLIGHT);\n      if (this.#options.countrySearch) {\n        const activeDescendant = this.highlightedItem.getAttribute(\"id\") || \"\";\n        this.searchInput.setAttribute(ARIA.ACTIVE_DESCENDANT, activeDescendant);\n      }\n    }\n\n    if (shouldFocus) {\n      this.highlightedItem.focus();\n    }\n  }\n\n  //* Highlight the next/prev item in the list (and ensure it is visible).\n  public handleUpDownKey(key: string): void {\n    let next =\n      key === KEYS.ARROW_UP\n        ? (this.highlightedItem?.previousElementSibling as HTMLElement)\n        : (this.highlightedItem?.nextElementSibling as HTMLElement);\n    if (!next && this.countryList.childElementCount > 1) {\n      //* Otherwise, we must be at the end, so loop round again.\n      next =\n        key === KEYS.ARROW_UP\n          ? (this.countryList.lastElementChild as HTMLElement)\n          : (this.countryList.firstElementChild as HTMLElement);\n    }\n    if (next) {\n      //* Make sure the next item is visible\n      //* (before calling focus(), which can cause the next item to scroll to the middle of the dropdown, which is jarring).\n      this.scrollTo(next);\n      //* If country search enabled, don't lose focus from the search input on up/down\n      this.highlightListItem(next, false);\n    }\n  }\n\n  // Update the selected list item in the dropdown\n  #updateSelectedItem(iso2: Iso2 | \"\"): void {\n    // if the existing selected item is different to the new country, set aria-selected to false\n    if (this.#selectedItem && this.#selectedItem.dataset.countryCode !== iso2) {\n      this.#selectedItem.setAttribute(ARIA.SELECTED, \"false\");\n      this.#selectedItem.querySelector(\".iti__country-check\")?.remove();\n      this.#selectedItem = null;\n    }\n\n    // if setting to a new country (rather than null/globe icon, or the existing selected item), find the new list item and set aria-selected to true\n    if (iso2 && !this.#selectedItem) {\n      const newListItem = this.countryList.querySelector(\n        `[data-country-code=\"${iso2}\"]`,\n      ) as HTMLElement;\n      if (newListItem) {\n        newListItem.setAttribute(ARIA.SELECTED, \"true\");\n        const checkIcon = createEl(\n          \"span\",\n          { class: \"iti__country-check\", [ARIA.HIDDEN]: \"true\" },\n          newListItem,\n        );\n        checkIcon.innerHTML = buildCheckIcon();\n        this.#selectedItem = newListItem;\n      }\n    }\n  }\n\n  //* Country search: Filter the country list to the given array of countries.\n  #filterCountries(matchedCountries: Country[]): void {\n    this.countryList.innerHTML = \"\";\n\n    let noCountriesAddedYet = true;\n    for (const c of matchedCountries) {\n      const listItem = c.nodeById[this.#id];\n      if (listItem) {\n        this.countryList.appendChild(listItem);\n\n        //* Highlight the first item\n        if (noCountriesAddedYet) {\n          this.highlightListItem(listItem, false);\n          noCountriesAddedYet = false;\n        }\n      }\n    }\n    if (noCountriesAddedYet) {\n      //* If no countries are shown, unhighlight the previously highlighted item.\n      this.highlightListItem(null, false);\n      if (this.#searchNoResults) {\n        this.#searchNoResults.classList.remove(CLASSES.HIDE);\n      }\n    } else if (this.#searchNoResults) {\n      this.#searchNoResults.classList.add(CLASSES.HIDE);\n    }\n    //* Scroll to top (useful if user had previously scrolled down).\n    this.countryList.scrollTop = 0;\n    this.#updateSearchResultsA11yText();\n  }\n\n  public destroy(): void {\n    // restore all instance refs to enable garbage collection\n    this.telInput.iti = undefined;\n    //* Remove attribute of id instance: data-intl-tel-input-id.\n    delete this.telInput.dataset.intlTelInputId;\n\n    //* Restore original styling\n    if (this.#options.separateDialCode) {\n      this.telInput.style.paddingLeft = this.#originalPaddingLeft;\n    }\n\n    //* Remove markup (but leave the original input).\n    const wrapper = this.telInput.parentNode as HTMLElement;\n    wrapper.before(this.telInput);\n    wrapper.remove();\n\n    // public\n    this.telInput = null;\n    this.countryContainer = null;\n    this.selectedCountry = null;\n    this.selectedCountryInner = null;\n    this.searchInput = null;\n    this.searchClearButton = null;\n    this.countryList = null;\n    this.hiddenInput = null;\n    this.hiddenInputCountry = null;\n    this.highlightedItem = null;\n\n    // private\n    this.#selectedDialCode = null;\n    this.#dropdownArrow = null;\n    this.#dropdownContent = null;\n    this.#searchIcon = null;\n    this.#searchNoResults = null;\n    this.#searchResultsA11yText = null;\n    this.#dropdownForContainer = null;\n    this.#selectedItem = null;\n\n    // also clear all references to the list items in the countries data (for garbage collection)\n    for (const c of this.#countries) {\n      delete c.nodeById[this.#id];\n    }\n    this.#countries = null;\n  }\n\n  // UI: Open the dropdown (DOM only).\n  public openDropdown(): void {\n    const {\n      countrySearch,\n      dropdownAlwaysOpen,\n      dropdownContainer,\n    } = this.#options;\n\n    // if fixDropdownWidth enabled, and the width was not set during init (e.g. because input was hidden), then set it now as the input must be visible now.\n    this.#maybeEnsureDropdownWidthSet();\n\n    // dropdownContainer is used (1) to show the inline dropdown when dropdownContainer option is set, and (2) to show the fullscreen popup on mobile\n    if (dropdownContainer) {\n      this.#handleDropdownContainer();\n    } else {\n      // inline dropdown\n      const positionBelow = this.#shouldPositionInlineDropdownBelowInput();\n      const distance = this.telInput.offsetHeight + LAYOUT.DROPDOWN_MARGIN;\n      if (positionBelow) {\n        this.#dropdownContent.style.top = `${distance}px`;\n      } else {\n        this.#dropdownContent.style.bottom = `${distance}px`;\n      }\n    }\n\n    this.#dropdownContent.classList.remove(CLASSES.HIDE);\n    this.selectedCountry.setAttribute(ARIA.EXPANDED, \"true\");\n\n    //* When countrySearch enabled, every time the dropdown is opened we reset by highlighting the first item and scrolling to top.\n    if (countrySearch) {\n      const firstCountryItem = this.countryList.firstElementChild as HTMLElement;\n      if (firstCountryItem) {\n        this.highlightListItem(firstCountryItem, false);\n        this.countryList.scrollTop = 0;\n      }\n      if (!dropdownAlwaysOpen) {\n        this.searchInput.focus();\n      }\n    }\n\n    // Update the arrow.\n    this.#dropdownArrow.classList.add(CLASSES.ARROW_UP);\n  }\n\n  // UI: Close the dropdown (DOM only).\n  public closeDropdown(): void {\n    const { countrySearch, dropdownContainer } = this.#options;\n\n    this.#dropdownContent.classList.add(CLASSES.HIDE);\n    this.selectedCountry.setAttribute(ARIA.EXPANDED, \"false\");\n\n    if (countrySearch) {\n      this.searchInput.removeAttribute(ARIA.ACTIVE_DESCENDANT);\n      // only clear the highlighted item if countrySearch is enabled as this gets reset each time the dropdown is opened\n      if (this.highlightedItem) {\n        this.highlightedItem.classList.remove(CLASSES.HIGHLIGHT);\n        this.highlightedItem = null;\n      }\n    }\n\n    // Update the arrow.\n    this.#dropdownArrow.classList.remove(CLASSES.ARROW_UP);\n\n    // Remove dropdown from container if using external container\n    if (dropdownContainer) {\n      this.#dropdownForContainer.remove();\n      this.#dropdownForContainer.style.top = \"\";\n      this.#dropdownForContainer.style.bottom = \"\";\n      this.#dropdownForContainer.style.paddingLeft = \"\";\n      this.#dropdownForContainer.style.paddingRight = \"\";\n    } else {\n      this.#dropdownContent.style.top = \"\";\n      this.#dropdownContent.style.bottom = \"\";\n    }\n  }\n\n  #shouldPositionInlineDropdownBelowInput(): boolean {\n    // for testing, it's helpful for it to always be shown below.\n    if (this.#options.dropdownAlwaysOpen) {\n      return true;\n    }\n    const inputPos = this.telInput.getBoundingClientRect();\n    const spaceAbove = inputPos.top;\n    const spaceBelow = window.innerHeight - inputPos.bottom;\n    return spaceBelow >= this.#inlineDropdownHeight || spaceBelow >= spaceAbove;\n  }\n\n  // inject dropdown into container and apply positioning styles\n  #handleDropdownContainer(): void {\n    const { dropdownContainer, useFullscreenPopup } = this.#options;\n\n    if (useFullscreenPopup) {\n      // on wider screens, constrain the popup to the input width instead of full width\n      if (window.innerWidth >= 500) {\n        const inputPos = this.telInput.getBoundingClientRect();\n        this.#dropdownForContainer.style.paddingLeft = `${inputPos.left}px`;\n        this.#dropdownForContainer.style.paddingRight = `${window.innerWidth - inputPos.right}px`;\n      }\n    } else {\n      // inline dropdown\n      // remember this inputPos is relative to the viewport, not the page\n      const inputPos = this.telInput.getBoundingClientRect();\n      this.#dropdownForContainer.style.left = `${inputPos.left}px`;\n      const positionBelow = this.#shouldPositionInlineDropdownBelowInput();\n      if (positionBelow) {\n        this.#dropdownForContainer.style.top = `${inputPos.bottom + LAYOUT.DROPDOWN_MARGIN}px`;\n      } else {\n        // unset the default top:-1000px in the CSS\n        this.#dropdownForContainer.style.top = \"unset\";\n        this.#dropdownForContainer.style.bottom = `${window.innerHeight - inputPos.top + LAYOUT.DROPDOWN_MARGIN}px`;\n      }\n    }\n\n    dropdownContainer.appendChild(this.#dropdownForContainer);\n  }\n\n  // UI: Whether the dropdown is currently closed (hidden).\n  public isDropdownClosed(): boolean {\n    return this.#dropdownContent.classList.contains(CLASSES.HIDE);\n  }\n\n  public setCountry(selectedCountryData: SelectedCountryData): void {\n    const { allowDropdown, showFlags, separateDialCode, i18n } = this.#options;\n    const { name, dialCode, iso2 = \"\" } = selectedCountryData;\n\n    if (allowDropdown) {\n      // Update the selected list item in the dropdown\n      this.#updateSelectedItem(iso2);\n    }\n\n    //* Update the selected flag class and the a11y text.\n    if (this.selectedCountry) {\n      const flagClass =\n        iso2 && showFlags\n          ? `${CLASSES.FLAG} iti__${iso2}`\n          : `${CLASSES.FLAG} ${CLASSES.GLOBE}`;\n      let ariaLabel, title, selectedCountryInner;\n      if (iso2) {\n        title = name;\n        ariaLabel = i18n.selectedCountryAriaLabel\n          .replace(\"${countryName}\", name)\n          .replace(\"${dialCode}\", `+${dialCode}`);\n        selectedCountryInner = showFlags ? \"\" : buildGlobeIcon();\n      } else {\n        title = i18n.noCountrySelected;\n        ariaLabel = i18n.noCountrySelected;\n        selectedCountryInner = buildGlobeIcon();\n      }\n      this.selectedCountryInner.className = flagClass;\n      this.selectedCountry.setAttribute(\"title\", title);\n      this.selectedCountry.setAttribute(ARIA.LABEL, ariaLabel);\n      this.selectedCountryInner.innerHTML = selectedCountryInner;\n    }\n\n    //* Update the selected dial code.\n    if (separateDialCode) {\n      const fullDialCode = dialCode ? `+${dialCode}` : \"\";\n      this.#selectedDialCode.textContent = fullDialCode;\n      this.#updateInputPadding();\n    }\n  }\n}\n"
  },
  {
    "path": "src/js/modules/data/country-data.ts",
    "content": "import allCountries, { type Country, type Iso2 } from \"../../intl-tel-input/data\";\nimport { normaliseString } from \"../utils/string\";\nimport type { AllOptions } from \"../types/public-api\";\n\nexport interface DialCodeProcessingResult {\n  dialCodes: Set<string>;\n  dialCodeMaxLen: number;\n  dialCodeToIso2Map: Record<string, Iso2[]>;\n}\n\n//* Process onlyCountries or excludeCountries array if present.\nexport const processAllCountries = (options: AllOptions): Country[] => {\n  const { onlyCountries, excludeCountries } = options;\n  if (onlyCountries?.length) {\n    const lowerCaseOnlyCountries = onlyCountries.map((country) =>\n      country.toLowerCase(),\n    );\n    return allCountries.filter((country) =>\n      lowerCaseOnlyCountries.includes(country.iso2),\n    );\n  } else if (excludeCountries?.length) {\n    const lowerCaseExcludeCountries = excludeCountries.map((country) =>\n      country.toLowerCase(),\n    );\n    return allCountries.filter(\n      (country) => !lowerCaseExcludeCountries.includes(country.iso2),\n    );\n  }\n  return allCountries;\n};\n\n//* Generate country names using Intl.DisplayNames\nexport const generateCountryNames = (\n  countries: Country[],\n  options: AllOptions,\n): void => {\n  const { countryNameLocale, i18n } = options;\n\n  //* Populate country names using Intl.DisplayNames (per instance) with countryNameLocale.\n  let displayNames;\n  try {\n    const hasDisplayNames =\n      typeof Intl !== \"undefined\" &&\n      typeof (Intl as any).DisplayNames === \"function\";\n    if (hasDisplayNames) {\n      displayNames = new (Intl as any).DisplayNames(countryNameLocale, {\n        type: \"region\",\n      });\n    } else {\n      displayNames = null;\n    }\n  } catch (e) {\n    console.error(e);\n    displayNames = null;\n  }\n  for (const c of countries) {\n    c.name = i18n[c.iso2] || displayNames?.of(c.iso2.toUpperCase()) || \"\";\n  }\n};\n\n//* Generate dialCodes and dialCodeToIso2Map.\nexport const processDialCodes = (countries: Country[]): DialCodeProcessingResult => {\n  //* Here we store just dial codes, where the key is the dial code, and the value is true\n  //* e.g. { 1: true, 7: true, 20: true, ... }.\n  const dialCodes = new Set<string>();\n  let dialCodeMaxLen = 0;\n\n  //* Here we map dialCodes (inc both dialCode and dialCode+areaCode) to iso2 codes e.g.\n  /*\n   * {\n   *   1: [ 'us', 'ca', ... ],    # all NANP countries (with dial code \"1\")\n   *   12: [ 'us', 'ca', ... ],   # subset of NANP countries (that have area codes starting with \"2\")\n   *   120: [ 'us', 'ca' ],       # just US and Canada (that have area codes starting \"20\")\n   *   1204: [ 'ca' ],            # only Canada (that has a \"204\" area code)\n   *   ...\n   *  }\n   */\n  const dialCodeToIso2Map: Record<string, Iso2[]> = {};\n\n  //* Add a dial code to this.dialCodeToIso2Map.\n  const addToDialCodeMap = (iso2: Iso2, dialCode: string) => {\n    // Bail if no iso2 or dialCode (this can happen with onlyCountries or excludeCountries options).\n    if (!iso2 || !dialCode) {\n      return;\n    }\n    //* Update dialCodeMaxLen.\n    if (dialCode.length > dialCodeMaxLen) {\n      dialCodeMaxLen = dialCode.length;\n    }\n    //* If this entry doesn't already exist, then create it.\n    if (!dialCodeToIso2Map.hasOwnProperty(dialCode)) {\n      dialCodeToIso2Map[dialCode] = [];\n    }\n    const iso2List = dialCodeToIso2Map[dialCode];\n    //* Bail if we already have this country for this dialCode.\n    if (iso2List.includes(iso2)) {\n      return;\n    }\n    iso2List.push(iso2);\n  };\n\n  // Sort countries by priority so that when we add to the dialCodeToIso2Map, higher priority countries come first\n  const countriesSortedByPriority = [...countries].sort((a, b) => a.priority - b.priority);\n  for (const c of countriesSortedByPriority) {\n    if (!dialCodes.has(c.dialCode)) {\n      dialCodes.add(c.dialCode);\n    }\n    // add the dial code partial matches to the map\n    for (let k = 1; k < c.dialCode.length; k++) {\n      const partialDialCode = c.dialCode.substring(0, k);\n      addToDialCodeMap(c.iso2, partialDialCode);\n    }\n    // add the full dial code to the map\n    addToDialCodeMap(c.iso2, c.dialCode);\n\n    if (c.areaCodes) {\n      const rootIso2Code = dialCodeToIso2Map[c.dialCode][0];\n      //* For each area code.\n      for (const areaCode of c.areaCodes) {\n        //* Add partial matches: For each digit in the area code\n        for (let k = 1; k < areaCode.length; k++) {\n          const partialAreaCode = areaCode.substring(0, k);\n          const partialDialCode = c.dialCode + partialAreaCode;\n          //* Start with the root country, as that also matches this partial dial code.\n          addToDialCodeMap(rootIso2Code, partialDialCode);\n          addToDialCodeMap(c.iso2, partialDialCode);\n        }\n        //* Add the full area code.\n        addToDialCodeMap(c.iso2, c.dialCode + areaCode);\n      }\n    }\n  }\n\n  return { dialCodes, dialCodeMaxLen, dialCodeToIso2Map };\n};\n\n//* Sort countries by countryOrder option (if present), then name.\nexport const sortCountries = (\n  countries: Country[],\n  options: AllOptions,\n): void => {\n  if (options.countryOrder) {\n    options.countryOrder = options.countryOrder.map(\n      (iso2) => iso2.toLowerCase() as Iso2,\n    );\n  }\n  countries.sort((a: Country, b: Country): number => {\n    //* Primary sort: countryOrder option\n    const { countryOrder } = options;\n    if (countryOrder) {\n      const aIndex = countryOrder.indexOf(a.iso2);\n      const bIndex = countryOrder.indexOf(b.iso2);\n      const aIndexExists = aIndex > -1;\n      const bIndexExists = bIndex > -1;\n      if (aIndexExists || bIndexExists) {\n        if (aIndexExists && bIndexExists) {\n          return aIndex - bIndex;\n        }\n        return aIndexExists ? -1 : 1;\n      }\n    }\n\n    //* Secondary sort: country name\n    return a.name.localeCompare(b.name);\n  });\n};\n\n//* Precompute and cache country search tokens to speed up filtering\nexport const cacheSearchTokens = (countries: Country[]): void => {\n  for (const c of countries) {\n    // Normalised name (lowercase, accents removed etc)\n    c.normalisedName = normaliseString(c.name);\n    // Name initials (first letter of each alpha sequence)\n    c.initials = c.normalisedName\n      .split(/[^a-z]/)\n      .map((word) => word[0])\n      .join(\"\");\n    // Cached +dialCode variant\n    c.dialCodePlus = `+${c.dialCode}`;\n  }\n};\n"
  },
  {
    "path": "src/js/modules/data/intl-regionless.ts",
    "content": "import { getNumeric } from \"../utils/string\";\n\n// Non-geographic / regionless international dial codes that should always be\n// treated as international numbers (no associated ISO2 country).\nexport const REGIONLESS_DIAL_CODES: Set<string> = new Set([\n  \"800\",\n  \"808\",\n  \"870\",\n  \"881\",\n  \"882\",\n  \"883\",\n  \"888\",\n  \"979\",\n]);\n\nexport const hasRegionlessDialCode = (number: string): boolean => {\n  const dialCode = getNumeric(number).slice(0, 3);\n  return number.startsWith(\"+\") && REGIONLESS_DIAL_CODES.has(dialCode);\n};\n"
  },
  {
    "path": "src/js/modules/data/nanp-regionless.ts",
    "content": "import { DIAL } from \"../constants\";\nimport { getNumeric } from \"../utils/string\";\n\n//* https://en.wikipedia.org/wiki/List_of_North_American_Numbering_Plan_area_codes#Non-geographic_area_codes\nexport const regionlessNanpNumbers = [\n  \"800\",\n  \"822\",\n  \"833\",\n  \"844\",\n  \"855\",\n  \"866\",\n  \"877\",\n  \"880\",\n  \"881\",\n  \"882\",\n  \"883\",\n  \"884\",\n  \"885\",\n  \"886\",\n  \"887\",\n  \"888\",\n  \"889\",\n];\n\n//* Check if the given number is a regionless NANP number (expects the number to contain an international dial code)\nexport const isRegionlessNanp = (number: string): boolean => {\n  const numeric = getNumeric(number);\n  if (numeric.startsWith(DIAL.NANP) && numeric.length >= 4) {\n    const areaCode = numeric.substring(1, 4);\n    return regionlessNanpNumbers.includes(areaCode);\n  }\n  return false;\n};\n"
  },
  {
    "path": "src/js/modules/format/caret.ts",
    "content": "//* Iterate through the formattedValue until hit the right number of relevant chars.\nexport const translateCursorPosition = (\n  relevantChars: number,\n  formattedValue: string,\n  prevCaretPos: number,\n  isDeleteForwards: boolean,\n): number => {\n  //* If the first char is a formatting char, and they backspace delete it:\n  //* Cursor should stay at the start (pos 0), rather than stick to the first digit (pos 1).\n  if (prevCaretPos === 0 && !isDeleteForwards) {\n    return 0;\n  }\n  let relevantCharCount = 0;\n  for (let i = 0; i < formattedValue.length; i++) {\n    //* Count this as a relevant char if it's a + or a digit.\n    if (/[+0-9]/.test(formattedValue[i])) {\n      relevantCharCount++;\n    }\n\n    //* Normal case: stop when you hit the right number of relevant chars\n    //* (cursor will be just after the final relevant char).\n    if (relevantCharCount === relevantChars && !isDeleteForwards) {\n      return i + 1;\n    }\n    //* Spacial case: delete forwards (fn + delete on a mac):\n    //* Wait until hit one extra relevant char, and put the cursor just before it (after any formatting chars).\n    if (isDeleteForwards && relevantCharCount === relevantChars + 1) {\n      return i;\n    }\n  }\n  return formattedValue.length;\n};"
  },
  {
    "path": "src/js/modules/format/formatting.ts",
    "content": "import type { SelectedCountryData } from \"../types/public-api\";\n\n//* Remove the dial code if separateDialCode is enabled\nexport const beforeSetNumber = (\n  fullNumber: string,\n  hasValidDialCode: boolean,\n  separateDialCode: boolean,\n  selectedCountryData: SelectedCountryData,\n): string => {\n  let number = fullNumber;\n  if (separateDialCode) {\n    //* If there is a valid dial code.\n    if (hasValidDialCode) {\n      //* In case _getDialCode returned an area code as well.\n      const dialCode = `+${selectedCountryData.dialCode}`;\n      //* a lot of numbers will have a space separating the dial code and the main number, and\n      //* some NANP numbers will have a hyphen e.g. +1 684-733-1234 - in both cases we want to get rid of it.\n      //* NOTE: Don't just trim all non-numerics as may want to preserve an open parenthesis etc.\n      const start =\n        number[dialCode.length] === \" \" || number[dialCode.length] === \"-\"\n          ? dialCode.length + 1\n          : dialCode.length;\n      number = number.substring(start);\n    }\n  }\n  return number;\n};\n\n//* Format the number as the user types.\nexport const formatNumberAsYouType = (\n  fullNumber: string,\n  telInputValue: string,\n  utils: any,\n  selectedCountryData: SelectedCountryData,\n  separateDialCode: boolean,\n): string => {\n  const result = utils\n    ? utils.formatNumberAsYouType(fullNumber, selectedCountryData.iso2)\n    : fullNumber;\n  //* If separateDialCode and they haven't (re)typed the dial code in the input as well, then remove the dial code.\n  const { dialCode } = selectedCountryData;\n  if (\n    separateDialCode &&\n    telInputValue.charAt(0) !== \"+\" &&\n    result.includes(`+${dialCode}`)\n  ) {\n    const afterDialCode = result.split(`+${dialCode}`)[1] || \"\";\n    return afterDialCode.trim();\n  }\n  return result;\n};\n"
  },
  {
    "path": "src/js/modules/types/events.ts",
    "content": "// Shared event type map for IntlTelInput custom events\n// Exported as a type-only module to be reused across implementations (vanilla, react, angular, vue)\n// Uses centralised string constants to avoid magic strings & ensure consistency.\nimport { EVENTS } from \"../constants\";\n\nexport type ItiEventMap = {\n  [EVENTS.COUNTRY_CHANGE]: Record<string, never>;\n  [EVENTS.OPEN_COUNTRY_DROPDOWN]: Record<string, never>;\n  [EVENTS.CLOSE_COUNTRY_DROPDOWN]: Record<string, never>;\n  [EVENTS.INPUT]: { isSetNumber?: boolean };\n};\n"
  },
  {
    "path": "src/js/modules/types/forEachInstanceArgsMap.ts",
    "content": "export type ForEachInstanceArgsMap = {\n  handleUtils: [];\n  handleUtilsFailure: [error?: unknown];\n  handleAutoCountry: [];\n  handleAutoCountryFailure: [];\n};\n"
  },
  {
    "path": "src/js/modules/types/public-api.ts",
    "content": "import type { Country, Iso2 } from \"../../intl-tel-input/data\";\nimport type { I18n } from \"../../intl-tel-input/i18n/types\";\n// Type-only import to avoid runtime circular dependency. This is erased after compilation.\nimport type { Iti } from \"../../intl-tel-input\";\nimport type { NUMBER_TYPE_SET, PLACEHOLDER_MODES } from \"../constants\";\n\n// Loader for the utils module\nexport type UtilsLoader = () => Promise<{ default: ItiUtils }>;\n\n// Library utilities (loaded lazily)\nexport type ItiUtils = {\n  formatNumber(\n    number: string,\n    iso2: string | undefined,\n    format?: number\n  ): string;\n  formatNumberAsYouType(number: string, iso2: string | undefined): string;\n  getCoreNumber(number: string, iso2: string | undefined): string;\n  getExampleNumber(\n    iso2: string | undefined,\n    nationalMode: boolean,\n    numberType: number,\n    useE164?: boolean\n  ): string;\n  getExtension(number: string, iso2: string | undefined): string;\n  getNumberType(number: string, iso2: string | undefined): number;\n  getValidationError(number: string, iso2: string | undefined): number;\n  isPossibleNumber(\n    number: string,\n    iso2: string | undefined,\n    numberType?: NumberType[] | null\n  ): boolean;\n  isValidNumber(\n    number: string,\n    iso2: string | undefined,\n    numberType?: NumberType[] | null\n  ): boolean;\n  numberFormat: {\n    NATIONAL: number;\n    INTERNATIONAL: number;\n    E164: number;\n    RFC3966: number;\n  };\n  numberType: Record<string, number>;\n};\n\n// Number types exposed publicly\ntype SetValues<T> = T extends ReadonlySet<infer U> ? U : never;\n\nexport type NumberType = SetValues<typeof NUMBER_TYPE_SET>;\n\ntype ValueOf<T> = T[keyof T];\n\n// All configurable options\nexport interface AllOptions {\n  allowDropdown: boolean;\n  allowedNumberTypes: NumberType[] | null;\n  allowNumberExtensions: boolean;\n  allowPhonewords: boolean;\n  autoPlaceholder: ValueOf<typeof PLACEHOLDER_MODES>;\n  containerClass: string;\n  countryNameLocale: string;\n  countryOrder: Iso2[] | null;\n  countrySearch: boolean;\n  customPlaceholder:\n    | ((\n        selectedCountryPlaceholder: string,\n        selectedCountryData: SelectedCountryData\n      ) => string)\n    | null;\n  dropdownAlwaysOpen: boolean;\n  dropdownContainer: HTMLElement | null;\n  excludeCountries: Iso2[];\n  fixDropdownWidth: boolean;\n  formatAsYouType: boolean;\n  formatOnDisplay: boolean;\n  geoIpLookup:\n    | ((success: (iso2: Iso2) => void, failure: () => void) => void)\n    | null;\n  hiddenInput:\n    | ((telInputName: string) => { phone: string; country?: string })\n    | null;\n  i18n: I18n;\n  initialCountry: Iso2 | \"auto\" | \"\";\n  loadUtils: UtilsLoader | null;\n  nationalMode: boolean;\n  onlyCountries: Iso2[];\n  placeholderNumberType: NumberType;\n  searchInputClass: string;\n  separateDialCode: boolean;\n  showFlags: boolean;\n  strictMode: boolean;\n  useFullscreenPopup: boolean;\n}\n\n// Partial options accepted by the factory\nexport type SomeOptions = Partial<AllOptions>;\n\n// Public interface for the factory function (kept here for consumers)\nexport interface IntlTelInputInterface {\n  (input: HTMLInputElement, options?: SomeOptions): Iti;\n  autoCountry?: Iso2;\n  defaults: AllOptions;\n  documentReady: () => boolean;\n  getCountryData: () => Country[];\n  getInstance: (input: HTMLInputElement) => Iti | null;\n  instances: { [key: string]: Iti };\n  attachUtils: (source: UtilsLoader) => Promise<unknown> | null;\n  startedLoadingAutoCountry: boolean;\n  startedLoadingUtilsScript: boolean;\n  version: string | undefined;\n  utils?: ItiUtils;\n}\n\n// A discriminated union for when there may be no selected country yet.\n// - When selected: it's a full Country (iso2 is present)\n// - When none selected: it's an empty object literal (iso2 is absent)\ntype EmptyObject = Record<string, never>;\nexport type SelectedCountryData = Country | EmptyObject;\n"
  },
  {
    "path": "src/js/modules/utils/dom.ts",
    "content": "/**\n * Build a space-delimited class string from an object map of className -> truthy/falsey.\n * Only keys with truthy values are included.\n */\nexport const buildClassNames = (flags: Record<string, unknown>): string =>\n  Object.keys(flags)\n    .filter((k) => Boolean(flags[k]))\n    .join(\" \");\n\n//* Create a DOM element.\nexport const createEl = (\n  tagName: string,\n  attrs?: object | null,\n  container?: HTMLElement,\n): HTMLElement => {\n  const el = document.createElement(tagName);\n  if (attrs) {\n    Object.entries(attrs).forEach(([key, value]) =>\n      el.setAttribute(key, value),\n    );\n  }\n  if (container) {\n    container.appendChild(el);\n  }\n  return el;\n};\n"
  },
  {
    "path": "src/js/modules/utils/isAndroid.ts",
    "content": "export const getIsAndroid = (): boolean =>\n  typeof navigator !== \"undefined\" ? /Android/i.test(navigator.userAgent) : false;\n"
  },
  {
    "path": "src/js/modules/utils/string.ts",
    "content": "//* Extract the numeric digits from the given string (\\D matches any non-digit).\nexport const getNumeric = (s: string): string => s.replace(/\\D/g, \"\");\n\n//* Normalise string: turns \"Réunion\" into \"Reunion\".\n//* from https://stackoverflow.com/a/37511463\nexport const normaliseString = (s: string = \"\"): string =>\n  s.normalize(\"NFD\").replace(/[\\u0300-\\u036f]/g, \"\").toLowerCase();"
  },
  {
    "path": "src/js/utils.js",
    "content": "goog.provide(\"i18n.phonenumbers.demo\");\n// includes\ngoog.require(\"i18n.phonenumbers.PhoneNumberFormat\");\ngoog.require(\"i18n.phonenumbers.PhoneNumberUtil\");\ngoog.require(\"i18n.phonenumbers.Error\");\ngoog.require(\"i18n.phonenumbers.AsYouTypeFormatter\");\n\n//* Format the number as the user types.\nconst formatNumberAsYouType = (number, countryCode) => {\n  try {\n    //* Have to clean it first, as AYTF stops formatting as soon as it hits any formatting char (even it's own)\n    //* (it's designed to be fed one char at a time, as opposed to every char every time).\n    const clean = number.replace(/[^+0-9]/g, \"\");\n    const formatter = new i18n.phonenumbers.AsYouTypeFormatter(countryCode);\n    let result = \"\";\n    for (let i = 0; i < clean.length; i++) {\n      result = formatter.inputDigit(clean.charAt(i));\n    }\n    return result;\n  } catch {\n    return number;\n  }\n};\n\n//* Format the given number to the given format.\nconst formatNumber = (number, countryCode, formatArg) => {\n  try {\n    const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();\n    const numberObj = phoneUtil.parseAndKeepRawInput(number, countryCode);\n    if (phoneUtil.isPossibleNumber(numberObj)) {\n      const format =\n        typeof formatArg === \"undefined\"\n          ? i18n.phonenumbers.PhoneNumberFormat.E164\n          : formatArg;\n      return phoneUtil.format(numberObj, format);\n    }\n    return number;\n  } catch {\n    return number;\n  }\n};\n\n//* Get an example number for the given country code.\nconst getExampleNumber = (countryCode, national, numberType, useE164) => {\n  try {\n    const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();\n    const numberObj = phoneUtil.getExampleNumberForType(\n      countryCode,\n      numberType,\n    );\n    let format;\n    if (useE164) {\n      format = i18n.phonenumbers.PhoneNumberFormat.E164;\n    } else {\n      format = national\n        ? i18n.phonenumbers.PhoneNumberFormat.NATIONAL\n        : i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL;\n    }\n    return phoneUtil.format(numberObj, format);\n  } catch {\n    return \"\";\n  }\n};\n\n//* Get the core number, without any international dial code, or national prefix.\nconst getCoreNumber = (number, countryCode) => {\n  try {\n    const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();\n    const numberObj = phoneUtil.parseAndKeepRawInput(number, countryCode);\n    return numberObj.getNationalNumber().toString();\n  } catch {\n    return \"\";\n  }\n};\n\n//* Get the extension from the given number\nconst getExtension = (number, countryCode) => {\n  try {\n    const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();\n    const numberObj = phoneUtil.parseAndKeepRawInput(number, countryCode);\n    return numberObj.getExtension();\n  } catch {\n    return \"\";\n  }\n};\n\n//* Get the type of the given number e.g. fixed-line/mobile.\nconst getNumberType = (number, countryCode) => {\n  try {\n    const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();\n    const numberObj = phoneUtil.parseAndKeepRawInput(number, countryCode);\n    return phoneUtil.getNumberType(numberObj);\n  } catch {\n    //* Broken\n    return -99;\n  }\n};\n\n//* Get more info if the validation has failed e.g. too long/too short.\n//* NOTE that isPossibleNumberWithReason returns a i18n.phonenumbers.PhoneNumberUtil.ValidationResult.\nconst getValidationError = (number, countryCode) => {\n  if (!countryCode) {\n    return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.INVALID_COUNTRY_CODE;\n  }\n  try {\n    const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();\n    const numberObj = phoneUtil.parseAndKeepRawInput(number, countryCode);\n    return phoneUtil.isPossibleNumberWithReason(numberObj);\n  } catch (e) {\n    //* Here I convert thrown errors into ValidationResult enums (if possible).\n    //* errors are from i18n.phonenumbers.Error in the file https://github.com/googlei18n/libphonenumber/blob/master/javascript/i18n/phonenumbers/phonenumberutil.js.\n    if (e.message === i18n.phonenumbers.Error.INVALID_COUNTRY_CODE) {\n      return i18n.phonenumbers.PhoneNumberUtil.ValidationResult\n        .INVALID_COUNTRY_CODE;\n    }\n    if (\n      //* Hack to solve issue where parseAndKeepRawInput throws weird error for zero or 1-digit (national) numbers e.g. \"3\" or \"+13\" s\n      number.length <= 3 ||\n      e.message === i18n.phonenumbers.Error.TOO_SHORT_AFTER_IDD ||\n      e.message === i18n.phonenumbers.Error.TOO_SHORT_NSN\n    ) {\n      return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.TOO_SHORT;\n    }\n    if (e.message === i18n.phonenumbers.Error.TOO_LONG) {\n      return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.TOO_LONG;\n    }\n\n    //* Broken\n    return -99;\n  }\n};\n\n//* FIXED_LINE_OR_MOBILE is its own category that is different to either MOBILE or FIXED_LINE (e.g. it is used for US numbers which could be used for either purpose - it should really be called something like FIXED_LINE_SLASH_MOBILE). So here we make it more user friendly by allowing it to be in any of those three categories. NOTE: this is actually in-line with how it behaves in other situations e.g. if you call isPossibleNumberForType with type=\"MOBILE\" and the number set to a US number, it returns VALID even though it's type is technically FIXED_LINE_OR_MOBILE.\nconst getPatchedNumberTypeNames = (numberTypeNames) => {\n  const additions = [];\n  if (numberTypeNames.includes(\"FIXED_LINE_OR_MOBILE\")) {\n    if (!numberTypeNames.includes(\"MOBILE\")) {\n      additions.push(\"MOBILE\");\n    }\n    if (!numberTypeNames.includes(\"FIXED_LINE\")) {\n      additions.push(\"FIXED_LINE\");\n    }\n  } else if (numberTypeNames.includes(\"MOBILE\") || numberTypeNames.includes(\"FIXED_LINE\")) {\n    additions.push(\"FIXED_LINE_OR_MOBILE\");\n  }\n  return numberTypeNames.concat(additions);\n};\n\n//* Check if given number is valid.\nconst isValidNumber = (number, countryCode, numberTypeNames) => {\n  try {\n    const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();\n    const numberObj = phoneUtil.parseAndKeepRawInput(number, countryCode);\n    const isValidNumber = phoneUtil.isValidNumber(numberObj);\n    if (numberTypeNames) {\n      const patchedTypeNames = getPatchedNumberTypeNames(numberTypeNames);\n      const numberTypes = patchedTypeNames.map((typeName) => numberType[typeName]);\n      return isValidNumber && numberTypes.includes(phoneUtil.getNumberType(numberObj));\n    }\n    return isValidNumber;\n  } catch {\n    return false;\n  }\n};\n\n//* Check if given number is possible.\nconst isPossibleNumber = (number, countryCode, numberTypeNames) => {\n  try {\n    const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();\n    const numberObj = phoneUtil.parseAndKeepRawInput(number, countryCode);\n\n    if (numberTypeNames) {\n      const patchedTypeNames = getPatchedNumberTypeNames(numberTypeNames);\n      for (let typeName of patchedTypeNames) {\n        //* Can't use phoneUtil.isPossibleNumberForType directly as it accepts IS_POSSIBLE_LOCAL_ONLY numbers e.g. local numbers that are much shorter.\n        const resultForType = phoneUtil.isPossibleNumberForTypeWithReason(numberObj, numberType[typeName]);\n        if (resultForType === i18n.phonenumbers.PhoneNumberUtil.ValidationResult.IS_POSSIBLE) {\n          return true;\n        }\n      }\n      return false;\n    }\n\n    //* Can't use phoneUtil.isPossibleNumber directly as it accepts IS_POSSIBLE_LOCAL_ONLY numbers e.g. local numbers that are much shorter.\n    const result = phoneUtil.isPossibleNumberWithReason(numberObj);\n    const isPossible = result === i18n.phonenumbers.PhoneNumberUtil.ValidationResult.IS_POSSIBLE;\n    return isPossible;\n  } catch {\n    return false;\n  }\n};\n\n/********************\n * NOTE: for following sections, keys must be in quotes to force closure compiler to preserve them\n ********************/\n\n//* copied this from i18n.phonenumbers.PhoneNumberFormat in the file https://github.com/googlei18n/libphonenumber/blob/master/javascript/i18n/phonenumbers/phonenumberutil.js.\nconst numberFormat = {\n  \"E164\": 0,\n  \"INTERNATIONAL\": 1,\n  \"NATIONAL\": 2,\n  \"RFC3966\": 3,\n};\n\n//* copied this from i18n.phonenumbers.PhoneNumberType in https://github.com/googlei18n/libphonenumber/blob/master/javascript/i18n/phonenumbers/phonenumberutil.js and put the keys in quotes to force closure compiler to preserve the keys\n// TODO: There must be a way to just tell closure compiler to preserve the keys on i18n.phonenumbers.PhoneNumberType and just export that.\nconst numberType = {\n  \"FIXED_LINE\": 0,\n  \"MOBILE\": 1,\n  \"FIXED_LINE_OR_MOBILE\": 2,\n  \"TOLL_FREE\": 3,\n  \"PREMIUM_RATE\": 4,\n  \"SHARED_COST\": 5,\n  \"VOIP\": 6,\n  \"PERSONAL_NUMBER\": 7,\n  \"PAGER\": 8,\n  \"UAN\": 9,\n  \"VOICEMAIL\": 10,\n  \"UNKNOWN\": -1,\n};\n\n//* copied this from i18n.phonenumbers.PhoneNumberUtil.ValidationResult in https://github.com/googlei18n/libphonenumber/blob/master/javascript/i18n/phonenumbers/phonenumberutil.js and again put the keys in quotes.\nconst validationError = {\n  \"IS_POSSIBLE\": 0,\n  \"INVALID_COUNTRY_CODE\": 1,\n  \"TOO_SHORT\": 2,\n  \"TOO_LONG\": 3,\n  \"IS_POSSIBLE_LOCAL_ONLY\": 4,\n  \"INVALID_LENGTH\": 5,\n};\n\n//* Exports\n//* Note: the below code defines window.intlTelInputUtilsTemp, which is so-called because it will be exported (as an ES Module) and then deleted at the end of this file (see output_wrapper in grunt/closure-compiler.js).\ngoog.exportSymbol(\"intlTelInputUtilsTemp\", {});\ngoog.exportSymbol(\"intlTelInputUtilsTemp.formatNumberAsYouType\", formatNumberAsYouType);\ngoog.exportSymbol(\"intlTelInputUtilsTemp.formatNumber\", formatNumber);\ngoog.exportSymbol(\"intlTelInputUtilsTemp.getExampleNumber\", getExampleNumber);\ngoog.exportSymbol(\"intlTelInputUtilsTemp.getExtension\", getExtension);\ngoog.exportSymbol(\"intlTelInputUtilsTemp.getNumberType\", getNumberType);\ngoog.exportSymbol(\"intlTelInputUtilsTemp.getValidationError\", getValidationError);\ngoog.exportSymbol(\"intlTelInputUtilsTemp.isValidNumber\", isValidNumber);\ngoog.exportSymbol(\"intlTelInputUtilsTemp.isPossibleNumber\", isPossibleNumber);\ngoog.exportSymbol(\"intlTelInputUtilsTemp.getCoreNumber\", getCoreNumber);\n//* Enums\ngoog.exportSymbol(\"intlTelInputUtilsTemp.numberFormat\", numberFormat);\ngoog.exportSymbol(\"intlTelInputUtilsTemp.numberType\", numberType);\ngoog.exportSymbol(\"intlTelInputUtilsTemp.validationError\", validationError);\n"
  },
  {
    "path": "svelte/README.md",
    "content": "# IntlTelInput Svelte Component\n\nA Svelte 5 component for the [intl-tel-input](https://github.com/jackocnr/intl-tel-input) JavaScript plugin. View the [source code](https://github.com/jackocnr/intl-tel-input/blob/master/svelte/src/intl-tel-input/IntlTelInput.svelte).\n\n[Explore docs »](https://intl-tel-input.com/docs/svelte-component)\n"
  },
  {
    "path": "svelte/demo/set-number/App.svelte",
    "content": "<script>\n  import IntlTelInput from \"../../src/intl-tel-input/IntlTelInputWithUtils.svelte\";\n\n  const errorMap = [\n    \"Invalid number\",\n    \"Invalid country code\",\n    \"Too short\",\n    \"Too long\",\n    \"Invalid number\",\n  ];\n\n  let isValid = $state(null);\n  let number = $state(null);\n  let errorCode = $state(null);\n  let notice = $state(null);\n\n  let intlTelInputRef = $state();\n\n  const handleSetNumber = () => {\n    intlTelInputRef?.getInstance()?.setNumber(\"+14155552671\");\n  };\n\n  const handleSubmit = () => {\n    if (isValid) {\n      notice = `Valid number: ${number}`;\n    } else {\n      const errorMessage = errorMap[errorCode || 0] || \"Invalid number\";\n      notice = `Error: ${errorMessage}`;\n    }\n  };\n</script>\n\n<form>\n  <IntlTelInput\n    bind:this={intlTelInputRef}\n    onChangeNumber={(n) => number = n}\n    onChangeValidity={(v) => isValid = v}\n    onChangeErrorCode={(e) => errorCode = e}\n    options={{ initialCountry: 'us' }}\n  />\n  <button class=\"button\" type=\"button\" onclick={handleSetNumber}>\n    Set Number\n  </button>\n  <button class=\"button\" type=\"button\" onclick={handleSubmit}>Validate</button>\n  {#if notice}\n    <div class=\"notice\">{notice}</div>\n  {/if}\n</form>\n"
  },
  {
    "path": "svelte/demo/set-number/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <title>Svelte App - Set Number Demo</title>\n</head>\n\n<body>\n  <h1>Svelte App - Set Number Demo</h1>\n  <p>A simple Svelte app demonstrating programmatically setting a phone number.</p>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"./main.js\"></script>\n</body>\n\n</html>\n"
  },
  {
    "path": "svelte/demo/set-number/main.js",
    "content": "import { mount } from \"svelte\";\nimport App from \"./App.svelte\";\nimport \"../../../build/css/intlTelInput.css\";\nimport \"../../../build/css/demo.css\";\n\nmount(App, { target: document.getElementById(\"app\") });\n"
  },
  {
    "path": "svelte/demo/set-number/vite.config.mjs",
    "content": "import { defineConfig } from \"vite\";\nimport { svelte } from \"@sveltejs/vite-plugin-svelte\";\nimport { version } from \"../../../package.json\";\n\nexport default defineConfig({\n  root: \"svelte/demo/set-number\",\n  define: {\n    \"process.env.VERSION\": `\"${version}\"`,\n  },\n  plugins: [svelte()],\n});\n"
  },
  {
    "path": "svelte/demo/simple/App.svelte",
    "content": "<script>\n  import IntlTelInput from \"../../src/intl-tel-input/IntlTelInputWithUtils.svelte\";\n</script>\n\n<IntlTelInput\n  options={{ initialCountry: 'us' }}\n/>\n"
  },
  {
    "path": "svelte/demo/simple/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <title>Svelte App - Simple Demo</title>\n</head>\n\n<body>\n  <h1>Svelte App - Simple Demo</h1>\n  <p>A simple Svelte app, using the IntlTelInput component.</p>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"./main.js\"></script>\n</body>\n\n</html>\n"
  },
  {
    "path": "svelte/demo/simple/main.js",
    "content": "import { mount } from \"svelte\";\nimport App from \"./App.svelte\";\nimport \"../../../build/css/intlTelInput.css\";\nimport \"../../../build/css/demo.css\";\n\nmount(App, { target: document.getElementById(\"app\") });\n"
  },
  {
    "path": "svelte/demo/simple/vite.config.mjs",
    "content": "import { defineConfig } from \"vite\";\nimport { svelte } from \"@sveltejs/vite-plugin-svelte\";\nimport { version } from \"../../../package.json\";\n\nexport default defineConfig({\n  root: \"svelte/demo/simple\",\n  define: {\n    \"process.env.VERSION\": `\"${version}\"`,\n  },\n  plugins: [svelte()],\n});\n"
  },
  {
    "path": "svelte/demo/toggle-disabled/App.svelte",
    "content": "<script>\n  import IntlTelInput from \"../../src/intl-tel-input/IntlTelInputWithUtils.svelte\";\n\n  let isDisabled = $state(true);\n\n  const toggleDisabled = () => {\n    isDisabled = !isDisabled;\n  };\n</script>\n\n<form>\n  <IntlTelInput disabled={isDisabled} />\n  <button class=\"button\" type=\"button\" onclick={toggleDisabled}>Toggle</button>\n</form>\n"
  },
  {
    "path": "svelte/demo/toggle-disabled/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <title>Svelte App - Toggle Disabled Demo</title>\n</head>\n\n<body>\n  <h1>Svelte App - Toggle Disabled Demo</h1>\n  <p>A simple Svelte app demonstrating enabling/disabling the IntlTelInput component.</p>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"./main.js\"></script>\n</body>\n\n</html>\n"
  },
  {
    "path": "svelte/demo/toggle-disabled/main.js",
    "content": "import { mount } from \"svelte\";\nimport App from \"./App.svelte\";\nimport \"../../../build/css/intlTelInput.css\";\nimport \"../../../build/css/demo.css\";\n\nmount(App, { target: document.getElementById(\"app\") });\n"
  },
  {
    "path": "svelte/demo/toggle-disabled/vite.config.mjs",
    "content": "import { defineConfig } from \"vite\";\nimport { svelte } from \"@sveltejs/vite-plugin-svelte\";\nimport { version } from \"../../../package.json\";\n\nexport default defineConfig({\n  root: \"svelte/demo/toggle-disabled\",\n  define: {\n    \"process.env.VERSION\": `\"${version}\"`,\n  },\n  plugins: [svelte()],\n});\n"
  },
  {
    "path": "svelte/demo/validation/App.svelte",
    "content": "<script>\n  import IntlTelInput from \"../../src/intl-tel-input/IntlTelInputWithUtils.svelte\";\n\n  const errorMap = [\n    \"Invalid number\",\n    \"Invalid country code\",\n    \"Too short\",\n    \"Too long\",\n    \"Invalid number\",\n  ];\n\n  let isValid = $state(null);\n  let number = $state(null);\n  let errorCode = $state(null);\n  let notice = $state(null);\n\n  const handleSubmit = () => {\n    if (isValid) {\n      notice = `Valid number: ${number}`;\n    } else {\n      const errorMessage = errorMap[errorCode || 0] || \"Invalid number\";\n      notice = `Error: ${errorMessage}`;\n    }\n  };\n</script>\n\n<form>\n  <IntlTelInput\n    onChangeNumber={(n) => number = n}\n    onChangeValidity={(v) => isValid = v}\n    onChangeErrorCode={(e) => errorCode = e}\n    options={{ initialCountry: 'us' }}\n  />\n  <button class=\"button\" type=\"button\" onclick={handleSubmit}>Validate</button>\n  {#if notice}\n    <div class=\"notice\">{notice}</div>\n  {/if}\n</form>\n"
  },
  {
    "path": "svelte/demo/validation/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <title>Svelte App - Validation Demo</title>\n</head>\n\n<body>\n  <h1>Svelte App - Validation Demo</h1>\n  <p>A simple Svelte app, using the IntlTelInput component to handle phone number entry and validation.</p>\n  <p>Enter a phone number below and click \"Validate\".</p>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"./main.js\"></script>\n</body>\n\n</html>\n"
  },
  {
    "path": "svelte/demo/validation/main.js",
    "content": "import { mount } from \"svelte\";\nimport App from \"./App.svelte\";\nimport \"../../../build/css/intlTelInput.css\";\nimport \"../../../build/css/demo.css\";\n\nmount(App, { target: document.getElementById(\"app\") });\n"
  },
  {
    "path": "svelte/demo/validation/vite.config.mjs",
    "content": "import { defineConfig } from \"vite\";\nimport { svelte } from \"@sveltejs/vite-plugin-svelte\";\nimport { version } from \"../../../package.json\";\n\nexport default defineConfig({\n  root: \"svelte/demo/validation\",\n  define: {\n    \"process.env.VERSION\": `\"${version}\"`,\n  },\n  plugins: [svelte()],\n});\n"
  },
  {
    "path": "svelte/src/intl-tel-input/IntlTelInput.svelte",
    "content": "<script lang=\"ts\">\n  import intlTelInput from \"../intl-tel-input\";\n  import type { Iti } from \"../intl-tel-input\";\n  import type { SomeOptions } from \"../modules/types/public-api\";\n  import { onMount, onDestroy } from \"svelte\";\n\n  // Props\n  let {\n    disabled = false,\n    inputProps = {},\n    options = {},\n    usePreciseValidation = false,\n    value = \"\",\n    onChangeNumber,\n    onChangeCountry,\n    onChangeValidity,\n    onChangeErrorCode,\n  }: {\n    disabled?: boolean;\n    inputProps?: Record<string, unknown>;\n    options?: SomeOptions;\n    value?: string;\n    usePreciseValidation?: boolean;\n    onChangeNumber?: (number: string) => void;\n    onChangeCountry?: (country: string) => void;\n    onChangeValidity?: (valid: boolean) => void;\n    onChangeErrorCode?: (errorCode: number | null) => void;\n  } = $props();\n\n  // State\n  let inputElement: HTMLInputElement | undefined = $state();\n  let instance: Iti | undefined = $state();\n  let lastEmittedNumber: string | undefined = $state();\n  let lastEmittedCountry: string | undefined = $state();\n  let lastEmittedValidity: boolean | undefined = $state();\n  let lastEmittedErrorCode: number | null | undefined = $state();\n  let hasInitialized = $state(false);\n\n  // Validation helper\n  const isValid = (): boolean | null => {\n    if (!instance) return null;\n    return usePreciseValidation\n      ? instance.isValidNumberPrecise()\n      : instance.isValidNumber();\n  };\n\n  // Update handlers\n  const updateValidity = () => {\n    if (!instance) return;\n    const isCurrentlyValid = isValid();\n    if (isCurrentlyValid === null) return;\n\n    const valid = !!isCurrentlyValid;\n    const errorCode = valid ? null : instance.getValidationError();\n\n    if (valid !== lastEmittedValidity) {\n      lastEmittedValidity = valid;\n      onChangeValidity?.(valid);\n    }\n\n    if (errorCode !== lastEmittedErrorCode) {\n      lastEmittedErrorCode = errorCode;\n      onChangeErrorCode?.(errorCode);\n    }\n  };\n\n  const updateValue = () => {\n    const number = instance?.getNumber() ?? \"\";\n    if (number !== lastEmittedNumber) {\n      lastEmittedNumber = number;\n      onChangeNumber?.(number);\n    }\n    updateValidity();\n  };\n\n  const updateCountry = () => {\n    const country = instance?.getSelectedCountryData().iso2 ?? \"\";\n    if (country !== lastEmittedCountry) {\n      lastEmittedCountry = country;\n      onChangeCountry?.(country);\n    }\n    updateValue();\n  };\n\n  // Lifecycle\n  onMount(() => {\n    if (inputElement) {\n      instance = intlTelInput(inputElement, options);\n      inputElement.addEventListener(\"countrychange\", updateCountry);\n      if (value) instance.setNumber(value);\n      if (disabled) instance.setDisabled(disabled);\n\n      lastEmittedNumber = instance.getNumber() ?? \"\";\n      lastEmittedCountry = instance.getSelectedCountryData().iso2 ?? \"\";\n\n      const initialValid = isValid();\n      if (initialValid !== null) {\n        lastEmittedValidity = !!initialValid;\n        lastEmittedErrorCode = initialValid ? null : instance.getValidationError();\n      }\n      hasInitialized = true;\n    }\n  });\n\n  onDestroy(() => {\n    if (inputElement) {\n      inputElement.removeEventListener(\"countrychange\", updateCountry);\n    }\n    instance?.destroy();\n  });\n\n  // Watch disabled prop changes (only after initialization)\n  $effect(() => {\n    if (hasInitialized && instance) {\n      instance.setDisabled(disabled);\n    }\n  });\n\n  // Expose instance and input for parent access\n  export function getInstance(): Iti | undefined {\n    return instance;\n  }\n  export function getInput(): HTMLInputElement | undefined {\n    return inputElement;\n  }\n\n  const sanitizeInputProps = (props: Record<string, unknown>) => {\n    // ignore keys that would break functionality\n    const {\n      value: _value,\n      // disabled: _disabled,\n      ...rest\n    } = props as Record<string, unknown>;\n\n    return rest;\n  };\n</script>\n\n<!-- inputProps must come first, so cannot override the other required props -->\n<input\n  {...sanitizeInputProps(inputProps)}\n  bind:this={inputElement}\n  type=\"tel\"\n  oninput={updateValue}\n/>\n"
  },
  {
    "path": "svelte/src/intl-tel-input/IntlTelInputWithUtils.svelte",
    "content": "<!-- THIS FILE IS AUTO-GENERATED. DO NOT EDIT. -->\n<script lang=\"ts\">\nimport intlTelInput from \"./intlTelInputWithUtils\";\n  import type { Iti } from \"../intl-tel-input\";\n  import type { SomeOptions } from \"../modules/types/public-api\";\n  import { onMount, onDestroy } from \"svelte\";\n\n  // Props\n  let {\n    disabled = false,\n    inputProps = {},\n    options = {},\n    usePreciseValidation = false,\n    value = \"\",\n    onChangeNumber,\n    onChangeCountry,\n    onChangeValidity,\n    onChangeErrorCode,\n  }: {\n    disabled?: boolean;\n    inputProps?: Record<string, unknown>;\n    options?: SomeOptions;\n    value?: string;\n    usePreciseValidation?: boolean;\n    onChangeNumber?: (number: string) => void;\n    onChangeCountry?: (country: string) => void;\n    onChangeValidity?: (valid: boolean) => void;\n    onChangeErrorCode?: (errorCode: number | null) => void;\n  } = $props();\n\n  // State\n  let inputElement: HTMLInputElement | undefined = $state();\n  let instance: Iti | undefined = $state();\n  let lastEmittedNumber: string | undefined = $state();\n  let lastEmittedCountry: string | undefined = $state();\n  let lastEmittedValidity: boolean | undefined = $state();\n  let lastEmittedErrorCode: number | null | undefined = $state();\n  let hasInitialized = $state(false);\n\n  // Validation helper\n  const isValid = (): boolean | null => {\n    if (!instance) return null;\n    return usePreciseValidation\n      ? instance.isValidNumberPrecise()\n      : instance.isValidNumber();\n  };\n\n  // Update handlers\n  const updateValidity = () => {\n    if (!instance) return;\n    const isCurrentlyValid = isValid();\n    if (isCurrentlyValid === null) return;\n\n    const valid = !!isCurrentlyValid;\n    const errorCode = valid ? null : instance.getValidationError();\n\n    if (valid !== lastEmittedValidity) {\n      lastEmittedValidity = valid;\n      onChangeValidity?.(valid);\n    }\n\n    if (errorCode !== lastEmittedErrorCode) {\n      lastEmittedErrorCode = errorCode;\n      onChangeErrorCode?.(errorCode);\n    }\n  };\n\n  const updateValue = () => {\n    const number = instance?.getNumber() ?? \"\";\n    if (number !== lastEmittedNumber) {\n      lastEmittedNumber = number;\n      onChangeNumber?.(number);\n    }\n    updateValidity();\n  };\n\n  const updateCountry = () => {\n    const country = instance?.getSelectedCountryData().iso2 ?? \"\";\n    if (country !== lastEmittedCountry) {\n      lastEmittedCountry = country;\n      onChangeCountry?.(country);\n    }\n    updateValue();\n  };\n\n  // Lifecycle\n  onMount(() => {\n    if (inputElement) {\n      instance = intlTelInput(inputElement, options);\n      inputElement.addEventListener(\"countrychange\", updateCountry);\n      if (value) instance.setNumber(value);\n      if (disabled) instance.setDisabled(disabled);\n\n      lastEmittedNumber = instance.getNumber() ?? \"\";\n      lastEmittedCountry = instance.getSelectedCountryData().iso2 ?? \"\";\n\n      const initialValid = isValid();\n      if (initialValid !== null) {\n        lastEmittedValidity = !!initialValid;\n        lastEmittedErrorCode = initialValid ? null : instance.getValidationError();\n      }\n      hasInitialized = true;\n    }\n  });\n\n  onDestroy(() => {\n    if (inputElement) {\n      inputElement.removeEventListener(\"countrychange\", updateCountry);\n    }\n    instance?.destroy();\n  });\n\n  // Watch disabled prop changes (only after initialization)\n  $effect(() => {\n    if (hasInitialized && instance) {\n      instance.setDisabled(disabled);\n    }\n  });\n\n  // Expose instance and input for parent access\n  export function getInstance(): Iti | undefined {\n    return instance;\n  }\n  export function getInput(): HTMLInputElement | undefined {\n    return inputElement;\n  }\n\n  const sanitizeInputProps = (props: Record<string, unknown>) => {\n    // ignore keys that would break functionality\n    const {\n      value: _value,\n      // disabled: _disabled,\n      ...rest\n    } = props as Record<string, unknown>;\n\n    return rest;\n  };\n</script>\n\n<!-- inputProps must come first, so cannot override the other required props -->\n<input\n  {...sanitizeInputProps(inputProps)}\n  bind:this={inputElement}\n  type=\"tel\"\n  oninput={updateValue}\n/>\n"
  },
  {
    "path": "svelte/viteConfig.mjs",
    "content": "import { defineConfig } from \"vite\";\nimport { svelte } from \"@sveltejs/vite-plugin-svelte\";\nimport { version } from \"../package.json\";\n\nexport const getConfig = (filename) => defineConfig({\n  root: \"svelte\",\n  define: {\n    \"process.env.VERSION\": `\"${version}\"`,\n  },\n  build: {\n    outDir: \"build\",\n    emptyOutDir: false,\n    lib: {\n      entry: `src/intl-tel-input/${filename}`,\n      formats: [\"es\"],\n      fileName: \"[name]\",\n    },\n    rollupOptions: {\n      external: [\"svelte\", \"svelte/internal\"],\n      output: {\n        globals: {\n          svelte: \"Svelte\",\n        },\n      },\n    },\n  },\n  plugins: [svelte()],\n});\n\nexport default getConfig(\"IntlTelInput.svelte\");\n"
  },
  {
    "path": "svelte/viteConfigWithUtils.mjs",
    "content": "import { getConfig } from \"./viteConfig.mjs\";\n\nexport default getConfig(\"IntlTelInputWithUtils.svelte\");\n"
  },
  {
    "path": "tests/integration/core/dropdownShortcuts.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst {\n  initPlugin,\n  teardown,\n  getSelectedCountryButton,\n  isDropdownOpen,\n  clickSelectedCountryAsync,\n  checkFlagSelected,\n  getHighlightedItemCode,\n} = require(\"../helpers/helpers\");\n\ndescribe(\"dropdown shortcuts\", () => {\n  let iti, input, user, container;\n  beforeEach(() => {\n    user = userEvent.setup();\n    ({ iti, container, input } = initPlugin());\n  });\n\n  afterEach(() => {\n    teardown(iti);\n  });\n\n  describe(\"focusing the selected country\", () => {\n    beforeEach(async () => {\n      const selectedCountry = await getSelectedCountryButton(container);\n      selectedCountry.focus();\n    });\n\n    test(\"dropdown is not already open\", () => {\n      expect(isDropdownOpen(container)).toBe(false);\n    });\n\n    test(\"pressing UP opens the dropdown\", async () => {\n      await user.keyboard(\"{ArrowUp}\");\n      expect(isDropdownOpen(container)).toBe(true);\n    });\n\n    test(\"pressing DOWN opens the dropdown\", async () => {\n      await user.keyboard(\"{ArrowDown}\");\n      expect(isDropdownOpen(container)).toBe(true);\n    });\n\n    test(\"pressing SPACE opens the dropdown\", async () => {\n      await user.keyboard(\" \");\n      expect(isDropdownOpen(container)).toBe(true);\n    });\n\n    test(\"pressing ENTER opens the dropdown\", async () => {\n      await user.keyboard(\"{Enter}\");\n      expect(isDropdownOpen(container)).toBe(true);\n    });\n  });\n\n  describe(\"clicking the selected country\", () => {\n    beforeEach(async () => {\n      await clickSelectedCountryAsync(container, user);\n    });\n\n    test(\"shows the dropdown and highlights the first country in the list\", () => {\n      expect(isDropdownOpen(container)).toBe(true);\n      expect(getHighlightedItemCode(container)).toEqual(\"af\");\n    });\n\n    test(\"pressing ESCAPE closes the dropdown\", async () => {\n      await user.keyboard(\"{Escape}\");\n      expect(isDropdownOpen(container)).toBe(false);\n    });\n\n    test(\"pressing TAB closes the dropdown and focuses the input\", async () => {\n      await user.keyboard(\"{Tab}\");\n      expect(isDropdownOpen(container)).toBe(false);\n      expect(input).toHaveFocus();\n    });\n\n    test(\"pressing ENTER closes the dropdown and updates the selected country\", async () => {\n      await user.keyboard(\"{Enter}\");\n      expect(isDropdownOpen(container)).toBe(false);\n      expect(checkFlagSelected(container, \"af\")).toBe(true);\n    });\n\n    test(\"pressing UP highlights the country at the very end of the list\", async () => {\n      await user.keyboard(\"{ArrowUp}\");\n      expect(getHighlightedItemCode(container)).toEqual(\"zw\");\n    });\n\n    describe(\"pressing DOWN\", () => {\n      beforeEach(async () => {\n        await user.keyboard(\"{ArrowDown}\");\n      });\n\n      test(\"highlights the next country in the list\", () => {\n        expect(getHighlightedItemCode(container)).toEqual(\"ax\");\n      });\n\n      test(\"pressing ENTER closes the dropdown and updates the selected country\", async () => {\n        await user.keyboard(\"{Enter}\");\n        expect(isDropdownOpen(container)).toBe(false);\n        expect(checkFlagSelected(container, \"ax\")).toBe(true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/core/easternNumerals.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst { fireEvent } = require(\"@testing-library/dom\");\nconst {\n  initPlugin,\n  teardown,\n  checkFlagSelected,\n  getPasteEventObject,\n} = require(\"../helpers/helpers\");\n\n// \"Eastern\" numerals that are still used for phone numbers in some countries:\n// - Arabic-Indic\n// - Persian\n\n// Western:      0 1 2 3 4 5 6 7 8 9\n// Arabic-Indic: ٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩\n// Persian:      ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹\n\n// NOTE: These numerals are written right-to-left in isolation, but when used in phone numbers they are treated as left-to-right, just like Western digits.\n\n// Use cases:\n// - Initialising plugin with eastern numerals should format the number and set the flag correctly\n// - setNumber with eastern numerals should format the number and set the flag correctly\n// - Typing a different international dial code in eastern numerals should update the selected flag\n// - formatOnDisplay should work with eastern numerals\n// - formatAsYouType should be disabled with eastern numerals (caret position too complicated)\n// - strictMode should allow typing/pasting eastern numerals\n// - getNumber should work with eastern numerals, preserving the numeral set in the output\n// - isValidNumber and isValidNumberPrecise should work with eastern numerals\n\ndescribe(\"Eastern numerals support\", () => {\n  describe(\"Vanilla plugin\", () => {\n    let iti, user, input, container;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      ({ iti, input, container } = initPlugin());\n    });\n\n    afterEach(() => {\n      teardown(iti);\n    });\n\n    describe(\"Arabic-Indic digits\", () => {\n      test(\"typing an intl number updates the selected flag but does not format the number as you type\", async () => {\n        // \"+447947123456\" with Arabic-Indic digits\n        await user.type(input, \"+٤٤٧٩٤٧١٢٣٤٥٦\");\n        expect(checkFlagSelected(container, \"gb\")).toBe(true);\n        // does not apply formatting while typing\n        expect(input.value).toBe(\"+٤٤٧٩٤٧١٢٣٤٥٦\");\n      });\n    });\n\n    describe(\"Persian digits\", () => {\n      test(\"typing an intl number updates the selected flag but does not format the number as you type\", async () => {\n        // \"+447947123456\" with Persian digits\n        await user.type(input, \"+۴۴۷۹۴۷۱۲۳۴۵۶\");\n        expect(checkFlagSelected(container, \"gb\")).toBe(true);\n        // does not apply formatting while typing\n        expect(input.value).toBe(\"+۴۴۷۹۴۷۱۲۳۴۵۶\");\n      });\n\n      test(\"setNumber formats number and sets flag\", () => {\n        // \"+447947123456\" with Persian digits\n        iti.setNumber(\"+۴۴۷۹۴۷۱۲۳۴۵۶\");\n        expect(checkFlagSelected(container, \"gb\")).toBe(true);\n        // formats to \"07947 123456\" in original numerals\n        expect(input.value).toBe(\"۰۷۹۴۷ ۱۲۳۴۵۶\");\n      });\n    });\n  });\n\n  describe(\"Initial value in Arabic-Indic numerals\", () => {\n    let iti, input, container;\n\n    beforeEach(() => {\n      ({ iti, input, container } = initPlugin({\n        inputValue: \"+١٧٠٢١٢٣٤٥٦٧\", // \"+17021234567\" in Arabic-Indic digits\n      }));\n    });\n\n    afterEach(() => {\n      teardown(iti);\n    });\n\n    test(\"initialising formats the number and sets the flag\", () => {\n      expect(checkFlagSelected(container, \"us\")).toBe(true);\n      // \"+17021234567\" formats to \"(702) 123-4567\" (NOTE that the hyphenated block is displayed back to front here due to LTR/RTL mixing)\n      expect(input.value).toBe(\"(٧٠٢) ١٢٣-٤٥٦٧\");\n    });\n  });\n\n  describe(\"strictMode\", () => {\n    let iti, user, input;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { initialCountry: \"us\", strictMode: true };\n      ({ iti, input } = initPlugin({ options }));\n    });\n\n    afterEach(() => {\n      teardown(iti);\n    });\n\n    test(\"typing US national number with Arabic-Indic digits is allowed\", async () => {\n      // \"7021234567\" in Arabic-Indic\n      await user.type(input, \"٧٠٢١٢٣٤٥٦٧\");\n      expect(input.value).toBe(\"٧٠٢١٢٣٤٥٦٧\");\n    });\n\n    test(\"pasting intl number with Arabic-Indic digits is allowed\", async () => {\n      await user.click(input);\n      const pastedContent = \"+١٩٨٧١٢٣١٢٣٤\"; // \"+19871231234\"\n      const eventObject = getPasteEventObject(pastedContent);\n      fireEvent.paste(input, eventObject);\n      // Display should preserve Eastern Arabic numerals while formatting: +1 987-123-1234\n      expect(input.value).toBe(\"+١٩٨٧١٢٣١٢٣٤\");\n    });\n\n    test(\"getNumber returns E.164 formatted number in original numerals\", async () => {\n      await user.type(input, \"+۴۴۷۹۴۷۱۲۳۴۵۶\"); // \"+447947123456\" in Persian digits\n      expect(iti.getNumber()).toBe(\"+۴۴۷۹۴۷۱۲۳۴۵۶\");\n    });\n\n    test(\"validation works with eastern numerals\", async () => {\n      await user.clear(input);\n      // valid US mobile example in Arabic-Indic: +17025550123\n      await user.type(input, \"+١٧٠٢٥٥٥٠١٢٣\");\n      expect(iti.isValidNumber()).toBe(true);\n      expect(iti.isValidNumberPrecise()).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/core/initialValues.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst {\n  initPlugin,\n  teardown,\n  checkFlagSelected,\n} = require(\"../helpers/helpers\");\n\ndescribe(\"initial values\", () => {\n  let iti, input, container;\n\n  afterEach(() => {\n    teardown(iti);\n  });\n\n  describe(\"init plugin on empty input\", () => {\n    beforeEach(() => {\n      ({ iti, input, container } = initPlugin({\n        inputValue: \"\",\n      }));\n    });\n\n    test(\"leaves the input empty\", () => {\n      expect(input.value).toEqual(\"\");\n    });\n\n    test(\"leaves the selected country empty\", () => {\n      expect(checkFlagSelected(container, \"\")).toBe(true);\n    });\n  });\n\n  describe(\"init plugin on input containing valid intl UK number\", () => {\n    beforeEach(() => {\n      ({ iti, input, container } = initPlugin({\n        inputValue: \"+44 7947 123 456\",\n      }));\n    });\n\n    test(\"formats the number\", () => {\n      expect(input.value).toEqual(\"07947 123456\");\n    });\n\n    test(\"updates the selected country\", () => {\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n    });\n  });\n\n  describe(\"init plugin on input containing valid intl regionless NANP number\", () => {\n    beforeEach(() => {\n      ({ iti, input, container } = initPlugin({\n        inputValue: \"+1 800 123 1234\",\n      }));\n    });\n\n    test(\"formats the number\", () => {\n      expect(input.value).toEqual(\"(800) 123-1234\");\n    });\n\n    test(\"updates the selected country\", () => {\n      expect(checkFlagSelected(container, \"us\")).toBe(true);\n    });\n  });\n\n  describe(\"init plugin on input containing valid intl Cook Island number\", () => {\n    beforeEach(() => {\n      ({ iti, input, container } = initPlugin({\n        inputValue: \"+682 21 234\",\n      }));\n    });\n\n    test(\"formats the number\", () => {\n      expect(input.value).toEqual(\"21 234\");\n    });\n\n    //* Issue 520.\n    test(\"updates the selected country\", () => {\n      expect(checkFlagSelected(container, \"ck\")).toBe(true);\n    });\n  });\n\n  describe(\"init plugin on input containing valid national UK number\", () => {\n    beforeEach(() => {\n      ({ iti, input, container } = initPlugin({\n        inputValue: \"07947123123\",\n      }));\n    });\n\n    test(\"does not format the number\", () => {\n      expect(input.value).toEqual(\"07947123123\");\n    });\n\n    test(\"leaves the selected country empty\", () => {\n      expect(checkFlagSelected(container, \"\")).toBe(true);\n    });\n  });\n\n  describe(\"init plugin on input containing valid national UK number, and initialCountry=GB\", () => {\n    beforeEach(() => {\n      ({ iti, input, container } = initPlugin({\n        inputValue: \"07947123123\",\n        options: { initialCountry: \"gb\" },\n      }));\n    });\n\n    test(\"formats the number\", () => {\n      expect(input.value).toEqual(\"07947 123123\");\n    });\n\n    test(\"updates the selected country\", () => {\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n    });\n  });\n\n  describe(\"init plugin on input containing valid intl FI number shared with AX\", () => {\n    beforeEach(() => {\n      ({ iti, input, container } = initPlugin({\n        inputValue: \"+358457234567\",\n      }));\n    });\n\n    // Issue 2111: initialising with a number range shared between FI and AX was selecting AX, but should select FI as it has the higher priority\n    test(\"updates the selected country\", () => {\n      expect(checkFlagSelected(container, \"fi\")).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/core/multipleInstances.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst {\n  initPlugin,\n  teardown,\n  getCountryListLength,\n  checkFlagSelected,\n  openDropdownSelectCountryAsync,\n  clickSelectedCountryAsync,\n  isDropdownOpen,\n} = require(\"../helpers/helpers\");\n\ndescribe(\"multiple instances\", () => {\n  let user, iti1, iti2, input1, container1, container2;\n\n  beforeEach(() => {\n    user = userEvent.setup();\n    ({ iti: iti1, input: input1, container: container1 } = initPlugin({\n      options: { onlyCountries: [\"af\", \"cn\"] },\n    }));\n    ({ iti: iti2, container: container2 } = initPlugin({\n      options: { onlyCountries: [\"al\", \"cn\", \"kr\", \"ru\"] },\n    }));\n  });\n\n  afterEach(() => {\n    teardown(iti1);\n    teardown(iti2);\n  });\n\n  test(\"instances have different country lists\", () => {\n    expect(getCountryListLength(container1)).toEqual(2);\n    expect(getCountryListLength(container2)).toEqual(4);\n  });\n\n  test(\"selecting a country in the first instance dropdown only updates the selected country for that instance\", async () => {\n    await openDropdownSelectCountryAsync(container1, \"cn\", user);\n\n    expect(checkFlagSelected(container1, \"cn\")).toBe(true);\n    expect(checkFlagSelected(container2, \"\")).toBe(true);\n  });\n\n  test(\"typing an intl number in the first instance input only updates the selected country for that instance\", async () => {\n    await user.type(input1, \"+86123456\");\n\n    expect(checkFlagSelected(container1, \"cn\")).toBe(true);\n    expect(checkFlagSelected(container2, \"\")).toBe(true);\n  });\n\n  describe(\"clicking open dropdown on the first instance\", () => {\n    beforeEach(async () => {\n      await clickSelectedCountryAsync(container1, user);\n    });\n\n    test(\"only opens the dropdown on that instance\", () => {\n      expect(isDropdownOpen(container1)).toBe(true);\n      expect(isDropdownOpen(container2)).toBe(false);\n    });\n\n    test(\"then clicking open dropdown on the second instance will close the first and open the second\", async () => {\n      await clickSelectedCountryAsync(container2, user);\n\n      expect(isDropdownOpen(container1)).toBe(false);\n      expect(isDropdownOpen(container2)).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/core/regionless.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\ndescribe(\"regionless (non-geographic) dial codes\", () => {\n  const regionlessNumber = \"+80012345678\";\n\n  describe(\"formatting with nationalMode=true + formatOnDisplay=true\", () => {\n    let iti, input;\n\n    beforeEach(() => {\n      const options = { nationalMode: true, formatOnDisplay: true };\n      ({ iti, input } = initPlugin({ inputValue: regionlessNumber, options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"regionless numbers are formatted as international\", () => {\n      // should remain international (start with +800)\n      expect(input.value).toMatch(/^\\+800/);\n    });\n  });\n\n  describe(\"validation, with allowedNumberTypes=[TOLL_FREE]\", () => {\n    let iti, input, user;\n\n    beforeEach(() => {\n      const options = { allowedNumberTypes: [\"TOLL_FREE\"] };\n      ({ iti, input } = initPlugin({ options }));\n      user = userEvent.setup();\n    });\n\n    afterEach(() => {\n      teardown(iti);\n    });\n\n    test(\"isValidNumber returns true for regionless number\", async () => {\n      await user.type(input, regionlessNumber);\n      expect(iti.isValidNumber()).toBe(true);\n    });\n\n    test(\"isValidNumberPrecise returns true for regionless number\", async () => {\n      await user.type(input, regionlessNumber);\n      expect(iti.isValidNumberPrecise()).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/core/usingDropdown.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst {\n  initPlugin,\n  teardown,\n  clickSelectedCountryAsync,\n  isDropdownOpen,\n  getSelectedCountryButton,\n  getHighlightedItemCode,\n  selectCountryAsync,\n  checkFlagSelected,\n  injectInput,\n  getDropdownElement,\n} = require(\"../helpers/helpers\");\n\ndescribe(\"using dropdown\", () => {\n  let iti, user, container, input;\n\n  beforeEach(() => {\n    user = userEvent.setup();\n    ({ iti, container, input } = initPlugin());\n  });\n\n  afterEach(() => {\n    teardown(iti);\n  });\n\n  test(\"allows focusing the selected country using the keyboard\", async () => {\n    await user.keyboard(\"{Tab}\");\n    const selectedCountry = getSelectedCountryButton(container);\n    expect(selectedCountry).toHaveFocus();\n  });\n\n  describe(\"clicking the selected flag to open the dropdown\", () => {\n    beforeEach(async () => {\n      await clickSelectedCountryAsync(container, user);\n    });\n\n    test(\"opens the dropdown with the top item highlighted\", () => {\n      expect(isDropdownOpen(container)).toBe(true);\n      const highlightedItemCode = getHighlightedItemCode(container);\n      expect(highlightedItemCode).toBe(\"af\");\n    });\n\n    test(\"clicking it again closes the dropdown\", async () => {\n      await clickSelectedCountryAsync(container, user);\n      expect(isDropdownOpen(container)).toBe(false);\n    });\n\n    test(\"clicking off closes the dropdown\", async () => {\n      await user.click(document.body);\n      expect(isDropdownOpen(container)).toBe(false);\n    });\n\n    describe(\"selecting a new country item\", () => {\n      beforeEach(async () => {\n        await selectCountryAsync(container, \"ca\", user);\n      });\n\n      test(\"updates the selected flag\", () => {\n        expect(checkFlagSelected(container, \"ca\")).toBe(true);\n      });\n\n      //* This was a bug.\n      test(\"typing a space in the input doesn't reset to the default country for that dial code\", async () => {\n        expect(input).toHaveFocus();\n        await user.keyboard(\" \");\n        expect(checkFlagSelected(container, \"ca\")).toBe(true);\n      });\n    });\n  });\n\n  // these tests should be the opposite of the RTL ones below\n  test(\"does not add dir attributes by default\", () => {\n    expect(container.getAttribute(\"dir\")).toBe(null);\n    const dropdownContent = getDropdownElement(container);\n    expect(dropdownContent.getAttribute(\"dir\")).toBe(null);\n    const firstDialCode = dropdownContent.querySelector(\".iti__dial-code\");\n    expect(firstDialCode.getAttribute(\"dir\")).toBe(null);\n  });\n});\n\ndescribe(\"with RTL context\", () => {\n  let iti, container;\n\n  beforeEach(() => {\n    document.body.setAttribute(\"dir\", \"rtl\");\n    ({ iti, container } = initPlugin());\n  });\n\n  afterEach(() => {\n    teardown(iti);\n    document.body.removeAttribute(\"dir\");\n  });\n\n  test(\"does add dir attributes by default\", () => {\n    // we add dir=LTR to the container, dir=RTL to the dropdown, and dir=LTR on the dial codes in the country list\n    expect(container.getAttribute(\"dir\")).toBe(\"ltr\");\n    const dropdownContent = getDropdownElement(container);\n    expect(dropdownContent.getAttribute(\"dir\")).toBe(\"rtl\");\n    const firstDialCode = dropdownContent.querySelector(\".iti__dial-code\");\n    expect(firstDialCode.getAttribute(\"dir\")).toBe(\"ltr\");\n  });\n});\n\ndescribe(\"using dropdown: disabled input\", () => {\n  let iti, user, container, input;\n\n  beforeEach(() => {\n    user = userEvent.setup();\n    input = injectInput({ disabled: true });\n    ({ iti, container } = initPlugin({ input }));\n  });\n\n  afterEach(() => {\n    teardown(iti);\n  });\n\n  test(\"prevents the user from opening the dropdown using the keyboard\", async () => {\n    await user.keyboard(\"{Tab}\");\n    const selectedCountry = getSelectedCountryButton(container);\n    expect(selectedCountry).not.toHaveFocus();\n  });\n\n  test(\"clicking the selected flag does not open the dropdown\", async () => {\n    await clickSelectedCountryAsync(container, user);\n    expect(isDropdownOpen(container)).toBe(false);\n  });\n});"
  },
  {
    "path": "tests/integration/core/usingInput.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst {\n  initPlugin,\n  teardown,\n  checkFlagSelected,\n  openDropdownSelectCountryAsync,\n} = require(\"../helpers/helpers\");\n\ndescribe(\"using input\", () => {\n  let iti, user, input, container;\n\n  beforeEach(() => {\n    user = userEvent.setup();\n    ({ iti, input, container } = initPlugin());\n  });\n\n  afterEach(() => {\n    teardown(iti);\n  });\n\n  describe(\"typing a number with an intl dial code\", () => {\n    beforeEach(async () => {\n      await user.type(input, \"+44 1234567\");\n    });\n\n    test(\"updates the selected flag\", () => {\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n    });\n\n    //* This was a bug.\n    test(\"clearing the input again does not change the selected flag\", async () => {\n      await user.clear(input);\n      await user.type(input, \" \");\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n    });\n  });\n\n  describe(\"typing a dial code containing a space\", () => {\n    beforeEach(async () => {\n      await user.type(input, \"+4 4 98765432\");\n    });\n\n    test(\"still updates the flag correctly\", () => {\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n    });\n\n    test(\"then changing the flag updates and re-formats the number correctly\", async () => {\n      await openDropdownSelectCountryAsync(container, \"zw\", user);\n      expect(input.value).toBe(\"09 876 5432\");\n    });\n  });\n\n  describe(\"typing a dial code containing a dot\", () => {\n    beforeEach(async () => {\n      await user.type(input, \"+4.4 98765432\");\n    });\n\n    test(\"still updates the flag correctly\", () => {\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n    });\n\n    test(\"then changing the flag updates and re-formats the whole number correctly\", async () => {\n      await openDropdownSelectCountryAsync(container, \"zw\", user);\n      expect(input.value).toBe(\"09 876 5432\");\n    });\n  });\n\n  describe(\"typing the bangladeshi intl dial code\", () => {\n    beforeEach(async () => {\n      await user.type(input, \"+880\");\n    });\n\n    test(\"selects the bangladesh flag\", () => {\n      expect(checkFlagSelected(container, \"bd\")).toBe(true);\n    });\n\n    // this was a bug: https://github.com/jackocnr/intl-tel-input/issues/533\n    describe(\"adding a 1 at the beginning\", () => {\n      beforeEach(async () => {\n        await user.keyboard(\"{ArrowLeft}{ArrowLeft}{ArrowLeft}1\");\n      });\n\n      test(\"changes to US flag\", () => {\n        expect(input.value).toBe(\"+1 880\");\n        expect(checkFlagSelected(container, \"us\")).toBe(true);\n      });\n    });\n  });\n\n  describe(\"selecting Canada and then typing a regionless NANP number\", () => {\n    beforeEach(async () => {\n      await openDropdownSelectCountryAsync(container, \"ca\", user);\n      await user.type(input, \"8005551212\");\n    });\n\n    test(\"leaves canada selected\", () => {\n      expect(checkFlagSelected(container, \"ca\")).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/events/closeCountryDropdownEvent.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst {\n  injectInput,\n  initPlugin,\n  teardown,\n  clickSelectedCountryAsync,\n  selectCountryAsync,\n} = require(\"../helpers/helpers\");\n\ndescribe(\"close:countrydropdown event\", () => {\n  let input, iti, mockEventHandler, container, user;\n\n  beforeEach(() => {\n    user = userEvent.setup();\n    input = injectInput();\n    mockEventHandler = jest.fn();\n    input.addEventListener(\"close:countrydropdown\", mockEventHandler);\n    ({ iti, container } = initPlugin({ input }));\n  });\n\n  afterEach(() => {\n    input.removeEventListener(\"close:countrydropdown\", mockEventHandler);\n    teardown(iti);\n  });\n\n  test(\"does not trigger the event\", () => {\n    expect(mockEventHandler).not.toHaveBeenCalled();\n  });\n\n  describe(\"opening dropdown\", () => {\n    beforeEach(async () => {\n      await clickSelectedCountryAsync(container, user);\n    });\n\n    test(\"clicking outside the dropdown triggers the event\", async () => {\n      await user.click(input);\n      expect(mockEventHandler).toHaveBeenCalled();\n    });\n\n    test(\"pressing escape triggers the event\", async () => {\n      await user.keyboard(\"{Escape}\");\n      expect(mockEventHandler).toHaveBeenCalled();\n    });\n\n    test(\"selecting Afghanistan triggers the event\", async () => {\n      await selectCountryAsync(container, \"af\", user);\n      expect(mockEventHandler).toHaveBeenCalled();\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/events/countryChangeEvent.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst {\n  injectInput,\n  initPlugin,\n  teardown,\n  openDropdownSelectCountryAsync,\n} = require(\"../helpers/helpers\");\n\ndescribe(\"countrychange event\", () => {\n  let input, iti, mockEventHandler, container, user;\n\n  beforeEach(() => {\n    user = userEvent.setup();\n    input = injectInput();\n    mockEventHandler = jest.fn();\n    input.addEventListener(\"countrychange\", mockEventHandler);\n    ({ iti, container } = initPlugin({ input }));\n  });\n\n  afterEach(() => {\n    input.removeEventListener(\"countrychange\", mockEventHandler);\n    teardown(iti);\n  });\n\n  test(\"does not trigger the event\", () => {\n    expect(mockEventHandler).not.toHaveBeenCalled();\n  });\n\n  test(\"calling setCountry triggers the event\", () => {\n    iti.setCountry(\"fr\");\n    expect(mockEventHandler).toHaveBeenCalled();\n  });\n\n  test(\"calling setNumber triggers the event\", () => {\n    iti.setNumber(\"+34\");\n    expect(mockEventHandler).toHaveBeenCalled();\n  });\n\n  test(\"selecting Afghanistan triggers the event\", async () => {\n    await openDropdownSelectCountryAsync(container, \"af\", user);\n    expect(mockEventHandler).toHaveBeenCalled();\n  });\n\n  test(\"typing another number triggers the event\", async () => {\n    await user.type(input, \"+44\");\n    expect(mockEventHandler).toHaveBeenCalled();\n  });\n});\n"
  },
  {
    "path": "tests/integration/events/openCountryDropdownEvent.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst {\n  injectInput,\n  initPlugin,\n  teardown,\n  clickSelectedCountryAsync,\n  getSelectedCountryButton,\n} = require(\"../helpers/helpers\");\n\ndescribe(\"open:countrydropdown event\", () => {\n  let input, iti, mockEventHandler, container, user;\n\n  beforeEach(() => {\n    user = userEvent.setup();\n    input = injectInput();\n    mockEventHandler = jest.fn();\n    input.addEventListener(\"open:countrydropdown\", mockEventHandler);\n    ({ iti, container } = initPlugin({ input }));\n  });\n\n  afterEach(() => {\n    input.removeEventListener(\"open:countrydropdown\", mockEventHandler);\n    teardown(iti);\n  });\n\n  test(\"does not trigger the event\", () => {\n    expect(mockEventHandler).not.toHaveBeenCalled();\n  });\n\n  test(\"clicking the selected country triggers the event\", async () => {\n    await clickSelectedCountryAsync(container, user);\n    expect(mockEventHandler).toHaveBeenCalled();\n  });\n\n  test(\"focusing the selected country and hitting Enter triggers the event\", async () => {\n    const selectedCountry = getSelectedCountryButton(container);\n    selectedCountry.focus();\n    await user.keyboard(\"{Enter}\");\n    expect(mockEventHandler).toHaveBeenCalled();\n  });\n});\n"
  },
  {
    "path": "tests/integration/helpers/helpers.js",
    "content": "require(\"@testing-library/jest-dom\");\n// test using the minified version to check for minification errors in /grunt/replace.js\nconst intlTelInputWithUtils = require(\"intlTelInputWithUtils.min.js\");\n\nexports.intlTelInput = intlTelInputWithUtils;\n\n/** @typedef {typeof import(\"intl-tel-input\").default} IntlTelInputInterface */\n/** @typedef {import(\"intl-tel-input\").Iti} Iti */\n/** @typedef {import(\"intl-tel-input\").SomeOptions} SomeOptions */\n\nexports.totalCountries = 244;\n\nconst injectInputDefaults = { inputValue: \"\", disabled: false };\n\nexports.injectInput = ({ inputValue = \"\", disabled = false } = injectInputDefaults) => {\n  const input = document.createElement(\"input\");\n  if (inputValue) {\n    input.value = inputValue;\n  }\n  if (disabled) {\n    input.disabled = true;\n  }\n  document.body.appendChild(input);\n  return input;\n};\n\n/**\n * Create an intl-tel-input instance to test.\n * @param {{intlTelInput?: IntlTelInputInterface, input?: any, inputValue?: string, options?: SomeOptions}} options\n * @returns {{input: HTMLInputElement, iti: Iti, container: HTMLElement}}\n */\nexports.initPlugin = ({ intlTelInput = intlTelInputWithUtils, input = null, inputValue = \"\", options = {} } = {}) => {\n  const inputToUse = input || exports.injectInput({ inputValue });\n  const iti = intlTelInput(inputToUse, options);\n  const container = inputToUse.parentElement;\n  return { input: inputToUse, iti, container };\n};\n\n/**\n * Tear down a standard test environment. Optionally takes an intl-tel-input\n * instance to tear down, or a copy of the whole library in which to tear down\n * all instances.\n * @param {Iti|IntlTelInputInterface} iti\n */\nexports.teardown = (iti) => {\n  let toDestroy = [];\n  if (iti?.instances) {\n    toDestroy = Object.values(iti.instances);\n  } else if (iti) {\n    toDestroy = [iti];\n  }\n\n  for (const instance of toDestroy) {\n    // Tests might intentionally set up an instance wrong, so ignore any\n    // rejections of the instance's promise (otherwise the unhandled\n    // rejection causes the test to fail). But don't *wait*, since the test\n    // may also create a state where the promise will never fulfill.\n    instance.promise.catch(() => {});\n\n    instance.destroy();\n  }\n\n  if (document.body) {\n    document.body.innerHTML = \"\";\n  }\n  jest.restoreAllMocks();\n};\n\n/**\n * @param {IntlTelInputInterface} intlTelInput\n */\nexports.resetPackageAfterEach = (intlTelInput = intlTelInputWithUtils) => {\n  const originalUtils = intlTelInput.utils;\n\n  afterEach(function() {\n    try {\n      exports.teardown(intlTelInput);\n    } finally {\n      // Reset package-wide state.\n      intlTelInput.utils = originalUtils;\n      intlTelInput.startedLoadingUtilsScript = false;\n    }\n  });\n};\n\nexports.getCountryContainer = (root) => root.querySelector(\".iti__country-container\");\n\nexports.getArrowElement = (root) => root.querySelector(\".iti__arrow\");\n\nexports.getCountryListElement = (root) => root.querySelector(\".iti__country-list\");\n\nexports.getCountryListLength = (container) => {\n  const countryList = exports.getCountryListElement(container);\n  return countryList.querySelectorAll(\"li.iti__country\").length;\n};\n\nexports.getHighlightedItemCode = (container) => {\n  return container.querySelector(\".iti__country-list .iti__highlight\").getAttribute(\"data-country-code\");\n};\n\nexports.getSelectedCountryButton = (container) => {\n  return container.querySelector(\".iti__selected-country\");\n};\n\nexports.getSelectedDialCodeText = (container) => {\n  return container.querySelector(\".iti__selected-dial-code\").textContent;\n};\n\nexports.isDropdownOpen = (container) => {\n  return !exports.getDropdownElement(container).classList.contains(\"iti__hide\");\n};\n\nexports.getDropdownElement = (container) => {\n  return container.querySelector(\".iti__dropdown-content\");\n};\n\nexports.getSearchInput = (container) => {\n  return container.querySelector(\".iti__search-input\");\n};\n\nexports.getCountriesInList = (container) => {\n  const countryListItems = container.querySelectorAll(\".iti__country-list .iti__country\");\n  return [...countryListItems].map(c => c.getAttribute(\"data-country-code\"));\n};\n\nexports.checkFlagSelected = (container, countryCode = \"\") => {\n  const flag = container.querySelector(\".iti__selected-country .iti__flag\");\n  if (countryCode.length === 2) {\n    return flag.classList.contains(`iti__${countryCode}`);\n  }\n  if (countryCode.length === 0) {\n    return flag.classList.contains(\"iti__globe\");\n  }\n  throw new Error(\"Invalid country code\");\n};\n\nexports.clickSelectedCountryAsync = async (container, user) => {\n  const selectedCountryButton = exports.getSelectedCountryButton(container);\n  await user.click(selectedCountryButton);\n};\n\nexports.selectCountryAsync = async (container, iso2, user) => {\n  const countryItem = container.querySelector(`li[data-country-code='${iso2}']`);\n  await user.click(countryItem);\n};\n\nexports.openDropdownSelectCountryAsync = async (container, iso2, user) => {\n  await exports.clickSelectedCountryAsync(container, user);\n  await exports.selectCountryAsync(container, iso2, user);\n};\n\nexports.selectCountryAndTypePlaceholderNumberAsync = async (container, iso2, user, input) => {\n  await exports.openDropdownSelectCountryAsync(container, iso2, user);\n  const placeholderNumberClean = await exports.typePlaceholderNumberAsync(user, input);\n  return placeholderNumberClean;\n};\n\nexports.typePlaceholderNumberAsync = async (user, input) => {\n  const placeholderNumberClean = exports.stripFormattingChars(input.getAttribute(\"placeholder\"));\n  await user.type(input, placeholderNumberClean);\n  return placeholderNumberClean;\n};\n\n// strip formatting chars like space, dash, brackets (leaving just numerics and optional plus)\nexports.stripFormattingChars = (str) => str.replace(/[^0-9+]/g, \"\");\n\nexports.oneTickAsync = async () => {\n  await new Promise(resolve => setTimeout(resolve));\n};\n\nexports.getPasteEventObject = (str) => {\n  return {\n    clipboardData: {\n      getData: () => str,\n    },\n  };\n};\n"
  },
  {
    "path": "tests/integration/helpers/matchers.js",
    "content": "/** Test whether something is a promise. Works across realms. */\nfunction toBeAPromise(actual) {\n  const pass = Object.prototype.toString.call(actual) === \"[object Promise]\";\n  return {\n    pass,\n    message: () => `expected ${this.utils.printReceived(actual)}${pass ? \" not\" : \"\"} to be a promise`,\n  };\n}\n\nasync function toBePending(actual) {\n  const pending = Symbol();\n  const resolution = await Promise.race([actual, Promise.resolve(pending)]);\n  const pass = resolution === pending;\n\n  return {\n    pass,\n    message: () => `expected ${this.utils.printReceived(actual)}${pass ? \" not\" : \"\"} to be a pending`,\n  };\n}\n\nexpect.extend({\n  toBeAPromise,\n  toBePending,\n});\n"
  },
  {
    "path": "tests/integration/methods/destroy.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst {\n  initPlugin,\n  teardown,\n  getCountryContainer,\n  getCountryListElement,\n} = require(\"../helpers/helpers\");\n\ndescribe(\"destroy method\", () => {\n  describe(\"vanilla init\", () => {\n    let iti, input;\n\n    beforeEach(() => {\n      ({ iti, input } = initPlugin());\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"adds markup on init\", () => {\n      // dont use the usual \"container\" here, instead use inputParent, to match the test below\n      const inputParent = input.parentElement;\n      expect(inputParent.classList.contains(\"iti\")).toBe(true);\n      expect(getCountryContainer(inputParent)).toBeTruthy();\n      expect(getCountryListElement(inputParent)).toBeTruthy();\n    });\n\n    test(\"destroy removes markup\", () => {\n      iti.destroy();\n      // wrapper removed at this point so parent probably is body element\n      const inputParent = input.parentElement;\n      expect(inputParent.classList.contains(\"iti\")).toBe(false);\n      expect(getCountryContainer(inputParent)).toBeFalsy();\n      expect(getCountryListElement(inputParent)).toBeFalsy();\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/methods/getExtension.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\ndescribe(\"getExtension method\", () => {\n  let iti, input, user;\n  const base = \"+17024181234\";\n  const ext = \"98765\";\n\n  beforeEach(() => {\n    ({ iti, input } = initPlugin());\n    user = userEvent.setup();\n  });\n\n  afterEach(() => teardown(iti));\n\n  test(\"works for various delimiters\", async () => {\n    const delimiters = [\"ext.\", \"ex.\", \"x.\", \"ext\", \"ex\", \"x\", \"#\"];\n    for (const d of delimiters) {\n      await user.clear(input);\n      await user.type(input, `${base} ${d} ${ext}`);\n      expect(iti.getExtension()).toEqual(ext);\n    }\n  });\n\n  test(\"fails for space or none\", async () => {\n    await user.type(input, `${base} ${ext}`);\n    expect(iti.getExtension()).toBeNull();\n    await user.clear(input);\n    await user.type(input, `${base}${ext}`);\n    expect(iti.getExtension()).toBeNull();\n  });\n});\n"
  },
  {
    "path": "tests/integration/methods/getInstance.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, injectInput, intlTelInput } = require(\"../helpers/helpers\");\n\ndescribe(\"getInstance static\", () => {\n  let iti, input;\n\n  beforeEach(() => {\n    ({ iti, input } = initPlugin());\n  });\n\n  afterEach(() => teardown(iti));\n\n  test(\"returns instance when exists\", () => {\n    const found = intlTelInput.getInstance(input);\n    expect(found).toBe(iti);\n  });\n\n  test(\"returns null for unknown input\", () => {\n    const other = injectInput();\n    const found = intlTelInput.getInstance(other);\n    expect(found).toBeNull();\n  });\n});\n"
  },
  {
    "path": "tests/integration/methods/getNumber.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, intlTelInput } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst numberFormat = intlTelInput.utils.numberFormat;\n\ndescribe(\"getNumber method\", () => {\n  describe(\"initial value US number\", () => {\n    let iti;\n\n    beforeEach(() => {\n      ({ iti } = initPlugin({ inputValue: \"+17024181234\" }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"default getNumber returns E.164\", () => {\n      expect(iti.getNumber()).toEqual(\"+17024181234\");\n    });\n\n    test(\"INTERNATIONAL format\", async () => {\n      expect(iti.getNumber(numberFormat.INTERNATIONAL)).toEqual(\"+1 702-418-1234\");\n    });\n\n    test(\"NATIONAL format\", async () => {\n      expect(iti.getNumber(numberFormat.NATIONAL)).toEqual(\"(702) 418-1234\");\n    });\n  });\n\n  describe(\"typing numbers\", () => {\n    let iti, input, user;\n\n    beforeEach(() => {\n      ({ iti, input } = initPlugin());\n      user = userEvent.setup();\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"American Samoa national -> full intl\", async () => {\n      iti.setCountry(\"as\");\n      await user.type(input, \"6847331234\");\n      expect(iti.getNumber()).toEqual(\"+16847331234\");\n    });\n\n    test(\"Anguilla intl formatted -> clean E.164\", async () => {\n      await user.type(input, \"+1 264-235-1234\");\n      expect(iti.getNumber()).toEqual(\"+12642351234\");\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/methods/getNumberType.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { initPlugin, teardown, intlTelInput } = require(\"../helpers/helpers\");\nconst numberType = intlTelInput.utils.numberType;\n\ndescribe(\"getNumberType method\", () => {\n  let iti;\n\n  beforeEach(() => {\n    ({ iti } = initPlugin());\n  });\n\n  afterEach(() => {\n    teardown(iti);\n  });\n\n  test(\"returns the right type for a UK mobile number\", async () => {\n    iti.setNumber(\"+447733123456\");\n    expect(iti.getNumberType()).toEqual(numberType.MOBILE);\n  });\n\n  test(\"returns the right type for a UK landline number\", async () => {\n    iti.setNumber(\"+441531123456\");\n    expect(iti.getNumberType()).toEqual(numberType.FIXED_LINE);\n  });\n\n  test(\"returns the right type for a UK toll-free number\", async () => {\n    iti.setNumber(\"+448000123456\");\n    expect(iti.getNumberType()).toEqual(numberType.TOLL_FREE);\n  });\n});\n"
  },
  {
    "path": "tests/integration/methods/getSelectedCountryData.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\ndescribe(\"getSelectedCountryData method\", () => {\n  let iti, input, user;\n\n  beforeEach(() => {\n    ({ iti, input } = initPlugin());\n    user = userEvent.setup();\n  });\n\n  afterEach(() => teardown(iti));\n\n  test(\"gets the right default country data (empty state)\", () => {\n    expect(iti.getSelectedCountryData().iso2).toBeUndefined();\n  });\n\n  test(\"typing +44 updates selected country to gb\", async () => {\n    await user.type(input, \"+44\");\n    expect(iti.getSelectedCountryData().iso2).toEqual(\"gb\");\n  });\n\n  test(\"setCountry updates selected country data\", () => {\n    iti.setCountry(\"ch\");\n    expect(iti.getSelectedCountryData().iso2).toEqual(\"ch\");\n  });\n});\n"
  },
  {
    "path": "tests/integration/methods/getValidationError.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, intlTelInput } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst validationError = intlTelInput.utils.validationError;\n\ndescribe(\"getValidationError method\", () => {\n  let iti, input, user;\n\n  beforeEach(() => {\n    ({ iti, input } = initPlugin());\n    user = userEvent.setup();\n  });\n\n  afterEach(() => teardown(iti));\n\n  test(\"too short\", async () => {\n    await user.type(input, \"+4477\");\n    expect(iti.getValidationError()).toEqual(validationError.TOO_SHORT);\n  });\n\n  test(\"too long\", async () => {\n    await user.type(input, \"+447733123456789\");\n    expect(iti.getValidationError()).toEqual(validationError.TOO_LONG);\n  });\n\n  test(\"invalid country code\", async () => {\n    await user.type(input, \"+969\");\n    expect(iti.getValidationError()).toEqual(validationError.INVALID_COUNTRY_CODE);\n  });\n});\n"
  },
  {
    "path": "tests/integration/methods/isValidNumber.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { initPlugin, teardown, intlTelInput } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\ndescribe(\"isValidNumber method\", () => {\n  let iti, input, user, utilsBackup;\n\n  beforeEach(() => {\n    ({ iti, input } = initPlugin());\n    user = userEvent.setup();\n    utilsBackup = intlTelInput.utils;\n  });\n\n  afterEach(() => {\n    teardown(iti);\n    // replace intlTelInput.utils after setting it to null in one of the tests\n    intlTelInput.utils = utilsBackup;\n  });\n\n  test(\"returns true for: valid intl number\", async () => {\n    await user.type(input, \"+44 7733 123456\");\n    expect(iti.isValidNumber()).toBe(true);\n  });\n\n  test(\"returns true for: possible but invalid (bad dial code) intl number\", async () => {\n    await user.type(input, \"+44 9999 123456\");\n    expect(iti.isValidNumber()).toBe(true);\n  });\n\n  test(\"returns false for: invalid (too short) intl number\", async () => {\n    await user.type(input, \"+44 7733 1234\");\n    expect(iti.isValidNumber()).toBe(false);\n  });\n\n  test(\"returns false for: invalid (too long) intl number\", async () => {\n    await user.type(input, \"+44 7733 1234567\");\n    expect(iti.isValidNumber()).toBe(false);\n  });\n\n  test(\"returns null when utils script is not available\", async () => {\n    // simulate utils unavailable AFTER init\n    intlTelInput.utils = null;\n    await user.type(input, \"+44 7733 123456\");\n    expect(iti.isValidNumber()).toBeNull();\n  });\n});\n\ndescribe(\"isValidNumber method - NANP Barbados\", () => {\n  let iti, input, user;\n  const options = { initialCountry: \"bb\" };\n\n  beforeEach(() => {\n    ({ iti, input } = initPlugin({ options }));\n    user = userEvent.setup();\n  });\n\n  afterEach(() => {\n    teardown(iti);\n  });\n\n  test(\"returns false for: NANP partial number that libphonenumber could auto-complete with area code\", async () => {\n    // Barbados (bb) has area code 246 and expects 10-digit national numbers.\n    // Typing \"2462501\" (7 digits) should be invalid - it must not be auto-completed\n    // by libphonenumber prepending area code \"246\" to yield \"2462462501\" (10 digits).\n    await user.type(input, \"2462501\");\n    expect(iti.isValidNumber()).toBe(false);\n  });\n\n  test(\"returns true for: full NANP Barbados number\", async () => {\n    await user.type(input, \"2462501234\");\n    expect(iti.isValidNumber()).toBe(true);\n  });\n});\n\n\n\n// // LONG RUNNING TESTS - RUN MANUALLY FROM TIME TO TIME\n\n// // NOTE: none of these are NANP countries\n// const countriesAllowingOneLessDigit = [\"af\", \"ax\", \"al\", \"dz\", \"ar\", \"am\", \"at\", \"az\", \"by\", \"be\", \"bt\", \"ba\", \"bw\", \"br\", \"bg\", \"kh\", \"cn\", \"cd\", \"hr\", \"cw\", \"ec\", \"er\", \"ee\", \"et\", \"fi\", \"fr\", \"gf\", \"ga\", \"de\", \"gp\", \"gn\", \"hu\", \"ir\", \"it\", \"jo\", \"kz\", \"ke\", \"ki\", \"xk\", \"kg\", \"la\", \"lb\", \"lr\", \"ly\", \"li\", \"lt\", \"mg\", \"mw\", \"mq\", \"mu\", \"yt\", \"md\", \"mc\", \"me\", \"ma\", \"mz\", \"mm\", \"na\", \"nz\", \"ng\", \"nu\", \"kp\", \"mk\", \"ps\", \"pa\", \"pg\", \"py\", \"pe\", \"pl\", \"re\", \"ro\", \"ru\", \"rw\", \"sa\", \"rs\", \"sl\", \"sk\", \"sb\", \"so\", \"za\", \"kr\", \"ss\", \"lk\", \"bl\", \"sh\", \"mf\", \"pm\", \"sd\", \"sr\", \"se\", \"ch\", \"sy\", \"tw\", \"tz\", \"th\", \"tl\", \"to\", \"tr\", \"tm\", \"tv\", \"ug\", \"ua\", \"ae\", \"vu\", \"va\", \"ve\", \"eh\", \"ye\", \"zm\"];\n\n// const countriesAllowingMultipleLessDigits = [\"ax\", \"dz\", \"ar\", \"at\", \"by\", \"be\", \"bg\", \"cd\", \"ec\", \"fi\", \"hu\", \"jo\", \"ke\", \"ki\", \"lr\", \"li\", \"mw\", \"mc\", \"na\", \"nu\", \"ps\", \"py\", \"pl\", \"ro\", \"rw\", \"sk\", \"sb\", \"so\", \"za\", \"se\", \"sy\", \"th\", \"to\", \"vu\", \"ye\"];\n\n// const {\n//   stripFormattingChars,\n//   openDropdownSelectCountryAsync,\n// } = require(\"../helpers/helpers\");\n// const allCountries = intlTelInput.getCountryData();\n// const countryCodes = allCountries.map((country) => country.iso2).filter((iso2) => !countriesAllowingMultipleLessDigits.includes(iso2));\n\n// describe(\"isValidNumber: each digit of placeholder number\", () => {\n//   let input, iti, user, container;\n\n//   beforeEach(() => {\n//     user = userEvent.setup();\n//     const options = {\n//       placeholderNumberType: \"MOBILE\",\n//       allowedNumberTypes: [\"MOBILE\"],\n//     };\n//     ({ input, iti, container } = initPlugin({ options }));\n//   });\n\n//   afterEach(() => {\n//     teardown(iti);\n//   });\n\n//   test.each(countryCodes)(\"returns false until full placeholder number is typed: %s\", async (iso2) => {\n//     await openDropdownSelectCountryAsync(container, iso2, user);\n//     const placeholder = input.getAttribute(\"placeholder\");\n//     const digits = stripFormattingChars(placeholder);\n//     // type each digit one at a time, checking isValidNumber after each\n//     for (let i = 0; i < digits.length - 1; i++) {\n//       await user.type(input, digits[i]);\n\n//       if (countriesAllowingOneLessDigit.includes(iso2) && i === digits.length - 2) {\n//         // for these countries, the penultimate digit is allowed to be missing, so it should be valid at this point\n//         expect(iti.isValidNumber()).toBe(true);\n//       } else {\n//         // for all other countries, it should still be invalid\n//         // include the input value in the assertion message for easier debugging if it fails\n//         expect(`${input.value}: ${iti.isValidNumber()}`).toBe(`${input.value}: false`);\n//       }\n//     }\n//     // type the final digit - now it should be valid\n//     await user.type(input, digits[digits.length - 1]);\n//     expect(iti.isValidNumber()).toBe(true);\n//   });\n// });\n\n"
  },
  {
    "path": "tests/integration/methods/isValidNumberPrecise.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, openDropdownSelectCountryAsync, intlTelInput } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\ndescribe(\"isValidNumberPrecise\", () => {\n  describe(\"vanilla\", () => {\n    let iti, input, user, utilsBackup;\n\n    beforeEach(() => {\n      ({ iti, input } = initPlugin());\n      user = userEvent.setup();\n      utilsBackup = intlTelInput.utils;\n    });\n\n    afterEach(() => {\n      teardown(iti);\n      // restore intlTelInput.utils after setting it to null in one of the tests\n      intlTelInput.utils = utilsBackup;\n    });\n\n    test(\"valid intl number\", async () => {\n      await user.type(input, \"+44 7733 123456\");\n      expect(iti.isValidNumberPrecise()).toBe(true);\n    });\n\n    test(\"too short\", async () => {\n      await user.type(input, \"+44 7733 123\");\n      expect(iti.isValidNumberPrecise()).toBe(false);\n    });\n\n    test(\"bad dial code\", async () => {\n      await user.type(input, \"+44 9999 123456\");\n      expect(iti.isValidNumberPrecise()).toBe(false);\n    });\n\n    test(\"utils missing -> null\", async () => {\n      intlTelInput.utils = null;\n      await user.type(input, \"+44 7733 123456\");\n      expect(iti.isValidNumberPrecise()).toBeNull();\n    });\n  });\n\n  describe(\"nationalMode true\", () => {\n    let iti, input, user, container;\n    const options = { nationalMode: true };\n\n    beforeEach(() => {\n      ({ iti, input, container } = initPlugin({ options }));\n      user = userEvent.setup();\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"no country selected\", async () => {\n      await user.type(input, \"07733 123456\");\n      expect(iti.isValidNumberPrecise()).toBe(false);\n    });\n\n    test(\"wrong country selected\", async () => {\n      await openDropdownSelectCountryAsync(container, \"us\", user, input);\n      await user.type(input, \"07733 123456\"); // gb number\n      expect(iti.isValidNumberPrecise()).toBe(false);\n    });\n\n    test(\"correct country selected, valid number\", async () => {\n      await openDropdownSelectCountryAsync(container, \"gb\", user, input);\n      await user.type(input, \"07733 123456\");\n      expect(iti.isValidNumberPrecise()).toBe(true);\n    });\n\n    test(\"correct country selected, number too short\", async () => {\n      await openDropdownSelectCountryAsync(container, \"gb\", user, input);\n      await user.type(input, \"07733 123\");\n      expect(iti.isValidNumberPrecise()).toBe(false);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/methods/setCountry.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, checkFlagSelected, getSelectedCountryButton } = require(\"../helpers/helpers\");\n\ndescribe(\"setCountry method\", () => {\n  describe(\"vanilla init\", () => {\n    let iti, input, container;\n\n    beforeEach(() => {\n      ({ iti, input, container } = initPlugin());\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"updates flag\", () => {\n      expect(checkFlagSelected(container, \"\")).toBe(true);\n      iti.setCountry(\"gb\");\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n    });\n\n    // we used to do this\n    test(\"does not insert dial code\", () => {\n      iti.setCountry(\"gb\");\n      expect(input.value).toEqual(\"\");\n    });\n  });\n\n  describe(\"separateDialCode disabled\", () => {\n    let iti, container;\n    const options = { showFlags: true, separateDialCode: false };\n\n    beforeEach(() => {\n      ({ iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"flag title\", () => {\n      iti.setCountry(\"gb\");\n      const btn = getSelectedCountryButton(container);\n      expect(btn.getAttribute(\"title\")).toEqual(\"United Kingdom\");\n    });\n  });\n\n  describe(\"separateDialCode enabled\", () => {\n    let iti, container;\n    const options = { showFlags: true, separateDialCode: true };\n\n    beforeEach(() => {\n      ({ iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"flag title\", () => {\n      iti.setCountry(\"gb\");\n      const btn = getSelectedCountryButton(container);\n      expect(btn.getAttribute(\"title\")).toEqual(\"United Kingdom\");\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/methods/setDisabled.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst {\n  initPlugin,\n  teardown,\n  clickSelectedCountryAsync,\n  isDropdownOpen,\n  getSelectedCountryButton,\n} = require(\"../helpers/helpers\");\n\ndescribe(\"setDisabled method\", () => {\n  let iti, user, container, input;\n\n  beforeEach(() => {\n    user = userEvent.setup();\n    ({ iti, container, input } = initPlugin());\n    iti.setDisabled(true);\n  });\n\n  afterEach(() => {\n    teardown(iti);\n  });\n\n  test(\"disables the input\", async () => {\n    expect(input.disabled).toBe(true);\n  });\n\n  test(\"disables clicking selected country to open dropdown\", async () => {\n    await clickSelectedCountryAsync(container, user);\n    expect(isDropdownOpen(container)).toBe(false);\n  });\n\n  test(\"disables focusing the selected country\", async () => {\n    await user.keyboard(\"{Tab}\");\n    const selectedCountryButton = getSelectedCountryButton(container);\n    expect(selectedCountryButton).not.toHaveFocus();\n  });\n\n  describe(\"then calling setDisabled(false)\", () => {\n    beforeEach(() => {\n      iti.setDisabled(false);\n    });\n\n    test(\"re-enables the input field\", async () => {\n      expect(input.disabled).toBe(false);\n    });\n\n    test(\"re-enables clicking selected flag to open dropdown\", async () => {\n      await clickSelectedCountryAsync(container, user);\n      expect(isDropdownOpen(container)).toBe(true);\n    });\n\n    test(\"re-enables focusing the selected country\", async () => {\n      await user.keyboard(\"{Tab}\");\n      const selectedCountryButton = getSelectedCountryButton(container);\n      expect(selectedCountryButton).toHaveFocus();\n    });\n  });\n});"
  },
  {
    "path": "tests/integration/methods/setNumber.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, checkFlagSelected, intlTelInput } = require(\"../helpers/helpers\");\n\ndescribe(\"setNumber method\", () => {\n  describe(\"no utils\", () => {\n    let iti, input, container, utilsBackup;\n\n    beforeEach(() => {\n      ({ iti, input, container } = initPlugin());\n      utilsBackup = intlTelInput.utils;\n      intlTelInput.utils = null;\n    });\n\n    afterEach(() => {\n      teardown(iti);\n      // replace intlTelInput.utils after setting it to null in one of the tests\n      intlTelInput.utils = utilsBackup;\n    });\n\n    test(\"sets raw value\", () => {\n      iti.setNumber(\"+447733123456\");\n      expect(input.value).toEqual(\"+447733123456\");\n    });\n\n    test(\"updates flag\", () => {\n      iti.setNumber(\"+447733123456\");\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n    });\n  });\n\n  describe(\"with utils, nationalMode true\", () => {\n    let iti, input;\n    const options = { nationalMode: true };\n\n    beforeEach(() => {\n      ({ iti, input } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"formats to national\", () => {\n      iti.setNumber(\"+447733123456\");\n      expect(input.value).toEqual(\"07733 123456\");\n    });\n  });\n\n  describe(\"with utils, nationalMode false\", () => {\n    let iti, input;\n    const options = { nationalMode: false };\n\n    beforeEach(() => {\n      ({ iti, input } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"formats to intl\", () => {\n      iti.setNumber(\"+447733123456\");\n      expect(input.value).toEqual(\"+44 7733 123456\");\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/methods/setPlaceholderNumberType.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown } = require(\"../helpers/helpers\");\n\n\ndescribe(\"setPlaceholderNumberType method\", () => {\n  let iti, input;\n\n  beforeEach(() => {\n    const options = { initialCountry: \"gb\" };\n    ({ iti, input } = initPlugin({ options }));\n  });\n  afterEach(() => teardown(iti));\n\n  test(\"sets placeholder type to fixed line\", async () => {\n    iti.setPlaceholderNumberType(\"FIXED_LINE\");\n    expect(input.getAttribute(\"placeholder\")).toEqual(\"0121 234 5678\");\n  });\n\n  test(\"sets placeholder type to mobile\", async () => {\n    iti.setPlaceholderNumberType(\"MOBILE\");\n    expect(input.getAttribute(\"placeholder\")).toEqual(\"07400 123456\");\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/allowDropdown.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst {\n  initPlugin,\n  teardown,\n  getCountryListElement,\n  checkFlagSelected,\n  getArrowElement,\n  clickSelectedCountryAsync,\n  isDropdownOpen,\n  getDropdownElement,\n  getSelectedCountryButton,\n} = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\ndescribe(\"allowDropdown option\", () => {\n  describe(\"allowDropdown=false\", () => {\n    let iti, container, input, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { allowDropdown: false };\n      ({ iti, container, input } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"hides arrow\", async () => {\n      expect(getArrowElement(container)).toBeFalsy();\n    });\n\n    test(\"hides country list\", async () => {\n      expect(getCountryListElement(container)).toBeFalsy();\n    });\n\n    test(\"does not add role=combobox\", async () => {\n      expect(getSelectedCountryButton(container).getAttribute(\"role\")).toBeNull();\n    });\n\n    test(\"clicking selected flag does not show dropdown\", async () => {\n      await clickSelectedCountryAsync(container, user);\n      expect(getDropdownElement(container)).toBeFalsy();\n    });\n\n    test(\"still updates flag when typing\", async () => {\n      expect(checkFlagSelected(container, \"\")).toBe(true);\n      await user.type(input, \"+44\");\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n    });\n  });\n\n  describe(\"allowDropdown=true\", () => {\n    let iti, container, input, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { allowDropdown: true };\n      ({ iti, container, input } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"shows arrow\", async () => {\n      expect(getArrowElement(container)).toBeTruthy();\n    });\n\n    test(\"shows country list\", async () => {\n      expect(getCountryListElement(container)).toBeTruthy();\n    });\n\n    test(\"adds aria-haspopup=dialog\", async () => {\n      expect(getSelectedCountryButton(container).getAttribute(\"aria-haspopup\")).toBe(\"dialog\");\n    });\n\n    test(\"updates flag when typing\", async () => {\n      expect(checkFlagSelected(container, \"\")).toBe(true);\n      await user.type(input, \"+44\");\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n    });\n\n    test(\"clicking selected flag shows dropdown\", async () => {\n      await clickSelectedCountryAsync(container, user);\n      expect(isDropdownOpen(container)).toBeTruthy();\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/allowNumberExtensions.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\ndescribe(\"allowNumberExtensions option\", () => {\n  const base = \"+17024181234\";\n  const ext = \"98765\";\n  const withExt = `${base} ext. ${ext}`;\n\n  describe(\"allowNumberExtensions=false\", () => {\n    let iti, input, user;\n\n    beforeEach(() => {\n      ({ iti, input } = initPlugin({ options: { allowNumberExtensions: false } }));\n      user = userEvent.setup();\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"valid number with extension fails validation\", async () => {\n      await user.type(input, withExt);\n      expect(iti.isValidNumber()).toBe(false);\n      expect(iti.isValidNumberPrecise()).toBe(false);\n    });\n  });\n\n  describe(\"allowNumberExtensions=true\", () => {\n    let iti, input, user;\n\n    beforeEach(() => {\n      ({ iti, input } = initPlugin({ options: { allowNumberExtensions: true } }));\n      user = userEvent.setup();\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"valid number with extension passes validation\", async () => {\n      await user.type(input, withExt);\n      expect(iti.isValidNumber()).toBe(true);\n      expect(iti.isValidNumberPrecise()).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/allowPhonewords.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\n\ndescribe(\"allowPhonewords option\", () => {\n  describe(\"allowPhonewords=false\", () => {\n    let iti, input, user;\n\n    beforeEach(() => {\n      ({ iti, input } = initPlugin({ options: { allowPhonewords: false } }));\n      user = userEvent.setup();\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"even a valid phoneword number fails (FLOWERS -> 3569377)\", async () => {\n      await user.type(input, \"+1 702 FLOWERS\");\n      expect(iti.isValidNumber()).toBe(false);\n      expect(iti.isValidNumberPrecise()).toBe(false);\n    });\n  });\n\n  describe(\"allowPhonewords=true\", () => {\n    let iti, input, user;\n\n    beforeEach(() => {\n      ({ iti, input } = initPlugin({ options: { allowPhonewords: true } }));\n      user = userEvent.setup();\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"valid phoneword number passes (FLOWERS -> 3569377)\", async () => {\n      await user.type(input, \"+1 702 FLOWERS\");\n      expect(iti.isValidNumber()).toBe(true);\n      expect(iti.isValidNumberPrecise()).toBe(true);\n    });\n\n    test(\"invalid phoneword fails (WRONGWORD)\", async () => {\n      await user.type(input, \"+1 702 WRONGWORD\");\n      expect(iti.isValidNumber()).toBe(false);\n      expect(iti.isValidNumberPrecise()).toBe(false);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/allowedNumberTypes.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\ndescribe(\"allowedNumberTypes option\", () => {\n  describe(\"default MOBILE/FIXED_LINE only\", () => {\n    let iti, input, user;\n\n    beforeEach(() => {\n      ({ iti, input } = initPlugin({ options: { initialCountry: \"gb\" } }));\n      user = userEvent.setup();\n    });\n    afterEach(() => teardown(iti));\n\n    test(\"GB mobile valid\", async () => {\n      await user.type(input, \"+447400123456\");\n      expect(iti.isValidNumberPrecise()).toBe(true);\n    });\n\n    test(\"GB landline valid\", async () => {\n      await user.type(input, \"+441512345678\");\n      expect(iti.isValidNumberPrecise()).toBe(true);\n    });\n\n    test(\"GB premium rate number invalid\", async () => {\n      await user.type(input, \"+448701234567\");\n      expect(iti.isValidNumberPrecise()).toBe(false);\n    });\n  });\n\n  describe(\"allow PREMIUM_RATE only\", () => {\n    let iti, input, user;\n\n    beforeEach(() => {\n      const options = {\n        initialCountry: \"gb\",\n        allowedNumberTypes: [\"PREMIUM_RATE\"],\n      };\n      ({ iti, input } = initPlugin({ options }));\n      user = userEvent.setup();\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"GB mobile invalid\", async () => {\n      await user.type(input, \"+447400123456\");\n      expect(iti.isValidNumberPrecise()).toBe(false);\n    });\n\n    test(\"GB premium rate number valid\", async () => {\n      await user.type(input, \"+448701234567\");\n      expect(iti.isValidNumberPrecise()).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/autoPlaceholder.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst { initPlugin, teardown, openDropdownSelectCountryAsync } = require(\"../helpers/helpers\");\n\ndescribe(\"autoPlaceholder option (empty initial placeholder)\", () => {\n  describe(\"autoPlaceholder=off\", () => {\n    let iti, input;\n\n    beforeEach(() => {\n      const options = { autoPlaceholder: \"off\", initialCountry: \"af\" };\n      ({ iti, input } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"leaves empty\", async () => {\n      expect(input.getAttribute(\"placeholder\")).toBeNull();\n    });\n  });\n\n  describe(\"autoPlaceholder=polite, nationalMode=true\", () => {\n    let iti, input, container, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { autoPlaceholder: \"polite\", nationalMode: true, initialCountry: \"af\" };\n      ({ iti, input, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"sets and updates placeholder\", async () => {\n      expect(input.getAttribute(\"placeholder\")).toEqual(\"070 123 4567\");\n      await openDropdownSelectCountryAsync(container, \"gb\", user);\n      expect(input.getAttribute(\"placeholder\")).toEqual(\"07400 123456\");\n    });\n  });\n\n  describe(\"autoPlaceholder=polite, nationalMode=false\", () => {\n    let iti, input, container, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { autoPlaceholder: \"polite\", nationalMode: false, initialCountry: \"af\" };\n      ({ iti, input, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"sets and updates placeholder\", async () => {\n      expect(input.getAttribute(\"placeholder\")).toEqual(\"+93 70 123 4567\");\n      await openDropdownSelectCountryAsync(container, \"gb\", user);\n      expect(input.getAttribute(\"placeholder\")).toEqual(\"+44 7400 123456\");\n    });\n  });\n});\n\ndescribe(\"autoPlaceholder option (existing placeholder)\", () => {\n  describe(\"autoPlaceholder=off\", () => {\n    let iti, input;\n\n    beforeEach(() => {\n      const options = { autoPlaceholder: \"off\", initialCountry: \"af\" };\n      ({ iti, input } = initPlugin({ inputValue: \"\", options }));\n      input.setAttribute(\"placeholder\", \"lol\");\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"placeholder unchanged\", async () => {\n      expect(input.getAttribute(\"placeholder\")).toEqual(\"lol\");\n    });\n  });\n\n  describe(\"autoPlaceholder=polite\", () => {\n    let iti, input;\n\n    beforeEach(() => {\n      const options = { autoPlaceholder: \"polite\", initialCountry: \"af\" };\n      ({ iti, input } = initPlugin({ options }));\n      input.setAttribute(\"placeholder\", \"lol\");\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"placeholder unchanged\", async () => {\n      expect(input.getAttribute(\"placeholder\")).toEqual(\"lol\");\n    });\n  });\n\n  describe(\"autoPlaceholder=aggressive\", () => {\n    let iti, input;\n\n    beforeEach(() => {\n      const options = { autoPlaceholder: \"aggressive\", initialCountry: \"af\" };\n      // need an existing placeholder BEFORE init so aggressive mode can overwrite it\n      input = document.createElement(\"input\");\n      input.setAttribute(\"placeholder\", \"lol\");\n      document.body.appendChild(input);\n      ({ iti, input } = initPlugin({ options, input }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"placeholder replaced\", async () => {\n      expect(input.getAttribute(\"placeholder\")).toEqual(\"070 123 4567\");\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/containerClass.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, clickSelectedCountryAsync } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\ndescribe(\"containerClass option\", () => {\n  describe(\"basic\", () => {\n    let iti, container;\n\n    beforeEach(() => {\n      const options = { containerClass: \"cpc\" };\n      ({ iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"adds class\", () => {\n      expect(container.classList).toContain(\"cpc\");\n    });\n  });\n\n  describe(\"with fullscreen popup\", () => {\n    let iti, container, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { containerClass: \"cpc\", useFullscreenPopup: true };\n      ({ iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"adds class to popup container\", async () => {\n      await clickSelectedCountryAsync(container, user);\n      const root = container.ownerDocument; // access the root document without using a global\n      expect(root.querySelector(\".iti--container\").classList).toContain(\"cpc\");\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/countryNameLocale.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, getCountriesInList, clickSelectedCountryAsync, getSearchInput } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\ndescribe(\"countryNameLocale = fr\", () => {\n  let iti, container, user;\n  const options = {\n    countryNameLocale: \"fr\",\n  };\n\n  beforeEach(() => {\n    ({ iti, container } = initPlugin({ options }));\n    user = userEvent.setup();\n  });\n\n  afterEach(() => teardown(iti));\n\n  test(\"uses Intl.DisplayNames to generate country names in French\", () => {\n    const usNameEl = container.querySelector(\n      \"li[data-country-code='us'] .iti__country-name\",\n    );\n    expect(usNameEl).toBeTruthy();\n    // In French, US is \"États-Unis\".\n    expect(usNameEl.textContent).toBe(\"États-Unis (+1)\");\n  });\n\n  test(\"country search matches without accents\", async () => {\n    // Open dropdown so the search input is interactive.\n    await clickSelectedCountryAsync(container, user);\n    const searchInput = getSearchInput(container);\n\n    // In French, US is typically \"États-Unis\"; searching without accents should still match.\n    await user.type(searchInput, \"etats\");\n    const visibleIso2s = getCountriesInList(container);\n    expect(visibleIso2s).toContain(\"us\");\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/countryOrder.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, getCountriesInList } = require(\"../helpers/helpers\");\n\n// countryOrder: ensure specified order is applied before default name sort.\ndescribe(\"countryOrder option\", () => {\n  let iti, container;\n\n  beforeEach(() => {\n    const options = { onlyCountries: [\"us\", \"gb\", \"ca\", \"fr\"], countryOrder: [\"fr\", \"ca\"] };\n    ({ iti, container } = initPlugin({ options }));\n  });\n\n  afterEach(() => teardown(iti));\n\n  test(\"puts ordered countries first and preserves relative order\", () => {\n    const list = getCountriesInList(container);\n    expect(list.length).toBe(4);\n    // first two should be fr then ca per countryOrder, rest keep default alpha (gb, us or us, gb depending on locale)\n    expect(list[0]).toBe(\"fr\");\n    expect(list[1]).toBe(\"ca\");\n    expect(list[2]).toBe(\"gb\");\n    expect(list[3]).toBe(\"us\");\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/countrySearch.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst {\n  initPlugin,\n  teardown,\n  getCountryListLength,\n  totalCountries,\n  getSearchInput,\n  clickSelectedCountryAsync,\n  getCountriesInList,\n} = require(\"../helpers/helpers\");\nconst allCountries = require(\"../../../build/js/data\");\nconst allCountryCodes = allCountries.map((country) => country.iso2);\n\n//* Without the advanceTimers bit, the (time related) tests just hang, see https://github.com/jestjs/jest/issues/12056#issuecomment-1090189268\njest.useFakeTimers({advanceTimers: true});\n\n\ndescribe(\"countrySearch option\", () => {\n  let searchInput, iti, container, user;\n\n  beforeEach(async () => {\n    user = userEvent.setup();\n    ({ container, iti } = initPlugin());\n    searchInput = getSearchInput(container);\n    await clickSelectedCountryAsync(container, user);\n  });\n\n  afterEach(() => {\n    searchInput = null;\n    teardown(iti);\n  });\n\n  test(\"generates the search input\", () => {\n    expect(searchInput).toBeTruthy();\n  });\n\n  test(\"shows all countries to start with\", () => {\n    const length = getCountryListLength(container);\n    expect(length).toBe(totalCountries);\n  });\n\n  test(\"typing iso2 code, 'gb' filters countries correctly\", async () => {\n    await user.type(searchInput, \"gb\");\n    //* Allow for the (intentional) 100ms delay on the search handler.\n    jest.advanceTimersByTime(100);\n\n    const countriesInList = getCountriesInList(container);\n    expect(countriesInList.length).toBe(2);\n    expect(countriesInList[0]).toBe(\"gb\");\n    expect(countriesInList[1]).toBe(\"gw\");\n  });\n\n  test(\"typing start of country name, 'uk', filters countries correctly\", async () => {\n    await user.type(searchInput, \"uk\");\n    //* Allow for the (intentional) 100ms delay on the search handler.\n    jest.advanceTimersByTime(100);\n\n    // Ukraine comes first because name-starts-with takes priority over initials\n    const countriesInList = getCountriesInList(container);\n    expect(countriesInList.length).toBe(2);\n    expect(countriesInList[0]).toBe(\"ua\");\n    expect(countriesInList[1]).toBe(\"gb\");\n  });\n\n  test(\"typing country initials, 'us', filters countries correctly\", async () => {\n    await user.type(searchInput, \"us\");\n    //* Allow for the (intentional) 100ms delay on the search handler.\n    jest.advanceTimersByTime(100);\n\n    const countriesInList = getCountriesInList(container);\n    expect(countriesInList.length).toBe(8);\n    expect(countriesInList[0]).toBe(\"us\");\n    expect(countriesInList[1]).toBe(\"au\");\n  });\n\n  test(\"typing 'sk', filters countries correctly\", async () => {\n    await user.type(searchInput, \"sk\");\n    //* Allow for the (intentional) 100ms delay on the search handler.\n    jest.advanceTimersByTime(100);\n\n    const countriesInList = getCountriesInList(container);\n    expect(countriesInList.length).toBe(3);\n    expect(countriesInList[0]).toBe(\"sk\"); // Slovakia first to iso2 match\n    expect(countriesInList[1]).toBe(\"kr\"); // South Korea second due to initials\n  });\n\n  test(\"typing 'bar', filters countries correctly\", async () => {\n    await user.type(searchInput, \"bar\");\n    //* Allow for the (intentional) 100ms delay on the search handler.\n    jest.advanceTimersByTime(100);\n\n    const countriesInList = getCountriesInList(container);\n    expect(countriesInList.length).toBe(4);\n    expect(countriesInList[0]).toBe(\"bb\"); // Barbados first as name-starts-with\n    expect(countriesInList[1]).toBe(\"bl\"); // St. Barthélemy second as name-contains\n  });\n\n  test(\"typing '44', filters countries correctly\", async () => {\n    await user.type(searchInput, \"44\");\n    //* Allow for the (intentional) 100ms delay on the search handler.\n    jest.advanceTimersByTime(100);\n\n    const countriesInList = getCountriesInList(container);\n    expect(countriesInList.length).toBe(5);\n    expect(countriesInList[0]).toBe(\"gb\"); // UK (dial code matches, and highest priority)\n    expect(countriesInList[4]).toBe(\"ao\"); // Angola +244 (dial code contains)\n  });\n\n  test(\"typing '+44', filters countries correctly\", async () => {\n    await user.type(searchInput, \"+44\");\n    //* Allow for the (intentional) 100ms delay on the search handler.\n    jest.advanceTimersByTime(100);\n\n    const countriesInList = getCountriesInList(container);\n    expect(countriesInList.length).toBe(4);\n    expect(countriesInList[0]).toBe(\"gb\"); // UK (dial code matches, and highest priority)\n  });\n\n  // this was a bug\n  test(\"typing 'c', then deleting it, displays original country list correctly\", async () => {\n    const countriesInListAtStart = getCountriesInList(container);\n    // double check the country list at the start matches the list in data.ts\n    expect(countriesInListAtStart).toEqual(allCountryCodes);\n\n    await user.type(searchInput, \"c\");\n    //* Allow for the (intentional) 100ms delay on the search handler.\n    jest.advanceTimersByTime(100);\n    // delete the 'c'\n    await user.type(searchInput, \"{backspace}\");\n    jest.advanceTimersByTime(100);\n\n    // check we are back to the starting list\n    const countriesInListAtEnd = getCountriesInList(container);\n    expect(countriesInListAtEnd).toEqual(countriesInListAtStart);\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/customPlaceholder.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst { initPlugin, teardown, openDropdownSelectCountryAsync } = require(\"../helpers/helpers\");\n\ndescribe(\"customPlaceholder option\", () => {\n  let iti, input, container, user;\n  const options = {\n    autoPlaceholder: \"polite\",\n    initialCountry: \"af\",\n    customPlaceholder: (p) => `e.g. ${p}`,\n  };\n\n  beforeEach(() => {\n    user = userEvent.setup();\n    ({ iti, input, container } = initPlugin({ options }));\n  });\n\n  afterEach(() => teardown(iti));\n\n  test(\"customises placeholder and updates on country change\", async () => {\n    expect(input.getAttribute(\"placeholder\")).toEqual(\"e.g. 070 123 4567\");\n    await openDropdownSelectCountryAsync(container, \"gb\", user);\n    expect(input.getAttribute(\"placeholder\")).toEqual(\"e.g. 07400 123456\");\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/dropdownContainer.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst {\n  initPlugin,\n  teardown,\n  clickSelectedCountryAsync,\n  getCountryListElement,\n  isDropdownOpen,\n  selectCountryAsync,\n} = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\ndescribe(\"dropdownContainer option\", () => {\n  describe(\"dropdownContainer=null\", () => {\n    let iti, container, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { dropdownContainer: null };\n      ({ iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"places list inline, next to input\", () => {\n      expect(getCountryListElement(container)).toBeTruthy();\n    });\n\n    describe(\"clicking selected flag\", () => {\n      beforeEach(async () => {\n        await clickSelectedCountryAsync(container, user);\n      });\n\n      test(\"opens dropdown\", async () => {\n        expect(isDropdownOpen(container)).toBeTruthy();\n      });\n\n      test(\"selecting a country closes dropdown\", async () => {\n        await selectCountryAsync(container, \"gb\", user);\n        expect(isDropdownOpen(container)).toBeFalsy();\n      });\n    });\n  });\n\n  describe(\"dropdownContainer=document.body\", () => {\n    let iti, container, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { dropdownContainer: document.body };\n      ({ iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"injects on open and removes on select\", async () => {\n      const root = container.ownerDocument; // access the root document without using a global\n      expect(getCountryListElement(root)).toBeFalsy();\n      await clickSelectedCountryAsync(container, user);\n      expect(getCountryListElement(root)).toBeTruthy(); // it's in the document\n      expect(getCountryListElement(container)).toBeFalsy(); // but it's not next to the input\n      await selectCountryAsync(container, \"gb\", user);\n      expect(getCountryListElement(root)).toBeFalsy();\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/excludeCountries.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst { initPlugin, teardown, getCountriesInList, totalCountries, checkFlagSelected } = require(\"../helpers/helpers\");\n\ndescribe(\"excludeCountries option\", () => {\n  describe(\"none excluded\", () => {\n    let iti, container, user, input;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { excludeCountries: [] };\n      ({ iti, container, input } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"has all countries, inc US and CA\", () => {\n      const countriesInList = getCountriesInList(container);\n      expect(countriesInList.length).toBe(totalCountries);\n      expect(countriesInList.includes(\"us\")).toBe(true);\n      expect(countriesInList.includes(\"ca\")).toBe(true);\n    });\n\n    test(\"typing us dial code selects US\", async () => {\n      await user.type(input, \"+1\");\n      expect(checkFlagSelected(container, \"us\")).toBe(true);\n    });\n  });\n\n  describe(\"exclude us + ca\", () => {\n    let iti, container, user, input;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { excludeCountries: [\"us\", \"ca\"] };\n      ({ iti, container, input } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"list excludes those countries\", () => {\n      const countriesInList = getCountriesInList(container);\n      expect(countriesInList.length).toBe(totalCountries - 2);\n      expect(countriesInList.includes(\"us\")).toBe(false);\n      expect(countriesInList.includes(\"ca\")).toBe(false);\n    });\n\n    test(\"typing us dial code selects Dominican Republic\", async () => {\n      await user.type(input, \"+1\");\n      expect(checkFlagSelected(container, \"do\")).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/fixDropdownWidth.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, getDropdownElement } = require(\"../helpers/helpers\");\n\n// fixDropdownWidth: when true, dropdown should not have flexible width class; when false it should.\ndescribe(\"fixDropdownWidth option\", () => {\n  describe(\"default true\", () => {\n    let iti, container;\n\n    beforeEach(() => {\n      ({ iti, container } = initPlugin());\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"no flexible width class\", async () => {\n      const dropdown = getDropdownElement(container);\n      expect(dropdown.classList).not.toContain(\"iti--flexible-dropdown-width\");\n    });\n  });\n\n  describe(\"explicit false\", () => {\n    let iti, container;\n\n    beforeEach(() => {\n      const options = { fixDropdownWidth: false };\n      ({ iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"adds flexible width class\", async () => {\n      const dropdown = getDropdownElement(container);\n      expect(dropdown.classList).toContain(\"iti--flexible-dropdown-width\");\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/formatAsYouType.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\n// formatAsYouType: true (default) should format while typing, false should not.\ndescribe(\"formatAsYouType option\", () => {\n  describe(\"default true\", () => {\n    let iti, input, user;\n\n    beforeEach(() => {\n      const options = { formatAsYouType: true, initialCountry: \"gb\" };\n      ({ iti, input } = initPlugin({ options }));\n      user = userEvent.setup();\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"formats UK mobile as typed\", async () => {\n      await user.type(input, \"07400123456\");\n      expect(input.value).toEqual(\"07400 123456\");\n    });\n  });\n\n  describe(\"disabled\", () => {\n    let iti, input, user;\n\n    beforeEach(() => {\n      const options = { formatAsYouType: false, initialCountry: \"gb\" };\n      ({ iti, input } = initPlugin({ options }));\n      user = userEvent.setup();\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"does not format while typing\", async () => {\n      await user.type(input, \"07400123456\");\n      expect(input.value).toEqual(\"07400123456\");\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/formatOnDisplay.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown } = require(\"../helpers/helpers\");\n\nconst us = \"+17024181234\";\n\ndescribe(\"formatOnDisplay option\", () => {\n  describe(\"formatOnDisplay=false, nationalMode=true, input value set to valid US intl number\", () => {\n    let iti, input;\n\n    beforeEach(() => {\n      const options = { formatOnDisplay: false, nationalMode: true };\n      ({ iti, input } = initPlugin({ inputValue: us, options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"leaves unformatted, inc with setNumber\", async () => {\n      expect(input.value).toEqual(us);\n      iti.setNumber(\"+14154181234\");\n      expect(input.value).toEqual(\"+14154181234\");\n    });\n  });\n\n  describe(\"formatOnDisplay=true, nationalMode=true, input value set to valid US intl number\", () => {\n    let iti, input;\n\n    beforeEach(() => {\n      const options = { formatOnDisplay: true, nationalMode: true };\n      ({ iti, input } = initPlugin({ inputValue: us, options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"formats to national, inc with setNumber\", async () => {\n      expect(input.value).toEqual(\"(702) 418-1234\");\n      iti.setNumber(\"+14154181234\");\n      expect(input.value).toEqual(\"(415) 418-1234\");\n    });\n  });\n\n  describe(\"formatOnDisplay=true, nationalMode=false, input value set to valid US intl number\", () => {\n    let iti, input;\n\n    beforeEach(() => {\n      const options = { formatOnDisplay: true, nationalMode: false };\n      ({ iti, input } = initPlugin({ inputValue: us, options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"formats to intl, inc with setNumber\", async () => {\n      expect(input.value).toEqual(\"+1 702-418-1234\");\n      iti.setNumber(\"+14154181234\");\n      expect(input.value).toEqual(\"+1 415-418-1234\");\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/geoIpLookup.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, oneTickAsync, intlTelInput } = require(\"../helpers/helpers\");\n\ndescribe(\"geoIpLookup option\", () => {\n  describe(\"vanilla\", () => {\n    let iti, resolved;\n\n    beforeEach(async () => {\n      resolved = false;\n      ({ iti } = initPlugin());\n      iti.promise.then(() => {\n        resolved = true;\n      });\n      await oneTickAsync(); // allow one tick for promise to resolve\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"iti.promise resolves immediately\", async () => {\n      expect(intlTelInput.startedLoadingAutoCountry).toBe(false);\n      expect(resolved).toEqual(true);\n    });\n  });\n\n  describe(\"geoIpLookup and initialCountry=auto\", () => {\n    let iti, resolved;\n\n    beforeEach(async () => {\n      resolved = false;\n      const options = {\n        initialCountry: \"auto\",\n        geoIpLookup: (cb) => setTimeout(() => cb(\"gb\"), 10),\n      };\n      ({ iti } = initPlugin({ options }));\n      iti.promise.then(() => {\n        resolved = true;\n      });\n      await oneTickAsync(); // allow one tick, as before\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"iti.promise does not resolve until geoIpLookup complete\", async () => {\n      expect(intlTelInput.startedLoadingAutoCountry).toBe(true);\n      expect(resolved).toBe(false);\n      await iti.promise;\n      expect(resolved).toBe(true);\n    });\n\n    describe(\"init a 2nd instance with geoIpLookup\", () => {\n      let iti2, resolved2;\n\n      beforeEach(async () => {\n        resolved2 = false;\n        await iti.promise; // allow the first instance to resolve before initialising the second\n        const options = {\n          initialCountry: \"auto\",\n          geoIpLookup: (cb) => setTimeout(() => cb(\"gb\"), 10),\n        };\n        ({ iti: iti2 } = initPlugin({ options }));\n        iti2.promise.then(() => {\n          resolved2 = true;\n        });\n        await oneTickAsync(); // allow one tick, as before\n      });\n\n      afterEach(() => teardown(iti2));\n\n      test(\"second instance resolves immediately\", async () => {\n        expect(resolved2).toBe(true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/hiddenInput.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown } = require(\"../helpers/helpers\");\n\ndescribe(\"hiddenInput option\", () => {\n  describe(\"valid names\", () => {\n    let iti, container;\n\n    beforeEach(() => {\n      const options = {\n        hiddenInput: () => ({\n          phone: \"phone_full\",\n          country: \"phone_country\",\n        }),\n      };\n      ({ iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"creates two hidden inputs with correct names\", () => {\n      const hidden = container.querySelectorAll(\"input[type='hidden']\");\n      expect(hidden.length).toBe(2);\n      expect(hidden[0].name).toBe(\"phone_full\");\n      expect(hidden[1].name).toBe(\"phone_country\");\n    });\n  });\n\n  describe(\"invalid names\", () => {\n    let iti, container;\n\n    beforeEach(() => {\n      const options = {\n        hiddenInput: () => ({\n          test: \"t\",\n          data: \"d\",\n        }),\n      };\n      ({ iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"creates none\", () => {\n      const hidden = container.querySelectorAll(\"input[type='hidden']\");\n      expect(hidden.length).toBe(0);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/i18n-locales.test.js",
    "content": "/**\n * Locale bundle integrity checks.\n *\n * Verifies that each locale file in src/js/intl-tel-input/i18n/*\n * contains exactly the expected UI translation keys.\n */\n\n// Keep in sync with src/js/intl-tel-input/i18n/types.ts\nconst UI_TRANSLATION_KEYS = [\n  \"selectedCountryAriaLabel\",\n  \"searchPlaceholder\",\n  \"clearSearchAriaLabel\",\n  \"countryListAriaLabel\",\n  \"noCountrySelected\",\n  \"searchEmptyState\",\n  \"searchSummaryAria\",\n];\n\nconst localeModules = require(\"../../../src/js/intl-tel-input/i18n/index.ts\");\n\ndescribe(\"i18n locale bundles\", () => {\n  test(\"each locale exports all and only the expected UI translation keys\", () => {\n    const expectedKeys = [...UI_TRANSLATION_KEYS].sort();\n    const locales = Object.keys(localeModules)\n      .filter((key) => key !== \"__esModule\")\n      .sort();\n\n    console.info(`Testing ${locales.length} locale bundles for expected translation keys...`);\n    for (const locale of locales) {\n      const translations = localeModules[locale];\n      const actualKeys = Object.keys(translations).sort();\n\n      try {\n        expect(actualKeys).toEqual(expectedKeys);\n      } catch (error) {\n        error.message = `Locale ${locale} has unexpected keys.\\n` + error.message;\n        throw error;\n      }\n    }\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/i18n.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, clickSelectedCountryAsync, getSearchInput } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\n// i18n: custom strings should override defaults (placeholder text, labels, and a country name override)\ndescribe(\"i18n option\", () => {\n  let iti, container, user;\n  const options = {\n    i18n: {\n      searchPlaceholder: \"Chercher pays\",\n      clearSearchAriaLabel: \"Effacer la recherche\",\n      searchEmptyState: \"Aucun résultat\",\n      searchSummaryAria: (count) => `Résumé: ${count}`,\n      fr: \"République Française\",\n    },\n  };\n\n  beforeEach(() => {\n    ({ iti, container } = initPlugin({ options }));\n    user = userEvent.setup();\n  });\n  afterEach(() => teardown(iti));\n\n  test(\"overrides country name\", () => {\n    const frItem = container.querySelector(\"li[data-country-code='fr'] .iti__country-name\");\n    expect(frItem.textContent).toContain(\"République Française\");\n  });\n\n  test(\"search input placeholder overridden\", () => {\n    const searchInput = getSearchInput(container);\n    expect(searchInput.getAttribute(\"placeholder\")).toBe(\"Chercher pays\");\n  });\n\n  test(\"clear search button aria-label overridden\", () => {\n    const clearButton = container.querySelector(\".iti__search-clear\");\n    expect(clearButton.getAttribute(\"aria-label\")).toBe(\"Effacer la recherche\");\n  });\n\n  test(\"no-results message and a11y summary use i18n strings\", async () => {\n    // open dropdown and type a nonsense search to get no results\n    await clickSelectedCountryAsync(container, user);\n    const searchInput = getSearchInput(container);\n    await user.type(searchInput, \"zzzzzz\");\n\n    // Filtering is debounced in the implementation.\n    await new Promise((resolve) => setTimeout(resolve, 150));\n\n    const noResults = container.querySelector(\".iti__no-results\");\n    expect(noResults).toHaveTextContent(\"Aucun résultat\");\n    expect(noResults.classList.contains(\"iti__hide\")).toBe(false);\n\n    const a11yText = container.querySelector(\".iti__a11y-text\");\n    expect(a11yText).toHaveTextContent(\"Résumé: 0\");\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/initialCountry.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, checkFlagSelected } = require(\"../helpers/helpers\");\n\ndescribe(\"initialCountry option\", () => {\n  describe(\"jp\", () => {\n    let iti, container;\n\n    beforeEach(() => {\n      const options = { initialCountry: \"jp\" };\n      ({ iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"jp selected\", () => {\n      checkFlagSelected(container, \"jp\");\n    });\n  });\n\n  describe(\"ca\", () => {\n    let iti, container;\n\n    beforeEach(() => {\n      const options = { initialCountry: \"ca\" };\n      ({ iti, container } = initPlugin({ inputValue: \"+1 800 123 1234\", options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"ca still selected even when regionless NANP number in input\", () => {\n      checkFlagSelected(container, \"ca\");\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/loadUtils.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst intlTelInput = require(\"intl-tel-input\");\nconst { initPlugin, resetPackageAfterEach } = require(\"../helpers/helpers\");\n\ndescribe(\"loadUtils option\", () => {\n  resetPackageAfterEach(intlTelInput);\n\n  const loadUtils = () => import(\"intl-tel-input/utils\");\n\n  test(\"does not load the utils script if `loadUtils` option is not set\", async () => {\n    const { iti } = initPlugin({ intlTelInput });\n\n    expect(intlTelInput).toHaveProperty(\"startedLoadingUtilsScript\", false);\n\n    await iti.promise;\n\n    expect(intlTelInput).toHaveProperty(\"startedLoadingUtilsScript\", false);\n  });\n\n  test(\"loads the utils script successfully\", async () => {\n    expect(intlTelInput).not.toHaveProperty(\"utils.isValidNumber\");\n\n    const { iti } = initPlugin({\n      intlTelInput,\n      options: { loadUtils },\n    });\n\n    await iti.promise;\n    expect(intlTelInput).toHaveProperty(\"utils.isValidNumber\");\n  });\n\n  test(\"waits until the page is loaded before loading utils\", async () => {\n    jest.spyOn(intlTelInput, \"documentReady\").mockReturnValue(false);\n\n    const { iti } = initPlugin({\n      intlTelInput,\n      options: { loadUtils },\n    });\n\n    expect(intlTelInput).toHaveProperty(\"startedLoadingUtilsScript\", false);\n\n    const loadEvent = new Event(\"load\");\n    window.dispatchEvent(loadEvent);\n\n    expect(intlTelInput).toHaveProperty(\"startedLoadingUtilsScript\", true);\n\n    await iti.promise;\n  });\n\n  test(\"loads utils immediately if page is already finished loading\", async function() {\n    jest.spyOn(intlTelInput, \"documentReady\").mockReturnValue(true);\n\n    const { iti } = initPlugin({\n      intlTelInput,\n      options: { loadUtils },\n    });\n\n    expect(intlTelInput).toHaveProperty(\"startedLoadingUtilsScript\", true);\n\n    await iti.promise;\n  });\n\n  test(\"rejects with an error if the utils script cannot load\", async function() {\n    jest.spyOn(intlTelInput, \"documentReady\").mockReturnValue(true);\n\n    const { iti } = initPlugin({\n      intlTelInput,\n      options: { loadUtils: () => import(\"/some/incorrect/url\") },\n    });\n\n    expect(intlTelInput).toHaveProperty(\"startedLoadingUtilsScript\", true);\n\n    await expect(iti.promise).rejects.toThrow();\n  });\n\n  test(\"works if loadUtils is a function\", async function() {\n    const mockUtils = { default: { mockUtils: true } };\n    jest.spyOn(intlTelInput, \"documentReady\").mockReturnValue(true);\n\n    const { iti } = initPlugin({\n      intlTelInput,\n      options: {\n        async loadUtils () {\n          return mockUtils;\n        },\n      },\n    });\n\n    expect(intlTelInput).toHaveProperty(\"startedLoadingUtilsScript\", true);\n    await iti.promise;\n\n    expect(intlTelInput.utils).toBe(mockUtils.default);\n  });\n\n  test(\"works if loadUtils returns a custom promise\", async function() {\n    const mockUtils = { default: { mockUtils: true } };\n\n    const { iti } = initPlugin({\n      intlTelInput,\n      options: {\n        loadUtils:  () => ({\n          // eslint-disable-next-line @typescript-eslint/no-unused-vars\n          then: (resolve, reject) => resolve(mockUtils),\n        }),\n      },\n    });\n\n    expect(intlTelInput).toHaveProperty(\"startedLoadingUtilsScript\", true);\n    await iti.promise;\n\n    expect(intlTelInput.utils).toBe(mockUtils.default);\n  });\n\n  describe(\"in 'withUtils' builds\", () => {\n    const intlTelInput = require(\"intl-tel-input/intlTelInputWithUtils\");\n    resetPackageAfterEach(intlTelInput);\n\n    test(\"ignores the `loadUtils` option and does not load\", async () => {\n      const { iti } = initPlugin({\n        intlTelInput,\n        options: { loadUtils },\n      });\n\n      expect(intlTelInput).toHaveProperty(\"startedLoadingUtilsScript\", false);\n\n      await iti.promise;\n\n      expect(intlTelInput).toHaveProperty(\"startedLoadingUtilsScript\", false);\n    });\n  });\n\n});\n"
  },
  {
    "path": "tests/integration/options/nationalMode.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, checkFlagSelected } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\ndescribe(\"nationalMode option\", () => {\n  describe(\"nationalMode=true, empty input\", () => {\n    let iti, input, user, container;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { nationalMode: true };\n      ({ iti, input, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"typing intl dial code still updates selected country\", async () => {\n      expect(checkFlagSelected(container, \"\")).toBe(true);\n      await user.type(input, \"+44\");\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n    });\n  });\n\n  describe(\"nationalMode=true initialCountry=gb\", () => {\n    let iti, input, container, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { nationalMode: true, initialCountry: \"gb\" };\n      ({ iti, input, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"typing Jersey area code updates flag\", async () => {\n      await user.type(input, \"01534\");\n      expect(checkFlagSelected(container, \"je\")).toBe(true);\n    });\n  });\n\n  describe(\"nationalMode=false initialCountry=us\", () => {\n    let iti, input, container, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { nationalMode: false, initialCountry: \"us\" };\n      ({ iti, input, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"typing US intl dial code keeps US selected and then typing CA area code updates flag\", async () => {\n      expect(checkFlagSelected(container, \"us\")).toBe(true);\n      await user.type(input, \"+1\");\n      expect(checkFlagSelected(container, \"us\")).toBe(true);\n      await user.type(input, \"204\");\n      expect(checkFlagSelected(container, \"ca\")).toBe(true);\n    });\n  });\n\n  describe(\"nationalMode=false initialCountry=ax\", () => {\n    let iti, input, container, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { nationalMode: false, initialCountry: \"ax\" };\n      ({ iti, input, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    // it previously changed to finland! (typing +3 gave globe icon, then 58 gave primary country for that dial code which is Finland)\n    test(\"typing intl dial code retains selected country\", async () => {\n      await user.type(input, \"+358\");\n      expect(checkFlagSelected(container, \"ax\")).toBe(true);\n    });\n  });\n\n  describe(\"nationalMode=false initialCountry=gb\", () => {\n    let iti, input, container, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { nationalMode: false, initialCountry: \"gb\" };\n      ({ iti, input, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"typing +44 maintains GB flag selection\", async () => {\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n      await user.type(input, \"+\");\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n      await user.type(input, \"4\");\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n      await user.type(input, \"4\");\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n      await user.type(input, \"{backspace}\");\n      // input now contains +4\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n      await user.type(input, \"{backspace}\");\n      // input now contains +\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n      await user.type(input, \"{backspace}\");\n      // input now empty\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n    });\n\n    // Prev bug: Typing +882 used to show Bangladesh flag (+880) instead of globe icon\n    test(\"typing invalid intl dial code should show globe icon\", async () => {\n      await user.type(input, \"+882\");\n      expect(checkFlagSelected(container, \"\")).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/onlyCountries.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst {\n  initPlugin,\n  teardown,\n  getCountryListLength,\n  checkFlagSelected,\n  getCountriesInList,\n} = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\ndescribe(\"onlyCountries option\", () => {\n  describe(\"restrict to japan, china and korea\", () => {\n    let iti, container;\n    const only = [\"jp\", \"cn\", \"kr\"];\n\n    beforeEach(() => {\n      const options = { onlyCountries: only };\n      ({ iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"list length equals onlyCountries length\", () => {\n      const len = getCountryListLength(container);\n      expect(len).toEqual(only.length);\n    });\n  });\n\n  describe(\"restrict to Afghanistan, Kazakhstan and Russia\", () => {\n    let iti, container, input, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { onlyCountries: [\"af\", \"kz\", \"ru\"] };\n      ({ iti, input, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"typing +7 selects RU over KZ\", async () => {\n      await user.type(input, \"+7\");\n      expect(checkFlagSelected(container, \"ru\")).toBe(true);\n    });\n  });\n\n  describe(\"two instances different onlyCountries\", () => {\n    let iti, container, iti2, container2;\n\n    beforeEach(() => {\n      const options1 = { onlyCountries: [\"jp\"] };\n      const options2 = { onlyCountries: [\"kr\"] };\n      ({ iti, container } = initPlugin({ options: options1 }));\n      ({ iti: iti2, container: container2 } = initPlugin({ options: options2 }));\n    });\n\n    afterEach(() => {\n      teardown(iti);\n      teardown(iti2);\n    });\n\n    test(\"each list has one country\", () => {\n      const countries1 = getCountriesInList(container);\n      const countries2 = getCountriesInList(container2);\n      expect(countries1.length).toEqual(1);\n      expect(countries1).toEqual([\"jp\"]);\n      expect(countries2.length).toEqual(1);\n      expect(countries2).toEqual([\"kr\"]);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/placeholderNumberType.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown } = require(\"../helpers/helpers\");\n\ndescribe(\"placeholderNumberType option\", () => {\n  describe(\"default (MOBILE)\", () => {\n    let iti, input;\n\n    beforeEach(() => {\n      const options = { nationalMode: true, initialCountry: \"gb\" };\n      ({ iti, input } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"mobile placeholder\", async () => {\n      expect(input.getAttribute(\"placeholder\")).toEqual(\"07400 123456\");\n    });\n  });\n\n  describe(\"fixed line\", () => {\n    let iti, input;\n\n    beforeEach(() => {\n      const options = { nationalMode: true, initialCountry: \"gb\", placeholderNumberType: \"FIXED_LINE\" };\n      ({ iti, input } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"fixed line placeholder\", async () => {\n      expect(input.getAttribute(\"placeholder\")).toEqual(\"0121 234 5678\");\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/separateDialCode.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst {\n  initPlugin,\n  teardown,\n  getSelectedDialCodeText,\n  typePlaceholderNumberAsync,\n  checkFlagSelected,\n} = require(\"../helpers/helpers\");\n\n\n//* We test with UK because the ntl number is different to the intl number (aside from the dial code).\ndescribe(\"separateDialCode option\", () => {\n  describe(\"initialCountry=GB\", () => {\n    let input, iti, container;\n\n    beforeEach(() => {\n      const options = {\n        initialCountry: \"gb\",\n        separateDialCode: true,\n      };\n      ({ input, iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => {\n      teardown(iti);\n    });\n\n    test(\"displays the dial code next to the input\", async () => {\n      expect(getSelectedDialCodeText(container)).toBe(\"+44\");\n    });\n\n    test(\"formats the placeholder correctly\", async () => {\n      expect(input.placeholder).toBe(\"7400 123456\");\n    });\n\n    describe(\"calling setNumber to a valid intl number\", () => {\n      const fullNumber = \"+447400123456\";\n\n      beforeEach(() => {\n        iti.setNumber(fullNumber);\n      });\n\n      test(\"formats the number correctly\", async () => {\n        expect(input.value).toBe(\"7400 123456\");\n      });\n\n      test(\"calling getNumber returns the full intl number\", async () => {\n        expect(iti.getNumber()).toBe(fullNumber);\n      });\n    });\n  });\n\n  //* We test with Canada because we had some bugs with area codes.\n  describe(\"initialCountry=CA\", () => {\n    let input, iti;\n\n    beforeEach(() => {\n      const options = {\n        initialCountry: \"ca\",\n        separateDialCode: true,\n      };\n      ({ input, iti } = initPlugin({ options }));\n    });\n\n    afterEach(() => {\n      teardown(iti);\n    });\n\n    test(\"sets the placeholder correctly\", async () => {\n      expect(input.placeholder).toBe(\"506-234-5678\");\n    });\n\n    test(\"calling setNumber will set the number correctly\", async () => {\n      iti.setNumber(\"+15194971234\");\n      expect(input.value).toBe(\"519-497-1234\");\n    });\n  });\n\n  //* We test with America Samoa because we had a bug.\n  describe(\"initialCountry=AS\", () => {\n    let input, iti;\n\n    beforeEach(() => {\n      const options = {\n        initialCountry: \"as\",\n        separateDialCode: true,\n      };\n      ({ input, iti } = initPlugin({ options }));\n    });\n\n    afterEach(() => {\n      teardown(iti);\n    });\n\n    test(\"sets the placeholder correctly\", () => {\n      expect(input.placeholder).toBe(\"684-733-1234\");\n    });\n\n    test(\"calling setNumber will set the number correctly\", () => {\n      iti.setNumber(\"+16847331234\");\n      expect(input.value).toBe(\"684-733-1234\");\n    });\n  });\n\n  //* We test with Russia because we had a bug.\n  describe(\"initialCountry=RU\", () => {\n    let input, iti;\n\n    beforeEach(() => {\n      const options = {\n        initialCountry: \"ru\",\n        separateDialCode: true,\n      };\n      ({ input, iti } = initPlugin({ options, inputValue: \"(922) 555-1234\" }));\n    });\n\n    afterEach(() => {\n      teardown(iti);\n    });\n\n    test(\"formats the number correctly\", () => {\n      //* Used to be '8 (922) 555-12-34'.\n      expect(input.value).toBe(\"922 555-12-34\");\n    });\n  });\n\n  //* We test with Aland Islands because we had a bug.\n  describe(\"initialCountry=AX\", () => {\n    let input, iti, container, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = {\n        initialCountry: \"ax\",\n        separateDialCode: true,\n      };\n      ({ input, iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => {\n      teardown(iti);\n    });\n\n    test(\"when you type the placeholder number it maintains the country selection\", async () => {\n      await typePlaceholderNumberAsync(user, input);\n      // it previously changed to Finland!\n      expect(checkFlagSelected(container, \"ax\")).toBe(true);\n    });\n  });\n\n  //* We test with Kazakhstan because we had a bug.\n  describe(\"initialCountry=KZ\", () => {\n    let input, iti, container, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = {\n        initialCountry: \"kz\",\n        separateDialCode: true,\n      };\n      ({ input, iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => {\n      teardown(iti);\n    });\n\n    test(\"typing/deleting different area codes should update the selected country\", async () => {\n      // typing area code starting with 1 changes to Russia\n      await user.type(input, \"1\");\n      expect(checkFlagSelected(container, \"ru\")).toBe(true);\n\n      // deleting area code keeps Russia selected\n      await user.type(input, \"{backspace}\");\n      expect(checkFlagSelected(container, \"ru\")).toBe(true);\n\n      // typing area code starting with 7 changes to Kazakhstan\n      await user.type(input, \"7\");\n      expect(checkFlagSelected(container, \"kz\")).toBe(true);\n\n      // deleting area code keeps Kazakhstan selected\n      await user.type(input, \"{backspace}\");\n      expect(checkFlagSelected(container, \"kz\")).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/showFlags.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, checkFlagSelected } = require(\"../helpers/helpers\");\n\ndescribe(\"showFlags option\", () => {\n  describe(\"default true, with input value set to valid GB number\", () => {\n    let iti, container;\n\n    beforeEach(() => {\n      ({ iti, container } = initPlugin({ inputValue: \"+447947123123\" }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"has flag element\", () => {\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n    });\n  });\n\n  describe(\"disabled, with input value set to valid GB number\", () => {\n    let iti, container;\n\n    beforeEach(() => {\n      const options = { showFlags: false, allowDropdown: true };\n      ({ iti, container } = initPlugin({ options, inputValue: \"+447947123123\" }));\n    });\n    afterEach(() => teardown(iti));\n\n    test(\"renders globe placeholder instead of specific flag\", () => {\n      expect(checkFlagSelected(container, \"\")).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/options/strictMode.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { userEvent } = require(\"@testing-library/user-event\");\nconst { fireEvent } = require(\"@testing-library/dom\");\nconst {\n  initPlugin,\n  teardown,\n  stripFormattingChars,\n  selectCountryAndTypePlaceholderNumberAsync,\n  checkFlagSelected,\n  getPasteEventObject,\n} = require(\"../helpers/helpers\");\n\n\n// NATIONAL MODE ENABLED\ndescribe(\"strictMode option\", () => {\n  describe(\"nationalMode=true\", () => {\n    let input, iti, user, container;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = {\n        initialCountry: \"us\",\n        strictMode: true,\n        nationalMode: true,\n      };\n      ({ input, iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => {\n      teardown(iti);\n    });\n\n    test(\"ignore irrelevant chars\", async () => {\n      await user.type(input, \"+1a9./-:$@&*b8c7d+\");\n      expect(input.value).toBe(\"+1 987\");\n    });\n\n    // PREV BUG\n    test(\"allows typing digit in middle of incomplete ntl number\", async () => {\n      await user.type(input, \"702123567\");\n      expect(input.value).toBe(\"(702) 123-567\");\n      // put cursor before the 5 and type a 4 - should be allowed\n      input.setSelectionRange(10, 10);\n      await user.type(input, \"4\", {\n        initialSelectionStart: 10,\n        initialSelectionEnd: 10,\n      });\n      expect(input.value).toBe(\"(702) 123-4567\");\n    });\n\n    test(\"prevents typing digit in middle of complete ntl number\", async () => {\n      await user.type(input, \"7021234567\");\n      expect(input.value).toBe(\"(702) 123-4567\");\n      // put cursor before the 4 and type a 1 - should NOT be allowed\n      input.setSelectionRange(10, 10);\n      await user.type(input, \"1\", {\n        initialSelectionStart: 10,\n        initialSelectionEnd: 10,\n      });\n      expect(input.value).toBe(\"(702) 123-4567\");\n    });\n\n    // PREV BUG: https://github.com/jackocnr/intl-tel-input/issues/1777\n    test(\"user can add dial code to national number\", async () => {\n      await user.type(input, \"79554053\");\n      expect(input.value).toBe(\"(795) 540-53\");\n      // put cursor at the beginning and type dial code - should be allowed\n      input.setSelectionRange(0, 0);\n      await user.type(input, \"+591\", {\n        initialSelectionStart: 0,\n        initialSelectionEnd: 0,\n      });\n      expect(input.value).toBe(\"+591 79554053\");\n    });\n\n    // PREV BUG\n    test(\"type full ntl number, select all, can type 0 to replace the number with just a 0\", async () => {\n      await user.type(input, \"7021234567\");\n      input.setSelectionRange(0, input.value.length);\n      await user.type(input, \"0\", {\n        initialSelectionStart: 0,\n        initialSelectionEnd: input.value.length,\n      });\n      expect(input.value).toBe(\"0\");\n    });\n\n    test(\"type full ntl number, can still insert + at beginning\", async () => {\n      await user.type(input, \"7021234567\");\n      // put cursor at beginning and type +\n      input.setSelectionRange(0, 0);\n      await user.type(input, \"+\", {\n        initialSelectionStart: 0,\n        initialSelectionEnd: 0,\n      });\n      expect(input.value).toBe(\"+7021234567\");\n      // changes flag to RU\n      expect(checkFlagSelected(container, \"ru\")).toBe(true);\n    });\n\n    test(\"type full US ntl number, can still insert digit in area code if it changes country\", async () => {\n      await user.type(input, \"4151234567\");\n      expect(input.value).toBe(\"(415) 123-4567\");\n      // put cursor at pos 3 and type a 6 to change to canadian area code 416, which should change flag to canada\n      input.setSelectionRange(3, 3);\n      await user.type(input, \"6\", {\n        initialSelectionStart: 3,\n        initialSelectionEnd: 3,\n      });\n      expect(input.value).toBe(\"41651234567\"); // guess it should remove all formatting as now invalid canadian number?\n      expect(checkFlagSelected(container, \"ca\")).toBe(true);\n      // remove digit again to return to US number and format\n      await user.type(input, \"{backspace}\", {\n        initialSelectionStart: 3,\n        initialSelectionEnd: 3,\n      });\n      expect(checkFlagSelected(container, \"us\")).toBe(true);\n      expect(input.value).toBe(\"(415) 123-4567\");\n    });\n\n    test(\"type full US ntl number, cannot insert digit in middle of number\", async () => {\n      await user.type(input, \"4151234567\");\n      expect(input.value).toBe(\"(415) 123-4567\");\n      // put cursor at pos 4 and type a 6\n      input.setSelectionRange(4, 4);\n      await user.type(input, \"6\", {\n        initialSelectionStart: 4,\n        initialSelectionEnd: 4,\n      });\n      // it should ignore the 6\n      expect(input.value).toBe(\"(415) 123-4567\");\n    });\n\n    test(\"type full intl number, can still change dial code if it changes country\", async () => {\n      await user.type(input, \"+17021234567\");\n      // put cursor at pos 1 and type 4 to change to Swiss dial code\n      input.setSelectionRange(1, 1);\n      await user.type(input, \"4\", {\n        initialSelectionStart: 1,\n        initialSelectionEnd: 1,\n      });\n      expect(input.value).toBe(\"+417021234567\"); // or should it re-format to swiss formatting???\n    });\n\n    test(\"can type US ntl placeholder number and no more\", async () => {\n      const placeholderNumberClean = await selectCountryAndTypePlaceholderNumberAsync(container, \"us\", user, input);\n      expect(checkFlagSelected(container, \"us\")).toBe(true);\n      // try typing extra digit, which should be ignored\n      await user.type(input, \"1\");\n      // sometimes AYT formatting is slightly different, so strip formatting chars\n      expect(stripFormattingChars(input.value)).toBe(placeholderNumberClean);\n    });\n\n    // this was a bug - the number was never capped\n    test(\"can type Canada ntl placeholder number and no more\", async () => {\n      const placeholderNumberClean = await selectCountryAndTypePlaceholderNumberAsync(container, \"ca\", user, input);\n      expect(checkFlagSelected(container, \"ca\")).toBe(true);\n      // try typing extra digit, which should be ignored\n      await user.type(input, \"1\");\n      // sometimes AYT formatting is slightly different, so strip formatting chars\n      expect(stripFormattingChars(input.value)).toBe(placeholderNumberClean);\n    });\n\n    test(\"can type UK ntl placeholder number and no more\", async () => {\n      const placeholderNumberClean = await selectCountryAndTypePlaceholderNumberAsync(container, \"gb\", user, input);\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n      // try typing extra digit, which should be ignored\n      await user.type(input, \"1\");\n      // sometimes AYT formatting is slightly different, so strip formatting chars\n      expect(stripFormattingChars(input.value)).toBe(placeholderNumberClean);\n    });\n\n    // PREV BUG\n    test(\"can type Russian ntl placeholder number and no more\", async () => {\n      const placeholderNumberClean = await selectCountryAndTypePlaceholderNumberAsync(container, \"ru\", user, input);\n      expect(checkFlagSelected(container, \"ru\")).toBe(true);\n      // try typing extra digit, which should be ignored\n      await user.type(input, \"1\");\n      // sometimes AYT formatting is slightly different, so strip formatting chars\n      expect(stripFormattingChars(input.value)).toBe(placeholderNumberClean);\n    });\n\n    test(\"paste strips invalid chars, caps length, and formats\", async () => {\n      await user.click(input);\n      const pastedContent = \"+1a9./-:$@&*b8c7d+123123456\";\n      const eventObject = getPasteEventObject(pastedContent);\n      // NOTE: could not get this working with user.paste\n      fireEvent.paste(input, eventObject);\n      expect(input.value).toBe(\"+1 987-123-1234\");\n    });\n\n    // PREV BUG\n    test(\"pasting a very long number still works\", async () => {\n      await user.click(input);\n      const pastedContent = \"2345678901234567999999\";\n      const eventObject = getPasteEventObject(pastedContent);\n      // NOTE: could not get this working with user.paste\n      fireEvent.paste(input, eventObject);\n      expect(input.value).toBe(\"(234) 567-8901\");\n    });\n  });\n\n  // NATIONAL MODE DISABLED\n  describe(\"nationalMode=false\", () => {\n    let input, iti, user, container;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = {\n        strictMode: true,\n        nationalMode: false,\n      };\n      ({ input, iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => {\n      teardown(iti);\n    });\n\n    test(\"can type US intl placeholder number and no more\", async () => {\n      const placeholderNumberClean = await selectCountryAndTypePlaceholderNumberAsync(container, \"us\", user, input);\n      expect(checkFlagSelected(container, \"us\")).toBe(true);\n      // try typing extra digit, which should be ignored\n      await user.type(input, \"1\");\n      // sometimes AYT formatting is slightly different, so strip formatting chars\n      expect(stripFormattingChars(input.value)).toBe(placeholderNumberClean);\n    });\n\n    test(\"can type UK intl placeholder number and no more\", async () => {\n      const placeholderNumberClean = await selectCountryAndTypePlaceholderNumberAsync(container, \"gb\", user, input);\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n      // try typing extra digit, which should be ignored\n      await user.type(input, \"1\");\n      // sometimes AYT formatting is slightly different, so strip formatting chars\n      expect(stripFormattingChars(input.value)).toBe(placeholderNumberClean);\n    });\n\n    test(\"can type Russian intl placeholder number and no more\", async () => {\n      const placeholderNumberClean = await selectCountryAndTypePlaceholderNumberAsync(container, \"ru\", user, input);\n      expect(checkFlagSelected(container, \"ru\")).toBe(true);\n      // try typing extra digit, which should be ignored\n      await user.type(input, \"1\");\n      // sometimes AYT formatting is slightly different, so strip formatting chars\n      expect(stripFormattingChars(input.value)).toBe(placeholderNumberClean);\n    });\n  });\n\n  // SEPARATE DIAL CODE ENABLED\n  describe(\"separateDialCode=true\", () => {\n    let input, iti, user, container;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = {\n        strictMode: true,\n        separateDialCode: true,\n      };\n      ({ input, iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => {\n      teardown(iti);\n    });\n\n    test(\"can type US placeholder number and no more\", async () => {\n      const placeholderNumberClean = await selectCountryAndTypePlaceholderNumberAsync(container, \"us\", user, input);\n      expect(checkFlagSelected(container, \"us\")).toBe(true);\n      // try typing extra digit, which should be ignored\n      await user.type(input, \"1\");\n      // sometimes AYT formatting is slightly different, so strip formatting chars\n      expect(stripFormattingChars(input.value)).toBe(placeholderNumberClean);\n    });\n\n    test(\"can type UK placeholder number and no more\", async () => {\n      const placeholderNumberClean = await selectCountryAndTypePlaceholderNumberAsync(container, \"gb\", user, input);\n      expect(checkFlagSelected(container, \"gb\")).toBe(true);\n      // try typing extra digit, which should be ignored\n      await user.type(input, \"1\");\n      // sometimes AYT formatting is slightly different, so strip formatting chars\n      expect(stripFormattingChars(input.value)).toBe(placeholderNumberClean);\n    });\n\n    test(\"can type Russian placeholder number and no more\", async () => {\n      const placeholderNumberClean = await selectCountryAndTypePlaceholderNumberAsync(container, \"ru\", user, input);\n      expect(checkFlagSelected(container, \"ru\")).toBe(true);\n      // try typing extra digit, which should be ignored\n      await user.type(input, \"1\");\n      // sometimes AYT formatting is slightly different, so strip formatting chars\n      expect(stripFormattingChars(input.value)).toBe(placeholderNumberClean);\n    });\n  });\n});\n\n\n\n\n\n\n// // LONG RUNNING TESTS - RUN MANUALLY FROM TIME TO TIME\n// // NOTE: these take so long they crash travis. BUT they run ok locally, they just take about 60 seconds.\n\n// const { intlTelInput } = require(\"../helpers/helpers\");\n// const allCountries = intlTelInput.getCountryData();\n// const countryCodes = allCountries.map((country) => country.iso2);\n// const countriesAllowingExtraDigit = [\"ax\", \"at\", \"by\", \"ba\", \"bg\", \"kh\", \"bq\", \"cd\", \"fi\", \"ga\", \"id\", \"ie\", \"lu\", \"my\", \"mm\", \"nz\", \"rs\", \"so\", \"tk\", \"tv\", \"vn\", \"zw\"];\n// const countriesAllowingMultipleExtraDigits = [\"at\", \"id\", \"mm\", \"tk\"];\n\n// // NATIONAL MODE ENABLED\n// describe(\"strictMode, with nationalMode=true\", () => {\n//   let input, iti, user, container;\n\n//   beforeEach(() => {\n//     user = userEvent.setup();\n//     const options = {\n//       strictMode: true,\n//       nationalMode: true,\n//       placeholderNumberType: \"MOBILE\",\n//       allowedNumberTypes: [\"MOBILE\"],\n//     };\n//     ({ input, iti, container } = initPlugin({ options }));\n//   });\n\n//   afterEach(() => {\n//     teardown(iti);\n//   });\n\n//   test.each(countryCodes)(\"can type ntl placeholder number and no more: %s\", async (iso2) => {\n//     let placeholderNumberClean = await selectCountryAndTypePlaceholderNumberAsync(container, iso2, user, input);\n//     expect(checkFlagSelected(container, iso2)).toBe(true);\n//     // try typing an extra digit, which should be ignored (in most cases - see below)\n//     await user.type(input, \"1\");\n\n//     // some countries allow an extra digit\n//     if (countriesAllowingExtraDigit.includes(iso2)) {\n//       placeholderNumberClean += \"1\";\n//       // try typing a second extra digit, which should be ignored (in most cases - see below)\n//       await user.type(input, \"1\");\n\n//       // some countries allow several extra digits which would be annoying to test, so give up here\n//       if (countriesAllowingMultipleExtraDigits.includes(iso2)) {\n//         placeholderNumberClean += \"1\";\n//       }\n//     }\n\n//     // sometimes AYT formatting is slightly different, so strip formatting chars\n//     expect(stripFormattingChars(input.value)).toBe(placeholderNumberClean);\n//   });\n// });\n\n\n\n// // NATIONAL MODE DISABLED\n// describe(\"strictMode, with nationalMode=false\", () => {\n//   let input, iti, user, container;\n\n//   beforeEach(() => {\n//     user = userEvent.setup();\n//     const options = {\n//       strictMode: true,\n//       nationalMode: false,\n//       allowedNumberTypes: [\"MOBILE\"],\n//       placeholderNumberType: \"MOBILE\",\n//     };\n//     ({ input, iti, container } = initPlugin({ options }));\n//   });\n\n//   afterEach(() => {\n//     teardown(iti);\n//   });\n\n//   test.each(countryCodes)(\"can type intl placeholder number: %s\", async (iso2) => {\n//     const placeholderNumberClean = await selectCountryAndTypePlaceholderNumberAsync(container, iso2, user, input);\n//     expect(checkFlagSelected(container, iso2)).toBe(true);\n//     // sometimes AYT formatting is slightly different, so strip formatting chars\n//     expect(stripFormattingChars(input.value)).toBe(placeholderNumberClean);\n//   });\n// });\n\n\n\n// // SEPARATE DIAL CODE ENABLED\n// describe(\"strictMode, with separateDialCode=true\", () => {\n//   let input, iti, user, container;\n\n//   beforeEach(() => {\n//     user = userEvent.setup();\n//     const options = {\n//       strictMode: true,\n//       separateDialCode: true,\n//       placeholderNumberType: \"MOBILE\",\n//       allowedNumberTypes: [\"MOBILE\"],\n//     };\n//     ({ input, iti, container } = initPlugin({ options }));\n//   });\n\n//   afterEach(() => {\n//     teardown(iti);\n//   });\n\n//   test.each(countryCodes)(\"can type separateDialCode placeholder number: %s\", async (iso2) => {\n//     const placeholderNumberClean = await selectCountryAndTypePlaceholderNumberAsync(container, iso2, user, input);\n//     expect(checkFlagSelected(container, iso2)).toBe(true);\n//     // sometimes AYT formatting is slightly different, so strip formatting chars\n//     expect(stripFormattingChars(input.value)).toBe(placeholderNumberClean);\n//   });\n// });\n"
  },
  {
    "path": "tests/integration/options/useFullscreenPopup.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, clickSelectedCountryAsync, getCountryListElement } = require(\"../helpers/helpers\");\nconst { userEvent } = require(\"@testing-library/user-event\");\n\ndescribe(\"useFullscreenPopup option\", () => {\n  describe(\"enabled\", () => {\n    let iti, container, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { useFullscreenPopup: true };\n      ({ iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"shows fullscreen container and country list on click selected country\", async () => {\n      const root = container.ownerDocument;\n      expect(getCountryListElement(root)).toBeFalsy();\n      await clickSelectedCountryAsync(container, user);\n      expect(getCountryListElement(root)).toBeTruthy();\n      expect(root.querySelector(\".iti--fullscreen-popup\")).toBeTruthy();\n    });\n  });\n\n  describe(\"disabled\", () => {\n    let iti, container, user;\n\n    beforeEach(() => {\n      user = userEvent.setup();\n      const options = { useFullscreenPopup: false };\n      ({ iti, container } = initPlugin({ options }));\n    });\n\n    afterEach(() => teardown(iti));\n\n    test(\"uses inline dropdown\", async () => {\n      const root = container.ownerDocument;\n      expect(getCountryListElement(container)).toBeTruthy();\n      await clickSelectedCountryAsync(container, user);\n      expect(root.querySelector(\".iti--fullscreen-popup\")).toBeFalsy();\n    });\n  });\n});\n"
  },
  {
    "path": "tests/integration/static/attachUtils.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst intlTelInput = require(\"intl-tel-input\");\nconst { initPlugin, resetPackageAfterEach } = require(\"../helpers/helpers\");\nrequire(\"../helpers/matchers\");\n\ndescribe(\"attachUtils\", function() {\n  resetPackageAfterEach(intlTelInput);\n\n  describe(\"calling attachUtils before init plugin\", () => {\n\n    const utilsLoader = () => import(\"intl-tel-input/utils\");\n    let loadResult;\n\n    beforeEach(() => {\n      loadResult = intlTelInput.attachUtils(utilsLoader);\n    });\n\n    test(\"starts loading the utils\", () => {\n      expect(intlTelInput).toHaveProperty(\"startedLoadingUtilsScript\", true);\n    });\n\n    test(\"resolves the promise\", async () => {\n      expect(loadResult).toBeAPromise();\n      await expect(loadResult).resolves.toBe(true);\n    });\n\n    test(\"installs the utils module at intlTelInput.utils\", async () => {\n      await loadResult;\n\n      expect(intlTelInput).toHaveProperty(\"utils.isValidNumber\");\n    });\n\n    describe(\"then init plugin with loadUtils option\", () => {\n\n      test(\"resolves the instance's promise\", async () => {\n        const { iti } = initPlugin({\n          intlTelInput,\n          options: {\n            loadUtils: () => import(\"some/other/url/ok\"),\n          },\n        });\n        await iti.promise;\n      });\n\n    });\n\n  });\n\n\n\n  describe(\"init plugin with loadUtils option, but force documentReady=false so it wont fire\", function() {\n    /** @type {jest.Mock<() => Promise<any>>} */\n    let utilsLoader;\n    /** @type {intlTelInput.Iti} */\n    let iti;\n\n    beforeEach(function() {\n      jest.spyOn(intlTelInput, \"documentReady\").mockReturnValue(false);\n      utilsLoader = jest.fn(async () => import(\"intl-tel-input/utils\"));\n\n      ({ iti } = initPlugin({\n        intlTelInput,\n        options: { loadUtils: utilsLoader },\n      }));\n    });\n\n    test(\"does not start loading the utils\", function() {\n      expect(intlTelInput).toHaveProperty(\"startedLoadingUtilsScript\", false);\n    });\n\n    test(\"does not resolve the promise\", async function() {\n      await expect(iti.promise).toBePending();\n    });\n\n\n\n    describe(\"calling attachUtils\", function() {\n      /** @type {Promise<any>} */\n      let attachUtilsPromise;\n\n      beforeEach(async function() {\n        attachUtilsPromise = intlTelInput.attachUtils(utilsLoader);\n      });\n\n      test(\"starts loading the utils\", function() {\n        expect(intlTelInput).toHaveProperty(\"startedLoadingUtilsScript\", true);\n      });\n\n      test(\"resolves the promise\", async function() {\n        await expect(attachUtilsPromise).resolves.toBe(true);\n      });\n\n\n\n      describe(\"then init another plugin instance with loadUtils option\", function() {\n\n        beforeEach(async function() {\n          // Wait for previous load to finish.\n          await attachUtilsPromise;\n\n          initPlugin({\n            intlTelInput,\n            options: { loadUtils: utilsLoader },\n          });\n        });\n\n        test(\"only loads once\", function() {\n          expect(utilsLoader).toHaveBeenCalledTimes(1);\n        });\n\n      });\n\n    });\n\n  });\n\n\n\n  describe(\"force documentReady=true then init plugin with loadUtils\", function() {\n\n    const utilsLoader = () => import(\"intl-tel-input/utils\");\n    /** @type {intlTelInput.Iti} */\n    let iti;\n\n    beforeEach(function() {\n      jest.spyOn(intlTelInput, \"documentReady\").mockReturnValue(true);\n      ({ iti } = initPlugin({\n        intlTelInput,\n        options: { loadUtils: utilsLoader },\n      }));\n    });\n\n    test(\"resolves the promise immediately\", async function() {\n      await expect(iti.promise).resolves.toBeInstanceOf(Array);\n    });\n\n    test(\"starts loading the utils\", function() {\n      expect(intlTelInput).toHaveProperty(\"startedLoadingUtilsScript\", true);\n    });\n\n  });\n\n\n\n  describe(\"calling with a function\", function() {\n    const mockUtils = { default: { mymodule: \"fakeutils\" } };\n\n    test(\"uses the object the function resolves with\", async () => {\n      const result = await intlTelInput.attachUtils(async () => mockUtils);\n\n      expect(result).toEqual(true);\n      expect(intlTelInput.utils).toBe(mockUtils.default);\n    });\n\n    test(\"rejects if the function rejects\", async () => {\n      const loadPromise = intlTelInput.attachUtils(async () => {\n        throw new Error(\"Uhoh!\");\n      });\n\n      await expect(loadPromise).rejects.toThrow(\"Uhoh!\");\n    });\n\n    test(\"rejects if the function throws\", async () => {\n      const loadPromise = intlTelInput.attachUtils(() => {\n        throw new Error(\"Uhoh!\");\n      });\n\n      await expect(loadPromise).rejects.toThrow(\"Uhoh!\");\n    });\n\n    test(\"rejects if the function returns a non-promise\", async () => {\n      const loadPromise = intlTelInput.attachUtils(() => ({\n        anObject: \"That is not a promise\",\n      }));\n\n      await expect(loadPromise).rejects.toThrow();\n    });\n\n    test(\"rejects if the function resolves to a non-object\", async () => {\n      const loadPromise = intlTelInput.attachUtils(async () => \"Hello!\");\n\n      await expect(loadPromise).rejects.toThrow();\n    });\n\n    test(\"does not call the function a second time\", async () => {\n      const loader = jest.fn(async () => mockUtils);\n\n      await intlTelInput.attachUtils(loader);\n      await intlTelInput.attachUtils(loader);\n\n      expect(loader).toHaveBeenCalledTimes(1);\n    });\n\n  });\n\n});\n"
  },
  {
    "path": "tests/integration/static/defaults.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { initPlugin, teardown, checkFlagSelected, intlTelInput } = require(\"../helpers/helpers\");\nconst backupInitialCountry = intlTelInput.defaults.initialCountry; // empty string by default\n\ndescribe(\"defaults static\", () => {\n  afterEach(() => {\n    intlTelInput.defaults.initialCountry = backupInitialCountry;\n  });\n\n  test(\"changing default initialCountry to ru\", () => {\n    intlTelInput.defaults.initialCountry = \"ru\";\n    const { iti, container } = initPlugin();\n    expect(checkFlagSelected(container, \"ru\")).toBe(true);\n    teardown(iti);\n  });\n});\n"
  },
  {
    "path": "tests/integration/static/getCountryData.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\nconst { intlTelInput } = require(\"../helpers/helpers\");\n\n// no setup/teardown needed for static method test\ndescribe(\"getCountryData static\", () => {\n  test(\"returns country data array\", () => {\n    const data = intlTelInput.getCountryData();\n    expect(Array.isArray(data)).toBe(true);\n    expect(data.length).toBeGreaterThan(200);\n  });\n});\n"
  },
  {
    "path": "tests/unit/core/countrySearch.test.js",
    "content": "/**\n * @jest-environment node\n */\n\nconst { getMatchedCountries, findFirstCountryStartingWith } = require(\"../../../src/js/modules/core/countrySearch.ts\");\n\n// helper to make a Country-like object\nconst makeCountry = (overrides) => ({\n  iso2: \"aa\",\n  dialCode: \"1\",\n  priority: 0,\n  areaCodes: null,\n  nationalPrefix: null,\n  name: \"\",\n  nodeById: {},\n  ...overrides,\n});\n\nconst countries = [\n  makeCountry({\n    iso2: \"gb\",\n    dialCode: \"44\",\n    priority: 0,\n    name: \"United Kingdom\",\n    normalisedName: \"united kingdom\",\n    dialCodePlus: \"+44\",\n    initials: \"uk\",\n  }),\n  makeCountry({\n    iso2: \"gg\",\n    dialCode: \"44\",\n    priority: 1,\n    name: \"Guernsey\",\n    normalisedName: \"guernsey\",\n    dialCodePlus: \"+44\",\n    initials: \"g\",\n  }),\n  makeCountry({\n    iso2: \"us\",\n    dialCode: \"1\",\n    priority: 0,\n    name: \"United States\",\n    normalisedName: \"united states\",\n    dialCodePlus: \"+1\",\n    initials: \"us\",\n  }),\n  makeCountry({\n    iso2: \"ua\",\n    dialCode: \"380\",\n    priority: 0,\n    name: \"Ukraine\",\n    normalisedName: \"ukraine\",\n    dialCodePlus: \"+380\",\n    initials: \"u\",\n  }),\n];\n\ndescribe(\"countrySearch getMatchedCountries\", () => {\n  test(\"ISO2 exact match wins over others\", () => {\n    const result = getMatchedCountries(countries, \"gb\");\n    expect(result[0].iso2).toBe(\"gb\");\n  });\n\n  test(\"name starts-with beats name contains\", () => {\n    const result = getMatchedCountries(countries, \"united k\");\n    expect(result[0].iso2).toBe(\"gb\");\n    expect(result.find(c => c.iso2 === \"us\")).toBeUndefined();\n  });\n\n  test(\"dial code exact (+ or bare) bucket ordering respected\", () => {\n    const resultBare = getMatchedCountries(countries, \"44\");\n    expect(resultBare[0].iso2).toBe(\"gb\");\n    expect(resultBare[1].iso2).toBe(\"gg\");\n\n    const resultPlus = getMatchedCountries(countries, \"+44\");\n    expect(resultPlus[0].iso2).toBe(\"gb\");\n    expect(resultPlus[1].iso2).toBe(\"gg\");\n  });\n\n  test(\"dial code contains bucket after exact dial code\", () => {\n    const result = getMatchedCountries(countries, \"+3\");\n    const uaIndex = result.findIndex(c => c.iso2 === \"ua\");\n    expect(uaIndex).toBeGreaterThanOrEqual(0);\n  });\n\n  test(\"initials bucket last (presence)\", () => {\n    const result = getMatchedCountries(countries, \"uk\"); // initials match for GB\n    expect(result.some(c => c.iso2 === \"gb\")).toBe(true);\n  });\n});\n\ndescribe(\"countrySearch findFirstCountryStartingWith\", () => {\n  test(\"finds first alphabetical match by original order\", () => {\n    const result = findFirstCountryStartingWith(countries, \"uni\");\n    expect(result?.iso2).toBe(\"gb\");\n  });\n\n  test(\"returns null when no match\", () => {\n    const result = findFirstCountryStartingWith(countries, \"zzz\");\n    expect(result).toBeNull();\n  });\n});\n"
  },
  {
    "path": "tests/unit/core/options.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { defaults, applyOptionSideEffects, validateOptions } = require(\"../../../src/js/modules/core/options.ts\");\n\ndescribe(\"core/options applyOptionSideEffects\", () => {\n  const clone = () => JSON.parse(JSON.stringify(defaults));\n\n  test(\"onlyCountries single sets initialCountry\", () => {\n    const o = clone();\n    o.onlyCountries = [\"us\"];\n    o.initialCountry = \"\";\n    applyOptionSideEffects(o);\n    expect(o.initialCountry).toBe(\"us\");\n  });\n\n  test(\"separateDialCode forces nationalMode false\", () => {\n    const o = clone();\n    o.separateDialCode = true;\n    o.nationalMode = true;\n    applyOptionSideEffects(o);\n    expect(o.nationalMode).toBe(false);\n  });\n\n  test(\"allowDropdown without flags dial code forces nationalMode false\", () => {\n    const o = clone();\n    o.allowDropdown = true;\n    o.showFlags = false;\n    o.separateDialCode = false;\n    o.nationalMode = true;\n    applyOptionSideEffects(o);\n    expect(o.nationalMode).toBe(false);\n  });\n\n  test(\"useFullscreenPopup without container sets dropdownContainer\", () => {\n    const o = clone();\n    o.useFullscreenPopup = true;\n    o.dropdownContainer = null;\n    applyOptionSideEffects(o);\n    expect(o.dropdownContainer).toBe(document.body);\n  });\n});\n\n\n\ndescribe(\"core/options validateOptions\", () => {\n  let warnSpy;\n\n  beforeEach(() => {\n    warnSpy = jest.spyOn(console, \"warn\").mockImplementation(() => {});\n  });\n\n  afterEach(() => {\n    warnSpy.mockRestore();\n  });\n\n  test(\"warns on unknown option key\", () => {\n    expect(() => validateOptions({ doesNotExist: true })).not.toThrow();\n    expect(warnSpy).toHaveBeenCalled();\n  });\n\n  test(\"warns on invalid initialCountry\", () => {\n    expect(() => validateOptions({ initialCountry: \"zz\" })).not.toThrow();\n    expect(warnSpy).toHaveBeenCalled();\n  });\n\n  test(\"warns on invalid allowedNumberTypes\", () => {\n    expect(() => validateOptions({ allowedNumberTypes: [\"NOPE\"] })).not.toThrow();\n    expect(warnSpy).toHaveBeenCalled();\n  });\n\n  test(\"warns on invalid autoPlaceholder\", () => {\n    expect(() => validateOptions({ autoPlaceholder: \"invalid\" })).not.toThrow();\n    expect(warnSpy).toHaveBeenCalled();\n  });\n\n  test(\"accepts a minimal valid options object\", () => {\n    expect(() =>\n      validateOptions({\n        allowDropdown: false,\n        initialCountry: \"auto\",\n        excludeCountries: [\"us\"],\n        allowedNumberTypes: [\"MOBILE\"],\n        autoPlaceholder: \"aggressive\",\n      }),\n    ).not.toThrow();\n    expect(warnSpy).not.toHaveBeenCalled();\n  });\n\n  test(\"warns on invalid i18n option type\", () => {\n    expect(() => validateOptions({ i18n: [] })).not.toThrow();\n    expect(warnSpy).toHaveBeenCalled();\n  });\n});\n"
  },
  {
    "path": "tests/unit/data/country-data.test.js",
    "content": "/**\n * @jest-environment node\n */\n\nconst {\n  processAllCountries,\n  generateCountryNames,\n  processDialCodes,\n  sortCountries,\n  cacheSearchTokens,\n} = require(\"../../../src/js/modules/data/country-data.ts\");\nconst allCountries = require(\"../../../src/js/intl-tel-input/data.ts\").default;\n\ndescribe(\"data/country-data processAllCountries\", () => {\n  test(\"onlyCountries filters list\", () => {\n    const out = processAllCountries({ onlyCountries: [\"us\"], excludeCountries: [], i18n: {} });\n    expect(out.every(c => c.iso2 === \"us\")).toBe(true);\n  });\n\n  test(\"excludeCountries removes those entries\", () => {\n    const out = processAllCountries({ onlyCountries: [], excludeCountries: [\"us\"], i18n: {} });\n    expect(out.find(c => c.iso2 === \"us\")).toBeUndefined();\n  });\n\n  test(\"no filters returns allCountries ref\", () => {\n    const out = processAllCountries({ onlyCountries: [], excludeCountries: [], i18n: {} });\n    expect(out.length).toBe(allCountries.length);\n  });\n});\n\ndescribe(\"data/country-data processDialCodes\", () => {\n  test(\"creates dial code map and length\", () => {\n    const sample = allCountries.slice(0, 5);\n    const { dialCodes, dialCodeMaxLen, dialCodeToIso2Map } = processDialCodes(sample, { onlyCountries: [], excludeCountries: [] });\n    expect(dialCodes.size).toBeGreaterThan(0);\n    expect(dialCodeMaxLen).toBeGreaterThan(0);\n    expect(Object.keys(dialCodeToIso2Map).length).toBeGreaterThan(0);\n  });\n});\n\ndescribe(\"data/country-data sortCountries\", () => {\n  test(\"countryOrder enforces ordering\", () => {\n    const sample = allCountries.slice(0, 3).map(c => ({ ...c }));\n    const order = sample.map(c => c.iso2).reverse();\n    sortCountries(sample, { countryOrder: order });\n    expect(sample.map(c => c.iso2)).toEqual(order);\n  });\n});\n\ndescribe(\"data/country-data cacheSearchTokens\", () => {\n  test(\"adds normalisedName, initials and dialCodePlus\", () => {\n    const sample = allCountries.slice(0, 1).map(c => ({ ...c }));\n    cacheSearchTokens(sample);\n    const c = sample[0];\n    expect(c.normalisedName).toBeDefined();\n    expect(c.initials).toBeDefined();\n    expect(c.dialCodePlus).toBe(`+${c.dialCode}`);\n  });\n});\n\ndescribe(\"data/country-data generateCountryNames\", () => {\n  test(\"uses Intl.DisplayNames with countryNameLocale\", () => {\n    // Pull out US for this test.\n    const sample = [{ ...allCountries.find(c => c.iso2 === \"us\") }];\n    generateCountryNames(sample, { countryNameLocale: \"fr\", i18n: {} });\n    // In French, US is \"États-Unis\".\n    expect(sample[0].name).toBe(\"États-Unis\");\n  });\n\n  test(\"i18n country name overrides DisplayNames\", () => {\n    // Pull out France for this test.\n    const sample = [{ ...allCountries.find(c => c.iso2 === \"fr\") }];\n    // In English, France is \"France\", but we will override it.\n    generateCountryNames(sample, {\n      countryNameLocale: \"en\",\n      i18n: { fr: \"République Française\" },\n    });\n    expect(sample[0].name).toBe(\"République Française\");\n  });\n});\n"
  },
  {
    "path": "tests/unit/data/nanp-regionless.test.js",
    "content": "/**\n * @jest-environment node\n */\n\nconst { isRegionlessNanp, regionlessNanpNumbers } = require(\"../../../src/js/modules/data/nanp-regionless.ts\");\n\ndescribe(\"data/nanp-regionless\", () => {\n  test(\"identifies known regionless NANP numbers\", () => {\n    for (const ac of regionlessNanpNumbers.slice(0,3)) {\n      expect(isRegionlessNanp(`+1${ac}1234567`)).toBe(true);\n    }\n  });\n\n  test(\"non NANP or non-regionless returns false\", () => {\n    expect(isRegionlessNanp(\"+441234567890\")).toBe(false);\n  });\n});\n"
  },
  {
    "path": "tests/unit/format/caret.test.js",
    "content": "/**\n * @jest-environment node\n */\n\nconst { translateCursorPosition } = require(\"../../../src/js/modules/format/caret.ts\");\n\ndescribe(\"format/caret translateCursorPosition\", () => {\n  test(\"returns 0 when at start and backspacing\", () => {\n    expect(translateCursorPosition(1, \"+1 23\", 0, false)).toBe(0);\n  });\n\n  test(\"moves to just after final relevant char (not skipping following space)\", () => {\n    // formatted value has 3 relevant chars (+12) then a space then next digit\n    // Implementation returns index immediately after 3rd relevant char (before the space) => 3\n    expect(translateCursorPosition(3, \"+12 3\", 4, false)).toBe(3);\n  });\n\n  test(\"delete forwards places caret before next relevant char\", () => {\n    // relevant chars = 2, expect position right before 3rd relevant\n    expect(translateCursorPosition(2, \"+12 3\", 2, true)).toBeGreaterThanOrEqual(2);\n  });\n});\n"
  },
  {
    "path": "tests/unit/format/formatting.test.js",
    "content": "/**\n * @jest-environment node\n */\n\nconst { beforeSetNumber, formatNumberAsYouType } = require(\"../../../src/js/modules/format/formatting.ts\");\n\ndescribe(\"format/formatting beforeSetNumber\", () => {\n  const selected = { dialCode: \"44\" };\n\n  test(\"removes dial code when separateDialCode and present\", () => {\n    const out = beforeSetNumber(\"+44 1234\", \"+44\", true, selected);\n    expect(out).toBe(\"1234\");\n  });\n\n  test(\"returns full number when separateDialCode false\", () => {\n    const out = beforeSetNumber(\"+44 1234\", \"+44\", false, selected);\n    expect(out).toBe(\"+44 1234\");\n  });\n});\n\ndescribe(\"format/formatting formatNumberAsYouType\", () => {\n  const selected = { dialCode: \"44\", iso2: \"gb\" };\n\n  test(\"uses utils when provided\", () => {\n    const utils = { formatNumberAsYouType: jest.fn(() => \"+44 1234\") };\n    const out = formatNumberAsYouType(\"+441234\", \"+441234\", utils, selected, false);\n    expect(out).toBe(\"+44 1234\");\n    expect(utils.formatNumberAsYouType).toHaveBeenCalled();\n  });\n\n  test(\"strips dial code for separateDialCode when not retyped\", () => {\n    const utils = { formatNumberAsYouType: jest.fn(() => \"+44 1234\") };\n    const out = formatNumberAsYouType(\"+441234\", \"1234\", utils, selected, true);\n    expect(out).toBe(\"1234\");\n  });\n\n  test(\"falls back to fullNumber when no utils\", () => {\n    const out = formatNumberAsYouType(\"+441234\", \"1234\", null, selected, false);\n    expect(out).toBe(\"+441234\");\n  });\n});\n"
  },
  {
    "path": "tests/unit/intl-tel-input/constructor.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst imported = require(\"../../../src/js/intl-tel-input.ts\");\nconst intlTelInput = imported.default || imported;\n\ndescribe(\"Iti constructor argument validation\", () => {\n  test(\"throws if input is not an HTMLInputElement\", () => {\n    expect(() => intlTelInput(null)).toThrow(TypeError);\n\n    const div = document.createElement(\"div\");\n    expect(() => intlTelInput(div)).toThrow(TypeError);\n  });\n});\n"
  },
  {
    "path": "tests/unit/utils/dom.test.js",
    "content": "/**\n * @jest-environment jsdom\n */\n\nconst { buildClassNames, createEl } = require(\"../../../src/js/modules/utils/dom.ts\");\n\ndescribe(\"utils/dom\", () => {\n  test(\"buildClassNames filters falsy values\", () => {\n    const result = buildClassNames({ a: true, b: 0, c: false, d: \"x\" });\n    expect(result.split(\" \").sort()).toEqual([\"a\", \"d\"].sort());\n  });\n\n  test(\"createEl creates element with attrs and optional container\", () => {\n    const container = document.createElement(\"div\");\n    const el = createEl(\"span\", { \"data-x\": \"1\", id: \"foo\" }, container);\n    expect(el.tagName).toBe(\"SPAN\");\n    expect(el.getAttribute(\"data-x\")).toBe(\"1\");\n    expect(container.contains(el)).toBe(true);\n  });\n});\n"
  },
  {
    "path": "tests/unit/utils/string.test.js",
    "content": "/**\n * @jest-environment node\n */\n\nconst { getNumeric, normaliseString } = require(\"../../../src/js/modules/utils/string.ts\");\n\ndescribe(\"utils/string\", () => {\n  test(\"getNumeric extracts digits only\", () => {\n    expect(getNumeric(\"+1 (234) 56-78 ext.90\")).toBe(\"1234567890\");\n  });\n\n  test(\"normaliseString lowercases and strips diacritics\", () => {\n    expect(normaliseString(\"Réunion\")) .toBe(\"reunion\");\n    expect(normaliseString(\"Åland Islands\")).toBe(\"aland islands\");\n  });\n\n  test(\"normaliseString handles empty/undefined input\", () => {\n    expect(normaliseString()).toBe(\"\");\n  });\n});\n"
  },
  {
    "path": "tests-e2e/angular.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\n\ntest.describe(\"angular component demo\", () => {\n  test(\"mounts and formats a number\", async ({ page }) => {\n    await page.goto(\"/angular/demo/simple/index.html\");\n\n    const input = page.locator(\"input[type=\\\"tel\\\"]\");\n    await expect(input).toBeVisible();\n\n    // Wait for plugin to initialise.\n    await expect(page.locator(\".iti\")).toBeVisible();\n\n    // Should default to US per demo initOptions.\n    const selectedCountry = page.locator(\".iti__selected-country\");\n    await expect(selectedCountry).toHaveAttribute(\"title\", \"United States\");\n\n    await input.pressSequentially(\"4155552671\");\n    await expect(input).toHaveValue(\"(415) 555-2671\");\n  });\n});\n"
  },
  {
    "path": "tests-e2e/fixtures/vanilla.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <title>intl-tel-input fixture</title>\n    <link rel=\"stylesheet\" href=\"/build/css/intlTelInput.css\" />\n    <style>\n      body { font-family: -apple-system, system-ui, sans-serif; padding: 24px; }\n      #phone { width: 260px; padding: 8px 10px; font-size: 16px; }\n      .container { margin-top: 16px; }\n    </style>\n  </head>\n  <body>\n    <h1>Fixture</h1>\n    <div class=\"container\">\n      <input id=\"phone\" type=\"tel\" />\n    </div>\n\n    <script src=\"/build/js/intlTelInputWithUtils.js\"></script>\n    <script>\n      const input = document.querySelector('#phone');\n      window.iti = window.intlTelInput(input);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests-e2e/react.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\n\ntest.describe(\"react component demo\", () => {\n  test(\"mounts and formats a number\", async ({ page }) => {\n    await page.goto(\"/react/demo/simple/simple.html\");\n\n    const input = page.locator(\"input[type=\\\"tel\\\"]\");\n    await expect(input).toBeVisible();\n\n    // Wait for plugin to initialise.\n    await expect(page.locator(\".iti\")).toBeVisible();\n\n    // Should default to US per demo initOptions.\n    const selectedCountry = page.locator(\".iti__selected-country\");\n    await expect(selectedCountry).toHaveAttribute(\"title\", \"United States\");\n\n    await input.pressSequentially(\"4155552671\");\n    await expect(input).toHaveValue(\"(415) 555-2671\");\n  });\n});\n"
  },
  {
    "path": "tests-e2e/simple.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\n\ntest.describe(\"basic usage test with vanilla plugin\", () => {\n  test(\"can select country and type number and getNumber works\", async ({ page }) => {\n    // Load vanilla fixture page\n    await page.goto(\"/tests-e2e/fixtures/vanilla.html\");\n\n    const input = page.locator(\"#phone\");\n    const selectedCountry = await page.locator(\".iti__selected-country\");\n\n    // no country is selected by default\n    await expect(selectedCountry).toHaveAttribute(\"title\", \"Select country\");\n\n    // open country dropdown\n    await selectedCountry.click();\n\n    // select US\n    await page.locator(\".iti__country-list li.iti__country\", { hasText: \"United States\" }).click();\n\n    // the selected country should update to US\n    await expect(selectedCountry).toHaveAttribute(\"title\", \"United States\");\n\n    // Type a US number in national format in the input\n    await input.pressSequentially(\"4155552671\");\n\n    // the input value should have formatting applied (format-as-you-type)\n    await expect(input).toHaveValue(\"(415) 555-2671\");\n\n    // Calling getNumber should return the full E.164 number\n    const e164 = await page.evaluate(() => (window as any).iti.getNumber());\n    expect(e164).toBe(\"+14155552671\");\n  });\n});\n"
  },
  {
    "path": "tests-e2e/svelte.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\n\ntest.describe(\"svelte component demo\", () => {\n  test.use({ baseURL: \"http://localhost:4175\" });\n\n  test(\"mounts and formats a number\", async ({ page }) => {\n    await page.goto(\"/\");\n\n    const input = page.locator(\"input[type=\\\"tel\\\"]\");\n    await expect(input).toBeVisible();\n\n    // Wait for plugin to initialise.\n    await expect(page.locator(\".iti\")).toBeVisible();\n\n    // Should default to US per demo options.\n    const selectedCountry = page.locator(\".iti__selected-country\");\n    await expect(selectedCountry).toHaveAttribute(\"title\", \"United States\");\n\n    await input.pressSequentially(\"4155552671\");\n    await expect(input).toHaveValue(\"(415) 555-2671\");\n  });\n});\n"
  },
  {
    "path": "tests-e2e/visual.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\n\ntest.describe(\"visual snapshots\", () => {\n  test.skip(\n    process.platform !== \"linux\",\n    \"Visual snapshots are recorded on Linux to avoid cross-platform rendering diffs.\",\n  );\n  test.beforeEach(async ({ page }) => {\n    await page.goto(\"/tests-e2e/fixtures/vanilla.html\");\n\n    // Wait for intl-tel-input to initialise and wrap the input.\n    await expect(page.locator(\".iti\")).toBeVisible();\n  });\n\n  test(\"initial render\", async ({ page }) => {\n    await expect(page).toHaveScreenshot(\"vanilla-initial.png\");\n  });\n\n  test(\"after selecting a country\", async ({ page }) => {\n    const selectedCountry = page.locator(\".iti__selected-country\");\n\n    await selectedCountry.click();\n    await page.locator(\".iti__country-list li.iti__country\", { hasText: \"United States\" }).click();\n\n    await expect(selectedCountry).toHaveAttribute(\"title\", \"United States\");\n\n    await expect(page).toHaveScreenshot(\"vanilla-us-selected.png\");\n  });\n\n  test(\"after typing a number\", async ({ page }) => {\n    const selectedCountry = page.locator(\".iti__selected-country\");\n    const input = page.locator(\"#phone\");\n\n    await selectedCountry.click();\n    await page.locator(\".iti__country-list li.iti__country\", { hasText: \"United States\" }).click();\n\n    await input.pressSequentially(\"4155552671\");\n    await expect(input).toHaveValue(\"(415) 555-2671\");\n\n    await expect(page).toHaveScreenshot(\"vanilla-us-number.png\");\n  });\n});\n"
  },
  {
    "path": "tests-e2e/vue.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\n\ntest.describe(\"vue component demo\", () => {\n  test.use({ baseURL: \"http://localhost:4174\" });\n\n  test(\"mounts and formats a number\", async ({ page }) => {\n    await page.goto(\"/\");\n\n    const input = page.locator(\"input[type=\\\"tel\\\"]\");\n    await expect(input).toBeVisible();\n\n    // Wait for plugin to initialise.\n    await expect(page.locator(\".iti\")).toBeVisible();\n\n    // Should default to US per demo options.\n    const selectedCountry = page.locator(\".iti__selected-country\");\n    await expect(selectedCountry).toHaveAttribute(\"title\", \"United States\");\n\n    await input.pressSequentially(\"4155552671\");\n    await expect(input).toHaveValue(\"(415) 555-2671\");\n  });\n});\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es6\",\n    \"module\": \"nodenext\",\n    \"moduleResolution\": \"nodenext\",\n    \"esModuleInterop\": true, // required for react (etc) default imports\n    \"declaration\": true, // generate .d.ts files\n    \"emitDeclarationOnly\": true, // ONLY generate .d.ts files (we use esbuild for the actual bundling)\n    \"outFile\": \"build/js/intlTelInput.d.ts\",\n    \"rootDir\": \"./src/js\",\n    \"allowJs\": true, // allow importing .mjs files e.g. i18n files\n  },\n  \"include\": [\n    \"src/js/intl-tel-input.ts\",\n    \"src/js/intl-tel-input/intlTelInputWithUtils.ts\",\n    \"src/js/intl-tel-input/i18n/index.ts\",\n  ],\n}"
  },
  {
    "path": "vue/README.md",
    "content": "# IntlTelInput Vue Component\n\nA Vue component for the [intl-tel-input](https://github.com/jackocnr/intl-tel-input) JavaScript plugin. View the [source code](https://github.com/jackocnr/intl-tel-input/blob/master/vue/src/intl-tel-input/IntlTelInput.vue).\n\n[Explore docs »](https://intl-tel-input.com/docs/vue-component)\n"
  },
  {
    "path": "vue/demo/set-number/App.vue",
    "content": "<script setup>\nimport { ref } from \"vue\";\nimport IntlTelInput from \"../../build/exports/IntlTelInputWithUtils\";\n//import IntlTelInput from \"../../src/intl-tel-input/IntlTelInputWithUtils.vue\";\n\nconst errorMap = [\n  \"Invalid number\",\n  \"Invalid country code\",\n  \"Too short\",\n  \"Too long\",\n  \"Invalid number\",\n];\n\nconst isValid = ref(null);\nconst number = ref(null);\nconst errorCode = ref(null);\nconst notice = ref(null);\n\nconst intlTelInputRef = ref(null);\n\nconst handleSetNumber = () => {\n  intlTelInputRef.value?.instance?.setNumber(\"+14155552671\");\n};\n\nconst handleSubmit = () => {\n  if (isValid.value) {\n    notice.value = `Valid number: ${number.value}`;\n  } else {\n    const errorMessage = errorMap[errorCode.value || 0] || \"Invalid number\";\n    notice.value = `Error: ${errorMessage}`;\n  }\n};\n</script>\n\n<template>\n  <form>\n    <IntlTelInput\n      ref=\"intlTelInputRef\"\n      @changeNumber=\"number = $event\"\n      @changeValidity=\"isValid = $event\"\n      @changeErrorCode=\"errorCode = $event\"\n      :options=\"{ initialCountry: 'us' }\"\n    />\n    <button class=\"button\" type=\"button\" @click=\"handleSetNumber\">\n      Set Number\n    </button>\n    <button class=\"button\" type=\"button\" @click=\"handleSubmit\">Validate</button>\n    <div v-if=\"notice\" class=\"notice\">{{ notice }}</div>\n  </form>\n</template>\n"
  },
  {
    "path": "vue/demo/set-number/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <title>Vue App - Set Number Demo</title>\n</head>\n\n<body>\n  <h1>Vue App - Set Number Demo</h1>\n  <p>A simple Vue app, using the IntlTelInput component to handle phone number entry, calling setNumber and validation.</p>\n  <p>Click \"Set Number\" then click \"Validate\" to check that the Vue internals have updated correctly.</p>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"./main.js\"></script>\n</body>\n\n</html>\n"
  },
  {
    "path": "vue/demo/set-number/main.js",
    "content": "import { createApp } from \"vue\";\nimport App from \"./App.vue\";\nimport \"../../../build/css/intlTelInput.css\";\nimport \"../../../build/css/demo.css\";\n\ncreateApp(App).mount(\"#app\");\n"
  },
  {
    "path": "vue/demo/set-number/vite.config.js",
    "content": "import { defineConfig } from \"vite\";\nimport vue from \"@vitejs/plugin-vue\";\nimport { version } from \"../../../package.json\";\n\nexport default defineConfig({\n  root: \"vue/demo/set-number\",\n  define: {\n    \"process.env.VERSION\": `\"${version}\"`,\n  },\n  esbuild: {\n    minify: false,\n  },\n  plugins: [vue()],\n});\n"
  },
  {
    "path": "vue/demo/simple/App.vue",
    "content": "<script setup>\nimport IntlTelInput from \"../../build/exports/IntlTelInputWithUtils\";\n//import IntlTelInput from \"../../src/intl-tel-input/IntlTelInputWithUtils.vue\";\n</script>\n\n<template>\n  <IntlTelInput :options=\"{ initialCountry: 'us' }\" />\n</template>\n"
  },
  {
    "path": "vue/demo/simple/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <title>Vue App - Simple Demo</title>\n</head>\n\n<body>\n  <h1>Vue App - Simple Demo</h1>\n  <p>A simple Vue app, using the IntlTelInput component to handle phone number entry.</p>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"./main.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "vue/demo/simple/main.js",
    "content": "import { createApp } from \"vue\";\nimport App from \"./App.vue\";\nimport \"../../../build/css/intlTelInput.css\";\nimport \"../../../build/css/demo.css\";\n\ncreateApp(App).mount(\"#app\");\n"
  },
  {
    "path": "vue/demo/simple/vite.config.js",
    "content": "import { defineConfig } from \"vite\";\nimport vue from \"@vitejs/plugin-vue\";\nimport { version } from \"../../../package.json\";\n\nexport default defineConfig({\n  root: \"vue/demo/simple\",\n  define: {\n    \"process.env.VERSION\": `\"${version}\"`,\n  },\n  esbuild: {\n    minify: false,\n  },\n  plugins: [vue()],\n});"
  },
  {
    "path": "vue/demo/toggle-disabled/App.vue",
    "content": "<script setup>\nimport { ref } from \"vue\";\nimport IntlTelInput from \"../../build/exports/IntlTelInputWithUtils\";\n//import IntlTelInput from \"../../src/intl-tel-input/IntlTelInputWithUtils.vue\";\n\nconst isDisabled = ref(true);\n\nconst toggleDisabled = () => {\n  isDisabled.value = !isDisabled.value;\n};\n</script>\n\n<template>\n  <form>\n    <IntlTelInput :disabled=\"isDisabled\" />\n    <button class=\"button\" type=\"button\" @click=\"toggleDisabled\">Toggle</button>\n  </form>\n</template>\n"
  },
  {
    "path": "vue/demo/toggle-disabled/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <title>Vue App - Toggle disabled prop Demo</title>\n</head>\n\n<body>\n  <h1>Vue App - Toggle disabled prop Demo</h1>\n  <p>A simple Vue app, using the IntlTelInput component to show it can be disabled and enabled.</p>\n  <p>Click the button to enable/disable component</p>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"./main.js\"></script>\n</body>\n\n</html>\n"
  },
  {
    "path": "vue/demo/toggle-disabled/main.js",
    "content": "import { createApp } from \"vue\";\nimport App from \"./App.vue\";\nimport \"../../../build/css/intlTelInput.css\";\nimport \"../../../build/css/demo.css\";\n\ncreateApp(App).mount(\"#app\");\n"
  },
  {
    "path": "vue/demo/toggle-disabled/vite.config.js",
    "content": "import { defineConfig } from \"vite\";\nimport vue from \"@vitejs/plugin-vue\";\nimport { version } from \"../../../package.json\";\n\nexport default defineConfig({\n  root: \"vue/demo/toggle-disabled\",\n  define: {\n    \"process.env.VERSION\": `\"${version}\"`,\n  },\n  esbuild: {\n    minify: false,\n  },\n  plugins: [vue()],\n});\n"
  },
  {
    "path": "vue/demo/validation/App.vue",
    "content": "<script setup>\nimport { ref } from \"vue\";\nimport IntlTelInput from \"../../build/exports/IntlTelInputWithUtils\";\n\nconst errorMap = [\n  \"Invalid number\",\n  \"Invalid country code\",\n  \"Too short\",\n  \"Too long\",\n  \"Invalid number\",\n];\n\nconst isValid = ref(null);\nconst number = ref(null);\nconst errorCode = ref(null);\nconst notice = ref(null);\n\nconst handleSubmit = () => {\n  if (isValid.value) {\n    notice.value = `Valid number: ${number.value}`;\n  } else {\n    const errorMessage = errorMap[errorCode.value || 0] || \"Invalid number\";\n    notice.value = `Error: ${errorMessage}`;\n  }\n};\n</script>\n\n<template>\n  <form>\n    <IntlTelInput\n      @changeNumber=\"number = $event\"\n      @changeValidity=\"isValid = $event\"\n      @changeErrorCode=\"errorCode = $event\"\n      :options=\"{\n        initialCountry: 'us',\n      }\"\n    />\n    <button class=\"button\" type=\"button\" @click=\"handleSubmit\">Validate</button>\n    <div v-if=\"notice\" class=\"notice\">{{ notice }}</div>\n  </form>\n</template>\n"
  },
  {
    "path": "vue/demo/validation/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <title>Vue App - Validation Demo</title>\n</head>\n\n<body>\n  <h1>Vue App - Validation Demo</h1>\n  <p>A simple Vue app, using the IntlTelInput component to handle phone number entry and validation.</p>\n  <p>Enter a phone number below and click \"Validate\".</p>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"./main.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "vue/demo/validation/main.js",
    "content": "import { createApp } from \"vue\";\nimport App from \"./App.vue\";\nimport \"../../../build/css/intlTelInput.css\";\nimport \"../../../build/css/demo.css\";\n\ncreateApp(App).mount(\"#app\");\n"
  },
  {
    "path": "vue/demo/validation/vite.config.js",
    "content": "import { defineConfig } from \"vite\";\nimport vue from \"@vitejs/plugin-vue\";\nimport { version } from \"../../../package.json\";\n\nexport default defineConfig({\n  root: \"vue/demo/validation\",\n  define: {\n    \"process.env.VERSION\": `\"${version}\"`,\n  },\n  esbuild: {\n    minify: false,\n  },\n  plugins: [vue()],\n});"
  },
  {
    "path": "vue/src/IntlTelInput.vue",
    "content": "<script setup lang=\"ts\">\nimport intlTelInput from \"./intl-tel-input\";\nimport type { SomeOptions } from \"./modules/types/public-api\";\nimport { onMounted, onUnmounted, ref, shallowRef, watch, computed } from \"vue\";\nimport type { InputHTMLAttributes } from \"vue\";\n\ninterface Props {\n  options?: SomeOptions;\n  usePreciseValidation?: boolean;\n  disabled?: boolean;\n  inputProps?: InputHTMLAttributes;\n  value?: string | null;\n  modelValue?: string | null;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  disabled: false,\n  usePreciseValidation: false,\n  inputProps: () => ({}),\n  options: () => ({}),\n});\n\ndefineOptions({ inheritAttrs: false });\n\nconst emit = defineEmits([\n  \"changeNumber\",\n  \"changeCountry\",\n  \"changeValidity\",\n  \"changeErrorCode\",\n  \"update:modelValue\",\n]);\n\nconst sanitizedInputProps = computed(() => {\n  // ignore keys that would break functionality\n  const {\n    type: _type,\n    ref: _ref,\n    value: _value,\n    // disabled: _disabled,\n    onInput: _onInput,\n    oninput: _oninput,\n    onCountrychange: _onCountrychange,\n    onCountryChange: _onCountryChange,\n    ...rest\n  } = (props.inputProps ?? {}) as Record<string, unknown>;\n\n  return rest;\n});\n\nconst displayed = computed(() => props.value ?? props.modelValue ?? \"\");\n\nconst input = ref<HTMLInputElement | null>(null);\n// Use shallowRef so Vue does not Proxy-wrap the Iti class instance.\n// A reactive Proxy breaks private fields (e.g. `#ui`) when calling instance methods.\nconst instance = shallowRef<ReturnType<typeof intlTelInput> | null>(null);\nconst lastEmittedNumber = ref<string>();\nconst lastEmittedCountry = ref<string>();\nconst lastEmittedValidity = ref<boolean>();\nconst lastEmittedErrorCode = ref<number | null>();\n\nconst isValid = () => {\n  if (instance.value) {\n    return props.usePreciseValidation\n      ? instance.value.isValidNumberPrecise()\n      : instance.value.isValidNumber();\n  }\n\n  return null;\n};\n\nconst updateValidity = () => {\n  const isCurrentlyValid = isValid();\n  if (isCurrentlyValid === null) return;\n\n  const valid = !!isCurrentlyValid;\n  const errorCode = valid\n    ? null\n    : instance.value?.getValidationError?.() ?? null;\n\n  if (valid !== lastEmittedValidity.value) {\n    lastEmittedValidity.value = valid;\n    emit(\"changeValidity\", valid);\n  }\n\n  if (errorCode !== lastEmittedErrorCode.value) {\n    lastEmittedErrorCode.value = errorCode;\n    emit(\"changeErrorCode\", errorCode);\n  }\n};\n\nconst updateValue = () => {\n  const number = instance.value?.getNumber() ?? \"\";\n\n  if (number !== lastEmittedNumber.value) {\n    lastEmittedNumber.value = number;\n    emit(\"changeNumber\", number);\n    emit(\"update:modelValue\", number);\n  }\n\n  updateValidity();\n};\n\nconst updateCountry = () => {\n  const country = instance.value?.getSelectedCountryData().iso2 ?? \"\";\n  if (country !== lastEmittedCountry.value) {\n    lastEmittedCountry.value = country;\n    emit(\"changeCountry\", country);\n  }\n  updateValue();\n};\n\nonMounted(() => {\n  if (input.value) {\n    instance.value = intlTelInput(input.value, props.options);\n\n    if (displayed.value) {\n      instance.value.setNumber(displayed.value);\n    }\n\n    if (props.disabled) {\n      instance.value.setDisabled(props.disabled);\n    }\n\n    lastEmittedNumber.value = instance.value.getNumber?.() ?? \"\";\n    lastEmittedCountry.value = instance.value.getSelectedCountryData().iso2 ?? \"\";\n\n    const initialValid = isValid();\n    if (initialValid !== null) {\n      lastEmittedValidity.value = !!initialValid;\n      lastEmittedErrorCode.value = initialValid\n        ? null\n        : instance.value.getValidationError?.() ?? null;\n    }\n  }\n});\n\nwatch(\n  () => props.disabled,\n  (newValue) => instance.value?.setDisabled(newValue),\n);\n\nwatch(\n  () => displayed.value,\n  (val) => {\n    if (!instance.value) return;\n\n    // Avoid cursor jumping when typing\n    const next = val ?? \"\";\n    const currentCanonical = instance.value.getNumber?.() ?? \"\";\n    const isFocused = document.activeElement === input.value;\n\n    if (isFocused || currentCanonical === next) return;\n\n    instance.value.setNumber(next);\n    updateValidity();\n  },\n  { flush: \"post\" },\n)\n\nonUnmounted(() => instance.value?.destroy());\n\ndefineExpose({ instance, input });\n</script>\n\n<template>\n  <!-- must come first, so cannot override the other required props -->\n  <input\n    v-bind=\"sanitizedInputProps\"\n    ref=\"input\"\n    type=\"tel\"\n    @countrychange=\"updateCountry\"\n    @input=\"updateValue\"\n  />\n</template>\n"
  },
  {
    "path": "vue/src/IntlTelInputWithUtils.vue",
    "content": "<!-- THIS FILE IS AUTO-GENERATED. DO NOT EDIT. -->\n<script setup lang=\"ts\">\nimport intlTelInput from \"./intl-tel-input/intlTelInputWithUtils\";\nimport type { SomeOptions } from \"./modules/types/public-api\";\nimport { onMounted, onUnmounted, ref, shallowRef, watch, computed } from \"vue\";\nimport type { InputHTMLAttributes } from \"vue\";\n\ninterface Props {\n  options?: SomeOptions;\n  usePreciseValidation?: boolean;\n  disabled?: boolean;\n  inputProps?: InputHTMLAttributes;\n  value?: string | null;\n  modelValue?: string | null;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  disabled: false,\n  usePreciseValidation: false,\n  inputProps: () => ({}),\n  options: () => ({}),\n});\n\ndefineOptions({ inheritAttrs: false });\n\nconst emit = defineEmits([\n  \"changeNumber\",\n  \"changeCountry\",\n  \"changeValidity\",\n  \"changeErrorCode\",\n  \"update:modelValue\",\n]);\n\nconst sanitizedInputProps = computed(() => {\n  // ignore keys that would break functionality\n  const {\n    type: _type,\n    ref: _ref,\n    value: _value,\n    // disabled: _disabled,\n    onInput: _onInput,\n    oninput: _oninput,\n    onCountrychange: _onCountrychange,\n    onCountryChange: _onCountryChange,\n    ...rest\n  } = (props.inputProps ?? {}) as Record<string, unknown>;\n\n  return rest;\n});\n\nconst displayed = computed(() => props.value ?? props.modelValue ?? \"\");\n\nconst input = ref<HTMLInputElement | null>(null);\n// Use shallowRef so Vue does not Proxy-wrap the Iti class instance.\n// A reactive Proxy breaks private fields (e.g. `#ui`) when calling instance methods.\nconst instance = shallowRef<ReturnType<typeof intlTelInput> | null>(null);\nconst lastEmittedNumber = ref<string>();\nconst lastEmittedCountry = ref<string>();\nconst lastEmittedValidity = ref<boolean>();\nconst lastEmittedErrorCode = ref<number | null>();\n\nconst isValid = () => {\n  if (instance.value) {\n    return props.usePreciseValidation\n      ? instance.value.isValidNumberPrecise()\n      : instance.value.isValidNumber();\n  }\n\n  return null;\n};\n\nconst updateValidity = () => {\n  const isCurrentlyValid = isValid();\n  if (isCurrentlyValid === null) return;\n\n  const valid = !!isCurrentlyValid;\n  const errorCode = valid\n    ? null\n    : instance.value?.getValidationError?.() ?? null;\n\n  if (valid !== lastEmittedValidity.value) {\n    lastEmittedValidity.value = valid;\n    emit(\"changeValidity\", valid);\n  }\n\n  if (errorCode !== lastEmittedErrorCode.value) {\n    lastEmittedErrorCode.value = errorCode;\n    emit(\"changeErrorCode\", errorCode);\n  }\n};\n\nconst updateValue = () => {\n  const number = instance.value?.getNumber() ?? \"\";\n\n  if (number !== lastEmittedNumber.value) {\n    lastEmittedNumber.value = number;\n    emit(\"changeNumber\", number);\n    emit(\"update:modelValue\", number);\n  }\n\n  updateValidity();\n};\n\nconst updateCountry = () => {\n  const country = instance.value?.getSelectedCountryData().iso2 ?? \"\";\n  if (country !== lastEmittedCountry.value) {\n    lastEmittedCountry.value = country;\n    emit(\"changeCountry\", country);\n  }\n  updateValue();\n};\n\nonMounted(() => {\n  if (input.value) {\n    instance.value = intlTelInput(input.value, props.options);\n\n    if (displayed.value) {\n      instance.value.setNumber(displayed.value);\n    }\n\n    if (props.disabled) {\n      instance.value.setDisabled(props.disabled);\n    }\n\n    lastEmittedNumber.value = instance.value.getNumber?.() ?? \"\";\n    lastEmittedCountry.value = instance.value.getSelectedCountryData().iso2 ?? \"\";\n\n    const initialValid = isValid();\n    if (initialValid !== null) {\n      lastEmittedValidity.value = !!initialValid;\n      lastEmittedErrorCode.value = initialValid\n        ? null\n        : instance.value.getValidationError?.() ?? null;\n    }\n  }\n});\n\nwatch(\n  () => props.disabled,\n  (newValue) => instance.value?.setDisabled(newValue),\n);\n\nwatch(\n  () => displayed.value,\n  (val) => {\n    if (!instance.value) return;\n\n    // Avoid cursor jumping when typing\n    const next = val ?? \"\";\n    const currentCanonical = instance.value.getNumber?.() ?? \"\";\n    const isFocused = document.activeElement === input.value;\n\n    if (isFocused || currentCanonical === next) return;\n\n    instance.value.setNumber(next);\n    updateValidity();\n  },\n  { flush: \"post\" },\n)\n\nonUnmounted(() => instance.value?.destroy());\n\ndefineExpose({ instance, input });\n</script>\n\n<template>\n  <!-- must come first, so cannot override the other required props -->\n  <input\n    v-bind=\"sanitizedInputProps\"\n    ref=\"input\"\n    type=\"tel\"\n    @countrychange=\"updateCountry\"\n    @input=\"updateValue\"\n  />\n</template>\n"
  },
  {
    "path": "vue/src/env.d.ts",
    "content": "// Ambient declaration to satisfy TypeScript when using Vite's `define` to inject process.env.VERSION\ndeclare const process: {\n  env?: {\n    VERSION?: string;\n  };\n};\n"
  },
  {
    "path": "vue/src/exports/IntlTelInput.ts",
    "content": "import IntlTelInput from \"../IntlTelInput.vue\";\nexport type { SomeOptions } from \"../modules/types/public-api\";\nexport default IntlTelInput;"
  },
  {
    "path": "vue/src/exports/IntlTelInputWithUtils.ts",
    "content": "import IntlTelInputWithUtils from \"../IntlTelInputWithUtils.vue\";\nexport type { SomeOptions } from \"../modules/types/public-api\";\nexport default IntlTelInputWithUtils;"
  },
  {
    "path": "vue/tsconfig.app.json",
    "content": "{\n  \"extends\": \"@vue/tsconfig/tsconfig.dom.json\",\n  \"compilerOptions\": {\n    /* Linting */\n    \"strict\": false,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"verbatimModuleSyntax\": true\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.tsx\", \"src/**/*.vue\", \"src/**/*.d.ts\"],\n\n  // exclude symlinks\n  \"exclude\": [\n    // \"src/index.ts\",\n    // \"src/intl-tel-input/i18n/**/*.ts\",\n    // \"src/intl-tel-input/utils-compiled.js\",\n    // \"src/intl-tel-input/intlTelInputWithUtils.ts\",\n    // \"src/intl-tel-input/data.ts\",\n  ]\n}"
  },
  {
    "path": "vue/tsconfig.json",
    "content": "{\n    \"files\": [],\n    \"references\": [\n      { \"path\": \"./tsconfig.app.json\" },\n      { \"path\": \"./tsconfig.node.json\" }\n    ]\n  }\n"
  },
  {
    "path": "vue/tsconfig.node.json",
    "content": "{\n    \"compilerOptions\": {\n      \"target\": \"ES2022\",\n      \"lib\": [\"ES2023\"],\n      \"module\": \"ESNext\",\n      \"skipLibCheck\": true,\n\n      /* Bundler mode */\n      \"moduleResolution\": \"bundler\",\n      \"allowImportingTsExtensions\": true,\n      \"isolatedModules\": true,\n      \"moduleDetection\": \"force\",\n      \"noEmit\": true,\n\n      /* Linting */\n      \"strict\": true,\n      \"noUnusedLocals\": true,\n      \"noUnusedParameters\": true,\n      \"noFallthroughCasesInSwitch\": true,\n    },\n    \"include\": [\"vite.config.mts\"]\n  }\n"
  },
  {
    "path": "vue/vite.config.mts",
    "content": "import { defineConfig } from \"vite\";\nimport vue from \"@vitejs/plugin-vue\";\nimport { version } from \"../package.json\";\nimport dts from \"vite-plugin-dts\";\nimport {resolve} from \"node:path\";\n\nexport default defineConfig({\n  root: \"vue\",\n  define: {\n    \"process.env.VERSION\": `\"${version}\"`,\n  },\n  build: {\n    outDir: \"build\",\n    emptyOutDir: true,\n    lib: {\n      entry: [\n        resolve(__dirname, \"src/exports/IntlTelInput.ts\"),\n        resolve(__dirname, \"src/exports/IntlTelInputWithUtils.ts\"),\n      ],\n      formats: [\"es\"],\n      fileName: \"exports/[name]\",\n    },\n    rollupOptions: {\n      external: [\"vue\"],\n      output: {\n        globals: {\n          vue: \"Vue\",\n        },\n      },\n    },\n  },\n  plugins: [vue(), dts({ tsconfigPath: \"./tsconfig.app.json\" })],\n});"
  }
]