Full Code of jackocnr/intl-tel-input for AI

master 419966a2c2dc cached
394 files
874.7 KB
246.3k tokens
360 symbols
1 requests
Download .txt
Showing preview only (968K chars total). Download the full file or copy to clipboard to get everything.
Repository: jackocnr/intl-tel-input
Branch: master
Commit: 419966a2c2dc
Files: 394
Total size: 874.7 KB

Directory structure:
gitextract_v3v4kr9z/

├── .eslintignore
├── .eslintrc.js
├── .github/
│   ├── CONTRIBUTING.md
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── 1_bug_report.yml
│   │   ├── 2_feature_request.yml
│   │   └── config.yml
│   ├── copilot-instructions.md
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── .gitmodules
├── .node-version
├── .npmrc
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── CHANGELOG.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── angular/
│   ├── README.md
│   ├── build.js
│   ├── demo/
│   │   ├── form/
│   │   │   ├── form.component.ts
│   │   │   ├── index.html
│   │   │   └── main.ts
│   │   ├── set-number/
│   │   │   ├── index.html
│   │   │   ├── main.ts
│   │   │   └── set-number.component.ts
│   │   ├── simple/
│   │   │   ├── index.html
│   │   │   ├── main.ts
│   │   │   └── simple.component.ts
│   │   ├── toggle-disabled/
│   │   │   ├── index.html
│   │   │   ├── main.ts
│   │   │   └── toggle-disabled.component.ts
│   │   └── validation/
│   │       ├── index.html
│   │       ├── main.ts
│   │       └── validation.component.ts
│   ├── src/
│   │   └── intl-tel-input/
│   │       ├── angular.ts
│   │       └── angularWithUtils.ts
│   └── tsconfig.json
├── build.js
├── composer.json
├── cspell.json
├── demo.html
├── functions/
│   └── _middleware.js
├── grunt/
│   ├── bump.js
│   ├── clean.js
│   ├── closure-compiler.js
│   ├── connect.js
│   ├── cssmin.js
│   ├── generate-sprite.js
│   ├── replace.js
│   ├── sass.js
│   ├── shell.js
│   ├── translations.js
│   └── watch.js
├── index.js
├── jest.config.js
├── package.json
├── playwright.config.ts
├── react/
│   ├── README.md
│   ├── build.js
│   ├── demo/
│   │   ├── set-number/
│   │   │   ├── SetNumberApp.tsx
│   │   │   └── set-number.html
│   │   ├── simple/
│   │   │   ├── SimpleApp.tsx
│   │   │   └── simple.html
│   │   ├── toggle-disabled/
│   │   │   ├── ToggleDisabledApp.tsx
│   │   │   └── toggle-disabled.html
│   │   └── validation/
│   │       ├── ValidationApp.tsx
│   │       └── validation.html
│   ├── src/
│   │   └── intl-tel-input/
│   │       ├── react.tsx
│   │       └── reactWithUtils.tsx
│   └── tsconfig.json
├── scripts/
│   ├── check-lpn-metadata.cjs
│   └── playwright-linux-docker.sh
├── site/
│   ├── .gitignore
│   ├── Gruntfile.js
│   ├── README.md
│   ├── esbuild/
│   │   ├── build.mjs
│   │   └── externalUtilsPlugin.mjs
│   ├── grunt/
│   │   ├── copy.js
│   │   ├── cssmin.js
│   │   ├── fetchStats.js
│   │   ├── replace.js
│   │   ├── sass.js
│   │   ├── shell.js
│   │   ├── template.js
│   │   ├── templateGruntHelpers.js
│   │   ├── templateNav.js
│   │   ├── templateUtils.js
│   │   └── watch.js
│   ├── package.json
│   ├── src/
│   │   ├── 404/
│   │   │   ├── 404_content.html
│   │   │   └── 404_page_template.html.ejs
│   │   ├── css/
│   │   │   ├── _base.scss
│   │   │   ├── _forms.scss
│   │   │   ├── _layout.scss
│   │   │   ├── _navbar.scss
│   │   │   ├── _variables.scss
│   │   │   ├── docs.scss
│   │   │   ├── highlightjs_overrides.scss
│   │   │   ├── homepage.scss
│   │   │   ├── large_flags_overrides.scss
│   │   │   ├── playground.scss
│   │   │   └── website.scss
│   │   ├── docs/
│   │   │   ├── docs_content_template.html.ejs
│   │   │   ├── docs_nav_template.html.ejs
│   │   │   ├── docs_page_template.html.ejs
│   │   │   └── markdown/
│   │   │       ├── accessibility.md
│   │   │       ├── angular_component.md
│   │   │       ├── choose_integration.md
│   │   │       ├── events.md
│   │   │       ├── faq.md
│   │   │       ├── getting_started.md
│   │   │       ├── localisation.md
│   │   │       ├── methods.md
│   │   │       ├── options.md
│   │   │       ├── react_component.md
│   │   │       ├── svelte_component.md
│   │   │       ├── theming.md
│   │   │       ├── troubleshooting.md
│   │   │       ├── utils.md
│   │   │       └── vue_component.md
│   │   ├── examples/
│   │   │   ├── copy/
│   │   │   │   ├── angular_component_desc.html
│   │   │   │   ├── display_number_desc.html
│   │   │   │   ├── hidden_input_desc.html
│   │   │   │   ├── large_flags_desc.html
│   │   │   │   ├── lookup_country_desc.html
│   │   │   │   ├── multiple_instances_desc.html
│   │   │   │   ├── react_component_desc.html
│   │   │   │   ├── right_to_left_desc.html
│   │   │   │   ├── single_country_desc.html
│   │   │   │   ├── svelte_component_desc.html
│   │   │   │   ├── validation_practical_desc.html
│   │   │   │   ├── validation_precise_desc.html
│   │   │   │   └── vue_component_desc.html
│   │   │   ├── css/
│   │   │   │   ├── multiple_instances.css
│   │   │   │   └── validation.css
│   │   │   ├── examples_content_template.html.ejs
│   │   │   ├── examples_nav_template.html.ejs
│   │   │   ├── examples_page_template.html.ejs
│   │   │   ├── html/
│   │   │   │   ├── component.html
│   │   │   │   ├── display_number.html
│   │   │   │   ├── display_number_display_code.html
│   │   │   │   ├── multiple_instances.html
│   │   │   │   ├── multiple_instances_display_code.html
│   │   │   │   ├── simple_input.html
│   │   │   │   ├── simple_input_display_code.html
│   │   │   │   ├── validation.html
│   │   │   │   └── validation_display_code.html
│   │   │   └── js/
│   │   │       ├── angular_component.ts
│   │   │       ├── angular_component_display_code.js
│   │   │       ├── hidden_input.js
│   │   │       ├── hidden_input_display_code.js
│   │   │       ├── lookup_country.js
│   │   │       ├── lookup_country_display_code.js
│   │   │       ├── multiple_instances.js
│   │   │       ├── multiple_instances_display_code.js
│   │   │       ├── react_component.js
│   │   │       ├── react_component_display_code.js
│   │   │       ├── right_to_left.js
│   │   │       ├── right_to_left_display_code.js
│   │   │       ├── simple_init_plugin.js
│   │   │       ├── simple_init_plugin_display_code.js
│   │   │       ├── single_country.js
│   │   │       ├── single_country_display_code.js
│   │   │       ├── svelte_component.svelte
│   │   │       ├── svelte_component_display_code.svelte
│   │   │       ├── svelte_main.js
│   │   │       ├── validation.js
│   │   │       ├── validation_display_code.js
│   │   │       ├── viteSvelteDemo.config.mjs
│   │   │       ├── viteVueDemo.config.js
│   │   │       ├── vue_component.vue
│   │   │       ├── vue_component_display_code.vue
│   │   │       └── vue_main.js
│   │   ├── homepage/
│   │   │   ├── homepage_content.html
│   │   │   └── homepage_page_template.html.ejs
│   │   ├── js/
│   │   │   ├── homepage.js
│   │   │   └── iti-live-results.js
│   │   ├── layout_template.html.ejs
│   │   ├── playground/
│   │   │   ├── js/
│   │   │   │   ├── modules/
│   │   │   │   │   ├── clipboard.js
│   │   │   │   │   ├── forms.js
│   │   │   │   │   ├── i18n.js
│   │   │   │   │   ├── initCode.js
│   │   │   │   │   ├── itiController.js
│   │   │   │   │   ├── playgroundConfig.js
│   │   │   │   │   ├── stateUtils.js
│   │   │   │   │   └── urlState.js
│   │   │   │   ├── playground.js
│   │   │   │   └── templates/
│   │   │   │       └── playgroundConstants.js.ejs
│   │   │   ├── playground_content.html
│   │   │   └── playground_page_template.html.ejs
│   │   └── shared/
│   │       ├── common_body_end.html
│   │       ├── common_head_end_prod.html
│   │       ├── common_meta_tags.html
│   │       ├── common_styles.html.ejs
│   │       ├── iti_live_results_script.html.ejs
│   │       └── iti_script.html.ejs
│   └── static/
│       ├── _redirects
│       ├── ads.txt
│       ├── css/
│       │   └── intlTelInput-largeFlags.css
│       └── screenshotting.html
├── src/
│   ├── css/
│   │   ├── _metadata.scss
│   │   ├── demo.scss
│   │   ├── intlTelInput.scss
│   │   └── intlTelInputWithAssets.scss
│   └── js/
│       ├── intl-tel-input/
│       │   ├── data.ts
│       │   ├── i18n/
│       │   │   ├── ar/
│       │   │   │   └── index.ts
│       │   │   ├── bg/
│       │   │   │   └── index.ts
│       │   │   ├── bn/
│       │   │   │   └── index.ts
│       │   │   ├── bs/
│       │   │   │   └── index.ts
│       │   │   ├── ca/
│       │   │   │   └── index.ts
│       │   │   ├── cs/
│       │   │   │   └── index.ts
│       │   │   ├── da/
│       │   │   │   └── index.ts
│       │   │   ├── de/
│       │   │   │   └── index.ts
│       │   │   ├── el/
│       │   │   │   └── index.ts
│       │   │   ├── en/
│       │   │   │   └── index.ts
│       │   │   ├── es/
│       │   │   │   └── index.ts
│       │   │   ├── et/
│       │   │   │   └── index.ts
│       │   │   ├── fa/
│       │   │   │   └── index.ts
│       │   │   ├── fi/
│       │   │   │   └── index.ts
│       │   │   ├── fr/
│       │   │   │   └── index.ts
│       │   │   ├── hi/
│       │   │   │   └── index.ts
│       │   │   ├── hr/
│       │   │   │   └── index.ts
│       │   │   ├── hu/
│       │   │   │   └── index.ts
│       │   │   ├── id/
│       │   │   │   └── index.ts
│       │   │   ├── index.ts
│       │   │   ├── it/
│       │   │   │   └── index.ts
│       │   │   ├── ja/
│       │   │   │   └── index.ts
│       │   │   ├── kn/
│       │   │   │   └── index.ts
│       │   │   ├── ko/
│       │   │   │   └── index.ts
│       │   │   ├── lt/
│       │   │   │   └── index.ts
│       │   │   ├── mr/
│       │   │   │   └── index.ts
│       │   │   ├── nl/
│       │   │   │   └── index.ts
│       │   │   ├── no/
│       │   │   │   └── index.ts
│       │   │   ├── pl/
│       │   │   │   └── index.ts
│       │   │   ├── pt/
│       │   │   │   └── index.ts
│       │   │   ├── ro/
│       │   │   │   └── index.ts
│       │   │   ├── ru/
│       │   │   │   └── index.ts
│       │   │   ├── sk/
│       │   │   │   └── index.ts
│       │   │   ├── sl/
│       │   │   │   └── index.ts
│       │   │   ├── sq/
│       │   │   │   └── index.ts
│       │   │   ├── sr/
│       │   │   │   └── index.ts
│       │   │   ├── sv/
│       │   │   │   └── index.ts
│       │   │   ├── te/
│       │   │   │   └── index.ts
│       │   │   ├── th/
│       │   │   │   └── index.ts
│       │   │   ├── tr/
│       │   │   │   └── index.ts
│       │   │   ├── types.ts
│       │   │   ├── uk/
│       │   │   │   └── index.ts
│       │   │   ├── ur/
│       │   │   │   └── index.ts
│       │   │   ├── uz/
│       │   │   │   └── index.ts
│       │   │   ├── vi/
│       │   │   │   └── index.ts
│       │   │   ├── zh/
│       │   │   │   └── index.ts
│       │   │   └── zh-hk/
│       │   │       └── index.ts
│       │   └── intlTelInputWithUtils.ts
│       ├── intl-tel-input.ts
│       ├── modules/
│       │   ├── constants.ts
│       │   ├── core/
│       │   │   ├── countrySearch.ts
│       │   │   ├── icons.ts
│       │   │   ├── numerals.ts
│       │   │   ├── options.ts
│       │   │   └── ui.ts
│       │   ├── data/
│       │   │   ├── country-data.ts
│       │   │   ├── intl-regionless.ts
│       │   │   └── nanp-regionless.ts
│       │   ├── format/
│       │   │   ├── caret.ts
│       │   │   └── formatting.ts
│       │   ├── types/
│       │   │   ├── events.ts
│       │   │   ├── forEachInstanceArgsMap.ts
│       │   │   └── public-api.ts
│       │   └── utils/
│       │       ├── dom.ts
│       │       ├── isAndroid.ts
│       │       └── string.ts
│       └── utils.js
├── svelte/
│   ├── README.md
│   ├── demo/
│   │   ├── set-number/
│   │   │   ├── App.svelte
│   │   │   ├── index.html
│   │   │   ├── main.js
│   │   │   └── vite.config.mjs
│   │   ├── simple/
│   │   │   ├── App.svelte
│   │   │   ├── index.html
│   │   │   ├── main.js
│   │   │   └── vite.config.mjs
│   │   ├── toggle-disabled/
│   │   │   ├── App.svelte
│   │   │   ├── index.html
│   │   │   ├── main.js
│   │   │   └── vite.config.mjs
│   │   └── validation/
│   │       ├── App.svelte
│   │       ├── index.html
│   │       ├── main.js
│   │       └── vite.config.mjs
│   ├── src/
│   │   └── intl-tel-input/
│   │       ├── IntlTelInput.svelte
│   │       └── IntlTelInputWithUtils.svelte
│   ├── viteConfig.mjs
│   └── viteConfigWithUtils.mjs
├── tests/
│   ├── integration/
│   │   ├── core/
│   │   │   ├── dropdownShortcuts.test.js
│   │   │   ├── easternNumerals.test.js
│   │   │   ├── initialValues.test.js
│   │   │   ├── multipleInstances.test.js
│   │   │   ├── regionless.test.js
│   │   │   ├── usingDropdown.test.js
│   │   │   └── usingInput.test.js
│   │   ├── events/
│   │   │   ├── closeCountryDropdownEvent.test.js
│   │   │   ├── countryChangeEvent.test.js
│   │   │   └── openCountryDropdownEvent.test.js
│   │   ├── helpers/
│   │   │   ├── helpers.js
│   │   │   └── matchers.js
│   │   ├── methods/
│   │   │   ├── destroy.test.js
│   │   │   ├── getExtension.test.js
│   │   │   ├── getInstance.test.js
│   │   │   ├── getNumber.test.js
│   │   │   ├── getNumberType.test.js
│   │   │   ├── getSelectedCountryData.test.js
│   │   │   ├── getValidationError.test.js
│   │   │   ├── isValidNumber.test.js
│   │   │   ├── isValidNumberPrecise.test.js
│   │   │   ├── setCountry.test.js
│   │   │   ├── setDisabled.test.js
│   │   │   ├── setNumber.test.js
│   │   │   └── setPlaceholderNumberType.test.js
│   │   ├── options/
│   │   │   ├── allowDropdown.test.js
│   │   │   ├── allowNumberExtensions.test.js
│   │   │   ├── allowPhonewords.test.js
│   │   │   ├── allowedNumberTypes.test.js
│   │   │   ├── autoPlaceholder.test.js
│   │   │   ├── containerClass.test.js
│   │   │   ├── countryNameLocale.test.js
│   │   │   ├── countryOrder.test.js
│   │   │   ├── countrySearch.test.js
│   │   │   ├── customPlaceholder.test.js
│   │   │   ├── dropdownContainer.test.js
│   │   │   ├── excludeCountries.test.js
│   │   │   ├── fixDropdownWidth.test.js
│   │   │   ├── formatAsYouType.test.js
│   │   │   ├── formatOnDisplay.test.js
│   │   │   ├── geoIpLookup.test.js
│   │   │   ├── hiddenInput.test.js
│   │   │   ├── i18n-locales.test.js
│   │   │   ├── i18n.test.js
│   │   │   ├── initialCountry.test.js
│   │   │   ├── loadUtils.test.js
│   │   │   ├── nationalMode.test.js
│   │   │   ├── onlyCountries.test.js
│   │   │   ├── placeholderNumberType.test.js
│   │   │   ├── separateDialCode.test.js
│   │   │   ├── showFlags.test.js
│   │   │   ├── strictMode.test.js
│   │   │   └── useFullscreenPopup.test.js
│   │   └── static/
│   │       ├── attachUtils.test.js
│   │       ├── defaults.test.js
│   │       └── getCountryData.test.js
│   └── unit/
│       ├── core/
│       │   ├── countrySearch.test.js
│       │   └── options.test.js
│       ├── data/
│       │   ├── country-data.test.js
│       │   └── nanp-regionless.test.js
│       ├── format/
│       │   ├── caret.test.js
│       │   └── formatting.test.js
│       ├── intl-tel-input/
│       │   └── constructor.test.js
│       └── utils/
│           ├── dom.test.js
│           └── string.test.js
├── tests-e2e/
│   ├── angular.spec.ts
│   ├── fixtures/
│   │   └── vanilla.html
│   ├── react.spec.ts
│   ├── simple.spec.ts
│   ├── svelte.spec.ts
│   ├── visual.spec.ts
│   └── vue.spec.ts
├── tsconfig.json
└── vue/
    ├── README.md
    ├── demo/
    │   ├── set-number/
    │   │   ├── App.vue
    │   │   ├── index.html
    │   │   ├── main.js
    │   │   └── vite.config.js
    │   ├── simple/
    │   │   ├── App.vue
    │   │   ├── index.html
    │   │   ├── main.js
    │   │   └── vite.config.js
    │   ├── toggle-disabled/
    │   │   ├── App.vue
    │   │   ├── index.html
    │   │   ├── main.js
    │   │   └── vite.config.js
    │   └── validation/
    │       ├── App.vue
    │       ├── index.html
    │       ├── main.js
    │       └── vite.config.js
    ├── src/
    │   ├── IntlTelInput.vue
    │   ├── IntlTelInputWithUtils.vue
    │   ├── env.d.ts
    │   └── exports/
    │       ├── IntlTelInput.ts
    │       └── IntlTelInputWithUtils.ts
    ├── tsconfig.app.json
    ├── tsconfig.json
    ├── tsconfig.node.json
    └── vite.config.mts

================================================
FILE CONTENTS
================================================

================================================
FILE: .eslintignore
================================================
**/demo/**
**/grunt/**
**/build/**
**/third_party/**
**/tmp/**
Gruntfile.js
**/intl-tel-input/utils.js
site/static/js/highlight.min.js
site/static/js/silktide-consent-manager.js
site/src/examples/js/*_display_code*
playwright-report/


================================================
FILE: .eslintrc.js
================================================
module.exports = {
  root: true,
  env: {
    browser: true,
    es2021: true,
    node: true,
    "jest/globals": true,
  },
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended",
  ],
  parser: "@typescript-eslint/parser",
  parserOptions: {
    "ecmaVersion": "latest",
    "sourceType": "module",
  },
  plugins: [
    "@typescript-eslint",
    "react",
    "jest",
  ],
  globals: {
    goog: true,
    i18n: true,
    require: true,
  },
  rules: {
    semi: ["error", "always"],
    "comma-dangle": ["error", "always-multiline"],
    quotes: ["error", "double"],
    "no-unused-vars": "off",
    "no-prototype-builtins": "off",
    "class-methods-use-this": "error",
    "@typescript-eslint/explicit-function-return-type": "off",
    "@typescript-eslint/no-unused-vars": ["error", {
      "argsIgnorePattern": "^_",
      "varsIgnorePattern": "^_",
      "caughtErrorsIgnorePattern": "^_",
      "ignoreRestSiblings": true,
    }],
    "@typescript-eslint/no-var-requires": "off",
    "@typescript-eslint/no-explicit-any": "off",
    "@typescript-eslint/no-require-imports": "off",
  },
  overrides: [{
    files: [".eslintrc.{js,cjs}"],
    env: { "node": true },
    parserOptions: { "sourceType": "script" },
  }],
  settings: {
    "import/resolver": {
      "typescript": {},
    },
    "react": {
      "version": "detect",
    },
  },
};


================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing

I'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/).

## Table of Contents
- [Changes to the plugin](#changes-to-the-plugin)
- [Updating the flag images](#updating-the-flag-images)
- [Adding a new translation](#adding-a-new-translation)

## Changes to the plugin

### Setup

Once 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!

### Making changes

Any 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:

- `npm run build` to build everything (slow)
  - Builds flag images, translations, CSS, and all of the JS (see below)
- `npm run build:js` to build all of the JS (slow)
  - Builds utils script, main plugin module, TS type declaration files, react/vue/angular/svelte components and demo bundles
- `npm run build:jsfast` to just build the JS needed for the demo/tests (fast)
- `npm run build:css` to just build the CSS (fast)
- And lots more - see [package.json "scripts" section](https://github.com/jackocnr/intl-tel-input/blob/master/package.json#L7-L29) for full list

### Tests

After 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`.

## Updating the flag images

We 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._

## Adding a new translation

NOTE: 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.

The [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)).

The 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.

If 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.


================================================
FILE: .github/FUNDING.yml
================================================
github: jackocnr


================================================
FILE: .github/ISSUE_TEMPLATE/1_bug_report.yml
================================================
name: Bug Report
description: Report a bug or issue with intl-tel-input
labels: ["bug"]
body:
  - type: checkboxes
    attributes:
      label: Prerequisites
      description: Take a couple of minutes to help our maintainers work faster.
      options:
        - label: I am using the latest version (v26.8.1) of both intl-tel-input and utils.js.
          required: true
        - 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.
          required: true
        - label: I have searched the existing issues (including closed issues) to ensure this has not already been discussed.
          required: true
  - type: textarea
    id: current
    attributes:
      label: Current behaviour
      description: A concise description of what you're experiencing.
    validations:
      required: true
  - type: textarea
    id: expected
    attributes:
      label: Expected behaviour
      description: A concise description of what you expected to happen.
    validations:
      required: true
  - type: textarea
    id: steps
    attributes:
      label: Steps to reproduce
      description: How can we reproduce the issue? (e.g. provide a [Playground](https://intl-tel-input.com/playground) link with the right configuration)
      value: |
        1.
        2.
        3.
    validations:
      required: true
  - type: textarea
    id: comments
    attributes:
      label: Anything else?
      description: Additional comments, screenshots/videos, initialisation options, browser/device info, etc.
    validations:
      required: false


================================================
FILE: .github/ISSUE_TEMPLATE/2_feature_request.yml
================================================
name: Feature Request
description: Suggest a new feature or improvement
labels: ["enhancement"]
body:
  - type: textarea
    id: description
    attributes:
      label: Description
      description: Describe the feature or improvement you'd like to see.
    validations:
      required: true
  - type: textarea
    id: use-case
    attributes:
      label: Use case
      description: Why do you need this feature? What problem does it solve?
    validations:
      required: true


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: Ask the community
    url: https://github.com/jackocnr/intl-tel-input/discussions
    about: Ask questions and get help from the community


================================================
FILE: .github/copilot-instructions.md
================================================
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.

When 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.

When 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.

When 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.

When writing code, prioritise clarity and brevity. Try to follow the existing code style as much as possible.

When 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.

When 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.

================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  push:
    branches: [ "**" ]
  pull_request:
    branches: [ "**" ]

permissions:
  contents: read

jobs:
  build-and-test:
    name: Build and Test (Node ${{ matrix.node-version }})
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        node-version: [20]
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          submodules: recursive
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build

      - name: Run JS tests
        run: npm run test:js -- --runInBand

      - name: Run e2e tests
        run: npm run test:e2e:linux

      - name: Upload Playwright artifacts
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: playwright
          path: |
            playwright-report/
            test-results/


================================================
FILE: .gitignore
================================================
/node_modules/
/lib/*
!/lib/libphonenumber
/.sass-cache/
/.grunt/
/tmp/
/vendor/
/.idea/
*.iml
.DS_Store
/build/
/react/build/
/svelte/build/
/vue/build/
/angular/build/
/*/demo/*/*-bundle.js
/test-results/
/playwright-report/
/.claude/

================================================
FILE: .gitmodules
================================================
[submodule "third_party/libphonenumber"]
	path = third_party/libphonenumber
	url = https://github.com/google/libphonenumber


================================================
FILE: .node-version
================================================
v20.12.0


================================================
FILE: .npmrc
================================================
# Jest uses the `vm` module, which does not have production ready module support
# yet. This option lets us use dynamic imports.
node-options='--experimental-vm-modules'


================================================
FILE: .vscode/extensions.json
================================================
{
  "recommendations": [
    "rvest.vs-code-prettier-eslint",
    "davidanson.vscode-markdownlint",
    "aaron-bond.better-comments"
  ]
}


================================================
FILE: .vscode/settings.json
================================================
{
    "editor.defaultFormatter": "rvest.vs-code-prettier-eslint",
    "editor.formatOnPaste": false, //* required
    "editor.formatOnType": false, //* required
    "editor.formatOnSaveMode": "file", //* required to format on save
    "vs-code-prettier-eslint.prettierLast": false, //* set as "true" to run 'prettier' last not first
    "typescript.tsdk": "node_modules/typescript/lib",
    "files.associations": {
      "LICENSE": "text",
    },
    "markdownlint.config": {
      "blanks-around-headings": false,
      "blanks-around-lists": false,
      "ul-style": false,
      "blanks-around-fences": false,
      "ol-prefix": false,
      "no-inline-html": false,
    },
    "cSpell.language": "en-GB",
    "cSpell.enabledLanguageIds": [
      "asciidoc",
      "c",
      "cpp",
      "csharp",
      "css",
      "dotenv",
      "go",
      "handlebars",
      "html",
      "ignore",
      "jade",
      "java",
      "javascript",
      "javascriptreact",
      "json",
      "jsonc",
      "latex",
      "less",
      "markdown",
      "php",
      "plaintext",
      "pug",
      "python",
      "restructuredtext",
      "rust",
      "scala",
      "scss",
      "text",
      "typescript",
      "typescriptreact",
      "yaml",
      "yml",
      "COBOL",
      "ACUCOBOL"
    ],
    "files.trimTrailingWhitespace": true,
    "[markdown]": {
      "files.trimTrailingWhitespace": false
    }
}


================================================
FILE: CHANGELOG.md
================================================
See the Github Releases page for changelog: https://github.com/jackocnr/intl-tel-input/releases

Or 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

## Breaking changes

- v26.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v26.0.0
- v25.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v25.0.0
- v24.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v24.0.0
- v23.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v23.0.0
- v22.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v22.0.0
- v21.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v21.0.0
- v20.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v20.0.0
- v19.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v19.0.0
- v18.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v18.0.0
- v17.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v17.0.0
- v16.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v16.0.0
- v15.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v15.0.0
- v14.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v14.0.0
- v13.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v13.0.0
- v12.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v12.0.0
- v11.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v11.0.0
- v10.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v10.0.0
- v9.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v9.0.0
- v8.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v8.0.0
- v7.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v7.0.0
- v6.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v6.0.0
- v5.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v5.0.0
- v4.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v4.0.0
- v3.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v3.0.0
- v2.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v2.0.0
- v1.0.0 https://github.com/jackocnr/intl-tel-input/releases/tag/v1.0.0


================================================
FILE: Gruntfile.js
================================================
module.exports = function(grunt) {

  // load all tasks from package.json
  require('load-grunt-config')(grunt);
  require('time-grunt')(grunt);
  require('google-closure-compiler').grunt(grunt, {
    platfrom: 'native'
  });

  /**
   * BUILD TASKS
   */
  // build everything ready for a commit
  grunt.registerTask('build', [
    'clean:allBuild',
    'build:img',
    'translations',
    'build:js',
  ]);

  // build translations
  grunt.registerTask('build:translations', [
    'clean:buildJs',
    'clean:tmpIntermediates',
    'translations',
    'build:js',
  ]);

  // build utils
  grunt.registerTask('build:utils', [
    'clean:utils',
    'closure-compiler:utils',
    'shell:checkLpnMetadata',
  ]);

  // just CSS
  grunt.registerTask('build:css', [
    'clean:buildCss',
    'sass',
    'cssmin',
  ]);

  // just images (and CSS)
  grunt.registerTask('build:img', [
    'clean:buildImg',
    'generate-sprite',
    'build:css',
  ]);

  // just javascript
  grunt.registerTask('build:js', [
    'clean:buildJs',
    'clean:tmpIntermediates',
    'shell:eslint',
    'closure-compiler:utils',
    'shell:genTsDeclaration',
    'shell:buildJs',
    'build:components',
  ]);

  // just 4 components
  grunt.registerTask('build:components', [
    'clean:reactBuild',
    'clean:vueBuild',
    'clean:angularBuild',
    'clean:svelteBuild',
    'build:react',
    'build:vue',
    'build:angular',
    'build:svelte',
  ]);

  // Ensure build/js/utils.js exists (src/js/intl-tel-input/utils.js is a symlink to it).
  grunt.registerTask('ensure:utils', 'Build utils if missing', function() {
    if (!grunt.file.exists('build/js/utils.js')) {
      grunt.task.run('closure-compiler:utils');
    }
  });

  // fast version which only builds the main plugin JS files (see root build.js file for details)
  grunt.registerTask('build:jsfast', [
    'clean:buildJsKeepUtils',
    'clean:tmpIntermediates',
    'ensure:utils',
    'shell:buildJs',
  ]);

  // just react
  grunt.registerTask('build:react', [
    'clean:reactBuild',
    'replace:reactWithUtils',
    'shell:genReactTsDeclaration',
    'shell:buildReact',
  ]);

  // just vue
  grunt.registerTask('build:vue', [
    'clean:vueBuild',
    'replace:vueWithUtils',
    'shell:buildVue',
  ]);

  // just angular
  grunt.registerTask('build:angular', [
    'clean:angularBuild',
    'replace:angularWithUtils',
    'shell:genAngularTsDeclarationAndJs',
    'shell:buildAngular',
  ]);

  // just svelte
  grunt.registerTask('build:svelte', [
    'clean:svelteBuild',
    'replace:svelteWithUtils',
    'shell:buildSvelte',
  ]);

  /**
   * VERSIONING TASKS
  */
  // (1) build for tests, and run tests before allowing a version bump
  // (2) bump version number in package.json etc
  // (3) rebuild js to update version numbers in those files, as well as readme etc
  // (4) commit, tag and push
  grunt.registerTask('version', [
    'build:jsfast',
    'shell:test',
    'bump-only',
    'versionNumbers',
    'bump-commit',
  ]);

  grunt.registerTask('version:minor', [
    'build:jsfast',
    'shell:test',
    'bump-only:minor',
    'versionNumbers',
    'bump-commit'
  ]);

  grunt.registerTask('version:major', [
    'build:jsfast',
    'shell:test',
    'bump-only:major',
    'versionNumbers',
    'bump-commit'
  ]);

  // update version numbers in docs etc
  grunt.registerTask('versionNumbers', [
    'replace:siteDocs',
    'replace:issueTemplate',
    'replace:packageLockInner',
  ]);

};


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2014-2016 Jack O'Connor

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# International Telephone Input
[![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)

A JavaScript plugin for entering, formatting and validating international telephone numbers. Includes TypeScript definitions, plus React, Vue, Angular and Svelte components.

[Explore docs »](https://intl-tel-input.com/docs/choose-integration)

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.github.com/jackocnr/intl-tel-input/master/screenshots/vanilla-dark.png">
  <source media="(prefers-color-scheme: light)" srcset="https://raw.github.com/jackocnr/intl-tel-input/master/screenshots/vanilla-light.png">
  <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">
</picture>

## Sponsored by
<img src="https://raw.github.com/jackocnr/intl-tel-input/master/screenshots/twilio.webp" height="100" alt="Twilio"/>

Use [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.

## React, Vue, Angular and Svelte Components
We 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).

## Docs and Examples
We 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.

## Features
* Automatically select the user's current country using an IP lookup
* Automatically set the input placeholder to an example number for the selected country
* Navigate the country dropdown by typing a country's name, or using the up/down keys
* Automatically format the number as the user types
* Optionally, only allow numeric characters and cap the number at the maximum valid length
* The user types their national number, and the plugin gives you the full standardised international number
* Number validation, including specific error types
* High-resolution flag images
* Accessibility provided via ARIA tags
* Typescript type definitions included
* Easily customise styles by overriding CSS variables, e.g. support dark mode
* React, Vue, Angular and Svelte components also included
* Translations provided in over 40 languages, as well as support for RTL layout and alternative numeral sets
* Lots of initialisation options for customisation, as well as instance methods/events for interaction

## Contributing
See 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.

## Attributions
* Flag images from [flag-icons](https://github.com/lipis/flag-icons)
* Original country data from mledoze's [World countries in JSON, CSV and XML](https://github.com/mledoze/countries)
* Formatting/validation/example number code from [libphonenumber](https://github.com/googlei18n/libphonenumber)

User testing powered by [BrowserStack Open-Source Program](https://www.browserstack.com/open-source)  

Browser 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>


================================================
FILE: angular/README.md
================================================
# IntlTelInput Angular Component

An 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).

[Explore docs »](https://intl-tel-input.com/docs/angular-component)


================================================
FILE: angular/build.js
================================================
const { build } = require("esbuild");
const fs = require("fs");
const packageJson = require("../package.json");

const mainShared = {
  bundle: true,
  external: ["@angular/core", "@angular/forms"],
  logLevel: "info",
  minify: false,
  define: { "process.env.VERSION": `"${packageJson.version}"` },
};

async function buildMain() {
  //* Angular Component - Default (ES Modules)
  await build({
    ...mainShared,
    entryPoints: ["angular/build/temp/intl-tel-input/angular.js"],
    format: "esm",
    outfile: "angular/build/IntlTelInput.js",
  });

  //* Angular Component With Utils - Default (ES Modules)
  await build({
    ...mainShared,
    entryPoints: ["angular/build/temp/intl-tel-input/angularWithUtils.js"],
    format: "esm",
    outfile: "angular/build/IntlTelInputWithUtils.js",
  });

  // remove temp folder after builds are complete
  fs.rmSync("angular/build/temp", { recursive: true, force: true });
}

buildMain().catch(console.error);

const demoShared = {
  bundle: true,
  define: { "process.env.VERSION": `"${packageJson.version}"` },
  format: "iife",
};

build({
  ...demoShared,
  entryPoints: ["angular/demo/simple/main.ts"],
  outfile: "angular/demo/simple/simple-bundle.js",
});

build({
  ...demoShared,
  entryPoints: ["angular/demo/validation/main.ts"],
  outfile: "angular/demo/validation/validation-bundle.js",
});

build({
  ...demoShared,
  entryPoints: ["angular/demo/set-number/main.ts"],
  outfile: "angular/demo/set-number/set-number-bundle.js",
});

build({
  ...demoShared,
  entryPoints: ["angular/demo/toggle-disabled/main.ts"],
  outfile: "angular/demo/toggle-disabled/toggle-disabled-bundle.js",
});

build({
  ...demoShared,
  entryPoints: ["angular/demo/form/main.ts"],
  outfile: "angular/demo/form/form-bundle.js",
});

================================================
FILE: angular/demo/form/form.component.ts
================================================
import { Component, OnInit, ViewChild } from "@angular/core";
import {
  FormControl,
  FormGroup,
  ReactiveFormsModule,
  Validators,
} from "@angular/forms";
import { IntlTelInputComponent } from "../../src/intl-tel-input/angularWithUtils";

@Component({
  selector: "app-root",
  template: `
    <form [formGroup]="fg" (ngSubmit)="handleSubmit()">
      <intl-tel-input
        #telInput
        formControlName="phone"
        name="phone"
        [initOptions]="{
          initialCountry: 'us',
        }"
      />
      <button class="button" type="submit" [disabled]="!fg.valid">
        Validate
      </button>
      <div class="notice">
        @if (phone?.errors?.["required"] && phone?.touched) {
          Phone number is required.
        } @else if (phone?.errors?.["invalidPhone"] && phone?.touched) {
          {{ phone?.errors?.["invalidPhone"].errorMessage }}
        } @else if (isSubmitted && fg.valid) {
          Valid number: {{ telInput.getInstance()?.getNumber() }}
        }
      </div>
    </form>
  `,
  standalone: true,
  imports: [IntlTelInputComponent, ReactiveFormsModule],
})
export class AppComponent implements OnInit {
  @ViewChild("telInput") telInput!: IntlTelInputComponent;

  fg: FormGroup = new FormGroup({
    phone: new FormControl<string>("", [Validators.required]),
  });

  isSubmitted = false;

  get phone() {
    return this.fg.get("phone");
  }

  ngOnInit(): void {
    this.phone?.valueChanges.subscribe(() => {
      this.isSubmitted = false;
    });
  }

  handleSubmit(): void {
    this.phone?.markAsTouched();
    if (this.fg.valid) {
      this.isSubmitted = true;
    }
  }
}


================================================
FILE: angular/demo/form/index.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <link rel="stylesheet" href="../../../build/css/intlTelInput.css" />
  <link rel="stylesheet" href="../../../build/css/demo.css" />
  <title>Angular App - Form Validation Demo</title>
</head>

<body>
  <h1>Angular Form Validation Demo</h1>
  <p>A simple Angular app using reactive forms with the IntlTelInput component for phone number entry and validation.</p>
  <p>Enter a phone number and click "Validate" to see form validation in action.</p>
  <app-root></app-root>
  <script src="./form-bundle.js"></script>
</body>

</html>



================================================
FILE: angular/demo/form/main.ts
================================================
import 'zone.js';
import "@angular/compiler";
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './form.component';

bootstrapApplication(AppComponent)
  .catch((err) => console.error(err));

================================================
FILE: angular/demo/set-number/index.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <link rel="stylesheet" href="../../../build/css/intlTelInput.css" />
  <link rel="stylesheet" href="../../../build/css/demo.css" />
  <title>Angular App - Set Number Demo</title>
</head>

<body>
  <h1>Angular App - Set Number Demo</h1>
  <p>A simple Angular app, using the IntlTelInput component to handle phone number entry, calling setNumber and validation.</p>
  <p>Click "Set Number" then click "Validate" to check that the angular internals have updated correctly.</p>
  <app-root></app-root>
  <script src="./set-number-bundle.js"></script>
</body>

</html>



================================================
FILE: angular/demo/set-number/main.ts
================================================
import 'zone.js';
import "@angular/compiler";
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './set-number.component';

bootstrapApplication(AppComponent)
  .catch((err) => console.error(err));

================================================
FILE: angular/demo/set-number/set-number.component.ts
================================================
import { Component, ViewChild } from '@angular/core';
import { IntlTelInputComponent, PHONE_ERROR_MESSAGES } from '../../src/intl-tel-input/angularWithUtils';

@Component({
  selector: "app-root",
  template: `
    <div>
      <intl-tel-input
        #telInput
        (numberChange)="handleNumberChange($event)"
        (validityChange)="handleValidityChange($event)"
        (errorCodeChange)="handleErrorCodeChange($event)"
        [initOptions]="{
          initialCountry: 'us',
        }"
      />
      <button class="button" type="button" (click)="handleSetNumber()">
        Set Number
      </button>
      <button class="button" type="button" (click)="handleSubmit()">
        Validate
      </button>
      @if (notice) {
        <div class="notice">{{ notice }}</div>
      }
    </div>
  `,
  standalone: true,
  imports: [IntlTelInputComponent]
})
export class AppComponent {
  @ViewChild('telInput') telInput!: IntlTelInputComponent;

  isValid: boolean | null = null;
  number: string | null = null;
  errorCode: number | null = null;
  notice: string | null = null;

  handleNumberChange(value: string): void {
    this.number = value;
  }

  handleValidityChange(value: boolean): void {
    this.isValid = value;
  }

  handleErrorCodeChange(value: number | null): void {
    this.errorCode = value;
  }

  handleSetNumber(): void {
    const instance = this.telInput?.getInstance();
    if (instance) {
      instance.setNumber('+14155552671');
    }
  }

  handleSubmit(): void {
    if (this.isValid) {
      this.notice = `Valid number: ${this.number}`;
    } else {
      const errorMessage = PHONE_ERROR_MESSAGES[this.errorCode || 0] || "Invalid number";
      this.notice = `Error: ${errorMessage}`;
    }
  }
}

================================================
FILE: angular/demo/simple/index.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <link rel="stylesheet" href="../../../build/css/intlTelInput.css" />
  <link rel="stylesheet" href="../../../build/css/demo.css" />
  <title>Angular App - Simple Demo</title>
</head>

<body>
  <h1>Angular App - Simple Demo</h1>
  <p>A simple Angular app, using the IntlTelInput component to handle phone number entry.</p>
  <app-root></app-root>
  <script src="./simple-bundle.js"></script>
</body>

</html>



================================================
FILE: angular/demo/simple/main.ts
================================================
import 'zone.js';
import "@angular/compiler";
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './simple.component';

bootstrapApplication(AppComponent)
  .catch((err) => console.error(err));

================================================
FILE: angular/demo/simple/simple.component.ts
================================================
import { Component } from '@angular/core';
import { IntlTelInputComponent } from '../../src/intl-tel-input/angularWithUtils';

@Component({
  selector: "app-root",
  template: `
    <intl-tel-input
      [initOptions]="{
        initialCountry: 'us',
      }"
    />
  `,
  standalone: true,
  imports: [IntlTelInputComponent]
})
export class AppComponent {}

================================================
FILE: angular/demo/toggle-disabled/index.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <link rel="stylesheet" href="../../../build/css/intlTelInput.css" />
  <link rel="stylesheet" href="../../../build/css/demo.css" />
  <title>Angular App - Toggle disabled prop Demo</title>
</head>

<body>
  <h1>Angular App - Toggle disabled prop Demo</h1>
  <p>A simple Angular app, using the IntlTelInput component to show it toggle.</p>
  <p>Click the button to enable/disable component.</p>
  <app-root></app-root>
  <script src="./toggle-disabled-bundle.js"></script>
</body>

</html>



================================================
FILE: angular/demo/toggle-disabled/main.ts
================================================
import 'zone.js';
import "@angular/compiler";
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './toggle-disabled.component';

bootstrapApplication(AppComponent)
  .catch((err) => console.error(err));

================================================
FILE: angular/demo/toggle-disabled/toggle-disabled.component.ts
================================================
import { Component } from '@angular/core';
import { IntlTelInputComponent } from '../../src/intl-tel-input/angularWithUtils';

@Component({
  selector: "app-root",
  template: `
    <div>
      <intl-tel-input
        [disabled]="isDisabled"
      />
      <button class="button" type="button" (click)="toggleDisabled()">Toggle</button>
    </div>
  `,
  standalone: true,
  imports: [IntlTelInputComponent]
})
export class AppComponent {
  isDisabled: boolean = false;

  toggleDisabled(): void {
    this.isDisabled = !this.isDisabled;
  }
}

================================================
FILE: angular/demo/validation/index.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <link rel="stylesheet" href="../../../build/css/intlTelInput.css" />
  <link rel="stylesheet" href="../../../build/css/demo.css" />
  <title>Angular App - Validation Demo</title>
</head>

<body>
  <h1>Angular App - Validation Demo</h1>
  <p>A simple Angular app, using the IntlTelInput component to handle phone number entry and validation.</p>
  <p>Enter a phone number below and click "Validate".</p>
  <app-root></app-root>
  <script src="./validation-bundle.js"></script>
</body>

</html>



================================================
FILE: angular/demo/validation/main.ts
================================================
import 'zone.js';
import "@angular/compiler";
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './validation.component';

bootstrapApplication(AppComponent)
  .catch((err) => console.error(err));

================================================
FILE: angular/demo/validation/validation.component.ts
================================================
import { Component } from '@angular/core';
import { IntlTelInputComponent, PHONE_ERROR_MESSAGES } from '../../src/intl-tel-input/angularWithUtils';

@Component({
  selector: "app-root",
  template: `
    <div>
      <intl-tel-input
        (numberChange)="handleNumberChange($event)"
        (validityChange)="handleValidityChange($event)"
        (errorCodeChange)="handleErrorCodeChange($event)"
        [initOptions]="{
          initialCountry: 'us',
        }"
      />
      <button class="button" type="button" (click)="handleSubmit()">
        Validate
      </button>
      @if (notice) {
        <div class="notice">{{ notice }}</div>
      }
    </div>
  `,
  standalone: true,
  imports: [IntlTelInputComponent]
})
export class AppComponent {
  isValid: boolean | null = null;
  number: string | null = null;
  errorCode: number | null = null;
  notice: string | null = null;

  handleNumberChange(value: string): void {
    this.number = value;
  }

  handleValidityChange(value: boolean): void {
    this.isValid = value;
  }

  handleErrorCodeChange(value: number | null): void {
    this.errorCode = value;
  }

  handleSubmit(): void {
    if (this.isValid) {
      this.notice = `Valid number: ${this.number}`;
    } else {
      const errorMessage = PHONE_ERROR_MESSAGES[this.errorCode || 0] || "Invalid number";
      this.notice = `Error: ${errorMessage}`;
    }
  }
}

================================================
FILE: angular/src/intl-tel-input/angular.ts
================================================
import intlTelInput from "../intl-tel-input";
//* Keep the TS imports separate, as the above line gets substituted in the angularWithUtils build process.
import { Iti } from "../intl-tel-input";
import {
  Component,
  Input,
  OnDestroy,
  ViewChild,
  ElementRef,
  Output,
  EventEmitter,
  forwardRef,
  AfterViewInit,
  OnChanges,
  SimpleChanges,
} from "@angular/core";
import {
  ControlValueAccessor,
  NG_VALUE_ACCESSOR,
  NG_VALIDATORS,
  Validator,
  AbstractControl,
  ValidationErrors,
} from "@angular/forms";
import { SomeOptions } from "../modules/types/public-api";

export { intlTelInput };

export const PHONE_ERROR_MESSAGES: string[] = [
  "invalid",
  "invalid-country-code",
  "too-short",
  "too-long",
  "invalid-format",
];

@Component({
  selector: "intl-tel-input",
  standalone: true,
  template: `
    <input
      type="tel"
      #inputRef
      (input)="handleInput()"
      (blur)="handleBlur($event)"
      (focus)="handleFocus($event)"
      (keydown)="handleKeyDown($event)"
      (keyup)="handleKeyUp($event)"
      (paste)="handlePaste($event)"
      (click)="handleClick($event)"
    />
  `,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => IntlTelInputComponent),
      multi: true,
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => IntlTelInputComponent),
      multi: true,
    },
  ],
})
export class IntlTelInputComponent
  implements
    AfterViewInit,
    OnDestroy,
    OnChanges,
    ControlValueAccessor,
    Validator
{
  @ViewChild("inputRef", { static: true })
  inputRef!: ElementRef<HTMLInputElement>;

  @Input() initialValue?: string;
  @Input() usePreciseValidation: boolean = false;
  @Input() inputProps: Record<string, string> = {};
  @Input() disabled: boolean = false;
  @Input() initOptions?: SomeOptions;

  @Output() numberChange = new EventEmitter<string>();
  @Output() countryChange = new EventEmitter<string>();
  @Output() validityChange = new EventEmitter<boolean>();
  @Output() errorCodeChange = new EventEmitter<number | null>();
  @Output() blur = new EventEmitter<FocusEvent>();
  @Output() focus = new EventEmitter<FocusEvent>();
  @Output() keydown = new EventEmitter<KeyboardEvent>();
  @Output() keyup = new EventEmitter<KeyboardEvent>();
  @Output() paste = new EventEmitter<ClipboardEvent>();
  @Output() click = new EventEmitter<MouseEvent>();

  private iti?: Iti;
  private appliedInputPropKeys = new Set<string>();

  private lastEmittedNumber?: string;
  private lastEmittedCountry?: string;
  private lastEmittedValidity?: boolean;
  private lastEmittedErrorCode?: number | null;

  private countryChangeHandler = () => this.handleInput();
  // eslint-disable-next-line class-methods-use-this
  private onChange: (value: string) => void = () => {};
  // eslint-disable-next-line class-methods-use-this
  private onTouched: () => void = () => {};
  // eslint-disable-next-line class-methods-use-this
  private onValidatorChange: () => void = () => {};

  ngAfterViewInit() {
    if (this.inputRef.nativeElement) {
      this.iti = intlTelInput(this.inputRef.nativeElement, this.initOptions);
    }

    this.inputRef.nativeElement.addEventListener(
      "countrychange",
      this.countryChangeHandler,
    );

    this.applyInputProps();

    if (this.initialValue) {
      this.iti?.setNumber(this.initialValue);
    }

    if (this.disabled) {
      this.iti?.setDisabled(this.disabled);
    }
  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes["disabled"]) {
      this.iti?.setDisabled(this.disabled);
    }

    if (changes["inputProps"]) {
      this.applyInputProps();
    }
  }

  handleInput() {
    if (!this.iti) return;

    const num = this.iti.getNumber() || "";
    const countryIso = this.iti.getSelectedCountryData().iso2 || "";

    let hasChanged = false;
    if (num !== this.lastEmittedNumber) {
      this.lastEmittedNumber = num;
      this.numberChange.emit(num);
      this.onChange(num);
      hasChanged = true;
    }

    if (countryIso !== this.lastEmittedCountry) {
      this.lastEmittedCountry = countryIso;
      this.countryChange.emit(countryIso);
      hasChanged = true;
    }

    const isValid = this.usePreciseValidation
      ? this.iti.isValidNumberPrecise()
      : this.iti.isValidNumber();

    const errorCode = isValid ? null : this.iti.getValidationError();

    if (isValid !== this.lastEmittedValidity) {
      this.lastEmittedValidity = isValid;
      this.validityChange.emit(isValid);
      hasChanged = true;
    }

    if (errorCode !== this.lastEmittedErrorCode) {
      this.lastEmittedErrorCode = errorCode;
      this.errorCodeChange.emit(errorCode);
      hasChanged = true;
    }

    if (hasChanged) {
      this.onValidatorChange();
    }
  }

  handleBlur(event: FocusEvent) {
    this.onTouched();
    this.blur.emit(event);
  }

  handleFocus(event: FocusEvent) {
    this.focus.emit(event);
  }

  handleKeyDown(event: KeyboardEvent) {
    this.keydown.emit(event);
  }

  handleKeyUp(event: KeyboardEvent) {
    this.keyup.emit(event);
  }

  handlePaste(event: ClipboardEvent) {
    this.paste.emit(event);
  }

  handleClick(event: MouseEvent) {
    this.click.emit(event);
  }

  /**
   * This method must be called in `ngAfterViewInit` or later lifecycle hooks,
   * not in `ngOnInit` or the `constructor`, as the component needs to be fully initialized.
   */
  getInstance(): Iti | null {
    return this.iti;
  }

  /**
   * This method must be called in `ngAfterViewInit` or later lifecycle hooks,
   * not in `ngOnInit` or the `constructor`, as the component needs to be fully initialized.
   */
  getInput(): HTMLInputElement | null {
    return this.inputRef.nativeElement;
  }

  ngOnDestroy() {
    this.iti?.destroy();

    this.inputRef.nativeElement.removeEventListener(
      "countrychange",
      this.countryChangeHandler,
    );
  }

  private applyInputProps(): void {
    const currentKeys = new Set<string>();
    Object.entries(this.inputProps).forEach(([key, value]) => {
      currentKeys.add(key);
      this.inputRef.nativeElement.setAttribute(key, value);
    });
    this.appliedInputPropKeys.forEach((key) => {
      if (!currentKeys.has(key)) {
        this.inputRef.nativeElement.removeAttribute(key);
      }
    });
    this.appliedInputPropKeys = currentKeys;
  }

  // ============ ControlValueAccessor Implementation ============

  writeValue(value: string | null): void {
    if (this.iti) {
      this.iti.setNumber(value || "");
    }
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
    this.iti?.setDisabled(isDisabled);
  }

  // ============ Validator Implementation ============

  validate(control: AbstractControl): ValidationErrors | null {
    if (!control.value || !this.iti) {
      return null;
    }

    const isValid = this.usePreciseValidation
      ? this.iti.isValidNumberPrecise()
      : this.iti.isValidNumber();

    if (isValid) {
      return null;
    }

    const errorCode = this.iti.getValidationError();
    return {
      invalidPhone: {
        errorCode,
        errorMessage: PHONE_ERROR_MESSAGES[errorCode] ?? "unknown",
      },
    };
  }

  registerOnValidatorChange(fn: () => void): void {
    this.onValidatorChange = fn;
  }
}


================================================
FILE: angular/src/intl-tel-input/angularWithUtils.ts
================================================
//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.
import intlTelInput from "./intlTelInputWithUtils";
//* Keep the TS imports separate, as the above line gets substituted in the angularWithUtils build process.
import { Iti } from "../intl-tel-input";
import {
  Component,
  Input,
  OnDestroy,
  ViewChild,
  ElementRef,
  Output,
  EventEmitter,
  forwardRef,
  AfterViewInit,
  OnChanges,
  SimpleChanges,
} from "@angular/core";
import {
  ControlValueAccessor,
  NG_VALUE_ACCESSOR,
  NG_VALIDATORS,
  Validator,
  AbstractControl,
  ValidationErrors,
} from "@angular/forms";
import { SomeOptions } from "../modules/types/public-api";

export { intlTelInput };

export const PHONE_ERROR_MESSAGES: string[] = [
  "invalid",
  "invalid-country-code",
  "too-short",
  "too-long",
  "invalid-format",
];

@Component({
  selector: "intl-tel-input",
  standalone: true,
  template: `
    <input
      type="tel"
      #inputRef
      (input)="handleInput()"
      (blur)="handleBlur($event)"
      (focus)="handleFocus($event)"
      (keydown)="handleKeyDown($event)"
      (keyup)="handleKeyUp($event)"
      (paste)="handlePaste($event)"
      (click)="handleClick($event)"
    />
  `,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => IntlTelInputComponent),
      multi: true,
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => IntlTelInputComponent),
      multi: true,
    },
  ],
})
export class IntlTelInputComponent
  implements
    AfterViewInit,
    OnDestroy,
    OnChanges,
    ControlValueAccessor,
    Validator
{
  @ViewChild("inputRef", { static: true })
  inputRef!: ElementRef<HTMLInputElement>;

  @Input() initialValue?: string;
  @Input() usePreciseValidation: boolean = false;
  @Input() inputProps: Record<string, string> = {};
  @Input() disabled: boolean = false;
  @Input() initOptions?: SomeOptions;

  @Output() numberChange = new EventEmitter<string>();
  @Output() countryChange = new EventEmitter<string>();
  @Output() validityChange = new EventEmitter<boolean>();
  @Output() errorCodeChange = new EventEmitter<number | null>();
  @Output() blur = new EventEmitter<FocusEvent>();
  @Output() focus = new EventEmitter<FocusEvent>();
  @Output() keydown = new EventEmitter<KeyboardEvent>();
  @Output() keyup = new EventEmitter<KeyboardEvent>();
  @Output() paste = new EventEmitter<ClipboardEvent>();
  @Output() click = new EventEmitter<MouseEvent>();

  private iti?: Iti;
  private appliedInputPropKeys = new Set<string>();

  private lastEmittedNumber?: string;
  private lastEmittedCountry?: string;
  private lastEmittedValidity?: boolean;
  private lastEmittedErrorCode?: number | null;

  private countryChangeHandler = () => this.handleInput();
  // eslint-disable-next-line class-methods-use-this
  private onChange: (value: string) => void = () => {};
  // eslint-disable-next-line class-methods-use-this
  private onTouched: () => void = () => {};
  // eslint-disable-next-line class-methods-use-this
  private onValidatorChange: () => void = () => {};

  ngAfterViewInit() {
    if (this.inputRef.nativeElement) {
      this.iti = intlTelInput(this.inputRef.nativeElement, this.initOptions);
    }

    this.inputRef.nativeElement.addEventListener(
      "countrychange",
      this.countryChangeHandler,
    );

    this.applyInputProps();

    if (this.initialValue) {
      this.iti?.setNumber(this.initialValue);
    }

    if (this.disabled) {
      this.iti?.setDisabled(this.disabled);
    }
  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes["disabled"]) {
      this.iti?.setDisabled(this.disabled);
    }

    if (changes["inputProps"]) {
      this.applyInputProps();
    }
  }

  handleInput() {
    if (!this.iti) return;

    const num = this.iti.getNumber() || "";
    const countryIso = this.iti.getSelectedCountryData().iso2 || "";

    let hasChanged = false;
    if (num !== this.lastEmittedNumber) {
      this.lastEmittedNumber = num;
      this.numberChange.emit(num);
      this.onChange(num);
      hasChanged = true;
    }

    if (countryIso !== this.lastEmittedCountry) {
      this.lastEmittedCountry = countryIso;
      this.countryChange.emit(countryIso);
      hasChanged = true;
    }

    const isValid = this.usePreciseValidation
      ? this.iti.isValidNumberPrecise()
      : this.iti.isValidNumber();

    const errorCode = isValid ? null : this.iti.getValidationError();

    if (isValid !== this.lastEmittedValidity) {
      this.lastEmittedValidity = isValid;
      this.validityChange.emit(isValid);
      hasChanged = true;
    }

    if (errorCode !== this.lastEmittedErrorCode) {
      this.lastEmittedErrorCode = errorCode;
      this.errorCodeChange.emit(errorCode);
      hasChanged = true;
    }

    if (hasChanged) {
      this.onValidatorChange();
    }
  }

  handleBlur(event: FocusEvent) {
    this.onTouched();
    this.blur.emit(event);
  }

  handleFocus(event: FocusEvent) {
    this.focus.emit(event);
  }

  handleKeyDown(event: KeyboardEvent) {
    this.keydown.emit(event);
  }

  handleKeyUp(event: KeyboardEvent) {
    this.keyup.emit(event);
  }

  handlePaste(event: ClipboardEvent) {
    this.paste.emit(event);
  }

  handleClick(event: MouseEvent) {
    this.click.emit(event);
  }

  /**
   * This method must be called in `ngAfterViewInit` or later lifecycle hooks,
   * not in `ngOnInit` or the `constructor`, as the component needs to be fully initialized.
   */
  getInstance(): Iti | null {
    return this.iti;
  }

  /**
   * This method must be called in `ngAfterViewInit` or later lifecycle hooks,
   * not in `ngOnInit` or the `constructor`, as the component needs to be fully initialized.
   */
  getInput(): HTMLInputElement | null {
    return this.inputRef.nativeElement;
  }

  ngOnDestroy() {
    this.iti?.destroy();

    this.inputRef.nativeElement.removeEventListener(
      "countrychange",
      this.countryChangeHandler,
    );
  }

  private applyInputProps(): void {
    const currentKeys = new Set<string>();
    Object.entries(this.inputProps).forEach(([key, value]) => {
      currentKeys.add(key);
      this.inputRef.nativeElement.setAttribute(key, value);
    });
    this.appliedInputPropKeys.forEach((key) => {
      if (!currentKeys.has(key)) {
        this.inputRef.nativeElement.removeAttribute(key);
      }
    });
    this.appliedInputPropKeys = currentKeys;
  }

  // ============ ControlValueAccessor Implementation ============

  writeValue(value: string | null): void {
    if (this.iti) {
      this.iti.setNumber(value || "");
    }
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
    this.iti?.setDisabled(isDisabled);
  }

  // ============ Validator Implementation ============

  validate(control: AbstractControl): ValidationErrors | null {
    if (!control.value || !this.iti) {
      return null;
    }

    const isValid = this.usePreciseValidation
      ? this.iti.isValidNumberPrecise()
      : this.iti.isValidNumber();

    if (isValid) {
      return null;
    }

    const errorCode = this.iti.getValidationError();
    return {
      invalidPhone: {
        errorCode,
        errorMessage: PHONE_ERROR_MESSAGES[errorCode] ?? "unknown",
      },
    };
  }

  registerOnValidatorChange(fn: () => void): void {
    this.onValidatorChange = fn;
  }
}


================================================
FILE: angular/tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "es6",
    "module": "esnext",
    "moduleResolution": "node",
    "esModuleInterop": true, //* Required for react (etc) default imports.
    "experimentalDecorators": true, //* Required for angular decorators.
    "emitDecoratorMetadata": true, //* Required for angular decorators.
    "declaration": true, //* Generate .d.ts files.
    "outDir": "build/temp",
    "declarationDir": "build/types",
    "rootDir": "./src",
    "allowJs": true, // allow importing .mjs files e.g. i18n files
  },
  "angularCompilerOptions": {
    "strictTemplates": true,
  },
  "include": [
    "src/intl-tel-input/angular.ts",
    "src/intl-tel-input/angularWithUtils.ts",
    "src/intl-tel-input/i18n/index.ts",
  ],
}

================================================
FILE: build.js
================================================
/* eslint-disable no-undef */
/* eslint-disable @typescript-eslint/no-var-requires */
const { build } = require("esbuild");
const packageJson = require("./package.json");

const getBanner = (moduleName) =>
  "/*\n" +
  ` * International Telephone Input v${packageJson.version}\n` +
  ` * ${packageJson.repository.url}\n` +
  " * Licensed under the MIT license\n" +
  " */\n\n" +
  // we can remove this UMD hack once it is supported by esbuild: https://github.com/evanw/esbuild/issues/507
  "// UMD\n" +
  "(function(factory) {\n" +
  "  if (typeof module === 'object' && module.exports) {\n" +
  "    module.exports = factory();\n" +
  "  } else {\n" +
  `    window.${moduleName} = factory();\n` +
  "  }\n" +
  "}(() => {\n";

const footer =
  "\n// UMD\n" +
  "  return factoryOutput.default;\n" +
  "}));";

const shared = {
  bundle: true,
  logLevel: "info",
  format: "iife",
  globalName: "factoryOutput",
  footer: {
    js: footer,
  },
  define: {
    "process.env.VERSION": `"${packageJson.version}"`,
  },
};

//* build/js/intlTelInput.js
build({
  ...shared,
  banner: {
    js: getBanner("intlTelInput"),
  },
  entryPoints: ["src/js/intl-tel-input.ts"],
  minify: false,
  outfile: "build/js/intlTelInput.js",
});

//* build/js/intlTelInput.min.js
build({
  ...shared,
  banner: {
    js: getBanner("intlTelInput"),
  },
  entryPoints: ["src/js/intl-tel-input.ts"],
  minify: true,
  outfile: "build/js/intlTelInput.min.js",
});

//* build/js/data.js
build({
  ...shared,
  banner: {
    js: getBanner("allCountries"),
  },
  entryPoints: ["src/js/intl-tel-input/data.ts"],
  minify: false,
  outfile: "build/js/data.js",
});

//* build/js/data.min.js
build({
  ...shared,
  banner: {
    js: getBanner("allCountries"),
  },
  entryPoints: ["src/js/intl-tel-input/data.ts"],
  minify: true,
  outfile: "build/js/data.min.js",
});

//* build/js/intlTelInputWithUtils.js
build({
  ...shared,
  banner: {
    js: getBanner("intlTelInput"),
  },
  entryPoints: ["src/js/intl-tel-input/intlTelInputWithUtils.ts"],
  minify: false,
  outfile: "build/js/intlTelInputWithUtils.js",
});

//* build/js/intlTelInputWithUtils.min.js
build({
  ...shared,
  banner: {
    js: getBanner("intlTelInput"),
  },
  entryPoints: ["src/js/intl-tel-input/intlTelInputWithUtils.ts"],
  minify: true,
  outfile: "build/js/intlTelInputWithUtils.min.js",
});

//* build/js/i18n
build({
  charset: "utf8",
  entryPoints: ["src/js/intl-tel-input/i18n/**/*.ts"],
  outdir: "build/js/i18n",
});

================================================
FILE: composer.json
================================================
{
  "name": "jackocnr/intl-tel-input",
  "version": "26.8.1",
  "description": "A JavaScript plugin for entering and validating international telephone numbers",
  "keywords": [
    "international",
    "i18n",
    "country",
    "dial",
    "code",
    "telephone",
    "tel",
    "number",
    "mobile",
    "input",
    "flag"
  ],
  "homepage": "https://github.com/jackocnr/intl-tel-input",
  "type": "library",
  "license": "MIT",
  "authors": [
    {
      "name": "Jack O'Connor",
      "homepage": "http://jackocnr.com"
    }
  ],
  "minimum-stability": "stable",
  "prefer-stable": true
}


================================================
FILE: cspell.json
================================================
{
  "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json",
  "version": "0.2",
  "allowCompoundWords": true,
  "maxNumberOfProblems": 1000,
  // "checkLimit": 5000, //! Setting that is in the vscode eextension not in the cli
  // "diagnosticLevel": "Warning", //! Setting that is in the vscode eextension not in the cli
  "language": "en_GB",
  "useGitignore": true,
  "minWordLength": 4,
  "dictionaries": [
    "css",
    "en_GB",
    "html",
    "ignore",
    "javascript",
    "javascriptreact",
    "json",
    "jsonc",
    "markdown",
    "plaintext",
    "scss",
    "text",
    "typescript",
    "typescriptreact",
    "COBOL",
    "ACUCOBOL"
  ],
  "dictionaryDefinitions": [],
  "import": [],
  "ignorePaths": [
    //* Directories
    "**/.git/**",
    "**/build/**",
    "**/node_modules/**",
    "**/vscode-extension/**",
    "**/.vscode/**",
    "**/.scraps/**",
    "**/third_party/**",
    //* Files
    "**/package-lock.json",
    "**/yarn.lock",
    "**/Gemfile",
    "**/cspell.json",
    "**/.env",
    //* File Extensions
    "**/*.svg",
    //* Specific files and folders
    "src/js/intl-tel-input/data.ts",
    "react/demo/validation-bundle.js",
    "react/demo/simple-bundle.js",
    "react/demo/toggle-disabled.js"
  ],
  "ignoreWords": [],
  "flagWords": [],
  "words": [
    "Åland",
    "AYTF",
    "dropup",
    "evenizer",
    "FAYT",
    "geoip",
    "iife",
    "ipapi",
    "jackocnr",
    "jsfast",
    "librsvg",
    "mledoze's",
    "NANP",
    "normalised",
    "optipng",
    "sass",
    "tmpl",
    "vars",
    "Veaudry"
  ]
}


================================================
FILE: demo.html
================================================
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>International Telephone Input</title>
    <link rel="stylesheet" href="build/css/intlTelInput.css" />
    <link rel="stylesheet" href="build/css/demo.css" />
  </head>

  <body>
    <h1>International Telephone Input</h1>
    <form>
      <input id="phone" name="phone" type="tel" value="" />
      <button class="button" id="btn" type="button">Validate</button>
      <span id="valid-msg" class="hide"></span>
      <span id="error-msg" class="hide"></span>
    </form>

    <script src="build/js/intlTelInputWithUtils.js"></script>
    <script>
      const input = document.querySelector("#phone");
      const iti = window.intlTelInput(input, {
        // allowDropdown: false,
        // allowedNumberTypes: ["TOLL_FREE"],
        // allowNumberExtensions: true,
        // allowPhonewords: true,
        // autoPlaceholder: "off",
        // containerClass: "test",
        // countryNameLocale: "ru",
        // countryOrder: ["jp", "kr"],
        // countrySearch: false,
        // customPlaceholder: function(selectedCountryPlaceholder, selectedCountryData) {
        //   return "e.g. " + selectedCountryPlaceholder;
        // },
        // dropdownContainer: document.querySelector('#custom-container'),
        // excludeCountries: ["us"],
        // fixDropdownWidth: false,
        // formatAsYouType: false,
        // formatOnDisplay: false,
        // geoIpLookup: function(success, failure) {
        //   fetch("https://ipapi.co/json")
        //     .then((res) => res.json())
        //     .then((data) => success(data.country_code))
        //     .catch(() => failure());
        // },
        // hiddenInput: () => ({ phone: "phone_full", country: "country_code" }),
        // i18n: { searchPlaceholder: "Custom search" },
        // initialCountry: "us",
        // loadUtils: () => import("/build/js/utils.js"), // leading slash (and http-server) required for this to work in chrome
        // nationalMode: false,
        // onlyCountries: ['us', 'gb', 'ch', 'ca', 'do'],
        // placeholderNumberType: "MOBILE",
        // showFlags: false,
        // separateDialCode: true,
        // strictMode: true,
        // useFullscreenPopup: true,
      });
      window.iti = iti; // useful for testing

      const button = document.querySelector("#btn");
      const errorMsg = document.querySelector("#error-msg");
      const validMsg = document.querySelector("#valid-msg");
      const errorMap = ["Invalid number", "Invalid country code", "Too short", "Too long", "Invalid number"];

      const reset = () => {
        input.classList.remove("error");
        errorMsg.innerHTML = "";
        validMsg.innerHTML = "";
        errorMsg.classList.add("hide");
        validMsg.classList.add("hide");
      };

      const showError = (msg) => {
        input.classList.add("error");
        errorMsg.innerHTML = msg;
        errorMsg.classList.remove("hide");
      };

      // on click button: validate
      button.addEventListener('click', () => {
        reset();
        if (!input.value.trim()) {
          showError("Required");
        } else if (iti.isValidNumber()) {
          validMsg.innerHTML = "Valid number: " + iti.getNumber();
          validMsg.classList.remove("hide");
        } else {
          const errorCode = iti.getValidationError();
          const msg = errorMap[errorCode] || "Invalid number";
          showError(msg);
        }
      });

      // on keyup / change flag: reset
      input.addEventListener('change', reset);
      input.addEventListener('keyup', reset);
    </script>
  </body>
</html>


================================================
FILE: functions/_middleware.js
================================================
/**
 * Cloudflare Workers middleware to inject a script tag with the user's geographical information (window.__IS_EUROPE)
 */

const EUROPE = new Set([
  // EU members
  "AT","BE","BG","HR","CY","CZ","DK","EE","FI","FR",
  "DE","GR","HU","IE","IT","LV","LT","LU","MT","NL",
  "PL","PT","RO","SK","SI","ES","SE",
  // EEA + other European countries
  "GB","NO","IS","LI","CH","AL","AD","BA","BY","GE",
  "MD","ME","MK","MC","RS","SM","TR","UA","VA",
]);

class GeoInjector {
  constructor(isEurope) {
    this.isEurope = isEurope;
  }

  element(element) {
    element.prepend(
      `<script>window.__IS_EUROPE=${this.isEurope};</script>`,
      { html: true },
    );
  }
}

export async function onRequest(context) {
  const country = context.request.cf?.country || "";
  const isEurope = EUROPE.has(country);
  const response = await context.next();

  const contentType = response.headers.get("content-type") || "";
  if (!contentType.includes("text/html")) {
    return response;
  }

  // eslint-disable-next-line no-undef
  return new HTMLRewriter()
    .on("head", new GeoInjector(isEurope))
    .transform(response);
}

================================================
FILE: grunt/bump.js
================================================
module.exports = function(grunt) {
  return {
    options: {
      files: ['package.json', 'package-lock.json', 'composer.json'],
      updateConfigs: ['package'],
      commitFiles: ['-a'],
      pushTo: 'origin'
    }
  };
};


================================================
FILE: grunt/clean.js
================================================
module.exports = function(grunt) {
  return {
    buildCss: ['build/css/*'],
    buildImg: ['build/img/*'],
    buildJs: ['build/js/*'],

    // Used by build:jsfast/watch. Preserves build/js/utils.js because
    // src/js/intl-tel-input/utils.js is a symlink to it.
    buildJsKeepUtils: ['build/js/*', '!build/js/utils.js'],

    reactBuild: ['react/build/*'],
    vueBuild: ['vue/build/*'],
    angularBuild: ['angular/build/*'],
    svelteBuild: ['svelte/build/*'],

    // Intermediate artifacts used by the build/minify/replace steps.
    tmpIntermediates: ['tmp/built.min.js', 'tmp/one.min.js'],

    // build:utils output
    utils: ['build/js/utils.js'],

    // Convenience target for top-level build.
    allBuild: [
      'build/css/*',
      'build/img/*',
      'build/js/*',
      'react/build/*',
      'vue/build/*',
      'angular/build/*',
      'svelte/build/*',
      'tmp/built.min.js',
      'tmp/one.min.js',
    ],
  };
};


================================================
FILE: grunt/closure-compiler.js
================================================
module.exports = function (grunt) {
  return {
    utils: {
      files: {
        "build/js/utils.js": "src/js/utils.js",
      },
      options: {
        js: [
          "node_modules/google-closure-library/**.js",
          "third_party/libphonenumber/javascript/i18n/phonenumbers/**.js",
          "!third_party/libphonenumber/javascript/i18n/phonenumbers/demo-compiled.js",
          "!third_party/libphonenumber/javascript/i18n/phonenumbers/metadatafortesting.js",
          "!third_party/libphonenumber/javascript/i18n/phonenumbers/metadatalite.js",
          "!third_party/libphonenumber/javascript/i18n/phonenumbers/regioncodefortesting.js",
          "!third_party/libphonenumber/javascript/i18n/phonenumbers/**_test.js",
        ],
        entry_point: "goog:i18n.phonenumbers.demo",
        compilation_level: "ADVANCED_OPTIMIZATIONS",
        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;",
      },
    },
  };
};


================================================
FILE: grunt/connect.js
================================================
module.exports = function(grunt) {
  return {
    test: {
      port: 8000
    }
  };
};


================================================
FILE: grunt/cssmin.js
================================================
module.exports = function(grunt) {
  return {
    target: {
      files: {
        'build/css/intlTelInput.min.css': 'build/css/intlTelInput.css',
        'build/css/intlTelInput-no-assets.min.css': 'build/css/intlTelInput-no-assets.css'
      }
    }
  };
};


================================================
FILE: grunt/generate-sprite.js
================================================
const fs = require('fs');
const path = require('path');
// ts-node allows us to require TypeScript files
require("ts-node").register();
const supportedCountries = require('../src/js/intl-tel-input/data.ts').default;

module.exports = function(grunt) {
  grunt.registerTask('generate-sprite', async function() {
    const done = this.async();
    // 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
    const sharp = require('sharp');
    // ensure /build/img/ dir exists before trying to write to it
    const buildImgDir = path.join(__dirname, '..', 'build', 'img');
    if (!fs.existsSync(buildImgDir)) {
      fs.mkdirSync(buildImgDir, { recursive: true });
    }

    const supportedCountryFilenames = supportedCountries.map(country => `${country.iso2}.svg`).sort();

    // customise this number to change the size of the flags (NOTE: flags are 4x3 ratio)
    // must be a multiple of 3
    const TARGET_HEIGHT = 12;

    const TARGET_WIDTH = (TARGET_HEIGHT / 3) * 4;
    const FLAG_MARGIN = 0;

    const specialCases = {
      'ac.svg': 'sh-ac.svg', // Ascension Island
      // Add more special cases here if needed
    };

    const handleSpecialCases = (filename) => specialCases[filename] || filename;

    const generateFlagMetadataAndSprite = async () => {
      try {
        const fileWarning = "//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.";
        const flagsPath = 'node_modules/flag-icons/flags/4x3';
        const outputFile = 'src/css/_metadata.scss';
        const spriteFile1xWebP = "build/img/flags.webp";
        const spriteFile2xWebP = "build/img/flags@2x.webp";
        const spriteFile1xPNG = "build/img/flags.png";
        const spriteFile2xPNG = "build/img/flags@2x.png";
        let outputFileContent = '';

        let totalWidth = supportedCountryFilenames.length * (TARGET_WIDTH + FLAG_MARGIN) - FLAG_MARGIN;
        const maxHeight = TARGET_HEIGHT;

        let flagsMetadata = "$flags: (\n";
        let currentOffset = 0;

        const scaledImages1x = [];
        const scaledImages2x = [];

        for (const filename of supportedCountryFilenames) {
          const countryCode = filename.split('.')[0];
          const processedFilename = handleSpecialCases(filename);
          const imagePath = path.join(flagsPath, processedFilename);
          const imagePathExists = fs.existsSync(imagePath);

          if (!imagePathExists) {
            console.log(`WARNING: Missing flag image: ${imagePath} - skipping this flag.`);
            break;
          }

          const svgBuffer = fs.readFileSync(imagePath);

          const pngBuffer1x = await sharp(svgBuffer)
            .resize({
              width: TARGET_WIDTH,
              height: TARGET_HEIGHT,
              fit: sharp.fit.fill,
              position: sharp.strategy.centre
            })
            .ensureAlpha()
            .png({ compressionLevel: 9, adaptiveFiltering: true, force: true })
            .toBuffer();

          const pngBuffer2x = await sharp(svgBuffer)
            .resize({
              width: TARGET_WIDTH * 2,
              height: TARGET_HEIGHT * 2,
              fit: sharp.fit.fill,
              position: sharp.strategy.centre
            })
            .ensureAlpha()
            .png({ compressionLevel: 9, adaptiveFiltering: true, force: true })
            .toBuffer();

          scaledImages1x.push({
            buffer: pngBuffer1x,
            offset: currentOffset
          });

          scaledImages2x.push({
            buffer: pngBuffer2x,
            offset: currentOffset * 2
          });

          flagsMetadata += `  ${countryCode}: (\n`;
          flagsMetadata += `    offset: ${-currentOffset}px,\n`;
          flagsMetadata += "  ),\n";

          currentOffset += TARGET_WIDTH + FLAG_MARGIN;
        }
        flagsMetadata += ");";

        // Create 1x sprites
        await createSprite(scaledImages1x, totalWidth, maxHeight, spriteFile1xWebP, 'webp');
        await createSprite(scaledImages1x, totalWidth, maxHeight, spriteFile1xPNG, 'png');
        console.log(`1x combined images saved as ${spriteFile1xWebP} and ${spriteFile1xPNG}`);

        // Create 2x sprites
        await createSprite(scaledImages2x, totalWidth * 2, maxHeight * 2, spriteFile2xWebP, 'webp');
        await createSprite(scaledImages2x, totalWidth * 2, maxHeight * 2, spriteFile2xPNG, 'png');
        console.log(`2x combined images saved as ${spriteFile2xWebP} and ${spriteFile2xPNG}`);

        // Generate SCSS content
        outputFileContent += fileWarning + "\n\n";

        outputFileContent += `$flags-sprite-1x: (\n`;
        outputFileContent += `  height: ${maxHeight}px,\n`;
        outputFileContent += `  width: ${totalWidth}px,\n`;
        outputFileContent += ");\n\n";

        outputFileContent += `$flag-width: ${TARGET_WIDTH}px;\n\n`;
        outputFileContent += `$flag-height: ${TARGET_HEIGHT}px;\n\n`;

        outputFileContent += flagsMetadata + "\n\n";
        outputFileContent += fileWarning + "\n";

        fs.writeFileSync(outputFile, outputFileContent);
        console.log('SCSS file generated successfully.');
        done();
      } catch (error) {
        console.error('Error:', error);
        done(error);
      }
    };

  const createSprite = async (images, width, height, outputFile, format) => {
      const combinedImage = sharp({
        create: {
          width: width,
          height: height,
          channels: 4,
          background: { r: 0, g: 0, b: 0, alpha: 0 }
        }
      });

      const compositeOperations = images.map((img) => ({
        input: img.buffer,
        left: img.offset,
        top: 0
      }));

      let processedImage = combinedImage.composite(compositeOperations);

      if (format === 'webp') {
        processedImage = processedImage.webp({
          quality: 100,
          lossless: true,
          effort: 6
        });
      } else if (format === 'png') {
        processedImage = processedImage.png({
          compressionLevel: 9,
          adaptiveFiltering: true,
          force: true
        });
      }

      await processedImage.toFile(outputFile);
    };

    generateFlagMetadataAndSprite();
  });
};

================================================
FILE: grunt/replace.js
================================================
module.exports = function(grunt) {
  return {

    /**************
     * Update version numbers
     **************/
    siteDocs: {
      options: {
        patterns: [
          {
            match: /intl-tel-input@([0-9.]+)\/build/g,
            replacement: 'intl-tel-input@<%= package.version %>/build'
          }
        ]
      },
      files: {
        'site/src/docs/markdown/options.md': 'site/src/docs/markdown/options.md',
        'site/src/docs/markdown/getting_started.md': 'site/src/docs/markdown/getting_started.md',
      }
    },
    issueTemplate: {
      options: {
        patterns: [
          {
            match: /the latest version \(v[0-9]+\.[0-9]+\.[0-9]+\)/,
            replacement: 'the latest version (v<%= package.version %>)'
          }
        ]
      },
      files: {
        '.github/ISSUE_TEMPLATE/1_bug_report.yml': '.github/ISSUE_TEMPLATE/1_bug_report.yml'
      }
    },
    // 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
    packageLockInner: {
      options: {
        patterns: [
          {
            match: /"name": "intl-tel-input",\n      "version": "[0-9]+\.[0-9]+\.[0-9]+"/,
            replacement: '"name": "intl-tel-input",\n      "version": "<%= package.version %>"'
          }
        ]
      },
      files: {
        'package-lock.json': 'package-lock.json'
      }
    },

    /**************
     * Generate reactWithUtils.tsx
     **************/
    reactWithUtils: {
      options: {
        patterns: [
          {
            match: /import intlTelInput from \"\.\.\/intl\-tel\-input\"\;/,
            replacement: '//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\nimport intlTelInput from "./intlTelInputWithUtils";'
          }
        ]
      },
      files: {
        'react/src/intl-tel-input/reactWithUtils.tsx': 'react/src/intl-tel-input/react.tsx',
      }
    },

    /**************
     * Generate vue/src/IntlTelInputWithUtils.vue
     **************/
    vueWithUtils: {
      options: {
        patterns: [
          {
            match: /\<script setup lang=\"ts\"\>\simport intlTelInput from \"\.\/intl\-tel\-input\"\;/,
            replacement: '<!-- THIS FILE IS AUTO-GENERATED. DO NOT EDIT. -->\n<script setup lang="ts">\nimport intlTelInput from "./intl-tel-input/intlTelInputWithUtils";'
          }
        ]
      },
      files: {
        'vue/src/IntlTelInputWithUtils.vue': 'vue/src/IntlTelInput.vue',
      }
    },

    /**************
     * Generate angular/src/angularWithUtils.ts
     **************/
    angularWithUtils: {
      options: {
        patterns: [
          {
            match: /import intlTelInput from \"\.\.\/intl\-tel\-input\"\;/,
            replacement: '//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\nimport intlTelInput from "./intlTelInputWithUtils";'
          }
        ]
      },
      files: {
        'angular/src/intl-tel-input/angularWithUtils.ts': 'angular/src/intl-tel-input/angular.ts',
      }
    },

    /**************
     * Generate svelte/src/IntlTelInputWithUtils.svelte
     **************/
    svelteWithUtils: {
      options: {
        patterns: [
          {
            match: /\<script lang="ts"\>\s*import intlTelInput from "\.\.\/intl\-tel\-input"\;/,
            replacement: '<!-- THIS FILE IS AUTO-GENERATED. DO NOT EDIT. -->\n<script lang="ts">\nimport intlTelInput from "./intlTelInputWithUtils";'
          }
        ]
      },
      files: {
        'svelte/src/intl-tel-input/IntlTelInputWithUtils.svelte': 'svelte/src/intl-tel-input/IntlTelInput.svelte',
      }
    },
  };
};

================================================
FILE: grunt/sass.js
================================================
const sass = require('sass');

module.exports = function(grunt) {
  return {
    main: {
      options: {
        implementation: sass,
        sourcemap: "none",
        style: "compressed"
      },
      files: {
        'build/css/intlTelInput-no-assets.css': 'src/css/intlTelInput.scss',
        'build/css/intlTelInput.css': 'src/css/intlTelInputWithAssets.scss'
      }
    },
    demo: {
      options: {
        implementation: sass,
        sourcemap: "none"
      },
      files: {
        'build/css/demo.css': 'src/css/demo.scss'
      }
    }
  };
};


================================================
FILE: grunt/shell.js
================================================
const os = require('os');
// 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.
const sedArg = os.platform() === 'darwin' ? '-i ""' : '-i';

module.exports = function(grunt) {
  return {
    buildReact: {
      command: 'node react/build.js'
    },
    buildVue: {
      command: 'vite build --config vue/vite.config.mts'
    },
    buildAngular: {
      command: 'node angular/build.js'
    },
    buildSvelte: {
      command: 'vite build --config svelte/viteConfig.mjs && vite build --config svelte/viteConfigWithUtils.mjs'
    },
    buildJs: {
      command: 'node build.js'
    },
    genTsDeclaration: {
      //* Clean up the module names by removing the /index suffix as this is how they will be used.
      command: `tsc --p tsconfig.json && sed ${sedArg} -e "s/\\/index\\"/\\"/g" build/js/intlTelInput.d.ts`
    },
    genReactTsDeclaration: {
      //* Clean up the module names by removing the /index suffix as this is how they will be used.
      command: `tsc --p react/tsconfig.json && sed ${sedArg} -e "s/\\/index\\"/\\"/g" react/build/IntlTelInput.d.ts`
    },
    genAngularTsDeclarationAndJs: {
      command: 'ngc --p angular/tsconfig.json'
    },
    eslint: {
      command: 'eslint .'
    },
    test: {
      command: 'npm run test'
    },
    checkLpnMetadata: {
      command: 'node scripts/check-lpn-metadata.cjs'
    },
  };
};


================================================
FILE: grunt/translations.js
================================================
const fs = require('fs');
const path = require('path');
// ts-node allows us to require TypeScript files
require("ts-node").register();

module.exports = function(grunt) {
  grunt.registerTask('translations', 'Generate country translations', function() {
    const supportedLocalesDirectory = "src/js/intl-tel-input/i18n";
    const rootIndexFilePath = path.join(supportedLocalesDirectory, 'index.ts');
    let rootIndexFileContent = "//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\n";

    const localeToExportName = (locale) => {
      // Convert locale folder names (e.g. "zh-hk") into valid JS identifiers (e.g. "zhHk").
      // Keep the first segment as-is, then capitalize the first letter of subsequent segments.
      const parts = String(locale)
        .trim()
        .split(/[^a-zA-Z0-9]+/)
        .filter(Boolean);

      if (parts.length === 0) return '';

      const [first, ...rest] = parts;
      return first + rest.map(part => part.charAt(0).toUpperCase() + part.slice(1)).join('');
    };

    //* Get list of translation locales that exist in this project.
    const supportedLocales = fs.readdirSync(supportedLocalesDirectory, { withFileTypes: true })
      .filter(dir => dir.isDirectory())
      .map(dir => dir.name);

    grunt.log.writeln(`Supported locales: ${supportedLocales.join(", ")}.\n`);

    //* For each supported locale: update the root index.ts file.
    supportedLocales.forEach(locale => {
      const translationFilePath = path.join(supportedLocalesDirectory, locale, 'index.ts');
      const translationExists = fs.existsSync(translationFilePath);

      //* If the interface file does not exist, skip the iteration.
      if (!translationExists) {
        grunt.log.writeln(`WARNING: Missing interface file: ${translationFilePath} - skipping this locale.\n`);
        return;
      }

      //* Add the locale export to the content of the root index.ts file
      const exportName = localeToExportName(locale);
      if (!exportName) {
        grunt.log.writeln(`WARNING: Could not generate export name for locale: ${locale} - skipping this locale.\n`);
        return;
      }
      rootIndexFileContent += `export { default as ${exportName} } from "./${locale}";\n`;
    });
    fs.writeFileSync(rootIndexFilePath, rootIndexFileContent);
  });
};


================================================
FILE: grunt/watch.js
================================================
module.exports = function(grunt) {
  return {
    js: {
      // only TS files (excludes utils.js, which is watched separately below)
      files: "src/js/**/*.ts",
      tasks: "build:jsfast"
    },
    utils: {
      files: "src/js/utils.js",
      tasks: ["build:utils", "build:jsfast"]
    },
    translations: {
      files: "src/i18n/**/*",
      tasks: "build:translations",
    },
    react: {
      files: [
        "react/src/intl-tel-input/react.tsx",
        "react/demo/set-number/SetNumberApp.tsx",
        "react/demo/simple/SimpleApp.tsx",
        "react/demo/toggle-disabled/ToggleDisabledApp.tsx",
        "react/demo/validation/ValidationApp.tsx",
      ],
      tasks: "build:react"
    },
    vue: {
      files: ["vue/src/IntlTelInput.vue"],
      tasks: "build:vue"
    },
    svelte: {
      files: ["svelte/src/intl-tel-input/IntlTelInput.svelte"],
      tasks: "build:svelte"
    },
    angular: {
      files: [
        "angular/src/intl-tel-input/angular.ts",
        "angular/demo/form/form.component.ts",
        "angular/demo/set-number/set-number.component.ts",
        "angular/demo/simple/simple.component.ts",
        "angular/demo/toggle-disabled/toggle-disabled.component.ts",
        "angular/demo/validation/validation.component.ts",
      ],
      tasks: "build:angular"
    },
    css: {
      files: "src/css/**/*",
      tasks: "build:css"
    }
  };
};


================================================
FILE: index.js
================================================
module.exports = require("./build/js/intlTelInput");


================================================
FILE: jest.config.js
================================================
/** @type {import('jest').Config} */
module.exports = {
  roots: [
    "<rootDir>/tests",
  ],
  moduleDirectories: [
    "node_modules",
    "build/js",
  ],
  transform: {
    // Support TypeScript source files directly in tests (lightweight)
    "^.+\\.(ts|tsx)$": [
      "babel-jest",
      {
        presets: [
          ["@babel/preset-typescript", { allowDeclareFields: true }],
        ],
        plugins: [
          "@babel/plugin-transform-modules-commonjs",
          "babel-plugin-add-module-exports",
        ],
      },
    ],
    // Special handling for the utils ESM file
    "utils.js$": [
      "babel-jest",
      {
        plugins: [
          "@babel/plugin-transform-modules-commonjs",
          "babel-plugin-add-module-exports",
        ],
      },
    ],
  },
};


================================================
FILE: package.json
================================================
{
  "name": "intl-tel-input",
  "version": "26.8.1",
  "description": "A JavaScript plugin for entering and validating international telephone numbers",
  "license": "MIT",
  "author": "Jack O'Connor (http://jackocnr.com)",
  "scripts": {
    "test": "npm run test:js && npm run test:e2e",
    "test:js": "jest",
    "test:e2e": "playwright test",
    "test:e2e:ui": "playwright test --ui",
    "test:e2e:linux": "./scripts/playwright-linux-docker.sh",
    "test:e2e:linux:update": "./scripts/playwright-linux-docker.sh --update-snapshots",
    "lint:js": "eslint .",
    "lint:spelling": "cspell --dot --gitignore --no-progress '**'",
    "watch": "grunt watch",
    "build": "grunt build",
    "build:js": "grunt build:js",
    "build:jsfast": "grunt build:jsfast",
    "build:translations": "grunt build:translations",
    "build:utils": "grunt build:utils",
    "build:css": "grunt build:css",
    "build:img": "grunt build:img",
    "build:react": "grunt build:react",
    "build:vue": "grunt build:vue",
    "build:angular": "grunt build:angular",
    "build:svelte": "grunt build:svelte",
    "prepublishOnly": "grunt build",
    "vue:demo": "vite --config vue/demo/validation/vite.config.js",
    "svelte:demo": "vite --config svelte/demo/validation/vite.config.mjs"
  },
  "devDependencies": {
    "@angular/compiler": "^19.2.14",
    "@angular/compiler-cli": "^19.2.14",
    "@angular/core": "^19.1.4",
    "@angular/forms": "^19.1.4",
    "@angular/platform-browser": "^19.1.4",
    "@babel/plugin-transform-modules-commonjs": "^7.25.7",
    "@babel/preset-typescript": "^7.27.1",
    "@playwright/test": "^1.58.0",
    "@sveltejs/vite-plugin-svelte": "^5.0.3",
    "@testing-library/jest-dom": "^6.4.6",
    "@testing-library/user-event": "^14.5.2",
    "@types/node": "^22.10.5",
    "@types/react": "^18.2.74",
    "@types/react-dom": "^18.2.24",
    "@typescript-eslint/eslint-plugin": "^8.1.0",
    "@typescript-eslint/parser": "^8.1.0",
    "@vitejs/plugin-vue": "^5.2.1",
    "@vue/tsconfig": "^0.7.0",
    "babel-plugin-add-module-exports": "^1.0.4",
    "cspell": "^8.6.1",
    "esbuild": "^0.25.0",
    "eslint": "^8.57.0",
    "eslint-import-resolver-typescript": "^3.6.1",
    "eslint-plugin-import": "^2.29.1",
    "eslint-plugin-jest": "^28.5.0",
    "eslint-plugin-react": "^7.34.1",
    "eslint-plugin-react-hooks": "^4.6.2",
    "fast-xml-parser": "^5.2.5",
    "flag-icons": "^7.2.3",
    "google-closure-compiler": "^20240317.0.0",
    "google-closure-library": "^20230802.0.0",
    "grunt": "^1.6.1",
    "grunt-bump": "^0.8.0",
    "grunt-cli": "^1.2.0",
    "grunt-contrib-clean": "^2.0.1",
    "grunt-contrib-connect": "^5.0.0",
    "grunt-contrib-cssmin": "^5.0.0",
    "grunt-contrib-watch": "^1.1.0",
    "grunt-replace": "^2.0.2",
    "grunt-sass": "^3.0.0",
    "grunt-shell": "^4.0.0",
    "http-server": "^14.1.1",
    "jest": "^29.7.0",
    "jest-environment-jsdom": "^29.7.0",
    "jquery": "^3.1.1",
    "load-grunt-config": "^4.0.1",
    "playwright": "^1.58.0",
    "prettier": "^3.2.5",
    "prettier-eslint": "^16.4.2",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "sass": "^1.83.1",
    "sharp": "^0.33.5",
    "svelte": "^5.46.4",
    "time-grunt": "^2.0.0",
    "ts-node": "^10.9.2",
    "typescript": "^5.5.3",
    "vite": "^6.1.0",
    "vite-plugin-dts": "^4.4.0",
    "vue": "^3.5.13",
    "vue-tsc": "^2.2.0",
    "zone.js": "^0.15.0"
  },
  "files": [
    "build/*",
    "react/build/*",
    "vue/build/*",
    "angular/build/*",
    "svelte/build/*",
    "CHANGELOG.md",
    "LICENSE",
    "package.json",
    "package-lock.json",
    "README.md",
    "index.js"
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/jackocnr/intl-tel-input.git"
  },
  "homepage": "https://intl-tel-input.com",
  "style": "build/css/intlTelInput.css",
  "main": "./build/js/intlTelInput.js",
  "types": "./build/js/intlTelInput.d.ts",
  "exports": {
    ".": {
      "types": "./build/js/intlTelInput.d.ts",
      "import": "./build/js/intlTelInput.js",
      "default": "./build/js/intlTelInput.js"
    },
    "./intlTelInputWithUtils": {
      "types": "./build/js/intlTelInput.d.ts",
      "import": "./build/js/intlTelInputWithUtils.js",
      "default": "./build/js/intlTelInputWithUtils.js"
    },
    "./data": "./build/js/data.js",
    "./utils": "./build/js/utils.js",
    "./react": {
      "types": "./react/build/IntlTelInput.d.ts",
      "require": "./react/build/IntlTelInput.cjs",
      "import": "./react/build/IntlTelInput.js",
      "default": "./react/build/IntlTelInput.js"
    },
    "./reactWithUtils": {
      "types": "./react/build/IntlTelInput.d.ts",
      "require": "./react/build/IntlTelInputWithUtils.cjs",
      "import": "./react/build/IntlTelInputWithUtils.js",
      "default": "./react/build/IntlTelInputWithUtils.js"
    },
    "./vue": {
      "types": "./vue/build/exports/IntlTelInput.d.ts",
      "import": "./vue/build/exports/IntlTelInput.mjs"
    },
    "./vueWithUtils": {
      "types": "./vue/build/exports/IntlTelInputWithUtils.d.ts",
      "import": "./vue/build/exports/IntlTelInputWithUtils.mjs"
    },
    "./angular": {
      "types": "./angular/build/types/intl-tel-input/angular.d.ts",
      "import": "./angular/build/IntlTelInput.js",
      "default": "./angular/build/IntlTelInput.js"
    },
    "./angularWithUtils": {
      "types": "./angular/build/types/intl-tel-input/angularWithUtils.d.ts",
      "import": "./angular/build/IntlTelInputWithUtils.js",
      "default": "./angular/build/IntlTelInputWithUtils.js"
    },
    "./svelte": {
      "import": "./svelte/build/IntlTelInput.mjs"
    },
    "./svelteWithUtils": {
      "import": "./svelte/build/IntlTelInputWithUtils.mjs"
    },
    "./i18n": {
      "types": "./build/js/intlTelInput.d.ts",
      "import": "./build/js/i18n/index.js",
      "default": "./build/js/i18n/index.js"
    },
    "./i18n/*": {
      "types": "./build/js/intlTelInput.d.ts",
      "import": "./build/js/i18n/*/index.js",
      "default": "./build/js/i18n/*/index.js"
    },
    "./styles": "./build/css/intlTelInput.css",
    "./*": "./*"
  },
  "typesVersions": {
    "*": {
      ".": [
        "build/js/intlTelInput.d.ts"
      ],
      "react": [
        "react/build/IntlTelInput.d.ts"
      ],
      "angular": [
        "angular/build/types/intl-tel-input/angular.d.ts"
      ]
    }
  },
  "keywords": [
    "international",
    "country",
    "dial code",
    "telephone",
    "phone",
    "mobile",
    "input",
    "flag",
    "dropdown",
    "javascript",
    "plugin",
    "css",
    "html",
    "validation",
    "formatting",
    "react",
    "vue",
    "angular",
    "svelte",
    "component",
    "typescript"
  ],
  "workspaces": [
    "site"
  ]
}


================================================
FILE: playwright.config.ts
================================================
import { defineConfig } from "@playwright/test";

export default defineConfig({
  testDir: "tests-e2e",
  timeout: 30_000,
  retries: 0,
  snapshotPathTemplate: "{testDir}/{testFilePath}-snapshots/{arg}{-projectName}{ext}",
  reporter: [["list"], ["html", { open: "never" }]],
  expect: {
    toHaveScreenshot: {
      // Reduce flakiness from caret blink / CSS animations.
      animations: "disabled",
      caret: "hide",
      scale: "css",
    },
  },
  use: {
    baseURL: "http://localhost:4173",
    trace: "on-first-retry",
    viewport: { width: 900, height: 600 },
    deviceScaleFactor: 1,
  },
  projects: [
    {
      name: "chromium",
      use: {
        browserName: "chromium",
      },
    },
  ],
  webServer: [
    {
      command: "./node_modules/.bin/http-server -p 4173 -c-1 .",
      url: "http://localhost:4173/tests-e2e/fixtures/vanilla.html",
      reuseExistingServer: true,
      timeout: 120_000,
    },
    {
      command:
        "./node_modules/.bin/vite --config vue/demo/simple/vite.config.js --port 4174 --strictPort",
      url: "http://localhost:4174/",
      reuseExistingServer: true,
      timeout: 120_000,
    },
    {
      command:
        "./node_modules/.bin/vite --config svelte/demo/simple/vite.config.mjs --port 4175 --strictPort",
      url: "http://localhost:4175/",
      reuseExistingServer: true,
      timeout: 120_000,
    },
  ],
});

================================================
FILE: react/README.md
================================================
# IntlTelInput React Component

A 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).

[Explore docs »](https://intl-tel-input.com/docs/react-component)


================================================
FILE: react/build.js
================================================
/* eslint-disable no-undef */
/* eslint-disable @typescript-eslint/no-var-requires */
const { build } = require("esbuild");
const packageJson = require("../package.json");

const mainShared = {
  bundle: true,
  external: ["react", "react-dom", "prop-types"],
  logLevel: "info",
  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
  define: { "process.env.VERSION": `"${packageJson.version}"` },
};

//* React Component - CommonJS
build({
  ...mainShared,
  entryPoints: ["react/src/intl-tel-input/react.tsx"],
  format: "cjs",
  outfile: "react/build/IntlTelInput.cjs",
});

//* React Component - Default (ES Modules)
build({
  ...mainShared,
  entryPoints: ["react/src/intl-tel-input/react.tsx"],
  format: "esm",
  outfile: "react/build/IntlTelInput.js",
});

//* React Component With Utils - CommonJS
build({
  ...mainShared,
  entryPoints: ["react/src/intl-tel-input/reactWithUtils.tsx"],
  format: "cjs",
  outfile: "react/build/IntlTelInputWithUtils.cjs",
});

//* React Component With Utils - Default (ES Modules)
build({
  ...mainShared,
  entryPoints: ["react/src/intl-tel-input/reactWithUtils.tsx"],
  format: "esm",
  outfile: "react/build/IntlTelInputWithUtils.js",
});

//* Demo shared config
const demoShared = {
  bundle: true,
  define: { "process.env.VERSION": `"${packageJson.version}"` },
  format: "iife",
};

//* Simple demo app
build({
  ...demoShared,
  entryPoints: ["react/demo/simple/SimpleApp.tsx"],
  outfile: "react/demo/simple/simple-bundle.js",
});

//* Validation demo app
build({
  ...demoShared,
  entryPoints: ["react/demo/validation/ValidationApp.tsx"],
  outfile: "react/demo/validation/validation-bundle.js",
});

//* Set Number demo app
build({
  ...demoShared,
  entryPoints: ["react/demo/set-number/SetNumberApp.tsx"],
  outfile: "react/demo/set-number/set-number-bundle.js",
});

//* Toggle Disabled demo app
build({
  ...demoShared,
  entryPoints: ["react/demo/toggle-disabled/ToggleDisabledApp.tsx"],
  outfile: "react/demo/toggle-disabled/toggle-disabled-bundle.js",
});


================================================
FILE: react/demo/set-number/SetNumberApp.tsx
================================================
import React, { useState, ReactElement, useRef } from "react";
import { createRoot } from "react-dom/client";
import IntlTelInput from "../../src/intl-tel-input/reactWithUtils";

const errorMap = [
  "Invalid number",
  "Invalid country code",
  "Too short",
  "Too long",
  "Invalid number",
];

const App = (): ReactElement => {
  const ref = useRef(null);
  const [isValid, setIsValid] = useState<boolean | null>(null);
  const [number, setNumber] = useState<string | null>(null);
  const [errorCode, setErrorCode] = useState<number | null>(null);
  const [notice, setNotice] = useState<string | null>(null);
  
  const handleSetNumber = (): void => {
    ref.current?.getInstance().setNumber("+14155552671");
  };
  
  const handleSubmit = (): void => {
    if (isValid) {
      setNotice(`Valid number: ${number}`);
    } else {
      const errorMessage = errorMap[errorCode || 0] || "Invalid number";
      setNotice(`Error: ${errorMessage}`);
    }
  };
  
  return (
    <form>
      <IntlTelInput
        ref={ref}
        onChangeNumber={setNumber}
        onChangeValidity={setIsValid}
        onChangeErrorCode={setErrorCode}
        initOptions={{
          initialCountry: "us",
        }}
      />
      <button className="button" type="button" onClick={handleSetNumber}>Set Number</button>
      <button className="button" type="button" onClick={handleSubmit}>Validate</button>
      {notice && <div className="notice">{notice}</div>}
    </form>
  );
};

const container = document.getElementById("app");
if (container) {
  const root = createRoot(container);
  root.render(<App />);
}

================================================
FILE: react/demo/set-number/set-number.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>React App - Validation Demo</title>
  <link rel="stylesheet" href="../../../build/css/intlTelInput.css" />
  <link rel="stylesheet" href="../../../build/css/demo.css" />
</head>

<body>
  <h1>React App - Set Number Demo</h1>
  <p>A simple react app, using the IntlTelInput component to handle phone number entry, calling setNumber and validation.</p>
  <p>Click "Set Number" then click "Validate" to check that the react internals have updated correctly.</p>
  <div id="app"></div>
  <script src="./set-number-bundle.js"></script>
</body>

</html>

================================================
FILE: react/demo/simple/SimpleApp.tsx
================================================
import React, { ReactElement } from "react";
import { createRoot } from "react-dom/client";
import IntlTelInput from "../../src/intl-tel-input/reactWithUtils";

const App = (): ReactElement => (
  <IntlTelInput
    initOptions={{
      initialCountry: "us",
    }}
  />
);

const container = document.getElementById("app");
if (container) {
  const root = createRoot(container);
  root.render(<App />);
}

================================================
FILE: react/demo/simple/simple.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>React App - Simple Demo</title>
  <link rel="stylesheet" href="../../../build/css/intlTelInput.css" />
  <link rel="stylesheet" href="../../../build/css/demo.css" />
</head>

<body>
  <h1>React App - Simple Demo</h1>
  <p>A simple react app, using the IntlTelInput component to handle phone number entry.</p>
  <div id="app"></div>
  <script src="./simple-bundle.js"></script>
</body>

</html>

================================================
FILE: react/demo/toggle-disabled/ToggleDisabledApp.tsx
================================================
import React, { useState, ReactElement } from "react";
import { createRoot } from "react-dom/client";
import IntlTelInput from "../../src/intl-tel-input/reactWithUtils";

const App = (): ReactElement => {
  const [isDisabled, setIsDisabled] = useState(true);

  const toggleDisabled = () => setIsDisabled(!isDisabled);

  return (
    <form>
      <IntlTelInput
        disabled={isDisabled}
      />
      <button className="button" type="button" onClick={toggleDisabled}>Toggle</button>
    </form>
  );
};

const container = document.getElementById("app");
if (container) {
  const root = createRoot(container);
  root.render(<App />);
}

================================================
FILE: react/demo/toggle-disabled/toggle-disabled.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>React App - Validation Demo</title>
  <link rel="stylesheet" href="../../../build/css/intlTelInput.css" />
  <link rel="stylesheet" href="../../../build/css/demo.css" />
</head>

<body>
  <h1>React App - Toggle disabled prop Demo</h1>
  <p>A simple react app, using the IntlTelInput component to show it toggle.</p>
  <p>Click the button to enable/disable component.</p>
  <div id="app"></div>
  <script src="./toggle-disabled-bundle.js"></script>
</body>

</html>

================================================
FILE: react/demo/validation/ValidationApp.tsx
================================================
import React, { useState, ReactElement } from "react";
import { createRoot } from "react-dom/client";
import IntlTelInput from "../../src/intl-tel-input/reactWithUtils";

const errorMap = [
  "Invalid number",
  "Invalid country code",
  "Too short",
  "Too long",
  "Invalid number",
];

const App = (): ReactElement => {
  const [isValid, setIsValid] = useState<boolean | null>(null);
  const [number, setNumber] = useState<string | null>(null);
  const [errorCode, setErrorCode] = useState<number | null>(null);
  const [notice, setNotice] = useState<string | null>(null);
  
  const handleSubmit = (): void => {
    if (isValid) {
      setNotice(`Valid number: ${number}`);
    } else {
      const errorMessage = errorMap[errorCode || 0] || "Invalid number";
      setNotice(`Error: ${errorMessage}`);
    }
  };
  
  return (
    <form>
      <IntlTelInput
        onChangeNumber={setNumber}
        onChangeValidity={setIsValid}
        onChangeErrorCode={setErrorCode}
        initOptions={{
          initialCountry: "us",
        }}
      />
      <button className="button" type="button" onClick={handleSubmit}>Validate</button>
      {notice && <div className="notice">{notice}</div>}
    </form>
  );
};

const container = document.getElementById("app");
if (container) {
  const root = createRoot(container);
  root.render(<App />);
}

================================================
FILE: react/demo/validation/validation.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>React App - Validation Demo</title>
  <link rel="stylesheet" href="../../../build/css/intlTelInput.css" />
  <link rel="stylesheet" href="../../../build/css/demo.css" />
</head>

<body>
  <h1>React App - Validation Demo</h1>
  <p>A simple react app, using the IntlTelInput component to handle phone number entry and validation.</p>
  <p>Enter a phone number below and click "Validate".</p>
  <div id="app"></div>
  <script src="./validation-bundle.js"></script>
</body>

</html>

================================================
FILE: react/src/intl-tel-input/react.tsx
================================================
import intlTelInput from "../intl-tel-input";
//* Keep the TS imports separate, as the above line gets substituted in the reactWithUtils build process.
import { Iti } from "../intl-tel-input";
import React, {
  useRef,
  useEffect,
  forwardRef,
  useImperativeHandle,
  useCallback,
} from "react";
import { SomeOptions } from "../modules/types/public-api";

// make this available as a named export, so react users can access globals like intlTelInput.utils
export { intlTelInput };

type InputProps = Omit<React.ComponentPropsWithoutRef<"input">, "onInput">;

type ItiProps = {
  initialValue?: string;
  onChangeNumber?: (number: string) => void;
  onChangeCountry?: (country: string) => void;
  onChangeValidity?: (valid: boolean) => void;
  onChangeErrorCode?: (errorCode: number | null) => void;
  usePreciseValidation?: boolean;
  initOptions?: SomeOptions;
  inputProps?: InputProps;
  disabled?: boolean | undefined;
};

export type IntlTelInputRef = {
  getInstance: () => Iti | null;
  getInput: () => HTMLInputElement | null;
};

const IntlTelInput = forwardRef(function IntlTelInput(
  {
    initialValue = "",
    onChangeNumber = () => {},
    onChangeCountry = () => {},
    onChangeValidity = () => {},
    onChangeErrorCode = () => {},
    usePreciseValidation = false,
    initOptions = {},
    inputProps = {},
    disabled = undefined,
  }: ItiProps,
  ref: React.ForwardedRef<IntlTelInputRef>,
) {
  const inputRef = useRef<HTMLInputElement | null>(null);
  const itiRef = useRef<Iti | null>(null);
  const lastEmittedNumberRef = useRef<string>();
  const lastEmittedCountryRef = useRef<string>();
  const lastEmittedValidityRef = useRef<boolean>();
  const lastEmittedErrorCodeRef = useRef<number | null>();

  // expose the instance and input ref to the parent component
  useImperativeHandle(ref, () => ({
    getInstance: () => itiRef.current,
    getInput: () => inputRef.current,
  }));

  const update = useCallback((): void => {
    // if the instance is not valid (e.g. has been destroyed/unmounted), do not attempt to call any methods on it
    if (!itiRef.current?.isActive()) {
      return;
    }
    const num = itiRef.current?.getNumber() || "";
    const countryIso = itiRef.current?.getSelectedCountryData().iso2 || "";
    // note: this number will be in standard E164 format, but any container component can use
    // intlTelInput.utils.formatNumber() to convert this to another format
    // as well as intlTelInput.utils.getNumberType() etc. if need be
    if (num !== lastEmittedNumberRef.current) {
      lastEmittedNumberRef.current = num;
      onChangeNumber(num);
    }
    if (countryIso !== lastEmittedCountryRef.current) {
      lastEmittedCountryRef.current = countryIso;
      onChangeCountry(countryIso);
    }

    if (itiRef.current) {
      const isValid = usePreciseValidation
        ? itiRef.current.isValidNumberPrecise()
        : itiRef.current.isValidNumber();
      const errorCode = isValid ? null : itiRef.current.getValidationError();

      if (isValid !== lastEmittedValidityRef.current) {
        lastEmittedValidityRef.current = isValid;
        onChangeValidity(isValid);
      }
      if (errorCode !== lastEmittedErrorCodeRef.current) {
        lastEmittedErrorCodeRef.current = errorCode;
        onChangeErrorCode(errorCode);
      }
    }
  }, [
    onChangeCountry,
    onChangeErrorCode,
    onChangeNumber,
    onChangeValidity,
    usePreciseValidation,
  ]);

  useEffect(() => {
    if (inputRef.current) {
      itiRef.current = intlTelInput(inputRef.current, initOptions);
    }
    return (): void => {
      itiRef.current?.destroy();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  useEffect(() => {
    // store a reference to the current input ref, which otherwise is already lost in the cleanup function
    const inputRefCurrent = inputRef.current;
    if (inputRefCurrent) {
      inputRefCurrent.addEventListener("countrychange", update);
      // when plugin initialisation has finished (e.g. loaded utils script), update all the state values
      itiRef.current.promise.then(update);
    }
    return (): void => {
      if (inputRefCurrent) {
        inputRefCurrent.removeEventListener("countrychange", update);
      }
    };
  }, [update]);

  useEffect(() => {
    if (itiRef.current && disabled !== undefined) {
      itiRef.current.setDisabled(disabled);
    }
  }, [disabled]);

  // ignore keys that would break functionality
  const {
    value: _value,
    // disabled: _disabled,
    ...sanitizedInputProps
  } = inputProps as unknown as Record<string, unknown>;

  return (
    <input
      {...(sanitizedInputProps as InputProps)}
      type="tel"
      ref={inputRef}
      onInput={update}
      defaultValue={initialValue}
    />
  );
});

export default IntlTelInput;


================================================
FILE: react/src/intl-tel-input/reactWithUtils.tsx
================================================
//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.
import intlTelInput from "./intlTelInputWithUtils";
//* Keep the TS imports separate, as the above line gets substituted in the reactWithUtils build process.
import { Iti } from "../intl-tel-input";
import React, {
  useRef,
  useEffect,
  forwardRef,
  useImperativeHandle,
  useCallback,
} from "react";
import { SomeOptions } from "../modules/types/public-api";

// make this available as a named export, so react users can access globals like intlTelInput.utils
export { intlTelInput };

type InputProps = Omit<React.ComponentPropsWithoutRef<"input">, "onInput">;

type ItiProps = {
  initialValue?: string;
  onChangeNumber?: (number: string) => void;
  onChangeCountry?: (country: string) => void;
  onChangeValidity?: (valid: boolean) => void;
  onChangeErrorCode?: (errorCode: number | null) => void;
  usePreciseValidation?: boolean;
  initOptions?: SomeOptions;
  inputProps?: InputProps;
  disabled?: boolean | undefined;
};

export type IntlTelInputRef = {
  getInstance: () => Iti | null;
  getInput: () => HTMLInputElement | null;
};

const IntlTelInput = forwardRef(function IntlTelInput(
  {
    initialValue = "",
    onChangeNumber = () => {},
    onChangeCountry = () => {},
    onChangeValidity = () => {},
    onChangeErrorCode = () => {},
    usePreciseValidation = false,
    initOptions = {},
    inputProps = {},
    disabled = undefined,
  }: ItiProps,
  ref: React.ForwardedRef<IntlTelInputRef>,
) {
  const inputRef = useRef<HTMLInputElement | null>(null);
  const itiRef = useRef<Iti | null>(null);
  const lastEmittedNumberRef = useRef<string>();
  const lastEmittedCountryRef = useRef<string>();
  const lastEmittedValidityRef = useRef<boolean>();
  const lastEmittedErrorCodeRef = useRef<number | null>();

  // expose the instance and input ref to the parent component
  useImperativeHandle(ref, () => ({
    getInstance: () => itiRef.current,
    getInput: () => inputRef.current,
  }));

  const update = useCallback((): void => {
    // if the instance is not valid (e.g. has been destroyed/unmounted), do not attempt to call any methods on it
    if (!itiRef.current?.isActive()) {
      return;
    }
    const num = itiRef.current?.getNumber() || "";
    const countryIso = itiRef.current?.getSelectedCountryData().iso2 || "";
    // note: this number will be in standard E164 format, but any container component can use
    // intlTelInput.utils.formatNumber() to convert this to another format
    // as well as intlTelInput.utils.getNumberType() etc. if need be
    if (num !== lastEmittedNumberRef.current) {
      lastEmittedNumberRef.current = num;
      onChangeNumber(num);
    }
    if (countryIso !== lastEmittedCountryRef.current) {
      lastEmittedCountryRef.current = countryIso;
      onChangeCountry(countryIso);
    }

    if (itiRef.current) {
      const isValid = usePreciseValidation
        ? itiRef.current.isValidNumberPrecise()
        : itiRef.current.isValidNumber();
      const errorCode = isValid ? null : itiRef.current.getValidationError();

      if (isValid !== lastEmittedValidityRef.current) {
        lastEmittedValidityRef.current = isValid;
        onChangeValidity(isValid);
      }
      if (errorCode !== lastEmittedErrorCodeRef.current) {
        lastEmittedErrorCodeRef.current = errorCode;
        onChangeErrorCode(errorCode);
      }
    }
  }, [
    onChangeCountry,
    onChangeErrorCode,
    onChangeNumber,
    onChangeValidity,
    usePreciseValidation,
  ]);

  useEffect(() => {
    if (inputRef.current) {
      itiRef.current = intlTelInput(inputRef.current, initOptions);
    }
    return (): void => {
      itiRef.current?.destroy();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  useEffect(() => {
    // store a reference to the current input ref, which otherwise is already lost in the cleanup function
    const inputRefCurrent = inputRef.current;
    if (inputRefCurrent) {
      inputRefCurrent.addEventListener("countrychange", update);
      // when plugin initialisation has finished (e.g. loaded utils script), update all the state values
      itiRef.current.promise.then(update);
    }
    return (): void => {
      if (inputRefCurrent) {
        inputRefCurrent.removeEventListener("countrychange", update);
      }
    };
  }, [update]);

  useEffect(() => {
    if (itiRef.current && disabled !== undefined) {
      itiRef.current.setDisabled(disabled);
    }
  }, [disabled]);

  // ignore keys that would break functionality
  const {
    value: _value,
    // disabled: _disabled,
    ...sanitizedInputProps
  } = inputProps as unknown as Record<string, unknown>;

  return (
    <input
      {...(sanitizedInputProps as InputProps)}
      type="tel"
      ref={inputRef}
      onInput={update}
      defaultValue={initialValue}
    />
  );
});

export default IntlTelInput;


================================================
FILE: react/tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "es6",
    "jsx": "react",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "esModuleInterop": true, //* Required for react (etc) default imports.
    "declaration": true, //* Generate .d.ts files.
    "emitDeclarationOnly": true, //* ONLY generate .d.ts files (we use esbuild for the actual bundling).
    "outFile": "build/IntlTelInput.d.ts",
    "rootDir": "./src",
    "allowJs": true, // allow importing .mjs files e.g. i18n files
  },
  "include": [
    "src/intl-tel-input/react.tsx",
    "src/intl-tel-input/reactWithUtils.tsx",
    "src/intl-tel-input/i18n/index.ts",
  ],
}


================================================
FILE: scripts/check-lpn-metadata.cjs
================================================
/**
 * This script loads the existing country data from data.ts,
 * then parses libphonenumber's PhoneNumberMetadata.xml to look for any updates.
 *
 * 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
 */


/* eslint-disable no-console */
const fs = require("fs");
const path = require("path");
const vm = require("vm");

const REPO_ROOT = path.resolve(__dirname, "..", "");
const XML_PATH = path.resolve(
  REPO_ROOT,
  "third_party/libphonenumber/resources/PhoneNumberMetadata.xml",
);
const OUT_TS = path.resolve(REPO_ROOT, "tmp/generated-rawCountryData.ts");
const CURATED_DATA_TS_PATH = path.resolve(REPO_ROOT, "src/js/intl-tel-input/data.ts");

// Extract a compact leading digits prefix representative for a region.
// Strategy:
// - Prefer a short literal digit prefix found in the first national number pattern for fixed-line or mobile
// - Fallback to a header-level leadingDigits string when present, trimming to a short digit-only prefix
// (legacy single-prefix extractor removed in favor of extractLeadingPrefixes)

function sortDigitStringsNumeric(arr) {
  // Lexicographic digit-by-digit ordering (e.g., 74576 < 7524)
  return arr.slice().sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
}

// Shared simple prefix parser used for XML mode
function splitTopLevelAlts(inner) {
    const parts = [];
    let cur = "";
    let depth = 0;
    for (let i = 0; i < inner.length; i++) {
      const ch = inner[i];
      if (ch === "(") depth++;
      else if (ch === ")") depth = Math.max(0, depth - 1);
      if (ch === "|" && depth === 0) { parts.push(cur); cur = ""; continue; }
      cur += ch;
    }
    parts.push(cur);
    return parts;
}

function expandCharClass(cls) {
    const res = new Set();
    for (let i = 0; i < cls.length; i++) {
      const c = cls[i];
      if (/\d/.test(c)) {
        if (i + 2 < cls.length && cls[i + 1] === "-" && /\d/.test(cls[i + 2])) {
          const start = Number(c), end = Number(cls[i + 2]);
          for (let d = start; d <= end; d++) res.add(String(d));
          i += 2;
        } else {
          res.add(c);
        }
      }
    }
    return Array.from(res);
}

function expandSimple(prefixPart) {
    if (/^\d{2,6}$/.test(prefixPart)) return [prefixPart];
    const m = prefixPart.match(/^(\d*)\[([0-9-]+)\](\d*)$/);
    if (m) {
      const lead = m[1] || "";
      const cls = m[2];
      const tail = m[3] || "";
      return expandCharClass(cls).map((d) => lead + d + tail).filter((s) => /^\d{2,6}$/.test(s));
    }
    return [];
}

function simplePrefixesFromPattern(pat) {
    // Normalize: collapse all whitespace to simplify parsing of multi-line XML patterns
    pat = String(pat).replace(/\s+/g, "");
    // Handle a leading literal digit sequence followed by a non-capturing group, e.g., '7(?:781|839)\\d|911[17])\\d{5}'
    let mLeadGroup = pat.match(/^(\d{1,3})\(\?:/);
    if (mLeadGroup) {
      const lead = mLeadGroup[1];
      // Extract the first non-capturing group's inner content after the leading digits
      let i = lead.length + 3; // position after '<lead>(?:'
      let depth = 1;
      let inner = "";
      while (i < pat.length) {
        const ch = pat[i];
        if (ch === "\\") { i += 2; continue; }
        if (ch === "(") depth++;
        else if (ch === ")") { depth--; if (depth === 0) { i++; break; } }
        inner += ch; i++;
      }
      const alts = splitTopLevelAlts(inner);
      const set = new Set();
      for (const a of alts) {
        const arr = simplePrefixesFromPattern(a);
        for (const v of arr) set.add(lead + v);
      }
      return Array.from(set).filter((s) => /^\d{2,6}$/.test(s));
    }
    // If the pattern starts with a top-level non-capturing group, extract its inner content
    if (pat.startsWith("(?:")) {
      let i = 3; // after (?:
      let depth = 1;
      let inner = "";
      while (i < pat.length) {
        const ch = pat[i];
        if (ch === "\\") { // skip escaped char
          i += 2;
          continue;
        }
        if (ch === "(") depth++;
        else if (ch === ")") {
          depth--;
          if (depth === 0) { i++; break; }
        }
        inner += ch;
        i++;
      }
      // Now 'inner' holds the content of the outer non-capturing group; ignore any suffix like \d{n}
      const alts = splitTopLevelAlts(inner);
      if (alts.length > 1) {
        const set = new Set();
        for (const a of alts) {
          const arr = simplePrefixesFromPattern(a);
          for (const v of arr) set.add(v);
        }
        return Array.from(set);
      }
      // Single alternative inside, recurse into it directly
      return simplePrefixesFromPattern(inner);
    }
    // Handle top-level alternations like "658|876" or "8001|8[024]9"
    const top = splitTopLevelAlts(pat);
    if (top.length > 1) {
      const set = new Set();
      for (const part of top) {
        const arr = simplePrefixesFromPattern(part);
        for (const v of arr) set.add(v);
      }
      return Array.from(set);
    }
    // Handle bare character class at start, e.g., "[347]" or "[3-7]24"
    let m = pat.match(/^\[([0-9-]+)\](\d*)$/);
    if (m) {
      const cls = m[1];
      const tail = m[2] || "";
      return expandCharClass(cls).map((d) => d + tail).filter((s) => /^\d{1,6}$/.test(s));
    }
    m = pat.match(/^(\d+)\[([0-9-]+)\](\d*)/);
    if (m) {
      const lead = m[1];
      const cls = m[2];
      const tail = m[3] || "";
      return expandCharClass(cls).map((d) => lead + d + tail).filter((s) => /^\d{2,6}$/.test(s));
    }
  m = pat.match(/^(\d{1,6})/);
    if (m) return [m[1]];
    return [];
}

// Collapse exhaustive ranges (prefix-aware): if for a parent prefix p we see all 10 next digits
// across any longer codes (e.g., p0*, p1*, ..., p9*), then replace all those children with p.
function collapseExhaustiveRanges(codes) {
  if (!Array.isArray(codes) || !codes.length) return codes || [];
  const set = new Set(codes);
  let changed = true;
  while (changed) {
    changed = false;
    // Build parent -> set(nextDigits)
    const parents = new Map();
    for (const s of set) {
      if (typeof s !== "string" || s.length < 2) continue;
      for (let k = 1; k < s.length; k++) {
        const parent = s.slice(0, k);
        const next = s[k];
        if (!/\d/.test(next)) continue;
        let bag = parents.get(parent);
        if (!bag) { bag = new Set(); parents.set(parent, bag); }
        bag.add(next);
      }
    }
    // For any parent with all 10 digits seen, collapse all children starting with parent+digit
    for (const [parent, bag] of parents) {
      if (bag.size === 10) {
        let mutated = false;
        const toDelete = [];
        for (const s of set) {
          if (s.length > parent.length && s.startsWith(parent)) {
            toDelete.push(s);
          }
        }
        for (const s of toDelete) { set.delete(s); mutated = true; }
        if (!set.has(parent)) { set.add(parent); mutated = true; }
        if (mutated) changed = true;
      }
    }
  }
  return sortDigitStringsNumeric(Array.from(set));
}

// Attempt to extract 3-digit NANP NPAs from a complex pattern like CA's,
// which uses structure: (?: 2(?:..|..|..) | 3(?:..|..) | ... )[2-9]\d{6}
function extractNanpNpasFromPattern(pat) {
  if (typeof pat !== "string" || !pat) return [];
  const s = pat.replace(/\s+/g, "");
  // Find the top-level non-capturing group at the start, if present
  let inner = s;
  if (s.startsWith("(?:")) {
    let i = 3, depth = 1; inner = "";
    while (i < s.length) {
      const ch = s[i];
      if (ch === "\\") { i += 2; continue; }
      if (ch === "(") depth++;
      else if (ch === ")") { depth--; if (depth === 0) { i++; break; } }
      inner += ch; i++;
    }
  }
  const alts = splitTopLevelAlts(inner);
  const out = new Set();
  for (const a of alts) {
    // Expect a like: 2(?:04|[23]6|...)
    const m = a.match(/^([2-9])\(\?:(.+)\)$/);
    if (!m) continue;
    const d = m[1];
    const rest = m[2];
    const parts = splitTopLevelAlts(rest);
    for (const p of parts) {
      // Expand simple pieces like 04, [23]6, 5[07], 63
      const expansions = expandSimple(d + p);
      for (const e of expansions) { if (/^\d{3}$/.test(e)) out.add(e); }
      // Also handle pure two-digit literals (e.g., '63') after the 1st digit
      if (/^\d{2}$/.test(p)) out.add(d + p);
    }
  }
  return sortDigitStringsNumeric(Array.from(out));
}

function deriveNanpNpasFromPatterns(fixedList, mobileList) {
  const set = new Set();
  const addFrom = (list) => {
    if (!Array.isArray(list)) return;
    for (const pat of list) {
      // 1) General extraction using simplePrefixesFromPattern
      const simple = simplePrefixesFromPattern(pat);
      for (const s of simple) {
        if (/^[2-9]\d{2}$/.test(s)) set.add(s);
        else if (/^[2-9]\d{3,6}$/.test(s)) set.add(s.slice(0, 3));
      }
      // 2) CA-style nested non-capturing groups as a supplement
      const caLike = extractNanpNpasFromPattern(pat);
      for (const v of caLike) set.add(v);
      if (set.size > 400) break; // safety guardrail
    }
  };
  addFrom(fixedList);
  addFrom(mobileList);
  return sortDigitStringsNumeric(Array.from(set));
}

// XML-mode generic extractor: only use territory.leadingDigits + fixedLine/mobile patterns
// Apply safe expansions and filter out obvious service/example-like prefixes
function extractGenericPrefixesXml(src) {
  const out = new Set();

  function isDisallowedPrefix(s) {
    // Leading zero prefixes are often trunk/formatting hints
    if (/^0/.test(s)) return true;
    // Non-geo/service families and known service groups
    if (/^(?:800|80\d|900|90\d|13|1300|1800|190\d)\d*$/.test(s)) return true;
    // Example-like tails
    if (/(?:0123|1234)$/.test(s)) return true;
    // Common synthetic sequences sometimes found in examples
    if (/701234$/.test(s)) return true;
    return false;
  }

  function addFrom(list) {
    if (!Array.isArray(list)) return;
    for (const p of list) {
      if (typeof p !== "string" || !p) continue;
      const arr = simplePrefixesFromPattern(p);
      for (const s of arr) out.add(s);
      if (out.size > 40) break;
    }
  }

  // Use only fixedLine and mobile patterns (ignore leadingDigits entirely)
  addFrom(src.fixed);
  addFrom(src.mobile);

  // Perform prefix-aware collapse before filtering so that short parents (e.g., '4') can be retained
  const rawList = sortDigitStringsNumeric(Array.from(out));
  const collapsed = collapseExhaustiveRanges(rawList);
  const rawSet = new Set(rawList);

  // Filter out obvious service/example-like prefixes
  const filtered = Array.from(collapsed).filter((s) => {
    if (isDisallowedPrefix(s)) return false;
    // Drop 709x branch (special allocations seen in 262/590 groups) to match curated area codes
    if (src && (src.dial === "262" || src.dial === "590") && /^709/.test(s)) return false;
    // Do not treat the full dial code itself as an area-code discriminator,
    // except for Kazakhstan (kz) which legitimately needs '7' to disambiguate from RU.
    if (src && s === src.dial && src.iso2 !== "kz") return false;
    // Enforce minimum length with exceptions:
    //  - allow 1-digit for dial 599 (BQ group)
    //  - allow 1-digit for KZ (iso2 === 'kz')
    const minLen = (src && (src.dial === "599" || src.iso2 === "kz")) ? 1 : 3;
    // If this value is a collapsed parent (i.e., not present in raw expansions), allow even if short
    const isCollapsedParent = !rawSet.has(s);
    if (s.length < minLen && !isCollapsedParent) return false;
    return true;
  });

  return sortDigitStringsNumeric(filtered);
}

// XML-only: extend a base prefix to at least minLen using only safe expansions from XML patterns
function extendPrefixXml(iso2, base, minLen, xmlSources) {
  const src = xmlSources[iso2];
  if (!src || !base) return base;
  const candidates = new Set();
  // Reuse filtering from extractor
  function isDisallowedPrefix(s) {
    if (/^0/.test(s)) return true;
    if (/^(?:800|80\d|900|90\d|13|1300|1800|190\d)\d*$/.test(s)) return true;
    if (/(?:0123|1234)$/.test(s)) return true;
    if (/701234$/.test(s)) return true;
    return false;
  }
  const collect = (list) => {
    if (!Array.isArray(list)) return;
    for (const pat of list) {
      if (typeof pat !== "string") continue;
      const arr = simplePrefixesFromPattern(pat);
      for (const p of arr) {
        if (p.startsWith(base) && p.length >= minLen && !isDisallowedPrefix(p)) candidates.add(p);
      }
    }
  };
  collect(src.fixed);
  collect(src.mobile);
  if (!candidates.size) return base;
  // choose the shortest candidate
  return Array.from(candidates).sort((a, b) => a.length - b.length || (a < b ? -1 : a > b ? 1 : 0))[0];
}



// No iso2-codes.json; we derive the allowlist from curated data.ts

function main() {
  let countryToMetadata;
  const dialCodeToRegions = {};
  const xmlNationalPrefixByIso2 = {};
  // Regions where we intentionally do NOT derive areaCodes as there is no way to distinguish numbers from main region
  const AREA_CODES_EXCLUDE = new Set(["bl", "mf", "cc", "cx"]);

  // For XML mode, we also keep a structured source set per region
  const xmlSourcesByIso2 = {};
  console.log("Loading libphonenumber XML from:", XML_PATH);
  // Lazy-require to keep default path lightweight
  let XMLParser;
  try {
    ({ XMLParser } = require("fast-xml-parser"));
  } catch {
    throw new Error(
      "fast-xml-parser is required. Install with: npm i -D fast-xml-parser",
    );
  }
  const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "" });
  const xmlRaw = fs.readFileSync(XML_PATH, "utf8");
  const xmlDoc = parser.parse(xmlRaw);
  const territoriesRoot = xmlDoc && xmlDoc.phoneNumberMetadata && xmlDoc.phoneNumberMetadata.territories;
  if (!territoriesRoot) throw new Error("Invalid PhoneNumberMetadata.xml structure: missing territories");
  let territories = territoriesRoot.territory || [];
  if (!Array.isArray(territories)) territories = [territories];

  // Build minimal structures compatible with the rest of the pipeline
  // countryToMetadataXml: REG -> array of arrays of strings (patterns) so extendPrefix can scan
  const countryToMetadataXml = {};

  territories.forEach((t) => {
    const reg = (t.id || "").toString();
    const iso2 = reg.toLowerCase();
    if (!reg) return;
    const dial = (t.countryCode || "").toString();
    if (!dial) return;
    const natPref = t.nationalPrefix ? String(t.nationalPrefix) : null;
    if (natPref) xmlNationalPrefixByIso2[iso2] = natPref;

    // Collect safe patterns from territory-level leadingDigits, fixedLine, mobile
    const patterns = [];
    const leading = [];
    const fixedPats = [];
    const mobilePats = [];
    // Some schemas put territory-level leadingDigits as child elements or arrays
    const ld = t.leadingDigits;
    if (typeof ld === "string") { patterns.push(ld); leading.push(ld); }
    else if (Array.isArray(ld)) ld.forEach((s) => { if (typeof s === "string") { patterns.push(s); leading.push(s); } });

    const fixed = t.fixedLine && t.fixedLine.nationalNumberPattern;
    if (typeof fixed === "string") { patterns.push(fixed); fixedPats.push(fixed); }
    const mobile = t.mobile && t.mobile.nationalNumberPattern;
    if (typeof mobile === "string") { patterns.push(mobile); mobilePats.push(mobile); }

    // Build dialCodeToRegions honoring mainCountryForCode where available
    if (!dialCodeToRegions[dial]) dialCodeToRegions[dial] = [];
    const isMain = String(t.mainCountryForCode || "") === "true";
    if (isMain) {
      // Ensure main region is first
      dialCodeToRegions[dial].unshift(iso2);
    } else {
      dialCodeToRegions[dial].push(iso2);
    }

    // Provide a scanning surface for extendPrefix: one inner array with collected patterns
    countryToMetadataXml[reg] = [patterns];
    xmlSourcesByIso2[iso2] = { iso2, leading, fixed: fixedPats, mobile: mobilePats, dial, main: isMain };
  });

  countryToMetadata = countryToMetadataXml;

  // Load curated area codes from src/js/intl-tel-input/data.ts and seed from those first.
  // We only add newly-derived codes if they are not already covered by curated ones.
  // Also capture curated priority per iso2 to preserve priority values in generated output (we don't compute priorities ourselves).
  const curatedPriorityByIso2 = new Map();
  const curatedIso2Set = new Set();
  function loadCuratedAreaCodes() {
    const map = new Map();
    if (!fs.existsSync(CURATED_DATA_TS_PATH)) return map;
    try {
      const ts = fs.readFileSync(CURATED_DATA_TS_PATH, "utf8");
      const anchor = ts.indexOf("export const rawCountryData");
      if (anchor === -1) return map;
      // Find first '[' after anchor and then parse matching brackets until the corresponding ']'
      let i = ts.indexOf("[", anchor);
      if (i === -1) return map;
      let depth = 0;
      let start = i;
      for (; i < ts.length; i++) {
        const ch = ts[i];
        if (ch === "[") depth++;
        else if (ch === "]") { depth--; if (depth === 0) { i++; break; } }
      }
      if (depth !== 0) return map;
      const arrSrc = ts.slice(start, i);
      // Evaluate the array literal. Comments are allowed in JS arrays, so this should work.
      // Wrap in parentheses to make it a valid expression.
      // eslint-disable-next-line no-new-func
      const curated = Function("return (" + arrSrc + ")")();
      if (Array.isArray(curated)) {
        for (const row of curated) {
          if (!Array.isArray(row) || row.length < 2) continue;
          const iso2 = row[0];
          const priority = row[2];
          const area = row[3];
          if (typeof iso2 === "string") curatedIso2Set.add(iso2.toLowerCase());
          if (typeof iso2 === "string" && Array.isArray(area) && area.length) {
            map.set(iso2.toLowerCase(), area.slice());
          }
          if (typeof iso2 === "string" && (typeof priority === "number" || typeof priority === "string")) {
            const p = Number(priority);
            if (!Number.isNaN(p)) curatedPriorityByIso2.set(iso2.toLowerCase(), p);
          }
        }
      }
    } catch (e) {
      console.warn("Warning: failed to parse curated data.ts for area codes:", e && e.message ? e.message : e);
    }
    return map;
  }
  const curatedAreaCodesByIso2 = loadCuratedAreaCodes();
  // Build allowlist from curated data.ts iso2s
  console.log("Building iso2 allowlist from curated data.ts");
  const allowIso2 = curatedIso2Set;

  const tuples = [];

  // Precompute candidate leading prefixes per region
  const candidatePrefixesByIso2 = {};
  const collapsedParentsByIso2 = {};
  // NANP behavior: derive 3-digit NPAs from fixed/mobile patterns
  Object.keys(countryToMetadata).forEach((REG) => {
    const iso2 = REG.toLowerCase();
    if (!allowIso2.has(iso2)) return;
    // find dial code for region
    let dial = null;
    // In XML mode we already built dialCodeToRegions, so find the dial by reverse lookup
    for (const [dc, regs] of Object.entries(dialCodeToRegions)) {
      if (regs.includes(iso2)) { dial = dc; break; }
    }
    if (!dial) return;
  const siblings = dialCodeToRegions[dial] || [];
  if (siblings.length <= 1) return; // unique dial code -> no areaCodes
  // Use explicit main flag from XML to decide skipping area code derivation
  const srcForRegion = xmlSourcesByIso2[iso2];
  if (srcForRegion && srcForRegion.main) return; // main region -> skip areaCodes
    // Skip area-code derivation for excluded regions
    if (AREA_CODES_EXCLUDE.has(iso2)) return;
    let cands = [];
    if (dial === "1") {
      // NANP: for non-main regions, extract 3-digit NPAs from fixed/mobile patterns
      const src = srcForRegion;
      if (src) {
        let npas = deriveNanpNpasFromPatterns(src.fixed, src.mobile);
        // Fallback: take a 3-digit slice from the first simple prefix if needed
        if (!npas.length) {
          const simple = [];
          (src.fixed || []).forEach((p) => simple.push(...simplePrefixesFromPattern(p)));
          (src.mobile || []).forEach((p) => simple.push(...simplePrefixesFromPattern(p)));
          const first = simple.find((s) => /^[2-9]\d{2,6}$/.test(s));
          if (first) npas = [first.slice(0, 3)];
        }
        if (npas.length) cands = npas;
      }
    } else {
      // XML mode: use filtered IM-style extraction from leading/fixed/mobile only
      const src = srcForRegion;
      if (src) cands = extractGenericPrefixesXml(src);
    }
    if (cands && cands.length) {
      const uniq = Array.from(new Set(cands));
      const collapsed = collapseExhaustiveRanges(uniq);
      candidatePrefixesByIso2[iso2] = collapsed;
      // Build raw expansion set from fixed/mobile patterns to detect which entries are collapsed parents
      const rawSet = new Set();
      const src = srcForRegion;
      if (src) {
        const collect = (list) => {
          if (!Array.isArray(list)) return;
          for (const p of list) {
            if (typeof p !== "string" || !p) continue;
            for (const s of simplePrefixesFromPattern(p)) rawSet.add(s);
          }
        };
        collect(src.fixed);
        collect(src.mobile);
      }
      const parents = new Set(collapsed.filter((s) => !rawSet.has(s)));
      if (parents.size) collapsedParentsByIso2[iso2] = parents;
    }
  });

  // For each dial code group, shrink prefixes to minimal unique values among non-main regions
  // Allow per-dial-code minimum lengths to ensure meaningful disambiguation (e.g., 44 → 4 digits)
  // const minUniqueLenByDial = {
  //   1: 3,
  //   7: 1,
  //   44: 4,
  //   262: 3,
  //   212: 4,
  //   599: 1,
  // };
  const uniquePrefixByIso2 = {};
  Object.keys(dialCodeToRegions).forEach((dial) => {
    const regions = dialCodeToRegions[dial];
    if (!regions || regions.length <= 1) return;
    const rest = regions.slice(1);
    const entries = rest
      .map((r) => ({ iso2: r, full: candidatePrefixesByIso2[r] }))
      .filter((e) => Array.isArray(e.full) && e.full.length);
    if (!entries.length) return;
    // determine minimal unique truncation length between 2 and full length
    const allLens = entries.flatMap((e) => e.full.map((s) => s.length));
    const maxLen = Math.min(6, Math.max(...allLens));
    // const minLenDefault = 2;
    const minLen = 1;//Math.max(minLenDefault, Number(minUniqueLenByDial[dial]) || minLenDefault);
    // Choose the longest possible unique length first (descending), then collapse if possible
    for (let len = maxLen; len >= minLen; len--) {
      const seen = new Map();
      let collision = false;
      // Expand all candidates per region, extending to len when needed
      const expanded = entries.map((e) => {
        const arr = [];
        for (const full of e.full) {
          let val = full;
          const parents = collapsedParentsByIso2[e.iso2];
          const isCollapsed = parents && parents.has(full);
          if (val.length < len && !isCollapsed) {
            val = extendPrefixXml(e.iso2, val, len, xmlSourcesByIso2);
          }
          arr.push(val.slice(0, Math.min(len, val.length)));
        }
        return { iso2: e.iso2, arr: Array.from(new Set(arr)) };
      });
      // Check collisions across all regions
      for (const e of expanded) {
        for (const k of e.arr) {
          if (seen.has(k)) { collision = true; break; }
          seen.set(k, e.iso2);
        }
        if (collision) break;
      }
      if (!collision) {
        // Try to collapse ranges per region without breaking uniqueness
        const collapsedPerIso2 = new Map();
        let collapseCollision = false;
        const seenCollapsed = new Map();
        for (const e of expanded) {
          const collapsedArr = collapseExhaustiveRanges(e.arr);
          collapsedPerIso2.set(e.iso2, collapsedArr);
        }
        // Validate uniqueness after collapse
        for (const e of expanded) {
          const arrToUse = collapsedPerIso2.get(e.iso2) || e.arr;
          for (const k of arrToUse) {
            if (seenCollapsed.has(k)) { collapseCollision = true; break; }
            seenCollapsed.set(k, e.iso2);
          }
          if (collapseCollision) break;
        }
        if (!collapseCollision) {
          for (const e of expanded) uniquePrefixByIso2[e.iso2] = collapsedPerIso2.get(e.iso2) || e.arr;
        } else {
          for (const e of expanded) uniquePrefixByIso2[e.iso2] = e.arr;
        }
        return;
      }
    }
    // If we reach here, use full prefixes
    for (const e of entries) uniquePrefixByIso2[e.iso2] = e.full;
  });

  function getNationalPrefix(iso2) {
    const np = xmlNationalPrefixByIso2[iso2];
    return typeof np === "string" && np.length ? np : null;
  }

  const dialCodes = Object.keys(dialCodeToRegions);
  console.log("Found", dialCodes.length, "country calling codes");
  dialCodes
    .sort((a, b) => Number(a) - Number(b))
    .forEach((dialCode) => {
  const regions = dialCodeToRegions[dialCode];
      regions.forEach((iso2) => {
        if (!allowIso2.has(iso2)) return;
        // Priority is ignored; always 0
        // Compute area codes starting from curated data.ts, then add uncovered generated codes
        let areaCodes = null;
        const baseline = curatedAreaCodesByIso2.get(iso2) || null;
        const area = uniquePrefixByIso2[iso2];
        const generated = Array.isArray(area) && area.length ? area.slice() : [];
        const usedCurated = Array.isArray(baseline) && baseline.length > 0;
        if (usedCurated) {
          const base = baseline.slice();
          // Determine which generated codes are not already covered by baseline
          const isCoveredByBaseline = (code) => base.some((b) => typeof b === "string" && code.startsWith(b));
          const isBroaderThanBaseline = (code) => base.some((b) => typeof b === "string" && b.startsWith(code));
          for (const g of generated) {
            if (!isCoveredByBaseline(g) && !isBroaderThanBaseline(g)) base.push(g);
          }
          areaCodes = base;
        } else {
          areaCodes = generated.length ? generated : null;
        }
        // Sort generated area codes numerically for deterministic, readable output
        if (Array.isArray(areaCodes)) {
          areaCodes = sortDigitStringsNumeric(areaCodes);
        }
        // Generic collapse: avoid collapsing when using curated baseline to preserve existing sets
        if (Array.isArray(areaCodes)) {
          const usedCurated = Array.isArray(baseline) && baseline.length > 0;
          if (!usedCurated) areaCodes = collapseExhaustiveRanges(areaCodes);
        }
        const nationalPrefix = getNationalPrefix(iso2);
        const curatedPriority = curatedPriorityByIso2.get(iso2) || 0;
        const tuple = [iso2, dialCode, curatedPriority, null, null];
        if (areaCodes) tuple[3] = areaCodes;
        if (nationalPrefix) tuple[4] = nationalPrefix;
        tuples.push(tuple);
      });
    });

  tuples.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
  const ts = `export const rawCountryData = ${JSON.stringify(tuples, null, 2)} as const;\n`;
  fs.mkdirSync(path.dirname(OUT_TS), { recursive: true });
  fs.writeFileSync(OUT_TS, ts, "utf8");
  console.log(`Wrote ${tuples.length} entries as TS to ${path.relative(process.cwd(), OUT_TS)}`);

  // Always run diff against curated data.ts after generation
  function loadArray(tsPath, isDataTs = false) {
    let s = fs.readFileSync(tsPath, "utf8");
    if (isDataTs) {
      // strip line comments
      s = s.replace(/\/\/.*$/mg, "");
      // drop trailing TS type exports if present
      s = s.replace(/export type[\s\S]*/, "");
    }
    // normalize export for eval
    s = s.replace(/export const rawCountryData\s*=\s*/, "var rawCountryData = ");
    s = s.replace(/\] as const;?\s*$/, "];\n");
    const ctx = {};
    vm.createContext(ctx);
    vm.runInContext(s, ctx, { filename: tsPath });
    return ctx.rawCountryData;
  }

  function toMap(arr) {
    const m = new Map();
    for (const t of arr) {
      const [iso2, dialCode, priority = 0, areaCodes = null, nationalPrefix = null] = t;
      m.set(iso2, { iso2, dialCode, priority, areaCodes: areaCodes ?? null, nationalPrefix: nationalPrefix ?? null });
    }
    return m;
  }

  function eqArr(a, b) {
    if (a === b) return true;
    if (!a && !b) return true;
    if (!a || !b) return false;
    if (a.length !== b.length) return false;
    for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
    return true;
  }

  function summarizeArrayChange(name, oldArr, newArr) {
    const oa = Array.isArray(oldArr) ? oldArr : [];
    const na = Array.isArray(newArr) ? newArr : [];
    const oSet = new Set(oa);
    const nSet = new Set(na);
    const added = na.filter((x) => !oSet.has(x));
    const removed = oa.filter((x) => !nSet.has(x));
    const header = `${name}: ${oldArr ? oa.length : "null"} items -> ${newArr ? na.length : "null"} items`;
    const limit = 12;
    const fmt = (arr) => {
      if (!arr.length) return "[]";
      if (arr.length <= limit) return `[${arr.map((s)=>JSON.stringify(s)).join(",")}]`;
      const head = arr.slice(0, limit - 2).map((s)=>JSON.stringify(s)).join(",");
      const tail = arr.slice(-2).map((s)=>JSON.stringify(s)).join(",");
      const more = arr.length - (limit);
      return `[${head},…${more} more… ,${tail}]`;
    };
    if (added.length === 0 && removed.length === 0 && Array.isArray(oldArr) && Array.isArray(newArr)) {
      // Order-only change
      return `${name}: reordered (${oa.length} items)`;
    }
    // For small diffs, show full arrays for readability
    if ((oa.length + na.length) <= 40 && (added.length + removed.length) <= 20) {
      return `${name}: ${JSON.stringify(oldArr)} -> ${JSON.stringify(newArr)}`;
    }
    const parts = [];
    if (added.length) parts.push(`New item(s): ${fmt(added)}`);
    if (removed.length) parts.push(`Removed item(s): ${fmt(removed)}`);
    return `${header}${parts.length ? ", " + parts.join(", ") : ""}`;
  }

  function runDiff() {
    const GEN_PATH = OUT_TS;
    const ORIG_PATH = CURATED_DATA_TS_PATH;
    if (!fs.existsSync(GEN_PATH)) {
      console.error("Missing generated file:", GEN_PATH);
      return;
    }
    if (!fs.existsSync(ORIG_PATH)) {
      console.error("Missing original file:", ORIG_PATH);
      return;
    }
    const gen = loadArray(GEN_PATH, false);
    const orig = loadArray(ORIG_PATH, true);
    const g = toMap(gen);
    const o = toMap(orig);
    const allIso2 = [...new Set([...g.keys(), ...o.keys()])].sort();
    const lines = [];
    let added = 0, removed = 0, changed = 0;
    for (const iso of allIso2) {
      const G = g.get(iso);
      const O = o.get(iso);
      if (!G) {
        removed++;
        lines.push(`- ${iso}: dial=${O.dialCode}, priority=${O.priority}, areaCodes=${JSON.stringify(O.areaCodes)}, nationalPrefix=${JSON.stringify(O.nationalPrefix)}`);
        continue;
      }
      if (!O) {
        added++;
        lines.push(`+ ${iso}: dial=${G.dialCode}, priority=${G.priority}, areaCodes=${JSON.stringify(G.areaCodes)}, nationalPrefix=${JSON.stringify(G.nationalPrefix)}`);
        continue;
      }
      const parts = [];
      if (G.dialCode !== O.dialCode) parts.push(`dial: ${O.dialCode} -> ${G.dialCode}`);
      if ((G.priority || 0) !== (O.priority || 0)) parts.push(`priority: ${O.priority || 0} -> ${G.priority || 0}`);
  if (!eqArr(G.areaCodes, O.areaCodes)) parts.push(summarizeArrayChange("areaCodes", O.areaCodes, G.areaCodes));
      if ((G.nationalPrefix || null) !== (O.nationalPrefix || null)) parts.push(`nationalPrefix: ${JSON.stringify(O.nationalPrefix || null)} -> ${JSON.stringify(G.nationalPrefix || null)}`);
      if (parts.length) {
        changed++;
        lines.push(`~ ${iso}: ${parts.join(", ")}`);
      }
    }
    const out = [`# Summary: +${added} added, ~${changed} changed, -${removed} removed`, ...(lines.length ? lines : [])].join("\n");
    console.log(out);
    // Write to tmp file
    const outDir = path.resolve(REPO_ROOT, "tmp");
    try { fs.mkdirSync(outDir, { recursive: true }); } catch { /* ignore */ }
    fs.writeFileSync(path.join(outDir, "rawCountryData.diff.txt"), out + "\n", "utf8");
    if (added || removed || changed) {
      // Exit with non-zero to signal differences (suitable for CI failure)
      process.exit(1);
    }
  }

  runDiff();

  // iso2 list is sourced from iso2-codes.js; we never write it from this script
}

try {
  main();
} catch (e) {
  console.error("Generation failed:", e && e.stack ? e.stack : e);
  process.exit(1);
}


================================================
FILE: scripts/playwright-linux-docker.sh
================================================
#!/usr/bin/env bash
set -euo pipefail

PLAYWRIGHT_VERSION_DEFAULT="1.58.0"
PLAYWRIGHT_VERSION="$PLAYWRIGHT_VERSION_DEFAULT"

# Try to keep the Docker image version in sync with the repo's Playwright version.
if command -v node >/dev/null 2>&1; then
  DETECTED_VERSION="$(node -e "
    try {
      const pkg = require('./package.json');
      const spec = (pkg.devDependencies && (pkg.devDependencies['@playwright/test'] || pkg.devDependencies.playwright)) || '';
      const v = String(spec).replace(/^[^0-9]*/, '');
      if (v) process.stdout.write(v);
    } catch (e) {
      // ignore
    }
  " 2>/dev/null || true)"

  if [[ -n "$DETECTED_VERSION" ]]; then
    PLAYWRIGHT_VERSION="$DETECTED_VERSION"
  fi
fi

IMAGE_DEFAULT="mcr.microsoft.com/playwright:v${PLAYWRIGHT_VERSION}-jammy"
IMAGE="${PLAYWRIGHT_DOCKER_IMAGE:-$IMAGE_DEFAULT}"

if ! command -v docker >/dev/null 2>&1; then
  echo "Docker is required to run Playwright in Linux." >&2
  echo "Install Docker Desktop, then re-run this command." >&2
  exit 1
fi

WORKDIR="/work"

HOST_UID=""
HOST_GID=""
if command -v id >/dev/null 2>&1; then
  HOST_UID="$(id -u)"
  HOST_GID="$(id -g)"
fi

DOCKER_ARGS=(
  --rm
  --shm-size=1g
  -e HOME=/tmp
  -e npm_config_cache=/tmp/.npm
  -e npm_config_prefix=/tmp/.npm-global
  -e npm_config_update_notifier=false
  -e npm_config_fund=false
  -e npm_config_audit=false
  -e HOST_UID
  -e HOST_GID
  -v "$(pwd):$WORKDIR"
  -w "$WORKDIR"
  # Keep node_modules inside the container (avoid host permission issues and platform-specific deps).
  -v "$WORKDIR/node_modules"
)

# Useful on Apple Silicon if the image/platform doesn't match.
if [[ -n "${PLAYWRIGHT_DOCKER_PLATFORM:-}" ]]; then
  DOCKER_ARGS+=( --platform "$PLAYWRIGHT_DOCKER_PLATFORM" )
fi

# Run the full workflow inside the container:
# - install deps
# - build
# - run Playwright tests (pass through any extra args)
exec docker run "${DOCKER_ARGS[@]}" "$IMAGE" bash -lc \
  '
    set -euo pipefail

    mkdir -p "$npm_config_cache" "$npm_config_prefix"

    npm ci

    # Closure Compiler (grunt-google-closure-compiler) requires Java.
    apt-get update
    apt-get install -y --no-install-recommends openjdk-17-jre-headless
    rm -rf /var/lib/apt/lists/*

    npm run build

    npm run test:e2e -- "$@"

    # If we ran as root, ensure generated files are owned by the host user.
    if [[ -n "${HOST_UID:-}" && -n "${HOST_GID:-}" ]]; then
      chown -R "${HOST_UID}:${HOST_GID}" \
        "$WORKDIR/build" \
        "$WORKDIR/tmp" \
        "$WORKDIR/playwright-report" \
        "$WORKDIR/test-results" \
        "$WORKDIR/tests-e2e" \
        2>/dev/null || true
    fi
  ' -- "$@"


================================================
FILE: site/.gitignore
================================================
node_modules/
tmp/
build/


================================================
FILE: site/Gruntfile.js
================================================
module.exports = function (grunt) {
  // load all tasks from package.json
  require("load-grunt-config")(grunt);

  // build css
  grunt.registerTask("build:css", [
    "sass",
    "template:website_css",
    "template:large_flags_overrides_css",
    "cssmin",
  ]);

  // build esbuild
  grunt.registerTask("build:esbuild", [
    "template:lookup_country_js", // needs to go through esbuild for IPAPI_TOKEN injection
    "template:right_to_left_js",
    "template:angular_component_js",
    "template:react_component_js",
    "template:playground_js",
    "shell:esbuild",
  ]);

  // build vue component
  grunt.registerTask("build:vue_component", [
    "template:vue_component_js",
    "shell:vite",
  ]);

  // build svelte component
  grunt.registerTask("build:svelte_component", [
    "template:svelte_component_js",
    "shell:viteSvelte",
  ]);

  // build all
  grunt.registerTask("build", [
    "shell:clearBuild",
    "shell:fetchStats",
    "copy",
    "build:css",
    "build:esbuild",
    "build:vue_component",
    "build:svelte_component",
    "template",
    "replace:validationPrecise",
    "strip-html-comments",
  ]);

  grunt.registerTask("strip-html-comments", () => {
    const htmlFiles = grunt.file.expand({ dot: true }, ["build/**/*.html"]);
    let updatedCount = 0;

    htmlFiles.forEach((filePath) => {
      const input = grunt.file.read(filePath);
      const output = input.replace(/<!--[\s\S]*?-->/g, "");
      if (output !== input) {
        grunt.file.write(filePath, output);
        updatedCount += 1;
      }
    });

    grunt.log.writeln(
      `strip-html-comments: processed ${htmlFiles.length} files, updated ${updatedCount}`
    );
  });
};


================================================
FILE: site/README.md
================================================
# Website for intl-tel-input

Live here: https://intl-tel-input.com

There are 4 page types:
- Homepage
- Docs page
- Playground
- Examples page

## Contributing

To build and run the site locally:
- Install the dependencies: run `npm install` in the root of the intl-tel-input project
- Build the website: cd into the site/ directory and run `npm run build`
- 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.

Making changes:  
In the site/ directory, run `npm run watch` to automatically re-build when you edit the source files.

See src/ directory for HTML templates/partials, JS/CSS and the docs markdown files.

See grunt/template.js for where everything is threaded together.

================================================
FILE: site/esbuild/build.mjs
================================================
import { build } from "esbuild";
import externalUtilsPlugin from "./externalUtilsPlugin.mjs";

const sharedOptions = {
  bundle: true,
  plugins: [externalUtilsPlugin],
  minify: true,
  define: {
    // This replaces the string "process.env.IPAPI_TOKEN" with the actual value from Cloudflare's environment
    "process.env.IPAPI_TOKEN": JSON.stringify(process.env.IPAPI_TOKEN || ""),
  },
};

// lookup country example
build({
  ...sharedOptions,
  entryPoints: ["tmp/examples/js/lookup_country.js"],
  outfile: "build/examples/js/lookup_country.js",
});

// right to left example
build({
  ...sharedOptions,
  entryPoints: ["tmp/examples/js/right_to_left.js"],
  outfile: "build/examples/js/right_to_left_bundle.js",
});

// react component example
build({
  ...sharedOptions,
  loader: { ".js": "jsx" },
  entryPoints: ["tmp/examples/js/react_component.js"],
  outfile: "build/examples/js/react_component_bundle.js",
});

// angular component example
build({
  ...sharedOptions,
  loader: { ".ts": "ts" },
  tsconfig: "../angular/tsconfig.json",
  entryPoints: ["tmp/examples/js/angular_component.ts"],
  outfile: "build/examples/js/angular_component_bundle.js",
});

// playground
build({
  ...sharedOptions,
  entryPoints: ["src/playground/js/playground.js"],
  outfile: "build/js/playground.js",
});

// all JS files in /src/js
build({
  ...sharedOptions,
  entryPoints: ["src/js/**/*.js"],
  outdir: "build/js",
});

================================================
FILE: site/esbuild/externalUtilsPlugin.mjs
================================================
const externalUtilsPlugin = {
  name: "external-utils-plugin",
  setup({ onResolve }) {
    onResolve({ filter: /utils/ }, args => {
      return { path: args.path, external: true };
    });
  },
};

export default externalUtilsPlugin;

================================================
FILE: site/grunt/copy.js
================================================
module.exports = function (grunt) {
  return {
    plugin: {
      cwd: "../build", // set working folder / root to copy
      src: "**/*", // copy all files and subfolders
      dest: "build/intl-tel-input", // destination folder
      expand: true, // required when using cwd
    },
    react: {
      cwd: "../react/build", // set working folder / root to copy
      src: "**/*", // copy all files and subfolders
      dest: "build/intl-tel-input/react", // destination folder
      expand: true, // required when using cwd
    },
    vue: {
      cwd: "../vue/build", // set working folder / root to copy
      src: "**/*", // copy all files and subfolders
      dest: "build/intl-tel-input/vue", // destination folder
      expand: true, // required when using cwd
    },
    angular: {
      cwd: "../angular/build", // set working folder / root to copy
      src: "**/*", // copy all files and subfolders
      dest: "build/intl-tel-input/angular", // destination folder
      expand: true, // required when using cwd
    },
    svelte: {
      cwd: "../svelte/build", // set working folder / root to copy
      src: "**/*", // copy all files and subfolders
      dest: "build/intl-tel-input/svelte", // destination folder
      expand: true, // required when using cwd
    },
    static: {
      cwd: "static", // set working folder / root to copy
      src: "**/*", // copy all files and subfolders
      dest: "build", // destination folder
      expand: true, // required when using cwd
    },
    htaccess: {
      cwd: "static", // set working folder / root to copy
      src: ".htaccess", // copy the .htaccess dotfile
      dest: "build", // destination folder
      expand: true, // required when using cwd
    },
    examples_css: {
      cwd: "src/examples/css", // set working folder / root to copy
      src: "*",
      dest: "build/examples/css", // destination folder
      expand: true, // required when using cwd
    }
  };
};


================================================
FILE: site/grunt/cssmin.js
================================================
module.exports = function(grunt) {
  return {
    target: {
      files: {
        // NOTE: don't minify large_flags_overrides.css because we link to it as an example from the large_flags example page
        'build/css/website.css': 'build/css/website.css',
        'build/css/playground.css': 'build/css/playground.css',
        'build/css/homepage.css': 'build/css/homepage.css',
        'build/css/docs.css': 'build/css/docs.css'
      }
    }
  };
};


================================================
FILE: site/grunt/fetchStats.js
================================================
const https = require("https");
const fs = require("fs");
const path = require("path");

function httpsGet(url) {
  return new Promise((resolve, reject) => {
    https.get(url, { headers: { "User-Agent": "intl-tel-input-build" } }, (res) => {
      let data = "";
      res.on("data", (chunk) => { data += chunk; });
      res.on("end", () => {
        if (res.statusCode !== 200) {
          reject(new Error(`HTTP ${res.statusCode} for ${url}`));
        } else {
          resolve(data);
        }
      });
    }).on("error", reject);
  });
}

function formatNumber(n) {
  if (n >= 1_000_000) {
    const val = n / 1_000_000;
    return `${val % 1 === 0 ? val.toFixed(0) : val.toFixed(1)}M`;
  }
  if (n >= 1_000) {
    const val = n / 1_000;
    return `${val % 1 === 0 ? val.toFixed(0) : val.toFixed(1)}k`;
  }
  return `${n}`;
}

function roundToDisplay(n) {
  if (n >= 1_000_000) {
    return Math.round(n / 100_000) * 100_000;
  }
  if (n >= 100_000) {
    return Math.round(n / 1_000) * 1_000;
  }
  if (n >= 1_000) {
    return Math.round(n / 100) * 100;
  }
  return n;
}

async function fetchStats() {
  const statsPath = path.join(__dirname, "..", "tmp", "stats.json");

  // Default/fallback values
  const defaults = { websites: "120k", downloads: "3.2M", stars: "8.2k" };

  const isProd = process.argv.includes("--env=prod");
  if (!isProd) {
    console.log("Dev build — using default stats");
    fs.mkdirSync(path.dirname(statsPath), { recursive: true });
    fs.writeFileSync(statsPath, JSON.stringify(defaults, null, 2));
    return;
  }

  let stats = { ...defaults };

  try {
    const ghData = JSON.parse(
      await httpsGet("https://api.github.com/repos/jackocnr/intl-tel-input")
    );
    stats.stars = formatNumber(roundToDisplay(ghData.stargazers_count));
  } catch (e) {
    console.warn("Failed to fetch GitHub stars, using fallback:", e.message);
  }

  try {
    const npmData = JSON.parse(
      await httpsGet("https://api.npmjs.org/downloads/point/last-month/intl-tel-input")
    );
    stats.downloads = formatNumber(roundToDisplay(npmData.downloads));
  } catch (e) {
    console.warn("Failed to fetch npm downloads, using fallback:", e.message);
  }

  try {
    const nerdyHtml = await httpsGet(
      "https://www.nerdydata.com/reports/international-telephone-input/719de9d2-d0e7-4988-b02f-9f9d52687076"
    );
    const match = nerdyHtml.match(/"answerCount"\s*:\s*(\d+)/);
    if (match) {
      stats.websites = formatNumber(roundToDisplay(Number(match[1])));
    }
  } catch (e) {
    console.warn("Failed to fetch NerdyData count, using fallback:", e.message);
  }

  fs.mkdirSync(path.dirname(statsPath), { recursive: true });
  fs.writeFileSync(statsPath, JSON.stringify(stats, null, 2));
  console.log("Stats fetched:", stats);
}

fetchStats();


================================================
FILE: site/grunt/replace.js
================================================
module.exports = function(grunt) {
  return {
    validationPrecise: {
      options: {
        patterns: [
          {
            match: /\biti\.isValidNumber\(\)/g,
            replacement: "iti.isValidNumberPrecise()",
          },
        ],
      },
      files: {
        "build/examples/js/validation_precise.js": "build/examples/js/validation_precise.js",
      },
    }
  };
};

================================================
FILE: site/grunt/sass.js
================================================
const sass = require('sass');

module.exports = function(grunt) {
  return {
    main: {
      options: {
        implementation: sass,
        sourcemap: "none",
      },
      files: {
        'build/css/website.css': 'src/css/website.scss',
        'build/css/large_flags_overrides.css': 'src/css/large_flags_overrides.scss',
        'build/css/highlightjs_overrides.css': 'src/css/highlightjs_overrides.scss',
        'build/css/playground.css': 'src/css/playground.scss',
        'build/css/docs.css': 'src/css/docs.scss',
        'build/css/homepage.css': 'src/css/homepage.scss',
      }
    },
  };
};


================================================
FILE: site/grunt/shell.js
================================================
const os = require('os');

module.exports = function(grunt) {
  return {
    clearBuild: {
      command: "rm -rf build tmp",
    },
    fetchStats: {
      command: `node grunt/fetchStats.js --env=${grunt.option("env") || "dev"}`,
    },
    esbuild: {
      command: "node esbuild/build.mjs",
    },
    vite: {
      command: "vite build --config src/examples/js/viteVueDemo.config.js",
    },
    viteSvelte: {
      command: "vite build --config src/examples/js/viteSvelteDemo.config.mjs",
    },
  };
};


================================================
FILE: site/grunt/template.js
================================================
module.exports = function (grunt) {
  const path = require("path");
  const {
    cacheBust,
    getDirHash,
    getI18nLanguages,
    createMarkdownRenderer,
    buildOpenGraphMetaTags,
  } = require("./templateUtils");

  const env = grunt.option("env");
  const isDevBuild = env === "dev" || env === "development";
  const showRightSidebarAd = !isDevBuild;

  const {
    makeTemplateTask,
    makeLayoutTask,
    readCommonPagePartials,
    readCommonBodyEndScript,
    readItiLiveResultsScript,
    readItiScript,
  } = require("./templateGruntHelpers");

  // Helper: create a cache-bust template task for a built asset path
  const makeCacheBustTask = (assetPath) => ({
    src: assetPath,
    dest: assetPath,
    options: { data: () => ({ cacheBust }) },
  });

  const {
    docsDropdownPages,
    examplesDropdownPages,
  } = require("./templateNav");

  const md = createMarkdownRenderer();

  const toBcp47LanguageTag = (code) => {
    const raw = String(code || "").trim();
    if (!raw) return "";
    const parts = raw.split("-");
    if (parts.length === 1) return parts[0].toLowerCase();
    const [lang, region, ...rest] = parts;
    const normLang = String(lang).toLowerCase();
    const normRegion = region && region.length === 2 ? String(region).toUpperCase() : String(region || "");
    return [normLang, normRegion, ...rest].filter(Boolean).join("-");
  };

  const escapeHtml = (value) =>
    String(value ?? "")
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/\"/g, "&quot;")
      .replace(/'/g, "&#39;");

  const createI18nLanguageListText = (languageCodes) => {
    const codes = Array.isArray(languageCodes) ? languageCodes.filter(Boolean) : [];
    if (!codes.length) {
      return "_No language modules found._";
    }

    let displayNames = null;
    try {
      if (typeof Intl !== "undefined" && Intl.DisplayNames) {
        displayNames = new Intl.DisplayNames(["en"], { type: "language" });
      }
    } catch {
      displayNames = null;
    }

    const items = codes
      .map((code) => {
        const tag = toBcp47LanguageTag(code);
        let label = null;
        try {
          label = displayNames && tag ? displayNames.of(tag) : null;
        } catch {
          label = null;
        }

        return {
          code: String(code),
          label: label ? String(label) : "",
        };
      });

    items.sort((a, b) => {
      const aKey = a.label || a.code;
      const bKey = b.label || b.code;
      return aKey.localeCompare(bKey, "en", { sensitivity: "base" });
    });

    return items
      .map(({ code, label }) => {
        const text = label ? `${label} (${code})` : code;
        return escapeHtml(text);
      })
      .join(", ");
  };

  const homepageTitle = "International Telephone Input";
  const homepageMetaDesc = "A JavaScript plugin for entering, formatting and validating international telephone numbers. Includes React, Vue, Angular and Svelte components.";
  const homepageCanonicalUrl = "https://intl-tel-input.com";

  const playgroundTitle = "Playground - International Telephone Input";
  const playgroundMetaDesc = "Try different initialisation options and see the plugin update live.";
  const playgroundCanonicalUrl = "https://intl-tel-input.com/playground";

  const notFoundTitle = "404 - Page not found | intl-tel-input";
  const notFoundMetaDesc = "Page not found.";
  const notFoundCanonicalUrl = "https://intl-tel-input.com/404";

  const config = {
    // cache bust common assets
    iti_script: {
      src: "src/shared/iti_script.html.ejs",
      dest: "tmp/shared/iti_script.html",
      options: {
        data: () => ({ cacheBust, isDevBuild }),
      },
    },
    website_css: makeCacheBustTask("build/css/website.css"),
    homepage_css: makeCacheBustTask("build/css/homepage.css"),
    docs_css: makeCacheBustTask("build/css/docs.css"),
    playground_css: makeCacheBustTask("build/css/playground.css"),
    large_flags_overrides_css: makeCacheBustTask("build/css/large_flags_overrides.css"),

    // homepage
    homepage_layout: {
      src: "src/layout_template.html.ejs",
      dest: "tmp/homepage/homepage_layout.html",
      options: {
        data: () => {
          const stats = JSON.parse(grunt.file.read("tmp/stats.json"));
          const content = grunt.file.read("src/homepage/homepage_content.html")
            .replace("{{STAT_WEBSITES}}", stats.websites)
            .replace("{{STAT_DOWNLOADS}}", stats.downloads)
            .replace("{{STAT_STARS}}", stats.stars);
          return {
            layoutClass: "iti-layout-no-sidebars",
            showLeftSidebar: false,
            content,
            name: "home",
            pageType: "home",
            docsDropdownPages,
            examplesDropdownPages,
          };
        },
      },
    },
    homepage_page: {
      src: "src/homepage/homepage_page_template.html.ejs",
      dest: "build/index.html",
      options: {
        data: () => ({
          homepageTitle,
          homepageMetaDesc,
          homepageCanonicalUrl,
          cacheBust,
          isDevBuild,
          ...readCommonPagePartials(grunt, {
            cacheBust,
            isDevBuild,
            iti_styles: "homepage",
          }),
          og_meta_tags: buildOpenGraphMetaTags({
            title: homepageTitle,
            description: homepageMetaDesc,
            url: homepageCanonicalUrl,
          }),
          layout: grunt.file.read("tmp/homepage/homepage_layout.html"),
          common_body_end: readCommonBodyEndScript(grunt),
          iti_live_results_script: readItiLiveResultsScript(grunt, { cacheBust }),
          iti_script: readItiScript(grunt),
        }),
      },
    },

    // playground
    playground_js: {
      src: "src/playground/js/templates/playgroundConstants.js.ejs",
      dest: "tmp/playground/playgroundConstants.js",
      options: {
        data: () => ({
          cacheBust,
          getDirHash,
          i18nLanguages: getI18nLanguages(),
        }),
      },
    },
    playground_layout: {
      src: "src/layout_template.html.ejs",
      dest: "tmp/playground/playground_layout.html",
      options: {
        data: () => ({
          showLeftSidebar: false,
          layoutClass: "iti-layout-no-sidebars iti-layout--playground",
          content: grunt.file.read("src/playground/playground_content.html"),
          pageType: "playground",
          name: "playground",
          docsDropdownPages,
          examplesDropdownPages,
        }),
      },
    },
    playground_page: {
      src: "src/playground/playground_page_template.html.ejs",
      dest: "build/playground.html",
      options: {
        data: () => ({
          playgroundTitle,
          playgroundMetaDesc,
          playgroundCanonicalUrl,
          cacheBust,
          ...readCommonPagePartials(grunt, {
            cacheBust,
            isDevBuild,
            iti_styles: "normal",
            highlightjs_styles: true,
          }),
          og_meta_tags: buildOpenGraphMetaTags({
            title: playgroundTitle,
            description: playgroundMetaDesc,
            url: playgroundCanonicalUrl,
          }),
          layout: grunt.file.read("tmp/playground/playground_layout.html"),
          common_body_end: readCommonBodyEndScript(grunt),
          iti_live_results_script: readItiLiveResultsScript(grunt, { cacheBust }),
          iti_script: readItiScript(grunt),
        }),
      },
    },

    // 404
    not_found_layout: {
      src: "src/layout_template.html.ejs",
      dest: "tmp/404/not_found_layout.html",
      options: {
        data: () => ({
          showLeftSidebar: false,
          layoutClass: "iti-layout-no-sidebars",
          content: grunt.file.read("src/404/404_content.html"),
          pageType: "home",
          name: "404",
          docsDropdownPages,
          examplesDropdownPages,
        }),
      },
    },
    not_found_page: {
      src: "src/404/404_page_template.html.ejs",
      dest: "build/404.html",
      options: {
        data: () => ({
          cacheBust,
          head_title: notFoundTitle,
          canonical_url: notFoundCanonicalUrl,
          meta_desc: notFoundMetaDesc,
          og_meta_tags: buildOpenGraphMetaTags({
            title: notFoundTitle,
            description: notFoundMetaDesc,
            url: notFoundCanonicalUrl,
          }),
          ...readCommonPagePartials(grunt, {
            cacheBust,
            isDevBuild,
            iti_styles: "none",
          }),
          layout: grunt.file.read("tmp/404/not_found_layout.html"),
          common_body_end: readCommonBodyEndScript(grunt),
        }),
      },
    },
  };

  const registerExample = ({
    key,
    title,
    metaDesc,
    js = {},
    extraJsTasks = [],
    content = {},
    layoutExtra = {},
    pageExtra = {},
  }) => {
    const slug = key.replace(/_/g, "-");
    const jsSrc = js.src || `src/examples/js/${key}.js`;
    // some examples build to tmp first
    const jsDest = js.dest || `${js.destDir || "build"}/examples/js/${key}.js`;
    const displayCode = content.displayCode || jsDest;
    const scriptName = js.script || `${key}.js`;

    const contentDest = content.dest || `tmp/examples/${key}_content.html`;
    const layoutDest = content.layoutDest || `tmp/examples/${key}_layout.html`;
    const pageDest = content.pageDest || `build/examples/${slug}.html`;

    // the HTML to actually use for the demo
    const markupName = content.markupName || key;
    const markupPath = `src/examples/html/${markupName}.html`;
    // the (cleaner) HTML we want to display in the "Html" section
    const displayMarkupCandidate = `src/examples/html/${markupName}_display_code.html`;
    const displayMarkupPath = grunt.file.exists(displayMarkupCandidate) ? displayMarkupCandidate : markupPath;

    const fullTitle = `${title} example - International Telephone Input`;
    const canonicalUrl = `https://intl-tel-input.com/examples/${slug}`;

    const templateData = {
      cacheBust,
      ...(js.data || {}),
    };

    config[`${key}_js`] = makeTemplateTask(jsSrc, jsDest, () => templateData);

    extraJsTasks.forEach((t) => {
      config[t.key] = makeTemplateTask(t.src, t.dest, () => ({ cacheBust }));
    });

    config[`${key}_content`] = makeTemplateTask(
      "src/examples/examples_content_template.html.ejs",
      contentDest,
      () => ({
        cacheBust,
        content_title: title,
        desc: grunt.file.read(`src/examples/copy/${key}_desc.html`),
        markup: grunt.file.read(markupPath),
        display_markup: grunt.file.read(displayMarkupPath),
        display_code: (() => {
          let displayCodeContent = grunt.file.read(displayCode);
          // hack so that the validation_precise example page shows the right validation method in the displayed code
          if (key === "validation_precise") {
            displayCodeContent = displayCodeContent.replace(
              /\biti\.isValidNumber\(\)/g,
              "iti.isValidNumberPrecise()"
            );
          }
          return grunt.template.process(displayCodeContent, { data: templateData });
        })(),
        script: scriptName,
        ...(content.demo_note ? { demo_note: content.demo_note } : {}),
        ...(content.hideMarkupSection
          ? { hideMarkupSection: true }
          : {}),
        ...(content.isRtl ? { isRtl: true } : {}),
        ...(content.extraData ? content.extraData() : {}),
        common_body_end: readCommonBodyEndScript(grunt),
        ...(content.includeItiScript
          ? { iti_script: readItiScript(grunt) }
          : {}),
      })
    );

    config[`${key}_layout`] = makeLayoutTask(grunt, {
      dest: layoutDest,
      showLeftSidebar: true,
      layoutClass: "iti-layout-both-sidebars",
      navPath: "src/examples/examples_nav_template.html.ejs",
      contentPath: contentDest,
      name: key,
      pageType: "examples",
      docsDropdownPages,
      examplesDropdownPages,
      extra: {
        ...layoutExtra,
        show_right_sidebar_ad: showRightSidebarAd,
      },
    });

    config[`${key}_page`] = makeTemplateTask(
      "src/examples/examples_page_template.html.ejs",
      pageDest,
      () => ({
        cacheBust,
        head_title: fullTitle,
        canonical_url: canonicalUrl,
        meta_desc: metaDesc,
        og_meta_tags: buildOpenGraphMetaTags({
          title: fullTitle,
          description: metaDesc,
          url: canonicalUrl,
        }),
        ...readCommonPagePartials(grunt, {
          cacheBust,
          isDevBuild,
          iti_styles: pageExtra.iti_styles || "normal",
          highlightjs_styles: true,
        }),
        content: grunt.file.read(layoutDest),
        ...pageExtra,
      })
    );
  };

  const docsDefinitions = [{
    key: "choose_integration",
    title: "Choose integration",
    metaDesc: "Which integration of intl-tel-input is right for you? Pure JavaScript, React, Vue, Angular or Svelte component?",
  }, {
    key: "getting_started",
    title: "Getting started",
    metaDesc: "How to quickly get up and running with intl-tel-input.",
  }, {
    key: "options",
    title: "Initialisation options",
    metaDesc: "All the different options you can use when initialising intl-tel-input.",
  }, {
    key: "localisation",
    title: "Localisation",
    metaDesc: "How to localise country names and user interface strings, including RTL support.",
  }, {
    key: "accessibility",
    title: "Accessibility",
    metaDesc: "Accessibility guidance for intl-tel-input, including keyboard and screen reader support.",
  }, {
    key: "methods",
    title: "Methods",
    metaDesc: "All the different methods you can call on an intl-tel-input instance.",
  }, {
    key: "events",
    title: "Events",
    metaDesc: "All the different events that an intl-tel-input instance can emit.",
  }, {
    key: "utils",
    title: "Utilities script",
    metaDesc: "Learn about the utils script, what it's for and how to load it.",
  }, {
    key: "theming",
    title: "Theming / dark mode",
    metaDesc: "How to theme the plugin, including how to set it up for dark mode.",
  }, {
    key: "troubleshooting",
    title: "Troubleshooting",
    metaDesc: "Solutions to common problems and FAQs about intl-tel-input.",
  }, {
    key: "faq",
    title: "FAQ",
    metaDesc: "Frequently asked questions about intl-tel-input, including common setup and localisation topics.",
  }, {
    key: "react_component",
    title: "React component",
    metaDesc: "How to use the intl-tel-input React component.",
  }, {
    key: "vue_component",
    title: "Vue component",
    metaDesc: "How to use the intl-tel-input Vue component.",
  }, {
    key: "angular_component",
    title: "Angular component",
    metaDesc: "How to use the intl-tel-input Angular component.",
  }, {
    key: "svelte_component",
    title: "Svelte component",
    metaDesc: "How to use the intl-tel-input Svelte component.",
  }];

  const exampleDefinitions = [{
    key: "lookup_country",
    title: "Lookup user's country",
    metaDesc: "Automatically set the country based on the user's IP address.",
    js: {
      // evaluate the template into tmp, then use esbuild for IPAPI_TOKEN injection
      destDir: "tmp",
    },
    content: {
      markupName: "simple_input",
      includeItiScript: true,
      displayCode: "src/examples/js/lookup_country_display_code.js",
    },
  }, {
    key: "right_to_left",
    title: "Right to left",
    metaDesc: "Support for right-to-left languages.",
    js: {
      destDir: "tmp",
      script: "right_to_left_bundle.js",
    },
    content: {
      markupName: "simple_input",
      isRtl: true,
      displayCode: "src/examples/js/right_to_left_display_code.js",
    },
    layoutExtra: { isRtl: true },
  }, {
    key: "single_country",
    title: "Single country",
    metaDesc: "When you only need to handle numbers from a single country.",
    content: {
      demo_note: "<p>Enter a US number:</p>",
      markupName: "validation",
      includeItiScript: true,
      displayCode: "src/examples/js/single_country_display_code.js",
    },
    pageExtra: { stylesheet_after_website_css: "/examples/css/validation.css" },
  }, {
    key: "validation_practical",
    title: "Validation",
    metaDesc:
      "Validate the user's phone number and if there's an error, display a relevant message.",
    js: {
      src: "src/examples/js/validation.js",
    },
    content: {
      markupName: "validation",
      includeItiScript: true,
      displayCode: "src/examples/js/validation_display_code.js",
      extraData: () => ({
        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>`,
      }),
    },
    pageExtra: {
      stylesheet_after_website_css: "/examples/css/validation.css",
    },
  }, {
    key: "validation_precise",
    title: "Precise validation (advanced)",
    metaDesc:
      "Validate the user's phone number using the more precise method, and if there's an error, display a relevant message.",
    js: {
      src: "src/examples/js/validation.js",
    },
    content: {
      markupName: "validation",
      includeItiScript: true,
      displayCode: "src/examples/js/validation_display_code.js",
      extraData: () => ({
        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>`,
      }),
    },
    pageExtra: {
      stylesheet_after_website_css: "/examples/css/validation.css",
    },
  }, {
    key: "hidden_input",
    title: "Hidden input",
    metaDesc:
      "Automatically populate a hidden input with the full international number, so it gets submitted to your backend.",
    content: {
      includeItiScript: true,
      markupName: "validation",
      displayCode: "src/examples/js/hidden_input_display_code.js",
    },
  }, {
    key: "multiple_instances",
    title: "Multiple instances",
    metaDesc: "Use multiple instances of the plugin with different configurations on the same page.",
    content: {
      includeItiScript: true,
      displayCode: "src/examples/js/multiple_instances_display_code.js",
    },
    pageExtra: {
      stylesheet_after_website_css: "/examples/css/multiple_instances.css",
    },
  }, {
    key: "display_number",
    title: "Display existing number",
    metaDesc: "Automatically format an existing number during initialisation.",
    js: {
      src: "src/examples/js/simple_init_plugin.js",
    },
    content: {
      includeItiScript: true,
      displayCode: "src/examples/js/simple_init_plugin_display_code.js",
    },
  }, {
    key: "large_flags",
    title: "Large flags",
    metaDesc: "How to display extra large flag images.",
    js: {
      src: "src/examples/js/simple_init_plugin.js",
    },
    content: {
      markupName: "simple_input",
      includeItiScript: true,
      displayCode: "src/examples/js/simple_init_plugin_display_code.js",
    },
    pageExtra: {
      iti_styles: "largeFlags",
    },
  }, {
    key: "angular_component",
    title: "Angular component",
    metaDesc: "How to use intl-tel-input with Angular.",
    js: {
      src: "src/examples/js/angular_component.ts",
      dest: "tmp/examples/js/angular_component.ts",
      script: "angular_component_bundle.js",
    },
    content: {
      markupName: "component",
      hideMarkupSection: true,
      displayCode: "src/examples/js/angular_component_display_code.js",
      script: "angular_component_bundle.js",
    },
  }, {
    key: "react_component",
    title: "React component",
    metaDesc: "How to use intl-tel-input with React.",
    js: {
      dest: "tmp/examples/js/react_component.js",
      script: "react_component_bundle.js",
    },
    content: {
      markupName: "component",
      hideMarkupSection: true,
      displayCode: "src/examples/js/react_component_display_code.js",
      script: "react_component_bundle.js",
    },
  }, {
    key: "vue_component",
    title: "Vue component",
    metaDesc: "How to use intl-tel-input with Vue.",
    js: {
      // need to specify the source because of the alternative .vue extension
      src: "src/examples/js/vue_component.vue",
      dest: "tmp/examples/js/vue_component.vue",
      script: "vue_component_bundle.js",
    },
    content: {
      markupName: "component",
      hideMarkupSection: true,
      displayCode: "src/examples/js/vue_component_display_code.vue",
      script: "vue_component_bundle.js",
    },
  }, {
    key: "svelte_component",
    title: "Svelte component",
    metaDesc: "How to use intl-tel-input with Svelte.",
    js: {
      // need to specify the source because of the alternative .svelte extension
      src: "src/examples/js/svelte_component.svelte",
      dest: "tmp/examples/js/svelte_component.svelte",
      script: "svelte_component_bundle.js",
    },
    content: {
      markupName: "component",
      hideMarkupSection: true,
      displayCode: "src/examples/js/svelte_component_display_code.svelte",
      script: "svelte_component_bundle.js",
    },
  }];

  exampleDefinitions.forEach((definition) => registerExample(definition));

  docsDefinitions.forEach(({ key, title, metaDesc }) => {
    const mdPath = path.join("src", "docs", "markdown", `${key}.md`);
    const urlSlug = key.replace(/_/g, "-");
    const destPath = `build/docs/${urlSlug}.html`;
    const canonicalUrl = `https://intl-tel-input.com/docs/${urlSlug}`;
    const fullTitle = `${title} docs - International Telephone Input`;

    config[`docs_content_${key}`] = {
      src: "src/docs/docs_content_template.html.ejs",
      dest: `tmp/docs/${key}_content.html`,
      options: {
        data: () => ({
          docKey: key,
          html: (() => {
            let source = grunt.file.read(mdPath);
            if (key === "localisation") {
              const languageList = createI18nLanguageListText(getI18nLanguages());
              source = source.replace("<!-- I18N_LANGUAGE_LIST -->", `\n${languageList}\n`);
            }
            return md.render(source, { docKey: key });
          })(),
        }),
      },
    };

    config[`docs_layout_${key}`] = {
      src: "src/layout_template.html.ejs",
      dest: `tmp/docs/${key}_layout.html`,
      options: {
        data: () => ({
          showLeftSidebar: true,
          layoutClass: "iti-layout-both-sidebars",
          nav: grunt.file.read("src/docs/docs_nav_template.html.ejs"),
          content: grunt.file.read(`tmp/docs/${key}_content.html`),
          name: key,
          pageType: "docs",
          docsDropdownPages,
          examplesDropdownPages,
          show_right_sidebar_ad: showRightSidebarAd,
        }),
      },
    };

    config[`docs_page_${key}`] = {
      src: "src/docs/docs_page_template.html.ejs",
      dest: destPath,
      options: {
        data: () => ({
          cacheBust,
          head_title: fullTitle,
          canonical_url: canonicalUrl,
          meta_desc: metaDesc,
          og_meta_tags: buildOpenGraphMetaTags({
            title: fullTitle,
            description: metaDesc,
            url: canonicalUrl,
          }),
          ...readCommonPagePartials(grunt, {
            cacheBust,
            isDevBuild,
            highlightjs_styles: true,
            iti_styles: "none",
          }),
          layout: grunt.file.read(`tmp/docs/${key}_layout.html`),
          common_body_end: readCommonBodyEndScript(grunt),
        }),
      },
    };
  });

  return config;
};


================================================
FILE: site/grunt/templateGruntHelpers.js
================================================
const makeTemplateTask = (src, dest, data) => ({
  src,
  dest,
  options: {
    data,
  },
});

const readCommonPagePartials = (grunt, data) => ({
  common_meta_tags: grunt.file.read("src/shared/common_meta_tags.html"),
  common_styles: grunt.template.process(grunt.file.read("src/shared/common_styles.html.ejs"), {
    data,
  }),
  common_head_end_prod: data && data.isDevBuild
    ? ""
    : grunt.file.read("src/shared/common_head_end_prod.html"),
});

const readCommonBodyEndScript = (grunt) => grunt.file.read("src/shared/common_body_end.html");

const readItiLiveResultsScript = (grunt, data) =>
  grunt.template.process(grunt.file.read("src/shared/iti_live_results_script.html.ejs"), {
    data,
  });

const readItiScript = (grunt) => grunt.file.read("tmp/shared/iti_script.html");

const makeLayoutTask = (
  grunt,
  {
    dest,
    showLeftSidebar,
    layoutClass,
    navPath,
    contentPath,
    name,
    pageType,
    docsDropdownPages,
    examplesDropdownPages,
    extra = {},
  }
) =>
  makeTemplateTask("src/layout_template.html.ejs", dest, () => ({
    showLeftSidebar,
    layoutClass,
    ...(navPath ? { nav: grunt.file.read(navPath) } : {}),
    content: grunt.file.read(contentPath),
    name,
    pageType,
    docsDropdownPages,
    examplesDropdownPages,
    ...extra,
  }));

module.exports = {
  makeTemplateTask,
  makeLayoutTask,
  readCommonPagePartials,
  readCommonBodyEndScript,
  readItiLiveResultsScript,
  readItiScript,
};


================================================
FILE: site/grunt/templateNav.js
================================================
const docsDropdownPages = [
  { name: "choose_integration", href: "/docs/choose-integration", label: "Choose integration" },
  { name: "getting_started", href: "/docs/getting-started", label: "Getting started" },
  { name: "options", href: "/docs/options", label: "Initialisation options" },
  { name: "localisation", href: "/docs/localisation", label: "Localisation" },
  { name: "accessibility", href: "/docs/accessibility", label: "Accessibility" },
  { name: "methods", href: "/docs/methods", label: "Methods" },
  { name: "events", href: "/docs/events", label: "Events" },
  { name: "utils", href: "/docs/utils", label: "Utilities script" },
  { name: "theming", href: "/docs/theming", label: "Theming / dark mode" },
  { name: "troubleshooting", href: "/docs/troubleshooting", label: "Troubleshooting" },
  { name: "faq", href: "/docs/faq", label: "FAQ" },
  { name: "react_component", href: "/docs/react-component", label: "React component" },
  { name: "vue_component", href: "/docs/vue-component", label: "Vue component" },
  { name: "angular_component", href: "/docs/angular-component", label: "Angular component" },
  { name: "svelte_component", href: "/docs/svelte-component", label: "Svelte component" },
];

const examplesDropdownPages = [
  { name: "validation_practical", href: "/examples/validation-practical", label: "Validation" },
  { name: "lookup_country", href: "/examples/lookup-country", label: "Lookup user's country" },
  { name: "single_country", href: "/examples/single-country", label: "Single country" },
  { name: "right_to_left", href: "/examples/right-to-left", label: "Right to left" },
  { name: "hidden_input", href: "/examples/hidden-input", label: "Hidden input" },
  { name: "display_number", href: "/examples/display-number", label: "Display existing number" },
  { name: "multiple_instances", href: "/examples/multiple-instances", label: "Multiple instances" },
  { name: "validation_precise", href: "/examples/validation-precise", label: "Precise validation (advanced)" },
  { name: "large_flags", href: "/examples/large-flags", label: "Large flags" },
  { name: "react_component", href: "/examples/react-component", label: "React component" },
  { name: "vue_component", href: "/examples/vue-component", label: "Vue component" },
  { name: "angular_component", href: "/examples/angular-component", label: "Angular component" },
  { name: "svelte_component", href: "/examples/svelte-component", label: "Svelte component" },
];

module.exports = {
  docsDropdownPages,
  examplesDropdownPages,
};


================================================
FILE: site/grunt/templateUtils.js
================================================
const path = require("path");
const fs = require("fs");
const crypto = require("crypto");
const MarkdownIt = require("markdown-it");
const markdownItAnchor = require("markdown-it-anchor");

const BUILD_DIR = "build";
const HASH_LENGTH = 12;

const hashCacheByPath = new Map();

const toPosixPath = (p) => String(p || "").replace(/\\/g, "/");

const defaultSlugifyHeading = (value) =>
  String(value)
    .trim()
    .toLowerCase()
    // remove apostrophes
    .replace(/['’]/g, "")
    // replace non-alphanumeric with hyphens
    .replace(/[^a-z0-9]+/g, "-")
    // collapse repeats
    .replace(/-+/g, "-")
    // trim hyphens
    .replace(/^-|-$/g, "");

// 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).
const addDocOptionsLayoutPlugin = (md) => {
  md.core.ruler.after("inline", "iti_doc_options_layout", (state) => {
    const env = state.env || {};
    const applyToDocKeys = ["options", "react_component", "vue_component", "angular_component", "svelte_component"];
    if (!applyToDocKeys.includes(env.docKey)) return;

    const tokens = state.tokens;
    const isHeadingOpen = (token, tag) => token && token.type === "heading_open" && token.tag === tag;
    const isAnyHeadingOpen = (token) => token && token.type === "heading_open" && /^h[1-6]$/.test(token.tag);

    const makeHtmlBlock = (content) => {
      const token = new state.Token("html_block", "", 0);
      token.content = content;
      return token;
    };

    const startRowAndKeyCellMarkup = () =>
      makeHtmlBlock(
        '<div class="iti-doc-options__row">\n' +
          '  <div class="iti-doc-options__cell iti-doc-options__cell--key">\n',
      );

    const endKeyCellAndStartValueCellMarkup = () =>
      makeHtmlBlock(
        "  </div>\n" +
          '  <div class="iti-doc-options__cell iti-doc-options__cell--value">\n',
      );

    const endValueCellAndRowMarkup = () => makeHtmlBlock("  </div>\n</div>\n");

    const nextTokens = [];
    let i = 0;

    const consumeThroughHeadingClose = (tagName) => {
      while (i < tokens.length) {
        const t = tokens[i];
        nextTokens.push(t);
        i += 1;
        if (t.type === "heading_close" && t.tag === tagName) return;
      }
    };

    const findHeadingCloseIndex = (startIndex, tagName) => {
      for (let k = startIndex; k < tokens.length; k += 1) {
        const t = tokens[k];
        if (t.type === "heading_close" && t.tag === tagName) return k;
      }
      return -1;
    };

    // ad blocks are divs with class="article-ad", but MarkdownIt treats divs and their contents as a single raw "html_block" token
    const isAdBlockOpen = (token) => token.type === "html_block" && token.content.includes('class="article-ad"');

    const consumeUntilNextHeadingOrAdBlock = () => {
      while (i < tokens.length) {
        const t = tokens[i];
        if (isAnyHeadingOpen(t) || isAdBlockOpen(t)) return;
        nextTokens.push(t);
        i += 1;
      }
    };

    const consumeOptionBlock = () => {
      nextTokens.push(startRowAndKeyCellMarkup());

      // H6 heading (option name) lives in the key cell.
      nextTokens.push(tokens[i]);
      i += 1;
      consumeThroughHeadingClose("h6");

      // The Type/Default paragraph also lives in the key cell.
      // Consume paragraph_open, inline, paragraph_close.
      while (i < tokens.length && tokens[i].type !== "paragraph_close") {
        nextTokens.push(tokens[i]);
        i += 1;
      }
      if (i < tokens.length && tokens[i].type === "paragraph_close") {
        nextTokens.push(tokens[i]);
        i += 1;
      }

      // Switch to the value cell for the rest of the option content.
      nextTokens.push(endKeyCellAndStartValueCellMarkup());

      consumeUntilNextHeadingOrAdBlock();
      nextTokens.push(endValueCellAndRowMarkup());
    };

    while (i < tokens.length) {
      const token = tokens[i];
      if (isHeadingOpen(token, "h6")) {
        // Only treat this heading as an "option" row when the very next block
        // after the heading close is a paragraph containing the Type/Default meta info
        const closeIndex = findHeadingCloseIndex(i, "h6");
        const afterClose = closeIndex >= 0 ? closeIndex + 1 : -1;
        if (afterClose >= 0 && tokens[afterClose] && tokens[afterClose].type === "paragraph_open") {
          const inlineToken = tokens[afterClose + 1];
          const inlineContent = inlineToken && inlineToken.type === "inline" ? inlineToken.content : "";
          if (inlineContent.includes("Type:") && inlineContent.includes("Default:")) {
            consumeOptionBlock();
            continue;
          }
        }

        // Not an options entry: emit the heading tokens unchanged.
        nextTokens.push(token);
        i += 1;
        continue;
      }

      nextTokens.push(token);
      i += 1;
    }

    state.toke
Download .txt
gitextract_v3v4kr9z/

├── .eslintignore
├── .eslintrc.js
├── .github/
│   ├── CONTRIBUTING.md
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── 1_bug_report.yml
│   │   ├── 2_feature_request.yml
│   │   └── config.yml
│   ├── copilot-instructions.md
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── .gitmodules
├── .node-version
├── .npmrc
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── CHANGELOG.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── angular/
│   ├── README.md
│   ├── build.js
│   ├── demo/
│   │   ├── form/
│   │   │   ├── form.component.ts
│   │   │   ├── index.html
│   │   │   └── main.ts
│   │   ├── set-number/
│   │   │   ├── index.html
│   │   │   ├── main.ts
│   │   │   └── set-number.component.ts
│   │   ├── simple/
│   │   │   ├── index.html
│   │   │   ├── main.ts
│   │   │   └── simple.component.ts
│   │   ├── toggle-disabled/
│   │   │   ├── index.html
│   │   │   ├── main.ts
│   │   │   └── toggle-disabled.component.ts
│   │   └── validation/
│   │       ├── index.html
│   │       ├── main.ts
│   │       └── validation.component.ts
│   ├── src/
│   │   └── intl-tel-input/
│   │       ├── angular.ts
│   │       └── angularWithUtils.ts
│   └── tsconfig.json
├── build.js
├── composer.json
├── cspell.json
├── demo.html
├── functions/
│   └── _middleware.js
├── grunt/
│   ├── bump.js
│   ├── clean.js
│   ├── closure-compiler.js
│   ├── connect.js
│   ├── cssmin.js
│   ├── generate-sprite.js
│   ├── replace.js
│   ├── sass.js
│   ├── shell.js
│   ├── translations.js
│   └── watch.js
├── index.js
├── jest.config.js
├── package.json
├── playwright.config.ts
├── react/
│   ├── README.md
│   ├── build.js
│   ├── demo/
│   │   ├── set-number/
│   │   │   ├── SetNumberApp.tsx
│   │   │   └── set-number.html
│   │   ├── simple/
│   │   │   ├── SimpleApp.tsx
│   │   │   └── simple.html
│   │   ├── toggle-disabled/
│   │   │   ├── ToggleDisabledApp.tsx
│   │   │   └── toggle-disabled.html
│   │   └── validation/
│   │       ├── ValidationApp.tsx
│   │       └── validation.html
│   ├── src/
│   │   └── intl-tel-input/
│   │       ├── react.tsx
│   │       └── reactWithUtils.tsx
│   └── tsconfig.json
├── scripts/
│   ├── check-lpn-metadata.cjs
│   └── playwright-linux-docker.sh
├── site/
│   ├── .gitignore
│   ├── Gruntfile.js
│   ├── README.md
│   ├── esbuild/
│   │   ├── build.mjs
│   │   └── externalUtilsPlugin.mjs
│   ├── grunt/
│   │   ├── copy.js
│   │   ├── cssmin.js
│   │   ├── fetchStats.js
│   │   ├── replace.js
│   │   ├── sass.js
│   │   ├── shell.js
│   │   ├── template.js
│   │   ├── templateGruntHelpers.js
│   │   ├── templateNav.js
│   │   ├── templateUtils.js
│   │   └── watch.js
│   ├── package.json
│   ├── src/
│   │   ├── 404/
│   │   │   ├── 404_content.html
│   │   │   └── 404_page_template.html.ejs
│   │   ├── css/
│   │   │   ├── _base.scss
│   │   │   ├── _forms.scss
│   │   │   ├── _layout.scss
│   │   │   ├── _navbar.scss
│   │   │   ├── _variables.scss
│   │   │   ├── docs.scss
│   │   │   ├── highlightjs_overrides.scss
│   │   │   ├── homepage.scss
│   │   │   ├── large_flags_overrides.scss
│   │   │   ├── playground.scss
│   │   │   └── website.scss
│   │   ├── docs/
│   │   │   ├── docs_content_template.html.ejs
│   │   │   ├── docs_nav_template.html.ejs
│   │   │   ├── docs_page_template.html.ejs
│   │   │   └── markdown/
│   │   │       ├── accessibility.md
│   │   │       ├── angular_component.md
│   │   │       ├── choose_integration.md
│   │   │       ├── events.md
│   │   │       ├── faq.md
│   │   │       ├── getting_started.md
│   │   │       ├── localisation.md
│   │   │       ├── methods.md
│   │   │       ├── options.md
│   │   │       ├── react_component.md
│   │   │       ├── svelte_component.md
│   │   │       ├── theming.md
│   │   │       ├── troubleshooting.md
│   │   │       ├── utils.md
│   │   │       └── vue_component.md
│   │   ├── examples/
│   │   │   ├── copy/
│   │   │   │   ├── angular_component_desc.html
│   │   │   │   ├── display_number_desc.html
│   │   │   │   ├── hidden_input_desc.html
│   │   │   │   ├── large_flags_desc.html
│   │   │   │   ├── lookup_country_desc.html
│   │   │   │   ├── multiple_instances_desc.html
│   │   │   │   ├── react_component_desc.html
│   │   │   │   ├── right_to_left_desc.html
│   │   │   │   ├── single_country_desc.html
│   │   │   │   ├── svelte_component_desc.html
│   │   │   │   ├── validation_practical_desc.html
│   │   │   │   ├── validation_precise_desc.html
│   │   │   │   └── vue_component_desc.html
│   │   │   ├── css/
│   │   │   │   ├── multiple_instances.css
│   │   │   │   └── validation.css
│   │   │   ├── examples_content_template.html.ejs
│   │   │   ├── examples_nav_template.html.ejs
│   │   │   ├── examples_page_template.html.ejs
│   │   │   ├── html/
│   │   │   │   ├── component.html
│   │   │   │   ├── display_number.html
│   │   │   │   ├── display_number_display_code.html
│   │   │   │   ├── multiple_instances.html
│   │   │   │   ├── multiple_instances_display_code.html
│   │   │   │   ├── simple_input.html
│   │   │   │   ├── simple_input_display_code.html
│   │   │   │   ├── validation.html
│   │   │   │   └── validation_display_code.html
│   │   │   └── js/
│   │   │       ├── angular_component.ts
│   │   │       ├── angular_component_display_code.js
│   │   │       ├── hidden_input.js
│   │   │       ├── hidden_input_display_code.js
│   │   │       ├── lookup_country.js
│   │   │       ├── lookup_country_display_code.js
│   │   │       ├── multiple_instances.js
│   │   │       ├── multiple_instances_display_code.js
│   │   │       ├── react_component.js
│   │   │       ├── react_component_display_code.js
│   │   │       ├── right_to_left.js
│   │   │       ├── right_to_left_display_code.js
│   │   │       ├── simple_init_plugin.js
│   │   │       ├── simple_init_plugin_display_code.js
│   │   │       ├── single_country.js
│   │   │       ├── single_country_display_code.js
│   │   │       ├── svelte_component.svelte
│   │   │       ├── svelte_component_display_code.svelte
│   │   │       ├── svelte_main.js
│   │   │       ├── validation.js
│   │   │       ├── validation_display_code.js
│   │   │       ├── viteSvelteDemo.config.mjs
│   │   │       ├── viteVueDemo.config.js
│   │   │       ├── vue_component.vue
│   │   │       ├── vue_component_display_code.vue
│   │   │       └── vue_main.js
│   │   ├── homepage/
│   │   │   ├── homepage_content.html
│   │   │   └── homepage_page_template.html.ejs
│   │   ├── js/
│   │   │   ├── homepage.js
│   │   │   └── iti-live-results.js
│   │   ├── layout_template.html.ejs
│   │   ├── playground/
│   │   │   ├── js/
│   │   │   │   ├── modules/
│   │   │   │   │   ├── clipboard.js
│   │   │   │   │   ├── forms.js
│   │   │   │   │   ├── i18n.js
│   │   │   │   │   ├── initCode.js
│   │   │   │   │   ├── itiController.js
│   │   │   │   │   ├── playgroundConfig.js
│   │   │   │   │   ├── stateUtils.js
│   │   │   │   │   └── urlState.js
│   │   │   │   ├── playground.js
│   │   │   │   └── templates/
│   │   │   │       └── playgroundConstants.js.ejs
│   │   │   ├── playground_content.html
│   │   │   └── playground_page_template.html.ejs
│   │   └── shared/
│   │       ├── common_body_end.html
│   │       ├── common_head_end_prod.html
│   │       ├── common_meta_tags.html
│   │       ├── common_styles.html.ejs
│   │       ├── iti_live_results_script.html.ejs
│   │       └── iti_script.html.ejs
│   └── static/
│       ├── _redirects
│       ├── ads.txt
│       ├── css/
│       │   └── intlTelInput-largeFlags.css
│       └── screenshotting.html
├── src/
│   ├── css/
│   │   ├── _metadata.scss
│   │   ├── demo.scss
│   │   ├── intlTelInput.scss
│   │   └── intlTelInputWithAssets.scss
│   └── js/
│       ├── intl-tel-input/
│       │   ├── data.ts
│       │   ├── i18n/
│       │   │   ├── ar/
│       │   │   │   └── index.ts
│       │   │   ├── bg/
│       │   │   │   └── index.ts
│       │   │   ├── bn/
│       │   │   │   └── index.ts
│       │   │   ├── bs/
│       │   │   │   └── index.ts
│       │   │   ├── ca/
│       │   │   │   └── index.ts
│       │   │   ├── cs/
│       │   │   │   └── index.ts
│       │   │   ├── da/
│       │   │   │   └── index.ts
│       │   │   ├── de/
│       │   │   │   └── index.ts
│       │   │   ├── el/
│       │   │   │   └── index.ts
│       │   │   ├── en/
│       │   │   │   └── index.ts
│       │   │   ├── es/
│       │   │   │   └── index.ts
│       │   │   ├── et/
│       │   │   │   └── index.ts
│       │   │   ├── fa/
│       │   │   │   └── index.ts
│       │   │   ├── fi/
│       │   │   │   └── index.ts
│       │   │   ├── fr/
│       │   │   │   └── index.ts
│       │   │   ├── hi/
│       │   │   │   └── index.ts
│       │   │   ├── hr/
│       │   │   │   └── index.ts
│       │   │   ├── hu/
│       │   │   │   └── index.ts
│       │   │   ├── id/
│       │   │   │   └── index.ts
│       │   │   ├── index.ts
│       │   │   ├── it/
│       │   │   │   └── index.ts
│       │   │   ├── ja/
│       │   │   │   └── index.ts
│       │   │   ├── kn/
│       │   │   │   └── index.ts
│       │   │   ├── ko/
│       │   │   │   └── index.ts
│       │   │   ├── lt/
│       │   │   │   └── index.ts
│       │   │   ├── mr/
│       │   │   │   └── index.ts
│       │   │   ├── nl/
│       │   │   │   └── index.ts
│       │   │   ├── no/
│       │   │   │   └── index.ts
│       │   │   ├── pl/
│       │   │   │   └── index.ts
│       │   │   ├── pt/
│       │   │   │   └── index.ts
│       │   │   ├── ro/
│       │   │   │   └── index.ts
│       │   │   ├── ru/
│       │   │   │   └── index.ts
│       │   │   ├── sk/
│       │   │   │   └── index.ts
│       │   │   ├── sl/
│       │   │   │   └── index.ts
│       │   │   ├── sq/
│       │   │   │   └── index.ts
│       │   │   ├── sr/
│       │   │   │   └── index.ts
│       │   │   ├── sv/
│       │   │   │   └── index.ts
│       │   │   ├── te/
│       │   │   │   └── index.ts
│       │   │   ├── th/
│       │   │   │   └── index.ts
│       │   │   ├── tr/
│       │   │   │   └── index.ts
│       │   │   ├── types.ts
│       │   │   ├── uk/
│       │   │   │   └── index.ts
│       │   │   ├── ur/
│       │   │   │   └── index.ts
│       │   │   ├── uz/
│       │   │   │   └── index.ts
│       │   │   ├── vi/
│       │   │   │   └── index.ts
│       │   │   ├── zh/
│       │   │   │   └── index.ts
│       │   │   └── zh-hk/
│       │   │       └── index.ts
│       │   └── intlTelInputWithUtils.ts
│       ├── intl-tel-input.ts
│       ├── modules/
│       │   ├── constants.ts
│       │   ├── core/
│       │   │   ├── countrySearch.ts
│       │   │   ├── icons.ts
│       │   │   ├── numerals.ts
│       │   │   ├── options.ts
│       │   │   └── ui.ts
│       │   ├── data/
│       │   │   ├── country-data.ts
│       │   │   ├── intl-regionless.ts
│       │   │   └── nanp-regionless.ts
│       │   ├── format/
│       │   │   ├── caret.ts
│       │   │   └── formatting.ts
│       │   ├── types/
│       │   │   ├── events.ts
│       │   │   ├── forEachInstanceArgsMap.ts
│       │   │   └── public-api.ts
│       │   └── utils/
│       │       ├── dom.ts
│       │       ├── isAndroid.ts
│       │       └── string.ts
│       └── utils.js
├── svelte/
│   ├── README.md
│   ├── demo/
│   │   ├── set-number/
│   │   │   ├── App.svelte
│   │   │   ├── index.html
│   │   │   ├── main.js
│   │   │   └── vite.config.mjs
│   │   ├── simple/
│   │   │   ├── App.svelte
│   │   │   ├── index.html
│   │   │   ├── main.js
│   │   │   └── vite.config.mjs
│   │   ├── toggle-disabled/
│   │   │   ├── App.svelte
│   │   │   ├── index.html
│   │   │   ├── main.js
│   │   │   └── vite.config.mjs
│   │   └── validation/
│   │       ├── App.svelte
│   │       ├── index.html
│   │       ├── main.js
│   │       └── vite.config.mjs
│   ├── src/
│   │   └── intl-tel-input/
│   │       ├── IntlTelInput.svelte
│   │       └── IntlTelInputWithUtils.svelte
│   ├── viteConfig.mjs
│   └── viteConfigWithUtils.mjs
├── tests/
│   ├── integration/
│   │   ├── core/
│   │   │   ├── dropdownShortcuts.test.js
│   │   │   ├── easternNumerals.test.js
│   │   │   ├── initialValues.test.js
│   │   │   ├── multipleInstances.test.js
│   │   │   ├── regionless.test.js
│   │   │   ├── usingDropdown.test.js
│   │   │   └── usingInput.test.js
│   │   ├── events/
│   │   │   ├── closeCountryDropdownEvent.test.js
│   │   │   ├── countryChangeEvent.test.js
│   │   │   └── openCountryDropdownEvent.test.js
│   │   ├── helpers/
│   │   │   ├── helpers.js
│   │   │   └── matchers.js
│   │   ├── methods/
│   │   │   ├── destroy.test.js
│   │   │   ├── getExtension.test.js
│   │   │   ├── getInstance.test.js
│   │   │   ├── getNumber.test.js
│   │   │   ├── getNumberType.test.js
│   │   │   ├── getSelectedCountryData.test.js
│   │   │   ├── getValidationError.test.js
│   │   │   ├── isValidNumber.test.js
│   │   │   ├── isValidNumberPrecise.test.js
│   │   │   ├── setCountry.test.js
│   │   │   ├── setDisabled.test.js
│   │   │   ├── setNumber.test.js
│   │   │   └── setPlaceholderNumberType.test.js
│   │   ├── options/
│   │   │   ├── allowDropdown.test.js
│   │   │   ├── allowNumberExtensions.test.js
│   │   │   ├── allowPhonewords.test.js
│   │   │   ├── allowedNumberTypes.test.js
│   │   │   ├── autoPlaceholder.test.js
│   │   │   ├── containerClass.test.js
│   │   │   ├── countryNameLocale.test.js
│   │   │   ├── countryOrder.test.js
│   │   │   ├── countrySearch.test.js
│   │   │   ├── customPlaceholder.test.js
│   │   │   ├── dropdownContainer.test.js
│   │   │   ├── excludeCountries.test.js
│   │   │   ├── fixDropdownWidth.test.js
│   │   │   ├── formatAsYouType.test.js
│   │   │   ├── formatOnDisplay.test.js
│   │   │   ├── geoIpLookup.test.js
│   │   │   ├── hiddenInput.test.js
│   │   │   ├── i18n-locales.test.js
│   │   │   ├── i18n.test.js
│   │   │   ├── initialCountry.test.js
│   │   │   ├── loadUtils.test.js
│   │   │   ├── nationalMode.test.js
│   │   │   ├── onlyCountries.test.js
│   │   │   ├── placeholderNumberType.test.js
│   │   │   ├── separateDialCode.test.js
│   │   │   ├── showFlags.test.js
│   │   │   ├── strictMode.test.js
│   │   │   └── useFullscreenPopup.test.js
│   │   └── static/
│   │       ├── attachUtils.test.js
│   │       ├── defaults.test.js
│   │       └── getCountryData.test.js
│   └── unit/
│       ├── core/
│       │   ├── countrySearch.test.js
│       │   └── options.test.js
│       ├── data/
│       │   ├── country-data.test.js
│       │   └── nanp-regionless.test.js
│       ├── format/
│       │   ├── caret.test.js
│       │   └── formatting.test.js
│       ├── intl-tel-input/
│       │   └── constructor.test.js
│       └── utils/
│           ├── dom.test.js
│           └── string.test.js
├── tests-e2e/
│   ├── angular.spec.ts
│   ├── fixtures/
│   │   └── vanilla.html
│   ├── react.spec.ts
│   ├── simple.spec.ts
│   ├── svelte.spec.ts
│   ├── visual.spec.ts
│   └── vue.spec.ts
├── tsconfig.json
└── vue/
    ├── README.md
    ├── demo/
    │   ├── set-number/
    │   │   ├── App.vue
    │   │   ├── index.html
    │   │   ├── main.js
    │   │   └── vite.config.js
    │   ├── simple/
    │   │   ├── App.vue
    │   │   ├── index.html
    │   │   ├── main.js
    │   │   └── vite.config.js
    │   ├── toggle-disabled/
    │   │   ├── App.vue
    │   │   ├── index.html
    │   │   ├── main.js
    │   │   └── vite.config.js
    │   └── validation/
    │       ├── App.vue
    │       ├── index.html
    │       ├── main.js
    │       └── vite.config.js
    ├── src/
    │   ├── IntlTelInput.vue
    │   ├── IntlTelInputWithUtils.vue
    │   ├── env.d.ts
    │   └── exports/
    │       ├── IntlTelInput.ts
    │       └── IntlTelInputWithUtils.ts
    ├── tsconfig.app.json
    ├── tsconfig.json
    ├── tsconfig.node.json
    └── vite.config.mts
Download .txt
SYMBOL INDEX (360 symbols across 85 files)

FILE: angular/build.js
  function buildMain (line 13) | async function buildMain() {

FILE: angular/demo/form/form.component.ts
  class AppComponent (line 39) | class AppComponent implements OnInit {
    method phone (line 48) | get phone() {
    method ngOnInit (line 52) | ngOnInit(): void {
    method handleSubmit (line 58) | handleSubmit(): void {

FILE: angular/demo/set-number/set-number.component.ts
  class AppComponent (line 31) | class AppComponent {
    method handleNumberChange (line 39) | handleNumberChange(value: string): void {
    method handleValidityChange (line 43) | handleValidityChange(value: boolean): void {
    method handleErrorCodeChange (line 47) | handleErrorCodeChange(value: number | null): void {
    method handleSetNumber (line 51) | handleSetNumber(): void {
    method handleSubmit (line 58) | handleSubmit(): void {

FILE: angular/demo/simple/simple.component.ts
  class AppComponent (line 16) | class AppComponent {}

FILE: angular/demo/toggle-disabled/toggle-disabled.component.ts
  class AppComponent (line 17) | class AppComponent {
    method toggleDisabled (line 20) | toggleDisabled(): void {

FILE: angular/demo/validation/validation.component.ts
  class AppComponent (line 27) | class AppComponent {
    method handleNumberChange (line 33) | handleNumberChange(value: string): void {
    method handleValidityChange (line 37) | handleValidityChange(value: boolean): void {
    method handleErrorCodeChange (line 41) | handleErrorCodeChange(value: number | null): void {
    method handleSubmit (line 45) | handleSubmit(): void {

FILE: angular/src/intl-tel-input/angular.ts
  constant PHONE_ERROR_MESSAGES (line 29) | const PHONE_ERROR_MESSAGES: string[] = [
  class IntlTelInputComponent (line 66) | class IntlTelInputComponent
    method ngAfterViewInit (line 110) | ngAfterViewInit() {
    method ngOnChanges (line 131) | ngOnChanges(changes: SimpleChanges) {
    method handleInput (line 141) | handleInput() {
    method handleBlur (line 184) | handleBlur(event: FocusEvent) {
    method handleFocus (line 189) | handleFocus(event: FocusEvent) {
    method handleKeyDown (line 193) | handleKeyDown(event: KeyboardEvent) {
    method handleKeyUp (line 197) | handleKeyUp(event: KeyboardEvent) {
    method handlePaste (line 201) | handlePaste(event: ClipboardEvent) {
    method handleClick (line 205) | handleClick(event: MouseEvent) {
    method getInstance (line 213) | getInstance(): Iti | null {
    method getInput (line 221) | getInput(): HTMLInputElement | null {
    method ngOnDestroy (line 225) | ngOnDestroy() {
    method applyInputProps (line 234) | private applyInputProps(): void {
    method writeValue (line 250) | writeValue(value: string | null): void {
    method registerOnChange (line 256) | registerOnChange(fn: any): void {
    method registerOnTouched (line 260) | registerOnTouched(fn: any): void {
    method setDisabledState (line 264) | setDisabledState(isDisabled: boolean): void {
    method validate (line 271) | validate(control: AbstractControl): ValidationErrors | null {
    method registerOnValidatorChange (line 293) | registerOnValidatorChange(fn: () => void): void {

FILE: angular/src/intl-tel-input/angularWithUtils.ts
  constant PHONE_ERROR_MESSAGES (line 30) | const PHONE_ERROR_MESSAGES: string[] = [
  class IntlTelInputComponent (line 67) | class IntlTelInputComponent
    method ngAfterViewInit (line 111) | ngAfterViewInit() {
    method ngOnChanges (line 132) | ngOnChanges(changes: SimpleChanges) {
    method handleInput (line 142) | handleInput() {
    method handleBlur (line 185) | handleBlur(event: FocusEvent) {
    method handleFocus (line 190) | handleFocus(event: FocusEvent) {
    method handleKeyDown (line 194) | handleKeyDown(event: KeyboardEvent) {
    method handleKeyUp (line 198) | handleKeyUp(event: KeyboardEvent) {
    method handlePaste (line 202) | handlePaste(event: ClipboardEvent) {
    method handleClick (line 206) | handleClick(event: MouseEvent) {
    method getInstance (line 214) | getInstance(): Iti | null {
    method getInput (line 222) | getInput(): HTMLInputElement | null {
    method ngOnDestroy (line 226) | ngOnDestroy() {
    method applyInputProps (line 235) | private applyInputProps(): void {
    method writeValue (line 251) | writeValue(value: string | null): void {
    method registerOnChange (line 257) | registerOnChange(fn: any): void {
    method registerOnTouched (line 261) | registerOnTouched(fn: any): void {
    method setDisabledState (line 265) | setDisabledState(isDisabled: boolean): void {
    method validate (line 272) | validate(control: AbstractControl): ValidationErrors | null {
    method registerOnValidatorChange (line 294) | registerOnValidatorChange(fn: () => void): void {

FILE: functions/_middleware.js
  constant EUROPE (line 5) | const EUROPE = new Set([
  class GeoInjector (line 15) | class GeoInjector {
    method constructor (line 16) | constructor(isEurope) {
    method element (line 20) | element(element) {
  function onRequest (line 28) | async function onRequest(context) {

FILE: react/src/intl-tel-input/react.tsx
  type InputProps (line 16) | type InputProps = Omit<React.ComponentPropsWithoutRef<"input">, "onInput">;
  type ItiProps (line 18) | type ItiProps = {
  type IntlTelInputRef (line 30) | type IntlTelInputRef = {

FILE: react/src/intl-tel-input/reactWithUtils.tsx
  type InputProps (line 17) | type InputProps = Omit<React.ComponentPropsWithoutRef<"input">, "onInput">;
  type ItiProps (line 19) | type ItiProps = {
  type IntlTelInputRef (line 31) | type IntlTelInputRef = {

FILE: scripts/check-lpn-metadata.cjs
  constant REPO_ROOT (line 14) | const REPO_ROOT = path.resolve(__dirname, "..", "");
  constant XML_PATH (line 15) | const XML_PATH = path.resolve(
  constant OUT_TS (line 19) | const OUT_TS = path.resolve(REPO_ROOT, "tmp/generated-rawCountryData.ts");
  constant CURATED_DATA_TS_PATH (line 20) | const CURATED_DATA_TS_PATH = path.resolve(REPO_ROOT, "src/js/intl-tel-in...
  function sortDigitStringsNumeric (line 28) | function sortDigitStringsNumeric(arr) {
  function splitTopLevelAlts (line 34) | function splitTopLevelAlts(inner) {
  function expandCharClass (line 49) | function expandCharClass(cls) {
  function expandSimple (line 66) | function expandSimple(prefixPart) {
  function simplePrefixesFromPattern (line 78) | function simplePrefixesFromPattern(pat) {
  function collapseExhaustiveRanges (line 167) | function collapseExhaustiveRanges(codes) {
  function extractNanpNpasFromPattern (line 207) | function extractNanpNpasFromPattern(pat) {
  function deriveNanpNpasFromPatterns (line 242) | function deriveNanpNpasFromPatterns(fixedList, mobileList) {
  function extractGenericPrefixesXml (line 266) | function extractGenericPrefixesXml(src) {
  function extendPrefixXml (line 322) | function extendPrefixXml(iso2, base, minLen, xmlSources) {
  function main (line 355) | function main() {

FILE: site/esbuild/externalUtilsPlugin.mjs
  method setup (line 3) | setup({ onResolve }) {

FILE: site/grunt/fetchStats.js
  function httpsGet (line 5) | function httpsGet(url) {
  function formatNumber (line 21) | function formatNumber(n) {
  function roundToDisplay (line 33) | function roundToDisplay(n) {
  function fetchStats (line 46) | async function fetchStats() {

FILE: site/grunt/templateUtils.js
  constant BUILD_DIR (line 7) | const BUILD_DIR = "build";
  constant HASH_LENGTH (line 8) | const HASH_LENGTH = 12;

FILE: site/src/examples/js/angular_component.ts
  class AppComponent (line 47) | class AppComponent {
    method inputValidityClass (line 67) | get inputValidityClass(): string {
    method invalidMsg (line 72) | get invalidMsg(): string | null {
    method validMsg (line 79) | get validMsg(): string | null {
    method inputProps (line 85) | get inputProps(): Record<string, unknown> {
    method enableValidation (line 94) | enableValidation(): void {
    method handleNumberChange (line 98) | handleNumberChange(newNumber: string): void {
    method handleSubmit (line 103) | handleSubmit(): void {

FILE: site/src/examples/js/angular_component_display_code.js
  class AppComponent (line 23) | class AppComponent {
    method phone (line 36) | get phone() {
    method noticeText (line 40) | get noticeText() {
    method handleSubmit (line 44) | handleSubmit() {

FILE: site/src/playground/js/modules/clipboard.js
  function copyTextToClipboard (line 1) | function copyTextToClipboard(text) {
  function bindCopyCodeButton (line 11) | function bindCopyCodeButton(buttonEl, codeEl) {

FILE: site/src/playground/js/modules/forms.js
  function initTooltips (line 15) | function initTooltips(container) {
  function cloneInfoIconSvg (line 26) | function cloneInfoIconSvg(infoIconTemplate) {
  function createInfoIcon (line 32) | function createInfoIcon(meta, infoIconTemplate) {
  function buildLabelGroup (line 50) | function buildLabelGroup(meta, { labelText, htmlFor, labelClassName, inf...
  function buildBooleanExampleControl (line 67) | function buildBooleanExampleControl(key, meta, { idPrefix, dataAttr, exa...
  function formatMultiDropdownSelection (line 111) | function formatMultiDropdownSelection(values) {
  function buildControlRow (line 118) | function buildControlRow(key, meta, { idPrefix, dataAttr, infoIconTempla...
  function createControlGroup (line 316) | function createControlGroup(optionGroupTemplate, title, groupId, descrip...
  function renderControls (line 366) | function renderControls(formEl, metaMap, { idPrefix, dataAttr, groups = ...
  function getStateFromForm (line 422) | function getStateFromForm(formEl, defaults, metaMap, dataAttr) {
  function setFormFromState (line 470) | function setFormFromState(formEl, state, metaMap, dataAttr, { defaultSta...

FILE: site/src/playground/js/modules/i18n.js
  function toBcp47LanguageTag (line 10) | function toBcp47LanguageTag(code) {
  function getLanguageLabel (line 24) | function getLanguageLabel(code) {
  function createI18nOptionLabels (line 33) | function createI18nOptionLabels(languageCodes) {
  function resolveI18nSelection (line 43) | async function resolveI18nSelection(value, { i18nDirHash }) {

FILE: site/src/playground/js/modules/initCode.js
  function renderInitCodeFromState (line 4) | function renderInitCodeFromState(state, initCodeEl, { defaultInitOptions...

FILE: site/src/playground/js/modules/itiController.js
  function applyInputAttributes (line 3) | function applyInputAttributes(state, telInput) {
  function destroyInstance (line 27) | function destroyInstance(iti) {
  function detachUtilsIfNeeded (line 37) | function detachUtilsIfNeeded(state) {
  function toInitOptions (line 46) | function toInitOptions(state, { defaultInitOptions, specialOptionKeys, u...
  class ItiPlaygroundController (line 86) | class ItiPlaygroundController {
    method constructor (line 87) | constructor({ telInput, utilsPath, i18nDirHash, defaultInitOptions, sp...
    method destroy (line 98) | destroy() {
    method initWithState (line 103) | async initWithState(state) {

FILE: site/src/playground/js/modules/playgroundConfig.js
  constant AUTO_PLACEHOLDER_OPTIONS (line 1) | const AUTO_PLACEHOLDER_OPTIONS = ["polite", "aggressive", "off"];
  constant NUMBER_TYPES (line 2) | const NUMBER_TYPES = [
  constant SPECIAL_PLAYGROUND_OPTION_KEYS (line 16) | const SPECIAL_PLAYGROUND_OPTION_KEYS = [
  constant OPTION_GROUPS (line 25) | const OPTION_GROUPS = [
  function createPlaygroundConfig (line 74) | function createPlaygroundConfig({ defaults, i18nLanguageCodes, i18nOptio...

FILE: site/src/playground/js/modules/stateUtils.js
  function deepClone (line 1) | function deepClone(value) {
  function parseBooleanParam (line 5) | function parseBooleanParam(value, fallback) {
  function parseJsonParam (line 28) | function parseJsonParam(value, fallback) {
  function parseQueryOverrides (line 41) | function parseQueryOverrides(defaults, metaMap, { aliases = {} } = {}) {
  function safeStringify (line 69) | function safeStringify(value) {
  function formatJsValue (line 77) | function formatJsValue(value) {
  function encodeJsonParam (line 89) | function encodeJsonParam(value) {
  function deepEqual (line 98) | function deepEqual(a, b) {
  function normalizeStringArrayAsSet (line 126) | function normalizeStringArrayAsSet(value) {
  function createDefaultState (line 134) | function createDefaultState(defaultInitOptions, defaultInputAttributes) {
  function isDefaultForKey (line 138) | function isDefaultForKey(key, meta, value, defaultState) {

FILE: site/src/playground/js/modules/urlState.js
  function buildShareUrlFromState (line 3) | function buildShareUrlFromState(
  function updateUrlFromState (line 38) | function updateUrlFromState(state, deps) {

FILE: site/src/playground/js/playground.js
  constant KEEP_DROPDOWN_OPEN_PARAM (line 36) | const KEEP_DROPDOWN_OPEN_PARAM = "keepDropdownOpen";
  function flashActionButtonLabel (line 47) | function flashActionButtonLabel(buttonEl, temporaryLabel) {
  function shouldDisableKeepDropdownOpen (line 62) | function shouldDisableKeepDropdownOpen(state) {
  function syncKeepDropdownOpenAvailability (line 72) | function syncKeepDropdownOpenAvailability(state) {
  function getFullState (line 82) | function getFullState(state) {
  function getKeepDropdownOpenFromUrl (line 91) | function getKeepDropdownOpenFromUrl() {
  function updateKeepDropdownOpenUrlParam (line 96) | function updateKeepDropdownOpenUrlParam(enabled) {
  function initItiWithState (line 187) | function initItiWithState(state) {
  function getCombinedStateFromControls (line 222) | function getCombinedStateFromControls() {
  constant HINT_CONFIGS (line 252) | const HINT_CONFIGS = [
  function findControl (line 415) | function findControl(optionKey, isMultidropdown) {
  function maybeShowHint (line 422) | function maybeShowHint(config) {
  function scheduleReinit (line 455) | function scheduleReinit() {
  function maybeQueueHintCheck (line 484) | function maybeQueueHintCheck(event) {
  function resetOptionGroupToDefaults (line 516) | function resetOptionGroupToDefaults(groupKeys) {
  function resetAllToDefaults (line 558) | function resetAllToDefaults() {
  function getSupportedCountries (line 612) | function getSupportedCountries() {
  function renderSupportedCountriesTable (line 620) | function renderSupportedCountriesTable() {
  constant ISO2_TEXTAREA_KEYS (line 657) | const ISO2_TEXTAREA_KEYS = ["countryOrder", "excludeCountries", "onlyCou...
  function getValidIso2Set (line 659) | function getValidIso2Set() {
  function isValidIso2Array (line 663) | function isValidIso2Array(value, validIso2s) {
  function validateIso2Textarea (line 676) | function validateIso2Textarea(textarea, { invalidOnly = false } = {}) {
  function validateInitialCountryInput (line 697) | function validateInitialCountryInput(input, { invalidOnly = false } = {}) {
  function isValidLocale (line 715) | function isValidLocale(value) {
  function validateCountryNameLocaleInput (line 724) | function validateCountryNameLocaleInput(input, { invalidOnly = false } =...

FILE: src/js/intl-tel-input.ts
  type HTMLInputElement (line 49) | interface HTMLInputElement {
  class Iti (line 63) | class Iti {
    method constructor (line 93) | public constructor(input: HTMLInputElement, customOptions: SomeOptions...
    method #getTelInputValue (line 125) | #getTelInputValue(): string {
    method #setTelInputValue (line 130) | #setTelInputValue(asciiValue: string): void {
    method #createInitPromises (line 135) | #createInitPromises(options: AllOptions): Promise<[unknown, unknown]> {
    method #init (line 169) | #init(): void {
    method #processCountryData (line 202) | #processCountryData(): void {
    method #setInitialState (line 216) | #setInitialState(overrideAutoCountry: boolean = false): void {
    method #initListeners (line 263) | #initListeners(): void {
    method #initHiddenInputListener (line 277) | #initHiddenInputListener(): void {
    method #initDropdownListeners (line 292) | #initDropdownListeners(): void {
    method #initRequests (line 353) | #initRequests(): void {
    method #loadAutoCountry (line 394) | #loadAutoCountry(): void {
    method #openDropdownWithPlus (line 434) | #openDropdownWithPlus(): void {
    method #initTelInputListeners (line 441) | #initTelInputListeners(): void {
    method #bindInputListener (line 447) | #bindInputListener(): void {
    method #maybeBindKeydownListener (line 561) | #maybeBindKeydownListener(): void {
    method #maybeBindPasteListener (line 637) | #maybeBindPasteListener(): void {
    method #cap (line 712) | #cap(number: string): string {
    method #trigger (line 718) | #trigger<K extends keyof ItiEventMap>(
    method #openDropdown (line 731) | #openDropdown(): void {
    method #bindDropdownListeners (line 754) | #bindDropdownListeners(): void {
    method #bindDropdownMouseoverListener (line 769) | #bindDropdownMouseoverListener(signal: AbortSignal): void {
    method #bindDropdownCountryClickListener (line 789) | #bindDropdownCountryClickListener(signal: AbortSignal): void {
    method #bindDropdownClickOffListener (line 805) | #bindDropdownClickOffListener(signal: AbortSignal): void {
    method #bindDropdownKeydownListener (line 830) | #bindDropdownKeydownListener(signal: AbortSignal): void {
    method #bindDropdownSearchListeners (line 881) | #bindDropdownSearchListeners(signal: AbortSignal): void {
    method #searchForCountry (line 898) | #searchForCountry(query: string): void {
    method #handleEnterKey (line 910) | #handleEnterKey(): void {
    method #updateValFromNumber (line 918) | #updateValFromNumber(fullNumber: string): void {
    method #updateCountryFromNumber (line 945) | #updateCountryFromNumber(fullNumber: string): boolean {
    method #ensureHasDialCode (line 954) | #ensureHasDialCode(number: string): string {
    method #getNewCountryFromNumber (line 970) | #getNewCountryFromNumber(fullNumber: string): Iso2 | "" | null {
    method #setCountry (line 1065) | #setCountry(iso2: Iso2 | ""): boolean {
    method #updateMaxLength (line 1090) | #updateMaxLength(): void {
    method #updatePlaceholder (line 1129) | #updatePlaceholder(): void {
    method #selectListItem (line 1160) | #selectListItem(listItem: HTMLElement): void {
    method #closeDropdown (line 1184) | #closeDropdown(isDestroy?: boolean): void {
    method #updateDialCode (line 1202) | #updateDialCode(newDialCodeBare: string): void {
    method #getDialCode (line 1225) | #getDialCode(number: string, includeAreaCode?: boolean): string {
    method #getFullNumber (line 1267) | #getFullNumber(overrideVal?: string): string {
    method #beforeSetNumber (line 1288) | #beforeSetNumber(fullNumber: string): string {
    method #triggerCountryChange (line 1300) | #triggerCountryChange(): void {
    method #handleAutoCountry (line 1309) | #handleAutoCountry(): void {
    method #handleAutoCountryFailure (line 1335) | #handleAutoCountryFailure(): void {
    method #handleUtils (line 1348) | #handleUtils(): void {
    method #handleUtilsFailure (line 1371) | #handleUtilsFailure(error: unknown): void {
    method destroy (line 1386) | public destroy(): void {
    method isActive (line 1412) | public isActive(): boolean {
    method getExtension (line 1417) | public getExtension(): string {
    method getNumber (line 1428) | public getNumber(format?: number): string {
    method getNumberType (line 1444) | public getNumberType(): number {
    method getSelectedCountryData (line 1455) | public getSelectedCountryData(): SelectedCountryData {
    method getValidationError (line 1460) | public getValidationError(): number {
    method isValidNumber (line 1469) | public isValidNumber(): boolean | null {
    method isValidNumberPrecise (line 1502) | public isValidNumberPrecise(): boolean | null {
    method #utilsIsPossibleNumber (line 1506) | #utilsIsPossibleNumber(val: string): boolean | null {
    method #validateNumber (line 1517) | #validateNumber(precise: boolean): boolean | null {
    method #utilsIsValidNumber (line 1558) | #utilsIsValidNumber(val: string): boolean | null {
    method setCountry (line 1569) | public setCountry(iso2: Iso2): void {
    method setNumber (line 1596) | public setNumber(number: string): void {
    method setPlaceholderNumberType (line 1613) | public setPlaceholderNumberType(type: NumberType): void {
    method setDisabled (line 1621) | public setDisabled(disabled: boolean): void {
    method forEachInstance (line 1640) | static forEachInstance<M extends keyof ForEachInstanceArgsMap>(

FILE: src/js/intl-tel-input/data.ts
  type Iso2 (line 1444) | type Iso2 = typeof rawCountryData[number][0];
  type Country (line 1446) | type Country = {

FILE: src/js/intl-tel-input/i18n/ar/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/bg/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/bn/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/bs/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/ca/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/cs/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/da/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/de/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/el/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/en/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/es/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/et/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/fa/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/fi/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/fr/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/hi/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/hr/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/hu/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/id/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/it/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/ja/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/kn/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/ko/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/lt/index.ts
  method searchSummaryAria (line 13) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/mr/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/nl/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/no/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/pl/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/pt/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/ro/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/ru/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/sk/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/sl/index.ts
  method searchSummaryAria (line 13) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/sq/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/sr/index.ts
  method searchSummaryAria (line 13) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/sv/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/te/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/th/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/tr/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/types.ts
  type I18n (line 5) | type I18n = Partial<Record<Iso2, string>> & {

FILE: src/js/intl-tel-input/i18n/uk/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/ur/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/uz/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/vi/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/zh-hk/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/intl-tel-input/i18n/zh/index.ts
  method searchSummaryAria (line 12) | searchSummaryAria(count) {

FILE: src/js/modules/constants.ts
  constant EVENTS (line 5) | const EVENTS = {
  constant CLASSES (line 12) | const CLASSES = {
  constant KEYS (line 23) | const KEYS = {
  constant INPUT_TYPES (line 32) | const INPUT_TYPES = {
  constant REGEX (line 37) | const REGEX = {
  constant TIMINGS (line 44) | const TIMINGS = {
  constant SENTINELS (line 50) | const SENTINELS = {
  constant LAYOUT (line 56) | const LAYOUT = {
  constant DIAL (line 66) | const DIAL = {
  constant PLACEHOLDER_MODES (line 85) | const PLACEHOLDER_MODES = {
  constant INITIAL_COUNTRY (line 91) | const INITIAL_COUNTRY = {
  constant NUMBER_TYPES (line 98) | const NUMBER_TYPES = [
  constant NUMBER_TYPE_SET (line 113) | const NUMBER_TYPE_SET = new Set(NUMBER_TYPES) as ReadonlySet<
  constant DATA_KEYS (line 120) | const DATA_KEYS = {
  constant ARIA (line 126) | const ARIA = {

FILE: src/js/modules/core/numerals.ts
  class Numerals (line 1) | class Numerals {
    method constructor (line 4) | constructor() {
    method #updateNumeralSet (line 9) | #updateNumeralSet(str: string): void {
    method denormalise (line 20) | public denormalise(str: string, currentInputValue: string): string {
    method normalise (line 32) | public normalise(str: string): string {
    method isAscii (line 45) | public isAscii(): boolean {

FILE: src/js/modules/core/ui.ts
  class UI (line 13) | class UI {
    method constructor (line 44) | public constructor(input: HTMLInputElement, options: AllOptions, id: n...
    method validateInput (line 58) | public static validateInput(input: unknown): void {
    method generateMarkup (line 75) | public generateMarkup(countries: Country[]): void {
    method #createWrapperAndInsert (line 95) | #createWrapperAndInsert(): HTMLElement {
    method #maybeBuildCountryContainer (line 120) | #maybeBuildCountryContainer(wrapper: HTMLElement): void {
    method #maybeEnsureDropdownWidthSet (line 195) | #maybeEnsureDropdownWidthSet(): void {
    method #buildDropdownContent (line 209) | #buildDropdownContent(): void {
    method #buildSearchUI (line 277) | #buildSearchUI(): void {
    method #maybeUpdateInputPaddingAndReveal (line 349) | #maybeUpdateInputPaddingAndReveal(): void {
    method #maybeBuildHiddenInputs (line 356) | #maybeBuildHiddenInputs(wrapper: HTMLElement): void {
    method #appendListItems (line 397) | #appendListItems(): void {
    method #updateInputPadding (line 441) | #updateInputPadding(): void {
    method #getBody (line 458) | static #getBody(): HTMLElement {
    method #getHiddenSelectedCountryWidth (line 475) | #getHiddenSelectedCountryWidth(): number {
    method #getHiddenInlineDropdownHeight (line 501) | #getHiddenInlineDropdownHeight(): number {
    method #updateSearchResultsA11yText (line 520) | #updateSearchResultsA11yText(): void {
    method filterCountriesByQuery (line 527) | public filterCountriesByQuery(query: string): void {
    method #doFilter (line 540) | #doFilter(): void {
    method handleSearchChange (line 551) | public handleSearchChange(): void {
    method handleSearchClear (line 562) | public handleSearchClear(): void {
    method scrollTo (line 569) | public scrollTo(element: HTMLElement): void {
    method highlightListItem (line 591) | public highlightListItem(
    method handleUpDownKey (line 615) | public handleUpDownKey(key: string): void {
    method #updateSelectedItem (line 637) | #updateSelectedItem(iso2: Iso2 | ""): void {
    method #filterCountries (line 664) | #filterCountries(matchedCountries: Country[]): void {
    method destroy (line 694) | public destroy(): void {
    method openDropdown (line 740) | public openDropdown(): void {
    method closeDropdown (line 784) | public closeDropdown(): void {
    method #shouldPositionInlineDropdownBelowInput (line 815) | #shouldPositionInlineDropdownBelowInput(): boolean {
    method #handleDropdownContainer (line 827) | #handleDropdownContainer(): void {
    method isDropdownClosed (line 856) | public isDropdownClosed(): boolean {
    method setCountry (line 860) | public setCountry(selectedCountryData: SelectedCountryData): void {

FILE: src/js/modules/data/country-data.ts
  type DialCodeProcessingResult (line 5) | interface DialCodeProcessingResult {

FILE: src/js/modules/data/intl-regionless.ts
  constant REGIONLESS_DIAL_CODES (line 5) | const REGIONLESS_DIAL_CODES: Set<string> = new Set([

FILE: src/js/modules/types/events.ts
  type ItiEventMap (line 6) | type ItiEventMap = {

FILE: src/js/modules/types/forEachInstanceArgsMap.ts
  type ForEachInstanceArgsMap (line 1) | type ForEachInstanceArgsMap = {

FILE: src/js/modules/types/public-api.ts
  type UtilsLoader (line 8) | type UtilsLoader = () => Promise<{ default: ItiUtils }>;
  type ItiUtils (line 11) | type ItiUtils = {
  type SetValues (line 48) | type SetValues<T> = T extends ReadonlySet<infer U> ? U : never;
  type NumberType (line 50) | type NumberType = SetValues<typeof NUMBER_TYPE_SET>;
  type ValueOf (line 52) | type ValueOf<T> = T[keyof T];
  type AllOptions (line 55) | interface AllOptions {
  type SomeOptions (line 97) | type SomeOptions = Partial<AllOptions>;
  type IntlTelInputInterface (line 100) | interface IntlTelInputInterface {
  type EmptyObject (line 118) | type EmptyObject = Record<string, never>;
  type SelectedCountryData (line 119) | type SelectedCountryData = Country | EmptyObject;

FILE: tests/integration/helpers/matchers.js
  function toBeAPromise (line 2) | function toBeAPromise(actual) {
  function toBePending (line 10) | async function toBePending(actual) {

FILE: tests/integration/options/i18n-locales.test.js
  constant UI_TRANSLATION_KEYS (line 9) | const UI_TRANSLATION_KEYS = [

FILE: tests/integration/options/loadUtils.test.js
  method loadUtils (line 86) | async loadUtils () {
Condensed preview — 394 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (966K chars).
[
  {
    "path": ".eslintignore",
    "chars": 234,
    "preview": "**/demo/**\n**/grunt/**\n**/build/**\n**/third_party/**\n**/tmp/**\nGruntfile.js\n**/intl-tel-input/utils.js\nsite/static/js/hi"
  },
  {
    "path": ".eslintrc.js",
    "chars": 1451,
    "preview": "module.exports = {\n  root: true,\n  env: {\n    browser: true,\n    es2021: true,\n    node: true,\n    \"jest/globals\": true,"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 4039,
    "preview": "# Contributing\n\nI'm very open to contributions, big and small! For general instructions on submitting a pull request on "
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 17,
    "preview": "github: jackocnr\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1_bug_report.yml",
    "chars": 1936,
    "preview": "name: Bug Report\ndescription: Report a bug or issue with intl-tel-input\nlabels: [\"bug\"]\nbody:\n  - type: checkboxes\n    a"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/2_feature_request.yml",
    "chars": 483,
    "preview": "name: Feature Request\ndescription: Suggest a new feature or improvement\nlabels: [\"enhancement\"]\nbody:\n  - type: textarea"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 192,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: Ask the community\n    url: https://github.com/jackocnr/intl-tel-inp"
  },
  {
    "path": ".github/copilot-instructions.md",
    "chars": 1978,
    "preview": "At the beginning of a chat, remind me to run `npm run watch`, so any changes get automatically built, meaning I can manu"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1057,
    "preview": "name: CI\n\non:\n  push:\n    branches: [ \"**\" ]\n  pull_request:\n    branches: [ \"**\" ]\n\npermissions:\n  contents: read\n\njobs"
  },
  {
    "path": ".gitignore",
    "chars": 236,
    "preview": "/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/"
  },
  {
    "path": ".gitmodules",
    "chars": 124,
    "preview": "[submodule \"third_party/libphonenumber\"]\n\tpath = third_party/libphonenumber\n\turl = https://github.com/google/libphonenum"
  },
  {
    "path": ".node-version",
    "chars": 9,
    "preview": "v20.12.0\n"
  },
  {
    "path": ".npmrc",
    "chars": 170,
    "preview": "# Jest uses the `vm` module, which does not have production ready module support\n# yet. This option lets us use dynamic "
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 139,
    "preview": "{\n  \"recommendations\": [\n    \"rvest.vs-code-prettier-eslint\",\n    \"davidanson.vscode-markdownlint\",\n    \"aaron-bond.bett"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 1411,
    "preview": "{\n    \"editor.defaultFormatter\": \"rvest.vs-code-prettier-eslint\",\n    \"editor.formatOnPaste\": false, //* required\n    \"e"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 2167,
    "preview": "See the Github Releases page for changelog: https://github.com/jackocnr/intl-tel-input/releases\n\nOr to view a specific v"
  },
  {
    "path": "Gruntfile.js",
    "chars": 3475,
    "preview": "module.exports = function(grunt) {\n\n  // load all tasks from package.json\n  require('load-grunt-config')(grunt);\n  requi"
  },
  {
    "path": "LICENSE",
    "chars": 1085,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014-2016 Jack O'Connor\n\nPermission is hereby granted, free of charge, to any perso"
  },
  {
    "path": "README.md",
    "chars": 4545,
    "preview": "# International Telephone Input\n[![CI](https://github.com/jackocnr/intl-tel-input/actions/workflows/ci.yml/badge.svg?bra"
  },
  {
    "path": "angular/README.md",
    "chars": 330,
    "preview": "# IntlTelInput Angular Component\n\nAn Angular component for the [intl-tel-input](https://github.com/jackocnr/intl-tel-inp"
  },
  {
    "path": "angular/build.js",
    "chars": 1774,
    "preview": "const { build } = require(\"esbuild\");\nconst fs = require(\"fs\");\nconst packageJson = require(\"../package.json\");\n\nconst m"
  },
  {
    "path": "angular/demo/form/form.component.ts",
    "chars": 1641,
    "preview": "import { Component, OnInit, ViewChild } from \"@angular/core\";\nimport {\n  FormControl,\n  FormGroup,\n  ReactiveFormsModule"
  },
  {
    "path": "angular/demo/form/index.html",
    "chars": 675,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width,"
  },
  {
    "path": "angular/demo/form/main.ts",
    "chars": 235,
    "preview": "import 'zone.js';\nimport \"@angular/compiler\";\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport {"
  },
  {
    "path": "angular/demo/set-number/index.html",
    "chars": 708,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width,"
  },
  {
    "path": "angular/demo/set-number/main.ts",
    "chars": 241,
    "preview": "import 'zone.js';\nimport \"@angular/compiler\";\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport {"
  },
  {
    "path": "angular/demo/set-number/set-number.component.ts",
    "chars": 1737,
    "preview": "import { Component, ViewChild } from '@angular/core';\nimport { IntlTelInputComponent, PHONE_ERROR_MESSAGES } from '../.."
  },
  {
    "path": "angular/demo/simple/index.html",
    "chars": 552,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width,"
  },
  {
    "path": "angular/demo/simple/main.ts",
    "chars": 237,
    "preview": "import 'zone.js';\nimport \"@angular/compiler\";\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport {"
  },
  {
    "path": "angular/demo/simple/simple.component.ts",
    "chars": 358,
    "preview": "import { Component } from '@angular/core';\nimport { IntlTelInputComponent } from '../../src/intl-tel-input/angularWithUt"
  },
  {
    "path": "angular/demo/toggle-disabled/index.html",
    "chars": 633,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width,"
  },
  {
    "path": "angular/demo/toggle-disabled/main.ts",
    "chars": 246,
    "preview": "import 'zone.js';\nimport \"@angular/compiler\";\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport {"
  },
  {
    "path": "angular/demo/toggle-disabled/toggle-disabled.component.ts",
    "chars": 543,
    "preview": "import { Component } from '@angular/core';\nimport { IntlTelInputComponent } from '../../src/intl-tel-input/angularWithUt"
  },
  {
    "path": "angular/demo/validation/index.html",
    "chars": 637,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width,"
  },
  {
    "path": "angular/demo/validation/main.ts",
    "chars": 241,
    "preview": "import 'zone.js';\nimport \"@angular/compiler\";\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport {"
  },
  {
    "path": "angular/demo/validation/validation.component.ts",
    "chars": 1389,
    "preview": "import { Component } from '@angular/core';\nimport { IntlTelInputComponent, PHONE_ERROR_MESSAGES } from '../../src/intl-t"
  },
  {
    "path": "angular/src/intl-tel-input/angular.ts",
    "chars": 7418,
    "preview": "import intlTelInput from \"../intl-tel-input\";\n//* Keep the TS imports separate, as the above line gets substituted in th"
  },
  {
    "path": "angular/src/intl-tel-input/angularWithUtils.ts",
    "chars": 7470,
    "preview": "//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\nimport intlTelInput from \"./intlTelInputWithUtils\";\n//* Keep the TS import"
  },
  {
    "path": "angular/tsconfig.json",
    "chars": 742,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"es6\",\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"esModuleInter"
  },
  {
    "path": "build.js",
    "chars": 2481,
    "preview": "/* eslint-disable no-undef */\n/* eslint-disable @typescript-eslint/no-var-requires */\nconst { build } = require(\"esbuild"
  },
  {
    "path": "composer.json",
    "chars": 598,
    "preview": "{\n  \"name\": \"jackocnr/intl-tel-input\",\n  \"version\": \"26.8.1\",\n  \"description\": \"A JavaScript plugin for entering and val"
  },
  {
    "path": "cspell.json",
    "chars": 1610,
    "preview": "{\n  \"$schema\": \"https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json\",\n  \"version\": \"0.2\""
  },
  {
    "path": "demo.html",
    "chars": 3726,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "functions/_middleware.js",
    "chars": 1127,
    "preview": "/**\n * Cloudflare Workers middleware to inject a script tag with the user's geographical information (window.__IS_EUROPE"
  },
  {
    "path": "grunt/bump.js",
    "chars": 228,
    "preview": "module.exports = function(grunt) {\n  return {\n    options: {\n      files: ['package.json', 'package-lock.json', 'compose"
  },
  {
    "path": "grunt/clean.js",
    "chars": 948,
    "preview": "module.exports = function(grunt) {\n  return {\n    buildCss: ['build/css/*'],\n    buildImg: ['build/img/*'],\n    buildJs:"
  },
  {
    "path": "grunt/closure-compiler.js",
    "chars": 1196,
    "preview": "module.exports = function (grunt) {\n  return {\n    utils: {\n      files: {\n        \"build/js/utils.js\": \"src/js/utils.js"
  },
  {
    "path": "grunt/connect.js",
    "chars": 89,
    "preview": "module.exports = function(grunt) {\n  return {\n    test: {\n      port: 8000\n    }\n  };\n};\n"
  },
  {
    "path": "grunt/cssmin.js",
    "chars": 260,
    "preview": "module.exports = function(grunt) {\n  return {\n    target: {\n      files: {\n        'build/css/intlTelInput.min.css': 'bu"
  },
  {
    "path": "grunt/generate-sprite.js",
    "chars": 6270,
    "preview": "const fs = require('fs');\nconst path = require('path');\n// ts-node allows us to require TypeScript files\nrequire(\"ts-nod"
  },
  {
    "path": "grunt/replace.js",
    "chars": 3628,
    "preview": "module.exports = function(grunt) {\n  return {\n\n    /**************\n     * Update version numbers\n     **************/\n  "
  },
  {
    "path": "grunt/sass.js",
    "chars": 564,
    "preview": "const sass = require('sass');\n\nmodule.exports = function(grunt) {\n  return {\n    main: {\n      options: {\n        implem"
  },
  {
    "path": "grunt/shell.js",
    "chars": 1448,
    "preview": "const os = require('os');\n// On MacOS, sed requires the empty quotes in order to avoid creating a backup file, whereas o"
  },
  {
    "path": "grunt/translations.js",
    "chars": 2295,
    "preview": "const fs = require('fs');\nconst path = require('path');\n// ts-node allows us to require TypeScript files\nrequire(\"ts-nod"
  },
  {
    "path": "grunt/watch.js",
    "chars": 1397,
    "preview": "module.exports = function(grunt) {\n  return {\n    js: {\n      // only TS files (excludes utils.js, which is watched sepa"
  },
  {
    "path": "index.js",
    "chars": 53,
    "preview": "module.exports = require(\"./build/js/intlTelInput\");\n"
  },
  {
    "path": "jest.config.js",
    "chars": 790,
    "preview": "/** @type {import('jest').Config} */\nmodule.exports = {\n  roots: [\n    \"<rootDir>/tests\",\n  ],\n  moduleDirectories: [\n  "
  },
  {
    "path": "package.json",
    "chars": 6737,
    "preview": "{\n  \"name\": \"intl-tel-input\",\n  \"version\": \"26.8.1\",\n  \"description\": \"A JavaScript plugin for entering and validating i"
  },
  {
    "path": "playwright.config.ts",
    "chars": 1394,
    "preview": "import { defineConfig } from \"@playwright/test\";\n\nexport default defineConfig({\n  testDir: \"tests-e2e\",\n  timeout: 30_00"
  },
  {
    "path": "react/README.md",
    "chars": 320,
    "preview": "# IntlTelInput React Component\n\nA React component for the [intl-tel-input](https://github.com/jackocnr/intl-tel-input) J"
  },
  {
    "path": "react/build.js",
    "chars": 2184,
    "preview": "/* eslint-disable no-undef */\n/* eslint-disable @typescript-eslint/no-var-requires */\nconst { build } = require(\"esbuild"
  },
  {
    "path": "react/demo/set-number/SetNumberApp.tsx",
    "chars": 1602,
    "preview": "import React, { useState, ReactElement, useRef } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport Int"
  },
  {
    "path": "react/demo/set-number/set-number.html",
    "chars": 697,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width,"
  },
  {
    "path": "react/demo/simple/SimpleApp.tsx",
    "chars": 404,
    "preview": "import React, { ReactElement } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport IntlTelInput from \".."
  },
  {
    "path": "react/demo/simple/simple.html",
    "chars": 543,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width,"
  },
  {
    "path": "react/demo/toggle-disabled/ToggleDisabledApp.tsx",
    "chars": 640,
    "preview": "import React, { useState, ReactElement } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport IntlTelInpu"
  },
  {
    "path": "react/demo/toggle-disabled/toggle-disabled.html",
    "chars": 614,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width,"
  },
  {
    "path": "react/demo/validation/ValidationApp.tsx",
    "chars": 1349,
    "preview": "import React, { useState, ReactElement } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport IntlTelInpu"
  },
  {
    "path": "react/demo/validation/validation.html",
    "chars": 628,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width,"
  },
  {
    "path": "react/src/intl-tel-input/react.tsx",
    "chars": 4819,
    "preview": "import intlTelInput from \"../intl-tel-input\";\n//* Keep the TS imports separate, as the above line gets substituted in th"
  },
  {
    "path": "react/src/intl-tel-input/reactWithUtils.tsx",
    "chars": 4871,
    "preview": "//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\nimport intlTelInput from \"./intlTelInputWithUtils\";\n//* Keep the TS import"
  },
  {
    "path": "react/tsconfig.json",
    "chars": 640,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"es6\",\n    \"jsx\": \"react\",\n    \"module\": \"nodenext\",\n    \"moduleResolution\": \"nod"
  },
  {
    "path": "scripts/check-lpn-metadata.cjs",
    "chars": 33156,
    "preview": "/**\n * This script loads the existing country data from data.ts,\n * then parses libphonenumber's PhoneNumberMetadata.xml"
  },
  {
    "path": "scripts/playwright-linux-docker.sh",
    "chars": 2648,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\n\nPLAYWRIGHT_VERSION_DEFAULT=\"1.58.0\"\nPLAYWRIGHT_VERSION=\"$PLAYWRIGHT_VERSION_DEFAU"
  },
  {
    "path": "site/.gitignore",
    "chars": 26,
    "preview": "node_modules/\ntmp/\nbuild/\n"
  },
  {
    "path": "site/Gruntfile.js",
    "chars": 1687,
    "preview": "module.exports = function (grunt) {\n  // load all tasks from package.json\n  require(\"load-grunt-config\")(grunt);\n\n  // b"
  },
  {
    "path": "site/README.md",
    "chars": 892,
    "preview": "# 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- Pl"
  },
  {
    "path": "site/esbuild/build.mjs",
    "chars": 1422,
    "preview": "import { build } from \"esbuild\";\nimport externalUtilsPlugin from \"./externalUtilsPlugin.mjs\";\n\nconst sharedOptions = {\n "
  },
  {
    "path": "site/esbuild/externalUtilsPlugin.mjs",
    "chars": 235,
    "preview": "const externalUtilsPlugin = {\n  name: \"external-utils-plugin\",\n  setup({ onResolve }) {\n    onResolve({ filter: /utils/ "
  },
  {
    "path": "site/grunt/copy.js",
    "chars": 1951,
    "preview": "module.exports = function (grunt) {\n  return {\n    plugin: {\n      cwd: \"../build\", // set working folder / root to copy"
  },
  {
    "path": "site/grunt/cssmin.js",
    "chars": 456,
    "preview": "module.exports = function(grunt) {\n  return {\n    target: {\n      files: {\n        // NOTE: don't minify large_flags_ove"
  },
  {
    "path": "site/grunt/fetchStats.js",
    "chars": 2799,
    "preview": "const https = require(\"https\");\nconst fs = require(\"fs\");\nconst path = require(\"path\");\n\nfunction httpsGet(url) {\n  retu"
  },
  {
    "path": "site/grunt/replace.js",
    "chars": 387,
    "preview": "module.exports = function(grunt) {\n  return {\n    validationPrecise: {\n      options: {\n        patterns: [\n          {\n"
  },
  {
    "path": "site/grunt/sass.js",
    "chars": 610,
    "preview": "const sass = require('sass');\n\nmodule.exports = function(grunt) {\n  return {\n    main: {\n      options: {\n        implem"
  },
  {
    "path": "site/grunt/shell.js",
    "chars": 510,
    "preview": "const os = require('os');\n\nmodule.exports = function(grunt) {\n  return {\n    clearBuild: {\n      command: \"rm -rf build "
  },
  {
    "path": "site/grunt/template.js",
    "chars": 23686,
    "preview": "module.exports = function (grunt) {\n  const path = require(\"path\");\n  const {\n    cacheBust,\n    getDirHash,\n    getI18n"
  },
  {
    "path": "site/grunt/templateGruntHelpers.js",
    "chars": 1468,
    "preview": "const makeTemplateTask = (src, dest, data) => ({\n  src,\n  dest,\n  options: {\n    data,\n  },\n});\n\nconst readCommonPagePar"
  },
  {
    "path": "site/grunt/templateNav.js",
    "chars": 2539,
    "preview": "const docsDropdownPages = [\n  { name: \"choose_integration\", href: \"/docs/choose-integration\", label: \"Choose integration"
  },
  {
    "path": "site/grunt/templateUtils.js",
    "chars": 9302,
    "preview": "const path = require(\"path\");\nconst fs = require(\"fs\");\nconst crypto = require(\"crypto\");\nconst MarkdownIt = require(\"ma"
  },
  {
    "path": "site/grunt/watch.js",
    "chars": 164,
    "preview": "module.exports = function(grunt) {\n  return {\n    js: {\n      files: [\"src/**/*\", \"static/**/*\", \"grunt/**/*\", \"../build"
  },
  {
    "path": "site/package.json",
    "chars": 1042,
    "preview": "{\n  \"name\": \"iti-website\",\n  \"version\": \"1.0.0\",\n  \"description\": \"The website for intl-tel-input\",\n  \"dependencies\": {\n"
  },
  {
    "path": "site/src/404/404_content.html",
    "chars": 409,
    "preview": "<section class=\"py-5\">\n  <div class=\"row justify-content-center\">\n    <div class=\"col-12 col-md-10 col-lg-7 text-center\""
  },
  {
    "path": "site/src/404/404_page_template.html.ejs",
    "chars": 418,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <%= common_meta_tags %>\n    <title><%= head_title %></title>\n    <link rel"
  },
  {
    "path": "site/src/css/_base.scss",
    "chars": 2451,
    "preview": "html {\n  /* fix: links with # scroll values in (e.g. docs/options#separatedialcode) scroll until subtitle is below heade"
  },
  {
    "path": "site/src/css/_forms.scss",
    "chars": 951,
    "preview": "// example page demos\n.demo input[type=\"tel\"] {\n  width: 250px;\n}\n// override bootstrap default placeholder color which "
  },
  {
    "path": "site/src/css/_layout.scss",
    "chars": 1337,
    "preview": "/* LAYOUT */\n.iti-gutter {\n  --bs-gutter-x: 3rem;\n}\n/* never show the right sidebar on mobile (or on some desktop pages)"
  },
  {
    "path": "site/src/css/_navbar.scss",
    "chars": 5441,
    "preview": "/* TOP NAVBAR */\n.iti-navbar {\n  padding: 0.75rem 0;\n  background-image: linear-gradient(rgb(var(--header-bg-color-rgb) "
  },
  {
    "path": "site/src/css/_variables.scss",
    "chars": 433,
    "preview": ":root {\n  /* Header background color as space-separated RGB for easy alpha usage via: rgb(var(--header-bg-color-rgb) / <"
  },
  {
    "path": "site/src/css/docs.scss",
    "chars": 1013,
    "preview": "/* Docs options page layout: keep code blocks within the content width */\n.iti-doc-options {\n  &__row {\n    border: var("
  },
  {
    "path": "site/src/css/highlightjs_overrides.scss",
    "chars": 267,
    "preview": "/* Highlight.js overrides */\n.hljs {\n  @media (prefers-color-scheme: light), (prefers-color-scheme: no-preference) {\n   "
  },
  {
    "path": "site/src/css/homepage.scss",
    "chars": 5473,
    "preview": "@use 'variables';\n\n/**************\n * HOMEPAGE\n **************/\n body.iti-page--homepage {\n  background-image: linear-gr"
  },
  {
    "path": "site/src/css/large_flags_overrides.scss",
    "chars": 708,
    "preview": ":root {\n  --iti-spacer-horizontal: 12px;\n  --iti-globe-height: 22px;\n  --iti-search-clear-icon-height: 16px;\n  --iti-arr"
  },
  {
    "path": "site/src/css/playground.scss",
    "chars": 5900,
    "preview": "@use 'variables';\n\n/**************\n * PLAYGROUND\n **************/\n\n// When navigating to a Playground heading via hash, "
  },
  {
    "path": "site/src/css/website.scss",
    "chars": 312,
    "preview": "\n/* Split into partials for maintainability. Partial files live alongside this file.\n   Keep this file as the single ent"
  },
  {
    "path": "site/src/docs/docs_content_template.html.ejs",
    "chars": 557,
    "preview": "<nav class=\"iti-breadcrumb\" aria-label=\"Breadcrumb\">\n  <ol class=\"iti-breadcrumb__list\">\n    <li class=\"iti-breadcrumb__"
  },
  {
    "path": "site/src/docs/docs_nav_template.html.ejs",
    "chars": 310,
    "preview": "<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  <"
  },
  {
    "path": "site/src/docs/docs_page_template.html.ejs",
    "chars": 490,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <%= common_meta_tags %>\n    <title><%= head_title %></title>\n    <link rel"
  },
  {
    "path": "site/src/docs/markdown/accessibility.md",
    "chars": 3255,
    "preview": "# Accessibility\n\nintl-tel-input aims to be accessible out of the box, but good accessibility also depends on how you int"
  },
  {
    "path": "site/src/docs/markdown/angular_component.md",
    "chars": 8181,
    "preview": "# Angular component\n\nAn Angular component for the intl-tel-input JavaScript plugin. View the [source code](https://githu"
  },
  {
    "path": "site/src/docs/markdown/choose_integration.md",
    "chars": 1918,
    "preview": "# Choosing your integration\n\n`intl-tel-input` is available in two primary flavors: a standalone **JavaScript plugin** an"
  },
  {
    "path": "site/src/docs/markdown/events.md",
    "chars": 621,
    "preview": "# Events\n\nYou can listen for the following events triggered on the input element.\n\n## countrychange\n\nThis is triggered w"
  },
  {
    "path": "site/src/docs/markdown/faq.md",
    "chars": 4944,
    "preview": "# 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 num"
  },
  {
    "path": "site/src/docs/markdown/getting_started.md",
    "chars": 5441,
    "preview": "# Getting started with the JavaScript plugin\n\nThis page is for getting started with the JavaScript plugin. For the frame"
  },
  {
    "path": "site/src/docs/markdown/localisation.md",
    "chars": 3609,
    "preview": "# Localisation\n\nintl-tel-input supports localisation in a few different areas:\n\n- Country names in the dropdown\n- User i"
  },
  {
    "path": "site/src/docs/markdown/methods.md",
    "chars": 7960,
    "preview": "# Methods\n\nThis page lists the plugin's public API methods.\n\n## Contents\n\n- [Instance methods](#instance-methods)\n- [Sta"
  },
  {
    "path": "site/src/docs/markdown/options.md",
    "chars": 22251,
    "preview": "# Initialisation options\n\nWhen you initialise the plugin, the first argument is the input element, and the second is an "
  },
  {
    "path": "site/src/docs/markdown/react_component.md",
    "chars": 6098,
    "preview": "# React component\n\nA React component for the intl-tel-input JavaScript plugin. View the [source code](https://github.com"
  },
  {
    "path": "site/src/docs/markdown/svelte_component.md",
    "chars": 5544,
    "preview": "# Svelte component\n\nA Svelte 5 component for the intl-tel-input JavaScript plugin. View the [source code](https://github"
  },
  {
    "path": "site/src/docs/markdown/theming.md",
    "chars": 1189,
    "preview": "# Theming / dark mode\n\nThere are lots of CSS variables available for theming. See [intlTelInput.scss](https://github.com"
  },
  {
    "path": "site/src/docs/markdown/troubleshooting.md",
    "chars": 2516,
    "preview": "# 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"
  },
  {
    "path": "site/src/docs/markdown/utils.md",
    "chars": 2620,
    "preview": "# Utilities script\n\nThe utilities script (src/js/utils.js) is a custom build of Google's [libphonenumber](https://github"
  },
  {
    "path": "site/src/docs/markdown/vue_component.md",
    "chars": 5864,
    "preview": "# Vue component\n\nA Vue component for the intl-tel-input JavaScript plugin. View the [source code](https://github.com/jac"
  },
  {
    "path": "site/src/examples/copy/angular_component_desc.html",
    "chars": 649,
    "preview": "<p>This project includes an official Angular component alongside the original JavaScript plugin. It supports all of the "
  },
  {
    "path": "site/src/examples/copy/display_number_desc.html",
    "chars": 1058,
    "preview": "<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 in"
  },
  {
    "path": "site/src/examples/copy/hidden_input_desc.html",
    "chars": 1490,
    "preview": "<p>If you're handling your form submissions using good old-fashioned page requests (instead of modern JavaScript fetch r"
  },
  {
    "path": "site/src/examples/copy/large_flags_desc.html",
    "chars": 1576,
    "preview": "<p>While you can easily override a lot of the plugin's styles in your own CSS, you cannot change the flag image dimensio"
  },
  {
    "path": "site/src/examples/copy/lookup_country_desc.html",
    "chars": 1396,
    "preview": "<p>For the best user experience, International Telephone Input supports setting the initial selected country to the user"
  },
  {
    "path": "site/src/examples/copy/multiple_instances_desc.html",
    "chars": 949,
    "preview": "<p>If you have multiple telephone inputs on the same page, it is safe to initialise International Telephone Input on eac"
  },
  {
    "path": "site/src/examples/copy/react_component_desc.html",
    "chars": 818,
    "preview": "<p>This project includes an official React component alongside the original JavaScript plugin. It supports all of the sa"
  },
  {
    "path": "site/src/examples/copy/right_to_left_desc.html",
    "chars": 569,
    "preview": "<p>In this demo, we add <code>dir=\"rtl\"</code> to the plugin container so the layout flows right-to-left. Phone numbers "
  },
  {
    "path": "site/src/examples/copy/single_country_desc.html",
    "chars": 845,
    "preview": "<p>Even if you only need to handle numbers from a single country, the plugin can still be helpful. It can provide an exa"
  },
  {
    "path": "site/src/examples/copy/svelte_component_desc.html",
    "chars": 753,
    "preview": "<p>This project includes an official Svelte component alongside the original JavaScript plugin. It supports all of the s"
  },
  {
    "path": "site/src/examples/copy/validation_practical_desc.html",
    "chars": 1995,
    "preview": "<p>It is important to validate any user input before saving it to your backend. The <a href=\"/docs/methods#isvalidnumber"
  },
  {
    "path": "site/src/examples/copy/validation_precise_desc.html",
    "chars": 1901,
    "preview": "<p>WARNING: Various countries around the world update their number rules every month, so <a href=\"/docs/methods#isvalidn"
  },
  {
    "path": "site/src/examples/copy/vue_component_desc.html",
    "chars": 791,
    "preview": "<p>This project includes an official Vue component alongside the original JavaScript plugin. It supports all of the same"
  },
  {
    "path": "site/src/examples/css/multiple_instances.css",
    "chars": 34,
    "preview": "td {\n  padding: 7px 10px 7px 0;\n}\n"
  },
  {
    "path": "site/src/examples/css/validation.css",
    "chars": 134,
    "preview": "#error-msg {\n  color: red;\n}\n#valid-msg {\n  color: #00c900;\n}\ninput.error {\n  border: 1px solid #ff7c7c;\n}\n.hide {\n  dis"
  },
  {
    "path": "site/src/examples/examples_content_template.html.ejs",
    "chars": 1333,
    "preview": "<nav class=\"iti-breadcrumb\" aria-label=\"Breadcrumb\">\n  <ol class=\"iti-breadcrumb__list\">\n    <li class=\"iti-breadcrumb__"
  },
  {
    "path": "site/src/examples/examples_nav_template.html.ejs",
    "chars": 317,
    "preview": "<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\">"
  },
  {
    "path": "site/src/examples/examples_page_template.html.ejs",
    "chars": 562,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <%= common_meta_tags %>\n    <title><%= head_title %></title>\n    <link rel"
  },
  {
    "path": "site/src/examples/html/component.html",
    "chars": 20,
    "preview": "<div id=\"app\"></div>"
  },
  {
    "path": "site/src/examples/html/display_number.html",
    "chars": 105,
    "preview": "<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",
    "chars": 52,
    "preview": "<input id=\"phone\" type=\"tel\" value=\"+447733312345\">\n"
  },
  {
    "path": "site/src/examples/html/multiple_instances.html",
    "chars": 684,
    "preview": "<form>\n  <div class=\"row mb-3 align-items-center\">\n    <label for=\"home\" class=\"col-sm-1 col-form-label\">Home</label>\n  "
  },
  {
    "path": "site/src/examples/html/multiple_instances_display_code.html",
    "chars": 93,
    "preview": "<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",
    "chars": 84,
    "preview": "<input id=\"phone\" type=\"tel\" class=\"form-control\" title=\"Enter your phone number\" />"
  },
  {
    "path": "site/src/examples/html/simple_input_display_code.html",
    "chars": 30,
    "preview": "<input id=\"phone\" type=\"tel\">\n"
  },
  {
    "path": "site/src/examples/html/validation.html",
    "chars": 487,
    "preview": "<form id=\"form\" class=\"row g-2 validation-demo\" noValidate>\n  <div class=\"col-auto\">\n    <input\n      id=\"phone\"\n      t"
  },
  {
    "path": "site/src/examples/html/validation_display_code.html",
    "chars": 126,
    "preview": "<form id=\"form\">\n  <input id=\"phone\" type=\"tel\">\n  <button type=\"submit\">Submit</button>\n</form>\n<span id=\"error-msg\"></"
  },
  {
    "path": "site/src/examples/js/angular_component.ts",
    "chars": 3186,
    "preview": "import \"zone.js\";\nimport \"@angular/compiler\";\nimport { bootstrapApplication } from \"@angular/platform-browser\";\nimport {"
  },
  {
    "path": "site/src/examples/js/angular_component_display_code.js",
    "chars": 1189,
    "preview": "import { Component, ViewChild } from \"@angular/core\";\nimport { FormControl, FormGroup, ReactiveFormsModule, Validators }"
  },
  {
    "path": "site/src/examples/js/hidden_input.js",
    "chars": 2194,
    "preview": "const form = document.querySelector(\"#form\");\nconst input = document.querySelector(\"#phone\");\nconst errorMsg = document."
  },
  {
    "path": "site/src/examples/js/hidden_input_display_code.js",
    "chars": 1586,
    "preview": "import intlTelInput from \"intl-tel-input\";\n\nconst form = document.querySelector(\"#form\");\nconst input = document.querySe"
  },
  {
    "path": "site/src/examples/js/lookup_country.js",
    "chars": 443,
    "preview": "const input = document.querySelector(\"#phone\");\nwindow.intlTelInput(input, {\n  initialCountry: \"auto\",\n  geoIpLookup: (s"
  },
  {
    "path": "site/src/examples/js/lookup_country_display_code.js",
    "chars": 391,
    "preview": "import intlTelInput from \"intl-tel-input\";\n\nconst input = document.querySelector(\"#phone\");\nintlTelInput(input, {\n  init"
  },
  {
    "path": "site/src/examples/js/multiple_instances.js",
    "chars": 808,
    "preview": "const inputHome = document.querySelector(\"#home\");\nconst inputMobile = document.querySelector(\"#mobile\");\nconst inputVac"
  },
  {
    "path": "site/src/examples/js/multiple_instances_display_code.js",
    "chars": 642,
    "preview": "import intlTelInput from \"intl-tel-input\";\n\nconst inputHome = document.querySelector(\"#home\");\nconst inputMobile = docum"
  },
  {
    "path": "site/src/examples/js/react_component.js",
    "chars": 2424,
    "preview": "import React, { useState } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport IntlTelInput from \"../../"
  },
  {
    "path": "site/src/examples/js/react_component_display_code.js",
    "chars": 1171,
    "preview": "import React, { useState } from \"react\";\nimport IntlTelInput from \"intl-tel-input/react\";\nimport \"intl-tel-input/styles\""
  },
  {
    "path": "site/src/examples/js/right_to_left.js",
    "chars": 396,
    "preview": "import intlTelInput from \"../../../build/intl-tel-input/js/intlTelInput\";\nimport ar from \"../../../build/intl-tel-input/"
  },
  {
    "path": "site/src/examples/js/right_to_left_display_code.js",
    "chars": 308,
    "preview": "import intlTelInput from \"intl-tel-input\";\n// Arabic\nimport { ar } from \"intl-tel-input/i18n\";\n\nconst input = document.q"
  },
  {
    "path": "site/src/examples/js/simple_init_plugin.js",
    "chars": 219,
    "preview": "const input = document.querySelector(\"#phone\");\nwindow.intlTelInput(input, {\n  initialCountry: \"us\",\n  loadUtils: () => "
  },
  {
    "path": "site/src/examples/js/simple_init_plugin_display_code.js",
    "chars": 193,
    "preview": "import intlTelInput from \"intl-tel-input\";\n\nconst input = document.querySelector(\"#phone\");\nintlTelInput(input, {\n  init"
  },
  {
    "path": "site/src/examples/js/single_country.js",
    "chars": 2076,
    "preview": "const form = document.querySelector(\"#form\");\nconst input = document.querySelector(\"#phone\");\nconst errorMsg = document."
  },
  {
    "path": "site/src/examples/js/single_country_display_code.js",
    "chars": 1099,
    "preview": "import intlTelInput from \"intl-tel-input\";\n\nconst form = document.querySelector(\"#form\");\nconst input = document.querySe"
  },
  {
    "path": "site/src/examples/js/svelte_component.svelte",
    "chars": 2086,
    "preview": "<script>\n  import IntlTelInput from \"../../../../svelte/src/intl-tel-input/IntlTelInput.svelte\";\n\n  const errorMap = [\n "
  },
  {
    "path": "site/src/examples/js/svelte_component_display_code.svelte",
    "chars": 908,
    "preview": "<script>\n  import IntlTelInput from \"intl-tel-input/svelteWithUtils\";\n  import \"intl-tel-input/styles\";\n\n  let number = "
  },
  {
    "path": "site/src/examples/js/svelte_main.js",
    "chars": 158,
    "preview": "import App from \"../../../tmp/examples/js/svelte_component.svelte\";\n\nimport { mount } from \"svelte\";\n\nmount(App, { targe"
  },
  {
    "path": "site/src/examples/js/validation.js",
    "chars": 2068,
    "preview": "const form = document.querySelector(\"#form\");\nconst input = document.querySelector(\"#phone\");\nconst errorMsg = document."
  },
  {
    "path": "site/src/examples/js/validation_display_code.js",
    "chars": 1054,
    "preview": "import intlTelInput from \"intl-tel-input\";\n\nconst form = document.querySelector(\"#form\");\nconst input = document.querySe"
  },
  {
    "path": "site/src/examples/js/viteSvelteDemo.config.mjs",
    "chars": 966,
    "preview": "// needed for svelte component demo page\n\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport"
  },
  {
    "path": "site/src/examples/js/viteVueDemo.config.js",
    "chars": 818,
    "preview": "// needed for vue component demo page\n\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { "
  },
  {
    "path": "site/src/examples/js/vue_component.vue",
    "chars": 2307,
    "preview": "<script setup>\n  import { computed, ref } from \"vue\";\n  import IntlTelInput from \"../../../build/intl-tel-input/vue/expo"
  },
  {
    "path": "site/src/examples/js/vue_component_display_code.vue",
    "chars": 950,
    "preview": "<script setup>\n  import { computed, ref } from \"vue\";\n  import IntlTelInput from \"intl-tel-input/vue\";\n  import \"intl-te"
  },
  {
    "path": "site/src/examples/js/vue_main.js",
    "chars": 126,
    "preview": "import { createApp } from \"vue\";\nimport App from \"../../../tmp/examples/js/vue_component.vue\";\n\ncreateApp(App).mount(\"#a"
  },
  {
    "path": "site/src/homepage/homepage_content.html",
    "chars": 10014,
    "preview": "<div class=\"d-flex flex-column align-items-center text-center homepage-content\">\n  <div class=\"section section--homepage"
  },
  {
    "path": "site/src/homepage/homepage_page_template.html.ejs",
    "chars": 637,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <%= common_meta_tags %>\n    <title><%= homepageTitle %></title>\n    <link "
  },
  {
    "path": "site/src/js/homepage.js",
    "chars": 423,
    "preview": "const input = document.querySelector(\"#phone\");\nwindow.intlTelInput(input, {\n  searchInputClass: \"form-control\",\n  initi"
  },
  {
    "path": "site/src/js/iti-live-results.js",
    "chars": 1978,
    "preview": "(() => {\n  const errorMap = [\"\", \"Bad country code\", \"Too short\", \"Too long\"];\n  const getItiInstance = () => Object.val"
  },
  {
    "path": "site/src/layout_template.html.ejs",
    "chars": 16165,
    "preview": "<!-- HEADER -->\n<header class=\"navbar navbar-expand-lg navbar-dark iti-navbar sticky-top\">\n  <nav class=\"container-xxl i"
  },
  {
    "path": "site/src/playground/js/modules/clipboard.js",
    "chars": 946,
    "preview": "function copyTextToClipboard(text) {\n  const value = String(text || \"\");\n  if (!value) return Promise.resolve(false);\n  "
  },
  {
    "path": "site/src/playground/js/modules/forms.js",
    "chars": 16969,
    "preview": "import { deepClone, parseJsonParam, safeStringify } from \"./stateUtils.js\";\n// Add a single event listener for all enabl"
  },
  {
    "path": "site/src/playground/js/modules/i18n.js",
    "chars": 1833,
    "preview": "const i18nDisplayNames = (() => {\n  try {\n    if (typeof Intl === \"undefined\" || !Intl.DisplayNames) return null;\n    re"
  },
  {
    "path": "site/src/playground/js/modules/initCode.js",
    "chars": 3388,
    "preview": "import { formatJsValue, isDefaultForKey } from \"./stateUtils.js\";\n\n// This module is responsible for generating the init"
  },
  {
    "path": "site/src/playground/js/modules/itiController.js",
    "chars": 3933,
    "preview": "import { resolveI18nSelection } from \"./i18n.js\";\n\nfunction applyInputAttributes(state, telInput) {\n  if (!state || !tel"
  },
  {
    "path": "site/src/playground/js/modules/playgroundConfig.js",
    "chars": 8886,
    "preview": "const AUTO_PLACEHOLDER_OPTIONS = [\"polite\", \"aggressive\", \"off\"];\nconst NUMBER_TYPES = [\n  \"FIXED_LINE\",\n  \"MOBILE\",\n  \""
  },
  {
    "path": "site/src/playground/js/modules/stateUtils.js",
    "chars": 4962,
    "preview": "export function deepClone(value) {\n  return JSON.parse(JSON.stringify(value));\n}\n\nexport function parseBooleanParam(valu"
  },
  {
    "path": "site/src/playground/js/modules/urlState.js",
    "chars": 1542,
    "preview": "import { encodeJsonParam, isDefaultForKey } from \"./stateUtils.js\";\n\nexport function buildShareUrlFromState(\n  state,\n  "
  },
  {
    "path": "site/src/playground/js/playground.js",
    "chars": 24501,
    "preview": "import {\n  I18N_LANGUAGE_CODES,\n  UTILS_PATH,\n  I18N_DIR_HASH,\n} from \"../../../tmp/playground/playgroundConstants.js\";\n"
  },
  {
    "path": "site/src/playground/js/templates/playgroundConstants.js.ejs",
    "chars": 350,
    "preview": "// 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"
  },
  {
    "path": "site/src/playground/playground_content.html",
    "chars": 8864,
    "preview": "<div class=\"iti-playground\" id=\"itiPlayground\">\n\n  <template id=\"itiPlaygroundInfoIconTemplate\">\n    <svg xmlns=\"http://"
  },
  {
    "path": "site/src/playground/playground_page_template.html.ejs",
    "chars": 650,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <%= common_meta_tags %>\n    <title><%= playgroundTitle %></title>\n    <lin"
  },
  {
    "path": "site/src/shared/common_body_end.html",
    "chars": 815,
    "preview": "<script\n  src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js\"\n  integrity=\"sha384-FKyoEFo"
  },
  {
    "path": "site/src/shared/common_head_end_prod.html",
    "chars": 2630,
    "preview": "<!-- PostHog error reporting / replays -->\n<script>\n    // our window.__IS_EUROPE variable must be defined and set to fa"
  },
  {
    "path": "site/src/shared/common_meta_tags.html",
    "chars": 158,
    "preview": "<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n<link rel=\"icon\" type=\"i"
  },
  {
    "path": "site/src/shared/common_styles.html.ejs",
    "chars": 2743,
    "preview": "<!-- Bootstrap styles -->\n<link\n  href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css\"\n  rel=\""
  },
  {
    "path": "site/src/shared/iti_live_results_script.html.ejs",
    "chars": 68,
    "preview": "<script src=\"<%= cacheBust('/js/iti-live-results.js') %>\"></script>\n"
  },
  {
    "path": "site/src/shared/iti_script.html.ejs",
    "chars": 158,
    "preview": "<% const scriptFile = isDevBuild ? 'intlTelInput.js' : 'intlTelInput.min.js'; %>\n<script src=\"<%= cacheBust('/intl-tel-i"
  },
  {
    "path": "site/static/_redirects",
    "chars": 3644,
    "preview": "# ancient links from when example pages were in ITI project, loaded into website project through node_modules\n/node_modu"
  },
  {
    "path": "site/static/ads.txt",
    "chars": 58,
    "preview": "google.com, pub-1090343328224651, DIRECT, f08c47fec0942fa0"
  }
]

// ... and 194 more files (download for full content)

About this extraction

This page contains the full source code of the jackocnr/intl-tel-input GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 394 files (874.7 KB), approximately 246.3k tokens, and a symbol index with 360 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!